Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 45 additions & 23 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,27 @@

Curated from the 2026-06-09 full-codebase audit (12-agent sweep, 199 findings) plus live playtest.
Wave 1+2 fixes covered the gameplay-killing bugs; these are the deliberate deferrals.
Fog of war + war termination shipped in game-loop v2; the intel suite + combat-on-contacts
shipped in v3 (docs/plans/intel-suite-v3.md) — v3 deferrals listed below.

## Intel suite v3 deferrals (design doc §7 + build-wave notes)

- Missions/doctrine cascade (patrol boxes, recon orbits, per-zone weapon release authority).
- HVT person-tracking chains, underground facility model, WAMI rewind, Staff Summary panel.
- Counter-OSINT levers: shutter control, disinfo plants, Iranian internet blackout.
- Live-data upgrades: OpenSky via a small Vercel proxy, aisstream AIS relay, Windy webcams,
GIBS fires MVT layer, USGS seismic ticker as MASINT flavor.
- STRIKE_LEAKED currently scoots the target + degrades the contact; the designed Iranian
point-defense readiness bump is not implemented.
- FLASH salvo-warning intercept path (getNextSalvoEstimate) has no test (couples to AI phase).
- ADS-B polling has no document-visibility pause; LiveFeeds drag is title-bar only.
- Destroying Iranian ISR assets (Mohajer orbit, picket boats, Noor) has no kill path.
- OSINT regime-mouthpiece nation detection is a name-regex heuristic; new Iranian names need
the patterns extended (osint-feed.ts IRAN_WEAPON_RE/IRAN_UNIT_RE).

## Big features (design needed before code)

- **Fog of war.** The snapshot ships full enemy state to the UI; detection/espionage/satellite
systems compute results that gate nothing. Decide the visibility model (per-unit detected
state with decay?), filter the snapshot, and wire HUMINT reveals + SIGINT range multiplier +
`satelliteDetectedUnitIds` into it. Until then the whole intel layer is cosmetic
(IntelBudgetPanel is a placebo).
- **Victory / end conditions.** Wars currently never end: no objectives evaluated, no
victory/defeat screen, CEASE_FIRE command handled by the engine but has no UI. The war just
goes silent. Define win conditions per scenario (e.g. Hormuz kept open N days, % enemy
strategic assets destroyed) and an end-of-war screen.
- **Logistics depth** (see below), **aircraft/sortie system**, **more scenarios**.
- **Logistics depth.** logistics-v2 (national stockpiles, shipments, production) was deleted as
dead code in the cleanup; the live v1 ignores `SupplyLine.capacity`/`distance_km`. If supply
is to matter strategically, re-design from the v1 base (git history has v2 for reference).
Expand All @@ -26,26 +35,39 @@ Wave 1+2 fixes covered the gameplay-killing bugs; these are the deliberate defer

## UX debt

- Panels overlap each other (SITREP opens on top of the strike dialog) and have no
drag/z-order management; Escape does not close panels.
- Mobile: LOG tab empty, IntelPanel X needs two taps, direct-fire flow yanks to UNIT panel.
- Strike timing modes (staggered/sequential) run on wall-clock setTimeout (0.3s/3s) instead of
game time, and fire while paused. Honest fix = engine-side scheduled salvos.
- Shipping lane name/throughput labels are built but never rendered; lanes/minefields have no
hover/click interactivity.
- Intel estimate markers are pickable but `moveEstimate`/`confirmEstimate` have no UI path.
- "BAB EL_MANDEB" event copy; review event vocabulary for raw enum leakage generally.
- No path back to the main menu from a running game.
- StartScreen shows a hardcoded stale version/date banner; project naming is split between
REALPOLITIK (UI/package/IndexedDB) and Ashfall Command (repo/Vercel).
- OFFER CEASEFIRE two-step confirm still uses silent blur-disarm — reuse TopBar's
useArmedCountdown for consistency with CONFIRM WAR.
- exitToMainMenu() in TopBar duplicates DebriefScreen's handleMainMenu — extract a shared
helper; consider SET_SPEED 0 on exit (worker keeps ticking behind the start screen).
- Package/IndexedDB internals still named realpolitik (package.json name, save-load DB_NAME,
map style ids) — cosmetic, renaming the DB key breaks saves.

## Engine scale (from the Wave A stress guard, 2026-06-11)

- Measured: ~60-unit shipped scenarios are comfortably real-time; at 500 units the
interceptor-saturation phase (~2100 live missiles) runs 80-190 ms/tick — missile
count dominates, units are cheap, terrain/LOS adds only ~9%. If bigger scenarios
ever become a target: spatial index / per-missile detection culling in
detection/visibility loops. NOT WASM-for-LOS. Guard: src/engine/__tests__/scale-stress.test.ts.

