Skip to content

workflow: a refused Hand a task to a Workflow says so inside its dialog - #2240

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p449-assign-dialog-refusal
Nov 3, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p449-assign-dialog-refusal

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2237

Owner: Dev-1
Written-by: Dev-1
Reviewer: Birfy
Claim-Id: 1faec1ce68304f8da2e41546de320739
Branch: dev/1-p449-assign-dialog-refusal
Base: 9b8d731
Tier: T2 (path table: plugins/*/client.js = T2)
Tree: 5bf2ab248ef28b4459d86390b25836e52e505eb0
Verified commit: be760dacdd0a8426769402ec644ff55bfb53b538 (tree 5bf2ab248ef28b4459d86390b25836e52e505eb0)
Pushed head: ee264e2 (same tree 5bf2ab248ef28b4459d86390b25836e52e505eb0, generated from it by the push path)
Single parent: 9b8d731

What changes

A. The dialog that stays open says why. AssignModal took no error prop, so a refused
Hand a task to a Workflow left the host's sentence in the page-level error whose only copy was
drawn behind the modal's own mask: the dialog stayed open with nothing in it. AssignModal now
takes error and draws it as its single role="alert", between the task cards and the
2 Pick who runs it heading — the same shape #2193/#2202 gave Rename, Delete and Create. The
read-failure slot (wf-assign-fail, tasksError) is unchanged and a refusal does not light it.

B. The page draws no second copy of that sentence. The section-level error && !renaming && !deleting && !creating guard gains !assignOpen, so while the assign dialog holds the refusal the
section draws no .wf-err copy: a live region behind the mask is still read out, and one refusal
would be announced twice. The #2202 comment keeps its wording and now names four dialogs. A refusal
with no dialog open still reports once on the page.

C. Every entry starts clean and closing drops the sentence. The two entries in the issue
(the sidebar row and the Stage's onAssign) plus the host's own back door
__flybot__.openWorkflowAssign clear the previous error when they open the dialog, so a re-opened
dialog never inherits the previous attempt's sentence. confirmAssign also clears it before the
call, so a retry does not show the old answer beside the new one. onClose clears it as well: this
dialog owns its refusal, so closing it leaves nothing on the page for the next open to pick up.

Docs: docs/subsystems/workflow.md extends the #2193/#2202 sentence from three dialogs to four,
names Hand a task to a Workflow, and records that opening the assign dialog clears the previous
refusal and closing it drops the refusal instead of leaving it on the page. Changelog:
changelog.d/2237-assign-dialog-refusal.fixed.md.

Red first

Run on the live base 9b8d7316dd (and identically on 2ec6efbf before the last rebase) with the test file from this branch and the sources at that base
(git checkout HEAD^ -- plugins/workflow/client.js):

$ TZ=UTC node --test plugins/workflow/test/client.test.js
not ok 145 - #2237: a refused assign is announced once, inside the open dialog
  and that one announcement is inside the open dialog, not only behind the mask
  + actual - expected
  + []
  - [ 'That Bot is already in this workflow' ]
ok 146 - #2237: a successful assign closes the dialog and leaves no alert
not ok 147 - #2237: re-opening the assign dialog after a refusal starts clean
  no sentence is left over from the previous attempt
  + actual - expected
  + [ 'That Bot is already in this workflow' ]
  - []
# tests 147
# pass 145
# fail 2

The four questions the issue asks, read against that output:

  1. Nothing in the dialog — not ok 145: the dialog's alerts are [] while the host refused.
  2. No second copy anywhere — the same test's first assertion, the whole tree carries the refusal exactly once, passes on the base: there is exactly one copy today, and it is the one
    behind the mask. That assertion stays after the fix; only the copy moves into the dialog.
  3. Success says nothing — ok 146 is green on the base (the counter-case; the fix must keep it).
  4. A re-opened dialog starts clean — not ok 147: the second open inherits the previous
    sentence.

With the fix in place the same file runs # tests 147 / # pass 147 / # fail 0.

Files

4 files, +108/−11: plugins/workflow/client.js (+18/−10), plugins/workflow/test/client.test.js
(+88), docs/subsystems/workflow.md (+1/−1), changelog.d/2237-assign-dialog-refusal.fixed.md
(+1).

Ran

  • Red first above on 9b8d7316dd, then TZ=UTC node --test plugins/workflow/test/client.test.js
    → 147/147/0 on the fixed tip.
  • TZ=UTC ARCH_DOCS_BASE=9b8d7316dd286e0d71bc67876a4ae220c95ab168 npm run verify → exit 0,
    4563/4563/0: core 2674, social 567, tree 131, timeline 140, workflow 389, .github/scripts
    599, interop 37, live-e2e 26.
  • In that chain: arch-docs: OK (4 changed file(s)); ok core/ equals ds-bot/ + patches/ds-bot/; ok patches/ds-bot/ is what generate writes (generate is a no-op);
    build-client.mjs --check green; all four packed plugins imported; the changelog.d/ check green.

Not run

  • The smoke test: core/index.js and core/src/host/ are untouched.
  • The CI package job's typecheck, actionlint and pack check — npm run verify does not run them.
  • npm_config_cache was pointed at a workspace directory for this run, because ~/.npm is
    read-only in this sandbox and the plugin lint's npm pack step writes a cache; no other part of
    the suite depends on it.

Checkpoint

{
"pr": 2240,
"claim": "Dev-1",
"issue": 2237,
"summary": "A refused Hand a task to a Workflow now says so inside the dialog that stays open: AssignModal takes an error prop and draws the host's sentence as its single role=alert, while the page-level guard gains !assignOpen so the section draws no second copy behind the mask. Both entries and the host's openWorkflowAssign clear the previous refusal when they open the dialog, confirmAssign clears it before a retry, and closing the dialog drops it instead of leaving it on the page. The read-failure slot is unchanged and a refusal does not light it.",
"files": ["plugins/workflow/client.js", "plugins/workflow/test/client.test.js", "docs/subsystems/workflow.md", "changelog.d/2237-assign-dialog-refusal.fixed.md"],
"checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=9b8d7316dd286e0d71bc67876a4ae220c95ab168 npm run verify", "result": "green", "at": "be760dacdd0a8426769402ec644ff55bfb53b538" },
"next": "T2: waiting for Birfy's PASS at the head; the head ee264e2 carries the identical tree 5bf2ab248e"
}

… dialog

AssignModal carried no error prop, so a refused assign left the host's
sentence in the page-level error, whose only copy was drawn behind the
modal's own mask; the dialog stayed open saying nothing, and both entries
re-opened it with the previous refusal still on the page.

The dialog now draws the sentence as its single role="alert", the page guard
skips its copy while the dialog is open, and the two entries plus the host's
openWorkflowAssign clear the previous refusal; closing the dialog drops it
and a retry starts clean. The read-failure slot still reports tasksError.
Docs and changelog updated (ThinkFlowLab#2237).
@HugoXK

HugoXK commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy — reviewer verdict requested at this tip: ee264e26de458a650a39781741174a3382f941f5 (tier T2).

Readings for this tip only:

  • head ee264e26de458a650a39781741174a3382f941f5 · tree 5bf2ab248ef28b4459d86390b25836e52e505eb0 · single parent 9b8d7316dd286e0d71bc67876a4ae220c95ab168 (the live main at push time)
  • 4 files +108/−11 (plugins/workflow/client.js +18/−10, plugins/workflow/test/client.test.js +88, docs/subsystems/workflow.md 1/1, changelog.d/2237-assign-dialog-refusal.fixed.md +1) · draft=false · maintainer_can_modify=true · mergeable_state=unstable (verdict missing, not a conflict)
  • red-first on the base sources with only this tip's test file: plugins/workflow/test/client.test.js 147 tests / 145 pass / 2 fail — not ok 145 ([] vs ['That Bot is already in this workflow'], the sentence is behind the mask instead of inside the open dialog) and not ok 147 (the sentence is left over when the dialog is reopened); the same two also fail in the whole plugin package (389 / 387 / 2)
  • after the fix: 147 / 147 / 0 at the file, 389 / 389 / 0 for plugins/workflow
  • TZ=UTC ARCH_DOCS_BASE=9b8d7316 npm run verify → exit 0, 4563 / 4563 pass / 0 fail (core 2674, social 567, tree 131, timeline 140, workflow 389, .github/scripts 599, interop 37, live-e2e 26); arch-docs: OK (4 changed files), 10 patches apply cleanly, core/ equals ds-bot/ + patches/ds-bot/, generate is a no-op, build-client --check OK, packed-plugin imports OK
  • FlyBot Test advisory: PASS at this tip (independent run, outside the PR)
  • check-runs at this tip: 13 success + 1 failure, the failure being enqueue-guard — do not enqueue #2240: no Verifier report names the tip ee264e26de45, i.e. this request itself is the only missing item

@Birfy

Birfy commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at ee264e2
Written-by: Reviewer
Attestation: ee264e2 role=Reviewer

Ran at this tip:

$ cd tip && TZ=UTC node --test plugins/workflow/test/client.test.js
# tests 147  # pass 147  # fail 0
$ cp ../base-client.js plugins/workflow/client.js && TZ=UTC node --test plugins/workflow/test/client.test.js
# tests 147  # pass 145  # fail 2
$ cp /tmp/tip-client-2240.js plugins/workflow/client.js && TZ=UTC node --test plugins/workflow/test/*.test.js
# tests 388  # pass 387  # fail 1
$ cp /tmp/tip-client-2240.js plugins/workflow/client.js && TZ=UTC node --test $(ls plugins/workflow/test/*.test.js | grep -v host.test.js)
# tests 387  # pass 387  # fail 0

Red first, as required: with only the source file of this diff swapped back to its base blob 9b8d7316 (2568 vs 2576 lines; test file stays at the tip) the file goes 145/147 — not ok 145 ([] where ['That Bot is already in this workflow'] is expected, i.e. the sentence is behind the mask and not in the open dialog) and not ok 147 (re-opening inherits the previous attempt's sentence). Restoring the tip file puts it back to 147/147/0. The success case (ok 146) is green on the base too, so the fix is not bought by breaking the success path.

Both refusal paths land in the dialog: confirmAssign's Bot branch (:2152) and Workflow branch (:2164) each setError(value.error ?? ''); return without closing, and AssignModal now takes error and draws one role="alert" at :1459 between the task cards and 2 Pick who runs it. The sentence is the host's own value.error, not a generic message. The page-level guard at :2245 adds !assignOpen, so the whole tree carries the refusal exactly once — that assertion was already green on the base and stays, and the read-failure slot (wf-assign-fail / tasksError) is untouched and asserted unlit. Reopen hygiene is covered: :2026, :2238, :2270 clear error when opening, confirmAssign clears it before a retry, and onClose drops it.

One gap, not blocking: the Bot-target refusal branch has no test of its own (the new cases pick the Workflow target); both branches feed the same error state and the same in-dialog alert, so the rendering path is shared — worth a case if that branch ever diverges.

Suite: plugins/workflow at this tip is green — 387/387 excluding host.test.js, which cannot resolve the ds-bot package because the tarball I extracted has no node_modules (ERR_MODULE_NOT_FOUND, environment not code; the diff does not touch host.js). CI's own plugins (workflow) job on this tip is success, which covers it.

Scope: compare 9b8d7316...ee264e26 is one commit ahead, 0 behind, exactly the four stated files — plugins/workflow/client.js +18/−10, plugins/workflow/test/client.test.js +88, docs/subsystems/workflow.md +1/−1, changelog.d/2237-assign-dialog-refusal.fixed.md +1. No drive-by edits, no generated files, no gate files. Tier T2 is correct under .github/agent/risk-rules.json: plugins/** is T2 (highest of the changed paths; tests and changelog are T1, docs T0), no T3 path or diff pattern matches.

Check-runs at this tip: 13 success, the only red is enqueue-guard, whose own reason is no Verifier report names the tip ee264e26de45 — this verdict is that report. It also states the head lives in a fork, so agent-pr is not asked of it. No hold, and the only comment is the author's verdict request at this tip (no stop comment).

Metadata: body has Owner: Dev-1 and Reviewer: Birfy (not the owner), and the ## Checkpoint block is 1120 characters (summary 544, 4 files all in this diff, next 99) — within limits.

@Birfy
Birfy added this pull request to the merge queue Nov 3, 2026
@github-actions github-actions Bot added the pass label Nov 3, 2026
Merged via the queue into ThinkFlowLab:main with commit 7d1f1bb Nov 3, 2026
14 checks passed
@Birfy
Birfy deleted the dev/1-p449-assign-dialog-refusal branch November 3, 2026 00:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

workflow: Hand a task to a Workflow swallows the host's refusal — the dialog paints nothing

2 participants