skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

skills(i18n): optimization flight — drop the restated overview, the zero-usage layout and plugin bootstrap and the duplicate template; teach the generated-bundle shape and the --objects-only divergence (net −2,069 tokens) - #14574

Merged
os-zhuang merged 14 commits into
mainfrom
claude/issue-14315-skills-i18n-optimization
Sep 2, 2026

Conversation

@os-litant

Copy link
Copy Markdown
Collaborator

Fixes#14315

Skills catalog optimization program #14292, maintainer mandate 2026-09-02: 「审核所有的 skills,进行全面的优化。」 Audit record posted verbatim on the card. Governed surface — this PR stays draft and awaits the maintainer's own merge.

Transliteration key. The GitHub body sanitizer eats angle-bracket-shaped fragments, so every one below is rewritten. [[X]] reads as "angle-bracketed X": [[os:check]] = the opt-in fence marker (an HTML comment whose text is os:check); [[locale]], [[o]], [[tab]], [[obj]], [[field]] = angle-bracketed placeholders; NonNullable[[TranslationData['objects']]] = NonNullable applied to TranslationData['objects']; Promise[[this]] = Promise of this.


Landing form — why Fixes and not Part of

The claim comment specified Part of #14315, for one stated reason: the deferred eval stub kept the card open. That reason is void. The maintainer ruled #142961A 2A 3A 4A, item 2 option A, which moved the stub from deferred to delivered — it is deleted in this PR (I18N-H-01 below). Every remaining DEFER item now has its own tracked home:

DEFER itemdisposition
I18N-H-01 (eval stub)delivered here under the 1A 2A 3A 4A ruling
I18N-C-03 (generated _index.md pointers)filed as #14565, unassigned, spec lane
I18N-A-03 (routing bullet in skills/README.md)recorded below as ruled, and filed as #14566 so it is tracked, not prose-only

Nothing is left on the card, so it closes on merge. That is also the state #14377 is waiting for — it carries Blocked-by: #14315 and becomes actionable once this lands. If the seat prefers the card kept open, this is a one-word edit before merge.


Per-item landing table

落点 = where it landed. Line numbers are the audit's (base 93940d49, byte-identical to the audit ref a59f78d for this package — verified, git diff --stat a59f78d origin/main -- skills/objectstack-i18n empty).

