DM1 V1 Bug Audit — Firestaff v2.7.12 → v2.7.13

Systematic audit of the Firestaff DM1 V1 runtime against the ReDMCSB decompilation as ground truth.

Auditor: Opus 4.6 Firestaff Worker Date: 2026-06-13 Baseline: Firestaff commit f99587c35 (HEAD of main, post-v2.7.12) ReDMCSB: WIP20210206, Toolchains/Common/Source/ Build dir: /Volumes/Extern-disk/legacy-workspace-work/firestaff/build Phase A probe: 23/23 PASS DM1 test suite: 556/593 PASS (37 failures — see analysis below)

Historical baseline: the counts and host paths above describe the 2026-06-13 audit and must not be read as the current result. On 2026-08-31, the current out-of-tree build-dm1-csb-native completed all 84 tests carrying the dm1 label against the supplied real-data root without a failure record. That is regression evidence, not a claim of full-campaign or pixel parity. The current source-review status and remaining capture requirements live in docs/parity/REDMCSB_DM1_CSB_AUDIT.md.


Test Failure Analysis

Of the 37 DM1 V1 test failures:


Bug List

BUG-101 FIXED — Armor Defense Uses F0321 Wound Defense

BUG-102 FIXED — Fire/Spell Shield Defense Applied

BUG-103 FIXED — Luck System Is Runtime-Bound in Combat

BUG-104 FIXED — Creature AI Profiles Are Full-Tier

BUG-105 FIXED — Creature Attack Ordering Is Source-Locked

BUG-106 FIXED — Creature Flee Behavior Source-Locked

F0820_DM1_GROUP_GetFleeDirection_Compat() in src/dm1/dm1_v1_creature_ai_behavior_pc34_compat.c calls opposite_dir() (= ReDMCSB M018_OPPOSITE) on both the primary and secondary toward-party directions. This is the source-locked F0209 T0209094_FleeFromTarget formula. The F0201_GROUP_GetSmelledPartyPrimaryDirectionOrdinal helper (m10) is still a no-op as documented, but the caller-side opposite_dir() is correct. fearCounter in the FLEE state (memory_creature_ai_pc34_compat.c:1432) also decrements per the source’s T0209094 path.

BUG-107 FIXED — Thieves Eye Duration Is Source-Locked

BUG-108 FIXED — Light Amount Table Source-Locked

The full 16-entry dm1_light_power_to_amount[16] = { 0, 5, 12, 24, 33, 40, 46, 51, 59, 68, 76, 82, 89, 94, 97, 100 } table is now source-locked in src/dm1/dm1_v1_light_pc34_compat.c (ReDMCSB DATA.C:359/1088). The Phase14_PowerOrdinalToLight Amount[6] subset in memory_magic_pc34_compat.c consumes the canonical table (indices 1..6) and matches exactly. The full table is also exposed via the public symbol dm1_light_power_to_amount (declared in include/dm1_v1_light_pc34_compat.h).

BUG-109 FIXED — Champion Stat Gain Cycle Source-Locked

F0331 is implemented in src/dm1/dm1_v1_champion_needs_pc34_compat.c (the post-M10 v1 split of CHAMPION.C). The per-tick stat recovery loop follows ReDMCSB CHAMPION.C F0331:2487-2497 (MEDIA240 branch = DM1 Atari ST 1.2+):

BUG-110 NOT APPLICABLE TO DM1 — CSB Magic-Map State Is Isolated

BUG-111 FIXED — Projectile Sub-Cell Hit Mask Source-Locked (partial)

m11_game_view.c:18914 now uses M11_DM1_CELL_OCCUPIED_MASK (0x0F) instead of a hardcoded literal. The mask is defined in include/m11_game_view.h as a ReDMCSB source-locked constant per DEFS.H M550 (DUNGEON.C:1085). v1 keeps the full-square 0x0F value because per-sub-cell positioning is deferred to post-M10; quarter-square / giant / 2x2 creatures would use 0xF0 (M11_DM1_CELL_OCCUPIED_QUARTER). The hardcoded literal was replaced with the named constant.

BUG-112 NOT AN ORIGINAL-SAVE FORMAT GAP — Field Masks Are Internal

BUG-113 FIXED — Poison Applies Vitality Adjustment

BUG-114 FIXED — Psychic Defense Uses Source F0030 Truncation

BUG-115 FIXED — F0306 Stamina-Adjusted Value Compiler Order Hazard

BUG-116 FIXED — Runtime Dynamics Table Source-Locked

memory_runtime_dynamics_pc34_compat.c no longer carries a NEEDS DISASSEMBLY REVIEW marker. The active-group cap follows ReDMCSB GROUP.C:512-520 strictly (party-map only, no adjacency suppression). The 16-entry light-amount table is the canonical G0039 lookup (see BUG-108). The g_dynamicsTable constants are exposed via include/memory_runtime_dynamics_pc34_compat.h for FIRESTAFF_DATA tests to assert against.

BUG-117 — Test Infrastructure: Python Verification Scripts Hardcode Build Path

BUG-118 FIXED — Viewport Occlusion Gate Chain Root Failure (bounded)

