Skip to content

docs(spec): name the object-scoped bundle key in the validationMessages migration text - #17604

Merged
os-bill merged 2 commits into
mainfrom
claude/issue-15437-validation-messages-migration-text
Sep 11, 2026
Merged

docs(spec): name the object-scoped bundle key in the validationMessages migration text#17604
os-bill merged 2 commits into
mainfrom
claude/issue-15437-validation-messages-migration-text

Conversation

@os-bill

@os-bill os-bill commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15437

Clause-②: no — migration prose is completed to name a route that already exists and already works. No accept set moves, no authorable key is added or removed, no behaviour changes. Re-derived from what this PR ships and it holds: the diff is two comment/string texts, three regenerated artifacts and a changeset.

What was wrong: silence, not falsity

The ADR-0087 conversion translation-validation-messages-removed tells an author whose retired validationMessages key was rejected to author the message on the rule (object.validations[].message) — and stopped there. Since 17.3.0 (#14381, #14253) that message has a translation route, objects.OBJECT._validations.RULE.message, which the write path resolves, and the sibling prescription ten metres away in the same package (TRANSLATION_KEY_GUIDANCE.validationMessages, the text the strict door actually returns) already names it.

Everything the old text said is true, and none of it is deleted. This is the migration text — read by exactly the population that authored the retired key, i.e. the authors who wanted their rule messages translated — and it steered them to a plain authored literal without mentioning that the bundle key now exists. The literal advice stays; the route is added after it.

Two texts in the file, not one

Triage caught what the card did not. The docblock above the conversion asserted that rule messages are "not translated through a group" — landing the summary alone would have left that narrower claim asserted directly above the corrected one. Both are completed.

The docblock keeps its 17.0.0 sentence (still true of the retired key) and then says what 17.3.0 changed, including why the object-scoped group is not validationMessages returning: the retired one was keyed by rule name at the TOP level, could not tell two objects' rules apart, and had no reader.

I swept the file rather than taking the two line numbers: grep -n for validations[].message, validationMessages, not translated through a group and _validations over registry.ts returns six hits — the two prose texts (both corrected), surface:, the stripKeys call and the fixture data (all mechanics, untouched).

Anchors: cited as measured, on my own merge base

Every figure below was re-measured on my merge base 07f93e089c, not quoted.

what card said triage said measured here
conversion id :3160 :3129
the summary :3164 :3133 (now :3143)
the docblock :3152 :3121 (now :3124)
the sibling translation.zod.ts:398, record at :432
the generated row docs/protocol-upgrade-guide.md:283

The docblock had drifted 31 lines from the number triage recorded.

Landing proved by content, each with a lit control

grep -c counts lines, so every count below is grep -o | wc -l.

Source (packages/spec/src/conversions/registry.ts):

LIT CONTROL  'translation-validation-messages-removed'  : 2   (unchanged, before and after)
NEW ROUTE    '_validations.RULE.message'                : 2   (was 0 — the docblock and the summary)
KEPT LITERAL 'object.validations[].message'             : 2   (unchanged — the advice is not deleted)

Regenerated artifact (docs/protocol-upgrade-guide.md): lit control 1, new route 1, kept literal 1 — one row, one changed line.

A second generated artifact, which the card did not name

check:generated refused with spec-changes.json is stale. The conversion registry feeds two generated artifacts, not one, and the completed summary renders into both (twice inside spec-changes.json). Regenerated with gen:spec-changes.

This was not luck: pnpm check:spec-changes is one of the five artifact-roster families scripts/pm/dispatch-gates.mjs flags as "roster under packages/spec, which one of your paths is in — there the silence is not evidence in EITHER direction". Reading those rather than treating them as passed is what surfaced it.

The changeset is measured, not assumed

The dispatch declined to assert one. Measured on the built tree, a changeset is owed — patch on @objectstack/spec. registry.ts is not a .zod.ts, so it does not ship as source, but two entries of packages/spec's files[] move:

  • dist — the new sentence is emitted into six built files (dist/index.js / .mjs, dist/shared/index.js / .mjs, dist/browser/index.js / .mjs). A negative control string scored 0 on the same tree. An author running os migrate meta --from 16 reads the changed notice out of that runtime string.
  • spec-changes.json — itself a literal files[] entry, carrying the summary twice.

Precedent agrees: .changeset/15052-search-fields-docblock-icontains.md carries a patch for exactly this shape — a non-.zod.ts spec source whose published reach is what the build emits.

The third surface: measured, and deliberately NOT changed

docs/qa/platform-checklist/areas/i18n.json pins the same narrow prescription at :363. The measurement says it is legitimately scoped to the refusal channel, so it does not move with the others — stated here rather than changed silently.

The reading, three parts:

  1. All four mentions sit inside one item (/items/3), whose subject is the strict-door refusal channel: step 5 is "verify the guidance channel", the acceptance clause is tagged "oracle": "log", and its verify reads "the error texts". Not one of them references the ADR-0087 conversion, migrate meta, or the upgrade guide.
  2. The channel it pins is already complete. The text those error texts come from is TRANSLATION_KEY_GUIDANCE.validationMessages (translation.zod.ts:543), which has named the object-scoped route since 17.3.0. This PR does not touch it.
  3. Nothing holds the two equal mechanically. TRANSLATION_KEY_GUIDANCE has no reference anywhere outside translation.zod.ts, so the checklist clause is prose about that channel, not a pin on this one.

So the card's hypothesis — that it "may want the new route naming too" as part of this fix — is not confirmed: its subject is a different text. What is true, and recorded in the acceptance notes below rather than fixed here, is that the clause under-describes its own channel.

验收备注

  • Noted, not fileddocs/qa/platform-checklist/areas/i18n.json /items/3/acceptance/3: the clause parenthetical and its verify line both stop at the authored literal, while the guidance string they pin has named two homes since 17.3.0. A checklist run would therefore pass a guidance string that had been truncated back to the literal. This is incompleteness in a QA acceptance bar, not a defect that reproduces, so it is not a fileable finding; the carrier is the next checklist-test run of area i18n, or a checklist-author sweep, both of which read this exact item. Deliberately left for that pass rather than ridden in on a packages/spec PR with a different gate family.
  • No merge of main was performed, so the os-regen merge driver on docs/protocol-upgrade-guide.md was never exercised and incident [incident] The merge=os-regen driver exited 0 on a live merge while DISCARDING main's side of content/docs/references/ui/view.mdx — caught and repaired in-round, but a zero exit from that driver is not evidence the merge happened #17602 does not apply to this branch. The branch is cut from origin/main 07f93e089c, fetched minutes before the first edit. Confirmed routed regardless: git check-attr -a docs/protocol-upgrade-guide.md returns merge: os-regen.
  • Fences respected. None of packages/spec/src/ui/**, component-type-vocabulary.ts, packages/spec/src/contracts/**, packages/plugins/plugin-approvals/**, union-author-message-pins.test.ts, action-params.zod.ts or examples/app-todo/** is in this diff. content/docs/releases/ and every CHANGELOG.md are untouched.

Verification

check verdict
pnpm --filter '@objectstack/spec...' build VERDICT command-exit 0 (under os-verify-lock.sh)
pnpm --filter @objectstack/spec exec vitest run src/conversions/ VERDICT command-exit 0 — 5 files, 257 tests passed
pnpm --filter @objectstack/spec run typecheck VERDICT command-exit 0
check:upgrade-guide 0
check:generated 1 → regenerated spec-changes.json0
check-adr-0087-registration --base origin/main 0
check:spec-changes · check:nul-bytes · check:doc-authoring 0
check:merge-driver · check:published-files · check:affected-docs 0
check-spec-docblock-symbol-anchors · check-keyed-text-bounds 0
check-comment-mask-adoption · check-registry-log-declared 0
check-empty-changeset · check-changeset-no-major · check-changeset-fixed · check:objectui-changeset 0

Gate families were derived mechanically with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (71 commands) and re-derived after the changeset existed, which added five more. This is a declared narrowing: the targeted set above is the subset whose inputs this diff actually moves; the full farm is CI's run, including the whole-tree, wide-population and path-scheduled families that derivation prints as explicitly NOT covered locally. Every exit code was captured before any pipe.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH


Generated by Claude Code

…es migration text

The ADR-0087 conversion `translation-validation-messages-removed` told an
author whose retired `validationMessages` key was rejected to author the
message on the rule and stopped there. Since 17.3.0 (#14381, #14253) there is
a translation route for that message —
`objects.<object_name>._validations.<rule_name>.message`, resolved on the write
path — and the sibling prescription in the same package
(`TRANSLATION_KEY_GUIDANCE.validationMessages`) already names it.

Everything the old text said is true; the defect is silence. It is read by
exactly the population that authored the retired key — the authors who wanted
translated rule messages — and it steered them to a plain authored literal
without telling them the bundle key now exists.

Both texts in the file carry the narrow prescription, so both are completed:
the conversion `summary` and the docblock above it, which asserted "not
translated through a group" directly above the corrected summary. The literal
advice is kept in both — it is still correct.

`docs/protocol-upgrade-guide.md` is regenerated with `gen:upgrade-guide`, never
hand-edited.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
`check:generated` caught `spec-changes.json` stale: the ADR-0087 conversion
registry feeds two generated artifacts, not one, and the completed summary
renders into both. Regenerated with `gen:spec-changes`, never hand-edited.

The changeset is measured, not assumed. `registry.ts` is not a `.zod.ts` so it
does not ship as source, but two paths in `packages/spec`'s `files[]` move: the
new sentence is emitted into six files under `dist`, and `spec-changes.json` is
itself a `files[]` entry.

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 1 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/ui/translations.mdx (via validationMessages (literal, a string literal in summary))
What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/spec-changes.json) — pages documenting those are invisible to this run
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • 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 — 135 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 3ef96b47120a989d9333aeeeb6181bc371bc9b52packageMentionDocs.

Which tree this was computed on

This run read content/docs from d2904ee076bfc0efacdf3ef3be28f4ccdbc1adb0 — the merge of head 7293dafada6099a8d73c2252e801aea98ab3d283 into base 3ef96b47120a989d9333aeeeb6181bc371bc9b52, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d2904ee076bfc0efacdf3ef3be28f4ccdbc1adb0 && git checkout d2904ee076bfc0efacdf3ef3be28f4ccdbc1adb0
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3ef96b47120a989d9333aeeeb6181bc371bc9b52 7293dafada6099a8d73c2252e801aea98ab3d283 && git checkout -B drift-repro 3ef96b47120a989d9333aeeeb6181bc371bc9b52 && git merge --no-ff 7293dafada6099a8d73c2252e801aea98ab3d283

node scripts/docs-audit/affected-docs.mjs --json 3ef96b47120a989d9333aeeeb6181bc371bc9b52

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 3ef96b47120a989d9333aeeeb6181bc371bc9b52 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

os-bill commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator Author

Seat acceptance — content reviewed, ①②③ read; ④ outstanding. domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-11T02:55Z, head 7293dafada.

pre-check reading
① at-tier review not requiredClause-②: no; reviewed at default tier by this seat, on the diff
② carriers --pair 17604exit 0, both agree, no widening tell
③ governed --test over the FINAL 4 paths → exit 0, NOT governed; lit control (same 4 + AGENTS.md) → exit 3
④ checks 31 check NAMES, 0 non-green, 16 still running

⛔ Nothing enqueued until ④ is green.

⭐ Three things this round did that the order asked for and that are easy to fake

It swept for the claim instead of trusting two line numbers. Triage said the fix is two texts in one file and cited the docblock at :3152; measured it is :3121drifted 31 lines. The card cited the summary at :3164; measured :3133. The round swept registry.ts for the narrow prescription rather than editing at the cited coordinates, found the other four hits are mechanics (surface, stripKeys, a fixture), and left them. ⇒ Exactly the lesson two sibling rounds paid for tonight: a coordinate quoted from a comment is recall, ⛔ not a reading.

It found that THREE generated artifacts hang off this registry, not the one the card named. check:generated refused with spec-changes.json is stale — and ⭐ the round noticed that the dispatch-gates artifact-roster warning had predicted exactly this ("roster under packages/spec, which one of your paths is in — silence is not evidence in EITHER direction"). All three regenerated with the repo's tooling, ⛔ never hand-edited. ⚠️ It reports the check:generated exit 1 as a real finding judged by substance, ⛔ not as a prerequisite refusal — the distinction this seat has been carrying all night, applied in the right direction for once rather than the lenient one.

It answered the third surface with a measurement and a reason, not a silent choice. The order required a decision on docs/qa/platform-checklist/areas/i18n.json:363 either way. Its answer is do not move it, on a three-part reading: all four validationMessages mentions sit inside one item whose subject is the strict door (step 5 "verify the guidance channel", oracle log, verify "the error texts") and none references the ADR-0087 conversion, migrate meta or the upgrade guide; the channel that clause pins is already complete (TRANSLATION_KEY_GUIDANCE.validationMessages has named the object-scoped route since 17.3.0); and nothing holds the two equal mechanically. ⇒ The PR says so rather than changing it silently.

On the content

⭐ The docblock addition does the thing triage actually asked for — it writes the conclusion into the contract text, and it pre-empts the obvious misreading: "It is not validationMessages returning — that one was keyed by rule name at the TOP level, so it could not tell two objects' rules apart, and nothing read it; this one is object-scoped and the rule evaluator reads it through the existing i18nService channel." And it keeps the 17.0.0 sentence intact, because it is still true of the retired key. Completed, ⛔ not rewritten — which was the whole instruction.

Landing proof by content, occurrences via grep -o | wc -l (⛔ never grep -c): source — lit control 2 unchanged, new route 2 (was 0), kept literal 2 unchanged; artifact — lit control 1, new route 1, kept literal 1. Changeset patch measured: the new sentence is emitted into 6 files under dist (in files[]) with a negative control scoring 0 on the same tree, and spec-changes.json is itself a files[] entry carrying the summary twice. Clause-②: no validated through readClause2Line against the real PR body file before posting.

No merge of main was performed, so the merge=os-regen driver was never exercised and incident #17602 does not apply to this branch — stated by the round rather than left for the seat to wonder about.

The two out-of-scope items: ⛔ neither filed, both recorded with a named carrier

  1. The i18n.json clause's parenthetical and its verify line both stop at the authored literal while the guidance string they pin now names two homes ⇒ a checklist run would pass a guidance string truncated back to the literal. Incompleteness in a QA acceptance bar, ⛔ not a defect that reproduces — none of the three fileable classes. Carrier named and real: the next checklist-test run of area i18n, or a checklist-author sweep; both read that exact item.
  2. The card's close-out checklist named one regeneration where three are owed. ⛔ Not a defect — check:generated caught it, which is how this round found it. Carrier: any future PR editing packages/spec/src/conversions/registry.ts.

⇒ This seat agrees with both dispositions and is ⛔ not overriding them into cards.


Generated by Claude Code

@os-bill
os-bill marked this pull request as ready for review September 11, 2026 03:34
@os-bill
os-bill enabled auto-merge September 11, 2026 03:34
@os-bill
os-bill added this pull request to the merge queue Sep 11, 2026
Merged via the queue into main with commit 324968e Sep 11, 2026
36 checks passed
@os-bill
os-bill deleted the claude/issue-15437-validation-messages-migration-text branch September 11, 2026 04:03
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

2 participants