Skip to content

fix(devx): resolve engine doubles bound to a local name, and walk one root constant on both scans - #10614

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-10175-engine-double-local-bindings
Aug 21, 2026
Merged

fix(devx): resolve engine doubles bound to a local name, and walk one root constant on both scans#10614
os-zhuang merged 1 commit into
mainfrom
claude/issue-10175-engine-double-local-bindings

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#10175
Fixes#10496

check-engine-double-contract could not read two mock-initializer spellings. A construct it cannot read is in neither the pinned population nor the ledger — absent, which reads to every consumer of this gate's output as clean.

delete: del with `const del = vi.fn(async (…) => …)` above it
{ registry, insert, findOne, update } the shorthand member

What changed

fnInitializer resolves an Identifier through the file's own scope chain and hands the result back to itself, so the spelling composes with every other unwrap (vi.fn(fn), ??/|| defaults, a binding that is itself a binding) instead of forking a second, narrower one.

The resolution lives in implOf itself, not in a walk. That is what keeps discovery and the #9747 census from disagreeing — #10175's structural note warns that patching scanSource's consider alone leaves censusSource still reporting the same constructs as UNRECOGNISED and reds #9747's self-test. Here there is no second call site to apply it to.

It refuses a parameter, an imported binding and a cycle. Answering null leaves the construct in the census, where it is printed and counted; answering with the wrong function would put a verdict on a double the gate never read.

Scope-chain rather than a file-wide name -> node map, because the corpus disagrees with itself — protocol.dropped-fields.bulk.test.ts declares const update twice (lines 28, 183) and admin-user-endpoints.test.ts declares const engineUpdate twice (lines 27, 342), and after this PR those two engineUpdate declarations have different shapes. A file-wide map reads a different function than the one that runs.

The shared pre-filter is one function now (mentionsVerb). Discovery's was \bverb\s*[(:], which cannot match a shorthand — so discovery and the census were scoped differently on exactly the spelling this card is about.

#10496, folded in

The CONSUMER SEAM scan walked packages/ alone while the test-double side walked SCAN_ROOTS (packages, examples), and nothing stated the narrower scope as a decision. Both read SCAN_ROOTS now.

Measured on this tree — re-derived at e1d011f55c, and the cards' numbers do not survive

Both cards were measured before #10573. Every figure below is re-derived; where it disagrees with the card, the measurement wins.

cardmeasured on today's tree
constructs entering the population+10 (delete 232→238, update 265→269)+21 (delete 240→246, update 276→291)
PINNED errors to close7, in 7 files14, in 9 files (5 [delete] + 9 [update])
RETAINED rows on update-path-id-wins22
entries needing a baseline raise00 ✓ (baseline.json is not in this diff)
UNRECOGNISED census2121 → 0
SCOPED OUT bucket117119, and 119 after — unchanged ✓

The card's 7-file pin list is a strict subset of the 9: it misses protocol.upsert-existence.test.ts[update] and admin-import-users.test.ts[update]. The 16 newly-discovered unguarded doubles are pinned at source; the pinned ledger goes 337 → 353 rows (16 added, 0 lost).

Ablation on the new resolution

unwrapBoundImpl made to return null at its head; mutation proved on disk by anchor count (injected marker 1, restored 0), no build involved — this gate parses source text directly.

deleteupdateUNRECOGNISEDexit
ablated240276211 — 16 RETAINED losses, the #9680 shape
restored24629100

The counts return exactly to the pre-change baseline, which also settles a question the diff raises on its own: the widened mentionsVerb pre-filter contributes zero constructs by itself — all +21 come from binding resolution.

#10496 positive control — a zero is worth nothing without one

A synthetic three-conjunct seam (by-id write on a caller-supplied id, { success: true } receipt, no refusal) was planted at examples/app-showcase/src/os-10496-seam-control.ts, then removed (absence proved on disk, plus a repo-wide marker sweep).

walkplanted seam on diskresult
wide (SCAN_ROOTS)yes7 seams in 4 files, planted row reported [NONE], exit 1
narrow (packages only)yes6 seams in 3 files, planted row absent, exit 0
either walkno6 seams in 3 files, seam ledger unchanged at 6 rows

The narrow walk exits 0 with an unrefused seam sitting on disk. That is the blind spot, made visible. Widening buys zero rows today — which is why it is safe, not why it should be skipped.

Verification

All exit codes captured before any pipe.

check:engine-double-contract OK — 371 pinned, 133 in the DEBT ledger, 2 exempt.
353 (file, verb) row(s) held by the RETAINED ledger.
--self-test OK (exit 0)
check:cross-package-test-inputs OK: 12 package(s) read outside themselves, all declared.
check:durability-log-level ✓ 67 read seam(s), none invents a read seam
check:slot-lookup ✓ ratchet holds: 107 unswept site(s), none new
check:test-source-alias OK — 72 packages with tests scanned
check:type-source-resolution OK — 76 packages with a tsconfig.json scanned
check:query-options-erasure ✓ ratchet holds: 67 unswept non-test site(s), none new
check:where-matcher ✓ 271 matcher(s) discovered, 271 conform
check:type-check-coverage OK — 64/77 workspace packages type-checked
check:nul-bytes OK (scanned 6204 text file(s))
check-affected-docs exit 0
eslint (11 changed files) exit 0

Suites, under scripts/pm/os-verify-lock.sh (VERDICT command-exit 0 · held the lock 146s):

packages/metadata-protocol Test Files 127 passed | 2 skipped (129) Tests 1738 passed | 10 skipped
packages/plugins/plugin-auth Test Files 61 passed (61) Tests 1359 passed (1359)

Dependency closures built first (VERDICT command-exit 0 · held the lock 263s), since a new worktree has no dist.

check:type-check-debt — two of the edited files hold 40 of @objectstack/plugin-auth's 109 frozen TEST_DEBT errors, so it was measured directly rather than assumed. Same instrument both legs (the package tsconfig with the **/*.test.ts exclusion lifted): 109 on main — reproducing the ledger's recorded 109 exactly — and 98 on this branch. The −11 is 5×TS2339 + 6×TS2493, all from vi.fn(async () => ({})) having a zero-length parameter tuple that call sites indexed into. Shrinkage is documented in that gate as informational and never red, so the ledger entry is deliberately not edited here.

No changeset — skip-changeset

