Skip to content

fix(platform-objects): decide the action body/ai en-echoes per leaf, and close the one-schema-two-forms blind spot - #19509

Merged
huangyiirene merged 4 commits into
mainfrom
claude/issue-19403-round5-en-echoes
Sep 21, 2026
Merged

huangyiirene merged 4 commits into
mainfrom
claude/issue-19403-round5-en-echoes

Conversation

@huangyiirene

Copy link
Copy Markdown
Collaborator

Part of #19403

Clause-②: no

What this is

Round 5 of this card. One panel family, decided leaf by leaf. The deliverable is a per-leaf judgement with its reason recorded, not twelve translations; every leaf below was decided on its own evidence, and the verdicts with their reasons live in a ledger the suite reads (action-body-panel-echo-decisions.test.ts), not only in this body.

The family: the five children of action.fields.body plus action.fields.ai — the body composite on the action metadata form, and the AI-exposure block one section over. Both string leaves of each key are decided.

Not Fixes — this is one family of the card's population, and the card outlives this round. Four families remain (23 label keys / 39 decidable sibling leaves), tabled at the foot of this body.

The tie at the top of round 5's table, and how it was broken

Round 4 fixed the sizing rule as largest by LEAVES. Re-derived on this branch's base 4045781fa, two rows tie at 12:

family keys leaves
action.fields.body.* + action.fields.ai — what this round took 6 12
six bare type-display pairs (seed·mapping·api·doc·book·capability) 6 12
object.fields.enable.* (+validations) 9 11
hook.fields.retryPolicy.* + body.memoryMb + timeoutMs 5 10
report.fields.drilldown + runtimeFilter + sections.dataset_binding 3 6

The tie-break is written into the ledger's header, four reasons, none of them preference:

  1. The instrument survives only one of them. The card's second property is a population DERIVED from en with a dark control. action is one panel: one derivation over one metadata type, and the dark control has something to exclude (it asserts retryPolicy, valueDomain, drilldown, enable and the hook section headings are not action leaves). The six type-display pairs are six unrelated types with no panel around them — the derivation degenerates into six one-entry lookups and the control has nothing to be dark about.
  2. Evidence density forces per-leaf work. Four of the six keys have an authored twin at a byte-identical en one metadata type over; one has a twin that is ITSELF an echo; one has no twin at all.
  3. The A metadata-form tooltip spells a strict enum's legal values in translated locales, so the value an author is shown is one the schema refuses #19430 trap is live here, on three of the twelve leaves, and is discharged at the schema below rather than deferred.
  4. Leaving it costs more. A bare type-display pair has no siblings, so deferring it leaves nothing half-done. Deferring this one leaves the action panel's Behavior and Advanced sections reading half Chinese and half English around a single composite.

The census — this branch's own reading, base 4045781fa

reading base this branch
en string leaves / .label leaves 893 / 538 893 / 538 — same population
label keys echoing in ALL THREE locales 29 23
⇒ as .label leaves 87 69
⇒ as decidable sibling leaves (label + its helpText/description) 51 39
POSITIVE CONTROL — labels genuinely translated (zh-CN) 509 515
POSITIVE CONTROL (ja-JP · es-ES) 493 · 493 499 · 499

Echoes fall 6 while the control rises 6 in each locale, same population, same run. A parser matching too broadly cannot produce that agreement. 87 and 69 count .label leaves; 51 and 39 count decidable sibling leaves — two questions, two right answers, and this body states which it means at every number.

The evidence, per key — and the one key that had none

actionForm and hookForm declare the same composite over the same schema: HookBodySchema (packages/spec/src/data/hook-body.zod.ts), five children, identical field names, byte-identical helpText sources. So the precedent is not a same-string match found elsewhere in the catalog — it is the other rendering of this very schema key.

