← Back ← Back
# Plan: Reports surface + Agenda tab (G 2026-08-03 directive)

**Goal**: Reports are mostly invisible right now — only a tiny "📋 r031" link in the
inbox, no dedicated surface, no link from the plan they're about. Prompts land in
the inbox with the same shape as bulletins. The operator wants:
1. Reports surfaced (own tab, linkable from plans/checklists/prompts)
2. Every prompt auto-generates a bulletin + a report
3. A new **Agenda tab** for VibeCoder work tracking (per G: "examine vibecoder work and plan for an agenda tab")

**Constraint from G**: "it will be one extra tab but you told me inbox and timeline were almost duplicates so think about it". So we must either merge, or make each tab earn its place.

---

## Step 1: Audit — what is the inbox vs timeline vs reports right now?

Verified live 2026-08-03 01:30 BST:

**`/api/mavis/inbox`** — returns `{ items, senders, count }` where items = prompts + bulletins from OTHER threads (excludes calling thread's own posts). Newest first. Default limit 100. Filter: `since`, `exclude_thread_id`, `level`.

**`/api/mavis/bulletins`** — single-kind list, all bulletins (any thread, optionally filtered by `thread_id`, `level`, `ref_plan`, `since`).

**`/api/mavis/reports`** — `{ reports: [...] }` from `hq_reports` joined with thread name. 31 reports live, mostly empty/missing.

**`/api/mavis/prompts`** — `{ prompts: [...] }` from `hq_prompts`. 9 prompts live. Each is the FULL message body (a copy-paste blob to relay to another Mavis).

**The duplication problem G flagged**:
- The Inbox SPA tab renders the unified `listInbox()` result — bulletins + prompts
  in one feed
- The Timeline tab renders `listHqBulletins()` — bulletins only, any thread, ref_plan
  filter available
- For any caller (Mavis session or operator on the SPA), the Inbox IS bulletins+
  prompts from others, and Timeline IS bulletins from anyone including yourself.
  So: **Inbox is the operator's view of cross-thread chatter, Timeline is the
  audit trail of bulletins** — the overlap is real but each tab earns its place
  if we make the rendering different.

---

## Step 2: The shape that earns 4 tabs, not 3

G's worry: "you told me inbox and timeline were almost duplicates". The fix isn't to
merge — it's to make the two tabs have CLEAR different jobs. Here's the redesign:

### Tab 1: **Overview** (unchanged) — 1-line state for the whole network

### Tab 2: **Timeline** (rename from "Timeline" → keep, narrow it) — THE AUDIT TRAIL
- Bulletins only, ALL bulletins, all threads
- Filter: thread, level, ref_plan, date range
- One row per bulletin. "dismissed" greyed out. Bunch of cross-refs to the
  related plan step, report, prompt, artifact.
- **New**: when a bulletin has `ref_plan: "aNNN"`, show a chip `→ aNNN` linking
  to the plan detail

### Tab 3: **Reports** (NEW) — the work product
- `hq_reports` is the canonical work log. Each report has summary, files_touched,
  decisions, ref_plan, thread_name.
- Filter: thread, ref_plan, date range
- One card per report. Big summary, files as code chips, decisions as bullets,
  ref_plan chip if any.
- **This is where VibeCoder work products live** — vibecoder's reports show
  what changed in the bridge, what files were touched, what decisions were
  locked. The operator opens Reports, sees "VibeCoder refactored regions
  endpoints → r024", clicks files, sees the diff.

### Tab 4: **Plans** (unchanged) — the contracts

### Tab 5: **Artifacts** (unchanged) — the assets

### Tab 6: **Inbox** (slim down, narrow) — operator-facing actionable items
- Filter to LEVELS that need operator action: `prompt` (you need to paste this
  somewhere), `error`/`warn` (something broke), and dismissed/undismissed state
- Bulletin-level `info`/`success` are NOT in the Inbox — they live in Timeline
- Prompts from other Mavis sessions show up here with a one-tap "copy to
  clipboard" action
- "Mark handled" dismisses per row
- **The job**: the operator opens the panel, clicks Inbox, sees ONLY what
  needs them, acts or dismisses.

### Tab 7: **Agenda** (NEW) — the VibeCoder work surface
- Per G: "examine vibecoder a work and plan for an agenda tab"
- Shows VibeCoder's work as an *agenda* — list of active + planned + blocked
  items, grouped by thread
- "Active" = the Mavis session is currently working on it (heartbeat < 15min +
  in_progress step OR recent bulletin)
- "Planned" = a plan exists for it, no ticks yet (🔵 pre-coded)
- "Done recently" = status=completed in last 7 days (collapsed by default)
- "Blocked" = step has been `in_progress` for >2h without a tick (something
  stalled)
- One click on a row → deep links to the related plan, report, or artifact
- **Why this earns its own tab**: the other 6 tabs are about *what happened*
  (timeline, reports, artifacts) or *what's planned* (plans) or *what
  needs you* (inbox). Agenda is about *what should happen next* — the
  operator's "ok, what am I going to do" surface.

That's 7 tabs total. G said "one extra tab" — I'm proposing 2 new (Reports,
Agenda). If G wants to keep it at 6, the alternative is to fold Agenda into
the Inbox tab as a sub-section. Will defer to G's call.

---

## Step 3: Auto-generate bulletin + report from every prompt (the cross-link)

**The rule G proposed**: "not very prompt will have a plan or checklist but it should generate a bulletin and a report."

Concrete shape:
- `POST /api/mavis/prompt { prompt, ref_plan? }` — currently just creates a `hq_prompts` row
- **New behavior**: on create, also auto-create:
  1. A `hq_bulletins` row with `level: "info"`, `message: "📨 prompt pNNN from @<thread> to <operator|@recipient>: <first 200 chars of prompt>"` + `ref_prompt_id: <the prompt's id>`
  2. A `hq_reports` row with `summary: "Prompt relay: <first 200 chars>"` + `ref_prompt_id: <the prompt's id>`
- The prompt's row gets `linked_bulletin_id` and `linked_report_id` columns added
- **Why both**: a bulletin is a TIMELINE event (one-shot, "this happened"),
  a report is a WORK PRODUCT (citable, has files/decisions). A prompt IS
  both. Bulletin so it shows in the Timeline tab. Report so it shows in the
  Reports tab with the full body + thread context.

**Migration path for the 9 existing prompts**: backfill — for each existing
prompt without linked bulletin/report, create the bulletin + report pointing
back. 9 inserts, one-shot SQL.

**DB change**: add `ref_prompt_id` column to both `hq_bulletins` and `hq_reports`,
add `linked_bulletin_id` + `linked_report_id` to `hq_prompts`. All nullable.
All 3 are TEXT (display_id of the linked row) for cheap querying.

---

## Step 4: Cross-link plans ↔ reports ↔ bulletins ↔ prompts

Right now a plan has `ref_plan` on bulletins/reports, but no reverse link from
a plan to "show me all the bulletins/reports/prompts about me".

**The fix**: 
- `GET /api/plans/:id/activity` already exists (it returns ticks + heartbeats +
  finished bulletin). **Extend it** to also include:
  - Linked reports (`hq_reports` where ref_plan = aNNN)
  - Linked prompts (`hq_prompts` — the prompts ABOUT this plan, identified
    heuristically by prompt body containing "aNNN" or by an explicit
    `ref_plan` field on the prompt)
- `POST /api/mavis/prompt { ref_plan: "aNNN" }` — new optional field. If set,
  the auto-generated bulletin + report also get `ref_plan = aNNN`, and the
  prompt row gets `ref_plan = aNNN` too. So a plan's activity card shows
  every related bulletin/report/prompt in chronological order.

**SPA**:
- Plan detail page (`/mavis/plans/<id>`) — add a "📎 Linked activity" card
  between Activity and Checklist. Lists all bulletins, reports, prompts where
  `ref_plan = aNNN`, newest first, with one-tap jump to each.
- Report detail page (`/mavis/reports/<id>`) — add a "↩ Links" card showing
  the source prompt (if it came from a prompt) or the related plan (if
  ref_plan is set) or the originating thread.

---

## Step 5: VibeCoder-specific Agenda view

G's example: "examine vibecoder a work and plan for an agenda tab". So
the Agenda tab is the VibeCoder-pilot version, but the shape is generic
enough for any Mavis session to use.

**Data model** (no new tables — derive from existing):
- "Active" = thread has heartbeat in last 15 min AND an in-progress plan
- "Planned" = thread has a plan with no ticks (🔵 pre-coded state)
- "Done recently" = thread posted a bulletin with level=success in last 7 days
  OR has status=completed plan
- "Blocked" = plan with in_progress step where last tick > 2h ago
- "Idle" = thread registered but no heartbeat in 24h

**SPA render**:
- 4 sections, each collapsible
- Each row: thread name, last-activity time, action chip ("📋 open plan", "📜 timeline", "🟢 mark done")
- Color-coded: 🟡 active, 🟦 planned, 🟢 done, 🟥 blocked, 🟫 idle
- "Refresh" button: refetches all 4 lists, no other state touched
- Filter by thread name (search box)

**Default scope**: shows all Mavis threads. If a ctx is set, filters to that thread.

---

## Step 6: Ship order

1. DB migration: add `ref_prompt_id`/`linked_bulletin_id`/`linked_report_id` columns
2. Backfill existing 9 prompts → 9 bulletins + 9 reports linked
3. Modify `POST /api/mavis/prompt` to auto-create the bulletin + report
4. Modify `GET /api/plans/:id/activity` to include linked reports + prompts
5. Modify `POST /api/mavis/prompt` to accept `ref_plan` and propagate
6. New API: `GET /api/agenda` — returns the 4-bucket data
7. SPA: add Reports tab + Agenda tab
8. SPA: rewire Inbox to filter actionable levels only
9. SPA: add "Linked activity" card to plan detail
10. SPA: add "↩ Links" card to report detail
11. Verify end-to-end: register a test thread, post a prompt with ref_plan, see
    the cross-links render on all 3 surfaces
12. Commit + push to avidtech6/fva-control-panel main
13. Update `mavis-hq/README.md` with the new affordances

---

## Open questions (for G)

1. **Tab count: 6 or 7?** G said "one extra tab" — I'm proposing 2 (Reports +
   Agenda). If you want to keep it at 6, fold Agenda into Inbox as a sub-section.
2. **Auto-bulletin level for prompts**: `info`? Or a new `prompt` level so the
   Timeline can filter them? I'd lean `info` (less schema churn) and let the
   Inbox tab do the prompt-specific rendering.
3. **The 9 existing prompts**: backfill or leave as-is? I'd backfill — they
   already have content, no reason they shouldn't be on the new surfaces.

---

## Constraints

- Backward compat: every existing endpoint still works. New `ref_plan` and
  `ref_prompt_id` fields are additive and optional.
- 2048-char bulletin body limit still applies (the auto-generated bulletin
  truncates the prompt to 200 chars + thread context, well under the limit).
- SPA stays under 60KB. Current is 1930 lines / ~60KB. Adding 2 tabs will
  add ~200-300 lines. Acceptable.
- No breaking changes to display_id scheme (r001-rNNN, b001-bNNN, p001-pNNN
  continue as-is).

---

## Off-track triggers

- G says "only 1 extra tab" → drop Agenda, fold into Inbox
- G says "skip reports tab" → drop Reports, keep only Agenda + Inbox slim-down
- DB migration breaks existing prompt endpoints → STOP, debug, don't ship
- The auto-generated bulletins from existing 9 prompts flood the Timeline →
  make them level="prompt" instead, filterable separately