chore(kanban): tier-2 batch salvage — doctor, started_at, parent-guard, latest_summary, selects, linked-children (closes #18344 #20022 #19473 #19828 #19743 #20251 #20019)#20448
Merged
Conversation
Widens _verify_created_cards to also accept ids that are children of the completing task in task_links. Previously we only accepted cards where created_by matched the completing task's assignee, which was too strict for legitimate orchestrator flows: a specifier creates a card (so created_by=specifier, not worker), then a worker picks it up and passes parents=[current_task] to kanban_create. The explicit link proves the relationship and should be trusted. Salvaged from #20022 @LeonSGP43 (full PR superseded by #20232 + this patch; the linked-children relaxation was the portable improvement).
After #19473 landed (enforce_max_runtime reads from task_runs.started_at rather than tasks.started_at), a regression test added earlier still only backdated the tasks column. Backdate both so the test is robust regardless of which column the enforcer reads from.
Add parent dependency guard to _set_status_direct so dragging a task to the ready column is rejected (409) when its parents are not all done. Previously the guard only existed in recompute_ready, allowing direct status writes via the dashboard API to bypass the dependency engine. Root cause: after reclaiming stale workers, both T3 and T4 were set to ready via dashboard status writes in quick succession, causing the writer to be spawned while the analyst was blocked — upstream work wasn't done yet.
… show``
The kanban-worker skill (built into the gateway dispatcher's spawn
prompt) instructs every worker to hand off via
``kanban_complete(summary=..., metadata=...)``. That writes the summary
onto the closing ``task_runs`` row, NOT onto ``tasks.result`` — the
latter is left NULL unless the caller passes ``result=`` explicitly.
Result: a glance at the dashboard or ``hermes kanban show <id>`` shows
a blank "Result:" section even when the worker did real work, which
on 2026-05-05 caused a Mac false-alarm ("Hermes did nothing") on a
task that had a 10-line completion summary on its run.
This patch surfaces the latest non-null run summary as
``latest_summary`` so the worker's actual handoff lands in front of
operators.
* New helpers ``kanban_db.latest_summary(conn, task_id)`` and
``kanban_db.latest_summaries(conn, task_ids)``. The batch variant
uses a single window-function SELECT so the dashboard board endpoint
doesn't pay an N+1 cost on multi-hundred-task boards.
* CLI ``hermes kanban show <id>`` prints a "Latest summary:" block
when ``tasks.result`` is empty but a run has produced a summary
(the existing "Result:" section still wins when populated, so the
back-compat path for hand-edited results is untouched). JSON output
gains a top-level ``latest_summary`` field.
* Dashboard ``/board`` and ``/tasks/{id}`` now include a
``latest_summary`` field on every task. Cards on /board carry a
200-character preview (cheap to render, plenty for "what did this
worker do?" at a glance); the drawer/detail endpoint returns the
full summary.
* Five new tests cover: empty-runs case, post-complete surface,
newest-of-multiple selection, empty-string skip, batch with
missing tasks + empty input.
Smoke-tested locally against the live profile DB on the three
acceptance-criterion targets (t_f08fef91 cron-hygiene-audit,
t_007b7f1c EMA-analysis, t_05746fa4 self-assessment) — all three now
return their populated summaries via both ``latest_summary`` and
``latest_summaries``.
Test plan: 255/255 kanban tests pass + 91/91 dashboard plugin tests
pass. No regression on tasks where ``tasks.result`` is explicitly
populated (the existing "Result:" branch is preserved).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced May 6, 2026
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Tier-2 batch salvage of six kanban contributor PRs. All narrow, well-tested, and non-overlapping with each other after careful rebase.
What each commit does
#18344 (@suncokret12) —
fix(doctor): report Kanban worker tools as runtime-gatedhermes doctorto treat thekanban_*worker tools as runtime-gated rather than missing, so users with kanban installed don't see spurious "tool not found" warnings.#20022 (@LeonSGP43) — partial salvage:
fix(kanban): accept created_cards linked as child of completing task_verify_created_cardsto also accept cards explicitly linked as children of the completing task (not just cards wherecreated_bymatches). Matters for orchestrator flows where a specifier creates a card and the worker later adopts it viakanban_create(parents=[current]).test_complete_accepts_cross_worker_card_when_linked_as_childregression test.#19473 (@mioimotoai-lgtm) —
fix(kanban): measure max runtime from current runenforce_max_runtimeread fromtasks.started_at(lifetime timestamp), so a task that timed out once and got re-claimed would immediately re-timeout against its original start time instead of the new run's start time. Infinite-immediate-timeout loop.task_runsand readCOALESCE(r.started_at, t.started_at)so per-attempt elapsed is measured from the active run. Preservestasks.started_atsemantics (first-ever-started).tasks.started_aton every claim.test-fix on top of #19473 —
test(kanban): backdate task_runs.started_at alongside tasks.started_attasks.started_at. After fix(kanban): measure max runtime from current run #19473's JOIN change, the test needs to backdate the run row too. One-line test fix.#19743 (@SimbaKingjoe / daixin1204) —
fix(kanban): prevent child task dispatch when parent is not done_set_status_direct(triggered by drag-to-Ready) didn't check parent dependencies. A user could drag a child task toreadywith an incomplete parent; the dispatcher would then happily spawn it. The guard existed inrecompute_readybut not on direct status writes._set_status_direct, returns 409 if any parent isn't done.#20251 (@Brecht-H) —
feat(kanban): surface task_runs.summary on dashboard cards + kanban showkanban-workerskill teaches workers to hand off viakanban_complete(summary=...), which writes totask_runs.summarybut NOT totasks.result. Result: "Result:" blocks on the dashboard andhermes kanban showwere blank even when workers produced a summary.latest_summaryon cards (200-char preview), drawer (full), andkanban showoutput.kanban_db.latest_summary()+ batchlatest_summaries()(window function, no N+1).hermes@orion.localtoBrecht-H@users.noreply.github.com(same pattern as PR fix(kanban): skip dispatch for tasks assigned to non-profile lanes (salvages #20105, #20134) #20165 / fix(kanban): preserve dashboard completion summaries + add kanban edit (salvages #20016) #20195).#20019 (@LeonSGP43) —
fix(kanban): wire dependency selectsonChangehandlers that didn't thread through the sharedselectChangeHandlerhelper used elsewhere. Selections didn't land on state, so the action buttons stayed disabled.selectChangeHandler(setNewParent/setNewChild)matching the rest of the codebase.Merge conflict resolutions
tests/hermes_cli/test_kanban_core_functionality.py(fix(kanban): measure max runtime from current run #19473): trivial; my test backdated one timestamp column, PR adds a second one to backdate.plugins/kanban/dashboard/plugin_api.py(feat(kanban): surface task_runs.summary on dashboard cards + kanban show #20251): kept both the diagnostics wiring (from earlier-merged feat(kanban): generic diagnostics engine for task distress signals #20332) and the newlatest_summary=full_summarykwarg.tests/plugins/test_kanban_dashboard_plugin.py(fix(kanban): wire dependency selects #20019): kept all three existing tests AND added the new dependency-selects regression test.Validation
Closures
Rebase-merge recommended to preserve per-commit contributor attribution.
Co-authored-by: suncokret12 suncokret@protonmail.com
Co-authored-by: LeonSGP43 cine.dreamer.one@gmail.com
Co-authored-by: 澪 / Mio mio.imoto.ai@gmail.com
Co-authored-by: daixin1204 daixin1204@gmail.com
Co-authored-by: Brecht-H 73849650+Brecht-H@users.noreply.github.com