key evidence verdict
body.language authored twin, byte-identical en, and it names both tokens translate ×3
body.source authored twin; the bare word Source has three authored senses in this catalog and the twin picks the code one translate ×3
body.capabilities authored twin, byte-identical en, keeps all four enum members translate ×3
body.timeoutMs authored twin; the en is the extractor's humanize of timeoutMs, and the twin renders the concept translate ×3
body.memoryMb ⛔ NO TWIN — the hook twin is itself an echo in all three locales. Composed from the sibling key's authored twin instead, and the row says so translate ×3
ai no twin of any kind (hookForm declares no ai); the authored section description one entry up the same panel names the concept, the schema settles the tokens translate ×3

⭐ Round 4's lesson, read the other way round. Round 4 recorded that a precedent answers only the question it actually contains: its twin settled the prose and named no token, so the schema had to settle the tokens. Here the twin settles both halves for language and capabilities — hook.fields.body.language.helpText is authored AND names expression and js AND keeps both English. Same rule, opposite reading: the twin is sufficient precisely because the question is inside it. For ai.helpText there is no twin at all and only the schema answers. Each row says which half its evidence covers.

⭐ And one leaf shows why a wholesale answer is refused. action.fields.ai.label reads Ai — the extractor's humanize of the two-letter key. The obvious repair is the case fix Ai to AI, which differs from the source in bytes, passes the echo predicate in every locale, and tells a zh-CN author exactly as much as the echo did. Rejected as a phantom translation; the label takes the concept the panel's own authored section description already names, with es-ES rendering the acronym IA and the other two keeping AI, exactly as that description does.

The #19430 trap was live on three leaves, and every one was read at the schema first

#19430 is not addressed here and remains open — this is the discipline it asked for, applied before a word was rendered.

leaf tokens named in prose what the schema says done
body.language.helpText expression, js the two z.literal discriminators of HookBodySchema kept English
body.capabilities.helpText api.read, api.write, crypto.uuid, log four of the five HookBodyCapability z.enum members (api.transaction is the fifth and the prose names it not at all) kept English
ai.helpText ai.exposed=true, ai.description ActionAiSchema's two canonical keys — a strictObject that declares enabled/enable/aiEnabled/expose/visible as aliases of exposed, so a rendered spelling is a spelling the schema refuses kept English

Also kept: ctx and import (the sandbox binding and the reserved word ScriptBodySchema forbids), and the schema bounds 256 (.max(256)) and 40 (.min(40)). All of it is asserted, not described — a machine-token guard reds when any of them stops being carried verbatim, and ablation C reds it specifically.

⭐ The blind spot's FIFTH shape: one schema, two forms, invisible in OPPOSITE ways

Rounds 1–4 measured record, composite, a pin green one level up from what it walks, and a pin's own vacuity control passing to the side. This round measured it across two forms.

repeater-row-properties.test.ts derives a row property for every child of a type: 'repeater' field. Measured on this base:

form covered by that pin invisible to it state before this round
actionForm 18 (params.*) 5 (body.*, a composite) all 18 authored · all 5 echoing
hookForm 0 7 (body.* + retryPolicy.*) 4 of 5 body children authored · memoryMb and both retryPolicy children echoing

⇒ the same composite is invisible to the same pin in two opposite ways. On action it is the lit vacuity control carrying.has('action') === true that passes — supplied by params, the repeater that was never broken (round 4's shape, reproduced). On hook it is the dark control carrying.has('hook') === false, which is true and correct — hookForm enumerates no repeater at all — while seven children sit enumerated and outside it. A correct control and a passing control produce the same green, and neither can be repaired by deriving harder on one panel.

⭐ Because the half that matters lives BETWEEN the two derivations. Before this round action.fields.body.* echoed on 5 of 5 children and hook.fields.body.* on 1 of 5: the same five schema keys, the same five en sources, two different states, and no assertion anywhere that they should agree. This PR adds one — wherever the hook twin is authored, action renders it identically in every locale — with its own lit control proving it compared real leaves (30 pairs, 6 excluded because memoryMb carries no authored twin in any locale), and the exclusion set asserted to be exactly ['memoryMb']. When the hook family lands in a later round, this assertion starts covering memoryMb too and reds if that round renders it differently from this one.

ablation D is the one worth reading: it makes action disagree with its authored twin and only that assertion reds. Every echo rule stays green, because the mutated string is not the en source.

The instrument — five independent assertions, proven by four ablations

Mutation, run and restore inside one ablation-replace.mjs process per ablation. Every restore verified: git diff HEAD empty and the blob back to HEAD's, checked independently afterwards as well.

ablation what it mutates reds
A a decided leaf reverted to its en source in one locale the enumerated verdict and the derived panel pin — 2, independently
B a provenance row re-pointed at a decided leaf a third — the second witness
C a kept spec key rendered as words a fourth — the machine-token guard
D the action rendering made to disagree with its authored hook twin a fifth — the new cross-panel invariant, and nothing else

⇒ 2 + 1 + 1 + 1 = five assertions that fail on five different triggers, not five spellings of one. All four dark controls carried from the earlier rounds are present and fire: the echo predicate flags all 12 rows when fed en; the derived predicate flags all 79 panel leaves when fed en; the provenance lookup is proved to be able to say "still a fill", matched by pattern so a later round cannot red it by fixing a family; and the machine-token guard's negative uses api.transaction — a real HookBodyCapability member this prose deliberately does not name — so a green cannot come from an includes that matches anything.

⛔ The silent-loss hazard: values only, measured with a discriminating control

*.metadata-forms.generated.ts is generator-owned but git check-attr merge reads unspecified, so it is NOT routed to merge=os-regen. Regenerated with pnpm i18n:extract, never by hand, and no key was added or removed:

en / zh-CN / ja-JP / es-ES:  base 893 keys | head 893 | added 0 | removed 0
values changed per bundle:   en 0 | zh-CN 12 | ja-JP 12 | es-ES 12   (exactly the decided leaves)
provenance rows:             12 removed per locale, 0 added   (exactly the decided leaves)
NEGATIVE CONTROL (drop one key by hand): removed 1 => the comparator CAN see a 1-key delta

⚠️ One measurement worth recording: the anchor for body.memoryMb occurs twice in each translated bundle — once under action, once under hook, byte-identical — so the edit was scoped to the top-level action block and refused on any count other than one. An unscoped replacement would have exited 0 having silently translated a leaf belonging to a family this round does not own.

The changeset, measured

@objectstack/platform-objects has no private key and ships files: ["dist", …], and the decided leaves reach dist — so skip-changeset would be measurably wrong and a patch changeset is included. Dist probe over all three entry points that carry the catalogs (index.mjs, plugin.mjs, metadata-translations/index.mjs), with controls:

zh  内存(MB)  as 内存(MB)   1 1 1      lower-case hex spelling   0 0 0
ja  メモリ(MB) as メモリ(MB)  1 1 1
es  Exposici\xF3n a IA                          3 3 3      lower-case hex spelling   0 0 0
ai.exposed=true kept verbatim                   4 4 4
NEGATIVE CONTROL  raw un-escaped 内存(MB)      0 0 0      raw un-escaped メモリ     0 0 0
NEGATIVE CONTROL  an invented string            0 0 0

⇒ the raw un-escaped control reads 0 including for the positive controls, so a zero here means "escaped", not "absent", and esbuild's UPPER-CASE hex is confirmed by its lower-case spelling reading 0.

Gates

  • pnpm --filter @objectstack/platform-objects test — 48 files / 690 passed (47/668 on the base: +1 file, +22 tests); typecheck exit 0; pnpm check:i18n exit 0; repo-wide pnpm lint exit 0 at 6f249d952.
  • Gate reconciliation: 60 derived / 60 run green / 0 NOT-MEASURED / 0 UNRUN, derived with scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack and reconciled with --ran carrying every exit code captured before any pipe, so the zero is DERIVED rather than claimed.
  • check:dual-build-cjs-loads first exited 3 — PREREQUISITE NOT MET, 14 packages with no dist/. Discharged, not reported as a 3: the closure the gate names was built and the gate re-run clean (104 published require entry points across 67 packages, 620 CJS files parsed).
  • origin/main moved three commits (one of them a packages/spec change) while this ran; merged, build state refreshed per AGENTS.md §9, check:generated for spec exit 0, the package suite re-run green, and the gate derivation re-taken on the merged tree — byte-identical.

Acceptance notes

Everything below is noted, not filed. This round files zero cards, and that is a reading rather than an absence:

  • The known trap was checked, not assumed. action.fields.ai.helpText is the leaf on this panel that could have carried A metadata-form tooltip spells a strict enum's legal values in translated locales, so the value an author is shown is one the schema refuses #19430 without a precedent to catch it — no twin exists anywhere in either catalog. It was taken to ActionAiSchema in packages/spec/src/ui/action.zod.ts and read there: two canonical keys, five declared aliases of exposed, a .min(40) bound. Nothing about it is a defect in this repo's code; it is a leaf that needed a schema read, and got one. ⇒ no card.
  • hook.fields.body.capabilities.label reads 功能 / 機能 ("feature") for a value that is a capability TOKEN, and this round copied that word rather than improving it, because one schema key must have one rendering across the two panels that declare it and rewording it is an edit to the hook family. It is a wording preference on a landed authored string, not a reproducible defect, not a contract violation and not a metadata-authoring trap — none of the three classes. 承接者: the round of this card that takes the hook family, which will hold both panels to whichever word it picks, because the assertion this PR adds makes divergence red.
  • Keys echoing in only one or two locales are outside this card's population by definition (the card counts the all-three intersection; its own filing note records 83 · 99 · 99 per locale at filing). object.fields.lifecycle.retention.maxAge.label is one such: 最长保留 in zh-CN, still Max Age in ja-JP and es-ES. Recorded so the next reader does not mistake the intersection for the whole. 承接者: 无 — no queued issue covers it and this round does not widen the card to reach it.
  • A toolchain reading, for whoever runs a closure build next. pnpm --filter PKG build --concurrency=2 forwards --concurrency=2 into each package's build script; tsup ignores it, but @objectstack/cli's shell-script build dies on sh: 1: Syntax error: word unexpected. The working spelling is pnpm exec turbo run build --concurrency=2 --filter=PKG. Pre-existing tooling behaviour, already documented in the agent rules' toolchain traps, reproduced here as a fact rather than filed. 承接者: 无.

What is left — in keys AND leaves, re-measured on this branch

family keys leaves
six bare type-display pairs (seed·mapping·api·doc·book·capability) 6 12
object.fields.enable.* (+validations) 9 11
hook.fields.retryPolicy.* + body.memoryMb + timeoutMs 5 10
report.fields.drilldown + runtimeFilter + sections.dataset_binding 3 6
total 23 39

⇒ as .label leaves that same remainder is 69. The tie at the top is gone: the six type-display pairs are now uniquely largest by leaves. ⚠️ Whoever takes them inherits the warning round 5's order carried — they are a different shape from every family so far, bare TYPE.label + TYPE.description with no panel around them, so the "authored twin one entry down the same panel" evidence has no analogue there and something must stand in for it. ⚠️ And the hook family now carries a second obligation: the cross-panel assertion this PR adds means its body.memoryMb rendering must match the one landed here.


Generated by Claude Code

Round 5 of #19403. Value-only edit to the three translated
metadata-form bundles; provenance tables not yet refreshed.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
…and measure the one-schema-two-forms blind spot

Round 5 of #19403. Twelve string leaves of the `action` metadata form
decided one at a time, each with its reason recorded in a ledger the
suite reads; provenance tables refreshed with `pnpm i18n:extract`.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
The diff publishes: the decided leaves reach the built dist in all
three entry points, so skip-changeset would be measurably wrong.

Claude-Session: https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

3 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

What this run could not see

Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json a227afa415f596269ed36aae0a0631c84270ccc9 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 04454f54fea8c6d657bfce7790d4f6a5c7709deb — the merge of head 6f249d9521f8074345cbf7e5373137d3505c0f94 into base a227afa415f596269ed36aae0a0631c84270ccc9, which is what actions/checkout gives a pull_request run. Not the PR head.

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

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 04454f54fea8c6d657bfce7790d4f6a5c7709deb && git checkout 04454f54fea8c6d657bfce7790d4f6a5c7709deb
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a227afa415f596269ed36aae0a0631c84270ccc9 6f249d9521f8074345cbf7e5373137d3505c0f94 && git checkout -B drift-repro a227afa415f596269ed36aae0a0631c84270ccc9 && git merge --no-ff 6f249d9521f8074345cbf7e5373137d3505c0f94

node scripts/docs-audit/affected-docs.mjs --json a227afa415f596269ed36aae0a0631c84270ccc9

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

@huangyiirene
huangyiirene marked this pull request as ready for review September 21, 2026 05:26
@huangyiirene
huangyiirene added this pull request to the merge queue Sep 21, 2026
Merged via the queue into main with commit c736eaa Sep 21, 2026
36 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-19403-round5-en-echoes branch September 21, 2026 05:48
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 28, 2026
…r leaf, and pay round 5's cross-panel exclusion (objectstack-ai#19530)

Part of objectstack-ai#19403

Clause-②: no

Round 6 of objectstack-ai#19403. The `hook` metadata-form panel's ten remaining
en-echoes, decided one leaf at a time, with the reason recorded for
each. **With them the panel is finished: zero of its 47 string leaves
now echoes its English source in all three locales.**

## The family, and why the leaf count did NOT decide it

Re-measured on this branch's base `0e671d20c` — the four remaining
families, in **both** counts:

| family | keys | leaves |
|:--|--:|--:|
| six bare type-display pairs
(`seed`·`mapping`·`api`·`doc`·`book`·`capability`) | 6 | **12** |
| `object.fields.enable.*` (+ `validations`) | 9 | 11 |
| **`hook.fields.retryPolicy.*` + `body.memoryMb` + `timeoutMs` ←
taken** | **5** | **10** |
| `report.fields.drilldown` + `runtimeFilter` +
`sections.dataset_binding` | 3 | 6 |

This family is **third of four by leaves**, so the tie-break is written
down — in the ledger's header and here. Two of the four reasons are ones
no other row can offer:

1. **It is the only family that makes the LANDED instrument stronger
rather than longer.** PR objectstack-ai#19509's cross-panel invariant compares 5
shared `HookBodySchema` children × 2 props × 3 locales = 30 pairs and
**excluded 6 of them**, because `hook.fields.body.memoryMb` was itself
an en-echo and an unauthored twin carries no evidence. That file asserts
the exclusion by name and states that a later round of this card owes
it. Deciding `body.memoryMb` here **pays it**: the exclusion set is
empty, 30 of 30 pairs are compared, and `memoryMb` is guarded on both
panels. Ablation D reds that landed assertion, which is the proof the
discharge is real.
2. **It is the remaining half of a measured blind spot, on the panel
where the existing pin is blind through a control that is CORRECT.**
Round 5 measured `hookForm`'s enumerated-but-unwalked children at
**seven** — the five `body.*` plus `retryPolicy.maxRetries` and
`retryPolicy.backoffMs` — and closed the five on the `action` side only.
The two `retryPolicy` children are what was left.
3. **Evidence density forces per-leaf work**: `body.memoryMb` takes a
twin that exists only because round 5 created it; `timeoutMs` takes an
authored twin one level down the same panel at a byte-identical `en`;
`retryPolicy.*` has no twin anywhere and is composed from this catalog's
authored answers for its separate words.
4. **It completes a panel** — recorded as a reason that did **not**
decide, because `object` and `report` would complete theirs too.

And why **not** the six bare pairs, which lead by leaves: round 5's
refusal stands unchanged — only a real panel keeps the
DERIVED-POPULATION property with a working dark control, and six
unrelated types degenerate the derivation into six one-entry lookups
with nothing for the control to exclude.

## The ten decisions

Every verdict is `translate` in all three locales; every row records the
authored leaf its wording came from.

| leaf | `en` | zh-CN / ja-JP / es-ES | evidence shape |
|:--|:--|:--|:--|
| `body.memoryMb.label` | `Memory Mb` | 内存(MB) · メモリ(MB) · Memoria (MB)
| the twin **round 5 created** — copied verbatim |
| `body.memoryMb.helpText` | `Per-invocation memory cap (MB, max 256)` |
单次调用内存上限(MB,最大 256) · 呼び出しごとのメモリ上限(MB、最大 256) · Límite de memoria por
invocación (MB, máx. 256) | same twin; `256` kept as a numeral
(`ScriptBodySchema.max(256)`) |
| `timeoutMs.label` | `Timeout Ms` | 超时(毫秒) · タイムアウト(ms) · Tiempo de
espera (ms) | authored twin **one level down the same panel** |
| `timeoutMs.helpText` | `Abort the hook after N milliseconds` | 超过 N
毫秒后中止该钩子 · N ミリ秒経過後にフックを中止 · Abortar el hook después de N milisegundos |
the objectstack-ai#19430 **near-miss**, cleared at the schema; `N` kept |
| `retryPolicy.label` | `Retry Policy` | 重试策略 · 再試行ポリシー · Política de
reintentos | composed from two authored answers in this catalog |
| `retryPolicy.helpText` | `Retry on failure — most useful for async
hooks` | 失败时重试——对异步钩子最有用 · 失敗時に再試行 — 非同期フックで特に有用 | `async` read at the
schema: a boolean, not a token |
| `retryPolicy.maxRetries.label` | `Max Retries` | 最大重试次数 · 最大再試行回数 ·
Reintentos máximos | extractor humanize rendered as the concept |
| `retryPolicy.maxRetries.helpText` | `Maximum retry attempts` |
最多可重试的次数 · 再試行の最大回数 · Número máximo de reintentos | the schema default
`3` deliberately **not** rendered |
| `retryPolicy.backoffMs.label` | `Backoff Ms` | 退避(毫秒) · バックオフ(ms) ·
Retroceso (ms) | `sys_job_queue.fields.backoff_type.label`'s authored
answer |
| `retryPolicy.backoffMs.helpText` | `Delay between retries (ms)` |
两次重试之间的延迟(毫秒) · 再試行間の遅延(ms) · Retraso entre reintentos (ms) | same
vocabulary; default `1000` not rendered |

**What was refused.** Four of the five labels are the extractor's
humanize of a camelCase key, not authored English — exactly where round
5's phantom-translation reading bites. `Memory Mb` → `Memory (MB)` or
`Max Retries` → `Max Retries (3)` would differ in bytes, **pass the echo
predicate in all three locales, drop the census by a key and tell a
zh-CN author nothing.** Every one is rendered into the locale's own
writing system instead, and the enumerated verdicts — not the census —
are what shows it.

## The objectstack-ai#19430 discipline, and the near-miss this family had to clear

Read at the schema before a word was rendered, and **asserted against
the live `HookSchema`** rather than described:

- `timeoutMs.helpText` says "Abort the hook after N milliseconds", and
`abort` **is** a legal value of `HookSchema.onError`
(`z.enum(['abort','log'])`). The near-miss is real. It is **cleared**
because the key this tooltip describes takes a **number** — the schema
accepts `5000` and refuses `"5000 毫秒"` — so no rendered word can land in
it, and the schema's own description uses the word as the runtime's verb
("before the hook is aborted"). Rendered.
- `retryPolicy.helpText` names `async`. `HookSchema.async` is
`z.boolean().default(false)` — accepts `true`, refuses `"异步"` — so it is
**not** a objectstack-ai#19430 token. Rendered, taking this panel's own authored 异步 /
非同期 / Asíncrono one entry up.
- `retryPolicy` is a `strictObject`, not an enum; its alias map governs
KEY spellings, which no label renders. Its defaults are `{maxRetries: 3,
backoffMs: 1000}` and neither tooltip carries a number — asserted both
ways.
- `N` and `256` are kept verbatim, and the token guard now judges
**Unicode word boundaries** rather than substring containment: `'Número
máximo de reintentos'.includes('N')` is `true`, so round 5's containment
check would have passed a locale that lost the placeholder entirely.
That is a control, and ablation E reds it.

## A second act: `body.capabilities` reworded on both panels

Round 5 **recorded rather than improved** that zh-CN 功能 and ja-JP 機能
read "feature" for what is a capability **token** from the
`HookBodyCapability` enum — and left the reword to the hook family,
because both forms declare the same schema key and diverging is the
defect the cross-panel invariant guards. This round is the hook family.

The replacement is **derived, not invented**: every authored leaf of the
objects catalog whose English names a capability renders it **能力**
(zh-CN) and **ケイパビリティ** (ja-JP), and none of them uses 功能 or 機能. The
ledger asserts that derivation over the catalog rather than quoting it.
es-ES already read `Capacidades` — the same word those leaves use — and
is **unchanged**, which is why the zh-CN and ja-JP bundle diffs are two
lines larger than es-ES's. The neighbouring senses
(`object.sections.capabilities` = 功能开关, the feature CHECKBOXES;
`agent.sections.capabilities` = 能力配置) are deliberately untouched and
pinned as such.

## The measurements, each with its control in the same run

| reading | base `0e671d20c` | head |
|:--|--:|--:|
| `en` string leaves / `.label` leaves | 893 / 538 | 893 / 538 — same
population |
| label keys echoing in ALL THREE | **23** ⇒ 69 `.label` leaves | **18**
⇒ **54** |
| POSITIVE CONTROL — labels genuinely translated | 515 · 499 · 499 |
**520 · 504 · 504** |
| decidable sibling remainder (label + its helpText/description) | 39 |
**29** |
| `hook` panel leaves echoing in all three | 10 of 47 | **0 of 47** |

Echoes −5 keys, control +5 in each locale, same population, same run.
**Two counts for two questions** — the headline moves by 5 because only
labels are counted, while this round decided 5 labels **and** 5
helpTexts.

- **No key added or removed.** Full flattened key sets, base vs head,
all four bundles: 893 / 893, 0 added, 0 removed — with a negative
control proving the comparator sees a one-key delta in both directions
(`hook.fields.__probe__.label`). `en` is untouched.
- **Regenerated with `pnpm i18n:extract`**, never by hand. It dropped
exactly the 10 decided leaves' provenance rows in each of the three
locales (30 rows, listed in full in the report comment) and **added
none**; the metadata-forms bundles round-tripped byte-identical.
- **Bundle diffs**: zh-CN 12/12, ja-JP 12/12, es-ES 10/10 — insertions
equal deletions in every bundle. The two extra lines per CJK bundle are
the `capabilities` reword on the two panels; es-ES needed none.

## The concurrent PR that held this region

**PR objectstack-ai#19520 HAD LANDED** by the time of the pre-push merge — so the
required check was **not vacuous**, and it passes. Its leaf is
`view.fields.userFilters.helpText`, and in all three bundles this branch
carries `origin/main`'s value, not the base's:

```
base  快速筛选栏:控件样式(下拉 / 标签页 / 开关)+ 暴露的字段或标签页预设
main  快速筛选栏:控件样式(dropdown / tabs / toggle)+ 暴露的字段或标签页预设
head  快速筛选栏:控件样式(dropdown / tabs / toggle)+ 暴露的字段或标签页预设
```

`base != main` (the fix moved it) and `head == main` (it survived) hold
for `zh-CN`, `ja-JP` and `es-ES`. The merge brought its
one-line-per-bundle change in cleanly, exactly as the dispatch's
five-round measurement predicted. That changeset —
`19430-user-filters-element-tokens` — is **this card's own round-2
finding** coming back fixed by another seat, so silently reverting it
would have been this lane undoing its own filed work.

## The instrument — five ledgers extended, not rebuilt

`hook-execution-panel-echo-decisions.test.ts` carries every property the
five earlier rounds established, plus two this family added:

- the echo-reason rule as a **predicate with a dark control** (proved on
a synthetic row, since every verdict here is `translate`);
- the panel population **DERIVED** from the `en` catalog, with a dark
control naming keys that exist on other types — including `action`'s own
`body.memoryMb` at a byte-identical `en`;
- the **second witness** over the three `*.source-hashes.generated.ts`
companions, with its own pattern-matched dark control;
- the `en` source pinned **equal** to the live bundle, for the decisions
**and** the rewords;
- the **machine-token guard**, upgraded to Unicode word boundaries — the
new part, forced by a bare `N`;
- the **cross-panel invariant**, now at 30/30 with a `compared` counter
keeping it lit;
- the **composite blind spot** on the hook side, closed.

`action-body-panel-echo-decisions.test.ts` is updated in the same PR:
its exclusion set is emptied, its lit assertion becomes a counted 30,
and the prose that quoted the old capability wording now points at the
derivation behind the new one.

### Five ablations, ten assertions, two files

Each ran mutation → run → restore inside **one**
`scripts/ablation-replace.mjs` process, which proves the mutation landed
by blob hash and proves the restore by `blob == HEAD` plus an empty `git
diff HEAD`. No rebuild is involved: these tests resolve the bundles as
sibling sources, not through `dist`.

| # | mutation | assertions reddened |
|:--|:--|:--|
| A | re-fill `zh-CN hook.fields.retryPolicy.label` with its `en` |
`zh-CN: every decided leaf matches its verdict`; `no leaf on this panel
reads its en source…` |
| B | reword the `en` source under the pinned row | `every row is pinned
to the live en source`; `the echo predicate can say "echo"` |
| C | put a decided leaf's provenance row back | `zh-CN: no decided leaf
is still recorded as an extractor fill` |
| D | render `body.memoryMb` differently on the two panels | **round 5's
landed invariant** `the half NO single-panel pin can see`; `30 of 30
pairs agree`; `the invariant can fire` |
| E | drop the `N` placeholder from the es-ES tooltip | `the machine
tokens… are still verbatim`; `the token predicate can say NO` |

## Changeset — measured, not assumed

`@objectstack/platform-objects` is `private: false` with `files:
["dist", …]`, and the decided strings reach `dist`: each appears in
**6** built files, escaped (`\uXXXX` upper-case hex, and `\xNN` for
U+0080–U+00FF — `Pol\xEDtica de reintentos`). A **raw un-escaped
negative control** reads **0 including for the positive controls**, so a
zero in the raw column means "escaped", not "absent", and a
never-written string reads 0 in both columns. ⇒ a **`patch`** changeset;
`skip-changeset` is measurably wrong.

## Acceptance notes

**Zero cards to file, and that is a reading rather than a silence.** The
leaf that could have carried the known objectstack-ai#19430 trap is
`hook.fields.timeoutMs.helpText`, whose `abort` **is** a real
`HookSchema.onError` enum value — it was checked at the schema, found to
describe a numeric key, and cleared, with the check asserted in the
ledger so a schema change reds it. `retryPolicy.helpText`'s `async` was
checked the same way and is a boolean. Neither is a defect, and neither
is a card.

Noted, not filed:

- **`hook.label` is `Gancho` in es-ES while every es-ES sentence on the
panel says `hook`** (`hook.sections.identity.description`,
`hook.fields.condition.helpText`, `hook.fields.async.helpText`, and now
`timeoutMs.helpText`, which copies that convention). This is a wording
inconsistency inside one panel, not a reproducible defect, not a
contract violation, and not a metadata-authoring trap — the author
writes neither word. Carrier: 无 (no PR or person is queued to touch this
file for it); it belongs to whichever round of objectstack-ai#19403 takes a family
that renders the type's own display pair. Dedupe words: `es-ES`,
`Gancho`, `hook.label`, `metadata-forms`, `panel vocabulary`.
- **`permission.fields.systemPermissions.helpText` renders "system
capability keys" as システム機能キー in ja-JP**, which is the word this PR just
replaced for the hook body's capability tokens — but that leaf is a
*permission* capability, a different sense, and it is authored and
outside this family. Recorded so a later round does not read this PR's
reword as a sweep. Carrier: 无. Dedupe words: `ja-JP`, `機能`, `ケイパビリティ`,
`systemPermissions`, `capability wording`.

Neither carries `class: a|b|c` evidence, so by this round's reporting
contract both stay here as prose.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01NcPSwnmJHczmTu6FG7NMjE)_

---------

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants