Module Map¶
This page shows the EN 15978 modular boundary the tool implements, every domain module and shared service, their dependency relationships, and their current build status. Status is read from the source files and the git history — nothing is invented here.
For the tool pipeline (how a BoQ becomes a result), see Data flow. This page is the life-cycle stage view.
EN 15978 modular boundary — current build status¶
flowchart TD
QTY["quantities<br/>BoQ → material explosion<br/>(tool entry — not an EN 15978 module)"]
subgraph PROD["A1–A3 · Product stage"]
A1A3["lca.a1_a3<br/>A1 raw supply · A2 transport · A3 manufacturing"]
end
subgraph CONS["A4–A5 · Construction process stage"]
A4["lca.a4<br/>A4 Transport to site"]
A5["lca.a5<br/>A5 Construction / installation"]
end
subgraph USE["B1–B7 · Use stage"]
B1["B1 Use"]
B2["B2 Maintenance"]
B3["B3 Repair"]
B4["lca.b4<br/>B4 Replacement"]
B5["B5 Refurbishment"]
B6["lca.b6<br/>B6 Operational energy"]
B7["B7 Operational water"]
end
subgraph EOL["C1–C4 · End-of-life stage"]
C1["lca.c1<br/>C1 Deconstruction / demolition"]
C2["lca.c2<br/>C2 Transport to waste processing"]
C3["lca.c3<br/>C3 Waste processing"]
C4["lca.c4<br/>C4 Disposal"]
end
subgraph BEYOND["D · Beyond the system boundary"]
D["lca.d<br/>Reuse · recovery · recycling potential<br/>reported separately, never netted against A–C"]
end
QTY --> PROD
PROD --> CONS
CONS --> USE
USE --> EOL
C1 --> C2
C2 --> C3
C3 --> C4
EOL --> BEYOND
classDef done fill:#2e7d32,stroke:#1b5e20,stroke-width:1px,color:#ffffff
classDef spec fill:#ef6c00,stroke:#e65100,stroke-width:1px,color:#ffffff
classDef todo fill:#757575,stroke:#424242,stroke-width:1px,color:#ffffff
classDef excl fill:none,stroke:#9e9e9e,stroke-width:1px,color:#757575,stroke-dasharray: 5 4
class QTY,A1A3,A4,A5,B4,B6 done
class C3,C4,D spec
class C1,C2 todo
class B1,B2,B3,B5,B7 excl
Legend¶
flowchart LR
L1["GREEN — implemented, tests green<br/>quantities · lca.a1_a3 · lca.a4 · lca.a5<br/>lca.b6 (fork-b; carbon with a runtime EF_grid)"]
L2["AMBER — scaffold file only, no logic;<br/>reverse-engineering spec committed"]
L3["GREY — scaffold file only, no logic;<br/>no reverse-engineering spec yet"]
L4["DASHED — excluded from v1<br/>(decision #2, documented rationale)"]
classDef done fill:#2e7d32,stroke:#1b5e20,stroke-width:1px,color:#ffffff
classDef spec fill:#ef6c00,stroke:#e65100,stroke-width:1px,color:#ffffff
classDef todo fill:#757575,stroke:#424242,stroke-width:1px,color:#ffffff
classDef excl fill:none,stroke:#9e9e9e,stroke-width:1px,color:#757575,stroke-dasharray: 5 4
class L1 done
class L2 spec
class L3 todo
class L4 excl
\"Scaffold only\" means a 5-line file — no logic, not a partial implementation
lca.c1, lca.c2, lca.c3, lca.c4 and lca.d are scaffold only (lca.b4 and
lca.b6 were, until Sessions 42 and 40). Each source file is exactly five lines: the AGPL-3.0 header, a one-line docstring,
and # TODO: Phase 2 — see ROADMAP.md. There is no function, no schema, no test, and no
behaviour of any kind to call. Verified by reading all seven files, not inferred from the
status column.
An amber node therefore means the Excel sheet has been reverse-engineered — the spec exists, the code does not. It does not mean the module is partly built. Amber and grey differ only in whether a Step-1 spec has been written; the code state is identical.
Read the B4/C3/C4/D spec accordingly: where it says "lca.b4 raises" or "lca.c3 raises",
it is stating a requirement on the module still to be written, not describing shipped
behaviour.
Green means the code exists and its tests pass — not that a number can be reported
lca.a1_a3, lca.a4 and lca.a5 are implemented and parity-tested, and all three
currently return total = None because impact factors they depend on are BLOCKED
under decision #23. lca.a5 does report its factor-independent half — 1 080 kWh of
site energy demand — which is the shape every blocked module should aim for.
Green is a build status, not a reportability status. Every open factor is listed in the
Action register.
Dependency graph¶
Arrows mean "depends on". Dashed arrows indicate reporting/output relationships.
graph LR
subgraph DOM_LCA ["Domain: lca/"]
A1A3["a1_a3"]
A4["a4"]
A5["a5"]
B4["b4"]
B6["b6"]
C1["c1"]
C2["c2"]
C3["c3"]
C4["c4"]
D["d"]
end
subgraph DOM_OTHER ["Domain: other"]
QTY["quantities"]
THERM["thermal"]
ENERGY["energy"]
end
subgraph SVC ["services/"]
SEEDGATE["seed_epd_gate"]
SEEDREG["seed_epd_registry"]
LCIMAT["lci_materials"]
PCOMP["project_compute"]
COMPARE["comparison"]
PROXY["proxy_engine"]
PEDIGREE["pedigree"]
INTERP["interpolation"]
UNCERT["uncertainty"]
REPORT["reporting"]
end
SEEDGATE --> SEEDREG
SEEDREG --> A1A3
A1A3 --> LCIMAT
A1A3 --> PCOMP
LCIMAT --> PCOMP
PCOMP --> COMPARE
QTY --> A1A3
QTY --> A4
QTY --> A5
QTY --> B4
A1A3 --> B4
QTY --> B6
QTY --> C1
QTY --> C2
QTY --> C3
QTY --> C4
QTY --> D
PEDIGREE --> PROXY
INTERP --> PROXY
PROXY --> A1A3
PROXY --> A4
THERM --> ENERGY
ENERGY --> B6
UNCERT --> A1A3
UNCERT --> A4
UNCERT --> A5
UNCERT --> B4
UNCERT --> B6
UNCERT --> C1
UNCERT --> C2
UNCERT --> C3
UNCERT --> C4
UNCERT --> D
REPORT -.->|"EN 15978 §9"| A1A3
Status table¶
Status progression is stub → implemented → parity-verified. Four modules have reached
implemented; none has reached parity-verified, because parity-verified requires a
non-BLOCKED factor set. Every module below those four is at the weakest end of stub —
a scaffold file with no logic. See the note under the legend.
Domain modules¶
| Module | Path | Status | Phase | Parity source |
|---|---|---|---|---|
quantities |
domains/quantities/ |
implemented (C3 slab rebar BLOCKED; C5 anti-termite EXPERT-JUDGEMENT pending) | Phase 1 | 1a_boq_input_lca.xlsx · Spec · Reconciliation |
lca.a1_a3 |
domains/lca/a1_a3.py |
implemented (Earth/Crude oil/Floor Tiles BLOCKED; Antirust PROVISIONAL) | Phase 1 | 2_lca_vn.xlsx — A1–A3 sheet · Spec |
lca.a4 |
domains/lca/a4.py |
implemented (both transport factors BLOCKED → 15 of 18 materials BLOCKED) | Phase 1 | 2_lca_vn.xlsx — A4 sheet · Spec |
lca.a5 |
domains/lca/a5.py |
implemented (site energy only; energy factor BLOCKED → all 3 plant items BLOCKED, GWP total None, kWh total reported) |
Phase 1 | 2_lca_vn.xlsx — A5 sheet · Spec |
lca._common |
domains/lca/_common.py |
implemented — shared stage helpers, not a stage: aggregate (the None-total rule and the "{label}: {reason}" incomplete_reason form api/examples.py parses), factor lookup / require_factor, blocked_keys, excel_reference_factors, and the zero-work → 0 rule (work_impact). Used by A1–A3, A4, A5 |
Phase 1 | — (behaviour-preserving extraction; covered by the three stages' parity tests and goldens) |
lca.b4 |
domains/lca/b4.py |
implemented — mechanism to the BLOCKED boundary (Session 42): compute_b4 reads the computed A1-A3 rows and each row's submitted finishes fraction f (+ required basis) and returns B4Result, N = ⌈RSP/RSL⌉ − 1 × f × A1-A3 row. Every RSL_TABLE record is BLOCKED (AR-12), so every row with f > 0 or no f is BLOCKED; f = 0 is an excluded real zero. No number wired. Not parity-verified — the formula is parity-tested with test-only RSLs, but no RSL is sourced |
Phase 1 (moved from Phase 2 by decision #32, Session 33) | 2_lca_vn.xlsx — B4 sheet · Spec |
lca.b6 |
domains/lca/b6.py |
parity-verified (formula B6-F1); EF runtime-supplied; D5 BLOCKED (AR-09) (Session 43): compute_b6 takes supplied delivered kWh/yr + carrier + free-text basis + an optional ef_grid (GridFactorInput: value ≥ 0 with year, source, scope, boundary, generated/delivered basis; a generated factor converts through a cited T&D loss) and returns B6Result. Grid + usable factor → computed, G_B6 = E · EF_delivered · RSP via the pure helper b6_carbon; grid without a factor → BLOCKED (AR-65), including 0 kWh (AR-90); generated factor without a loss rate → BLOCKED; diesel → BLOCKED out of Phase-1 scope (AR-88). No factor is stored or defaulted. Formula B6-F1 is VALIDATED and parity-tested against the "B1" sheet cells read at test time (tests/regression/test_b6_keur_songho.py, spec §8.1); the workbook's factor D5 is never a product value |
Phase 1 (moved from Phase 2 by decision #32, Session 33) | 2_lca_vn.xlsx — "B1" sheet (mislabelled) · Design spec · Methodology spec |
lca.c1 |
domains/lca/c1.py |
scaffold only — 5-line file, no logic; no Step 1 RE yet. Must not be implemented against the uncited 0.26 it inherits from A5 (AR-57) |
Phase 2 | 2_lca_vn.xlsx — C1 sheet |
lca.c2 |
domains/lca/c2.py |
scaffold only — 5-line file, no logic; inherits the BLOCKED A4 road factor (task A4-6) | Phase 2 | 2_lca_vn.xlsx — C2 sheet |
lca.c3 |
domains/lca/c3.py |
scaffold only — 5-line file, no logic; RE spec committed | Phase 2 | 2_lca_vn.xlsx — C3 sheet · Spec |
lca.c4 |
domains/lca/c4.py |
scaffold only — 5-line file, no logic; RE spec committed | Phase 2 | 2_lca_vn.xlsx — C4 sheet · Spec |
lca.d |
domains/lca/d.py |
scaffold only — 5-line file, no logic; RE spec committed | Phase 2 | 2_lca_vn.xlsx — D sheet · Spec |
thermal |
domains/thermal/ |
stub | Phase 2 | 1b_thermal_properties.xlsx |
energy |
domains/energy/ |
stub | Phase 2 | 1c_energetic_analysis.xlsx |
Modules B1, B2, B3, B5, B7 are excluded from v1 per PROJECT.md decision #2 and carry no source file.
Phase column — reconciled against the source files
All seven scaffold modules are Phase 2, which is the phase the scaffold files themselves
declare (# TODO: Phase 2). The Phase column and the file TODOs now agree, so the column is
no longer provisional. "In scope for v1" and "Phase 2" were never in conflict: v1.0 ships at
the end of Phase 3, so a Phase 2 module is in scope for v1 and simply not started. Only the
dashed B1/B2/B3/B5/B7 set is out of scope for v1 outright.
Session 33: decision #32 moves lca.b4 and lca.b6 into Phase 1, so for those two rows the
column now leads the files' # TODO: Phase 2. The module PRs update the files. lca.a4 and
lca.a5 stay implemented and parity-tested, but Phase-1 results report them as a documented
cut-off ("module not assessed", #32).
Shared services¶
| Service | Path | Status | Phase | Spec doc |
|---|---|---|---|---|
seed_epd_gate |
services/seed_epd_gate.py |
implemented | Phase 1 | proxy_provenance ext (gate rules: CLAUDE.md) |
seed_epd_registry |
services/seed_epd_registry.py |
implemented | Phase 1 | Value resolution · AR-63 · factor basis per the declared-unit seam |
lci_materials |
services/lci_materials.py |
implemented | Phase 1 | GET /lci/materials (the Materials-picker source) — derived read-only from lca.a1_a3.FACTOR_TABLE, restates no factor. 18 selectable rows + 8 non-selectable categories (7 umbrella, raw_earth per AR-53). Interim until AR-77/AR-78 · UI spec §0.1, §6.2 |
project_compute |
services/project_compute.py |
implemented | Phase 1 | POST /projects/compute (AR-72): ProjectCreate + variants in, ResultEnvelope out. Validates ids and units against lci_materials; A1-A3 via lca.a1_a3.compute_a1_a3_submitted, which applies each row through the declared-unit seam's basis rules (kg computes; m² on glass/PE film/tile is BLOCKED until their seed EPDs are wired with a sourced basis block). A4/A5 not_assessed (#32); B4 via lca.b4.compute_b4 (incomplete while any row is BLOCKED — every RSL is, AR-12); B6 incomplete (carbon BLOCKED, AR-65) or not_provided; whole-life total null while any counted module is incomplete · UI spec §6.3 |
comparison |
services/comparison.py |
implemented | Phase 1 | compare() (decision #18, amended #29): reads the computed variant summaries only, calls no engine. Five gates (conventional submitted, same building attested, earth total complete, conventional total complete, band present), every failed gate a fixed reason. band_present is constant False (AR-83, AR-13), so the result is always pending; complete is unconstructable (delta / band typed None). Never parity-verified: there is no reference to be on par with · Comparison design |
proxy_engine |
services/proxy_engine.py |
stub | Phase 1 | — |
pedigree |
services/pedigree.py |
stub | Phase 1 | Pedigree / DQI |
interpolation |
services/interpolation.py |
stub | Phase 2 | METHODOLOGY.md §5a |
uncertainty |
services/uncertainty.py |
stub | Phase 2 | Uncertainty |
reporting |
services/reporting.py |
stub | Phase 3 | — |
Keeping this page current
Per CLAUDE.md, every new domain module or service session must update the boundary
diagram colours, the dependency graph, and the status table — and must update the
Action register in the same session.