> 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-session02-server-suggestion-manager-and-persistence/spec.md).

# Session Specification

**Session ID**: `phase18-session02-server-suggestion-manager-and-persistence` **Phase**: 18 - Quest Board Suggestion Parity **Status**: Not Started **Created**: 2026-06-09 **Package**: apps/server **Package Stack**: TypeScript

***

## 1. Session Overview

This session creates the server-side SuggestionManager that will become the canonical source of truth for Phase 18 Quest Board state. Session 01 already landed the shared protocol contracts in `packages/protocol`, including typed suggestion cards, freshness constants, dismissed-ID caps, summary shapes, and the `suggestion_update` snapshot event. The current server still has no manager-owned suggestion state: idle suggestions are emitted as string-only compatibility events and the web store keeps transient browser memory.

The manager implemented here owns active idle suggestions, codebase issues, session summaries, analysis results, project scan state, dismissed-ID sets, freshness checks, and local JSON persistence under the FactionOS home directory. It uses the same manager-owned persistence and shutdown-cleanup pattern proven by the Notice Board work: load on startup, debounce saves, write atomically through a temporary file and rename, recover from corrupt stores without crashing, and flush pending writes during server shutdown.

This session deliberately stops before REST routes, WebSocket hydration, idle lifecycle generation, scanner engines, and web Quest Board UI work. It exposes a single manager-level `emitUpdate` path that builds the protocol `suggestion_update` snapshot, so Session 03 can wire routes and WebSocket hydration without inventing a second state path.

***

## 2. Objectives

1. Implement `SuggestionManager` in `apps/server` using Session 01 protocol types, constants, parsers, freshness helpers, and bounded normalization.
2. Persist Quest Board state to `suggestions.json` under `FACTIONOS_HOME` or the injected test state directory with debounced atomic writes and startup load recovery.
3. Provide manager APIs for idle suggestions, accept/dismiss transitions, dismissed-ID FIFO caps, incremental codebase issues, summaries, analysis results, project scan state, freshness predicates, and summary counts.
4. Emit a canonical `suggestion_update` snapshot through one manager method while keeping route and WebSocket handler wiring deferred to Session 03.
5. Prove manager behavior with focused Vitest coverage for transitions, dedupe, caps, freshness, persistence round-trip, corrupt-file recovery, and server shutdown flush.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase18-session01-protocol-suggestion-contract-parity` - provides `packages/protocol/src/suggestions.ts`, `SuggestionSnapshot`, `suggestion_update`, REST aliases, constants, parsers, and freshness helpers.
* [x] `phase17-session02-server-notice-manager-persistence-and-context` - provides the established manager-owned local persistence pattern.

### Required Tools/Knowledge

* Node 26.2.0 or newer with npm workspaces.
* TypeScript manager and Vitest patterns in `apps/server`.
* Existing `NoticeBoard` persistence, `Broadcaster.emit`, and `createFactionOsServer().stop()` lifecycle patterns.
* Phase 18 PRD Appendix A and the Session 02 stub.

### 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.

***

## 4. Scope

### In Scope (MVP)

* Server code can create a `SuggestionManager` that stores idle suggestions, codebase issues, session summaries, analysis result, project scan, scan status, dismissed suggestion IDs, and dismissed issue IDs.
* Manager callers can create or replace idle suggestions with generated IDs, session/hero linkage, created-at timestamps, a per-session idle suggestion cap of 1, and duplicate-trigger prevention while state mutation is in flight.
* Manager callers can accept active idle suggestions by removing them from active state and can dismiss idle suggestions or codebase issues by adding their IDs to capped FIFO dismissed sets.
* Manager callers can add codebase issues incrementally with dedupe by `type` + `filePath` + `line`, query by type and severity, and clear issues by type.
* Manager callers can set session summaries, analysis results, project scan results, and scan status using protocol-validated shapes.
* Manager callers can evaluate freshness windows: analysis result 30 minutes, project scan 24 hours, and codebase scan 24 hours.
* Server runtime can load `suggestions.json` at startup, save after mutation with a roughly 1 second debounce, write atomically through temp-plus-rename, recover to empty state from corrupt or malformed stores, and flush on shutdown.
* Manager callers can request a summary containing counts and severity buckets.
* Manager callers can emit one canonical `suggestion_update` snapshot through `Broadcaster.emit`.
* Unit tests cover state transitions, dedupe, caps, freshness, persistence round-trip, corrupt-file recovery, and server shutdown flush.

### Out of Scope (Deferred)

* Express `/suggestions/*` routes and Zod/body validation - *Reason: Session 03 owns REST route parity and rate-limited handlers.*
* WebSocket connection hydration and client message handling for suggestions - *Reason: Session 03 owns runtime route and WebSocket parity.*
* Idle lifecycle generation, transcript context, cooldowns, and LLM fallback - *Reason: Session 04 owns automatic idle generation.*
* Session summary generation and analysis noise filtering - *Reason: Session 05 owns those engines and filters.*
* Codebase scanner implementations and scan orchestration - *Reason: Sessions 06 and 07 own scanners and ignore patterns.*
* On-demand analysis and project scan engines - *Reason: Session 08 owns engine behavior.*
* Web Quest Board typed cards, action buttons, and keyboard shortcuts - *Reason: Sessions 09 and 10 own web store and UI behavior.*
* Historical suggestion telemetry or entitlement gates - *Reason: Phase 18 explicitly excludes analytics capture and entitlement gating.*

***

## 5. Technical Approach

### Architecture

Add `apps/server/src/managers/suggestionManager.ts` as the only owner of server-side Quest Board state. The manager should import and use the protocol Suggestion types, constants, parsers, cap helpers, and freshness helpers from `@factionos/protocol` rather than redefining the contract locally. Public methods should return cloned snapshots or cloned entries so callers cannot mutate internal state by reference.

Persistence follows the `NoticeBoard` pattern with a dedicated store file: resolve `stateDir` from injected test options, then `process.env.FACTIONOS_HOME`, then `~/.factionos`; read `suggestions.json` on construction; write JSON with mode `0o600`; use a unique temp file in the same directory and `renameSync` for atomic replacement; remove temp files on failed writes; debounce saves by default for about 1000 ms; and flush in `destroy()`.

The manager should expose a single `emitUpdate(broadcaster)` or equivalent single-path method that builds `SuggestionUpdateEvent` from `getCurrentState` and calls `Broadcaster.emit`. Mutating methods should either call that path when a broadcaster is injected or leave emission explicit, but no second snapshot builder should exist. `createFactionOsServer` should instantiate the manager, accept optional manager options for tests, and call `destroy()` in the existing shutdown `finally` block.

### Design Patterns

* Manager-owned state and persistence: all mutations, caps, filtering, and shutdown flushes live behind one boundary.
* Protocol-first types: the server consumes `SuggestionSnapshot`, `IdleSuggestion`, `CodebaseIssue`, `AnalysisResult`, `ProjectScan`, freshness constants, and validation helpers from `@factionos/protocol`.
* Atomic local persistence: writes use temp-plus-rename and never leave partial `suggestions.json` content after process interruption.
* Bounded mutation APIs: dismissed IDs cap at 100 FIFO, codebase issues cap by protocol helper, and active idle suggestions cap per session.
* Privacy by default: no raw prompts, absolute paths, file contents, provider payloads, or broad local paths are logged during persistence errors.

### Technology Stack

* TypeScript in `apps/server`.
* Node `fs`, `os`, `path`, and `crypto` standard library APIs.
* `nanoid` or `randomUUID` for generated identifiers, following local server patterns.
* Vitest through the root/server test project.

***

## 6. Deliverables

### Files to Create

| File                                                   | Purpose                                                                                                     | Est. Lines |
| ------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | ---------- |
| `apps/server/src/managers/suggestionManager.ts`        | Server-owned suggestion state, mutation APIs, summary, snapshot emission, persistence, and shutdown cleanup | \~520      |
| `apps/server/tests/suggestionManager.test.ts`          | Manager unit tests for transitions, dedupe, caps, freshness, persistence, corrupt recovery, and emission    | \~360      |
| `apps/server/tests/suggestionManagerLifecycle.test.ts` | Server lifecycle test proving shutdown flush wiring                                                         | \~120      |

### Files to Modify

| File                           | Changes                                                                                        | Est. Lines |
| ------------------------------ | ---------------------------------------------------------------------------------------------- | ---------- |
| `apps/server/src/server.ts`    | Instantiate `SuggestionManager`, accept test options, and destroy/flush during shutdown        | \~20       |
| `apps/server/README_server.md` | Add server-local Quest Board manager and persistence notes without claiming routes are shipped | \~25       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] `SuggestionManager` can create, list, accept, dismiss, and clear active idle suggestions with a per-session cap of 1.
* [ ] Dismissed suggestion IDs and dismissed issue IDs are capped at 100 entries with deterministic FIFO eviction and survive restart.
* [ ] Codebase issue adds dedupe by `type` + `filePath` + `line`; type and severity queries return deterministic ordering; clear-by-type only removes matching issue types.
* [ ] Analysis, project scan, and codebase scan freshness checks use the Session 01 protocol windows exactly: 30 minutes, 24 hours, 24 hours.
* [ ] `getSummary` returns counts and severity buckets matching current state.
* [ ] `getCurrentState` builds a protocol-valid `SuggestionSnapshot` and `emitUpdate` emits `type: "suggestion_update"` through `Broadcaster`.
* [ ] `suggestions.json` loads on startup, recovers from corrupt or malformed JSON as empty state, saves through temp-plus-rename, and leaves no temp files after successful save.
* [ ] Server shutdown calls the manager destroy/flush path so pending mutations are persisted.

### Testing Requirements

* [ ] Focused manager tests are written and passing.
* [ ] Server lifecycle shutdown flush test is written and passing.
* [ ] `npm --workspace apps/server run typecheck` passes.

### Non-Functional Requirements

* [ ] New persistence keeps local-first boundaries: no external transfer, no analytics, no hosted storage, no entitlement gating.
* [ ] Persistence errors expose compact stable codes and do not log raw paths, prompt text, file contents, or provider payloads.
* [ ] Server tests isolate state with temporary directories and clean them up.

### Quality Gates

* [ ] All files ASCII-encoded.
* [ ] Unix LF line endings.
* [ ] Code follows project conventions.

***

## 8. Implementation Notes

### Key Considerations

* `suggestions.json` expands the local data surface because it may contain file paths and code-derived suggestion text. This does not close trusted erasure; it should remain local-only and be documented as a manual deletion path in later docs/closeout work.
* Keep `unsupportedRoutes.ts` unchanged for `/suggestions/*` in this session. Routes remain unsupported until Session 03 wires handlers.
* Avoid importing, copying, or transforming any historical `EXAMPLES/` code. Use Appendix A only as behavior evidence.

### Potential Challenges

* Persistence corruption: Recover to empty state, record a compact error, and continue startup instead of crashing.
* Timer cleanup: Fake-timer tests should prove destroy clears pending save timers and does not leave asynchronous work behind.
* Snapshot drift: Always build snapshots from the manager store and protocol helpers so Session 03 routes and web consumers share one shape.

### Relevant Considerations

* \[P17-apps/server] **Manager-owned persistence and cleanup**: Keep filtering, pruning, and shutdown flush in one boundary so test isolation and lifecycle cleanup stay predictable.
* \[P03-packages/protocol] **Protocol leads cross-package work**: Session 01 already defined the shared shapes; this session consumes them instead of adding server-only contracts.
* \[P03-apps/server] **Local server boundary must stay conservative**: Keep local persistence and future route behavior inside validation, auth, rate limit, and body-size boundaries.
* \[P07] **Redaction is boundary-specific**: Suggestion text, issue messages, file paths, and persisted snapshots must stay minimized and must not leak through logs or diagnostics.
* \[P03] **Stable docs are the current contract**: README and API docs describe shipped behavior; `EXAMPLES/` remains evidence only.

### Behavioral Quality Focus

Checklist active: Yes Top behavioral risks for this session:

* Duplicate accept/dismiss or repeated issue adds can leave divergent active state unless mutations are deterministic and idempotent.
* Local persistence can corrupt state unless writes are atomic, debounced, flushed on shutdown, and recover safely from malformed input.
* Snapshot consumers can drift unless every emission uses one protocol-valid builder with cloned state.

***

## 9. Testing Strategy

### Unit Tests

* Test idle suggestion creation, per-session cap, accept, dismiss, duplicate dismiss, and deterministic list order.
* Test dismissed-ID FIFO caps for suggestions and issues.
* Test codebase issue add dedupe, type query, severity query, clear by type, and protocol cap behavior.
* Test analysis/project/codebase freshness boundaries with explicit fake clocks.
* Test `getSummary`, `getCurrentState`, and `emitUpdate`.
* Test persistence debounce, atomic save cleanup, restart round-trip, corrupt JSON recovery, malformed shape recovery, and destroy flush.

### Integration Tests

* Start `createFactionOsServer` with a temporary suggestions state directory, mutate the manager through an exposed test handle or lifecycle option, stop the server, and verify the pending store was flushed.

### Manual Testing

* After implementation, start the local server with a temporary `FACTIONOS_HOME`, trigger manager mutations through a focused test harness or later Session 03 route, stop the server, and inspect that only `suggestions.json` plus expected existing state files are written.

### Edge Cases

* Missing store file.
* Corrupt JSON and malformed but parseable JSON.
* Duplicate issue keys with and without line numbers.
* Dismissed ID cap overflow beyond 100 entries.
* Pending debounce timer during shutdown.
* Invalid timestamps and stale future/negative freshness calculations.

***

## 10. Dependencies

### External Libraries

* `@factionos/protocol`: Session 01 suggestion types, constants, parsers, and event contract.
* `nanoid` or Node `crypto`: generated suggestion IDs.
* Vitest: focused manager and lifecycle tests.

### Other Sessions

* **Depends on**: `phase18-session01-protocol-suggestion-contract-parity`
* **Enables**: `phase18-session03-suggestion-routes-and-websocket-parity`, `phase18-session04-idle-lifecycle-engine-parity`, `phase18-session05-session-summary-engine-and-noise-filtering`, `phase18-session06-codebase-issue-scanners`, `phase18-session08-on-demand-analysis-and-project-scan-engines`

***

## 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-session02-server-suggestion-manager-and-persistence/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.
