Skip to content

chore(spec): re-anchor the closing twelve liveness ledgers to consuming symbols (#13003 batch 4) - #13044

Merged
os-trump merged 1 commit into
mainfrom
claude/issue-13003-anchor-adoption-b4
Aug 29, 2026
Merged

chore(spec): re-anchor the closing twelve liveness ledgers to consuming symbols (#13003 batch 4)#13044
os-trump merged 1 commit into
mainfrom
claude/issue-13003-anchor-adoption-b4

Conversation

@os-trump

Copy link
Copy Markdown
Collaborator

Part of #13003

Adoption batch 4 — the intended closing batch — of the path#symbol liveness citation grammar landed by #12516. Batches 1–3 are PRs #13035, #13038 and #13039, all merged; this branch takes the entire measured tail.

It does not close the card. The gate's own line-citation counter reads 2, not 0, so per ruling comment 5458356183 and the guard comments themselves the two non-vacuity floors stay in place, the two test files are untouched, and this body opens Part of rather than a closing keyword. The two survivors are not skips: both are entries whose live VERDICT is falsified, not their citation, and re-anchoring either one would have dressed a dead entry as freshly verified in order to hit a number. They are reported instead — see "The two held entries" below.

What landed

Eighty of the eighty-two remaining path:NNN citations, across twelve ledgers, become path#symbol, together with every path-only pointer in the same entries. Each was re-closed by READING the code on the tree at 8cb96ec41 — never by shifting a line, never by trusting byte-identity, never carried forward unread. verifiedAt: "2026-08-28" is stamped on every re-closed entry (the date the dispatch pinned and the date the re-closure was performed; the run crossed into 08-29 UTC during verification).

Census, from the gate's own counters

counter base 8cb96ec41 head 88a1b24e2
line citations written path:NNN 82 (82 in range) 2 (2 in range)
symbol anchors written path#symbol 383 (383 resolved) 499 (499 resolved)
repo-local evidence paths declared by live entries 464 (464 resolved) 467 (467 resolved)
key-mention (entry, cited file) pairs 464 asked / 463 anchored / 1 exempt 467 asked / 466 anchored / 1 exempt

Verdict lines at head, quoted from the gate itself:

line citations: 2 pointer(s) written `path:NNN`, 2 inside the cited file.
symbol anchors: 499 pointer(s) written `path#symbol`, 499 naming a symbol the cited file contains.
key-mention anchoring: 467 (entry, cited file) pair(s) asked, 466 anchored, 1 exempt.

What the re-closure found

67 of the 80 were already wrong, every one IN RANGE — so the existence check, the line bound and the key-mention check were all green on all 67. Six files were 100% rotted: validation.json 10/10, seed.json 6/6, view.json 5/5, dashboard.json 4/4, flow.json 4/4, qa.json 4/4.

The dominant shape is block drift — a whole ledger's pointers moving together when the file above them grows, so each one stays plausible beside its neighbours:

  • validation.json: nine of ten pointers had settled inside ONE 70-line ADR-0124 docblock, roughly 1,200 lines above evaluateValidationRules.
  • app.json: name / label / description had come to rest on three CONSECUTIVE lines of one unrelated function, resolveActionResultDialog.
  • capability.json: label / description / scope on three consecutive lines of one docblock about an outcome counter.
  • qa.json: all four moved by the same amount when a diagnostic helper was added at the head of runner.ts.

The sharpest single case is flow.status, which had come to rest on private flowLedgerDisabled = new Set() — the map ADJACENT to the one this key writes. #10243 had split the single flowEnabled map into flowStatusDisabled (this key) and flowLedgerDisabled (the durable toggle), composed by isFlowEnabled; the pointer landed on a plausible line, in the right class, naming the wrong fact.

Two silent classes close with the drift. Seventeen positions across four files were written as bare :NNN suffixes with no path in front of them — PATH_RE never matches those, so they degraded to prose that nothing has ever resolved, bounded or key-checked (translation.objects alone carried five of its six positions that way). Three more entries — hook.retryPolicy / timeout / onError — cited a path with NO line, which a line bound cannot falsify by construction. All twenty are repo-rooted anchors now.

Three citations were falsified in PROSE as well as position, and the prose is corrected with the line, because a name written beside a line is what a reader trusts when the line still looks plausible:

entry the prose said the tree says
view.object getViewsByObject() in packages/objectql/src/engine.ts it is in packages/metadata/src/metadata-manager.ts
field.requiredWhen fieldRequiresParentRoot no such symbol (0 occurrences); it is hasParentScopedRequiredWhen
translation.metadataForms translateMetaTypes no such symbol; it is translateMetaTypesResponse

mapping.json is the batch's counter-example and is kept as one: five of its seven pointers landed on the read or inside the statement performing it, because #11210 re-measured that file on 2026-08-23. The other two had already drifted five lines in five days. Correctness of a line is a property of the day it was measured; the anchor is what makes it a property of the code.

The two held entries

action.json / execute — the singleton batch 1 left standing

The dispatch asked why it survived batch 1 and for an honest disposition. It survived by decision, not by oversight, and batch 1 said so in its own commit message: its cited .transform lowering was removed in protocol 17 (#3855) and the key is now a retiredKey tombstone, so the live verdict — not the citation — is what is falsified. That is an ADR-0049 re-classification, which the dispatch method reports rather than executes. It was reported as #13036, which is open and awaiting triage.

Re-checked here on the current tree rather than taken on trust: packages/spec/src/ui/action.zod.ts declares execute: retiredKey('executewas removed in @objectstack/spec 17 — usetarget…'), the target docblock states the alias "was removed in protocol 17", and there is no .transform touching the key anywhere in the file. The suggested disposition remains dead, matching the already-tombstoned siblings action.shortcut / action.bulkEnabled with the row STAYING (the rls.priority precedent — retiredKey keeps the key in the walked shape). Not executed here.

Its citation is neither foreign-adjacent nor a producer pointer: packages/spec/src/ui/action.zod.ts:581 is a local evidence line, in range, in a file that names the key — which is exactly why nothing has ever flagged it.

field.json / conditionalRequired — the same class, found by this batch

Measuring the tail turned up a second instance of the identical shape, previously unrecorded. field.conditionalRequired is ledgered live on packages/spec/src/data/field.zod.ts:639 with the claim that "the FieldSchema .transform lowers conditionalRequired to requiredWhen and DROPS the alias". On the current tree:

  1. The key is a retiredKey tombstone (protocol 17, [P3] Retire the three deprecated aliases — via the ADR-0087 D2 conversion layer, not by deleting the keys #3855). requiredWhen's own describe() says it: "The only slot; the conditionalRequired alias was removed in protocol 17."
  2. The fallback the entry's note relies on — "objectql rule-validator.ts keeps its requiredWhen ?? conditionalRequired fallback on purpose" — is also gone. rule-validator.ts states "The ONLY predicate slot. The retired conditionalRequired alias…", and rule-validator.test.ts pins the absence: "does NOT read the retired conditionalRequired alias (PD Add comprehensive test suite for Zod schema validation #12 — no dialect fallback)".
  3. What remains is close-out machinery, not consumers: the ADR-0087 conversion field-conditionalRequired-to-requiredWhen, its migration-registry row, and an explicit "no entry for conditionalRequired" note in authoring-key-lint.ts.

Filed as #13043, a sub-issue of #13003, unassigned and bare for triage, on the #13036 shape. The entry here is byte-for-byte untouched and deliberately NOT re-stamped.

Consequence for the endgame, stated plainly: the line-citation counter cannot reach zero by citation work alone. Both survivors need an ADR-0049 verdict (#13036, #13043); once either entry stops being live, its evidence leaves the counted population, and when both do the counter is 0 and the floor deletion becomes due exactly as the guard comments instruct.

Per-entry tables

FROM shows the citation as it stood at the base commit and what that line is today, read straight off the file (angle brackets in quoted code are rendered as single guillemets, deliberately — see the byte discipline in AGENTS.md). ROT = the cited line (or range) did not cover a read of the key. OK = it did, so the row is a grammar migration rather than a repair. HELD = left untouched and reported.

action.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
execute action.zod.ts:581 — // the location table below. (House style set by the 'crypto.hash' / — (entry left untouched, verdict falsified — reported) HELD

app.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
name i18n-resolver.ts:478 — opts?: ResolveOptions, i18n-resolver.ts#translateApp ROT
label i18n-resolver.ts:481 — if (!spec) return spec; i18n-resolver.ts#lookupAppAttr · i18n-resolver.ts#translateApp ROT
description i18n-resolver.ts:482 — const title = lookupActionResultDialogText(bundle, action, (n) =› n.title, opt… i18n-resolver.ts#lookupAppAttr · i18n-resolver.ts#translateApp ROT
hidden account.app.ts:40 — hidden: true, account.app.ts#ACCOUNT_APP OK
navigation.label i18n-resolver.ts:456 — opts?: ResolveOptions, i18n-resolver.ts#lookupNavLabel · i18n-resolver.ts#translateApp ROT
navigation.requiredPermissions rest-server.ts:1844 — try { rest-server.ts#filterNav ROT
navigation.requiresService rest-server.ts:1832 — environmentId = await this.resolveRequestEnvironmentId(environmentId, req); rest-server.ts#filterNav ROT
requiredPermissions rest-server.ts:1814 — }; rest-server.ts#filterAppForUserWithReason ROT
_unpublished rest-server.ts:2669 — * Not translated and not cached, both deliberately: this is a diagnostic rest-server.ts#filterAppForUserWithReason · packages.ts#handlePackagesRequest ROT
_unpublished packages.ts:245 — const ec: any = _context?.executionContext; rest-server.ts#filterAppForUserWithReason · packages.ts#handlePackagesRequest ROT

capability.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
name bootstrap-declared-capabilities.ts:210 — /** bootstrap-declared-capabilities.ts#upsertPackageCapability · validate-capability-references.ts#validateCapabilityReferences ROT
name validate-capability-references.ts:99 — if (typeof cap.name === 'string' && cap.name.length › 0) known.add(cap.name); bootstrap-declared-capabilities.ts#upsertPackageCapability · validate-capability-references.ts#validateCapabilityReferences OK
label bootstrap-declared-capabilities.ts:122 — * identical; they are not. bootstrap-declared-capabilities.ts#capabilityRowFields ROT
description bootstrap-declared-capabilities.ts:123 — */ bootstrap-declared-capabilities.ts#capabilityRowFields ROT
scope bootstrap-declared-capabilities.ts:124 — unchanged: number; bootstrap-declared-capabilities.ts#capabilityRowFields ROT
packageId bootstrap-declared-capabilities.ts:294 — // bootstrap-declared-capabilities.ts#bootstrapDeclaredCapabilities · bootstrap-declared-capabilities.ts#upsertPackageCapability ROT

dashboard.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
widgets.requiresService rest-server.ts:1931 — // rest-server.ts#filterDashboardForUser · rest.test.ts#filterDashboardForUser ROT
widgets.requiresService rest.test.ts:3305 — it('ITEM level: nav entries the caller cannot satisfy are stripped from the se… rest-server.ts#filterDashboardForUser · rest.test.ts#filterDashboardForUser ROT
widgets.filterBindings validate-widget-bindings.ts:280 — } validate-widget-bindings.ts#effectiveFilterField ROT
widgets.suppressWarnings validate-widget-bindings.ts:367 — } validate-widget-bindings.ts#validateWidgetBindings ROT

field.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
requiredWhen rule-validator.ts:1810 — : 'requiredWhen for '${name}' failed to evaluate — skipped', rule-validator.ts#evaluateValidationRules · rule-validator.ts#hasParentScopedRequiredWhen OK
requiredWhen rule-validator.ts:763 — if (readsParentRoot(def.requiredWhen)) return true; rule-validator.ts#evaluateValidationRules · rule-validator.ts#hasParentScopedRequiredWhen OK
conditionalRequired field.zod.ts:639 — ])); — (entry left untouched, verdict falsified — reported) HELD
unique sql-driver.ts:1853 — return unsupportedFilterError( sql-driver.ts#registerManagedObjectMetadata · sql-driver.ts#uniqueIndexesFromFields ROT
reference engine.ts:1672 — ); engine.ts#cascadeDeleteRelations · engine.ts#planCascadeAtomicity · engine.ts#expandRelatedRecords ROT
autonumberFormat engine.ts:765 — // [#8690] Same door as the object branch, on the LOWERED condition — the engine.ts#applyAutonumbers ROT
maxLength record-validator.ts:127 — options?: string[]; record-validator.ts#validateOne ROT
minLength record-validator.ts:130 — export class ValidationError extends Error { record-validator.ts#validateOne ROT

flow.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
successMessage engine.ts:1292 — * ''runtime'' — a 'sys_metadata'/runtime-authored row with no real package engine.ts#executeWithoutRetry · engine.ts#resumeInternal ROT
errorMessage engine.ts:1348 — * ## ⛔ This is NOT the retired 'flowEnabled' map under a new name engine.ts#retryExecution · engine.ts#resumeInternal ROT
status engine.ts:1382 — private flowLedgerDisabled = new Set‹string›(); engine.ts#registerFlow · engine.ts#isFlowEnabled ROT
type engine.ts:637 — * Structured rather than a log line because the finding has two consumers with engine.ts#resolveTriggerBinding ROT

hook.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
name hook-binder.ts:182 — : 'no handler'; hook-binder.ts#bindHooksToEngine ROT
object hook-binder.ts:171 — } hook-binder.ts#bindHooksToEngine · hook-binder.ts#normalizeObjects ROT
events hook-binder.ts:171 — } hook-binder.ts#bindHooksToEngine ROT
handler hook-binder.ts:237 — } hook-binder.ts#resolveHandler · hook-binder.ts#bindHooksToEngine ROT
priority hook-binder.ts:177 — result.skipped += 1; hook-binder.ts#bindHooksToEngine ROT
retryPolicy [producer] hook-binder.ts:221 — const wrapped = wrapDeclarativeHook(hook, resolved, { logger, metrics: opts.me… hook-binder.ts#bindHooksToEngine OK
timeout [producer] hook-binder.ts:221 — const wrapped = wrapDeclarativeHook(hook, resolved, { logger, metrics: opts.me… hook-binder.ts#bindHooksToEngine OK
onError [producer] hook-binder.ts:221 — const wrapped = wrapDeclarativeHook(hook, resolved, { logger, metrics: opts.me… hook-binder.ts#bindHooksToEngine OK

mapping.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
name import-mapping.ts:70 — return { ok: false, status: 404, code: 'MAPPING_NOT_FOUND', error: 'No mapping… import-mapping.ts#resolveNamedMapping OK
sourceFormat import-mapping.ts:97 — return { import-mapping.ts#resolveNamedMapping OK
targetObject import-mapping.ts:75 — error: 'Mapping "${mappingName}" targets object "${artifact.targetObject}", no… import-mapping.ts#resolveNamedMapping OK
fieldMapping import-mapping.ts:102 — } import-mapping.ts#resolveNamedMapping · import-mapping.ts#applyMappingToRows OK
fieldMapping import-mapping.ts:164 — } import-mapping.ts#resolveNamedMapping · import-mapping.ts#applyMappingToRows OK
mode import-prepare.ts:322 — if (!resolved.ok) return resolved; import-prepare.ts#prepareImportRequest ROT
upsertKey import-prepare.ts:325 — // request writeMode/matchFields wins; absent ones fall back to the import-prepare.ts#prepareImportRequest ROT

qa.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
scenarios.id runner.ts:101 — stepName: step.name, runner.ts#runScenario ROT
scenarios.setup runner.ts:54 — } runner.ts#runScenario ROT
scenarios.steps runner.ts:83 — steps: [], runner.ts#runScenario ROT
scenarios.teardown runner.ts:98 — try { runner.ts#runScenario ROT

seed.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
object seed-loader.ts:98 — function quotableSeedFailureDetail(err: unknown): string | undefined { seed-loader.ts#loadDataset ROT
externalId seed-loader.ts:119 — * suggests. Both passes share this one vocabulary so the two halves of the fil… seed-loader.ts#loadDataset · seed-loader.ts#decideWriteAction ROT
mode seed-loader.ts:245 — export class SeedLoaderService implements ISeedLoaderService { seed-loader.ts#loadDataset · seed-loader.ts#decideWriteAction ROT
env [producer] seed-loader.ts:174 — /** seed-loader.ts#resolveEnvConfig ROT
env seed-loader.ts:191 — * A dataset carrying no 'env' at all is unrestricted — which is precisely what seed-loader.ts#datasetAllowsEnv · seed-loader.ts#filterByEnv ROT
records seed-loader.ts:434 — pairs.push({ field: fieldName, objectField: fieldDef.referenceVia }); seed-loader.ts#loadDataset ROT

translation.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
locale authored-translation-sync.ts:148 — ); authored-translation-sync.ts#readAuthoredTranslationLayer OK
objects i18n-resolver.ts:735 — widgets?: WidgetLike[]; i18n-resolver.ts#lookupObjectField · i18n-resolver.ts#lookupObjectFieldAttr · i18n-resolver.ts#resolveViewLabel · i18n-resolver.ts#lookupActionField · i18n-resolver.ts#lookupTabLabel ROT
apps i18n-resolver.ts:442 — action: ActionLike, i18n-resolver.ts#lookupAppAttr · i18n-resolver.ts#lookupNavLabel · rest-server.ts#translateMetaItem ROT
apps rest-server.ts:2001 — (blank line) i18n-resolver.ts#lookupAppAttr · i18n-resolver.ts#lookupNavLabel · rest-server.ts#translateMetaItem ROT
messages audit-writers.ts:579 — if (!milestones || !after || !before) return null; audit-writers.ts#writeAudit · audit-writers.ts#writeCommentMentions · audit-writers.ts#translateWith ROT
globalActions i18n-resolver.ts:200 — ): TranslationData | undefined { i18n-resolver.ts#lookupActionField · i18n-resolver.ts#lookupActionResultDialogNode ROT
dashboards i18n-resolver.ts:538 — * Apply the active locale to an action metadata document by overwriting i18n-resolver.ts#lookupDashboardAttr · i18n-resolver.ts#lookupWidgetAttr · i18n-resolver.ts#translateDashboard ROT
pages i18n-resolver.ts:636 — function lookupAppAttr( i18n-resolver.ts#lookupPageAttr · i18n-resolver.ts#lookupPageComponentCopy ROT
metadataForms rest-server.ts:2058 — * enforce" both survive unchanged. rest-server.ts#translateMetaTypesResponse · i18n-resolver.ts#lookupMetadataForm · i18n-resolver.ts#lookupMetadataFormField ROT

validation.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
name rule-validator.ts:665 — * the server; a lock that fails open leaves enforcement in the courtesy rule-validator.ts#evaluateValidationRules · rule-validator.ts#checkPredicate ROT
name rule-validator.ts:676 — * reaches a runtime this branch has to judge. rule-validator.ts#evaluateValidationRules · rule-validator.ts#checkPredicate ROT
active rule-validator.ts:647 — * ## Two faults, two answers (#4889) rule-validator.ts#evaluateValidationRules ROT
events rule-validator.ts:654 — * demonstrably locked; the historical (and deliberate, documented) policy i… rule-validator.ts#evaluateValidationRules ROT
priority rule-validator.ts:657 — * rule-validator.ts#evaluateValidationRules ROT
severity rule-validator.ts:678 — function isReadonlyWhenLocked( rule-validator.ts#evaluateValidationRules ROT
message rule-validator.ts:676 — * reaches a runtime this branch has to judge. rule-validator.ts#checkPredicate · rule-validator.ts#checkStateMachine · engine.ts#update ROT
message engine.ts:3703 — * the probe's own failure, the same disposition #8895 gave the rule-validator.ts#checkPredicate · rule-validator.ts#checkStateMachine · engine.ts#update ROT
type rule-validator.ts:706 — } rule-validator.ts#evaluateRule · rule-validator.ts#evaluateValidationRules ROT
condition rule-validator.ts:697 — logger?.warn?.( rule-validator.ts#checkPredicate ROT

view.json

entry FROM (citation at 8cb96ec — and what that line is TODAY) TO verdict
object engine.ts:1653 — if (excludeObjects === undefined) return; engine.ts#resolveMetadataItemName · metadata-manager.ts#getViewsByObject ROT
list.name view.zod.ts:1432 — }, { view.zod.ts#expandViewContainerWithDiagnostics ROT
form.sharing rest-server.ts:4384 — // are the same fact, resolved by the one reader that rest-server.ts#findPublicFormView · rest-server.ts#resolveFormBySlug ROT
listViews view.zod.ts:1432 — }, { view.zod.ts#expandViewContainerWithDiagnostics ROT
formViews rest-server.ts:4384 — // are the same fact, resolved by the one reader that rest-server.ts#findPublicFormView · view.zod.ts#expandViewContainerWithDiagnostics ROT

Ablation — one new anchor, both legs proven on disk

Mutated ONE anchor added by this batch, on the COMMITTED tree, with an EXIT INT TERM trap holding an absolute-path restore. No build sits between the edit and the reading: check:liveness runs from source over the JSON ledgers via tsx, so there is no dist/ for a stale artifact to hide in — stated rather than assumed.

Predicted direction before running: RED, naming validation/condition and the invented symbol.

=== HEAD blob hash for packages/spec/liveness/validation.json ===
HEAD_BLOB=8d5b57e229a0561b96e4ede5a18684db071d915b
BEFORE=8d5b57e229a0561b96e4ede5a18684db071d915b
=== pre-mutation grep counts (both directions) ===
occurrences of 'rule-validator.ts#checkPredicate ' : 3   (expect 3)
occurrences of 'checkPredicateXQZ'                  : 0   (expect 0)
=== MUTATE ===
=== post-mutation grep counts (both directions) ===
occurrences of 'rule-validator.ts#checkPredicate ' : 2   (was 3, expect 2)
occurrences of 'checkPredicateXQZ'                  : 1   (was 0, expect 1)
AFTER=6c9306b9aa095983df680c0c4b5d7cdb492481c8
MUTATION CONFIRMED ON DISK
=== RUN GATE (mutated tree) ===
MUTATED_EXIT=1
symbol anchors: 499 pointer(s) written `path#symbol`, 498 naming a symbol the cited file contains, 1 UNRESOLVED.
✗ 1 anchored citation(s) name a symbol the cited file does not contain:
    validation/condition → packages/objectql/src/validation/rule-validator.ts#checkPredicateXQZ
=== RESTORE ===
RESTORED=8d5b57e229a0561b96e4ede5a18684db071d915b
git diff HEAD --name-only: []
after restore — 'rule-validator.ts#checkPredicate ' : 3  (expect 3)
after restore — 'checkPredicateXQZ'                  : 0  (expect 0)
RESTORE CONFIRMED (blob equals HEAD, git diff HEAD empty)
=== RUN GATE (restored tree) ===
RESTORED_EXIT=0
line citations: 2 pointer(s) written `path:NNN`, 2 inside the cited file.
symbol anchors: 499 pointer(s) written `path#symbol`, 499 naming a symbol the cited file contains.

Observed direction matched the prediction. The mutation is proven on disk by BOTH grep directions plus a changed blob hash (hash-object compared against the HEAD blob, an empty hash treated as failure); the restore is proven by blob equality with HEAD plus an empty git diff HEAD, not by the exit code of git checkout. Restore is git checkout HEAD -- ABSOLUTE_PATH, never the bare form, so a polluted index cannot hand back the mutation.

Gates

Family derived at the committed head with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, no paths passed — 13 paths vs merge base 8cb96ec41, 25 families matched, no convention trigger fired (this diff adds and edits no test file, no gate script, no root-program TypeScript, no error code). Exit codes captured by redirect-then-read, before any pipe.

25 green, plus pnpm check:nul-bytes run on top of the derivation:

check:changeset-gate-self-tests · @objectstack/spec check:empty-state · @objectstack/spec check:liveness · check:merge-driver · check:objectui-changeset · check:page-declaration-shape · check:pm-half-states (the self-test, 1551 cases) · check:published-files · check:slot-lookup · @objectstack/spec check:strictness-ledger · check:test-source-alias · check:type-source-resolution · @objectstack/spec check:variant-docs · check-adr-0087-registration.mjs · check-changeset-no-major.mjs · check-comment-mask-adoption.mjs · check-dev-prereqs.mjs · check-empty-changeset.mjs · check-keyed-text-bounds.mjs · check-plugin-teardown-shape.mjs · check-undeclared-dep-imports.mjs · docs-audit/check-affected-docs.mjs · docs-audit/check-drift-comment.mjs · pm/release-rehearsal-clone.mjs --self-test · check:nul-bytes.

check-dev-prereqs.mjs was EXIT=1 on the unbuilt worktree ("67 of 67 workspace packages declare an entry point under dist/ that is not on disk"), which is a container prerequisite and NOT a reading. It was converted into a real one by building the closure the way lint.yml does — pnpm exec turbo run build --filter='./packages/*' --filter='./packages/*/*' --concurrency=2, 70/70 tasks successful in 6m34s under the shared verify lock — after which it prints ✓ 67 package build artifacts present and ✓ @objectstack/spec built from the sources on disk, EXIT=0.

One NOT MEASURED remains: node scripts/pm/check-half-states.mjs, EXIT=3 — PREREQUISITE NOT MET; the container has no valid GitHub credential and the gate says so itself: "Nothing was swept … It is not a clean board and it is not a dirty one — it is no reading at all." Standing across all four batches.

check:nul-bytes verdict line: check-nul-bytes: OK (scanned 7246 text file(s) -- 7246 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes). Plus a hand scan of the twelve edited ledgers for the C0 class plus DEL: clean.

Every gate above ran at 88a1b24e2 with git diff HEAD empty, so the tree measured is the tree committed. No typecheck leg is owed: this diff contains no TypeScript.

Honest residual

Not in this PR

No status flip, no re-classification, no ledger row added or removed, no changes to check-liveness.mts, evidence.mts or any scan behaviour, no test file touched, and the two non-vacuity floors left exactly as ruling 5458356183 set them. .changeset/liveness-symbol-anchors-batch-4.md is a patch on @objectstack/specliveness/ is named in that package's files array, so these ledgers ship in the npm tarball and this is published data.

Clause ②: not reachable — ledgers plus one changeset, outside packages/spec/src/**, no accept-set or public-surface movement.

Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4


Generated by Claude Code

…ng symbols (#13003 batch 4)

Adoption batch 4 -- the closing batch -- of the #12516 symbol-anchor citation
grammar, file-disjoint from batches 1-3 except for the one action.json entry
batch 1 deliberately left standing.

Eighty of the eighty-two remaining `path:NNN` citations across
liveness/app.json, validation.json, translation.json, field.json, hook.json,
mapping.json, capability.json, seed.json, view.json, dashboard.json, flow.json
and qa.json become `path#symbol`, together with every path-only pointer in the
same entries -- each re-closed by reading the code on the current tree rather
than by shifting a line. The gate's own counters move 82 -> 2 line citations
and 383 -> 499 symbol anchors.

Sixty-seven of the eighty were already wrong, every one IN RANGE and so
invisible to the existence check, the line bound and the key-mention check.
Six files were 100% rotted (validation 10/10, seed 6/6, view 5/5, dashboard
4/4, flow 4/4, qa 4/4). The dominant shape is BLOCK drift: nine of
validation.json's ten pointers had settled inside ONE 70-line ADR-0124
docblock ~1,200 lines above evaluateValidationRules, and app.json's
name/label/description onto three consecutive lines of one unrelated action
resolver. flow.status is the sharpest case -- it had come to rest on
`flowLedgerDisabled`, the map ADJACENT to the one this key writes, after
#10243 split the single flowEnabled map in two.

Two silent classes close with them: seventeen positions across four files were
bare `:NNN` suffixes with no path, which PATH_RE never matches, so nothing had
ever resolved, bounded or key-checked them (translation.objects alone carried
five); three entries (hook retryPolicy/timeout/onError) cited a path with no
line, unfalsifiable by a line bound by construction.

Three citations were falsified in PROSE as well as position: view.object
credited getViewsByObject() to packages/objectql/src/engine.ts (it is in
packages/metadata), field.requiredWhen named fieldRequiresParentRoot (no such
symbol; it is hasParentScopedRequiredWhen), and translation.metadataForms
named translateMetaTypes (it is translateMetaTypesResponse).

Nothing is re-classified. Two entries stay byte-for-byte untouched and NOT
re-stamped because their `live` VERDICT, not their citation, is what is
falsified -- action.execute (reported as #13036) and field.conditionalRequired
(reported as #13043), both retiredKey tombstones since protocol 17 whose cited
`.transform` lowering no longer exists. Those are ADR-0049 re-classifications
with their own card shape, so the line-citation counter reads 2 rather than 0
and the two non-vacuity floors stay in place.

Part of #13003

Claude-Session: https://claude.ai/code/session_01LpRNHxWZgSUgVnFT9mQQo4

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling labels Aug 29, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 12 changed file(s) yielded no anchor (packages/spec/liveness/app.json, packages/spec/liveness/capability.json, packages/spec/liveness/dashboard.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 12 changed file(s) yielded no anchor (packages/spec/liveness/app.json, packages/spec/liveness/capability.json, packages/spec/liveness/dashboard.json, …) — pages documenting those are invisible to this run
  • 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 — 126 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 8cb96ec41b34d987ca713cd94174af2fa45c955bpackageMentionDocs.

@os-trump
os-trump marked this pull request as ready for review August 29, 2026 00:39
@os-trump
os-trump enabled auto-merge August 29, 2026 00:39
@os-trump
os-trump added this pull request to the merge queue Aug 29, 2026
Merged via the queue into main with commit 8f10a79 Aug 29, 2026
35 checks passed
@os-trump
os-trump deleted the claude/issue-13003-anchor-adoption-b4 branch August 29, 2026 01:02
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/m tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants