Skip to content

fix(driver-sql)!: findWithWindowFunctions presents its rows like every other read door (#16609) - #16716

Merged
os-zhuang merged 5 commits into
mainfrom
claude/issue-16609-window-functions-presentation
Sep 8, 2026
Merged

fix(driver-sql)!: findWithWindowFunctions presents its rows like every other read door (#16609)#16716
os-zhuang merged 5 commits into
mainfrom
claude/issue-16609-window-functions-presentation

Conversation

@os-musk

@os-musk os-musk commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16609

Clause-②: yes — this changes the shape of the payload a published driver door returns.

The defect

SqlDriver#findWithWindowFunctions was the one record read door that returned await builder with no presentation at all: no formatOutput (which every find() / findOne() row gets) and no presentReadValue (which aggregate() / distinct() got under #3797 / #3849). So it handed back storage forms where every other door hands back the declared type's presentation.

Reproduced first, on this branch's own build

The card's probe, run verbatim against packages/drivers/driver-sql/dist/index.js built from origin/main 2e6a2ea4c9before any code change. Not relayed from the card's transcript:

find():                    {"id":"a","created_at":"…","updated_at":"…","ok":true,"closed_at":"2026-01-10T09:00:00.123Z","meta":{"k":1}}
findWithWindowFunctions(): {"id":"a","created_at":"…","updated_at":"…","ok":1,   "closed_at":"2026-01-10T09:00:00.123Z","meta":"{\"k\":1}","rn":1}
  ok:   find=true (boolean)   window=1 (number)           same=false
  meta: find={"k":1} (object) window="{\"k\":1}" (string)  same=false

After the fix, the same probe on the same rebuilt dist:

  ok:   find=true (boolean)   window=true (boolean)   same=true
  meta: find={"k":1} (object) window={"k":1} (object) same=true

Symbols, re-derived (every anchor on the card had drifted)

Read authoritatively with git show origin/main:packages/drivers/driver-sql/src/sql-driver.ts (17582 lines):

symbol line on origin/main 2e6a2ea4c9
findWithWindowFunctions :8961, terminal statement return await builder; at :9006
distinct's presentation (the firing control) :8944:8946
readPresentationKind / presentReadValue / formatOutput :12874 / :12901 / :16655called, never edited

The fix

Each row runs through the same formatOutput pass find() runs, minus the window-function alias columns.

The seven column classes this door now moves

formatOutput is one pass over seven rules, so routing through it moves all seven — not only the boolean and JSON classes the defect was reported as. Enumerated so the reviewer signing Clause-②: yes sees the whole payload change, not a subset (contract review F1):

# class what moves dialects where the row actually changes
1 Field.boolean 1 / 0true / false sqlite, mysql (the isSqlite || isMysql gate; PG stores a native boolean)
2 Field.object (JSON) stored JSON TEXT → the parsed object sqlite (PG jsonb and mysql2 already parse)
3 numeric fields numeric STRING off a legacy TEXT-affinity column → number sqlite
4 instants — the audit stamps created_at / updated_at and every declared Field.datetime client Date → canonical YYYY-MM-DDTHH:MM:SS.sssZ text postgres, mysql (sqlite already stores the canonical text since #3912)
5 Field.date DateYYYY-MM-DD text mysql (sqlite is identity on text; the driver pins the PG date OID parser to text)
6 Field.time → canonical HH:MM:SS[.fff], re-padding the fraction PG trims postgres, mysql
7 external.columnMap the row KEY renames: remote column key → local field key every dialect

Six of the seven are what #16609 asks for in so many words ("the same presentation the other doors apply"). The seventh — external.columnMap — nobody named on the card; it is a KEY move rather than a value move, and it is listed here rather than discovered at merge. All seven now carry FROM/TO lines in the changeset.

⚠️ ADR-0053 D-F1 (docs/adr/0053-date-and-datetime-semantics.md:1080-1081, :1157-1159) still says this door is not covered; docs-only governed card #16782 carries the amendment. The tree is ahead of the declaration on that line until #16782 lands — declared narrower than enforced. The non-governed half of the same staleness (the header comment of sql-driver-13973-canonical-iso-read-door.test.ts) is corrected in this PR.

formatOutput rather than presentReadValue, and that choice is load-bearing: ReadPresentationKind is 'datetime' | 'date' | 'time' | 'boolean' | 'number' — it has no json member, so the per-value helper the other two doors use cannot present a declared Field.object at all. These are rows, which is exactly what formatOutput takes.

The carve-out is snapshot-and-restore rather than a "which keys would formatOutput touch?" pre-computation, because that question can only be answered by re-reading the declared-field registries formatOutput reads — a second, worse copy that would go stale the next time formatOutput learns a rule.

The alias / declared-field collision — ruled and pinned

An alias wins the key, and its value stays raw.

SQL had already decided the first half before the driver sees it: select * plus a window function aliased as ok projects two columns named ok, and the row object keeps the last — so the computed value wins and the declared column's value is not in the row at all. Measured on origin/main 2e6a2ea4c9 with alias: 'ok' over a declared Field.boolean ok seeded true / false: rows came back ok: 1 and ok: 2 — the ROW_NUMBERs. That is unchanged by this PR.

What this PR rules is the second half: the winning value is not presented as the declared type. Folding ROW_NUMBER 1 and 2 through the boolean rule would yield true and true and destroy the value the caller asked for.

This matches the ruling aggregate() already makes for a date-bucketed column aliased as its own field name ("leaves a date-BUCKETED column as its label, not an instant").

⭐ Pinned as a test, not prose — sql-driver-window-function-output.test.ts, the case "a colliding alias wins the key AND keeps its raw computed value", whose assertion can fail three distinguishable ways — plus the in-code comment block at the fix site.

Conformance case

New packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts (12 cases) asserts door-to-door agreementfindWithWindowFunctions() versus find() on the same row — across boolean, json, date, time, datetime and the audit stamps.

⚠️ Deliberately written as an agreement, not as absolute datetime literals, because PR #16619 is changing what formatOutput produces for the instant classes (ADR-0053 D-F1). Both doors run the same pass, so they move together and this stays true whichever way that lands. Booleans and JSON are additionally pinned absolutely — they are wrong on SQLite today and #16619 does not touch them.

📌 Superseded by the patch round (contract review F3 / F5). #16619 has since landed on main and is merged in here, so formatOutput is now the post-B1 presenter; the file gained a measure(cell) arm over DIALECT_CELLS that asserts the instant shape absolutely on the live cells, where the fold is real work. The counts below are the pre-patch-round ones — the current file is 17 passed | 2 skipped (19), the 2 being the named live-PG / live-MySQL skips. See the patch-round comment on this PR.

Two-leg ablation

  • Leg 1 — with the fix: 12 passed, 0 failed, 0 skipped.
  • Leg 2 — the formatOutput call ablated at the window-door site only (the findRows site at :5928 left intact, verified by anchor count 2 → 1): 4 failed | 8 passed, e.g. AssertionError: typeof ok on row 0: expected 'number' to be 'boolean'.
  • Restore verified by blob hash, not by eye: git hash-object back to 057310aead2557a750e4884f3a3434c55b7c21f1, equal to the HEAD blob, with git diff HEAD empty. The mutation was proven to land on disk first (hash changed, injected marker present) so the leg could not be a silent no-op.

8 cases stayed green in leg 2, and that is reported rather than glossed. The layer holding them is canonical-on-write storage, measured directly on the raw SQLite table:

tok=integer  ok=1                                  <- presentation changes it
tmeta=text   meta={"k":1}                          <- presentation parses it
tca=text     closed_at=2026-01-10T09:00:00.123Z    <- ALREADY the presented form
tco=text     closed_on=2026-01-10                  <- ALREADY the presented form
tsa=text     starts_at=09:30:00.500                <- ALREADY the presented form
tcr=text     created_at=2026-09-08T00:19:10.214Z   <- ALREADY the presented form

Since #3912 the instant classes are stored canonically on SQLite, so removing the read presentation moves nothing for them — those assertions cannot fail on this dialect today. They still earn their place: they hold the door-to-door invariant on live Postgres and MySQL, where the client library returns Date objects, and those arms are skipped in this container (see below).

Verification

leg result
pnpm --filter @objectstack/driver-sql test 158 passed | 10 skipped (168) files, 2414 passed | 141 skipped (2555) tests
pnpm --filter @objectstack/driver-sql typecheck green — one leg, tsc --noEmit, read out of package.json
new conformance file 12 passed, 0 skipped

⚠️ All 141 skipped tests are the live Postgres / MySQL arms, gated on OS_TEST_POSTGRES_URL / OS_TEST_MYSQL_URL, absent in this container. That is load-bearing here: the boolean rule fires on SQLite and MySQL (formatOutput gates isSqlite || isMysql), so the MySQL half of this fix is not measured locally and is declared to CI.

The typecheck leg genuinely covers the new test: tsc --noEmit --listFiles names sql-driver-window-function-output.test.ts (count 1, not 0), and the package's tsconfig.json has include: ["src/**/*"] with no test exclusion.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack57 families, all run, reconciled at the final head:

Run reconciliation — 57 derived, 57 run, 0 NOT-MEASURED, 0 UNRUN.
✓ dispatch-gates --ran: 57 derived famil(ies) accounted for — 57 run, 0 NOT-MEASURED.

55 exited 0. Two exited 3 — PREREQUISITE NOT MET, which is not a pass and not a failure:

  • check:dual-build-cjs-loads"this gate reads built output, and some package has no dist/ … 79 packages … ⛔ This is NOT a pass: nothing was measured."
  • check:type-check-debt — same class.

Both need a whole-repo pnpm build, which is CI's run rather than this seat's. Recorded as NOT MEASURED, declared to CI.

Repo-wide pnpm lint is likewise CI's run, not owed here.

Changeset grade — derived from the repo, and where it is genuinely ambiguous

Graded minor on @objectstack/driver-sql, with a **BREAKING** banner and an ADR-0087 disposition. check-adr-0087-registration accepts it:

✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
    .changeset/window-functions-row-presentation.md  [BREAKING+bang]  not-required (no-migration-prescription)

Precedent found, reading the published CHANGELOG rather than a remembered table — both entries are in packages/drivers/driver-sql/CHANGELOG.md, both in 17.0.0:

card grade banner / disposition
#3797 (temporal half) patch — under ### Patch Changes none
#3849 (boolean + scalar half — this card's class) minor — under ### Minor Changes no **BREAKING** banner and no ADR-0087 marker in its 88-line entry

That zero-hit has a firing positive control: the same file carries BREAKING 9 times and adr-0087 17 times elsewhere, so markers demonstrably survive into CHANGELOGs and the absence is real. #3849 declared breaking through the third spelling instead — the conventional-commit ! in its summary.

⚠️ Both precedents are pre-rule. The WHICH LEVEL ruling (pr-automation.yml:667-682, maintainer, 2026-09-04, decision batch #35 on #15294) states "The 64 historical patch precedents are pre-rule and nothing is retro-fixed."

Under the current rule the case is genuinely ambiguous, and this is flagged for the reviewer rather than smoothed over:

  • The rule's widening bucket is "a new exported symbol on an index, a new accepted key or value" → at least minor. This PR adds none of those.
  • Its other bucket is "a fix( that changes no public surface stays patch". This PR does change a public surface (the returned payload's value types) without widening it — so it sits cleanly in neither bucket.
  • AGENTS.md separately says a bug fix in a released package takes a patch changeset.

minor was chosen because grading patch while declaring Clause-②: yes is exactly the "self-contradiction inside one PR" the LEVEL axis names, and because #3849 — the same defect class on the same door family — chose minor. No gate forbids minor; patch would be forbidden if the axis could see this package. ⇒ If the reviewer reads the surface as unchanged, lowering to patch is a one-line edit.

⚠️ The LEVEL axis cannot actually adjudicate this PR, and its green here carries no information. Its packages/*/src/** pattern matches one segment, so packages/drivers/driver-sql/src/** never matches; packagesTouched returns { packages: [], unreadable: [] } for this diff. Verified with a control: grading this changeset patch under a Clause-②: yes payload stayed green. Filed separately as #16713 (51 of 74 workspace packages affected) — a distinct axis from the sibling #16692, which covers non-src roots at depth 1.

Scope

Hot-file fence held. This PR touches findWithWindowFunctions and its body only; formatOutput / readPresentationKind / presentReadValue are called, never edited, and git diff origin/main confirms sql-driver.ts did not move for #15546's or #16570's regions. #13973 is not folded in and #3797 / #3849 are not reopened.

验收备注

  • 派发词给的门禁脚本路径 .claude/scripts/dispatch-gates.mjs 不存在;实际路径是 scripts/pm/dispatch-gates.mjs,已按 agent 文件为准使用并回报 PM。
  • noted, not filed:check-adr-0087-registration--base origin/main 读数与 --event 读数在本地互不相干,两者都绿,但只有后者能判 LEVEL 轴 —— 这只是观察,不构成缺陷。

🤖 Generated with Claude Code

https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg


Generated by Claude Code


Generated by Claude Code

…er read door (#16609)

The one record read door that returned `await builder` with no presentation:
no `formatOutput` (every `find()`/`findOne()` row gets it) and no
`presentReadValue` (`aggregate()`/`distinct()` got it under #3797/#3849). So a
declared `Field.boolean` answered `1` where `find()` answered `true`, and a
declared `Field.object` answered the stored JSON text where `find()` answered
the parsed object.

Each row now runs through the same `formatOutput` pass, minus the window
function alias columns, which are computed values rather than declared fields.
The collision case is ruled and pinned: an alias spelled the same as a declared
field already won the key in SQL (`select *` plus `<window> as ok` keeps the
last column), and its value now stays raw rather than being folded through the
declared type's rule.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Sep 8, 2026
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/driver-sql, touching 1 documentable anchor(s).

2 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/queries.mdx (via findWithWindowFunctions (symbol, a method of class SqlDriver))
  • content/docs/protocol/objectql/query-syntax.mdx (via findWithWindowFunctions (symbol, a method of class SqlDriver))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via findWithWindowFunctions (symbol, a method of class SqlDriver))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 61 of 219 client-bound route-ledger rows — the other 158 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 158: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 56 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 102 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 10 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json b38821d1ce220527e2a7f34e254a96c48e2a9ba3packageMentionDocs.

Which tree this was computed on

This run read content/docs from dce03cc6802761f1c99eeddb27ece57aab5e8442 — the merge of head ea93dbea526f5a0084c742686dee5875951fe224 into base b38821d1ce220527e2a7f34e254a96c48e2a9ba3, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin dce03cc6802761f1c99eeddb27ece57aab5e8442 && git checkout dce03cc6802761f1c99eeddb27ece57aab5e8442
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin b38821d1ce220527e2a7f34e254a96c48e2a9ba3 ea93dbea526f5a0084c742686dee5875951fe224 && git checkout -B drift-repro b38821d1ce220527e2a7f34e254a96c48e2a9ba3 && git merge --no-ff ea93dbea526f5a0084c742686dee5875951fe224

node scripts/docs-audit/affected-docs.mjs --json b38821d1ce220527e2a7f34e254a96c48e2a9ba3

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs b38821d1ce220527e2a7f34e254a96c48e2a9ba3 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Contributor

Contract review (CONTRACT_REVIEW_TIER) — verdict: PASS WITH FINDINGS

Isolated seat; read only #16609, the B1 ruling on #13973 (comment 5507803003), this PR, and the tree (refs/pull/16716/head = 8981967939, base a0856e3bf9, origin/main now 8ccf7a1dfd).

Ruling implemented: partially — the code implements #16609's deliverable on every dialect through the shared presenter; the enforcement cells cover SQLite only, and the governed declaration (ADR-0053 D-F1) still says this door is not covered.

1. Ruling

#16609 has no maintainer ruling of its own. It carries a triage note (os-zhuang, 2026-09-07) and a PM dispatch (5576867473 / 5577336659) that adopt the card's own deliverable verbatim; Clause-②: yes is carried by the PM claim, not by a ruling. The parent ruling it derives from, #13973 comment 5507803003, verbatim:

Ruled: B1, narrow form. For the builtin audit columns (created_at, updated_at) and every declared Field.datetime column, driver-sql's read door presents the canonical instant YYYY-MM-DDTHH:MM:SS.sssZ on every dialect, exactly as it already does on SQLite: the two if (this.isSqlite) gates around repairNaiveUtcAuditTimestamp and normalizeSqliteDatetimeOutput in formatOutput become unconditional […], the aggregate() / distinct() presentation path (readPresentationKind) gains the same arm for the two column classes […]

#16716 is not a pure extension of B1 — it is wider (finding F1 below). B1 rules two column classes (audit stamps, Field.datetime). Routing the window door through formatOutput moves seven: those two, plus boolean, JSON, numeric-string, Field.date, Field.time, and the external.columnMap key rename. Six of the seven are what #16609 explicitly asks for ("the same presentation the other doors apply"); the seventh (columnMap) nobody named.

2. What changes on the wire for a findWithWindowFunctions caller

Enumerated from formatOutput at the PR head (sql-driver.ts:16711) and at origin/main post-#16619 (:16787, where the instant gates are unconditional and the audit arm is presentAuditTimestampOutput). memory: @objectstack/driver-memory has no findWithWindowFunctions — this is a driver-sql-only extension door, so N/A.

class sqlite postgres mysql
Field.boolean 0/1false/true unchanged (native bool) 0/1false/true (isSqlite || isMysql gate)
Field.object / JSON JSON text → parsed object unchanged (jsonb native) unchanged (mysql2 parses JSON)
numeric fields numeric string → number unchanged unchanged
Field.datetime + created_at/updated_at unchanged (already canonical text since #3912; naive-UTC repair applies) after merge onto origin/main: DateYYYY-MM-DDTHH:MM:SS.sssZ text (B1) same as postgres
Field.date unchanged (toDateOnly on text is identity) unchanged (driver pins PG_OID_DATE parser to text, :5347) DateYYYY-MM-DD text (toDateOnly, :16768)
Field.time unchanged canonical HH:MM:SS[.fff] via toTimeOnly same
external.columnMap row key renames: remote column key → local field key (formatOutput :16717-16724, populated at :9605) same same

Alias columns: carved out, and the carve-out is sound — aliasIdentifierSql (:4826) wraps the alias through knex wrapIdentifier, so Postgres does not case-fold it and the row key equals String(wf.alias); the snapshot-and-restore at :9047-9060 therefore always finds the alias it protects.

3. Same presenter — confirmed, not a copy

sql-driver.ts:9058 calls this.formatOutput(object, row), the exact call findRows() makes at :5928; find()/findOne() apply nothing else per row. formatOutput / readPresentationKind / presentReadValue are called, never edited (hot-file fence held; git merge-tree origin/main refs/pull/16716/head is clean).

4. Governed paths

No. Three files: .changeset/window-functions-row-presentation.md, packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts, packages/drivers/driver-sql/src/sql-driver.ts. docs/adr/** untouched — so this is not the maintainer's merge on that ground. But see F4: the governed declaration goes stale the moment this merges.

5. Changeset — minor, and that is the repo-mandated level for a ! commit

.changeset/window-functions-row-presentation.md:2 grades @objectstack/driver-sql: minor with a **BREAKING** banner and an ADR-0087 not-required (no-migration-prescription) disposition. scripts/check-changeset-no-major.mjs:5-6, 41-45, 64-66 forbids major during the launch window and names the banner + disposition as the two carriers of breaking-ness. So ! + minor is compliant here, not a finding. FROM/TO coverage is incomplete — F2.

6. Tests / CI

  • Pin that reddens on revert: yespresents a declared Field.boolean as a boolean, not 1/0 and the JSON pin (sql-driver-window-function-output.test.ts:113-124), plus the three-way collision pin (:167-178); the PR's ablation shows 4 red.
  • CI on 8981967939: 46 check runs, all success or skipped, none failed (Temporal Conformance, Test Core 6/6, Lint & Repo Gates, all four Type Check jobs, Governed Surface Queue Guard). mergeable_state: clean, draft.

Findings

F1 — wider than B1, with no ruling of its own on #16609. sql-driver.ts:9058 — routing through formatOutput moves the seven classes in §2, not the two B1 rules. That is what #16609 asks for and what triage adopted, so it is not blocking; but the maintainer signing Clause-②: yes should know the door moves booleans, JSON, numerics, Field.date, Field.time and columnMap keys, not only instants. Expectation: the PR body's "The fix" section lists all seven classes (it currently names boolean, JSON and the instants).

F2 — changeset FROM/TO incomplete. .changeset/window-functions-row-presentation.md:36-41 names boolean (1true), object (JSON text → parsed) and the instants (TO given only by reference, "the same presented value find() gives"). Expectation: add FROM/TO lines for (a) external.columnMap — remote column key → local field key, every dialect; (b) SQLite numeric string → number; (c) MySQL Field.date DateYYYY-MM-DD text; (d) Field.time → canonical HH:MM:SS[.fff]; and spell the instant TO as YYYY-MM-DDTHH:MM:SS.sssZ text on every dialect now that #16619 (45cfa1b88) is on main.

F3 — conformance cells on SQLite only. sql-driver-window-function-output.test.ts:52 — one describe, one in-memory SQLite driver; no DIALECT_CELLS / declareDialectCell arm although live-dialect-matrix.testkit.ts exists on the PR base and sql-driver-13973-canonical-iso-read-door.test.ts:118-124 shows the measure(cell) pattern. Consequence: the MySQL boolean half (the isSqlite || isMysql gate at :16751) and the PG/MySQL instant fold for this door are unmeasured — Temporal Conformance runs pnpm --filter @objectstack/driver-sql test with the live URLs, but this file has no live arm so it runs SQLite there too. The PR body's own admission stands: the five instant-agreement cases cannot fail on SQLite. Expectation: a measure(cell) over DIALECT_CELLS for this door asserting typeof row.ok === 'boolean' (MySQL cell) and expectCanonicalInstant for closed_at / created_at / updated_at (PG + MySQL cells).

F4 — the governed declaration contradicts the tree after merge. docs/adr/0053-date-and-datetime-semantics.md:1080-1081 ("findWithWindowFunctions is not one of these doors") and :1157-1159 ("Not covered: findWithWindowFunctions, which applies no read presentation of any kind today") on origin/main, plus sql-driver-13973-canonical-iso-read-door.test.ts:20-22, all become false when this merges — declared narrower than enforced, which is the inverse of the D-F addendum's stated purpose. Expectation: either fold a one-line D-F1 amendment into this PR (which makes it governed → the maintainer's merge), or file a docs-only governed card for the ADR line and link it in the PR body before merge; the test-header comment is non-governed and can be corrected here once the branch is on origin/main.

F5 — branch predates #16619. The head's last merge is of a0856e3bf9; origin/main has since taken 45cfa1b88 (#16619), whose formatOutput is the post-B1 presenter. merge-tree is clean, but every CI leg — including Temporal Conformance — measured this door through the pre-B1 formatOutput (:16745 at the head still gates the instants on isSqlite). Expectation: merge origin/main and re-run, so the "declared to CI" claim for PG/MySQL is measured against the presenter that will actually ship.

Not findings, recorded: no git stash, no governed edit, #13973 not folded in, #3797/#3849 not reopened; the collision ruling (alias wins the key, value stays raw) is pinned in code and test as triage demanded.


Generated by Claude Code

claude and others added 2 commits September 8, 2026 05:17
…FROM/TO table

Contract-review patch round on PR #16716 (findings F2, F3, F5 and the
non-governed half of F4). No production code changes.

F5 — merged origin/main, so this branch now carries #16619: `formatOutput`'s
instant gates are unconditional, which is the presenter this door actually
ships through. Every CI leg on the previous head measured the pre-B1 presenter.

F3 — `sql-driver-window-function-output.test.ts` gains a `measure(cell)` arm
over `DIALECT_CELLS`, declared through `declareDialectCell` so an
unprovisioned cell is a NAMED SKIP and never a silent pass. It asserts the two
halves the SQLite-only arm cannot: `typeof row.ok === 'boolean'` (the MySQL
half of the `isSqlite || isMysql` boolean gate) and the canonical
`YYYY-MM-DDTHH:MM:SS.sssZ` text for `closed_at` / `created_at` / `updated_at`
(the PG + MySQL instant fold). SS4 reads the same row back through raw knex to
prove the fold is the driver's and not the client's.

F2 — the changeset gains a per-class, per-dialect FROM/TO table covering all
seven classes this door moves: adds `external.columnMap` (remote column key ->
local field key, every dialect), the SQLite numeric-string -> `number` move,
the MySQL `Field.date` `Date` -> `YYYY-MM-DD` move and `Field.time` ->
canonical `HH:MM:SS[.fff]`, and spells the instant TO as the canonical text on
every dialect. `minor`, the BREAKING banner and the ADR-0087 disposition are
unchanged.

F4 (non-governed half) — the header comment of
`sql-driver-13973-canonical-iso-read-door.test.ts` said this door applies no
read presentation. It routes through `formatOutput` since #16609, so the
comment now says that and flags that ADR-0053 D-F1 still records it as not
covered, with governed docs-only card #16782 carrying the amendment.

`docs/adr/**` is untouched here.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TezFG8ZMrNH6n5VTNpPpdH
@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation tests tooling and removed size/m labels Sep 8, 2026
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Patch round — F2, F3, F5 and the test-header half of F4 (director seat)

New head: d257234a75ee5263ca001f6debb7734f43eeb765 (was 8981967939). One commit, no production code touched — sql-driver.ts is byte-identical to the reviewed head. docs/adr/** untouched: the ADR-0053 D-F1 amendment is governed and rides on docs-only card #16782.

F5 — merged origin/main

Plain merge of origin/main ed7243d52 into the branch (no rebase, no force-push). The head now carries #16619 45cfa1b88, so formatOutput's instant gates are unconditional and every leg from here measures the post-B1 presenter that will actually ship — the thing the review flagged as unmeasured. Merge was clean; merge-tree had already predicted that.

F3 — the live-dialect arm

sql-driver-window-function-output.test.ts gains a measure(cell) arm over DIALECT_CELLS, declared through declareDialectCell exactly as sql-driver-13973-canonical-iso-read-door.test.ts does — so an unprovisioned cell is a named skip, never a silent pass, and a named RED under OS_EXPECT_LIVE_DIALECT_MATRIX=1.

Five cells per dialect, asserting the two halves the SQLite-only arm structurally cannot reach:

  • §L1 typeof row.ok === 'boolean' — the MySQL half of the isSqlite || isMysql boolean gate, plus the value pair [true, false] so a rule folding every value one way could not pass.
  • §L2 canonical YYYY-MM-DDTHH:MM:SS.sssZ for closed_at / created_at / updated_at on the PG + MySQL cells, asserted absolutely (not as a door agreement) — an agreement between two doors that are both wrong is green.
  • §L0 fixture non-vacuity: both rows came back carrying every column under test, so no assertion below can pass having checked nothing.
  • §L3 the window row equals the find() row on every declared column, value and type.
  • §L4 the fold is the driver's, not the client's: raw knex past every presentation still hands back a Date for the instants and a number for MySQL tinyint(1). This is what makes §L1/§L2 measurements rather than restatements of client behaviour (ADR-0053 D-F2).

Live cells also run assertThreeWayZoneSkew, so a Z that survived only because every clock agreed cannot pass.

F2 — changeset FROM/TO now covers all seven classes

.changeset/window-functions-row-presentation.md gains a per-class, per-dialect FROM → TO table. minor, the **BREAKING** banner and the ADR-0087 not-required (no-migration-prescription) disposition are unchanged. Added, as the review asked:

class added FROM → TO
external.columnMap the row KEY renames: remote column key → local field key, every dialect
numeric fields numeric STRING off a legacy TEXT-affinity column → number (sqlite)
Field.date DateYYYY-MM-DD text (mysql)
Field.time → canonical HH:MM:SS[.fff]; PG's trimmed fraction re-padded ('09:30:00.5''09:30:00.500')
instants TO spelled out as YYYY-MM-DDTHH:MM:SS.sssZ text on every dialect, never a Date

unchanged rows are recorded rather than omitted — the same code path now runs for them, and silence there would read as "not considered". The two per-dialect claims that are not self-evident were re-verified in the merged tree rather than relayed: the PG date OID parser is pinned to text (sql-driver.ts:5418) and columnFieldByObject is populated at :9676 and consumed at :16849.

F4 — the non-governed half only

The header comment of sql-driver-13973-canonical-iso-read-door.test.ts said this door "applies no read presentation of any kind". Since #16609 it routes through formatOutput, so the comment now says that, points at the pin, and explicitly flags that ADR-0053 D-F1 still records the door as not covered with #16782 carrying the amendment — so a reader cannot mistake the stale ADR line for current behaviour. The governed ADR edit itself is not in this PR.

Test counts

pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-window-function-output.test.ts

Test Files  1 passed (1)
     Tests  17 passed | 2 skipped (19)

Was 12 passed / 0 skipped. The 2 skipped are the named live cells, verbatim from --reporter=verbose:

↓ sql-driver — window-function row presentation (#16609) matrix (live postgres)
    > is provisioned — set OS_TEST_POSTGRES_URL to run this cell of the D-A3 driver axis
↓ sql-driver — window-function row presentation (#16609) matrix (live mysql)
    > is provisioned — set OS_TEST_MYSQL_URL to run this cell of the D-A3 driver axis

⚠️ NOT MEASURED locally: the MySQL boolean half and the PG/MySQL instant fold for this door. No live URLs in this container. They are measured by CI's Temporal Conformance (live PG + MySQL), which provisions both and runs pnpm --filter @objectstack/driver-sql test. The five SQLite cells of the new arm DID run and are green, so the arm is not vacuous even here.

Gates — exit codes verbatim

command exit
pnpm --filter @objectstack/driver-sql exec vitest run src/sql-driver-window-function-output.test.ts 0
pnpm --filter @objectstack/driver-sql typecheck 0 (tsc --noEmit)
node scripts/check-changeset-no-major.mjs --base origin/main 0
node scripts/check-adr-0087-registration.mjs --base origin/main 0
node scripts/check-empty-changeset.mjs --base origin/main 0
pnpm check:nul-bytes 0
pnpm check:test-source-alias 0
pnpm check:cross-package-test-inputs 0

Exit codes were captured by redirecting to a file before any pipe, never read through | tail. The three the review named print their own verdict lines:

✓ This diff introduces no `major` bump.
✓ check-adr-0087-registration: 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition.
    .changeset/window-functions-row-presentation.md  [BREAKING+bang]  not-required (no-migration-prescription)
✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).

The last two are the ones that could have reddened on an F2 edit — the banner and the disposition survived the rewrite, which is what they assert.

check-changeset-no-major additionally reports LEVEL AXIS: NOT MEASURED locally (no pull_request payload was available to read a declaration from). That is the same local/--event split the original body already recorded as noted, not filed; it is neither a pass nor a failure here, and CI's --event run is what judges the LEVEL axis.

dispatch-gates --commands derives 57 families for this change set (unchanged from the reviewed head — the same four paths, no new file kind). The eight above are the ones this patch round could move; the remaining families and the repo-wide pnpm lint are CI's run, not this seat's.

Not done, and why


Generated by Claude Code

Copy link
Copy Markdown
Contributor

Contract review (CONTRACT_REVIEW_TIER, isolated seat) — PR #16716 @ d257234 — re-review

Verdict: CHANGES REQUIRED — one line, in the PR's own changeset.

Every code and test expectation from the prior review (comment 5578892124) is discharged and independently re-verified below. What blocks is new and was introduced by the patch round itself: the F2 discharge walked the changeset into the ADR-0087 gate's FROM → TO label detector, and Check Changeset is red on d257234a7 (check-adr-0087-registration, job 101948563951). The patch-round comment reports that same script at exit 0 locally; it does not reproduce — see R1. The governed ADR-0053 D-F1 amendment stays on card #16782 and does not block this PR.

Read only #16609, this PR, its three comments, the tree at refs/review/16716b = d257234a75ee5263ca001f6debb7734f43eeb765 (merge-base with origin/main = ed7243d52, the PR's recorded base), and the CI on that head. Never checked out, never stashed, no edit; ref deleted after.

Prior findings — discharged?

# prior expectation status evidence
F1 PR body "The fix" lists all seven classes discharged body §"The seven column classes this door now moves" — 7-row table (boolean, object/JSON, numeric, instants incl. audit stamps, Field.date, Field.time, external.columnMap); names #16782
F2 changeset FROM/TO covers columnMap, SQLite numeric, MySQL Field.date, Field.time; instant TO spelled discharged in content — but the discharge reddens a required gate (R1) .changeset/window-functions-row-presentation.md:35-53 — 7-row × 3-dialect table; :50 "YYYY-MM-DDTHH:MM:SS.sssZ TEXT on every dialect, never a JS Date"; minor, **BREAKING**, adr-0087: not-required (no-migration-prescription) all intact
F3 measure(cell) over DIALECT_CELLS, typeof row.ok === 'boolean', canonical instants for closed_at/created_at/updated_at, named skips discharged verification 3
F4 (test-header half) sql-driver-13973-canonical-iso-read-door.test.ts header corrected, ADR line flagged as stale, #16782 named discharged verification 4
F4 (governed half) ADR-0053 D-F1 amendment not in this PR, by design — card #16782 docs/adr/** absent from the diff (verification 1)
F5 merge origin/main post-#16619 and re-measure discharged merge commit 40b7cd4ff of ed7243d52; head's formatOutput (sql-driver.ts:16843) is the post-B1 presenter (presentAuditTimestampOutput at :16943, the instant fold outside any isSqlite gate at :16926-16952); Temporal Conformance re-ran on this head — verification 7

Verification

  1. Files. git diff ed7243d52..d257234a7 --name-status: A .changeset/window-functions-row-presentation.md, M packages/drivers/driver-sql/src/sql-driver-13973-canonical-iso-read-door.test.ts, A packages/drivers/driver-sql/src/sql-driver-window-function-output.test.ts, M packages/drivers/driver-sql/src/sql-driver.ts — four files. docs/adr/**, content/docs/releases/**, packages/spec/**: absent. The 156-file 8981967..d257234 range is origin/main's own motion arriving through the merge, not this PR's. Two commits on the branch since the prior head: the merge and d257234a7.

  2. Changeset. Frontmatter "@objectstack/driver-sql": minor; the **BREAKING** banner and the single <!-- adr-0087: not-required (no-migration-prescription) … --> marker are byte-unchanged from 8981967 (the diff between the two heads touches only the body: the new §"What moves" heading + table + two paragraphs, one added Number(row.amount) bullet, one bullet reworded, one columnMap bullet). Per-dialect claims spot-checked against the head's sql-driver.ts: boolean gate isSqlite || isMysql (:16861 arm + MySQL arm), JSON parse under isSqlite (:16897), numeric-string fold (:16916), PG date OID parser pinned to text (:5418), columnFieldByObject populated :9676 / consumed :16849-16851. Content correct. Gate outcome: R1.

  3. Live-dialect arm (sql-driver-window-function-output.test.ts:227-380). Imports DIALECT_CELLS, declareDialectCell, assertThreeWayZoneSkew, readServerZone, type DialectCell from ./live-dialect-matrix.testkit.js (:50-56); for (const cell of DIALECT_CELLS) declareDialectCell(cell, 'window-function row presentation (#16609)', measure) (:378-380). declareDialectCell (testkit :506-540) routes an unprovisioned cell to declareUnprovisionedCell, whose it.skipIf(!EXPECT_LIVE_DIALECTS) is a named skip locally and an expect.fail RED under OS_EXPECT_LIVE_DIALECT_MATRIX=1 — no silent path. §L1 expect(typeof row.ok).toBe('boolean') + [true, false] (:317-325); §L2 expectCanonicalInstant over closed_at/created_at/updated_at — not Date, typeof 'string', /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/ — and closed_at equal to the two seeded ISO literals (:327-335); §L4 reads the raw row through (driver as any).knex(LIVE_TABLE)…first() and asserts the instants are Date and MySQL ok is a number off the client (:349-371), which is what makes §L1/§L2 a measurement of formatOutput rather than of the client. Live cells run assertThreeWayZoneSkew (:269). §L0 non-vacuity (:305-315), §L3 door agreement (:337-347).

  4. 13973 header. sql-driver-13973-canonical-iso-read-door.test.ts:20-28 now: "Since driver-sql: findWithWindowFunctions returns storage forms — a declared boolean answers 1 and an object field answers JSON text where find() answers true and the parsed object #16609 it routes each row through the SAME formatOutput pass find() runs (minus the window-alias columns) … pinned by sql-driver-window-function-output.test.ts⚠️ ADR-0053 D-F1 still RECORDS that door as not covered … docs-only governed card docs(adr-0053): D-F1 says findWithWindowFunctions applies no read presentation — false once #16716 merges (governed, docs-only) #16782 carries the amendment." Nothing else in the file moved.

  5. sql-driver.ts hunk unchanged. git diff a0856e3bf..8981967 vs git diff ed7243d52..d257234a7 on the file, @@/index lines stripped: byte-identical — one hunk, return await builder; → rows + snapshot / this.formatOutput(object, row) / restore. It now sits at :9074-9133 (call at :9129, was :9058) purely because origin/main grew above it. Blob differs from 8981967 only by the merged main content. formatOutput / readPresentationKind / presentReadValue still called, never edited.

  6. PR body. "The fix" carries the seven-class table with per-dialect "where the row actually changes"; the ADR-0053 D-F1 / docs(adr-0053): D-F1 says findWithWindowFunctions applies no read presentation — false once #16716 merges (governed, docs-only) #16782 note; the "Superseded by the patch round" note on the conformance counts (17 passed | 2 skipped).

  7. CI on d257234a7 (33 check runs, read 05:38Z): Check Changeset — failure (R1). Temporal Conformance (live PG + MySQL) — success (05:31–05:37Z). Success: Build Core, Test Core 1/6, Dogfood Verify CLI, Type Check · source gates / debt ledger / consumer gates, Governed Surface Queue Guard, Check Documentation Links, Flag docs, Check PR Size, Auto Label, the three claim guards ×2. Skipped: Build Docs, Console Pin Gate, Packed-tarball smoke. Still in progress: Test Core 2–6/6, Dogfood Regression Gate 1–3/3, Lint & Repo Gates, Type Check · workspace.
    On the live cells having actually run: the job's driver-sql step (ci.yml at the head, job Temporal Conformance (live PG + MySQL), step "Run driver-sql suite against both live servers") sets OS_TEST_POSTGRES_URL, OS_TEST_MYSQL_URL and OS_EXPECT_LIVE_DIALECT_MATRIX: '1', runs pnpm --filter @objectstack/driver-sql test (= bare vitest run, no include/exclude), so an unprovisioned #16609 cell would have been a RED, not a skip; success therefore means §L0–§L4 passed on live PG and live MySQL. Stated as an inference from job config + conclusion: the log API returns only the last 5000 of 11027 lines and the driver-sql block is in the first half I could not retrieve.

Residual findings

R1 — Check Changeset red on the head; the F2 discharge is the cause. Blocking. check-adr-0087-registration --base ed7243d52 (job 101948563951, 05:31:25Z): "not-required (no-migration-prescription) contradicts the changeset's own body, which carries a migration prescription. Evidence (from-to-label): **What moves, FROM → TO, per column class and per dialect.** Routing this door". Reproduced with the gate's own export: scripts/check-adr-0087-registration.mjs is the same blob at the head and at the merge base (ebc55c70cc), and findMigrationPrescription() from that file returns null for the 8981967 body, {branch:'from-to-label', line:'**What moves, FROM → TO, …'} for the d257234 body, and null again for the d257234 body with only that heading reworded (**What moves, per column class and per dialect (storage form → presented form).**). The table rows (1 / 0true / false, Date'2026-01-10T…'), the rewrite bullets and the word "renames" in the columnMap row do not trip any branch — the prior head already carried the arrow bullets and was green. Only the literal FROM … → … TO used as a label does (FROM_TO_LABEL_RE :1132; labelPositioned :1308 — a comma before FROM is not a governing word). So the patch-round line "check-adr-0087-registration.mjs --base origin/main → exit 0" does not reproduce against this head; CI is authoritative. Expectation: reword that one heading so the placeholder token is not used as a label (the reworded form above verifies null; do not drop the **BREAKING** token or switch category — the gate's own text forbids both routes here). No other file moves.

R2 — nits, non-blocking. (a) sql-driver-window-function-output.test.ts:49 imports '../src/index.js' from inside src/ — resolves to the same file, but the sibling convention is './index.js' (sql-driver-13973-…:71). (b) The same file's header :14-21 and :168-171 still describe the instant presentation as "under change (ADR-0053 D-F1)"; #16619 has landed and is merged here, so the SQLite arm's agreement-not-literal rationale is now historical — the ## Two arms section (:26-45) states the current position correctly, so this is wording, not substance.

Not findings, recorded: no governed path in the diff; no git stash (stash list empty); #13973 not folded in; #3797 / #3849 not reopened; the alias-collision ruling remains pinned in code (sql-driver.ts:9094-9115) and test (:208-217).

Blocks landing besides #16782: R1 only — a one-line changeset heading edit, then Check Changeset must be green on the new head (and the still-running jobs must finish green).


Generated by Claude Code

…s no prescription (#16609)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TezFG8ZMrNH6n5VTNpPpdH
@claude

claude Bot commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

Patch round 2 — R1 (director seat)

R1 is fixed on a new head; nothing else moved. One file, one line.

The reword (.changeset/window-functions-row-presentation.md:35) — the placeholder token is no longer used as a label:

  • before: **What moves, FROM → TO, per column class and per dialect.**
  • after: **What moves, per column class and per dialect (storage form → presented form).**

The **BREAKING** token, the minor frontmatter and the adr-0087: not-required (no-migration-prescription) marker are byte-unchanged — the gate's own success line re-reads all three back ([BREAKING+bang] not-required (no-migration-prescription)).

Detector reading, both legs. findMigrationPrescription imported from scripts/check-adr-0087-registration.mjs and run over the changeset body:

  • new body (ea93dbea5): null
  • old body (d257234a7, control leg): {"branch":"from-to-label","line":"**What moves, FROM → TO, per column class and per dialect.** Routing this door"}

The control leg is why the null is a measurement and not a vacuous pass: the same function on the same head still finds the old line.

Gates, exit codes captured before any pipe, run in the worktree at the new head with --base origin/main (merge base ed7243d52):

command exit
node scripts/check-adr-0087-registration.mjs --base origin/main 0
node scripts/check-changeset-no-major.mjs --base origin/main 0
node scripts/check-empty-changeset.mjs --base origin/main 0

The first one was 1 on d257234a7 with exactly the evidence line R1 quoted, which is the reproduction the prior patch-round comment lacked: the gate diffs HEAD, not the working tree, so an uncommitted reword still reads red. check-changeset-no-major repeats its standing LEVEL AXIS: NOT MEASURED note (no pull_request payload locally); that is neither pass nor failure and is unchanged from the prior head.

New head: ea93dbea5 (ea93dbea526f5a0084c742686dee5875951fe224), branch claude/issue-16609-window-functions-presentation. Not rebased, not amended; d257234a7 is its parent.

R2 left alone, deliberately. (a) '../src/index.js''./index.js' is unambiguous, but it moves a test file, and this round's commit is scoped docs(changeset) — a test edit under that message, or a second unsanctioned commit, buys a nit at the cost of a re-run of the whole driver-sql matrix. (b) The stale "under change (ADR-0053 D-F1)" wording: the re-review places it in sql-driver-window-function-output.test.ts:14-21 / :168-171, while the dispatch names sql-driver-13973-canonical-iso-read-door.test.ts — the two disagree on which file, so it is not the unambiguous edit the round allows. Both are non-blocking by the reviewer's own grading and are better carried on the next round or a follow-up card.

CI on the new head is not waited on here.


Generated by Claude Code

Copy link
Copy Markdown
Contributor

Contract review — R1 discharged at ea93dbea5 (director seat, CONTRACT_REVIEW_TIER)

The re-review at d257234 (5579881064) left exactly one blocking item, R1: the changeset heading **What moves, FROM → TO, …** read by check-adr-0087-registration as a migration prescription. Patch round 2 (5579915463) reworded that single line; the gate's own findMigrationPrescription() answers null on the new body and from-to-label on the old one (control leg), and the three changeset gates exit 0 at the committed head.

Read on CI at ea93dbea5 (05:5xZ): Check Changeset success, Governed Surface Queue Guard success, Type Check · source gates success; Test Core, Temporal Conformance and Lint & Repo Gates in progress, 0 failures. The diff d257234..ea93dbea5 is the one changeset line. Verdict for the head: PASS (F1–F5 discharged at d257234, R1 here). R2 nits stay non-blocking. ADR-0053 D-F1 amendment is card #16782 (governed, maintainer merge), tracked separately.

Landing: needs:contract-review cleared on both carriers, ready, auto-merge; the queue admits it only when the in-progress runs are green.


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 8, 2026 05:51
@os-zhuang
os-zhuang enabled auto-merge September 8, 2026 05:51
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 78bc4ad Sep 8, 2026
38 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-16609-window-functions-presentation branch September 8, 2026 06:36
os-musk pushed a commit that referenced this pull request Sep 8, 2026
…d door

PR #16716 (card #16609) routes findWithWindowFunctions rows through the same
`formatOutput` pass `find()` runs, so ADR-0053's three statements that the
door applies no read presentation are false on the tree as of that merge.

Three carriers amended, all prose, all re-derived by symbol:

  - `:3`   the Status line — the "but `findWithWindowFunctions`" exception,
           which is the ADR's summary for a reader who never opens the addendum
  - `:1081` D-F1's body — "is not one of these doors"
  - `:1157` the Consequences bullet — "Not covered: ... applies no read
           presentation of any kind today"

Each amendment states separately what the door moves (the columnMap row-KEY
rename, JSON, numeric strings, the two instant classes, boolean, date, time)
and which of it D-F1 governs (the two instant classes only), so the correction
does not replace one overstatement with another. The window ALIAS carve-out is
recorded at each site: a computed alias wins the key and its value stays raw.

D-F3's Invalid `Date` carve-out is untouched, verbatim, at both sites that
carry it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ADLdAs2pVcH17h9tZKWMBg
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

4 participants