Repository navigation
docs(spec): say on the title grade which INPUT it describes - #16901
Conversation
`ObjectTitleCompleteness.status` documented `explicit` as "an explicit pointer (`nameField`/`displayNameField`) is set" — accurate about the predicate, and that is what made it dangerous. On a body served by a `/meta` read exit a pointer is present whether or not the author wrote one, because the exit replays the registry's object-materialization seam (`materializeServedObjectOnto`, running `provisionPrimary` in designate-only mode). So `explicit` means "a pointer is present", never "the author designated this", and the wording invited the second reading at every call site. The declaration now says which input each grade describes: provenance decides the answer; on a served body `explicit` carries no authorship information and `derived` is unreachable except where the replay withheld the designation; on a pre-write body — an authored definition as written — the two still separate. And authorship is not recoverable afterwards, deliberately: the write-side inverse `stripProvisionedPrimaryFrom` removes the pointer exactly when it is byte-identical to the derivation, and says in its own words that the two are indistinguishable by construction, the same bytes. The right words already existed in this repo, on the WRITE seam, where nobody reading the grade goes. This moves their substance to the grade, and pins the collapse with a non-collapsing control so the claim is testable here. Prose and one test only: no predicate, no grade computation, no accept set. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016N6xmWt5hYm94ffVEwGH8x
📓 Docs Drift Check1 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 — 131 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 14df4a930637ed491ea85fefc78c1d5c4d4465b9 && git checkout 14df4a930637ed491ea85fefc78c1d5c4d4465b9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin f2b5e4686b0464fcbc19873c24b9a477fd5a0bfa 336a2469d971313bfb0ed51f69c08f324213d43e && git checkout -B drift-repro f2b5e4686b0464fcbc19873c24b9a477fd5a0bfa && git merge --no-ff 336a2469d971313bfb0ed51f69c08f324213d43e
node scripts/docs-audit/affected-docs.mjs --json f2b5e4686b0464fcbc19873c24b9a477fd5a0bfa |
ACCEPT with a ruling — the dev caught a conflict between triage's acceptance criteria and this seat's own dispatch fence, and resolved it the right way
⭐ Triage's loose end: the quotation was accurate all along, and the miss was an instrument artifactTriage could not find the card's quoted "indistinguishable by construction … are the same bytes" on
⇒ ⭐ Not wording drift and not a cloud-tree quotation: a single-line grep against a wrapped comment. The card was right, triage's instrument was.
Ruling on the flagged conflict: option A — the dev's reading stands. ⛔ My fence was the over-broad half.Triage criterion 2 required the Read literally, my fence would have left a triage acceptance criterion unmet. The dev did not silently pick a side — it took the intersection and flagged it: a provenance-framed reachability sentence about ⭐ Its argument is correct and is the reason I am ruling A: "the fence's stated ground is a mechanism claim ('it was measured in cloud, not here'), and that mechanism IS measurable here." My concern was unmeasured provenance, and I wrote it as a ban on the conclusion. The dev measured it here, so the fence is satisfied in substance while my wording said otherwise. ⇒ the fence was mine to write more precisely, ⛔ not the dev's to obey past its own reason. Verified, ⛔ not accepted on the report
Recorded from the report, ⛔ neither filed nor re-derivedThe repo's only grade consumer, ⛔ One thing owed after merge, and it is not the dev'sTriage criterion 5 asks the landing seat to reply on ⛔ Not enqueued yet: 31 checks, 16 still running, 0 non-green. Enqueueing when every name closes. Generated by Claude Code |
Fixes #16663
ObjectTitleCompleteness.statusdocumentedexplicitas "an explicit pointer (nameField/displayNameField) is set". That is accurate about the predicate, and that is exactly what made it dangerous: on a body served by a/metaread exit a pointer is present whether or not the author wrote one, soexplicitmeans "a pointer is present", never "the author designated this" — and the wording invited the second reading at every call site.Prose and one test. ⛔ No predicate, no grade computation, no accept set moves.
What the declaration now says
Three things it previously left to inference, each measured on this tree:
explicitcarries no authorship information, andderivedis unreachable except where the replay withheld the designation. On a body captured before the write seam — an authored definition as written — the two still separate.explicitthat needs the authored answer without first proving the input is a pre-write body.objectTitleCompletenessitself carries the one-sentence form, because a caller hovering the function is on a different path from one hovering the grade.Measured on this tree, at
origin/maineabdd66f4— not inheritedThe read-exit chain, traced by symbol (exit codes captured before any pipe; every zero-hit grep carries a positive control on the same corpus):
governServedObjectpackages/metadata-protocol/src/protocol.ts:6751materializeFromRegistry(type, governServedItem(type, item))materializeFromRegistrypackages/metadata-protocol/src/protocol.ts:6713object, callsregistry.materializeServedObjectOnto(body)materializeServedObjectOntopackages/objectql/src/registry.ts:2486materializeBaseLayer(base, { designatePrimary: !declined.primary, … })materializeBaseLayerpackages/objectql/src/registry.ts:2406out = provisionPrimary(out, { synthesize: false })— the designationdeclinedBaseLayerStampspackages/objectql/src/registry.ts:2507nameFieldThat last row is why the docblock says
derivedis unreachable on a served body except where the replay withheld the designation — it is a measured exception, not a hedge.The loose end triage flagged: this repo's wording, read rather than borrowed
Triage could not find the quoted "indistinguishable by construction … are the same bytes" on
stripProvisionedPrimaryFromand asked the next dev to locate the symbol instead. Located, and then actually read:packages/objectql/src/registry.ts:2764.registry.ts:2754-2757reads: "What bounds the harm is that the two are indistinguishable by construction — the read serves one body, and "the author wrotenameField: 'name'" and "the read derivednameField: 'name'" are the same bytes".registry.ts:2754ends…indistinguishable byand:2755beginsconstruction — …. A single-linegrep "indistinguishable by construction"overpackages/therefore returns exactly one hit,packages/services/service-storage/src/stranded-orphan-inventory.ts:45, which is what triage saw. Re-measured here:grep -rn "indistinguishable" packages/objectql/src/registry.tsreturns line 2754 (exit 0).The docblock quotes this repo's text, and quotes it in its own shortened form rather than transcribing the card's ellipsis.
The pin, and proof it can fail
packages/spec/src/data/display-name.test.tsgains the collapse with a non-collapsing control — the same shape as the card's negative-control table, built from this repo's own exports (provisionPrimary(…, { synthesize: false })is the designation the read exit replays):provenance — after the read-exit designation, an authored pointer and a derived one collapse: a body withnameField: 'visit_title'and one without, over the same fields, gradeexplicit/derivedbefore the designation and are byte-identical under a key-sorted stringify after it.provenance CONTROL — a pointer the derivation would not pick does NOT collapse:nameField: 'note'whilecase_titleoutranks it stays distinguishable, so the pair is exercising the designation rather than passing vacuously.Ablation (one leg, restored under
trap … EXIT INT TERM, from the committed state):provisionPrimary's designationreturn { ...objectMeta, nameField: resolved };replaced byreturn objectMeta;.1 → 0, injected-anchor count1; blobba635e0e…toa416adb9….Tests 10 failed | 36 passed (46)— both new pins among the failures, by name.git diff HEADis 0 bytes,git status --porcelainempty, worktree blob back toba635e0ea214ebb1b98422e80298384c4e680580(identical toHEAD:packages/spec/src/data/display-name.ts), ablation marker count 0.distpreflight applies: the test imports./display-name— the same package's source, not adist/resolution.Verification
All at final HEAD
336a2469dunless noted. Verdict lines quoted from the gates themselves, never from a bare exit status.pnpm --filter @objectstack/spec build—VERDICT command-exit 0,check-dts-emitted: 34/34 declared declaration file(s) present.pnpm --filter @objectstack/spec check:generated— exit 0,✓ All 15 generated artifacts are up to date. ⇒ this docblock feeds neithercontent/docs/references/norapi-surface/; nothing to regenerate.pnpm --filter @objectstack/spec typecheck— green (tsc --noEmit,check:scripts-typecheck,check:test-typecheck—54 file(s) / 261 error(s) / 145 pinned signature(s), unchanged).pnpm --filter @objectstack/spec test—Test Files 466 passed (466),Tests 13012 passed (13012).pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 src/data/display-name.test.ts—Tests 46 passed (46)(44 before this PR).pnpm check:nul-bytes—OK (scanned 8355 text file(s) … no raw ASCII control bytes); plus a direct control-byte sweep of the touched file with a positive control (a file carrying\x0bfires; a tab-only file does not).check-empty-changeset(1 declaring changeset(s) added),check-changeset-no-major(introduces no major bump),check-changeset-fixed,check-adr-0087-registration(adds no declared-breaking changeset),check-closing-keyword-parity— all exit 0.check:comment-mask-adoption,check:comment-mask-corpus(6377 files, 0 disagree),check:test-source-alias,check:cross-package-test-inputs,check:doc-authoring— all exit 0.Lint, as a declared narrowing rather than a repo sweep — three pieces, so it reads as a measurement and not as "not run":
eslint.config.mjs, whose own header states it "never enables type-aware linting (noparserOptions.project, no typed@typescript-eslintrules) for ANY file, test or not" (eslint.config.mjs:327-329).npx eslint --no-inline-config --format jsonover the two changed source files reportsfiles linted: 2 | total errors: 0 | total warnings: 0.importstatement — the only construct the activeno-restricted-importsguard reads.The repo-wide sweep stays CI's (
Lint & Repo Gates).node scripts/pm/dispatch-gates.mjs --ranreconciles this record as 75 derived, 11 run, 0 NOT-MEASURED, 64 UNRUN — the 64 are the declared narrowing, not an omission.dispatch-gatesalso reports this branch as a STALE TREE relative toorigin/main(68aee4c99at the time of writing); the two derivation inputs that moved (scripts/check-react-page-adapter-contract.mjs,scripts/cross-package-test-inputs.mjs) are unrelated to these paths. The merge queue rebuilds on currentmainregardless.Changeset
patchon@objectstack/spec, notskip-changeset— measured, not assumed.packages/specshipsdistin itsfiles[], and the new text lands there:grep -rl "pre-write body" packages/spec/dist/andgrep -rl "designate-only mode" packages/spec/dist/each returndist/data/index.d.tsanddist/data/index.d.mts(exit 0); positive control (the pre-existing sentence) hits the same two files; negative control returns exit 1 with no hits. ⇒ something published moves, so a changeset is owed.验收备注
Triage's acceptance criteria (comment
5578857427), each answered:ObjectTitleCompleteness.status, with the reachability caveat and the non-recoverability from either the served document or the stored row.derived/explicit的可达性 … ⛔ 不要只修explicit那一行" — done, but narrowed, see the note below.display-name.tsplus a test block;git diff --statshows 0 deletions in the source file.materializeServedObjectOnto,stripProvisionedPrimaryFrom,validateRecordTitle), ⛔ never by line number.objectstack-ai/cloudis outside this session's scope. Flagged to the PM in the structured report so the two sides do not each assume the other is handling it.content/docs/releases/" — held; not touched.derived-reachability half 必须一起做 (criterion 2, and "⭐ 你顺带提的那条,我判它应当一起做,⛔ 不是'值得考虑'"). This dispatch fenced it: "⛔ Do not act on thederivedmay be dead code at most call sites observation … it was measured in cloud, not here." Resolved by taking the intersection, on the reasoning that the fence's stated ground is a mechanism claim, and that mechanism is measurable here:derivedis dead atverify_buildorapply_edit; those names do not appear in the diff.packages/spec/src/system/constants/platform-tool-names.tsclassifiesapply_blueprint/create_object/apply_edit/verify_buildas@objectstack/service-ai-studio(cloud) tools, so their behaviour is not measurable from this repo — which is also why the docblock describes a pre-write body generically and names this repo's own authoring path (os build/os lintthrough@objectstack/lint'svalidateRecordTitle) instead of cloud's staging tools.a5eccf92in the card, and are ⛔ not restated anywhere as this repo's readings.Trim the
derivedsentence if the reviewer reads the fence more strictly than the intersection above; nothing else depends on it.Noted, not filed (observations, not defects — no repro, no contract violation, no metadata-authoring trap):
display-name.ts:3-27listsobjectTitleCompletenessas "a completeness classifier for lint/quality reporting" and makes no authorship claim, so it needed no correction.packages/lint/src/validate-record-title.ts:95, reads onlystatus === 'none', so it was never exposed to theexplicitmisreading. Its input is the authored config (os build/os lint), which is the pre-write side.Generated by Claude Code