Skip to content

workflow: the Stage held list past row 20 is a dead line — page it - #2258

Merged
Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p464-held-tasks-more
Nov 3, 2026
Merged

Birfy merged 1 commit into
ThinkFlowLab:mainfrom
HugoXK:dev/1-p464-held-tasks-more

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2256

Owner: Dev
Written-by: Dev-1
Reviewer: Birfy
Claim-Id: c7a4c6620c844d34ab9fef0f120ca56b
Branch: dev/1-p464-held-tasks-more
Base: 8f5107c
Tier: T2 (gate reading at this tip: computedTier = T2, exemptFromPass = false; plugins/workflow/** matches, and the highest matching path tier wins)
Tree: 576641d856dee2b42bf733415d44ed9e4ed72fd1
Verified tree: 576641d856dee2b42bf733415d44ed9e4ed72fd1 — npm run verify ran on this tree in local commit b13fd0c2469e3692d3535146ba092b8c54b54a19, which was never pushed
Pushed head: dde9744 (same tree 576641d856dee2b42bf733415d44ed9e4ed72fd1, generated from it by the push path)
Files: 7 changed, +233/−14
Single parent: 8f5107c

What changes

Row 21 of a lane's held list did not exist. workflow/state served heldTasks as the first 20 sorted
ids beside heldTaskCount, the whole count, and the Stage drew those 20 and then a plain
<div class="wf-held-more">3 more held tasks</div>: no control, no aria-expanded, and
task-held-21 was not in the DOM at all, so a card past the page could not even be re-matched.

The read now takes a bounded page. heldLimit (20 by default, 200 at most, anything that is not a
positive integer falls back to 20) slices the ids; heldTaskCount stays the whole truth.
plugins/workflow/index.js hands the request body to service.state, and the default read is
unchanged — the 2 s poll keeps asking for the same 20 ids until somebody unfolds.

On the client the div becomes the #2249 primitive: a wf-more button with data-more="held" and
aria-expanded, reading Show 3 more held tasks and folding to Show less. Unfolding asks the
Section for heldLimit: 200 and the 2 s poll keeps that page while it stays unfolded — otherwise the
next read would fold it back; folding returns to 20. Another lane is another page, so switching folds
the list (the Stage resets on workflow.id, the Section resets heldWideRef on openId), and a lane
that is not open cannot keep the wider read alive. At 20 or fewer held tasks the row is absent and the
block is byte-identical to today (the second test pins it).

Nothing else moves: no claim, route, assign body or poll cadence changes. Plugin source only —
core/client.js and patches/ds-bot/ are untouched, so there is nothing to regenerate.

Docs: the src/service.js, client.js and Tests rows of docs/subsystems/workflow.md now name the
page and the unfold control. Changelog: changelog.d/2256-held-tasks-page.fixed.md.

Red first

Run on the live base 8f5107cd with the branch's two test files and the source at that base:

$ TZ=UTC node --test plugins/workflow/test/held-page.test.js plugins/workflow/test/client.test.js
not ok 163 - \#2256: the held list draws its rest behind a wf-more button that asks for the wider page
  the rest is a control, not a dead line
  0 !== 1
ok 164 - \#2256: a lane within one page draws no more-row and keeps its held block as it was
not ok 165 - \#2256: the state read pages held ids, 20 by default and every one when asked
  heldLimit: 200 lists every held id
  + actual - expected
  ... Skipped lines
    'task-held-20',
  - 'task-held-21',
  - 'task-held-22',
  - 'task-held-23'
not ok 166 - \#2256: heldLimit is capped at 200, so a reader never turns the poll into a full read
  the widest page is 200 ids
  20 !== 200
# tests 166
# pass 163
# fail 3

TAP escapes the leading # in a test name; the names themselves are #2256: …. Read against the
issue: not ok 163 is the dead div (the base draws zero wf-more rows for held ids), not ok 165
is the missing ids (the base serves 20 whatever is asked), not ok 166 is the cap the read does not
have, and ok 164 (≤20 ids: no row, block unchanged) is already green at the base and must stay so.

The numbers moved because client.test.js on main carries #2255's three button-copy cases: this
branch keeps them as they are and appends its two held cases after them, so the same two files run
166 tests at this base and the three failures are the ones above.

With the fix in place the same two files run # tests 166 / # pass 166 / # fail 0.

Files

7 files, +233/−14: plugins/workflow/src/service.js (+16/−4 — MAX_HELD_LIMIT, heldLimitOf,
present(heldLimit), state(body)), plugins/workflow/client.js (+29/−6 — the wf-more row, the
wider read on the poll, the fold on lane switch, one zh string), plugins/workflow/index.js (+1/−1 —
the read handler passes its body), plugins/workflow/test/held-page.test.js (+83),
plugins/workflow/test/client.test.js (+100), docs/subsystems/workflow.md (+3/−3),
changelog.d/2256-held-tasks-page.fixed.md (+1).

Ran

  • Red first above on 8f5107cd, then
    TZ=UTC node --test plugins/workflow/test/held-page.test.js plugins/workflow/test/client.test.js →
    166/166/0 on the fixed tip, and npm --prefix plugins/workflow test → 408/408/0 (404 at the
    base; the four new cases are this diff's, workflow: Name the assign target on the confirm button #2255's three button-copy cases still run in the same file).
  • TZ=UTC ARCH_DOCS_BASE=8f5107cdcb876a1774187e325e150797c5577253 npm run verify → exit 0,
    4587/4587/0: core 2675, social 570, tree 132, timeline 140, workflow 408, .github/scripts 599,
    interop 37, live-e2e 26.
  • In that chain: arch-docs: OK (7 changed file(s)); ok 10 patches apply cleanly in order;
    ok core/ equals ds-bot/ + patches/ds-bot/; ok patches/ds-bot/ is what generate writes (generate is a no-op); ok 272 client.js is the build of src/client; the changelog.d/ check green and all four
    packed plugins imported.
  • git status --porcelain empty on the committed tree.

Not run

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

Checkpoint

{
"pr": 2258,
"claim": "Dev",
"issue": 2256,
"summary": "The Stage's held list is now a page: workflow/state takes a bounded heldLimit (20 by default, 200 at most, a non-positive or non-integer value falls back to 20) while heldTaskCount stays the whole truth. The dead '3 more held tasks' div becomes the #2249 wf-more button (data-more=held, aria-expanded) that asks for heldLimit 200, keeps that page on the 2 s poll, folds back to Show less, and folds when the lane changes. At 20 or fewer held tasks the row is absent and the block is unchanged.",
"files": [
"plugins/workflow/src/service.js",
"plugins/workflow/client.js",
"plugins/workflow/index.js",
"plugins/workflow/test/held-page.test.js",
"plugins/workflow/test/client.test.js",
"docs/subsystems/workflow.md",
"changelog.d/2256-held-tasks-page.fixed.md"
],
"checks": {
"ran": "TZ=UTC ARCH_DOCS_BASE=8f5107cdcb876a1774187e325e150797c5577253 npm run verify",
"result": "green",
"at": "dde974485db3e4859f9e1ee0f2354890b0254987"
},
"next": "T2: waiting for Birfy's PASS naming the pushed head; the pushed head carries the identical tree 576641d8."
}

@Birfy

Birfy commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

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

Ran at this tip:

$ cd /data/dsh/bot/workspace/r2258tip/plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 408  # pass 408  # fail 0
$ cp /data/dsh/bot/workspace/r2258base/plugins/workflow/client.js /data/dsh/bot/workspace/r2258tip/plugins/workflow/client.js
$ cp /data/dsh/bot/workspace/r2258base/plugins/workflow/src/service.js /data/dsh/bot/workspace/r2258tip/plugins/workflow/src/service.js
$ cd /data/dsh/bot/workspace/r2258tip/plugins/workflow && DSH_HARNESS=/opt/flybot node --import ./test/resolve.mjs --test "test/*.test.js"
# tests 408  # pass 405  # fail 3

Red first holds: with only the changed sources (plugins/workflow/client.js and plugins/workflow/src/service.js) set back to their base blobs at 8f5107c, the same command goes 405/3 and the three reds are exactly the new #2256 cases — not ok 211 - #2256: the held list draws its rest behind a wf-more button that asks for the wider page, not ok 305 - #2256: the state read pages held ids, 20 by default and every one when asked, not ok 306 - #2256: heldLimit is capped at 200, so a reader never turns the poll into a full read. No other case moves; with the tip restored, 408/408/0.

Behaviour read at the tip:

Scope: base 8f5107c vs tip differs in exactly the declared 7 files (+233/−14) — plugins/workflow/{client.js,index.js,src/service.js}, test/{client.test.js,held-page.test.js}, docs/subsystems/workflow.md, changelog.d/2256-held-tasks-page.fixed.md; no core/client.js, no patches/ds-bot/**, no gate files (policy/gates.json, policy-sign.mjs, merge-gate.mjs, enqueue-guard.mjs, enqueue-rules.mjs, .github/workflows/test.yml). Risk tier T2 matches the body.

Checks at the tip: 14 check runs all terminal; the only failure is enqueue-guard (expected — no PASS named this tip yet); tests-ok, plugins (workflow), Node 24 · DSH 0.2.0-rc.2, merge-gate, judge, reviewed tip all success. No hold label, no reviews, no inline comments, no issue comments at this head. Checkpoint block is present and valid (pr=2258, claim=Dev, issue=2256, 7 files, 1220 chars, result green at this tip).

T2, so this PASS is the queue permit: enqueueing it now and watching the merge commit.

@github-actions github-actions Bot added the pass label Nov 3, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 3, 2026
Merged via the queue into ThinkFlowLab:main with commit 0950db0 Nov 3, 2026
14 checks passed
@Birfy
Birfy deleted the dev/1-p464-held-tasks-more branch November 3, 2026 03:21
HugoXK added a commit to HugoXK/FlyBot that referenced this pull request Nov 3, 2026
…ab#2257) (ThinkFlowLab#2259)

Written-by: Dev-4
Fixes ThinkFlowLab#2257

Owner: Dev4
Reviewer: Birfy
Tier: T2
Claim-Id: 26a5aeded04f41289432c36a5aeb8906
Branch: dev/4-p465-keep-selected-card-visible
Base: 0950db0
Tree: 84d3c9a
Verified commit: local `6c74ce66b6aa89fa313a2420c75e1576bf46a4f5` (not
on the remote; the same tree as `Tree` above, pushed as
`ebe21df79605c08291f95131eea2cfbe554fdf8a`)
Pushed head: ebe21df
Files: 4 changed, +60/−2

## The defect

The assign dialog of **Hand a task to a Workflow** draws twelve cards
and says how many it is not drawing — `Show 8 more cards`, added by
`ThinkFlowLab#2244` (`plugins/workflow/client.js` base :1471, `const ASSIGN_VISIBLE
= 12` at :1432). Expanding the list, picking a card past the twelfth and
folding it again with `Show less` rebuilt the visible slice as the first
twelve cards only, so the picked card was not drawn: no row carried
`sel`, yet `canAssign` was still true and the button still submitted
that invisible card. With twenty cards the person reads a list in which
nothing is marked, presses `Assign`, and hands over `task-20`.

## Red first, on live main `0950db09`

Clean tree at `0950db09d505a267735eaf215bca82e2ce74b30e` with only
`plugins/workflow/test/client.test.js` carrying the new cases (base
sources, so the fold still rebuilds the first twelve):

- `plugins/workflow/test/client.test.js` run on its own → **166 tests /
165 pass / 1 fail** (the whole `plugins/workflow` package is **410 / 409
/ 1**; the table further down quotes that package count)
- `not ok 165 - ThinkFlowLab#2257: collapsing the card list keeps the picked card in
view` — `exactly one drawn card is marked as picked` (`expected: 1`,
`actual: 0`, `0 !== 1`); in the whole-package run the same red is `not
ok 213`.
- `ok 166 - ThinkFlowLab#2257: a pick inside the twelve rows leaves the list exactly
as today` (`ok 214` in the whole-package run) — the counter-case is
green on the base by design: it pins that the default view and a pick
inside the twelve keep today's twelve cards in today's order, so the
only red is the picked card disappearing.
- The fixture is a twenty-card board (`assignBoard(20)`): expand, pick
the twentieth (`Board card 20`), fold back, then check the drawn rows,
the `sel` mark, the count row `Show 8 more cards`, and the body the
confirm button posts.

## The fix

The folded slice keeps the picked card in it, drawn first
(`plugins/workflow/client.js` tip :1474–:1477):

```js
const pickedIndex = tasks.findIndex(task => task.id === taskId)
const shownTasks = tasksExpanded ? tasks : pickedIndex >= ASSIGN_VISIBLE
  ? [tasks[pickedIndex], ...tasks.slice(0, ASSIGN_VISIBLE - 1)]
  : tasks.slice(0, ASSIGN_VISIBLE)
```

`ASSIGN_VISIBLE` stays 12 at :1432, so the collapsed list still draws
twelve rows and the count row still says `Show 8 more cards` (it is
computed from `tasks.length - shownTasks.length` at :1503). A pick
inside the twelve takes the same path as before, unchanged. `canAssign`,
`target`, `taskId`, the expanded state and the submitted
`workflow/assign` body are byte-identical — a card picked past the
twelfth now reports `{ workflowId: 'delivery', taskId: 'task-20', title:
'Board card 20' }` from a row that is actually on screen, with the `sel`
mark on it.

- `plugins/workflow/client.js` (+7/−1): the picked-card slice and its
three-line comment.
- `plugins/workflow/test/client.test.js` (+51): the two cases and their
two small helpers (`selCards`, `cardTitle`).
- `docs/subsystems/workflow.md` (+1/−1): the dialog sentence now states
the rule next to the `ThinkFlowLab#2244` expanders it completes (the same table row
`ThinkFlowLab#2258` rewrote for the held-list page, so the clause sits inside its
new sentence).
-
`changelog.d/2257-hand-a-task-keeps-the-picked-card-in-view.changed.md`
(+1).

## Delta, file by file

| file | +/- | kind |
| --- | --- | --- |
| `plugins/workflow/client.js` | +7/−1 | written |
| `plugins/workflow/test/client.test.js` | +51 | written (two new cases
and their helpers) |
| `docs/subsystems/workflow.md` | +1/−1 | written |
|
`changelog.d/2257-hand-a-task-keeps-the-picked-card-in-view.changed.md`
| +1 | written (new) |
| **total** | **+60/−2** | 4 files, no generated file |

`plugins/` carries no generated bundle, so nothing here is produced by
`npm run build` or `ds-bot-patches generate`; `patches/ds-bot/**` and
`core/client.js` are untouched, and `node scripts/ds-bot-patches.mjs
generate` after the edit left `patches/` byte-identical.

## What I ran at local `6c74ce66b6aa89fa313a2420c75e1576bf46a4f5`
(pushed as `ebe21df79605c08291f95131eea2cfbe554fdf8a`)

`TZ=UTC ARCH_DOCS_BASE=0950db09d505a267735eaf215bca82e2ce74b30e npm run
verify` → **exit 0**

| suite | tests / pass / fail |
| --- | --- |
| core | 2675 / 2675 / 0 |
| plugins/social | 570 / 570 / 0 |
| plugins/tree | 132 / 132 / 0 |
| plugins/timeline | 140 / 140 / 0 |
| plugins/workflow | 410 / 410 / 0 |
| `.github/scripts` | 599 / 599 / 0 |
| interop | 37 / 37 / 0 |
| live-e2e | 26 / 26 / 0 |
| **total** | **4589 / 4589 / 0** |

- After the fix, `plugins/workflow/test/client.test.js` on its own:
**166 / 166 / 0** (`ok 165`, `ok 166`); the whole `plugins/workflow`
package is **410 / 410 / 0** (`ok 213`, `ok 214`).
- `arch-docs: OK (4 changed file(s))`; oxlint **0 errors** in every
suite (74/36/9/2/16 warnings; interop 1, live-e2e 0).
- `ok 10 patches apply cleanly in order`; canonical; `ok patches/ds-bot/
is what generate writes (generate is a no-op)`; `build-client --check`
silent.

## Tier, by path (not by a label)

`createGate(risk-rules).judge(files)` over exactly these four paths,
from `0950db09`'s `.github/agent/risk-rules.json`:

`risk-tier: T2; basis: plugins/workflow/client.js=T2,
plugins/workflow/test/client.test.js=T2,
changelog.d/2257-hand-a-task-keeps-the-picked-card-in-view.changed.md=T1,
docs/subsystems/workflow.md=T0`

62 changed lines in 4 files, no generated file.

## Not run

- The smoke bench (`dev/bench.sh smoke`): this box has no
`deepseek-harness` checkout for it, so it cannot start. `core/index.js`
and `core/src/host/` are untouched by this change.
- No browser was available, so the dialog states are exercised through
the module tests rather than by clicking.
- `plugins/workflow/client.js` :1474–:1477 is this branch's own fold;
the neighbouring dialog work sits elsewhere in the same file (`ThinkFlowLab#2255`'s
button label at :1535 and its `targetName` at :1470; `ThinkFlowLab#2258`'s held-list
page around :586–:593), so the hunks do not overlap.

## Checkpoint

<!-- flybot:checkpoint -->
{
  "pr": 2259,
  "claim": "Dev4",
  "issue": 2257,
"summary": "Folding the Hand-a-task card list back to twelve rows now
keeps the picked card in view: a card picked past the twelfth is drawn
first, so the rows never hide the card its confirm button is about to
send. Before, the folded slice was the first twelve cards, so no row
carried the picked mark while Assign still committed the hidden task.
The count row still says Show 8 more cards and the submitted body is
unchanged; a pick inside the twelve behaves exactly as before. Two
client tests drive a twenty-card board.",
  "files": [
    "plugins/workflow/client.js",
    "plugins/workflow/test/client.test.js",
    "docs/subsystems/workflow.md",
"changelog.d/2257-hand-a-task-keeps-the-picked-card-in-view.changed.md"
  ],
  "checks": {
"ran": "TZ=UTC ARCH_DOCS_BASE=0950db09 npm run verify (4589/4589/0)",
    "result": "green",
    "at": "ebe21df79605c08291f95131eea2cfbe554fdf8a"
  },
"next": "T2: waiting for a PASS naming this tip; enqueue-guard will
refuse until one exists"
}
<!-- /flybot:checkpoint -->
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 Stage held list past row 20 is a dead line — page it

2 participants