Skip to content
Paolo Veronelli edited this page Aug 25, 2026 · 2 revisions

Updated: 2026-08-25 UTC

Outcome: 9.12.3 server on 2018 gruppi; the 2018 binary and a pre-v1 gruppi/ stay bootable until a responsabile publish is proven on the live URL.

Legend: ✅ done · 🟡 active/next · ⏳ queued · ⛔ blocked · ❓ unknown

Shape

flowchart LR
  subgraph F["#37 freeze + baseline"]
    I26["#26 freeze, integrity-check,<br/>boot, record 2018 baseline"]
    I41["#41 parse-probe frozen stato<br/>with 9.12.3 Read"]
    I26 --> I41
  end
  subgraph B["#38 9.12.3 binary — 3 PRs, 3 gates"]
    I28["#28 sqlite v1<br/>values + refuses prod"]
    I29["#29 SCGI via separate<br/>nginx block"]
    I30["#30 RSA-1.0 unlock<br/>+ CI gate"]
    I28 --> I29 --> I30
  end
  subgraph C["#39 copy probe"]
    I31["#31 clone from freeze,<br/>checksum-verified"]
    I32["#32 migrate + boot;<br/>values = baseline"]
    I33["#33 live responsabile<br/>types own password"]
    I31 --> I32 --> I33
  end
  subgraph X["#40 cut-over — copy-and-switch"]
    I34["#34 runbook + timed<br/>rehearsal incl. rollback"]
    I42["#42 fresh re-freeze<br/>inside the window"]
    I35["#35 switch live URL<br/>to gruppi.v1"]
    I36["#36 responsabile publish<br/>+ values = baseline"]
    I34 --> I42 --> I35 --> I36
  end
  I26 --> I31
  I30 --> I32
  I31 --> I34
  I33 --> I35
  F --> C
  B --> C
  C --> X
  I36 --> T["live URL 9.12.3 · publish proven · freeze still boots"]
Loading

#41 hangs off the freeze, not the probe epic, so the milestone's largest unknown — whether 9.12.3 can Read years of live Eventi constructors — is answered the day the freeze lands, in parallel with #38, instead of at position 8 of 11.

Epics

  • 🟡 #37 Freeze + baseline + parse probe — next: #26. Absorbs the old #27.
  • 🟡 #38 9.12.3 binary — local branch has the code as two commits; it is re-cut into three PRs with three gates (#28, #29, #30). Now genuinely parallel with #37: #28 no longer needs live data.
  • ⏳ #39 copy probe — blocked by #37 and #38. #33 is gated on a person, not on code.
  • ⏳ #40 cut-over — blocked by #39. Copy-and-switch, fresh re-freeze in the window, same-window rollback if #36 fails.

Tickets

# Ticket State
#26 Freeze, integrity-check, boot, record the 2018 baseline 🟡 next
#41 Parse-probe frozen stato with the 9.12.3 Read parser ⛔ by #26
#28 sqlite v1 — values asserted, refuses production root 🟡 ready
#29 SCGI through a separate nginx block ⛔ by #28
#30 RSA-1.0 Segreto unlock + golden-keys/migrate-selftest in ci.yaml ⛔ by #29
#31 Clone live gruppi from the freeze, checksum-verified ⛔ by #26
#32 Migrate + boot the copy; saldi/totals equal the baseline ⛔ by #30, #31
#33 Live responsabile publishes on the copy, typing their own password ⛔ by #32
#34 Copy-and-switch runbook + timed rehearsal including rollback ⛔ by #31
#42 Fresh re-freeze of production inside the cut-over window ⛔ by #34
#35 Switch the live URL to 9.12.3 on gruppi.v1 ⛔ by #33, #34, #42
#36 Responsabile publish on the live URL; values still match ⛔ by #35
#27 Prove the frozen 2018 server boots ✅ closed, merged into #26

What changed on 2026-08-25

Review of the milestone found the shape right — freeze first, prove on a copy, cut over last — but the proofs too weak to conclude "9.12.3 runs old gruppi", and the rollback stale by construction. Applied:

  1. #26 + #27 merged. Booting the freeze was the only cheap chance to capture the 2018 baseline while 2018 is still the truth. Freeze now means: quiesce → copy (sqlite first, stato.* last) → PRAGMA integrity_check + row-count compare vs live → read-only → boot → record per-user saldi and per-group totals. #27 closed as duplicate.
  2. Values, not counts. #32 and #36 now require 9.12.3's saldi and totals to equal the recorded 2018 baseline, value by value. Previously a migration could preserve every row count, corrupt every amount, and pass all 15 tickets green.
  3. #42 re-freeze added. Rollback used to restore a freeze taken days of live publishes before the switch. A fresh consistency-checked freeze is now taken inside the maintenance window, before any migrate.
  4. Cut-over is copy-and-switch. #34/#35: stop 2018 → fresh freeze → cp -a gruppi gruppi.v1 → migrate gruppi.v1 → 9.12.3 on gruppi.v1. gruppi/ stays v0, so rollback is a process restart, and the partial-migration .bak deadlock can never happen on the live path.
  5. #41 parse probe added, under #37, runnable the day the freeze lands.
  6. Two routes to taking down :8000 closed. #28: migrate-sqlite must refuse the production root unless explicitly flagged, asserted by a test. #29: SCGI proven through a separate nginx block, with config backup, nginx -t, and a re-check that the live URL is still 2018.
  7. #30 puts the gates in CI. just golden-keys and just migrate-selftest run in .github/workflows/ci.yaml — which today runs only cabal build all, fourmolu and hlint, so the milestone's central proof had zero regression protection.
  8. Playwright ACs replaced with operator-observed + screenshot on the issue. The repo has no Playwright config or spec; an unenforceable gate is worse than an honest observation.
  9. #34 can now fail: accepted only after a timed end-to-end rehearsal including rollback, and the window is sized from those numbers.
  10. #35 gets provenance and supervision: production binary is nix build .#default at the merged commit (build-artifacts.yaml already does this — the gap was a tagged, durable release, not a reproducible build), hash and store path recorded, copy archived beside the freeze. Not dist-newstyle. Plus: same supervisor as today, or one is added, and it survives a unit restart.
  11. Same-window rollback written into #35 and #36: if the publish does not pass, roll back before leaving.
  12. #38 ∥ #37 is now true. #28's live-tarogas row-count AC moved to #32, so the binary epic needs no live data.
  13. Scope resolved: production holds exactly one group, tarogas. Baseline comparison is "every group in the freeze", and #26 records the actual inventory rather than assuming it.

Still open

  • Board Status is unset on all items and the parent/child links are prose, not native GitHub sub-issues, so the board's "Sub-issues progress" column stays blank. This page is the tracker.
  • Data-loss policy: #42 states the window and who tells the responsabili — it is answered there, at the window, not in advance.
  • What the 2018 binary does when started on a v1 sqlite is still ❓; #34's rehearsal must establish it, because that answer decides whether a failed cut-over is recoverable or terminal.
  • Whether 9.12.3's stato.corrente can be read back by the 2018 binary is still ❓; #34 must state it.
  • Whether Segreti exist written by binaries older than the 2018 one is still ❓. #41 will surface it if the parser chokes.

Milestone: https://github.com/paolino/reactivegas/milestone/1

Clone this wiki locally