Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
8307a7a
docs: add C1 tickets 04-06 (#21, #22, #23) [skip ci]
ma-z-am Sep 25, 2026
90780b3
docs: add C2 tickets 07-09 (#24, #25, #26); combined-failure arrangem…
ma-z-am Sep 25, 2026
57bdd21
feat: failure inputs on treatment steps in the configurator (#21)
ma-z-am Sep 25, 2026
52e6087
feat: failure only on steps with a positive LRV, and messages for gue…
ma-z-am Sep 25, 2026
7f05bcd
feat: personal treatment steps store failure inputs and the configura…
ma-z-am Sep 25, 2026
315517e
docs: tick the finished criteria of C1 tickets 04-06 (#21, #22, #23)
ma-z-am Sep 25, 2026
93c8f76
refactor: calculate the risk per exposure event, with unchanged resul…
ma-z-am Sep 25, 2026
2f6efec
feat: failure days per exposure event; worst-case loses failing steps…
ma-z-am Sep 25, 2026
a8a1972
feat: best-case applies the mixed-water assumption on failure days (#26)
ma-z-am Sep 25, 2026
026e4fe
docs: tick the finished criteria of C2 tickets 07-09 (#24, #25, #26) …
ma-z-am Sep 25, 2026
0ad7da2
docs: tick the manual check on dev of C1 ticket 06 (#23) [skip ci]
ma-z-am Sep 25, 2026
faa21b2
docs: C2 tickets 07-09 to in-progress; record the 2026-09-28 review i…
yaniv-kwb Sep 28, 2026
755baeb
docs: worst-case loses every positive LRV of a failing step (D4 wordi…
yaniv-kwb Sep 28, 2026
5468a1f
docs: C1 and C2 tickets 04-09 to review, matching the board [skip ci]
yaniv-kwb Sep 28, 2026
ca2d027
Merge main into c1-failure-inputs: D6, D7 and ADR-0002 [skip ci]
ma-z-am Oct 16, 2026
892d063
Merge c1-failure-inputs into c2-failure-calculation: main with D6, D7…
ma-z-am Oct 16, 2026
e9d5ce7
docs: revise the C1 spec for D7, number of parallel units [skip ci]
ma-z-am Oct 16, 2026
f9e4abd
docs: add C1 tickets 14-15 for D7, number of parallel units [skip ci]
ma-z-am Oct 16, 2026
039fcf3
docs: link C1 tickets 14-15 to issues #36 and #37 [skip ci]
ma-z-am Oct 16, 2026
ed18e31
Merge c1-failure-inputs into c2-failure-calculation: C1 spec and tick…
ma-z-am Oct 16, 2026
e547ca1
feat: number of parallel units on treatment steps in the configurator…
ma-z-am Oct 16, 2026
f19a791
feat: personal treatment steps store the number of parallel units (#37)
ma-z-am Oct 16, 2026
96243a7
docs: tick the manual check on dev of C1 ticket 15 (#37) [skip ci]
ma-z-am Oct 17, 2026
60ca0e7
Merge c1-failure-inputs into c2-failure-calculation: tickets 14-15, n…
ma-z-am Oct 17, 2026
3196dc4
docs: revise the C2 spec for D6 and D7, daily LRV and parallel units …
ma-z-am Oct 17, 2026
933cc0a
docs: add C2 tickets 16-17 for D6 and D7; mark tickets 08 and 09 as p…
ma-z-am Oct 17, 2026
6377abb
docs: link C2 tickets 16-17 to issues #38 and #39 [skip ci]
ma-z-am Oct 17, 2026
99ad33d
feat: both cases use the daily LRV of each failing step (#38)
ma-z-am Oct 17, 2026
ff9891d
feat: the failure LRV follows from the number of parallel units (#39)
ma-z-am Oct 17, 2026
4bb2649
docs: tick the manual check on dev of C2 ticket 17 (#39) [skip ci]
ma-z-am Oct 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs/review-2026-10-dev-scenarios.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,4 +135,7 @@ Answers (2026-10-16):

| Ticket | Finding | Change | Branch |
|--------|---------|--------|--------|
| | | | |
| 14 (#36) Number of parallel units on a treatment step in the configurator | F2 (D7) | C1 | `c1-failure-inputs` |
| 15 (#37) Personal treatment steps store the number of parallel units | F2 (D7) | C1 | `c1-failure-inputs` |
| 16 (#38) Both cases use the daily LRV of each failing step | F1 (D6) | C2 | `c2-failure-calculation` |
| 17 (#39) The failure LRV follows from the number of parallel units | F2 (D7) | C2 | `c2-failure-calculation` |
328 changes: 201 additions & 127 deletions docs/specs/c1-failure-inputs.md

Large diffs are not rendered by default.

327 changes: 201 additions & 126 deletions docs/specs/c2-failure-calculation.md

Large diffs are not rendered by default.

24 changes: 24 additions & 0 deletions docs/tickets/04-c1-failure-inputs-in-configurator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 04: Failure inputs on a treatment step in the configurator: enter, check ranges, save, reopen, edit

**What to build:** Every treatment step in the assessment configurator gets two more fields: **failure frequency** (failure days per year) and **failure duration** (minutes). Registered users can save an assessment with these values, and when they reopen or edit it they see the same values again. The values do not change the result yet; the calculation follows in C2.

Tidy-up comes first. The configurator's treatment step form and the personal treatment step form both contain the same minimum ≤ maximum LRV check. It becomes one shared rule that both forms use, and the failure rules are added to it later. This Ticket also adds the first HTTP-level tests. They use Django's test client and a logged-in test user, and they check what a browser or a later request can see.

Delivered on branch `c1-failure-inputs` as part of the single C1 pull request. The pull request stays open until the release (D5).

Source: Spec C1 Failure inputs (`docs/specs/c1-failure-inputs.md`), Change C1 in `docs/failure_roadmap.md`. The Spec issue does not exist yet. GitHub issue #21.

**Blocked by:** None (can start immediately)

**Status:** review

- [x] The minimum ≤ maximum LRV check is defined once and used by both treatment step forms. Nothing changes that a user can notice, and the existing test suite passes unchanged.
- [x] A schema migration adds failure frequency (a decimal number) and failure duration (a whole number) to the treatment steps of an assessment. Existing rows get failure frequency 0 and failure duration 30.
- [x] Each treatment step card in the configurator shows both fields below its LRV rows, labelled "Failure frequency (days per year)" and "Failure duration (minutes)". New steps start at 0 and 30, bundled steps included.
- [x] Failure frequency outside 0–365, and failure duration outside 1–1440 or not a whole number, are rejected with a message on the saved configurator. The duration range is checked even when the frequency is 0.
- [x] A fractional failure frequency such as 0.5 is accepted.
- [x] Saving an assessment and reopening it gives back the same failure values for each step. Editing them and saving again stores the new values.
- [x] Each value stays with its own step when other steps are removed. Two copies of the same bundled step can have different values.
- [x] An assessment created without failure values, as before the migration, reopens with failure frequency 0 and failure duration 30.
- [x] An assessment's result and export package are the same with and without failure inputs.
- [x] All of the above is covered by HTTP-level tests through Django's test client, and the existing test suite passes unchanged.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 05: Failure frequency only on steps with a positive LRV (D4), with messages for guests too

**What to build:** A treatment step can only have a failure frequency above 0 if at least one of its LRVs is positive, because a failure removes only positive LRVs (D4). The configurator checks this when the assessment is run or saved, and the error appears on the failure frequency field.

Guests can use the failure fields too, and the same rules apply to them. Today, when a guest runs an assessment with invalid input, the guest result sends back an empty error and nothing appears on the page. With this Ticket, the guest result sends back the error messages and the configurator shows them on the affected treatment step, as it does for registered users. This also makes the existing LRV errors, such as minimum above maximum, visible to guests.

Delivered on branch `c1-failure-inputs` as part of the single C1 pull request.

Source: Spec C1 Failure inputs (`docs/specs/c1-failure-inputs.md`), roadmap decision D4. The Spec issue does not exist yet. GitHub issue #22.

**Blocked by:** 04 (#21)

**Status:** review

- [x] A failure frequency above 0 is rejected, with a message on the failure frequency field, if none of the step's six LRVs (minimum and maximum for bacteria, viruses and protozoa) is above 0. An empty LRV counts as 0.
- [x] A step whose only positive LRV is one maximum (for example protozoa 0–2) accepts a failure frequency above 0.
- [x] A step with only negative LRVs (recontamination) or only empty LRVs keeps failure frequency 0. A higher value is rejected.
- [x] A guest sees the failure fields, can run an assessment with valid failure inputs, and nothing is stored.
- [x] When a guest's input is invalid, whether a failure input or an LRV, the guest result sends back the error messages and the configurator shows them on the affected step. The rules are the same as for registered users.
- [x] HTTP-level tests cover the D4 cases on the saved configurator and on the guest result, and cover rejection with a message on the guest result. The existing test suite passes unchanged.
24 changes: 24 additions & 0 deletions docs/tickets/06-c1-personal-treatment-step-failure-inputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 06: Personal treatment steps store failure inputs and the configurator copies them in

**What to build:** A registered user can store a failure frequency and a failure duration on a personal treatment step, with the same rules as in the configurator. When they add the step to an assessment, its failure values are copied in together with its LRVs. The copy can then be changed in that one assessment without changing the personal treatment step.

Today the personal treatment step form sends back an empty error for invalid input, so the user sees nothing. With this Ticket it shows the messages, the same way it already does for a duplicate name.

Delivered on branch `c1-failure-inputs` and closes the C1 pull request. The pull request is tested on the dev environment and not merged before the release (D5).

Source: Spec C1 Failure inputs (`docs/specs/c1-failure-inputs.md`). The Spec issue does not exist yet. GitHub issue #23.

**Blocked by:** 04 (#21), 05 (#22)

**Status:** review

- [x] A schema migration adds failure frequency and failure duration to personal treatment steps. Existing personal treatment steps get failure frequency 0 and failure duration 30.
- [x] The personal treatment step form shows both fields with their units and applies the same shared rule as the configurator: the ranges, whole minutes, D4 and minimum ≤ maximum LRV. Invalid input is rejected and the message is shown in the form.
- [x] A personal treatment step saved with failure inputs appears with them in its owner's list of personal treatment steps. Another user's list does not contain it.
- [x] Adding a personal treatment step to an assessment copies its failure frequency and failure duration into the new step. Bundled steps still start at 0 and 30.
- [x] Changing the copied values in an assessment and saving leaves the personal treatment step unchanged.
- [x] HTTP-level tests cover the form rules, old personal treatment steps reading back 0 and 30, and owner-only visibility. The existing test suite passes unchanged.
- [x] Manual check on the dev environment, announced to the team first:
- Create a personal treatment step with failure inputs and add it to an assessment.
- See the copied values, change them, save and reopen.
- Confirm that the personal treatment step itself is unchanged.
18 changes: 18 additions & 0 deletions docs/tickets/07-c2-calculation-per-exposure-event.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# 07: Tidy-up: the calculation works per exposure event, with results unchanged

**What to build:** Tidy-up before failures enter the calculation. No result changes.

The Monte Carlo simulation is reorganised to work per simulated exposure event. Each event picks its concentration sample by position instead of by value, which uses exactly the same random numbers as today, and each event gets its own LRV. For now every event still gets the same LRV. Best-case and worst-case use the same building blocks, and the summary statistics (mean, minimum, quartiles, median, maximum) and the reference-level exceedance are computed in one place for both cases and both risk measures. The calculation's public entry point keeps its interface.

Delivered on branch `c2-failure-calculation`, based on `c1-failure-inputs`, as part of the single C2 pull request. The pull request stays open until the release (D5).

Source: Spec C2 Failure calculation (`docs/specs/c2-failure-calculation.md`), Change C2 in `docs/failure_roadmap.md`. The Spec issue does not exist yet. GitHub issue #24.

**Blocked by:** None (can start immediately)

**Status:** review (built in pull request #29; CI green on `a8a1972`; the pull request stays open until the release, D5)

- [x] Every simulated exposure event has its own concentration sample, picked by position, and its own LRV. The random numbers are the same as today's.
- [x] Best-case and worst-case share the same calculation steps. Summary statistics and the reference-level exceedance are computed in one place.
- [x] The existing regression test passes with exactly the same numbers, and the existing test suite passes unchanged.
- [x] A new test at the calculation's public entry point shows that calculating the same scenario twice gives identical results.
23 changes: 23 additions & 0 deletions docs/tickets/08-c2-failure-days-and-worst-case.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 08: Failure days drawn per exposure event; worst-case loses the positive LRVs of failing steps

**What to build:** The worst-case result includes treatment failures. For every simulated exposure event, and for every treatment step with a failure frequency above 0, the step fails that day with probability failure frequency / 365 (ADR-0001). On a failure day, worst-case calculates the event with the positive minimum LRVs of every failing step removed, whatever the failure duration. LRVs of 0, below 0 or empty stay in place (D4).

The failure draws come from their own fixed-seed random generator and are shared by both cases and all reference pathogens. Raising a failure frequency therefore only adds failure days. If every failure frequency is 0, no failure draws are made and the results are exactly today's. Best-case does not change in this Ticket; the mixed-water assumption follows in the next one.

The result page, the reference-level exceedance, saved results and the export's result table show whatever the calculation returns. Their layout does not change.

Source: Spec C2 Failure calculation (`docs/specs/c2-failure-calculation.md`), ADR-0001, roadmap decision D4. The Spec issue does not exist yet. GitHub issue #25.

**Blocked by:** 07 (#24), 04 (#21, C1 stored failure inputs)

**Status:** review (built in pull request #29; CI green on `a8a1972`; the pull request stays open until the release, D5)

**Partly replaced (2026-10-17):** The worst-case loss for the whole day regardless of the failure duration is replaced by the daily LRV of Ticket 16 (D6, ADR-0002); the failure-day draws stay.

- [x] With every failure frequency at 0, all results equal today's exactly, including the existing regression test's numbers.
- [x] A step with failure frequency 365 gives the same worst-case results as the same scenario without that step's positive minimum LRVs.
- [x] Raising a failure frequency never lowers any mean. Changing a failure duration leaves worst-case unchanged.
- [x] Worst-case mean ≥ best-case mean for every reference pathogen and risk measure, with and without failures.
- [x] D4: a failing step with a negative LRV in one pathogen group and a positive LRV in another leaves the first group's results unchanged. A group with LRV 0 is unaffected.
- [x] Calculating the same scenario with failures twice gives identical results.
- [x] All tests go through the calculation's public entry point and check only the returned results. The existing test suite passes unchanged.
42 changes: 42 additions & 0 deletions docs/tickets/09-c2-best-case-mixed-water.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# 09: Best-case applies the mixed-water assumption, including combined failures

**What to build:** The best-case result includes treatment failures under the mixed-water assumption. On a failure day, the consumed water is a mixture of water treated during the failure events and water treated in normal operation, in proportion to the failure durations. The event's LRV is −log₁₀ of the time-weighted average of 10^(−LRV) over the segments of the day, where a segment's LRV is the best-case LRV of the train minus the positive maximum LRVs of the steps down in that segment.

Combined failures of one day:
- if their durations fit into one day, they do not overlap;
- two failure events longer than a day in total overlap only by the minutes beyond the day;
- three or more failure events longer than a day in total count like worst-case: all these steps are down for the whole day (changed on 2026-09-25 from the lowest-concentration arrangement, which needed a numerical solver for a rare exception). Only steps that lose LRV for the pathogen group count.

For a single failing step, the result equals Eq. 5 of the failure approach. Work per failure day is reused for repeated combinations of failing steps.

This Ticket closes C2: it includes the manual check on the dev environment and the time measurement.

Source: Spec C2 Failure calculation (`docs/specs/c2-failure-calculation.md`), ADR-0001, `CONTEXT.md` (*Mixed-water assumption*, *Combined failure*), decision 6 in `docs/QMRA_Failure_Approach.md`. The Spec issue does not exist yet. GitHub issue #26.

**Blocked by:** 08 (#25)

**Status:** review (built in pull request #29; CI green on `a8a1972`; the pull request stays open until the release, D5)

**Partly replaced (2026-10-17):** The overlap rules for combined failures are replaced by the daily LRV of Ticket 16 (D6, ADR-0002), and the failure LRV of Ticket 17 replaces the full loss of a failing step (D7).

- [x] A step with failure frequency 365 and failure duration 1,440 gives the same best-case results as the same scenario without that step's positive maximum LRVs.
- [x] A step with failure frequency 365 and failure duration 60 gives the same best-case results as the scenario with the train's best-case LRV replaced by the Eq. 5 mixed LRV, computed by hand in the test.
- [x] Two steps always failing with 900 + 900 minutes equal the hand-computed result with 360 minutes of overlap.
- [x] Three steps always failing within a day in total equal the hand-computed result without overlap.
- [x] Three steps always failing with more than a day in total equal the scenario without their positive maximum LRVs.
- [x] A failing step without removal for one pathogen group does not count for that group.
- [x] Raising a failure duration never lowers a best-case mean, and raising a failure frequency never lowers any mean.
- [x] Worst-case mean ≥ best-case mean for every reference pathogen and risk measure, with failures.
- [x] With every failure frequency at 0, all results equal today's exactly. The same scenario gives identical results every time. The existing test suite passes unchanged.
- [x] Manual check on the dev environment, announced to the team first:
- Enter a scenario with failures and run it as a guest and as a registered user.
- Confirm that the result page, the saved-assessments page, the assessment comparison and the export's result table show the changed results, with unchanged layout.
- [ ] The calculation time for a drinking-water scenario (365 events per year) with failures is measured on the dev environment and recorded in the pull request.

Review on 2026-09-28 (against the Spec, ADR-0001 and the code of the three C2 commits):

- [x] The calculation matches the Spec: failure days per exposure event from a fixed-seed generator shared by both cases and all pathogens; worst-case loses only positive minimum LRVs; best-case follows the mixed-water rules including the three-or-more exception; LRVs of 0 or below are untouched. The hand-computed examples of decision 6 (mixed LRV about 6.9 and about 2.6) agree with the code's formula.
- [x] The concentration samples are picked by position with the same generator calls as before, so results without failures stay bit-identical. The PR records a 28-scenario check of this.
- [x] Wording left over from before D4 (fixed 2026-09-28): `CONTEXT.md` (*Combined failure*: "loses all their LRVs"), `docs/vision.md` MVP item 3 and the C2 scope in `docs/failure_roadmap.md` still say worst-case loses the *full* LRV of a failing step. It should say every *positive* LRV.
- [ ] Decide whether it is acceptable that the failure draws of a step depend on its position in the train. Two scenarios that differ only by an extra non-failing step placed *before* a failing one give slightly different results, because the later step's random stream changes. The difference is sampling noise, but it can surprise a user comparing two saved assessments, and C5's independent script must use the same keying to match.
- [ ] The Spec still says "Status: draft" and "Spec issue: #___". Set it to implemented and create the Spec issue, or record that C2 has none.
24 changes: 24 additions & 0 deletions docs/tickets/14-c1-parallel-units-in-configurator.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 14: Number of parallel units on a treatment step in the configurator: enter, check, save, reopen, edit

**What to build:** Every treatment step in the assessment configurator gets a third failure input next to failure frequency and failure duration: the **number of parallel units**, starting at 1. Guests and registered users see and use it. Registered users can save an assessment with it, and when they reopen or edit the assessment they see the same value again, for each step. The value does not change the result yet; the failure LRV follows in C2 (ADR-0002).

The shared validation rule that both treatment step forms use is extended with the new input, not duplicated. A number of parallel units is a whole number of 1 or more, with no upper limit (D7).

Delivered on branch `c1-failure-inputs` as part of the C1 pull request (#28). The pull request is tested on the dev environment again and not merged before the release (D5).

Source: Spec C1 Failure inputs, revision for D7 (`docs/specs/c1-failure-inputs.md`), Change C1 in `docs/failure_roadmap.md`, roadmap decision D7, ADR-0002. The Spec issue does not exist yet. GitHub issue #36.

**Blocked by:** None (can start immediately)

**Status:** ready-for-agent

- [x] A schema migration adds the number of parallel units to the treatment steps of assessments. Existing treatment steps get 1.
- [x] Each treatment step card in the configurator shows the field "Number of parallel units" below the LRV rows, next to failure frequency and failure duration, starting at 1. It is always shown, also when the failure frequency is 0. Guests see the same field.
- [x] A bundled treatment step added to an assessment starts at 1 parallel unit.
- [x] The shared rule rejects a number of parallel units of 0, a negative number or a fraction such as 1.5 with a message that names the field and its range, on the saved configurator and on the guest result. 1, 2 and a large value such as 1000 are accepted.
- [x] Saving an assessment and reopening it returns the same number of parallel units per step. Editing and saving again stores the new value, and values stay with their step when other steps are reordered or removed.
- [x] An assessment saved before this Ticket reopens with 1 parallel unit, failure frequency 0 and failure duration 30, and its result is unchanged.
- [x] The result and the export package of an assessment are identical with and without failure inputs, including a number of parallel units above 1.
- [x] HTTP-level tests through Django's test client cover the cases above, extending the existing C1 failure input tests. The existing test suite passes unchanged.

Quality goals (`docs/vision.md`): Input validity (label and checked range), Reproducibility (saved values and old assessments read back unchanged), Export stability (export package unchanged by C1).
Loading