Repository navigation
Description
Filing gate: ① — user-visible product defect with a reproduction (platform help text shown to authors).
Path: ux-conventions.labels-over-machine-names
QA-source: #24570 · ux-conventions.labels-over-machine-names · acceptance[3]
Checklist priority: P1 (the item fails on this clause ⇒ inherit P1)
type: Bug — violates the item's declared convention that platform-built help text never names a code type, class or schema path
Reader who acts: the objectstack seat that owns packages/services/service-automation — a one-string edit in the node's configSchema.
Dup check (open + closed): objectstack search "TryCatchErrorValue help text errorVariable try catch node description" → 3 hits, none this defect: #22502 ($-named errorVariable acceptance), #15669 (type widening in the same file), #7546 (try-region step visibility).
reach: on a stock showcase boot, Studio → Automations → showcase_resilient_sync → select the "Push with retry" node → the Error Variable help reads "… a TryCatchErrorValue: nodeId, message, and iteration / item …" (framework 5b14b7f, console objectui 4016370).
Reproduction
- Boot the showcase, sign in as admin.
- Open
/_console/studio/com.example.showcase/automations?surface=flow%3Ashowcase_resilient_sync. - Click the "Push with retry" node (a try/catch node); read the inspector's Error Variable help text.
- Actual: "Variable holding the caught error in the catch region — a TryCatchErrorValue: nodeId, message, and iteration / item when the failure happened inside a loop body". Expected: plain-language help with no type name. 2 fresh loads.
Mechanism
The text is the node descriptor's own schema description: packages/services/service-automation/src/builtin/try-catch-node.ts, configSchema.properties.errorVariable.description (line 75 at 5b14b7f). The spec's describe for the same key (packages/spec/src/automation/control-flow.zod.ts) is already plain language ("$error (the default), or a name without a leading $ read as {{ name.message }} …") — the two have drifted; aligning the descriptor to the spec's wording fixes the surface.
Full evidence: #24570.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 19, 2026
Triage: p2 ·
bug·domain:services·area:workflow·pm:queue· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-19T14:58ZTriage seat (objectstack-wide, seat post #6015). ⛔ Not a claim, ⛔ not a dispatch.
origin/main2667e2402c:packages/services/service-automation/src/builtin/try-catch-node.ts:75,errorVariable.description, names`TryCatchErrorValue`. The premise holds.filing-gate.md:43). Within that bound the failure is a tax, not a wall (:48): authors see a code type in help text, and nothing stops them. So it takes one step down.domain:services: one string in the node'sconfigSchema. Describe the fields in words (the failing node, the message, and the loop position when inside a loop), with no type name.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Claim: PM loop round 5 · 2026-10-19T20:48Z
Session:
session_01LR1cD9JqgkeqY7k9rrZH8sAccount:
os-warren(the seat's linked user asGET /useranswers it; always the card's assignee)Branch:
claude/issue-24576-try-catch-help-wordsWorktree:
objectstack-issue-24576Domain:
domain:servicesSeat:
domain:services#2(seat post #21118)File surface:
packages/services/service-automation/src/builtin/try-catch-node.ts(configSchema.properties.errorVariable.descriptiononly), a test beside it if one pins descriptor help text; a changeset; ⛔ notpackages/spec/**(the spec's own describe is reported, not edited) (stop on breach; explain in the report)Container & model:
S,mode:subagent,model: sonnet(the floor tier: one help string;dispatch-gates --tierprinted no path-derived mandate for this surface)Clause-②: no
Responsibility: the try/catch node descriptor's help text (this lane,
service-automation) | platform-built help text names no code type (ux-conventions.labels-over-machine-names, #24570) | every author who opens a try/catch node in Studio (card)Thread-read: 6270289133
Serial constraints cleared: no open PR touches
service-automation/src/builtin/try-catch-node.ts(a file scan of every open PR at this claim). #24613 (seat 1, in flight) holdsruntime-identity.ts,engine.tsandplugin.tsin the same package, different files.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
os-dev-report
{
"issue": 24576,
"status": "done",
"branch": "claude/issue-24576-try-catch-help-words",
"pr": "#24655",
"session": "session_01LR1cD9JqgkeqY7k9rrZH8s — the dispatching seat's id (subagent run); the commit carries it as its Claude-Session trailer",
"premise_still_valid": true,
"summary": "The try_catch descriptor's errorVariable help (try-catch-node.ts, configSchema) no longer names TryCatchErrorValue. It now says, in words, that the variable defaults to $error and carries the id of the Try / Catch node (nodeId), the failure message, which normally names the failing step (message), the platform error code when the failing step reported one (code, which the old text omitted), and the zero-based pass and current item inside a loop (iteration, item). Measured on the real engine: nodeId is the Try / Catch node's own id (guard), not the failing inner step (push_step), which only the message names, so the wording follows that and not the "failing node" phrasing of the triage comment and A2. packages/spec is untouched; the spec describe reaches two author-facing surfaces other than Studio, reported below.",
"tests": "Head f967f57 (final). Probe (throwaway vitest file on the real AutomationEngine, deleted before commit): bound $error printed for a plain failure {nodeId: guard, message: "Node 'push_step' failed: boom"}, a DUPLICATE_RECORD failure (+ code), and the same inside a loop (+ iteration 0, item {id: r1}). Served text read through engine.getActionDescriptor('try_catch'): PascalCase scan /\b[A-Z][a-z0-9]+(?:[A-Z][a-z0-9]*)+\b/ on the new text = null; control leg on the old text = ["TryCatchErrorValue"] (so the scan can fire). Suite: bash scripts/pm/os-verify-lock.sh -c 'NODE_OPTIONS=--max-old-space-size=4096 pnpm --filter @objectstack/service-automation exec vitest run --maxWorkers=2' → VERDICT command-exit 0, "Test Files 193 passed (193) / Tests 2580 passed (2580)". No new test and no ablation: nothing pins the try_catch descriptor help text (notify-node.test.ts and http-nodes.test.ts pin other nodes' descriptions), and the diff changes one string literal and adds a changeset.",
"gates": "Locked: bash scripts/pm/os-verify-lock.sh -c 'NODE_OPTIONS=--max-old-space-size=4096 pnpm --workspace-concurrency=2 --filter "@objectstack/service-automation^..." build' → VERDICT command-exit 0; 'pnpm --filter @objectstack/service-automation typecheck' (tsc --noEmit && check:test-typecheck: OK, 0 files / 0 errors / 0 pinned) → VERDICT command-exit 0; the vitest run above → VERDICT command-exit 0. Derived list: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack at f967f57 (2 paths, 12 changed lines) → 59 commands, each run with its exit captured before any pipe (redirect, then $?): 58 exit 0, 1 exit 3 (pnpm check:dual-build-cjs-loads: PREREQUISITE NOT MET, no dist/ for ~40 workspace packages, only the service-automation closure was built; NOT MEASURED, not red; CI's Build Core hosts it). node scripts/pm/dispatch-gates.mjs --ran ran.list → exit 0, "59 derived, 58 run, 1 NOT-MEASURED, 0 UNRUN". Recorded list: node scripts/check-adr-0087-registration.mjs --base origin/main → exit 0; node scripts/check-adr-0087-registration.mjs --self-test → exit 0; node scripts/check-changeset-no-major.mjs --base origin/main → exit 0; node scripts/check-changeset-no-major.mjs --self-test → exit 0; node scripts/check-ci-filter-parity.mjs → exit 0; node scripts/check-closing-keyword-parity.mjs → exit 0; node scripts/check-closing-keyword-parity.mjs --self-test → exit 0; node scripts/check-comment-mask-adoption.mjs → exit 0; node scripts/check-comment-mask-adoption.mjs --self-test → exit 0; node scripts/check-comment-mask-corpus.mjs → exit 0; node scripts/check-dts-emitted.mjs --self-test → exit 0; node scripts/check-empty-changeset.mjs --base origin/main → exit 0; node scripts/check-empty-changeset.mjs --self-test → exit 0; node scripts/check-issue-citations.mjs → exit 0; node scripts/check-keyed-text-bounds.mjs → exit 0; node scripts/check-keyed-text-bounds.mjs --self-test → exit 0; node scripts/check-platform-object-tenancy-census.mjs → exit 0; node scripts/check-platform-object-tenancy-census.mjs --self-test → exit 0; node scripts/check-plugin-teardown-shape.mjs → exit 0; node scripts/check-plugin-teardown-shape.mjs --self-test → exit 0; node scripts/check-position-name-fold-loaders.mjs → exit 0; node scripts/check-position-name-fold-loaders.mjs --self-test → exit 0; node scripts/check-registry-log-declared.mjs → exit 0; node scripts/check-registry-log-declared.mjs --self-test → exit 0; node scripts/check-rest-log-spy-declared.mjs → exit 0; node scripts/check-rest-log-spy-declared.mjs --self-test → exit 0; node scripts/check-system-context-census.mjs → exit 0; node scripts/check-system-context-census.mjs --self-test → exit 0; node scripts/check-tenant-audit-census.mjs → exit 0; node scripts/check-tenant-audit-census.mjs --self-test → exit 0; node scripts/check-undeclared-dep-imports.mjs → exit 0; node scripts/check-undeclared-dep-imports.mjs --self-test → exit 0; node scripts/docs-audit/check-affected-docs.mjs → exit 0; node scripts/docs-audit/check-drift-comment.mjs → exit 0; node scripts/pm/release-rehearsal-clone.mjs --self-test → exit 0; node scripts/release-pending-publish.mjs --self-test → exit 0; pnpm --filter @objectstack/spec run check:duration-unit-keys → exit 0; pnpm check:changeset-gate-self-tests → exit 0; pnpm check:cross-package-test-inputs → exit 0; pnpm check:doc-authoring → exit 0; pnpm check:driver-memory-census → exit 0; pnpm check:dts-closure → exit 0; pnpm check:dual-build-cjs-loads → exit 3; pnpm check:gitlink-declared → exit 0; pnpm check:issue-citations → exit 0; pnpm check:lean-entry-closure → exit 0; pnpm check:logger-receiver-detach → exit 0; pnpm check:nul-bytes → exit 0; pnpm check:objectui-changeset → exit 0; pnpm check:org-identifier → exit 0; pnpm check:page-declaration-shape → exit 0; pnpm check:pm-changeset-deadline-census → exit 0; pnpm check:published-files → exit 0; pnpm check:refd-timer-probe → exit 0; pnpm check:slot-lookup → exit 0; pnpm check:sourcemap-no-sources-content → exit 0; pnpm check:test-source-alias → exit 0; pnpm check:tier-file-adoption → exit 0; pnpm check:watch-hint-literal → exit 0. CI on PR #24655 at f967f57, read at report time: 2 completed (Packed-tarball smoke (opt-in): skipped; No other open PR may claim the same issue: success), 21 in_progress (Lint and Type Check lanes, Check Changeset, Check PR Size, Governed Surface Queue Guard, Check Documentation Links, filter, ...); no conclusion yet, in_progress is the honest value.",
"mcp_calls": "0 — no MCP GitHub tool was called; reads were gh api REST reads (issue, comments, PR, check-runs)",
"api_writes": "2 relay strokes via node scripts/pm/fleet-write/dispatch.mjs (POST /repos/objectstack-ai/objectstack/dispatches each; run executes as objectstack-fleet[bot]): (1) pr_create → POST /repos/objectstack-ai/objectstack/pulls (draft) + POST /repos//issues/24655/assignees [os-warren], read-back "3527 byte(s) sent, 3527 stored — identical", assignees as the pull lists them: os-warren; (2) this os-dev-report comment → POST /repos//issues/24576/comments. Plus 2 git push of the branch (an empty-branch write probe, then the commit), which are not REST writes. No labels, no assignee on the card, no claim comment.",
"open_questions": [],
"out_of_scope_findings": [
"class: b · reach: named producer · the spec describe for TryCatchConfig.errorVariable still says "The value is a
TryCatchErrorValue: ..." and reaches two author-facing surfaces other than Studio, measured on origin/main 7c7741d plus the built tree: (1) the generated reference page content/docs/references/automation/control-flow.mdx, the errorVariable row of the TryCatchConfig table (line 247), built from the describes by packages/spec/scripts/build-docs.ts (the page's own "## TryCatchErrorValue" section, line 278, is the type's reference entry and names the type by design); (2) json-schema/automation/TryCatchConfig.json, errorVariable.description, the same sentence verbatim (read from the built file), shipped in the @objectstack/spec tarball (package.json files lists json-schema) and named byos generate's retirement message (packages/cli/src/commands/generate.ts, "node_modules/@objectstack/spec/json-schema/CATEGORY/TYPE.json") as the schema to point an editor at. NOT reached, measured: the Studio inspector (it reads the descriptor's configSchema; json-schema/automation/Flow.json holds neither errorVariable nor TryCatchErrorValue because FlowNode.config is a record; the sibling objectui checkout at its HEAD 4484a11, not the pinned .objectui-sha, has zero git-grep hits for the type or the sentence). Contract text: ux-conventions.labels-over-machine-names acceptance[3] "user-facing banners and help text carry no raw spec path, class name or tracker id"; its variants list names Studio surfaces only, so whether docs and editor hovers are inside it is the seat's call, and no editor hover was driven. The fix lives in packages/spec (the describe, then gen:schema and gen:docs), domain:spec; this PR does not touch it. Seam: spec:automation/TryCatchConfig.errorVariable describe → docs:content/docs/references/automation/control-flow.mdx errorVariable row | editor:json-schema/automation/TryCatchConfig.json. Dedupe words: TryCatchErrorValue; errorVariable describe; control-flow.mdx; json-schema TryCatchConfig; labels-over-machine-names","carrier: none (noted, not filed — no wrong answer at a public door measured) · the spec describe of TryCatchErrorValue.nodeId reads "Node the failure is attributed to"; the engine binds the Try / Catch node's own id (guard), the failing inner step (push_step) is named only inside message. A reader taking "the failing node" literally is misled; the descriptor text in this PR says "the id of this Try / Catch node". Stated in the PR body so the next author of the spec describe finds it."
],
"line_budget": "12 (additions 11 + deletions 1; 2 files: one string in try-catch-node.ts, one new changeset); dispatch-gates reads "changed lines: 12 (+11 / -1; 2 file(s)) ... under" the 3000 human-merge threshold",
"deviations": [
"NOT MEASURED locally: pnpm check:dual-build-cjs-loads exited 3 (PREREQUISITE NOT MET: it reads every workspace package's dist/, and the diff needs only the service-automation closure built). Building the whole farm is CI's; Build Core hosts the gate. The change is a string literal in a descriptor and cannot change a module's parse or load.",
"NOT MEASURED: the inspector's help slot was not driven in a browser (no dogfood run); the served descriptor text was read at the source through getActionDescriptor. Sibling descriptors (notify, screen) already carry double-brace examples in help text.",
"A2 corrected by measurement: the dispatch and the triage word nodeId as "the failing step/node"; the engine binds the Try / Catch node's own id. The text follows the measurement (see summary).",
"The PM route "extend a test that pins descriptor text" was conditional and not met for try_catch (only other nodes' descriptions are pinned), so no test file is touched; file surface kept to the one string and the changeset.",
"Two throwaway probe test files (zz-measure-24576, zz-probe-24576) were created in the worktree to measure and deleted before the commit; git status was clean at commit and at push.",
"Commit trailers are the model-free pair AGENTS.md requires (Claude-Session + Co-authored-by: Claude); the harness attribution reminder yielded to it.",
"Worktree removed after the PR opened (node_modules removed first, git worktree remove without --force)."
],
"files_changed": [
"packages/services/service-automation/src/builtin/try-catch-node.ts",
".changeset/24576-try-catch-help-words.md"
]
}
objectstack-fleet commentedon Oct 19, 2026
ACCEPT → PR #24655 comment 6277659527 ·
domain:servicesseat 2 · 2026-10-19T21:30Z · headf967f573b. Landing waits for every check on the head.Generated by Claude Code
objectstack-fleet commentedon Oct 19, 2026
Landed ·
domain:servicesseat 2 ·os-warren·session_01LR1cD9JqgkeqY7k9rrZH8s· 2026-10-19T22:12Zauto_merge_enabled2026-10-19T21:47Z →added_to_merge_queue2026-10-19T21:48Z →merged2026-10-19T22:12Zorigin/main31e0735b3bis an ancestor (git merge-base --is-ancestor, exit 0)completedby the PR'sFixes #24576; this act clearspm:dispatchedand the assigneeACCEPT
6277659527is on the PR. The spec's own copy of the sentence is #24659 (domain:spec).Generated by Claude Code