diff --git a/.ciagent/CHECKPOINT.json b/.ciagent/CHECKPOINT.json index 7b400fd..14018a5 100644 --- a/.ciagent/CHECKPOINT.json +++ b/.ciagent/CHECKPOINT.json @@ -1,8 +1,8 @@ { - "phase": 6, - "stage": "approval_pending", + "phase": 7, + "stage": "execute", "milestone": "v0.1", - "phase_role": "final", + "phase_role": "execution", "attempts": 0, - "updated_at": "2026-09-27T04:40:00Z" + "updated_at": "2026-09-27T05:10:00Z" } \ No newline at end of file diff --git a/.ciagent/PLAN.md b/.ciagent/PLAN.md index 27bb1aa..a0ce778 100644 --- a/.ciagent/PLAN.md +++ b/.ciagent/PLAN.md @@ -1,54 +1,48 @@ -# PLAN — Milestone v0.1 Phase 6 (FINAL): Review + Approval Re-presentation + Ship +# PLAN — Milestone v0.1 Phase 7: Ask Screen Intelligence — Suggestions + Refine --- -phase: 6 +phase: 7 milestone: v0.1 -status: executed +status: planned must_haves: - - "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" + - "Follow-up query suggestions on the New Research view, derived from unanswered questions (open task-inbox items joined with source reports); click fills the question box — editable, never auto-starts (REQ-67)" + - "Refine option: opt-in back-and-forth dialogue in an inline drawer (scripted clarifying questions → answers → composed refined question + suggested context); accept fills the form, or keep original (REQ-68)" + - "Both features opt-in — zero required clicks added to the ≤3-click happy path (pinned by test)" + - "All new data access extends the ApiClient boundary + MockApiClient fixtures (REQ-53/54 discipline)" personas: + - frontend-engineer - ci-lead-developer -requirements: [REQ-62] +requirements: [REQ-67, REQ-68] --- ## User-Facing Surface -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. **Ask suggestions** (`/ask`, below the primary form): "Pick up an open question" — cards for each open task-inbox item (question text + source report title + "from your research" attribution). Click fills the question textarea (editable, no auto-submit, focus moves to the box). Honest empty state: "No open questions yet — they collect here as you run research." +2. **Refine drawer** (`/ask`, inline): "Refine question" button (disabled until a draft exists) opens the established dials-drawer pattern — one clarifying question at a time with answer input, progress indicator (1/2/3), then the composed refined question + suggested context preview. **Use this** fills question + context and closes; **Keep original** dismisses; **Restart** re-runs the exchange. Escape closes. Honest caption: "Scripted refinement in the prototype — model-driven refine lands in v0.2." -## 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 - -## 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. +1. `cd frontend && pnpm install && pnpm dev` → http://localhost:3000/ask (shell present) +2. Default path unchanged: question → Research this (click 1) → Choose → Start research (click 2) → Approve plan (click 3) +3. Suggestions: scroll to "Pick up an open question" → click a card → question box fills with the open question → edit if desired → Research this as usual +4. Refine: type a draft → "Refine question" → answer the 3 clarifying questions → review the composed question + suggested context → "Use this" → both boxes filled → Research this +5. Keep-original: open the drawer, answer partway, Escape → the original draft is untouched ## UX Acceptance Criteria -- 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 +- ≤3-click default happy path still green (both features opt-in; zero required interactions) +- Suggestions never compete with the primary action (below the form, not modal, no auto-start) +- Refine drawer: draft visible behind, Escape-safe, honest scripted-prototype caption +- Keyboard accessible; focus ring in accent (global standard) +- All data via `api.*` — components never import MockApiClient; v0.2 OpenAPI swap unaffected -## Verification +## Waves -- 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 +1. **T-7.1 Boundary**: types (`SuggestedQuery`, `RefinementQuestion`, `RefinedQuestion`) → ApiClient gains `getSuggestedQueries` (mock: open `taskInboxFixture` items joined with `reportLibraryFixture`; v0.2: GET /api/suggestions), `getRefinementQuestions(draft)` (mock: scripted 3-question set; v0.2: POST /api/refine step 1), `composeRefinedQuestion(draft, answers)` (mock: deterministic composer; v0.2: model-driven) → co-located boundary tests +2. **T-7.2 Suggestions UI**: `AskSuggestions` component on /ask; click-fills-box contract; empty state; tests +3. **T-7.3 Refine drawer**: `RefineQuestionButton` + drawer component; exchange state machine; use/keep/restart; tests +4. **T-7.4 Regression + polish**: 3-click re-pin, README, typography pass, gates green ## Scope Guard -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 +P8 (final review + approval re-presentation) follows P7 ship + UAT deploy. 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 model calls (scripted refine only — the model-driven version is v0.2). \ No newline at end of file diff --git a/.ciagent/REQUIREMENTS.md b/.ciagent/REQUIREMENTS.md index 1f99df9..212db5d 100644 --- a/.ciagent/REQUIREMENTS.md +++ b/.ciagent/REQUIREMENTS.md @@ -25,6 +25,10 @@ - **REQ-65**: Settings surface behind the ApiClient boundary: API keys (model/search, masked), storage endpoint (local directory), methodology defaults (resolved dial display), appearance slot, about/version - **REQ-66**: Avatar button (top bar) opening a user menu: local single-user profile (no auth), links to settings and about; future slots visible but honest (preview-marked) +### Ask Intelligence Extension (P7 — owner gate feedback, directed 2026-09-27) +- **REQ-67**: New Research view surfaces follow-up query suggestions derived from unanswered questions (open task-inbox items from past runs); one click loads a suggestion into the question box for editing — never auto-starts a run +- **REQ-68**: Refine option on the question: an opt-in back-and-forth dialogue (clarifying questions → answers → composed refined question + suggested context); accepting fills the form, or the original is kept + ## MVP (Milestone v0.2 — core loop, gated on prototype approval) ### Methodology Framework @@ -115,8 +119,9 @@ | REQ-57 (Plan & Run) | v0.1 | P2 | complete | | REQ-58, REQ-59 (Reading Room, exports) | v0.1 | P2 | complete | | REQ-60, REQ-61 (v2 mockups, 3-click) | v0.1 | P3 | complete | -| REQ-62 (approval gate) | v0.1 | P6 | in_progress (awaiting owner decision on extended prototype) | +| REQ-62 (approval gate) | v0.1 | P8 | in_progress (awaiting owner decision on extended prototype) | | REQ-63, REQ-64, REQ-65, REQ-66 (app shell, library, settings, avatar) | v0.1 | P5 | complete | +| REQ-67, REQ-68 (ask suggestions, refine dialogue) | v0.1 | P7 | in_progress | | REQ-01..06, REQ-08..11, REQ-31 (framework, adapters) | v0.2 | P1 | blocked | | REQ-07, REQ-12, REQ-13..24 (probing, engine, trust) | v0.2 | P2 | blocked | | REQ-25, REQ-26, REQ-33 (API, client, tooling) | v0.2 | P3 | blocked | diff --git a/.ciagent/ROADMAP.md b/.ciagent/ROADMAP.md index 5d60989..6fbcc47 100644 --- a/.ciagent/ROADMAP.md +++ b/.ciagent/ROADMAP.md @@ -47,12 +47,26 @@ Two milestones, owner-directed 2026-09-27. **Milestone v0.1 — Prototype**: the - **Depends on**: 4 - **Status**: complete (PR #5 squash-merged to milestone/v0.1-prototype, tag v0.1.5, release created; verify PASS with one P1 fixed in-phase — main landmark restored) -### Phase 6 — Final Review + Approval Re-presentation + Ship (FINAL) +### Phase 6 — Final Review + Approval Re-presentation + Ship (shipped v0.1.6 — superseded by 2nd extension) - **Description**: Multi-persona review of the full milestone diff (now including P5), audit, then re-present the **extended prototype** at the REQ-62 owner approval gate. On recorded approval: milestone merge to main, tag v0.1.6 — the milestone release. (v0.2 P1 branches only after this tag.) - **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**: 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) +- **Status**: shipped (PR #6 squash-merged, tag v0.1.6 + release created; review PASS-WITH-FIXES — deep-link P0 + 5 P1s fixed; audit PASS; owner directed UAT deploy then returned 2nd gate feedback → binding extension cycle P7/P8; milestone merge to main stays halted) + +### Phase 7 — Ask Screen Intelligence: Suggestions + Refine (2nd owner gate feedback) +- **Description**: New Research view gains follow-up query suggestions derived from unanswered questions (open task-inbox items joined with their source reports); one click fills the question box — editable, never auto-starts. An opt-in Refine dialogue (inline drawer, one clarifying question at a time, scripted in the prototype) composes a refined question + suggested context; accept fills the form, or keep the original. Both are opt-in affordances — zero required clicks added to the ≤3-click happy path. All data through the ApiClient boundary. +- **Requirements covered**: REQ-67, REQ-68 +- **Success criteria**: suggestions render from open inbox items with source attribution; click fills the box (no auto-submit); refine drawer completes a scripted exchange and composes a refined question; both flows keyboard-accessible; 3-click path pinned green; gates green +- **Depends on**: 6 +- **Status**: in_progress + +### Phase 8 — Final Review + Approval Re-presentation + Ship (FINAL) +- **Description**: Multi-persona review of the full milestone diff (now including P7), audit, then re-present the extended prototype at the REQ-62 owner approval gate. On recorded approval: milestone merge to main, tag v0.1.8 — the milestone release. (v0.2 P1 branches only after this tag.) +- **Requirements covered**: REQ-62 (gate) + milestone approval of REQ-52..68 +- **Success criteria**: review PASS (P0s fixed in-phase); audit PASS; v0.1.8 tagged + release; UAT staged per owner directive; pipeline halts at the approval gate +- **Depends on**: 7 +- **Status**: planned ## Milestone v0.2 — MVP (feature) — BLOCKED until prototype approval @@ -87,9 +101,11 @@ Phases renumbered from the original v1.0 plan (1-based within the milestone): - **Status**: blocked ## Milestone Releases -- **v0.1.4 = extended-prototype candidate** (feature milestone; final phase patch — superseded by the P5/P6 extension cycle) +- **v0.1.4 = extended-prototype candidate** (superseded by the P5/P6 extension cycle) - **v0.1.5 = app shell + research library + settings + avatar** (P5 patch) -- **v0.1.6 = milestone release** (P6 final phase patch — the approved extended prototype release, pending owner decision) +- **v0.1.6 = extended-prototype candidate** (superseded by the P7/P8 extension cycle) +- **v0.1.7 = ask suggestions + refine dialogue** (P7 patch) +- **v0.1.8 = milestone release** (P8 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 diff --git a/frontend/README.md b/frontend/README.md index 6b2184c..b0e7ba1 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -35,6 +35,13 @@ The happy path: The shell adds zero required clicks to that path. Everything else it hosts is wayfinding and accumulated research: +- **Open questions** (`/ask`, below the form) — follow-up suggestions + derived from unanswered questions of your past runs; one click loads + one into the question box for editing (never auto-starts). +- **Refine question** (`/ask`, beside the box) — an opt-in back-and-forth: + a few clarifying questions, then a composed refined question with + suggested context. Use it, or keep your original. Scripted in the + prototype — model-driven refine lands with v0.2. - **Library** (`/library`) — every report you've generated, filed into auto-categorized folders; sort by recency or title; open any card to re-enter its Reading Room. The **Connections** panel draws the links diff --git a/frontend/src/app/ask/page.tsx b/frontend/src/app/ask/page.tsx index 87e77e7..3e2a9bb 100644 --- a/frontend/src/app/ask/page.tsx +++ b/frontend/src/app/ask/page.tsx @@ -1,8 +1,10 @@ -import { AskForm } from "@/components/ask-form"; +import { AskView } from "@/components/ask-view"; /** - * /ask (T-3.1, REQ-55) — the front door: one question box, one optional - * context box, one primary action. Generous whitespace, no competition. + * /ask (T-3.1, REQ-55; T-7.2/7.3, REQ-67/68) — the front door: one + * question box, one optional context box, one primary action. The + * opt-in affordances live below and beside the form — open-question + * suggestions and the refine dialogue — never competing with it. * The app shell (sidebar + top bar) lives in the root layout. */ export default function AskPage() { @@ -21,11 +23,11 @@ export default function AskPage() {

