fix(board): unwrap Hermes kanban task-detail envelope so the drawer renders#1014
Merged
Conversation
…enders
The operator-board task drawer showed a blank/mis-populated detail view
(status defaulting to "triage", no description/assignee, event rows with no
timestamp or payload, empty worker log, bare run rows) while the Hermes
dashboard showed the same task in full.
Root cause is a wire-shape mismatch in the UI normaliser, not the proxy
(hal0-api forwards /tasks/{id} verbatim). The Hermes kanban plugin returns
TWO shapes: GET /board embeds a flat task row per column, but GET /tasks/{id}
WRAPS the row in an envelope `{task, comments, events, attachments, links,
runs}` with snake_case item fields (created_at/payload/outcome/summary) and
epoch-second timestamps. normaliseTask() read scalars off the top level, so
every field under `.task` fell to its default and only the top-level arrays
survived — with the wrong per-item field names.
- normaliseTask: unwrap `raw.task` for scalars; pull comments/events/runs and
`links`→deps off the envelope (falling back to the flat row + already-
normalised camelCase objects the optimistic-update path re-feeds).
- Add normaliseEvent/normaliseRun/normaliseComment + relTime/durLabel helpers:
created_at→at (as "20m ago"), payload→json, outcome/status→state,
started/ended→dur, summary→msg, workspace_path→workspace, created_at→created.
- useBoardTaskLog: accept the `{content: "<text>"}` log shape (split into
per-line entries), not just array/`{entries}` — this is why the worker log
was always "no worker log yet".
- task-drawer: read `t.body` (the normaliser never emitted `t.desc`).
- e2e: add a regression test that drives the drawer with the REAL envelope
wire shape (the existing fixtures encode the flat shape the plugin never
emits, which is why CI stayed green while production was blank).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
thinmintdev
added a commit
that referenced
this pull request
Jul 4, 2026
…ce + logs/memory fixes Bumps version 0.8.3b1 → 0.8.4b1 and folds the [Unreleased] CHANGELOG into the v0.8.4b1 section covering everything since the v0.8.3b1 tag: model preferred profiles, ComfyUI/image-gen model surface, persistent slot context, unified logs/events, memory audit trail + console shape guards (#1024/#1026/#1028/#1030), stack edit drawer (#1023), board kanban drawer (#1014), and optional-cosign installer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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.
Problem
The hal0 operator-board task drawer shows a blank / mis-populated detail view while the Hermes dashboard shows the same task in full. Reported against
t_37a898aa:donetriage(default)0/ unassigned@homelabonlyRoot cause
A wire-shape mismatch in the UI normaliser, not the proxy —
hal0-apiforwards/tasks/{id}verbatim (board.py), so both UIs read the same store.The Hermes kanban plugin returns two shapes:
GET /board→ a flat task row per columnGET /tasks/{id}→ an envelope:{task, comments, events, attachments, links, runs}with snake_case item fields (created_at,payload,outcome,summary) and epoch-second timestampsnormaliseTask()read scalars off the top level, so everything under.taskfell to its default (hencestatus → "triage") and only the top-level arrays survived — with the wrong per-item field names (created_at≠at,payload≠json,outcome≠state,summary≠msg). The worker-log hook only understood array /{entries}, never the{content}shape the plugin actually returns.Fix (
ui/src/api/hooks/useBoard.ts+task-drawer.jsx)normaliseTask— unwrapraw.taskfor scalars; pullcomments/events/runsandlinks → depsoff the envelope. Falls back to the flat row and to already-normalised camelCase objects (the optimistic-update path re-feeds them), so it's backward-compatible and idempotent.normaliseEvent/normaliseRun/normaliseComment+relTime/durLabelhelpers remap the item fields (created_at → atas "20m ago",payload → json,outcome/status → state,started/ended → dur,summary → msg,workspace_path → workspace).useBoardTaskLog— accept{content: "<text>"}(split into per-line entries).task-drawer.jsx— readt.body(the normaliser never emittedt.desc).Tests
npm run typecheck✅ ·playwright test board-drawer-v3.spec.ts→ 22 passed (all prior flat-fixture tests still green, proving no regression).🤖 Generated with Claude Code