Stochastic Warfare -- Block 4 Brainstorm¶
Context¶
Blocks 1--3 (Phases 0--36) built the complete simulation engine, 5 historical eras, ~700 YAML data files, a REST API, a full React web application, and a documentation site. ~7,705 tests passing. 41 scenarios (27 modern + 14 historical). Zero TODOs/FIXMEs in the codebase.
Block 3 delivered the web application. Block 4 is the first opportunity to use it in anger and fix what surfaces. The goal: make the product reliable, polished, and self-contained enough that someone can clone, install, and run a full simulation through the browser without friction.
Current Inventory¶
What Works¶
- Engine: 19 modules + sub-packages, multi-domain (land/air/naval/sub/space/EW/CBRN), 5 eras, full campaign + battle management
- API: 25 REST endpoints + 2 WebSocket, async run execution, SQLite persistence, batch MC, analysis
- Frontend: Scenario browser, unit catalog, run submission, live WebSocket progress, 5 interactive chart types, battle narrative, tactical map with playback, scenario editor (clone-and-tweak), export (JSON/CSV/YAML/print), keyboard shortcuts
- Docs: MkDocs site live at GitHub Pages, 8 user-facing guides
- Data: 41 scenarios, 46+ unit types, 51 weapons, 63 ammo types, 16 sensors, 21 doctrines, 13 commanders
- Tests: 7,474 Python + 231 vitest = 7,705 total, zero TODOs
What's Broken / Unwired¶
DEW (Directed Energy Weapons) -- completely unwired (5 deficits from Phase 28.5):
- DEWEngagementEvent published but zero subscribers
- dew_engine wired in ScenarioLoader but never called in battle.py tick loop
- No scenario YAML references dew_config (engine exists but is never exercised)
- ADUnitType.DEW exists but not handled in air defense engagement routing
- route_engagement() not called from battle.py -- DEW routing untested in loop
config_overrides not applied (Phase 32):
- API accepts config_overrides dict and stores it in the DB, but it's never injected into CampaignScenarioConfig before ScenarioLoader.load(). The calibration overrides from the scenario editor aren't actually applied.
Force time series reconstruction ignores reinforcements (Phase 34):
- eventProcessing.ts builds the force strength chart by walking destruction events and decrementing from initial totals. Reinforcement arrivals never increment the count back up.
What's Deferred but Impactful¶
Map enhancements (Phase 35 deferrals): - FOW toggle: show only one side's known contacts vs omniscient view - Detection circles: sensor range visualization - Elevation shading: terrain relief on the map - Engagement fade: arcs fade over time instead of appearing/disappearing
Chart sync (Phase 35):
- Only ForceStrengthChart shows the tick sync marker line from map playback. The other 3 chart types don't.
UI polish (Phase 36 deferrals): - Dark mode - Config diff view (show changes from original scenario) - Scenario save to server filesystem - Virtualized lists for large event logs
Hardcoded strings (Phase 34):
- Morale state names and event type strings in eventProcessing.ts -- fragile if engine adds new types
- Analysis API responses are untyped Record<string, unknown> -- compare/sweep return free-form dicts
Gap Analysis: What Matters for First Real Use¶
When someone runs the web UI for the first time, they will:
- Browse scenarios -- works well, all 41 scenarios load
- Run a scenario -- works, WebSocket progress streams correctly
- View results -- charts render, narrative generates, map plays back
- Clone and tweak -- editor loads, but calibration overrides don't actually apply (config_overrides bug)
- Run a DEW scenario -- impossible, no scenario exercises DEW and the engine doesn't call the DEW engine in the tick loop
- Look at force charts for a campaign with reinforcements -- force counts will be wrong (never go back up)
The critical bugs are #4 (calibration overrides silently ignored) and #6 (wrong chart data). #5 is a feature gap -- DEW exists in the engine but isn't usable.
Beyond bugs, the biggest friction points will be: - No "just run it" script -- must manually start API server in one terminal, frontend in another - No production build -- no way to serve the built frontend from the API server - Large event logs are slow -- no pagination or virtualization in the event list
Proposed Block 4: Integration, Polish & Packaging¶
Phase 37: Integration Fixes & End-to-End Validation¶
Fix the broken integration points that will surface during real use. Then run every scenario through the full web UI pipeline to catch any remaining issues.
37a: Critical Bug Fixes
- Apply config_overrides: In api/run_manager.py, merge config_overrides into the loaded CampaignScenarioConfig before creating the SimulationContext. This makes calibration slider changes in the scenario editor actually affect the simulation.
- Force time series reinforcements: Update eventProcessing.ts to handle reinforcement arrival events (increment force counts when units arrive).
- Terrain types from data: Replace hardcoded terrain type list in GET /api/meta/terrain-types with values derived from actual terrain configs or scenario data.
37b: DEW Wiring
- Wire dew_engine into battle.py tick loop (call DEW engagement evaluation during combat phase)
- Handle ADUnitType.DEW in air defense engagement routing
- Wire route_engagement() call for DEW engagement types
- Subscribe to DEWEngagementEvent for recording/damage application
- Create at least one scenario YAML that references dew_config and exercises DEW units
- Add a dew_config section to one existing modern scenario (e.g., Taiwan Strait or Suwalki Gap)
37c: End-to-End Smoke Test
- Run every scenario through POST /api/runs and verify it completes without error
- Verify map renders for each (terrain + frames captured)
- Verify charts render for each (force strength, engagements, morale)
- Fix any scenario-specific issues that surface (missing signatures, invalid configs, wiring gaps)
- Verify scenario editor can clone any scenario, validate, and run the clone
Tests: ~40-60 new (Python integration + vitest)
Phase 38: Map & Chart Enhancements¶
Bring the tactical map and charts up to the quality level planned in the Block 3 design.
38a: FOW Toggle
- Add a "Fog of War" toggle to MapControls
- When FOW is active for a side, only show units that side has detected (requires detection event data in frames or a separate detection layer)
- Implementation: extend frame capture to include per-side detected unit IDs, filter rendering by selected side
- Omniscient view (default, current behavior) shows all units
38b: Map Visual Enhancements - Detection circles: optional sensor range circles around selected units (toggleable in MapControls) - Elevation shading: use heightmap data from terrain response to apply subtle brightness variation to terrain cells - Engagement fade: arcs decay over 10 ticks instead of instant appear/disappear - Better destroyed unit rendering: X marker or grayed-out icon instead of just opacity
38c: Cross-Chart Tick Sync
- Extend tick sync marker line to EngagementTimelineChart, MoraleChart, and EventActivityChart
- All charts read ?tick=N from URL params and draw the vertical reference line
- Clicking on a chart point sets ?tick=N (bidirectional sync)
38d: Dark Mode
- Tailwind dark mode classes throughout the frontend
- useTheme hook with localStorage persistence
- Toggle in sidebar footer
- Map canvas dark palette (darker terrain colors, brighter unit markers)
- Chart theme integration (Plotly dark layout)
Tests: ~30-40 new vitest
Phase 39: Quality, Performance & Packaging¶
Close remaining quality gaps, optimize performance for large runs, and package the application for single-command startup.
39a: Test Coverage Gaps
- useBatchProgress dedicated test file
- useViewportControls dedicated test file
- RunDetailPage cancelled/error state tests
- Typed analysis API responses (define TypeScript interfaces for compare/sweep results)
39b: Performance
- Frame capture interval configurable (add to EngineConfig or API run request)
- Virtualized event list (react-window or @tanstack/react-virtual) for runs with 10K+ events
- Lazy-load event data (fetch pages on scroll, not all at once)
39c: Startup & Packaging
- Single-command dev startup: Script/Makefile that launches both API server and frontend dev server
- Production build: Vite npm run build output served by FastAPI as static files (mount frontend/dist/ at /)
- Single-command production: uv run python -m api starts the API server serving the built frontend -- one command, one port
- Docker: Dockerfile that builds both Python and frontend, runs uvicorn serving everything on port 8000
- README update: Add "Quick Start (Web UI)" section with the single command
39d: Minor Polish
- Config diff view: show what changed from original scenario in the editor (simple before/after comparison)
- Hardcoded morale states and event types -> derive from API or use constants
- GET /api/meta/terrain-types from data instead of hardcoded list
- Better error messages when a run fails (show the Python traceback in the UI)
Tests: ~20-30 new
Deferred Beyond Block 4¶
These are real features but not needed for the "make it work well" goal:
| Item | Why Deferred |
|---|---|
| Scenario save to server filesystem | Clone-and-tweak + download YAML is sufficient |
| Real PDF generation | window.print() with CSS works |
| Drag-and-drop unit reordering | Click-based add/remove is sufficient |
| Real-time map streaming during run | Post-hoc replay covers the core use case |
| Multi-run map overlay | Comparison charts are sufficient |
| Mobile-optimized touch controls | Desktop-first product |
| Authentication / multi-user | Single-user by design |
| SGP4/TLE orbital mechanics | Keplerian is sufficient for game-scale simulation |
| Individual carrier deck spots | Aggregate model is sufficient |
| Cooperative jamming | Single-jammer model is sufficient |
| All the Phase 6 logistics edge cases | Don't materially change outcomes |
| All the Phase 8 planning simplifications | Analytical COA wargaming is working |
| All the Phase 17 space simplifications | Statistical models are sufficient |
Block 4 Summary¶
| Phase | Focus | Estimated Tests | Key Deliverables |
|---|---|---|---|
| 37 | Integration Fixes & E2E Validation | ~50 | config_overrides applied, DEW wired, reinforcement charts fixed, all 41 scenarios smoke-tested |
| 38 | Map & Chart Enhancements | ~35 | FOW toggle, detection circles, elevation shading, cross-chart tick sync, dark mode |
| 39 | Quality, Performance & Packaging | ~25 | Test gaps closed, virtualized lists, single-command startup, Docker, production build |
| Total | ~110 |
Principle: Block 4 is a tightening block, not a feature block. No new engine subsystems. No new simulation domains. Fix what's broken, polish what's rough, package what's scattered. The goal is that after Block 4, someone can docker run or uv run python -m api and use the full product through a browser without any friction or surprises.
Implementation Order¶
37a (bug fixes) -> 37b (DEW wiring) -> 37c (E2E smoke test)
|
v
38a/38b/38c/38d (parallel within phase)
|
v
39a/39b (parallel) -> 39c (packaging) -> 39d (polish)
Phase 37 must come first -- it fixes the bugs that would otherwise surface during Phase 38/39 work. Phases 38 and 39 are largely independent and could be reordered, but map enhancements (38) are more user-visible and should come before packaging (39).
Verification¶
# After Phase 37: all scenarios run through API without error
uv run python -m pytest tests/api/ --tb=short -q
uv run python -m pytest --tb=short -q
# After Phase 38: frontend tests pass with new components
cd frontend && npm test && npm run build
# After Phase 39: single-command startup works
uv run python -m api # serves API + built frontend at :8000
docker build -t stochastic-warfare . # Docker build succeeds
docker run -p 8000:8000 stochastic-warfare # full stack in one container