Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions docs/BACKLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1729,6 +1729,13 @@ lane; demand-gated on a first enterprise Windows/AD deployment.
> 🔢 **Re-scored 2026-08-03 → DEMAND-GATE.** Value **2/10** · Difficulty **6/10** · _money pit_. Value 2 stands — stdout + NSSM rotation, the RFC 5425 TLS syslog forwarder (`_TlsSysLogHandler`, logging_setup.py:281) and #50's disk metering already carry log durability and visibility, so this is marginal and substantially covered. But difficulty 5 prices the wrong shape of work. D5 is "a new connector/codec behind the transport registry" — this is not a connector. logging_setup.py's module docstring (lines 3-13) records that the engine "deliberately do[es] not add file handlers here" because NSSM owns rotation, and `grep FileHandler _(was 2/10 · 5/10.)_

> **On-trigger / demand-gate.** Numbered for tracking only — build when the trigger below fires (“demand-gate, don’t schedule”).
> ⚠️ **AMENDED 2026-08-11 — THE OWNER RULED THIS ITEM IN, and that ruling POST-DATES the DEMAND-GATE and the 2/10 above.** The owner's words: *"we never want to process stuff if the processing cannot be logged."* That coupling **is** the count-and-log invariant CLAUDE.md marks do-not-break — every received message is persisted before the ACK and nothing is accepted-and-dropped — so the gate is discharged and the trigger is moot.

> **The 2/10 was arrived at by conflating VISIBILITY with ENFORCEMENT.** stdout, NSSM rotation, the TLS syslog forwarder and #50's disk metering make the log *visible*; none of them makes processing *stop* when the log cannot be written. The item is the enforcement, and that is what the owner ruled in. **A guard that logs a warning and lets processing continue does not satisfy this item** — that is the specific defect to check for at review.

> **Work exists and is UNVERIFIED.** `w3-log-write-failure` (`d26d66a6`, pushed and anchored) carries five commits whose subjects claim: the halt let the backlog keep routing so the internal stages are stopped too; a reload-recovery claim that *"was a guess"* was measured and three docs corrected; two PHI-inventory anti-rot gates found red on the branch; and a console-contract seam bump. **The lane died mid-flight on a usage limit, so none of it is verified.** It carries **ADR 0162**, whose number was independently confirmed to come from `alloc.ps1` — a real allocation record exists and `main`'s highest is 0161 — so the number is sound, but the index row should be re-checked before landing since a rebase can drop it.

> **The claim most worth verifying by execution is the partial halt:** a halt that stops intake while routed and outbound rows keep draining would still violate the invariant this item exists to protect.

**Cluster:** Logging & Audit. **Priority:** P3. **Verdict:** demand-gate. **Severity (vs Corepoint):** minor.

Expand Down Expand Up @@ -7581,6 +7588,17 @@ gate is the wrong shape, validation of the walk is the right one.
> 5. **It weakens a forcing function, and that trade should be made deliberately.** `allow_unbounded_phi` (#186a) currently makes a PHI instance REFUSE TO START in prod unless both windows are bounded -- which forces every operator to choose a retention window consciously. With a bounded default that gate rarely fires: **more secure by default, less explicit by default.**

> **Framing note:** 60 days is an OPERATIONAL default, not a compliance number. The compliance retention here is the audit chain, which stays keep-forever. Nothing in this item should imply 60 days satisfies a regulatory requirement.
> ⚠️ **AMENDED 2026-08-11 — BUILT, REFUTED, AND REVERTED. Do not read this item as merely unbuilt; the obvious implementation is now known to be WRONG.** The change was made (default `messages_days` / `dead_letter_days` `0` -> `60`), reviewed by another session, verified by execution, and **reverted at `ed8a09d7`** on `a-1212-retention`. Forward-only, so the two reverted commits stay readable as the record.

> **THE DEFECT, and it is the inversion of this item's intent.** `serve`'s posture gate is wrapped in `if not settings.retention.allow_unbounded_phi:` — so when the **audited, deliberate keep-forever opt-out is TRUE, the auto-bound block is SKIPPED ENTIRELY and the field keeps its model default.** Measured: `RetentionSettings(allow_unbounded_phi=True).messages_days` returns `60` under the change. The `RetentionRunner` then starts and **purges PHI bodies after 60 days on exactly the instances whose operator consciously chose to keep them forever.** Silent PHI deletion against an explicit audited decision, landing *only* on the opt-in path. That is worse than the unbounded default this item was filed to fix.

> **AND IT WAS A NO-OP WHERE IT WAS AIMED.** The auto-bound keys on `model_fields_set` — whether the operator *wrote* the key, not what its value is — so an operator who says nothing still resolves to **30** and always did. Changing the model default never survives the gate on any instance the gate runs on.

> **A PREMISE IN THIS ITEM'S OWN TRAP 5 IS STALE.** Trap 5 warns the change *"weakens a forcing function"* because a PHI instance would otherwise refuse to start unbounded. **PR #71 already inverted that gate:** a production PHI instance under `enforce` with an unset window **no longer refuses — it auto-bounds at 30 and starts**, and the repo's own test flipped with it (`tests/test_cli.py:1619` is now `test_serve_auto_bounds_an_unset_body_window_in_prod`, asserting `rc == 0`). So the "more secure by default, less explicit by default" trade **was already made** and is not this item's to weigh. Note `docs/PHI.md` §8 still described the old refusal, which is how that stale premise survived into this filing — the doc-vs-code gap recorded under ASVS 14.2.4 ground (5), misleading a second engineer in practice.

> **IF THE 60 IS STILL WANTED, the value to change is `auto_bound_days` in `messagefoundry/config/retention_classification.py`** — what the gate actually applies — **not the field default. And moving it `30` -> `60` LOOSENS the shipped PHI bound rather than tightening it**, which sharpens the owner's question rather than answering it. That module carries **nine** classified windows behind `MIN_PHI_RETENTION_WINDOWS = 9` with a **two-way** drift test against `docs/PHI.md` §2, so any real window change must move the tuple **and** the doc together or `tests/test_retention_classification_drift.py` reds.

> **`audit_days` staying `0` was the one part of this item that held up** — the audit log is a tamper-evident chain with a ~6-year HIPAA expectation, and a sweep that "fixed all the zero defaults" would convert a compliance property into a defect while looking finished.

**Cluster:** Store / Retention. **Priority:** P2. **Verdict:** build. **Severity:** conditional -- on a first deployment an operator who omits the setting would retain full raw PHI bodies indefinitely; there are zero deployments today.

Expand Down
Loading