Uh oh!
There was an error while loading. Please reload this page.
fix(metadata-protocol): listCommits emits the ISO-8601 string createdAt declares - #15008
Conversation
…At declares `listCommits`'s declared return type says `createdAt?: string`, but the mapping assigned the raw driver value straight through. `created_at` is an engine-injected audit column, and `SqlDriver#formatOutput` repairs it only inside its `if (this.isSqlite)` arm, so Postgres and MySQL hand it out of the record read door as a JS `Date` — a value every in-process consumer received in a field the type promised was a `string`. Follows #14037's precedent: a narrow per-site `isoFromValidDate` helper converts the one measured shape (a valid `Date`) and returns every other shape, including an Invalid `Date`, unchanged — deliberately not the shared `canonicalIsoInstant` spelling, which raises RangeError on an Invalid `Date` reachable on both live dialects (#14078, on which #13973 is blocked). Fixes#14038 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
check:system-context-census pins doc-table anchors by line number; the listCommits fix above inserted ~40 lines earlier in protocol.ts and shifted one anchored elevation-read site. Mechanical re-anchor via `node scripts/check-system-context-census.mjs --fix` — no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
check:engine-double-contract requires every findOne/update/delete fake engine double in a test file to be registered in the pinned ledger. Registers the read-only findOne double the new #14038 pin test uses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…stcommits-createdat-iso
Merging origin/main shifted lines in identity-write-guard.ts, auth-plugin.ts and share-link-service.ts (unrelated incoming commits); mechanical re-anchor via `node scripts/check-system-context-census.mjs --fix` — no behaviour change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
…stcommits-createdat-iso
📓 Docs Drift Check3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 9 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin e27aefacbc14bbdf7ca08b3bbcbcc13db5e5d500 && git checkout e27aefacbc14bbdf7ca08b3bbcbcc13db5e5d500
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a54855005d337927632be4d864f0c85fa3a698e6 c313a7c20519743c80771961a0b9dd381670fb8e && git checkout -B drift-repro a54855005d337927632be4d864f0c85fa3a698e6 && git merge --no-ff c313a7c20519743c80771961a0b9dd381670fb8e
node scripts/docs-audit/affected-docs.mjs --json a54855005d337927632be4d864f0c85fa3a698e6 |
os-musk
commented
Sep 3, 2026
Independent review — PASS; marked ready and armed for the merge queueAn independent reviewer (not the author) re-derived this PR from the card, the ruling and the code rather than from the PR body. Verdict PASS, no blocking defects. What was re-proved rather than accepted: The helper decision is not an implementer's invention — it executes a ruling that predates #14037. Triage's ruling on #14038 (comment 5487875301) rules option 1 (canonicalise at the mapping) and names this route explicitly: 「与 #13997 走的、以及 #14037 将走的是同一条路」. So the local-helper pattern was pre-authorised before #14037 existed, and #14939 (which closed #14037) then landed the byte-identical The three premises behind refusing The reviewer found stronger evidence for the choice than the PR argues for itself. Tracing what would happen if the Ruling compliance on the one instruction that constrains the prose. The ruling also says ⛔ 「PR说明里不要把这次修复写成「行为变更」」 — the fix honours a declaration rather than changing behaviour. Verified: no "behaviour/behavior change" framing appears in this PR's own text (the two hits in Changeset Clause-② = no. Symbol fence, resolved on this head rather than from quoted numbers. Two ablations, both directions, restore proven by state. Restoring the raw assignment reddens exactly §A ( Consumers searched independently. Merge fallout resolved the right way. The two Gates: 72 commands derived off the merge base with no path arguments — 68 exit 0, 4 genuine One non-blocking fast-follow, recorded not filed
Generated by Claude Code |
…stcommits-createdat-iso
Line numbers in protocol.ts and engine.ts shifted after merging main's in-flight work (through f594e70); re-run scripts/check-system-context-census.mjs --fix to repair pure line rot. 22 anchors rewritten, 0 elevation read sites added or removed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0112hMx9hjJ9BgB28X97DS68
os-musk
commented
Sep 4, 2026
os-dev-report {
"issue": 14038,
"status": "done",
"branch": "claude/issue-14038-listcommits-createdat-iso",
"pr": "https://github.com/objectstack-ai/objectstack/pull/15008",
"premise_still_valid": true,
"summary": "Resolved PR #15008's mergeable_state:dirty by merging origin/main (f594e70d7) into the branch with a real merge commit (no rebase/amend/force-push). Three conflicts, each resolved per its artifact kind: protocol.ts kept all three disjoint symbol sets (this PR's isoFromValidDate + listCommits call site, #14982's ADR-0094 projector calls, #15043's bumpWriteEpoch call); system-context.mdx was re-derived with check-system-context-census.mjs --fix (22 pure line-rot anchors, 0 population change); engine-double-contract.pinned.json merged cleanly keeping both sides' rows (append-only, 772 pinned, 0 dropped). Added one re-anchor commit, pushed both, and updated the PR body with a merge-resolution section.",
"tests": "Build: pnpm --filter '@objectstack/metadata-protocol^...' build, exit 0. PR's own pins (protocol-14038-list-commits-created-at-iso.test.ts + sys-metadata-repository-14037-event-ts-canonicalisation.test.ts + protocol.commit-timeline-instant-order.test.ts): 3 files, 13 tests, all passed. Full package suite (pnpm --filter @objectstack/metadata-protocol test): 161 passed/2 skipped test files, 2370 passed/10 skipped tests, 0 failed. Typecheck (pnpm --filter @objectstack/metadata-protocol typecheck): clean, exit 0. isoFromValidDate confirmed declared at protocol.ts:1727 AND called at the listCommits createdAt mapping protocol.ts:19125 (not merged away). node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands re-derived post-merge: 78 commands, 75 exit 0; the other 3 (check:dual-build-cjs-loads, check:type-check-debt, check:skill-examples) all fail closed on the same precondition -- no full-tree dist/ build in this worktree -- none touch this diff's files, and the first two print their own explicit PREREQUISITE NOT MET / exit 3; read as NOT MEASURED, not red, per dispatch instructions. node scripts/pm/check-governed-merges.mjs --test the 5 changed paths: 0 of 5 hit the register -- NOT governed, ordinary queue landing applies. node scripts/check-system-context-census.mjs (no --fix, post-commit): OK. node scripts/check-engine-double-contract.mjs: OK -- 772 pinned, 133 DEBT, 3 exempt.",
"mcp_calls": 4,
"open_questions": [],
"out_of_scope_findings": []
}Conflicted files
Generated by Claude Code |
Fixes#14038
The defect
listCommits's declared return type sayscreatedAt?: string(
packages/metadata-protocol/src/protocol.ts:19049), but the mappingassigned the raw driver value straight through
(
protocol.ts:19107before this change:...(r.created_at ? { createdAt: r.created_at } : {})).created_atonsys_metadata_commitis an engine-injected audit column — not indatetimeFields— andSqlDriver#formatOutputrepairs it only inside itsif (this.isSqlite)arm (packages/drivers/driver-sql/src/sql-driver.ts:16056,audit-column repair at
:16123, thedatetimeFieldsfold at:16137—both inside the same arm). So on Postgres and MySQL,
listCommitshandedevery in-process consumer a
Datein a field the type promised was astring. The REST door (GET /packages/:id/commits) was never affected —JSON.stringifyalready renders aDateas canonical ISO-Z text.The fix
A narrow per-site conversion at the producer
(
protocol.ts:1726,isoFromValidDate), following #14037'sprecedent (landed as #14939) rather than the shared
canonicalIsoInstantspelling: a valid
Dateis canonicalised to ISO-8601, and every othershape — an already-canonical SQLite string, an absent column, and
an Invalid
Date— passes through unchanged. #14037 deliberately didnot adopt
canonicalIsoInstanthere because #14078 measured an InvalidDatereachable on both live dialects (a MySQL zero datetime; a Postgresyear in 275760..294276) where that spelling raises
RangeError, and #13973is blocked on that ruling. This PR imports neither answer to #14078 — it
just doesn't regress the field it touches. The Invalid-
Dateneutrality ispinned in §D of the new test file so it reds if the contested spelling is
ever swapped in here.
Evidence (Zone 2, file:line)
packages/metadata-protocol/src/protocol.ts:19049;emitted (pre-fix)
protocol.ts:19107(r.created_at, unconverted).canonicalIsoInstant; seethe doc comment on
isoFromValidDate(protocol.ts:1726) and §D of thenew test.
sql-driver.ts'sformatOutputgates both the builtin-audit-column repair and thedatetimeFieldsfold inside oneif (this.isSqlite)arm(
:16056..:16137). No live cell needed — the new test drives ahand-made
Date, matching this package's existing convention (no driverdependency).
rollbackToPackageCommit(
protocol.ts:19742) readslistCommits'createdAtthroughcompareAuditInstants, which already normalises both aDateand anISO string to epoch ms via
canonicalVersionInstant— so canonicalisingthe emitted value to a string does not change its behaviour (no stop
condition; not a regression).
Scope
Out of scope, per the dispatch:
#14938(listDrafts, a sixth site withan inline return type) and the family's standing question of a shared
@objectstack/metadata-coreexport (ruled: after #14078, not before).Tests
New:
packages/metadata-protocol/src/protocol-14038-list-commits-created-at-iso.test.ts(§A Postgres/MySQL
Date→ ISO; §B SQLite text passthrough; §C absentcolumn; §D #14078 neutrality pin).
Union run at final head
8c9c060f2(after mergingorigin/maintwice whilethis branch was in flight, most recently to pick up #14939 itself):
pnpm --filter @objectstack/metadata-protocol exec vitest runover the newfile plus the sibling
#13995and#14037pins in this package — 3 files,13 tests, all passed;
pnpm --filter @objectstack/metadata-protocol typecheckclean;
pnpm check:engine-double-contract,check:system-context-census,check:doc-authoring,check:nul-bytes,check:type-check-coverage, andthe rest of the ~70 commands
node scripts/pm/dispatch-gates.mjs --commandsderives for this diff — all green.
check:dual-build-cjs-loads,check:test-completenessandcheck:type-check-debtread NOT MEASUREDlocally (each needs a full-tree build or a CI test log this environment
doesn't produce) — not evidence of a red, left for CI.
Mechanical fallout from merging
origin/maintwice: twocheck:system-context-censusre-anchors (doc-table line numbers shifted byunrelated commits) and one
check:engine-double-contractpinned-ledgerentry for the new test's read-only fake engine. No behaviour change in
either.
Merge conflict resolution (2026-09-04)
The PR sat at head
8c9c060f2/ base23c72be3cand wentmergeable_state: dirtyagainstmain(never entered the merge queue). Resolved with a realmerge commit (no rebase/amend/force-push) of
origin/main(f594e70d7) —three conflicted files, each resolved per its own kind:
packages/metadata-protocol/src/protocol.tsisoFromValidDate+ itslistCommitscall site, #14982's ADR-0094 mutation-projector calls (revertCommit,rollbackMetaItem,deleteMetaItem's legacy exit), and #15043'sbumpWriteEpochcall inapplyRemoteMetadataMutation's receipt pathcontent/docs/permissions/system-context.mdxnode scripts/check-system-context-census.mjs --fix— 22 anchors rewritten (pure line rot), 0 elevation read sites added or removed;check-system-context-census.mjsnow reports OKscripts/engine-double-contract.pinned.jsoncheck-engine-double-contract.mjsreports 772 pinned, 0 droppedPost-merge verification at new head
c313a7c20(base nowf594e70d7):isoFromValidDateconfirmed still declared (protocol.ts:1727) andstill called at the
listCommitscreatedAtmapping (protocol.ts:19125).functions (
revertCommit:19178-19831,rollbackMetaItem:19832-20321,deleteMetaItem:20322+,applyRemoteMetadataMutation:5235+).protocol-14038-list-commits-created-at-iso.test.tssys-metadata-repository-14037-event-ts-canonicalisation.test.ts+protocol.commit-timeline-instant-order.test.ts— 3 files, 13 tests, allpassed.
pnpm --filter @objectstack/metadata-protocol test, now carrying fix(metadata-protocol): recovery doors run the ADR-0094 mutation projector #14982's and fix(metadata-protocol): bump write epoch on remote metadata mutation receipt #15043's behaviour changes in the samefile): 161 passed / 2 skipped test files, 2370 passed / 10 skipped tests,
0 failed.
pnpm --filter @objectstack/metadata-protocol typecheck: clean.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsre-derived post-merge (merge base moved tof594e70d7): 78commands, 75 exit 0; the remaining 3 (
check:dual-build-cjs-loads,check:type-check-debt,check:skill-examples) all fail closed on thesame precondition — no full-tree
dist/build in this worktree — and areunrelated to this diff (none touch
client-react, and the first twoexplicitly exit their own PREREQUISITE-NOT-MET code); left for CI same as
before.
node scripts/pm/check-governed-merges.mjs --teston the 5 changed paths:NOT governed — ordinary queue landing applies.
Generated by Claude Code