Repository navigation
Packaged automation: a flow disabled by deployment policy shows a status chip; the policy is explained once #12609
Description
Source
Walked on objectui main 8415d9f11, /apps/com.objectstack.setup/component/automation/packaged (packages/app-shell/src/views/setup/PackagedAutomationPage.tsx): each scheduled flow the deployment policy keeps off (Campaign Auto-Completion, Case SLA Monitor, …) renders a six-line paragraph inside its table row — "disabled by deployment policy — package-authored scheduled work is off on this deployment (OS_AUTOMATION_SCHEDULED_WORK_ENABLED is unset or not truthy), so no time trigger arms and no packaged defineJob is scheduled. This is not a binding failure and nothing about the flow needs fixing: set OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true … It is OFF by default in every posture — a clock-driven workload's cost is a fact about the deployment, not about the flow." — repeated verbatim per flow, while the row's switch still reads On.
Target
- The row shows one status chip after the flow name: "Off by deployment policy" (muted, with a clock icon); the switch reflects the effective state (off, disabled) with the objectui#12303 tooltip reason.
- One banner above the table, shown once when at least one flow is policy-disabled: "Scheduled flows are off on this deployment. Set OS_AUTOMATION_SCHEDULED_WORK_ENABLED=true to run package-authored scheduled work." with a link to the docs page; the long paragraph moves into the chip's tooltip or the banner's "Why" disclosure.
- Rows stay 40 px; the table scans as a table again.
File surface
packages/app-shell/src/views/setup/PackagedAutomationPage.tsx, PackagedActionsSection.tsx if it shares the row, packages/i18n/src/locales/*.ts, tests beside.
Acceptance
- Tests: a policy-disabled flow renders the chip and no paragraph in the row; the banner renders once for two disabled flows and not at all for none.
- Screenshot at 1280 px, light and dark.
pnpm --filter @object-ui/app-shell testgreen.
Constraints
- The policy text comes from the server's answer; the console shortens the display, not the server's reason.
Evidence
Screenshots on the temporary branch claude/console-home-login-design-20261016 of objectstack-ai/objectui (commit 01244b886, never merged): setup-packaged-automation.png.
Filed by the epic PM session session_cdb7a717-def7-4e71-9988-f089fbaa18ff from the 2026-10-16 second review pass: objectui main 8415d9f11, HotCRM 63db83e2, the vite console on :5180 against objectstack serve --dev on :4001, the Studio pillars and all 43 Setup pages walked in the maintainer's browser pane at 1024 × 768 (en) with a 1280 × 900 re-check (zh). The maintainer asked for every finding to be filed, verbatim: 「全部立卡」.
Activity
objectstack-fleet commentedon Oct 16, 2026
pm:retriage— the target needs a field the contract says not to parse ·domain:uiseat 3 ·session_01Vk15bz8xHtKGUfXfCm16h4· 2026-10-16T07:27ZRead in this act: this card's body (no comments),
PackagedAutomationPage.tsxandpackagedFlows.tson objectuiorigin/main0a41305, andFlowRuntimeStateon objectstackorigin/main43567a8fc.Governing text.
@objectstack/specpackages/spec/src/contracts/automation-service.ts,FlowRuntimeState.reason, verbatim: "A free-form sentence, deliberately — the shape the two surfaces that already carry this reason use. Consumers RENDER it; ⛔ do not parse it."The same docblock says a deployment-policy row and a real binding failure both arrive as
enabled: true, bound: falsewith areason. Thereasonsentence is the only thing that tells them apart. objectui follows this already:PackagedFlowRow.reasoninpackagedFlows.tsreads "It is RENDERED, never read … No sentence is matched, compared or restyled on this side" (objectui#9217).The conflict. Target 1 (an "Off by deployment policy" chip on exactly the policy-disabled rows) and target 2 (a banner "shown once when at least one flow is policy-disabled") both need the console to know which rows are policy-disabled. On the wire that means parsing
reason. ⛔ The seat does not dispatch an option that conflicts with the governing text.What is buildable without parsing: target 3 (rows stay 40 px), with the verbatim
reasonmoved off the row into a tooltip or disclosure on a neutral chip that does not classify.Asked of triage (route and split; the lane is not this seat's call):
Seam:card on objectstack-ai/objectstack adds a structured discriminator besidereasononFlowRuntimeState, spec first. This card keeps the console half and carriesBlocked-by:that card.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Retriage answered: the seat's option (B) · this card narrows to the half that classifies nothing; the discriminator goes to objectstack as objectstack-ai/objectstack#23842 · triage seat ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-16T08:04ZRead in this act: the seat's request
6210398091, andFlowRuntimeStatein objectstackpackages/spec/src/contracts/automation-service.ts.reasonis rendered, ⛔ never parsed, and a policy-disabled row and a binding failure shareenabled: true, bound: false. So targets 1 and 2 (a classifying chip, a once-banner) cannot be built on today's wire.reasonmoved off the row into a tooltip or disclosure on a neutral chip that does not classify.pm:queue.FlowRuntimeStategains a structured discriminator besidereason(deployment policy vs a binding failure), so a consumer can classify a disabled packaged flow without parsing the sentence (objectui#12609) objectstack#23842 (domain:spec, a vertical seam withservice-automation, p1 +target:v18): a closed discriminator besidereason.@objectstack/spec.pm:retriageremoved. p2 anddomain:uistand.⛔ Not a claim.
objectstack-fleet commentedon Oct 16, 2026
Claim: PM loop round 1 (
priority:p2; the narrowed half ruled by triage at6210990723; filed on the maintainer's 「全部立卡」)Session:
session_011K9g23W67dy2zwnyzaoB6oAccount:
os-teslaBranch:
claude/issue-12609-packaged-automation-rowsWorktree:
objectui-issue-12609Domain:
domain:uiSeat:
domain:ui#1File surface:
packages/app-shell/src/views/setup/PackagedAutomationPage.tsx(the flow rows only; ⛔ not the page header, which objectui#12606 owns),PackagedActionsSection.tsxonly if it shares the row,packages/i18n/src/locales/*.tsif the neutral chip needs a label, tests beside each, changesets for the touched packages (stop on breach; explain in the report).Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuiover the planned paths: no path-derived mandate)Clause-②: yes
Clause-② reason: the neutral chip or its disclosure may add a key to
@object-ui/i18n's publishedenpack («拿不准 ⇒ 按 yes»). The seat'sTIER_DEFAULTreview comes before ready.Responsibility: n/a — not a defect card
Thread-read: 6210990723
Serial constraints cleared: objectui#12606 (seat 4) owns the header row of each Setup component page under
packages/app-shell/src/views/setup/, and its claim names this card's rows as outside its surface; the later lander mergesmain. No other in-flight claim of this lane names these files. The classification half (the chip that names the policy, the once-banner) is objectstack-ai/objectstack#23842 and ⛔ is not this card's:FlowRuntimeState.reasonis rendered, never parsed.Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
os-dev-report
{
"issue": 12609,
"status": "done",
"branch": "claude/issue-12609-packaged-automation-rows",
"pr": "#12647",
"session": "session_011K9g23W67dy2zwnyzaoB6o",
"premise_still_valid": true,
"summary": "H1 held on main 8ed7e5a: PackagedAutomationPage.tsx rendered row.reason as a paragraph element inside the flow cell; PackagedActionsSection renders no reason, so it is untouched. The paragraph is gone. A local ReasonDetails component draws a neutral outline chip (Info icon + 'Details', one new key packagedAutomation.reasonDetails in all ten packs) inline on the label's line, and a Popover that shows the reason verbatim, whole and muted, with no alert role. The chip renders iff joinPackagedFlows kept a non-empty string reason (rule unchanged); its label, icon and classes never depend on the sentence, and nothing regexes, includes or matches it. Popover rather than tooltip because the sentence is several lines and names an operator switch to copy (opens on click, tap and keyboard). The switch is unchanged (H3; noted in the PR Acceptance notes), and the header row is untouched (H4).",
"tests": "PackagedAutomationPage.test.tsx section 6 reworked: policy row and binding-failure row = no paragraph and none of the sentence in the row, the 'Details' chip opens a dialog-role disclosure whose textContent is exactly the sentence, muted, no alert; a new does-not-classify test (policy chip and failure chip: same text 'Details', same className); no-reason rows (bound, disabled, older-backend bare row, non-string reason) keep the old text and have no chip. Single file on ad380ce: 15 passed (15). Ablation via objectstack scripts/ablation-replace.mjs in WRAP mode (anchor x1 to x0, blob changed, restore blob == HEAD 09f1c5c3b1ca and git diff HEAD empty, each leg). The test imports from src (vitest alias), so no build sits in between. A paragraph back in the row: 2 failed, 13 passed ('expected ... length of +0 but got 1'). B chip removed: 3 failed, 12 passed. C chip on every row: 2 failed (no-reason rows, non-string reason), 13 passed. D chip label chosen from the sentence: 2 failed (policy row, does-not-classify), 13 passed. A's first attempt was refused by the tool before running (replacement contained the anchor, count could not drop); re-anchored and re-run, and the counts are from the landed run. Tree clean after all legs (git status empty).",
"gates": "Local, all on ad380ce, each exit captured to a file before any pipe; objectui list hand-derived per dispatch (no dispatch-gates.mjs in this repo). Closure build turbo run build --filter='@object-ui/app-shell^...' --concurrency=2 (it contains i18n's closure) via lock: VERDICT command-exit 0, Tasks 28 successful 28. pnpm --filter @object-ui/i18n type-check && pnpm --filter @object-ui/app-shell type-check via lock: VERDICT command-exit 0, both script names echoed; app-shell tsconfig.test.json includes src/**/*.test.tsx. pnpm exec vitest run --maxWorkers=2 packages/app-shell/src/views/setup/ packages/i18n/ via lock: exit 0, Test Files 88 passed (88), Tests 1387 passed, 13 skipped (1400). check:i18n-keys 0; check:i18n-drift 0 (0 en values changed, 1 key added); check:i18n-dead-keys 0 (report-only; no packagedAutomation key listed); check:changeset-claims 0; changeset:check 0; check:test-path-roots 0; check:spec-symbols 0; check:new-line-citations 0 (0 new); node scripts/check-changeset-presence.mjs 0 (12 source files of 2 released packages, 1 changeset); eslint --no-inline-config on the 12 changed ts/tsx files 0, no output. PR check-runs on ad380ce when the report was written: 35 total; label success, Changeset Fixed Group Check success, Changeset Bump Policy success, dependabot / Test (coverage) / coverage-shard template skipped; Lint, Type Check, Test shards 1-16, Test (dist pins), Repo Gates, Line Citation Gate, Build Docs, Build & E2E, Changeset Declaration, Changeset Overwrite Report, Changeset Claim Re-read, Governed Surface Queue Guard, Bundle Analysis, Live E2E (informational) in_progress.",
"mcp_calls": "0 — no MCP GitHub tool was called",
"api_writes": "2 — (1) fleet relay pr_create stroke fw-20261016T095909Z-6078a1: POST /repos/objectstack-ai/objectstack/dispatches (204), executed as objectstack-fleet[bot] as POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos//issues/12647/assignees [os-tesla]; read-back 7079 bytes sent = 7079 stored. (2) this os-dev-report comment: POST /repos//issues/12609/comments via post-stamped.mjs. Plus git push x2 (empty-branch probe, then ad380ce), which are not REST writes. Zero label writes.",
"files_changed": [
".changeset/12609-packaged-flow-reason-chip.md",
"packages/app-shell/src/views/setup/PackagedAutomationPage.tsx",
"packages/app-shell/src/views/setup/PackagedAutomationPage.test.tsx",
"packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts"
],
"line_budget": "13 files, +166 / -41 against base 8ed7e5a; no line-ratchet ledger is touched",
"deviations": [
"Ablation A attempt 1 was a no-op that the tool refused before running (its replacement contained the anchor); re-anchored by reordering the code element's two class names, then re-run. Declared, not hidden.",
"Card acceptance 3, pnpm --filter @object-ui/app-shell test (the whole package), not run locally: a declared narrowing to the touched directory plus the whole i18n package; CI runs every shard.",
"Card acceptance 2, the screenshot, and the row's pixel height: NOT MEASURED, per the dispatch; the PR lists what a reader needs. H1's 'about 40 px' is not confirmed either way: rows without a reason already stack the label over the machine name in p-4 cells, so what this PR claims is that a reason row is as tall as any other row, not a pixel figure.",
"Comment-only fix in the test file's header, which said 'Five properties' over a six-item list; now 'Six'.",
"The changeset-presence check's output was briefly redirected to gates.tmp in the worktree root, then deleted before any commit (git status clean after).",
"Commits end with the AGENTS.md model-free trailer pair (Claude-Session + Co-authored-by: Claude), not the harness's model-named Co-Authored-By line, per AGENTS.md precedence.",
"Worktree /home/user/objectui-issue-12609 removed (node_modules first, then git worktree remove without --force, exit 0) after the PR was opened."
],
"open_questions": [],
"out_of_scope_findings": []
}
Generated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
ACCEPT → PR #12647 comment 6213285944 · contract review
6213273666(TIER_DEFAULTPASS) ·domain:uiseat 1 ·session_011K9g23W67dy2zwnyzaoB6o· 2026-10-16T10:17ZGenerated by Claude Code
objectstack-fleet commentedon Oct 16, 2026
Landed ·
domain:uiseat 1 ·session_011K9g23W67dy2zwnyzaoB6o· 2026-10-16T10:32Zmain8a8bcf3dde, with one parent,ca7a4c3162. It is an ancestor oforigin/main.mainmoved under the locale packs while the PR queued, so blob equality holds only for the three non-locale files. The merge commit's own diff equals the PR's diff from its merge-base to the reviewed headad380ce46d, line for line: 13 files, +166/−41. Positive control:reasonDetails: 'Details'is present inen.tsat the merge commit (1 hit).completedbyFixes #12609. This note precedes the strip ofpm:dispatched.FlowRuntimeStategains a structured discriminator besidereason(deployment policy vs a binding failure), so a consumer can classify a disabled packaged flow without parsing the sentence (objectui#12609) objectstack#23842's structured field, per triage6210990723.Generated by Claude Code