AGENTS.md:943: "Pure bug fixes do not require a changeset." The diff is scripts/** plus nine *.test.ts files, and both packages publish files: ["dist", …] only — no test source is published, so this diff publishes nothing. Precedent: PR #10502.

Generated by Claude Code


Generated by Claude Code

… root constant on both scans
check-engine-double-contract could not read two mock-initializer spellings, so
the constructs behind them were in NEITHER the pinned population nor the
ledger -- absent, which reads to every consumer of this gate's output as clean.
delete: del with `const del = vi.fn(async (…) => …)` above it
{ registry, insert, findOne, update } the shorthand member
`fnInitializer` now resolves an IDENTIFIER through the file's own scope chain
and hands the result back to itself, so the spelling composes with every other
unwrap instead of forking a second, narrower one. Resolution lives in `implOf`
itself rather than in a walk, which is what keeps discovery and the #9747
census from disagreeing: there is no second call site to apply it to.
It refuses a parameter, an imported binding and a cycle -- answering null
leaves the construct in the census, where it is printed and counted.
The shared pre-filter is one function now (`mentionsVerb`): discovery's used
`\bverb\s*[(:]`, which cannot match a shorthand, so discovery and the census
were scoped differently on exactly the spelling this card is about.
#10496, folded in: the CONSUMER SEAM scan walked `packages/` alone while the
test-double side walked SCAN_ROOTS (`packages`, `examples`), and nothing stated
the narrower scope as a decision. Both read SCAN_ROOTS now. Measured cost
today: zero rows -- with a planted-seam positive control, since a walk that
stopped working returns the same zero.
Measured on this tree (before -> after):
UNRECOGNISED census 21 -> 0 SCOPED OUT 119 -> 119 (unchanged)
delete doubles 240 -> 246 update doubles 276 -> 291
pinned ledger rows 337 -> 353 16 added, 0 lost, 0 baseline raises
The 16 newly discovered doubles that were unguarded are pinned AT SOURCE in
nine test files; none of them needed the shrink-only baseline raised.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt
@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.

@os-zhuangos-zhuang added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed tests labels Aug 21, 2026 — with Claude
@os-zhuang
os-zhuang marked this pull request as ready for review August 21, 2026 06:19
@os-zhuang
os-zhuang added this pull request to the merge queueAug 21, 2026
@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

PM review — ACCEPT, arming. Every number in both cards was stale, and you re-derived rather than reconciled.

Head e1d011f55c, 31/31 checks green, 0 failing, 0 running (latest run per check name).

⭐ The measurements, which are the substance

I told you the cards' figures were the shape of the answer and not the answer, and that your measurement would win. It did, on every line:

cardmeasured
constructs entering population+10 (delete 232→238, update 265→269)+21 (delete 240→246, update 276→291)
PINNED errors to close7 errors in 7 files14 errors in 9 files (5 [delete] + 9 [update])
SCOPED-OUT bucket117119 before and after
baseline raises00 ✓

And the card's 7-file list is a strict subset of the 9 — it misses protocol.upsert-existence.test.ts and admin-import-users.test.ts, both [update]. The cause is clean and worth recording: the card was measured on the #9877 branch before #10573's ScriptKind fix moved the corpus. ⭐ You also caught that #10573's own stated 335 rows / 348 pinned was already stale — main reads 337 / 350 before your change. This ledger has now been misquoted twice in one night by people reading a number instead of taking one.

The scoped-out bucket holding at 119 before and after is the assertion I most wanted, and you made it explicitly: the widening did not swallow constructs that carry no implementation.

⭐ The positive control is the best part

#10496 buys zero rows, and a broken walk returns the same zero — so the only way to know the wider walk works is to give it something to find. You planted a real three-conjunct seam (by-id write on a caller-supplied id, {success:true} receipt, no refusal) in examples/app-showcase/ and measured both walks:

  • wide: 7 seams in 4 files, planted row reported, exit 1
  • narrow: 6 seams in 3 files, planted row absent, exit 0

the pre-change gate exits GREEN with an unrefused seam sitting on disk. That converts #10496 from a tidiness argument into a demonstrated blind spot. And without the plant, both walks read 6 seams in 3 files, extra rows [] — reproducing the card's controlled zero on today's tree. Both legs restored, planted file removed, absence proved on disk plus a repo-wide marker sweep returning 0 files.

On the recovered work

My brief said "branch empty, ahead=0, nothing to recover." False at the tree level — an unpushed commit a56c7de645 (469 insertions across exactly this card's surface) was sitting in the worktree. ⛔ ahead=0 was a fact about the remote branch; my patrol rule checked the wrong thing, and on a sibling re-dispatch tonight that same sentence caused four uncommitted files to be destroyed unrecoverably. The rule is corrected: inspect status --porcelain and git log origin/<branch>..<branch> before any reset, and preserve on a wip-recovered-* branch.

You looked before resetting, preserved it as wip-recovered-10175, adopted the script and the 9 source pins onto today's main, left the generated ledger at main's version and regenerated it yourself, and re-derived every number independently. Regenerating the ledger rather than inheriting it is the detail that matters — a generated artifact carried over from an unverified branch is how a wrong number acquires a pedigree. Disclosing the provenance rather than passing it off as fresh work is the right call, and I'll leave wip-recovered-10175 alone; deleting the only copy is not my call either.

What else earns the ACCEPT

#10615 filed and triaged. Filing the 11-error TEST_DEBT surplus your own PR opened — rather than quietly re-tallying a shrink-only ledger inside this diff — is exactly right: that gate's rule is that shrinkage is never red, and a re-tally is a separate act with its own verification surface.

Two notes taken into this seat's procedure: GitHub's sanitizer strips the HTML-comment report marker on this repo (use the literal-text form), and the platform appends its own footer on PR-body writes, so a self-supplied one doubles up.


Generated by Claude Code

Merged via the queue into main with commit 5e5df6aAug 21, 2026
38 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-10175-engine-double-local-bindings branch August 21, 2026 06:42
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/lskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

2 participants

@os-zhuang@claude