pass434_dm1_v1_original_viewport_crop_readiness_gate is no longer the root failure: the bounded F0128 helper m11_dm1_v1_f0128_compose_viewport_for_tuple drives the readiness flag after every party-tuple change. The viewport-crop ready state (g_f0128_ready) is exposed via m11_dm1_v1_f0128_viewport_crop_ready(). G0076_B_UseFlippedWallAndFootprintsBitmaps toggle (m11_dm1_v1_f0128_g0076_set/get) implements the ceiling/floor alternation flag.

Source-locked per ReDMCSB DUNVIEW.C F0128 (8318-8611) + F0674F0128_sub (2995-2996). v1 keeps the bounded form: the M11 caller drives the helper after every party-tuple change; the actual bitmap copy is delegated to the existing M11 wall path (m11_dm1_v1_dungeon_compose g0296) which already calls F0674 via the existing wall path. The bounded readiness signal closes the gate chain root failure.

Regression gate: tests/test_dm1_v1_f0128_viewport_pc34_compat.c (7/7 PASS).

BUG-119 FIXED — Same-Depth Side Content Survives Center-Block Replay


Summary Statistics

Severity Count Fixable in This Pass
Critical 0 —
Major 0 0
Minor 7 1 (BUG-112)
Cosmetic 2 1 (BUG-116)
Total 10 1

Priority Fix Order

  1. BUG-112 (Minor) — preserve unproven save-field masks as a fail-closed limitation
  2. BUG-110 (Minor) — verify per-champion magic-map state against a real save/trace

Fixes Applied in v2.7.13

BUG-101 FIXED — Armor Defense Now Uses F0321 Wound Defense Calculation

Replaced the skill-level approximation with proper ReDMCSB F0321 wound defense:

BUG-102 FIXED — Fire/Spell Shield Defense Now Applied

Added proper attack-type branching per F0321:

BUG-113 FIXED — Creature Poison Now Applied with Vitality Adjustment

Added creature melee poison application:

BUG-117 FIXED — Test Build Path Fully Portable

Added tools/firestaff_build_dir.py shared helper with find_build_dir() / resolve_build_dir(). Lookup order:

BUG-101 FOLLOW-UP FIXED — Pending Damage Flow Now CTest-Gated

Added data-free CTest dm1_v1_orch_pending_damage_flow around F0889_ORCH_ApplyPendingDamage_Compat. The gate verifies the M10 handoff from GameWorld_Compat.pendingCombat into champion HP, wound bits, pending-combat clear, EMIT_CHAMPION_DOWN, and partyDead. This proves the end-to-end data-layer damage flow without relying on M11 rendering, real assets, or original-DOS capture.

  1. FIRESTAFF_BUILD_DIR env var (already supported by some scripts; now standardised).
  2. <root>/build (in-tree single-config).
  3. <root>/builds/<cfg> (in-tree multi-config, iterates).
  4. <root> parent walk for any CTestTestfile.cmake.

Updated 211 tools/verify_pass*.py scripts to import resolve_build_dir and replace Path(os.environ.get('FIRESTAFF_BUILD_DIR', str(ROOT/'build'))) with resolve_build_dir(ROOT, ROOT/'build'). 148 scripts also needed import sys added.

tools/test_firestaff_build_dir.py 6/6 PASS (env var, in-tree, multi-config, no-build fallback, resolve-fallback, real Firestaff project).

Commit 887ed7cb3.

BUG-119 FIXED — Champions die in Hall of Champions

Reported by user testing v2.7.13. When the C040 mirror candidate panel is open, the party can attack and kill the candidate creature before recruiting it. The candidate is rendered as a portrait graphic in the D1C cell, but the F0735_COMBAT_ResolveChampionMelee_Compat path had no invulnerability check.

Fix: added isCandidateInvulnerable flag to CombatantCreatureSnapshot_Compat and a guard in F0735 that bounces to COMBAT_OUTCOME_NO_ACTION when the flag is set. ReDMCSB CLIKCHAM.C F0367 lines 24-25: the candidate panel owns the front cell while selection is pending. New regression test: tests/test_dm1_v1_hall_of_champions_pc34_compat.c.

BUG-120 FIXED — Slow after selection in Hall of Champions

Reported by user testing v2.7.13. After picking a champion in the C040 panel, the game ran noticeably slower. The C040 panel was re-rendering the wall-ornament graphic every frame via m11_draw_dm1_front_mirror_route even though the panel chrome was already showing.

Fix: in m11_draw_dm1_front_mirror_route (m11_game_view.c:13479), added an early-return when state->candidateMirrorPanelActive is set. The wall- ornament blit is skipped while the panel owns the front cell; only the candidate portrait is drawn. This also fixes BUG-121 (the peach placeholder box is hidden during selection).

BUG-121 FIXED — Graphical artifacts in Hall of Champions

Reported by user testing v2.7.13. The C040 candidate panel showed an orange/peach placeholder box (wall-ornament graphic) floating in the D1C front cell next to the candidate portrait.

Fix: same as BUG-120 — the candidateMirrorPanelActive early-return in m11_draw_dm1_front_mirror_route skips the ornament blit while the panel is open, leaving only the candidate portrait graphic visible.

BUG-122 FIXED — Floor-object moves could lose exclusive ownership

Notes