| Ch.1 — Architecture Inventory |
"Verification required" (process doc, unresolved) |
N/A — planning doc only |
Open |
Never closed out despite the rest of the roadmap having moved well past it. |
| Ch.2 — Core Hockey Skills |
All skill slices still "Planned" |
scripts/players/*, ability defs live under scripts/gameplay/abilities |
Reconcile |
The actual completed skills (Blast Dash, Heavy Shot, Boomerang Puck) are marked COMPLETE in Chapter 25's doc, not here — Ch.2 itself looks stale. |
| Ch.3 — Chaos Event Engine |
Only "verification scaffolding," no completion markers |
Large event/reaction dispatch pipeline in scripts/gameplay/reactions/* |
Reconciled |
A working reaction/event system exists per the Ch.24 log; Ch.3's own doc never credits it. Audit Audited 2026-09-25 and annotated. The engine (GameEventManager, weighted selection, cooldowns, minimum match time) and three implemented events are now recorded in Ch.3. Its CE-00 verification boxes stay unticked on purpose — that documentation work was never actually done, and ticking it after the fact would be inventing a record. |
| Ch.4 — Arena Hazards |
Global architecture + per-hazard specs (v2.3) |
scripts/hazards/* — 11 files matching the doc's hazard catalog |
In Sync |
Good agreement between spec and implementation. |
| Ch.5 — Buffs & Debuffs |
Planning-stage only |
Full Gameplay Effect/Modifier framework in scripts/gameplay/effects/* |
Code Ahead |
A complete effects framework exists that the doc doesn't credit at all. |
| Ch.6 — Player Personalities |
Planning-stage only |
No dedicated personality files found |
Not Started |
Doc and code agree — genuinely not begun. |
| Ch.7 — Arena Themes |
Only Farm Arena (AT-01) spec'd |
No theme-specific code beyond Farm |
Art >> Code |
21 Art Bible categories / ~13 fully-realized themed worlds exist as concept art; only 1 has any code/roadmap traction. See Art Bible. |
| Ch.8–19 — Match Objectives, Mini-Games, Game Modes, Tournament/League, Boss Events, Progression/Unlocks, Presentation, Audio, Statistics, Replay/Highlights, Multiplayer, Modding |
All "v2.0 working draft" — milestones + catalog + parking lot only |
No corresponding script folders |
Not Started |
Confirmed not started; doc and code agree across all twelve chapters. Audit Audited 2026-09-25. Was the worst case on the board: every milestone unticked while a real objective system existed. Six milestones re-marked against the code, five confirmed still open (team/individual ownership, AI priority, replay evidence, match HUD, completion ceremony). |
| Ch.20 — Accessibility & Ch.21 — Balance/Tuning |
Working-draft planning stage |
None |
Not Started |
— |
| Ch.22/23 — Future Plans & Fun Dev Packages |
Pure ideation (Time Travel, Gravity, Pinball worlds, etc.) |
N/A by design |
Parked |
Correctly labeled as long-term/parked, not a gap. |
| Ch.24 — Gameplay Foundation Framework |
GR-00 through GR-09A all marked Complete |
scripts/gameplay/* — 122 files (attributes, effects, reactions, damage, explosions, placeables) |
In Sync |
The most mature system in the game; doc tracks it accurately. Most-maintained roadmap doc. Audit Audited 2026-09-25. Attribute taxonomy update added (54 attributes, 8 categories). Partial-class filenames corrected from dot to underscore form; the candidate-partials list annotated — two of four built, two still open and left listed as open. |
| Ch.25a — Ability/Skill Progression |
Complete through HS-15F (verified to a specific commit) |
scripts/gameplay/abilities/* (Blast Dash, Exploding/Ghost/Magnetized/Boomerang Puck + loadout system) |
In Sync |
Matches code well. |
| Ch.25b — Character Progression Framework |
Still planning-stage |
No leveling/persistence code found |
Not Started |
Docs formerly shared "Chapter 25" with 25a; now explicitly renamed 25a/25b in docs/roadmap. Doc and code agree — this broader progression/persistence track hasn't started. A cited real-world research pass (shot speed/skating/passing/zone-entry data across NHL/junior leagues) plus a proposed ~25-attribute RPG design now exists as reference input for GA-01/GA-02 — see Research — but it's design input only, not implemented code. |
| Ch.26 — Scenario / Formation Framework |
Doc (v1.6) reconciles through SC-06; frames SC-07A–F as "next" |
scripts/scenario/* (54 files) + scripts/gameplay/formations/* — git shows SC-07A through SC-07F.6 already committed |
Reconciled |
The single largest doc/code gap found — and it's the system the current branch (scenarioeditor) is named after.89 wordsThe single largest doc/code gap found — and it's the system the current branch (scenarioeditor) is named after. Also captured (2026-09-22, not started): a long-term idea from the user to extend formation placement from static (one position per player) to "playbooks" — ordered multi-waypoint movement assignments for multiple players at once, consumed by Chapter 27's VT-08 skate-to-waypoint mechanism so the human player can learn the practiced formation movement. Real complexity, honestly assessed in the doc: needs per-player waypoint sequencing, multi-player timing coordination, and dynamic (not just place-once) formation application. Audit Audited 2026-09-25. Its own historical-preservation rule is the right convention and was followed. One filename spelling annotated; eight FormationDesignerWindow partials confirmed. |
| Ch.27 — Vision Training Mode |
VT-01–06 and VT-08 Phase 1 all user-confirmed complete; the full wayfinding feedback system (direction arrow, pulsing markers, beacon beams/lights, screen-space edge-of-viewport fallback) has had six real testing rounds of fixes — the beacon beams/lights and the edge indicator (after its rendering bug was fixed) are both user-confirmed visible now, though the arrow/marker-pulse/Elite-reveal pieces built earlier haven't each been individually re-confirmed in this same pass; testing the edge indicator then surfaced a real HUD collision (the Ability window hiding it) that opened a new Chapter 28 — UI/UX & HUD Framework. New, untested: verbal coach feedback is now directional, detects "stopped moving" in open ice, and varies its phrasing instead of repeating the same line. VT-09, a Ch.26 "playbook" extension, and a "FASTER!" too-slow-movement signal remain captured long-term ideas, not started; VT-07 not started. See Chapter 27's Milestone Status table and Development Log for the full at-a-glance breakdown. |
Direction arrow (constant Beginner/Intermediate, periodic-escalating Advanced, last-resort-only Elite) + pulsing waypoint markers with beacon beams/point lights + screen-space edge-of-viewport compass icon for off-screen targets |
VT-08 + VT-09 Slice Confirmed |
User chose VT-08 before VT-07 ("easier and more straightforward"), then resolved both design questions the original proposal deliberately left open…4,090 wordsUser chose VT-08 before VT-07 ("easier and more straightforward"), then resolved both design questions the original proposal deliberately left open by asking for all three options of each built and switchable live for direct comparison rather than one picked blind — "these seem like good options depending on skill level of the player... we can easily remove them later, or allow the user to customize which look they prefer." Round 1 testing found the core drill logic worked correctly on the first try (confirmed from the log), but surfaced two real UI-only bugs: the text panel rendered behind the scoreboard (an undetected CanvasLayer collision with two other layers already in the scene) and cycling settings gave no visible confirmation of what was active (the mode label only showed inside a popup, which only appears once off-track). Both fixed; round 2 confirmed clean — "Much easier to tell which mode we were in this time. I even heard the tone with the audio cue," verified directly from the log (Audio tone played for stage CoachBark fired correctly every time). Also found and recorded a stale-documentation gap along the way: the pinned session memory described a VisionTrainingHintPanel as still existing; git history showed it was built, then fully reverted the same day after user pushback about over-coupling Training-mode UI into the Formation/Scenario Editor — exactly why VT-08's new UI was built standalone rather than reused. One tuning follow-up caught right after, correctly scoped by the user as balance, not mechanics ("it starts prompting you almost immediately... that's a balance issue, not a mechanics issue"): WrongLocationRadius equalled ArrivalRadius, so reaching a waypoint could immediately flag the player as "wrong" relative to the new target before they'd even turned to leave. Fixed with a 2-second post-advance grace window. Follow-up (same day): user asked whether the escalation delay could vary by difficulty ("for easy mode... a bit slower... for NHL, it would expect a much faster response") — built a per-tier escalation profile (GetEscalationProfile) so thresholds and the grace window now scale with DrillAssistTier, unverified in-editor so far. Also confirmed but not built: off-track detection is proximity-only today (no movement/progress tracking, so a "FASTER" prompt would need a new signal), and the coach-feedback presenters are already architecturally reusable for passing/shooting feedback with no changes — both captured in the roadmap as real future extensions. Next round: "the 2nd and 3rd are firing way too fast" — ruled out repeat-offense-count compounding by checking the log (never exceeded 1 all session), confirmed the dwell timing matched configuration exactly (not a bug), and widened the Nudge→Flash/Flash→Bark gaps to roughly double. Separately, cycling (tier/reveal/style) silently stopped registering partway through the session while toggle kept working — traced the "Objective running" report to an unrelated plain-number-key press (confirmed via log, not a collision with these shortcuts), found and fixed a real defensive bug along the way (two other input handlers matched raw keycodes with no modifier check, which could misfire on Ctrl+ combos) — mitigated by moving the three cycle actions off the F-row to Ctrl+[/]/\\ entirely, keeping Ctrl+F8 since it was reliable throughout. No crash evidence found for a separately-reported window close. Update, found later the same day: fixing an unrelated bug in controls.html's scraper (it silently dropped Ctrl/Shift/Alt/Meta from every displayed binding — fixed) led to checking the full input map, which confirmed the actual F5/F6/F7 cause: pre-existing, unmodified debug_swap_primary_utility/debug_swap_secondary_tertiary/debug_increase_blast_dash_level actions, polled via Input.IsActionJustPressed (not raw keycodes, so a source grep never would have found them). Godot's default non-exact action matching means Ctrl+F6 also satisfied the plain F6 action — confirmed directly from the log, where every successful reveal-mode cycle is immediately followed by "PlayerMain swapped Secondary and Tertiary." The bracket-key rebind already sidesteps this. Final retest, same day: confirmed directly from the log — zero ability-swap side effects across the whole session, the bracket-key rebind is fully reliable. User's verdict: "It definitely feels better and more patient at the lower levels... I think it's a success but just needs some balance and 'feel' adjustment... Functionality appears to work." One new gap surfaced and captured for later, not fixed yet: Advanced tier hides every marker but the current target, so a lost player gets no feedback when they're far from everything rather than standing at an identifiable wrong spot — "Maybe there needs to be a feedback scenario if the player is completely lost." VT-08 Phase 1 marked complete on the user's own basis; remaining items are tuning, not blockers. Follow-up, same day: talked through the actual "lost" mechanic across several turns (pointy arrow vs. periodic path line? persistent vs. weaning?) before landing on a design tied to the existing four-tier dial: arrow constant at Beginner/Intermediate (Intermediate currently has no path line at all, so this is real added value there), periodic and escalating at Advanced (reusing the same per-tier timing already driving coach feedback, not a second tuned system), absent at Elite. User also floated a hint-usage scoring idea ("assign points based on how long the helper arrows are activated"), self-identified as depending on VT-09 existing first — recorded as a VT-09 refinement. Built, not yet tested in the running game. First real test, same day: "I'm not seeing anything that tells me to turn a specific direction... nothing else that I can tell." Checked the log before touching anything — the visibility trigger was firing correctly, ruling out a logic bug in one step. Root cause: the arrow was a flat triangle lying in the ground plane, which presents almost edge-on to a normal 3rd-person camera and is effectively invisible. Rebuilt as a real 3D cone (genuine silhouette from any angle), and added the user's own suggested touch — urgency colors for Advanced's blink stages (yellow → orange → red), matching the same family CoachFeedbackPopup's avatar already escalates through. Still not re-verified visually. Second real test, same day: "Advanced only shows the red arrow" (real bug — CycleTier() never reset the elapsed timer, so switching tiers mid-drill inherited stale elapsed time already past Advanced's bark threshold, confirmed from the log and fixed) and "hard to tell... you only see a circle" (a cone viewed near its own axis genuinely degenerates to an ambiguous silhouette). Added a shaft behind the cone (real arrow shape) and repositioned to mid-body height offset toward the target each frame, so on-screen position itself carries direction. User's own fallback plan, captured not built: a screen-space edge-of-viewport indicator if the in-world shape still doesn't read well. Still not re-verified. Fair criticism, same day: "why were there not tests for these items written? change of state and change of mode are easily testable. Change of mode mishandles is sloppy on your part." Correct — GetEscalationProfile and the arrow's elapsed-time-to-stage bucketing were pure math with zero Godot dependency but lived as private methods inside the Node-derived controller, unlike every other piece of pure logic in this chapter. Extracted both into standalone, unit-tested classes (DrillTierTimingProfile, ArrowEscalationCalculator) with 15 new tests (93/93 total), including one written specifically to demonstrate the bug's exact mechanism (fresh vs. stale elapsed time, same thresholds, different outcome). Third real test, same day: "still above the player... not at midheight, nor is it offset." Checked the player's own scene rather than guess again — the visible model is a placeholder capsule with no separate head/torso, and the player's origin sits at the capsule's exact vertical center, not the feet. The previous height offset (1.0, written as if measuring up from the feet) put the arrow above the capsule's top again. First fix attempt just shrank the same offset — the user correctly rejected that as the wrong kind of fix: "it shouldn't be based on an origin definition... when the capsules are replaced with real meshes, all of this positioning stuff will break. We need a single absolute marker." Fixed properly instead: a new MeasurePlayerFloorOffset() reads the player's actual collision shape at runtime and computes the real floor position from it, so the arrow's height is now a genuine "height above the ground" measurement rather than a guess from wherever the origin happens to sit — this keeps working correctly even once the placeholder capsule is replaced with a real character mesh, as long as it still carries a reasonably-sized collision shape. Also fixed a real wording bug caught in the same message: the current-target marker never actually pulses/flashes outside one specific tier+mode combination, so "head to the flashing marker" was simply wrong most of the time — changed to "head to the yellow marker," which is always accurate. Clarified, not a bug: the screen-space edge-of-viewport indicator the user asked about was only ever captured as a fallback plan if the in-world fix kept failing, never built. Called out for the lazy fix, same day: "that's lazy. you took away the one attention grabbing feature that a new player would definitely use." Correct — rewording the message instead of building the real pulse was the easy way out. User's follow-up: "make it flash. we already have flashing graphics here for objects and powerups. i know it can be done behind the scenes." Found and reused the existing convention (ElectrifiedFloorBehavior.UpdateWarningVisual()'s sine-Lerp pulse formula) for the marker's alpha instead of inventing a new one, extracted the pulse math into a tested pure function proactively this time (PulseAlphaCalculator, 4 new tests, 97/97 total), and made the coach message tier-aware so it no longer references a marker at Elite, where none are ever shown. Fourth real test, same week: "C'mon over here on elite level gives no indicator where 'here' is" plus "a marker on the other end of the rink is hard to see" plus a direct architecture question ("are you recreating the object every frame?"). Gave Elite a last-resort arrow reveal gated on the same per-tier urgency thresholds that drive CoachBark; added a vertical beacon beam and a real OmniLight3D per waypoint marker (reusing AbilityPickup's existing glow convention) for long-distance visibility; found and fixed a genuine per-frame ImmediateMesh reallocation bug in the Beginner-tier path guide line. 97/97 tests passing. Fifth round, same week: asked directly rather than deferring a fourth time whether to build the screen-space edge-of-viewport fallback the user had now raised three times — answer: build it now. New pure ViewportEdgeIndicatorCalculator projects the current waypoint to screen space and clamps a compass icon to the viewport edge (handling the behind-camera mirroring case explicitly); new ViewportEdgeIndicator draws it, parented inside the human player's own gameplay SubViewport (not the main scene's CanvasLayer stack) so its pixel coordinates stay correct regardless of container scaling. Reuses the exact same tier/escalation gating as the in-world arrow via a newly extracted ComputeHintState(), only adding "is the target off-screen" as its own condition, so the two presentations can't drift out of sync. Confirmed for the user mid-build: purely additive, the existing in-world arrow was untouched. 106/106 tests passing (9 new). Sixth round, next day — explicit night hand-off: "add the feature that provides coach feedback when you aren't standing on the way point. Directional feed back, not moving, etc... It needs to be meaningful....and helpful... Maybe even vary the verbal feedback to keep it interesting?" Added directional phrasing (CoachDirectionalPhraseBuilder, body-relative — "behind you," "to your left," etc. — gated at Elite the same way the direction arrow already is), a "you've stopped moving" signal (a second CoachFeedbackEscalation reusing the same per-tier timing as the existing wrong-waypoint one, sampled every 0.5s rather than every physics frame to avoid jitter, mutually exclusive with wrong-waypoint feedback by construction — explicitly not the still-deferred "FASTER!" too-slow-movement idea), and message variety (CoachMessageVariationPicker, small phrase pools per stage/reason, never repeating back-to-back). All three pure and unit-tested up front. 126/126 tests passing (20 new). Seventh round, same day — "FASTER!" built: the too-slow-movement signal explicitly deferred in the sixth round was picked up immediately after, per "whats next...I'll test after next implementation." New ClosingSpeedCalculator (pure) plus a per-tier minimum closing speed, wired as a third CoachFeedbackEscalation instance with precedence WrongWaypoint > Stationary > TooSlow > None. Caught and fixed before shipping: the too-slow flag was first a local variable reset every physics frame instead of a persistent field, matching an existing correct pattern. 137/137 tests passing. Eighth round, same day — first real test found two real bugs: "hints on even the easy mode are firing way too fast... as soon as someone moves, it resets to the first phase level of instruction... an instruction of move forward and to the left, when it was located almost to my left exactly." Root-caused to CoachFeedbackEscalation fully resetting dwell to zero (not just pausing) the instant its tracked condition went false for even one frame — which, combined with the repeat-offense mechanic incrementing on every one of those false→true flickers, compounded into ever-shrinking thresholds even from ordinary movement noise. Fixed: the condition going false now pauses (not resets) dwell/stage, with a new explicit Reset() called only at genuine waypoint arrival; Stationary/TooSlow now use flat, non-shrinking thresholds (repeat-offense scaling stays only on WrongWaypoint, where repeatedly visiting the same wrong spot is a real repeated mistake). Also narrowed CoachDirectionalPhraseBuilder's diagonal-phrase angle band (30°-75° → 20°-55°) per the "almost exactly to my left" report. 138/138 tests passing. Ninth round, same day — a second real bug from the same test: "rotating in place and the messages escalate as expected, until the last phase. After the last phase is reached, continued rotation does not trigger a continued / updated message" — confirmed to affect both a wrong-waypoint ("white reticle") stand and an open-ice stationary stand. Root cause: UpdateFeedbackPresentation() gated its entire rebuild (including the spoken direction) behind a stage-changed check, which by construction can never fire again once CoachBark (the escalation ceiling) is reached. Fixed by tracking the last-displayed reason and direction separately, so a direction-only refresh re-renders the same coaching line with just its direction suffix updated, without re-rolling the message pool or replaying the audio tone. Extracted a new standalone, unit-tested CoachDirectionSuffixBuilder class along the way. 149/149 tests passing. Tenth round, same day — the fix immediately caught its own gap: "okay so the rotation updates at the current bark level. But contiunued rotating rapidly fires the messages. I think you should still be using the same duration before phases and barks. even if the message changes." The direction-refresh fix above had no pacing at all, so fast rotation crossing several directional-phrase buckets per second spammed the coach line. Fixed with a cooldown reusing the current tier's own nudge threshold (already driving phase-escalation pacing and the arrow's blink) as the minimum gap between direction-only refreshes — a genuine stage/reason change still always displays immediately. 149/149 tests passing. Eleventh round, same day — new feature: "maybe the coach should give 'atta boy' or something positive for reinforcement when a marker is moved or the situation is improving... fun interjections in the bark system." Added reason-aware praise message pools, triggered when an active escalation resolves back to silence and holds for a short debounce (filtering the same flicker noise that caused the earlier reset bug), auto-hiding after 2 seconds with distinct green styling and an upbeat tone. 149/149 tests passing. User's verdict on all of the above, same day: "cheesy but functional. mark it complete and we'll clean up the verbage and text pools for the coach during the polish phase." Also captured for that later polish pass, explicitly not now: occasional humorous encouragement/insults in the Chaos Hockey voice ("grandmother skates faster than you"), floated as a future "message dictionary." VT-08 marked complete in Chapter 27's Milestone Status on this basis. Next, same day — VT-09's first real slice, per the user's "do all of these" direction: drills are now a repeating, hint-usage-scored curriculum of one-lap DrillObjectives, reusing Ch.8's ObjectiveBase data type directly rather than routing through ChallengeManager's match-flow state machine (which would have frozen the player mid-drill during its Intro/Countdown phases — a real design call made before writing any code). New pure, unit-tested HintUsageScoreCalculator (weights hint-time by escalation urgency) and DrillObjective; the existing status readout now shows a live lap/score line. 159/159 tests passing (10 new). Honestly scoped as a first slice, not full VT-09 — no named milestone variety, no persistence, no dedicated UI yet. Third and final item, same day — coach-feedback presenters reused for live shot/pass critique: new BasicPlayer.PassExecuted/ShotExecuted events fire the already-computed evaluation data after any completed pass/shot; new pure ActionQualityClassifier and PassEvaluationQualityCalculator classify it Poor/Average/Great (only Poor/Great get a comment); scoped to only fire during an active drill with no escalation feedback already showing, reusing a new shared DisplayTransientCoachMessage() helper extracted from the praise system. 166/166 tests passing (7 new). All three of the user's "do all of these" items are now built. Update, same day — a real log-spam regression found from code, not a log file: asked to test, the user instead asked "Is it still filling the log with 1,000s of pass evaluation notices?" VisionTrainingScene.tscn set PrintPassEvaluationDebug = true on all 7 players, overriding the safe class default; traced the call chain to confirm EvaluatePassAnalysis() runs unconditionally every physics tick for whichever AI player holds the puck. Fixed by flipping all 7 back to false, leaving the separate "a pass/shot actually happened" logging untouched. None of the day's coach-feedback work is re-verified in the running game yet. Update, same day — the first real in-game test, using the correct log this time: traced everything from logs/godot.log (this project's own res://-relative log path, not the default location). Confirmed the Active Effects panel change is NOT a regression (scene baseline is hidden, so nothing to visibly toggle there). Found and fixed a real bug: a Great-rated shot critique genuinely fired right after a goal, but was overwritten within a fraction of a second by a new escalation triggered by the post-goal stationary reset — never actually readable. Fixed by protecting a transient message for its full display duration before any new escalation can overwrite it, plus added logging for the previously-silent "suppressed"/"Average" cases. Added a corner category tag ("MOVEMENT"/"PASS"/"SHOT") to both coach panels per the user's ask. Disabled AI stealing for this scene only (StealRange = 0.0 on all 6 AI players, not the global rule). 166/166 tests passing. Update, same day — second real test found the actual root cause: after relaunching, the user reported the identical symptom ("never saw any messages for passing or shooting"). The new diagnostics paid off immediately: 5 of 7 real human pass/shot attempts were suppressed by an active Stationary/TooSlow escalation (not WrongWaypoint), the other 2 landed in the silent "Average" band — zero ever reached Poor/Great. Root cause: stopping to aim a shot/pass is completely normal hockey behavior that trips the Stationary detector almost every time, so "defer to any active escalation" accidentally suppressed the critique on the overwhelming majority of real attempts. Fixed by only deferring to WrongWaypoint specifically, and narrowed the classification thresholds toward the real observed score cluster. 166/166 tests passing. Update, same day — user's direct follow-up: "should we always allow feedback for Scoring and Passing...even if a Movement message is always shown?" Answer: yes — dropped the precedence check entirely, since a shot/pass critique is tied to a specific, time-sensitive event while a Movement message describes an ongoing state still true a couple seconds later regardless of a brief display pause. Safe because of the transient-message protection fix. 166/166 tests passing. Update, same day — new feature: user reported "passing notifications only appear if the AI is not frozen" and reasoned the notification should be about passer quality, not catch success, proposing a separate receiver-side signal. Log check found no evidence of a freeze-gating bug (critique already fires purely from release-time data, confirmed regardless of freeze/catch outcome) — but built the proposed "Good catch!" feature anyway, since it's genuinely good: fires when the human successfully receives a teammate's pass within a 1.5s window, reusing the same transient-message machinery with a new "CATCH" tag. 166/166 tests passing. Update, same day — a real formula bug found: "I still wasn't seeing many pass notifications on screen." Re-examined the raw data: real pass qualities still mostly landed in the silent "Average" band. Root cause was the formula, not the thresholds — ForwardDot is bounded to [cos(halfConeDegrees), 1.0] for any pass that survives the cone check (~0.82 floor at the default 70-degree cone), so it contributed a near-constant high value to every score regardless of real quality. Dropped it from the composite entirely (now a 3-way average of Distance/Goal/Facing scores) and added component-level debug logging for future tuning. 166/166 tests passing. Update, same day — testability, per the user's own ask ("I'm pretty terrible at this game and most of my attempts aren't showing up anywhere in the system"): new LiveStatsPanel, a persistent color-coded numeric readout (Movement/Pass/Shot, "Category: NN% (Rating)", flashes briefly on change, never fades away) separate from the transient coach messages — Movement's number reuses DrillObjective's existing 0-100 hint-usage score rather than a second calculation. Plus two testing aids: an AlwaysCommentOnActions toggle (Ctrl+F11) that bypasses the Average-silence gate, and a force-trigger cycle (Ctrl+F12) that steps through one example of every message type with synthetic data. Live graphing of these numbers captured as a future idea, not built. 166/166 tests passing. Update, same day — two reworks from direct feedback on that readout: the rating scale went from three bands to five (VeryPoor/Poor/Average/Good/Great, ramping red → orange → white → light green → green, with a test sweeping the full range to guarantee ratings never go backwards), and lap scoring was rebuilt from bounded decay into unbounded additive — a lap now starts at zero and earns points per waypoint (base + a ratio-based speed bonus with no practical ceiling + a clean-leg bonus), replacing the old start-at-100-and-decay model, per the user's own "the upper end is unbounded and can always be improved by player skills / speed boosts." Movement now reads in raw points rated per waypoint reached, with best-lap tracking to beat. 179/179 tests passing. Update, same day — cross-lap tracking: "A single lap score is good. But you want a continuous running tally for multiple laps... Maybe a 'splits listing' like runners use?" New DrillSplitLog (plain C#, unit tested) owns session total, lap count, best lap and a rolling split window — with tests pinning down that the totals cover every lap even after older splits scroll out of the visible window. The panel gained a runner-style splits section (L3 284 pts 11.8s ★) and the status readout now shows live lap score/time plus session total and best. A real multi-lap objective ("race 3 laps") captured as a VT-09 curriculum decision, not built. 190/190 tests passing. Confirmed working, same day: "okie everything is confirmed." Splits, running tally, best-lap tracking, additive scoring and five-band ratings all verified in a real session — VT-08 and VT-09's first slice are now user-confirmed, not just tested. One evidence-backed follow-up captured: the five rating bands fire correctly (Poor ×5, Average ×1, Good ×4, Great ×1 in the confirmed session) but collapse back to three perceptually — adjacent greens/reds are near-identical at HUD text size and both positive bands share one message pool, so a Good and a Great shot can speak the identical line. Colour separation + per-band message pools captured as their own row, with a note to check colour-blind-safe ramps (Ch.20). Also noted: lap scoring discriminates correctly but the spread is compressed (~30% time difference → ~10% score difference) because the flat base bonus dominates. See Chapter 27's Development Log for the full detail — this row is getting long; the roadmap doc is the source of truth for the blow-by-blow. Earlier round, folded in here 2026-09-24 (it had been sitting as a second, duplicate Ch.27 row on this table): the VT-05 retest instrumentation fix, plus an F9/F10 rebind to Ctrl+F9/Ctrl+F10 in project.godot. The heat map genuinely rebuilt real samples (112, up from 0) and resolved positions/goals correctly — its own diagnostic assertion was the flaw, comparing ShotLaneScore near a placeholder column against open ice and expecting the column to score lower, when tracing both raycasts by hand showed neither passes anywhere near a column. Both were instead blocked by PlayerGoalieRed, standing almost exactly on the goal line it defends, as goalies should — an opposing player, so LaneEvaluator scores it a hard 0.0 regardless of shooter position. Replaced with a deterministic check. The user caught a second real bug from first principles in the same round — "isn't F9 a reserved Godot keybinding?" — and they were right: F9/F10 are Godot's built-in Toggle Breakpoint/Step Over editor shortcuts, which explained both a ~5s F9 lag and an earlier VT-06 test needing several F10 presses to recover. Audit Audited 2026-09-25. Milestone table found accurate — one of the better-maintained chapters. Three additive notes: a renamed DrillObjective method, a shipped multi-lap objective, and the drill library's move to Ch.29. |
| Ch.28 — UI/UX & HUD Framework |
Update (2026-09-24, night): UI-01 is now the active piece of work, moved up at the user's direct request after a playtest — "A lot of the viewable screen is obstructed by the drill instructions... I really think we need to redesign the window to make it as wide as possible. And cut our space above and below for all these flyouts, windows and messaging system... does this move the UI-01 harnass up?" A layout harness is built (HudRegion/HudRect/HudLayout/HudRegionRegistry/HudPlacement + HudLayer), which found a real collision on its first run: BottomLeft overlapped TopLeft at 1280×720, the size the game actually runs at. The harness makes conflicts detectable; it does not yet make the HUD good, and the visual redesign is what remains. Fullscreen switched on as the cheap half of the visibility win, and logged under UI-09 as something that should become a preference rather than a forced setting. Earlier the same day: three new rows. UI-07 (speed meter, always on) built; UI-08 (throttled HUD refresh as a standing convention) partly done — RefreshGate exists and is used by the speed meter, but the older per-frame readouts (LiveStatsPanel, HudController) have not been moved onto it; UI-09 (a game preferences screen, with the speed meter as its first toggle) parked at the user's request, with the honest blocker recorded — there is no preferences screen of any kind yet. UI-01's real layout redesign is still the chapter's actual open work. Earlier: design work not started — but several real, separate bugs found and fixed along the way, all user-confirmed: FormationDesignerWindow silently forcing HUD panels visible, a genuinely duplicated AI-vis toggle removed, and the gameplay viewport now actually resizes with the window at a genuine 1:1 pixel ratio — root cause turned out to be a project setting (window/stretch/mode was canvas_items, now disabled), not per-node code, after eight rounds of investigation. User: "that solved it." Update (2026-09-23): the Active Effects panel now collapses while the tactical view ('0' key) is in its expanded/maximized state, freeing screen space as the user requested back on 2026-09-22 — reuses the same "restore to the scene's own captured baseline, not a hardcoded default" pattern FormationDesignerWindow's earlier fix established. Not yet seen running. |
project.godot (the real fix), scenes/vision_training/VisionTrainingScene.tscn (scoped hides/removals), scripts/scenario/editor/FormationDesignerWindow.cs + FormationDesignerWindow_FacingEditor.cs, scripts/vision_training/HumanSkatingDrillController.cs, scripts/players/PlayerMain.cs (viewport resize), scripts/cameras/TacticalViewportController.cs + TacticalViewportController_Layout.cs (Active Effects collapse) |
Fixes Confirmed |
User: "I guess UI design probably needs its own roadmap chapter." Opened after a screenshot revealed the Ability window (active_effects_panel.tscn)…714 wordsUser: "I guess UI design probably needs its own roadmap chapter." Opened after a screenshot revealed the Ability window (active_effects_panel.tscn) hiding VT-08's new edge-of-viewport indicator was one of four collisions, not one: VT-08's own status readout was found overlapping game_hud_framework.tscn's pre-existing top-left player panel (Claude's own regression, fixed immediately — moved below it), the HUD's "COMBOS/DEAD" text overlaps the scoreboard (pre-existing, captured not fixed), and a fourth element the user wasn't sure about turned out, on inspection, to be a legitimate working flyout (the AI debugger's collapse/expand panel) rather than a bug. Clarified an important distinction along the way: the Ability window bundles a real "Active Effects" status display (buffs like Speed Boost/Ghost Puck) with an "ABILITY DEBUG"/"LOADOUT DEBUG" section that's a dev stand-in for a future real skill-equip screen — "currently its a debug tester but we'll need something like that for the player to equip skills when we get to it... add it to a roadmap chapter with a relevant reminder entry" — tracked as UI-02, cross-referenced from Chapter 25a's Parking Lot since it consumes that chapter's already-complete equip/unequip backend. Immediate scoped fix (this pass only): the Ability window hidden by default in VisionTrainingScene.tscn alone (other scenes untouched), VT-08's readout repositioned. Real layout redesign deliberately deferred — "I don't know what a good design looks like... feel free to redesign" — this chapter is where that design work happens next, not a build queue yet. Update, same day: "Nope ActiveEffects window is still visible. As is the stuff behind it." The scene-level hide DID apply, but FormationDesignerWindow (also instanced in this scene) ran ApplyExpandedState() unconditionally from its own _Ready(), which hardcoded these panels' visibility back to a fixed true whenever the window was collapsed (its default state) — a real, separate bug, not a config mistake. Fixed by capturing each panel's actual configured visibility once in _Ready() and restoring to that captured baseline instead of a hardcoded value, in both ApplyExpandedState() and its _ExitTree() cleanup path. Update, same day: a redundant standalone "AI VIS: ON" toggle found sitting behind the Ability window, traced and confirmed fully duplicated by an existing "World" checkbox already in the scene's AI-debugger flyout — removed; its legend panel (not duplicated anywhere) kept, shrunk to 75%, and tucked into the true screen corner. Update, same day — a separate, real bug: "if the user resizes the game window... currently it stays constant and just grows the black border around it." The gameplay SubViewport rendered at a hardcoded 1280×720 always, relying on SubViewportContainer.stretch to scale that fixed image to fit. Fixed project-wide in PlayerMain.cs: the container and its child SubViewport's render size are now actively resynced to the real window size on every resize, making the stretch scale factor a mathematical no-op — genuine 1:1 pixels, confirmed with no stretching in either direction on both the 2D blit and the 3D camera side (which already used Godot's non-distorting default aspect mode). A separate floated hypothesis ("meters instead of feet... maybe thats why everything feels crowded") was corrected rather than chased — world-unit choice doesn't affect on-screen crowding; that's a camera-framing question. Update, same day: "looks to be working correct[ly for the earlier fixes], [but] resizing the window doesnt resize the viewport... [and maximizing] dont expand or increase in size at all" — the first viewport fix had no effect. Real root cause, found via the user's own sharp follow-up questions ("are the viewport dimensions hardcoded... or overridden with constant values by the godot engine or inspector?"): SubViewportContainer's parent is PlayerMain, a Node3D, not a Control — Godot's anchor system needs a parent-to-child Control resize notification to recompute a child's rect, which never fires with a non-Control parent, so a plain .Size = assignment was likely just getting silently overwritten. Fixed with TopLevel = true, plus real diagnostic logging so the next test settles it either way. Update, same day — a real regression: user noticed "about 13,000 warnings in the Godot debugger." One (a Puck.tscn case-mismatch, pre-existing, unrelated) fixed outright; the dominant source was a genuine regression from removing the redundant AI-vis button earlier — clearing its NodePath exposed a per-frame (_Process()) warning call that fired ~60x/second for an intentionally-unassigned, permanent config choice. Fixed to return silently for that case. See Chapter 28's own doc for the full investigation. Audit Audited 2026-09-25. Header said “Not started” above nine milestones, three complete. Corrected, with the original header kept for the record. UI-02 and UI-06 updated for the loadout screen and the aiming reticles. |
| Ch.29 — Vision Training Drill Library |
All 13 catalogued drills marked built; DL-F3 (general possession rework) still open and explicitly NOT a prerequisite; deking deferred |
scripts/vision_training/ — catalog/ (13 drills, 5 difficulty tiers), drills/ (IDrillLogic + DrillHost + per-drill evaluators), scoring/DrillScoreBuilder, ui/ (selection, results, records screens) |
In Sync |
Update (2026-09-24, night) — the acceleration change is user-confirmed good: "The speed meter actually looks and feels a lot better.342 wordsUpdate (2026-09-24, night) — the acceleration change is user-confirmed good: "The speed meter actually looks and feels a lot better. Skating definitely feels different, and managing turning into corners without losing speed or slamming the wall or missing the marker is definitely more challenging...even at Amateur difficulty." That is precisely what momentum was meant to buy — the circuit is a skating problem now rather than a steering problem. Same session: average speed added to the results, the countdown de-blinded (it had been a near-opaque slab of 130px text over the ice), a READY gate added before the countdown so the skater is placed and the objectives read before the count starts, and any whole lap count from 1-50 now selectable. Still only 2 of 13 drills have been played. Earlier: written overnight to a brief and reconciled continuously since, so the doc has not been allowed to drift. The most useful finding was structural: DL-F3, a possession rework touching 23 files, had four Phase 2 drills and two Phase 3 drills gated behind it — and none of them actually needed the general mechanic, only the mechanic for themselves. A drill is allowed to own its own puck. An hour of thinking instead of a week of rewriting possession; the general mechanics remain genuinely unbuilt and each evaluator says so rather than letting someone assume otherwise. Playtest-driven changes since: drill #1 is now lap-based rather than racing a countdown (the earlier timed-vs-time-trial split was the wrong cut — both circuit drills end on laps, and what separates them is whether the score grades route quality or elapsed time); a 3-2-1 countdown now precedes every drill; and a live speed meter was added, which promptly found a real gameplay gap — see Notable Findings. A lap is 19.4 seconds, measured from a real logged run rather than guessed. Honest caveat: only drills #1 and #22 have been played. The other eleven are "built and unit-tested," which is a weaker claim, and the catalogue flags that per-drill via isUserConfirmed rather than implying otherwise. |
| Ch.30 — Player Records, Progress & Analytics |
PR-01–PR-12 built (profiles, persistence, leaderboards, personal bests, Hall of Legends, 17 feats, results screen, progress graphs, data management, sample data); PR-10 filtering partial; PR-13/14 not started |
scripts/vision_training/records/ — DrillHistory, LeaderboardCalculator, FeatCatalog, DrillHistorySerializer/Store, PersonalProgress, RunQuery; JSON under user:// |
In Sync |
Update (2026-09-24, night): three new rows, all user-driven.353 wordsUpdate (2026-09-24, night): three new rows, all user-driven. PR-13 grew from "achievements" into a full badge/honour/reward design — "I really want to include a badge and honor system so the players feel like they are winning something. These drills could also award points that allow the user to buy skills, gear, etc to customize or improve their character...or skating." The design is written up on the Vision Trainer page and is explicitly design-only. PR-15 records a real defect in the current records model: they key on (drill, difficulty) only, so on a lap-count-configurable drill more laps means more points and the longest run always wins regardless of skill. PR-16 captures the deferred analytics the user asked for — running average, last-5, and trendlines over a user-selected window. An 18th feat, average skating speed, was added and is time-weighted so a frame stutter cannot skew it. Earlier: split out of Ch.16 (Statistics) deliberately: Ch.16 is about match statistics, this is about a player across runs, and the two would have fought over one chapter. Shaped more by the user's framing than by any technical consideration — "I'm an engineer....data is good and interesting and fun if delivered correctly. And I love 'progress graphs'... The more filters and options we can give for displaying data, the better." — which is why the run record is captured richly from the very first run and every view is a derivation over it. Almost entirely pure C# with no Godot dependency outside the file IO and the screens, which is what let ~90 unit tests cover the rules. One real defect found and fixed by playtest: lap drills were sampling skating speed every frame into a dictionary the recorder never copied onto the run, so the top-speed feat had never seen a sample from the one family of drills that is entirely about skating. Per-lap splits did not exist at all. Both fixed, with tests specifically guarding the gap between "measured" and "stored". Not yet exercised at scale: the history cap and trimming behaviour have only ever been tested against seeded sample data, never a real long-running history. Audit Audited 2026-09-25. The “lap circuit does not report” claim was traced in code and corrected — DrillHost writes a full DrillRunRecord with PR-15 per-lap normalisation. The narrower surviving fact (the lap circuit is the only non-IDrillLogic drill, wrapped deliberately) is recorded in its place. |
| Roadmap Page Health (site infrastructure, not a game chapter) |
N/A — this row tracks the dev site's roadmap.html viewer itself, not a gameplay chapter |
html/roadmap.html: sidebar-grouping regex + a Markdown pre-processing step + explicit heading CSS |
Fixed |
User reported Ch.27 missing from the roadmap page, then (once regenerated) showing after Ch.23 instead of near its actual dependencies (24-26), wit…123 wordsUser reported Ch.27 missing from the roadmap page, then (once regenerated) showing after Ch.23 instead of near its actual dependencies (24-26), with "crazy big fonts, inconsistent typography." All three were real: a sidebar regex lumped Ch.27 with chapters 1-23 (fixed by moving it to the Foundation & Progression group); and every roadmap doc's bare --- section dividers were triggering CommonMark's Setext-heading rule (a paragraph immediately followed by a divider line becomes an accidental heading), silently turning random sentences into giant headers site-wide, not just for Ch.27. Fixed with a markdown pre-processing step (forces a blank line before every divider) plus real heading-size CSS — verified against the actual marked npm package before/after, not guessed. This benefits every chapter doc on the site automatically. |
| AI Debugger |
Complete through HUD-14H (Behavior/Decision Tree Viewer, decision recording) |
scripts/ui/AIDebug*, AITeam* + scripts/ai (103 files) |
In Sync |
Agrees well. A branch audit initially flagged 25 "unmerged" BasicAI commits as at-risk work, but 23 turned out to already be ported to scenarioeditor by hand; the 1 genuine gap was judged not worth reconciling given how far the code has since diverged. |
| Heat Map System |
HM-09 & HM-11 Complete; HM-10 (additional maps) explicitly parked |
Visualization code in AITeamTacticalVisualization.cs etc. |
In Sync |
Folder is literally named HeatMaps_Completed — genuinely done. |
| Historical Replay |
HR-00–04, HR-07 Complete; HR-05/06 In Progress; HR-08–12 not started |
Present in debugger scripts |
In Sync |
Agrees with doc. |
| Renderer Refactor |
Nearly all steps ✅; only VIS-RF-09 (final cleanup) active |
Matches file list in scripts/ui |
In Sync |
One concrete, scoped dead-code cleanup task remains open. |