Skip to content

Case Cockpit: surface dropped state, per-stage actions, live data #104

Description

@toasterman234

Plan — Mira Case Cockpit: surface dropped state, per-stage actions, live data

Written 2026-07-15. Self-contained: assumes zero prior context. Intended to be filed as a
GitHub issue on byteseek/Mira and picked up by an implementing agent.

Context (why)

The dashboard at dashboard/ (see dashboard/HANDOFF.md) is a read-only mirror of Mira's
research cases. It shows where a case sits (board stage + cockpit: route, readiness ladder,
expectation map, evidence bars, artifacts) but gives the operator nothing to do from there.

Mira itself is not a state store — it is a set of loops/skills (actions) in loops/ and
skills/. So the dashboard today drops the parts that make Mira Mira, and offers no way to act
on a card. Ben reviewed it and named three gaps to close:

  1. Surface dropped state — the cockpit throws away thesis-ledger, decision-log, refresh
    triggers, expectation-map surprise columns, and evidence conflict detail.
  2. Per-stage action menus — each stage has specific legitimate next moves (real Mira loops).
    The dashboard should offer them, turning the mirror into a launcher.
  3. Live data connector — every case is connector: none. A real tools/mira_data Futu/IBKR
    connector already exists; wire its output in.

Architecture decision: stay static for v1 (Python build step → data/cases.json → vanilla JS,
no running backend), matching Mira's stdlib-only convention (tools/mira_pipeline_sync.py,
tools/mira_data). Per-stage actions copy the exact Mira prompt/command to paste into an
agent; live prices are folded in at build time from existing private/futu|ibkr/ snapshots.
Leave a clean, documented seam so a small local backend can later execute actions and fetch live
prices on click. Do not build the backend in v1.

Hard invariant: the dashboard is read-only over cases/. build_data.py must never write into
case files. (This is why parsing helpers were copied out of mira_pipeline_sync.py rather than
imported — that module writes frontmatter at import time.)

Files touched

  • dashboard/build_data.py — new parsing (ledger, decision-log, refresh radar, conflict detail,
    live snapshot fold). Stdlib only.
  • dashboard/app.js — new cockpit panels, expectation-map columns, per-stage action menu +
    copy-to-clipboard, board overdue badge, live-data rendering.
  • dashboard/style.css — styles for new panels, action popover/modal, badges.
  • dashboard/index.html — minor (popover/modal container if needed).
  • dashboard/HANDOFF.md — document the live-snapshot fetch step and the backend seam.

Feature 1 — Surface dropped state

build_data.py (new pure helpers, wired into build_case())

  • parse_ledger(case_dir) → read thesis-ledger.md if present. Extract:
    • the - key: value metadata block (state, last_updated, selected_framework,
      selected_overlays, stale_after) with the same line-anchored regex style as fm_get;
    • the first paragraph under ## Current Thesis;
    • ## Key Assumptions items, and any kill_criteria / reversal_condition lines.
      Return a dict or None. (Sample shape confirmed in cases/aapl-2026-04/thesis-ledger.md.)
  • parse_decision_log(case_dir)load_csv_rows("decision-log.csv"); keep
    date, research_object, decision_type, basis, risk, required_followup. Return list or None.
    (Header confirmed in cases/aapl-2026-04/decision-log.csv.)
  • Refresh radar — add to each case:
    • refresh_statusoverdue | due_soon | ok | unknown and days_to_stale (int|null),
      computed by extracting the first DATE_RE match from the stale_after frontmatter field and
      comparing to datetime.date.today() (allowed — this is the build step, not the sandboxed
      research workflow). due_soon = within 14 days.
    • must_refresh_if and kill_criteria from README frontmatter when present.
  • Evidence conflict detail — in build_evidence(), alongside conflict_count, add
    conflicts: a short list of {claim, conflict_status, source_id} for rows whose
    conflict_status ∉ {none, ""}. Cap at ~10 to keep JSON small.
  • Expectation map already passes full CSV rows — no builder change; the extra columns
    (consensus_proxy, evidence_status, next_check) are surfaced in the frontend.

app.js (new/updated cockpit panels)

  • Thesis Ledger panelstate badge (stale/active), current-thesis text, kill/reversal
    criteria, key assumptions, framework + overlays. Empty-state when no ledger.
  • Decision Log panel — compact timeline: date · decision_type · basis (truncated) · followup.
  • Expectation Map — extend renderExpectationMap() to show consensus_proxy and next_check
    (and evidence_status as a chip). This is the "where's the surprise vs consensus" view.
  • Refresh chip in the case header — red "overdue Nd" / amber "due in Nd" from refresh_status.
  • Evidence panel — make conflict_count expandable to list evidence.conflicts.
  • Board cards — add a small overdue dot/badge when refresh_status === "overdue".

