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 (
-
- );
-}
\ No newline at end of file
diff --git a/frontend/src/components/ask-suggestions.tsx b/frontend/src/components/ask-suggestions.tsx
new file mode 100644
index 0000000..c1d5420
--- /dev/null
+++ b/frontend/src/components/ask-suggestions.tsx
@@ -0,0 +1,88 @@
+"use client";
+
+/**
+ * AskSuggestions (T-7.2, REQ-67) — open questions worth following up.
+ *
+ * Follow-up query suggestions derived from unanswered questions of
+ * past runs, behind the ApiClient boundary. Clicking a card fills the
+ * question box (editable — never auto-submits). Sits below the primary
+ * form action; the honest empty state explains how items collect here.
+ */
+
+import { useEffect, useState } from "react";
+import { api, type SuggestedQuery } from "@/lib/api/client";
+
+type SuggestionsState =
+ | { kind: "loading" }
+ | { kind: "error" }
+ | { kind: "ready"; suggestions: SuggestedQuery[] };
+
+export function AskSuggestions({ onPick }: { onPick: (question: string) => void }) {
+ const [state, setState] = useState({ kind: "loading" });
+
+ useEffect(() => {
+ let cancelled = false;
+ api
+ .getSuggestedQueries()
+ .then((suggestions) => {
+ if (!cancelled) setState({ kind: "ready", suggestions });
+ })
+ .catch(() => {
+ if (!cancelled) setState({ kind: "error" });
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, []);
+
+ return (
+
+