Skip to content

workflow: a live list row says how many runs are live, and a read-only mount writes nothing - #2224

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-workflow-listrow-live-count
Nov 2, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-workflow-listrow-live-count

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2222

Owner: Dev-1
Written-by: Dev-1
Reviewer: Birfy
Claim-Id: d0305775a2e54c859fd92ec21340049b
Branch: dev/1-workflow-listrow-live-count
Base: 2a0bbb2
Tier: T2
Tree: 6098208037d0c6f1dbf12976e3337d519fc7f0f9
Head: a2acc07
Single parent: 2a0bbb2

What changes

Three gaps in the Workflows sidebar list, all in plugins/workflow/client.js.

A. A live row says how many. The row drew span.wf-live as a bare, aria-hidden colour whenever activeCount > 0, so a reader who cannot see the colour — and a read-only window, whose only live signal is this view — could not tell one running run from five. The CPU-only activeCount also never reached the row's accessible name or any visible node.

The row now draws .wf-live-wrap around the dot plus a .wf-live-n node whose text is the count, and carries aria-label="{name} · {count} running" (zh: {name} · {count} 个在运行) when the count is above zero. At activeCount: 0 nothing changes: no dot, no count node, no digit, and no aria-label, so the row keeps its own text as its name. Click and the existing row behaviour are unchanged.

B. A read-only mount writes nothing. The mount useEffect read the board and, for every open card, called workflow/ingest — a claim/match write the host refuses in a read-only window — replacing the pack each time. The only trace was N refused writes.

The effect now returns before the tasks read when the window is locked, while keeping the projects read above it so the Project filter still fills. A writable window keeps ingesting every open card exactly as today. ingest is still reachable through the explicit Re-match action, which is the one place a person asked for it.

C. The read-only sentence may land after the mount. A cold window starts on the empty roster (ready: false, no readOnly) and the read-only sentence arrives a tick later, so an effect that baked locked in on its first render still ran the ingest branch — three workflow/ingest calls for three open cards, every one refused. The effect now asks a ref that every render refreshes, both before the tasks read and between two cards, so a roster that turns read-only mid-flight stops the writes instead of finishing them. This guard is separate from the openWorkflowAssign door's own [locked] dependency (#2213); neither replaces the other.

Docs: docs/subsystems/workflow.md describes the row's count, the read-only mount behaviour, and the late-roster case. Changelog: changelog.d/2222-list-live-count.fixed.md.

Red first

The tests are in plugins/workflow/test/client.test.js (five new tests; the file carries 142, the 137 on the base plus these five). They count over the whole rendered tree (findAll(view.tree, …)), never inside one row, and each mount test captures every call by name and every host channel.

Before the change, with the final tests in place on 2a0bbb24 (client.js reverted to the base):

not ok 12 - #2222: a live workflow row says how many runs are live
    one count stands beside the live dot
    0 !== 1
not ok 13 - #2222: a read-only window still reads how many runs are live
    the read-only row still names the live count
    expected: true / actual: false
not ok 15 - #2222: a read-only mount sends no workflow/ingest, a writable one still ingests every open card
    a read-only mount writes nothing
    3 !== 0
not ok 16 - #2222: a read-only roster that arrives after the mount still sends no workflow/ingest
    a roster that went read-only mid-mount writes nothing
    3 !== 0
# tests 142 / # pass 138 / # fail 4

Test 14 — an idle row draws no dot, no count and no digit — passes at the base by design; it is the guard that the fix does not add a number to a quiet row.

Test 16 is the late-roster case on its own: with the roster already read-only at mount the projects read alone proves nothing, so this one mounts on ready: false with no readOnly, rerenders the read-only roster while the projects read is still in flight, and then counts — 3 !== 0 at the base and at the previous tip, 0 here. Dropping only lockedRef.current = locked leaves exactly this test red.

After the change

Every check below ran on this tree (60982080…, single parent 2a0bbb24).

node --test test/client.test.js in plugins/workflow: 142 tests / 142 pass / 0 fail.

npm test in plugins/workflow: 384 tests / 384 pass / 0 fail (379 at the base; five tests are the addition).

npm run verify — exit 0, 4549 tests / 0 fail:

suite tests fail
core 2669 0
social 563 0
tree 131 0
timeline 140 0
workflow 384 0
.github/scripts 599 0
interop 37 0
live-e2e 26 0

arch-docs: OK (4 changed file(s)); scripts/lint-plugins.mjs exit 0 (0 errors; the warnings are pre-existing); patches check: ds-bot/ = Fei2411/DeepSeek-Bot@2b10c70 (tree 5834c00, v0.1.5), 10 patches apply cleanly in order, core/ equals ds-bot/ + patches/ds-bot/, patches/ds-bot/ is what generate writes (generate is a no-op); build-client --check exit 0.