Feature 2 — Per-stage action menus (launcher)

Add a STAGE_ACTIONS config in app.js: pipeline_stage → [{ label, kind, template }].
kindprompt | shell | open (this typing is the backend seam — a future server executes by
kind; v1 copies template to clipboard via navigator.clipboard.writeText). template is a
function of the case (interpolates slug / ticker / label / loop path).

Stage → actions (sourced from AGENT_QUICKSTART.md routing table + loops/):

Stage Actions (each yields a real Mira prompt/command)
idea Route it → loops/analysis-routing.md · Promote to Queued
queued Run first-pass loops/research-loop.md (paste task card for this case)
intake Confirm route · pick framework/overlay · run the routed skill
researching Continue loop · complete expectation-map.csv · run gates
package_complete Review → refresh / extend / archive · loops/monitoring-loop.md · loops/thesis-update-loop.md · check actionability-bridge.md
needs_refresh Refresh now → scripts/check_updates.sh then loops/monitoring-loop.md / loops/event-delta-loop.md (show the stale-trigger date)
archived Reference only · reopen as new case

Card-level actions (any stage): Open memo / evidence-log / expectation-map (copy path or
file:// link) · Copy next-step prompt (from next_action) · Copy case folder path
cases/<slug>/.

UI: clicking an action opens a small popover/modal showing the exact text with a Copy button;
no execution in v1.


Feature 3 — Live data connector (static snapshot-fold + seam)

build_data.py

  • load_live_snapshot(ticker) → look under private/futu/<TICKER>* and private/ibkr/<TICKER>*
    for the newest emitted snapshot JSON. Implementation step: read tools/mira_data/emit.py and
    tools/mira_data/adapters/ to match the exact emitted JSON shape (last price, as-of timestamp,
    source id). Return {price, as_of, source, connector} or None.
  • build_live_data() — when a snapshot exists, set connector to futu_opend_local /
    ibkr_gateway_local, fill real price / as_of / freshness (compare as-of to today), else keep
    the honest connector: "none" default. Never fabricate a price.

app.js

  • renderLiveData() — when connector !== "none", show live price / as_of / source / freshness
    with a live-vs-stale badge; keep the current honest empty state otherwise.

Operator step (document in HANDOFF.md)

PYTHONPATH=tools python -m mira_data fetch futu_market_price US.<TICKER> --out private/futu/<TICKER>
python3 dashboard/build_data.py   # re-fold the snapshot into cases.json

private/ is gitignored — snapshots stay local. No account ids / positions enter tracked files
(per docs/futu-private-data.md, docs/ibkr-private-data.md).

Backend seam (v2, documented not built)

A thin dashboard/server.py (stdlib http.server) exposing /api/live/<slug> (shells
mira_data fetch) and /api/run (executes a stage action by kind). Frontend prefers these
endpoints when reachable, falls back to static JSON / clipboard otherwise. The kind typing on
stage actions and the connector field already make this a drop-in.


Verification

  1. python3 dashboard/build_data.py → runs clean, 36+ cases, no writes into cases/
    (git status cases/ clean after run). Spot-check aapl-2026-04 in data/cases.json: ledger
    fields, decision-log rows, refresh_status, expectation-map extra columns, conflicts list.
  2. Serve: python3 -m http.server 8420 --directory dashboard, open in browser. Confirm:
    • Cockpit shows Thesis Ledger + Decision Log panels for aapl-2026-04.
    • Expectation map shows consensus_proxy + next_check.
    • needs_refresh cases past their stale date show an overdue badge on board + cockpit.
    • Each card/stage shows an action menu; clicking an action copies the correct Mira prompt.
    • Live panel: drop a sanitized test snapshot into private/futu/AAPL, re-run build, confirm
      the live price renders; then remove the test snapshot.
  3. Re-run build_data.py; confirm data/cases.json still gitignored and cases/ untouched.

Out of scope (v1)

  • The local backend server (seam only).
  • Drag-to-change-stage / any write-back into case frontmatter (stage is owned by
    mira_pipeline_sync.py, not the browser).
  • The mira_pipeline_sync.py regex fix (tracked separately in HANDOFF.md).

Filing

After approval, file this as a GitHub issue on byteseek/Mira via gh issue create (title:
"Case Cockpit: surface dropped state, per-stage actions, live data"), body = this plan.
byteseek/Mira is a public repo — confirm that's the right target before filing.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions