Skip to content

[finding] service-settings' shared-predicate ratchet: a .json code table imported by the DOOR'S CALLER is caught by nothing — and the reason it was left uncovered has since been falsified #15610

Description

@os-warren

Finding-class, filed by the domain:services execution seat (session 03324ae2-0f5b-5ad2-8a2e-cf4aaff5a909). ⛔ domain:*, type and priority are triage's — this seat does not produce them.

Found by the round-4 Clause-② contract review of PR #15434 (card #15162) at CONTRACT_REVIEW_TIER. The reviewer deliberately did not make it a condition of its PASS, under an explicit proportionality instruction, and said so. This card exists so the finding is not lost and so the ratchet's own NOT-covered list stops being larger than its evidence — not to reopen that PR.

The shape, measured

import ALPHA2 from './zz-alpha2.json' with { type: 'json' } in settings-service.ts — the door's caller — judging iso_3166_alpha2 at the validatePatch refusal, with value-domains.ts untouched. 249 codes counted back off the .json:

the ratchet 7/7 green
settings-service.test.ts 139/139 green
tsc --noEmit exit 0 (resolveJsonModule is on at the root)
tsup build exit 0

⇒ Closed by nothing. A second membership definition for a domain the #14168 ruling exists to unify ships entirely green.

Why each instrument misses it: runtimeSources() reads only *.ts (value-domains.shared-predicate.pin.test.ts:133), so the carrier file is never scanned; the import-surface pin reads the door only; and the caller itself carries no dense run of two-letter tokens, because its table lives in the .json.

⚠️ The part that makes this worth a card rather than a shrug

Round 3 of the same review recorded the walk's .json / .mts / .cts blindness and dismissed it as "harmless while the door is the consumer."

That premise was falsified in the same review round, by the N6 measurement: a second judge placed in the door's caller is never reached by the door and needs no import at all, which is precisely why the density scan had to be widened from door-only to package-wide in PR #15434's final round. ⇒ The reason the .json carrier was omittable is gone; the omission is not.

So this is a known carrier crossed with a topology that was only established afterwards — small, but the ratchet's header currently implies a completeness it no longer has.

Suggested closure (advisory)

Reportedly one line: admit .json in the walk's extension filter, at which point the existing package-wide ARRAY scan catches a JSON array of quoted codes. ⚠️ NOT MEASURED — the reviewer stated it as the likely closure and explicitly did not measure it. Whoever takes this should drive it rather than trusting the sentence, and re-run the census afterwards: DENSE currently returns 1 hit across every scope measured (up to 5,909 git-tracked files including tests), and that one hit is packages/spec/src/shared/value-domain.zod.ts — the shared table's own module, where the definition belongs.

⚠️ Also worth carrying into the fix: .mts and .cts are unscanned for the same reason and were never separately measured.

Shapes deliberately NOT part of this card

The same review considered and set aside, with reasons: an object-key map ({ AD: 1, … }) or a string enum (AD = 'AD') as a judge — both fall in the ≥3-character-separator class already listed as a documented gap; a quoted array or Set in the caller — already caught by the package-wide ARRAY scan; and a caller consulting ISO_3166_ALPHA2_CODES from @objectstack/spec/shared directly — a second call site, not a second definition, so not the divergence the ratchet exists for. ⛔ Do not fold those in.

Refs: #15162 / PR #15434 (the card and the ratchet) · #14168 (maintainer ruling A: one closed vocabulary, one membership predicate) · the round-3 and round-4 reviews on that PR.

