Skip to content

feat(plan): lint and import a plan/ package onto the Kanban board, with an approval card - #17

Merged
Its-My-Work merged 1 commit into
mainfrom
feat/plan-kanban-import
Sep 22, 2026
Merged

Its-My-Work merged 1 commit into
mainfrom
feat/plan-kanban-import

Conversation

@Its-My-Work

Copy link
Copy Markdown
Owner

Closes the loop the planner bot (#9-#13) exists for: a planner writes plan/tasks/*.md but never a Kanban card — the model that writes a plan is not the one that gets to decide it is correct (see docs/room-guardrails.md). This is the bridge: a linted preview the user approves, then the server, not an LLM, writes the cards.

What

  • plan-lib.js (pure): parseTaskFile, lintPlan (errors block approval: duplicate id, unknown/missing bot, a depends_on cycle or dangling reference, missing title; warnings don't: no acceptance/verification section, no ТЗ coverage), buildImportPlan (create/update/skip — never a dangling depends_on), guessTzFile (name-based, for the coverage check).
  • plan-import.js: reads plan/tasks/*.md + 00-overview.md + the guessed ТЗ file (fs injectable for tests) into the review object; no DB.
  • server.js: tasks.plan_task_id column so re-approving a plan updates its cards instead of duplicating them, and never resets a status a human already moved past backlog. POST /api/plans/review (dry run) and /api/plans/import (writes, re-lints fresh, cards land as backlog — approving a plan never starts unattended work). The room posts a plan_review card automatically the moment its closing step changes plan/tasks/*.md (the disk diff it already computes — no extra model call). WS approve_plan applies the card's selection.
  • UI: the approval card (checkboxes; deselecting a task also deselects its dependents; errors/warnings shown; an "imported" done state), restored identically on reload, wired into the same places agent_plan already is.

Tests

plan-lib 54, plan-import 21 (fake fs), plans-api 19 (real server + real files: review/import/re-import/idempotency/status-preserved/subset-selection/dangling-dependency-skip), render/plan-review (card HTML + wiring). Confirmed live: a scripted room run (real runConversationRoom, a fake CLI that writes real plan files) posts the card the moment the closing step writes plan/tasks/*.md, with correct coverage math; the card itself checked in real headless Chrome (render, checkbox/dependent interaction, the imported state) against this branch's own server.js. Whole npm test suite green.

Not verified

A live run with a real model as the closing writer, all the way through the actual card (the scripted-CLI run above covers the server-side wiring end to end; PR #12's earlier live run already showed the planner writing real plan/ files).

Known gap (documented in docs/plan-kanban-import.md)

No manual "review this plan again" button in the UI yet — only the automatic trigger right after a room's closing step, plus the two HTTP endpoints.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f

…th an approval card

A planner bot writes plan/tasks/*.md but never a Kanban card (the model that writes a plan is
not the one that gets to decide it is correct, see docs/room-guardrails.md). This is the bridge:
a linted preview the user approves, then the server — not an LLM — writes the cards.

- plan-lib.js (pure, no fs): parseTaskFile (front matter + body), lintPlan (errors: duplicate
  id, unknown/missing bot, a depends_on cycle or dangling reference, missing title; warnings:
  no acceptance/verification section, no ТЗ coverage), buildImportPlan (create/update/skip,
  never a dangling depends_on), guessTzFile (name-based, for the coverage check).
- plan-import.js: reads plan/tasks/*.md + 00-overview.md + the guessed ТЗ file (fsImpl
  injectable) and builds the review object; never touches the DB.
- server.js: tasks.plan_task_id column (re-approving a plan UPDATES its cards instead of
  duplicating them, and never resets a status a human already moved past 'backlog');
  POST /api/plans/review (dry run) and /api/plans/import (writes, re-lints fresh, cards land
  as 'backlog' — approving a plan never starts unattended work); the room posts a plan_review
  card automatically the moment its closing step changes plan/tasks/*.md (the disk diff it
  already computes, no extra model call); WS approve_plan applies the card's selection.
- public/index.html: the approval card (checkboxes, deselecting a task also deselects its
  dependents, errors/warnings, an "imported" done state), restored identically from the DB
  on reload, wired into the same places agent_plan already is (Done-footer skip, markdown
  export skip, the message-ingestion loop).

Tests: plan-lib (54), plan-import (21, fake fs), plans-api (19, a real server + real files:
review/import/re-import/idempotency/status-preserved/subset-selection/dangling-dependency-
skip), render/plan-review.test.mjs (card HTML + wiring). Confirmed live: a scripted room run
(real runConversationRoom, a fake CLI that writes real plan files) posts the card the moment
the closing step writes plan/tasks/*.md, with correct coverage; the card itself checked in
real headless Chrome (render, checkbox/dependent interaction, the imported state) against
the branch's own server.js. Whole npm test suite green.

Not verified: a live run with a real model as the closing writer end to end through the
actual card (the scripted-CLI run above covers the server-side wiring; PR #12's live run
already showed the planner writing real plan/ files).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y2ZSHUdJHqVsvSNQ5ZeQ3f
@Its-My-Work
Its-My-Work merged commit 19e7bd5 into main Sep 22, 2026
1 check passed
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