Bug: timeSinceStart only updated when an envelope event fired
(combatStart, attack-roll, etc.). If the combat was idle — a
player thinking about their turn, between turns — the timer froze.
Fix: 1Hz self-rescheduling setTimeout in the constructor that
bumps timeSinceStart and triggers a throttled render while
_state.isActive is true. Stopped on unwireHooks (combat end).
TDD: Section O (5 assertions) added BEFORE the fix.
- O.1 initial timeSinceStart reflects _combatStartedAt
- O.2 advances without an envelope event
- O.3 tick interval is ~1s
- O.4 timer does not tick when combat is inactive
- O.5 timer resumes when combat becomes active again
Tests: 65/65 passing in ~2s. Playwright 31/31.
Bug 1: current-turn indicator not updated on combatant switch.
Foundry's combatTurn hook fires BEFORE the new state is committed,
so game.combat.combatant is the OLD combatant at that moment. The
event-translation layer now resolves the new combatant from
combat.turns[newTurn] and passes combatantId + combatantName +
combatantTokenId through. The HUD stashes these on _state._latestTurn
and uses it as a tiebreaker when the encounter's combatantId is
stale (which battle-focus's encounter always is, since bf doesn't
update currentTurn on turn events).
Bug 2: HUD doesn't open on session-open with a pending/active combat.
At ready, check battle-focus.api.getActiveEncounter(); if it's
live (not endedAt), open the HUD and populate from the encounter.
This handles browser-refresh mid-combat and 'pending combat'
(tracker exists but Start Combat not yet clicked).
Bonus: encounter.combatantId key resolution. The encounter's
combatantId is the Foundry combatant document id, but the
combatants map may be keyed by tokenId, actorId, or combatantId.
The currentTurn resolver now tries all three.
Tests: 60/60 passing in <1s. Sections M (turn event) + N
(session-open) added.
- New api.isReady() returns true after the ready hook fires (and
core sections have registered). Tests now wait for isReady()
before asserting on the section list.
- 3-place version rule (module.json + package.json + main.js
MODULE_VERSION) — v0.2.2 was missing the source bump; corrected
by going to v0.2.3 across all three places.
- Tests: 50/50 passing in <1s.
Missed the 3-place version rule on the v0.2.2 bump. MODULE_VERSION
was still v0.2.0 in scripts/main.js, so Foundry's init log showed
the old version and the live system didn't have getFeed/clearFeed.
Caught during the e2e Playwright run when Foundry loaded v0.2.0
of the main.js but v0.2.2 of the module.json.
- api.getFeed(sectionId) returns a shallow copy of the section's
feed entries. Useful for tests + consumer introspection.
- api.clearFeed(sectionId) empties a section's feed.
- Smoke: 50/50 passing. Section C extended with new assertions.
- This unblocks the its-achievable v0.3.0 Playwright test, which
needs getFeed to verify chatBubble → pushToHubFeed → chh feed.
- Combatants row whose tokenId matches encounter's current combatant
gets data-chh-current-turn=true, an accent-color border + tint, an
outlined portrait, and a '▶' marker before the name.
- Header section prepends '▶' before the turn name.
- buildRenderContext now resolves currentTurn from the encounter
singleton (encounter.currentTurn / encounter.combatantId) instead
of reading game.combat directly. battle-focus owns the encounter.
- Tests: 46/46 in <1s, new section L covers the indicator.
- Public API: addSection, removeSection, pushFeedEntry, listSections,
getHud, openHud, closeHud.
- Soft-dep on foundry-hooks-lib (envelope subscription) + battle-focus
(encounter seam).
- Built-in core sections (round, current turn, per-PC damage, dice
streak) register only when both deps are present at ready.
- Smoke tests: 22/22 in <1s covering sections A-G of tests/PLAN.md.
- HUD instance is a stub in v0.1.0; ApplicationV2 mounting in v0.2.0.