Mission Control

Artifacts

K
← Back to artifactsOpen JAM-40

JAM-40 task lifecycle hardening implementation

ImplementationCompletedJAM-40Created Apr 9, 20265 min readFull screen ↗

JAM-40 task lifecycle hardening implementation

Findings summary

Root cause was not one bug. It was a stack of conflicting write paths:

  1. UI status moves used full-array writes. Board moves and arrow moves rewrote the entire tasks.json list through PUT /api/tasks, so a status change had no canonical server-side lifecycle path.
  2. No lifecycle gate existed server-side. Any client that could write the full list could jump a task to any status, regardless of the intended workflow.
  3. No read-after-write verification existed before success. A write could be reported as successful without confirming the persisted task state.
  4. Chat guidance still told agents to mutate status by rewriting the full array. That kept the same reliability risk alive outside the UI.
  5. Task capture vocabulary and defaults still carried legacy terms. next and blocked still showed up in capture flows, and new captures defaulted to next, which front-loaded prioritization instead of durable intake.

Implementation shipped

1) Canonical server-side lifecycle transition helper

Implemented in mission-control/lib/tasks-store.ts:

  • Added transitionTask(...) as the canonical lifecycle mutation path.
  • Added explicit allowed transition graph:
  • backlog -> up-next
  • up-next -> backlog | in-progress
  • in-progress -> needs-review
  • needs-review -> in-progress | done
  • Invalid moves now fail closed with Invalid transition: <from> -> <to>.
  • Transition writes now run under the task-store lock.
  • After each transition write, the store re-reads tasks.json and verifies the persisted task matches the intended post-transition state before returning success.
  • needs-review transitions auto-assign to Pete.

2) Targeted task mutation APIs

Added targeted routes so normal task edits no longer need full-array PUTs:

  • PATCH /api/tasks/[taskId]
  • DELETE /api/tasks/[taskId]
  • POST /api/tasks/[taskId]/transition

Notes:

  • PATCH uses the canonical lifecycle helper when status changes.
  • POST /transition is the dedicated status-change path for UI and chat flows.
  • The old bulk PUT /api/tasks still exists for bulk reorder/save cases, but it is no longer the normal lifecycle path.

3) UI status changes now use the canonical path

Updated mission-control/components/tasks/TasksWorkspace.tsx:

  • Board status moves now call POST /api/tasks/[taskId]/transition.
  • Left/right lifecycle moves now call the same transition endpoint.
  • Task editor save now uses targeted PATCH /api/tasks/[taskId] for edits, which routes status changes through the canonical helper.
  • Create uses POST /api/tasks and delete uses DELETE /api/tasks/[taskId].
  • UI now applies server-returned task lists instead of assuming a local optimistic full-array save was durable.

4) Chat/task operational model hardened

Updated /Users/vinny/.openclaw/workspace/AGENTS.md:

  • Added a mandatory task-vs-chat boundary section.
  • Default is now chat-native unless durable work is clearly present.
  • Agents are told to create/update Mission Control tasks when work is multi-turn, async, delegated, deliverable-based, or needs later resumption.
  • Task lifecycle section now explicitly forbids normal status moves via full-array task rewrites.
  • The move [task] to [column] workflow now uses the new transition helper script instead of raw full-array PUT.

5) Chat move helper added

Added mission-control/scripts/transition-task.mjs:

  • Resolves task by --id, --display-id, or --match.
  • Calls POST /api/tasks/[taskId]/transition.
  • Only reports success after the server returns a verified persisted transition.
  • Lists ambiguous matches instead of guessing.

6) Vocabulary alignment and intake default fix

Updated:

  • mission-control/scripts/capture-task.mjs
  • skills/mission-control-task-capture/scripts/capture-from-chat.mjs
  • skills/mission-control-task-capture/SKILL.md

Changes:

  • Canonical statuses in docs and help are now:
  • backlog
  • up-next
  • in-progress
  • needs-review
  • done
  • Legacy aliases still work:
  • next -> up-next
  • blocked -> backlog plus blocked=true
  • New task capture defaults now use backlog, not next.

This aligns capture with intake instead of implicit prioritization.

Files changed

Workspace guidance / capture

  • /Users/vinny/.openclaw/workspace/AGENTS.md
  • /Users/vinny/.openclaw/workspace/skills/mission-control-task-capture/SKILL.md
  • /Users/vinny/.openclaw/workspace/skills/mission-control-task-capture/scripts/capture-from-chat.mjs
  • /Users/vinny/.openclaw/workspace/mission-control/scripts/capture-task.mjs

Mission Control code

  • /Users/vinny/.openclaw/workspace/mission-control/lib/tasks-store.ts
  • /Users/vinny/.openclaw/workspace/mission-control/app/api/tasks/route.ts
  • /Users/vinny/.openclaw/workspace/mission-control/app/api/tasks/[taskId]/route.ts
  • /Users/vinny/.openclaw/workspace/mission-control/app/api/tasks/[taskId]/transition/route.ts
  • /Users/vinny/.openclaw/workspace/mission-control/components/tasks/TasksWorkspace.tsx
  • /Users/vinny/.openclaw/workspace/mission-control/scripts/transition-task.mjs

Validation

Build

  • npm run build in /Users/vinny/.openclaw/workspace/mission-control passed.

Service restart

  • npm run service:restart completed.
  • npm run service:status shows LaunchAgent running.
  • curl -sf http://localhost:3100/api/tasks returned success.

Live lifecycle validation against localhost

Temporary validation tasks were created, transitioned through the live API, then deleted after validation.

Task A

  • JAM-41 created in backlog
  • backlog -> up-next : HTTP 200
  • up-next -> in-progress : HTTP 200
  • in-progress -> needs-review : HTTP 200, assignee persisted as Pete
  • needs-review -> done : HTTP 200

Task B

  • JAM-42 created in needs-review
  • needs-review -> in-progress : HTTP 200
  • in-progress -> needs-review : HTTP 200, assignee persisted as Pete

Invalid transition guard

  • JAM-43 created in backlog
  • backlog -> done : HTTP 409 with Invalid transition: backlog -> done

Chat move helper validation

  • Ran node /Users/vinny/.openclaw/workspace/mission-control/scripts/transition-task.mjs --display-id JAM-43 --status up-next
  • Script reported Transition verified: JAM-43 -> up-next

Targeted PATCH path validation

  • Patched JAM-43 through PATCH /api/tasks/[taskId] with:
  • title change
  • description change
  • assignee change to David
  • status: in-progress
  • Response returned HTTP 200 with persisted status=in-progress

Cleanup

  • Temporary validation tasks JAM-41, JAM-42, and JAM-43 were deleted after validation.

Deferred work

Nothing blocking the JAM-40 approval criteria remains.

Non-blocking follow-up if desired:

  • Move any remaining reorder-only flows off bulk PUT /api/tasks as a second pass.
  • Add automated integration tests around lifecycle transitions and invalid move rejection.
  • Consider surfacing invalid transition errors more explicitly in the board UI affordances.

Shipped vs deferred

Shipped

  • Canonical server-side lifecycle transition helper
  • UI status changes routed through canonical path
  • Read-after-write verification before lifecycle success
  • Chat move flow routed through canonical path
  • Task-vs-chat guidance update
  • Status vocabulary alignment in capture flows
  • Live validation and service restart

Deferred

  • Non-lifecycle reorder refactor
  • Automated test suite coverage for these cases
  • Extra UI polish around invalid move affordances

Ready for review

Yes.