Migrate MC reports to cloud (PKA-Outputs) — cloud-first + workspace fallback [non-breaking]
## Goal Migrate Mission Control's report store from `~/workspace/reports` to the shared cloud outputs tree on Google Drive (`PKA-Outputs/reports`, reachable on Luci at `/home/lu...
StateDoneNext ActionClosedOwnerLuciRuntimeClosedAge4d ago
Ticket is done; runtime is closed.·profile claude_opus_1m_high
Description
MC-4867
## Goal
Migrate Mission Control's report store from `~/workspace/reports` to the shared cloud outputs tree on Google Drive (`PKA-Outputs/reports`, reachable on Luci at `/home/lucienne/gdrive/PKA-Outputs/reports` via the rclone mount `gdrive-mount.service`). MC must keep serving reports the ENTIRE time — NON-BREAKING.
## Background
The PKA repo already migrated dashboards/reports/outputs to `PKA-Outputs/` on GDrive; the PKA dashboard server (Mac) now serves cloud-first with repo fallback (commit 0fdc637f). This ticket does the Luci/MC half. Plan: `~/workspace/PKA/docs/plans/2026-06-05-outputs-to-cloud-storage-split.md` + checklist `docs/plans/2026-06-09-outputs-migration-checklist.md`.
Resolver: `python3 ~/workspace/PKA/pka_paths.py outputs-dir reports` prints the host-correct cloud dir (on Luci = `/home/lucienne/gdrive/PKA-Outputs/reports`). Verify the mount first: `ls /home/lucienne/gdrive/PKA-Outputs/reports`.
## Changes — mission-control/app.py
1. `REPORTS_DIR = WORKSPACE / "reports"` (line ~4514). Add a cloud-first resolver mirroring the PKA pattern: a helper returning `[cloud_reports_dir, WORKSPACE/'reports']` (cloud only if the mount exists). The `/reports/<file>` route (~4728), `/md-view` reports branch, the report LISTING globs (~4646, 4664, 4906), and the DELETE/pdf/convert handlers (~4778/4793/4860) must resolve per-file CLOUD-FIRST then workspace fallback.
- Listing: scan BOTH bases, dedupe by filename, cloud wins.
- DELETE: move the file to `_deleted/` within the SAME base it lives in (no cross-device rename).
- PRESERVE every existing security check: reject `..`, `str(fpath).startswith(str(base.resolve()))`, and the suffix allow-list. Do not weaken them.
2. Migrate existing files: copy `~/workspace/reports/*` (html/md/assets, but EXCLUDE the `radio/` subdir — that is Ticket 2) into `/home/lucienne/gdrive/PKA-Outputs/reports/`, preserving subdirs. Parity-check file counts before/after.
3. Repoint Luci report-WRITER skills that write to `~/workspace/reports/`: audit with `grep -rn "workspace/reports" ~/.claude/skills`, then update `deep-research` and `research-brief` (and any others found) SKILL.md write steps to target the cloud reports dir via the resolver `python3 ~/workspace/PKA/pka_paths.py outputs-dir reports --ensure`. (~/.claude is a SEPARATE git repo — commit + push there.)
4. Reload MC SAFELY: this is the LIVE control plane. Validate app.py parses (`python3 -c "import ast; ast.parse(open('app.py').read())"`) BEFORE any reload. Use the guarded deploy path (mc_orchestrator_deploy.py) if applicable; re-verify branch state at land time. DO NOT take MC down (the MC-3725 lesson).
## Verification — ALL must pass before done
- urllib GET `http://127.0.0.1:3001/reports/<an existing report>.html` -> 200
- A report you migrated to cloud serves 200 AND a workspace-only one still serves 200 (fallback works)
- `/md-view?file=reports/<file>.md` -> 200
- Reports listing page renders the FULL set (no drop in count vs before)
- MC board http://127.0.0.1:3001/ still loads; gunicorn not crashed; `tail` MC logs clean
- Path traversal `/reports/../app.py` still rejected (400/404)
## Hard constraints
- NON-BREAKING: cloud-first + workspace fallback so MC serves regardless of cloud state.
- Preserve all security checks in serve_report.
- Do NOT touch radio/mp3 handling (Ticket 2).
- Touch ONLY report-serving code + the named writer skills. No scope creep.
## Terminal-state report (REQUIRED)
End with: STATUS (success|warning|error), 1-line summary, commit SHA(s) for both repos, the literal verification HTTP codes, and follow-ups. On error: root cause + safe-retry + explicit stop condition. Commit artifacts incrementally. Lucienne (Mac) will review the diff + re-verify before this is closed.
Activity
done
INTERACTIVE
Luci is working...
Details —
Done
· Medium
· Luci
▼
SState
Done
Closed
PPeople
TTiming / Details▼
api (human)
Mission Control
4d ago
4d ago
Advanced / Operator evidence
RRouting owner
ROperator console
Ticket is done; runtime is closed.MC is visibility-only. Hermes Luci launches and gates work outside MC, then mirrors evidence/status here.
WWorkflow
Start Dev Review + QA ▾
Select phases to include:
Agents
Review Gates
Decision
WAT routing: choose an agent, review gate, or decision. Buttons use the live runtime when one is attached.
REVIEW (Lucienne): DEPLOYED + verified live. Guarded deploy mc_orchestrator_deploy.py merged live master into kanban branch (clean, no conflicts) -> f307977 -> reset+SIGHUP reload, health gate PASSED, pushed origin. Live checks: board/=200, cloud report=200, /md-view=200, traversal /reports/../app.py=400 (blocked, raw+encoded). Migration complete (340 cloud reports; 0 workspace-only left). Skills deep-research/research-brief repointed. Security checks preserved (.. reject, post-resolve _path_is_within containment, suffix allowlist). app.py parses, 5 tests pass. ONE FOLLOW-UP (not a blocker): /reports LISTING is slow (~21s cold+warm) because it scans+reads metadata from 340 files over the rclone mount per request. Fix = a _state/index.json manifest (generators write, listing reads) or a short-TTL listing cache. Tracking as Ticket 1b.
luci-board-manager4d ago
[visibility-only] Routed Luci-owned technical work to internal Hermes Kanban card t_79227b31 on board mc-internal. Worker profile=codexbuilder, pid=3047905, cwd=/home/lucienne/workspace/state/kanban-worktrees/mc-4867-reports-cloud. MC remains visibility-only: no MC runtime/pickup/send/harvest endpoints were used. The ticket is parked while the internal worker produces commit/test/handoff evidence for controller gate.
luci-board-manager4d ago
Controller gate PASS (visibility-only, no MC runtime/pickup used): internal Kanban card t_79227b31 review-required handoff verified. Evidence: `37bc6260` (reports cloud-first implementation) is an ancestor of `origin/master` at `f3079775`; `app.py` AST parse rc=0; `uv run --with flask --with flask-sock --with pyyaml --with croniter --with pytest python -m pytest tests/test_mc4867_reports_cloud.py -q` => 5 passed in 0.59s; live checks on 127.0.0.1:3001 returned cloud `/reports/...html` 200, `/md-view?file=reports/...md` 200, workspace-only fallback temp report 200, `/board` 200, and traversal `/reports/../app.py` rejected with 400. Claude skills commit `89e293a` is on `/home/lucienne/.claude` origin/master; unrelated dirty telegram/server.ts file remains untouched. Marking MC-4867 done.