Uh oh!
There was an error while loading. Please reload this page.
docs(service-datasource): make the primaryKeyReader docblock true again, and record why the isPrimary arm stays - #11206
Conversation
…in after the driver aligned to spec The `primaryKeyReader` docblock is the one place this seam's reasoning is written down, and two of its statements went false when the driver was aligned to the `packages/spec` introspection contract (`95437e7d2d`, #10676/#10998) and when the SQLite composite-key truncation was repaired (#10997, PR #11104). Comment-only. No executable line changes. - The producer table said `SqlDriver` spells the per-column signal `isPrimary`. It emits `primaryKey` and no `isPrimary` at all. - The `#10997` note said the SQLite composite-key truncation "is upstream of this seam and is not repaired here". It was repaired; `introspectPrimaryKeys` now reports every member of a composite key in declared key order. - The closing note said reading the extra spellings structurally was needed "because reconciling `objectql/src/util.ts` with the spec contract is a spec-owned change". That reconciliation has happened. - The `refreshCatalog` seam comment repeated the stale claim that "a real driver spells this `isPrimary` / `primaryKeys`, never `primaryKey`". - The seam suite's second `describe` said `SqlDriver` derives `isPrimary` FROM `primaryKeys`; it derives `primaryKey`. The union read is UNCHANGED — all three arms stay. `table.primaryKeys` is live and independent: after #10997 it is the only signal here that carries a composite key in declared key order, which a per-column boolean cannot express. The `isPrimary` arm is now recorded as a deliberate compatibility belt rather than a bridge to a live producer, with the measurement that decided it, so the next reader does not re-derive it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
📓 Docs Drift Check2 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ✅ What this run could not see
Coarse fallback — 0 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 c39349a221de1747015fe28c90982deea8ccecca && git checkout c39349a221de1747015fe28c90982deea8ccecca
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 21756b3254ed61a6c38baa46642bd003952194b4 57e6a7ccbf0f80bdee4a355cb2247959f5ee8dc1 && git checkout -B drift-repro 21756b3254ed61a6c38baa46642bd003952194b4 && git merge --no-ff 57e6a7ccbf0f80bdee4a355cb2247959f5ee8dc1
node scripts/docs-audit/affected-docs.mjs --json 21756b3254ed61a6c38baa46642bd003952194b4 |
Uh oh!
There was an error while loading. Please reload this page.
Part of #11123 — deliberately NOT a closing keyword.
This lands the half that is executable today: the docblock truth repair (ruled, not re-litigable). The other half — whether the union's
isPrimaryarm is retired — is escalated as a contract-review decision rather than taken here, with the measurement behind it below. #11123 stays open to carry that decision; closing it on this merge would drop the open question into a closed card.Comment-only. Zero executable lines change — verified mechanically: every added and removed line in this diff is a comment or blank (
git diff -U0filtered for non-comment+/-lines returns empty). The union read inprimaryKeyReaderis byte-identical; all three arms stay.What was stale, and the evidence it is stale
The
primaryKeyReaderdocblock is the one place this seam's reasoning is written down. Re-verified on the merged ref (52a41b72ee) before editing — both card claims hold, plus three the card did not name:SqlDriverspells the per-column signalisPrimary95437e7d2d(#11124).sql-driver.ts:10151now doesif (primaryKeys.includes(col.name)) col.primaryKey = true;and emits noisPrimary#10997note: the SQLite composite-key truncation "is upstream of this seam and is not repaired here"introspectPrimaryKeysfiltersrow.pk > 0and sorts on the ordinal, so it reports every composite member in declared key orderobjectql/src/util.tswith the spec contract is a spec-owned change"objectql/src/util.tsnowextends Omit<SpecIntrospectedColumn, …>refreshCatalogseam comment: "a real driver spells thisisPrimary/primaryKeys, neverprimaryKey"describe: "SqlDriverderivesisPrimaryFROMprimaryKeys"primaryKeyRows 4 and 5 were not named on the card. They are the same defect class (stale prose asserting the retired producer spelling), mechanical, pinned by the same commit, inside the file surface already claimed, and add no verification surface — so they were repaired in place rather than left as a second card that would go stale against this one. Naming them here is the condition for that.
What did NOT change, and why
The union read is untouched.
col.primaryKey || col.isPrimary || table.primaryKeys— all three arms.table.primaryKeysstays — inherited verbatim from thedomain:engineseat and not re-litigated. A per-column boolean cannot express key ORDER, and since driver-sql: SQLite introspection reports only the FIRST column of a composite primary key (pk === 1vs SQLite's 1,2,3… numbering) #10997 this list is the only signal here carrying a composite key in declared key order. Collapsing it would silently drop composite-key handling at this seam.isPrimaryarm stays too, and that is a decision this PR records rather than one it acts on. See below.The
isPrimaryarm: why it is not a dead-code deletionThe card's claim — no in-tree producer — is true. Swept whole-identifier (
\bisPrimary\b), every surviving non-test hit is prose;objectql'sisPrimaryKeyFieldmerely contains the substring and is unrelated. But in-tree is not the same population as supported, and two independent measurements say removal would narrow accepted input:contracts/datasource-driver-factory.tsstates the framework "deliberately ships no universal driver-by-id registry — concrete drivers are constructed by the host stack", and types the handle asintrospectSchema?(): Promise<unknown>. The retirement shipped as a BREAKING change whose stated migration channel is the compiler: "TypeScript consumers of the removed member are told by the compiler, precisely and at every site" (.changeset/driver-emits-spec-introspection-shape.md). Against aPromise<unknown>handle that channel never fires. A host-built driver still emitting the old spelling is reached by nothing today and would silently lose its remote key if the arm went..changeset/external-catalog-introspected-primary-key.md) and the retirement (fix(driver-sql):introspectSchema()emits the spec introspection contract —primaryKey,dialect,introspectedAt(#10676, #10998) #11124,.changeset/driver-emits-spec-introspection-shape.md) are both still unconsumed changesets at17.1.0. No released version has ever emittedprimaryKeyfrom this driver. Removing the belt now would ship the narrowing in the same release as the break it exists to absorb.So the arm is kept, and the docblock now records it as a deliberate compatibility belt with the measurement behind it, so the next reader does not re-derive this. Retiring it is a contract-review decision, not a cleanup — flagged back to the PM rather than taken here.
Its only exercise anywhere in the tree is the staged-disagreement pair in
external-introspection-seam.test.ts; the suite docblock now says so, and says those two cases go with the arm if it is ever retired.No changeset —
skip-changesetNothing published changes.
primaryKeyReaderis module-private (never in a.d.ts), and test files are outside the package'sfileswhitelist (check:published-filesgreen). This PR releases nothing, which is what the label is for; this repo rejects the empty-frontmatter form (check-empty-changeset.mjs).Verification — all on the final commit
57e6a7ccbfGate union derived on that commit with no hand-supplied paths:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(repo assertion held; 2 paths vs merge base52a41b72e).pnpm --filter '@objectstack/service-datasource^...' buildVERDICT command-exit 0pnpm --filter @objectstack/service-datasource typecheckVERDICT command-exit 0pnpm --filter @objectstack/service-datasource testTest Files 26 passed (26)·Tests 563 passed (563)pnpm check:published-files✓ check:published-files — 69 publishable package(s) …pnpm check:slot-lookup✓ slot-lookup ratchet holds … none newpnpm check:test-source-aliascheck-test-source-alias OK — 72 packages with tests scannedpnpm check:type-source-resolutioncheck-type-source-resolution OK — 77 packages … scannednode scripts/check-ci-filter-parity.mjsOK: all 86 declared cross-package glob(s) …node scripts/check-plugin-teardown-shape.mjs✓ check:plugin-teardown-shape: 63 Plugin implementation(s) …node scripts/docs-audit/check-affected-docs.mjspnpm check:query-options-erasure✓ query-options-erasure ratchet holds … none newpnpm check:type-check-coveragepnpm check:engine-double-contractcheck-engine-double-contract: OK — 384 pinned …pnpm check:where-matcher✓ where-matcher conformance holds … none newpnpm check:cross-package-test-inputsOK: 13 package(s) read outside themselves, all declaredFull workspace closure built for the ratchet gate:
turbo run build --filter='./packages/*' --filter='./packages/*/*'→Tasks: 70 successful, 70 total,VERDICT command-exit 0.One declared narrowing.
pnpm check:type-check-debt(the--re-measureratchet half) is NOT MEASURED locally — its prerequisite closure was built, but the run was killed by the container's 10-minute foreground cap (exit 143) on the first attempt and the lock was held by a parallel agent running the same gate on the retry. Not reported as a verdict. What is measured in its place: its structural siblingcheck:type-check-coverageis green; the ratchet's population is the three ledgers inscripts/check-type-check-coverage.mjs, andservice-datasourceis in none of them (enumerated:TEST_DEBT20 entries,DEBT13,EXEMPT1 — absent from all three), so there is no ledger count of this package's for the ratchet to move; the package's owntypecheckis green; and the diff adds no test file and changes no executable line. CI runs the gate regardless.Generated by Claude Code