## Smaller deferred fixes

- DIRECT FIRE cluster popup z-order vs strike panel (replaces it on screen).
- DECLARE WAR confirm silently times out (by design?) — consider a visible countdown.
- visualTimestamp interpolation assumes nominal worker speed; visual clock can snap back.
- Multiple radars on one unit: max range paired with first radar's antenna/sector.
- detectThreats runs twice per radar unit per tick (perf, harmless at current scale).
- Sensor types sonar/irst/ew have no mechanics; `Sensor.detection_prob` unread.
- Sensor types sonar/irst/ew have no mechanics; `Sensor.detection_prob` data-only (noted in code).
- `GameTime.tickIntervalMs` set everywhere, read nowhere (visual interpolation hardcodes 100).
- Cursor elevation readout tracks mousemove even with ELV overlay off.

## Done 2026-06-11 (backlog wave 1)

- Panels: drag by title bar, click-to-raise z-order, Escape closes topmost, per-title
position memory (Panel.tsx + ui-store panels slice).
- Strike timing engine-side: LAUNCH_SALVO spacingTicks + state.scheduledLaunches queue —
pause-safe, save/load-safe, one leak roll per salvo. Staggered/sequential now space
rounds within the salvo (2 s/8 s); old 30 s/10 min between-tier delays removed.
- MAIN MENU in the ··· menu (two-step confirm), CONFIRM WAR visible 5 s countdown,
lane/supply-line names prettified (Bab el-Mandeb).
- Multi-radar pairing fixed (per-sensor range/antenna/sector evaluation), detectThreats
per-tick memo (3× → 1× per unit), ELV cursor listener gated on the toggle.
138 changes: 138 additions & 0 deletions docs/plans/air-war-v5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# Air war v5 — squadrons, flights, missions

Binding design for Roadmap Wave C. Research basis: CMO/Falcon/DCS Liberation
convergent design + real 2026 orbat (roadmap-v4.md Wave C). Core principle:
squadrons are pools, flights are transient map tracks, orders exist ONLY at
mission level. No per-airframe anything.

## 1. Data model

```ts
// types/game.ts
export interface SquadronState {
id: string // 'vfa14'
name: string // 'VFA-14 Tophatters'
airframe: AirframeId // 'fa18e' | 'f35c' | 'ea18g' | 'e2d' | 'f14' | 'mig29' | 'su24' | 'su35'
total: number // airframes on the books
available: number // ready now (total − airborne − turnaround − maintenance − lost)
readyAt: number[] // ticks when turning-around airframes rejoin `available`
}
// Unit gains: airWing?: SquadronState[] (carrier_group + airbase only)

export interface AirframeSpec {
id: AirframeId
name: string
speed_kts: number // cruise
combat_radius_km: number
sensors: Sensor[] // radar per airframe (E-2D huge + datalink)
weapons: WeaponLoadout[] // per-airframe loadout template by mission
datalink_range_km?: number // E-2D/E-3 are hubs
rcs_class: 'stealth' | 'fighter' | 'large'
}

export type AirMissionKind = 'cap' | 'strike' | 'aew'
export interface AirMission {
id: string
kind: AirMissionKind
squadronId: string
fromUnitId: UnitId
flightSize: number // 2-4
station?: Position // cap/aew orbit point
targetId?: UnitId // strike
escortSead?: boolean // EA-18G pair attached (USA only)
extendedRange?: boolean // tanker tax: +35% radius, −2 fa18e sorties
status: 'planning' | 'active' | 'complete' | 'aborted'
planningCompleteTick?: number
flightUnitId?: UnitId // spawned Flight unit
}
// GameState gains: airMissions?: AirMission[]
```

Flights are real `Unit`s, category `'aircraft'`, id `flight_<n>`, name
"2× F/A-18E (VFA-14)" — movement, visibility contacts, SAM engagement,
war-support, datalink all work day one. Flight unit carries the airframe's
sensors/weapons (scaled by flightSize) and `flightMeta` fields on Unit:
`{ missionId, bingoTick, rtbTo: UnitId }` (optional, plain data).

## 2. Sortie economy (CMO numbers)

