← Back
← Back
# Plan coordination — who's working, who's done, how to take over
**Session**: 418281651208440 (operator-panel-mavis, thread id 7)
**Date**: 2026-08-03 01:05 BST
**Operator directive**: "if an AI is currently in control of the plan Ie actively working on it it needs to be shown and that AI thread is responsible for maintaining the checklist. if another AI wants to work on a plan it needs to see a finish checklist bulletin from previous ai. and check if further activity is happening. coordinate correctly or takeover"
## Goal
Make it impossible for two Mavis threads to silently step on each other working the same plan. Three signals must be visible on the plan card:
1. **🟡 Active** — the plan's owning thread has a heartbeat within the last 15 min AND has not posted a "plan finished" bulletin
2. **🟢 Done** — owning thread posted a "🟢 plan finished" bulletin (with checklist)
3. **⚪ Stale** — no heartbeat for >15 min AND no finish bulletin
And add a takeover protocol: any thread can see "previous AI finished" and either continue (with new sub-plan) or take over (with explicit bulletin).
## Constraints
- Don't break existing plans (a433, a434, a435, a436, a429, a413, etc.) — backfill where reasonable
- No new DB tables — extend the existing meta shape
- The signal must be COMPUTABLE, not just self-declared (otherwise threads lie)
- SPA must show the signal on BOTH the plan list AND the plan detail view
- Pickup-by-id sessions must be able to see the signal in one call
## Steps
1. **Add `plan_activity` summary endpoint** `GET /api/plans/:id/activity` — returns:
- `last_heartbeat` for the owning thread (ms, with display_id like h147)
- `last_bulletin` for the owning thread that's a "plan activity" bulletin (ms + id)
- `last_finished` — the most recent "plan finished" bulletin (ms + id)
- `last_tick` — the most recent plan tick (ms + which step)
- Computed state: `active` (heartbeat < 15 min, no finish after last tick) | `done` (finish bulletin after last tick) | `stale` (no heartbeat > 15 min)
- The owning thread's name + id (from artifact.thread_id)
2. **Add a `finish_plan(plan_id, summary)` helper** in mavis-hq.js — auto-posts a bulletin of the form `🟢 plan finished: <title> — <n>/<n> steps ticked, last evidence bNNN, ready for takeover or next plan`
3. **Add a `takeover_plan(plan_id, reason)` helper** — auto-posts `🟡 plan takeover: <title> — previous owner @old (last activity <time>), reason: <reason>`. Records `meta.taken_over_by` + `meta.taken_over_at` so the SPA can show the lineage.
4. **Update the plan meta shape** to include `meta.ownership`:
```json
"ownership": {
"thread_id": 7,
"thread_name": "operator-panel-mavis",
"started_at": 1785710415919,
"last_activity_at": 1785710871234,
"finished_at": null,
"taken_over_from": null
}
```
Set on every plan upload + every tick.
5. **Update the SPA list view** — show on every plan card:
- Right side of the top row: small `🟡 active 2m ago @operator-panel-mavis` chip if active
- `🟢 done 4h ago` if finished
- `⚪ stale 2d ago @old-owner` if no activity
- For pickup: a "🤚 take over" button (posts the takeover bulletin)
6. **Update the SPA detail view** — add an "👷 Activity" card between Targeting and Checklist. Shows the full activity log (last 10 heartbeats + bulletins + ticks for the owning thread, filtered to the plan's lifespan).
7. **Auto-finish detection** — when the last step is ticked as `done` via `/api/plans/:id/tick`, auto-call `finish_plan(plan_id, summary)` UNLESS the calling thread has set a `no_auto_finish: true` flag in the tick body (escape hatch for "I want to mark step N done but I'm not really done with the plan").
8. **Backfill the existing 6 plans** with `ownership` + `last_activity_at`:
- a433, a434, a435, a436: owned by operator-panel-mavis, all done
- a429: owned by helper-mavis, in flight
- a413: owned by hopfan thread, parked
9. **Bulletins for the major state transitions**:
- `🟡 plan started: aNNN — <title> (by @thread)` on first tick
- `🟢 plan finished: aNNN — <title> — <n>/<n> steps, evidence bNNN, ready for takeover`
- `🟡 plan takeover: aNNN — <title> — by @new, from @old, reason: <r>`
## Open questions (for operator)
- **Q1: What counts as "active"?** — Heartbeat < 15 min? Or any bulletin in the plan's span? Or a `plan_activity` bulletin? I lean heartbeat-based with a 15-min cutoff. Confirm.
- **Q2: Auto-finish or explicit?** — When the last step ticks done, should the system auto-post the finished bulletin, or require the owning thread to explicitly call `finish_plan`? I lean auto-finish (matches the Q3 evidence-required rule — the act of ticking the last step IS the evidence). Confirm.
- **Q3: Takeover vs handoff** — Is "takeover" a different verb from "handoff"? I lean: takeover = I'm taking this from you, handoff = I'm giving this to you. Same underlying mechanism, different framing. Confirm.
- **Q4: Display old plans how?** — a413/a429/a433 etc. are all from before the system. Should they show "stale 2d ago" with a "🤚 take over" button, or should we mark them as "📜 historical" and hide the takeover affordance? I lean historical = hide the takeover button but keep the activity card visible. Confirm.
## Off-track triggers
- If I find myself rewriting the entire SPA — STOP, the changes are additive
- If I find myself adding a new DB table — STOP, extend the existing meta shape
- If the existing `last_activity_at` from the targeting sub-shape is enough — STOP, don't add a new field
- If the auto-finish logic gets complex — STOP, fall back to explicit `finish_plan` calls
- If pickup-by-id sessions are blocked because they don't have the new endpoint — STOP, add a `?activity=1` query param to `GET /api/plans/:id` instead of a new endpoint
## Handoff
- **Status**: Plan written, not yet started. Operator will see this on the next prompt.
- **Next step**: Get Q1-Q4 answers from operator, then build steps 1-3 in one turn (small, additive), then steps 4-6 in a second turn (the SPA work).
- **Current state of the live system**:
- GET /api/plans/:id returns full meta + steps ✓ (a434 shows 6/6 done)
- GET /api/plans (list) does NOT unpack meta ✗ (shows 0/0 for everything — confirmed live 01:05 BST)
- SPA renders from p.meta.steps — currently empty for all plans on the list view
- No "currently working" indicator anywhere
- No "finished" bulletin convention
- No takeover endpoint
- **Blocking**: Q1-Q4 answers