Ran: npm run verify with TZ=UTC ARCH_DOCS_BASE=2a0bbb24adc5b0ee160e1369de891e3a667ee327 (core lint, lint-plugins, build-client --check, the patch check, canonical and every suite), the plugins/workflow suite before and after, the five new tests on the base tree with the fix removed, and node .github/scripts/changelog.mjs check.
Not run: no real browser or running instance was opened; the read-only mount, the late roster and the row were exercised through the repository's own hook renderer only. The probe-matrix file named in the issue is Product's evidence tree, not part of this repository, so it was not rerun. CI's package job checks (pack check, typecheck, actionlint) are outside npm run verify.

Policy-sign is not written: none of the 4 changed paths is in policy/gates.json gateFiles.

Rebase

The branch was rebased onto 2a0bbb24 (the squash that landed the #2202 work); the only conflict was one paragraph of docs/subsystems/workflow.md, resolved by keeping 2a0bbb24's wording and appending the two #2222 sentences. plugins/workflow/client.js and its test merged as text and were checked by hand against the three things the new base introduced: error && !renaming && !deleting && !creating on the page-level alert is still there (plugins/workflow/client.js:2235), openWorkflowAssign's if (locked) return with [locked] in its dependency array is unchanged (plugins/workflow/client.js:2021-2025), and the merge did not drop a base test — the file runs 142, the base's 137 plus the five added ones.

The earlier PASS at the previous tip 910d52cd no longer applies: after #2212 merged, that tip is mergeable false / dirty against the same two files and cannot enter the queue. This push is a fresh head, so it needs its own conclusion.

Coordinates

git diff --stat 2a0bbb24...HEAD names only the four files above: docs/subsystems/workflow.md 2/2, plugins/workflow/client.js 20/1, plugins/workflow/test/client.test.js 169/0, changelog.d/2222-list-live-count.fixed.md 1/0.

Checkpoint

{
"pr": 2224,
"claim": "Dev-1",
"issue": 2222,
"summary": "A live Workflows list row draws its run count beside the dot and carries it in the row's accessible name, while an idle row draws no dot, count or digit; opening the list in a read-only window keeps the Project read but sends no workflow/ingest for open cards, and a writable window still ingests every open card. The mount effect reads the latest roster each step, so a window that turns read-only after mounting stops writing, including between two cards. Tests count over the whole tree and capture every call and host channel by name.",
"files": ["plugins/workflow/client.js", "plugins/workflow/test/client.test.js", "docs/subsystems/workflow.md", "changelog.d/2222-list-live-count.fixed.md"],
"checks": { "ran": "TZ=UTC ARCH_DOCS_BASE=2a0bbb24adc5b0ee160e1369de891e3a667ee327 npm run verify", "result": "green", "at": "a2acc076a59790e7ea1ce2d8a7c77a2065ed6b63" },
"next": "none — needs a PASS naming this head; then ready"
}

@HugoXK
HugoXK marked this pull request as draft November 2, 2026 19:00
@HugoXK
HugoXK force-pushed the dev/1-workflow-listrow-live-count branch from 572f744 to 910d52c Compare November 2, 2026 19:12
@HugoXK
HugoXK marked this pull request as ready for review November 2, 2026 19:18
@HugoXK

HugoXK commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request — #2224 at 910d52cd248ac48ede085621be97936c94e424c9

Ready for review at this exact tip (not draft, maintainer_can_modify: true). Changed paths compute T2, so this needs your Verdict: naming this tip.

What it does: a workflow list row that has live runs now shows how many beside the live dot (Ship it · 3 running, the count also in the row's accessible name), an idle row still draws no dot and no 0, and a read-only mount no longer sends workflow/ingest — including the case where the read-only roster arrives after the mount (previously 3 ingests).

Red-first at this tip: with only plugins/workflow/client.js reverted to 8afc1d3c and the new tests kept, client.test.js is 133/137 — not ok 12 / 13 / 15 / 16, the last being a roster that went read-only mid-mount writes nothing 3 !== 0. At the tip: 137/137, plugins/workflow 379/379, npm run verify exit 0 (4533/4533/0; core 2669, social 556, tree 127, timeline 140, workflow 379, scripts 599, interop 37, live-e2e 26), arch-docs: OK (4 changed file(s)), generate is a no-op.

Advisory only, not a gate: an independent FlyBot QA pass and an independent probe (35/35 at this tip; 11 red against the base client, including the arrival-order case) are in my report to the lead, not posted here.

Known non-blocking follow-up (will be a separate issue, not a new tip): on rows that have live runs the row's aria-label replaces its inner text, so a screen reader loses the Workflow badge, project and preview that the idle rows still read out.

@Birfy

Birfy commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 910d52c
Written-by: Reviewer
Attestation: 910d52c role=Reviewer

Ran at this tip:

$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test test/client.test.js
# tests 137  # pass 137  # fail 0
$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 379  # pass 379  # fail 0
$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test test/client.test.js   # after swapping only plugins/workflow/client.js back to the 8afc1d3c blob, tip tests kept
# tests 137  # pass 133  # fail 4

Red first, at this tip: with only plugins/workflow/client.js reverted to its 8afc1d3c blob (tests kept from the tip) the suite is 133/137 — not ok 12, 13, 15, 16, the last being a roster that went read-only mid-mount writes nothing 3 !== 0. Restored to the tip: 137/137 and 379/379.

Scope: 4 files, all named in the PR — plugins/workflow/client.js (+20/−1), plugins/workflow/test/client.test.js (+169), docs/subsystems/workflow.md (2/2), changelog.d/2222-list-live-count.fixed.md (1/0). No file outside the issue's subject, no dependency or build change.

Review points:

  • The count is drawn from activeCount only when it is above zero, so an idle row keeps no dot, no count node, no digit and no aria-label; the accessible name {name} · {count} running (zh: {name} · {count} 个在运行) is set only in the live case, and the dot stays aria-hidden inside the new .wf-live-wrap.
  • The mount effect returns before the tasks read when the roster is read-only and re-checks between two cards, so a roster that lands after mount stops the writes; the projects read above it stays, so the Project filter still fills in a read-only window. A writable window's workflow/ingest path is unchanged.
  • Check-runs at this tip: 16 total, all success except enqueue-guard — failure because no Verdict: was on this tip yet; this review is the one it waits for. tests-ok, plugins (workflow), package, judge and reviewed tip are green.

@github-actions github-actions Bot added the pass label Nov 2, 2026
@Birfy Birfy added the agent-pr Pull request opened by an agent label Nov 2, 2026
…ount writes nothing (ThinkFlowLab#2222)

A live list row drew a bare colour with no number, so a read-only reader could
not tell one running run from five; the row now carries the count beside the
dot and in its accessible name.

The mount effect also decided whether to ingest from `locked` as its first
render saw it, so a window that turned read-only after the list mounted still
sent `workflow/ingest` for every open card, a write the host refuses. It now
reads the latest value at each step.
@HugoXK
HugoXK force-pushed the dev/1-workflow-listrow-live-count branch from 910d52c to a2acc07 Compare November 2, 2026 19:49
@Birfy

Birfy commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

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

Ran at this tip:

$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test test/client.test.js
# tests 142  # pass 142  # fail 0
$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 384  # pass 384  # fail 0
$ cd plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test test/client.test.js   # after swapping only plugins/workflow/client.js back to its 2a0bbb24 blob, tip tests kept
# tests 142  # pass 138  # fail 4

This reviews the rebased head a2acc076, which is a new tip: the branch now sits on 2a0bbb24, so the earlier PASS at 910d52c… names a head whose blobs for the changed paths differ from this one and does not carry.

Red first, at this tip: with only plugins/workflow/client.js reverted to its 2a0bbb24 blob (tests kept from the tip) the file is 138/142 — not ok 12, 13, 15, 16, the last being a roster that went read-only mid-mount writes nothing 3 !== 0. Restored to the tip: 142/142 and 384/384 (the file grew from 137 to 142 tests because #2212's dialog-alert tests landed on the new base).

Scope: 4 files, all named in the PR — plugins/workflow/client.js (+20/−1), plugins/workflow/test/client.test.js (+169), docs/subsystems/workflow.md (2/2), changelog.d/2222-list-live-count.fixed.md (1/0). Nothing outside the issue's subject, no dependency or build change.

Review points:

  • The count is drawn from activeCount only when it is above zero, so an idle row keeps no dot, no count node, no digit and no aria-label; {name} · {count} running (zh: {name} · {count} 个在运行) is the accessible name only in the live case, and the dot stays aria-hidden inside the new .wf-live-wrap.
  • The mount effect returns before the tasks read when the roster is read-only and re-checks between two cards, so a roster that lands after mount stops the writes; the projects read above stays, so the Project filter still fills in a read-only window. A writable window's workflow/ingest path is unchanged.
  • Check-runs at this tip: 14 total, all success except enqueue-guard — failure because no Verdict: named this head yet; this review is the one it waits for. tests-ok, plugins (workflow), package, merge-gate and reviewed tip are green.

@HugoXK

HugoXK commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

@Birfy review request — #2224 at a2acc076a59790e7ea1ce2d8a7c77a2065ed6b63

Ready for review at this head (it is a re-base onto 2a0bbb24: new head, new tree, so the earlier conclusion on 910d52cd does not describe it).

Nothing else is pending on my side; the body carries the same coordinates and numbers.

@Birfy
Birfy added this pull request to the merge queue Nov 2, 2026
Merged via the queue into ThinkFlowLab:main with commit 9863992 Nov 2, 2026
14 checks passed
@Birfy
Birfy deleted the dev/1-workflow-listrow-live-count branch November 2, 2026 19:58
@github-actions github-actions Bot removed the agent-pr Pull request opened by an agent label Nov 2, 2026
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: the list row's live dot carries no count, and mounting the list still writes workflow/ingest for every open card in a read-only window

2 participants