- +

Prototype: runs on scripted mock data — no API keys, no live search.

); -} +} \ No newline at end of file diff --git a/frontend/src/components/ask-form.test.tsx b/frontend/src/components/ask-form.test.tsx deleted file mode 100644 index 42aa577..0000000 --- a/frontend/src/components/ask-form.test.tsx +++ /dev/null @@ -1,82 +0,0 @@ -/** - * AskForm tests (T-3.3, REQ-55). - * - * - Button disabled until the question is non-empty - * - Submit stores the payload in the ask store (state carry) and - * routes to /choose - */ - -import { cleanup, render, screen, waitFor } from "@testing-library/react"; -import userEvent from "@testing-library/user-event"; -import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; -import { AskForm } from "@/components/ask-form"; -import { clearAskState, getAskState } from "@/lib/ask-store"; - -const pushMock = vi.hoisted(() => vi.fn()); - -vi.mock("next/navigation", () => ({ - useRouter: () => ({ push: pushMock }), -})); - -describe("AskForm", () => { - beforeEach(() => { - pushMock.mockClear(); - clearAskState(); - }); - - afterEach(cleanup); - - it("disables the submit button until a question is typed", async () => { - const user = userEvent.setup(); - render(); - - const button = screen.getByRole("button", { name: "Research this" }); - expect(button).toBeDisabled(); - - const question = screen.getByLabelText(/what do you want to know\?/i); - await user.type(question, "What are the latest advances in solid-state batteries?"); - - expect(button).toBeEnabled(); - }); - - it("submits: stores question + context and routes to /choose", async () => { - const user = userEvent.setup(); - render(); - - await user.type( - screen.getByLabelText(/what do you want to know\?/i), - "What are the latest advances in solid-state batteries?", - ); - await user.type( - screen.getByLabelText(/anything else it should know\?/i), - "Focus on automotive applications", - ); - await user.click(screen.getByRole("button", { name: "Research this" })); - - // State carry (UX criterion): the payload survives navigation. - const carried = getAskState(); - expect(carried?.question).toBe( - "What are the latest advances in solid-state batteries?", - ); - expect(carried?.context).toBe("Focus on automotive applications"); - - await waitFor(() => { - expect(pushMock).toHaveBeenCalledWith("/choose"); - }); - }); - - it("submits without storing an empty context", async () => { - const user = userEvent.setup(); - render(); - - await user.type( - screen.getByLabelText(/what do you want to know\?/i), - "Who makes solid-state cells today?", - ); - await user.click(screen.getByRole("button", { name: "Research this" })); - - const carried = getAskState(); - expect(carried?.question).toBe("Who makes solid-state cells today?"); - expect(carried?.context).toBeUndefined(); - }); -}); \ No newline at end of file diff --git a/frontend/src/components/ask-form.tsx b/frontend/src/components/ask-form.tsx deleted file mode 100644 index bf20f44..0000000 --- a/frontend/src/components/ask-form.tsx +++ /dev/null @@ -1,78 +0,0 @@ -"use client"; - -/** - * AskForm (T-3.1, REQ-55) — the front door. - * - * Exactly ONE question textarea + ONE optional context textarea + one - * primary action. Nothing else competes for attention. Submit stores - * the payload in the ask store and routes to /choose. - */ - -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import type { FormEvent } from "react"; -import { setAskState } from "@/lib/ask-store"; - -export function AskForm() { - const router = useRouter(); - const [question, setQuestion] = useState(""); - const [context, setContext] = useState(""); - const [submitting, setSubmitting] = useState(false); - - const canSubmit = question.trim().length > 0 && !submitting; - - function handleSubmit(event: FormEvent) { - event.preventDefault(); - if (!canSubmit) return; - setSubmitting(true); - setAskState({ - question: question.trim(), - ...(context.trim() !== "" ? { context: context.trim() } : {}), - }); - router.push("/choose"); - } - - return ( -
- -