diff --git a/.ciagent/ARCHITECTURE.md b/.ciagent/ARCHITECTURE.md index 88d98e9..71dd724 100644 --- a/.ciagent/ARCHITECTURE.md +++ b/.ciagent/ARCHITECTURE.md @@ -47,7 +47,7 @@ FastAPI routes: POST /api/runs, GET /api/methodologies, GET /api/runs/{id}, appr - **DependsOn:** engine, methodologies, config ### 7. Frontend (Next.js App Router, TypeScript strict) -Screens: Ask (question + optional context), Choose (methodology cards with ETA/cost/why-recommended; dials behind "advanced"), Plan & Run (approve/edit plan, live SSE trace, pause/cancel), Reading Room v0 (rendered report, hover-a-citation → exact quoted passage, ledger panel, Markdown/JSON export), plus v2 surface mockups (project graph, task inbox) in the prototype. +App shell (root layout, P5): persistent sidebar (primary nav — New research / Library / Settings; adapter connection-status dots as a v2 preview) + top bar (⌘K navigation palette — screens + stored reports; avatar button → user menu with the local single-user profile). Screens: Ask (question + optional context), Choose (methodology cards with ETA/cost/why-recommended; dials behind "advanced"), Plan & Run (approve/edit plan, live SSE trace, pause/cancel), Reading Room v0 (rendered report, hover-a-citation → exact quoted passage, ledger panel, Markdown/JSON export), Library (P5: folder-structure report storage — auto-categorized folders, recency/title sorting, report cards into the Reading Room; cross-report Connections panel — shared-sources ⇄ / related-topic → edges; project graph + task inbox as v2-preview panels), Settings (P5: masked API-key status, storage directory, default methodology + resolved 12-dial display, appearance preview, about/version). - **Boundaries:** consumes the typed `ApiClient` interface only — components never import the mock client directly. In v0.1 the interface is bound to `MockApiClient` (fixture data + scripted event replay); in v0.2 phase 3 the binding swaps to the OpenAPI-generated client with no component changes. - **DependsOn:** ApiClient contract (v0.1: mock; v0.2: API layer) @@ -58,7 +58,7 @@ v0.1 (prototype): Ask (question + context) → Choose (methodology slug + dial o v0.2 (MVP): Ask (question + context) → Choose (methodology slug + dial overrides) → POST /api/runs → engine PLANNING (planner model via model adapter, dial bundle resolved) → AWAITING_APPROVAL (plan persisted; SSE events; user approves/edits) → SEARCHING (search adapter, routing per dials) → EXTRACTING (fetch pages → evidence store, stable passage IDs) → REFLECTING (planner model; depth loop per dials) → SYNTHESIZING (writer model; context = evidence passages only, per-claim citation markers) → VERIFYING (liveness pass, quote anchoring, confidence labels) → DONE (report + ledger + checklist persisted to storage; Reading Room renders; export). ## Build Order -Milestone v0.1 (prototype): 1. Frontend scaffold + ApiClient/MockApiClient boundary → 2. Ask + Choose → 3. Plan & Run + Reading Room v0 → 4. v2 mockups + polish + 3-click validation → 5. review + owner approval gate + ship. +Milestone v0.1 (prototype): 1. Frontend scaffold + ApiClient/MockApiClient boundary → 2. Ask + Choose → 3. Plan & Run + Reading Room v0 → 4. v2 mockups + polish + 3-click validation → 5. review + owner approval gate (owner returned gate feedback → extension cycle: 5. app shell + research library + settings + avatar → 6. final review + approval re-presentation + ship). Milestone v0.2 (MVP, gated on v0.1 approval): 1. Methodology framework (dial schema + 2 seed methodologies + registry) diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index e157cc1..7b400fd 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,8 +1,8 @@ { "phase": 6, - "stage": "execute", + "stage": "approval_pending", "milestone": "v0.1", "phase_role": "final", "attempts": 0, - "updated_at": "2026-09-27T03:30:00Z" -} + "updated_at": "2026-09-27T04:40:00Z" +} \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index 8f2da8a..27bb1aa 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -1,53 +1,54 @@ -# PLAN — Milestone v0.1 Phase 5: App Shell + Research Library + Settings + Avatar +# PLAN — Milestone v0.1 Phase 6 (FINAL): Review + Approval Re-presentation + Ship --- -phase: 5 +phase: 6 milestone: v0.1 -status: planned +status: executed must_haves: - - "Persistent app shell (sidebar + top bar + avatar menu) on every screen, adding zero required clicks to the happy path (REQ-63, REQ-66)" - - "Research library with folder-structure storage view, auto-categorization, sorting, and cross-report connection drawing in the project graph (REQ-64)" - - "Settings screen — API keys (masked), storage endpoint, methodology defaults, appearance slot, about/version — all through the ApiClient boundary (REQ-65)" - - "All new data access extends the ApiClient interface + MockApiClient fixtures; zero throwaway for v0.2 (REQ-53/54 discipline)" + - "Full-milestone review: PASS-WITH-FIXES — deep-link P0 (stored-report cards resolved to the wrong run) + 5 P1s fixed and verified in-phase; 75/75 tests" + - "Audit: PASS-WITH-NOTES — reconstruction, tags, file/branch/commit discipline, secrets sweep all green; notes applied (version bump, ROADMAP status, ARCHITECTURE surfaces)" + - "Final phase patch v0.1.6 shipped — milestone release candidate, presented at the REQ-62 owner approval gate" personas: - - frontend-engineer - ci-lead-developer -requirements: [REQ-63, REQ-64, REQ-65, REQ-66] +requirements: [REQ-62] --- ## User-Facing Surface -Five routes gain a persistent shell; two new surfaces ship: +The extended prototype presented for owner approval: the full 5-screen click-path (Ask → Choose → Plan & Run → Reading Room → Library) now inside the persistent app shell (sidebar + top bar + avatar), the folder-structure research library with cross-report connections, the settings surface (API keys, storage, methodology defaults, appearance, about), and the ⌘K navigation palette. Releases v0.1.0–v0.1.5; v0.1.6 is the extended-prototype release candidate. -1. **App shell** (all screens): left sidebar — New research (Ask), Library, Settings nav + future slots (connection-status dots previewing v0.2 adapter health, methodology-defaults preview); top bar — search/⌘K slot (navigation-only command palette: screens + stored reports) + avatar button (right) opening a user menu (profile, Settings, About). Honest "preview" pills on non-functional slots, per the established library-banner pattern. -2. **Research library** (`/library`): folder-structure view of stored reports (auto-categorized by topic + methodology, sortable by recency/title), report cards linking to Reading Room, project graph extended with cross-report edges (shared-source ⇄ and topical → connections), task inbox retained. -3. **Settings** (`/settings`): API keys section (model: Ollama cloud/local; search: Tavily — masked values, save wired through the mock boundary), storage endpoint (local directory), methodology defaults (resolved 12-dial display), appearance (theme slot, marked preview — paper palette is light-only), about/version (v0.1.5, prototype notice). +## Happy Path (owner review script) -## Happy Path +1. `cd frontend && pnpm install && pnpm dev` → http://localhost:3000 → shell renders on /ask +2. Ask → question → Research this (click 1) +3. Choose → Deep Report card → Start research (click 2) +4. Plan & Run → Approve plan (click 3) → live trace → pause/resume/cancel +5. Read the report → hover [n] → exact quote; ledger; export Markdown/JSON +6. Sidebar → Library → folder tree → open a stored report → matches its card title +7. Library → Connections → shared-source ⇄ and related-topic → edges +8. ⌘K → type "lith" → jump to the stored report +9. Avatar → user menu → Settings → API keys masked, save round-trips +10. Owner decision: approve → v0.1.6 published + milestone merge to main; or request changes -1. `cd frontend && pnpm install && pnpm dev` → http://localhost:3000 → shell renders (sidebar + top bar) on /ask -2. New research → question → Research this (click 1) → Choose → Start research (click 2) → Approve plan (click 3) — ≤3 clicks, shell adds zero -3. Sidebar → Library → browse folder tree → open a stored report → Reading Room -4. Library → Connections panel → cross-report edges visible (⇄ shared sources, → related topic) -5. Top bar avatar → user menu → Settings → API keys + storage + methodology defaults render; save persists (mock) -6. ⌘K (top bar search slot) → type "lib" → navigate to Library +## What this phase did + +1. **Review** (multi-persona, full milestone diff `main..HEAD`, P5-weighted): verdict PASS-WITH-FIXES. P0-1: stored-report deep links resolved to the wrong run (every library card + palette entry opened the demo report under a mismatched id) — fixed via the `#storedRun` resolver + card-title override in the scripted builders; probe + boundary tests prove run/report/ledger/export agree. P1s fixed in-phase: avatar-menu dismiss race (toggleRef guard, pinned by a new test), lost v2-preview pills on graph/inbox panels, stale root README, settings stale-after-save + non-live dial preview (now previews the live selection with formatted values), doc rot (run-page header, VERSIONING.md table). Suite 72 → 75. +2. **Audit**: PASS-WITH-NOTES — tags v0.1.0–v0.1.5 verified annotated + on-branch and mapped to ship records; REQ traceability consistent with ---ci--- blocks; secrets clean across full history; branch hygiene exact. Notes applied in this phase: settings About bumped to v0.1.6 (P1-watch), ROADMAP P6 → in_progress, ARCHITECTURE.md §7 + Build Order extended with P5/P6 surfaces, release README example bumped; `---/ci---` closer convention restored going forward. +3. **Ship (this commit sequence)**: PR #6 (phase/06 → milestone), squash-merge, tag v0.1.6, release with full milestone summary. Per REQ-62 and the prototype_approval escalation hook, the milestone merge to main does NOT proceed autonomously — the pipeline halts at the gate for the recorded owner decision. ## UX Acceptance Criteria -- Shell on every screen; sidebar/top bar never break the 68ch reading measure (Reading Room unaffected) -- 3-click happy path pinned green post-shell (three-click-path.test.tsx extended) -- No placeholder/stub text beyond honest "preview" pills; typography care per vision §4 on all new surfaces -- All new data through `api.*` — no component imports MockApiClient; v0.2 OpenAPI swap unaffected -- Avatar menu keyboard-accessible; focus ring in accent (global :focus-visible standard) +- Owner demo script runs clean on the shipped code (deep-link P0 fix verified by test) +- No placeholder/stub text in owner-visible surfaces; preview pills honest +- The 3-click criterion still pinned green post-shell +- Avatar menu opens/closes cleanly; keyboard accessible throughout -## Waves +## Verification -1. **T-5.1 Boundary first**: types (ReportSummary, ReportFolder, Settings, UserProfile, ConnectionStatus, SavedSettings) → ApiClient gains getReports, getSettings, saveSettings, getUserProfile, getConnectionStatus → MockApiClient + fixtures (stored-reports tree, masked keys, adapter health, profile) → co-located tests -2. **T-5.2 Shell chrome**: sidebar + top bar + palette + avatar components; layout restructure (all 5 routes under the shell); dedupe per-page `
` wrappers; regression tests -3. **T-5.3 Library**: folder tree + sortable cards + cross-report graph edges; extract reusable panels; tests -4. **T-5.4 Settings**: 5 sections, save flow through the boundary; tests -5. **T-5.5 Polish**: README screens table, typography pass, 3-click re-pin, gates green +- Gates: typecheck ✓ lint ✓ tests 75/75 ✓ build ✓ (re-run after review fixes, plus by audit) +- Deep-link boundary test: every stored report resolves to its own id/question/methodology/title +- Post-hoc v0.2 log: demo-run fallback 404 semantics, shared scripted report body disclosure, palette scroll-into-view, raw methodology slugs on cards, pt-[14vh] token ## Scope Guard -P6 (final review + approval re-presentation) follows P5 ship. Milestone merge to main remains gated on the owner's recorded approval (REQ-62, prototype_approval escalation hook). No backend work, no v0.2 scope, no auth/multi-tenancy (local single-user profile only). \ No newline at end of file +Milestone merge to main, v0.1.6 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/REQUIREMENTS.md b/.ciagent/REQUIREMENTS.md index 3581b7b..1f99df9 100644 --- a/.ciagent/REQUIREMENTS.md +++ b/.ciagent/REQUIREMENTS.md @@ -122,4 +122,4 @@ | REQ-25, REQ-26, REQ-33 (API, client, tooling) | v0.2 | P3 | blocked | | REQ-17, REQ-32, REQ-34 (adversarial, E2E, round-trip) | v0.2 | P4 | blocked | | All REQ-01..34 (milestone gate) | v0.2 | P5 | blocked | -| REQ-35..51 | v2 backlog (post-MVP milestones) | — | pending | \ No newline at end of file +| REQ-35..51 | v2 backlog (post-MVP milestones) | — | pending | diff --git a/.ciagent/ROADMAP.md b/.ciagent/ROADMAP.md index dc19479..5d60989 100644 --- a/.ciagent/ROADMAP.md +++ b/.ciagent/ROADMAP.md @@ -52,7 +52,7 @@ Two milestones, owner-directed 2026-09-27. **Milestone v0.1 — Prototype**: the - **Requirements covered**: REQ-62 (gate) + milestone approval of REQ-52..66 - **Success criteria**: review PASS (P0s fixed in-phase); audit PASS; v0.1.6 tagged + release; pipeline halts at the approval gate for the owner's recorded decision - **Depends on**: 5 -- **Status**: planned +- **Status**: in_progress (review PASS-WITH-FIXES — deep-link P0 + 5 P1s fixed; audit PASS-WITH-NOTES; shipping as v0.1.6, then the gate halts for the owner decision) ## Milestone v0.2 — MVP (feature) — BLOCKED until prototype approval @@ -92,4 +92,5 @@ Phases renumbered from the original v1.0 plan (1-based within the milestone): - **v0.1.6 = milestone release** (P6 final phase patch — the approved extended prototype release, pending owner decision) - **v0.2.x = MVP release** (MAJOR still 0; ship only if all v0.2 phases complete) - If MVP scope extends: additional 0.x minor milestones (v0.3, …), each a full milestone cycle -- **v1.0.0**: owner-approved release only — not created by the pipeline autonomously \ No newline at end of file +- **v1.0.0**: owner-approved release only — not created by the pipeline autonomously + diff --git a/.ciagent/release/README.md b/.ciagent/release/README.md index 2475bc4..3cffd00 100644 --- a/.ciagent/release/README.md +++ b/.ciagent/release/README.md @@ -28,7 +28,7 @@ bash .ciagent/release/deploy.sh Examples: ```bash -bash .ciagent/release/deploy.sh v0.1.4 # deploy tagged version +bash .ciagent/release/deploy.sh v0.1.5 # deploy tagged version bash .ciagent/release/deploy.sh v0.2.1 # future releases ``` diff --git a/.ciagent/release/VERSIONING.md b/.ciagent/release/VERSIONING.md index 5919c6a..4973932 100644 --- a/.ciagent/release/VERSIONING.md +++ b/.ciagent/release/VERSIONING.md @@ -8,7 +8,7 @@ as established by the CIAgent branch strategy and `config.json`. | Milestone | Tags | Deployed by | |---|---|---| -| v0.1 prototype | v0.1.0 … v0.1.4 | `bash .ciagent/release/deploy.sh v0.1.4` | +| v0.1 prototype | v0.1.0 … v0.1.5 | `bash .ciagent/release/deploy.sh v0.1.5` | | v0.2 MVP | v0.2.0 … | `bash .ciagent/release/deploy.sh v0.2.x` | ## Rules @@ -32,7 +32,8 @@ as established by the CIAgent branch strategy and `config.json`. `deploy.sh`'s parameter block. 6. **Milestone gating applies to deployment too.** The v0.1 prototype deploys for owner review. v0.2 releases deploy only after the - v0.1.4 owner-approval gate is cleared. + REQ-62 owner-approval gate is cleared (recorded on the extended + prototype, v0.1.6). ## Container conventions diff --git a/README.md b/README.md index da9be12..98a3da7 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ pnpm install pnpm dev ``` -Open http://localhost:3000 — you land on **Ask**. Type a question, click **Research this**, pick a methodology on **Choose** (Deep Report is the recommended default), and press **Start research**. +Open http://localhost:3000 — you land on **Ask**, inside the app shell (left sidebar, top bar with ⌘K search and your avatar). Type a question, click **Research this**, pick a methodology on **Choose** (Deep Report is the recommended default), and press **Start research**. Three clicks from cold start to initiated research. @@ -24,7 +24,10 @@ Three clicks from cold start to initiated research. | `/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]` | 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. | +| `/library` | Stored reports filed into auto-categorized folders (sortable, links into the Reading Room), the Connections panel (shared sources ⇄, related topics →), plus v2-preview project graph and task inbox. | +| `/settings` | API-key status (masked, never raw), the reports storage directory, methodology defaults with the resolved 12 dials, appearance, and about/version. | + +The persistent shell (every screen): sidebar nav + adapter connection-status dots, top bar with the ⌘K navigation palette (screens + stored reports) and the avatar menu (local profile, Settings, About). 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/src/app/ask/page.tsx b/frontend/src/app/ask/page.tsx index a5e8ccd..87e77e7 100644 --- a/frontend/src/app/ask/page.tsx +++ b/frontend/src/app/ask/page.tsx @@ -28,4 +28,4 @@ export default function AskPage() {

); -} \ No newline at end of file +} diff --git a/frontend/src/app/choose/page.tsx b/frontend/src/app/choose/page.tsx index 31eda5e..1fdd687 100644 --- a/frontend/src/app/choose/page.tsx +++ b/frontend/src/app/choose/page.tsx @@ -11,4 +11,4 @@ import { api } from "@/lib/api/client"; export default async function ChoosePage() { const methodologies = await api.getMethodologies(); return ; -} \ No newline at end of file +} diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index dfd50f8..907c97e 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -36,4 +36,4 @@ export default function RootLayout({ ); -} \ No newline at end of file +} diff --git a/frontend/src/app/library/page.tsx b/frontend/src/app/library/page.tsx index c5172cc..20215fc 100644 --- a/frontend/src/app/library/page.tsx +++ b/frontend/src/app/library/page.tsx @@ -11,4 +11,4 @@ export default function LibraryPage() { ); -} \ No newline at end of file +} diff --git a/frontend/src/app/run/[id]/page.tsx b/frontend/src/app/run/[id]/page.tsx index a50ad21..005d141 100644 --- a/frontend/src/app/run/[id]/page.tsx +++ b/frontend/src/app/run/[id]/page.tsx @@ -1,12 +1,11 @@ import { RunView } from "@/components/run-view"; /** - * /run/[id] (T-3.2b) — placeholder for the Plan & Run screen. + * /run/[id] (T-2.4, REQ-57) — the Plan & Run screen. * - * Phase 2 ships the real screen (plan approval, live scripted trace, - * pause/cancel). For now: the run's question + methodology and a clear - * "ships in phase 2" notice, so the Start-research click-path lands - * somewhere honest. + * Server shell for the run stage: plan approval, the live scripted + * trace, and the terminal-state onward links. Run data flows through + * the ApiClient boundary inside RunView. */ export default async function RunPage({ params, @@ -19,4 +18,4 @@ export default async function RunPage({ ); -} \ No newline at end of file +} diff --git a/frontend/src/app/run/[id]/report/page.tsx b/frontend/src/app/run/[id]/report/page.tsx index 265c145..e79c88a 100644 --- a/frontend/src/app/run/[id]/report/page.tsx +++ b/frontend/src/app/run/[id]/report/page.tsx @@ -11,4 +11,4 @@ export default async function ReportPage({ ); -} \ No newline at end of file +} diff --git a/frontend/src/app/settings/page.tsx b/frontend/src/app/settings/page.tsx index 87d0067..f148b52 100644 --- a/frontend/src/app/settings/page.tsx +++ b/frontend/src/app/settings/page.tsx @@ -11,4 +11,5 @@ export default function SettingsPage() { ); -} \ No newline at end of file +} + diff --git a/frontend/src/components/app-shell.test.tsx b/frontend/src/components/app-shell.test.tsx index db91008..8d1e72c 100644 --- a/frontend/src/components/app-shell.test.tsx +++ b/frontend/src/components/app-shell.test.tsx @@ -115,6 +115,16 @@ describe("AppTopBar (REQ-63, REQ-66)", () => { await user.keyboard("{Escape}"); expect(screen.queryByRole("menu", { name: "User menu" })).toBeNull(); }); + + it("clicking the avatar again closes the menu (toggle, not reopen)", async () => { + const user = userEvent.setup(); + render(); + const avatar = screen.getByRole("button", { name: "User menu" }); + await user.click(avatar); + await screen.findByRole("menu", { name: "User menu" }); + await user.click(avatar); + expect(screen.queryByRole("menu", { name: "User menu" })).toBeNull(); + }); }); describe("CommandPalette (REQ-63)", () => { diff --git a/frontend/src/components/app-top-bar.tsx b/frontend/src/components/app-top-bar.tsx index aa946ea..54d29e6 100644 --- a/frontend/src/components/app-top-bar.tsx +++ b/frontend/src/components/app-top-bar.tsx @@ -11,7 +11,7 @@ */ import { usePathname, useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; +import { forwardRef, useEffect, useRef, useState } from "react"; import { api, type ReportLibrary, @@ -26,6 +26,7 @@ export function AppTopBar() { const [paletteOpen, setPaletteOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [library, setLibrary] = useState(null); + const avatarRef = useRef(null); // ⌘K / Ctrl-K opens the palette from anywhere in the app. useEffect(() => { @@ -86,11 +87,16 @@ export function AppTopBar() { - setMenuOpen((v) => !v)} /> + setMenuOpen((v) => !v)} + /> {menuOpen && ( setMenuOpen(false)} onDismiss={() => setMenuOpen(false)} /> @@ -111,13 +117,10 @@ export function AppTopBar() { } /** The avatar disc — initials from the profile fixture. */ -function AvatarButton({ - menuOpen, - onToggle, -}: { +const AvatarButton = forwardRef void; -}) { +}>(function AvatarButton({ menuOpen, onToggle }, ref) { const [profile, setProfile] = useState(null); useEffect(() => { let cancelled = false; @@ -136,6 +139,7 @@ function AvatarButton({ return (