Skip to content

Commit cd55929

Browse files
os-steveclaude
andcommitted
docs(skills): objectstack-i18n teaches the books translation group, with an eval for a two-language doc book
The top-level group list names `books` with its three keys, by `Book.name` and the group's stable `key`; eval 9 expects the book label plus a group label by key for a two-language manual and never a `pages.*` key. Five eval sentences that restated a rule the skill already teaches pay for most of the case; the eval's ratchet row is raised by the measured remainder (1642 -> 1790) under the standing ruling recorded beside it. Claude-Session: https://claude.ai/code/session_01ADHkmPLgCH4kXgfdFGF4Z2 Co-authored-by: Claude <noreply@anthropic.com>
1 parent 7eb613c commit cd55929

4 files changed

Lines changed: 53 additions & 10 deletions

File tree

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
'@objectstack/skills': patch
3+
---
4+
5+
docs(skills): objectstack-i18n teaches the `books` translation group, with an eval for a two-language doc book
6+
7+
Clause-②: no
8+
Metadata-protocol: none
9+
10+
- **Before.** The top-level group list in `skills/objectstack-i18n/SKILL.md` named fourteen of the bundle's fifteen groups and left out `books`, the group the spec added so a doc book's title and section titles follow the reader's locale. An agent writing a zh-CN bundle from the skill left the docs sidebar in one language or invented a key under `pages`.
11+
- **Now.** The list names `books` with its three keys — `books.{book}.label` / `.description` by `Book.name` and `books.{book}.groups.{group_key}.label` by each group's stable `key` — says the pages themselves localize through `doc.translations` and that a book's inline `translations` map stays retired; `os i18n extract --no-objects-only` seeds the group like every other. Eval 9 in `evals/bundles-locales-coverage.json` checks that an answer for a two-language manual spells the book label and a group label by key and never a `pages.*` key. Five eval sentences that restated a rule the skill already teaches were cut to pay for it.
12+
- No schema, key or behaviour changes.

‎scripts/check-skills-token-ratchet.mjs‎

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -968,7 +968,25 @@ export const CEILINGS = new Map([
968968
// pin duplicated case 5's (77 bytes cut, 20 tokens); the remainder is the new
969969
// case; pinned AT the measurement, zero headroom. The raising PR's body carries
970970
// the same arithmetic.
971-
['skills/objectstack-i18n/evals/bundles-locales-coverage.json', 1642],
971+
// 1642 -> 1790 (2026-10-28, the owed Skills-docs follow-up of the spec PR that
972+
// added the `books` translation group): one eval prompt for a doc book's
973+
// spine copy — `books.<book>.label` / `.description` and
974+
// `books.<book>.groups.<key>.label`. Same ruling as the rows above, tap label
975+
// verbatim and untranslated: 「A:先削重复,余额按实测提高」. PAID DOWN FIRST: five
976+
// sentences restated a rule whose home the reader already reads — case 1's
977+
// "no ICU plural/select" (SKILL.md "No ICU MessageFormat" + its own
978+
// `{count, plural` pin), case 3's "retired o.* dialect" (SKILL.md "The
979+
// Retired o.* Shape" + its own `o: {` pin), case 5's "extract --check is the
980+
// freshness gate" (SKILL.md "os i18n extract --check — freshness, not
981+
// coverage"), case 6's "inherited by every field referencing the picklist"
982+
// (SKILL.md's `picklists` entry in the top-level group list), case 8's
983+
// "neither is a pages.*.components key" (its own `components.open_block`
984+
// pin): 227 bytes = 57 units (6567 -> 6340 bytes, 1642 -> 1585); the new case
985+
// adds 817 bytes = 205 units (6340 -> 7157 bytes, 1585 -> 1790):
986+
// ceil($(wc -c < skills/objectstack-i18n/evals/bundles-locales-coverage.json) / 4) == ceil(7157 / 4)
987+
// pinned AT the measurement, zero headroom. The raising PR's body carries the
988+
// same arithmetic.
989+
['skills/objectstack-i18n/evals/bundles-locales-coverage.json', 1790],
972990
// 1211 -> 1458 (card #26302): one eval for a demo whose seed names people —
973991
// expected: both in `demoStaff` with a declared `position` /
974992
// `permissionSets`, created by `os dev`; NO `role:` alias and NO platform

‎skills/objectstack-i18n/SKILL.md‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,10 +171,13 @@ Top-level groups alongside `objects`: `apps` (label, description, navigation),
171171
an **error** whose message names the `objects.{owner}._actions.{action_name}` key to write
172172
instead), `picklists` (`label`, `options` by stored value — every field referencing the
173173
picklist inherits them), `dashboards`, `datasets`, `reports`, `pages`, `flows`, `positions`,
174-
`permissions` (permission sets, spelled as the stack's `permissions[]`), `metadataForms`,
175-
`settingsCommon`; `reports`, `positions` and `permissions` hold `label` + `description` by name,
176-
and a joined report's block headings sit at `reports.{report}.blocks.{block_name}.{label,description}`,
177-
keyed by the block's required `name`.
174+
`permissions` (permission sets, spelled as the stack's `permissions[]`), `books` (a doc book's
175+
spine, by `Book.name`: `books.{book}.label` / `.description`, and `groups.{group_key}.label` by
176+
each group's stable `key`, never its position — `books.crm_manual.groups.getting_started.label`;
177+
the pages themselves localize through `doc.translations`, and the inline `translations` map a
178+
book once carried stays retired), `metadataForms`, `settingsCommon`; `reports`, `positions` and
179+
`permissions` hold `label` + `description` by name, and a joined report's block headings sit at
180+
`reports.{report}.blocks.{block_name}.{label,description}`, keyed by the block's required `name`.
178181
Two keys sit deeper than their group's `label`: a `page:tabs` item at
179182
`pages.{page}.components.{tabs_id}.items.{value}.label` (by the item's `value`; an item without
180183
one has no key) and an approval step at `flows.{flow}.approvals.{node_id}.label`.

‎skills/objectstack-i18n/evals/bundles-locales-coverage.json‎

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{
55
"id": 1,
66
"prompt": "Add Simplified Chinese for the `support_case` object: its label and plural, the `status` field with options New / Open / Closed, the `close` action, and a welcome message that greets the user by name.",
7-
"expected_output": "A `TranslationData` per locale under `objects.support_case` — `label`, `pluralLabel`, `fields.status.label`, `fields.status.options` keyed by the STORED option value (`new`, `open`, `closed`, never the display label), `_actions.close.label` — assembled with `defineTranslationBundle({ en, 'zh-CN': zhCN })` and registered under `defineStack({ translations })`. The message uses double-brace interpolation, `'welcome.user': '欢迎,{{userName}}!'`; there is no ICU plural/select.",
7+
"expected_output": "A `TranslationData` per locale under `objects.support_case` — `label`, `pluralLabel`, `fields.status.label`, `fields.status.options` keyed by the STORED option value (`new`, `open`, `closed`, never the display label), `_actions.close.label` — assembled with `defineTranslationBundle({ en, 'zh-CN': zhCN })` and registered under `defineStack({ translations })`. The message uses double-brace interpolation, `'welcome.user': '欢迎,{{userName}}!'`.",
88
"files": [],
99
"assertions": {
1010
"must_contain": ["defineTranslationBundle", "objects", "pluralLabel", "options", "_actions", "{{"],
@@ -24,7 +24,7 @@
2424
{
2525
"id": 3,
2626
"prompt": "A customer admin wants to relabel `account` as 客户 in their Studio without waiting for a redeploy.",
27-
"expected_output": "A `translation` metadata item (author with `defineTranslation`, shape `TranslationItemSchema`) carrying `locale: 'zh-CN'` and the same `objects.account.label` group as a file bundle; published items layer over the shipped bundles at boot and on every publish, and deleting the item restores the shipped value. One locale per item; the retired `o.*` dialect is rejected on this door too.",
27+
"expected_output": "A `translation` metadata item (author with `defineTranslation`, shape `TranslationItemSchema`) carrying `locale: 'zh-CN'` and the same `objects.account.label` group as a file bundle; published items layer over the shipped bundles at boot and on every publish, and deleting the item restores the shipped value. One locale per item.",
2828
"files": [],
2929
"assertions": {
3030
"must_contain": ["translation", "locale", "objects", "defineTranslation"],
@@ -44,7 +44,7 @@
4444
{
4545
"id": 5,
4646
"prompt": "`os i18n check` reports zh-CN is missing app, navigation and dashboard keys even though we just ran `os i18n extract`. What is wrong?",
47-
"expected_output": "Diagnoses that `--objects-only` is on by default, so extract wrote only the `objects` / `globalActions` subtree while check also demands `apps`, `navigation`, `dashboard`, `widget`, `page` and `flow` keys. Re-runs `os i18n extract --no-objects-only` (or hand-authors those groups), re-checks with `os i18n check --locales=zh-CN`, and adds `--strict` / `--threshold` in CI; `extract --check` is the separate freshness gate.",
47+
"expected_output": "Diagnoses that `--objects-only` is on by default, so extract wrote only the `objects` / `globalActions` subtree while check also demands `apps`, `navigation`, `dashboard`, `widget`, `page` and `flow` keys. Re-runs `os i18n extract --no-objects-only` (or hand-authors those groups), re-checks with `os i18n check --locales=zh-CN`, and adds `--strict` / `--threshold` in CI.",
4848
"files": [],
4949
"assertions": {
5050
"must_contain": ["--no-objects-only", "os i18n check", "apps", "--strict"],
@@ -54,7 +54,7 @@
5454
{
5555
"id": 6,
5656
"prompt": "Add zh-CN for the `pipeline_by_stage` report, the `sales_manager` permission set, the `regional_lead` position and the `deal_stage` picklist's option labels.",
57-
"expected_output": "Top-level groups keyed by name: `reports.pipeline_by_stage` and `positions.regional_lead` (`label`, `description`), `permissions.sales_manager` (the same, spelled as the stack's `permissions[]`, not the camelCase name), `picklists.deal_stage.options` by stored value, inherited by every field referencing the picklist.",
57+
"expected_output": "Top-level groups keyed by name: `reports.pipeline_by_stage` and `positions.regional_lead` (`label`, `description`), `permissions.sales_manager` (the same, spelled as the stack's `permissions[]`, not the camelCase name), `picklists.deal_stage.options` by stored value.",
5858
"files": [],
5959
"assertions": {
6060
"must_contain": ["reports", "permissions", "positions", "picklists"],
@@ -74,12 +74,22 @@
7474
{
7575
"id": 8,
7676
"prompt": "In zh-CN the `Products` grid heading on the `crm_opportunity` form and the `Open Tasks` block heading of the joined `showcase_task_overview` report are still English.",
77-
"expected_output": "`objects.crm_opportunity_line_item.fields.opportunity.inlineTitle` — the field lives on the child object, so the key is the child's, and an untranslated `inlineTitle` keeps its authored text (no fallback to the child's plural label); `reports.showcase_task_overview.blocks.open_block.label`, keyed by the block's required `name`. Neither is a `pages.*.components` key.",
77+
"expected_output": "`objects.crm_opportunity_line_item.fields.opportunity.inlineTitle` — the field lives on the child object, so the key is the child's, and an untranslated `inlineTitle` keeps its authored text (no fallback to the child's plural label); `reports.showcase_task_overview.blocks.open_block.label`, keyed by the block's required `name`.",
7878
"files": [],
7979
"assertions": {
8080
"must_contain": ["inlineTitle", "crm_opportunity_line_item", "blocks", "open_block"],
8181
"must_not_contain": ["fields.opportunity_line_item", "components.open_block"]
8282
}
83+
},
84+
{
85+
"id": 9,
86+
"prompt": "Our `crm_manual` doc book ships in English and zh-CN, but the docs sidebar still shows the book title and its `getting_started` section title in English.",
87+
"expected_output": "The `books` group, keyed by `Book.name`: `books.crm_manual.label` / `.description` and `books.crm_manual.groups.getting_started.label`, by the group's stable `key` (never its position); `os i18n extract --no-objects-only` seeds all three. The pages' own titles and bodies localize through `doc.translations`, and the inline `translations` map a book once carried stays retired.",
88+
"files": [],
89+
"assertions": {
90+
"must_contain": ["books", "groups", "getting_started", "doc.translations"],
91+
"must_not_contain": ["pages.crm_manual", "sections.getting_started"]
92+
}
8393
}
8494
]
8595
}

0 commit comments

Comments
 (0)