# Retrospective — Epic 1: Core Camera Visibility Control

**Date:** 2026-05-22  
**Facilitator:** Amelia (Developer)  
**Project Lead:** Morr  
**Status:** Complete

---

## Epic Summary

| Metric | Value |
|--------|-------|
| Stories Completed | 6 / 6 (100%) |
| Test Count at Close | 296 tests, all passing |
| Technical Debt Items | 8 open (deferred-work.md) |
| Major Blockers | 1 (OQ-1 WebRTC spike — resolved as css-fallback) |
| Architecture Deviations Corrected | 3 |
| Previous Retrospective | None (first epic) |

**Stories:**
- 1.1 — Module Scaffold, CI/CD & Design Token System ✅
- 1.2 — WebRTC Spike — Track Disabling API Validation ✅
- 1.3 — Data Layer — FoundryAdapter, StateStore & Socket Infrastructure ✅
- 1.4 — Core Logic — ScryingPoolController & VisibilityManager ✅
- 1.5 — GM Control UI — ScryingPoolStrip, ActionPopover & AV Tile Integration ✅
- 1.6 — Player Camera Status Badge ✅

---

## Successes & Strengths

1. **TDD discipline held across all 6 stories.** Started at 49 tests (Story 1.1), ended at 296 — every story delivered a passing pipeline with zero regressions.

2. **Import boundary enforcement worked.** ESLint `import/no-restricted-paths` wired in Story 1.1 held for the full epic. Zero cross-layer violations slipped through.

3. **Accessibility built into ACs, not added later.** `prefers-reduced-motion` gates, native `<dialog>` for focus trapping, `aria-live="polite"` on the VisibilityBadge — all specified upfront and delivered.

4. **UI pattern reuse was excellent.** `ActionPopover` (Story 1.5) was directly reused as the pattern for `FirstEncounterPanel` and `VisibilityDetailsPanel` (Story 1.6) — zero wheel reinvention.

5. **Architecture guardrails saved multiple errors.** Three corrections were caught and applied mid-story rather than shipping as bugs.

6. **Morr's assessment:** Everything runs fine. Clean green on all tests.

---

## Challenges & Growth Areas

### 1. Epic/Architecture Spec Drift (3 occurrences)

Mismatches between the epics file and architecture document caught mid-story:
- Story 1.1: `.eslintrc.js` spec vs ESLint 9 flat config (`eslint.config.js`)
- Story 1.2: Epic path `src/adapters/foundry-adapter.js` vs architecture-canonical `src/foundry/FoundryAdapter.js`
- Story 1.2: Setting namespace `video-view-manager.*` in spec vs correct `scrying-pool.*`

**Root cause:** Architecture doc written before epics were finalized; paths/namespaces drifted.  
**Resolution:** Dev agent caught all three. No shipped bugs, but correction cycles add overhead.

### 2. Spike Story AC Format

Story 1.2's ACs assumed `track-disable` would be the spike outcome. The spike proved it wasn't — `css-fallback` is the FoundryVTT v14 reality. The dev agent correctly updated test expectations, but the story file had wrong expectations going in.

**Root cause:** Spike ACs written as implementation expectations, not hypothesis format.

### 3. Vitest Fake Timer Nested Pattern Not Documented

Story 1.6 discovered that `vi.advanceTimersByTime(N)` requires two separate advance calls when a timer callback schedules a nested timer. The first advance fires the outer timer; the second fires the inner one. This cost debugging time.

**Pattern discovered:** 
```js
vi.advanceTimersByTime(10_001); // fires 10s collapse timeout
vi.advanceTimersByTime(301);    // fires 300ms CSS transition replacement timer
```

### 4. Story 1.1 Review Depth

The Story 1.1 code review generated 3 separate review prompt files and 7 deferred CI/CD items. While appropriate for a scaffold story, the depth felt heavy relative to the scope.

---

## Key Insights & Lessons

1. **Spike stories need hypothesis-format ACs.** Write: "Given X, when spike runs, then outcome will be documented as one of [A/B/C]" — not pre-determined implementation expectations.

2. **Vitest nested timer testing requires two-step advance.** Always use two `vi.advanceTimersByTime()` calls when the callback under test schedules a subsequent timer. Document this pattern visibly in dev notes for any story with timer-based logic.

