tooling(vocabulary-gate): census the lower-case-LOCAL object-literal blind spot — CENSUSED-AND-EMPTY - #13789
Merged
os-project-manager merged 1 commit intoAug 31, 2026
Conversation
…blind spot `OBJECT_LITERAL_CODE_HELPER_BLINDNESS` declared the object-literal twin of the `assignconst` local as UNCLOSED and UNCENSUSED. It is still unclosed; it is no longer uncensused. Measured on this tree through the gate's own primitives, with the `objlithelper` branch replicated and one line changed (fall back to `resolveConstant` where it declines a non-parameter identifier, exactly as the assignment position does): 125 candidate positions - 36 already objlitconst's = 89 newly reached 0 of the 89 reduce => 0 verdict rows owed => 0 unregistered wire codes the card's exact class (a lower-case local holding a reducible literal set): 0 So the widening would be PREVENTIVE, not a live defect. The position is populated - 24 value-position candidates feed a `code:` through a local or a destructured binding - but every one holds a RUNTIME value, which is the declared #9460 bound rather than a hole. What is empty is the REDUCIBLE subclass. The other 65 are not value stamps at all: the candidate regex reads `code: Ident`, and in a type literal that Ident is a type name. No shape is widened and no grammar changes; the reconciliation this gate runs in both directions is untouched (51 sites before and after). `--self-test` gains 8 assertions that keep the reading re-checkable instead of leaving five numbers behind. They fail in opposite directions: one set asserts the HOLE is still open (no site AND no unresolved for the card's exact shape), so closing it forces a re-census rather than a silent edit of the figures; the other is the POSITIVE CONTROL for the zero - `resolveConstant` still reduces that same local to both branches - because a corpus zero and a dead-instrument zero are the same number. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
This was referenced Aug 31, 2026
os-project-manager
marked this pull request as ready for review
August 31, 2026 13:31
os-project-manager
deleted the
claude/issue-13478-objlit-local-code-census
branch
August 31, 2026 13:52
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of #13478 — and deliberately not a closing reference. The triage ruling pre-decided both endings, and the census came back on the preventive side, whose ending is close as not planned, not close as completed. That disposition is the PM's to apply on the card; this PR only lands the reading it turns on.
Scope
SHAPESstill has 9 members and the gate still derives 51 sites — before and after, unchanged. The gate's reconciliation runs in both directions, so a widening cannot land before its declaration rows and the rows cannot land before the widening; that lock is exactly why the measurement had to come first, and why the measurement is all that is here.The question
OBJECT_LITERAL_CODE_HELPER_BLINDNESSdeclared a remainder as UNCLOSED and UNCENSUSED: acode:in an object literal fed by a lower-case LOCAL — the twin of theassignconstlocal closed for the assignment position.objlithelperdeclines it (the identifier is not a parameter),objlitconstneeds SCREAMING_SNAKE,objlitneeds a quote. So: no site and no unresolved — the one failure this gate cannot notice by itself.It is still unclosed. It is no longer uncensused.
The reading
objlitconstdispatcher-error-vocabulary.ts⇒ preventive, not a live defect. The escalation branch (unregistered wire codes above zero) is not reached.
The position is populated: 24 value-position candidates feed a
code:through a local or a destructured binding. Every one holds a runtime value, which is the declared #9460 bound rather than a hole. What is empty is the reducible subclass — not one local in this tree holds the ternary-or-chain of literals the widening would newly admit.Entries reducing to nothing — counted as their own class, and classified
Each would be a red with no verdict available unless it can be classified, so each is classified. The four sum to 89:
code: string/number/undefined— not a value stamp; the candidate regex readscode: Ident, and in a type literal that Ident is a type namecode: FieldErrorCode/ErrorCode/FlowRefusalCode/StandardErrorCode/PreflightViolationCodeconst code = e?.body?.code ?? 'INTERNAL_ERROR'and kin — the declared #9460 boundconst { code } = resolveThrownHttpError(...)— nothing for the resolver to anchor onThat 65 of the 89 are not value stamps at all is itself a finding about cost: a widening here would carry materially lower precision than the assignment position's did.
How it was measured
The instrument is the gate's own primitives (
SHAPES,enclosingOpeners,helperCodesFor,resolveConstant), with theobjlithelperbranch replicated and exactly one line changed — where it declines a non-parameter identifier, the replica falls back toresolveConstant, as the assignment position'scodehelperbranch already does. Two instrument checks, because a zero from a broken instrument and a zero from a clean corpus are the same number:deriveSitesobjlithelperoutput exactly: 29 sites, 5 unresolved, identical after the dedup the gate itself applies.The control earned its keep: its first run failed, and the failure was real signal —
resolveConstantresolves a name file-scoped, so two same-named locals in one file collapse tonulleven when each is individually reducible. That is a precision bound of the fallback, now pinned as its own control rather than mistaken for an instrument fault.Verifying the ruling's mechanical premise directly rather than from the brief:
resolveConstant('c', ...)on the card's own shape returns["NOT_CREATABLE","NOT_OVERRIDABLE"]. The machinery is ready; the corpus is what is empty.What lands
The census is recorded beside the constant it answers —
localTwinCensus— flipping the declaration from UNCENSUSED to CENSUSED-AND-EMPTY, and the figures are printed in the gate's own bounds block so a partial gate cannot read as a complete one. Nothing inpackages/is touched; the declaration ledger is what this diff is measured against, never a fix site.--self-testgains 8 assertions (217 to 225 on this tree; still 9 shapes) so the reading stays re-checkable instead of five numbers going stale in a comment. They fail in opposite directions:resolveConstantstill reduces that same local to both branches. This is the positive control for the zero: a broken resolver would also report "nothing reduces", and without this half "censused and empty" is indistinguishable from "measured with a dead instrument".Plus three coherence pins: the classes sum to the newly-reached count, the candidate split adds up, and
reduce == 0forces both decision rows to zero.The pins were ablated, not assumed
Each mutation was proved on disk before the run and each restore proved by blob hash against
HEADplus an emptygit diff HEAD— never by an exit code.objlithelperfall back toresolveConstant)literalCodeValuesnewlyReached89 to 88)The first attempt at ablation 1 named a variable that does not exist in that scope and died with a
ReferenceErrorbefore reaching any assertion. That was recorded as a broken instrument and re-run correctly, not read as a red.Verification
Gate family derived after the final commit with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack(commite2c2b3441, 1 path). Every exit code below was captured before any pipe.GREEN (exit 0):
check:dispatcher-error-vocabulary(runs--self-testthen the gate) ·check:agent-test-spelling·check:bash32-floor·check:cli-command-ids·check:cross-package-test-inputs·check:entry-guard·check:parse-guard·check:pnpm-filter-targets·check:watch-hint-literal·check-ci-filter-parity.mjs·check-cross-package-test-inputs.mjs·check-shard-attestation.mjs·check:nul-bytes·check-self-test-wired.mjsConvention-triggered by editing a gate script, both GREEN:
scripts/pm/bare-root-worklist.mjs --self-test·check:pm-dispatch-gates.NOT MEASURED:
check-test-completeness.mjs— exit 3, the gate's own distinct code for "no savedturbo run testlog to read". Its failure text states this is not a finding and there is nothing to fix; recorded as not measured rather than as a red.Repo-wide
pnpm lint(eslint . --no-inline-config) run in full rather than narrowed: exit 0, 105s.pnpm --filter @objectstack/spec exec vitest run src/api/error-code-ledger.test.ts: 18/18 pass — the only file in the tree naming this script.No changeset:
scripts/publishes from no package. Confirmed against whatCheck Changesetactually does rather than assumed — it counts added.changeset/*.mdagainst the merge base with no path exemption, soskip-changesetis the mechanism and the label is applied.Found while censusing, filed rather than fixed
A sibling spelling reached by no shape: an inline literal ternary at the stamp itself,
code: cond ? 'A' : 'B', as opposed to a local holding one.objlitneeds the quote immediately,objlitconstneeds SCREAMING_SNAKE,objlithelpercaptures the condition's identifier and declines. Measured with its own positive control: 2 positions in the tree, 5 distinct values, and 0 unregistered wire codes — one is three ledger-registered codes at the REST door, the other a D6 field-addressed catalog entry ({ field: 'name', code: ... }) that is legitimately not a wire code. Preventive, like this card, and out of scope here.Generated by Claude Code