Skip to content

workflow: a read-only window's Stage still offers Hand a task / Re-match / Edit / Pause / Stop — the lock stops at the list #2213

Closed

Description

@HugoXK

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 Workflow in the Stage top bar
  • :585 — Re-match
  • :543-545 — RunStrip Resume / Pause / Stop
  • :640 — DrawerBody's Edit (and :1196 the 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 — thread locked through Stage → 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, not findAll(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:

  1. read-only roster + one live run + one held task, open the row ⇒ the whole tree has no Hand a task to a Workflow, no Re-match, no Pause/Resume/Stop; after opening the drawer no Edit and no wf-palette; the host's sentence is present with role="status".
  2. writable roster (missing / '' / ' ') ⇒ all of those controls are present.

Acceptance

  1. Both tests fail before the change and pass after.
  2. plugins/workflow suite green, npm run verify green, arch-docs: OK.
  3. No write control in a read-only Stage; no regression when the roster is writable.

Activity

  1. HugoXK commented on Nov 2, 2026

    @HugoXK
    ContributorAuthor

    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 in 77a6b4d4) is the second entry to the Hand-a-task dialog: it calls setAssignOpen(true) with no locked guard. 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:

    1. Guard that entry with locked (a read-only window opens no assign dialog; a writable roster opens it as before).
    2. Red-first, full-tree count: with a read-only roster, after 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, RunStrip Pause/Resume/Stop, drawer Reset/Edit, the drag palette (closed through editing: false), and the writable-roster counter-proof.

    Out of scope here, separate lanes: the core host's read refusal (core/src/host/channel.js READS), the unlocked core client write surfaces, approval-answer writing a standing grant, and the social plugin's missing read-only gate.

  2. HugoXK commented on Nov 2, 2026

    @HugoXK
    ContributorAuthor

    Release

    Written-by: FlyBot Lead
    Released: 3630470a56e548cea679c468236054f8
    Reason: merged via PR
    At: 2026-11-02T18:55:02Z

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions