Skip to content

docs-audit: a quoted client: on a row whose route: could not be read is absent from the clientsDeclared denominator #10636

Closed

Description

@os-zhuang

Filed unassigned by the os-dev seat implementing #10500 (session session_01DdCnBGcHeufjrq7drTD3wt). Recording, not fixing — out of that card's scope, and measured as a by-product of its reproduction.

What

#10500 closes the silence for a route: / client: value in no quote at all: it is now counted and named as unread. A narrower residue survives it.

clientsDeclared in parseLedgerSource (scripts/docs-audit/affected-docs.mjs) is computed as:

const clientsDeclared = rows.filter((r) => r.client).length + declined.filter((d) => d.key === 'client').length;

Both terms are row-relative. A correctly single-quoted client: that belongs to a row whose route: could not be read is in neither:

  • it is not rows.filter(r => r.client) — its row never became a row, and
  • it is not declined — its own spelling is the one the recognizer reads, so nothing declined it.

The row's client: value therefore leaves the denominator without a word, while the file plainly declares it.

Measured

On ba2d8d4730, adding one row to packages/services/service-i18n/src/i18n-route-ledger.ts:

{ route: I18N_BASE + '/plurals', family: 'i18n', disposition: 'sdk', client: 'i18n.getPlurals' },

node scripts/docs-audit/affected-docs.mjs --bridge-coverage reports:

read 3 of 4 declared `route:` value(s) and 3 of 3 declared `client:` value(s); declined 1: line 101: route: I18N_BASE + '/plurals'

The route: half is exact — 3 of 4, and the unread one is named. The client: half says 3 of 3 on a file that declares four client: values.

Why this is a residue and not the same defect

The ROW does end up in a verdict: the file is flagged PARTIAL read and the unreadable row is named with its line, so nothing here is the silence #9896 and #10500 were about — a reader is told this ledger was not fully read and where. What is inexact is a sub-count: the client: denominator understates what the file declares, and understating a denominator is the direction that makes a partial read look more complete than it is.

Direction, not a prescription

  1. Leave it. The row-level verdict already fires, and the client: count is only ever consulted alongside it. Cost: N of N can read as complete on a file that is not.
  2. Count client: declarations the same way route: is counted — file-wide over code-only source (comments and string contents masked, type declarations excluded), rather than through row windows. unreadableIn from docs-audit: a route-ledger row whose route: is not a string literal is invisible to BOTH the row recognizer and the new partial-read counter #10500 already computes exactly that population for the non-quoted case; the quoted case would need the same treatment without double-counting the ones a row window already claimed.
  3. Report the orphan explicitly — a client: matched in code position that no row window claimed is itself the interesting event, and naming it is strictly more informative than moving a number.

(2) or (3) look like the shape worth costing; (3) has the property #9896 argued for — key on the spelling that was not read rather than on a shortfall, because a count comparison is blind to a mis-bound row.

Refs: #10500, #9896, #9572.


Generated by Claude Code

