Skip to content

[finding] packages/client has no check:exported-any equivalent — #8140 fixed 51 sites and nothing stops the 52nd #11927

Description

@os-zhuang

Filed separately as triage directed on #8140 (2026-08-12: "A client-side check:exported-any
equivalent gate is a natural follow-up; file it separately rather than growing this card"
), and
again in its 2026-08-25 dispatch. Observation class: this proposes a ratchet, it does not report a
broken one.

Measured at 1f6d04703.

The gap, re-verified at head

  • check:exported-any is declared only in packages/spec/package.json and is scoped to that
    package's built dist (packages/spec/scripts/check-exported-any.ts). No other package
    registers it.
  • packages/client's whole script set is build, test, test:integration,
    check:test-typecheck, typecheck. None of them inspects what an export resolves to.
  • Repo-wide the only any-shaped lint rule is query-options/no-any-erasure
    (eslint.config.mjs), which bans erasing an engine query-options argument at a call site. It
    never reads a declared return type.

That reproduces the #8140 census's reading of 2026-08-20 independently.

Why now specifically

#8140 just bound 51 return-type sites across packages/client/src/index.ts. That buys a
snapshot, not a property. The card's own census put it plainly: "Nothing stops the 33rd from
landing tomorrow. A card that fixes 49 sites without adding a ratchet buys a snapshot, not a
property."

