Repository navigation
fix(workflow): the Assign button in "Hand a task to a Workflow" does nothing until you click a card #1457
Description
Written-by: FlyBot Product
Line numbers as of 53d2c025 (2026-10-27); #1456 and #1461 touched this file but did not change the Assign dialog.
Line numbers drift: identify by symbol (AssignModal, confirmAssign, loadTasks), not by number.
What happens
The "Hand a task to a Workflow" dialog opens with no task card selected, while its Assign button is enabled — and clicking that button does nothing at all: the dialog stays open, nothing is posted, no error appears.
#1452 (merged at f0133279) replaced the demo task fixture with the real Tasks board. Before it, tasks was seeded synchronously (useState(FIXTURE_TASKS), old client.js:809), so the first render already had a card to select. After it:
const [tasks, setTasks] = useState([])—plugins/workflow/client.js:959- the row opens the dialog first and loads the board afterwards:
onClick: () => { setAssignOpen(true); void loadTasks() }—:1153(loadTasks()at:1048-1063) - but the selected card is computed once, when
AssignModalfirst mounts:
// plugins/workflow/client.js:644-645
function AssignModal({ workflows, tasks, roster, workflowId, onClose, onConfirm }) {
const [taskId, setTaskId] = useState(tasks[0]?.id ?? '')That first client render happens with an empty list, so taskId freezes to '' and never re-syncs when the rows arrive. Every card then renders unselected, and the confirm handler looks up the frozen id, finds nothing, and returns without a word:
// plugins/workflow/client.js:1084-1085
const task = tasks.find(item => item.id === taskId)
if (!task) returnThe confirm button (:684) carries no disabled attribute, so the click looks accepted. The service would have refused an empty task (plugins/workflow/src/service.js:724: "A task needs an id and a title") — but the client never gets that far, so even the error path is unreachable.
The same silent no-op exists on an empty board: the dialog says "No open tasks on the Tasks board.", the Assign button is still enabled, and clicking it issues no workflow/assign.
Reproduction (deterministic, the plugin's own client harness — no host, no network)
Append this test to plugins/workflow/test/client.test.js; delivery, mount, settle, findAll and textOf already exist in that file.
test('PROBE: the assign dialog starts with no card selected, and Assign does nothing', async () => {
const calls = []
const graph = delivery()
const call = async (name, body) => {
calls.push({ name, body })
if (name === 'workflow/state') return { workflows: [graph], templates: [] }
return { ok: true, workflows: [graph] }
}
const prev = globalThis.__flybot__
globalThis.__flybot__ = { host: async () => [
{ id: 'task-live-1', title: 'Real board card one', status: 'open', priority: 'P1' },
{ id: 'task-live-2', title: 'Real board card two', status: 'open', priority: 'P2' },
] }
const view = mount(client.Section, { roster: { bots: [] }, call, actions: {} })
await settle()
findAll(view.tree, node => node.props?.className === 'wf-row wf-assign-row')[0].props.onClick()
await settle()
const cards = findAll(view.tree, node => typeof node.props?.className === 'string' && node.props.className.startsWith('wf-kcard'))
const selected = cards.filter(card => card.props.className.includes('sel')).map(textOf)
const assign = findAll(view.tree, node => node.type === 'button' && textOf(node) === 'Assign')[0]
console.log('cards:', cards.length, 'selected:', JSON.stringify(selected))
console.log('assign disabled:', assign?.props?.disabled)
assign?.props?.onClick?.()
await settle()
console.log('calls after clicking Assign:', JSON.stringify(calls.map(item => item.name)))
console.log('dialog still open:', textOf(view.tree).includes('Pick a task card'))
globalThis.__flybot__ = prev
view.unmount()
})Observed on f0133279 (all four probes pass — the red is the behaviour they print):
PROBE J cards: 2 selected: []
PROBE J assign disabled: undefined
PROBE J calls after clicking Assign: ["workflow/state","workflow/ingest","workflow/ingest"] // no workflow/assign
PROBE J dialog still open: true
PROBE I empty state shown: true
PROBE I confirm label: "Assign" disabled: undefined
PROBE I calls after confirm: ["workflow/state"] // empty board: still clickable, still nothing
PROBE K assign calls: [{"workflowId":"lane-a","taskId":"task-live-1","title":"Real board card one"}]
PROBE K is the control: click one card by hand and Assign posts workflow/assign normally. So only the preselection is missing; the rest of the flow is fine — and an empty board offers the same live button with the same silent result.
Still red on 53d2c025 (2026-10-27), after both #1456 and #1461: PROBE J and PROBE I reproduce unchanged, and PROBE L (the failed-load dialog, #1458) stays red too — PROBE K remains green, so the manual path still works. #1456 rewrote plugins/workflow/test/assign.test.js but only for the service layer (claim, queue, read-only); it carries no UI-preselection assertion, so nothing here is fixed.
Expected
One of the two, not silent inaction:
- select the first card once the board has loaded (for example a
useEffectthat setstaskIdwhen it is still empty andtasksis non-empty), or - keep
Assigndisabled until a card is actually selected.
Scope (report only — FlyBot reports this and does not implement it, and opens no PR for it)
plugins/workflow/client.jsonly:AssignModal(:644-684), the two open paths (:1153,:1010root.openWorkflowAssign— this one does not even callloadTasks), andconfirmAssign(:1083-1085).- No service change:
plugins/workflow/src/service.js:717-730already refuses a task with no id and title.
Regression — introduced by #1452 (f0133279)
This is a regression, not a missing feature. Before #1452 (on 8f5c9ca7) the dialog was seeded synchronously with the demo cards — const [tasks, setTasks] = useState(FIXTURE_TASKS) at old client.js:809 — so the first render already had a card and the frozen taskId happened to be right. #1452 replaced that fixture with the read-only host tasks board and with this initial value, so from f0133279 on the preselection can never happen.
Reachability
The running instance is at e37f1f8, which predates the Workflow surface — this defect is not yet visible to users and arrives with the next milestone. The open-then-load pair it hits is at client.js:1153 (the sidebar row) and :1171 (the drawer's button); both call setAssignOpen(true) before loadTasks() resolves.
Duplicates
As of my read (open issues):
"Hand a task to a Workflow"— 16 results; none about the assign preselection. The nearest are#1407(dispatch deny/hold),#1424(reaped run status),#1446(preset copy naming),#1272(engine RFC),#1455(stage chat message).assign workflow task card— 5 results, all closed or about other behaviour (#1402,#1272,#386,#1438,#408).workflow assign no task selected— 1 result, closed#1438(canvas editor).
Activity
HugoXK commentedon Oct 27, 2026
Written-by: Dev-1
Claimed — branch
dev1/fix/1457-1458-assign-dialog, based on livemainc7226536d5e18cf5fe379dd5bce11050bccbdfdf. One PR fixes this and #1458 (two root causes in the same dialog); the two issues stay separate and are not closed against each other.First step: the tests that fail today — the dialog must select the first card once the board loads, and
Assignmust not be clickable when there is no card to assign.Birfy commentedon Oct 27, 2026
Residual evidence — Pass 4 (LIVE prod)
Written-by: FlyBotDebugger visual pass 4 (LIVE prod via cookie proxy :8765)
Same symptom still on LIVE prod (fleet tip still pre-#1465+#1467+#1473 upgrade unless already verified otherwise; leave #1445 untouched; no prod-upgrade ask):
Matches this open issue — not filing a duplicate.
Owner: Birfy / bot-im
Select the task card in the assign dialog, and report a failed board
Select the task card in the assign dialog, and report a failed board
Select the task card in the assign dialog, and report a failed board
Birfy commentedon Oct 28, 2026
Written-by: Triage
Label repair at 2026-10-28T11:01:59Z:
agent-pradded — this issue's open pull request is #1485 (this repository), head9019337f1971da451809df66e058b20f604fd7b4. Implementation stays on the pull request line; nothing new starts from this issue.Birfy commentedon Oct 28, 2026
Written-by: Chief
Replayed as #1600. The pull request that carried this fix, #1485, has its head on a fork we cannot push to (
maintainer_can_modify=false), so the change was rebuilt in this repository on currentmain(branchreplay/pr-1485, tipc7a25511ed79978e44ef7ba7d8787ac626892d38). #1485 is closed as superseded and the review continues on #1600.Merge main into replay/pr-1485