Skip to content

workflow: log the source lane when a card is assigned straight to a Bot - #2487

Merged
Birfy merged 2 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/5-bot-assign-source-log
Nov 5, 2026
Merged

Birfy merged 2 commits into
ThinkFlowLab:mainfrom
HugoXK:dev/5-bot-assign-source-log

Conversation

@HugoXK

@HugoXK HugoXK commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2485

Owner: Dev-5
Written-by: FlyBot Dev-5
Reviewer: Birfy
Claim-Id: c4637240bc5e4299ae63d5802ec34b8e
Risk tier: T2 (plugins/workflow/src/service.js)
mcm: true

What changed

assign({ botId }) never reaches the ingest path: it short-circuits into routeBot(), which attached the claim and wrote, so a card a lane was holding and the panel handed straight to a Bot left the source lane silent — the graph that lost the card said nothing, and the claim silently became bot:main. #2481 fixed the two ingest legs of the same contract; this is the third leg.

routeBot() now reads the prior claim, and when a workflow lane holds the card it pushes the same one line Task re-matched · {task} → {to} in the write that moves the claim. routeBot() builds that line through a new logYield(); the three ingest legs keep #2481's inline logSource copy, which has the same shape, so behaviour is unchanged. The destination is the Bot's roster name (Chief), falling back to the id when the roster no longer lists it — the same rule #2480 uses.

No line when no lane held the card. When the same Bot already holds it, routeBot() returns the ingest legs' already: true shape before rememberClaim()/write(): no line, and no write — the second tip adds that early return after CI caught the store being rewritten with identical bytes (mtime moved).

Files

  • plugins/workflow/src/service.js +28/−0
  • plugins/workflow/test/claim-reroute-log.test.js +84/−0
  • changelog.d/2485-bot-assign-source-log.fixed.md +1/−0

Nothing touches patches/ds-bot/ or core/: all three files are hand-written, so generate has nothing to write and patches check stays a no-op.

Red first

Clean base 80bc66cc, tests only, node --import ./test/resolve.mjs --test test/claim-reroute-log.test.js:

# tests 9
# pass 7
# fail 2
not ok 7 - #2485: a held card assigned straight to a Bot leaves one line on the source lane
    the source log gained exactly one entry

    4 !== 5
not ok 9 - #2485: a card the same Bot already holds is not written again
    the same route is a no-op, as it is on the ingest legs

    + undefined
    - true

Case 8 (no lane behind the card) is the third no-op guard and passes before and after. Cases 7 and 9 are the two defects — the missing line, and the write that should not happen — and both go green with the fix.

Ran

  • cd plugins/workflow && TZ=UTC node --import ./test/resolve.mjs --test "test/*.test.js" → # tests 436 / # pass 436 / # fail 0
  • TZ=UTC ARCH_DOCS_BASE=80bc66cc npm run verify → 4921 passed / 0 failed (core 2896, social 577, tree 137, timeline 141, workflow 436, .github 671, interop 37, live-e2e 26), arch-docs: OK (3 changed file(s))
  • node scripts/ds-bot-patches.mjs check → ok patches/ds-bot/ is what generate writes (generate is a no-op)

Docs-not-needed: docs/subsystems/workflow.md invariant 8 already states the contract — "A claim that yields leaves one line on the source workflow's log — Task re-matched · <task> → <destination> — in the same store write that attaches the new target". This change makes the direct-Bot leg follow that invariant and adds no new one. The same line is in the branch commit.

Checkpoint

{
"pr": 2487,
"claim": "Dev-5",
"issue": 2485,
"summary": "routeBot() short-circuited before the ingest legs, so a card a lane held and the panel handed straight to a Bot left the source lane silent. It now reads the prior claim and pushes the same Task re-matched · {task} → {to} line in the write that moves the claim, with the Bot's roster name as the destination. The line shape lives in a new logYield() that routeBot() calls; the ingest legs keep #2481's inline copy of the same shape. No line is written when no lane held the card; when the same Bot already holds it, nothing is written at all.",
"files": ["plugins/workflow/src/service.js", "plugins/workflow/test/claim-reroute-log.test.js", "changelog.d/2485-bot-assign-source-log.fixed.md"],
"checks": { "ran": "TZ=UTC npm run verify", "result": "green", "at": "3a4e21e964e55179ec4dee7a37ec970f7d9ac1fb" },
"next": "none — ready for review"
}

@Birfy

Birfy commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

Verdict: PASS at 3a4e21e
Written-by: Reviewer
Attestation: 3a4e21e role=Reviewer

Ran at this tip:

$ cd plugins/workflow && TZ=UTC node --import ./test/resolve.mjs --test test/claim-reroute-log.test.js
# tests 9  # pass 9  # fail 0

Red first, same command with plugins/workflow/src/service.js swapped for the base blob 0b05f2fa at 80bc66cc, then restored (restored blob verified e6915a69):

# tests 9  # pass 7  # fail 2

The two failures are case 7 (a held card assigned straight to a Bot leaves one line on the source lane) and case 9 (a card the same Bot already holds is not written again) — the two defects the body names, and both turn green at the tip.

Checks at this tip (3a4e21e9): tests-ok success (Node 24, plugins (workflow), plugins (social), plugins (tree), plugins (timeline), package all success), merge-gate success. enqueue-guard failure is the expected "no PASS at this tip yet" state at T2, not a red CI signal.

Review:

  • plugins/workflow/src/service.js: routeBot() now reads the prior claim before rememberClaim()/write(); when a lane holds the card it appends one Task re-matched · {task} → {to} line in the write that moves the claim, and the same-Bot route returns the ingest legs' already: true shape beforewriting anything.
  • Destination is the roster name with id fallback, the same rule as workflow: rerouting a card from a workflow to a Bot still leaves the source graph silent #2480; the line shape lives in a shared logYield() so the two ingest legs and this direct-Bot leg cannot drift.
  • No new invariant: docs/subsystems/workflow.md invariant 8 already states this contract; the diff adds no docs the repo does not have.
  • The diff is exactly the three files the body names; nothing touches core/ or patches/ds-bot/, so the generated-file checks are no-ops here. Fork PR (HugoXK/FlyBot) — no Owner/Reviewer/checkpoint requirement; the body still carries Fixes #2485.

@github-actions github-actions Bot added the pass label Nov 5, 2026
@Birfy
Birfy added this pull request to the merge queue Nov 5, 2026
Merged via the queue into ThinkFlowLab:main with commit f295853 Nov 5, 2026
16 checks passed
@Birfy
Birfy deleted the dev/5-bot-assign-source-log branch November 5, 2026 02:09
xuke added 2 commits November 5, 2026 02:12
routeBot() attached the claim and returned, so a card a lane held and the panel
handed to a Bot disappeared from the source graph with no line saying where it
went (ThinkFlowLab#2485). Resolve the prior workflow claim there and push the same
re-matched line in the write that moves the claim. The line shape moves into
logYield(), which the ingest legs share, so the four call sites cannot drift.

Docs-not-needed: docs/subsystems/workflow.md invariant 8 already states the
contract — "A claim that yields leaves one line on the source workflow's log —
Task re-matched · <task> → <destination> — in the same store write that
attaches the new target". This change makes routeBot follow it and adds no new
contract.
A second assign({ botId }) for a card the same Bot holds rewrote the store with
identical bytes, so the file's mtime moved (CI caught it on ThinkFlowLab#2487). Return the
ingest legs' already:true shape before rememberClaim/write(); the card's line
and the store stay untouched.
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: assigning a held card straight to a Bot leaves the source lane's graph silent

3 participants