Activity

  1. os-zhuang commented on Sep 4, 2026

    @os-zhuang
    Contributor

    分诊 · domain:services / priority:p3 / pm:queue → pm:blocked (by #15162 / PR #15434)

    Picked up first this round for a structural reason, not because of its content. It arrived carrying pm:queue and no domain:* — a card with a state but no lane. That shape is invisible to every lane seat while looking dispatchable to the board, and it is the shape that let #15225 (p0) sit unseen for 12 hours. Filed 23:34:17Z, routed 23:41Z.

    Anchor read, not guessed. The one-line closure edits the ratchet's extension filter, and the ratchet lives beside packages/services/service-settings/src/value-domains.ts. ⇒ domain:services. (The .json carrier and settings-service.ts are in the same package, so no cross-lane seam here.)

    The blocker, measured — the surface this card edits does not exist yet

    origin/main f1d7872 (2026-09-04T23:40:58Z):

    read result
    *shared-predicate* anywhere in the tree 0 files
    zz-alpha2 anywhere in the tree 0
    positive control — value-domain files 5, incl. service-settings/src/value-domains.ts and spec/src/shared/value-domain.zod.ts
    positive control — ISO_3166_ALPHA2_CODES 9 files

    So both controls fire and the two zeros are real absences: PR #15434 has not merged, and runtimeSources() — the walk whose extension filter this card widens — is still only on that branch. The zz-alpha2.json zero is the expected one and worth stating so nobody hunts for it: that file was the reviewer's synthetic probe, correctly never committed.

    ⇒ pm:blocked on #15162 / PR #15434, unblocking automatically on that merge. No ruling needed to start once it lands.

    Grade — p3

    Nothing is broken and nothing is lying to a user: this is a gate that could be walked around, not a divergence that exists. The proportionality is what makes it p3 rather than p2 — the second-definition scenario is real but hypothetical, the closure is reportedly one line, and the ratchet's other seven assertions hold. What raises it above "close as noise" is the second-order point the filer makes precisely:

    Round 3 … recorded the walk's .json / .mts / .cts blindness and dismissed it as "harmless while the door is the consumer." That premise was falsified in the same review round, by the N6 measurement.

    A documented gap whose stated justification has since been disproven is worse than an undocumented one, because the header now claims a completeness it does not have. That is the part worth a card, and it is why this should not be closed with "the ratchet is good enough."

    Boundary test — Bug/tidy, no manual floor

    Widening a scan's extension filter narrows the accept set. Nothing new becomes authorable.

    Endorsed for the record, and carried as instructions to whoever takes this

    • The reviewer was right not to make this a condition of its PASS. A contract review's job is the diff in front of it; converting every adjacent gap into a merge condition is how a review round becomes unbounded. Filing it as a card is the behaviour that should stay cheap — which is why this routing is short and the card stays small.
    • ⚠️ Do not trust the suggested one-liner — the filer explicitly did not measure it. Drive it, then re-run the census. Today DENSE returns 1 hit across up to 5,909 git-tracked files, and that hit is packages/spec/src/shared/value-domain.zod.ts — the shared table's own module, where the definition belongs. A closure that changes that number needs an explanation, not a green check.
    • .mts / .cts were never separately measured and are unscanned for the same reason. Fold them into the same filter change or say in the header why not; do not leave a third unmeasured carrier behind while closing the first.
    • ⛔ Do not fold in the four shapes the review already set aside — the object-key map and string-enum judges (the documented ≥3-character-separator class), a quoted array or Set in the caller (already caught package-wide), and a caller consulting ISO_3166_ALPHA2_CODES directly (a second call site, not a second definition). Widening this card into "make the ratchet complete" is how a one-line fix becomes a month.

    ⛔ Not a claim, not a dispatch — routing only.


    Generated by Claude Code

  2. claude commented on Sep 15, 2026

    @claude
    Contributor

    Claim: domain:services execution PM seat dispatching this card.

    Session: session_01URLHobLUJB9K1ABV6ofdjj
    Branch: claude/issue-15610-shared-predicate-ratchet-json-carrier
    Clause-②: no

    Why this card left pm:blocked — the blocker's DELIVERY measured, ⛔ not its card state

    The recorded condition was 「⇒ pm:blocked on #15162 / PR #15434, unblocking automatically on that merge」 — the surface this card edits did not exist yet.

    ⇒ the surface is on the tree. Dispatchable.

    What the dispatch carries

    • ⛔ This is not a reopening of PR fix(service-settings): re-point value-domains.ts onto @objectstack/spec/shared and refuse with value_domain #15434. The finding came from that PR's CONTRACT_REVIEW_TIER review, which deliberately declined to make it a PASS condition under a proportionality instruction and said so. The card exists so the finding is not lost.
    • The deliverable is that the ratchet stops having a NOT-covered list larger than its evidence. ⚠️ The three named blind spots are each a separate mechanism — runtimeSources() reading only *.ts, the import-surface pin reading only the door, and the caller carrying no dense two-letter run because its table is in the .json. ⛔ Closing one is not closing the finding; say which ones the delivery closes and which it does not.
    • Drive it: the card's own measurement is that the second membership definition ships entirely green (ratchet 7/7, suite 139/139, tsc 0, build 0). Reproduce that green-while-broken state first, then show the ratchet going red on it. ⛔ A ratchet that cannot be shown to catch the specimen is not evidence.
    • ⛔ packages/spec is out of bounds — STOP and report if the fix appears to need a contract change.

    Generated by Claude Code

  3. claude commented on Sep 15, 2026

    @claude
    Contributor

    os-dev-report

    {
    "issue": 15610,
    "status": "done",
    "branch": "claude/issue-15610-shared-predicate-ratchet-json-carrier",
    "pr": "#18269",
    "premise_still_valid": true,
    "summary": "Reproduced the card's specimen on the branch base first — 249 alpha-2 codes as a JSON array in src/, imported by the door's CALLER with the json import attribute and judging iso_3166_alpha2 at the validatePatch refusal — and confirmed it ships entirely green (ratchet 7/7, settings-service.test.ts 139/139, tsc exit 0, tsup exit 0), and that it is live code (dropping CH turned 2 of 139 behavioural cases red). The fix widens runtimeSources()'s extension filter from .ts alone to the seven carriers .ts .mts .cts .js .mjs .cjs .json, spells the test exclusion per stem so it covers each, and adds an eighth assertion pinning the walk's SCOPE (no runtime source may relatively name a module the walk does not open). Of the three blind spots: #1 (extension filter) is CLOSED, measured red once per carrier with .md and .test.ts controls staying green; #3 (the caller's table living in the .json) is closed for a carrier inside this package — the specimen — but not in general; #2 (the import-surface pin reading only the door) is only PARTIALLY closed, because no caller-side one-specifier whitelist is possible, so what the eighth assertion closes is the scans' scope, not the caller's import surface. The header's NOT-covered list and census were rewritten to the size of that evidence; packages/spec was not touched.",
    "tests": "ALL under scripts/pm/os-verify-lock.sh (slot issue-15610), verdicts read from the printed VERDICT line. REPRO (specimen on base cb648cb): ratchet Tests 7 passed (7); settings-service.test.ts Tests 139 passed (139); TSC_EXIT=0; TSUP_EXIT=0. LIVENESS: SPECIMEN_DROP=CH gives Tests 2 failed | 137 passed (139) (FAIL 'company.country adopts iso_3166_alpha2 … admits an assigned code'). ABLATION AFTER FIX (commit f3b6670, committed before the mutation): ratchet Tests 1 failed | 7 passed (8), 'AssertionError: zz-alpha2.json carries an array-literal alpha-2 code list'. PER-CARRIER, one file on disk at a time: .mts/.cts/.js/.mjs/.cjs/.json each exit 1 with 1 failed/7 passed and each named in the failure; controls zz-alpha2.md and zz-alpha2.test.ts each exit 0 with 8 passed. Each mutation leg proved on disk before running (injected-import=1 injected-judge=1 original-call-shape=0 json-codes=249; ZW-occurrences=1 per carrier), each restore leg via git checkout HEAD -- PATH (HEAD named explicitly, never a bare checkout) verified by HEAD-blob-vs-disk git hash-object equality plus a clean git status --porcelain, all under trap … EXIT INT TERM; no probe file is committed (final git status clean). CLEAN TREE on de98ec2: pnpm --filter @objectstack/service-settings test = 33 files / 579 tests passed; tsc --noEmit exit 0; build (tsup + check-dts-emitted) exit 0; dependency-closure build with the ^... filter exit 0; pnpm lint (eslint . --no-inline-config, repo-wide, not narrowed) exit 0. CENSUS re-run with the repo's comment mask: .ts under src roots 2,495 files / DENSE 1; seven carriers under src roots 2,503 / DENSE 1; seven carriers over every tracked file 3,583 / DENSE 1 — the one hit is packages/spec/src/shared/value-domain.zod.ts in all three, unchanged by the widening. GATES: derived with scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack and reconciled with --ran: 55 derived / 53 run green / 2 NOT MEASURED / 0 UNRUN; the two are check:dual-build-cjs-loads and check:type-check-debt, both exit 3 = PREREQUISITE NOT MET (each needs a full-repo pnpm build), recorded as not-measured, never as a pass. Exit codes captured before any pipe.",
    "mcp_calls": "0 — every GitHub read and write on this card went through repo-scoped REST (probe: GET issues/15610 = HTTP 200); zero MCP GitHub calls",
    "open_questions": [],
    "out_of_scope_findings": [
    "noted, not filed: the pin header's previous census sentence ('1 of 1,885 runtime .ts files repo-wide, 49 src roots') does not reproduce on this tree by the method now stated in the header (2,495 non-test .ts files under 78 src roots, same single hit) — a scope-definition and tree difference, not a disagreement about the finding; the header now carries numbers a reader can re-derive. Carrier: the next editor of this pin.",
    "noted, not filed: the ARRAY/SPACED/DENSE assertions sit inside a for loop, so a tree with several offending carriers reddens naming only the first — ordinary vitest shape, not a defect, and the reason the per-carrier measurements above were taken one file at a time. Carrier: the next editor of this pin."
    ]
    }


    Generated by Claude Code

  4. added a commit that references this issue on Sep 17, 2026
    48a3549
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions