Skip to content

docs(spec): stop the def-key-collision prose committing to a live self-alias count - #12854

Merged
os-sales merged 1 commit into
mainfrom
claude/issue-12608-def-key-collisions-prose
Aug 28, 2026
Merged

docs(spec): stop the def-key-collision prose committing to a live self-alias count#12854
os-sales merged 1 commit into
mainfrom
claude/issue-12608-def-key-collisions-prose

Conversation

@os-sales

Copy link
Copy Markdown
Collaborator

Fixes#12608

Three prose sites in the def-key-collision guard claimed a self-alias population that does not exist, and cited an export that does not exist. Comments and one build-output string only — no behaviour anywhere, no new gate.

Day-of re-measure (premise check)

The card measured at 7c0d0c395; #12588 has since landed (146f448a), so the population was re-measured on today's origin/main (6f0fec3d) from a real generator run rather than re-derived by hand — pnpm --filter @objectstack/spec run check:authorable-surface, which is build-schemas.ts --check and prints the population #12588 added:

 Generated: 1597 (124 as input shape)
Skipped: 24 (unsupported types: function, date, bigint, custom)
11 emit(s) collapsed into 11 existing def key(s) — all self-aliases
(one schema object reached by two export names), so 1597 emits publish
1586 definitions

The 11 def keys it lists: api/ApiEndpoint, api/RestApiConfig, api/RestServerConfig, api/ApiDocumentationConfig, api/ApiTestCollection, api/OpenApiSpec, api/RestApiPluginConfig, api/RestApiRouteRegistration, system/MiddlewareConfig, system/QueueConfig, system/Task.

Categories: api (8) and system (3). No ui entry, as the card said. The card's other two (system/BatchTask, system/WorkerConfig) are still self-aliases in src/system/worker.zod.ts but both halves are skipped as unrepresentable ("Function types cannot be represented in JSON Schema"), so they never reach an emit — the card's 13-at-the-walk / 11-surviving split still holds, unchanged. Fourteen was wrong then and is wrong now, and the number that is right depends on which of two questions you are asking — which is the whole argument for prose that does not carry one.

git grep -n "export const ThemeMode\b" -- packages/spec/src still returns nothing: the cited symbol does not exist.

One correction beyond the card

The card assumed the real convention is spelled export const X = XSchema. It is not — all 11 use Object.assign(XSchema, { … }):

exportconstApiEndpoint=Object.assign(ApiEndpointSchema,{create: (config)=>config,// real line is generic; type parameters elided here});

(src/api/endpoint.zod.ts:225 — the generic signature is elided above on purpose, since angle-bracket fragments do not survive this body reliably.)

Object.assign returns its target, so ApiEndpoint and ApiEndpointSchema are one object and the guard's identity predicate exempts it. Swapping ThemeMode for ApiEndpoint while keeping the = XSchema spelling would have replaced one unfindable citation with another, so the new prose cites the spelling that is actually in the file, and names the plain re-export as the same shape by the same test.

The three sites

SiteBeforeAfter
scripts/lib/def-key-collisions.ts module doc"export const ThemeMode = ThemeModeSchema, and fourteen more across api, system and ui"describes the shape, cites src/api/endpoint.zod.ts's real ApiEndpoint line, and states the population is deliberately not restated — pointing at the per-run report line and at findSelfAliasedDefKeys that builds it
scripts/def-key-collisions.test.ts line 326"the fourteenexport const X = XSchema self-aliases this package really does carry""the self-aliases this package really does carry — the Object.assign(XSchema, …) exports under api/ and system/"; states the number is deliberately not pinned and names the generator as the live source
formatDefKeyCollisions remedy text (user-facing build output)"(export const ThemeMode = ThemeModeSchema — that shape is allowed here …)"export const Foo = FooSchema as the placeholder the surrounding text already uses, plus the real Object.assign form src/api/endpoint.zod.ts uses for ApiEndpoint; closes by pointing at the build's own summary

The acceptance criterion is that the wording stays correct when the population moves again: no site now names a count, a category set, or a symbol that a future edit can falsify.

No test asserts the remedy string. Checked before editing — git grep for duplicate DECLARATION / re-export the survivor across packages/spec and scripts returns only scripts/lib/schema-index.ts, which is a different file's own message. The pin test asserts the collision header and the shared/HttpMethod line, both untouched. So no assertion updates were owed.

Deliberately not done

  • No new gate, no count-pinning test — the card's own reasoning: pinning a comment's count costs more than it saves.
  • The test's ThemeMode fixtures are left alone (lines 16, 62-67, 95-96, 156-157, 166, 170). Those construct a synthetic EmittedDef on the spot from an obvious placeholder literal, so they claim nothing about what the package exports — unlike the module doc, which claimed "the package's self-alias convention". Renaming them would be diff noise across a file this PR only comments on. Flagging the judgment rather than burying it.
  • No ablation. Documentation-only diff; no guard, predicate or threshold changed, so there is no detector whose ability to fail needs demonstrating.

Verification

Gate union derived against the actual changed set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (it reads the merge-base change set itself), then every derived family run. All readings below are from the final commit, 9e896a04, on a clean tree.

25 green, each exit code captured before any pipe: check:authorable-surface, check:cross-package-test-inputs (both the pnpm and the ci.yml spelling), check:empty-state, check:liveness, check:merge-driver, check:objectql-double-limit, check:page-declaration-shape, check:pm-governed-merges, check:published-files, check:slot-lookup, check:strictness-ledger, check:test-source-alias, check:type-source-resolution, check:variant-docs, check-ci-filter-parity, check-comment-mask-adoption, check-plugin-teardown-shape, check-affected-docs, check-drift-comment, check:nul-bytes, plus the convention-triggered set for editing a test file: check:query-options-erasure, check:engine-double-contract, check:where-matcher, check:type-check-coverage.

The guard and the reworded output still agreepnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 scripts/def-key-collisions.test.ts: 17 passed (17). That file's last test mutates a fixture copy of the package to introduce a real collision, spawns build-schemas.ts, and asserts the guard's verdict on its output, so the remedy string is exercised through the generator rather than read.

pnpm --filter @objectstack/spec run typecheck — exit 0. It is three programs, and both edited files are genuinely covered: tsconfig.scripts.json compiles the lib, tsconfig.test.json the test file (check:test-typecheck: OK — @objectstack/spec's test layer compiles).

NOT MEASURED (1):node scripts/check-dev-prereqs.mjs exits 1 with its own printed line — "The workspace is not built — 1 unmet precondition, not a list of problems. 67 of 67 workspace packages declare an entry point under dist/ that is not on disk". That is the unbuilt-worktree precondition, not a verdict on this diff: the gate measures whether built dist/ entry points exist, which two comment-only files under packages/spec/scripts/ cannot move. CI builds from a fresh checkout and measures it there. Declared rather than silently dropped.

Repo-wide pnpm lint was not run — CI owns that sweep; heavy runs here went through scripts/pm/os-verify-lock.sh on a contended shared box.

Release impact: nothing, so skip-changeset rather than a changeset file

Measured, not assumed. packages/spec's files allowlist is dist, json-schema, liveness, prompts, llms.txt, README.md, src/**/*.zod.ts, CHANGELOG.md, api-surface, spec-changes.jsonscripts/ is not in it, so neither edited file enters the npm tarball. Neither string reaches a generated artifact either: the module doc and the test comment are comments, and the remedy text is printed to the console only on a collision, which fails the build rather than writing anything. A grep for the changed strings across content/docs, json-schema/, api-surface/ and objectstack.json finds no artifact carrying them (the single content/docs/releases/v17.mdx hit is an unrelated sentence about inert metadata keys).

So this diff releases nothing, and the honest route is the skip-changeset label, applied by the dispatching seat — not a changeset file, and not an empty one (those are rejected). Flagging it here so the seat can apply it.


Generated by Claude Code

…f-alias count
The module doc, the pin test's comment and the user-facing remedy text all
cited `export const ThemeMode = ThemeModeSchema` plus "fourteen more across
api, system and ui". No such export exists anywhere in packages/spec/src, the
population is not fourteen, and no `ui` def key is in it.
Describe the shape instead of restating a total, cite a self-alias that really
exists (api/ApiEndpoint, with the Object.assign spelling the package actually
uses), and point the reader at the per-run report line build-schemas.ts has
printed since #12588. Comments and one build-output string only.
@os-salesos-sales added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed size/s labels Aug 28, 2026 — with Claude
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 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 — 0 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 b489d3c725e86583410c20a91e59e0c5eb0ba255packageMentionDocs.

@os-sales
os-sales marked this pull request as ready for review August 28, 2026 04:22
@os-sales
os-sales enabled auto-merge August 28, 2026 04:22
@os-sales
os-sales added this pull request to the merge queueAug 28, 2026
Merged via the queue into main with commit 3ca2365Aug 28, 2026
41 checks passed
@os-sales
os-sales deleted the claude/issue-12608-def-key-collisions-prose branch August 28, 2026 04:44
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

skip-changesetPR has no user-facing published change; bypasses the changeset gateteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[finding] def-key-collisions prose names "fourteen" self-aliases and a ThemeMode export that no longer exists — measured: 13, and none in ui

2 participants

@os-sales@claude