id落点beforeafter
I18N-B-03SKILL.md:19-25opening paragraph restating the frontmatter descriptiondeleted; the H1 stays
I18N-B-04:28-41"When to Use This Skill", 7 bulletsdeleted whole
I18N-A-01:35-39locale-formatting trigger (dates, numbers, currency)deleted with B-04 — a whole-file grep for currency/timezone/format/Intl. matched only those 5 lines, so the trigger routed agents here for work the package teaches nowhere
I18N-B-01:44-66"Core Concepts" — 4 numbered itemsdeleted; each is restated in full by a later section
I18N-D-02:99-1483 prose "strategies" with ASCII treesone 3-row table + a lead sentence
I18N-D-03:143-148 and :526-530the localesDir flat-file rule, stated twiceboth deleted — localesDir has zero real usages and os serve never passes it
I18N-B-05orderingthe layout prose sat between the config table and the object sub-key tableorder is now config → canonical bundle → object sub-keys → naming → layout
I18N-F-03:308-362the translation metadata item, 570 tokens incl. a full worked example, 0 measured usagesone schema-pointing paragraph keeping the three rules that differ from a file bundle. Live surface, so the last mention stays
I18N-D-04:356-362the retired o.* dialect, stated twicethe Common Pitfalls copy survives — it alone carries the migration map
I18N-C-01:506-530hand-wired new ObjectKernel() + kernel.use(new I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrapone sentence on auto-registration + the 3 routes + a pointer to the platform anchor
I18N-E-01:508-524the one unmarked fence presenting as a complete program that would fail the example gate (used II18nService with the import in a different fence, TS2304)dies with C-01
I18N-E-02:513kernel.use(...) un-awaited (hazard 1)dies with C-01
I18N-D-01:455-469"AI-Powered Translation Suggestions" — vendor list + best-practice asidedeleted; also trimmed the 4th restatement of the same "contract-only" fact at the workflow's Translate step
I18N-D-05:569-585"CRM I18n Blueprint", a 4-row table restating three other sectionsthe two example paths it alone carried
I18N-D-07:608-638two pitfalls restating the naming callout and the option-value paragraphdeleted; the surviving paragraph is the stronger statement (it names the failure)
I18N-B-02:646-688"Quick-Start Template", a strict subset of the canonical example, as its own preamble concededdeleted
I18N-C-02:690-704"Verify your work" restating objectstack-platform then pointing at it2 commands + the pointer
I18N-D-06:270-274the validationMessages removal narrated with its historyone redirect line — see the collision note below
I18N-G-01:300-302lint unknown-target enumeration presented as exhaustive, _tabs missing"tab" added; the lint walks objects.[[o]]._tabs.[[tab]] and reports translation-target-unknown
I18N-F-02 / I18N-G-02:535-560, :546-547extract → translate → check, documented as convergent; --objects-only absent from a 6-flag listthe flag is named, and a paragraph states the divergence (hazard 2)
I18N-F-01:535-547 gapthe highest-count real pattern had no example — a reader following extract dead-ended at a file that cannot be registereda canonical block: wrap the generated subtree, withSourceFallback, register or load at kernel:ready
I18N-F-04:546 gap--source-hashes had one word in a flag listits purpose stated where the argument is passed
I18N-A-02frontmatter descriptiontriggered on none of what the body teaches for runtime authoringtriggers on the translation metadata item and on shipping a generated bundle
I18N-G-05:247-248 vs the new block(audit filed this repo-side)the new block authors through defineTranslationBundle, so it no longer contradicts the file's own advice — see the gate note below
I18N-H-01evals/README.md45 lines / 410 tokens, an ASCII tree naming 6 files that do not exist, zero rubricsfile deleted, ceiling row dropped
I18N-B-06evals/README.md:13-24the fictional file treedies with H-01
I18N-G-03, I18N-G-04"no action" in the auditno action, except that G-03's verdict is falsified at this base — see the collision note

The two hazards

  1. kernel.use is async.packages/core/src/kernel.ts:192 at this base reads async use(plugin: Plugin): Promise[[this]], and os serve's own call site awaits it (packages/cli/src/commands/serve.ts:2896). The skill's block did not. Fixed by deletion, not by adding await — the whole block was teaching a path nothing runs.
  2. extract and check could not converge.os i18n extract's --objects-only is declared default: true, allowNo: true (packages/cli/src/commands/i18n/extract.ts:89-93), so extract writes only objects / globalActions; COVERAGE_SOURCE in packages/cli/src/utils/i18n-coverage.ts:192-220 also demands app, navigation, dashboard, widget, page and flow. Following the skill ended in coverage gaps the extract never scaffolded. Now stated, with --no-objects-only as the way out.

Anchor (I18N-C-01), cited by section

Plugin wiring / os serve auto-registration belongs to objectstack-platform → ## Runtime Boot Sequence, which carries the boot-tree line naming I18nServicePlugin. Verified by content on bothorigin/main and origin/claude/issue-14299-skills-platform-optimization (PR #14351, approved and may land first) — the section heading and the line are present on both, so the pointer survives whichever lands first. Cited by section, never by line number. Field and view labels stay in this package.

Why the generated-bundle block is NOT marked [[os:check]]

check:skill-examples compiles marked fences per surface, and the skills + docs surface is declared resolutionDir: SPEC_DIR, selfPackages: [SPEC_DIR] (packages/spec/scripts/check-skill-examples.ts, SURFACES) — it resolves @objectstack/spec and nothing else. @objectstack/spec does not depend on @objectstack/platform-objects (checked: not in its dependencies, no packages/spec/node_modules/@objectstack link), and the real shape needs withSourceFallback from @objectstack/platform-objects/apps. Marking the block would red it with TS2307 on correct code, so it is left unmarked and this paragraph is the reason.

It is verified anyway. A throwaway driver in packages/services/service-storage reproduced the block verbatim against the real exports and typechecked clean (tsc --noEmit, exit 0), twice — once for the original shape and again after the factory change below. Deleted in the same step both times; git status clean, confirmed by observing the files gone rather than by reading an exit code.

Symbols verified at source: withSourceFallback (packages/platform-objects/src/apps/translations/source-hash.ts:462, public via the apps barrel), the 4-argument order (translated, source, recorded, filledFrom), the generated module's NonNullable[[TranslationData['objects']]] export, and i18n.loadTranslations at kernel:ready (5 plugin call sites).


Token accounting — measured, not smoothed

Per the maintainer's 2026-08-21 ruling on published skills (「对外发布的 skills 是整个平台的最大价值,尤其要整体考虑和评估。」「……不能为了一个小功能扩写很多。」), both readings:

The edited file, whole-file:

beforeafterceilingheadroom
skills/objectstack-i18n/SKILL.md6337 tok / 719 lines4678 tok / 496 lines6338 (unchanged)1660
skills/objectstack-i18n/evals/README.md410 tok / 45 linesdeletedrow dropped

The whole published package: 7296 → 5227 tokens shipped (−2069, −28.4%). Ratcheted total 6747 → 4678 (−2069, −30.7%). No ceiling was raised; no new file was added.

Per commit, against the audit's own estimates. Gaps are itemised per cluster, not netted away:

commitfinding idsaudit estmeasuredgap
7164f69eB-03, B-04 (incl. A-01), B-01−633−626+7
1da918fbD-02, D-03 (1st copy), B-05−280 + part of −89−234see note
c97dafddF-03, D-04−337−395−58
ff489c18C-01, E-01, E-02, D-03 (2nd copy)−150 + rest of −89−105see note
84a1928dD-01, D-05, D-07−448−443+5
74dd2f70B-02, C-02, G-01, D-06−357−324+33
36c7a16cF-01, F-02, F-04, G-02+205+423+218
4de6130eA-02+12+33+21
bc84ff2bF-01 follow-up (G-05)not in the audit+12+12
SKILL.md net−1685−1659+26
d0dbe12cH-01−267 (as a rewrite)−410 (as a deletion)−143

Notes on the three gaps that are not rounding:

  • 36c7a16c, +218 over. The audit costed F-01 at +180 for one block and F-04 at +0 ("fold one clause into F-01"). The block as written also carries the --objects-only divergence paragraph (audit: +25; actual closer to +70, because naming the six diverging key groups is what makes it actionable) and the --source-hashes rationale inline. I did not trim it to hit the number: it is the package's highest-count real pattern and its only convergence hazard. Flagging it rather than smoothing it.
  • 1da918fb + ff489c18, +180 under (combined est −519, measured −339). Both are REWRITE-AS-CONSTRUCT items the audit costed as if only one line survived. The layout table keeps three rows and a lead sentence; the registration paragraph keeps the three route paths and the platform pointer. Both are smaller than a one-line estimate implies, and I judged the surviving content load-bearing.
  • c97dafdd, −58 more than estimated. F-03's compression removed the whole worked example, not just :343-354.

On the dispatch's target of ≈ −1,418 on SKILL.md. That figure was the audit's −1,685 minus the deferred −267, but those are two different files: the audit's "Reducible total" gives −1,685 as SKILL.md's own net and −267 as evals/README.md's. The subtraction crossed files. Measured against the audit's actual per-file rows: SKILL.md −1,659 vs −1,685 estimated, and evals −410 vs −267 estimated (larger because the 1A 2A 3A 4A ruling deletes the stub where the audit had costed a rewrite).


Collision recorded and deliberately left alone: the object sub-key inventory, owned by #14377

While verifying premises at this base I measured that #14253 has landed, and it changes what is true in this file:

  • ObjectTranslationDataSchema (packages/spec/src/system/translation.zod.ts:169) now declares a ninth object sub-key, _validationsobjects.[[obj]]._validations.[[rule]].message, with a reader on the write path. packages/lint/src/validate-translation-references.ts:918-929 walks it.
  • The same landing added objects.[[obj]]._views.[[view]].bulkActions.* and a top-level datasets group.

Consequences for this file, stated and left alone:

  1. The D-06 blockquote is false today — validation rule messages are translatable now. My edit compressed that blockquote per the card's D-06 ruling and left its truth state exactly as it was. It did not pre-emptively repair it.
  2. The object sub-key table still lists 8 sub-keys, and the lint-surface enumeration I extended with "tab" is still short by validation rules and datasets.
  3. Audit finding I18N-G-03 is falsified at this base. It read "12 of 14 presented-as-exhaustive enumerations are COMPLETE" and passed the object sub-key table as COMPLETE. That was measured at a59f78d, before i18n: three authored display surfaces have no bundle key at all (bulk-action defs, custom validation messages, dataset labels) #14253. At 93940d49 it is not.

Why none of that is repaired here. The correction is owned by #14377, which instructs twice and explicitly (quoted verbatim, its emphasis):

⛔ Whoever takes #14315 must not pre-emptively fix line 270

⛔ Whoever takes #14315 should not fold this correction in on the way past: the budget ruling applies to both, and mixing them makes the net token change unreadable.

That card carries Blocked-by: #14315, and its triage ordering is "#14315 first, this correction second". Following the instruction is why the stale inventory ships one more round; recording the measurement here is so the next seat inherits it rather than re-deriving it.


premise_false

Every other finding's premise held. Nothing was forced.

Follow-ups (not in this PR)


Gates

Head sha bc84ff2b — the tree every reading below was taken from, and the tree pushed to this branch.

Card-named gates, exit codes captured before any pipe:

gateresult
node scripts/check-skills-token-ratchet.mjsexit 0 — SKILL.md 4678 / 6338, headroom 1660
pnpm --filter @objectstack/spec check:skill-examplesexit 0 — 259 prose examples type-check across 3 surfaces (see the prerequisite note)
pnpm check:skill-compatibilityexit 0
pnpm check:skill-identifier-livenessexit 0
pnpm --filter @objectstack/spec check:skill-docsexit 0 — both artifacts in sync

check:skill-identifier-liveness was also checked before the first deletion: its BINDINGS table registers 8 exhaustive sections across 7 files, and none is in skills/objectstack-i18n (zero occurrences of i18n in the script), so no registered binding could be broken by a deletion here.

The full re-derived union was run, not just the card-named list.node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands was re-derived after the doc regeneration and after merging origin/main — the first derivation printed a STALE TREE warning naming 148 changed files, which is exactly the "well-formed answer about a tree nobody is on" failure, so it was re-run from a fresh tree. It prints 45 commands; all 45 were run on bc84ff2b.

Two results that are not green and are not red either:

  • node scripts/check-test-completeness.mjsexit 3, NOT MEASURED. The gate says so itself: "Nothing was measured … ⛔ It is NOT a finding". Reported as unmeasured, not as a pass.
  • pnpm --filter @objectstack/spec check:skill-examples — first run exit 1 with PREREQUISITE NOT MET: packages/spec/dist/**/*.d.ts older than packages/spec/src, because merging origin/main moved spec's sources under an earlier build. Rebuilt the @objectstack/spec + @objectstack/client-react closures and re-ran on bc84ff2b: exit 0, "259 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them". Reported as green only from the run that actually read fresh declarations.
  • pnpm --filter @objectstack/spec check:docs — first run exit 1 with PREREQUISITE NOT MET (packages/spec/json-schema is a gitignored build artifact older than src). Satisfied it with gen:schema — run after the merge was committed, never in MERGE state — which left zero tracked drift, then re-ran: exit 0.

One gate caught a real defect in this PR's own addition, which is recorded rather than quietly fixed: pnpm check:doc-authoring went red on the new block, #2035 / ADR-0059, "Bare metadata-literal authoring found in docs/skills" — the block exported a bare : TranslationBundle literal, the one pattern that rule keeps out of the published catalog, and the same inconsistency the audit recorded from the other side as I18N-G-05. Commit bc84ff2b reauthors it through defineTranslationBundle; the gate is exit 0 on the final head, and the block was re-driven against the real exports after the change.

The merge of origin/main conflicted in both generated artifacts because sibling flights changed them. scripts/pm/os-regen-merge.sh correctly refused them (they are not os-regen driver paths), so the documented order was followed by hand: take main's side, commit the merge first, then regenerate with gen:skill-docs, then commit the regeneration separately. Asserted afterwards: every sibling package's row is byte-identical to origin/main's and both artifacts still carry all 17 rows — the staged diff is this card's description change and nothing else.

Labels

skip-changeset — this PR releases nothing. Checked against scripts/check-empty-changeset.mjs's own enumeration ("It releases nothing (.github/, .claude/, skills/, docs/, content/, examples/, tests-only, and the like)"): the five changed paths are three under skills/, one under content/, and one CI-internal gate script under scripts/ that publishes from no package.

needs:contract-review on this PR and on the card — the generated-bundle block, the --objects-only extract semantics and the un-awaited kernel.use hazard are contract claims.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1


Generated by Claude Code

…ts (I18N-B-03, I18N-B-04, I18N-A-01, I18N-B-01)
- I18N-B-03: the opening paragraph restated the frontmatter description the
agent already holds when the skill loads.
- I18N-B-04: "When to Use This Skill" restated the description in seven
bullets; one of them pointed at a contract-only surface with no
implementation, and one was I18N-A-01.
- I18N-A-01: the locale-formatting trigger (dates, numbers, currency) routed
agents here for work this package teaches nowhere — a whole-file grep for
currency/timezone/format/Intl. matched only those five lines. The currency
fallback is carried by objectstack-data's field-types rules and
objectstack-ui; the localization endpoint is API surface.
- I18N-B-01: "Core Concepts / Translation Architecture Overview" was a table
of contents — each of its four items is restated in full by a later
section (object sub-keys, bundle authoring, coverage, runtime authoring).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…below the object constructs (I18N-D-02, I18N-D-03, I18N-B-05)
- I18N-D-02: three prose "strategies" with ASCII trees and size heuristics
became one three-row table. The per-namespace layout has zero real usages
in the repo (a search for an i18n/<locale>/*.json tree finds only
package.json/tsconfig.json), and the section already conceded the layout is
inert — the import graph, not the platform, assembles it.
- I18N-D-03: the FileI18nAdapter localesDir flat-file rule was stated twice,
here and again under Plugin Setup. Both go: localesDir has zero real usages
and os serve never passes it (see I18N-C-01). The surviving row states the
operative fact — your own imports assemble the tree.
- I18N-B-05: the layout prose sat between the config table and the object
sub-key table, so ~380 tokens of authoring convention stood in front of the
most decision-changing construct in the file. The order is now config →
canonical bundle → object sub-keys → naming → layout.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ing paragraph (I18N-F-03, I18N-D-04)
- I18N-F-03: the `translation` metadata item held 570 tokens (9% of the file)
including a full worked example, for a surface with zero measured usage —
a search for defineTranslation( across examples and packages returns only
the schema's own docstring and definition. It is a live surface, so the
last mention stays: one paragraph carrying the three rules that differ from
a file bundle (locale required, one locale per item, layers over the shipped
bundle) and a pointer to TranslationItemSchema.
- I18N-D-04: the retired `o.*` dialect was stated twice — here and under
Common Pitfalls. Only the pitfall copy carries the migration map for the
sibling keys, so that is the copy that survives.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…act (I18N-C-01, I18N-E-01, I18N-E-02)
- I18N-C-01: the section taught new ObjectKernel() + kernel.use(new
I18nServicePlugin({localesDir, registerRoutes, basePath})) + bootstrap.
os serve auto-registers I18nServicePlugin whenever the config carries
translations or i18n, and passes only defaultLocale/fallbackLocale — never
localesDir. Zero apps in this repo hand-wire it. Kernel bootstrap is
objectstack-platform's domain, whose Runtime Boot Sequence section already
carries the same fact; this file keeps one sentence plus the three routes.
- I18N-E-01: that block was the one unmarked fence presenting as a complete
program that would fail the example gate — it used II18nService with the
import in a different fence (TS2304). It dies with the section.
- I18N-E-02: the same block called kernel.use(...) un-awaited while
ObjectKernel.use is `async use(plugin: Plugin): Promise<this>`
(packages/core/src/kernel.ts:192) and the platform's own call site awaits
it. It dies with the section rather than being patched.
- I18N-D-03 (second half): the duplicate localesDir flat-file note goes with
it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…I18N-D-01, I18N-D-05, I18N-D-07)
- I18N-D-01: "AI-Powered Translation Suggestions" was a vendor list plus a
best-practice aside for a contract method no shipped adapter implements and
no CLI reaches. The same "contract-only" fact is already stated by the Diff
& Coverage Schemas paragraph and again by the service-contract section.
- I18N-D-05: the "CRM I18n Blueprint" table restated the config table, the
canonical assembly and the naming callout. The only facts it alone carried
were the two example paths, which is what remains.
- I18N-D-07: "Mismatched Object Names" restated the Critical callout under
Naming Conventions, and "Hardcoded Option Values" restated the option-value
paragraph there — which is the stronger statement, because it names the
failure (both the display label and a hyphenated spelling parse, ship, and
resolve to nothing).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng lint surface (I18N-B-02, I18N-C-02, I18N-G-01, I18N-D-06)
- I18N-B-02: "Quick-Start Template" was a strict subset of the canonical
example under Authoring Translation Bundles — same object, same sub-keys,
same apps/messages — and its own preamble said so. Two checked example
blocks were paying to teach one shape.
- I18N-C-02: "Verify your work" restated objectstack-platform's own section
and then pointed at it. Two commands and the pointer remain.
- I18N-G-01: the lint unknown-target enumeration presented as exhaustive but
omitted tabs. validateTranslationReferences walks objects.<o>._tabs.<tab>
and reports translation-target-unknown for it, and this file's own object
sub-key table already lists _tabs.
- I18N-D-06: the validationMessages note narrated why the key was removed.
The removal changes no decision the reader can act on; where to author the
message does.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…orkflow converge (I18N-F-01, I18N-F-02, I18N-F-04, I18N-G-02)
- I18N-F-01: the highest-count real pattern had no canonical example. Eight
packages ship their own generated bundle (36 *.objects.generated.ts across
four locales, 11 withSourceFallback call sites, 5 kernel:ready
loadTranslations sites) and the generated module exports an objects subtree
rather than a TranslationData, so a reader following the extract workflow
dead-ended at a file that cannot be registered. The block shows the wrap,
the bundle, and the kernel:ready alternative for a plugin.
- I18N-F-04: --source-hashes had one word in a flag list although stale fill
is the platform's own headline i18n hazard. Its purpose is now stated where
the argument is passed: without the companion a leaf whose source moved
goes on serving the superseded fill under a green os i18n check.
- I18N-F-02 / I18N-G-02: the documented extract-translate-check workflow
could not converge. os i18n extract defaults to --objects-only (default:
true, allowNo: true) so it writes only objects/globalActions, while
os i18n check's COVERAGE_SOURCE also demands app, navigation, dashboard,
widget, page and flow. The flag was missing from a six-flag enumeration
presented as the remainder; it is named now, with the divergence and the
way out.
The block is deliberately NOT marked for check:skill-examples: that gate's
skills surface resolves @objectstack/spec only, and the real shape needs
withSourceFallback from @objectstack/platform-objects. Marking it would fail
on correct code.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…teaches (I18N-A-02)
The description triggered on adding *.translation.ts files, wiring a locale
and resolving missing-translation warnings. It did not name the `translation`
metadata item — nor, after this flight, the package-ships-its-own-generated-
bundle shape, which is the highest-count real pattern in the repo and the one
construct this flight added. Both are now trigger phrases, so an agent facing
either task can load the package at all. "per-locale source layout" became
"source layout": the file no longer privileges one of the three.
skills/README.md and content/docs/ai/skills-reference.mdx are generator output
(`pnpm --filter @objectstack/spec gen:skill-docs`), regenerated because
check:skill-docs demanded it; both files carry only the derived description.
No prose in skills/README.md was hand-edited — the I18N-A-03 routing bullet is
a follow-up recorded in the PR body.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ng down (I18N-H-01)
Maintainer ruling on #14296 (decision batch 1), reply `1A 2A 3A 4A`. Item 2
option A, verbatim from the card: 「删除存根(整包约 −2,200 tokens),保留真实
fixture(ui 的 analytics json、automation 的 approvals md);formula 不新建。」
and 「裁后执行:各飞行内删除存根,棘轮上限随之下调」.
skills/objectstack-i18n/evals/README.md was 45 lines / 410 tokens shipped into
every customer session: an ASCII tree naming six eval files that do not exist
(the package has three files in total), plus Format / Status / Contributing
process prose about evals that do not exist. Zero rubrics — nothing in it can
grade anything. It is the stub the ruling names.
The only change to scripts/check-skills-token-ratchet.mjs is dropping the
deleted file's ceiling row; nothing else in that script is touched, and the
expanding-remedy authority sweep (check-ratchet-remedy-authority) stays green.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…ills-i18n-optimization
# Conflicts:
#	content/docs/ai/skills-reference.mdx
#	skills/README.md
…t only)
`pnpm --filter @objectstack/spec gen:skill-docs` re-derived from all 11
SKILL.md files after merging origin/main. The merge conflicted in both
generated artifacts because sibling flights changed them on main; main's side
was taken and the generator re-derived on top, which is the only safe order
for a generated file — resolving one textually would have kept whichever side
the driver happened to pick.
Asserted after regeneration: every sibling package's row is byte-identical to
origin/main's, and both files still carry all 17 rows. The staged diff is this
card's i18n description and nothing else.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
…lationBundle (I18N-F-01 follow-up, I18N-G-05)
check:doc-authoring (#2035, ADR-0059) refused the block: it exported a bare
`: TranslationBundle` literal, which is the one pattern that rule exists to
keep out of the published catalog. The file's own canonical example already
says to prefer the factory over a bare literal, so the block contradicted its
own package — the inconsistency audit finding I18N-G-05 recorded from the
other side (the flagship shipped example does the discouraged thing).
The factory takes the wrapped values unchanged, so the shape the block teaches
is the same one; only the authoring form is now the one the skill prescribes.
Re-driven against the real exports before and after (throwaway driver in
packages/services/service-storage typechecking the exact block against
@objectstack/spec + @objectstack/platform-objects, tsc --noEmit exit 0,
deleted in the same step with a clean tree).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
@os-zhuang
os-zhuang marked this pull request as ready for review September 2, 2026 14:05
Sibling skills flights landed on main and conflicted in three files:
- skills/README.md and content/docs/ai/skills-reference.mdx (generated
catalog artifacts, not routed merge=os-regen): main's side taken here
to clear the markers; regenerated wholesale in the following commit,
per the procedure this branch's earlier main-merge already followed.
- scripts/check-skills-token-ratchet.mjs: both sides' deletions applied
— main dropped the objectstack-api evals row, this branch drops the
objectstack-i18n one. No ceiling value changed, no row re-added.
skills/objectstack-i18n is byte-identical to the pre-merge head.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
gen:skill-docs run on the committed merge, never in MERGE state. The
regenerated artifacts differ from origin/main by exactly this package's
own rows — the delta is line-for-line identical to the one this branch
authored before the merge — so every sibling flight's row survives as
origin/main has it.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01489YWhZEoHT9oXshiyywQy
@os-zhuang
os-zhuang added this pull request to the merge queueSep 2, 2026
Merged via the queue into main with commit 937ec14Sep 2, 2026
32 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14315-skills-i18n-optimization branch September 2, 2026 15:50
os-litant pushed a commit that referenced this pull request Sep 2, 2026
`objects.OBJECT._validations.RULE.message` is object-scoped and resolved on the
write path by the ObjectQL rule evaluator through the existing i18n service, so
the blockquote teaching that validation messages are not a translation group is
false on main. Correct it and the two neighbouring inventories it sits in:
`_views.VIEW.bulkActions` on the object sub-key table and `datasets` in the
top-level group list, plus the validation-rule target `os validate` / `os lint`
already report but the list omitted.
A correction, not an expansion: the retired-key blockquote's substance moves into
the `_validations` row it belongs in, which pays the three additions in the same
file. Net +7 tokens (4678 to 4685), 3 lines shorter; ceiling unchanged and the
headroom left un-re-locked by #14574 is not spent.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LraLgQVGq8egUwfYZpbYt1
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

3 participants

@os-litant@os-zhuang@claude