- 180 new side-quest descriptions (9 pools × 20) in data/sidequests.json:
Apex, MindWeave, and Zenith each post patrol/bounty/escort copy in their
own doctrine voice, every body naming the seeded rival via {enemy}
- descriptionPool() in SideQuests.js now chains faction's own pool → shared
war-voice pool → neutral fallback; neutral boards never read war copy
- New dev/lib/faction-voice-lint.mjs catches article disagreement ("an Apex
hull"), doubled words, and unresolved braces across every rival resolution
- dev/jobboard-voice-shot.mjs renders all 180 listings through the real
JobBoardWindow panel, asserting no clipping, overflow, or grammar faults
- Updated sidequests/jobboard tests to assert pool counts, {enemy} presence,
dossier uniqueness, fallback chain, and zero brace leaks
- Documented the pool structure in README and PROJECT_NOTES
- Faction-controlled systems now spawn only opposing-faction hostiles (never pirates/neutrals), and local-faction fighters join the player as allies during the fight.
- Rework patrol, bounty, and escort side-quests to the faction war model: patrols are always in-system vs one seeded rival; bounties target a rival's member in a hostile neighbor gated on jump-gate access; escorts rescue a trapped transport in hostile space and deliver it back through the gate into friendly territory.
- Implement per-faction reputation standing (setFaction/changeFaction) with save persistence; faction-owned places now resolve standingFor() via the controlling faction.
- Job boards on faction worlds post only faction-context missions with generic war-voice descriptions ({enemy} placeholder); neutral boards unchanged.
- Update all dev test suites (encounters, bounty-hunts, escort, escort-flow, reputation, sidequests) to cover the new faction rules and gate locks.
- 8 new enemy types (frames 50–57) split 4/4: combo seekers fire flank-angle homing missiles with capped turn-rate; elementals throw darts that bloom into growing, damage-ticking electrified zones
- Balance follows the faction rule (2× damage, 3.5× armor vs pirate raider), documented in combat.json comments
- New `flank_barrage` and `shock_dart` abilities in EnemyAbilities.js with shared `stepShock` frame step wired into CombatSystem.update
- Generalized `_combatBootMode` to accept any id from `factionIds()` so new factions need no code change
- Painted frames 50–57 in ships-enemy.png/.psd; added capital-mindweave-surface-01 reference art
- Tests: dev/zenith.test.mjs (Node), dev/zenith-arena-browser.mjs (Playwright), updated combat.test.mjs roster and sheet-art checks, extended cdp-faction-arena.mjs with zenith tab
- Inferno (Offense, seat 2): flame arcs home on the nearest 2 hostiles
(3 from rank 4) and ignite a burn that ticks on the combat loop's clock;
scales in duration and per-tick damage with rank.
- Nanobots (Passive, no seat): mends 1% of max hull every 2s per point;
a full hull banks nothing.
- Wire flameArc + hullRegen effect handlers in SkillEffects.js and the
arc/burn/step logic in CombatSystem.js.
- Add firstPassiveOf() / hullRegen() to the skill ledger so the combat
loop can read the mend's interval and color off the ranked def.
- Update icon sheet, dev shots, and the browser/combat/data test suites for
the two new skills.
- Combat bands split at level 6 (5–7), 9–12, and 13+ (9–12 cap) with
weighted pirate rosters; compose() now draws `pirateTypes` from the band
roster and GameScene uses it when spawning one-shots.
- Patrol encounters gain tiered bands (levels 6+) with mixed rosters,
fatter rolling waves (up to 20 deep / 6 concurrent), contact-count odds
overrides, and per-tier `xpScale` dials; legacy array rosters normalize
to the weighted table.
- Bounty hunts replace the stubbed marksman band with tuned tiers
(marksman 6–10, deadeye 11–14, apex 15+): growing pickets and escorts,
escort roster mixes, elite armor/damage scales 3×–4×, and `xpScale`
trims so payout pace tracks the curve.
- Tests updated to assert the new band shapes, roster draws, XP scaling,
and tier resolution; skills-bar screenshots refreshed.
- Strung hostiles dim to the data's fx.stunAlpha and show a crackling discharge (Fx.stunArms) while the snare holds, then recover with a release beat when the stun expires or on kill/revive
- Add Fx.stunArms/killStunArms + jagBolt helpers for the per-frame discharge (jagged bolts, sparks, hull stutters) tied to the snare's clock
- Store per-craft stun color/alpha from the bola catch and drive hold/release via _stunHoldFrame/_stunRelease in CombatSystem
- Add dev/stun-shot.mjs CDP script + screenshots, extend combat tests for dim/discharge/release, bump skills-bar test timeout, reformat skills.json, refresh toxic-land video
- Define both skills in data/skills.json with energy, cooldown, ranks, seats,
and per-rank stun/decoy durations
- Implement projectile flight, target intercept, capture radius, and stun in
CombatSystem (launchBoleadoras / _stepBoleadoras / _snagBola)
- Implement Blink: spawn a decoy sprite at the old position, teleport the
hull to a random visible-screen point, and let the decoy absorb aggro and
enemy bolts until its clock expires
- Add bola orb/string visuals and snag readout in Fx.js (bolaStart/Step/Stop,
snag)
- Register new combat SFX events (bola_snag, blink) in CombatSfx.js
- Extend SkillEffects with stunMs/decoyMs helpers and boleadoras/blink handlers
- Update tests: Wraith model checks, Boleadoras/Blink combat behavior, decoy
aggro/fizzle, and SFX event roster
- Add canvas-drawn comet head, arrival fireball, and damage-radius ring textures in Fx.js, replacing the old single-dot streak/ring combo
- Switch all ram-related (and general glow) sprites from ADD to SCREEN blend to fix the "dark box" artifact over semi-transparent nebula backgrounds
- Introduce ember trail particles that linger ~1 s behind the hull during the dash, plus a flickering comet head that rides the ship and is released on impact
- Add `ramBlast()` impact sequence: ignition flash, blooming fireball, exact damage-radius ring, shock ripples, and debris sparks
- Wire in the heaviest camera shake beat (`ram`) and layer the data's sfxEvent under a thunder boom at impact
- Bump skill fx `ringLifeMs` to 480 so the damage-radius ring lingers long enough to read
- Add dev CDP harnesses (`cdp.mjs`, `ram-shot.mjs`, `ram-video.mjs`) and reference stills/video for the charge's three key frames
- Drop the `rankT` text object, its draw/update logic and data spec (`rankTextPx`) so seats only show icon, veil, sweep and seconds.
- Update `_ensureSkillIcons` fallback wording to reference frame/veil/sweep instead of rank stamp.
- Adjust browser test: stop asserting on rank text, recompute seat clip from the seat box center, and remove the rank-pixel regression check.
- Refresh screenshot fixtures (`dev/shots/*`) to match the new seat layout.
- On a confirmed class lock-in, GameScene now sets `_skillTreeAfterPick` and, once the class window's close beat finishes, delays 320 ms then opens SkillWindow with `source: 'classPick'`, handing the player straight into spending their banked points.
- Add an e2e check in classes-browser.test.mjs that a confirmed pick closes the class console and opens the skill tree with spendable points.
- Refresh skills-window screenshot fixtures to match current rendering.
- Add the fill graphics to the container before the label so it paints behind the text instead of covering it at 0.8 alpha
- Use C.dim instead of C.faint for inert labels and borders, which were too dark on the panel fill
- Swap tag/text colors so inert tags stay readable while active tags dim appropriately
- Refresh dev screenshots to match the corrected rendering
- Replace per-skill rows with a scrollable tile grid (authored `node` cells or
auto-layout by dependency depth), elbow join lines, rank badges and empty sockets
- Add detail drawer at bottom third showing the selected skill's copy plus ADD A SKILL
POINT, RESET and ASSIGN/CLEAR SEAT actions
- Introduce a console session in SkillState: points spent while open are PENDING and
resettable; closing commits them; reset cascades down a branch bought this session
or refuses when it would strand a committed point
- Wire GameScene onReset/onClose doors, toast copy and passive re-roll on refund
- Move all tree geometry/palette knobs into data/skills.json (window.tree) with clamped
fallbacks in SkillModel.windowSpec()
- Add pure treeLayout()/classTreeLayout() to SkillModel for canvas-free layout tests
- Expand dev/skills-window-browser.test.mjs to verify regions, tile placement, session
lifecycle, scroller (wheel/drag/thumb), and pixel-prove the gold join line
- Update README, PROJECT_NOTES and screenshot fixtures
- New `data/skills.json` (registered in manifest) owns every number/string for the
per-class skill trees, type taxonomy, seat table, bar look, and window copy;
code only reads behavior off it so adding a third skill is a data edit.
- Pure skill model + ledger (`js/skills/SkillModel.js`, `SkillState.js`): rank
points from the class bank, spend-through-unlock edges, seat bindings, save
round-trip via `record.playerSkills` (old saves load as an empty ledger).
- Combat runtime in `CombatSystem.js`: cast pipeline with per-skill recharge
clocks, energy spend/regen (now on by default), Ramming Charge dash + arrival
blast (4x..12x weapon damage by rank), Reinforced Plating hull pool re-rolled
from base so it never compounds; charge passes through the pack and settles once.
- UI: full-screen Skill Tree console (`js/ui/SkillWindow.js`) opened from the ship
console's SKILL TREE door (lit when the class owns a tree), combat bar seats draw
icon/rank/cooldown/affordability states with click-to-assign and right-click clear,
keys 1-8 cast or place.
- Evidence: new Node + Playwright suites (`skills.test.mjs`, `skills-combat.test.mjs`,
`skills-bar-browser.test.mjs`, `skills-window-browser.test.mjs`,
`skills-devdoor-browser.test.mjs`) plus screenshots in `dev/shots/`; README and
PROJECT_NOTES document the layer and dev doors (`?skill`, `?skilltree`).
- Pass `wordWrap` as `{ width, useAdvancedWrap }` instead of a bare number; Phaser 4 ignores the Phaser 3 shape so description copy was running off the panel.
- Add dev-only CDP probe (`dev/jobboard-wrap-*`) that boots to the Job Board, selects a posting, and dumps text-object geometry + overflow vs the panel's right edge.
- Commit captured probe artifacts (`dev/shots/jb-wrap.{png,json}`) as a regression reference.
- Add neutral ally assets: music, attack/scan/scan-complete speech lines, and pilot video.
- Enable the `bounty` category in data/sidequests.json with level-2 gate,
Neutral-only contexts, encounter chain odds, and 20 written leader dossiers
- Add pure bounty model to SideQuests.js: plan derivation, wave scripts, XP,
sequential waypoint rule, and completion only via the mark's kill
- Extend combat seam (Waves/CombatSystem/EnemyShip) for a named elite unit
deployed with the opening group at scaled hull/damage, plus haltWaves() so
killing the mark stops deployments while survivors stay hostile
- Wire GameScene/SurfaceScene/JobBoardWindow for locked rows, dossier readout,
sequential check-ins, engagement chain, and single idempotent claim
- Add dev/bounty-hunts.test.mjs (591 checks) and in-browser probe with JSON/PNG
evidence; update patrol/sidequests tests for the new live category
- Stamp `c.lockAt` as elapsed beat time instead of an absolute scene-clock value, so `el >= c.lockAt` holds and the slots actually lock onto the final level during a live session (previously they kept cycling random digits)
- Add regression test 6 covering a marquee on a scene clock at 60s
- Refresh marquee dev screenshots
- Add trestle-q4-04/05 speech assets
- New js/ui/LevelUpMarquee.js: a self-contained full-screen beat (dim, old-level shatter, digit-by-digit decode, lock-in FX, neon shutter peel) driven by hand-stepped timeline with no tweens; coalesces multi-level grants into one "+N LEVELS" beat and buffers mid-beat level-ups so none are dropped
- GameScene wires the marquee to XpManager's 'levelup' event, gates ship/combat/research/build/discovery ticks on its active flag (same pause contract as comms ack), tears it down on shutdown, and exposes a ?combat dev `marquee(from,to)` preview hook
- Add headless Node test (dev/marquee.test.mjs) covering trigger/timeline/reap, coalesce, buffer, teardown, and the FIRMWARE-MAXED cap branch
- Add CDP screenshot harness (dev/marquee-shot.mjs) plus committed dev/shots/ evidence frames for each beat phase via a new debugSeek/debugRelease latch
- Kills grant the hostile type's own `xp` (the roster sets XP-per-threat;
the curve lives in data/progression.json, 100 x n^2, capped at level 80)
- Each crossed level fires one `levelup` event: +3 hull / +2 shields with
a full heal, cyan burst + a distinct voice, and a kill-feed line — the
placeholder toughness the skill layer will subscribe to later
- Combat bar gains an XP row below the orbs (level, live gold fill,
banked / next cost; hidden when the snapshot carries no xp, so the
range bar keeps its old face)
- Kills show a gold "+N" reward floater; the level-up's SFX is the new
8th combat event (solar_flare_01)
- XpManager is pure and tested headless (node dev/xp.test.mjs); the
ledger saves as { level, xp } and old saves load fresh at level 0
- dev hooks (xp/grantXp), sfxcheck (range seven, main eight), shot
captures, and README all follow along
- Introduce js/ui/JobBoardWindow.js: nearly full-screen (depth 80) build-console-style window with a muted looping 2:3 feed on the left, and AVAILABLE JOBS / JOB DETAILS placeholder sections on the right; open/close state machine, boot reveal, ambient glitch/sweep/cursor effects, ✕ close door
- Wire the Job Board slot through SurfaceScene: toggleJobBoard/leaveJobBoard, setServiceLocked (generalized from setBarLocked) so deck + drawer stand down while the board is up with the jobBoard slot kept live as a second door; pause/resume the surface clip under the console
- Preload assets/videos/jobboard.mp4 in GameScene via data/actionbar.json → surface.explore.video (2:3 aspect), adding a NO SIGNAL fallback when missing
- Update data/actionbar.json explore drawer with the jobBoard item (isHomeSystem:false, no labelActive) and the shared video block
- Add headless test dev/jobboard.test.mjs covering config, build, geometry, open/close, update ticks, and destroy; extend phaser-stub with setVisible/setDepth
- Add dev screenshot harness dev/jobboard-shot.{html,mjs} and reference shots for drawer/board/left stages
- Prevent overlapping hit flashes from stacking, which captured the in-flash
state as the restore value and left crafts permanently white
- Track flashed crafts in a Set so the expiry path can always settle them
- Add purgeCraftTints(), called every ~250 ms from update(), that re-asserts
normal colors on any craft (tracked or roster) not holding a live flash
- Wire purgeRoster in both CombatScene and CombatSystem so the purge covers
the full player + enemy pack, not just Fx's own flashes
- Prune destroyed crafts from the tracking set to avoid leaked references
- Add tests covering overlap extension, kill-seam settle, periodic purge,
roster coverage, and dead-craft pruning
- Add Fx.releaseCraft() to settle a craft's pending white tintFlash
immediately on kill/destroy, so sprites never die mid-flash
- Add exported settleCraftTint() helper that resets tint mode and tint
to identity; call it on revive (EnemyShip), respawn, and reuse seams
- Guard restore callbacks against destroyed sprites with try/catch so a
half-restored craft can't leak white state
- Update combat screenshots
- Replace dot tracers with elongated glowing bolts (trail band + core streak + head bloom)
and a directional muzzle flare in both the range and main-game contexts.
- Add enemy impact reads: core flash, directional sparks along the travel vector,
cyan arc ripple on shield hits vs orange shards on hull hits, white craft hit-flash,
and a polished damage-number pop.
- Add player shield-sphere strike effect: full-sphere outline + bright arc centered on
the laser's incoming bearing, radial ripple, spark burst, and a shield-collapse cue
when shields break to hull.
- Add subtle world-camera shake on player hits, enemy kills, and shield-break while
keeping the HUD/combat bar perfectly still via the fx-ui camera split.
- Expose new visual knobs (bolt size/trail, muzzle, arc span, shake intensity) in
data/combat.json; fix Fx.update destroying ships on tintFlash expiry.
- Replace vertical gauges with draining hull/shield/energy orbs
(hull with overlapping shield on the left, energy on the right).
- Share the main game's CombatBar in the combat range and add
range energy tracking with optional regen.
- Make gauge hit flashes shape-aware: circle orbs get a soft bloom,
rects keep the legacy white pulse.
- Update combat data and assets for the new orb-strip storyboard.