Skip to content

feat: a Kanban card names the dials its next run will use (#84) - #85

Merged
Lexus2016 merged 1 commit into
mainfrom
feat/84-kanban-run-badges
Aug 31, 2026
Merged

feat: a Kanban card names the dials its next run will use (#84)#85
Lexus2016 merged 1 commit into
mainfrom
feat/84-kanban-run-badges

Conversation

@Lexus2016

Copy link
Copy Markdown
Owner

Closes the Kanban Visibility half of #84.

The defect

A card rendered one badge, tk.sess_model. It was wrong twice over:

  1. Blank on a task that had never run. A board of freshly created cards said nothing about the model/effort/engine the user had just picked in the create modal.
  2. Silent about a bot's model, which overrides the session's inside the runner — so a card assigned to a bot named the wrong model.

The fix

kbRunBadges() in public/kanban.html mirrors server.js/startTask exactly:

model  = taskBot?.model || session?.model || task.model || 'sonnet'
effort = task.effort
engine = task.run_engine === 'subscription' ? 'subscription' : 'api'
  • bot_model is joined in getTasks, not looked up client-side. The board fetches its bot roster only when a modal opens, so kbBots is [] at first paint; a client-side lookup would render one model before the user touched anything and a different one afterwards. The join carries AND b.deleted_at IS NULL because stmts.getBot does — without it a card advertises the model of a bot the runner will refuse to load.
  • Effort and engine render only when they are NOT the default. auto effort and the api engine are what nearly every card carries; a badge on all of them is noise in a footer already holding the project, schedule, session and retry badges. An unknown effort is shown verbatim rather than dropped — a bad stored dial reaching the run is exactly what a silent badge would hide.
  • The chain card (renderChainCard) is untouched. A chain has no bot and no per-task engine.

Cost

  • Schema: none. One additive LEFT JOIN + one column on an existing SELECT; /api/tasks gains a key, which is backward-compatible.
  • i18n: no new keys — reuses tb.model / tb.effort / tb.engine / effort.* / engine.sub, all already present in en/fr/he/ru/uk.
  • Perf: the join is on tasks.bot_id against a table with a handful of rows; the board already polls this endpoint.
  • Risk: the card and the runner live in different files. test/kanban-run-badges.test.js therefore pins the runner's own expression as source text as well as the helper's behaviour — a reorder in startTask fails the suite instead of silently making every card lie.

Deliberately NOT in this PR

The rest of #84 — a per-task provider (Codex/Gemini/…), dynamic option catalogs, task templates — is answered in a comment on the issue. In short: the Kanban worker parses claude's stream-json; external agents reach the studio only via /api/delegate + the terminal, which is fire-and-forget with no turn budget, no retry and no session resume. Wiring one into taskWorker is a new execution backend, not a dropdown.

Verification

$ npm test
...
asar-helpers: 9 passed, 0 failed
EXIT=0

$ node test/kanban-run-badges.test.js
40 passed, 0 failed

Live, against a real server booted on a throwaway APP_DIR:

server booted -> stmts.getTasks prepared without error
POST /api/bots -> 200 {"id":"reviewer","label":"Reviewer",...,"model":"opus",...}
POST /api/tasks -> 200 200
{"title":"plain task","bot_id":null,"bot_model":null,"sess_model":null,"model":"haiku","effort":"high","run_engine":"subscription"}
{"title":"bot task","bot_id":"reviewer","bot_model":"opus","sess_model":null,"model":"haiku","effort":null,"run_engine":null}
ASSERT bot_model present on the bot task: PASS (opus)

So the first card renders haiku · High · Subscription, the second opus — which is what each will actually run with.

🤖 Generated with Claude Code

The board rendered one badge, `tk.sess_model`, and it was wrong twice over:
blank on a task that had never run — so a board of freshly created cards said
nothing about settings the user had just picked in the modal — and silent about
a bot's model, which OVERRIDES the session's inside the runner.

`kbRunBadges()` replaces it and mirrors server.js/startTask exactly:
  model  = taskBot?.model || session?.model || task.model || 'sonnet'
  effort = task.effort
  engine = task.run_engine === 'subscription' ? 'subscription' : 'api'

`bot_model` is joined in `getTasks` rather than looked up client-side: the board
fetches its bot roster only when a modal opens, so `kbBots` is [] at first paint
and a client lookup would render one model before the user touched anything and
a different one afterwards. The join carries `AND b.deleted_at IS NULL` because
`stmts.getBot` does — without it a card advertises the model of a bot the runner
will refuse to load.

Effort and engine render only when they are NOT the default: 'auto' effort and
the 'api' engine are what nearly every card carries, and a badge on all of them
is noise in a footer already holding the project, schedule, session and retry
badges. An unknown effort is shown verbatim rather than dropped.

No new i18n keys — the badges reuse tb.model / tb.effort / tb.engine / effort.*
/ engine.sub, which already exist in all five dictionaries.

test/kanban-run-badges.test.js lifts the helper out of kanban.html and pins the
runner's own expression as source text: the card and the runner live in
different files, so a reorder there makes the card lie and nothing else notices.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@Lexus2016
Lexus2016 merged commit 78be930 into main Aug 31, 2026
2 checks passed
@Lexus2016
Lexus2016 deleted the feat/84-kanban-run-badges branch August 31, 2026 23:12
Lexus2016 added a commit that referenced this pull request Aug 31, 2026
PR #85 shipped the feature but carried no CHANGELOG entry or version bump, which
is how every user-facing change in this repo is announced (cf. 4088214 for #83).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant