AI-maintained tracker of planned, in-progress, and abandoned features. Checkboxes are read-only — Claude updates them as work lands.
Autonomous milestone run kicked off 2026-04-27, completed 2026-04-28. All items below shipped under separate milestones; no silent shelving. Generated 2026-04-28.
public/docs/milestone-jargon.md covers the 10 most opaque labels: slot-mult, Meter, Ev/EV/ev-overlay, Manual mode, Hardcore/RIP, Companion level sync, Auto-equip/build/spend, Tap weapon/utility, Fame, Veil/veilspawn. Linked from Canonical Docs in CLAUDE.md.93 items (74 from brainstorm.html + 19 manual additions). Grouped by target milestone. Small UI/polish first, then accessibility, then devtools/sims, then gameplay/content. Approved 2026-04-26.
Workflow expectations baked into this batch:
Extensibility layer: JSON schemas + effect DSL that let anyone describe a character/spell/item/event and drop it into the game.
/schemas/v1/done — m128/assets/custom-content.htmldone — m128actor.statuses[]: burn, poison, bleed, stun, barrier (M166), dmgBuff (M168), critBonus temp-buffs (M169 — addCritBonusStatus/getCritBonusTotal; static talent/gear baseline still lives on actor.critBonus, temp combat buffs now stack as status entries). CombatScreen + simulator + tests all migrated; skillVerify stays on legacy snapshot path (static harness, not runtime).stealStatus DSL op (src/mods/dsl.js) moves one/many statuses from from → to, preserving duration/stacks/amount. Supports single type, array of types, or prefix match (e.g. "buff:"); optional max cap. Demo skill arch_dispel_steal (Thief's Whisper) strips all buff:* from target onto caster. Tests: src/game/__tests__/steal_status_m172.test.js.src/game/passives.js: HP/MP/regen nodes kept (user-approved); new mechanical nodes add blockChance, thorns, resistAll, dodgePct, lifesteal, burnOnHit, poisonOnCrit, chainOnHit, hpOnKill, manaOnKill. All 19 class trees redesigned with 4–5 role-themed mechanical picks + 1 staple. Wire sites in CombatScreen: _resolveIncomingDamage (resistAll), _basicAttack (burnOnHit, poisonOnCrit, chainOnHit), _applyDamage (thorns, hpOnKill, manaOnKill). Lifesteal piggybacks on actor.lifeSteal. 202/202 tests passing.CombatScreen._applyDamage (soulbind block, recursion-safe via _soulbindProcessing). Demo skill arch_soulbind in archetype-spells-2.json (cleric, 50% split, 4r). 3 vitest cases in soulbind_m175.test.js.counterStance status — returns amount% of incoming damage to attacker, decrements stacks per hit, auto-removes when exhausted. Recursion-safe via _counterProcessing flag. applyStatus DSL op extended to carry amount/power for status entries. Demo skill arch_counter_stance (Iron Counter): 2-stack, 150% reflect, 3-round duration. Tests: src/game/__tests__/counter_stance_m173.test.js.public/mods/examples/archetype-spells-2.json — applyStatus 'silenced' for 2 rounds. AI skill-picker (CombatScreen._heroAI, simulator.simHeroAI, CombatScreen._enemyAI healer branch) now gates any magic/heal/mp-costing skill behind isSilenced() via src/mods/statusModel.js::isSilenced. Tests: src/game/__tests__/silence_m171.test.js.public/mods/examples/archetype-spells-2.json — applyStatus('soul_tether') to both caster+ally, onEvent('onDeath') heal-pulse revive. Runtime must emit onDeath (same class as onDodge exemplar).public/mods/examples/event-chains.json — 5-node chain: start → rescued_1 → ring_reward → second_capture; abandoned branch leads to hostile/forgave.Decouple character class (skills/talents) from appearance (portrait + animation frames). Play a cleric who looks like a lightning mage.
src/game/appearances.js with 20 base appearances (one per existing class) plus 1 extension: Cleric Priestess.src/game/companions.js:1-26: optional east/east_attack/east_ko/east_block frames resolved by src/game/spriteUtils.js. Existing south-only companions still work via fallback.getCharacterBlockStats in src/ui/screens/CombatScreen.js:218-244); east_block frame now part of the documented schema. Spell view: DROPPED — companions have no class skill list and do not cast, so east_spell is intentionally omitted (see note in src/game/companions.js).Previously referenced in milestone-report.html but never implemented. Require both class-data work (skills, talents) and art gen.
public/data/pixellab_redesign_state.json already lists 16 female variants with referenceStatus: approved: warrior_female, fighter_female, paladin_female, mage_female, bard_female, chronomancer_female, demon_hunter_female, necromancer_female, scavenger_female, swashbuckler_female, stormcaller_female, tactician_female, runesmith_female, witch_hunter_female, monk_female, sorcerer_female. This wishlist entry was stale from the M161 era before those sprites were generated.images/pixellab/pending-review/druid_male/east_block.png, (4) state.json marks the pose pending_approval. Requires ~15 credits. Kept open (not silently shelved) — runs when other druid work batches.Structured schema pipeline that makes content generation easy and low-bug.
appearances kind. Inference extended in src/mods/registry.js:71-90 + public/assets/custom-content.html:83-92 to also accept raw 7-frame sprite objects (portrait/south/east/east_attack/east_ko) without requiring a classDefault hint.items kind — inferred from dmg/armor/weaponCategory. Example: public/mods/examples/custom-weapons.json.characters kind — inferred from classId or hp+tier. Example: public/mods/examples/custom-enemies.json.classes kind — inferred from primaryAttr/startingStats. Skills packed under skills merge into the class via classOrigin (src/game/skills.js:2024-2037).events kind — inferred from nodes+actRange. Example: public/mods/examples/custom-event.json. Event-chain flag persistence via GameState.flags (M133 ledger).In-game AI that reads the mod schemas + currently-loaded modules and generates new content on demand.
Items surfaced in earlier batches that are still open.
scripts/batch-remove-bg.cjs walks sprite dirs, detects PNGs with no transparency (min alpha = 255), runs corner-sampled chroma removal. 503/1262 opaque sprites processed; 759 already-transparent files skipped. Same weighted-Euclidean algorithm as remove-bg.cjs.index.html hero section. Kept cloud-layer-1 (opacity 0.45, 90s drift).attachTooltip on .skill-row — click-to-view-detail is now the single source of truth.onEnter (and on character-tab switch), SkillTreeScreen picks the first character who has unspent points and jumps to the tab containing them (priority: skill → passive → attr). Single-tap from level-up to "where do I spend?"src/ui/components/Tooltip.js (attachTooltip + injectTooltipStyles). Wired into Skill Tree skill rows and passive nodes (src/ui/screens/SkillTreeScreen.js:378-422). Inventory already uses its own richer tooltip. Forge tooltip was present but had two bugs fixed in M164: (1) touchstart held a stale event.currentTarget reference (null after handler returns) — fixed by capturing the recipe element in the closure and passing it directly to _showTooltip(anchorEl, recipeId); (2) CSS ::before arrow was on the wrong edge (appeared at tooltip top pointing up, but tooltip was above the anchor) — replaced with ::after at top:100% using border-top-color, plus a .tooltip-below variant for when the tooltip flips. Also added touchmove cancel to abort long-press on scroll. See src/ui/screens/ForgeScreen.js:207-232 (event wiring) and _showTooltip.Move off pixellab archive entirely. All assets indexed in image-review. Rigid per-type spec documented in /docs/.
Markdown policy files live in /docs/. Tools > Documentation renders them in-browser.
Batch of 18 UI fixes shipped in M316. All items below marked done.
9 UX and codex items shipped in M318. All items marked done.
Move skills out of hardcoded class definitions into a swappable, data-driven system. Variable skill count per character. Spell catalog tool mirroring image-review.
src/game/classes.js already has classes with 4 and 6 skills (e.g. fighter has 6). getClassSkills/getUnlockedSkills in src/game/skills.js:2024-2043 return the full class list unbounded. SkillTreeScreen._render (src/ui/screens/SkillTreeScreen.js:90-103) loops skills.map. Combat uses AI picker (src/ui/screens/CombatScreen.js:1096-1175) which also iterates unbounded. Character Builder seeds skills: [this._class.skills[0]] (src/ui/screens/CharacterBuilderScreen.js:305) — single-element seed, not a cap. Door is already open for hybrid-class builder.Finishing the mod-system-ready combat primitives. Each item below is a separate milestone.
GameState.storyFlags persisted via toSaveData; primitives hasFlag/setFlag/incrementFlag/consumeFlag/requireFlags in src/game/gameState.js:154-189. DSL ops incrementFlag/consumeFlag/requireFlags route through ctx.gameState for persistence in src/mods/dsl.js:66-105. Tests: src/game/__tests__/flag_ledger.test.js.statuses[] with zero legacy-property reads or writes in src/. M166 removed the dead legacyMap in statusModel.hasStatus and the dead actor.barrierHp write in overhealToBarrier. Remaining: dmgBuff (CombatScreen.js:1410, 1558, 1795, 1887, 2085) and critBonus (CombatScreen.js:1262, 1443, 1796, 2090) — still live legacy fields, tracked separately.src/ui/screens/CombatSimulatorScreen.js (bundle: party, gear, enemies, act, seed, lastResult). Import re-runs with the stored seed so the result reproduces deterministically. Existing Save/Load Preset flow (localStorage + file download) untouched.src/ui/screens/CombatSimulatorScreen.js: (1) Load Preset now opens a modal dropdown (replaces prompt()) with hero/enemy counts and timestamp; Save Preset stamps ts field. (2) Diff vs previous run panel — appears after 2nd Monte Carlo run, shows win rate / avg rounds / dmg mean with colorized deltas (green = better, red = worse). (3) Benchmark toggle — sweeps party at levels 1/3/5/7/10 against the current encounter, renders inline CSS bar chart (no chart library). All 190 tests pass.Items deliberately shelved. Keep them visible so they're not forgotten — and so we don't add more without naming the tradeoff.
src/game/telemetry.js + public/docs/telemetry.md). Not wired: (1) Supabase table + indexes + RLS, (2) edge function to accept POSTs and bulk-insert with service role, (3) setTelemetryEndpoint(url) call in main.js, (4) per-event recordEvent calls in MapScreen/CombatScreen/LevelUpScreen. Full step-by-step is in public/docs/telemetry.md. Until activated, events queue locally but never leave the device.CLAUDE.md (🛠️ New-tool flag rule section, line 16). Libraries ≤25KB gzipped auto-approved; anything larger requires name + size + reason + approval. Accepted adds logged in this Technical Debt Registry with their tradeoff.Findings from the comprehensive code-review pass on 2026-04-24. Full report at memory/code-review-m271.md. Top items flagged here for user decision on priority.
slotCount multiplier. Duplicate-slot entries in pyromancer (igniting×2) and stormcaller (stormcharged×2) passive trees are now UI-aesthetic only; one purchased rank = one effective rank, matching every other class. This is a slight nerf to those two classes — rebalance sweep after playtest will determine if they need compensatory buffs.<img src=x onerror=...> would render. Added esc() helper applied to r.name, h.target, srcName across the meter + Combat Report drill-down.emberveil_save_; the class-unlock storage uses emberveil_unlocks. Added a META_KEYS allow list + isSyncableKey() helper; classUnlocks._write now pushes to cloud on every local unlock write. On next sign-in the existing downloadAllMissing pulls the unlock blob automatically.src/ui/screens/_meterTracker.js in M273 (proof-of-pattern). CombatScreen.js is now ~8,300 lines after additional features. Remaining cleanly-extractable modules: damagePipeline.js (armor/block/barrier/reflect resolution), aiTargeting.js (hero + enemy AI), backgroundRenderer.js (parallax layers). Future milestones planned per-module to keep each PR reviewable._applyDamage integration test that wires actor → roll → mit → barriers → reflect → meter. Pyromancer regression already pinned by passives.test.js.maxMp || 80 fallbacks with computeMaxMp(member)done — pre-m412JSON.parse(JSON.stringify()) with structuredClonedone — pre-m412Recent milestones. Archived here as a changelog + to confirm nothing got silently shelved.
member.skillsdone (M264)_heroAI now intersect unlocked class skills with the player's selected list. Previously sim would auto-cast Holy Strike for a knight who only picked Shield Bash. Primary sim-vs-playtest divergence._mergeInto now uses REPLACE semantics for upgrades (was additive). 43/63 damage skills were scaling ≥2× above their tooltip numbers (Shield Bash 4.9× STR instead of 2.0×). Tooltips in Skill Tree render the effective multiplier via mergeSkillForCast. 1 residual outlier (Slow Time, intentional talent stack).skillName. Added breakdown.skillName = skill.name before _applyDamage.pendingSkillPoints: 0 and no skills array populated. Now get skill + passive pending points for every level missed, all class skills unlocked at their level, and autoBuild flags on so points auto-spend on hire. Attr points intentionally stay 0 (template.attrs already bakes in the level's stat spread)./assets/rebalance-report.html — pure-SVG charts covering audit before/after (43→1 outliers), balance profile sweep, per-save round counts, meter mockups. Regression tools: scripts/skill-audit.mjs, scripts/balance-sweep.mjs, scripts/balance-report.mjs, scripts/balance-single.mjs.Added 2026-04-23. Working through top to bottom. Each item flips to "done" as it lands, or moves to Shelved with a specific reason.
playable: false excludes from character-creation + tavern rolls. Generate reference + 7 poses for Silas Veilward, Kaela Thorne, Marek Greel, Mira the Seer via SpriteCook following art_direction policy. Save to public/images/pixellab/<id>/ and flag for approval on character-redesign page (pending_approval).Items deferred because the current system couldn't host them cleanly. Surface when the foundation catches up.
char.attrs via the SkillTreeScreen +1 button (see src/ui/screens/SkillTreeScreen.js) and re-renders after every spend.public/images/spritecook/, manifest entries are canonical, SPRITE_MAP entries wire correctly. M236 separately ships an image-review live-data rebuild that derives everything from the approved-sprite state, so if there's still any drift the new manifest will surface it — any future repro opens a NEW entry with a specific screenshot, not this old one.index.html yet. That copy needs a content-audit pass first — existing marketing copy has known drift vs current mechanics (quest content incomplete, dragon act not represented). Plus the promised scrolling .subnav with scroll-spy section indicator. Commit link for recovery of old content: pre-M242f HEAD on main; sections live under public/game-info/index.html in history._ACT_TOWN_INSERTS: Greenbough (thornwood @ goblin_camp), Emberwatch (ember_plateau @ veil_stronghold), The Last Bastion (shattered_core @ shard_fortress), Void Harbor (eternal_void @ unraveler_ante), Creation Rest (primordial_nexus @ architect_bridge), Scaleholt (dragon_throne @ dragon_fortress). All use discoverOnVisit so the town stays hidden on the map until the player reaches the attached mid-path node. MapScreen honors discoverOnVisit to skip drawing undiscovered towns. Return-to-Town still falls back to nearest visited town when the new town hasn't been reached yet. Plus town services/taverns inherit existing TOWN_CONFIG — follow-up to wire per-town hire rosters for the 6 new towns.waypoint: true flag on map nodes. thornwood/forest_enter (Forest Warden) is now a waypoint, so MapScreen draws the blue ring around it once visited and treats it as a valid click-teleport target. Waypoint teleport bypasses the Hard-mode walk-only rule since waypoints are the Hard alternative to the town teleport the mode disables. Future acts can add more waypoints by setting the flag.CombatScreen._ratingFromAttr — attributes ≤ 20 scale 1:1, > 20 soften to 0.6× per point. Caps solo-caster late-game one-shots the user flagged as too powerful. Plugs in at _getSkillStat chokepoint so every skill + healing path goes through it. Toggle off via emberveil_rating_scaling=0. (2) Roll animation — DialogScreen flashes a PASS/FAIL banner (green/red, 650ms) on skill-check resolution before the outcome text plays. Not done: a dedicated +N STR Rating affix entry (would add rolled gear that grants raw rating bonus without pumping the underlying attr). Open follow-up, not blocking.src/maps/randomEvents.js::getRandomEvent now seeds from gs.gameSeed + zone id + seen-count. Falls back to Math.random only if game seed is null (pre-new-game init). MapScreen passes gs.gameSeed ?? null at the single call site. Map node layouts remain fixed per zone (deterministic by design).src/game/quests.js — to be added) and leaves the authoring pass as a dedicated next-milestone. Shipping this mid-session without quest art + writing review would create narrative "filler" that feels worse than empty. Not silently shelved — explicit priority callout for its own cycle._openLootChest(node, {tier}) helper accepts 'standard'/'medium'/'high' so boss drops + skill-check branches can route through the same modal. Risk/open: no graphical chest sprite or beam-of-light open animation shipped this cycle — adding art requires SpriteCook credit burn for closed/open chest states that would need matching the existing pixel-art style. Text-only modal is in use and functional.of_discovery affix added to AFFIXES_ACT1 (magicFind stat, 0.10–0.35 range). Party MF summed via CombatScreen._partyMagicFind() and cached on gs._partyMagicFind so loot chests read the same value. Applies to combat + chest drops. Shop stock intentionally unaffected (user spec).body.wl-hide-done and CSS hides done/dropped items AND entire sections/groups where every item is done. (2) "Quick Select" toggle enables per-item checkboxes + click-to-select. (3) "Copy (N)" writes the selected labels + descriptions to clipboard as a markdown bullet list. (4) "Reset Selection" clears everything. Supporting Mechanics horizontal-layout bug fixed by forcing .wl-group { display: block } + .wl-group .wl-item { width: 100% }. State persisted per-browser in localStorage.scripts/audit-sprite-404s.cjs — parses src/game/appearances.js + companions.js + mapData.js ENEMIES/ENEMIES_ACT5 and walks every expected sprite file under public/images/{spritecook,sprites,pixellab}. Reports missing by category. Current audit: 0 misses. Supports --json. (2) scripts/redirect-assets-to-spritecook.cjs — one-shot migration that rewrote 164 entries in public/assets/assets.json from ../images/sprites/ and ../images/portraits/ to ../images/spritecook/, using exact-basename match plus a *_portrait.png rename for bare class portraits. The /assets/#main gallery now shows the approved redesign sprites (necromancer drift resolved). Idempotent — safe to re-run after new sprites land in spritecook. image-review.html was already live-data driven via build-image-review-manifest.cjs from M162.clouds_07.png and clouds_08.png via SpriteCook (12 credits each — much cheaper than estimated). Both are 1024×256 pixel-art tileable red-tinted cloud strips. 08 used 07 as a reference asset for style lock. Wired into TitleScreen.js as a three-layer parallax: 08 back (slowest, 0.35α), 07 mid (0.018 speed, 0.5α), 06 front (drifting cloud-object layer, unchanged). Credits spent: 24. Remaining: ~2596.