Skip to content

docs(skills): state the label contract spec 17 actually enforces (#5081) - #5464

Merged
os-support-ai merged 1 commit into
mainfrom
claude/issue-5081-i18n-spec-version-fossil-v2
Aug 21, 2026
Merged

docs(skills): state the label contract spec 17 actually enforces (#5081)#5464
os-support-ai merged 1 commit into
mainfrom
claude/issue-5081-i18n-spec-version-fossil-v2

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes#5081

Draft, and skills/ is maintainer-merge-only. Do not enable auto-merge; do not merge. This queues for the maintainer.

What was wrong — twice over

skills/objectui/guides/i18n.md backed its label rule with @objectstack/specv4 on lines 117 and 162 while every manifest here declares ^17.0.0 (33 declarations, node_modules carrying 17.0.0) — thirteen majors, on the surface an agent reads before it writes a user's project.

The version number was the reported defect. The rule it was backing was wrong too, which is why the card's original fork had no writable arm: renumbering to v17 and deleting the qualifier both keep the sentence, and the sentence is false.

Maintainer ruling, 2026-08-20 — quoted verbatim

Ruled: A — restate the rule per the installed spec 17, with no version qualifier: a label is a plain string or an inline locale map keyed by BCP-47 tags (or default); the {key, defaultValue} key-reference form was retired (#5055) and is rejected with its own error. Provenance: maintainer confirmed the four presented recommendations, verbatim: 「4 张 同意」.

Same commit: remove the KNOWN_CLAIMS stale entry (doc-version-claims.test.ts:791-796) and correct the ledger's header prose (:213-219), which restates the falsified two-branch fork — the ratchet enforces the pairing. Option B (recommendation-style softening) is rejected: the guide would stay silent about a form readers meet on the platform's own published pages (31 sites). The dev's measured probes on this thread are the evidence base for the new wording.

My own measurement of what spec 17 admits

Re-verified from scratch this round against the INSTALLED package — node_modules/.pnpm/@objectstack+spec@17.0.0_ai@7.0.65_zod@4.4.3_/node_modules/@objectstack/spec, package.json version 17.0.0 — not from the thread, not from source, not from memory.

Declaration (dist/ui/index.d.ts:614, angle brackets spaced so GitHub's sanitizer keeps them):

declare const I18nLabelSchema: z.ZodUnion< readonly [z.ZodString, z.ZodRecord< z.ZodString, z.ZodString >] >;

Runtime (dist/ui/index.mjs:1932-1943) — this is the load-bearing half, because the .d.ts type says z.ZodRecord of plain strings while the runtime constrains the KEY:

var INLINE_LOCALE_KEY = /^(default|[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*)$/;
var InlineLocaleMapSchema = z.record(
z.string().regex(INLINE_LOCALE_KEY, "an inline label map is keyed by BCP-47 locale tags (`en`, `zh-CN`, …) or `default` — not by `key`/`defaultValue`, which was the retired key-reference form (#5055) and resolves to nothing"),
z.string()
);
var I18nLabelSchema = z.union([z.string(), InlineLocaleMapSchema]);

Executed probes against dist/ui/index.mjs (safeParse, this worktree, spec 17.0.0):

inputresult
'Save'ACCEPTED
''ACCEPTED
{ en: 'Save', 'zh-CN': '保存' }ACCEPTED
{ default: 'Save' }ACCEPTED
{ key: 'common.save', defaultValue: 'Save' }REJECTED
{ notALocale: 'Save' }REJECTED
{ en: 42 } / 42 / nullREJECTED

The rejection carries the bespoke message, nested one level under invalid_union at path: ["defaultValue"], code: "invalid_key":

an inline label map is keyed by BCP-47 locale tags (`en`, `zh-CN`, …) or `default` — not by
`key`/`defaultValue`, which was the retired key-reference form (#5055) and resolves to nothing

Per-key admission, probed one key at a time: en, zh-CN, zh-Hans-CN, default, EN ACCEPTED; defaultValue, notALocale, x-private, e REJECTED.

So the ruling's description matches the installed package exactly, and 回翻条款 did not fire this round. Two authorized forms; the key-reference form rejected with its own error.

Two measured details that changed the wording

  1. objectui resolves the map form on the two keys the guide's example uses — checked rather than assumed, because a guide that shows a shape the renderer drops is worse than one that omits it. ComponentRegistry.register('card', PageCardRenderer, …) (packages/components/src/renderers/layout/containers.tsx:775) resolves schema.title through pickLocalized at :739; ElementButtonRenderer resolves props.label at packages/components/src/renderers/basic/elements.tsx:230. The new example puts an inline map on exactly those two keys and nowhere else.
  2. The {key, defaultValue} bullet keeps its conclusion and gains a true reason. Executed: pickLocalized({ key: 'common.save', defaultValue: 'Save' }, 'en') returns 'common.save' — the last-resort "first string value" limb — so a map-shaped key-reference object that reaches a renderer paints the raw translation key on screen. That, plus the spec's parse-time rejection, replaces the old false reason ("uses plain strings only").

The pairing, and reverse-verification of both halves

The ledger's downward ratchet says no entry may outlive the claim it excuses, so the fix and the KNOWN_CLAIMS deletion are one commit. Predictions were written before each run.

Leg A — revert the ledger half only (stale entry restored, guide fixed). Predicted: downward ratchet red, upward green.

× keeps the inventory honest - no entry may outlive the claim it excuses
AssertionError: KNOWN_CLAIMS names version claims that are no longer in the tree:
Tests 1 failed | 17 passed (18)

Leg B — revert the guide half only (v4 back on :117/:162, entry stays deleted). Predicted: upward ratchet red, downward green.

× records every version literal on the scanned surfaces
- skills/objectui/guides/i18n.md:117 "@objectstack/spec` v4"
- skills/objectui/guides/i18n.md:162 "@objectstack/spec` v4"
Tests 1 failed | 17 passed (18)

Both legs landed in the predicted direction, each naming the other ratchet's test as still green — so the pairing is load-bearing in both directions, not incidental. No rebuild sits between the ablation and the thing under test: this gate reads the markdown and the manifests straight off disk with fs.readFileSync from repoRoot, and the test file is the ledger, so there is no dist in the resolution path for a stale artifact to hide in.

Restored with git checkout HEAD -- ... after each leg; git status --porcelain is empty and git diff HEAD is empty — a byte-identical restore, not an approximate one.

Gates run locally, all at 600807f8a (final commit, clean tree)

gateresult
pnpm exec vitest run scripts/__tests__/doc-version-claims.test.tsTest Files 1 passed (1) / Tests 18 passed (18)
pnpm check:control-bytesOK — 4521 tracked text files scanned
pnpm check:skills-pathsOK — 95/96 stated paths resolve, 1 baselined
pnpm check:doc-typesOK — every documented component type is registered
node scripts/check-changeset-presence.mjsOK — 3 files changed, 0 under a released package's src/, 1 changeset added
check-changeset-no-major.mjs / check-changeset-fixed.mjsOK
pnpm type-check:scriptsexit 0
eslint scripts/__tests__/doc-version-claims.test.tsexit 0

Heavy runs were serialized under /tmp/os-heavy-verify.lock.

One gate deliberately not claimed green, because a green from it would have been false.pnpm check:doc-snippets exits 0 here with "The snippet program was NOT run: the packages it resolves against are not built" — a silent no-op locally. It would not have covered this change anyway: that gate's scan surface is stated in its own header as content/docs plus packages/(name)/README.md, and skills/ is not in it, so the new snippet is compiled by no typechecker on any machine. It was hand-checked against the schema instead (detail 1 above). Filed as a finding — see the linked issue on #5081.

Counter-probe of the zero

Before claiming no other @objectstack/spec v4 attribution survives, the same search was run with a version string known to be present:

  • Zero-candidate@objectstack/spec + a v4-style qualifier across skills/, content/docs/, packages/*/README.md: two hits remain, both on content/docs/guide/release-notes.md (:16, :55). They sit under the ## v3.3.0 — 2026-04-17 heading, are historically accurate (that release did align with ^4.0.4), and are structurally exempt from the ratchet by the version-heading rule. Not fossils, and not this card's surface.
  • Positive control 1 — same regex shape with 17: hits skills/objectui/guides/schema-expressions.md:262 and packages/app-shell/README.md:319. The pattern finds live claims.
  • Positive control 2 — bare v4 on the same surfaces: hits Tailwind v4 lines in styling.md, project-setup.md, theming.md and the actions/checkout@v4 block in ci-cd-pipeline.md. The corpus is being read.

So the accurate statement is not "no v4 remains" but "the only surviving @objectstack/spec v4 attributions are historical release notes, exempt by design".

Scope

Two files plus a changeset. skills/objectui/guides/page-builder.md (sibling #5352) was not touched — verified in the diff.

 .changeset/i18n-guide-label-rule-5081.md | new
scripts/__tests__/doc-version-claims.test.ts | 28 +++++++++-------
skills/objectui/guides/i18n.md | 32 ++++++++++++++++--

Side effect worth naming for the reviewer: two passages in the ledger's header say "NO stale entry remains" (:291) and "NONE remains today" (:632). Those were written at the census and were falsified when #4981 inventoried a tenth stale entry. Deleting that entry makes both sentences true again, so they are left untouched rather than edited.

Generated by Claude Code


Generated by Claude Code

`skills/objectui/guides/i18n.md` backed its label rule with `@objectstack/spec`
v4 on lines 117 and 162 while every manifest here declares `^17.0.0`. Measured
against the installed 17.0.0 dist, the rule was wrong too: `I18nLabelSchema` is
a union of a plain string AND an inline locale map keyed by BCP-47 tags or
`default`, so neither renumbering the qualifier nor deleting it was writable —
both keep a false sentence. Maintainer ruling 2026-08-20, option A: restate the
rule per the installed spec, with no version qualifier.
The `{key, defaultValue}` advice stays and gains its real reason: that
key-reference vocabulary was retired (objectstack#5055), the spec rejects the
object with its own message, and `pickLocalized` would paint the raw key.
Same commit, as the ratchet requires: the `KNOWN_CLAIMS` `stale` entry that
inventoried the fossil is deleted, and the ledger header prose that restated
the now-falsified two-branch fork is corrected.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RV6yuVCxymHYE16PL9vQkE
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills/objectui/guides/i18n.md 两处把 label 规则归给 @objectstack/spec v4,仓内实测 ^17.0.0 —— agent 面上 13 个 major 的版本化石

2 participants

@os-support-ai@claude