> 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/phase17-session04-web-store-and-cockpit-notice-board/spec.md).

# Session Specification

**Session ID**: `phase17-session04-web-store-and-cockpit-notice-board` **Phase**: 17 - Notice Board Coordination Parity **Status**: Completed **Validated**: 2026-06-05 **Created**: 2026-06-05 **Package**: apps/web, packages/protocol **Package Stack**: TypeScript

***

## 1. Session Overview

This session turns the bottom-right cockpit Notice Board from a compact compatibility feed into a real active-room coordination surface. Sessions 01 through 03 restored the expanded protocol contracts, persisted server manager, canonical REST routes, and WebSocket hydrate/message/resolve events. The web store now needs to consume those canonical and compatibility payloads without losing older notice shapes.

The work adds web-side normalization for expanded Notice Board payloads, handles realtime resolution without a refresh, applies target visibility when the cockpit has enough local hero or session identity context, and updates the Notice Board component to render the recovered coordination fields in compact form. The cockpit must distinguish a real empty board from a disconnected board and from a board where Settings tint filters hide every visible notice.

Notice Board messages are coordination data, not raw event logs. The web surface must display safe normalized notice fields only, preserve the local-first and optional War Room boundaries, and avoid implying hosted identity, public collaboration safety, trusted erasure, production auditability, or real executor behavior.

***

## 2. Objectives

1. Normalize canonical and compatibility Notice Board hydrate, message, and resolve events in the web store.
2. Render active-room notices newest first with recovered type, priority, author, age, target, related-file, content, and resolved-state details.
3. Preserve Settings severity filters as UI tint filters, not workflow type filters.
4. Add focused store and component tests for hydration, compatibility payloads, resolve handling, target filtering, empty states, filtered states, and compact list behavior.

***

## 3. Prerequisites

### Required Sessions

* [x] `phase17-session01-protocol-notice-contract-parity` - provides expanded Notice Board data model, aliases, event types, and protocol helpers.
* [x] `phase17-session02-server-notice-manager-persistence-and-context` - provides canonical active-room storage, targeting, resolution, context, pruning, and dedupe behavior.
* [x] `phase17-session03-server-routes-and-websocket-parity` - provides canonical routes and WebSocket hydrate/message/resolve events consumed by the web store.

### Required Tools/Knowledge

* Node 26.2.0 or newer with npm workspaces.
* React 19 component patterns in `apps/web/src/components`.
* Zustand store reducer patterns in `apps/web/src/store/useGameStore.ts`.
* Protocol Notice Board contracts in `packages/protocol/src/notices.ts` and `packages/protocol/src/events.ts`.
* Web test patterns under `apps/web/tests` using Vitest, React Testing Library, and happy-dom.

### 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.
* Run focused web tests before broader validation.

***

## 4. Scope

### In Scope (MVP)

* The web store normalizes `notice_board_hydrate` using both `messages` and `notices` payload keys - prefer canonical messages while preserving compatibility hydration.
* The web store normalizes `notice_board_message` using both `message` and `notice` payload keys - dedupe by notice id and keep deterministic newest-first ordering.
* The web store handles `notice_resolved` without refresh - update by `noticeId` or `messageId`, and merge a provided `notice` or `message` when present.
* The web store preserves compatibility for `body`, `content`, `severity`, `type`, `priority`, `targets`, `targetSessionIds`, `postedAt`, `createdAt`, `relatedFiles`, `resolved`, `resolvedAt`, and `resolution`.
* The web store enforces target visibility when local identity context is available from selected hero, selected mission, active mission, hero ids, or known session ids, and fails open only when the cockpit lacks enough identity context to decide.
* The bottom-right `NoticeBoard` renders active-room notices newest first and keeps the six-item compact cockpit list.
* The UI shows historical workflow `type` labels in addition to severity tint.
* The UI shows priority, author, age, content, target state, related files, and resolved state in compact form.
* The UI exposes an accessible expansion path inside the board when more than six visible notices are available.
* The UI provides distinct empty, disconnected, and filtered-out states for real Notice Board behavior.
* Settings severity filters continue to filter only visual severity/tint, not recovered workflow type.
* Cockpit metadata is updated if needed so loading, offline, and closed-stream copy matches the real Notice Board.
* Store and component tests cover hydration, message compatibility, resolve, target filtering, empty state, disconnected state, filtered state, and compatibility notice shapes.

### Out of Scope (Deferred)

* Server route implementation - Reason: Session 03 completed canonical REST and WebSocket parity.
* CLI notice commands - Reason: Session 05 owns user-facing notice commands.
* Hook context fetching and provider skill parity - Reason: Session 06 owns agent context access.
* Worker relay behavior - Reason: Session 07 owns Worker relay and catch-up.
* Local War Room bridge convergence - Reason: Session 08 owns room notice convergence.
* Automatic mission lifecycle posts - Reason: Session 09 owns status and completion auto-posts.
* Phase documentation handoff - Reason: Session 10 owns validation, documentation, and final handoff.

