diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json
index 278c0c3..44900f6 100644
--- a/.ciagent/CHECKPOINT.json
+++ b/.ciagent/CHECKPOINT.json
@@ -1,8 +1,8 @@
{
- "phase": 3,
- "stage": "complete",
+ "phase": 4,
+ "stage": "verify",
"milestone": "v0.1",
- "phase_role": "execution",
+ "phase_role": "final",
"attempts": 0,
- "updated_at": "2026-09-27T02:10:00Z"
+ "updated_at": "2026-09-27T02:30:00Z"
}
\ No newline at end of file
diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md
index 3b9c693..14185c9 100644
--- a/.ciagent/PLAN.md
+++ b/.ciagent/PLAN.md
@@ -1,64 +1,50 @@
-# PLAN — Milestone v0.1 Phase 3: v2 Surface Mockups + Polish + 3-Click Validation
+# PLAN — Milestone v0.1 Phase 4 (FINAL): Review + Owner Approval Gate + Ship
---
-phase: 3
+phase: 4
milestone: v0.1
-status: planned
+status: executed
must_haves:
- - "Project graph mockup panel: cross-linked sources and findings across projects, rendered from mock data (REQ-60)"
- - "Task inbox mockup panel: open questions become checkable follow-ups, rendered from mock data (REQ-60)"
- - "Typography care pass: reader experience per vision §4 — report body measure/leading, section rhythm, small-caps/labels audit across all screens (REQ-61)"
- - "≤3-click happy path validated on defaults: Ask → Choose → Run initiated in 3 clicks, verified by test (REQ-61)"
+ - "Full-milestone review: PASS-WITH-FIXES verdict — all 5 P0s fixed and verified (drawer separators, empty-question guard, border-hairline token, fixture URL honesty, README accuracy)"
+ - "Audit: PASS — reconstruction test, file discipline, branch hygiene, commit discipline, tag verification, critical-issue check all green"
+ - "Final phase patch v0.1.4 shipped (pending owner approval for milestone merge to main)"
personas:
- - frontend-engineer
-requirements: [REQ-60, REQ-61]
+ - ci-lead-developer
+requirements: [REQ-62]
---
## User-Facing Surface
-- **`/library`** — the v2-surface mockup page: two panels rendered from fixture data — (1) **project graph**: nodes for research topics/projects with cross-links to shared sources and findings (visual mockup: SVG or CSS positioned nodes with labeled edges, clearly marked "v2 preview"); (2) **task inbox**: open questions extracted from past runs as checkable follow-up items with status toggles. A page-level banner explains these are v2 vision mockups, not yet functional.
-- All existing screens get the typography care pass: consistent section rhythm, reading measure enforced on long-form text, caption/label treatment unified.
-- Navigation affordance: the Reading Room and run screens gain a "Library (v2 preview)" link so the mockup page is discoverable in the demo.
+The prototype deliverable presented for owner approval: the full 5-screen click-path (Ask → Choose → Plan & Run → Reading Room → Library preview), README quickstart, releases v0.1.0–v0.1.3, and this phase's review-driven polish (P0 fixes) as v0.1.4-pending.
-## Happy Path
+## Happy Path (owner review script)
-1. Owner runs the full P1+P2 path (ask → choose → approve → trace → report)
-2. From the report screen, clicks "Library (v2 preview)" → sees project graph with 4–6 topic nodes sharing source edges + task inbox with 3–5 checkable items
-3. Toggles a task's checkbox → item visibly flips status (client state only)
-4. Back on /ask: with defaults (no context, Deep Report pre-recommended), the owner reaches a streaming run in exactly 3 clicks: Research this → (arrive /choose) Start research → (arrive /run) Approve plan & start research
-5. The 3-click criterion is pinned by an automated test, not just manual belief
+1. `cd frontend && pnpm install && pnpm dev` → http://localhost:3000
+2. Ask → question → Research this (click 1)
+3. Choose → Deep Report recommended card → Start research (click 2)
+4. Plan & Run → Approve plan (click 3) → live trace → pause/resume/cancel
+5. Read the report → hover [2] → exact quote; ledger panel; export Markdown/JSON
+6. Library (v2 preview) → graph + inbox
+7. Owner decision: approve → v0.1.4 + milestone merge to main; or request changes
+
+## What this phase did
+
+1. **Review** (multi-persona, full milestone diff `main..HEAD`): verdict PASS-WITH-FIXES — 5 P0s (all owner-visible) + 2 P1s + P2 notes. All P0s fixed in-phase: dials separator rendering (P1-1 flag from P1), empty-question guard (P1-2 flag from P1), border-hairline Tailwind token (21 surfaces were near-black), fixture URL switched from a real arXiv link (resolving to an unrelated paper) to example.org, README screens table rewritten for all 5 routes. P1s: report title hardcodes Deep Report shape (documented prototype limitation), getRun fallback lacks a demo-run caption (documented). P2 notes logged for v0.2 hardening.
+2. **Audit**: AUDIT PASS — tags v0.1.0–v0.1.3 verified annotated + on milestone branch; REQ-52..61 spot-checked as genuinely delivered; secrets never committed; branch hygiene clean; commit discipline PASS (2 zero-loss deviations noted); gates re-run green 42/42.
+3. **Ship (this commit sequence)**: PR #4 (phase/04 → milestone), squash-merge, tag v0.1.4, release with full milestone summary. **Milestone merge to main happens ONLY after recorded owner approval (REQ-62)** — the pipeline halts at the gate.
## UX Acceptance Criteria
-- Graph: nodes labeled with topic names; edges show shared-source counts; hover on node highlights it (title attribute acceptable); visually distinct from production screens ("v2 preview" banner + muted styling)
-- Inbox: items show question text + originating run status; checkboxes toggle open↔done; at least one item pre-checked
-- Typography: body text ≥16px, reading measure ≤68ch on report/ask/choose long-form, consistent heading scale, no clipped or truncated labels
-- 3-click test: automated test counts discrete user clicks from /ask to a streaming run using default methodology — must be ≤3
-- Quality gates: `pnpm typecheck && pnpm lint && pnpm test && pnpm build` green
-
-## Tasks (wave-ordered; persona: frontend-engineer unless noted)
-
-### Wave 1 — Fixtures
-- **T-3.1**: Library fixtures in mock layer: `getProjectGraph()` (nodes: topic/project + linked sources + findings) and `getTaskInbox()` (items: question, source run, status) — extend ApiClient interface (both map cleanly to v0.2 GET /api/graph + GET /api/tasks) (REQ-60)
-
-### Wave 2 — Library page
-- **T-3.2**: `/library` route with v2-preview banner; ProjectGraph panel (SVG/CSS nodes + labeled edges from fixtures); TaskInbox panel with toggleable checkboxes (client state) (REQ-60)
-- **T-3.3**: Navigation links: Reading Room + run screens gain "Library (v2 preview)" link; root nav hint if layout has one (REQ-60)
-
-### Wave 3 — Typography pass
-- **T-3.4**: Audit + fix typography across screens: reading measure on long-form, section rhythm (spacing-section usage), caption/label letter-spacing consistency, heading scale order (display → headline → body) (REQ-61)
-
-### Wave 4 — 3-click validation
-- **T-3.5**: Automated 3-click test: /ask → Research this (1) → /choose → Start research on recommended default (2) → /run → Approve plan (3) → assert trace streaming; document the count in the test name (REQ-61)
-
-### Wave 5 — Gate (persona: ci-lead-developer)
-- **T-3.6**: Boundary review + gate run; P1-flag fixes deferred to P4 are NOT pulled forward (P3 stays scoped)
+- Owner demo script runs clean on the shipped code
+- No placeholder/stub text in owner-visible surfaces
+- The 3-click criterion still pinned green post-fixes
## Verification
-- Gates green; happy path walk passes; REQ-60, REQ-61 covered
-- 3-click criterion pinned by test
+- Gates: typecheck ✓ lint ✓ tests 42/42 ✓ build ✓ (re-run after P0 fixes, plus by audit)
+- border-hairline utility verified present in compiled production CSS
+- Review P0 worklist: 5/5 closed with tests where feasible (dials unit test, empty-question guard test)
## Scope Guard
-P3 only. The P4 final phase handles: review, audit, P1 flags (P1-1 drawer hyphen rendering from P1, P1-2 empty-question guard from P1, P2's routed P2 notes), owner approval gate, milestone merge. No v0.2 work.
\ No newline at end of file
+Milestone merge to main, v0.1.4 release publication, v0.2 unblocking, and all v0.2 P1 work wait on the owner approval gate (REQ-62, prototype_approval escalation hook). The pipeline does not proceed autonomously past this commit.
\ No newline at end of file
diff --git a/.ciagent/ROADMAP.md b/.ciagent/ROADMAP.md
index 45b08b5..eb6e284 100644
--- a/.ciagent/ROADMAP.md
+++ b/.ciagent/ROADMAP.md
@@ -38,7 +38,7 @@ Two milestones, owner-directed 2026-09-27. **Milestone v0.1 — Prototype**: the
- **Requirements covered**: milestone gate (REQ-52..61 approved)
- **Success criteria**: owner approval recorded; v0.1.4 tagged; milestone branches deleted per ship workflow
- **Depends on**: 3
-- **Status**: not_started
+- **Status**: in_progress (review PASS-WITH-FIXES → 5 P0s fixed; audit PASS; awaiting owner approval)
## Milestone v0.2 — MVP (feature) — BLOCKED until prototype approval
diff --git a/README.md b/README.md
index f3b2fef..da9be12 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,9 @@ Three clicks from cold start to initiated research.
|---|---|
| `/ask` | One question box + one optional context box. Nothing else competing for attention. |
| `/choose` | All 8 locked methodology cards — mock ETA, cost estimate, why-recommended, and the 12 methodology dials behind each card's Advanced drawer. |
-| `/run/[id]` | Placeholder — Plan & Run ships in phase 2. |
+| `/run/[id]` | Plan & Run: approve the research plan (sub-questions, source universe, outline), watch the live trace stream, pause/resume or cancel. |
+| `/run/[id]/report` | Reading Room: the report with page-anchored citations — hover or focus any `[n]` marker for the exact quoted passage — plus the citation ledger and Markdown/JSON exports. |
+| `/library` | v2 preview: project graph and task inbox mockups rendered from fixture data. |
All data flows through a typed `ApiClient` interface backed by `MockApiClient` — the same components will talk to the FastAPI backend via the OpenAPI-generated client in the MVP milestone, with zero rewrites.
diff --git a/frontend/README.md b/frontend/README.md
index 3ec3f6c..499420b 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -61,7 +61,7 @@ The happy path:
```
src/
- app/ App Router routes (ask, choose, run/[id], run/[id]/report)
+ app/ App Router routes (ask, choose, run/[id], run/[id]/report, library)
components/ UI components (server by default, client where interactive)
lib/ Domain types, ApiClient boundary, mock data, stores
test/ Test harness setup
@@ -71,5 +71,5 @@ Prototype scope note: ETAs, cost estimates, and the scripted run are
realistic fixture data — not live research. The trace stream replays a
deterministic Deep Report run with pause/resume/cancel controls; the
Reading Room's report, ledger, and exports are generated from the same
-fixture. The v2 surface mockups (project graph, task inbox) ship in the
-next phase.
\ No newline at end of file
+fixture. The `/library` project graph and task inbox are v2 preview
+mockups behind the same ApiClient boundary.
\ No newline at end of file
diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css
index 4d97c74..065d95b 100644
--- a/frontend/src/app/globals.css
+++ b/frontend/src/app/globals.css
@@ -50,6 +50,7 @@
--color-ink-soft: #4a4945; /* secondary text */
--color-ink-faint: #8a8880; /* muted metadata */
--color-line: #e5e2da; /* hairline borders */
+ --color-hairline: #e5e2da; /* border-hairline utility */
--color-accent: #1e5c3f; /* deep green — primary action */
--color-accent-strong: #164732; /* hover / pressed */
diff --git a/frontend/src/components/choose-view.test.tsx b/frontend/src/components/choose-view.test.tsx
index 0fe962f..069e6b6 100644
--- a/frontend/src/components/choose-view.test.tsx
+++ b/frontend/src/components/choose-view.test.tsx
@@ -116,4 +116,26 @@ describe("ChooseView", () => {
expect(runRoute).toMatch(/^\/run\/run-[a-z0-9]+$/);
});
});
+
+ it("empty-question guard: Start research disabled without a question (P1-2 fix)", async () => {
+ const methodologies = await loadFixtures();
+ const deepReport = methodologies.find((m) => m.slug === "deep-report");
+ if (!deepReport) throw new Error("fixture missing deep-report");
+
+ // Direct /choose navigation or a hard refresh: no carried question.
+ setAskState({ question: "" });
+ render(
+ ,
+ );
+
+ const start = screen.getByRole("button", { name: "Start research" });
+ expect(start).toBeDisabled();
+ expect(start).toHaveAttribute("aria-disabled", "true");
+
+ // Whitespace-only questions are equally blocked.
+ render(
+ ,
+ );
+ expect(screen.getAllByRole("button", { name: "Start research" })[0]).toBeDisabled();
+ });
});
\ No newline at end of file
diff --git a/frontend/src/components/methodology-card.tsx b/frontend/src/components/methodology-card.tsx
index e893770..51d9e23 100644
--- a/frontend/src/components/methodology-card.tsx
+++ b/frontend/src/components/methodology-card.tsx
@@ -128,7 +128,13 @@ export function MethodologyCard({