Activity

  1. claude commented on Aug 21, 2026

    @claude
    Contributor

    Claim: /pm-dispatch domain:devx execution seat.
    Session: session_01DdCnBGcHeufjrq7drTD3wt
    Branch: claude/issue-10636-clients-declared-residue
    Worktree: objectstack-issue-10636
    Domain: domain:devx
    File surface: scripts/docs-audit/affected-docs.mjs (parseLedgerSource's clientsDeclared) + its --self-test fixtures

    ⭐ Unlocked: PR #10637 (#10500) merged, so that file is free and the unreadableIn counter this residue sits beside is on main.

    ⚠️ The filing seat scoped this precisely and the PR must keep that framing: it is "a sub-count residue, not the silence this card is about" — the row itself already lands in a verdict after #10500. ⛔ Do not write it up as a live false green.

    ⛔ Governed surface out of scope (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md, content/docs/releases/**) and the #9465 epic fence (.changeset/ tooling, cut-rc.yml, release.yml, root package.json).
    ⚠️ No formatter of record (no format/prettier script, prettier not in devDeps, no .prettierrc/.editorconfig) — ⛔ do not run prettier --write (#10622).
    ⭐ Changeset: AGENTS.md:943 — "Pure bug fixes do not require a changeset"; a scripts/**-only diff that publishes nothing takes skip-changeset via the additive POST /labels (⛔ never a whole-set PUT — see #5533).


    Generated by Claude Code

  2. added a commit that references this issue on Aug 21, 2026
    19c982a
  3. claude commented on Aug 21, 2026

    @claude
    Contributor
    {
      "issue": 10636,
      "status": "done",
      "branch": "claude/issue-10636-clients-declared-residue",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/10674",
      "premise_still_valid": true,
      "summary": "The `clientsDeclared` denominator in `parseLedgerSource` was two row-relative terms, so a correctly single-quoted `client:` on a row whose `route:` could not be read was in neither. It is now swept file-wide over the same code-only, type-declaration-excluded source #10500 built, minus the values a row window already claimed, and each unclaimed value is pushed to `declined` so it is NAMED with its own line rather than only bumping a number. NUMERATOR unchanged (`clientRows` = values read and bound to a row, `rows` byte for byte identical); DENOMINATOR now = every `client:` the file declares in code position. The two stay comparable because every unit added to the denominator arrives as a named 'declared, not read' event the numerator was never going to count — ablation C shows what dropping that guard costs (real tree `221 of 442`). The card's framing is kept: the PR says sub-count residue, not a live false green. Slightly wider than the card, same defect class: the sweep keys on 'no row claimed it' rather than on the quote, so it also counts a declined-spelling `client:` outside every row window (before the first assembled row, or in a truncated-window gap) — pinned by its own fixture and called out in the PR body.",
      "tests": "All at HEAD 19c982a268 (final commit; merge base 72d75ebb55).\n\nRE-DERIVED MEASUREMENT (card measured on ba2d8d4730; reproduces verbatim on today's tree). Mutation: the card's row appended to packages/services/service-i18n/src/i18n-route-ledger.ts; confirmed on disk by anchor count before each run (`client: '` 3 -> 4, I18N_BASE 0 -> 1) and after restore (4 -> 3, 1 -> 0).\n  BEFORE (origin/main code, mutated tree): \"read 3 of 4 declared `route:` value(s) and 3 of 3 declared `client:` value(s); declined 1: line 101: route: I18N_BASE + '/plurals'\" — totals 259 of 260 / 221 of 221.\n  AFTER  (this branch, same tree):        \"read 3 of 4 declared `route:` value(s) and 3 of 4 declared `client:` value(s); declined 2: line 101: client: 'i18n.getPlurals' (no row read it), line 101: route: I18N_BASE + '/plurals'\" — totals 259 of 260 / 221 of 222.\n  RESTORED tree, this branch: 259 of 259 / 221 of 221, exit 0, no verdict. The real-tree number does NOT move, and should not: all seven ledgers are wholly single-quoted and every row was assembled (259 of 259), so no `client:` can be orphaned. The seven were also re-measured under each ablation (below) where they DO move.\n\nREJECT SIDE, asserted positively: all seven live ledgers spell the member `client?: string;`, which the scan's /\\b(route|client)\\s*:/ never matches — the live tree cannot exercise it. The fixture therefore uses the hostile spelling, a literal-union TYPE member that opens with the quote the counter reads: `export interface Entry { route: string; client: 'i18n.getLocales' | 'i18n.getPlurals' }`. The quote test alone would swallow it; typeDeclRegions is what rejects it. Pinned twice (no line-1 entry in the declined list; deleting the interface changes no count), and ablation B reds exactly that assertion.\n\nABLATIONS — each mutation confirmed on disk by anchor count (an editor exit code proves nothing), each restored and re-run green (308 pass) before the next:\n  A remove the unclaimedClientsIn sweep      call sites 2->1   11 red  (predicted 9 by NAME before running; returned exactly those 9, then 11 after the extra orphanTail fixture was added below it)\n  B remove the typeDeclRegions skip          skip line 1->0    13 red  (incl. the literal-union member and #10500's `route: string;` pins; live tree 259 of 266)\n  C remove the !claimed.has(...) guard       guard 1->0        22 red  (double counting; live tree 221 of 442 — the exact 'denominator widened past what the numerator can ever count' failure)\n  D remove the codeOnly mask                 mask 1->0          3 red  (prose/string payloads become declarations; live tree 259 of 260)\nNo shortfall against prediction (A was the only one predicted numerically, and it matched by name).\n\nSELF-TEST: 291 cases on origin/main -> 308 on this branch (+17).\n\nGATE UNION: `node scripts/pm/dispatch-gates.mjs`, no paths passed — exit 0, NOT empty: change set 1 path, three-dot vs merge base 72d75ebb5; 4 families matched. Hand-cross-checked: `grep -rln docs-audit .github/workflows/` names lint.yml and docs-drift-check.yml only, both covered, plus the scripts/** family. All run at 19c982a268 through scripts/pm/os-verify-lock.sh, exit codes captured by redirect-then-capture (never after a pipe):\n  os-verify-lock: VERDICT command-exit 0 · held the lock 2s · waited 199s (3m19s)\n    check-affected-docs.mjs [docs-drift-check.yml]   ✓ affected-docs self-test: 308 cases pass.\n    check:docs-audit-scope  [lint.yml]               ✓ affected-docs self-test: 308 cases pass. / ✓ check-audit-scope self-test: 24 cases pass. / ✓ docs-accuracy-audit scope is in sync with content/docs/: 189 doc(s).\n    check-cross-package-test-inputs.mjs [ci/lint]    OK: 13 package(s) read outside themselves, all declared.\n    check-nul-bytes.mjs                              OK (scanned 6212 text file(s); no raw ASCII control bytes) + hand self-scan of the edited file: 0 hits.\nNo pnpm install was needed or run: the gate union is node-builtin-only, which is the property docs-drift-check.yml depends on. No dependency import was added.",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #10683: [finding] the ledger ROW recognizer reads raw text, so a `route: 'X'` written in a comment becomes a real row — measured 2 rows from a fixture declaring 1, with routesDeclared agreeing so no verdict fires; no live instance on today's tree (all seven ledger comments scanned, zero hits). Every other scan in the file reads code-only; this one does not. Labelled `finding`, unassigned, no pm:queue."
      ]
    }

    Generated by Claude Code

  4. added a commit that references this issue on Aug 23, 2026
    db4341d
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