Skip to content

docs(spec): translation liveness _note names the live/planned boundary instead of a hand-maintained total - #16138

Merged
huangyiirene merged 1 commit into
mainfrom
claude/issue-15775-translation-liveness-group-count
Sep 6, 2026
Merged

docs(spec): translation liveness _note names the live/planned boundary instead of a hand-maintained total#16138
huangyiirene merged 1 commit into
mainfrom
claude/issue-15775-translation-liveness-group-count

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Fixes #15775

packages/spec/liveness/translation.json's _note header claimed "11 of 12 groups live; the twelfth, datasets, …". No reading of the file's own props produces that pair — and this is the second wrong total the same sentence has carried. So the integers are deleted rather than re-derived.

One file's prose changes; props is byte-identical; no status value moves.

The measurement (verified independently, not taken from the card)

A translation group is a key of translationDataShape() in packages/spec/src/system/translation.zod.ts — the function TranslationItemSchema spreads before adding locale, the item-identity keys name / label, and the ADR-0010 protection envelope. Enumerated mechanically from that function at this branch's base (1f2a02ba):

objects · apps · messages · globalActions · dashboards · datasets
pages · flows · settings · metadataForms · settingsCommon        -> 11 groups

Against the ledger's own rows:

reading rows live non-live
all of props 14 13 flows (planned)
groups only (props minus locale / name / label) 11 10 flows (planned)

Neither is "11 of 12", and datasets is one of the eleven groups rather than a twelfth. Walked at every nesting level the file has, the non-live rows are flows, flows.children.label and flows.children.screens — i.e. every non-live row in the file is flows or one of its children, which is what makes the boundary sentence true at every granularity rather than at one chosen one.

What the sentence became, and why not a corrected pair of integers

The card and its triage both ruled the fix is a boundary, on #7377's precedent — that PR retired this ledger family's other hand-maintained counts by moving them out of prose into the generated state-counts.md, and the README's counting-method section now says in writing that a Notes cell "is not a place to restate a number". The same shape applies one file down: the per-prop rows carry the verdicts, state-counts.md carries this type's totals, and the header states the boundary.

The header now reads (one line in the JSON; wrapped here for review):

Every group but flows is live — flows is the one that is planned, and datasets was seeded LIVE and DRILLED by #14253 with its reader (translateDataset) in the same change. A BOUNDARY and not a total, deliberately: the per-prop rows below carry the verdicts, the generated state-counts.md carries this type's totals (#7377), and props holds the groups PLUS locale and the item-identity keys name/label — so no total taken over props is a total of groups, which is how both totals this sentence has carried came to be wrong. ⚠️ It first read "10 of 11 groups live; the one dead group (validationMessages) …" — describing a group REMOVED in 17.0.0 (#4667), i.e. prose outliving its subject in the header of the very file whose rows warn about that; corrected 2026-09-02 (#14253) to "11 of 12 groups live; the twelfth, datasets, …", which matched no reading of props at all — datasets is one of the groups, never a twelfth. Corrected again 2026-09-06 by deleting the integers rather than re-deriving them, on #7377's precedent for this ledger family's other hand-maintained counts.

Both former totals survive quoted, as the sentence's own correction record — the file's established habit, and the reason the first failure was catchable at all. The second one is now recorded too, which it was not before: the pre-existing ⚠️ clause described only the validationMessages occurrence, so leaving it untouched would have left the file recording one failure where there were two. That clause is the summary claim's own provenance, so it moved with the claim; no other sentence in the _note is touched, and the diff is one line.

Diff, line by line

  • packages/spec/liveness/translation.json — the _note string only. props compares byte-identical against HEAD~; the JSON key order, the type field and every row are unchanged.
  • .changeset/translation-liveness-group-boundary.mdpatch on @objectstack/spec. A changeset is owed: liveness is in that package's package.json files array, so these ledgers ship in the npm tarball and this is published data. That is the ledger's own recorded convention, quoted from two earlier entries in packages/spec/CHANGELOG.md: "liveness/ is in this package's files array, so these ledgers ship in the npm tarball and this is published data." skip-changeset is therefore wrong here and is not applied.

Verification

Final commit 1412bcb6. All figures below were taken at that commit.

Gates — derived against the real diff, not recalled. node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands derived 51 families (re-derived after the changeset existed, which added 6). All 51 run; accounted with --ran:

✓ dispatch-gates --ran: 51 derived famil(ies) accounted for — 51 run, 0 NOT-MEASURED.

49 green, including every gate that actually reads this file: check:liveness, check:empty-state, check:strictness-ledger, check:variant-docs, plus check:nul-bytes, check:published-files, check:merge-driver, the changeset family (check-empty-changeset, check-changeset-no-major, check-adr-0087-registration, check:changeset-gate-self-tests, check:objectui-changeset) and both docs-drift audits. check:liveness reports:

