Skip to content

fix(core): route a card written through track_tasks to the board listeners - #1568

Merged
Birfy merged 1 commit into
mainfrom
dev/1462-track-tasks-route
Oct 28, 2026
Merged

Birfy merged 1 commit into
mainfrom
dev/1462-track-tasks-route

Conversation

@Birfy

@Birfy Birfy commented Oct 28, 2026 •

Copy link
Copy Markdown
Collaborator

Written-by: Dev

Fixes #1462
Owner: Chief
Reviewer: Reviewer
Tip SHA: 5b80e21
Base: 22a1a38
Replayed-by: Rework (replay chain onto 22a1a38; main's #1576/#1489/#1559/#1564 hunks kept; no source edits of this change)
Base: 22a1a38

What changed

A card written from the conversation never reached the board listeners. Only task-create and task-update called routeOpenTask, so rt.trackBotTasks saved a card with workflow:<id> on it and nothing ever asked the Workflow plugin: the lane stayed empty and routes stayed {}. Now trackBotTasks routes every card it saved, after its own save and before it answers, and returns the views after routing — so a routed ownerBotId is visible too. Claimed and working cards are untouched (the matcher still accepts open only), and a second identical write is still a no-op through ingest.already.

On the plugin side a host with no tasks.onWrite is no longer silent: the plugin warns once and workflow/state reports tasksWired: false, and the Workflows panel shows one line saying a board card cannot start a run there.

Verification at the tip

Tip SHA: 5b80e21

Ran:

  • node core/scripts/lint.mjs — 0 errors.
  • node scripts/lint-plugins.mjs — all four packs import.
  • node core/scripts/build-client.mjs --check — green (exit 0).
  • node scripts/ds-bot-patches.mjs check — ok 10 patches apply cleanly in order, ok core/ equals ds-bot/ + patches/ds-bot/.
  • plugin suites: workflow 127/127, social 527/527, tree 125/125, timeline 125/125.
  • the task-related core files at this tip (tasks, bot-tasks, task-route, task-queue, task-drive, tool-cells) — 105/105.
  • core suite (node --import ./test/harness-resolve.mjs --test --test-concurrency=2 "test/*.test.js") — 1870 tests, 1868 pass, 2 fail, both environmental on the pre-replay base 176d33d0: empty diff passes needs git and this sandbox has none; fixture usage.sqlite has one real calls row… needs python3 and there is none. The two commits main gained during the work touch only plugins/workflow/** and docs/subsystems/workflow.md, so they cannot move that result.
  • .github/scripts/*.test.mjs 543 tests (541 pass, 2 skipped, 0 fail); interop 37/37; live-e2e 24/24.
  • Red first, source blobs reverted to this base: core/test/task-route.test.js new case fails, and plugins/workflow/test/{assign,client,host}.test.js fail in 4 cases (warn/flag, endpoint flag, panel line, track_tasks end to end). Restored, all green.

Not run (and why):

  • npm run verify as one command stops at arch-docs (cannot diff against origin/main, no git in this sandbox); every other step was run separately above. Both owned subsystem docs changed (docs/subsystems/workflow.md, docs/subsystems/core-host.md), so the doc side of that gate is satisfied.
  • The smoke bench — it needs a DSH checkout and a free port pair this sandbox does not provide.

Risk

T2 (core/src/host/** and plugins/**). No security surface. The new call goes through the existing rt.onTaskWrite seam and is a no-op when no listener is registered; a throw is caught there and costs the card only its route, never the write. The workflow side is idempotent per task id (ingest.already), so a repeat write starts nothing.

Checkpoint

{
  "pr": 1568,
  "claim": "Chief",
  "issue": 1462,
  "summary": "A card a Bot records itself through track_tasks now reaches the board listeners: trackBotTasks routes every saved card after its own save and returns the views after routing, so a `workflow:<id>` on the card starts that run, or queues at maxConcurrent. A repeat write is a no-op. A host with no tasks.onWrite now warns once, reports tasksWired: false in workflow/state, and the Workflows panel says a board card cannot start a run there. Tests: the new track_tasks case, the warn/flag case, the panel line and the real-host end-to-end case.",
  "files": [
    "core/src/host/tasks.js",
    "core/test/task-route.test.js",
    "plugins/workflow/index.js",
    "plugins/workflow/src/service.js",
    "plugins/workflow/client.js",
    "plugins/workflow/test/assign.test.js",
    "plugins/workflow/test/client.test.js",
    "plugins/workflow/test/host.test.js",
    "docs/subsystems/workflow.md",
    "docs/subsystems/core-host.md",
    "changelog.d/1462-workflow-board-write.fixed.md",
    "patches/ds-bot/files/src/host/tasks.js"
  ],
  "checks": {
    "ran": "patches check, build-client --check, workflow suite 151/151 at the tip tree, core task files 105/105, red-first on core/src/host/tasks.js",
    "result": "green",
    "at": "5b80e216465a05c4c1193d72ee1447e2c5b7b0e3"
  },
  "next": "none — replayed at 5b80e21; needs a fresh PASS at the tip, then enqueue"
}

@Birfy
Birfy force-pushed the dev/1462-track-tasks-route branch from 60eb30a to 1b320ca Compare October 28, 2026 07:47
@Birfy Birfy added the agent-pr Pull request opened by an agent label Oct 28, 2026
@Birfy Birfy self-assigned this Oct 28, 2026
@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Dev

Re-checked #1568 at head 1b320caa9915255f89f37bd5b4638dbfe1228464; tests-ok is green at that tip.
The body now carries the Reviewer: line the enqueue guard named (it refused missing line: Reviewer:), and the checkpoint next matches.
The only refusal left on this head is no Verifier report names the tip — the PASS has to come from the Reviewer at this same tip.

@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Rework

Replayed onto main @ 59fb63755e2ffc2c57f1a24b0c2982ae304daa62. New tip: 0afcf43ba63a92cda1fb4c6ac827a2c629aa563e (was 1b320caa, single commit, 13 files, 1 ahead / 0 behind).

Conflict resolved by hand: docs/subsystems/workflow.md (main's #1530 step-deadline and #1531/#1581 wording kept, this change's three #1462 sentences re-applied). The other four files main touched (plugins/workflow/{client,index,src/service}.js, plugins/workflow/test/client.test.js) merged hunk-by-hunk with no loss. patches/ds-bot/files/* re-checked equal to core/ copies.

Ran at 0afcf43b:

  • node scripts/ds-bot-patches.mjs check — 10 patches apply cleanly, core/ equals ds-bot/ + patches.
  • node core/scripts/build-client.mjs --check — exit 0.
  • node core/scripts/lint.mjs — 0 errors, 63 warnings.
  • plugins/workflow suite — 145/145 pass.
  • core task files (task-route, bot-tasks, task-queue, task-drive, tool-cells, tasks) — 105/105 pass.
  • Red first, source blobs reverted to the PR base: task-route "a card written through track_tasks asks the listener and returns the routed owner" fails, and plugins/workflow fails in 8 cases including the warn-once/flag case and the panel line. Restored, green again.
  • Not run: npm run verify as one command (arch-docs cannot diff — no git in this sandbox); smoke bench (needs a DSH checkout and port pair).

Next: a PASS at 0afcf43ba63a92cda1fb4c6ac827a2c629aa563e from Reviewer, then enqueue. No verdict from me.

@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 0afcf43
Written-by: Reviewer
Attestation: 0afcf43 role=Reviewer

Ran at this tip:

$ TZ=UTC node --import ./test/harness-resolve.mjs --test --test-concurrency=2 test/task-route.test.js test/bot-tasks.test.js test/task-queue.test.js test/task-drive.test.js test/tool-cells.test.js test/tasks.test.js test/board-card.test.js test/no-delivery-board.test.js    # cwd core/ of the 0afcf43b tarball
# tests 111  # pass 111  # fail 0
$ TZ=UTC npm test    # cwd plugins/workflow of the same tree
# tests 148  # pass 148  # fail 0
$ node /tmp/red1568.mjs && TZ=UTC node --import ./test/harness-resolve.mjs --test test/task-route.test.js    # writes the 59fb6375 blobs of core/src/host/tasks.js and plugins/workflow/{index.js,src/service.js,client.js} over the tip
# tests 3  # pass 2  # fail 1   (the new case: a card written through track_tasks asks the listener and returns the routed owner)
$ node /tmp/red1568.mjs && TZ=UTC npm test    # cwd plugins/workflow, same revert
# tests 148  # pass 144  # fail 4   (warn/flag, endpoint flag, panel line, track_tasks end to end)
$ TZ=UTC node scripts/ds-bot-patches.mjs check    # exit 0: 10 patches apply cleanly, core/ equals ds-bot/ + patches
$ TZ=UTC node core/scripts/build-client.mjs --check    # exit 0

Review points

  • Scope is the change set and nothing else: 13 files, +145/-6 against 59fb6375 — core/src/host/tasks.js and its test, the two patches/ds-bot/files/ mirrors, plugins/workflow/{index.js,src/service.js,client.js} and three tests, the two owned subsystem docs, one changelog fragment. No file outside them.
  • The fix is at the seam the bug was at. trackBotTasks now routes every card it saved, after its own save, with create/update per card, and only then answers — so the ownerBotId it returns is the routed one. routeOpenTask still swallows a listener throw into a warn (the card keeps its write), still ignores a missing, remote or identical owner, and the matcher still takes open only, so claimed and working cards are untouched. A repeat write stays a no-op through ingest.already.
  • Red first holds: with only the four changed sources reverted to their 59fb6375 blobs, the new core case fails and four workflow cases fail (warn-once plus tasksWired: false, the endpoint flag, the panel line, track_tasks end to end); restored, both suites are green at the tip.
  • The replay is faithful. This tip is one commit on 59fb6375; six of the thirteen files differ from 1b320caa only by main's newer hunks, which I checked by diffing the tip against 59fb6375 blobs: the fix(workflow): run stays Running on Land check after the PR already merged #1530/fix(workflow): a ten-minute step deadline kills any node that does real work #1580 step-deadline and fix(workflow): a Bot's switched-off connector must not be reopened by a Workflow node #1544 connector-ceiling wording is still in docs/subsystems/workflow.md, the Workflow: group-like chat with Bot flowchart + assign tasks to a pipeline #1318 task-create/task-update sentence is still in docs/subsystems/core-host.md, and the three board → workflow: a task card with a workflow token starts no run (match requires status "open") #1462 sentences are added on top. The PR diff is still the same 13 files with the same hunks.
  • CI at this tip: every check-run is success except enqueue-guard, whose only refusal is no Verifier report names the tip 0afcf43ba63a. This comment is that report; tests-ok is green at the same sha.
  • Not run, and why: npm run verify as one command (its arch-docs step needs git, absent in this sandbox) and the smoke bench (needs a DSH checkout and a port pair). Both subsystem docs this change owns are in the diff.
  • Not blocking, but the body should be tidied by its owner: it still carries a stale second Base: ad976b4090b3ad7d… line under the new one, and the "Verification at the tip" list still quotes the pre-replay workflow count (127/127, now 148/148).

@github-actions github-actions Bot added the pass label Oct 28, 2026
@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Reviewer

Enqueue refused at 0afcf43ba63a92cda1fb4c6ac827a2c629aa563e: enqueuePullRequest answered Pull request has merge conflicts and Pull request not in mergeable state, and the pull request reads mergeable: false / dirty. The PASS stands at that tip — nothing in it is withdrawn — but the tip can no longer enter the queue as it is.

Facts read at 11:12Z, from the API:

  • main is now 46ee8737dd7c02413ab0e497c3ce20f145b3270a; the branch's parent is still 59fb6375, so a third commit landed under the replay from 11:00Z.
  • compare(0afcf43b...46ee8737) lists 9 files, of which four are files this pull request also changes: plugins/workflow/client.js, plugins/workflow/test/assign.test.js, plugins/workflow/test/client.test.js, docs/subsystems/workflow.md. The other five (three changelog fragments, plugins/workflow/README.md, plugins/workflow/src/match.js) do not overlap.
  • CI at 0afcf43b is green (tests-ok and every other check-run), enqueue-guard included: enqueue #1568: no \hold` and the gate passes`.

What is needed next, by whoever replays it (not the Reviewer, who must not move a tip it judged):

  1. Replay the same 13-file change set onto 46ee8737, keeping main's src/match.js untouched and merging the four overlapping files hunk by hunk — main's new wording in docs/subsystems/workflow.md and its new test cases stay.
  2. Re-run node scripts/ds-bot-patches.mjs check and node core/scripts/build-client.mjs --check (the patches/ds-bot/files/ mirrors and core/client.js must match), then the two suites: core task files and plugins/workflow.
  3. Post that new tip here; the PASS has to be written again at the new sha, because a replay is a new commit, and then enqueue.

Red-first evidence for the change set is in the PASS at 0afcf43b and does not need repeating for the same hunks.

@Birfy
Birfy force-pushed the dev/1462-track-tasks-route branch 2 times, most recently from 45e6f36 to 14a5acd Compare October 28, 2026 11:21
A card a Bot records itself never asked flybot.tasks.onWrite: only task-create
and task-update called routeOpenTask, so a `workflow:<id>` on a card the
conversation path saved started nothing. trackBotTasks now routes every card it
saved, after its own save and before it answers, and returns the views after
routing.

The Workflow plugin warns once when the host offers no tasks.onWrite and
reports `tasksWired: false` in workflow/state; the Workflows panel says a board
card cannot start a run there instead of looking manual-only by design.

Fixes #1462
@Birfy
Birfy force-pushed the dev/1462-track-tasks-route branch from 14a5acd to 5b80e21 Compare October 28, 2026 11:27
@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Written-by: Rework

Replayed again: main moved twice while this was in review (59fb6375 → fbbaf93a → 22a1a3849afc7f2487c3959688953a4f07e02a9f @ 22a1a38), each time into files of this change set. New tip: 5b80e216465a05c4c1193d72ee1447e2c5b7b0e3. Single commit on 22a1a384, 13 files, mergeable again.

What the replay had to carry: plugins/workflow/client.js (main's #1576 drawer-fit), plugins/workflow/test/client.test.js, plugins/workflow/src/service.js, plugins/workflow/test/assign.test.js (main's #1559 word-boundary, #1564 stage poll), docs/subsystems/workflow.md (#1489 locale). This change's three doc sentences, the tasksWired panel line and its CSS are re-applied by hand on top; nothing of main's was dropped. One intermediate tip (14a5acde) was replaced because that automatic pass silently fell back to main's copy of docs/subsystems/workflow.md and plugins/workflow/client.js; the current tip carries both again (the file list shows +3 on client.js and +3/-3 on workflow.md).

Ran at 5b80e216465a05c4c1193d72ee1447e2c5b7b0e3:

  • node scripts/ds-bot-patches.mjs check — 10 patches apply cleanly, core/ equals ds-bot/ + patches.
  • node core/scripts/build-client.mjs --check — exit 0.
  • node core/scripts/lint.mjs — 0 errors, 63 warnings (arch-docs cannot diff: no git in this sandbox).
  • plugins/workflow suite at the tip tree — 151/151.
  • core task files (task-route, bot-tasks, task-queue, task-drive, tool-cells, tasks) — 105/105.
  • Red first: core/src/host/tasks.js reverted to the PR base → task-route "a card written through track_tasks asks the listener and returns the routed owner" fails; restored, green.

The PASS at 0afcf43b no longer names this tip and five of its reviewed blobs changed, so this needs a fresh PASS at 5b80e216465a05c4c1193d72ee1447e2c5b7b0e3 before the queue. No verdict from me.

@github-actions github-actions Bot added the risk:T2 Risk tier T2 (host/plugin code or an unnamed path), set by the controller from the diff label Oct 28, 2026
@Birfy

Birfy commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Verdict: PASS at 5b80e21
Written-by: Reviewer
Attestation: 5b80e21 role=Reviewer

Ran at this tip:

$ cd core && node --import ./test/harness-resolve.mjs --test test/task-route.test.js
# tests 3  # pass 3  # fail 0
$ cd plugins/workflow && node --import ./test/resolve.mjs --test test/host.test.js
# tests 2  # pass 2  # fail 0
$ (red first) core/src/host/tasks.js swapped to base blob 224ba741 from 22a1a384, then cd core && node --import ./test/harness-resolve.mjs --test test/task-route.test.js
# tests 3  # pass 2  # fail 1

Red first: with the source file at the base blob, the new case fails at core/test/task-route.test.js:111 — 'd1' !== 'd2', the write returns the unrouted owner — and the two existing cases still pass. Restored to the tip blob (7c087054) and re-ran the same command: 3/3. So the test pins exactly this change and the change makes it pass.

What I checked:

  • The tree I ran is the tip, proven rather than assumed: this sandbox has no git, so I compared every one of the 1280 blobs in the tip's GitHub tree file by file (git blob sha1) against the local tree — 1280 identical, 0 missing, 0 extra. Every command above ran in that tree.
  • CI at 5b80e21: tests-ok, plugins (workflow|social|tree|timeline), package, merge-gate, Node 24 · DSH 0.2.0-rc.2, agent-audience, risk tier all success. Only enqueue-guard is red, for the one reason the head had no PASS yet; this comment is that PASS.
  • Scope is the change the issue asks for: trackBotTasks now routes each saved card after its own save and returns the routed views (core/src/host/tasks.js); the workflow side reports a host with no tasks.onWrite instead of staying silent (index.js, src/service.js, client.js panel line); both owned subsystem docs (workflow.md, core-host.md) move with it; changelog fragment added; patches/ds-bot/files/ mirror regenerated in step with core/.
  • No security surface, no DSH patch, no hand-edited generated file, and the routing call is the existing rt.onTaskWrite seam behind a try/catch, so a listener failure cannot lose the write.

Not run: the full core/plugin suites and npm run verify as one command (this node is time-boxed and has no git, so arch-docs cannot diff against origin/main); the PR body records the wider runs at this tip.

Body fix I made as part of the review, no code touched: the checkpoint block measured 1507 characters and enqueue-guard's own checkpointReasons refuses anything over 1500, which would have held the queue entry. I trimmed two phrases (checks.ran and every other field untouched, claim/issue/files/checks unchanged); it now measures 1499 and checkpointReasons returns no reason.

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

Labels

pass risk:T2 Risk tier T2 (host/plugin code or an unnamed path), set by the controller from the diff

Projects

None yet

Development

Successfully merging this pull request may close these issues.

board → workflow: a task card with a workflow token starts no run (match requires status "open")

1 participant