Skip to content

finding: check-role-word.mjs hardcodes its own regenerated baseline size (43) in two prose sites — accurate today, wrong on the next --update #9947

Closed

Description

@claude

Filed by the domain:devx PM seat from PR #9944 (#9915)'s H4 sibling sweep. Named there rather than fixed, per that dispatch's ruling 4.

The defect

scripts/check-role-word.mjs states its own regenerated baseline's size in two prose sites:

  • L133 — check-role-word: OK (43 baselined file(s), no new occurrences).
  • L144 — ledger untouched — `43 problem(s)`, exit 1

scripts/role-word-baseline.json holds exactly 43 entries today, so both are accurate right now.

⇒ Latent, not live. They go wrong on the next --update, which is the sanctioned operation that rewrites that baseline.

Why file it while it is still correct

This is the identical state check-engine-double-contract.mjs's L206/L214 were in — and L214 had been refreshed to a correct value two hours before PR #9944 measured it stale again. Refreshing a self-describing number does not fix it; it resets the clock.

The class, stated once: a script that hardcodes a figure its own sanctioned regenerator invalidates, with nothing to catch the drift. --update is not a mistake anyone makes — it is the supported remedy the gate tells you to run.

The fix shape that worked next door

PR #9944 established that run-time derivation is not available for comments — nothing renders a comment, and making --update rewrite the script's own source would be a behaviour change (and would flip Clause-② to yes).

What worked instead: rest the claim on something the ratchet already guarantees, so the sentence cannot go stale in the direction that matters. Its examples:

  • 308 generated rows → "a census that ALREADY OUTNUMBERS them and, by the opposite polarities below, can only outnumber them further" — monotone-true given grow-only/shrink-only invariants;
  • one problem per census row -- hundreds of them, and growing, since this ledger is grow-only — a floor on a grow-only ledger cannot rot.

⚠️ check-role-word's baseline is shrink-only (it is debt being paid down), so the polarity is the opposite one and the phrasing must follow its invariant, not that file's. Do not copy the sentence; copy the method.

⚠️ Both sites are sample output inside comments — text a reader compares against what the gate actually prints. Check whether they are load-bearing as examples of format (in which case the number could go generic) or as measurements (in which case they need the treatment above). Those want different fixes.

Scope

The bound on how this was found

PR #9944's sweep enumerated scripts/ files carrying a --write/--update/--fix mode (11), resolved each one's artifact, computed its array lengths, and grepped the script's comments for those values. Four other regenerators came back clean: check-error-status-conformance (34), check-i18n-coverage (12), check-query-options-erasure-ratchet (29/17), check-slot-lookup-ratchet (25).

⚠️ That method only catches a figure still EQUAL to the live size — it finds latent sites and misses already-stale ones. In its author's words: "it would not have found this card's own 308s." So a clean result for those four means "no latent site", not "no stale site". Finding already-stale siblings needs the per-file hand classification #9915 used, and is not this card.

Refs: PR #9944 / #9915 (source, method, and the fix shape) · PR #9931 / #9910 (this file, same morning) · #9943 (the sibling class this card is not: present-tense population sizes that --write does not invalidate).


Generated by Claude Code

