docs(P04): complete final review + audit + approval-gate phase
Final phase: full-milestone review PASS-WITH-FIXES (5 P0s fixed with tests), audit PASS, PLAN/CHECKPOINT advance. REQ-62 owner approval gate: milestone merge to main + v0.1.4 publication halt for recorded owner approval. Gates 42/42 green.
This commit was merged in pull request #4.
This commit is contained in:
@@ -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"
|
||||
}
|
||||
+30
-44
@@ -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.
|
||||
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.
|
||||
+1
-1
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
+3
-3
@@ -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.
|
||||
fixture. The `/library` project graph and task inbox are v2 preview
|
||||
mockups behind the same ApiClient boundary.
|
||||
@@ -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 */
|
||||
|
||||
@@ -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(
|
||||
<MethodologyCard methodology={deepReport} question={""} />,
|
||||
);
|
||||
|
||||
const start = screen.getByRole("button", { name: "Start research" });
|
||||
expect(start).toBeDisabled();
|
||||
expect(start).toHaveAttribute("aria-disabled", "true");
|
||||
|
||||
// Whitespace-only questions are equally blocked.
|
||||
render(
|
||||
<MethodologyCard methodology={deepReport} question={" "} />,
|
||||
);
|
||||
expect(screen.getAllByRole("button", { name: "Start research" })[0]).toBeDisabled();
|
||||
});
|
||||
});
|
||||
@@ -128,7 +128,13 @@ export function MethodologyCard({
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleStart}
|
||||
disabled={starting}
|
||||
disabled={starting || question.trim() === ""}
|
||||
aria-disabled={question.trim() === ""}
|
||||
title={
|
||||
question.trim() === ""
|
||||
? "Ask a question first — the Ask screen is one click away"
|
||||
: undefined
|
||||
}
|
||||
className="w-full rounded-pill bg-accent px-5 py-2.5 text-small font-medium text-paper-raised transition-colors hover:bg-accent-strong disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{starting ? "Starting…" : "Start research"}
|
||||
|
||||
@@ -85,7 +85,7 @@ describe("ReadingRoom", () => {
|
||||
expect(tooltip.textContent).toContain(
|
||||
"Sulfide electrolytes exhibit ionic conductivity comparable to liquid electrolytes",
|
||||
);
|
||||
expect(tooltip.textContent).toContain("arxiv.org");
|
||||
expect(tooltip.textContent).toContain("example.org");
|
||||
expect(tooltip.textContent).toContain("tier 1");
|
||||
});
|
||||
|
||||
@@ -95,7 +95,7 @@ describe("ReadingRoom", () => {
|
||||
expect(markdown.filename).toBe(`researchx-${id}.md`);
|
||||
expect(markdown.content).toContain("## Sources");
|
||||
expect(markdown.content).toMatch(/\[.*\]\(https:\/\/.+\)/);
|
||||
expect(markdown.content).toContain("](https://arxiv.org/abs/2401.00001)");
|
||||
expect(markdown.content).toContain("](https://example.org/arxiv-sulfide-electrolytes-review)");
|
||||
|
||||
const json = await api.exportReport(id, "json");
|
||||
expect(json.filename).toBe(`researchx-${id}.json`);
|
||||
|
||||
@@ -128,7 +128,7 @@ describe("MockApiClient (bound as `api`)", () => {
|
||||
// Per-claim markers survive as footnote-style references…
|
||||
expect(markdown.content).toContain("[^p1]:");
|
||||
// …and each citation is a real Markdown link to its source URL.
|
||||
expect(markdown.content).toContain("](https://arxiv.org/abs/2401.00001)");
|
||||
expect(markdown.content).toContain("](https://example.org/arxiv-sulfide-electrolytes-review)");
|
||||
expect(markdown.content).toContain("✓ live");
|
||||
expect(markdown.content).toContain("△ stale");
|
||||
});
|
||||
|
||||
@@ -36,9 +36,9 @@ export const scriptedCitations: readonly Citation[] = [
|
||||
},
|
||||
{
|
||||
id: "c2",
|
||||
url: "https://arxiv.org/abs/2401.00001",
|
||||
url: "https://example.org/arxiv-sulfide-electrolytes-review",
|
||||
title: "Sulfide Electrolytes for All-Solid-State Lithium Batteries: A Review",
|
||||
site: "arxiv.org",
|
||||
site: "example.org",
|
||||
passageId: "p2",
|
||||
quote:
|
||||
"Sulfide electrolytes exhibit ionic conductivity comparable to liquid electrolytes, but interface degradation during cycling remains the central obstacle to commercial cycle life.",
|
||||
@@ -161,7 +161,7 @@ export function scriptedTraceEvents(runId: string): TraceEvent[] {
|
||||
type: "source_visited",
|
||||
message:
|
||||
"Visited: Sulfide Electrolytes for All-Solid-State Lithium Batteries: A Review",
|
||||
detail: "arxiv.org · authority tier 1",
|
||||
detail: "example.org · authority tier 1",
|
||||
},
|
||||
{
|
||||
id: `${runId}-e09`,
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Dial formatting tests (final-phase review fix — P0-1).
|
||||
*
|
||||
* formatDialValue must preserve separator meaning: hyphens stay
|
||||
* hyphens ("per-claim" → "Per-Claim"), plus stays plus
|
||||
* ("web+academic" → "Web+Academic"). This survived three phases
|
||||
* unpinned; it is pinned now.
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { formatDialValue } from "@/lib/dials";
|
||||
|
||||
describe("formatDialValue", () => {
|
||||
it("numbers pass through as strings", () => {
|
||||
expect(formatDialValue(1)).toBe("1");
|
||||
expect(formatDialValue(3)).toBe("3");
|
||||
});
|
||||
|
||||
it("preserves hyphens: per-claim renders Per-Claim, not Per+Claim", () => {
|
||||
expect(formatDialValue("per-claim")).toBe("Per-Claim");
|
||||
expect(formatDialValue("plan-only")).toBe("Plan-Only");
|
||||
expect(formatDialValue("plan+draft")).toBe("Plan+Draft");
|
||||
});
|
||||
|
||||
it("preserves plus separators: web+academic renders Web+Academic", () => {
|
||||
expect(formatDialValue("web+academic")).toBe("Web+Academic");
|
||||
});
|
||||
|
||||
it("single words title-case; connectors stay lowercase", () => {
|
||||
expect(formatDialValue("encyclopedic")).toBe("Encyclopedic");
|
||||
// Space-separated words beyond connectors title-case normally;
|
||||
// the connector rule keeps "and"/"or" lowercase mid-compound.
|
||||
expect(formatDialValue("breadth and depth")).toBe("Breadth and depth");
|
||||
});
|
||||
});
|
||||
@@ -9,14 +9,21 @@ import { DIALS } from "@/lib/types";
|
||||
/** Humanize a raw dial value: numbers pass through, strings title-case. */
|
||||
export function formatDialValue(value: DialValues[keyof DialValues]): string {
|
||||
if (typeof value === "number") return String(value);
|
||||
// Split into words AND separators, title-case words, keep the
|
||||
// original separator characters ("per-claim" → "Per-Claim",
|
||||
// "web+academic" → "Web+Academic") — separators are meaningful.
|
||||
return value
|
||||
.split(/[-+]/)
|
||||
.split(/([-+])/)
|
||||
.map((part) =>
|
||||
part === "and" || part === "or"
|
||||
part === "-" || part === "+"
|
||||
? part
|
||||
: part.charAt(0).toUpperCase() + part.slice(1),
|
||||
: part === "and" || part === "or"
|
||||
? part
|
||||
: part === ""
|
||||
? part
|
||||
: part.charAt(0).toUpperCase() + part.slice(1),
|
||||
)
|
||||
.join("+");
|
||||
.join("");
|
||||
}
|
||||
|
||||
/** All 12 dial definitions (for the Advanced drawer). */
|
||||
|
||||
Reference in New Issue
Block a user