feat(FN-3266): address quick chat FAB CSS token consistency

Fixed QuickChatFAB styling to align with the design token system, replacing hardcoded CSS values with consistent token variables in the component's stylesheet.

Fusion-Task-Id: FN-3266
This commit is contained in:
Fusion
2026-05-04 09:42:30 -07:00
committed by gsxdsm
parent 27544a1880
commit 3b956b773c
4 changed files with 165 additions and 295 deletions

View File

@@ -1,281 +1,157 @@
# Research Hardening Preflight + Shape Freeze (FN-3012)
# Research Hardening Preflight Baseline (FN-2999)
Date: 2026-05-02
Date: 2026-05-04
Task: FN-3266
## 1) Preflight verdict (dependency check against FN-2998 contract)
## Scope and intent
**Outcome:** **landed research subsystem present** (with naming drift from the FN-2998 prompt examples).
This document is the verified baseline for FN-2999 hardening work. It reflects **shipped behavior in-repo** and explicitly calls out drift from older assumptions.
### Evidence from repository state
## 1) Verified module map (Research vs Insights)
Present research-layer files/routes/types:
### Core (`@fusion/core`)
- `packages/core/src/research-types.ts`
- Canonical research statuses/types/lifecycle contracts.
- `packages/core/src/research-store.ts`
- Persistence-backed run lifecycle, transitions, cancellation request, retry creation, events/sources/results/exports operations.
- `packages/core/src/research-settings.ts`
- `packages/dashboard/src/research-routes.ts` (**present; named differently than spec example `register-research-routes.ts`**)
- `packages/dashboard/src/__tests__/research-routes.test.ts`
- `packages/dashboard/app/components/ResearchView.tsx`
- `packages/dashboard/app/hooks/useResearch.ts`
- `packages/cli/src/commands/research.ts`
- `packages/cli/src/commands/__tests__/research.test.ts`
- `packages/cli/src/extension.ts` research tool surface (`fn_research_*` tools)
- `packages/engine/src/research-orchestrator.ts`
- `packages/engine/src/research-step-runner.ts`
- `packages/engine/src/research/provider-registry.ts`
Also mounted in dashboard API integration:
- `packages/dashboard/src/routes/register-integrated-routers.ts` mounts `createResearchRouter(store)` at `/api/research`
Database schema is present:
- `packages/core/src/db.ts` includes `research_runs` and `research_exports`
- existing insights schema (`project_insights`, `project_insight_runs`) remains present and separate
### FN-2998 expected-path comparison
Prompt examples that were expected but differ in exact path/name:
- `packages/dashboard/src/routes/register-research-routes.ts`**actual:** `packages/dashboard/src/research-routes.ts`
- `packages/cli/src/commands/research*`**actual:** `packages/cli/src/commands/research.ts`
- `packages/core/src/*research*` modules → **actual:** present (`research-types.ts`, `research-store.ts`, `research-settings.ts`)
No blocker condition detected in preflight; dependency is considered landed.
---
## 2) Verified module/file inventory (present vs absent vs speculative)
## 2.1 Core persistence/domain surface
### Present
- `packages/core/src/research-types.ts`
- Exports: `ResearchRun`, `ResearchRunStatus`, `ResearchSource`, `ResearchResult`, `ResearchExport`, orchestration types (`ResearchOrchestrationConfig`, `ResearchOrchestrationStep`, etc.)
- `packages/core/src/research-store.ts`
- Class `ResearchStore` with concrete methods:
- run lifecycle: `createRun`, `getRun`, `updateRun`, `listRuns`, `deleteRun`, `updateStatus`, `retryRun` support via orchestrator
- run detail mutation: `addEvent`/`appendEvent`, `addSource`, `updateSource`, `setResults`
- exports: `createExport`, `getExports`, `getExport`
- discovery/stats: `searchRuns`, `getStats`
- `packages/core/src/store.ts`
- `TaskStore.getResearchStore()` and `TaskStore.getInsightStore()` both exist; each lazily instantiates separate stores
- Feature enablement and resolved runtime limits/provider settings.
- `packages/core/src/db.ts`
- `research_runs`, `research_exports` schema + indexes
- separate insight tables: `project_insights`, `project_insight_runs`
- Research tables: `research_runs`, `research_run_events`, `research_exports`.
### Absent
- No unified/merged “research+insight” store class. They are separate stores attached to `TaskStore`.
### Speculative-from-old-contract only
- Any imagined `register-research-routes.ts` location (not present under that name)
## 2.2 Dashboard API surface
### Present
- `packages/dashboard/src/research-routes.ts` (`createResearchRouter`):
- run endpoints: `GET /runs`, `POST /runs`, `GET /runs/:id`, `POST /runs/:id/cancel`, `POST /runs/:id/retry`, `PATCH /runs/:id`, `DELETE /runs/:id`
- output endpoints: `GET /runs/:id/export`, `POST /runs/:id/exports`, `GET /runs/:id/exports`, `GET /exports/:exportId`
- detail endpoints: `POST /runs/:id/events`, `POST /runs/:id/sources`, `PATCH /runs/:id/sources/:sourceId`, `PUT /runs/:id/results`, `PATCH /runs/:id/status`, `GET /stats`, `GET /search`
- task integration endpoints from FN-2998 chain:
- `POST /runs/:runId/findings/:findingId/task`
- `POST /runs/:runId/findings/:findingId/tasks/:taskId/enrich`
### Dashboard API/UI (`@fusion/dashboard`)
- `packages/dashboard/src/research-routes.ts`
- Research API endpoints (run CRUD + cancel/retry + results/sources/events + exports + finding-to-task actions).
- `packages/dashboard/src/routes/register-integrated-routers.ts`
- mounts research router at `/api/research`
- `packages/dashboard/src/insights-routes.ts`
- separate insight run/insight CRUD and `/api/insights/run` extraction path
### Absent
- No route file named `register-research-routes.ts`.
### Speculative-only
- Any assumption that research runs are served from `/api/insights/*`.
## 2.3 Dashboard UI/client surface
### Present
- `packages/dashboard/app/components/ResearchView.tsx`
- Research run create/list/detail actions
- finding-level task creation/enrichment modal integration
- Mounts router at `/api/research` via `router.use("/research", createResearchRouter(store))`.
- `packages/dashboard/app/hooks/useResearch.ts`
- uses `/api/research/*` endpoints via api layer
- subscribes to SSE run lifecycle events (`research:run:*`) and polling fallback
- `packages/dashboard/app/api/legacy.ts`
- concrete research bindings under `/research/...` and insight bindings under `/insights/...` (both under `/api` prefix via shared `api()` helper)
- `packages/dashboard/app/App.tsx`
- lazy-loads `ResearchView` and `InsightsView` separately; each has distinct `taskView` gate
- Dashboard hook consuming `/api/research/*` and run lifecycle updates.
- `packages/dashboard/app/components/ResearchView.tsx`
- Standalone Research view, run controls, result display, finding task actions.
### Absent
- No single combined “ResearchInsightsView”.
### Speculative-only
- assumption that only insights UI exists and research UI is absent (not true in current repo)
## 2.4 Engine automation/orchestration surface
### Present
### Engine (`@fusion/engine`)
- `packages/engine/src/research-orchestrator.ts`
- phased run execution: planningsearchingfetchingsynthesizingfinalizing
- concurrency guard: `AgentSemaphore`
- cancellation: `cancelRun()` via `AbortController`
- retry support: `retryRun()`
- Phase execution (`planning/searching/fetching/synthesizing/finalizing`), cancellation, retry handoff, status/event writes.
- `packages/engine/src/research-step-runner.ts`
- timeout-wrapped provider query/fetch/synthesis with typed error classification
- `packages/engine/src/project-engine.ts`
- creates `ResearchOrchestrator` when `TaskStore.getResearchStore` exists
- also independently wires insight extraction automation via cron + memory-insight pipeline
- Provider adapters with timeout + abort handling and error classification.
### Absent
- no evidence that insight extraction cron directly writes research run tables.
## 2.5 CLI + extension surface
### Present
### CLI + extension (`@runfusion/fusion`)
- `packages/cli/src/commands/research.ts`
- commands: run/create, list, show, export, cancel, retry; runtime bootstrap through orchestrator/provider registry
- `create`, `list`, `show`, `export`, `cancel`, `retry` commands.
- `packages/cli/src/extension.ts`
- tools: `fn_research_run`, `fn_research_list`, `fn_research_get`, `fn_research_cancel`
- separate insight tool family (`fn_insight_*`) coexists
- Research tools: `fn_research_run`, `fn_research_list`, `fn_research_get`, `fn_research_cancel`, `fn_research_retry`.
### Absent
- no extra `research-*` command split files; command is consolidated in one module.
### Boundary with Insights (separate subsystem)
- Insights files/routes/stores remain separate (`insight-store`, `insights-routes`, `project_insights*` tables).
- Research is **not** a wrapper around Insights and does not share run tables.
---
## 2) Renamed-path drift and stale assumptions
## 3) Real lifecycle map (entry -> execution -> persistence -> UI)
- Older wording referencing `packages/dashboard/src/routes/register-research-routes.ts` is stale.
- Actual implementation is:
- route file: `packages/dashboard/src/research-routes.ts`
- mount file: `packages/dashboard/src/routes/register-integrated-routers.ts`
- mount path: `/api/research`
## 3.1 Research lifecycle (landed)
## 3) Lifecycle/status contract as shipped
1. Entry points:
- Dashboard API: `POST /api/research/runs` (`research-routes.ts`)
- CLI: `packages/cli/src/commands/research.ts` (`runResearchCreate`)
- Extension tool: `fn_research_run` in `packages/cli/src/extension.ts`
Canonical `ResearchRunStatus` values in `research-types.ts`:
- `queued`
- `running`
- `cancelling`
- `retry_waiting`
- `completed`
- `failed`
- `cancelled`
- `timed_out`
- `retry_exhausted`
2. Store creation:
- `TaskStore.getResearchStore()` -> `ResearchStore.createRun()` writes `research_runs`
### Important mismatch to older baseline text
Older baseline language (`pending | running | completed | failed | cancelled`) is obsolete. `pending` is normalized to `queued` for compatibility in store code, but not a primary status in current contracts.
3. Orchestration execution:
- Engine orchestrator (`research-orchestrator.ts`) runs phases with provider-backed `ResearchStepRunner`
- statuses and events updated through `ResearchStore.updateStatus`/`addEvent`/`addSource`/`setResults`
## 4) Cancel/retry behavior (API + store + orchestrator)
4. Persistence:
- run/event/source/result payloads in JSON columns of `research_runs`
- exports in `research_exports`
### Dashboard route behavior (`research-routes.ts`)
- `POST /runs/:id/cancel`
- Rejects terminal statuses (`completed`, `failed`, `cancelled`, `timed_out`, `retry_exhausted`) with `409 INVALID_TRANSITION`.
- Otherwise calls `ResearchStore.requestCancellation()` → run moves to `cancelling`.
- `POST /runs/:id/retry`
- Delegates to `ResearchStore.createRetryRun()`.
- Maps retry exhaustion to `409 RETRY_EXHAUSTED`, non-retryable failures to `409 NON_RETRYABLE_PROVIDER_ERROR`, invalid state to `409 INVALID_TRANSITION`.
5. Consumption/UI:
- `useResearch.ts` reads `/api/research/runs` + `/api/research/runs/:id`
- `ResearchView.tsx` displays summary/findings/events, exports, retry/cancel
- finding actions call:
- create task: `/api/research/runs/:runId/findings/:findingId/task`
- enrich task: `/api/research/runs/:runId/findings/:findingId/tasks/:taskId/enrich`
### Store behavior (`research-store.ts`)
- `requestCancellation()`
- Non-terminal runs transition to `cancelling`, append `cancel_requested` lifecycle event.
- `createRetryRun()`
- Only from `failed`/`timed_out`.
- Enforces retryable + max-attempt budget; can set source run to `retry_exhausted` and throw `not_retryable`.
- Creates new run and sets new run status to `retry_waiting` with `retry_scheduled` event.
6. Task linkage:
- provenance uses `sourceType: "research"` and metadata via research route handlers
- research markdown goes to task documents (`research-{sanitizedRunId}` key)
### Orchestrator behavior (`research-orchestrator.ts`)
- `cancelRun(runId)` always calls `store.requestCancellation(runId)` first.
- If run is active in orchestrator `activeRuns`, abort controller is triggered and final state transitions through cancellation handling.
- If run is **not** active, orchestrator directly sets status `cancelled`.
- Practical limitation: orchestrator-side graceful cancellation logic only applies to runs currently tracked in `activeRuns`.
## 3.2 Adjacent insight lifecycle (still separate and landed)
## 5) Persistence/storage model (verified)
1. Entry: `POST /api/insights/run` in `insights-routes.ts`
2. AI extraction path: `createFnAgent` + `promptWithFallback` using memory extraction prompt
3. Persistence path: `InsightStore` (`project_insights`, `project_insight_runs`) + memory-insight file updates
4. UI path: `useInsights.ts` + `InsightsView.tsx` and `/api/insights/*`
5. Automation path: project engine startup syncs/schedules insight extraction automation (cron)
### Tables (`db.ts`)
- `research_runs`
- Primary run row including JSON columns: `providerConfig`, `sources`, `events`, `results`, `tokenUsage`, `tags`, `metadata`, `lifecycle`.
- `research_run_events`
- Append-only lifecycle/event stream (`seq`, `type`, `message`, optional status/classification/metadata).
- `research_exports`
- Export artifacts linked by `runId`.
---
### Read/write shape (`research-store.ts`)
- Run row stores denormalized snapshots (`sources`, `events`, `results`) in `research_runs` JSON fields.
- Lifecycle events are also persisted separately in `research_run_events` (durable ordered log).
- Results/citations/findings are stored in `research_runs.results` JSON.
## 4) Classification decision: standalone, layered, or absent?
## 6) Provider execution path and abort semantics
**Decision: Research is a standalone landed subsystem that coexists with (and is adjacent to) the insights subsystem, not a thin wrapper over insights.**
`research-step-runner.ts`:
- `runSourceQuery` → provider `search(query, options, signal)`
- `runContentFetch` → provider `fetchContent(url, options, signal)`
- `runSynthesis` → configured synthesis runner with model settings + signal
Evidence:
- Separate core types/stores (`research-*` vs `insight-*`)
- Separate DB tables (`research_runs/research_exports` vs `project_insights/project_insight_runs`)
- Separate dashboard route families (`/api/research/*` vs `/api/insights/*`)
- Separate dashboard hooks/views (`useResearch` + `ResearchView` vs `useInsights` + `InsightsView`)
- Separate CLI/extension tool families (`research` commands/tools vs `insight` tools)
- Engine contains both research orchestrator path and insight extraction automation path as distinct flows
All step calls are wrapped by `withTimeout(...)`:
- timeout classification: `ResearchStepTimeoutError` (`retryable: true`)
- abort classification: `ResearchStepAbortError` (`retryable: false`)
- provider failures: `provider_error` (`retryable: true`)
Relationship note:
- They are **architecturally parallel** subsystems sharing project settings, task store root, and dashboard/engine host infrastructure.
Abort propagation uses `AbortSignal` listeners and races promise vs timeout vs abort.
---
## 7) API shape summary (`/api/research`)
## 5) Hardening matrix for FN-3013 through FN-3017 (scope-frozen)
Key endpoints:
- `GET /runs`, `POST /runs`, `GET /runs/:id`, `PATCH /runs/:id`, `DELETE /runs/:id`
- `POST /runs/:id/cancel`, `POST /runs/:id/retry`, `PATCH /runs/:id/status`
- `POST /runs/:id/events`, `POST /runs/:id/sources`, `PATCH /runs/:id/sources/:sourceId`, `PUT /runs/:id/results`
- `GET /runs/:id/export`, `POST /runs/:id/exports`, `GET /runs/:id/exports`, `GET /exports/:exportId`
- `GET /stats`, `GET /search`
- `POST /runs/:runId/findings/:findingId/task`
- `POST /runs/:runId/findings/:findingId/tasks/:taskId/enrich`
This matrix is bounded to safeguards only (timeouts, cancel, rate limits, error surfaces, regression coverage, verification sequencing).
## 8) Hardening pressure points recorded (no fixes in this task)
| Follow-on | Primary lifecycle stage | Exact file targets (first-pass) | Scope guard |
|---|---|---|---|
| FN-3013 | Timeouts + cancellation reliability | `packages/engine/src/research-orchestrator.ts`, `packages/engine/src/research-step-runner.ts`, `packages/engine/src/research/providers/page-fetch-provider.ts`, `packages/engine/src/research/providers/web-search-provider.ts`, `packages/engine/src/research/providers/local-docs-provider.ts`, `packages/engine/src/research/providers/github-provider.ts`, `packages/engine/src/research/providers/llm-synthesis-provider.ts`, `packages/dashboard/src/research-routes.ts`, `packages/cli/src/commands/research.ts`, `packages/cli/src/extension.ts` | No new providers/modules; strengthen abort propagation, timeout classification, and route/CLI cancellation semantics |
| FN-3014 | Rate limiting/backpressure | `packages/engine/src/research-orchestrator.ts`, `packages/engine/src/research/provider-registry.ts`, `packages/engine/src/research/providers/index.ts`, `packages/engine/src/research/providers/page-fetch-provider.ts`, `packages/engine/src/research/providers/web-search-provider.ts`, `packages/engine/src/research/providers/local-docs-provider.ts`, `packages/engine/src/research/providers/github-provider.ts`, `packages/engine/src/research/providers/llm-synthesis-provider.ts`, `packages/engine/src/agent-tools.ts`, `packages/core/src/research-settings.ts`, `packages/core/src/research-types.ts`, settings consumers in `packages/engine/src/project-engine.ts` | Tune and enforce existing limits; avoid adding novel provider architecture |
| FN-3015 | Error surface normalization | `packages/dashboard/src/research-routes.ts`, `packages/dashboard/app/api/legacy.ts`, `packages/dashboard/app/hooks/useResearch.ts`, `packages/dashboard/app/components/ResearchView.tsx`, `packages/cli/src/commands/research.ts`, `packages/cli/src/commands/__tests__/research.test.ts`, `packages/dashboard/src/__tests__/research-routes.test.ts` | Normalize user-facing error payloads/states only; no UX expansion beyond existing view/actions |
| FN-3016 | Regression coverage expansion | `packages/dashboard/src/__tests__/research-routes.test.ts`, `packages/cli/src/commands/__tests__/research.test.ts`, `packages/engine/src/__tests__/research-orchestrator.test.ts`, `packages/engine/src/__tests__/research-step-runner.test.ts`, `packages/engine/src/research/providers/__tests__/*.test.ts` | Add behavior tests around cancellations/timeouts/rate-limits/error mapping |
| FN-3017 | Full verification and integration gate | workspace verification commands + touched tests around research/insights seams | Verify research hardening does not regress insights flows or task integration endpoints |
1. **Status drift risk**
- Some surfaces (notably tool parameter enums in extension) still expose legacy compact status sets while core status domain is broader.
## sequencing
1. FN-3013 first (cancel/timeout correctness)
2. FN-3014 second (rate/backpressure), because limits interact with cancel behavior
3. FN-3015 third (error contract consistency after behavior stabilizes)
4. FN-3016 fourth (lock regressions)
5. FN-3017 final integration gate
2. **Orchestrator cancel scope nuance**
- Full graceful cancel path is only available when run is in `activeRuns`; non-active cancellation takes direct status path.
## non-goals (frozen)
- No new research providers
- No new research UI surfaces beyond current ResearchView/task-action flows
- No schema redesign across research/insights
- No migration of insights pipeline into research subsystem
3. **Dual event storage model complexity**
- `research_runs.events` JSON snapshot and `research_run_events` append-only log coexist; hardening should preserve consistency guarantees.
---
4. **Export surface asymmetry**
- Route export endpoint advertises markdown/json/html behavior while core export type includes `pdf`; CLI command accepts `pdf` format but markdown renderer fallback behavior should remain explicitly documented/validated.
## 6) FN-3016 coverage hardening landed
## 9) Validation references used for this baseline
FN-3016 added standalone Research interaction coverage across component/hook/route/app layers using this shape-freeze map.
Primary coverage files:
- `packages/dashboard/app/components/__tests__/ResearchView.test.tsx`
- `packages/dashboard/app/hooks/__tests__/useResearch.test.ts`
- `packages/dashboard/src/__tests__/research-routes.test.ts`
- `packages/dashboard/app/components/__tests__/App.test.tsx`
- `packages/core/src/__tests__/research-store.test.ts`
- `packages/engine/src/__tests__/research-orchestrator.test.ts`
- `packages/cli/src/commands/__tests__/research.test.ts`
Covered user-visible flows:
- create-run UX and in-flight/disabled behavior
- loading/empty/error/setup-needed rendering
- run history/search, cited results reading, and detail selection
- export/cancel/retry controls
- finding actions for creating new tasks and enriching existing tasks
- dashboard navigation to standalone Research + feature-disabled fallback/persistence
Bounded fix included by test guidance:
- `ResearchView.handleCreateRun` now clears submitting state before early-return on zero enabled providers (prevents stuck disabled create button).
---
## 7) Blockers status
- **No dependency blocker detected for FN-2998 in current repo state.**
- Existing research subsystem is landed and test-covered (`research-routes.test.ts`, CLI research tests).
- Hardening tasks should target **current concrete files above**, not speculative `register-research-routes.ts` or hypothetical merged insight/research modules.
- Architecture docs were updated in this task to add a dedicated `Research Runs` section and explicit research-vs-insights boundary note (`docs/architecture.md`).
---
## 8) FN-3017 final contract-coverage summary (insights-backed + extension outputs)
FN-3017 added/validated regression coverage for the shipped insights-backed contract and extension structured outputs:
- Dashboard insights route coverage: `packages/dashboard/src/__tests__/insights-routes.test.ts`
- Added explicit not-found contract assertion for `GET /api/insights/runs/:id` (stable JSON error payload).
- Existing coverage retained for run trigger/list/show filters, failed runs, and create-task suggestion payload (`POST /api/insights/:id/create-task`).
- Dashboard hook coverage: `packages/dashboard/app/hooks/__tests__/useInsights.test.ts`
- Added assertion that refresh uses latest run from `fetchInsightRuns` response ordering.
- Existing coverage retained for failed run error propagation and create-task suggestion mapping.
- Dashboard component coverage: `packages/dashboard/app/components/__tests__/InsightsView.test.tsx`
- Added run-level failed-state rendering assertion (`runError` alert path) tied to hook state.
- CLI extension structured-output coverage: `packages/cli/src/__tests__/extension.test.ts`
- Added runnable, CI-safe regression slice (outside env-gated integration block) asserting machine-consumable `details` fields for task creation/dependencies and assignment validation failures.
- Assertions avoid hardcoded `FN-*` IDs and rely on returned structured metadata.
Bounded production fix landed during this hardening:
- `packages/dashboard/app/hooks/useInsights.ts` now uses `useEffect` for initial refresh side effect (replacing side-effect-in-`useMemo` misuse) to keep hook behavior React-compliant and deterministic.
Final workspace verification on integrated surface completed in FN-3017:
- `pnpm lint`
- `pnpm test`
- `pnpm typecheck`
- `pnpm build`
These tests were used as behavioral evidence while preparing this baseline.