- CAP quick-turn 90 game-min; strike turnaround 6 h (surge) / 20 h (sustained).
- Carrier ceiling ~100 sorties/day enforced implicitly by pools + turnarounds.
- 30% of each squadron's `total` starts in maintenance (deduct from available
at init; returns at scenario start + 12-24 h via readyAt).
- SURGE OPS: one global toggle (command). 96 h of halved ready times, then
×1.5 sustained for the rest of the war. TopBar chip while active.
- `extendedRange` deducts 2 extra fa18e sorties from the carrier (buddy
tanking; MQ-25 slipped to 2029).
- Strike planning delay: 2-6 h (rng seeded by mission id) between order and
launch window — `status: 'planning'`. CAP/AEW launch next tick.

## 3. Mission behavior (engine/systems/air-ops.ts)

Per game-minute evaluation + per-tick movement (movement.ts already moves
aircraft units; air-ops only sets waypoints/decisions):

- **Launch**: pop `available`, spawn Flight at the host position, waypoints to
station/target. bingoTick = now + 2×(transit time) + on-station allowance
derived from combat_radius vs distance (never fuel liters).
- **CAP**: orbit station (small 4-waypoint racetrack, re-issued when reached).
Auto-intercept: nearest enemy `aircraft` contact (tracked+, live) within
commit range 120 km → engage via AUTO_ENGAGEMENT path with A2A pK table
(new weapon type 'aam', simple pK per rcs_class; one engagement roll per
30 game-s within 40 km). Iranian flights symmetric.
- **Strike**: transit to release range of targetId (per loadout weapon),
spawn EXISTING missiles via launchMissile (trackQuality from the nation
picture like friendly-ai), then RTB. SEAD escort: while within 80 km of the
flight, enemy SAM detection_prob×0.6 and pk×0.6 vs this flight's missiles
AND emitting SAMs within 150 km become 'detected' contacts (ELINT hook).
- **AEW**: orbit station; the Flight has E-2D sensors + datalink_range_km →
the existing visibility + fire-control network does the rest.
- **RTB**: at bingo or out of weapons → waypoints home, on arrival the Flight
unit is removed, squadron readyAt += turnaround, `available` restored minus
losses. Flight destroyed → airframes lost, pilot roll per airframe:
KIA (−2 war support) / rescued (+1) / POW (−4, OSINT event).
- **Iran AI**: scramble-only — when a detected USA package/flight approaches a
defended box, spawn an interceptor CAP from the nearest airbase with
available fighters (ai.ts hook, max 2 concurrent). Su-35 squadron only
scrambles to defend Hamadan/Tehran. Parked airframes: each Iranian airbase
exposes `airWing` totals to BDA — airbase damage destroys parked airframes
proportionally (UNIT damage events → pool reduction; satellite pass caption
mentions counted airframes).

## 4. Commands

`LAUNCH_AIR_MISSION { kind, squadronId, fromUnitId, flightSize, station?, targetId?, escortSead?, extendedRange? }`,
`CANCEL_AIR_MISSION { missionId }` (RTB immediately), `SET_SURGE_OPS { enabled }`.

## 5. UI — Air Plan board

- New AIR button in TopBar → AirOpsPanel (Panel, draggable like the rest):
per host (carrier/airbase): squadron rows (name, airframe, available/total,
next-ready countdown), active missions list (kind, flight, status, BINGO
countdown, CANCEL), mission composer: kind → squadron → flight size →
(station: click-map capture like satellite tasking / target: enemy contact
picker) → SEAD/extended-range checkboxes → LAUNCH. SURGE OPS toggle with
the 96 h consequence spelled out.
- Map: Flight units render with existing aircraft icon; CAP/AEW stations get
a small racetrack glyph (IntelLayers-style); strike flights show a target
line when selected.
- ViewState: `airMissions` slice (player nation only) + squadron pools on
ViewUnit (own units only, scrubbed for enemies — enemy pools NEVER leak;
BDA estimates come via imagery product captions only).

## 6. Out of scope (→ BACKLOG)

Individual airframes, fuel-liter modeling, tanker tracks, weather aborts,
A2A missile entities (pK roll abstraction instead), runway cratering state
(airbase health already gates), carrier deck cycles visualization.

## 7. Build split

Scaffold (orchestrator): types + commands + airframe/squadron data files +
air-ops.ts skeleton + game-engine wiring + view slice.
- C1: air-ops.ts full implementation + ai scramble hook + tests.
- C2: AirOpsPanel + TopBar AIR + map glyphs + tests.
- C3: data verification pass (CVW-9 + Iranian orbat numbers vs research),
BDA/pool wiring on airbase damage + imagery captions, pilot-fate events +
AlertFeed formatters + OSINT feed coverage, tests.
- T: smoke extension (launch CAP, see flight on map, RTB).
Loading