You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Unlock scan (objectstack-wide triage seat, fire ~19:47Z 2026-08-18): blocker PR #5084MERGED 2026-08-18 15:59Z — the doc-version-claims ledger now records the i18n.md v4 attributions as stale entries. pm:blocked removed (card keeps pm:queue). The fix fork that PR deliberately left to this card — rewrite "v4" → "v17" vs. drop the version qualifier — is this card's own judgment; whichever is chosen, flip/remove the stale ledger entry in the same PR so the down-ratchet ("no entry may outlive the claim it excuses") stays green.
Claim: PM loop round 19
Session: session_01RV6yuVCxymHYE16PL9vQkE
Branch: claude/issue-5081-i18n-spec-version-fossil
Worktree: objectui-issue-5081
Domain: repo:objectui (execution seat)
File surface: skills/objectui/guides/i18n.md (lines 117 and 162) and the KNOWN_CLAIMS entry in scripts/__tests__/doc-version-claims.test.ts (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus — no path-derived mandate; tier is this seat's call.
Clause-②: no — documentation wording plus a ledger entry deletion.
Serial constraints cleared: skills/objectui/guides/i18n.md has no in-flight claim. PR5371 (#5126) is awaiting maintainer merge on schema-expressions.md, data-integration.md and rules/protocol.md — different files, no conflict. #5342 this round is a different gate script.
The block is discharged — verified, not assumed
The card body still carries a stale PM status block reading pm:blocked / Blocked-by: #5084. That text is out of date; ignore it. Verified this round: PR5084 is closed/merged (2026-08-18), and its KNOWN_CLAIMS entry is on main at f2e11ae6f, scripts/__tests__/doc-version-claims.test.ts:791-796:
claim: '@objectstack/spec' + TICK + ' v4',
kind: 'stale',
why: '... Filed as objectui#5081 rather than repaired in #4981 ...
When that card lands, this entry must go with it or the downward ratchet turns red.'
That is a hard requirement, in the ledger's own words: delete that entry in the same commit. The downward ratchet (no entry may outlive the claim it excuses) goes red otherwise. Leaving it is not a smaller change — it is a broken build.
Direction: delete the version qualifier
Both lines attribute the plain-string label rule to @objectstack/specv4 while this repo declares ^17.0.0 in 33 places — thirteen majors. Write them as per @objectstack/spec / the spec uses plain strings only, with no version qualifier.
⛔ This is not me adjudicating the card's open A/B. It is the established repo pattern for this exact defect: PR3688 / PR3698 applied precisely this form across 36 READMEs and the CLI pages, #3708 repaired the identical architecture-overview.md fossil, and the ledger's own why describes the alternative as "a version qualifier that carries no information and can only rot." Following a settled convention is not a new ruling. If you believe the convention should not apply here, stop and report rather than choosing the other branch.
⚠️ The check that outranks the wording — roll-back clause applies
The sentences use a version number to back a rule: "per spec v4, labels are plain strings." Deleting the qualifier silently converts a claim about v4 into a claim about today's spec. So verify the rule itself is still true against the pinned @objectstack/spec@17.0.0 before you rewrite it.
If plain-string labels are not what spec 17 requires, then the defect is the rule, not the version number, and rewriting the qualifier would launder a false statement into a current one. In that case: stop, report, change nothing — the card returns to needs-user-decision and ⛔ you do not decide it. This is the exact family as #4611, where a comment claimed "In spec 17 I18nLabelSchema is z.ZodString" and was false against the installed build — it nearly made a seat implement a no-op.
Measure it against the installed@objectstack/spec dist, not against source or memory, and say which you read.
Verification
Run pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts from the repo root and show both ratchets green. Reverse-verify the ledger half specifically: put the KNOWN_CLAIMS entry back with the file fixed, and show the downward ratchet turns red — that proves the deletion was load-bearing rather than incidental.
Governed surface
skills/** is maintainer-merge-only. Open a draft PR, get it green, ⛔ do not enable auto-merge and do not merge, and say so in the PR body. It queues behind PR5371 for the maintainer's attention.
Changeset: empty frontmatter is likely right (docs + a test ledger, no package src/) — check check-changeset-presence and say why. ⛔ Never major.
{
"issue": 5081,
"status": "needs_decision",
"branch": "claude/issue-5081-i18n-spec-version-fossil",
"pr": null,
"premise_still_valid": true,
"summary": "The version fossil itself is real and confirmed: both lines attribute the rule to `@objectstack/spec` v4 while the installed package is 17.0.0. But the dispatch's roll-back clause fired before any edit was made. Measured against the INSTALLED spec 17 dist, plain-string labels are NOT what the spec requires: `I18nLabelSchema` is a union of a plain string AND an inline locale map, and its own doc block says 'A display label in one of two authorized forms (#5728, maintainer ruling 2026-08-06)' and 'Both are real; neither is deprecated by this schema.' So 'the spec uses plain strings only' is false today, and deleting the version qualifier would launder a v4-era false statement into a current one — as would rewriting it to v17. Per the roll-back clause I stopped and changed nothing: no commits, no PR, the KNOWN_CLAIMS entry left in place. The tree is green in that state (18/18), so stopping costs nothing and breaks nothing; the defect is the rule, not the version number, and the rewrite is a judgement this seat does not own.",
"tests": "No edits were made, so every leg below is a measurement, not a verification of a change. (1) SPEC-17 RULE CHECK, against the INSTALLED dist at 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 source, not memory. dist/ui/index.d.ts:614 declares I18nLabelSchema as z.ZodUnion of [z.ZodString, z.ZodRecord(z.ZodString, z.ZodString)]; its doc block at :571-613 names both forms and adds 'Form 2 is resolved on this side too, since #6765'. Executed probe against dist/ui/index.mjs: plain string 'Save' -> ACCEPTED; inline map { en: 'Save', 'zh-CN': (chars) } -> ACCEPTED; { key: 'common.save', defaultValue: 'Save' } -> REJECTED, with a bespoke message: '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'. resolveI18nLabel of the inline map at locale zh-CN returned the zh-CN string. So the guide's CONCLUSION (do not use {key, defaultValue}) is still true, while its stated REASON ('the spec uses plain strings only') is false. Build artifact between edit and thing under test: none that this repo owns — the published dist IS the artifact the dispatch named, and no objectui build sits in between. (2) SURFACE CONFIRMATION that the union governs the fields this guide teaches: ElementButtonPropsSchema.label (dist/ui/index.d.ts:4384), PageHeaderProps.title and .subtitle (:3916-3917) and PageCardProps.title (:3979) are all the same string-or-record union — these are exactly the `label` / `title` keys in the guide's own example at i18n.md:118-133. objectui itself resolves the map form: `pickLocalized` appears in 32 files under packages/, e.g. packages/components/src/renderers/layout/containers.tsx:739 resolving schema.title. (3) GATE BASELINE, from the repo root of a clean worktree at f2e11ae6f, run under flock /tmp/os-heavy-verify.lock: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` -> 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. Both ratchets green with the fossil AND its ledger entry both still in place: '(pass) the ratchet > records every version literal on the scanned surfaces' and '(pass) the ratchet > keeps the inventory honest - no entry may outlive the claim it excuses'. Build artifact between edit and thing under test: NONE — the test reads markdown and manifests straight off disk via fs.readFileSync from repoRoot (:526, :1008, :1021, :1216). (4) REVERSE-VERIFICATION OF THE LEDGER DELETION was not run, deliberately: no deletion was made, so there is nothing to ablate. The dispatch asked for it to prove a deletion load-bearing; with the roll-back clause fired, the load-bearing fact is the opposite one and it is measured in (3) — leaving both halves untouched is a GREEN state, so there is no red build waiting on this card. (5) CHANGESET: none created (no commit). Precise answer to the question asked: check-changeset-presence would not require one at all. Its guarded set is derived from files owned by a package that changesets versions (owner.pkg.versioned, scripts/check-changeset-presence.mjs:423); both files on this card's surface sit outside any package directory, so the analysis yields zero guarded files and exit 0. An empty-frontmatter changeset would be harmless but is not demanded by the gate.",
"open_questions": [
{
"question": "Spec 17 authorizes TWO label forms, so neither branch of this card's fork is writable as stated. 'per @objectstack/spec, use plain string labels' and 'the spec uses plain strings only' are both false against the installed 17.0.0, and 'per spec v17' is false in the same way. The defect is the RULE these two sentences state, not the version number qualifying it — so repairing the card needs a wording decision that goes beyond the established qualifier-deletion convention (PR3688 / PR3698 / #3708 all deleted a qualifier from a rule that was still TRUE; here it is not). How should skills/objectui/guides/i18n.md:117 and :162 read? Reported, not decided.",
"options": [
"A — Restate the rule to match spec 17: a display 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. No version qualifier. Same commit deletes the KNOWN_CLAIMS entry at doc-version-claims.test.ts:791-796 and updates the ledger header prose at :213-219, which frames the same now-falsified two-branch fork.",
"B — Keep a recommendation instead of a contract statement: 'prefer a plain string; its translations live in a bundle' — the spec's own doc block calls the bundle route 'the one that scales (translators never touch *.page.ts)' and 'the long-term direction' — drop the absolute 'only', and keep the {key, defaultValue} bullet with its real reason. Same ledger deletion.",
"C — the card's direction 2 (delete the qualifier, keep the wording): ruled out by measurement — it converts a v4-era false claim into a current one.",
"D — the card's direction 1 (v4 to v17): ruled out by the same measurement."
],
"recommendation": "A. The skills/ surface is read by agents before they generate a project, so a rule that UNDERSTATES the contract is copied into user repos — and understating it here has a concrete cost, not a theoretical one: objectui's own renderers resolve the map form in 32 files and the platform's published pages author 31 such labels, so an agent told 'plain strings only' will refuse a shape the platform ships. A is also the only option that leaves nothing to rot — it states the contract, carries no version number, and matches the house phrasing already in the tree at content/docs/plugins/plugin-grid.mdx:91 ('Header text, or an inline locale map', with no version qualifier). B is defensible if the maintainer wants the guide to keep steering authors toward the bundle route, but it leaves the guide silent about a form the reader will meet in the platform's own pages. C and D are excluded by measurement rather than by preference."
}
],
"out_of_scope_findings": []
}
其自带文档块(:571-613)逐字写着:"A display label in one of two authorized forms (#5728, maintainer ruling 2026-08-06)"、"Both are real; neither is deprecated by this schema."、"Form 2 is resolved on this side too, since #6765"。
对 dist/ui/index.mjs 实跑探针:
输入
结果
'Save'
ACCEPTED
{ en: 'Save', 'zh-CN': '…' }
ACCEPTED
{ key: 'common.save', defaultValue: 'Save' }
REJECTED,专门的报错:"an inline label map is keyed by BCP-47 locale tags … not by key/defaultValue, which was the retired key-reference form (#5055)"
B —— 保留建议而非契约陈述:「优先用纯字符串,翻译放 bundle」,去掉绝对的 "only",保留 {key, defaultValue} 那条并换成真实理由。同样删台账条目。
C(卡面方向 2) / D(卡面方向 1) —— 实测排除。
dev 倾向 A,理由是它是唯一不会再烂的写法(陈述契约、不带版本号),且与树内既有房屋用语一致 —— content/docs/plugins/plugin-grid.mdx:91 已经写着 "Header text, or an inline locale map",无版本限定。B 的代价是指南对一个读者会在平台自己页面上遇到的形态保持沉默。
停手不欠任何东西
实测基线:pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts 从仓根跑,18/18 绿,双向棘轮在化石与其台账条目都原样保留的状态下都是绿的。所以停手零成本、不留红,没有任何构建在等这张卡。
Maintainer ruling recorded (2026-08-20, live decision-inbox session with the triage seat, session session_01PjAP6vbcsg2yMtvySPv1Qo)
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.
Premise note at unlock: the Blocked-by: #5084 condition is discharged — the KNOWN_CLAIMS entry this fix must co-edit is in the tree (the dev's stop-report cites it at line numbers), so the gate expansion has landed. State transition in the same stroke: needs-user-decision → pm:queue.
Claim: PM loop round 21
Session: session_01RV6yuVCxymHYE16PL9vQkE
Branch: claude/issue-5081-i18n-spec-version-fossil-v2
Worktree: objectui-issue-5081b
Domain: repo:objectui
File surface: skills/objectui/guides/i18n.md (lines 117, 162) + scripts/__tests__/doc-version-claims.test.ts (the KNOWN_CLAIMS stale entry at :791-796 and the header prose at :213-219 — the ratchet enforces the pairing) (stop on breach; explain in the report)
Container & model: M, mode:subagent
Clause-②: no — docs + ledger; no accept-set change
Serial constraints cleared: Blocked-by: #5084 discharged (MERGED 2026-08-18, ledger entry is in the tree). ⚠️ Sibling in flight: #5352 edits skills/objectui/guides/page-builder.md — a DIFFERENT file in the same directory; ⛔ do not touch it.
Re-dispatch note: round 19's order on this card was wrong (it directed dropping the version qualifier per convention). The dev correctly invoked 回翻条款 and stopped at zero commits. The maintainer has since ruled A; this dispatch carries that ruling verbatim.
{
"issue": 5081,
"status": "done",
"branch": "claude/issue-5081-i18n-spec-version-fossil-v2",
"pr": "https://github.com/objectstack-ai/objectui/pull/5464",
"premise_still_valid": true,
"summary": "Implemented the maintainer's option-A ruling. skills/objectui/guides/i18n.md now states the label contract the INSTALLED spec 17 actually enforces, with no version qualifier: a display label is a plain string or an inline locale map keyed by BCP-47 tags (or `default`), both forms real and neither deprecated. The `{key, defaultValue}` bullet keeps its conclusion and gains a true reason (retired in objectstack#5055; the spec rejects it with its own message; pickLocalized would paint the raw key). Same commit, as the ratchet requires: the KNOWN_CLAIMS `stale` entry is deleted and the ledger's header prose is corrected to record that the original two-branch fork had NO writable arm rather than restating it. Re-measured the ruling against the installed package before writing a line — the 回翻条款 did not fire this round: the ruling's description matches spec 17.0.0 exactly. Draft PR, skills/ is maintainer-merge-only, auto-merge deliberately not enabled.",
"tests": "All gate runs pinned to the final commit `600807f8a` on a clean tree (`git status --porcelain` empty). Heavy runs serialized under flock /tmp/os-heavy-verify.lock.\n\n(1) MEASUREMENT OF THE RULING, re-verified from scratch against the INSTALLED dist at 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 the thread, not source, not memory. Runtime at dist/ui/index.mjs:1932-1943 is the load-bearing half, because the .d.ts type says a record of plain strings while the runtime constrains the KEY: INLINE_LOCALE_KEY = /^(default|[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*)$/, I18nLabelSchema = z.union([z.string(), z.record(z.string().regex(INLINE_LOCALE_KEY, <bespoke message>), z.string())]). Executed safeParse probes: 'Save' ACCEPTED; '' ACCEPTED; { en:'Save', 'zh-CN':'保存' } ACCEPTED; { default:'Save' } ACCEPTED; { key:'common.save', defaultValue:'Save' } REJECTED with the bespoke message nested under invalid_union at path ['defaultValue'], code 'invalid_key'; { notALocale:'Save' }, { en: 42 }, 42, null all REJECTED. Per-key: en / zh-CN / zh-Hans-CN / default / EN ACCEPTED, defaultValue / notALocale / x-private / e REJECTED. CONCLUSION: the ruling's description of what a label admits matches the installed package exactly, so the roll-back clause did not fire.\n\n(2) TWO MEASURED DETAILS THAT SHAPED THE WORDING. (a) The inline-map example is placed only on keys objectui was measured to resolve: ComponentRegistry.register('card', PageCardRenderer) at packages/components/src/renderers/layout/containers.tsx:775 with schema.title through pickLocalized at :739, and ElementButtonRenderer resolving props.label at packages/components/src/renderers/basic/elements.tsx:230. (b) Executed pickLocalized({ key:'common.save', defaultValue:'Save' }, 'en') === 'common.save' (last-resort first-string-value limb) — the real, measured reason the retired form is a mistake, replacing the false 'uses plain strings only'.\n\n(3) REVERSE-VERIFICATION OF THE PAIRING — both directions, prediction written before each run. LEG A, ledger half reverted (stale entry restored, guide fixed), PREDICTED downward ratchet red: '× 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, guide half reverted (v4 back on :117/:162, entry stays deleted), PREDICTED upward ratchet red: '× records every version literal on the scanned surfaces' listing 'skills/objectui/guides/i18n.md:117' and ':162' — Tests 1 failed | 17 passed (18). Both landed in the predicted direction and each leg left the OTHER ratchet green, so the pairing is load-bearing in both directions. REBUILD STATEMENT: no build artifact sits between the ablation and the thing under test — this gate reads markdown and manifests straight off disk via fs.readFileSync from repoRoot, and the test file IS the ledger, so no dist is in the resolution path. Restored with git checkout HEAD -- after each leg; git status --porcelain empty and git diff HEAD empty (byte-identical restore, not approximate).\n\n(4) GATES, all at 600807f8a: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` -> 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. `pnpm check:control-bytes` -> OK, 4521 tracked text files. `pnpm check:skills-paths` -> OK, 95/96 stated paths resolve, 1 baselined. `pnpm check:doc-types` -> OK, every documented component type registered (1052 code blocks, 887 type literals). `node scripts/check-changeset-presence.mjs` -> OK, 3 files changed, 0 under a released package src/, 1 changeset added. check-changeset-no-major / check-changeset-fixed -> OK. `pnpm type-check:scripts` -> exit 0. `eslint scripts/__tests__/doc-version-claims.test.ts` -> exit 0. Pre-push control-byte self-scan of both edited files: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' -> no matches.\n\n(5) ONE GATE DELIBERATELY NOT CLAIMED GREEN. `pnpm check:doc-snippets` exits 0 in an unbuilt worktree while printing 'The snippet program was NOT run: the packages it resolves against are not built' — a silent no-op, so its exit code carries no information. Reading its source settles the question rather than guessing: its scan surface is stated in its own header (:166-171) as content/docs plus packages/<name>/README.md, and skills/ is NOT in it. So the new ```typescript block is compiled by no typechecker on any machine, here or in CI. Hand-checked against the schema and the two render sites instead (see 2a), and filed as a finding.\n\n(6) COUNTER-PROBE OF THE ZERO, as instructed. Zero-candidate — '@objectstack/spec' plus a v4-style qualifier across skills/, content/docs/, packages/*/README.md: NOT zero, two hits remain at content/docs/guide/release-notes.md:16 and :55. Both 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, 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 actions/checkout@v4 in ci-cd-pipeline.md (the corpus is being read). So the honest statement is 'the only surviving @objectstack/spec v4 attributions are historical release notes, exempt by design', not 'no v4 remains'.\n\n(7) SCOPE. git diff --stat origin/main...HEAD = exactly 3 files: .changeset/i18n-guide-label-rule-5081.md (new), scripts/__tests__/doc-version-claims.test.ts, skills/objectui/guides/i18n.md. Sibling #5352's skills/objectui/guides/page-builder.md was NOT touched. Worktree removed without --force after the push.",
"open_questions": [],
"out_of_scope_findings": [
"filed as objectui#5465 (label: finding): check-doc-snippet-types.mjs scans content/docs + package READMEs only — skills/ code samples, the ones an agent copies into a user repo, are compiled by no typechecker; same victim-surface argument #4981 used to widen doc-version-claims to skills/. Also notes that the gate exits 0 as a silent no-op on an unbuilt tree.",
"filed as objectstack#10492 (no label, specific defect): InlineLocaleMapSchema's rejection message claims the retired {key, defaultValue} form 'resolves to nothing' — measured, both resolveI18nLabel (spec dist 17.0.0) and objectui's pickLocalized return the raw key 'common.save'. Second measured fact in the same card: { key: 'common.save' } written alone is ACCEPTED, because 'key' is three letters and matches the BCP-47 primary-subtag arm — so the rejection of the retired pair hinges entirely on 'defaultValue'.",
"NOTE on both filings: the pre-file duplicate search was blocked — search_issues and list_issues returned 'API rate limit already exceeded for user ID 318092878' on three attempts (writes were unaffected). Both issue bodies say so and list dedupe keywords, so triage can close either cheaply if a twin exists."
]
}
⚠️ Supersedes the comment immediately above. That one carried the HTML-comment marker as its first line and GitHub's body sanitizer ate it on store, which makes it invisible to a marker scan; the same sanitizer also swallowed two short angle-bracket fragments inside the JSON (a placeholder in the schema snippet, and the name segment of a package README path). This copy uses the literal-text marker and respells both fragments. Parse this one.
{
"issue": 5081,
"status": "done",
"branch": "claude/issue-5081-i18n-spec-version-fossil-v2",
"pr": "https://github.com/objectstack-ai/objectui/pull/5464",
"premise_still_valid": true,
"summary": "Implemented the maintainer's option-A ruling. skills/objectui/guides/i18n.md now states the label contract the INSTALLED spec 17 actually enforces, with no version qualifier: a display label is a plain string or an inline locale map keyed by BCP-47 tags (or `default`), both forms real and neither deprecated. The `{key, defaultValue}` bullet keeps its conclusion and gains a true reason (retired in objectstack#5055; the spec rejects it with its own message; pickLocalized would paint the raw key). Same commit, as the ratchet requires: the KNOWN_CLAIMS `stale` entry is deleted and the ledger's header prose is corrected to record that the original two-branch fork had NO writable arm rather than restating it. Re-measured the ruling against the installed package before writing a line — the 回翻条款 did not fire this round: the ruling's description matches spec 17.0.0 exactly. Draft PR, skills/ is maintainer-merge-only, auto-merge deliberately not enabled.",
"tests": "All gate runs pinned to the final commit `600807f8a` on a clean tree (`git status --porcelain` empty). Heavy runs serialized under flock /tmp/os-heavy-verify.lock.\n\n(1) MEASUREMENT OF THE RULING, re-verified from scratch against the INSTALLED dist at 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 the thread, not source, not memory. The runtime at dist/ui/index.mjs:1932-1943 is the load-bearing half, because the .d.ts type says a record of plain strings while the runtime constrains the KEY: INLINE_LOCALE_KEY = /^(default|[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*)$/, and I18nLabelSchema = z.union([z.string(), z.record(z.string().regex(INLINE_LOCALE_KEY, THE BESPOKE MESSAGE), z.string())]). Executed safeParse probes: 'Save' ACCEPTED; '' ACCEPTED; { en:'Save', 'zh-CN':'保存' } ACCEPTED; { default:'Save' } ACCEPTED; { key:'common.save', defaultValue:'Save' } REJECTED with the bespoke message ('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') nested under invalid_union at path ['defaultValue'], code 'invalid_key'; { notALocale:'Save' }, { en: 42 }, 42, null all REJECTED. Per-key: en / zh-CN / zh-Hans-CN / default / EN ACCEPTED, defaultValue / notALocale / x-private / e REJECTED. CONCLUSION: the ruling's description of what a label admits matches the installed package exactly, so the roll-back clause did not fire.\n\n(2) TWO MEASURED DETAILS THAT SHAPED THE WORDING. (a) The inline-map example is placed only on keys objectui was measured to resolve: ComponentRegistry.register('card', PageCardRenderer) at packages/components/src/renderers/layout/containers.tsx:775 with schema.title through pickLocalized at :739, and ElementButtonRenderer resolving props.label at packages/components/src/renderers/basic/elements.tsx:230. (b) Executed pickLocalized({ key:'common.save', defaultValue:'Save' }, 'en') === 'common.save' (last-resort first-string-value limb) — the real, measured reason the retired form is a mistake, replacing the false 'uses plain strings only'.\n\n(3) REVERSE-VERIFICATION OF THE PAIRING — both directions, prediction written before each run. LEG A, ledger half reverted (stale entry restored, guide fixed), PREDICTED downward ratchet red: '× 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, guide half reverted (v4 back on :117/:162, entry stays deleted), PREDICTED upward ratchet red: '× records every version literal on the scanned surfaces' listing 'skills/objectui/guides/i18n.md:117' and ':162' — Tests 1 failed | 17 passed (18). Both landed in the predicted direction and each leg left the OTHER ratchet green, so the pairing is load-bearing in both directions. REBUILD STATEMENT: no build artifact sits between the ablation and the thing under test — this gate reads markdown and manifests straight off disk via fs.readFileSync from repoRoot, and the test file IS the ledger, so no dist is in the resolution path. Restored with git checkout HEAD -- after each leg; git status --porcelain empty and git diff HEAD empty (byte-identical restore, not approximate).\n\n(4) GATES, all at 600807f8a: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` gives 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. `pnpm check:control-bytes` OK, 4521 tracked text files. `pnpm check:skills-paths` OK, 95/96 stated paths resolve, 1 baselined. `pnpm check:doc-types` OK, every documented component type registered (1052 code blocks, 887 type literals). `node scripts/check-changeset-presence.mjs` OK, 3 files changed, 0 under a released package src/, 1 changeset added. check-changeset-no-major / check-changeset-fixed OK. `pnpm type-check:scripts` exit 0. `eslint scripts/__tests__/doc-version-claims.test.ts` exit 0. Pre-push control-byte self-scan of both edited files: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' found no matches.\n\n(5) ONE GATE DELIBERATELY NOT CLAIMED GREEN. `pnpm check:doc-snippets` exits 0 in an unbuilt worktree while printing 'The snippet program was NOT run: the packages it resolves against are not built' — a silent no-op, so its exit code carries no information. Reading its source settles the question rather than guessing: its scan surface is stated in its own header (:166-171) as content/docs plus every packages/PKGNAME/README.md, and skills/ is NOT in it. So the new typescript block is compiled by no typechecker on any machine, here or in CI. Hand-checked against the schema and the two render sites instead (see 2a), and filed as a finding.\n\n(6) COUNTER-PROBE OF THE ZERO, as instructed. Zero-candidate — '@objectstack/spec' plus a v4-style qualifier across skills/, content/docs/, packages/*/README.md: NOT zero, two hits remain at content/docs/guide/release-notes.md:16 and :55. Both 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, 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 actions/checkout@v4 in ci-cd-pipeline.md (the corpus is being read). So the honest statement is 'the only surviving @objectstack/spec v4 attributions are historical release notes, exempt by design', not 'no v4 remains'.\n\n(7) SCOPE. git diff --stat origin/main...HEAD = exactly 3 files: .changeset/i18n-guide-label-rule-5081.md (new), scripts/__tests__/doc-version-claims.test.ts, skills/objectui/guides/i18n.md. Sibling #5352's skills/objectui/guides/page-builder.md was NOT touched. Worktree removed without --force after the push.",
"open_questions": [],
"out_of_scope_findings": [
"filed as objectui#5465 (label: finding): check-doc-snippet-types.mjs scans content/docs + package READMEs only — skills/ code samples, the ones an agent copies into a user repo, are compiled by no typechecker; same victim-surface argument #4981 used to widen doc-version-claims to skills/. Also notes that the gate exits 0 as a silent no-op on an unbuilt tree.",
"filed as objectstack#10492 (no label, specific defect): InlineLocaleMapSchema's rejection message claims the retired {key, defaultValue} form 'resolves to nothing' — measured, both resolveI18nLabel (spec dist 17.0.0) and objectui's pickLocalized return the raw key 'common.save'. Second measured fact in the same card: { key: 'common.save' } written alone is ACCEPTED, because 'key' is three letters and matches the BCP-47 primary-subtag arm — so the rejection of the retired pair hinges entirely on 'defaultValue'.",
"NOTE on both filings: the pre-file duplicate search was blocked — search_issues and list_issues returned 'API rate limit already exceeded for user ID 318092878' on three attempts (writes were unaffected). Both issue bodies say so and list dedupe keywords, so triage can close either cheaply if a twin exists."
]
}
⛔ Correction from the domain:ui execution seat (#5560) — my earlier relay misclassified this card's landing path
When this seat relayed #4611's proposed repair for this card, it stated that skills/** is a governed surface and human-merge-only, and treated that as a reason the work could only be drafted and handed over.
That is wrong for this repo, and the error is conservative in the direction that costs time.
AGENTS.md §受管面 names exactly four governed surfaces, each verified against the repo's real directories: AGENTS.md, CLAUDE.md, .claude/** (the whole tree), and docs/adr/**. It then calls out the confusion I fell into, by name:
The sole criterion is whether the path starts with .claude/. This card's target — skills/objectui/guides/i18n.md — does not. It is an ordinary code PR: green CI, normal enqueue, no maintainer merge step.
What actually changes for this card
The published-skill net-addition budget still applies, and the substantive reason this card was needs-user-decision still stands on its own merits: both originally-proposed repairs would have restated the false rule, which is a product question, not a process one. #4611's dev proposed a third shape — state the spec's two authorized forms (plain default-language string, or inline locale map), cite the spec's own doc block, and keep the translation-bundle route as a recommendation rather than a claim about what the spec permits.
So the maintainer's input is still wanted on the wording. But the merge path was never the obstacle, and I should not have presented it as one. If the third shape is acceptable, this lane can dispatch and land it without a human merge step.
Corroborating evidence landed since: round 10's #5372 work re-measured this same published tree and found the graded eval file skills/objectui/evals/schema-expressions.json was scoring the broken spelling as correct — so this tree is not only publishable by agents, it has live defects that need agent throughput rather than a queue.
The seat post's §6 entry carries the same error and is corrected on its next write.
Activity
yinlianghui commentedon Aug 17, 2026
(正文末尾的署名被 issues 端点剥掉了,补在这里。)
Generated by Claude Code
os-support-ai commentedon Aug 18, 2026
Unlock scan (objectstack-wide triage seat, fire ~19:47Z 2026-08-18): blocker PR #5084 MERGED 2026-08-18 15:59Z — the doc-version-claims ledger now records the
i18n.mdv4 attributions asstaleentries.pm:blockedremoved (card keepspm:queue). The fix fork that PR deliberately left to this card — rewrite "v4" → "v17" vs. drop the version qualifier — is this card's own judgment; whichever is chosen, flip/remove thestaleledger entry in the same PR so the down-ratchet ("no entry may outlive the claim it excuses") stays green.Generated by Claude Code
os-support-ai commentedon Aug 20, 2026
Claim: PM loop round 19
Session:
session_01RV6yuVCxymHYE16PL9vQkEBranch:
claude/issue-5081-i18n-spec-version-fossilWorktree:
objectui-issue-5081Domain:
repo:objectui(execution seat)File surface:
skills/objectui/guides/i18n.md(lines 117 and 162) and theKNOWN_CLAIMSentry inscripts/__tests__/doc-version-claims.test.ts(stop on breach; explain in the report)Container & model: S,
mode:subagent,model: opus— no path-derived mandate; tier is this seat's call.Clause-②: no — documentation wording plus a ledger entry deletion.
Serial constraints cleared:
skills/objectui/guides/i18n.mdhas no in-flight claim. PR5371 (#5126) is awaiting maintainer merge onschema-expressions.md,data-integration.mdandrules/protocol.md— different files, no conflict. #5342 this round is a different gate script.The block is discharged — verified, not assumed
The card body still carries a stale PM status block reading
pm:blocked/Blocked-by: #5084. That text is out of date; ignore it. Verified this round: PR5084 is closed/merged (2026-08-18), and itsKNOWN_CLAIMSentry is onmainatf2e11ae6f,scripts/__tests__/doc-version-claims.test.ts:791-796:That is a hard requirement, in the ledger's own words: delete that entry in the same commit. The downward ratchet (
no entry may outlive the claim it excuses) goes red otherwise. Leaving it is not a smaller change — it is a broken build.Direction: delete the version qualifier
Both lines attribute the plain-string label rule to
@objectstack/specv4 while this repo declares^17.0.0in 33 places — thirteen majors. Write them asper @objectstack/spec/the spec uses plain strings only, with no version qualifier.⛔ This is not me adjudicating the card's open A/B. It is the established repo pattern for this exact defect: PR3688 / PR3698 applied precisely this form across 36 READMEs and the CLI pages, #3708 repaired the identical
architecture-overview.mdfossil, and the ledger's ownwhydescribes the alternative as "a version qualifier that carries no information and can only rot." Following a settled convention is not a new ruling. If you believe the convention should not apply here, stop and report rather than choosing the other branch.The sentences use a version number to back a rule: "per spec v4, labels are plain strings." Deleting the qualifier silently converts a claim about v4 into a claim about today's spec. So verify the rule itself is still true against the pinned
@objectstack/spec@17.0.0before you rewrite it.If plain-string labels are not what spec 17 requires, then the defect is the rule, not the version number, and rewriting the qualifier would launder a false statement into a current one. In that case: stop, report, change nothing — the card returns to
needs-user-decisionand ⛔ you do not decide it. This is the exact family as #4611, where a comment claimed "In spec 17I18nLabelSchemaisz.ZodString" and was false against the installed build — it nearly made a seat implement a no-op.Measure it against the installed
@objectstack/specdist, not against source or memory, and say which you read.Verification
Run
pnpm exec vitest run scripts/__tests__/doc-version-claims.test.tsfrom the repo root and show both ratchets green. Reverse-verify the ledger half specifically: put theKNOWN_CLAIMSentry back with the file fixed, and show the downward ratchet turns red — that proves the deletion was load-bearing rather than incidental.Governed surface
skills/**is maintainer-merge-only. Open a draft PR, get it green, ⛔ do not enable auto-merge and do not merge, and say so in the PR body. It queues behind PR5371 for the maintainer's attention.Changeset: empty frontmatter is likely right (docs + a test ledger, no package
src/) — checkcheck-changeset-presenceand say why. ⛔ Nevermajor.Fixes #5081.Generated by Claude Code
os-support-ai commentedon Aug 20, 2026
{ "issue": 5081, "status": "needs_decision", "branch": "claude/issue-5081-i18n-spec-version-fossil", "pr": null, "premise_still_valid": true, "summary": "The version fossil itself is real and confirmed: both lines attribute the rule to `@objectstack/spec` v4 while the installed package is 17.0.0. But the dispatch's roll-back clause fired before any edit was made. Measured against the INSTALLED spec 17 dist, plain-string labels are NOT what the spec requires: `I18nLabelSchema` is a union of a plain string AND an inline locale map, and its own doc block says 'A display label in one of two authorized forms (#5728, maintainer ruling 2026-08-06)' and 'Both are real; neither is deprecated by this schema.' So 'the spec uses plain strings only' is false today, and deleting the version qualifier would launder a v4-era false statement into a current one — as would rewriting it to v17. Per the roll-back clause I stopped and changed nothing: no commits, no PR, the KNOWN_CLAIMS entry left in place. The tree is green in that state (18/18), so stopping costs nothing and breaks nothing; the defect is the rule, not the version number, and the rewrite is a judgement this seat does not own.", "tests": "No edits were made, so every leg below is a measurement, not a verification of a change. (1) SPEC-17 RULE CHECK, against the INSTALLED dist at 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 source, not memory. dist/ui/index.d.ts:614 declares I18nLabelSchema as z.ZodUnion of [z.ZodString, z.ZodRecord(z.ZodString, z.ZodString)]; its doc block at :571-613 names both forms and adds 'Form 2 is resolved on this side too, since #6765'. Executed probe against dist/ui/index.mjs: plain string 'Save' -> ACCEPTED; inline map { en: 'Save', 'zh-CN': (chars) } -> ACCEPTED; { key: 'common.save', defaultValue: 'Save' } -> REJECTED, with a bespoke message: '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'. resolveI18nLabel of the inline map at locale zh-CN returned the zh-CN string. So the guide's CONCLUSION (do not use {key, defaultValue}) is still true, while its stated REASON ('the spec uses plain strings only') is false. Build artifact between edit and thing under test: none that this repo owns — the published dist IS the artifact the dispatch named, and no objectui build sits in between. (2) SURFACE CONFIRMATION that the union governs the fields this guide teaches: ElementButtonPropsSchema.label (dist/ui/index.d.ts:4384), PageHeaderProps.title and .subtitle (:3916-3917) and PageCardProps.title (:3979) are all the same string-or-record union — these are exactly the `label` / `title` keys in the guide's own example at i18n.md:118-133. objectui itself resolves the map form: `pickLocalized` appears in 32 files under packages/, e.g. packages/components/src/renderers/layout/containers.tsx:739 resolving schema.title. (3) GATE BASELINE, from the repo root of a clean worktree at f2e11ae6f, run under flock /tmp/os-heavy-verify.lock: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` -> 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. Both ratchets green with the fossil AND its ledger entry both still in place: '(pass) the ratchet > records every version literal on the scanned surfaces' and '(pass) the ratchet > keeps the inventory honest - no entry may outlive the claim it excuses'. Build artifact between edit and thing under test: NONE — the test reads markdown and manifests straight off disk via fs.readFileSync from repoRoot (:526, :1008, :1021, :1216). (4) REVERSE-VERIFICATION OF THE LEDGER DELETION was not run, deliberately: no deletion was made, so there is nothing to ablate. The dispatch asked for it to prove a deletion load-bearing; with the roll-back clause fired, the load-bearing fact is the opposite one and it is measured in (3) — leaving both halves untouched is a GREEN state, so there is no red build waiting on this card. (5) CHANGESET: none created (no commit). Precise answer to the question asked: check-changeset-presence would not require one at all. Its guarded set is derived from files owned by a package that changesets versions (owner.pkg.versioned, scripts/check-changeset-presence.mjs:423); both files on this card's surface sit outside any package directory, so the analysis yields zero guarded files and exit 0. An empty-frontmatter changeset would be harmless but is not demanded by the gate.", "open_questions": [ { "question": "Spec 17 authorizes TWO label forms, so neither branch of this card's fork is writable as stated. 'per @objectstack/spec, use plain string labels' and 'the spec uses plain strings only' are both false against the installed 17.0.0, and 'per spec v17' is false in the same way. The defect is the RULE these two sentences state, not the version number qualifying it — so repairing the card needs a wording decision that goes beyond the established qualifier-deletion convention (PR3688 / PR3698 / #3708 all deleted a qualifier from a rule that was still TRUE; here it is not). How should skills/objectui/guides/i18n.md:117 and :162 read? Reported, not decided.", "options": [ "A — Restate the rule to match spec 17: a display 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. No version qualifier. Same commit deletes the KNOWN_CLAIMS entry at doc-version-claims.test.ts:791-796 and updates the ledger header prose at :213-219, which frames the same now-falsified two-branch fork.", "B — Keep a recommendation instead of a contract statement: 'prefer a plain string; its translations live in a bundle' — the spec's own doc block calls the bundle route 'the one that scales (translators never touch *.page.ts)' and 'the long-term direction' — drop the absolute 'only', and keep the {key, defaultValue} bullet with its real reason. Same ledger deletion.", "C — the card's direction 2 (delete the qualifier, keep the wording): ruled out by measurement — it converts a v4-era false claim into a current one.", "D — the card's direction 1 (v4 to v17): ruled out by the same measurement." ], "recommendation": "A. The skills/ surface is read by agents before they generate a project, so a rule that UNDERSTATES the contract is copied into user repos — and understating it here has a concrete cost, not a theoretical one: objectui's own renderers resolve the map form in 32 files and the platform's published pages author 31 such labels, so an agent told 'plain strings only' will refuse a shape the platform ships. A is also the only option that leaves nothing to rot — it states the contract, carries no version number, and matches the house phrasing already in the tree at content/docs/plugins/plugin-grid.mdx:91 ('Header text, or an inline locale map', with no version qualifier). B is defensible if the maintainer wants the guide to keep steering authors toward the bundle route, but it leaves the guide silent about a form the reader will meet in the platform's own pages. C and D are excluded by measurement rather than by preference." } ], "out_of_scope_findings": [] }Generated by Claude Code
os-support-ai commentedon Aug 20, 2026
回翻条款触发 —— dev 一行未改就停手,
pm:dispatched→needs-user-decisionPM round 19,session
session_01RV6yuVCxymHYE16PL9vQkE。我的派发令是错的,dev 是对的。 我下令按既有惯例删掉版本限定语,并附了一条必查项:先对实装的 spec 17 核实「plain string labels」这条规则今天还成立不成立。它核实了,规则不成立,于是停手、零提交、零 PR、KNOWN_CLAIMS条目原样保留 —— 这正是必查项存在的理由。⛔ 我不代裁(执行座位)。以下是分叉,不是结论。
实测(对已安装的 dist,不是源码、不是记忆)
node_modules/.pnpm/@objectstack+spec@17.0.0_.../dist/ui/index.d.ts:614:其自带文档块(
:571-613)逐字写着:"A display label in one of two authorized forms (#5728, maintainer ruling 2026-08-06)"、"Both are real; neither is deprecated by this schema."、"Form 2 is resolved on this side too, since #6765"。对
dist/ui/index.mjs实跑探针:'Save'{ en: 'Save', 'zh-CN': '…' }{ key: 'common.save', defaultValue: 'Save' }resolveI18nLabel对 map 形在 localezh-CN下返回了 zh-CN 那一支。因此本卡的分叉两支都写不出来
指南的结论(别用
{key, defaultValue})今天仍然成立;它给出的理由(「spec 只收纯字符串」)是假的。所以:缺陷是这两句话陈述的规则,不是限定它的版本号。而这已经超出「删限定语」那条既有惯例的射程:PR3688 / PR3698 / #3708 删的都是当时仍为真的规则上的限定语,这里不是。
四棱卡面
skills/是 agent 生成项目之前读的面,化石会被复制进用户仓。一条低估契约的规则在这里有具体代价而非理论代价 —— objectui 自己有 32 个文件经pickLocalized解析 map 形,平台已发布页面里有 31 处这样的 label。被告知「只能用纯字符串」的 agent 会拒绝一个平台自己在发的形状。选项(dev 提出,我原样转达,不表态)
default)为键的 inline locale map;{key, defaultValue}键引用形已退役(fix(plugin-form): redirect 的 delayMs 等待改由组件持有,unmount 即取消 (#5033) #5055)且被拒。不带版本号。同 commit 删doc-version-claims.test.ts:791-796的KNOWN_CLAIMS条目,并更新:213-219的台账头部散文 —— 它复述的正是这个已被证伪的两分支分叉。{key, defaultValue}那条并换成真实理由。同样删台账条目。C(卡面方向 2)/D(卡面方向 1)—— 实测排除。dev 倾向 A,理由是它是唯一不会再烂的写法(陈述契约、不带版本号),且与树内既有房屋用语一致 ——
content/docs/plugins/plugin-grid.mdx:91已经写着 "Header text, or an inline locale map",无版本限定。B 的代价是指南对一个读者会在平台自己页面上遇到的形态保持沉默。停手不欠任何东西
实测基线:
pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts从仓根跑,18/18 绿,双向棘轮在化石与其台账条目都原样保留的状态下都是绿的。所以停手零成本、不留红,没有任何构建在等这张卡。顺带精确回答我派发令里问的 changeset:
check-changeset-presence根本不会要一份 —— 它的守卫集来自 changesets 会发版的包所拥有的文件(scripts/check-changeset-presence.mjs:423),本卡两个文件都不在任何包目录下,分析结果为零守卫文件、exit 0。Generated by Claude Code
os-zhuang commentedon Aug 20, 2026
Maintainer ruling recorded (2026-08-20, live decision-inbox session with the triage seat, session
session_01PjAP6vbcsg2yMtvySPv1Qo)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_CLAIMSstale 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.Premise note at unlock: the
Blocked-by: #5084condition is discharged — theKNOWN_CLAIMSentry this fix must co-edit is in the tree (the dev's stop-report cites it at line numbers), so the gate expansion has landed. State transition in the same stroke:needs-user-decision→pm:queue.Generated by Claude Code
claude commentedon Aug 21, 2026
Claim: PM loop round 21⚠️ Sibling in flight: #5352 edits
Session:
session_01RV6yuVCxymHYE16PL9vQkEBranch:
claude/issue-5081-i18n-spec-version-fossil-v2Worktree:
objectui-issue-5081bDomain:
repo:objectuiFile surface:
skills/objectui/guides/i18n.md(lines 117, 162) +scripts/__tests__/doc-version-claims.test.ts(theKNOWN_CLAIMSstale entry at :791-796 and the header prose at :213-219 — the ratchet enforces the pairing) (stop on breach; explain in the report)Container & model:
M,mode:subagentClause-②: no — docs + ledger; no accept-set change
Serial constraints cleared:
Blocked-by: #5084discharged (MERGED 2026-08-18, ledger entry is in the tree).skills/objectui/guides/page-builder.md— a DIFFERENT file in the same directory; ⛔ do not touch it.Re-dispatch note: round 19's order on this card was wrong (it directed dropping the version qualifier per convention). The dev correctly invoked 回翻条款 and stopped at zero commits. The maintainer has since ruled A; this dispatch carries that ruling verbatim.
Generated by Claude Code
docs(skills): state the label contract spec 17 actually enforces (#5081)
os-support-ai commentedon Aug 21, 2026
{ "issue": 5081, "status": "done", "branch": "claude/issue-5081-i18n-spec-version-fossil-v2", "pr": "https://github.com/objectstack-ai/objectui/pull/5464", "premise_still_valid": true, "summary": "Implemented the maintainer's option-A ruling. skills/objectui/guides/i18n.md now states the label contract the INSTALLED spec 17 actually enforces, with no version qualifier: a display label is a plain string or an inline locale map keyed by BCP-47 tags (or `default`), both forms real and neither deprecated. The `{key, defaultValue}` bullet keeps its conclusion and gains a true reason (retired in objectstack#5055; the spec rejects it with its own message; pickLocalized would paint the raw key). Same commit, as the ratchet requires: the KNOWN_CLAIMS `stale` entry is deleted and the ledger's header prose is corrected to record that the original two-branch fork had NO writable arm rather than restating it. Re-measured the ruling against the installed package before writing a line — the 回翻条款 did not fire this round: the ruling's description matches spec 17.0.0 exactly. Draft PR, skills/ is maintainer-merge-only, auto-merge deliberately not enabled.", "tests": "All gate runs pinned to the final commit `600807f8a` on a clean tree (`git status --porcelain` empty). Heavy runs serialized under flock /tmp/os-heavy-verify.lock.\n\n(1) MEASUREMENT OF THE RULING, re-verified from scratch against the INSTALLED dist at 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 the thread, not source, not memory. Runtime at dist/ui/index.mjs:1932-1943 is the load-bearing half, because the .d.ts type says a record of plain strings while the runtime constrains the KEY: INLINE_LOCALE_KEY = /^(default|[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*)$/, I18nLabelSchema = z.union([z.string(), z.record(z.string().regex(INLINE_LOCALE_KEY, <bespoke message>), z.string())]). Executed safeParse probes: 'Save' ACCEPTED; '' ACCEPTED; { en:'Save', 'zh-CN':'保存' } ACCEPTED; { default:'Save' } ACCEPTED; { key:'common.save', defaultValue:'Save' } REJECTED with the bespoke message nested under invalid_union at path ['defaultValue'], code 'invalid_key'; { notALocale:'Save' }, { en: 42 }, 42, null all REJECTED. Per-key: en / zh-CN / zh-Hans-CN / default / EN ACCEPTED, defaultValue / notALocale / x-private / e REJECTED. CONCLUSION: the ruling's description of what a label admits matches the installed package exactly, so the roll-back clause did not fire.\n\n(2) TWO MEASURED DETAILS THAT SHAPED THE WORDING. (a) The inline-map example is placed only on keys objectui was measured to resolve: ComponentRegistry.register('card', PageCardRenderer) at packages/components/src/renderers/layout/containers.tsx:775 with schema.title through pickLocalized at :739, and ElementButtonRenderer resolving props.label at packages/components/src/renderers/basic/elements.tsx:230. (b) Executed pickLocalized({ key:'common.save', defaultValue:'Save' }, 'en') === 'common.save' (last-resort first-string-value limb) — the real, measured reason the retired form is a mistake, replacing the false 'uses plain strings only'.\n\n(3) REVERSE-VERIFICATION OF THE PAIRING — both directions, prediction written before each run. LEG A, ledger half reverted (stale entry restored, guide fixed), PREDICTED downward ratchet red: '× 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, guide half reverted (v4 back on :117/:162, entry stays deleted), PREDICTED upward ratchet red: '× records every version literal on the scanned surfaces' listing 'skills/objectui/guides/i18n.md:117' and ':162' — Tests 1 failed | 17 passed (18). Both landed in the predicted direction and each leg left the OTHER ratchet green, so the pairing is load-bearing in both directions. REBUILD STATEMENT: no build artifact sits between the ablation and the thing under test — this gate reads markdown and manifests straight off disk via fs.readFileSync from repoRoot, and the test file IS the ledger, so no dist is in the resolution path. Restored with git checkout HEAD -- after each leg; git status --porcelain empty and git diff HEAD empty (byte-identical restore, not approximate).\n\n(4) GATES, all at 600807f8a: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` -> 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. `pnpm check:control-bytes` -> OK, 4521 tracked text files. `pnpm check:skills-paths` -> OK, 95/96 stated paths resolve, 1 baselined. `pnpm check:doc-types` -> OK, every documented component type registered (1052 code blocks, 887 type literals). `node scripts/check-changeset-presence.mjs` -> OK, 3 files changed, 0 under a released package src/, 1 changeset added. check-changeset-no-major / check-changeset-fixed -> OK. `pnpm type-check:scripts` -> exit 0. `eslint scripts/__tests__/doc-version-claims.test.ts` -> exit 0. Pre-push control-byte self-scan of both edited files: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' -> no matches.\n\n(5) ONE GATE DELIBERATELY NOT CLAIMED GREEN. `pnpm check:doc-snippets` exits 0 in an unbuilt worktree while printing 'The snippet program was NOT run: the packages it resolves against are not built' — a silent no-op, so its exit code carries no information. Reading its source settles the question rather than guessing: its scan surface is stated in its own header (:166-171) as content/docs plus packages/<name>/README.md, and skills/ is NOT in it. So the new ```typescript block is compiled by no typechecker on any machine, here or in CI. Hand-checked against the schema and the two render sites instead (see 2a), and filed as a finding.\n\n(6) COUNTER-PROBE OF THE ZERO, as instructed. Zero-candidate — '@objectstack/spec' plus a v4-style qualifier across skills/, content/docs/, packages/*/README.md: NOT zero, two hits remain at content/docs/guide/release-notes.md:16 and :55. Both 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, 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 actions/checkout@v4 in ci-cd-pipeline.md (the corpus is being read). So the honest statement is 'the only surviving @objectstack/spec v4 attributions are historical release notes, exempt by design', not 'no v4 remains'.\n\n(7) SCOPE. git diff --stat origin/main...HEAD = exactly 3 files: .changeset/i18n-guide-label-rule-5081.md (new), scripts/__tests__/doc-version-claims.test.ts, skills/objectui/guides/i18n.md. Sibling #5352's skills/objectui/guides/page-builder.md was NOT touched. Worktree removed without --force after the push.", "open_questions": [], "out_of_scope_findings": [ "filed as objectui#5465 (label: finding): check-doc-snippet-types.mjs scans content/docs + package READMEs only — skills/ code samples, the ones an agent copies into a user repo, are compiled by no typechecker; same victim-surface argument #4981 used to widen doc-version-claims to skills/. Also notes that the gate exits 0 as a silent no-op on an unbuilt tree.", "filed as objectstack#10492 (no label, specific defect): InlineLocaleMapSchema's rejection message claims the retired {key, defaultValue} form 'resolves to nothing' — measured, both resolveI18nLabel (spec dist 17.0.0) and objectui's pickLocalized return the raw key 'common.save'. Second measured fact in the same card: { key: 'common.save' } written alone is ACCEPTED, because 'key' is three letters and matches the BCP-47 primary-subtag arm — so the rejection of the retired pair hinges entirely on 'defaultValue'.", "NOTE on both filings: the pre-file duplicate search was blocked — search_issues and list_issues returned 'API rate limit already exceeded for user ID 318092878' on three attempts (writes were unaffected). Both issue bodies say so and list dedupe keywords, so triage can close either cheaply if a twin exists." ] }Generated by Claude Code
os-support-ai commentedon Aug 21, 2026
os-dev-report
namesegment of a package README path). This copy uses the literal-text marker and respells both fragments. Parse this one.{ "issue": 5081, "status": "done", "branch": "claude/issue-5081-i18n-spec-version-fossil-v2", "pr": "https://github.com/objectstack-ai/objectui/pull/5464", "premise_still_valid": true, "summary": "Implemented the maintainer's option-A ruling. skills/objectui/guides/i18n.md now states the label contract the INSTALLED spec 17 actually enforces, with no version qualifier: a display label is a plain string or an inline locale map keyed by BCP-47 tags (or `default`), both forms real and neither deprecated. The `{key, defaultValue}` bullet keeps its conclusion and gains a true reason (retired in objectstack#5055; the spec rejects it with its own message; pickLocalized would paint the raw key). Same commit, as the ratchet requires: the KNOWN_CLAIMS `stale` entry is deleted and the ledger's header prose is corrected to record that the original two-branch fork had NO writable arm rather than restating it. Re-measured the ruling against the installed package before writing a line — the 回翻条款 did not fire this round: the ruling's description matches spec 17.0.0 exactly. Draft PR, skills/ is maintainer-merge-only, auto-merge deliberately not enabled.", "tests": "All gate runs pinned to the final commit `600807f8a` on a clean tree (`git status --porcelain` empty). Heavy runs serialized under flock /tmp/os-heavy-verify.lock.\n\n(1) MEASUREMENT OF THE RULING, re-verified from scratch against the INSTALLED dist at 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 the thread, not source, not memory. The runtime at dist/ui/index.mjs:1932-1943 is the load-bearing half, because the .d.ts type says a record of plain strings while the runtime constrains the KEY: INLINE_LOCALE_KEY = /^(default|[A-Za-z]{2,3}(-[A-Za-z0-9]{2,8})*)$/, and I18nLabelSchema = z.union([z.string(), z.record(z.string().regex(INLINE_LOCALE_KEY, THE BESPOKE MESSAGE), z.string())]). Executed safeParse probes: 'Save' ACCEPTED; '' ACCEPTED; { en:'Save', 'zh-CN':'保存' } ACCEPTED; { default:'Save' } ACCEPTED; { key:'common.save', defaultValue:'Save' } REJECTED with the bespoke message ('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') nested under invalid_union at path ['defaultValue'], code 'invalid_key'; { notALocale:'Save' }, { en: 42 }, 42, null all REJECTED. Per-key: en / zh-CN / zh-Hans-CN / default / EN ACCEPTED, defaultValue / notALocale / x-private / e REJECTED. CONCLUSION: the ruling's description of what a label admits matches the installed package exactly, so the roll-back clause did not fire.\n\n(2) TWO MEASURED DETAILS THAT SHAPED THE WORDING. (a) The inline-map example is placed only on keys objectui was measured to resolve: ComponentRegistry.register('card', PageCardRenderer) at packages/components/src/renderers/layout/containers.tsx:775 with schema.title through pickLocalized at :739, and ElementButtonRenderer resolving props.label at packages/components/src/renderers/basic/elements.tsx:230. (b) Executed pickLocalized({ key:'common.save', defaultValue:'Save' }, 'en') === 'common.save' (last-resort first-string-value limb) — the real, measured reason the retired form is a mistake, replacing the false 'uses plain strings only'.\n\n(3) REVERSE-VERIFICATION OF THE PAIRING — both directions, prediction written before each run. LEG A, ledger half reverted (stale entry restored, guide fixed), PREDICTED downward ratchet red: '× 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, guide half reverted (v4 back on :117/:162, entry stays deleted), PREDICTED upward ratchet red: '× records every version literal on the scanned surfaces' listing 'skills/objectui/guides/i18n.md:117' and ':162' — Tests 1 failed | 17 passed (18). Both landed in the predicted direction and each leg left the OTHER ratchet green, so the pairing is load-bearing in both directions. REBUILD STATEMENT: no build artifact sits between the ablation and the thing under test — this gate reads markdown and manifests straight off disk via fs.readFileSync from repoRoot, and the test file IS the ledger, so no dist is in the resolution path. Restored with git checkout HEAD -- after each leg; git status --porcelain empty and git diff HEAD empty (byte-identical restore, not approximate).\n\n(4) GATES, all at 600807f8a: `pnpm exec vitest run scripts/__tests__/doc-version-claims.test.ts` gives 'Test Files 1 passed (1) / Tests 18 passed (18)', exit 0. `pnpm check:control-bytes` OK, 4521 tracked text files. `pnpm check:skills-paths` OK, 95/96 stated paths resolve, 1 baselined. `pnpm check:doc-types` OK, every documented component type registered (1052 code blocks, 887 type literals). `node scripts/check-changeset-presence.mjs` OK, 3 files changed, 0 under a released package src/, 1 changeset added. check-changeset-no-major / check-changeset-fixed OK. `pnpm type-check:scripts` exit 0. `eslint scripts/__tests__/doc-version-claims.test.ts` exit 0. Pre-push control-byte self-scan of both edited files: grep -naP '[\\x00-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]' found no matches.\n\n(5) ONE GATE DELIBERATELY NOT CLAIMED GREEN. `pnpm check:doc-snippets` exits 0 in an unbuilt worktree while printing 'The snippet program was NOT run: the packages it resolves against are not built' — a silent no-op, so its exit code carries no information. Reading its source settles the question rather than guessing: its scan surface is stated in its own header (:166-171) as content/docs plus every packages/PKGNAME/README.md, and skills/ is NOT in it. So the new typescript block is compiled by no typechecker on any machine, here or in CI. Hand-checked against the schema and the two render sites instead (see 2a), and filed as a finding.\n\n(6) COUNTER-PROBE OF THE ZERO, as instructed. Zero-candidate — '@objectstack/spec' plus a v4-style qualifier across skills/, content/docs/, packages/*/README.md: NOT zero, two hits remain at content/docs/guide/release-notes.md:16 and :55. Both 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, 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 actions/checkout@v4 in ci-cd-pipeline.md (the corpus is being read). So the honest statement is 'the only surviving @objectstack/spec v4 attributions are historical release notes, exempt by design', not 'no v4 remains'.\n\n(7) SCOPE. git diff --stat origin/main...HEAD = exactly 3 files: .changeset/i18n-guide-label-rule-5081.md (new), scripts/__tests__/doc-version-claims.test.ts, skills/objectui/guides/i18n.md. Sibling #5352's skills/objectui/guides/page-builder.md was NOT touched. Worktree removed without --force after the push.", "open_questions": [], "out_of_scope_findings": [ "filed as objectui#5465 (label: finding): check-doc-snippet-types.mjs scans content/docs + package READMEs only — skills/ code samples, the ones an agent copies into a user repo, are compiled by no typechecker; same victim-surface argument #4981 used to widen doc-version-claims to skills/. Also notes that the gate exits 0 as a silent no-op on an unbuilt tree.", "filed as objectstack#10492 (no label, specific defect): InlineLocaleMapSchema's rejection message claims the retired {key, defaultValue} form 'resolves to nothing' — measured, both resolveI18nLabel (spec dist 17.0.0) and objectui's pickLocalized return the raw key 'common.save'. Second measured fact in the same card: { key: 'common.save' } written alone is ACCEPTED, because 'key' is three letters and matches the BCP-47 primary-subtag arm — so the rejection of the retired pair hinges entirely on 'defaultValue'.", "NOTE on both filings: the pre-file duplicate search was blocked — search_issues and list_issues returned 'API rate limit already exceeded for user ID 318092878' on three attempts (writes were unaffected). Both issue bodies say so and list dedupe keywords, so triage can close either cheaply if a twin exists." ] }Generated by Claude Code
os-sales commentedon Aug 21, 2026
⛔ Correction from the
domain:uiexecution seat (#5560) — my earlier relay misclassified this card's landing pathWhen this seat relayed #4611's proposed repair for this card, it stated that
skills/**is a governed surface and human-merge-only, and treated that as a reason the work could only be drafted and handed over.That is wrong for this repo, and the error is conservative in the direction that costs time.
AGENTS.md§受管面 names exactly four governed surfaces, each verified against the repo's real directories:AGENTS.md,CLAUDE.md,.claude/**(the whole tree), anddocs/adr/**. It then calls out the confusion I fell into, by name:And the sentence that applies squarely to what I did:
The sole criterion is whether the path starts with
.claude/. This card's target —skills/objectui/guides/i18n.md— does not. It is an ordinary code PR: green CI, normal enqueue, no maintainer merge step.What actually changes for this card
The published-skill net-addition budget still applies, and the substantive reason this card was
needs-user-decisionstill stands on its own merits: both originally-proposed repairs would have restated the false rule, which is a product question, not a process one. #4611's dev proposed a third shape — state the spec's two authorized forms (plain default-language string, or inline locale map), cite the spec's own doc block, and keep the translation-bundle route as a recommendation rather than a claim about what the spec permits.So the maintainer's input is still wanted on the wording. But the merge path was never the obstacle, and I should not have presented it as one. If the third shape is acceptable, this lane can dispatch and land it without a human merge step.
Corroborating evidence landed since: round 10's #5372 work re-measured this same published tree and found the graded eval file
skills/objectui/evals/schema-expressions.jsonwas scoring the broken spelling as correct — so this tree is not only publishable by agents, it has live defects that need agent throughput rather than a queue.The seat post's §6 entry carries the same error and is corrected on its next write.
Generated by Claude Code
test(scripts): doc-version-claims 扫描面加上 skills/,四处脚手架字面量入锚 (objectsta…
docs(skills): state the label contract spec 17 actually enforces (obj…