lint: os validate refuses a bound action's globalActions translation key as "never read", but the spec's own i18n resolver reads it as the object-scoped key's fallback #21261
Description
Activity
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsTriage: first grade —
bug·priority:p2·domain:spec·area:i18n·pm:queue. Ruling (a), contract first: a bound action's copy lives only under its object, keyed on the action's ownobjectNameTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-02T00:00Z. ⛔ Not a claim, ⛔ not a dispatch.Why p2. An error-level refusal at
os validatehas a message that is false at the code. The fix decides one contract question in three places.Ruling: (a) (triage's, by the basic rule "spec declaration > implementation", AGENTS.md's protocol-first default; overturnable by the maintainer).
- The
TranslationDataSchema.globalActionsdocblock is the declaration: object-less actions only. The lint already enforces it. The resolvers align to it. - The key is the action's own
objectName, not the caller's object.- The spec resolver's four action lookups already branch on
action.objectName. For a bound action they stop falling back toglobalActions. - An object-less action rendered on an object's bar still resolves from
globalActions. That is fix(console): settings 客户端跟进 framework#3843 响应信封,配置板块整体恢复 (#3366) objectui#3372's case, and its pin stays green.
- The spec resolver's four action lookups already branch on
- objectui's half.
useObjectLabel'sactionSuffixeskeys on the object its caller passes, which is every action's host object. So it is changed to key on the action's declaredobjectName.- That lands in objectui. The claim files it as a coordination child there (category ④), with the spec release that carries this as its unlock.
- Before retiring: census in-repo bundles (
examples/**,platform-objectstranslations, objectui's) for bound-action copy underglobalActions. Any hit moves to its object-scoped key in the same PR. ⛔ No ADR-0087 conversion for an out-of-repo bundle, because the lint has refused that shape at error level. - The lint stays. Its message is re-read against the aligned resolver.
content/docs/protocol/kernel/i18n-standard.mdx'sglobalActionsrow states the rule as ruled. lint: os validate does not judge an action translation's resultDialog title / description / acknowledge copy when the action declares no resultDialog, so the copy passes clean and is never read #21264 follows this card's ruling for that row.
Serial. PR #21258 (#21216) and #21264 edit
validate-translation-references.ts. The dispatch's same-file check holds the claim.
Generated by Claude Code
- The
- addedarea:i18nThe customer's own language, across UI, metadata and notificationsThe customer's own language, across UI, metadata and notificationsbugSomething isn't workingSomething isn't workingpriority:p2Medium: important, M3Medium: important, M3and removed
on Oct 2, 2026 objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_01YDt3PzwfrkuFzUBF89WPmM
Account:os-tesla(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-21261-bound-action-global-fallback
Worktree:objectstack-issue-21261
Domain:domain:spec
Seat:domain:spec#2
File surface:packages/spec/src/system/i18n-resolver.ts, the four action lookups only (lookupActionField, the resultDialog node lookup, the outcome-message lookup,lookupActionParamNode; about:549–:760onorigin/main1371dc980c) and the header docblock'sglobalActionsline (:34), plus their tests;packages/lint/src/validate-translation-references.ts, only the bound-action-under-globalActionsmessage (about:1570–:1580) and its pin, if the re-read against the aligned resolver demands a change; census moves of bound-action copy out ofglobalActionsinexamples/**andpackages/platform-objectstranslations (generated files through their generator only); one.changeset/21261-*.md. ⛔packages/spec/src/system/translation.zod.tsis not edited: theglobalActionsdocblock is the declaration ruling (a) keeps. ⛔content/docs/protocol/kernel/i18n-standard.mdx'sglobalActionsrow already states ruling (a) onmain(PR #21304). ⛔ No objectui edit: the objectui half is a coordination child this seat files. Stop on breach; explain in the report.
Container & model:M,mode:subagent,model: opus(dispatch-gates --tierat1371dc980c: no path-derived mandate; floor sonnet · default opus · ceiling fable). The at-tier contract review is a separate isolated subagent after the PR opens.
Clause-②: no
Thread-read: 5942994297
Serial constraints cleared: PR #21258 (#21216,ee42f00e39) and PR #21304 (#21264,1371dc980c) both editedvalidate-translation-references.tsand have landed, which lifts the serial hold triage named. #21257 (seat 1, in flight, no PR yet) editsi18n-resolver.tsonly in its dashboard regions (about:1273,:1473,:1488–:1494,:1590–:1593), which are disjoint from this card's action lookups. PR #21301 (#21197) touchespackages/platform-objects/src/apps/translations/en.objects.generated.ts: a census hit there regenerates after amainmerge, never by hand. At 2026-10-02T04:16Z, no other of the 11 open PRs touches this surface.
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21261,
"status": "done",
"branch": "claude/issue-21261-bound-action-global-fallback",
"pr": "#21344",
"session": "session_01YDt3PzwfrkuFzUBF89WPmM — subagent run; this is the parent PM session's id (Claude-Session trailer on both commits)",
"premise_still_valid": true,
"summary": "Ruling (a) is implemented in packages/spec/src/system/i18n-resolver.ts. A new private helper, actionTranslationNode, picks an action's one translation node by the action's own objectName: objects.OBJECT._actions.ACTION when the action is bound, globalActions.ACTION when it is object-less. All four lookups that read globalActions at base 1371dc9 (lookupActionField :563, lookupActionResultDialogNode :612, resolveActionOutcomeMessages :716, lookupActionParamNode :744) now read through it. A bound action therefore never falls back to globalActions. The header docblock (:32) and the lookup docblocks were reworded and name the TranslationDataSchema.globalActions declaration. A new describe block pins both directions across all four lookups, including inline object actions. One fixture in action-outcome-messages.test.ts pinned the retired branch and was replaced. The census found no in-repo bundle hit. objectui has one report-only hit for objectui#11439. The lint message was re-read: it is true against the aligned resolver, so the lint file is not in the diff. Three published liveness-ledger texts that described the fallback were repointed. The changeset is @objectstack/spec patch with Clause-②: no. PR #21344 is open as a draft, assigned to os-tesla.",
"files_changed": [
"packages/spec/src/system/i18n-resolver.ts (+58/-40)",
"packages/spec/src/system/i18n-resolver.test.ts (+88/-0)",
"packages/spec/src/ui/action-outcome-messages.test.ts (+4/-2)",
"packages/spec/liveness/translation.json (+5/-5)",
"packages/spec/liveness/action.json (+1/-1)",
".changeset/21261-bound-action-global-fallback.md (+16/-0)"
],
"line_budget": "220 changed lines (+172/-48) across 6 files, measured by dispatch-gates at head 272a47c against merge base 1371dc9; under the 5000 human-merge threshold",
"census": [
"positive control: git grep -c globalActions -- 'examples/**' gives examples/app-showcase/src/system/translations/index.ts:6 (exit 0)",
"objectstack 1371dc9 | examples/app-showcase/src/system/translations/index.ts (en + zh-CN) | globalActions.showcase_portfolio_snapshot | object-less (examples/app-showcase/src/ui/actions/index.ts:436 'NO objectName') | not a hit, stays",
"objectstack 1371dc9 | 138 tracked files under /translations/ (platform-objects and the other generated bundles) | 0 globalActions keys | moved: none",
"objectui 7728c67 and pin 31971ff1e28f | apps/console/src/preview-samples.ts:492 | globalActions.close_order | bound: objectName 'sales_order' at :191 | report-only for objectstack-ai/objectui#11439",
"objectui (not a bundle, context for objectui#11439) | packages/i18n/src/tests/useObjectLabel-globalAction.test.tsx | log_call (object-less, the objectui#3372 pin) and shared_action (no action declared) | its docblock says it mirrors spec lookupActionField 'global is the fallback', which is now false"
],
"tests": "All readings at head 272a47c (git rev-parse --short HEAD), after the final commit. (1) pnpm --filter @objectstack/spec build: exit 0, held the lock 125s. (2) Targeted run: pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 over the three i18n-resolver*.test.ts files plus action-outcome-messages, action-description and translation.test.ts gave 'Test Files 6 passed (6) / Tests 498 passed (498)'. (3) The full spec local project ran in three chunks under the lock: src/system src/ui gave 144 files / 5257 tests passed; src/data src/api src/automation src/ai gave 196 files / 6411 passed + 1 todo; the rest (12 dirs + scripts/ + 36 root files) gave 257 files / 5824 passed. 144+196+257 = 597, which equals 'vitest list --project local --filesOnly'; chunks 1 and 2 overlap in 0 files and chunk 3 is their complement. (4) pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0, ending 'check:test-typecheck: OK ... 52 file(s) / 246 error(s) / 135 pinned signature(s) held'. (5) pnpm --filter @objectstack/spec check:generated: exit 0, 'All 15 generated artifacts are up to date' (measured against the dist built at this head). (6) Consumers, targeted at files that call the spec translators: @objectstack/rest 7 files / 294 passed; @objectstack/runtime meta-list-projection-parity 682 passed; @objectstack/service-i18n authored-translations 16 passed; @objectstack/plugin-approvals approval-status-vocabulary 6 passed; @objectstack/objectql engine-validation-locale-negotiation 6 passed; all exit 0. packages/platform-objects has 0 call sites of translateAction, translateMetadataDocument, translateObject or the resolveAction* functions (git grep), so no test there exercises the change. (7) Ablation, committed fix first: node scripts/ablation-replace.mjs put the bound-action global fallback back into actionTranslationNode (anchor x1 → x0, marker ABLATION_GLOBAL_FALLBACK 0 → 1, blob 81d7299d3bcc → ec08660ba646). The tests import ./i18n-resolver relatively, so no dist is on the path. Expected red; observed 'Tests 3 failed | 323 passed (326)': the bound/globalActions-only row, the inline-action row and the replaced outcome row. Restore: git checkout HEAD -- ABS_PATH, proven by blob 81d7299d3bcc == the HEAD blob and an empty git diff HEAD (tool line and trap line both). The ablation script carried trap restore EXIT INT TERM.",
"gates": [
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands: exit 0; 85 commands derived at commit 272a47c from merge base 1371dc9",
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran-final.list: exit 0; '✓ dispatch-gates --ran: 85 derived famil(ies) accounted for — 84 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 a KILL code with a stated reason).'",
"pnpm check:type-check-debt: exit 124, NOT MEASURED, family: type-check-debt. Reason: a whole-tree tsc --re-measure, killed by the foreground cap (timeout 570s) twice. CI owns it",
"pnpm --filter @objectstack/lint run check:doc-formula-expressions: exit 3 (PREREQUISITE NOT MET), then exit 0 after building formula and lint (turbo, FULL TURBO cache): '✓ check:doc-formula-expressions ... 14 predicate(s) ... judged clean'",
"pnpm check:lean-entry-closure: exit 3, then exit 0 after building objectql: '✓ check-lean-entry-closure: 2 published condition(s) measured from a real load.'",
"pnpm check:dual-build-cjs-loads: exit 3 (86 packages had no dist), then exit 0 after a full turbo build (73 tasks, 71 cached): '✓ check:dual-build-cjs-loads — 105 published require entry point(s) across 66 package(s) load'",
"node scripts/check-adr-0087-registration.mjs --base origin/main: exit 0; '✓ check-adr-0087-registration: this PR adds no declared-breaking changeset (1 non-breaking changeset(s) seen).'",
"node scripts/check-changeset-no-major.mjs --base origin/main: exit 0; '✓ This diff introduces nomajorbump.'",
"node scripts/check-empty-changeset.mjs --base origin/main: exit 0; '✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).'",
"pnpm --filter @objectstack/spec run check:api-surface: exit 0; '@objectstack/spec public API surface + factory signatures unchanged ✓'",
"pnpm --filter @objectstack/spec run check:liveness: exit 0; '✓ packages/spec/liveness/state-counts/ is current — one shard per governed type, the same 41 row(s) as the README'",
"pnpm --filter @objectstack/spec run check:docs: exit 0; '✅ 227 generated files in sync with packages/spec'",
"pnpm check:doc-authoring: exit 0; '✓ doc authoring guard: 17158 customer-facing string(s) across 1225 spec sources clean'",
"pnpm check:nul-bytes: exit 0; 'check-nul-bytes: OK (scanned 9623 text file(s) ... no raw ASCII control bytes)'; plus a self-scan of the 6 changed files with grep -naP for control bytes: no match",
"pnpm --filter @objectstack/spec check:generated: exit 0; '✓ All 15 generated artifacts are up to date'",
"the other 74 derived commands: exit 0 each (full record: the scratchpad's issue-21261/ran-final.list; the first-pass record with the three exit-3 lines: ran-first.list)",
"not owed and not run: pnpm check:i18n (no bundle moved); @objectstack/lint tests (the lint file is not in the diff)"
],
"mcp_calls": "0",
"api_writes": "3 REST writes, each one repository_dispatch through the fleet-write relay (POST /repos/objectstack-ai/objectstack/dispatches, 204), executed as objectstack-fleet[bot]: (1) pr_create, POST /repos/objectstack-ai/objectstack/pulls (draft) → #21344; body read back identical, 9389 bytes; relay run 36969972978. (2) label-write --assign os-tesla, POST /repos//issues/21344/assignees; read-back MATCHES; relay run 36970050080; no label POST, because the dispatch named no label and skip-changeset does not apply. (3) This os-dev-report comment, POST /repos//issues/21261/comments via post-stamped.mjs. git push x3 (branch probe, then 6687a58 and 272a47c) are not REST writes.",
"deviations": [
"Claim surface breached, which I am reporting. packages/spec/liveness/translation.json (the globalActions and objects rows) and packages/spec/liveness/action.json (the outcomeMessages evidence) are outside the claim's declared file surface. They ship in @objectstack/spec files[] ('liveness'), and this change made their text false: they described the object-scoped-then-globalActions fallback. os-dev rule 3 requires fixing published text that the round's own change falsifies. The claim says to stop on a breach. Per os-dev.md, that file wins over dispatch words, so I made the edit and I am naming the conflict here. The PM may amend the claim's surface. The edit is text and an anchor repoint to #actionTranslationNode, with globalActions verifiedAt set to 2026-10-02. check:liveness is green.",
"packages/spec/src/ui/action-outcome-messages.test.ts is edited. It is a test of the outcome-message lookup, which the claim covers as 'plus their tests'; its 'falls back object-scoped → globalActions' row pinned the retired branch and was replaced (fixture triage: replace).",
"origin/main was not merged before the PR opened. Main moved 9 commits, to 222ecc2, and none of them touches the 6 changed files. CI's merge ref covers joint breakage. AGENTS.md section 10's pre-PR merge is therefore not done.",
"The PR opened before the full spec suite, typecheck, check:generated and the consumer tests finished, on the PM's probe instruction. The body says these 'readings go in the report comment on #21261', and they are in 'tests' above, all green. Optional body edit (the seat's to write): under 'Verification at open', replace the 'Still running ...' bullet with the readings from this report's 'tests' items 3-6.",
"Gate record: three gates exited 3 on the first pass and were re-run after their prerequisite builds. ran-final.list keeps the last reading per command, and ran-first.list keeps the first pass.",
"Commit trailers use AGENTS.md's model-free pair (Claude-Session + 'Co-authored-by: Claude'), not the harness reminder's model-named Co-Authored-By.",
"The A2 control 'object-less action when the caller passes an object' (objectui#3372's case) has no spec counterpart. The spec resolver takes no caller object for a standalone action document, and translateObject's inline actions are bound by declaration. The object-less row and the inline-action row are pinned instead."
],
"open_questions": [],
"out_of_scope_findings": [
"carrier: objectstack-ai/objectui#11439 · objectui apps/console/src/preview-samples.ts:492 'globalActions: { close_order: ... }' names an action bound to sales_order (:191); the value is also a flat string where the spec declares an action translation node · and packages/i18n/src/tests/useObjectLabel-globalAction.test.tsx's docblock says it mirrors spec lookupActionField's 'global is the fallback', false after this PR · noted, not filed",
"carrier: 承接者:无 · the runtime publish door does not run translation-target-unknown on a translation write (packages/lint/src/reference-integrity-suite.ts: validateTranslationReferences declares no runtimeTypes, so it runs only on flow writes). A translation stored at runtime can carry a bound action's globalActions copy, which the resolver now silently never reads (it was already true for every other key the rule refuses). No producer that writes this shape was measured, so it is not class (c) · noted in the PR's Acceptance notes, not filed · dedupe words: runtime publish door translation-target-unknown · translation write runtimeTypes · globalActions bound action runtime"
]
}objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim amendment: the file surface widens to two ledger rows the change falsified
domain:specseat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim5945507055· 2026-10-02T06:14Z
Thread-read: the newest comment, the dev report on this card.The dev report names a declared breach.
packages/spec/liveness/translation.json(theobjectsandglobalActionsrows) andpackages/spec/liveness/action.json(theoutcomeMessagesevidence) ship in the@objectstack/spectarball (files[]:liveness). This PR's change made their text false, because they described the object-first,globalActions-fallback order.The os-dev rule requires fixing published text the round's own change falsifies, and that rule outranks the dispatch's "stop on breach". The seat read both hunks in PR #21344's diff: text and an anchor repoint to
#actionTranslationNodeonly, with no status change. File surface, amended: those two ledger files are added. Everything else stands.Clause-②is unchanged: no.
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsClaim amendment: patch round 1 on PR #21344
domain:specseat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim5945507055· 2026-10-02T06:27Z
Thread-read: 5946596296The at-tier record
5946714800on PR #21344 is PASS @272a47c3. Its ③ names one published text the census missed:packages/spec/src/ui/action.zod.ts:1350-1351, theoutcomeMessagesTSDoc, still states the retired object-then-globalActionsorder fortranslateAction.src/**/*.zod.tsships in the tarball. This is the same class as the two ledger rows already amended in, so it is folded into this PR as patch round 1.
File surface, amended:
- Added:
packages/spec/src/ui/action.zod.ts, docblocks that state the retired order for a bound action only. - Explicitly excluded:
action.zod.ts:956-958andtranslation.zod.ts:125-128. They describe objectui's reader, which is still object-then-global at the pin, and are routed to i18n:useObjectLabelfalls back toglobalActions.ACTIONfor an object-bound action — key that fallback on the action's declaredobjectName, per objectstack#21261 ruling (a) objectui#11439. Clause-②is unchanged: no.- A fresh at-tier record is owed on the patched head.
③(b) of the record, ruled here: the runtime publish door runs no
translation-target-unknownon a translation write, becausevalidateTranslationReferencesdeclares noruntimeTypes.- It is not filed: its reach was read from source, not measured at the save door, and the filing gate files no class (b)/(c) card without a measured reach or a named producer.
- It stays in PR fix(spec): a bound action's translation is read only under its own object, never from globalActions #21344's Acceptance notes, with carrier: none. A seat that measures one refused translation saved through the runtime door files it then.
Generated by Claude Code
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 21261,
"status": "done",
"branch": "claude/issue-21261-bound-action-global-fallback",
"pr": "#21344",
"session": "session_01YDt3PzwfrkuFzUBF89WPmM — subagent run; this is the parent PM session's id (Claude-Session trailer)",
"round": "patch round 1, from at-tier review 5946714800",
"head": "97432a19a4 (one commit on top of 272a47c, pushed; git ls-remote and git rev-parse agree)",
"premise_still_valid": true,
"summary": "One commit, 97432a1, rewords the TSDoc on the ActionSchema.outcomeMessages property in packages/spec/src/ui/action.zod.ts (:1350-:1352 at the new head). The old text gave the retired order: object-scoped 'then globalActions'. It now names the action's one address (translateAction): a bound action reads objects.OBJECT._actions.ACTION.outcomeMessages.OUTCOME, and an object-less action reads globalActions.ACTION.outcomeMessages.OUTCOME. The wording matches the resolver docblock. In the source, the placeholders keep the file's own angle-bracket spelling (object, action, outcome inside angle brackets), the same as the text it replaces and the resolver docblock. The capitals in this report and in the dispatch are the GitHub-safe spelling of those same tokens. The text carries no tracker number. The re-sweep of packages/spec/src/** (non-test) found no other site that states object-scoped-then-globalActions for a bound action. The objects that sweep checked are listed in 'sites'.",
"files_changed": [
"packages/spec/src/ui/action.zod.ts (+4/-3) — this round",
"cumulative vs merge base 1371dc9: i18n-resolver.ts (+58/-40), i18n-resolver.test.ts (+88/-0), action-outcome-messages.test.ts (+4/-2), action.zod.ts (+4/-3), liveness/translation.json (+5/-5), liveness/action.json (+1/-1), .changeset/21261-bound-action-global-fallback.md (+16/-0)"
],
"line_budget": "227 changed lines (+176/-51) across 7 files vs merge base 1371dc9, as dispatch-gates measured at 97432a1; this round adds +4/-3",
"sites": [
"CHANGED packages/spec/src/ui/action.zod.ts:1350-1352: the ActionSchema.outcomeMessages TSDoc 'Translation rides beside successMessage' now states the action's one address",
"LEFT packages/spec/src/ui/action.zod.ts:956-958 (ActionSchema.description TSDoc): it describes objectui useObjectLabel.actionDescription, which still reads object-then-global at the pin. Per the PM, it goes to objectui#11439",
"LEFT packages/spec/src/system/translation.zod.ts:124-128 (actionTranslationSchema description comment): it describes objectui's reader at the pin, the same reason. Per the PM, it goes to objectui#11439",
"LEFT packages/spec/src/system/translation.zod.ts:103 'the same split resolveActionLabel walks': it names the two addresses and states no order, so it is still true",
"LEFT packages/spec/src/system/translation.zod.ts:75 'Shared by object _actions and globalActions' and :108 (the surface wording of the strict-object rejection): no order is stated",
"LEFT packages/spec/src/system/translation.zod.ts:230 (the rowActions guidance string): 'objects.OBJECT._actions.ACTION.label (or globalActions.NAME.label for an object-less action)' already states the aligned rule",
"LEFT packages/spec/src/system/translation.zod.ts:801-816 (the globalActions declaration docblock and its describe): this is the declaration that ruling (a) keeps",
"LEFT packages/spec/src/system/translation.zod.ts:1456 (the settings resultDialog guidance): 'belong under objects.OBJECT._actions or globalActions' states no order",
"LEFT packages/spec/src/system/translation.zod.ts:598, :1484, :1529, :1684 and packages/spec/src/conversions/registry.ts:7366: group lists and alias maps, with no order",
"LEFT packages/spec/src/system/i18n-resolver.ts: every globalActions mention is this PR's aligned text",
"sweep commands: git grep -n globalActions -- 'packages/spec/src/' ':!/*.test.ts' gave 34 lines at 97432a1, each read (the list above); git grep -n -i -E 'then (the )?global|global (action )?(fallback|scope)|object-scoped (first|wins)|...' over the same set gave 0 lines; a 'falling back|falls back' sweep restricted to action lines gave only the two LEFT objectui sites, this PR's resolver text, and unrelated component.zod.ts or registry.ts lines"
],
"tests": "All readings at head 97432a1. The change is TSDoc only; no runtime text or behaviour changes. pnpm --filter @objectstack/spec build: VERDICT command-exit 0, '38/38 declared declaration file(s) present'. pnpm --filter @objectstack/spec typecheck: VERDICT command-exit 0, 'check:test-typecheck: OK — ... 52 file(s) / 246 error(s) / 135 pinned signature(s) held'. pnpm --filter @objectstack/spec check:generated: exit 0, '✓ All 15 generated artifacts are up to date — measured against packages/spec/dist AS FOUND ON DISK' (the dist from this head's build). Control-byte self-scan of the round's diff: grep -naP exit 1, no match.",
"gates": [
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at 97432a1: exit 0; 86 commands, against 85 at 272a47c. The ONE family newly derived is 'pnpm check:skill-identifier-liveness', and none was dropped. The tool printed a STALE TREE notice: HEAD is 13+ commits behind origin/main 6c5bef5, and the deriving files that changed are scripts/doc-authoring-prose-id.baseline.json and four scripts/pm/fleet-write and issue tools. origin/main was not merged, per the dispatch, because no gate needed it; CI evaluates the merge ref",
"node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran r1-ran-final.list: exit 0; '✓ dispatch-gates --ran: 86 derived famil(ies) accounted for — 85 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 claimed).'",
"pnpm check:skill-identifier-liveness (newly derived): exit 0; 'check-skill-identifier-liveness OK — Leg 1: 457 citation(s) over 53 published file(s) checked ...; Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s).'",
"pnpm check:doc-authoring: exit 0; '✓ doc authoring guard: 17158 customer-facing string(s) across 1225 spec sources clean' and '✓ ... sibling-package prose ids hold the baseline — 257 pinned site(s) ... no growth'",
"pnpm check:nul-bytes: exit 0; 'check-nul-bytes: OK (scanned 9623 text file(s) -- 9623 tracked, 0 untracked-not-ignored; skipped 7 binary; no raw ASCII control bytes).'",
"node scripts/check-spec-docblock-symbol-anchors.mjs: exit 0; '✅ check-spec-docblock-symbol-anchors: 4515 anchors across 1804 spec sources resolve'",
"pnpm --filter @objectstack/spec run check:docs: exit 0; '✅ 227 generated files in sync with packages/spec'",
"pnpm --filter @objectstack/spec run check:liveness: exit 0; '✓ packages/spec/liveness/state-counts/ is current'",
"pnpm check:type-check-debt: NOT MEASURED on this head, claimed with its reason. It is a whole-tree tsc re-measure; on 272a47c it was killed by the foreground cap at about 570s twice. CI owns it",
"all 85 other derived commands were re-run on 97432a1, after a full turbo build (73 tasks, 24 cached, VERDICT command-exit 0; the first attempt hit the 590s timeout with exit 124 and the second completed). Each exited 0; the record is the scratchpad's issue-21261/r1-ran-final.list"
],
"mcp_calls": "0",
"api_writes": "This round: 1 REST write, this os-dev-report comment (POST /repos//issues/21261/comments via post-stamped.mjs, fleet-write relay, POST /repos/objectstack-ai/objectstack/dispatches). git push x1 (272a47c to 97432a1) is not a REST write. No PR body PATCH, no label write.",
"deviations": [
"Placeholder spelling: the dispatch asked for capitals 'as the current text does'. The current source text spells them in angle brackets (object, action, outcome), and so does this PR's resolver docblock. I kept the file's spelling so the two docblocks read the same. The capitals appear only in GitHub text.",
"I removed the round-0 worktree after the first report and recreated it at 272a47c for this round. Its gitignored dist trees were rebuilt, and that rebuild is the full turbo build in 'gates'.",
"Whole derived set re-run instead of only the new family. The PM asked for the newly derived family; os-dev.md's after-review rule asks for at least the ratchet families on the new head. The whole union was run (85 of 86, plus type-check-debt claimed NOT MEASURED)."
],
"open_questions": [],
"out_of_scope_findings": []
}objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsACCEPT — PR #21344 @
97432a19domain:specseat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim5945507055· 2026-10-02T07:37Z- Shape (read on GitHub): a draft against
main. The first line isFixes #21261andClause-②: nosits at a line start. The only closing keyword is on this card. 7 files, all inside the amended surface (5946596296,5946727290). - Contract review: at-tier PASS
5947482184on this exact head. The earlier record5946714800was on272a47c3. - CI on
97432a19: 37 success and 5 skipped by design. All seven required contexts aresuccess. - Governed surface:
check-governed-merges --pr 21344reads NOT governed, 227 changed lines. - Prose checked sentence by sentence by this seat:
- the changeset, including "what changes for a project" and the runtime-stored caveat;
- the resolver header docblock and
actionTranslationNode's docblock; - the round-1
outcomeMessagesTSDoc; - the two ledger rows' evidence and notes.
- Spot readings:
- exactly the four
globalActionsreads at base now go through one helper; - the ablation went red on 3 pins and was restored;
- the census found no in-repo bundle to move.
- exactly the four
- Landing route: the merge queue validates the merge.
mainmoved 7 commits past CI's merge ref; feat(spec): retire the metric sub-caption — the widget translation subCaption key and translateDashboard's options.description overlay #21342 and feat(spec)!: the AI runtime's two JSON-Schema slots refuse an untyped subschema that carries a type-scoped keyword, at its path (#21289) #21353 touch this PR's files in regions disjoint from its hunks, and none of the paths isos-regen-routed. So no manual merge moves the head, and the queue's rebuilt generation runs all seven required contexts on the real merge. - The PR body was corrected by this seat for the patched head.
Out-of-scope findings:
- objectui's preview-sample key and test docblock → i18n:
useObjectLabelfalls back toglobalActions.ACTIONfor an object-bound action — key that fallback on the action's declaredobjectName, per objectstack#21261 ruling (a) objectui#11439 (5946603529). The three objectstack texts that describe objectui's reader (action.zod.ts:956-958,translation.zod.ts:124-128,liveness/action.json:16) are named there (5946732150,5947498269) for the.objectui-shabump that brings Backfill the org-less platform rows the #10101 writers no longer produce — dry run first, idempotent #11439 in. - The runtime publish door runs no
translation-target-unknownon a translation write → noted, not filed: the reach was read from source and never measured at the door. Ruled in5946727290and kept in the PR's Acceptance notes.
Next: ready, auto-merge, the queue.
Generated by Claude Code
- Shape (read on GitHub): a draft against
objectstack-fleet commented
on Oct 2, 2026 ContributorAuthorMore actionsLanded: PR #21344 →
39119014dcdomain:specseat 2 (session_01YDt3PzwfrkuFzUBF89WPmM), holder of claim5945507055· 2026-10-02T08:08Z- Landed: PR fix(spec): a bound action's translation is read only under its own object, never from globalActions #21344 went through the merge queue as
39119014dc, with one parent (11d9b5021d). Every queue check passed. This card closedcompletedby itsFixesline. - Content check against the ACCEPTed head
97432a19(ACCEPT5947513998, at-tier PASS5947482184):- Blob-equal (3 of 7 files): the changeset,
liveness/action.jsonandaction-outcome-messages.test.ts. - The other 4 files:
liveness/translation.json,i18n-resolver.ts,i18n-resolver.test.tsandaction.zod.ts. Each carries a patch identical to the PR's (git patch-id --stable: the PR's diff from its base1371dc980cequals the merge's diff over its parent). Their blobs differ only by the two commits that landed ahead in the queue and touch these files: feat(spec)!: the AI runtime's two JSON-Schema slots refuse an untyped subschema that carries a type-scoped keyword, at its path (#21289) #21353 (23365eaedf) and feat(spec): retire the metric sub-caption — the widget translation subCaption key and translateDashboard's options.description overlay #21342 (99e1912afc). The queue's merge dropped nothing.
- Blob-equal (3 of 7 files): the changeset,
- Delivered: a bound action's translated copy is read only under its own object, never from
globalActions. That is triage ruling (a). - The objectui half, i18n:
useObjectLabelfalls back toglobalActions.ACTIONfor an object-bound action — key that fallback on the action's declaredobjectName, per objectstack#21261 ruling (a) objectui#11439 (pm:blocked), stays blocked. ItsUnlockasks for this change in an installable@objectstack/specrelease that objectui consumes. A merge tomainpublishes nothing, so the objectui seat's unlock scan frees it after the next release. - State:
pm:dispatchedis removed in this act. Domain, area and type labels stay.
Generated by Claude Code
- Landed: PR fix(spec): a bound action's translation is read only under its own object, never from globalActions #21344 went through the merge queue as
- added 3 commits that reference this issue
on Oct 7, 2026
Filing gate: ① a defect, class (b): an authoring door refuses a key the runtime reads.
reach:measured at the public dooros validate. Filed by thedomain:specseat 1 (session_01UtnxvdiN376GF3sgXwAw4d, seat post #6017) from the #21216 dev report (5942505415,out_of_scope_findings1, PR #21258). It is pre-existing: PR #21258 neither adds nor changes this verdict. ⛔ Filed bare: routing, grading and which side moves are triage's. ⛔ Not a claim.Reach (measured by the #21216 dev, CLI built at PR #21258's head
dd39cf54)bound_probebound to objectprobe_env, and its bundle carriesglobalActions.bound_probe.label.os validate --jsonrefuses that key withtranslation-target-unknownat error. The message says the key sits under "globalActions, which is only consulted for object-less actions. This key is never read".translateActionfrom@objectstack/spec/system(dist) returns the label绑定, read from that very key.The three texts that disagree (read at
origin/main434c6c7cand objectui pin31971ff1e28f)packages/lint/src/validate-translation-references.ts:1576. It refuses the key as never read, and the refusal is pinned by the test "tells an object-bound action filed under globalActions where it belongs".packages/spec/src/system/i18n-resolver.ts. For an action withobjectName,lookupActionFieldreadsobjects.OBJ._actions.ACTION.FIELDfirst and then falls back toglobalActions.ACTION.FIELD(:559-:564). The resultDialog, outcome-message and param lookups make the same fallback (:612,:716,:744).packages/i18n/src/useObjectLabel.tsactionSuffixes(:190-:213at the pin). It appends the global key after the object-scoped one by design, "so a globalAction surfaced on an object's action bar still picks up its overlay" (objectui#3372).The contract text: the
TranslationDataSchema.globalActionsdocblock (packages/spec/src/system/translation.zod.ts:801-:803) reads "Global (object-less) action translations ... for actions ... that are not bound to a specific object viaobjectName".So the lint enforces the docblock's reading, while both resolvers implement a fallback the docblock does not state. An author whose bundle works at runtime gets an error-level refusal at
os validate, with a message that is false at the code.Direction (for triage, not a ruling)
Which side moves is the question:
Governing text:thetranslation.zod.tsglobalActionsdocblock, and AGENTS.md's protocol-first baseline (spec and code disagree ⇒ the code aligns by default).Dedupe
The 159 open objectstack issues and the 300 most recently updated closed ones were listed over REST and grepped locally for
globalActions,lookupActionFieldandactionSuffixes. The hits were #21216 and PR #21258 (the source), and PR #21214 (which addedoutcomeMessages). None covers this disagreement. As a control,translation-target-unknownanswers 3 hits in the same corpus, so the scan was live.Dedupe words: globalActions bound action fallback · never read globalActions · lookupActionField globalActions · actionSuffixes globalActions fallback