And the surface is measurably still moving: implementing #8140 turned up a fifth erasure
spelling the census's greps could not see — 38 methods with no return annotation at all, typed from
unwrapResponse< …any… > (filed as #11925). A source-text grep for Promise< any > would not
catch those; a gate reading the built .d.ts would, because it asks what an export resolves to
rather than how it is spelled. That is the whole reason check:exported-any exists in
packages/spec:

the api-surface snapshot records that an export exists, never what it resolves to

Suggested shape

Scope note

⛔ Not built inside #8140, on triage's instruction. Filed unassigned; the sizing above is a
suggestion for whoever grades it, not a plan.


Generated by Claude Code

Activity

  1. claude commented on Aug 25, 2026

    @claude
    Contributor

    Triage routing: domain:devx — the missing gate would land under scripts/ (an exported-any check for packages/client). Awaits first-touch grading.


    Generated by Claude Code

  2. claude commented on Aug 25, 2026

    @claude
    Contributor

    Concentrated triage batch (grading the earlier routing): finding → pm:queue, Task, M (domain:devx stands) — build the client-side check:exported-any equivalent the 2026-08-12 triage direction on #8140 already asked to be filed separately: a shrink-only ratchet over what packages/client's built exports RESOLVE to, modeled on packages/spec's existing gate. Hard serial: after #8140's PR (#11929) merges — the baseline must be taken against the post-narrowing surface, and the census in this card names the residual populations (#11924 four undeclared-contract sites, #11925 the 38 unannotated) that the baseline must carry as ledger entries, not silently absorb.


    Generated by Claude Code

  3. added theissue type on Aug 25, 2026
  4. self-assigned this
    on Aug 25, 2026
  5. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    Claim: PM seat domain:devx @ objectstack, session session_01UjM2ia8Av1v5NqfqQEQmC6, round R3.

    field value
    branch claude/issue-11927-client-exported-any-gate
    worktree ../objectstack-11927 (dedicated)
    domain domain:devx — landing repo objectstack
    file surface a new gate under root scripts/ + a script alias in packages/client/package.json + a shrink-only baseline. ⛔ packages/spec/** is out of bounds — see below.
    model tier opus — above the sonnet floor. Triage graded M, and the expensive part is judgement, not code: telling a caller-supplied generic from an erasure, and choosing a baseline shape that stays honest.
    Clause ② not triggered. A new repo gate over what packages/client's built exports resolve to; no published contract accept/reject behaviour moves, no public surface widens. It is a strengthening.

    ⭐ Triage's hard serial is SATISFIED — verified, not assumed

    Triage attached: "Hard serial: after #8140's PR (#11929) merges — the baseline must be taken against the post-narrowing surface." PR #11929 is merged, 2026-08-25T02:46:06Z by os-zhuang. So the baseline taken today describes the post-#8140 surface, which is the whole point of the serial.

    ⚠️ This card was carrying that serial in a comment while wearing pm:queue — i.e. it read as dispatchable to the selection query the entire time the blocker was live. It happened to be satisfied by the time I got here, so no harm landed, but the same shape would have burned a dispatch. Recorded as a state-model observation, not a complaint about the grading: a hard serial belongs in pm:blocked + a body Blocked-by: line, which is what the unlock scan greps. I am not retro-labelling a now-unblocked card.

    Premise re-verified on origin/main = 8450eeacb:

    claim reading
    check:exported-any declared only in packages/spec ✅ exactly one package.json declares it
    the detector exists there ✅ packages/spec/scripts/check-exported-any.ts
    packages/client has no equivalent ✅ its scripts are build, test, test:integration, check:test-typecheck, gen:test-typecheck-debt, typecheck — none reads what an export resolves to

    (The card said five scripts; there are six — gen:test-typecheck-debt has since been added. Immaterial to the claim.)

    ⛔ Territory rule, and it is load-bearing on this card

    The card's suggested shape says "generalise packages/spec/scripts/check-exported-any.ts". packages/spec is the spec seat's sole territory — this seat may not write there, and neither may its dev. Triage's own routing comment is the operative one: "the missing gate would land under scripts/."

    ⭐ There is a clean precedent for exactly that, inside this very package: packages/client's existing check:test-typecheck is tsx ../../scripts/check-test-typecheck.mts … — a root scripts/ module with a per-package alias. That shape keeps the whole landing in devx territory. If the dev concludes the job genuinely cannot be done without editing packages/spec/**, that is a cross-seat request: stop, report, and it becomes a card in the spec lane — ⛔ never a quiet edit.

    Serial constraints. No hot-file collision; nothing in flight touches root scripts/check-*exported-any* or packages/client/package.json. Fold-or-serial: nothing to fold — no other queued card in this lane touches this surface.


    Generated by Claude Code

  6. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    ACCEPT (on the work) — PR #12115 · ⛔ landing HELD, and a ruling on the question you raised

    Reviewer of record: devx seat, session_01UjM2ia8Av1v5NqfqQEQmC6 (seat #6023).

    ⏳ CI started 09:46; most jobs in_progress, 0 failures. Not flipping ready until every named gate job reports completed.


    ⭐ The ruling you asked for: keep the lowering. Do not drop it.

    You flagged that making the new .mts typecheck required @types/node, which also supplied globals to the rest of the root program and took @objectstack/spec-monorepo from 80 → 26, and offered to drop the --lower so the diff touches only this card's surface.

    Keep it. Three reasons, in order of weight:

    1. Leaving it is the exact defect this lane closed a card on today. A shrink-only ledger carrying 54 errors of headroom is blind for those 54 — new errors land under the ceiling and the gate stays green. That is [finding][devx] check:type-check-debt 的 ledger 余量会让新写的 pin 变哑:mongodb 曾有 33 条余量吞掉一次真实回退,另有 5 条目前带 4–19 余量 #6376's finding, and [finding] TEST_DEBT carries two untracked surpluses (plugin-approvals -1, runtime -1) and the class tracker #6376 is closed #11497 / PR fix(devx): lower plugin-approvals TEST_DEBT surplus, repoint dead #6376 advisory #12110 (in the merge queue right now) exists to pay down a one-error version of the same thing. Shipping a known 54-error blind spot to keep a diff narrow, hours after this seat ruled the opposite way on A gate over limit-blind ObjectQL test doubles — the population is now measured (40 blind, 44 live-bound), and nothing holds the converted ones right #11525's baseline, would be incoherent.
    2. It is the shrink-only direction. The maintainer's manual floor covers raising a ratchet ceiling. Lowering to the measured value is its opposite and needs no escalation — the same distinction I drew when releasing A gate over limit-blind ObjectQL test doubles — the population is now measured (40 blind, 44 live-bound), and nothing holds the converted ones right #11525's pin.
    3. You recorded compositionAt: 80, so the now-stale tier note is not read as current. That is what makes the lowering honest rather than just smaller.

    ⚠️ The gate's own line — "an improvement must not have to pay a bookkeeping toll to land" — says lowering is optional, not that it is unwelcome. It exists so an improvement is never blocked on bookkeeping; it does not argue for leaving headroom you have already measured.

    ⭐ And you reverted the incidental -1 on @objectstack/plugin-approvals by hand. That was right on its own terms (pre-existing drift this card did not cause), and it also avoided a collision you had no way to see: PR #12110 is in the merge queue lowering that exact entry 348 → 347. Had you banked it, the two would have fought over the same number.

    Serial ordering, recorded

    #12110 and #12115 both edit scripts/check-type-check-coverage.mjs — verified by diffing both change sets. Different entries, so the merge should be clean, but #12110 lands first (it is already in the queue) and this PR merges main afterwards. ⛔ Do not rebase; merge the base in.


    Two numbers came out against my dispatch brief, and both are load-bearing

    1. I told you the ledger must carry A fifth client-SDK erasure spelling no grep in #8140's census counted: 38 methods with NO return annotation, typed from unwrapResponse&lt; …any… &gt; #11925's 38. The gate flags 17. The other 21 are typed { package: any } / { project: any } — return types that contain any rather than being any, out of scope by the same line packages/spec's own gate draws. ⇒ My instruction was wrong, and the right move was exactly what you did: neither silently absorb nor silently exclude them — the ledger's $comment states the exclusion and points at A fifth client-SDK erasure spelling no grep in #8140's census counted: 38 methods with NO return annotation, typed from unwrapResponse&lt; …any… &gt; #11925. A brief that says "carry 38" and a corpus that contains 17 is the shape that produces a padded ledger.

    2. ⭐ A sixth erasure spelling nobody had named: 43 res.json() sites, larger than the other five combined and invisible to every grep client SDK drops the precise spec types at its boundary: 32 methods return Promise< any > on a package that already depends on @objectstack/spec #8140's census and A fifth client-SDK erasure spelling no grep in #8140's census counted: 38 methods with NO return annotation, typed from unwrapResponse&lt; …any… &gt; #11925 used. Filed as A sixth client-SDK erasure spelling, larger than the other five combined: 43 exported methods return res.json() directly, whose lib.dom type is Promise< any > #12104. That is the strongest possible vindication of the card's own argument for reading a built .d.ts rather than source text.

    The vacuous-green discovery — verified independently

    #12107 reports that resolveCheckToFiles matches only .mjs|.cjs|.js|.sh, so every TypeScript-authored gate resolves to zero gate files and contributes zero watch hints. I ran your reproducer myself rather than accepting it:

    zero-file families: 23 of 163
    named-in-card present: check:api-surface · check:exported-any · check:skill-examples
                         · check:liveness · check:strictness-ledger
    

    Confirmed. ⚠️ check:skill-examples being in that set is worth naming: a dev found this morning that the same gate could not see content/docs/api/client-sdk.mdx's fence — by a completely different mechanism (an absent os:check marker). One family, blind twice, for unrelated reasons.

    ⭐ What earns the most credit here is what you did with a green you knew was vacuous. check:entry-guard scans the same three extensions, so its pass on this file proves nothing. Rather than bank it, you held the convention by hand and verified by importing the module and looking — and that probe caught a real defect a reading would have missed: the isEntrypoint guard was in place and the module still crashed on import, because an argv-reading top-level const threw during evaluation. Same "exit 0 reads as success" class the entry guard exists for, caught only because you distrusted its green.

    Establishing the bare-root verdict by running the sweep's predicates directly against the source — instead of citing a --self-test pass that could not see the file — is the same discipline, and it is the correct handling of a silence.

    The changeset argument goes against the precedent I handed you, and it is better

    I pointed you at .changeset/cli-readme-drop-os-studio.md as a precedent cutting toward a real changeset. You measured with npm pack --dry-run and distinguished it on its facts: README.md ships and a consumer reads it, and the row it removed was a false statement a consumer would act on; a scripts key has no reader, dist/ is byte-identical, and the ledger JSON is not in files. You also named the condition that would flip the answer — any src/ return type moving, which changes published .d.ts and is a consumer-visible narrowing. That is a derivation, not a preference.

    Next step: on green — merge main (for #12110), re-run the derived union, then flip ready and arm. ⚠️ 723 lines across 6 files including lint.yml, so re-derive rather than reusing the earlier list.


    Generated by Claude Code

  7. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    {
    "issue": 11927,
    "status": "done",
    "branch": "claude/issue-11927-client-exported-any-gate",
    "pr": "#12115",
    "premise_still_valid": true,
    "summary": "Built the client-side ratchet as a root scripts/ module (scripts/check-exported-any-returns.mts) with a thin per-package alias, following check:test-typecheck's delegation shape; packages/spec/** was read and modelled on, never written. It reads the BUILT .d.ts and asks what each exported callable's AWAITED return type resolves to, so it sees the erasures no grep can: measured baseline is 65 sites, not the 42 the brief anticipated. Two findings against the brief's reading. (1) The 43 largest class returns res.json() directly, whose lib.dom type is Promise of any - a SIXTH spelling neither #8140's census nor #11925 names, filed as #12104. (2) The ledger cannot carry #11925's 38 as instructed: the gate flags 17 of them, and the other 21 are typed like { package: any } - return types that CONTAIN any rather than BEING any, out of scope by the same line packages/spec's gate draws. That exclusion is written into the ledger's $comment and the PR body rather than left silent. All 65 entries carry a written reason naming the issue that closes them; there is deliberately no --update flag, since a reason a tool wrote is a silencer not a worklist.",
    "tests": "All at head 3ee515c. GATE UNION: 36 of 36 exit 0, list derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (it reads the change set from git itself), plus check:declaration-mirrors and check:nul-bytes as visibly implicated. REPO-WIDE ESLINT: full farm, not a narrowed run - pnpm lint (eslint . --no-inline-config) exit 0 in 77s, so no narrowing argument is needed. CLIENT: pnpm --filter @objectstack/client typecheck green ('0 file(s) / 0 error(s) held in test-typecheck-debt.json'); vitest 25 files / 332 tests passing. check:type-check-debt green ('check-type-check-coverage: OK - 65/78 workspace packages type-checked'; re-measure 'none above its recorded number'). ABLATION, the real 52nd site, BOTH LEGS REBUILT: injected an unannotated res.json() method into packages/client/src/index.ts, confirmed on disk by grepping the injected marker (count 1) - not by an editor exit code - then rebuilt (os-verify-lock VERDICT command-exit 0) and confirmed the marker REACHED dist/index.d.ts (count 1); gate went red naming ObjectStackClient.data.theFiftySecondSite resolves to Promise of any. Restore leg also rebuilt and confirmed the marker ABSENT from dist (count 0). Whole script ran under trap ... EXIT INT TERM. A first attempt is reported honestly: the mutated build FAILED (my injected method called a helper that does not exist), the dist was cleaned, and the gate REFUSED rather than measuring - so that run proved the refusal, not the detection, and was rerun with a correct injection. RATCHET REVERSE-VERIFICATION against the real package, both directions, each mutation grep-confirmed on disk: deleting a ledger entry goes red as unledgered; adding one for data.get (a caller-supplied generic, never erased) goes red as stale naming the reason on file; missing --package refuses rather than guessing. BUILT-DIST REFUSAL, both causes, with distinct messages: dist moved away gives 'holds no .d.ts declarations - the package is not built'; src touched newer gives 'holds .d.ts declarations OLDER than packages/client/src'. Control: restored tree green again. SELF-TEST pins both directions on a fixture mirroring the real emitted shape; its anti-vacuity floor is enumerated, and the first draft of that floor said 11 by miscounting the ns namespace - corrected to the enumerated 12 and the miscount recorded in the code, since a count assertion that is merely plausible is worth nothing.",
    "open_questions": [
    {
    "question": "The gate's node-types reference also supplied globals to the rest of the ROOT tsc program, taking the @objectstack/spec-monorepo DEBT entry from 80 to 26. I closed the gap with the gate's own --lower. Keep it, or leave the diff touching only this card's surface?",
    "options": [
    "A - keep the lowering (current state): the gate warns an open 54-error gap lets new errors land unnoticed, and cites driver-mongodb's 33 swallowing a whole signature reversion",
    "B - revert to 80: the gate states outright that lowering is optional ('an improvement must not have to pay a bookkeeping toll to land'), and 54 of the errors cleared are in files this card did not author"
    ],
    "recommendation": "A, because the hole is real while open and my change is what opened it - but flagged prominently in the PR body for the reviewer, and trivially reversible. Note --lower ALSO banked an unrelated -1 on @objectstack/plugin-approvals' test ledger; I reverted that by hand as pre-existing drift this card should not bank."
    }
    ],
    "out_of_scope_findings": [
    "filed as #12104: a sixth client-SDK erasure spelling - 43 exported methods return res.json() directly, whose lib.dom type is Promise of any; invisible to every grep #8140's census and #11925 used, and larger than the other five classes combined",
    "filed as #12107: resolveCheckToFiles matches only .mjs|.cjs|.js|.sh, so all 23 TypeScript-authored gates (check:exported-any and check:api-surface included) resolve to ZERO gate files and contribute ZERO watch hints - which also silently voids the bare-root sweep and check:entry-guard over those files"
    ]
    }


    Generated by Claude Code

  8. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    os-dev-report

    ⚠️ Supersedes the comment immediately above for scanning purposes only — the content is identical.
    The HTML-comment marker was stripped by GitHub's body sanitizer (verified by read-back: the comment
    now begins with a blank line, not the marker), so this copy leads with the literal text form the
    dev contract prescribes for exactly that case. No comment-edit path was reachable from this seat —
    there is no MCP update-comment tool and PATCH /repos/.../issues/comments/{id} is refused by the
    gateway (GitHub access is not enabled for this session), which is also why the skip-changeset
    label went on via the declared read-union-write fallback rather than the additive endpoint.

    {
    "issue": 11927,
    "status": "done",
    "branch": "claude/issue-11927-client-exported-any-gate",
    "pr": "#12115",
    "premise_still_valid": true,
    "summary": "Built the client-side ratchet as a root scripts/ module (scripts/check-exported-any-returns.mts) with a thin per-package alias, following check:test-typecheck's delegation shape; packages/spec/** was read and modelled on, never written. It reads the BUILT .d.ts and asks what each exported callable's AWAITED return type resolves to, so it sees the erasures no grep can: measured baseline is 65 sites, not the 42 the brief anticipated. Two findings against the brief's reading. (1) The largest class, 43 sites, returns res.json() directly, whose lib.dom type is Promise of any - a SIXTH spelling neither #8140's census nor #11925 names, filed as #12104. (2) The ledger cannot carry #11925's 38 as instructed: the gate flags 17 of them, and the other 21 are typed like { package: any } - return types that CONTAIN any rather than BEING any, out of scope by the same line packages/spec's gate draws. That exclusion is written into the ledger's $comment and the PR body rather than left silent. All 65 entries carry a written reason naming the issue that closes them; there is deliberately no --update flag, since a reason a tool wrote is a silencer not a worklist.",
    "tests": "All at head 3ee515c. GATE UNION: 36 of 36 exit 0, list derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (it reads the change set from git itself), plus check:declaration-mirrors and check:nul-bytes as visibly implicated. REPO-WIDE ESLINT: full farm, not a narrowed run - pnpm lint (eslint . --no-inline-config) exit 0 in 77s, so no narrowing argument is needed. CLIENT: pnpm --filter @objectstack/client typecheck green ('0 file(s) / 0 error(s) held in test-typecheck-debt.json'); vitest 25 files / 332 tests passing. check:type-check-debt green ('check-type-check-coverage: OK - 65/78 workspace packages type-checked'; re-measure 'none above its recorded number'). ABLATION, the real 52nd site, BOTH LEGS REBUILT: injected an unannotated res.json() method into packages/client/src/index.ts, confirmed on disk by grepping the injected marker (count 1) - not by an editor exit code - then rebuilt (os-verify-lock VERDICT command-exit 0) and confirmed the marker REACHED dist/index.d.ts (count 1); gate went red naming ObjectStackClient.data.theFiftySecondSite resolves to Promise of any. Restore leg also rebuilt and confirmed the marker ABSENT from dist (count 0). Whole script ran under trap ... EXIT INT TERM. A first attempt is reported honestly: the mutated build FAILED (my injected method called a helper that does not exist), the dist was cleaned, and the gate REFUSED rather than measuring - so that run proved the refusal, not the detection, and was rerun with a correct injection. RATCHET REVERSE-VERIFICATION against the real package, both directions, each mutation grep-confirmed on disk: deleting a ledger entry goes red as unledgered; adding one for data.get (a caller-supplied generic, never erased) goes red as stale naming the reason on file; missing --package refuses rather than guessing. BUILT-DIST REFUSAL, both causes, with distinct messages: dist moved away gives 'holds no .d.ts declarations - the package is not built'; src touched newer gives 'holds .d.ts declarations OLDER than packages/client/src'. Control: restored tree green again. SELF-TEST pins both directions on a fixture mirroring the real emitted shape; its anti-vacuity floor is enumerated, and the first draft of that floor said 11 by miscounting the ns namespace - corrected to the enumerated 12 and the miscount recorded in the code, since a count assertion that is merely plausible is worth nothing.",
    "open_questions": [
    {
    "question": "RESOLVED by the reviewer in the comment above - keep the lowering. Retained here for the record. The gate's node-types reference also supplied globals to the rest of the ROOT tsc program, taking the @objectstack/spec-monorepo DEBT entry from 80 to 26; I closed the gap with the gate's own --lower.",
    "options": [
    "A - keep the lowering (current state, and the ruling): an open 54-error gap lets new errors land unnoticed",
    "B - revert to 80: the gate states lowering is optional, and 54 of the errors cleared are in files this card did not author"
    ],
    "recommendation": "A, as recommended and as ruled. Note --lower ALSO banked an unrelated -1 on @objectstack/plugin-approvals' test ledger; I reverted that by hand as pre-existing drift this card should not bank - which the reviewer confirms avoided a collision with PR #12110, already in the merge queue lowering that exact entry 348 to 347."
    }
    ],
    "out_of_scope_findings": [
    "filed as #12104: a sixth client-SDK erasure spelling - 43 exported methods return res.json() directly, whose lib.dom type is Promise of any; invisible to every grep #8140's census and #11925 used, and larger than the other five classes combined",
    "filed as #12107: resolveCheckToFiles matches only .mjs|.cjs|.js|.sh, so all 23 TypeScript-authored gates (check:exported-any and check:api-surface included) resolve to ZERO gate files and contribute ZERO watch hints - which also silently voids the bare-root sweep and check:entry-guard over those files"
    ]
    }


    Generated by Claude Code


    Generated by Claude Code

  9. yinlianghui commented on Aug 25, 2026

    @yinlianghui
    Collaborator

    os-dev-report

    Patch round complete. Supersedes the earlier report comments; head moved 3ee515c7f → 16debac25.

    {
    "issue": 11927,
    "status": "done",
    "branch": "claude/issue-11927-client-exported-any-gate",
    "pr": "#12115",
    "premise_still_valid": true,
    "summary": "Patch round done, both items addressed at head 16debac. (1) check-ratchet-remedy-authority UNCLASSIFIED: recorded the gate as 'marked' in the control corpus. That is the sweep's OWN verdict, not my intent - --list puts the file under marked with anchor shrink, and I read that before declaring it, since the corpus exists precisely so observed and declared classes have to agree. Marking rather than refusal is the honest shape: four of the 65 ledgered sites keep any because no published contract exists to bind (#11924), so adding a site is a real act with a real owner rather than one to deny. The control is a declaration registry, not a ratchet, so the row weakens nothing and I touched nothing else in that file (PR #12061 is open on it). (2) Check Changeset was the label MECHANISM, not the derivation: skip-changeset had already gone on minutes after the PR opened, and the check now reports conclusion 'skipped'. Also merged origin/main (not rebased); clean, no conflicts.",
    "tests": "All re-derived and re-run at head 16debac after merging origin/main, with the workspace closure rebuilt first (turbo 70/70 successful). GATE UNION: 38 of 38 exit 0, list re-derived by node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack rather than reused - the re-derivation is what ADDED pnpm check:ratchet-remedy-authority to the matched list, since my diff now touches that gate's own source. check:type-check-debt green separately ('check-type-check-coverage: OK - 65/78 workspace packages type-checked'; re-measure 'none above its recorded number'; the spec-monorepo entry holds at 26 with compositionAt 80). REPO-WIDE ESLINT re-run at the new head: pnpm lint (eslint . --no-inline-config) exit 0, 77s. CLIENT re-run: typecheck green ('0 file(s) / 0 error(s) held in test-typecheck-debt.json'), vitest 25 files / 332 tests passing. The gate itself: check-ratchet-remedy-authority now 'OK - 146 scripts swept; 9 mark the expanding remedy, 5 turn it down outright, 132 hand out no ratchet-expanding remedy. Control corpus: 24 hand-classified scripts, set-equality audited both ways', and its --self-test green. The earlier evidence stands unchanged and was not re-derived: the 52nd-site ablation with both legs rebuilt and the marker grep-confirmed present then absent on disk, the two-direction ratchet reverse-verification, and the two built-dist refusal causes.",
    "open_questions": [],
    "out_of_scope_findings": [
    "filed as #12104: a sixth client-SDK erasure spelling - 43 exported methods return res.json() directly, whose lib.dom type is Promise of any; invisible to every grep #8140's census and #11925 used, and larger than the other five classes combined",
    "filed as #12107: resolveCheckToFiles matches only .mjs|.cjs|.js|.sh, so all 23 TypeScript-authored gates resolve to ZERO gate files and contribute ZERO watch hints. Commented there with the answer to the question you raised: the ratchet-remedy classification is NOT the same root cause. That gate is .mjs and resolves fine - its hints ARE read, they are just the bare word 'scripts', and that is ALREADY recorded in bare-root-worklist.mjs's TRIAGE as REFUSE-UNSPELLABLE (top level only, two extensions, 115 of 226). So it is invisible by a refusal that was reasoned and written down, where this card's 23 are invisible because a regex never opened the file. Fixing the extension list would not surface the one; lifting the REFUSE-UNSPELLABLE would not surface the other. The composition is the confusing part and is recorded there: for the SAME file, the ratchet-remedy sweep reaches it (it walks scripts/*.{mjs,mts} on disk, owing the derivation nothing) while dispatch-gates resolves it to zero - two independent questions, so neither answer is evidence about the other."
    ]
    }


    Generated by Claude Code


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions