> For the complete documentation index, see [llms.txt](https://faction-os.gitbook.io/faction-os-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase18-session09-web-quest-board-typed-cards/spec.md).

# Session Specification

**Session ID**: `phase18-session09-web-quest-board-typed-cards` **Phase**: 18 - Quest Board Suggestion Parity **Status**: Complete **Created**: 2026-06-10 **Package**: apps/web **Package Stack**: TypeScript

***

## 1. Session Overview

This session upgrades the web Quest Board from a transient list of idle suggestion strings into a typed multi-source card surface backed by the canonical `suggestion_update` snapshot event. Sessions 01-08 already shipped the shared protocol contracts, server-side manager, persistence, route family, idle lifecycle generation, summaries, codebase scanners, scan orchestration, on-demand analysis, and project scan engines. The web package is now the first unfinished consumer of those contracts.

The implementation stays in `apps/web` and does not add server behavior. It adds pure normalization helpers that validate incoming snapshot payloads before store mutation, preserves compatibility with the legacy `idle_suggestion` event during migration, and renders typed Quest Board cards for idle suggestions, codebase issues, session follow-ups, analysis items, and project scan items. The UI must show source type, title or message, category or type, priority or severity, relative file and line details where present, created timestamps, summary counts, severity buckets, empty/loading/offline states, and scan-in-progress state.

This session deliberately stops before accept, dismiss, hero assignment, manual trigger buttons, and keyboard shortcuts. Those interactions are Session 10 scope. The Quest Board must remain visually and conceptually distinct from the mission log and from the Phase 17 Notice Board while preparing a stable typed card model for the next session's actions.

***

## 2. Objectives

1. Replace string-only Quest Board state in the web store with typed multi-source cards derived from canonical suggestion snapshots.
2. Add pure web normalization and summary helpers that tolerate malformed entries without breaking the store.
3. Render the Quest Board as a typed, scan-friendly card surface with metadata, summary counts, severity buckets, and explicit loading, empty, offline, and scan-in-progress states.
4. Add focused web tests for normalization, store event handling, and component rendering with mixed-source snapshot fixtures.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase18-session01-protocol-suggestion-contract-parity` - provides canonical suggestion snapshot, route, validation, and WebSocket contracts.
* [x] `phase18-session02-server-suggestion-manager-and-persistence` - provides persisted server-owned Quest Board state and summary aggregation.
* [x] `phase18-session03-suggestion-routes-and-websocket-parity` - provides `suggestion_update` WebSocket hydrate and legacy `idle_suggestion` compatibility emission.
* [x] `phase18-session05-session-summary-engine-and-noise-filtering` - provides session follow-up suggestions and analysis noise filtering.
* [x] `phase18-session06-codebase-issue-scanners` - provides codebase issue sources for typed cards.
* [x] `phase18-session07-scan-orchestration-and-quest-ignore-patterns` - provides scan status and ignore pattern inputs.
* [x] `phase18-session08-on-demand-analysis-and-project-scan-engines` - provides analysis and project scan results for typed cards.

### Required Tools/Knowledge

* Node 26.2.0 or newer with npm workspaces.
* React 19, Vite, TypeScript, Zustand, Tailwind, and Vitest in `apps/web`.
* Existing `@factionos/protocol` suggestion types and validation helpers.
* Existing `useGameStore` WebSocket event reducer patterns and Notice Board normalization pattern.
* Phase 18 PRD Appendix A as traceability evidence only; no historical code, bundle, or asset import.

### Environment Requirements

* Work from the repository root.
* Use package-relative monorepo paths from the repo root.
* Keep all generated and edited files ASCII-only with Unix LF line endings.
* Do not add hosted telemetry, entitlement gating, server routes, or executor behavior in this session.

***

## 4. Scope

### In Scope (MVP)

* Operators can see typed Quest Board cards hydrated from `suggestion_update` snapshots.
* Operators can see idle suggestions, codebase issues, session follow-up tasks, analysis items, and project scan items in one bounded, deterministic card list.
* Operators can read each card's source type, title/message, category or type, priority/severity, relative file path and line where present, and created-at or generated-at timestamp.
* Operators can see summary counts and severity buckets derived from snapshot state instead of a raw string-array length.
* Operators can see empty, loading, offline/disconnected, malformed snapshot, and scan-in-progress states without blank panels.
* Legacy `idle_suggestion` events still populate compatibility cards while the server migration remains dual-emission.
* Malformed snapshot entries are dropped or downgraded by pure normalization before store mutation, with compact local error state instead of a broken cockpit.
* Store and component tests cover mixed-source fixtures, malformed entries, summary counts, scan status, and legacy compatibility.

### Out of Scope (Deferred)

* Accept and dismiss interactions - *Reason: Session 10 owns card actions.*
* Assign-to-hero flow - *Reason: Session 10 owns hero assignment and action failure feedback.*
* Manual scan, analysis, and project-scan trigger buttons - *Reason: Session 10 owns Quest action controls.*
* Keyboard shortcuts `1`/`2`/`3`, `Alt+X`, and `Alt+R` - *Reason: Session 10 owns shortcut handling and help overlay updates.*
* Server, protocol, route, or persistence changes - *Reason: Sessions 01-08 shipped the backend contracts this session consumes.*
* Historical analytics telemetry and entitlement gating - *Reason: Phase 18 explicitly excludes them under local-first hosted-service guardrails.*

***

## 5. Technical Approach

### Architecture

Add `apps/web/src/lib/questBoard.ts` as the pure web boundary for Quest Board normalization and display derivation. It will consume protocol suggestion types, normalize `suggestion_update` payloads into a web card model, map source-specific metadata into display fields, drop malformed card entries without mutating store state, cap the displayed list deterministically, derive summary counts and severity buckets, sanitize file labels to relative paths, and expose compatibility normalization for legacy `idle_suggestion` frames.

Upgrade `apps/web/src/store/useGameStore.ts` from `questSuggestions: { heroId, text }[]` to a typed Quest Board state that includes cards, summary, scan status, updated timestamp, loading/error metadata, and legacy fallback state. The `suggestion_update` reducer should run pure normalization before calling `set`, and the legacy `idle_suggestion` reducer should produce compatibility cards without overriding newer canonical snapshot data.

Update `apps/web/src/components/QuestBoard.tsx` to render a dense cockpit card surface from the typed store model. The component should keep stable scroll regions, show clear state labels, render source-specific metadata compactly, surface summary counts and severity buckets, and preserve the current global widget placement until Session 10 decides whether selected-hero side-panel integration is added.

### Design Patterns

* Pure normalization before store mutation: invalid external payloads cannot partially corrupt Zustand state.
* Contract-backed display model: web card types mirror the protocol snapshot and handle source enums exhaustively.
* Compatibility without authority drift: `idle_suggestion` remains a fallback path while `suggestion_update` is the canonical source.
* Bounded deterministic ordering: card lists are capped and sorted by timestamp/source without relying on object insertion behavior.
* Local-first privacy: file labels remain relative, compact, and never broaden into absolute scan roots or raw payload dumps.
* Explicit UI states: loading, empty, offline, malformed snapshot, and scan-in-progress are visible and testable.

### Technology Stack

* TypeScript in `apps/web`.
* React 19 component rendering.
* Zustand store updates in `useGameStore`.
* Existing `@factionos/protocol` suggestion contracts.
* Existing Tailwind/cockpit shell class patterns.
* Vitest with happy-dom and Testing Library.

***

## 6. Deliverables

### Files to Create

| File                                     | Purpose                                                                                                                                   | Est. Lines |
| ---------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/lib/questBoard.ts`         | Pure Quest Board normalization, card display model, summary derivation, file label sanitization, legacy event mapping, and source helpers | \~360      |
| `apps/web/tests/questBoard.test.ts`      | Unit tests for mixed-source normalization, malformed entry dropping, ordering, summary buckets, file label safety, and legacy mapping     | \~260      |
| `apps/web/tests/questBoardStore.test.ts` | Store reducer tests for `suggestion_update`, malformed snapshots, scan status, summary state, and legacy compatibility                    | \~220      |
| `apps/web/tests/QuestBoard.test.tsx`     | Component tests for typed card rendering, metadata, empty/loading/offline, malformed, and scan-in-progress states                         | \~240      |

### Files to Modify

| File                                     | Changes                                                                                                                                       | Est. Lines |
| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/store/useGameStore.ts`     | Replace string-only Quest Board state with typed cards, summary, scan status, update/error metadata, and canonical plus legacy event handling | \~220      |
| `apps/web/src/components/QuestBoard.tsx` | Render typed multi-source cards, summary counts, severity buckets, state banners, scan progress, and metadata while remaining non-actionable  | \~260      |

***

## 7. Success Criteria

### Functional Requirements

* [ ] A mixed-source `suggestion_update` snapshot renders idle suggestions, codebase issues, session follow-ups, analysis items, and project scan items with correct source labels and metadata.
* [ ] Malformed snapshot entries are dropped or downgraded by normalization without breaking the store or blanking the cockpit.
* [ ] Summary counts and severity buckets derive from snapshot data and do not depend on raw array length alone.
* [ ] Scan-in-progress, empty, loading, offline/disconnected, and malformed payload states are visible and testable.
* [ ] Legacy `idle_suggestion` events still produce compatibility cards when no newer canonical snapshot has superseded them.
* [ ] The Quest Board remains separate from Mission Log and Notice Board surfaces and does not introduce accept/dismiss/action controls.

### Testing Requirements

* [ ] Unit tests cover normalization for all card sources, malformed entries, deterministic ordering, summary derivation, severity buckets, safe file labels, and legacy mapping.
* [ ] Store tests cover canonical snapshot hydrate, malformed payload handling, scan status, summary state, legacy fallback, and reset behavior.
* [ ] Component tests cover mixed-source rendering, metadata, count badges, empty/loading/offline/malformed states, and scan-in-progress copy.
* [ ] Focused web tests and web typecheck pass.

### Non-Functional Requirements

* [ ] The UI never displays absolute scan roots, raw provider payloads, prompts beyond bounded suggestion text, tokens, secrets, or raw command output.
* [ ] The card list is bounded, deterministically ordered, and responsive without overlapping text or controls.
* [ ] No hosted telemetry, entitlement gating, server routes, external transfer path, or executor behavior is added.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.
* [ ] `npm --workspace apps/web run typecheck` passes.
* [ ] Focused Quest Board web tests pass.

***

## 8. Implementation Notes

### Key Considerations

* Protocol already exports the suggestion snapshot and source-specific shapes; this session should not duplicate canonical enum vocabulary by hand.
* `parseSuggestionSnapshot` may reject a whole malformed snapshot, so the web helper should preserve item-level tolerance where needed to satisfy the PRD requirement that malformed entries are dropped without breaking the store.
* The store should treat `suggestion_update` as canonical and `idle_suggestion` as compatibility-only.
* Session 10 needs stable card IDs and source types for accept, dismiss, hero assignment, and shortcuts, so avoid an anonymous display-only model.
* The current Quest Board is a compact global widget; historical evidence favors selected-hero side-panel placement, but hero assignment lands in Session 10. Preserve the widget now and record any side-panel decision in implementation notes.

### Potential Challenges

* Mixed timestamp sources: Normalize idle/codebase/session created times and analysis/project generated times into a single `sortAt` field.
* Malformed arrays: Validate individual entries so one bad source item does not discard all other valid cards.
* Path privacy: Display only relative protocol file paths and hide or reject absolute/URL-like labels.
* UI density: Keep cards readable in the existing compact cockpit without marketing-style layout or nested card clutter.

### Relevant Considerations

* \[P03-packages/protocol] **Protocol leads cross-package work**: Consume existing protocol contracts and avoid inventing web-only source vocabulary.
* \[P17-apps/web] **Pure normalization before store mutation**: Apply the same deterministic hydrate/message handling pattern used for Notice Board.
* \[P07] **Redaction is boundary-specific**: Keep Quest Board file and text display bounded at the browser boundary.
* \[P07] **Hosted services ship as disabled-default guardrails only**: Historical telemetry and entitlement gating remain excluded.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* External snapshot payloads could be malformed or partially invalid and must not corrupt the store.
* A typed multi-source card list could leak overly broad paths or raw payload details if display helpers bypass protocol-safe fields.
* The compact panel could hide empty/offline/loading/scan-running states if state handling is implicit.

***

## 9. Testing Strategy

### Unit Tests

* Test `normalizeSuggestionSnapshotForQuestBoard` with one fixture containing idle suggestions, codebase issues, session follow-ups, analysis items, and project scan items.
* Test malformed entries are dropped or reported while valid entries remain.
* Test summary counts, severity buckets, timestamp ordering, source labels, priority/severity labels, safe file labels, and list caps.
* Test legacy `idle_suggestion` normalization produces compatibility cards.

### Integration Tests

* Test `useGameStore.applyEvent` for `suggestion_update`, invalid payloads, scan status updates, summary state, legacy events, and reset behavior.
* Test `QuestBoard` rendering against store fixtures and happy-dom user-visible output.

### Manual Testing

* Run the local app after implementation and confirm the Quest Board panel shows typed snapshot cards without adding action controls.
* Confirm disconnected/no-server state remains visible when WebSocket status is not connected.
* Confirm long metadata wraps without overlapping on compact desktop and mobile-width layouts if browser validation is run.

### Edge Cases

* Snapshot with no cards and idle scan status.
* Snapshot with running scan status and no cards.
* Snapshot with some malformed items and some valid items.
* Legacy idle event after a canonical snapshot.
* Missing optional file path, line, analysis result, or project scan.
* Very long titles, prompts, categories, and file labels.

***

## 10. Dependencies

### External Libraries

* `@factionos/protocol`: existing workspace package for suggestion contracts.
* `zustand`: existing app state store.
* `react` and `react-dom`: existing component runtime.
* `vitest` and `@testing-library/react`: existing web test stack.

### Internal Dependencies

* `packages/protocol/src/suggestions.ts` for card source contracts, validation helpers, summary types, and scan status vocabulary.
* `packages/protocol/src/events.ts` for `SuggestionUpdateEvent` and `IdleSuggestionEvent`.
* `apps/web/src/store/useGameStore.ts` for WebSocket event reduction.
* `apps/web/src/components/QuestBoard.tsx` for the cockpit surface.
* `apps/web/src/lib/timeFormat.ts` for timestamp display conventions where applicable.
* `apps/web/src/lib/classNames.ts` for component class composition.

### Other Sessions

* **Depends on**: `phase18-session01-protocol-suggestion-contract-parity`, `phase18-session02-server-suggestion-manager-and-persistence`, `phase18-session03-suggestion-routes-and-websocket-parity`, `phase18-session05-session-summary-engine-and-noise-filtering`, `phase18-session06-codebase-issue-scanners`, `phase18-session07-scan-orchestration-and-quest-ignore-patterns`, `phase18-session08-on-demand-analysis-and-project-scan-engines`.
* **Depended by**: `phase18-session10-quest-actions-and-keyboard-shortcuts`, `phase18-session11-validation-documentation-and-handoff`.

***

## Next Steps

Run the implement workflow step to begin AI-led implementation.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://faction-os.gitbook.io/faction-os-docs/.spec_system/archive/sessions/phase18-session09-web-quest-board-typed-cards/spec.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
