# User Manual Coverage Contract

Every user-facing surface and background process, mapped to the manual
section that teaches it. A row is resolved when its **Manual section** cell
names a volume anchor (e.g. `V4 §5.7.2`) or a reasoned `n/a`. M8's programme
gate requires 100 % of rows resolved. Maintenance rules: [EVOLUTION.md](EVOLUTION.md) §3.

OffTaker Operations usability update · 2026-10-07: route/export counts remain unchanged. The directory is taught in V5 §2 and the five workspace tabs in V5 §2.4 and §§3–7; imports require a direction-aware preview.

Operations usability update · 2026-10-06: existing route and controller-export
counts are unchanged. The static download `/templates/settlement-intervals.csv`
and meter explorer in `/org/ipp-sites/sites/:site_id/operations` are taught in
[V4 §11.9](v4-ipp.html#119-meter-history-and-interval-imports).

**Reconciliation header** — S14 catalog sync · verified 2026-09-26 ·
**173 catalogued LiveView routes / 116 modules; 13 catalogued controller exports.**
Counts re-derived from `WB.Setup.CoverageCatalog.verify!/0`; the headers below had
drifted from the verifier pins. Catalogues three routes that were live but missing
from the S14 catalog: the OffTaker Sites register (`/org/off-taker-sites/sites`), the
off-taker site workspace (`/org/off-taker-sites/sites/:site_id/:section`) and the
trader pool workspace (`/org/trader-pools/pools/:pool_id/:section`). The manual does
not yet teach the register or the workspace sections; their rows say so.

Prior header — Carbon rooms · verified 2026-09-24 ·
**165 catalogued LiveView routes / 118 modules; 12 catalogued controller exports (unchanged).**
Tax, Market and Certificates keep their routes. Teaching now follows the
workspace tabs: Tax (`overview`, `activity`, `facilities`, `offsets`,
`returns`), Market (`discover`, `orders`, `negotiations`, `trades`),
Certificates (Overview, Portfolio, Origination, Registry pipeline,
Claims & reporting). Pack downloads were already one controller route.

Prior header — Bid Desk trading-day overview · verified 2026-09-24 ·
**165 catalogued LiveView routes / 118 modules; 12 catalogued controller exports (unchanged).**
The landing overview, evidence drawers and direct unit handoffs change within the existing route.
DailyDashboard is now a function component.

Prior header — Accounts workspace · verified 2026-09-24 ·
**165 catalogued LiveView routes / 118 modules; 12 catalogued controller exports.**
Adds Payments & receipts, Balances and Statements routes. Legacy account paths
remain compatible; vesting redirects into Month. Replenishment lives in Account & cover.

Prior header — CT6 · verified 2026-08-31 · **counts
re-derived from the router; live count 142 (unchanged).** CT6 adds no
route: the Certificate Desk **Projects** tab patches onto
`/org/certificates`. Demo step **S23** (Atlantis Wind origination)
is listed after S22. CS1 and Q2 mainnet still wait.

Prior header — CT5 · verified 2026-08-31 · **counts
re-derived from the router; live count 142.** CT5 adds
`/operator/cadt` (`Operator.CadtLive`). Demo step **S22** (CAD Trust
observer hit-and-resolve) is listed after S21. Mainnet participant
still waits on Q2.

Prior header — CT4 · verified 2026-08-31 · **counts
re-derived from the router; live count 141.** CT4 adds
`/signup/carbon` (`Public.CarbonSignupLive`),
`/mo/carbon-activations` (`Mo.CarbonActivationsLive`), and
`/org/carbon-tax` (`Org.CarbonTaxLive`). Demo step **S21** (BrickCo
carbon tax) is listed after S20. Live tax-pack wording still waits
on CS1.

Prior header — CT3 · verified 2026-08-31 · **counts
re-derived from the router; live count 138 (unchanged).** CT3 adds no
route: the Certificate Desk **Pipeline** tab patches onto
`/org/certificates`. CS2 closed.

Prior header — CT2 · verified 2026-08-31 · **counts
re-derived from the router; live count 138.** CT2 adds
`/org/carbon-market` (`Org.CarbonMarketLive`). The Certificate Desk
**Trading** tab patches onto `/org/certificates` (no extra S14 entry).
Demo step **S20** (carbon venue) is listed after S19.

Prior header — CT1 · verified 2026-08-31 · **counts
re-derived from the router; live count unchanged at 137.** CT1 adds no
route: external registration and bilateral trades patch onto
`/org/certificates`. Certificate export routes in the catalogue are
now 5 (the two Money Room exports plus `green-statement.pdf` /
`green-statement.csv` / `traceability.pdf`). Demo step **S19** (carbon
market) is listed after S14; S18 remains the Regional desk.

Prior header — T13M5 · verified 2026-08-30 · Stage 13 T13M0–T13M5:
currency dimension, FX register, SAPP client, cross-border ATC,
settlement shadowing, FPM products, `/org/regional` (four live
declarations), SO Network Model **Usage** tab (no new route), demo
step S18, and **V16**. Stage 12's `/org/trader-pools/:id/strategy/exposure`
is already on `main`. **Five new live routes versus Stage 11; two new LiveViews.**

What MS4–MS13 added, and where it is taught:

| Milestone | Surfaces added | Manual section |
| --- | --- | --- |
| MS4 | party lifecycle screens, accession register endpoint | V7 §3 |
| MS5 | Bid Desk, gate clock, submission endpoints | V7 §4 |
| MS6 | MO Clear-day, public price board, DAM price endpoints | V7 §4, §10, §13 |
| MS7 | settlement views, §9.10 report endpoints | V7 §13 |
| MS8 | Intraday Desk, IDM endpoints | V7 §5 |
| MS9 | Dispatch Desk, instructed-energy endpoints | V10 §3 · §4 |
| MS10 | allocation exceptions inbox, metering endpoints | V7 §14 |
| MS11 | MO Balancing Board, balancing statement, balancing endpoints | V7 §6 |
| MS12 | Billing Board, credit cover, query workflow, settlement endpoints | V7 §15, §16 |
| MS13 | §16 publication, §6.5 events, reference sandbox — **API only, no new screen** | V7 §12, §17 |

MS13 adds no LiveView. Its surfaces are the published document, the event
feed and the sandbox control plane, all of which are read by machines rather
than by a seat — which is why the `live` count moves by nothing and the
controller-route count moves by fourteen.

| Count | Value | Reconcile with |
| --- | --- | --- |
| `live` route declarations | 158 | `grep -c 'live "' lib/wb_wheeling_web/router.ex` — includes the owner's Plant telemetry page added in Foundation Stage 11; 118 routed LiveView modules |
| Controller routes | 178 | `get`/`post`/`put`/`patch`/`delete` in `router.ex` — includes `POST /api/v1/oauth/token` and the four forward-schedule endpoints |
| Oban cron entries / distinct workers | 85 / 56 | `config/config.exs` crontab — includes `CertificateRunner`, `Insight.Workers.FactBuilder`, `LevyConservationRunner`, `FetchWeatherCycle`, `FetchWeatherDrift`, `RunStochastic`, `RunFleet`, `Cadt.Workers.SyncRunner`, `Cadt.Workers.PublishRunner`, MO cadence (`DamCadence` / `IdmCadence` / `DeliveryCadence` / `SettlementCadence`) |
| Dev-only routes | 2 | `live_dashboard` + `forward` in `router.ex` |
| Stateful LiveComponents | 37 | `rg -l 'use WB\.WheelingWeb, :live_component\|use Phoenix\.LiveComponent' lib/wb_wheeling_web \| wc -l` — nine Bidding + seven org Bid Desk components |
| Non-cron Oban workers | 10 | Oban workers under `lib/wb/**/workers/` not named in crontab |
| AshOban triggers | 4 | `PhysicalPpa` `:activate` / `:suspend_unsigned`, `VirtualPpa` `:suspend_unsigned`, `Proposal` `:expire_stale` |
| Long-running processes | 3 | `WB.Sim.LiveMeter.Simulator`, `WB.Sim.Driver`, `WB.Sim.Market.DamSubmitter` |
| Mix tasks | 7 | `lib/mix/tasks/` |
| Seats | 8 | PLAN §3 |
| SAWEM API + internal routes | 78 | `WB.Sawem.Api.Publication.routes/0` — **generated**, and `publication_test.exs` fails if it and the router disagree |

**Coverage status:** Matrix 1–4 complete through M13 surfaces (M14
reconciliation). Shared function components (chart kits, badges) remain
covered through their host screens (CODEX convention); every stateful
LiveComponent has its own Matrix 2 row.

**The API surface is not covered by a Matrix row and does not need to be.**
It is covered by something stronger: `WB.Sawem.Api.Publication.routes/0`
generates the published contract from the router itself, and
`publication_test.exs` asserts the two sets are equal in both directions. A
route the application serves and the document omits fails the build, and so
does the reverse. That is a coverage contract that cannot go stale between
reconciliation passes, which is what this file's own header has to be
re-derived by hand to avoid.

## Matrix 1 — Routes and LiveViews

### Public and session

| Route | Module | Manual section |
| --- | --- | --- |
| `/` | `IndexLive` (app chooser + login) | V0 §2.1–2.3 (orientation; V1 owns the deep walkthrough) · V7 §18 (`#market-phase-badge`) |
| `GET /set-organization` | `PageController.set_organization` | V0 §2.2 |
| `POST /session` | `SessionController.create` | V0 §2.2 |
| `DELETE /logout` | `SessionController.delete` | V0 §2.2 |
| *(unrouted)* | `Org.OrgLive` | n/a — unrouted legacy module |

### Eskom app

| Route | Module | Manual section |
| --- | --- | --- |
| `/eskom` | `Eskom.IndexLive` (redirect) | V19 §1 |
| `/eskom/virtual-contracts` | `Eskom.VirtualContractsLive` | V15 §9 · V19 §1.1 |
| `/eskom/wheeling-contracts` | `Eskom.PhysicalContractsLive` *(placeholder)* | V19 §1.1 |
| `/eskom/tariffs` | `Eskom.TariffsLive` | V2 §8 (concepts) · V19 §1.2–1.4 (workflows) |
| `/eskom/tariffs/:tariff_id/charges` | `Eskom.ChargesLive` | V19 §1.3 |
| `/eskom/tariffs/:tariff_id/rates` | `Eskom.RatesLive` | V19 §1.3 |
| `/eskom/loss-factors` | `Eskom.LossFactorsLive` | V2 §8.3 (concepts) · V19 §1.5 (workflows) |
| `POST /operator/session` | `Operator.SessionController` | V13 §9 · V19 §1.6 |
| `/operator` · `/operator/register` | `Operator.RegisterLive` | V13 §9 · §13 |
| `/operator/documents/:id` | `Operator.DocumentLive` | V13 §13 · §6 |
| `/operator/received` | `Operator.ReceivedLive` | V13 §13 |
| `/operator/run` | `Operator.RunLive` | V13 §6 · §13 |
| `/operator/settings` | `Operator.SettingsLive` | V13 §13 |
| `/operator/cadt` | `Operator.CadtLive` | V19 §9 · V17 §22 |
| `GET /operator/register.csv` · `/month-pack.zip` · `/documents/:id/file` | `Operator.ExportController` | V13 §9 · §18 |

### Operator consoles and Scenario Lab

| Route | Module | Manual section |
| --- | --- | --- |
| `/mo` | `Mo.BiddingRedirectController` → Daily | V9 §13 |
| `/mo/registry` | `Mo.MarketDeskLive` | V9 §22.4 · Trading Units |
| `/mo/registry/counterparty` | `Mo.MarketDeskLive` | V9 §22.5 · Counter Party |
| `/mo/registry/standing-data` | `Mo.MarketDeskLive` | V9 §22.6 · Standing Data |
| `/mo/bidding` | `Mo.BiddingLive` `:dashboard` | V9 §13.7 |
| `/mo/bidding/submissions` | `Mo.BiddingLive` `:submissions` | V9 §13.8 · V7 §4.8 |
| `/mo/bidding/idm` | `Mo.BiddingLive` `:idm` | V9 §13.9 · V7 §5.10 |
| `/mo/bidding/settlement` | `Mo.BiddingLive` `:settlement` | V9 §13.10 · V7 §13 |
| `/mo/bidding/month` | `Mo.BiddingLive` `:month` | V9 §13.11 |
| `/mo/bidding/clear` | `Mo.BiddingLive` `:clear` | V9 §13.12 · §12 |
| `/mo/cadence` | `Mo.CadenceLive` | V9 §14 |
| `/mo/forward` | `Mo.ForwardRegisterLive` `:register` | V9 §27 · V7 §20.8 |
| `/mo/forward/capacity` | `Mo.ForwardRegisterLive` `:capacity` | V9 §27.2 · V7 §20.6 |
| `/mo/metering` | `Mo.MeteringLive` | V9 §16 · V7 §14.6 |
| `/mo/balancing` | `Mo.BalancingLive` | V9 §17 · V7 §6 |
| `/mo/accounts` | `Mo.AccountsLive` | V9 §18 · V7 §3.16 · §15 |
| `/mo/billing` | `Mo.BillingLive` | V9 §19 · V13 §14 |
| `/mo/billing/documents/:id` | `Mo.BillingDocumentLive` | V9 §19.8 · V13 §14 · §6 |
| `GET /mo/billing.csv` · `/billing/documents/:id/file` | `Mo.BillingExportController` | V9 §19 · V13 §14 |
| `/mo/dispute` | `Mo.DisputeLive` | V9 §20 · V7 §8 |
| `/mo/notices` | `Mo.NoticesLive` | V9 §15 · V7 §9 |
| `/mo/participants` · `/accession` · `/clocks` · `/extensions` · `/defaults` · `/balancing-groups` · `/credentials` · `/vesting` (`/suspensions` · `/blocked` patch to Registrations) | `Mo.ParticipantsLive` | V9 §21 · §21.12 · §21.13 |
| `/mo/carbon-activations` | `Mo.CarbonActivationsLive` | V9 §12 · V18 §1 · V19 |
| `/mo/ipps` | `Mo.IppsLive` | V9 §23 |
| `/mo/zones` · `/mo/zones/unplaced` | `Mo.ZonesLive` | V9 §24 |
| `/mo/rules` | `Mo.RulesLive` | V9 §25 · V7 §17 · V7 §18 (`#market-phase-section`) |
| `/mo/runs` | `Mo.RunsLive` | V9 §26 |
| `/mo/bid-desk` | `Mo.BiddingRedirectController` → Daily | V9 §13 — surviving clear |
| `GET /mo/dam` · `/dam/submissions` · `/dam/idm` · `/dam/settlement` · `/dam/month` | `Mo.BiddingRedirectController` (301) | V9 §13.5 — handbook paths |
| `/so` | `So.DispatchDeskLive` | V10 §4 (Issue / Active / Issued) |
| `/so/network-model` | `So.SONetworkModelLive` | V10 §2 · §5 (Model / Map / Usage / API) · §5.5 (flags) · V16 §4 (usage) |
| `/so/registry` | `So.RegistryLive` | V10 §6 (IPPs search/sort · Inbox · Certification) |
| `/so/registry/ipps/:id` | `So.IppLive` | V10 §6.3–§6.6 (Info / Reserve / Notices) |
| `/org/sim` | `Org.Sim.ScenarioLabLive` (sim-gated) | V12 · V3 §17 · V12 §17 |
| `/demo` | `Demo.DashboardLive` (sim-gated) | V19 §8 · §8.7 (`#market-phase-line`) |

### Munix app

| Route | Module | Manual section |
| --- | --- | --- |
| `/munix` | `Munix.IndexLive` (redirect) | V19 §2 |
| `/munix/configuration` | `Munix.ConfigurationLive` | V19 §2.1 · V2 §9 |
| `/munix/wheeling-contracts` | `Munix.WheelingContractsLive` *(placeholder)* | V19 §2.2 |
| `/munix/tariffs` | `Munix.TariffsLive` | V19 §2.3 |
| `/munix/wheeling-charges` | `Munix.WheelingChargesLive` *(placeholder)* | V19 §2.2 |
| `/munix/tariffs/:tariff_id/charges` | `Eskom.ChargesLive` (`:munix`) | V19 §2.3 |
| `/munix/tariffs/:tariff_id/rates` | `Eskom.RatesLive` (`:munix`) | V19 §2.3 |

### Org — configuration and directories

| Route | Module | Manual section |
| --- | --- | --- |
| `/org` · `/org/configuration` · `/org/configuration/accreditations` · `/org/configuration/feeds` | `Org.ConfigurationLive` (4 routes: Organization, Accreditations and Feeds & webhooks tabs) | V2 §2 · §2.5 · §2.6 · V21 §11 |
| `/org/market-accounts` | `Org.MarketAccountsLive` · Overview | V10 §14 |
| `/org/market-accounts/payments` · `/balances` · `/statements` | `Org.MarketAccountsLive` (3 workflow routes) | V10 §14.4–14.10 |
| `/org/market-accounts/bank` · `/clearing` · `/collateral` · `/vesting` | `Org.MarketAccountsLive` (4 legacy paths; vesting redirects to Month) | V10 §14.0.1 · §13.5 |
| `/org/billing` | `Org.BillingLive` | V15 §10 · §12 · §16 · §18 · §22 |
| `/org/money` | `Org.MoneyLive` | V16 §1 · §5 · §7 · §9 · V4 §16 · V4 §10.5 (bridge: no Annual exposure tile; Panel 4 `curtailment_unrecovered`) |
| `GET /org/money.csv` | `Org.MoneyExportController.csv` | V16 §9 |
| `GET /org/money.pdf` | `Org.MoneyExportController.pdf` | V16 §9 |
| `GET /org/billing.csv` | `Org.BillingExportController.csv` | V15 §12 · §18 |
| `GET /org/billing/utility-bills/:id` | `Org.BillingExportController.utility_bill` | V15 §22 |
| `GET /org/billing/payments/:id/remittance` | `Org.BillingExportController.remittance` | V15 §16 |
| `GET /org/billing/documents/:id` | `Org.BillingExportController.document` | V13 §5 · §12 |
| `/org/certificates` | `Org.CertificatesLive` | V19 §9 · §12 · §13 · §19 · §21 |
| `/org/carbon-market` | `Org.CarbonMarketLive` | V19 §20 |
| `/org/carbon-tax` | `Org.CarbonTaxLive` | V20 §1–§8 |
| `/signup/carbon` | `Public.CarbonSignupLive` | V20 §1 |
| `/mo/carbon-activations` | `Mo.CarbonActivationsLive` | V20 §1 · V19 · V9 §12 |
| `/org/virtual-wheeling` | `Org.VirtualWheelingLive` | V17 §8 |
| `GET /org/certificates/green-statement.pdf` | `Org.CertificatesExportController.pdf` | V19 §13 |
| `GET /org/certificates/green-statement.csv` | `Org.CertificatesExportController.csv` | V19 §13 |
| `GET /org/certificates/traceability.pdf` | `Org.CertificatesExportController.traceability` | V19 §8 |
| `GET /org/carbon-tax/packs/:id/:format` | `Org.CarbonTaxExportController.show` | V20 §7 |
| `/org/off-taker-sites` | `Org.OffTakerSitesLive` | V2 §3.1 · V5 §2.1 (columns `#211-columns`, portfolio summary `#213-portfolio-summary`) · V5 §2.1.5 (`#215-needs-you`) · V5 §2.3 (`#off-taker-onboarding-drafts`) |
| `/org/off-taker-sites/sites` | `Org.OffTakerSitesLive` (Sites register, `:index`) | V5 §2.1 (`#21-the-directory`) — searchable site table and portfolio summary |
| `/org/off-taker-sites/sites/:site_id/:section` | `Org.OffTakerSitesLive` (site workspace, `:workspace`: operations · performance · allocation · commercial · dashboard) | V5 §2.4 (`#25-the-tabs`) · V5 §3 (`#26-operations--meter-history-and-imports`, energy table `#32-view-energy-data-and-choose-a-month`, Reads `#33-the-reads-tab`, site location `#34-site-location`) — all five sections; Allocation evidence and month-scoped drilldowns: V5 §5 (`#135-sources--where-settled-energy-came-from`) |
| `/org/off-taker-sites/onboarding/:id` | `Org.OffTakerSiteOnboardingLive` | V5 §2.3 (home) · V2 §3.2 (demand classes) · V2 §3.3 (Edit modal) |
| `/org/ipp-sites` | `Org.Strategy.FleetLive` (Fleet tab) | V4 §2 · V8 §3 |
| `/org/ipp-sites/sites` | `Org.IppSitesLive` (Sites tab) | V2 §4 · §4.3A · §4.8 · V4 §3 · V4 §16 · V2 §7.7 |
| `/org/ipp-sites/onboarding/:id` | `Org.IppSiteOnboardingLive` | V2 §4.3 · §4.3A · §4.3B (completion checks) · §4.8 |
| `/org/fleet` | `Org.Strategy.FleetLive` | V4 §2 · V8 §3 |
| `/org/fleet/counterparties` · `/org/fleet/sites` | `Org.Strategy.FleetLive` (redirect → `/org/ipp-sites`) | V4 §2 |
| `/org/generator-sites` | `Org.GeneratorSitesRedirectLive` (redirect → `/org/ipp-sites/sites`) | V2 §4.1 |
| `/org/trader-pools` | `Org.TraderPoolsLive` | V2 §5 · V6 §2 · V6 §2B (drafts rail) |
| `/org/trader-pools/pools/:pool_id/:section` | `Org.TraderPoolsLive` (pool workspace: operations · performance · allocation · commercial · dashboard) | V2 §5.2 (opened from the directory) — the workspace sections are not yet taught |
| `/org/trader-pools/onboarding/:id` | `Org.TraderPoolOnboardingLive` | V6 §2B (home) · V2 §5.4 (Edit pointer) |

### Org — trader strategy (book-keyed)

| Route (`/org/trader-pools/:id/strategy…`) | Module | Manual section |
| --- | --- | --- |
| `…/` | `Strategy.StrategyOverviewLive` | V6 §4 (position · gauges · concentration · flow · actions · clock · pool health · heatmap · drift · Run Forecast Now · Explanations) |
| `…/pools` | `Strategy.PoolsLive` | V6 §5 (Book · Rules) · §5.7 (wiring gutter) · §5.7.2 (create a joiner) |
| `…/allocations` | `Strategy.AllocationsLive` (redirect → `…/book?mode=forecast`) | V6 §6 |
| `…/book` | `Strategy.BookLive` (bare URL → Rules / `…/pools`; `?mode=outcomes` Outcomes; `?mode=forecast` Forecast; top tab stays Book) | V6 §5 (default Rules) · §6A (Outcomes) · §6A.5–§6A.15 (Outcomes walk) · §6 (Forecast) |
| `…/chains` | `Strategy.TraderChainsLive` (redirect → `…/book?mode=outcomes`) | V6 §6B |
| `…/allocations/:run_id` | `Strategy.AllocationDetailLive` | V6 §8 · §8.6 (arrival · Book lit · back href) · §8.7 (empty / failed) · §8.8 (T1 Sep P50) · §8.9 (ids) |
| `…/simulations` | `Strategy.SimulationsLive` (the Desk: position · candidates · basket · strategy board · exposure · commit) | V6 §9 · §9.6 · §9.8–§9.10 |
| `…/simulations/proposals/:id/sell` | `Strategy.TraderSellProposalStudioLive` | V6 §9.6 · V7 §3.4f (trader sell compose) |
| `…/simulations/proposals/:id/buy` | `Strategy.TraderBuyProposalStudioLive` | V6 §9.6 · V7 §3.4e (trader buy compose) |
| `…/nominations` | `Strategy.NominationsLive` (the Desk's Commit step; own URL kept) | V6 §10 · §9.9 |
| `…/bess` | `Strategy.BessLive` (under Book; the Forecast battery fold links here) | V6 §11 · §11.11 (full page walk) |
| `…/dashboard` | `Strategy.RiskDashboardLive` (the pool's story: six chapters, the ten panels folded) | V6 §12 · V3 §22 |
| `…/exposure` | `Strategy.CounterpartyExposureLive` (redirect → `…/simulations`, `date` carried; 404 cross-org) | V6 §9.10 · V3 §22.5 |

### Org — off-taker strategy (site-keyed)

| Route (`/org/off-taker-sites/:id/strategy…`) | Module | Manual section |
| --- | --- | --- |
| `…/allocations` | `Strategy.OfftakerAllocationsLive` (redirect → Overview `?view=settled`) | V5 §5 · §4 |
| `…/` | `Strategy.OfftakerOverviewLive` (Performance tab; `?view=settled&month=` is the retired Sources view) | V5 §4 · §4.1 hero · §4.2 mix · §4.3 wedge · §4.4 actions · §4.5 Run / Assumptions · §4.6 missing rates · §5 Allocation · §4.7 routes · §4.8 weather · §4.9 price rail · §4.10 example · §4.11 Explanations |
| `…/procurement` | `Strategy.ProcurementLive` | V5 §6 (Commercial — the Procurement Desk) · §6.1 (Coverage Rationale tool) |
| `…/procurement/proposals/:id` | `Strategy.OfftakerProposalStudioLive` | V7 §3 · §3.4 · §3.4b · §3.4c (instrument) · §3.5d (studio + findings; V5 §6 bridge) |
| `/org/off-taker-sites/:id/proposals/:id/review` | `Strategy.ProposalReviewStudioLive` | V7 §3.5a–c · §3.5e (physical + virtual review; Decide counter; M11 · MV4 · MX3) |
| `…/dashboard` | `Strategy.OfftakerDashboardLive` | V5 §7 (the story of the site) |

### Org — IPP strategy (site-keyed)

Stage 10 adds `/org/generator-sites/:generator_site_id/strategy/battery` →
`Strategy.IppBatteryLive`, covered by V4 §20: no-asset and no-ACTUAL states, wear editor,
health register, reserve register, KPIs and cycle-cap evidence. The IPP Dashboard
(`IppDashboardLive`) weaves those KPIs into Chapter 5 (`#ipp-story-battery`) only when
this site has a `BessAsset`; a site without a battery does not mention one (V4 §12.6.1).
The trader's existing BESS page adds depth/wear, the reserve-floor band, operational
contract chips and two-plan comparison (V3 §10.8–10.10). Health, pack cost, availability
and reserve revenue are owner-only.

| Route (`/org/generator-sites/:id/strategy…`) | Module | Manual section |
| --- | --- | --- |
| `…/` | `Strategy.IppOverviewLive` | V4 §3.2 · §4 · V3 §14 (pointer) |
| `…/haf` | `Strategy.IppHafLive` (redirect → Sales) | V4 §5 · §3.2 |
| `…/sales` | `Strategy.SalesLive` | V4 §3.2 · §6 · §6.7 · §6.8 · §5.9 |
| `…/sales/proposals/:id` | `Strategy.IppProposalStudioLive` | V5 §3.4a · §3.4b · §3.4c (instrument) · §3.5d (studio + findings; V4 §6 bridge) |
| `/org/generator-sites/:id/proposals/:id/review` | `Strategy.ProposalReviewStudioLive` | V5 §3.5a–c · §3.5e (physical + virtual review; Decide counter; M11 · MV4 · MX3) |
| `…/hierarchy` | `Strategy.IppHierarchyLive` (Allocation tab · Rules) | V4 §3.2 · §7 · §7.9 (Where the cascade runs dry) |
| `…/forecast` | `Strategy.IppForecastLive` (Allocation tab · Forecast) | V4 §3.2 · §8 · §8.1.8–§8.1.10 |
| `…/allocations` | `Strategy.IppAllocationsLive` (Allocation tab · Outcomes) | V4 §3.2 · §9 · §9.4 · §9.5.0 · §9.5.5 · §9.6 |
| `…/dashboard` | `Strategy.IppDashboardLive` | V4 §3.2 · §11 · §11.1 (`#ipp-story-verdict` · `#ipp-story-grade`) · §11.2 (`#ipp-story-timeline` · `#ipp-story-timeline-chart` · `#ipp-story-timeline-legend-*` · Why the plan was missed) · §11.4 (`#ipp-story-sources` · `#ipp-story-sources-table` · `data-source-id='certificates'` · `data-source-id='market'`) · §11.5 (`#ipp-story-losses` · `#ipp-story-loss-ledger` · `#ipp-story-heat-chart` · `#ipp-curtailment-exposure`) · §11.6.1 (`#ipp-story-battery` when `BessAsset`) · §11.7 · §11.8 (`#ipp-story-next` · `#ipp-story-actions` · `data-role='next-action'`) · Chapter 5 **Open plant** navigates to `…/plant` (V4 §11 · §12.8.7) · §5.8 · §16 · V4 §10.5 |
| `…/plant` | `Strategy.IppPlantLive` (Plant tab) | V4 §3.2 · §11 · V2 §11 |

### Org — BRP / market

| Route | Module | Manual section |
| --- | --- | --- |
| `/org/brp` | `Strategy.RegistrationLive` `:info` | V8 §5 · V7 §3 |
| `/org/brp/account-cover` | `Strategy.RegistrationLive` `:account_cover` | V8 §6 |
| `/org/brp/bid-desk` | `Strategy.BidDeskLive` `:dashboard` | V10 §12.1–12.2 · V9 §4.20 · §4.21 |
| `/org/brp/bid-desk/submissions` | `Strategy.BidDeskLive` `:submissions` | V8 §12.3 · V7 §4.5 · §4.20 · V4 §17 |
| `/org/brp/bid-desk/idm` | `Strategy.BidDeskLive` `:idm` | V8 §12.4 · V7 §5.11 · §4.20 |
| `/org/brp/bid-desk/forward` | `Strategy.BidDeskLive` `:forward` | V8 §12.5 · V7 §20.8 |
| `/org/brp/bid-desk/settlement` | redirect → Bid Desk › Settlement | V8 §12.6 |
| `/org/brp/bid-desk/month` | redirect → Market Month | V8 §13.2 |
| `/org/brp/after-delivery` | `Strategy.AfterDeliveryLive` `:settlement` | V8 §12.6 |
| `/org/brp/after-delivery/month` | `Strategy.AfterDeliveryLive` `:month` | V8 §13.2 |
| `/org/brp/after-delivery/imbalance` | `Strategy.ImbalanceLive` | V8 §12.7 · V7 §6 |
| `/org/brp/after-delivery/reconciliation` | same LiveView as Month (bookmark) | V8 §13.3 · V7 §8 |
| `/org/brp/notices` | `Strategy.NoticesLive` | V8 §7 · V7 §9 |
| `/org/brp/imbalance` | redirect → Bid Desk › Imbalance | V8 §12.7 |
| `/org/brp/reconciliation` | redirect → Market Month | V8 §13.3 |
| `/org/brp/so` | `Strategy.SoFeedLive` | V8 §8 · V10 §3.2 |
| `/org/brp/registry` | `Strategy.MarketRegistryLive` | V8 §9 · V7 §3.3–§3.6 |
| `/org/brp/standing-data` | `Strategy.StandingDataLive` | V8 §10 · V7 §3.7–§3.8 |
| `/org/brp/api` | `Strategy.ApiLive` (opened from Info `#market-api-link`, not a tab) | V10 §11 · V13 |
| `/org/regional` | `Org.RegionalDeskLive` `:positions` | V16 §6 · V8 §19 |
| `/org/regional/rates` | `Org.RegionalDeskLive` `:rates` | V16 §6 · §3 |
| `/org/regional/exposure` | `Org.RegionalDeskLive` `:exposure` | V16 §6 · §3 |
| `/org/regional/nominations` | `Org.RegionalDeskLive` `:nominations` | V16 §6 · §4 |

### Org — deal lifecycle

| Route | Module | Manual section |
| --- | --- | --- |
| `/org/proposals` | `Org.ProposalsLive` | V5 §3 |
| `/org/negotiations` | `Org.NegotiationsLive` | V5 §4.1 |
| `/org/contracts` | redirect → `/org/deals/contracts` | V7 §6 |
| `/org/contracts/:ppa_id/settlements` | redirect → `/org/deals/contracts/:ppa_id/settlements` | V8 §12.3 |
| `/org/contracts/:ppa_id/settlements/:period_id` | redirect → `/org/deals/contracts/:ppa_id/settlements/:period_id` | V8 §12.6 |
| `/org/deals/contracts` | `Org.ContractsLive` | V7 §6 |
| `/org/deals/contracts/:ppa_id` | `Org.ContractLive` | V8 §12.2 · §12.16–§12.18 · V7 §6.1 |
| `/org/deals/contracts/:ppa_id/settlements` | `Org.ContractSettlementsLive` | V8 §12.3 |
| `/org/deals/contracts/:ppa_id/settlements/:period_id` | `Org.ContractSettlementLive` | V8 §12.6–§12.15 |
| `/org/deals/contracts/:ppa_id/ledger` | `Org.ContractLedgerLive` | V8 §12.4 |
| `/org/deals/contracts/:ppa_id/statements` | `Org.ContractStatementsLive` | V8 §12.5 · §9 |
| `/org/deals/contracts/:ppa_id/forward` | `Org.ContractForwardLive` | V8 §18.1 · V7 §20.8 · V10 §18.1 |
| `/org/agreements/:id` | `Org.AgreementController.show` | V5 §6.3 (written PPA PDF download) |
| `/org/agreements/:id/view` | `Org.AgreementController.view` | V5 §6.3 (inline PDF for contracts preview modal) |
| `GET /org/samples/offtaker-consumption.csv` | `SampleController.offtaker_consumption` | V2 §7 · V2 §3 (template CSV for bulk consumption upload) |

### Dev-only

| Route | Module | Manual section |
| --- | --- | --- |
| `/dev/dashboard` | Phoenix LiveDashboard | n/a — developer tooling, gated on `:dev_routes` |
| `/dev/mailbox` | Swoosh mailbox preview | n/a — developer tooling, gated on `:dev_routes` |

## Matrix 2 — Stateful LiveComponents (37)

| Module | Host screen(s) | Manual section |
| --- | --- | --- |
| `InstrumentStageComponent` | Buyer / seller proposal studios (physical + virtual compose) | V5 §3.4c · §3.4 · §3.4a · §3.4b |
| `ConsumptionModalComponent` | site / meter explorers | V2 §7 |
| `LeafletMapModalComponent` | site / pool wizards, Munix config | V2 §3–§5.7 · V19 §2 |
| `ContractDetailModalComponent` | Contracts register | V5 §6 |
| `AgreementPdfModalComponent` | Contracts → written agreement PDF preview | V5 §6.3 |
| `IppSiteWizardComponent` | *(retired from IPP Sites create & edit)* | n/a — create opens a blank `/org/ipp-sites/onboarding/new`, Edit opens `/org/ipp-sites/onboarding/:id` (V2 §4.3); the six-step field tables stay as reference in V2 §4.2 |
| `GeneratorSiteSearchComponent` | IPP Sites / market search | V2 §4 · V5 §2 |
| `ContractSigningComponent` | Negotiations | V5 §5 |
| `PhysicalPpaWizardComponent` | Negotiations | V5 §4–§4.7 · §4.3 (brief chips / walk-away) · §4.4 (per-step field guide, including Top-up step 11) · V5 §7 |
| `VirtualPpaWizardComponent` | Negotiations | V5 §4–§4.7 · §4.3 (brief chips / walk-away) · §4.5 (per-step field guide) · V5 §8 · §9 |
| `OffTakerSiteWizardComponent` | leftover six-step modal (create and Edit now use onboarding) | V2 §3 · V5 §2.3 |
| `OffTakerSiteSearchComponent` | *(retired from OffTaker Sites directory)* | n/a — buyer-directory market search removed |
| `ProposalDetailComponent` | Proposals | V5 §3 |
| `VirtualProposalDetailComponent` | Proposals (virtual) | V5 §3 |
| `ProposalStudioVirtualComponent` | Buyer / seller proposal studios (virtual) | V5 §3.4b · §9 |
| `TermSuggestionComponent` | Proposal studios (compose + review) | V5 §3.4 · §3.4a · §3.4b · §3.5a |
| `MembershipFormComponent` | Strategy Pools | V6 §5 · V7 §4.4 (Top-up fields) |
| `PoolFormComponent` | Strategy Pools | V6 §5 |
| `RuleListComponent` | Strategy Pools | V6 §5 |
| `TraderPoolSearchComponent` | Trader Pools / market search | V2 §5 · V5 §2 |
| `TraderPoolWizardComponent` | *(retired from Trader Pools Edit)* | n/a — Edit opens `/org/trader-pools/onboarding/:id` |
| `Mo.Bidding.DailyDashboard` | Bidding → Daily | V9 §13.7 |
| `Mo.Bidding.SubmissionsBoard` | Bidding → Submissions | V9 §13.8 · V7 §4.8 |
| `Mo.Bidding.SubmissionDrawer` | Bidding → Submissions drawer | V9 §13.8 · V7 §4.8 · §4.17 |
| `Mo.Bidding.IdmBoard` | Bidding → IDM | V7 §5.10 · V9 §13.9 |
| `Mo.Bidding.IdmAuctionDrawer` | Bidding → IDM stack | V7 §5.4 · §5.10 · V9 §13.9 |
| `Mo.Bidding.SettlementBoard` | Bidding → Settlement | V9 §13.10 · V7 §13 |
| `Mo.Bidding.HourLinesDrawer` | Bidding → Settlement hour | V7 §4.16 · V9 §13.10 · §13.13 |
| `Mo.Bidding.MonthDashboard` | Bidding → Month | V9 §13.11 |
| `Mo.Bidding.ClearDay` | Bidding → Clear | V9 §12 · §13.12 |
| `Org.Strategy.BidDesk.DailyDashboard` (function component) | Bid Desk → Your trading day, hourly evidence and benchmark drawers | V10 §12.1–12.2 · V9 §21 |
| `Org.Strategy.BidDesk.SubmissionsBoard` | Bid Desk → Submissions | V8 §12.3 · V7 §4.5 · §4.20 |
| `Org.Strategy.BidDesk.BookLadder` | Bid Desk → Submissions ladder | V8 §12.3 · V7 §4.2 · §4.5 · V4 §17 |
| `Org.Strategy.BidDesk.IdmBoard` | Bid Desk → IDM | V8 §12.4 · V7 §5.11 |
| `Org.Strategy.BidDesk.SettlementBoard` | Bid Desk → Settlement | V8 §12.6 · V7 §4.12 · §4.20 |
| `Org.Strategy.BidDesk.MonthDashboard` | Market Month (After Delivery) | V8 §13.2 · V7 §4.20 |

Shared **function** components (chart kit, badges/icons, decision cards, IPP
HAF/hierarchy widgets, sim ribbon) remain covered through the LiveViews that
render them — no separate rows, per the CODEX convention.

## Matrix 3 — Background processes

### Oban cron (85 entries, 56 distinct workers)

| Schedule (SAST) | Worker | Manual section |
| --- | --- | --- |
| `45 4 1 * *` (1st) | `Carbon.Workers.CertificateRunner` | V17 §3 · V19 §3.3 |
| `20 7 * * *` (daily) | `Cadt.Workers.SyncRunner` | V17 §22 · V19 §9 |
| `50 7 * * *` (daily) | `Cadt.Workers.PublishRunner` | V17 §22 · V19 §9 |
| `0 6 1 * *` (1st) | `Insight.Workers.FactBuilder` | V16 §4 |
| `0 1 * * *` (daily) | `Settlement.Workers.SettlementRunner` (DAILY) | V19 §3.3 · V6 §11 |
| `0 2 * * 1` (Mondays) | `Settlement.Workers.SettlementRunner` (WEEKLY) | V19 §3.3 · V6 §11 |
| `0 3 1 * *` (1st) | `Settlement.Workers.SettlementRunner` (MONTHLY) | V19 §3.3 · V6 §11 |
| `0 3 1 */3 *` (quarterly) | `Settlement.Workers.SettlementRunner` (QUARTERLY) | V19 §3.3 · V6 §11 |
| `0 4 1 * *` (1st) | `Settlement.Workers.ChainReconciliationRunner` | V19 §3.3 · V6 §10 |
| `10 4 1 * *` (1st) | `Settlement.Workers.LevyConservationRunner` | V19 §3.3 · V6 §4.2.1 · §10 |
| `0 4 4 * *` (4th) | `Strategy.Workers.RunBackcast` — also merges trailing battery contract KPIs | V19 §3.3 · V5 · V4 §20 |
| `30 2 * * *` (daily) | `Brp.Workers.RunNightly` | V19 §3.3 · V7 §1 · §11 |
| `45 2 * * *` (daily) | `Strategy.Workers.RunStochastic` | V3 §22 · V19 §3.3 |
| `15 3 * * *` (daily) | `Strategy.Workers.RunFleet` | V4 §2 · V19 §3.3 |
| `0 5 20 * *` (20th) | `Strategy.Workers.ProposeNominations` | V19 §3.3 · V3 §9 |
| `0 3 * * *` (daily) | `Brp.Workers.SubmitDamBids` | V19 §3.3 · V7 §4 |
| `0 2 * * *` (daily) | `Forecast.Workers.FetchWeatherCycle` | V4 §17 · V19 §3.3 |
| `0 6,9,12,15 * * *` | `Forecast.Workers.FetchWeatherDrift` — replans battery suffixes before IDM | V4 §17.6 · V19 §3.3 · V3 §10.10 |
| `0 12 * * *` (daily) | `Brp.Workers.FetchSchedule` | V19 §3.3 · V7 §4 |
| `30 6 * * *` (daily) | `Brp.Workers.ComputeImbalance` | V19 §3.3 · V7 §6 |
| `0 4,10,16,22 * * *` | `Brp.Workers.IdmMonitor` | V0 §6 · V19 §3.3 · V7 §5 |
| `*/15 * * * *` | `Brp.Workers.PollSo` — applies battery activations/constraints before IDM | V19 §3.3 · V10 §3.2 · V3 §10.10 |
| `5 0 * * *` (daily) | `Brp.Workers.ArchiveSoInstructions` | V19 §3.3 · V10 §3.2 |
| `5 0 * * *` (daily) | `Insight.Workers.MecBreachScan` | V0 §6 · V19 §3.3 |
| `30 12 * * *` (daily) | `Brp.Workers.RefreshPriceForecast` | V19 §3.3 · V7 §9 |
| `0 7 3 * *` (3rd) | `Brp.Workers.ReconcileMonth` | V19 §3.3 · V7 §8 |
| `0 4 5 * *` (5th) | `Forecast.Workers.MonthlyForecastRunner` | V19 §3.3 · V6 |
| `30 3 1 * *` (1st) | `Settlement.Workers.SettlementRunner` (MONTHLY virtual) | V0 §6 · V6 |
| `0 2 1 * *` (1st) | `Settlement.Workers.RefreshSettlementAggregates` | V0 §6 · V13 |
| `30 4 1 * *` (1st) | `Settlement.Workers.StatementCadence` | V0 §6 · V6 §9 |
| `0 5 1 * *` (1st) | `Billing.IssuerRunner` | V13 §7 · V0 §6 |
| `30 5 1 * *` (1st) | `Billing.Workers.OrgBillingRunner` | V13 §11 · V0 §6 |
| `30 6 1 * *` (1st) | `Insight.Workers.Planner` | V16 · V0 §6 |
| `15 5 * * *` (daily) | `Settlement.Workers.DisputeClock` | V0 §6 · V6 |
| `30 5 * * *` (daily) | `Sawem.Workers.ApplicationClock` | V0 §6 · V7 |
| `0 8 * * *` | `Sawem.Workers.DamCadence` `close_dam_gate` | V0 §6 · V9 |
| `1 8 * * *` | `Sawem.Workers.DamCadence` `generate_rolled_over_submissions` | V0 §6 · V9 |
| `5 8 * * *` | `Sawem.Workers.DamCadence` `clear_unconstrained` | V0 §6 · V9 |
| `35 11 * * *` | `Sawem.Workers.DamCadence` `clear_constrained` | V0 §6 · V9 |
| `50 11 * * *` | `Sawem.Workers.DamCadence` `calculate_prices` | V0 §6 · V9 |
| `0 12 * * *` | `Sawem.Workers.DamCadence` `publish_schedule_reports` | V0 §6 · V9 |
| `0 16 * * *` | `Sawem.Workers.IdmCadence` iteration 1 (D−1) | V0 §6 · V9 |
| `0 22 * * *` | `Sawem.Workers.IdmCadence` iteration 2 | V0 §6 · V9 |
| `0 4 * * *` | `Sawem.Workers.IdmCadence` iteration 3 | V0 §6 · V9 |
| `0 10 * * *` | `Sawem.Workers.IdmCadence` iteration 4 | V0 §6 · V9 |
| `0 16 * * *` | `Sawem.Workers.IdmCadence` iteration 5 (D) | V0 §6 · V9 |
| `15 0 * * *` | `Sawem.Workers.DeliveryCadence` `ingest_metering` | V0 §6 · V9 |
| `30 0 * * *` | `Sawem.Workers.DeliveryCadence` `allocate_metering` | V0 §6 · V9 |
| `0/5/10/15/20/25 15 * * *` | `Sawem.Workers.DeliveryCadence` `run_balancing` offsets 1–6 | V0 §6 · V9 |
| `30 15 * * *` | `Sawem.Workers.DeliveryCadence` `run_balancing` offset 7 VERIFIED | V0 §6 · V9 |
| `0 15 * * 1-5` | `Sawem.Workers.SettlementCadence` `issue_indicative_statements` | V0 §6 · V9 |
| `0 10 * * 1-5` | `Sawem.Workers.SettlementCadence` `issue_initial_statements` | V0 §6 · V9 |
| `5 10 * * 1-5` | `Sawem.Workers.SettlementCadence` `issue_invoices` | V0 §6 · V9 |
| `0 6 1 * *` (1st) | `Sawem.Workers.SettlementCadence` `run_timetabled_rerun` | V0 §6 · V9 |
| `0 15 * * 1-5` | `Sawem.Workers.SettlementCadence` `recalculate_credit_cover` | V0 §6 · V9 |
| `0 6 1 9 *` (1 Sep) | `Sawem.Workers.SettlementCadence` `publish_settlement_calendar` | V0 §6 · V9 |
| `0 2 * * *` | `Sawem.Workers.SettlementCadence` `nightly_reassertion` | V0 §6 · V9 |
| `15 22 * * *` | `Sawem.Workers.TrustInterest` | V0 §6 · V9 |
| `45 8 * * 1-5` | `Fx.Workers.FetchRates` primary (10:45 SAST) | V19 §3.3 · V16 §3 |
| `0 12 * * 1-5` | `Fx.Workers.FetchRates` retry (14:00 SAST) | V19 §3.3 · V16 §3 |
| `30 15 * * 1-5` | `Fx.Workers.FetchRates` fallback (17:30 SAST) | V19 §3.3 · V16 §3 |

### Non-cron Oban workers (8)

| Worker | Triggered by | Manual section |
| --- | --- | --- |
| `Settlement.Workers.PipelineRunner` | fanned out per allocation set by `SettlementRunner` | V19 §3.4 · V6 §2 · §10 |
| `Settlement.Workers.StatementRunner` | settlement finalisation | V19 §3.4 · V6 §9 |
| `Forecast.Workers.ForecastRunner` | fanned out per IPP by `MonthlyForecastRunner` | V19 §3.4 · V6 |
| `Forecast.Workers.FetchWeather` | fanned out per site by `FetchWeatherCycle` | V4 §17 · V19 §3.4 |
| `Strategy.Workers.RunAllocation` | strategy runs | V19 §3.4 · V5 |
| `Sim.LiveMeter.IngestReadingWorker` | live-meter ticks | V19 §3.4 · V19 §6 |
| `Setup.Workers.BackfillMeterHistory` | `mix wb.setup` S05 | V19 §3.4 · §5 · V2 §7 |
| `Agreements.Workers.GenerateAgreement` | accept / agreement regenerate | V19 §3.4 · V5 §5.3 |

### AshOban triggers (4)

| Trigger | Cadence | Manual section |
| --- | --- | --- |
| `PhysicalPpa` `:activate` (SIGNED → ACTIVE on start date) | daily | V19 §3.5 · V0 §6.4 · V5 |
| `PhysicalPpa` `:suspend_unsigned` (AGREED past 10-day sign window → SUSPENDED) | daily 01:00 | V19 §3.5 · V0 §6.4 · V5 §5.1 |
| `VirtualPpa` `:suspend_unsigned` (same for virtual AGREED deals) | daily 01:00 | V19 §3.5 · V0 §6.4 · V5 §5.1 |
| `Proposal` `:expire_stale` (PENDING past `expires_at` → EXPIRED; discards `ReviewDraft` via `expire_proposal`) | hourly | V19 §3.5 · V0 §6.4 · V5 §3.2 · V5 §3.5a |

### Proposal resource guards (7)

| Guard | Surface | Manual section |
| --- | --- | --- |
| `expires_at` window (3–90 days, future) on create / send / renew / reactivate | Proposal actions + dispatch footer | V5 §3.2 |
| Originator-slice `TermsDigest` on accept (`reviewed_digest`) | `AcceptAndInitiate` + review studio | V5 §3.2 · §3.5c |
| Accept gating (physical + virtual receiving readiness, dealbreakers, infeasibilities, below-LCOE, digest) | `AcceptAndInitiate.AcceptGating` | V5 §3.5b · §3.5c |
| Public embeds `requested_joint_terms` / `seller_price_card` (no floors on card) | `create_proposal` / `update_terms` + `TermsDigest` | V5 §3.5 |
| Public embed `virtual_strike_ladder` (no floors; keyed by structure) | `create_proposal` / `update_terms` + `TermsDigest` | V5 §3.5 · §4.7 |
| `RequestedJointTerms.vwp_vendor` (virtual) · physical volume fields unused on virtual | `RequestedJointTerms` moduledoc | V5 §3.5 · §4.7 |
| `indicative_*` kept in sync from public embeds | `SyncIndicativeFromPublicEmbeds` | V5 §3.4 · §3.5 |

### Virtual settlement basis & WER credits (MV0) (3)

| Surface | Manual section |
| --- | --- |
| `VirtualSettlementBasis` / `settlement_volume_basis` (no default; Completeness) | V5 §4.7 |
| Per-category `wer_credit` `NetworkRate` rows + logged nil-category fallback | V6 §4.2 |
| `WerCreditRates.assert_coverage!/1` in S09 verifier | V6 §4.2 · V19 §3 |

### Structured decline & re-propose (M12) (5)

| Action / invariant | Surface | Manual section |
| --- | --- | --- |
| `decline_proposal` with `decline_findings` (target never walk-away) | `DeclineFinding` + `DeclineFindings` | V5 §3.5d |
| Disclosure: share dealbreaker / hold negotiable; infeasible always shared | `PublishDeclineFindings` | V5 §3.5d |
| `repropose` clones both slices → DRAFT; source → `SUPERSEDED` | `Proposal.repropose` + `superseded_proposal_id` | V5 §3.1 · §3.5d |
| Proposal status action gates (send/accept/decline/withdraw/renew/reactivate/expire) + initiator/counterparty visibility | `Proposal` lifecycle actions · `list_for_organization` | V5 §3.1 |
| PPA negotiation status action gates (Done/Sign/Re-open/Decline/Activate) + list actions | `PhysicalPpa`/`VirtualPpa` `negotiation_status` · `SigningGate` | V5 §4.1 · §6 |
| ReviewDraft carried forward on decline → re-propose | `Changes.Repropose` | V5 §3.5a · §3.5d |
| Findings checklist + infeasible option restriction in compose studios | `ProposalStudioFindings` | V5 §3.5d |

### Virtual compose readiness (MV3) (3)

| Surface | Manual section |
| --- | --- |
| `ProposalStudioVirtualComponent` (four-stage virtual studio + send) | V5 §3.4b · §9 |
| `VirtualBuyerReadiness` / `VirtualSellerReadiness` (send gate + studio checklist) | V5 §3.4.1 · §3.4b |
| Low matched-fraction acknowledgement (`DispatchReadiness` / studio ack) | V5 §3.4.1 |

### Virtual review studio (MV4) (4)

| Surface | Manual section |
| --- | --- |
| `ProposalReviewStudioStages` / Live — five virtual stages + gap after offer | V5 §3.5a · §9 |
| Stage 1 CP schedule risk + strike ladder (seller-originated) | V5 §3.5a |
| Autosave → `ReviewDraft.staged_terms` only (never `virtual_terms`) | V5 §3.5a |
| Digest guard through UI (mid-flight originator strike change) | V5 §3.5c |

### Virtual accept & carry-through (MV5) (5)

| Surface | Manual section |
| --- | --- |
| Virtual `AcceptGating` (receiving readiness · dealbreakers · four infeasibilities · below-LCOE · digest) | V5 §3.5b · §3.5c |
| `link_ppa` stamps `virtual_ppa_id` on both parties' current briefs | V5 §3 · §4.3 |
| Virtual wizard owner-only chips + walk-away confirm + buyer GENERATED low-match warning | V5 §4.3 · §4.7 |
| Gap Rand figures display volume basis beside the saving | V5 §3.5a · §10 |
| Two-direction virtual protocol E2E + adversarial digest path | V5 §3 · §9 (behaviour; test-only) |

### NegotiationBrief resource (M1 / M13 / MV0) (8)

Private, versioned per-org brief. Manual: V5 §3.5 (compose) · V5 §4.3
(wizard chips after accept).

| Action / invariant | Surface | Manual section |
| --- | --- | --- |
| `create_brief` (v1) | `WB.Wheeling.create_negotiation_brief` | V5 §3.5 |
| `regenerate` (v n+1, carry `accepted_value`) | `WB.Wheeling.regenerate_negotiation_brief` | V5 §3.5 |
| `accept_position` / `adjust_position` | domain updates | V5 §3.5 |
| `link_ppa` stamps `physical_ppa_id` (M13) or `virtual_ppa_id` (MV5) | `AcceptAndInitiate` + domain update | V5 §3 · §4.3 |
| `belongs_to :virtual_ppa` beside `:physical_ppa` (exactly one set) | `NegotiationBrief` | V5 §3 · §4.3 |
| Owner-only target / walk-away chips + walk-away confirm (+ virtual GENERATED warn) | `PhysicalPpaWizardComponent` / `VirtualPpaWizardComponent` | V5 §4.3 |
| Org-scoped reads only (`organization_id` required) | `for_organization` / `current_for_organization` | V5 §3.5 |
| Never load `:negotiation_briefs` on shared proposal queries | `Proposal` moduledoc + web grep test | V5 §3.5 |

### Instrument availability & promote (MX0) + MX3 surfaces (8)

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Proposal.InstrumentAvailability` (physical + virtual, three states) | V5 §1.1 · V5 §2 |
| Search PPA buttons (`InstrumentAvailabilityButtons` on three search components) | V5 §1.1 · V5 §2 |
| `Promote` / `PromoteWithBrief` pool → instrument + refuse when unavailable | V3 §8.6 · §13.2 · V5 §1.1 |
| Proposal detail alternative-instrument note → review Gaps pointer (no Rand) | V5 §1.1 · V5 §3 |
| Review Gaps instrument comparison header (`InstrumentGapHeader`) | V5 §3.5a · §3.5b |
| Instrument money + estimate honesty (`data-volume-basis` · disclosed-terms-only) | V5 §3.4c · §3.5b |
| `:carried_from_counter` suggestion provenance (Accept / Adjust / Why?) | V5 §3.5a · §3.5e |
| `WB.Wheeling.Proposal.SwitchInstrumentDraft` (draft switch; never sets `superseded_proposal_id`) | V5 §3.4c |

### Cross-instrument counter & lineage (MX1) (6)

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Proposal.CounterWithInstrument` (compose + atomic dispatch; inherits `renewed_count`) | V5 §3.5e |
| `CrossTypeCarryOver` + `:wrong_instrument` findings / disclosure | V5 §3.5e · §3.5d |
| `counter_affordance/3` + **Counter with…** (review *Decide* only; modal lineage only) | V5 §3.5c · §3.5e · §3.6 |
| Lineage banner (replaces / replaced-by) + SUPERSEDED list link forward | V5 §3.1 · §3.5e |
| `superseded_proposal_id` / `cross_instrument?` | V5 §3.1 · §3.5e |
| Digest guard binds to the counter's own originator digest | V5 §3.5c · §3.5e |

### Cross-instrument comparison engine (MX2) (4)

Pure engine — no route, LiveView or LiveComponent. Manual chapters that teach
the comparison land with MX3.

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Proposal.InstrumentComparison` (verdicts, bridge, estimate, materiality) | n/a — no user surface (MX3) |
| Brief evidence `"instrument_comparison"` + dual-digest staleness | n/a — private brief only (MX3) |
| `finding_from_comparison/2` → `:wrong_instrument` (target, never walk_away) | V5 §3.5e · §3.5d (finding shape; MX3 wires UI) |
| Nothing from the comparison on the `Proposal` row | V5 §3.5 (privacy) |

### GapAnalysis (M10)

Pure per-field offer vs brief verdicts (+ Rand on price gaps; HardConstraints /
below-LCOE as infeasibilities). Manual: V5 §3.5b.

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Proposal.GapAnalysis.analyze/3` · `accept_allowed?/2` | V5 §3.5b |
| Persisted on `ReviewDraft.gap_analysis` | V5 §3.5a · §3.5b |

### ReviewDraft resource (M9) (7)

Private counterparty working copy while a proposal is PENDING. Staging never
touches public `physical_terms` / `virtual_terms` until accept-merge or
explicit share. Manual: V5 §3.5a.

| Action / invariant | Surface | Manual section |
| --- | --- | --- |
| `create_draft` / `stage_terms` | `WB.Wheeling.create_review_draft` / `stage_review_draft_terms` | V5 §3.5a |
| `share_terms` (publish without accepting; clear draft) | `WB.Wheeling.share_review_draft_terms` | V5 §3.5a |
| Accept merges `staged_terms` inside `AcceptAndInitiate` txn | `MergeStagedTerms` | V5 §3.5a |
| `expire_proposal` discards drafts (AshOban `:expire_stale` + sweep + manual) | `DiscardReviewDrafts` on `expire_proposal` | V5 §3.5a · V5 §3.2 |
| Decline **preserves** the draft (M12 re-propose) | `decline_proposal` (no discard) | V5 §3.5a |
| Org-scoped reads only; never load `:review_drafts` on shared queries | `for_organization` + `Proposal` moduledoc | V5 §3.5a |
| Near-expiry warning (≤20% of validity window remaining) | `Proposal.ExpiryWindow` | V5 §3.2 · V5 §3.5a |

### Long-running processes (3) and mix tasks (7)

| Process / task | What | Manual section |
| --- | --- | --- |
| `WB.Sim.LiveMeter.Simulator` | GenServer, 20-min simulated readings (sim-gated) | V19 §3.6 · V19 §6 |
| `WB.Sim.Driver` | sim-clock scenario driver (callable module) | V12 §11 · V19 §3.6 |
| `WB.Sim.Market.DamSubmitter` | GenServer, daily §9.3 DAM for the S16 overlay (dev-only) | V19 §3.6 · §4.7 |
| `mix wb.setup` | consolidated demo/acceptance data setup (`--include-market`) | V19 §3.7 · §4 · §4.7 |
| `mix setup.market` | foundation plus fifty simulated SAWEM participants | V19 §3.7 · §4.7 |
| `mix setup.market_month` | a whole Billing Period traded through the S16 overlay (S17) | V19 §3.7 · §4.8 |
| `mix setup` / `mix setup.demo` | foundation / full_demo wrappers; `--include-market` appends S16 | V19 §3.7 · §4.7 |
| `mix wb.setup.roll_forward` | rebuild sealed demo onto today's clock | V19 §3.7 · V19 §4 · V12 §13 |
| `mix wb.forecast_backfill` | historical forecast backfill | V19 §3.7 · V6 |
| `mix wb.replay_hierarchy_diff` | hierarchy settlement replay/diff | V19 §3.7 · V6 · V4 |

### Monthly billing VAT engine (MB0)

Pure money-truth — no new route. The existing ledger VAT column and
statement mini-stat now show computed cents.

| Surface | Manual section |
| --- | --- |
| `WB.Billing.VatRate` `:create` / `:lookup` / `:read` | V13 §3 |
| Seeded ZA standard rate (15% from 2018-04-01, S02 reference pack) | V13 §3 |
| `WB.Billing.VatTreatment` (20 categories + B1/B2 interim constants) | V13 §3 |
| `Ledger.line/8` computes `vat_cents` (signed, Σ = 0) | V13 §3 · V6 §8 |
| `StatementRenderer` resolved rate (or “rate not configured”) | V13 §3 · V6 §9 |
| Running ledger VAT column + CSV `vat_cents` parity | V6 §12.4 · V13 §3 |

### Monthly billing issuer registry (MB1)

Identity and numbering — no new route. Documents are not issued yet.

| Surface | Manual section |
| --- | --- |
| `WB.Billing.LegalEntity` (VAT form, vendor contradiction, invoicing-map parse) | V13 §4 |
| `WB.Billing.Issuer` `:create` / `:read` / `:update` / `:activate` / `for_backing` | V13 §4 |
| `Issuer.issuable_categories/1` (§3.1 matrix + refusals) | V13 §4 |
| `WB.Billing.InvoiceSeries` `:create` / `:read` / `take!/2` | V13 §5 |
| `WB.Billing.SeriesAllocation` `:create` / `:read` | V13 §5 |
| `WB.Billing.BillingElection` `:create` / `:read` / `:close` | V13 §4 |
| Seeded issuers (S02 + S03) — Eskom, municipalities, MO, sites, pools | V13 §4 |
| Ledger `party_id` for Eskom / municipality / VWP vendor | V13 §4 · V6 §8 |

### Monthly billing artifacts (MB2)

| Surface | Manual section |
| --- | --- |
| `WB.Billing.Artifact` `:create` / `:read` / `put/3` | V13 §5 |
| `WB.Billing.DocumentRenderer` (HTML source of truth + deterministic PDF) | V13 §5 |
| `StatementRunner` stores the issued PDF (no longer `nil`) | V13 §5 · V6 §9 |
| Printable `.statement-document` + `@media print` | V13 §5 · V6 §9 |
| `WB.WheelingWeb.Billing.BillingDocumentModal` | V13 §5 · V6 §9 |
| Mounted HTML statement `#statement-document` | V13 §5 · V6 §9 |

### Monthly billing run (MB3)

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Settlement.Workers.RefreshSettlementAggregates` (`0 2 1 * *`) | V13 §6 |
| `WB.Billing.Preflight` | V13 §6 |
| `WB.Wheeling.Settlement.Workers.StatementCadence` (`30 4 1 * *`) | V13 §6 |
| `WB.Billing.FinalisePolicy` (B7, 5 business days) | V13 §6 · V6 §11 |
| `WB.Billing.BillingRun` `:record` / `:read` | V13 §6 |
| `NextRun` derives from the Oban crontab | V13 §6 |

### Monthly billing document model (MB4)

The document and the two lenses. The issuer run (MB5) now builds them
from ledger pairs.

| Surface | Manual section |
| --- | --- |
| `WB.Billing.BillingDocument` `:create` / `:read` / `:update` / `:issue` / `:void` / `:acknowledge` | V13 §2 · §9 |
| `WB.Billing.DocumentLine` `:create` / `:read` (provenance required) | V13 §2 |
| `WB.Billing.SupplyCoverage` (one tax invoice per supply) | V13 §2 · §4 |
| `WB.Billing.Registers.issued/2` · `received/2` | V13 §9 |
| `BillingDocument.correction_path/2` (`:version` / `:note`) | V13 §2 |
| `WB.Billing.Money` (Decimal → integer cents at the boundary) | V13 §3 · §2 |

### Monthly billing utility identities (MU0)

Identity and transition — no utility document yet (MU1).

| Surface | Manual section |
| --- | --- |
| `WB.Billing.UtilityRouting.issuer_for/2` | V13 §4 |
| `WB.Billing.Reconstruction` (not a BillingDocument) | V13 §4 |
| `Issuer.transition_mode` / `issuing_from` / `leave_transition` | V13 §4 |
| Supply-only BRP (generating units only, §3.5) | V13 §4 |
| Preflight `:supply_only_demand` | V13 §4 · §6 |

### Monthly billing issuer run (MB5)

| Surface | Manual section |
| --- | --- |
| `WB.Billing.IssuerRunner` (`0 5 1 * *`) | V13 §7 · §8 · §9 |
| `WB.Billing.IssuerAdapter` / `Adapters.GeneratorSite` / `Adapters.TraderSupply` | V13 §7 |
| `WB.Billing.PairGrouping` (ordered pair, not unordered) | V13 §7 |
| `WB.Billing.ChargeGrouping` / `BillGroup` | V13 §7 |
| `WB.Billing.DueDate` (PPA terms; B12 weekend left as-is) | V13 §8 · V5 §4.5.8 |
| `BillingElection.grain` (PER_ENTITY / ORG_CONSOLIDATED) | V13 §8 · §4 |

### Monthly billing utility documents (MU1)

| Surface | Manual section |
| --- | --- |
| `/operator` · `RegisterLive` · `DocumentLive` · `ReceivedLive` · `RunLive` · `SettingsLive` | V13 §9 · §13 · V19 §1.6 |
| Eskom / Munix sidebar **Billing** (`POST /operator/session`) | V13 §9 · V19 §1.6 · §2.4 |
| `/mo/billing` · `BillingLive` · `BillingDocumentLive` | V13 §14 · V9 §19 |
| `WB.Billing.Adapters.Eskom` / `Adapters.Municipality` | V13 §13 |
| `WB.Billing.WheelingBearer` (`:SHARED` / `:PASS_THROUGH` / `:FIXED`) | V13 §13 |
| `WB.Billing.ResidualPricing` (informational vs full-bill; B13) | V13 §13 |

### Monthly billing TU/BG documents (MM1)

| Surface | Manual section |
| --- | --- |
| `WB.Billing.Adapters.MarketOperator` (TU + BG grain) | V13 §14 |
| `WB.Billing.PaymentRouting` / `PaymentDestination` ([D-16]) | V13 §14 |
| `WB.Billing.InvoiceSplit` (B6, default off) | V13 §14 |

### Monthly billing org receiver (MB6)

| Surface | Manual section |
| --- | --- |
| `/org/billing` · `BillingLive` | V15 §10 · §12 · §16 · §18 · §22 |
| `WB.Billing.Workers.OrgBillingRunner` (`30 5 1 * *`) | V13 §11 · §12 · §14 |
| `WB.Billing.OrgStatement` (`create` / `read`) | V13 §11 · §12 · §14 |
| `WB.Billing.SiteView` / `WheelingRollup` / `ReceivedRecords` | V13 §10 · §11 |
| Org sidebar **Billing** (beside Accounts) | V13 §12 |

### Monthly billing party documents (MM2)

| Surface | Manual section |
| --- | --- |
| `WB.Billing.PartyDocument` (tax invoice / self-billing) | V13 §14 |
| `WB.Billing.DebitNoteIssue` (cascade + excess disclosure) | V13 §14 |
| `WB.Billing.SupplyOnlyBoundary` (B10 flag, preflight bypass) | V13 §14 |
| `WB.Billing.MarketRollup` (level 3) | V13 §12 · §14 |
| `WB.Billing.MasterStatement` (level 5, FINAL) | V13 §12 |
| `WB.Billing.RerunNotes` (§15.5(3) fan-out) | V13 §17 |
| `/org/billing` Overview · Market source filter | V15 §12 · §14 |

### Monthly billing payments, delivery, close (MZ0–MZ2)

| Surface | Manual section |
| --- | --- |
| `WB.Billing.Payment` / `PaymentAllocation` | V13 §16 |
| `WB.Billing.Aging` (derived overdue) | V13 §16 |
| `WB.Billing.Remittance` / `Matching` | V13 §16 |
| `/org/billing` Documents · payments · drawer · aging | V15 §16 |
| `WB.Billing.Deliver` / `DeliveryPreference` (opt-in) | V13 §10 · §18 |
| `GET /api/v1/billing/documents` · registers | V13 §18 · V11 |
| `GET /org/billing/month-pack.zip` | V15 §18 |
| `/org/billing` Overview · comparisons | V15 §18 |
| `WB.Billing.MonthlyClose` / `Audit` / `mix billing.audit` | V13 §19 |

### Money Room fact base (MA1)

No new route. The P&L fact resource later panels will read. Provenance
is mandatory; amounts are ex-VAT.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.PnlFact` `:create` | V16 §3 |
| `WB.Insight.for_org_month` | V16 §3 |
| `WB.Insight.for_org_period` | V16 §3 |
| `WB.Insight.by_stream` | V16 §3 |

### Money Room fact builder (MA2)

No new route. The monthly fact build, backfill, and operator-only residue.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Workers.FactBuilder` (`0 6 1 * *`) | V16 §4 |
| `WB.Insight.Backfill` | V16 §4 |
| `WB.Insight.BuildDiscrepancy` | V16 §4 |

### Money Room loss facts (MA3)

No new route. The Loss Ledger resource and the four valuers whose
baselines the contract or the Market Code already states. Losses are
never added to the P&L.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.LossFact` `:create` | V16 §5 |
| `WB.Insight.loss_facts_for_org_month` | V16 §5 |
| `WB.Insight.LossValuer` (`value_month/3`, `run/1`) | V16 §5 |
| `WB.Insight.LossValuer.SpillForfeited` | V16 §5 |
| `WB.Insight.LossValuer.SpillDiscountGap` | V16 §5 |
| `WB.Insight.LossValuer.ImbalanceUninstructed` | V16 §5 |
| `WB.Insight.LossValuer.BankingExpired` | V16 §5 |

### Money Room remaining loss kinds (ML1)

No new route. Six further Loss Ledger valuers against the signed-interim
baselines. `residual_premium` stays unbuilt (A1 still interim / not
countersigned).

| Surface | Manual section |
| --- | --- |
| `WB.Insight.LossValuer.CurtailmentUnrecovered` | V16 §5 |
| `WB.Insight.LossValuer.ShortfallPenalties` | V16 §5 |
| `WB.Insight.LossValuer.MarketGateMisses` | V16 §5 |
| `WB.Insight.LossValuer.BessCeiling` | V16 §5 |
| `WB.Insight.LossValuer.EscalationDrift` | V16 §5 |
| `WB.Insight.LossValuer.MarketUncaptured` | V16 §5 |
| `WB.Strategy.Bess.Dispatcher` (`reason: :cycle_cap`) | V16 §5 |

### Money Room double-counting guard (ML2)

No new route. Overlap rules across live loss kinds, conservation of
lost energy against the month's envelope, and Panel 4 complete for
every live kind (baseline, confidence, lineage hook). `residual_premium`
stays unbuilt (A1).

| Surface | Manual section |
| --- | --- |
| `WB.Insight.LossValuer.Overlap` | V16 §5 |
| `WB.Insight.LossValuer.Conservation` | V16 §5 |
| `/org/money` Panel 4 (all live kinds · lineage attributes) | V16 §5 · §7 · V4 §10.5 (IPP: no Annual exposure tile) |

### Money Room reconciliation (MA4)

No new route. The gate that refuses a month whose invoiced facts do
not equal the billing registers and rollups to the cent, and the
operator-only build report that names residue and variance separately.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Reconciliation.check/2` | V16 §6 |
| `WB.Insight.Workers.FactBuilder` (assemble → reconcile → publish or refuse) | V16 §6 |
| `WB.Insight.BuildDiscrepancy` `:reconciliation_variance` | V16 §6 |
| `WB.Insight.BuildReport.for_run/1` | V16 §6 |

### Money Room panels 1–2 (MQ1)

The verdict (two lenses) and the waterfall on `/org/money`. No new
route. `waterfall_chart/1` is a function component on the existing
chart kit, covered through this host screen.

| Surface | Manual section |
| --- | --- |
| `/org/money` Panel 1 verdict (cash + better-off) | V16 §7 |
| `/org/money` Panel 2 waterfall | V16 §7 |
| `WB.Insight.BetterOff.for_org_period/2` | V16 §7 |
| `WB.Insight.BetterOffFact` | V16 §7 |
| `WB.WheelingWeb.StrategyChartComponents.waterfall_chart/1` | V16 §7 |

### Money Room panels 3–5 (MQ2)

Document-grade owed/owing, the Loss Ledger, and deterministic findings
on `/org/money`. No new route.

| Surface | Manual section |
| --- | --- |
| `/org/money` Panel 3 owed/owing (gross) | V16 §7 |
| `/org/money` Panel 4 Loss Ledger | V16 §5 · §7 |
| `/org/money` Panel 5 deterministic findings | V16 §7 · §5 |

### Money Room lineage and exports (MQ3)

Lineage drawer on `/org/money` (no new LiveView). P&L statement PDF
and fact CSV as controller GETs. V16 chapter 09.

| Surface | Manual section |
| --- | --- |
| `/org/money` lineage drawer (`#money-lineage-drawer`) | V16 §7 · §6 |
| `GET /org/money.pdf` P&L statement | V16 §9 |
| `GET /org/money.csv` fact CSV | V16 §9 |
| `WB.Insight.PnlStatement` | V16 §9 |

### Money Room ontology (MK1)

The advisor-visible dictionary, generated from Ash domains. Drift
between code and the committed artefact is a test failure.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Ontology` / `priv/insight/ontology.json` | V16 §8 |
| Curated glossary (WER, deemed energy, pots, bill groups, spill, instructed) | V16 §8 |

### Money Room map and retrieval (MK2)

No new route. Typed, org-month-scoped edges written as a by-product
of the fact build, and the bounded walk that hands the advisor a
small relevant neighbourhood rather than a warehouse.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.GraphEdge` `:create` | V16 §10 |
| `WB.Insight.Graph.write_for_build/4` | V16 §10 |
| `WB.Insight.Graph.neighbourhood/2` | V16 §10 |
| Predicate enum (`bills`, `of`, `between`, `belongs_to`, `evidenced_by`, `covers`) | V16 §10 |

### Money Room model and data boundary (MK0)

Spike R3. No new route. The allow-list, the LLM seam (Mock default in
every environment), and the signed-interim legal memo. No real model
call. Ask the Money Room is taught in the MK3 section below.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.PromptAllowlist` | V16 §11 |
| `WB.Insight.Llm` / `Llm.Mock` | V16 §11 |
| `WB.Insight.Llm.Anthropic` (unreachable unless config + org flag) | V16 §11 |
| `WB.Insight.Llm.Benchmark` (golden vectors / fixtures only) | n/a — engineering spike, not a user-facing surface |
| Data-boundary memo | n/a — legal memo, not a user-facing surface |

### Money Room Ask (MK3)

The question box on `/org/money`. Parse → retrieve → compose → cite.
Seven read-only tools. `QuestionLog`. Per-org flag, off by default.
No real model call.

| Surface | Manual section |
| --- | --- |
| `/org/money` `#money-ask` (flag on) | V16 §11 |
| `WB.Insight.Tools` (seven read-only tools) | V16 §11 |
| `WB.Insight.Ask` / citation post-check | V16 §11 |
| `WB.Insight.QuestionLog` | V16 §11 |
| `WB.WheelingWeb.Org.Money.Ask` | V16 §11 |

### Money Room eval harness and pilot (MK4)

The question/answer evaluation set over the simulated month, four
measured dimensions, CI on the mock, a gated real-model runner, and
pilot enablement via the existing per-org flag (off by default).

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Eval` / `priv/insight/eval_thresholds.json` | V16 §11 |
| Mock CI harness (`mix test test/wb/insight/eval_test.exs`) | V16 §11 |
| Real-model runner (refuses unless config **and** org flag) | V16 §11 |
| Pilot flag `WB.Insight.Llm.put_org_enabled/2` (exactly one org) | V16 §11 |
| INTERIM verbatim Mock answers | n/a — engineering/pilot record, not a user-facing surface |

### Money Room detector library (MV1)

Eight pure finding detectors, carbon aggregation, and dismissal
storage. No new route.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Detectors` (eight class modules) | V16 §12 |
| `WB.Carbon.Detectors` aggregated (REC dormancy, not duplicated) | V16 §12 |
| `WB.Insight.FindingDismissal` | V16 §12 |
| `WB.Insight.Detectors.take_face/2` (noise floor helper) | V16 §12 |
| `/org/money` Panel 5 deterministic findings (AI off) | V16 §7 · §5 |

### Improvement Planner lifecycle (MV2)

`WB.Insight.Recommendation` with a guarded lifecycle, AI composition
through the MK3 Mock seam, Panel 5 live for opted-in organisations,
MQ2 fallback when the flag is off. A recommendation is advice — the
platform never acts on one by itself. Measured outcomes and monthly
delivery landed in MV3. Phase P5 is delivered-with-caveat.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Recommendation` (guarded named actions) | V16 §12 |
| `WB.Insight.Composer` (value pinned byte for byte) | V16 §12 |
| `WB.Insight.Planner` (per-org flag, off by default) | V16 §12 |
| `WB.Insight.Workers.Planner` (`30 6 1 * *`, opted-in only) | V16 §12 · §4 |
| `/org/money` Panel 5 AI-drafted recs (flag on) | V16 §7 · §12 |
| `/org/money` Panel 5 MQ2 fallback (flag off) | V16 §7 · §5 |

### Measured outcomes and monthly delivery (MV3)

The same detector that found the problem measures the later month.
Realised sits beside estimated. Unmeasurable is first-class. Delivery
is opt-in through existing `DeliveryPreference`; no findings, no email.
Phase P5 is delivered-with-caveat: business countersignature of R5 / ML0
remains open. `VERSION` is not bumped.

| Surface | Manual section |
| --- | --- |
| `WB.Insight.Measurer` (`Detectors.run_class/2`) | V16 §12 |
| `WB.Insight.Recommendation` `:measure` / `:mark_unmeasurable` | V16 §12 |
| `WB.Insight.MethodAccuracy` (per method_version MAPE / bias) | V16 §12 |
| `/org/money` Panel 5 estimated vs realised (`#money-recommendation-{id}-estimated` / `-realised`) | V16 §7 · §12 |
| `WB.Insight.AdviceStatement` (DeterministicPdf) | V16 §12 · §9 |
| `WB.Insight.Delivery` (opt-in, `DeliveryPreference`, no empty email) | V16 §12 · §4 |
| `WB.Insight.Workers.Planner` measurement + delivery pass | V16 §12 · §4 |

### Runtime MEC enforcement (G9 / Stage 1)

No new route. A ninth detector class watches metered export against the
effective-dated connection-agreement limit. The badge lives on the existing
IPP sites directory; the card lives on existing Money Room Panel 5. An
`:excess_charge` pair posts only when a penalty rate is on file.

| Surface | Manual section |
| --- | --- |
| `/org/ipp-sites/sites` `#ipp-site-mec-breach-{id}` | V4 §16 |
| `/org/ipp-sites/sites` `#ipp-site-activate-{id}` / `#ipp-site-deactivate-{id}` | V2 §4.8 |
| `/org/money` Panel 5 `:mec_breach` card | V4 §16 · V16 §7 |
| `WB.Insight.Detectors.MecBreach` | V4 §16 · V16 §12 |
| `WB.Wheeling.GeneratorSite.MecLimit` | V4 §16 |
| `WB.Insight.Workers.MecBreachScan` (`5 0 * * *`) | V4 §16 |

### Market phase register and matrix (G13 / Stage 3)

No new route. `market_phase` is a twenty-fifth `MarketRules` row; the phase
× capability matrix is a separate resource with identity
`{market_phase, capability, party_class, effective_from}`. Guards and labels
read `WB.Sawem.Phase`. Live count is unchanged (124).

| Surface | Manual section |
| --- | --- |
| `WB.Sawem.PhaseCapability` `:publish` / `:current` / `:read` / `:destroy` | V7 §18 |
| `WB.Sawem.Phase` (`current/1`, `enabled?/2`, `threshold_mw/2`, `refusal/2`, `matrix/1`) | V7 §18 |
| `/mo/rules` `#market-phase-section` (read-only matrix; phase publishes with the register form) | V7 §18 |
| `/` `#market-phase-badge` | V7 §18 |
| `/demo` `#market-phase-line` | V19 §8.7 |
| `WB.Sawem.Registry.ApplicationFlow` `:CONTRACTS_ISSUED` phase precondition | V7 §18.2 |
| `SUB_PHASE_INELIGIBLE` / `SUB_BELOW_WHOLESALE_FLOOR` | V7 §18.3 |

### Market-indexed CfDs (G2 / Stage 4)

No new route. `floating_leg` / `index_source` extend `VirtualPpa` and the
existing Tariff & Pricing wizard step. Settlement detail and statements
read a stored `index_snapshot`. Live count is unchanged (124).

| Surface | Manual section |
| --- | --- |
| `WB.Wheeling.Settlement.MarketIndex` (published SMP reader) | V6 §7.4 |
| `VirtualPpa.floating_leg` / `index_source` (`ESKOM_CREDIT_RATE` default) | V5 §9.6.1 · V6 §7.4 |
| Virtual wizard step 4 `#floating-leg-section` / `#floating-leg-explainer` | V5 §9.6.1 · V5 §4.5 |
| `ContractSettlementLive` `#settlement-index-ref` | V8 §12.8 |
| Statement energy block — index label + realised average | V6 §9.11 |
| Ledger market-indexed branch (no `wer_credit`) | V6 §8.7 |
| Demo `V3 market-indexed VWA` (`:SAWEM_TOU_AVG`, settled in S09) | V5 §8.10 |

### Monthly billing virtual parity (MP0)

| Surface | Manual section |
| --- | --- |
| `SettlementRunner` virtual cron `30 3 1 * *` | V13 §15 · V6 §14 |
| `StatementRunner` virtual PPA reference | V13 §15 |
| `WB.Billing.Adapters.Virtual` (CfD, vendor fee, WER reference) | V13 §15 |
| `WB.Billing.VirtualIntervals` (no interval rows) | V13 §15 |

### Monthly billing SAWEM persistence (MM0)

The §15 engine now persists its output. Cadence without data is a miss.

| Surface | Manual section |
| --- | --- |
| `WB.Sawem.Mo.SettlementStatement` `:create` / `:issue` / `:read` | V13 §14 · V9 §18.4 |
| `WB.Sawem.Mo.Settlement.Source` | V13 §14 |
| `WB.Sawem.MarketInvoice` `:issue` (`:DEBIT_NOTE`, `tax_number`, `artifact_id`) | V13 §14 |
| `MarketInvoice.from_calculated/3` (caller: `Cycles.issue_invoices/2`) | V13 §14 |
| `WB.Sawem.Mo.Billing.Snapshot` (document ids, not stubs) | V9 §18.4 · V13 §14 |
| Cadence miss `:no_parties` / `:no_statements` / `:settlement_calendar_not_published` | V13 §14 · V9 §14 |

### API security (G7 / Stage 7)

OAuth2 client-credentials tokens, optional mTLS pinning, optional HMAC
request signing, and the MO Credentials tab. Live count was 125 at
stage 7; stage 8 moved it to 126.

| Surface | Manual section |
| --- | --- |
| `POST /api/v1/oauth/token` | V11 §2.1 |
| `/mo/participants/credentials` `#mo-participants-credentials` | V9 §21.12 |
| Registrations **API auth** `#mo-brp-registration-{id}-adoption` | V9 §21.4 |
| Dedicated API listener (`WB.WheelingWeb.ApiEndpoint`) | V11 §2.1 · V11 §14 |
| Sandbox published `oauth` vectors + conformance test 21 | V11 §15.8 |

### Virtual Wheeling (G1 / Stage 8)

One registered account, many meters, 15-minute allocation, one
consolidated 45-day refund. Live count is 126.

| Surface | Manual section |
| --- | --- |
| `/org/virtual-wheeling` `#org-virtual-wheeling-page` | V17 §8 (`#vw-account` · `#vw-month` · `#vw-arithmetic` · `#vw-excess` · `#vw-fill-order` · `#vw-members` · `#vw-cite`) |
| `/eskom/virtual-contracts` `#eskom-vwa-register` | V17 §9 |
| `VwAccount` / `VwPortfolioMembership` | V17 §2 · §3 |
| `VwAllocation` hypertable + greedy allocator | V17 §5 |
| `Wer.for_portfolio/1` + `VwRefundStatement` | V17 §4 · §6 · §7 |
| Demo portfolio (S09 enrol, S10 issue) | V17 §10 · V19 §4 |

### Vesting contracts (G3 / Stage 9)

A regulator-imposed two-way CfD on a legacy generation asset. Two new
tab routes on existing LiveViews. Live count is 128.

| Surface | Manual section |
| --- | --- |
| `/mo/participants/vesting` `#mo-participants-vesting` | V9 §21.13 |
| `/org/market-accounts/vesting` `#org-market-accounts-vesting` | V8 §14.11 · V7 §19 |
| `WB.Sawem.VestingContract` / `VestingScheduleRow` | V7 §19 |
| `:VESTING_GENERATOR` / `:VESTING_COUNTERPARTY` settlement lines | V7 §19 |
| Demo vested unit `BRP-SIM-IPP-12` + CPA `BRP-CPA-SAWEM` | V7 §19 · V9 §21.13 |

### Forward Physical Market (G6 / Stage 11)

The register, residual clearing, remaining transfer capability, the
participant API, and the two consoles. Live count is 137 (Stage 12
exposure plus four Regional desk routes from Stage 13).

| Surface | Manual section |
| --- | --- |
| `WB.Sawem.ForwardSchedule` / `ForwardScheduleProfile` | V7 §20 |
| `WB.Sawem.Forward.Gate` + `forward_registration_lead_seconds` | V7 §20.3 |
| `:forward_registration` phase-capability guard | V7 §18 · §20.3 |
| `Model.forward_schedules` + C1/C17 constants (`MO-SPEC-4.3/2026-08`) | V9 §6.8 · V7 §20.5 |
| Contracted Energy = residual DAM/IDM + forward base | V7 §5.7 · V7 §6.1 · V7 §20.5 |
| `NetworkModel.remaining_transfer_capability/2` | V7 §20.6 · V9 §27.2 |
| `GET/POST/PUT /api/v1/registry/forward/…` (four operations) | V7 §20.7 |
| `/mo/forward` `#mo-forward-register` | V9 §27.1 |
| `/mo/forward/capacity` `#mo-forward-capacity` | V9 §27.2 |
| `/org/deals/contracts/:ppa_id/forward` `#contract-forward` | V8 §18.1 |
| `/org/brp/bid-desk/forward` `#bid-desk-forward` | V8 §18.2 |
| Demo S08 two largest ACTIVE agreements registered forward | V7 §20 · V19 |

### Certificate portfolio (CT0)

No new route. The Portfolio tab patches `?tab=portfolio` on
`/org/certificates`. Live count is unchanged.

| Surface | Manual section |
| --- | --- |
| Portfolio tab (`/org/certificates?tab=portfolio`) | V17 §19 |
| `WB.Carbon.Portfolio` | V17 §19 |
| `WB.Carbon.CertificateLot` | V17 §19 |
| `WB.Carbon.CertificateMovement` | V17 §19 |
| Certificate `SupplyCoverage` (`certificate_allocation:` / `lot:`) | V17 §19 · V13 §5 |

### Bilateral trades and external registration (CT1)

No new live route (137 unchanged). Holdings / Portfolio affordances
patch onto `/org/certificates`. Demo step S19.

| Surface | Manual section |
| --- | --- |
| Register external holding (`#register-external-form`) | V17 §19 |
| `WB.Carbon.ExternalCertificateAsset` + verification ladder | V17 §19 |
| Bilateral trade form (`#bilateral-trade-form-*`) | V17 §19 |
| `WB.CarbonMarket.Trade` / `FeeSchedule` | V17 §19 |
| Demo S19 carbon market (EVIDENCED holding + SETTLED trade) | V19 · V17 §19 |

### Carbon venue (CT2)

New live route `/org/carbon-market`. Trading tab patches onto
`/org/certificates`. Demo step S20.

| Surface | Manual section |
| --- | --- |
| `/org/carbon-market` (`#org-carbon-market`) | V17 §20 |
| Certificate Desk Trading tab (`#certificates-trading`) | V17 §20 |
| `WB.CarbonMarket.Listing` / `Order` / `Matching` / `Book` | V17 §20 |
| `WB.CarbonMarket.CarbonRfq` / `CarbonAuction` | V17 §20 |
| `WB.CarbonMarket.Admission` / trading agreement | V17 §20 |
| Demo S20 carbon venue (book, settled ORDER_BOOK, auction, RFQ, admitted trader) | V19 · V17 §20 |

### Carbon tax (CT4)

Three new live routes. CS1 live tax-pack wording stays unticked.

| Surface | Manual section |
| --- | --- |
| `/signup/carbon` (`#carbon-signup`) | V18 §1 |
| `/mo/carbon-activations` (`#mo-carbon-activations`) | V18 §1 · V9 §12 |
| `/org/carbon-tax` (`#org-carbon-tax`) | V18 §1–§8 |
| `WB.CarbonTax.Assessment` section-6 engine | V18 §5 |
| `DeclarationPack` DA 180 | V18 §7 |
| Demo S21 BrickCo (READY pack) | V19 · V18 |

### CAD Trust (CT5)

One new live route. Mainnet participant still waits on Q2.

| Surface | Manual section |
| --- | --- |
| `/operator/cadt` (`#operator-cadt`) | V19 §9 · V17 §22 |
| Certificate Desk CADT banner (`#cadt-double-count-banner`) | V17 §22 |
| `WB.Cadt` observer + Mock / NodeApi | V17 §22 · V19 §9 |
| `Cadt.Workers.SyncRunner` / `PublishRunner` | V17 §22 · V19 §9 |
| Demo S22 CAD Trust hit-and-resolve | V19 · V17 §22 |

### Origination (CT6)

No new live route. Certificate Desk **Projects** tab patches onto
`/org/certificates`. Programme close: CS1 and Q2 mainnet remain
business-owned.

| Surface | Manual section |
| --- | --- |
| Certificate Desk Projects tab (`#certificates-projects-register`) | V17 §9 · §14 |
| `EaAssignment` / `ProjectValidation` / `ProjectVerification` | V17 §14 |
| `Issuance.mirror/2` verification linkage | V17 §14 |
| `Telemetry.Provider` + Mock + `MonitoringReport` | V17 §14 · §23 |
| Demo S23 Atlantis Wind origination spine | V17 §14 · §23 |
| Worked trade, end to end | V17 §23 |

### Registry connectors (CT3)

No new live route. Certificate Desk **Pipeline** tab patches onto
`/org/certificates`. CS2 closed.

| Surface | Manual section |
| --- | --- |
| Certificate Desk Pipeline tab (`#certificates-pipeline`) | V17 §21 |
| `WB.Carbon.Registry.Connector` + Mock / Verra / Gold Standard / COAS | V17 §21 · V19 §9 |
| `RegistrySyncRun` / `RegistryAssetConfirmation` | V17 §21 |
| CONFIRMED verification rung | V17 §21 |
| COAS pipeline steps (`#certificates-pipeline-block-*`) | V17 §21 |

## Matrix 4 — Seats

| Seat | Hub reading path | Manual section |
| --- | --- | --- |
| Trader | V0 → V5 → V7 → V4 | hub · V0 §2 |
| IPP operator | V0 → V2 → V6 → V4 | hub · V0 §2 |
| Off-taker energy manager | V0 → V2 → V5 → V4 | hub · V0 §2 |
| Commercial / deal desk | V0 → V2 → V3 → V4 | hub · V0 §2 |
| Eskom analyst | V0 → V19 §1 → V4 | V19 §1 · V0 §2 |
| Municipal (Munix) officer | V0 → V19 §2 → V2 §9 | V19 §2 · V0 §2 |
| Market / System Operator | V0 → V7 → V9 / V10 → V11 | V9 §1 · §12–§27 · V10 §1–§4 · V0 §2 |
| Platform operator | V0 → V12 → V19 §3–§7 | V12 · V19 · V0 §2 |
| API integrator | V0 → V7 → V11 | V11 · V7 |


### Foundation Stage 1 — 2026-09-05

| Surface | Manual coverage |
| --- | --- |
| `/health`, `/health/live`, `/health/ready` on both endpoints; Demo daily health tile | V19 §10, §7 |
| `/metrics` on the web endpoint, bearer protected and disabled when unset | V19 §10 |
| `WB.Fx.Workers.FetchRates`, SARB/ECB adapters, `mix wb.fx.backfill` | V19 §3.3, V16 §3, V8 §20 |
| BRP acceptance rate; unwritten firm-backing and price-exposure gauges removed | V8 §20, V3 walkthrough |

No LiveView route was added; Stage 1 runs `coverage_catalog_test.exs` against the existing S14 pins.


### Foundation Stage 2 · contract devices

| Surface / process | Manual section |
| --- | --- |
| UpsideSharingTerms and VolumeStrikeTerms; physical/virtual pricing panels | V5 §4.8; V6 §7.5–7.6 |
| AvailabilityGuaranteeTerms; versioned AvailabilityMeasurement; monthly pre-warm | V5 §4.9; V6 §7.7 |
| TerminationTerms, TerminationNotice, daily effective action and final settlement worker | V5 §4.10; V6 §7.8 |
| Devices evidence panel on existing settlement page | V6 §7.5–7.7, §13.3b–c |
| Guarantee against trailing measurement on Sales; acknowledgement warning | V4 §5 guarantee line; §11.6 |
| CfD strike preserves WER | V6 §14.7; V15 §4 |
| Scenario Lab published evening spike and S17 future-effective notice | V6 §13.3b; V5 §4.10 |

No route is added. The physical wizard gains step 12; the virtual wizard retains its existing ten steps.
Stage acceptance evidence and any unticked gates stay on the stage record.

## Foundation Stage 3 additions — 2026-09-05

| Surface / process | Implementation | Manual section |
| --- | --- | --- |
| Existing generator wizard and Overview | Five plant profiles, technology and dispatch cards | V2 §4.7; V4 §4.2 · §17 |
| `/operator/hydrology` | Operator.HydrologyLive; RegimeIndicator | V16 §9 |
| Existing Regional desk | Source-dated regime chip and Exposure card | V16 §6; V16 §9 |
| `/eskom/fuel-prices` | Eskom.FuelPricesLive; FuelPrice | V19 §1, Fuel prices |
| Hydrology weekly fetch | Sapp.Workers.FetchHydrology | V16 §9; V19 §3 |
| Generation bridge | CarbonTax.Workers.GenerationBridgeRunner; SETTLED records | V18 §4.5; V19 §3 |

Combined Stage 3 catalog: **148 routes / 111 LiveView modules**, accounting for both additions. Existing volume anchors remain stable.

## Foundation Stage 4 additions — 2026-09-05

Routes remain **148**, LiveView modules **111**, export routes **5**.

| Surface or process | Manual section |
| --- | --- |
| IPP Capture card and provenance states | V4 §4.3 |
| Sales forecast baseload comparator and realised capture | V4 §6, Captured forecast and realised capture |
| Market-indexed vPPA captured SMP and cannibalisation | V4 §19 |
| Bid Desk technology strip; acceptance and DAM capture | V7 §21 |
| CapturePolicy, daily job, backfill, shape completeness, solar-glut scenario | V7 §21 |
| Money Room bridge and lineage | V16 §7, The plan-to-actual bridge |
| Dashboard bridge and plan of record | V3 §11, Capture and the plan-to-actual bridge; V4 §12.2 |
| Seven-step worked month and residual | V16 §5, Why the plan was missed |
| PDF and bridge CSV | V16 §9, Export the explanation separately |
| S11 MoneyRoomFacts and FactBuilder bridge hook | V16 §7, The plan-to-actual bridge |


## Foundation Stage 5 · 3.14.0

| Surface | Manual section |
| --- | --- |
| `/org/brp/bid-desk/controls` | V8 §12 `#trading-controls`; V7 §4 `#trading-checks` |
| `/org/approvals` | V8 §21 `#approvals` (existing FX §20 retained) |
| `/org/brp/controls/self-assessment.csv` | V8 §12; V7 §17 participant control evidence |
| Organisation business roles | V2 §2 business roles and legal form |
| ProposeEscalation, ExpireApprovals, SelfAssessmentSnapshot | V19 §3 trading-control workers |
| Daily controls status | V19 §8 trading-control status |

Counts: 150 LiveView routes, 112 routed modules, six catalogued exports. Export routes are counted separately from LiveViews.

## Foundation Stage 6 — 2026-09-06

Manual 3.15.0. Router, catalog and verifier: **150 LiveView routes, 112 routed
LiveView modules, 7 catalogued controller exports**. No new LiveView route.

| Surface / process | Manual section |
| --- | --- |
| Forecast weekly skill and provider comparison | V4 §17.8 · on-page table V4 §12.6 (Dashboard) · bridge V4 §8.1.10 |
| Grid Code pack list, refusals and owned XML export | V4 §17.9 · on-page table V4 §12.6 (Dashboard) · bridge V4 §8.1.10 |
| Bid Desk skill provenance chip | V7 §4.2 |
| Forecast Quality nMAE and diversification tiles | V3 §11.9 |
| Ensemble / Solcast pilot pre-flight and quota limits | V4 §17.3–17.4 |
| ScoreSkill, BuildSoPack, BuildSoRolling | V19 §3 |

The download URL ends in `/strategy/forecast/so-pack/<id>.xml`; Phoenix captures
the complete final `:filename` segment and the controller validates its suffix.

## Foundation Stage 7 — Curtailment claims

Manual 3.16.0. The router, catalogue and verifier now agree on **151 LiveView routes, 113 routed LiveView modules and eight exports**. F17's earlier 147/110/6 pin predates Stages 3–6. Existing chapter anchors are retained; the new V4 material appends at §17.

| Surface | Manual |
| --- | --- |
| `/org/generator-sites/:generator_site_id/strategy/curtailment` | V4 §3.2 · §10 · §10.1–§10.8 (Record form by status in §10.4; six `#claim-totals` tiles in §10.7; Annual exposure maths and Money Room relationship in §10.5 — no Money Room exposure tile) |
| `/org/generator-sites/:generator_site_id/strategy/curtailment/claims/:id/export.csv` | V4 §10.3 (Review drawer · Export CSV · Evidence bundle CSV `?bundle=true`) |
| `/so/registry/ipps/:id` Claims tab | V10 §7 |
| `/so` Claims column | V10 §7 |
| `/api/v1/registry/participants/:mpid/curtailment-claims` | V4 §10.6, V10 §7 |
| `/api/v1/registry/curtailment-claims/:claim_ref` | V4 §10, V10 §7 |
| ClaimClock; RefreshCurtailmentExposure; daily clock counts | V4 §10.1, §10.3–10.5 |
| Claim-derived loss events and NERSA final paid cost | V6 §7.10, V10 §7, V4 §10.5 (Money Room Panel 4 sentence, not an exposure tile) |
| S08/S11 gallery and scenario claim scripts | V12 §6 |


## Foundation Stage 8 — data fabric

Manual 3.17.0. Catalogue: **153 LiveView routes, 115 routed modules, nine exports**.

| Surface | Coverage |
| --- | --- |
| `/ops/feeds` | Rich / State — V19 §11 |
| `/ops/events` | Rich / State — V19 §11.1 |
| `/api/v1/publication/interval-schema` | Export — V2 §7.7 |
| `WB.Feeds`, `WB.Events` | Registry policies, attempts, subscriptions and deliveries — V19 §11 |
| Organization Configuration / Market API | Scoped feeds and webhook controls — V11 §13 |
| IPP / off-taker site directories | Interval import drawer — V2 §7.7 |
| Settlement statement and detail | Confidence roll-ups and locked-revision finding — V6 §§9,11 |

The original F12 counts predate Stages 3–7. V19 §10 remains the health and metrics chapter.

## Foundation Stage 9 — fleet view

Manual 3.18.0. Catalogue: **156 LiveView routes, 116 routed modules, ten exports**. The Stage 9 delta is three LiveView paths, one CSV export and one scheduled worker.

| Surface | Coverage |
| --- | --- |
| `/org/fleet` | Rich / State — four-chapter story + drawers, V4 §2.1–§2.6 |
| `/org/fleet/counterparties` | Redirect → `/org/ipp-sites` — V4 §2.3 |
| `/org/fleet/sites` | Redirect → `/org/ipp-sites` — V4 §2.1 (membership) · §3 Sites |
| `GET /org/fleet.csv` | Export CSV (`#fleet-export`) — today’s KPI keys, active sites, pair sources — V4 §2.6 |
| Sidebar **IPP Sites** (`/org/ipp-sites`) | Fleet tab (default, four-chapter story) — V4 §2 · V8 §3 |
| `#fleet-export` / `#fleet-recompute` | Title-row Export CSV and Recompute now — V4 §2.6 |
| `#fleet-open-sales` / `#fleet-next-sales` | Review the sales plan drawer (`#fleet-open-sales`) — V4 §2.2 · chapter 04 row 01 **Open its Sales desk** / **Open the Sites register** (`#fleet-next-sales`) — V4 §2.5 |
| `#fleet-open-evidence` / `#fleet-next-evidence` | How sure are we? drawer (`#fleet-open-evidence`) — V4 §2.2 Volume correlation · chapter 04 row 03 **Import intervals** (`#fleet-next-evidence`, the interval import modal) — V4 §2.5 · V2 §7.7 |
| `#fleet-open-buyers` / `#fleet-next-buyers` | See buyer exposure drawer (`#fleet-open-buyers`) — V4 §2.3 · Adverse basis p90/p10 · chapter 04 row 02 **Open contracts** (`#fleet-next-buyers`, Deals › Contracts) — V4 §2.5 |
| `#fleet-open-risk` / `#fleet-var-one-day` / `#fleet-var-one-month` / `#fleet-fan-data` | Understand the estimate · What the risk estimate means · one-day and one-month VaR-95 · Distribution data — V4 §2.4 |
| `#fleet-open-sites` / `#fleet-story-drawer` | Explore the fleet · shared drawer chrome — V4 §2.1 |
| IPP Sites `#open-interval-import` / `#interval-import-modal` | Supply interval import — V2 §7.7 |
| OffTaker Sites `#open-interval-import` / `#interval-import-modal` | Demand interval import — V2 §7.7 |
| `WB.Strategy.Workers.RunFleet` | Nightly 03:15 and **Recompute now** — fleet KPI, exposure, correlation and distribution snapshots — V4 §2.6, V19 §3.3 |
| `WB.Sim.FleetCorrelationSweep` | Correlation sensitivity — V4 §2.2, V12 |

## Foundation Stage 11 — plant telemetry (3.20.0)

| Surface / process | Behaviour | Manual section |
| --- | --- | --- |
| `/org/generator-sites/:generator_site_id/strategy/plant` | Plant tab: register, dated samples, quality chart, scoped consent, trace/template/schema downloads | V4 §11 · V2 §11 |
| IPP dashboard Plant now and HAF detail | Compact glance + Open plant navigate; hourly sparkline and measured availability today | V4 §12.6 · V4 §11 |
| Trader Strategy overview | Only visible plant-feed chips under effective consent | V4 §11 |
| Physical proposal wizard | Draft collector/data-schedule annex | V2 §11 |
| POST `/api/v1/telemetry/samples` | Owner-scoped push, HMAC, idempotency, 120/minute | V11 §17 |
| GET `/api/v1/telemetry/feeds` | Ownership/consent register | V11 §17 |
| GET `/api/v1/telemetry/feeds/:plant_feed_id/samples/:trading_date` | Scoped samples and freshness header | V11 §17 |
| `Telemetry.Workers.IngestMirrorDrop` · `*/5 * * * *` UTC | CSV ingestion and archive | V19 §3 · V2 §11 |
| `Telemetry.Workers.FreshnessSweep` · `1-59/5 * * * *` UTC | Transition-only operational status | V19 §3 |
| `Telemetry.Workers.IntradayMecScan` · `2-59/15 * * * *` UTC | Provisional device warnings | V19 §3 · V4 §11 |
| `Telemetry.Workers.BuildCurtailmentTrace` · event-driven | Frozen method/sample evidence, meter retries | V19 §3 · V4 §11 |
| `LiveTelemetry.Simulator` · S15 only | Five-minute virtual companion | V19 §6 |
| S03/S05, S12/S16/S17 Driver | Mock feed estate, history and trace day-log references | V12 §6 · V19 §11 |

Stage 11 adds one LiveView route and one routed module: **158 / 118**; catalogued exports remain **10** (plant downloads are LiveView events). The S14 catalogue, verifier constants and acceptance selector are aligned. P11M2 is **not opened — trigger unmet, checked 2026-09-06**. The vendor pull worker rows will be added only when their trigger opens.

The Stage 11 runtime audit also corrects the older background-count header: **85 cron entries / 56 distinct cron workers / 10 non-cron workers**. The telemetry delta is three cron workers and one non-cron worker; the other differences were existing jobs omitted from the previous totals. S14 excludes generated authentication/LiveDashboard routes; the unfiltered Phoenix route list is 161 / 119 in development.


### Foundation audit repairs — 3.20.1

V5 §4.10 covers reference selection, assumption review and confirmed lost-margin notices on the Contracts register. V12 §11 covers the organization controls script, rollback and retained audit proof, plus S17 incident evidence. Existing catalogue remains 158 LiveView routes, 118 routed modules and ten exports.

## 3.20.23 — IPP Overview supporting panels

| Surface | Manual section |
| --- | --- |
| IPP Overview `#ipp-capture` (Value of your generation hours) | V4 §4.4 `#34-value-of-generation-hours` (alias `#322-value-of-generation-hours`; maths remain V4 §19; `#33-the-capture-card` is a pointer) |
| IPP Overview `#ipp-buyers` · `#ipp-weather-outlook` · `#ipp-price-rail` | V4 §4.5 `#35-who-buys` · §4.6 `#36-weather-outlook` · §4.7 `#37-price-vs-cost` (aliases `#who-buys` · `#weather-outlook` · `#price-vs-cost`) |

## 3.24.0 — OffTaker Overview, each card and action

| Surface | Manual section |
| --- | --- |
| OffTaker Overview `#offtaker-mix-hero` (use vs pay) | V5 §3.1 `#1311-five-kpi-cards` · `#31a-the-headline` · `#31b-the-mix-bar` · `#31c-what-you-use` · `#31d-what-you-pay-for` |
| OffTaker Overview `#offtaker-actions` (ranked next-best-actions) | V5 §3.4 `#1314-next-best-actions` · `#34a-trim` · `#34b-buy` · `#34c-sawem` · `#34d-green` · `#34e-coverage` |
| OffTaker Overview `#offtaker-supply-mix` · `#offtaker-supply-view` · `#offtaker-profile-period` | V5 §3.2 `#1312-supply-mix` · Typical vs Tomorrow reading `#32b-typical-day-or-tomorrow` · `#32d-typical-vs-tomorrow` · Settled taught in V5 §3.7 |
| OffTaker Overview `#offtaker-routes` · `#offtaker-weather-outlook` · `#offtaker-price-rail` | V5 §3.8 `#37-what-to-buy-next` · §3.9 `#38-will-your-supplier-deliver` · §3.10 `#39-what-a-kwh-costs` |
| OffTaker Overview `#offtaker-escalation` · `#offtaker-params-modal` · Run Forecast Now · Missing grid rates | V5 §3.3 · §3.5 · §3.6 |

## 3.25.3 — V6 2A → §2, later chapters +1

Display numbers only. Frozen `#2a-trader-pools`, `#2-getting-in`,
`#3-overview--your-landing-page`, `#5a-settled-book--procured-vs-sold`,
`#5b-settled-chains--end-to-end-path-health` stay.

| Surface | Manual section |
| --- | --- |
| Trader Pools directory | V6 §2 `#2-trader-pools` · `#2a-trader-pools` |
| Getting in (open the book) | V6 §3 `#3-getting-in` · `#2-getting-in` |
| Overview | V6 §4 `#4-overview--your-landing-page` · `#3-overview--your-landing-page` · `#31-kpi-cards` · `#42-interpret-green-amber-and-red` · `#43-concentration` · `#44-where-the-energy-goes` · `#33-next-best-actions` · `#46-hour-by-hour` · `#32-pool-cards` · `#48-margin-heatmap` · `#49-forecast-said-book-settled` · `#34-run-forecast-now` |
| Book · Rules / Forecast / Outcomes / Chains | V6 §5 · §6 · §6A · §6B |
| Dashboard | V6 §12 `#12-dashboard--margin--risk` · `#11-dashboard--margin--risk` |

## 3.25.2 — V5 Sources → Overview §3.7, landed cost → §7

Display numbers only. Frozen `#135-sources--where-settled-energy-came-from`
and `#8-landed-cost` stay.

| Surface | Manual section |
| --- | --- |
| Overview Settled (retired Sources) | V5 §3.7 `#135-sources--where-settled-energy-came-from` · `#37-sources` |
| Landed-cost model | V5 §7 `#7-landed-cost` · `#8-landed-cost` |
| Overview routes / weather / price rail / example / Explanations | V5 §3.8 `#37-what-to-buy-next` · §3.9 `#38-will-your-supplier-deliver` · §3.10 `#39-what-a-kwh-costs` · §3.11 `#310-worked-example` · §3.12 `#311-explanations` |

## 3.25.0 — V5 OffTaker volume rewrite (how to read)

No new routes. V5 chapters now teach how to read each surface's
numbers and shapes, and how each chapter feeds the lifetime Dashboard
story. Frozen anchors stay.

| Surface | Manual section |
| --- | --- |
| OffTaker Sites `#fleet-strip` · `#fleet-needs` · onboarding | V5 §2 (tiles as readings · Needs you · commissioning) |
| OffTaker Overview use-vs-pay · load chart · Typical / Tomorrow P50 outline | V5 §3 · `#32d-typical-vs-tomorrow` |
| Procurement Desk need band · Option 0 · compare | V5 §4 · glossary `g-option-0-offtaker` |
| Coverage Rationale Cu / Co / q* | V5 §5 |
| OffTaker Dashboard six chapters | V5 §6 (the story the other chapters feed) |
| Overview Settled (retired Sources) | V5 §3.7 `#135-sources--where-settled-energy-came-from` |
| Landed-cost model | V5 §7 `#8-landed-cost` |

## 3.24.1 — Overview Explanations sidebar

| Surface | Manual section |
| --- | --- |
| Generator Overview `#overview-explanations-open` | V4 §4.8 `#48-explanations` |
| OffTaker Overview `#overview-explanations-open` | V5 §3.12 `#311-explanations` |
| Trader Overview `#overview-explanations-open` | V6 §4.11 `#411-explanations-and-chrome` |
| Trader Book `#book-explanations-open` | V6 §5.8 `#58-explanations` · §6.8 `#68-explanations` · §6A.4 `#6a4-explanations` · §6A.5 `#6a5-how-to-read-outcomes-block-by-block` |