3. **Architecture doc is canonical over epics.** When epics and architecture disagree, architecture wins. Path inconsistencies should be resolved in epics review before stories are created.

4. **Foundation story reviews deserve more scrutiny.** Story 1.1 review depth was appropriate; the overhead is proportional to its cross-cutting nature. Non-foundation stories can use lighter review.

5. **Reuse patterns establish themselves within 1-2 stories.** ActionPopover → FirstEncounterPanel/VisibilityDetailsPanel shows that patterns spread naturally when the first implementation is clean.

---

## Deferred Technical Debt (from deferred-work.md)

These items are tracked, not forgotten, and should be addressed in Epic 2:

**HIGH PRIORITY (should fold into Story 2.1 or 2.2):**
- [ ] Memory leak: `_revisions` Map in `ScryingPoolController.js:31` — no cleanup on user disconnect
- [ ] Listener cleanup: `init()` socket + Hooks listeners never unregistered in `ScryingPoolController.js` and `VisibilityManager.js`
- [ ] Echo revision validation: No guard against NaN/Infinity in `ScryingPoolController.js:164`

**MEDIUM PRIORITY:**
- [ ] Echo accepts non-finite revisions (same file, revision ?? 0 doesn't validate type)
- [ ] CVE-2023-43645 in flat-cache-4.0.1 (needs verification — may be transitive dev dep only)

**LOW PRIORITY (CI/CD, pre-existing):**
- [ ] Missing failure notifications, missing build artifact upload, no Node matrix testing
- [ ] Missing concurrency control, missing i18n schema validation, ubuntu-latest not pinned

---

## Epic 2 Preview & Readiness

**Epic 2: Player Notifications & Director's Board**  
Stories: 2.1 NotificationBus, 2.2 Director's Board Core, 2.3 Director's Board Bulk Actions

**Dependencies on Epic 1 work:**
- `ScryingPoolController.action()` + event system ✅ solid
- `SocketHandler` echo/broadcast ✅ solid
- `AVTileAdapter` pattern available for Director's Board card tiles ✅
- `StateStore` visibility matrix ✅

**Readiness: CLEAR TO PROCEED ✅**  
No blockers. Recommended: fold memory leak + listener cleanup into Story 2.1 dev tasks.

**Preparation notes for Story 2.1:**
- NotificationBus introduces a 3s coalescing timer — explicitly include the vitest fake timer two-advance pattern in dev notes
- Include deferred debt items (_revisions cleanup, listener unregistration) as Story 2.1 technical tasks
- NotificationBus subscribes to ScryingPoolController events — the listener cleanup debt becomes higher priority once another subscriber is added

---

## Action Items

### Technical (carry into Epic 2)

| Item | Owner | Priority | Target Story |
|------|-------|----------|--------------|
| Fix `_revisions` Map leak (`ScryingPoolController.js:31`) | Dev | High | 2.1 or 2.2 |
| Add listener cleanup to ScryingPoolController + VisibilityManager | Dev | High | 2.1 or 2.2 |
| Add echo revision type validation (reject NaN/Infinity) | Dev | Medium | 2.1 or 2.2 |
| Verify CVE-2023-43645 flat-cache scope | Dev | Low | Backlog |

### Process Improvements

| Item | Action |
|------|--------|
| Spec drift | Review epic paths/namespaces against architecture doc before story creation |
| Spike ACs | Write spike ACs as hypothesis format, not expected outcomes |
| Fake timer pattern | Add vitest nested timer two-advance pattern to story dev notes whenever coalescing timers are involved |

---

## Next Steps

1. ✅ Retrospective saved
2. ✅ `epic-1-retrospective` marked `done` in sprint-status.yaml
3. → Create Story 2.1 (NotificationBus & Notification Verbosity) using `bmad-create-story`
4. → Fold deferred debt items into Story 2.1 dev tasks

---

*Amelia (Developer): "Great session, Morr. Epic 1 delivered a solid, tested foundation. Epic 2 is clear to go."*

*Winston (System Architect): "The architecture held. Import boundaries, constructor rules, token system — all intact."*

*Sally (UX Designer): "Accessibility-first delivered. Epic 2 has its own UX challenges with the Director's Board — looking forward to it."*