✓ packages/spec/liveness/state-counts.md is current — the same 36 row(s), no count column left in the README.

2 of the 51 are NOT MEASURED, not green and not redcheck:dts-closure and check:dual-build-cjs-loads both exit 3 with PREREQUISITE NOT MET, their own words: "⛔ This is NOT a pass and NOT a finding: nothing was swept". Both read a built tree and need every one of the 79 workspace packages to carry a dist/; they are matched here only by the coarse packages/** CI job filter for Build Core. Declared narrowing: a whole-workspace build is CI's Build Core job, which runs both on the built tree. The diff cannot reach their subject in any case — it adds no export, changes no manifest, and touches no compiled source.

node scripts/check-plugin-teardown-shape.mjs --self-test first exited 1 on the shallow checkout ("cannot read the positive control at 621a4876"), which is a clone-depth refusal and not a finding; after git fetch origin 621a4876… it passes 47 cases.

Tests. packages/spec's liveness suites, under the shared verify lock:

Test Files  11 passed (11)
     Tests  297 passed (297)
os-verify-lock: VERDICT command-exit 0 · held the lock 95s · waited 1s

Declared narrowing on the affected set. TURBO_SCM_BASE=1f2a02ba pnpm exec turbo ls --affected names 25+ packages — the whole downstream closure of @objectstack/spec, because the file lives inside that package, not because anything in them reads it. Narrowed deliberately, and the narrowing is proved rather than assumed: props is byte-identical, and nothing in the tree reads the file-level _note. The two tests that do read the real shipped ledgerpackages/cli/test/i18n-flow-liveness-gate.test.ts and i18n-flow-screen-coverage.test.ts — read row fields (status, authorWarn) through authorWarnedTranslationGroups; lint-liveness-properties.ts reads a row's own note/authorHint, which is a different key from the file-level _note. All of those inputs are unchanged bytes.

Declared narrowing on pnpm lint. Measured, not assumed. The population comes from eslint's own config: every block in eslint.config.mjs is scoped to **/*.{ts,tsx,mts,cts,js,jsx,mjs,cjs} or narrower — no block names .json or .md. Running eslint on exactly the two changed paths with --format json returns 2 file entries, 0 errors, and one message each: "File ignored because no matching configuration was supplied." Invariance: no rule in the config reads a .json or .md file and no type-aware linting is triggered by them, so this diff cannot move the verdict on any untouched file. The narrowing here is total rather than partial — the diff contributes zero files to the linted population.

Reverse verification — the direction is NOT the usual one, and that is the finding. An ablation was run from the committed state: the boundary sentence was replaced on disk with a deliberately absurd claim (ZZZ 97 of 4 groups live ZZZ), the mutation was proved to have landed (blob hash moved ceeeb575dbf163e8; injected-marker grep count 1, removed-text grep count 0), and check:liveness was re-run. It stayed green, exit 0. Restore leg: git checkout HEAD -- <path>, proved by git diff HEAD empty on the target and by the blob hash returning to ceeeb575.

So the observed direction is no change, not "turns red" — nothing in this repository can fail on this sentence's content, in either direction. That is precisely why the fix had to be a boundary rather than a fresh pair of integers: there is no gate to catch the third occurrence, only a reader. It is also the mechanism half already recorded as #15765, and the reason this PR adds no test — there is no assertion surface to add one to without inventing the gate that card is about, which is out of this card's scope.

Out of scope, filed not fixed

Two same-class findings surfaced while measuring and were not ridden in — both filed unassigned, both deduped against open and closed issues first:

Neither is addressed here, and neither blocks this PR.

🤖 Generated with Claude Code


Generated by Claude Code

…ary, not a total

The `_note` header claimed "11 of 12 groups live; the twelfth, `datasets`, …",
which matches no reading of the file's own `props`: fourteen entries — eleven
translation groups plus `locale` and the identity keys `name`/`label` — with
exactly one non-`live` row (`flows`), and `datasets` among the groups rather
than a twelfth.

Second wrong total in the same sentence, so the integers are deleted rather
than re-derived, on #7377's precedent: the sentence now names the boundary,
which the per-prop rows carry and `state-counts.md` totals. Both former totals
survive quoted, as the sentence's correction record. No `status` value moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T6HeZvT9wdSJD1ZxJb5Eno
@github-actions github-actions Bot added the size/s label Sep 6, 2026
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/liveness/translation.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/translation.json) — pages documenting those are invisible to this run
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 130 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 932acc3df41e016812cfc17ffe1527fd7b5df538packageMentionDocs.

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

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(liveness): translation.json header says "11 of 12 groups live" while its own props hold 11 groups with 10 live

2 participants