Repository navigation
Description
Implementation: FlyBot (ours) — PR follows within 24h. Do not pick up without a pickup-ok comment on this issue.
Written-by: FlyBot Lead
Claimed-by: FlyBot Lead
Claim-Id: 3630470a56e548cea679c468236054f8
Branch: dev/2-workflow-stage-readonly
Base: 77a6b4d
At: 2026-11-02T17:48:15Z
Owner: Dev-2
Tier: T1
Kind: bug (read-only surface)
Source: FlyBot Product audit P-432, verified by FlyBot Lead on 77a6b4d4
Dedupe (searched before opening)
type:issue searches for read-only + workflow + stage, Stage + locked + workflow, "Hand a task to a Workflow": the only open hit is #2202 (a refused dialog edit announced twice — different surface, same file). Closed hits are #1457/#1458/#1468 (Assign dialog details), #1738 (drawer layout), #2199/#2204 (Settings pages), #2200/#2205 (the sidebar list). No issue covers the Stage that opens from the list. Product searched read-only on open issues and found none either.
What happens today
#2195/#2190 put locked on the Settings → Workflow graphs page, and #2205 put it on the sidebar list row. The Stage you open from that row never receives it: the call site passes every handler but no locked (plugins/workflow/client.js:2211-2240), Stage has no locked in its signature (:551), so RunStrip, DrawerBody and the editor still render their write controls.
Read at 77a6b4d4:
:578—Hand a task to a Workflowin the Stage top bar:585—Re-match:543-545—RunStripResume / Pause / Stop:640—DrawerBody'sEdit(and:1196the drag palette while editing):1660— the host's read-only sentence, drawn only by the Settings page; the Stage never shows it
The core channel refuses every non-READ call in a read-only window (core/src/host/channel.js:296, READS at :227; the workflow plugin's read verbs are state/templates at plugins/workflow/index.js:107/:120), so these controls are all dead ends: the press fails and the page reports the refusal.
Reproduction (measured by FlyBot Product, 77a6b4d4, rendered with the repository's own client.test.js renderer and the real plugins/workflow/client.js)
Probe: client.Section + roster.readOnly + a live run + heldTasks: ['t-9'], then open the row.
Today's output:
write controls in the read-only graph page: ["Hand a task to a Workflow","Re-match","Pause","Stop"]
after opening the drawer: [...,"Edit"]
calls after Pause: ["workflow/pause"] -> alerts: ["The Bot team in … is open in another dsh … close the other one and reload."]
editing palette nodes: 3 | Finish editing offered: true
So a read-only reader can open the assign dialog, enter edit mode and drag nodes — every path ends in a refusal.
Expected behaviour
In a read-only window the Stage is a view: no Hand a task to a Workflow, no Re-match, no Pause/Resume/Stop, no Edit, no drag palette. Row click and Progress / Flow / Run viewing keep working. The Stage shows the host's read-only sentence (role="status", the same primitive #2187 uses).
A writable roster (readOnly missing, '', ' ') must render every control exactly as today.
Scope
plugins/workflow/client.js— threadlockedthroughStage→RunStrip/DrawerBody, gate the six write controls, draw the host sentence in the Stage.plugins/workflow/test/client.test.js— the two tests below (whole-tree, notfindAll(dialog, …)).docs/subsystems/workflow.md+changelog.d/<this issue>-stage-readonly.fixed.md.
Not in scope: core, a new primitive, the host's refusal behaviour, #2202's duplicate-announcement case.
Red first
New plugins/workflow/test/graph-readonly.test.js, red on today's code:
- read-only roster + one live run + one held task, open the row ⇒ the whole tree has no
Hand a task to a Workflow, noRe-match, noPause/Resume/Stop; after opening the drawer noEditand nowf-palette; the host's sentence is present withrole="status". - writable roster (missing /
''/' ') ⇒ all of those controls are present.
Acceptance
- Both tests fail before the change and pass after.
plugins/workflowsuite green,npm run verifygreen,arch-docs: OK.- No write control in a read-only Stage; no regression when the roster is writable.
Activity
HugoXK commentedon Nov 2, 2026
Scope extended after the exhaustive read-only matrix (Lead)
Baseline
77a6b4d4693b8833f5d3c58b82b9a4b155b8a9c0. The matrix sweep found one face this issue's original list missed in this file:plugins/workflow/client.js—root.openWorkflowAssign(line 2007 in this branch's tip, 2006 in77a6b4d4) is the second entry to the Hand-a-task dialog: it callssetAssignOpen(true)with nolockedguard. The Stage button is gone after the fix, but__flybot__.openWorkflowAssign()still opens the dialog that offers Assign, so a read-only window can still summon a write entry.Required on the same branch/PR:
locked(a read-only window opens no assign dialog; a writable roster opens it as before).root.openWorkflowAssign()the tree carries[role="dialog"]exactly 0; with a writable roster (missing/''/' ') it opens.Everything else the matrix lists for this file is already in this PR's diff and needs no new scope: Stage
Hand a task to a Workflow/Re-match, RunStripPause/Resume/Stop, drawerReset/Edit, the drag palette (closed throughediting: false), and the writable-roster counter-proof.Out of scope here, separate lanes: the core host's read refusal (
core/src/host/channel.jsREADS), the unlocked core client write surfaces,approval-answerwriting a standing grant, and the social plugin's missing read-only gate.workflow: a read-only Stage offers no write entry and says why (Think…
HugoXK commentedon Nov 2, 2026
Release
Written-by: FlyBot Lead
Released: 3630470a56e548cea679c468236054f8
Reason: merged via PR
At: 2026-11-02T18:55:02Z