You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
spec(translation): an approval node has no translation key — flows carries screens.NODE and refusals.NODE only, so a pack's approval-step label is refused translation-target-unknown (Seam: plugin-approvals reads it; split from #24863) #24915
Filing gate ①: a product defect with reach measured, split from #24863 by triage (R539) at the domain:services seat's request (6290681512). Filed by the triage seat (#6015, session_01AavokzJ5DndAwitDXvKy4U). ⛔ Not a claim.
Reader (who acts): the domain:spec seat. Seam: spec declares the key, and plugin-approvals reads it (the reader is #24863's services half).
The gap (origin/main)
packages/spec/src/system/translation.zod.ts: TranslationItemSchema.flows carries label, successMessage, errorMessage, screens.NODE (screen nodes only) and refusals.NODE (refused end nodes only). No key names an approval node.
Reach: every non-English user who reads an approval's current step, in any app that ships an approval flow (HotCRM ships six flows with seven step nodes).
Done when
flows.FLOW gains a key for an approval node's label, spelled by the spec seat in the shape of screens.NODE / refusals.NODE. os validate accepts it for approval nodes and refuses it on other node types.
Claim: PM loop round 16
Session: session_01D6F9axfoPzR7t8G1J7Ytzz
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-24915-approval-node-translation-key
Worktree: objectstack-issue-24915
Domain: domain:spec
Seat: domain:spec#3 (seat post #18883)
File surface (at origin/main81e6edd463 or later; stop on a breach and explain it in the report):
packages/spec/src/system/translation.zod.ts (TranslationItemSchema.flows gains an approval-node key in the shape of screens.NODE / refusals.NODE);
the translation-target resolver that answers translation-target-unknown (wherever os validate judges flows.* keys), so the key is accepted on approval nodes and refused on others;
their tests, generated artifacts regenerated by repo tooling, and .changeset/24915-*.md.
The reader that renders it is #24863's services half, not this PR's.
Container & model: M, mode:subagent, model: default (no path-derived mandate)
Clause-②: yes (widening)
Responsibility: n/a — a widening (standing 「甲」 rule, #6015 6292005854: ships in any 18.x minor)
Thread-read: none
Serial constraints cleared: read at this act; the dev re-checks open PRs touching translation.zod.ts and the resolver before opening its own.
Read on origin/main81e6edd463 at 2026-10-20T15:13Z: flows.FLOW carries label, successMessage, errorMessage, screens.NODE and refusals.NODE, and no approval-node key.
{
"issue": 24915,
"status": "done",
"branch": "claude/issue-24915-approval-node-translation-key",
"pr": "https://github.com/objectstack-ai/objectstack/pull/24943",
"session": "session_01D6F9axfoPzR7t8G1J7Ytzz",
"premise_still_valid": true,
"summary": "Premise confirmed at origin/main 81e6edd463. TranslationDataSchema.flows had no key for an approval node. Added flows.FLOW.approvals.NODE.label, spelled like screens.NODE and refusals.NODE (grouped by node type, keyed by FlowNode.id). The leaf is `label` because ApprovalNodeConfigSchema declares no person-facing text (no title, instructions or comment prompt), so the step's only translatable string is FlowNode.label, which plugin-approvals snapshots into current_step_label. title, name, heading and stepLabel are aliases onto label, and approval is an alias onto approvals. The os validate translation-reference check (packages/lint) accepts the key over approval nodes at any depth. It refuses it as translation-target-unknown on other node types (a screen is pointed at screens.NODE.title) and on unknown ids (with Did-you-mean). A screens.NODE or refusals.NODE key written over an approval node is now pointed at approvals.NODE.label. decisionOutputs[].label is deliberately left as an owed row in the family pin, with its reason updated. The liveness row is planned because the reader is the #24863 services half. Changeset: minor for @objectstack/spec and @objectstack/lint, Clause-②: yes (widening). The PR body says the PR owes a CONTRACT_REVIEW_TIER review, since it changes a published schema and touches no governed surface. The dispatch asked for 'Closes #24915' while os-dev.md says 'Fixes'; both are closing keywords, and I used the dispatch's wording.",
"tests": "Repro tests were committed first (ebb460a714). Reverse verification: the fix was committed, then translation.zod.ts and validate-translation-references.ts were restored to BASE 81e6edd463 with git restore --source (tree only). On-disk proof: `approvals: z.record` count 0 and APPROVAL_NODE_TYPE count 0. After a spec rebuild, spec failed '6 failed | 158 passed' (the 4 new approvals cases and the 2 face-pin cases) and lint failed '6 failed | 180 passed' (the 5 new refusal/redirect cases and the arm-coverage case). Restored with git checkout HEAD -- PATHS; git diff HEAD was empty and status clean. After the spec rebuild, ablation-dist-preflight reported the marker present in 20 built files. At merged head 606faa06ee: `pnpm --filter @objectstack/spec test` 703 files, 21365 passed; spec typecheck exit 0; `pnpm --filter @objectstack/lint test` 151 files, 7002 passed; lint typecheck OK (check:test-typecheck OK); spec check:generated reported 'All 14 generated artifacts are up to date' (docs mdx, strictness-ledger counts and liveness state-counts were regenerated with --fix); check:i18n OK; check:nul-bytes OK. dispatch-gates --commands (113 families) was re-run on 606faa06ee with exit codes recorded, and --ran reconciled to '113 derived, 113 run, 0 NOT-MEASURED (a DERIVED zero)'. eslint was narrowed to the 5 touched src/test files (--no-inline-config --format json): 5 files, 0 errors, 0 warnings. All 5 are in config per --print-config, and the config has no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move. Step ① was `pnpm --filter '@objectstack/lint...' build` (lint plus its deps); cli, client-react and objectql were built only to meet gate prerequisites. CI convergence: in_progress at report time.",
"mcp_calls": "0",
"api_writes": "2 — fleet-write relay: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, plus POST /issues/24943/assignees baozhoutao) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/24915/comments). Plus git push, which is not REST. 0 label writes: the dispatch named no labels, and skip-changeset does not apply because a changeset ships.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: #24863 (services half) · noted, not filed: the CLI i18n extractor (packages/cli/src/utils/i18n-extract.ts) emits no approvals.NODE.label skeleton slot. Demand should arrive with the reader; check:i18n-walk-parity is green with this diff.",
"carrier: #24863 (reader) · noted, not filed: no flowApprovalLabelKey helper beside flowRefusalMessageKey in packages/spec/src/system/i18n-resolver.ts. With no reader it would be an export with no consumer; the reader card adds it.",
"carrier: approval decision-dialog reader (承接者:无) · noted, not filed: decisionOutputs[].label stays an `owed` row in flows-translation-face.test.ts. It is a separate surface and needs its own reader."
]
}
Activity
objectstack-fleet commentedon Oct 20, 2026
Claim: PM loop round 16
Session:
session_01D6F9axfoPzR7t8G1J7YtzzAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-24915-approval-node-translation-keyWorktree:
objectstack-issue-24915Domain:
domain:specSeat:
domain:spec#3(seat post #18883)File surface (at
origin/main81e6edd463or later; stop on a breach and explain it in the report):packages/spec/src/system/translation.zod.ts(TranslationItemSchema.flowsgains an approval-node key in the shape ofscreens.NODE/refusals.NODE);translation-target-unknown(whereveros validatejudgesflows.*keys), so the key is accepted on approval nodes and refused on others;.changeset/24915-*.md.The reader that renders it is #24863's services half, not this PR's.
Container & model:
M,mode:subagent,model: default (no path-derived mandate)Clause-②: yes (widening)
Responsibility:
n/a — a widening (standing 「甲」 rule, #60156292005854: ships in any 18.x minor)Thread-read: none
Serial constraints cleared: read at this act; the dev re-checks open PRs touching
translation.zod.tsand the resolver before opening its own.Read on
origin/main81e6edd463at 2026-10-20T15:13Z:flows.FLOWcarrieslabel,successMessage,errorMessage,screens.NODEandrefusals.NODE, and no approval-node key.Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
os-dev-report
{ "issue": 24915, "status": "done", "branch": "claude/issue-24915-approval-node-translation-key", "pr": "https://github.com/objectstack-ai/objectstack/pull/24943", "session": "session_01D6F9axfoPzR7t8G1J7Ytzz", "premise_still_valid": true, "summary": "Premise confirmed at origin/main 81e6edd463. TranslationDataSchema.flows had no key for an approval node. Added flows.FLOW.approvals.NODE.label, spelled like screens.NODE and refusals.NODE (grouped by node type, keyed by FlowNode.id). The leaf is `label` because ApprovalNodeConfigSchema declares no person-facing text (no title, instructions or comment prompt), so the step's only translatable string is FlowNode.label, which plugin-approvals snapshots into current_step_label. title, name, heading and stepLabel are aliases onto label, and approval is an alias onto approvals. The os validate translation-reference check (packages/lint) accepts the key over approval nodes at any depth. It refuses it as translation-target-unknown on other node types (a screen is pointed at screens.NODE.title) and on unknown ids (with Did-you-mean). A screens.NODE or refusals.NODE key written over an approval node is now pointed at approvals.NODE.label. decisionOutputs[].label is deliberately left as an owed row in the family pin, with its reason updated. The liveness row is planned because the reader is the #24863 services half. Changeset: minor for @objectstack/spec and @objectstack/lint, Clause-②: yes (widening). The PR body says the PR owes a CONTRACT_REVIEW_TIER review, since it changes a published schema and touches no governed surface. The dispatch asked for 'Closes #24915' while os-dev.md says 'Fixes'; both are closing keywords, and I used the dispatch's wording.", "tests": "Repro tests were committed first (ebb460a714). Reverse verification: the fix was committed, then translation.zod.ts and validate-translation-references.ts were restored to BASE 81e6edd463 with git restore --source (tree only). On-disk proof: `approvals: z.record` count 0 and APPROVAL_NODE_TYPE count 0. After a spec rebuild, spec failed '6 failed | 158 passed' (the 4 new approvals cases and the 2 face-pin cases) and lint failed '6 failed | 180 passed' (the 5 new refusal/redirect cases and the arm-coverage case). Restored with git checkout HEAD -- PATHS; git diff HEAD was empty and status clean. After the spec rebuild, ablation-dist-preflight reported the marker present in 20 built files. At merged head 606faa06ee: `pnpm --filter @objectstack/spec test` 703 files, 21365 passed; spec typecheck exit 0; `pnpm --filter @objectstack/lint test` 151 files, 7002 passed; lint typecheck OK (check:test-typecheck OK); spec check:generated reported 'All 14 generated artifacts are up to date' (docs mdx, strictness-ledger counts and liveness state-counts were regenerated with --fix); check:i18n OK; check:nul-bytes OK. dispatch-gates --commands (113 families) was re-run on 606faa06ee with exit codes recorded, and --ran reconciled to '113 derived, 113 run, 0 NOT-MEASURED (a DERIVED zero)'. eslint was narrowed to the 5 touched src/test files (--no-inline-config --format json): 5 files, 0 errors, 0 warnings. All 5 are in config per --print-config, and the config has no type-aware linting (no parserOptions.project), so untouched files' verdicts cannot move. Step ① was `pnpm --filter '@objectstack/lint...' build` (lint plus its deps); cli, client-react and objectql were built only to meet gate prerequisites. CI convergence: in_progress at report time.", "mcp_calls": "0", "api_writes": "2 — fleet-write relay: pr_create (POST /repos/objectstack-ai/objectstack/pulls, draft, plus POST /issues/24943/assignees baozhoutao) and this os-dev-report comment (POST /repos/objectstack-ai/objectstack/issues/24915/comments). Plus git push, which is not REST. 0 label writes: the dispatch named no labels, and skip-changeset does not apply because a changeset ships.", "open_questions": [], "out_of_scope_findings": [ "carrier: #24863 (services half) · noted, not filed: the CLI i18n extractor (packages/cli/src/utils/i18n-extract.ts) emits no approvals.NODE.label skeleton slot. Demand should arrive with the reader; check:i18n-walk-parity is green with this diff.", "carrier: #24863 (reader) · noted, not filed: no flowApprovalLabelKey helper beside flowRefusalMessageKey in packages/spec/src/system/i18n-resolver.ts. With no reader it would be an export with no consumer; the reader card adds it.", "carrier: approval decision-dialog reader (承接者:无) · noted, not filed: decisionOutputs[].label stays an `owed` row in flows-translation-face.test.ts. It is a separate surface and needs its own reader." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 20, 2026
Landed ·
domain:specseat 3 ·baozhoutao· sessionsession_01D6F9axfoPzR7t8G1J7Ytzz· 2026-10-20T18:20Zb9a79efcd1through the merge queue. Verified onorigin/main: single parent, and thegit diff C^ Cpatch-id equals the reviewed head606faa06ee's against its merge base (9facd03dc5).CONTRACT_REVIEW_TIERon that head (6297324675).flows.FLOW.approvals.NODE.labelis a new translation key.approvalnodes at any depth.translation-target-unknownon other node types and unknown ids, with Did-you-mean.screens/refusalskeys written over an approval node are pointed at it.planned: the reader is Approvals show their flow and step names in the authoring language only: the request snapshots Flow.label / node.label at open, and the translation contract has no key for an approval step #24863's services half.Clause-②: yes (widening), spec and lint minor.Carried by #24863, not filed: the CLI extractor slot, and a
flowApprovalLabelKeyhelper.decisionOutputs[].labelstays an owed row.Claim
6294228866ends here;pm:dispatchedis removed in this stroke.Generated by Claude Code