7.9 KiB
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
-
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.
-
Import boundary enforcement worked. ESLint
import/no-restricted-pathswired in Story 1.1 held for the full epic. Zero cross-layer violations slipped through. -
Accessibility built into ACs, not added later.
prefers-reduced-motiongates, native<dialog>for focus trapping,aria-live="polite"on the VisibilityBadge — all specified upfront and delivered. -
UI pattern reuse was excellent.
ActionPopover(Story 1.5) was directly reused as the pattern forFirstEncounterPanelandVisibilityDetailsPanel(Story 1.6) — zero wheel reinvention. -
Architecture guardrails saved multiple errors. Three corrections were caught and applied mid-story rather than shipping as bugs.
-
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.jsspec vs ESLint 9 flat config (eslint.config.js) - Story 1.2: Epic path
src/adapters/foundry-adapter.jsvs architecture-canonicalsrc/foundry/FoundryAdapter.js - Story 1.2: Setting namespace
video-view-manager.*in spec vs correctscrying-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:
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
-
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.
-
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. -
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.
-
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.
-
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:
_revisionsMap inScryingPoolController.js:31— no cleanup on user disconnect - Listener cleanup:
init()socket + Hooks listeners never unregistered inScryingPoolController.jsandVisibilityManager.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 ✅ solidSocketHandlerecho/broadcast ✅ solidAVTileAdapterpattern available for Director's Board card tiles ✅StateStorevisibility 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
- ✅ Retrospective saved
- ✅
epic-1-retrospectivemarkeddonein sprint-status.yaml - → Create Story 2.1 (NotificationBus & Notification Verbosity) using
bmad-create-story - → 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."