Activity

  1. claude commented on Aug 19, 2026

    @claude
    ContributorAuthor

    Claim: PM loop round 21
    Session: session_01XqDQYVU5smx29ts9pAErja
    Branch: claude/issue-9947-role-word-baseline-size
    Worktree: objectstack-issue-9947
    Domain: domain:devx
    File surface: scripts/check-role-word.mjs (the two prose sites) (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus (node scripts/pm/dispatch-gates.mjs --tier scripts/check-role-word.mjs run this round: "no path-derived mandate … This line is a FLOOR, never a clearance"; content limb no — comment prose only)
    Clause-②: no
    Serial constraints cleared: swept all open PRs for any touching role-word — none. PR #9931 (#9910), which rewrote this file's success branch and its --update confirmation, is MERGED — read it first, because it changed what the sample output in these comments is supposed to look like.


    Generated by Claude Code

  2. os-steve commented on Aug 19, 2026

    @os-steve
    Collaborator
    {
      "issue": 9947,
      "status": "done",
      "branch": "claude/issue-9947-role-word-baseline-size",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10044",
      "premise_still_valid": true,
      "summary": "H1: the ledger still holds exactly 43 entries (129 occurrences) on current main, so both named sites were still accurate and this stayed LATENT, not live — severity unchanged. Ruling 1's second axis is FALSIFIED: the sample output is NOT stale, because both named sites were authored BY #9931 itself as a deliberate before/after narrative — the two old-format quotes are past-tense by construction ('The green line used to be, in full'), and the one fragment quoting the NEW format ('0 .md/.mdx file(s) read') is an exact substring of what the gate prints today; verified the old 'OK (' line is genuinely gone from the program. RULING 3 per-site verdicts: site 1 (was L133) is a FORMAT EXAMPLE — the argument it supports ('Every number in it came from the LEDGER') rests on the line's shape, not N's value, and decisively the figure is UNREFRESHABLE because #9931 deleted that line from the program, so after the next --update there is no correct value at all; the number went generic (N). Site 2 (was L144, '43 problem(s)') is a MEASUREMENT — a real ablation result offered as evidence a dead scan fails loudly — so it got ruling 2's treatment, not a refresh. H2 INVARIANT + POLARITY: I rest on the cardinality IDENTITY the ratchet-DOWN loop enforces — it walks Object.entries(baseline) and raises one error per entry missing from current, so a dead scan raises as many problems as the ledger holds, true at 43, at 1 and at 0. I deliberately did NOT use the mirror-image ceiling, and that is the polarity finding: check-role-word's 'shrink-only' is a GOVERNANCE rule, not a mechanical monotone — the same --update flag is also the baseline-EXPANDING path the #8435 MAINTAINER-ONLY marker gates — so the ledger CAN rise and NO bound is durable in either direction (a floor rots on the first sanctioned ratchet-down, the very remedy the gate tells authors to run; a ceiling rots on the maintainer-approved expansion the gate explicitly provides for, and would rest on a policy, which is the one thing a comment cannot verify). Per H2's own escape clause: no monotone magnitude claim is available here, so the magnitude claim was removed rather than re-based. Ruling 4 honoured and proven mechanically: every changed line is a comment, gate output is byte-identical before/after, baseline JSON untouched; Clause-② stays no.",
      "tests": "All gates on final commit 163d8292d7 (= PR head), quoting each gate's own printed verdict line, exit codes captured before any pipe. Gate set derived by `node scripts/pm/dispatch-gates.mjs` with NO paths (it takes the changeset from the merge base itself): 1 path, 3 families named for scripts/**. (1) `pnpm check:role-word` -> 'OK  self-test: the NEW-use remedy marks baseline expansion as maintainer-only... ' and 'check-role-word: OK, no new occurrences of the reserved word. / Scanned: 216 .md/.mdx file(s) read across 2 root(s) - content/docs 180, skills 36. / Ledger: 43 baselined file(s) still carrying it (129 occurrence(s))', EXIT_CODE=0. (2) `pnpm check:nul-bytes` -> 'check-nul-bytes: OK (scanned 6358 text file(s) -- 6358 tracked, 0 untracked-not-ignored; skipped 5 binary; no raw ASCII control bytes).', EXIT_CODE=0; plus a manual grep -naP over the edited file, clean. (3) `node scripts/check-cross-package-test-inputs.mjs` -> 'OK: 12 package(s) read outside themselves, all declared, and turbo.json hashes every declared glob.', EXIT_CODE=0. NO-BEHAVIOUR-CHANGE PROOF (ruling 4), two independent observations: (a) `git diff -U0 | grep -E '^[+-]' | grep -vE '^[+-]\\s*//'` returns NOTHING — every added/removed line is a comment; (b) the gate's stdout captured before the edit and after is BYTE-IDENTICAL (`diff` exit 0), exit 0 both times, and `git status` lists only scripts/check-role-word.mjs so the baseline JSON is untouched. No ablation was run: this change cannot alter behaviour by construction (comments only), so an ablation would have nothing to measure. H1 MUTATION CONFIRMATION (per the card's warning that sed/perl/str.replace report success on zero matches): each replacement asserted an exact match count of 1 and aborted otherwise, and the result was then confirmed by an observation independent of the edit tool — `grep -n '\\b43\\b' scripts/check-role-word.mjs` now exits 1 (no matches remain anywhere in the file), and `git diff --stat` shows the file changed.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10042: check-role-word.mjs's header says `--update` expands the baseline 'which is the shrink-only direction of this ratchet' — backwards on either reading of the relative clause (if 'which' = the baseline, a baseline is not a direction; if it = expanding, expanding is the WEAKENING direction). The same file states it correctly twice elsewhere. NOT fixed here: different defect class (garbled prose, not a self-invalidated figure), so it failed the bounded in-place exemption's same-class condition. Unassigned, no labels, left for PM triage.",
        "H3 (already-stale check, the sibling method's blind spot — reported, deliberately NOT 'corrected'): the self-test scan fixtures read content/docs 179 + skills 36 = 215, while the live tree is 180 + 36 = 216. They are SYNTHETIC fixtures every assertion closes over, so they stay correct however the corpus moves and the 215 regex can never fail for a tree-related reason. Refreshing them to 180/216 would convert a closed fixture into a figure the tree CAN falsify — manufacturing the exact defect this card removes. They are instead pinned as synthetic with a MAINTAINER-ONLY-style note against future refreshing. No issue filed: not a defect.",
        "H4 census (14 figures/claims audited in the file; 3 self-invalidated by --update; 3 changed, plus the 1 preventive fixture pin above). The third self-invalidated site is the header, which listed the ledger's legitimate contents as an INVENTORY: 'many are legitimate — the better-auth boundary, ARIA `role=` in samples, educational \"formerly roles\" mentions'. Measured against the live baseline: better-auth 14 baselined files, ARIA `role=` ZERO, 'formerly roles' 3. ALREADY STALE — the only two `role=` hits in baselined files are `sys_member.role = ...`, better-auth org-plugin vocabulary, not ARIA. Fixed in this PR per the card's H4 ('an example entry ... belongs in this card's population'), and fixed the ruling-2 way: it now names the legitimate KINDS (a rule about what may be baselined, which --update cannot falsify) rather than taking a census. Claims audited and found SOUND, changed nothing: the three failure conditions vs the code, the scope/ROOTS/SKIP_DIRS description, 'Four assertions' in the #8435 block, 'two non-existent directories' (ROOTS.length 2), the old --update line quote, and the cross-file claim that the #8435 convention landed in check-engine-double-contract.mjs and check-type-check-coverage.mjs (both still carry the marker).",
        "SCOPE DECLARATION for PM: the claim declared the file surface as 'the two prose sites'; this PR touches FOUR sites in that same file — the two named, plus the H4 header inventory (which the card's H4 pre-authorised into the population) and the H3 fixture pin. Same file, same defect class, same gate family, no new verification surface. Flagged here rather than passed as silent widening."
      ]
    }

    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

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions