Repository navigation
fix(platform-objects): decide the action body/ai en-echoes per leaf, and close the one-schema-two-forms blind spot - #19509
Conversation
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>
📓 Docs Drift Check3 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): Which tree this was computed onThis run read A worktree cut from an older # 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 |
…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>
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.bodyplusaction.fields.ai— thebodycomposite 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:action.fields.body.*+action.fields.ai— what this round tookseed·mapping·api·doc·book·capability)object.fields.enable.*(+validations)hook.fields.retryPolicy.*+body.memoryMb+timeoutMsreport.fields.drilldown+runtimeFilter+sections.dataset_bindingThe tie-break is written into the ledger's header, four reasons, none of them preference:
enwith a dark control.actionis one panel: one derivation over one metadata type, and the dark control has something to exclude (it assertsretryPolicy,valueDomain,drilldown,enableand thehooksection 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.enone metadata type over; one has a twin that is ITSELF an echo; one has no twin at all.The census — this branch's own reading, base
4045781faenstring leaves /.labelleaves.labelleaveshelpText/description)zh-CN)ja-JP·es-ES)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
.labelleaves; 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
actionFormandhookFormdeclare the same composite over the same schema:HookBodySchema(packages/spec/src/data/hook-body.zod.ts), five children, identical field names, byte-identicalhelpTextsources. So the precedent is not a same-string match found elsewhere in the catalog — it is the other rendering of this very schema key.body.languageen, and it names both tokensbody.sourceSourcehas three authored senses in this catalog and the twin picks the code onebody.capabilitiesen, keeps all four enum membersbody.timeoutMsenis the extractor's humanize oftimeoutMs, and the twin renders the conceptbody.memoryMbaihookFormdeclares noai); the authored section description one entry up the same panel names the concept, the schema settles the tokens⭐ 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
languageandcapabilities—hook.fields.body.language.helpTextis authored AND namesexpressionandjsAND keeps both English. Same rule, opposite reading: the twin is sufficient precisely because the question is inside it. Forai.helpTextthere 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.labelreadsAi— the extractor's humanize of the two-letter key. The obvious repair is the case fixAitoAI, 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, withes-ESrendering the acronymIAand the other two keepingAI, 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 hereand remains open — this is the discipline it asked for, applied before a word was rendered.body.language.helpTextexpression,jsz.literaldiscriminators ofHookBodySchemabody.capabilities.helpTextapi.read,api.write,crypto.uuid,logHookBodyCapabilityz.enummembers (api.transactionis the fifth and the prose names it not at all)ai.helpTextai.exposed=true,ai.descriptionActionAiSchema's two canonical keys — astrictObjectthat declaresenabled/enable/aiEnabled/expose/visibleas aliases ofexposed, so a rendered spelling is a spelling the schema refusesAlso kept:
ctxandimport(the sandbox binding and the reserved wordScriptBodySchemaforbids), and the schema bounds256(.max(256)) and40(.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.tsderives a row property for every child of atype: 'repeater'field. Measured on this base:actionFormparams.*)body.*, acomposite)hookFormbody.*+retryPolicy.*)bodychildren authored ·memoryMband bothretryPolicychildren echoing⇒ the same composite is invisible to the same pin in two opposite ways. On
actionit is the lit vacuity controlcarrying.has('action') === truethat passes — supplied byparams, the repeater that was never broken (round 4's shape, reproduced). Onhookit is the dark controlcarrying.has('hook') === false, which is true and correct —hookFormenumerates 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 andhook.fields.body.*on 1 of 5: the same five schema keys, the same fiveensources, two different states, and no assertion anywhere that they should agree. This PR adds one — wherever thehooktwin is authored,actionrenders it identically in every locale — with its own lit control proving it compared real leaves (30 pairs, 6 excluded becausememoryMbcarries no authored twin in any locale), and the exclusion set asserted to be exactly['memoryMb']. When thehookfamily lands in a later round, this assertion starts coveringmemoryMbtoo and reds if that round renders it differently from this one.ablation Dis the one worth reading: it makesactiondisagree with its authored twin and only that assertion reds. Every echo rule stays green, because the mutated string is not theensource.The instrument — five independent assertions, proven by four ablations
Mutation, run and restore inside one
ablation-replace.mjsprocess per ablation. Every restore verified:git diff HEADempty and the blob back toHEAD's, checked independently afterwards as well.ensource in one localeactionrendering made to disagree with its authoredhooktwin⇒ 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 feden; 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 usesapi.transaction— a realHookBodyCapabilitymember this prose deliberately does not name — so a green cannot come from anincludesthat matches anything.⛔ The silent-loss hazard: values only, measured with a discriminating control
*.metadata-forms.generated.tsis generator-owned butgit check-attr mergereadsunspecified, so it is NOT routed tomerge=os-regen. Regenerated withpnpm i18n:extract, never by hand, and no key was added or removed:body.memoryMboccurs twice in each translated bundle — once underaction, once underhook, byte-identical — so the edit was scoped to the top-levelactionblock 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-objectshas noprivatekey and shipsfiles: ["dist", …], and the decided leaves reachdist— soskip-changesetwould be measurably wrong and apatchchangeset is included. Dist probe over all three entry points that carry the catalogs (index.mjs,plugin.mjs,metadata-translations/index.mjs), with controls:⇒ 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);typecheckexit 0;pnpm check:i18nexit 0; repo-widepnpm lintexit 0 at6f249d952.scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstackand reconciled with--rancarrying every exit code captured before any pipe, so the zero is DERIVED rather than claimed.check:dual-build-cjs-loadsfirst exited 3 — PREREQUISITE NOT MET, 14 packages with nodist/. 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/mainmoved three commits (one of them apackages/specchange) while this ran; merged, build state refreshed per AGENTS.md §9,check:generatedfor 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:
action.fields.ai.helpTextis 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 toActionAiSchemainpackages/spec/src/ui/action.zod.tsand read there: two canonical keys, five declared aliases ofexposed, 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.labelreads 功能 / 機能 ("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 thehookfamily, which will hold both panels to whichever word it picks, because the assertion this PR adds makes divergence red.object.fields.lifecycle.retention.maxAge.labelis one such: 最长保留 inzh-CN, stillMax Ageinja-JPandes-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.pnpm --filter PKG build --concurrency=2forwards--concurrency=2into each package's build script; tsup ignores it, but@objectstack/cli's shell-script build dies onsh: 1: Syntax error: word unexpected. The working spelling ispnpm 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
seed·mapping·api·doc·book·capability)object.fields.enable.*(+validations)hook.fields.retryPolicy.*+body.memoryMb+timeoutMsreport.fields.drilldown+runtimeFilter+sections.dataset_binding⇒ as⚠️ Whoever takes them inherits the warning round 5's order carried — they are a different shape from every family so far, bare ⚠️ And the
.labelleaves that same remainder is 69. The tie at the top is gone: the six type-display pairs are now uniquely largest by leaves.TYPE.label+TYPE.descriptionwith 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.hookfamily now carries a second obligation: the cross-panel assertion this PR adds means itsbody.memoryMbrendering must match the one landed here.Generated by Claude Code