***

## 5. Technical Approach

### Architecture

Add a small pure helper module in `apps/web/src/lib/noticeBoard.ts` that owns web-side normalization, ordering, dedupe, target visibility, severity mapping, and resolve merging. Keeping this logic outside the React component gives the store reducer and tests a single source of truth without moving canonical Notice Board filtering out of the server.

Update `apps/web/src/store/useGameStore.ts` to call these helpers for `notice_board_hydrate`, `notice_board_message`, and `notice_resolved`. The store should continue to cap local board history at the existing compact history limit, but it must avoid replacing useful persisted notices with compatibility-only or empty mock-style state when canonical messages are available.

Update `apps/web/src/components/NoticeBoard.tsx` to map normalized notices to compact accessible rows. The component should still use Settings severity filters as tint filters, render no more than six compact rows by default, and provide an accessible expansion path for additional visible history without turning the cockpit rail into an unbounded scrolling surface.

### Design Patterns

* Pure normalization helper: keep contract compatibility and edge-case logic unit-testable outside React.
* Store reducer ownership: WebSocket event handling mutates Zustand state through compact, deterministic reducer paths.
* Compatibility adapter: canonical Notice Board fields lead, older aliases are preserved during migration.
* Accessibility-first compact UI: interactive expansion uses explicit button labels, focusable controls, and stable layout.
* Boundary-aware rendering: render normalized coordination fields only and do not expose raw prompts, transcripts, terminal output, command bodies, file contents, secrets, absolute paths, logs, exports, replay buffers, scans, media drafts, diagnostics, backups, or quarantined historical content.

### Technology Stack

* TypeScript in `apps/web` and protocol type imports from `packages/protocol`.
* React 19 for the cockpit Notice Board component.
* Zustand for web store state.
* Vitest plus React Testing Library for focused web tests.
* Biome formatting and linting through the root workspace scripts.

***

## 6. Deliverables

### Files to Create

| File                                      | Purpose                                                                                      | Est. Lines |
| ----------------------------------------- | -------------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/lib/noticeBoard.ts`         | Web Notice Board normalization, sorting, visibility, and resolve helpers                     | \~240      |
| `apps/web/tests/noticeBoardStore.test.ts` | Store reducer coverage for hydrate, message, resolve, dedupe, target visibility, and aliases | \~240      |
| `apps/web/tests/NoticeBoard.test.tsx`     | Component coverage for compact rendering, states, filters, expansion, and accessibility      | \~260      |

### Files to Modify

| File                                      | Changes                                                                               | Est. Lines |
| ----------------------------------------- | ------------------------------------------------------------------------------------- | ---------- |
| `apps/web/src/store/useGameStore.ts`      | Normalize hydrate/message/resolve events and enforce target visibility when possible  | \~110      |
| `apps/web/src/components/NoticeBoard.tsx` | Render recovered Notice Board fields, distinct states, and accessible expansion       | \~190      |
| `apps/web/src/lib/cockpitShell.ts`        | Align Notice Board loading/offline/error descriptions with real coordination behavior | \~20       |
| `apps/web/tests/CockpitShell.test.tsx`    | Adjust or add shell metadata expectations for real Notice Board states                | \~40       |

***

## 7. Success Criteria

### Functional Requirements

* [ ] Hydration uses canonical `messages` when present and compatibility `notices` otherwise.
* [ ] New messages use canonical `message` when present and compatibility `notice` otherwise.
* [ ] Duplicate notice ids do not create duplicate rows.
* [ ] `notice_resolved` updates the matching notice without refresh using `noticeId`, `messageId`, or the provided notice/message payload.
* [ ] Notices sort newest first using `createdAt`, `postedAt`, and safe fallbacks.
* [ ] Targeted notices hide from unrelated sessions or heroes when the web store has enough local identity context.
* [ ] The compact board shows no more than six rows by default.
* [ ] Additional visible history is reachable through an accessible expansion path.
* [ ] Type, priority, author, age, content, target state, related files, and resolved state render for compatibility and canonical notice shapes.
* [ ] Empty, disconnected, and filtered-out states are visually and textually distinct.
* [ ] Settings severity filters affect severity tint visibility only and do not filter by workflow type.

### Testing Requirements

* [ ] Store tests written and passing.
* [ ] Notice Board component tests written and passing.
* [ ] Cockpit shell metadata expectations updated and passing.
* [ ] Focused web tests and web typecheck passing.

### Non-Functional Requirements

* [ ] UI remains compact and stable in the cockpit rail without overlapping text or controls.
* [ ] Notice Board rendering does not expose raw prompts, transcripts, terminal output, command bodies, file contents, secrets, absolute paths, logs, exports, replay buffers, scans, media drafts, diagnostics, backups, or quarantined historical content.
* [ ] War Room collaboration remains optional and no copy implies hosted identity, public collaboration safety, production auditability, trusted erasure, or real executor behavior.
* [ ] Interactive expansion controls have accessible names, visible focus states, and keyboard support.

### Quality Gates

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

***

## 8. Implementation Notes

### Key Considerations

* Web-side target filtering should not replace canonical server filtering. It is a defensive UI visibility layer for the active cockpit identity context.
* Compatibility aliases are part of the migration contract. Do not drop `body`, `severity`, `targets`, `postedAt`, or `notice` payload handling.
* The existing Settings severity filter remains a UI tint filter. Notice workflow type labels such as `status`, `review`, or `conflict` are separate and must remain visible when their derived severity is enabled.
* The board is a compact rail component. Additional history should be reachable without letting a long list push cockpit layout out of shape.

### Potential Challenges

* Target identity ambiguity: mitigate by collecting known hero ids, selected hero id, selected mission id, active mission ids, and notice author session ids, then fail open only when there is no meaningful identity context.
* Event alias drift: mitigate with pure helper tests for `messages`/`notices`, `message`/`notice`, `targetSessionIds`/`targets`, and `createdAt`/`postedAt`.
* State replacement edge cases: mitigate by testing empty hydrate, duplicate messages, resolved messages, and mixed canonical/compatibility payloads.
* Compact UI density: mitigate with stable row structure, truncation where needed, accessible labels, and explicit expansion for additional history.

### Relevant Considerations

* \[P03-packages/protocol] **Protocol leads cross-package work**: Use existing protocol Notice Board types and event contracts instead of inventing web-local workflow vocabularies.
* \[P06-apps/warroom+apps/web] **War Room federation is optional and redacted**: The board may render coordination messages, but must not imply hosted identity, public collaboration safety, production auditability, certification, or full erasure.
* \[P07] **Redaction is boundary-specific**: Web rendering should stay limited to normalized notice fields and avoid displaying unsafe raw developer data.
* \[P03] **Stable docs are the current contract**: Treat `EXAMPLES/` as evidence only; current protocol, server, and web code define runtime truth.

### Behavioral Quality Focus

Checklist active: Yes

Top behavioral risks for this session:

* Contract alias handling may silently drop notices if canonical and compatibility event keys diverge.
* Target filtering may hide relevant notices or show targeted notices to an unrelated local identity.
* Empty, disconnected, and filtered states may collapse into the same visual state and mislead operators.
* Expansion controls may be inaccessible or may destabilize the cockpit rail layout.

***

## 9. Testing Strategy

### Unit Tests

* Test `apps/web/src/lib/noticeBoard.ts` helpers through store tests for canonical and compatibility notice shapes, ordering, dedupe, target visibility, and resolve merging.
* Test `useGameStore.applyEvent` for `notice_board_hydrate`, `notice_board_message`, and `notice_resolved`.

### Integration Tests

* Render `NoticeBoard` with controlled store and settings state.
* Verify row content, severity tint filtering, type labels, priority, related files, resolved state, empty state, disconnected state, filtered state, and expansion controls.
* Verify cockpit shell metadata copy for Notice Board surface states.

### Manual Testing

* Run the web app against the local server and post canonical Notice Board messages through the server route or WebSocket path.
* Confirm messages appear newest first, resolve without refresh, and keep the cockpit rail stable on desktop and mobile-sized layouts.

### Edge Cases

* Hydrate contains `messages`, `notices`, both, or neither.
* Message event contains `message`, `notice`, both, or an already-known id.
* Resolve event uses `noticeId`, `messageId`, a full `notice`, a full `message`, or only resolution metadata.
* `content` missing but `body` present, or `postedAt` missing but `createdAt` present.
* `targetSessionIds` and `targets` disagree.
* Settings filters hide all visible notices.
* More than six notices are visible.

***

## 10. Dependencies

### External Libraries

* React 19.2.6: existing web component runtime.
* Zustand 5.0.14: existing web store runtime.
* Vitest 4.1.8: existing test runner.
* React Testing Library 16.3.2: existing component test utility.

### Other Sessions

* **Depends on**: `phase17-session01-protocol-notice-contract-parity`, `phase17-session02-server-notice-manager-persistence-and-context`, `phase17-session03-server-routes-and-websocket-parity`.
* **Depended by**: `phase17-session10-validation-documentation-and-handoff`; Sessions 05 through 09 can use the cockpit board as the visible proof surface for CLI, hook, Worker relay, local bridge, and automatic-post behavior.

***

## 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/phase17-session04-web-store-and-cockpit-notice-board/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.
