Skip to content

spec: withdraw the never-honored introspection indexes promise; widen defaultValue/maxLength to the measured emitted types - #11270

Merged
os-sam merged 2 commits into
mainfrom
claude/issue-11122-introspection-contract
Aug 23, 2026
Merged

spec: withdraw the never-honored introspection indexes promise; widen defaultValue/maxLength to the measured emitted types#11270
os-sam merged 2 commits into
mainfrom
claude/issue-11122-introspection-contract

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#11122

Implements the maintainer ruling recorded 2026-08-23 (comment 5383569868, verbatim 「其他同意你的意见」 adopting recommendation B): withdraw the never-honored promise. Wiring the index read (option A) is explicitly NOT done here; if real consumer demand appears, that is a new card with its own onFailure ruling.

What changed

  • packages/spec/src/contracts/schema-diff-service.ts — the two ruled keys only (kept tight for the An UNREACHABLE federated remote is reported as missing_table — a transient outage is indistinguishable from a dropped table, and aborts boot under the default onMismatch: 'fail' #11166 serial handoff, which lands in this file after this PR):
    • IntrospectedTable.indexes is now optional, with TSDoc fixing the semantics: absent = "the producer did not read indexes"; empty array = a positive claim the table HAS none. Producers that did not look must omit the key — never emit a false-empty array.
    • IntrospectedColumn.defaultValue widens from the never-honored string to unknown — raw as the driver reported it, with the measurements in the TSDoc.
  • packages/drivers/driver-sql/src/sql-driver.ts + packages/objectql/src/util.ts — the deliberate Omit workarounds and their explanatory notes are retired: both packages' IntrospectedColumn/IntrospectedTable/IntrospectedSchema now extend the spec contract directly (same PR, per the ruling). The SQL layer still adds only extra facts (isUnique, maxLength, foreignKeys, primaryKeys), never a second spelling of a spec key.
  • maxLength (the noted same-class case) widens from number to number | string at all three SQL-layer sites: driver-sql IntrospectedColumn, PhysicalColumn in schema-drift.ts (fed directly from the same read; its varchar differ already narrows via typeof before comparing), and objectql IntrospectedColumn.
  • No runtime behavior changes anywhere — producers emitted these shapes all along; the declarations now tell the truth.

Premise check (re-verified on today's origin/main, base e5ea701)

Both premises held: the spec still declared indexes required and defaultValue as optional string; the Omit workarounds still stood at driver-sql sql-driver.ts (formerly lines 3687/3711/3725) and objectql util.ts (formerly lines 34/66/83).

Re-measurement (SQLite in-memory, knex columnInfo() pass-through — the driver's exact source, 2026-08-23)

columndefaultValuetypeofmaxLengthtypeof
t.string('id').primary()nullnull"255"string
t.string('name').defaultTo('abc')'abc' (quoted)string"255"string
t.integer('n').defaultTo(5)'5'stringnullnull
t.boolean('b').defaultTo(true)'1'stringnullnull

Honest limit, as the card states: SQLite only — no PG/MySQL server is reachable from this container. Both keys are built at a single dialect-independent site (introspectSchema/introspectColumns), so the SHAPE cannot vary by dialect; per-dialect CONTENT is not claimed. unknown (not a closed union) is deliberate for defaultValue: a closed union would claim completeness across dialects nobody measured; unknown also forces consumers to narrow, which is the authoring-safety direction the four-axis analysis picked.

Consumer readings (recorded per the ruling; a found consumer would have forked back to the card)

(a) objectstack repo-wide — CLEAN. Every typed consumer of the spec introspection types was enumerated (imports of the types across all packages): service-datasource (external-datasource-service.ts reads columns and primaryKeys; never table.indexes, never a string operation on defaultValue — its PhysicalColumn.defaultValue was already unknown and comparisons go through physicalDefaultIsToken), objectql convertIntrospectedSchemaToObjects (reads columns/foreignKeys; copies defaultValue opaquely behind a null-guard), and test fixtures. IntrospectedIndex has zero consumers outside the spec itself. The wire never carries the shape either: the REST family serves RemoteTable (name + columnCount only), so no UI can read indexes structurally. Fixtures in service-datasource/rest/runtime tests that carry indexes: [] remain legal under the optional key (an empty array is still a valid positive claim) and were deliberately left untouched — outside this card's file surface, and service-datasource is H17-adjacent (neither H17 hold-trigger file is in this diff).

(b) objectui — CLEAN, positive-controlled. Read at fetched origin/main 3ddc8c261aa31ee85ce307330fb2ff4d246b15db: git grep -nE "Introspected(Schema|Table|Column|Index)|introspectSchema|introspectIndexes" over all .ts/.tsx → NO MATCHES. Positive controls on the same SHA: defaultValue (matches — e.g. apps/console/dev/tests/setup/common-mocks.ts) and @objectstack/spec (matches — e.g. apps/console/src/tests/component-input-union-specimens.test.ts) both hit, so the empty result is a measurement, not a broken grep.

(c) cloud — NOT this PR's reading to take. Requested as seam card #11232 (which gates enqueue only). This PR parks as draft until #11232's reading (a) posts clean.#11232 remains open and is not addressed here.

Tests

  • New packages/spec/src/contracts/schema-diff-service.test.ts: compile-time pins (a table WITHOUT indexes typechecks; indexes: [] and populated stay distinct legal claims; defaultValue: null / quoted-string / true accepted; a string operation on defaultValue without narrowing does not typecheck — spelled as the narrowing guard).
  • sql-driver-introspection-spec-contract.test.ts gains three pins on live introspection bytes: 'indexes' in table === false (absence, never a false-empty []), the emitted schema assigned to the spec type (the compile-time seam pin), and the measured raw emissions (defaultValue === null for a no-default column; varchar maxLength within the declared number | string union, Number(...) === 255).

Verification (all at HEAD 96b4153, the final commit — quoted from the runs themselves)

  • Suites: spec 419 files / 11114 tests passed; driver-sql 112 passed / 6 skipped files, 1825 passed / 85 skipped tests; objectql 229 files / 4049 tests passed.
  • Typechecks: spec (tsc + scripts + test-typecheck), driver-sql, objectql — all exit 0.
  • check:generated: "All 14 generated artifacts are up to date." (No api-surface/docs churn — the surface records existence, and no export was added or removed.)
  • Gate union re-derived from the ACTUAL diff by node scripts/pm/dispatch-gates.mjs (no paths passed; stderr line: "gate list derived from the tree of 'objectstack-ai/objectstack' at commit 96b4153"; change set = exactly the 7 files of this PR). All 24 path-matched + 5 convention-triggered families run locally, every one exit 0 with its own OK line — including check:type-check-debt ("33 ledger entries re-measured … none above its recorded number", after the full turbo package build it requires), check:type-check-coverage, check:cross-package-test-inputs, check:engine-double-contract, check:adr-0087-registration (patch changeset — not declared-breaking, no marker owed), and check:nul-bytes. Two initial refusals were environment artifacts, each fixed by its own printed remedy and re-run green: check:dev-prereqs (fresh worktree, unbuilt sibling dists → full turbo build) and check-engine-split-ratio (shallow clone → deepened to 2026-05-18).

Reverse verification (mutation proven on disk, both legs rebuilt)

Mutation = restore the OLD declarations (required indexes, string defaultValue) from base e5ea701; proven on disk by grep (old lines present 1/1, new line absent 0) and in the rebuilt spec dist (old required-indexes line present in dist contracts d.ts). Predicted directions stated before running:

  • spec check:test-typecheck → predicted RED, observed RED (exit 1: 5 type errors in the new pin file — exactly the literals the old declaration refuses).
  • driver-sql tsc → predicted RED, observed RED (exit 2: TS2741: Property 'indexes' is missing … but required in type 'IntrospectedTable' at the introspectSchema construction site — the Omit-retirement failing to compile, as required).
  • objectql tsc → predicted RED, observed GREEN — prediction wrong, and the reason is worth recording: objectql's tsconfig excludes all test files from tsc and the package has no test-typecheck program, so the defaultValue: true fixture in util.test.ts is compiled by no tsc program (vitest strips types) — a genuinely uncoupled compile surface, not a broken discriminator. objectql src itself only CONSUMES introspection values (no construction site), so its green under mutation is correct. This hidden test layer is a known, ledgered state — @objectstack/objectql carries a measured TEST_DEBT entry (354 frozen raw errors) in check:type-check-coverage, tracked under the standing coverage program ([P2] framework: 66 个包用 tsup 构建、无人做类型检查 —— 实测 18 个包共 380 处 code-tier 错误(#4118 的 framework 侧对应) #4311 / [finding][devx] check:type-check-debt 的 ledger 余量会让新写的 pin 变哑:mongodb 曾有 33 条余量吞掉一次真实回退,另有 5 条目前带 4–19 余量 #6376) — so no new issue is filed; recording the fact here instead. The compile-time pins for this card therefore live in spec and driver-sql, both proven able to fail.
  • Restore leg: src restored from HEAD (git clean), spec rebuilt, dist proven restored (old marker 0, new marker 1), and all three checks re-run green (exit 0 / 0 / 0).
  • Mutation-leg narrowings, declared: spec ran check:test-typecheck only (the program carrying the pins; spec src compiles under either declaration), objectql ran main tsc only (its scripts program does not touch introspection). The green-tree runs above were full.

Notes for review

Generated by Claude Code


Generated by Claude Code

…maxLength, retire Omit workarounds
Declaration edits only so far; tests, changeset, verification to follow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RadETjNRLALFLhFA3xehZP
…lue/maxLength; changeset
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RadETjNRLALFLhFA3xehZP
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/driver-sql, @objectstack/objectql, @objectstack/spec, touching 8 documentable anchor(s).

35 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 064d484bd7adc8874a609c7ec226a2f0e3bc25aa.

2 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 45 of 221 client-bound route-ledger rows — the other 176 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 129 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 064d484bd7adc8874a609c7ec226a2f0e3bc25aapackageMentionDocs.

Which tree this was computed on

This run read content/docs from e4c31ea0ada7649c7c863577a5867c4ab831ee02 — the merge of head 96b4153cd3638a7965ec4a9e90215e8016014bfd into base 064d484bd7adc8874a609c7ec226a2f0e3bc25aa, 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 e4c31ea0ada7649c7c863577a5867c4ab831ee02 && git checkout e4c31ea0ada7649c7c863577a5867c4ab831ee02
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 064d484bd7adc8874a609c7ec226a2f0e3bc25aa 96b4153cd3638a7965ec4a9e90215e8016014bfd && git checkout -B drift-repro 064d484bd7adc8874a609c7ec226a2f0e3bc25aa && git merge --no-ff 96b4153cd3638a7965ec4a9e90215e8016014bfd
node scripts/docs-audit/affected-docs.mjs --json 064d484bd7adc8874a609c7ec226a2f0e3bc25aa

⚠️ 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 064d484bd7adc8874a609c7ec226a2f0e3bc25aa → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tests tooling labels Aug 23, 2026
@claude

claudeBot commented Aug 23, 2026

Copy link
Copy Markdown
Contributor

云读数已到并已关卡:#11232 两项读数均贴于该卡(cloud origin/main @ b7d3daf)——Reading (a) PRIMARY/SECONDARY 零命中、CONTROL 293,cloud 腿干净;Reading (b) 零命中同 CONTROL 通过。落地闸已释放,请按标准解锁纪律在所贴 ref 上复验后入队。(skills 席代跑读数;缝卡等待 9 小时后由维护者点名催办)


Generated by Claude Code

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mteststooling

Projects

None yet

2 participants

@os-sam@claude