# Session ledger -- in-flight work continuity record

**Purpose.** Long ops sessions on this cloud routinely exceed a single context window
and get COMPACTED (sometimes several times). Anything living only in the chat scrollback
is lost at compaction. This ledger is the durable, committed record of what is IN FLIGHT,
so any session -- after a compaction, or a fresh one -- can resume without losing pending work.

**How to use it (standing practice).**
1. At session start: read this ledger AND run `bash scripts/ledger-scan.sh`. Reconcile.
2. `ledger-scan.sh` is the DRIFT CHECK -- it derives the open-work it reliably can
   (PROPOSED/OPEN decisions, OPEN security rows, next-free numbers) straight from the repo.
   This narrative must not claim CLOSED anything the scan shows OPEN, nor omit what it surfaces.
3. Update this ledger at every deliverable/commit -- it is a standing deliverable like the
   changelog. The changelog says what CHANGED; this ledger says what is still OPEN.
4. The machine-derived block below is seeded from a scan; re-run the scan and re-seed rather
   than editing it by hand.

**SINGLE STREAM (collapsed 2026-07-13).** This ledger previously carried three parallel,
separately-owned stream sections (`main-chat`, `jumphost`, `shared`) plus ~30 append-only
session narratives. Those streams are CLOSED and reconciled into the one list below. There is
now ONE stream. Do not re-introduce per-stream sections.

**Where the history went.** The 2,189-line session-by-session narrative is NOT lost -- it is in
git history (the parent of the collapse commit) and, in durable form, in the 65 `docs/changelog-*.md`
files, `docs/design-decisions.md`, and the incident reports. This ledger deliberately carries only
what is still OPEN, plus the facts that would otherwise be lost because they live nowhere else.

---

<!-- SECTION: machine-derived | OWNER: ledger-scan (regenerate; never hand-edit) -->
## Machine-derived (re-seed from `scripts/ledger-scan.sh`; do not hand-edit)

_Re-seeded from a 2026-07-27 scan at the STAGE 4 CLOSE. RE-VERIFIED 2026-07-29 against
`bash scripts/ledger-scan.sh` -- every value below unchanged, so this is a confirmation,
not a re-seed. Re-run the scan to refresh._

- **PROPOSED / OPEN decisions (3):** D-068 (Vault substrate hardening, Roosevelt -- sole
  remainder is Q2 path selection at Roosevelt Vault design time), D-131 (node-facing DNS for
  rack-only controllers [ARCH] -- sub-4 open + the pinned DNS architectural review), D-132
  (Roosevelt per-DC MAAS topology [ARCH], operator-pinned to the next deployment).
  **D-136 was ADOPTED 2026-07-27 at option (D)** and correctly drops off the scan, as D-137
  did before it (was 4). Status lines in `docs/design-decisions.md` are the only ruling
  authority.
- **OPEN security rows:** **21** per `bash scripts/ledger-scan.sh` (was 20). SEC-025 opened
  2026-07-27 -- the NetBox web-GUI `admin` password, a HUMAN login that had never left the VM
  it was minted on, now consolidated to `~/vr1-office1-creds/`; the row covers the at-rest
  exposure the copy CREATES, not the gap it closed. (SEC-024, 2026-07-26, was the previous
  addition.) The SEC register of record is `docs/security-ledger.md`; row-level dispositions
  live THERE only (GA-R4/F3) -- this block carries pointer + count, never rows.
- **Next-free numbers:** D = **138**, DOCFIX = **206**, BUNDLEFIX = **053**. RE-SEEDED from
  `bash scripts/ledger-scan.sh` at the 2026-07-30 close: **DOCFIX-205 was assigned that day**
  (the D-117 annotation batch -- four ratified decision texts annotated, D-117's own overstated
  "FULLY EXECUTED" Status corrected, phase-6's selector-into-FQDN defect fixed). 204 remains
  2026-07-27's stale "NTP from edge" Stage-4 gate bullet.
- **Gates:** G17 OPENED 2026-07-27 by operator ruling (node-side artifact reachability split out
  of Stage 4; the gate table in `docs/CURRENT-STATE.md` is the authority).
- **Standing numbering rule:** never write an identifier-shaped token (D-/DOCFIX-/BUNDLEFIX-NNN)
  ABOVE the real high-water mark anywhere in `docs/` or `runbooks/` prose -- historically a
  decoy token in prose inflated the next-free counter (hardened in DOCFIX-174).
- **`ledger-scan` D-115 false-positive: RESOLVED.** The scan keys on the LAST `**Status:**`
  line per `## D-NNN:` block, not arbitrary body prose. The general hygiene -- trust the
  Status line over body text -- still holds.

<!-- END: machine-derived -->

---

## ROTATED 2026-07-19 (GA-R4 rule 6 -- the one-time rotation; F1 cap now enforceable)

The pre-rotation bodies (~1080 lines: Live state, OPEN WORK VR1/VR0, NetBox
write-path bugs, PINNED rulings, standing lessons, state facts, project-
completion, NetBox import narrative) moved VERBATIM to
`docs/archive/session-ledger-rotated-20260719.md`. Standing lessons + VR0
state facts were ROUTED to durable homes first (platform-traps already
carried most; juju format=line -> appendix-A; guard discipline ->
operating-discipline; VR0 cloud facts -> maas-as-built-reference.md).
Open work is machine-derived above + CURRENT-STATE.md; do not re-grow
bodies here -- sessions append BOUNDED summaries only (15-line cap).

Retained compact blocks (still-live obligations without another home yet):
- **NetBox write-path bugs:** ROUTED 2026-07-23 to
  `docs/netbox-write-path-findings.md` (the durable home; the rotated-body
  section is history). Open residue: dumper blind spot + duplicate-CIDR
  collapse unfixed, hardened fidelity re-run + fix re-verification owed --
  ALL gated on the next NetBox WRITE campaign, none current.
- **Project-completion (after D-011 passes):** consolidate the 10 per-phase
  do-documents into a v1 deploy runbook; flip repo PRIVATE (SEC-004);
  revoke/rotate SEC-005/006/007; v2-deferred: GitBucket SSH, IPv6
  dual-stack, NetBox import bundle.
- **Deployment-close consideration review -- tenant-onboarding items (PINNED
  2026-07-21, operator-directed):** the committee review of Chat's three
  tenant-onboarding inserts refuted each as written but identified corrected
  residue; the operator DEFERRED all of it to an end-of-deployment review
  (posture and end goals are shifting this deployment -- reconsider fit then;
  nothing lands before that review). Items: (1) appendix-A symptom entry for
  sub-floor OR nonexistent flavor -> cluster-create rejection (capi-helm
  thresholds are CONFIG OPTIONS, defaults 2 vCPU / 2048 MB RAM, as-built sets
  neither; the RAM error also fires for mistyped flavors; command shape
  `--public -f json`, never the deprecated `--long` -- archive R9); (2)
  appendix-A entry for tenant-net-cannot-reach-public-keystone -> nodes
  register but stall uninitialized (auth_url in the per-cluster clouds secret
  is hardcoded PUBLIC at capi-helm 1.4.0; as-built carries no interface key),
  pointing at the phase-08 D-011.3 probe, plus a per-DC precondition line in
  dc-dc-phase6 Step 12; (3) tenant contract hardening list: PROPOSED
  `-svc`-only password neutralization (admin-rotate to discarded value;
  keystone `lock_password` blocks password CHANGE, not auth; NEVER lock
  `-cluster` -- it keeps PASSWORD auth permanently per D-066); (4) flavor
  catalog marking = commercial catalog policy, operator prioritization only.
  RE-VERIFY every citation at review time -- the full verdict lives in the
  2026-07-21 disconnected-session transcript, not in a repo surface.


## ROTATED 2026-07-26 (GA-R4 rule 3 / F1 -- oldest-first, cap restored)

The eight oldest closed-session summaries (2026-07-18 through the fifth 2026-07-21
session) moved VERBATIM to `docs/archive/session-ledger-rotated-20260726.md`. The live
ledger was 413 lines against the 300-line cap -- a breach the 2026-07-25 close recorded as
OWED. Each rotated summary still points at its own archived full body; only the summaries
moved. Sessions from 2026-07-22 onward remain live below.

## ROTATED 2026-07-27 (GA-R4 rule 3 / F1 -- oldest-first, cap restored at the Stage 4 close)

The three oldest live summaries (all 2026-07-23: G12 dc1 edge->commission+merge, Stage 4
OPEN+carve+mirrors, queue pass + D-068 rulings) moved VERBATIM to
`docs/archive/session-ledger-rotated-20260727.md`. The live ledger was 317 lines against
the 300-line cap. Sessions from 2026-07-24 onward remain live below.

## ROTATED 2026-07-27 (second pass, GA-R4 rule 3 / F1 -- cap restored at the grounding-audit close)

The oldest remaining live summary (2026-07-24, caveman guardrails + DC1 proxy-build start)
moved VERBATIM to `docs/archive/session-ledger-rotated-20260727.md`. The live ledger was 305
lines against the 300-line cap. Sessions from 2026-07-25 onward remain live below.

## ROTATED 2026-07-27 (third pass, GA-R4 rule 3 / F1 -- cap restored at the grounding-audit close)

The TWO oldest remaining live summaries -- 2026-07-25 handoff-pack execution + recon +
Chat D-136 coupling, and 2026-07-25 MAAS admin-account recovery (SEC-020) -- moved VERBATIM
to `docs/archive/session-ledger-rotated-20260727.md`. The live ledger was 321 lines against
the 300-line cap; one pass left it under. (This paragraph was left MANGLED by an earlier
edit -- a sentence broke off mid-number and ran into a stray fragment of the
2026-07-25 MAAS-admin-recovery summary. Found by a 2026-07-29 sweep and repaired here;
that summary's full body is at
docs/archive/changelogs/changelog-20260725-maas-admin-recovery.md.)

## ROTATED 2026-07-27 (fourth pass, GA-R4 rule 3 / F1 -- cap restored at this close)

The oldest remaining live summary ("POST-CLOSE ADDENDUM 2026-07-26 -- D-137 ADOPTED (GA-R4; 07-18/07-21 addendum precedent)") moved VERBATIM to
`docs/archive/session-ledger-rotated-20260727.md`. The live ledger was 316 lines against the 300-line cap.

## ROTATED 2026-07-29 (GA-R4 rule 3 / F1 -- oldest-first, cap restored at this close)

The TWO oldest live summaries -- 2026-07-26 (D-137 build + committee audit + remediation)
and 2026-07-27 (creds consolidation + the STAGE 4 CLOSE-OUT) -- moved VERBATIM to
`docs/archive/session-ledger-rotated-20260729.md`. The live ledger would otherwise have
been 321 lines against the 300-line cap once this session's bookend was appended.
Sessions from the 2026-07-27 grounding audit onward remain live below.

## ROTATED 2026-07-30 (GA-R4 rule 3 / F1 -- oldest-first, cap restored at this close)

The TWO oldest live summaries -- the 2026-07-27 STAGE-5 GROUNDING AUDIT close and its
14-RULINGS post-close addendum -- moved VERBATIM to
`docs/archive/session-ledger-rotated-20260730.md`. The live ledger stood at exactly 300
lines, so the next append would have breached the cap. Rotated HERE rather than leaving it
owed, because the next session is directed straight at the juju deployment.
Sessions from the 2026-07-27 Phase-0 close onward remain live below.

## SESSION CLOSE 2026-07-30 (part 2) -- F9 CLOSED live, the reissue tool, P7, lib-identity (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **19 commits** pushed (`1ddb078..`). NO stage opened/closed. Scan unchanged: 3 decisions, SEC 21, D 138 / **DOCFIX 206** / BUNDLEFIX 053.
- **>>> OPERATOR DIRECTIVE, STANDING: THE NEXT SESSION PROCEEDS TO THE JUJU DEPLOYMENT (Stage 5), NO MATTER WHAT.** Verbatim: "we have to continue to juju deployment next session no matter what". Precondition polishing is DONE; further hardening is OUT OF SCOPE unless it blocks the deploy. This is the GA-F06 circuit-breaker made explicit -- do not spend the session on records.
- **DOCFIX-205:** Q1 WITHDRAWN -- D-117 ruled it 2026-07-13; it resurfaced only because D-117's annotation half was never executed (0 of 4) while its own Status claimed "FULLY EXECUTED". All four annotated; Status corrected. Q2 withdrawn too (D-137 fork 1 ruled 2026-07-25). **Third stale premise in one findings file.**
- **F9 CLOSED LIVE, both DCs** (gated per DC, never batched): controller certs reissued into their own zones -- `omega.dc0.vr1` / `omega.dc1.vr1`, fresh P-256 keys, CAs untouched. Capture `docs/audit/octavia-reissue-executed-20260730.txt`.
- **`octavia-pki.sh reissue` SHIPPED** (operator: "Full script minter now"): derived zone, printf config (F8 paid off), stage-assert-promote, single-value overlay surgery, backup-before-mint.
- **VALIDATION AGENTS EARNED THEIR COST.** Mutation testing deleted my three new assertions and the harness stayed GREEN every time. Six defects closed: **A15** (no keyUsage/EKU minted + passed everything), **A16** (`-days` defaults to **30** and verify had NO validity check), **A17** (overlay/workspace desync read PASS), the re-run guard REFUSING to fix broken certs, A8's vacuous negative, two overlay checks moved pre-mint.
- **F8 + F9 FIXED AT SOURCE in 1.0-GEN.c** -- the generation path would otherwise recreate both at the next DC standup. F10 handled: 13 mint-refs re-anchored single-pass by row id; S4 clean.
- **P7: the PKI is now an actual GATE** in `preflight.sh` -- headend-only, NOT EVALUATED elsewhere, rc3 mapped explicitly, and it requires the literal zone line because `verify` exits 0 on a wrong-zone cert while inert.
- **`scripts/lib-identity.sh`** -- CLOUD_NAME + CLOUD_DOMAIN in ONE file; a rebuild that renames the estate edits one file. T47 catches shell-vs-OpenTofu drift, proven able to fail.
- **Backup custody done + REGISTERED** (2 rows, manifests, `n-reissue-backup`); the secrets-storage PIN now covers this step's creds and certs. Hazard recorded: these archives carry CA issuance state -- never restore over a workspace that has issued since.
- **OWNED:** I withdrew two of my own recommendations after measuring (the vr0-dc0 retirement, and the D-137 escalation framing); pushed one red-lint commit via `;` instead of `&&`; two harness stub bugs. All corrected on-surface.
- Guard misfire tally now **SEVEN** -- it blocked a command that only TESTED it, then the heredoc documenting its own misfires. Hardening it needs no ruling (fork 1 is ruled).
- Gauntlet ALL GREEN (89) BOTH hosts; repo-lint 0 fail; octavia-pki 51/51; preflight 33/33; creds-matrix 65/65 + 5 pre-existing findings. **verify 37/0 and P7 [ok] on both DCs.**
- **NEXT (Stage 5 entry):** preflight is RED only on P5's pre-existing credential register -- decide accept-or-remediate at the gate, do not re-audit it. G17 arms at first boot. Bodies: `docs/changelog-20260730-docfix205-d117-annotation.md`, `-octavia-reissue-tool.md`. Status ONLY in CURRENT-STATE.md.

## SESSION CLOSE 2026-07-30 (part 3) -- STAGE 5 OPENED; three bootstraps; D-138 + D-132 ruled; per-DC MAAS region LIVE at dc0 (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **12 commits** pushed (`726127d..`). **STAGE 5 OPENED**, not closed. Scan: 3 decisions, **SEC 23** (SEC-026, -027 opened), **D 139** / DOCFIX 206 / BUNDLEFIX 053.
- **5 RULINGS (GA-R5, quoted):** P5 accepted; bootstrap flags **"Use both flags"**; **D-138** cloud-facing client moves INTO the DC; **D-132 q1** per-DC MAAS region; its placement **VM at utility `.6`** (extends the D-134 octet map: .4 artifact / .5 juju / .6 region).
- **THREE BOOTSTRAP ATTEMPTS, each failing one layer deeper -- every failure a real defect no record would have shown.** (1) no juju-client->node-plane path anywhere (SEC-010 + isolated libvirt nets; never existed). (2) D-138 fixed the client, then the CONTROLLER VM had NO DEFAULT ROUTE -- under-carved, added after both Stage-4 carves. (3) route fixed, agent fetched on attempt 1, then `jujud` could not reach the MAAS region API. **SEC-010 is INCOMPATIBLE with D-104-in-DC-controller + Office1 region**; per-DC regions REMOVE the requirement rather than excepting it, so **SEC-010 stays UNAMENDED**.
- **BUILT:** D-134 `.5`+v6 on BOTH controllers (was ruled-but-not-built); region VM applied dc0 (`2 add/0/0`, MACs pinned, converged ZERO DIFF); **dc0 region LIVE** -- noble + MAAS 3.7.2 + PostgreSQL 16.14 matching Office1, API **200** on `10.12.8.6:5240`, `dhcpd` INACTIVE (no DHCP conflict), jammy+noble **Synced**.
- **G17 dc0 CAPTURED, both assertions PASS** -- the one-shot window opened during a FAILED bootstrap and was taken. Found G17's own text names `chronyc`, absent on the MAAS jammy image, so the gate as written could only REFUSE.
- **CREDENTIALS (operator-directed):** 3 region secrets minted on-VM, never printed, consolidated to `~/vr1-dc0-creds/` (sha256-verified identical), **SEC-027** opened; matrix now EXPECTS them at BOTH DCs (5 rows/DC, new `region` host-role enum, 4 `vm-secret-locations` rows). **dc1's correctly FAIL S2** -- the forward register making absence detectable. Harness 65/65.
- **MEASURED, DO NOT CONFLATE:** MAAS boot images come from `images.maas.io`, NOT the DC mirror (404s on every simplestreams path -- it mirrors apt only). Three artifact classes, ONE local. **The D-135/D-107 narrowing would BREAK image sync AND bootstrap; nothing sequences it after them** (queued F10 -- highest-consequence unruled item).
- **OWNED:** ran the DC checkers on the wrong host; then re-probed egress FROM THE RACK and called the window open when the NODE does the fetching -- same wrong-host class, made right after writing that note; scoped D-138 without enumerating that `jujud` is itself a MAAS client, which is why a 4th blocker appeared after the 3rd was fixed. All corrected on-surface.
- **NOTHING IS HALF-APPLIED.** Office1 still owns all 9 dc0 nodes, correctly carved and `Ready`. Pre-migration carve captured (`dc0-maas-carve-premigration-20260730.txt`, 179 lines) -- MAAS cannot move machines between regions, so the migration must recreate it.
- **AS-EXECUTED LOG IS PARTIAL** -- the classifier refused several `script -aqe` wrapped forms; index row says so (F6). A log that looks complete is worse than one declaring its gap.
- **NEXT:** recreate topology on the new region -> DHCP handover (two servers on one segment is the hazard) -> re-enrol/re-commission 9 nodes -> re-apply statics/br-ex/tags -> re-point Juju at `10.12.8.6:5240`. dc1's region VM authored, NOT applied. Start with fresh context: the destructive half is nine nodes.
- Sweep: `docs/audit/queued-findings-20260730-stage5.txt` (F1-F12). Bodies: `docs/changelog-20260730-stage5-open.md`. Status ONLY in CURRENT-STATE.md.

## SESSION CLOSE 2026-07-30 (part 4) -- dc0 region topology BUILT 40/0; cutover BLOCKED on a permission wall (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **8 commits** pushed (`0ec9c97..b1afa42`). NO stage opened/closed. Scan unchanged: 3 decisions, SEC 23, D 139 / DOCFIX 206 / BUNDLEFIX 053.
- **STAGE 5 REMAINS BLOCKED** -- now on the DHCP cutover, refused by the harness classifier and NOT retried in an altered shape. Exact 4-command sequence, every parameter re-resolved live, is in `changelog-20260730-dc0-region-migration.md` item 10. **NOTHING half-applied** (Office1 verified read-only still `dhcp_on=True primary_rack=7chphy`).
- **NEW dc0 REGION TOPOLOGY LIVE: `dc-region-topology.sh check` = 40 assertions / 0 failed, EXIT 0.** 5 named plane fabrics, 6 spaces, 6 v4 subnets + gateways, 6 VLAN->space bindings, site tag, metal-admin dns/dynamic range. Power key installed + proven (9/9, real virsh enumerating 12 domains -- the snap ssh dir did not exist at all, so commissioning could not have powered a node on).
- **THREE TOOLS THE REPO NEVER HAD:** `maas-profile-assert.sh` (region identity by RACK IDENTITY -- a machine count is not proof), `maas-region-power-key.sh`, `dc-region-topology.sh`. A survey found the named fabrics, v4 subnets and site tag were built AD-HOC in the Stage-4 window, logged only to an as-executed file NOT in the repo -- there was nothing to re-run. dc1 now rebuilds from tools.
- **I INTRODUCED A DEFECT THAT PASSED MY OWN GATE 39/39.** The first apply CREATED the provider-public fabric and MOVED the subnet; MAAS does not bring interface links along, leaving the region VM's `enp2s0` on a subnet-less VLAN -- the same under-carve class that cost three bootstrap attempts. Live connectivity was unaffected so nothing flagged it. Fixed live + in the tool (RENAME, never move) + in the gate (stranded-link assertion).
- **A GATE WAS ALREADY RED AT HEAD:** `tests/node-vm` T8/T9 (11/66 vs assertions reading 10/60) -- commits `086c827`+`447315f` added the region VMs and the gauntlet was never re-run. PROVEN pre-existing by stashing this session's work. Re-pointed to the new invariant.
- **NEARLY RECORDED A FALSE FINDING:** a `+time=3 +tries=1` probe said the new region's BIND could not resolve externally, which would have forced keeping the cross-fiber DNS dependency. Cold-cache timeout; at `+time=5 +tries=3` it resolves everything. Node DNS now points at the DC-local `10.12.8.6`.
- **MEASURED, load-bearing:** VLAN row id 5005 is dc0 metal-admin in Office1 and `vr1-dc0-data-tenant` in the new region; `hot-kid` is `c3aqh8` there and `tw7ptw` in Office1. Ids do not cross regions. NIC->plane order recorded in `lib-hosts.sh` and is NOT `PLANE_CIDRS` order (walking that positionally strands PXE on provider-public).
- **`dc-node-v6-carve.py` FIXED, not just noted** -- it was env-blind (`export MAAS_PROFILE` silently hit OFFICE1) and tracebacked on an absent CLI. Harness 14/14.
- **DELIBERATELY NOT WRITTEN: `dc-node-carve.sh`** (60 NIC re-homes + 9 `br-ex` + 54 statics). Both defects found today were caught by LIVE runs, not fixtures, and it cannot be exercised until the nodes are re-enrolled. Its two hard inputs are now settled and recorded.
- **OWED:** wire `maas-profile-assert.sh` into `dc-plane-ipam.sh` / `maas-role-tags.sh` / `maas-node-power.sh` (all still default to `admin`, where dc1's nine nodes live); rack DNS forwarder upstream; remove the `vr1-dc0-region` profile from voffice1 (SEC-026) and Office1's dc0 power-key copy.
- Gauntlet **ALL GREEN (92)**; repo-lint 0 fail. AS-EXECUTED LOG NOT USED -- `run-logged.sh` needs an interactive shell; captures went to `docs/audit/*` and this is declared. Body: `docs/changelog-20260730-dc0-region-migration.md`. Status ONLY in CURRENT-STATE.md.

## SESSION CLOSE 2026-07-30 (part 5) -- dc0 MIGRATED to its own MAAS region: 10/10 Ready, 4 named gates green (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **24 commits** pushed (`0ec9c97..`). NO stage opened/closed. Scan: 3 decisions, SEC 23, D 139 / DOCFIX 206 / BUNDLEFIX 053. **SUPERSEDES part 4**, which closed with the cutover blocked.
- **THE dc0 MACHINE MIGRATION IS DONE.** Office1 holds **ZERO dc0 machines** (23 -> 13). The per-DC region holds **all ten, `Ready`**, shapes EXACT to D-121 Option C, 6 interfaces each, every one matched to its libvirt domain by PINNED BOOT MAC (system_id AND hostname are both re-minted -- only the MAC is stable).
- **FOUR NAMED GATES GREEN:** `dc-region-topology.sh check` **40/0**; `maas-region-power-key.sh check` **9/0** (ends on a real virsh enumerating 12 domains); `maas-role-tags.sh check` **0 missing / 0 needing / 0 not-in-MAAS**; `dc-plane-ipam.sh check` **24/0** (was 7/17).
- **DHCP CUTOVER EXECUTED** in the ruled order, verified BY PROCESS at both ends (`pgrep -c dhcpd` = 0 on the rack, 2 on the region). The permission wall was a rule that FAILED TO MATCH -- existing ask rules pin the double-quote form and the `maas admin` profile; commands used single quotes and `maas vr1-dc0-region`. Operator ruled **ask** (not allow), added to gitignored local settings.
- **THREE TOOLS THE REPO NEVER HAD:** `maas-profile-assert.sh` (region identity by RACK identity -- a machine count is not proof), `maas-region-power-key.sh`, `dc-region-topology.sh`. A survey found the named fabrics, v4 subnets and site tag were built ad-hoc in the Stage-4 window, logged only to a file not in the repo.
- **FINDINGS THAT CHANGE THE STANDUP PROCEDURE:** (1) a fresh region has **ZERO SSH keys** -- MAAS injects them into deployed machines and juju needs SSH, so this was the same signature that burned three bootstraps today; found by accident, now imported. (2) enlistment does **not** scale to 9 concurrent nodes (15 min, zero records) -- batches of three; egress/proxy/region-load all measured healthy first. (3) **never `commission` a machine whose `power_type` is unset** -- it passes every script and drops to `New`. (4) the node carve is **ROLE-DEPENDENT**: role nodes get 6 planes + `br-ex`; the juju controller gets 2 planes, no `br-ex`.
- **A GREEN GATE ON AN INCOMPLETE STATE:** `maas-role-tags.sh` PASSed while all ten still lacked `openstack-vr1-dc0` -- it is not in its `ROLES` set and has no creator in the repo. `bundle.yaml` places on it and `dc-node-v6-carve.py` dies without it. Caught ONLY by diffing against the pre-migration snapshot, which earned its keep.
- **OWNED -- five instrument errors, all the same shape** (a confident negative from an under-checked instrument): a cold-cache DNS probe, a `head -5`-truncated listener list, a nine-day-stale serial log read as current, a bad elapsed-time estimate, and counting DHCP renewals as cloud-init progress. Also batched nine destructive deletes into a loop (the guard stopped me; hard rule 3 forbids it), and twice over-claimed in the records -- writing the re-enrolment ordering as proven before any machine reached `Ready`, and "no `New` detour" when I had aborted them there myself. All corrected on-surface.
- **NEXT, and it is the ONLY thing between here and the deploy:** `scripts/dc-node-carve.sh` does not exist -- 60 NIC re-homes + 9 `br-ex` + 54 v4 statics. Its hard inputs are now MEASURED and guarded: `NIC_PLANE_ORDER` (NOT `PLANE_CIDRS` order -- walking that positionally strands PXE on provider-public), the role asymmetry, and `carve-host-interfaces.sh:211-236`'s `build_ovs_brex()` call sequence. Then `dc-node-v6-carve.py --profile vr1-dc0-region`, then re-point Juju at `10.12.8.6:5240` (needs a region-scoped API key + `juju unregister vr1-dc0-controller` on the rack).
- **CLOSE SWEEP: `docs/audit/queued-findings-20260730-dc0-region-migration.txt` (F1-F10).** THREE items lived ONLY in the transcript: **F1** this session's permission rules exist only in GITIGNORED `settings.local.json` (verbatim text now recorded, incl. a broad `Bash(ssh vr1-dc0-rack *)` auto-added by an approval and worth review); **F2** Office1 STILL registers a rack controller on the dc0 rack (`vvr1-dc0`/`7chphy`) and still holds the region VM's machine record; **F10** the five instrument errors as one pattern. F9 carries the next session's first commands and every measured input the carve tool needs.
- Gauntlet **ALL GREEN (92)**; repo-lint 0 fail. Body: `docs/changelog-20260730-dc0-region-migration.md` (24 items). Status ONLY in CURRENT-STATE.md.

## SESSION CLOSE 2026-07-31 -- dc0 node carve COMPLETE, controller BOOTSTRAPPED, deploy blocked on prefer-ipv6 (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **11 commits** pushed (`a859352..80510f4`). NO stage opened/closed. Scan: 3 decisions, **SEC 25** (SEC-028, -029 opened), D 139 / **DOCFIX 207** / BUNDLEFIX 053.
- **`scripts/dc-node-carve.sh` SHIPPED** (harness 48/48) -- the last no-tool gap. **dc0 v4 carve COMPLETE 10/10, named gate 134/0**, and the diff against the pre-migration capture is **EMPTY**: every node's six legs match Office1 address for address. v6 carve **54/54**; the controller's own v6 restored separately (the tool walks only the nine tagged role nodes).
- **>>> JUJU CONTROLLER LIVE: `vr1-dc0-controller`, active/idle. <<<** FOURTH attempt, first success; the three failures were each a real defect one layer deeper, closed by D-138, the under-carve fix, and D-132 q1 respectively. Agent binaries resolved on attempt 1.
- **Model `vr1-dc0` created, spaces gate PASS (6/6), `apt-mirror` set** to the DC mirror -- key VERIFIED against the live client, not guessed; URL asserted on `dists/jammy/Release` content.
- **2 RULINGS (GA-R5, quoted):** *"Mint juju-vr1-dc0 on the new region (Recommended)"*; *"Copy dc0's PKI overlay to the dc0 rack (Recommended)"*. Both registered BEFORE use -- **SEC-028** (juju service credential; `vm-secret-locations` gained its first `rack` rows) and **SEC-029** (PKI overlay residency). Key proven to AUTHENTICATE before bootstrap, never printed, sha256-verified at every hop.
- **DOCFIX-206: Step 2.0's credential gate was NOT region-scoped, and I hit it** -- credential listed, folder present, user existed, all in OFFICE1. The SKIP branch would have led to an auth failure that reads as a network fault.
- **>>> BUNDLE DEPLOY ATTEMPT 1 FAILED; NOTHING IS HALF-APPLIED (model measured EMPTY). <<<** `unknown option "prefer-ipv6"` on barbican. **The dry-run PASSED and the deploy did not** -- `--dry-run` does not validate config option NAMES. Measured plan is **56 apps / 108 relations**, not the 50/97 preflight still quotes.
- **Root cause measured against CHARMHUB's own schema: 7 of 13 VIP charms declare `prefer-ipv6`, 6 do not**, and barbican never had it at ANY channel -- so R2's uniform application was never valid. **RULED: *"Research what those 6 charms do with v6 VIPs first (Recommended)"*** -- overlay NOT edited, R2 NOT amended. **STAGE 5 IS BLOCKED ON THAT RESEARCH.**
- **FINDING, logged not fixed: `preflight.sh` is REGION-BLIND** -- 19 false negatives for dc0 ("not enrolled in MAAS") because it defaults to the Office1 profile. `MAAS_PROFILE=vr1-dc0-region` drops them to zero. A blanket refusal would be WRONG: dc1's nodes still live in Office1.
- **Mutation testing earned its cost again:** a first pass proved less than it looked (deleting an assertion's MESSAGE only proves it exists), and the sharper pass found the `br-ex` static compare **could not fail**. Added a fixture, re-proved the kill.
- **OWNED -- three instrument errors, all one shape:** a capture parsed at the wrong field, a `systemctl` poll racing an async start, and a charmhub query with a wrong field name returning a uniform "no config" across 13 charms. The last two were caught by the SHAPE of the answer being implausible, not by discipline; that detector is now in auto-memory.
- Gauntlet **ALL GREEN (93)**; repo-lint 0 fail; creds-matrix 65/65. Permission allowlist +12 read-only rules (all pinned to check/verify/assert/plan subcommands).
- **NEXT:** the prefer-ipv6 research, then re-deploy. Steps 1-3.5 and the controller are DONE and need no repeat. Body: `docs/changelog-20260730-dc0-node-carve.md` (11 items). Status ONLY in CURRENT-STATE.md.

## ROTATED 2026-07-31 (GA-R4 rule 3 / F1 -- cap restored at this close)

The oldest closed-session summary moved VERBATIM to
`docs/archive/session-ledger-rotated-20260731.md`. The live ledger stood at 295 lines and
this close's summary would have breached the 300-line cap.

## SESSION CLOSE 2026-07-31 -- Stage 5 dc0: prefer-ipv6 ruled, deploy succeeded then torn down for a clean rebuild (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **45 commits** pushed. NO stage opened/closed. Scan: 3 decisions, **SEC 26**, D 139 / DOCFIX 207 / BUNDLEFIX 053.
- **8 RULINGS (GA-R5, all utterances quoted):** prefer-ipv6 dropped from the six, then from ALL thirteen; P5's five new findings accepted; **option D** (explicit base on 56 apps AND a clean model); jammy-backports synced; UCA points in-DC; snaps get an in-DC forward proxy; and the MAAS proxy declined in favour of the owned one.
- **THE RESEARCH INVERTED ITS OWN PREMISE.** Read from the charm ARTIFACTS: `prefer-ipv6` is NOT what makes HAProxy bind `:::port` -- that is gated on the kernel `disable_ipv6` sysctl, and pacemaker picks IPv6addr by family detection. Neither consults the option. vault ships no haproxy at all (`[::]:8200` hardcoded).
- **>>> THE BUNDLE DEPLOYED (attempt 3, `Deploy of bundle completed.`) <<<** after three failures each one layer deeper: unknown option; a missing `policies/overrides.zip` on the client host; and a bundle relying on `default-base` being deployable exactly ONCE.
- **FOUR ARTIFACT DEFECTS, each masked by the one before it** (`apt-get update --error-on=any` fails on the first bad source): backports 404, upstream UCA unreachable, `prefer-ipv6` fatal on containers with only link-local v6, and snaps with no path. Units tracked 22 -> 17 -> 4 -> 7-in-one-class.
- **THE v6 HALF IS CARVED AS ADDRESSES BUT NEVER MADE OPERATIONAL** -- all six v6 plane subnets carry ZERO allocatable ranges, the rack has NO global v6 at all, the mirror does not answer on v6, and nodes have no v6 default route. Operator: dual-stack was "only a safety net". DC1 carries the IPv6 build; DC0 folds it back.
- **TEARDOWN STALLED AND WAS FORCED.** All 26 agents were `stopped`, so no hook could run -- a fixed point, not backoff. `--force` cleared it; NOTHING was stranded. All nine nodes renamed to their ruled `vr1-dc0-<role>-NN` by pinned boot MAC and back to `Ready`.
- **SNAP PROXY BUILT AND INDEPENDENTLY REVIEWED, NOT APPLIED.** squid at the utility `.4:3129`, allowlisted, 53/53 mutations killed; review says the ruled design is HONOURED on all four counts and logs FOUR real bugs. Nothing proven end to end.
- **OWNED:** a red-lint push (`| tail` masked the exit code); a `git add -A` that swept an agent's files into an unrelated commit; two instrument misreads (a `juju models` summary read as progress, and stale unit logs read as current); and a wrong "immutable system_id" claim the operator corrected.
- **NEXT:** apply the review cuts, re-run the mutation pass (S8 invalidates it), confirm BUG-3's deny shape once, install the proxy, then `add-model` + spaces + `deploy`. Sweep: `docs/audit/queued-findings-20260731-stage5-deploy.txt` (11 FIRST SURFACE). Bodies: `docs/changelog-20260731-prefer-ipv6-research.md`. Status ONLY in CURRENT-STATE.md.

## ROTATED 2026-08-01 (GA-R4 rule 3 / F1 -- cap restored at this close)

The oldest closed-session summary/summaries moved VERBATIM to
`docs/archive/session-ledger-rotated-20260801.md`. The live ledger stood at 296 lines
and this close's summary would have breached the 300-line cap.

## SESSION CLOSE 2026-08-01 -- snap proxy LIVE, D-139 adopted, IPv6 PROVEN on the node planes (bounded, GA-R4)

- Branch `dc-dc-stage5-preconditions`, **8 commits** pushed (`d555b90..844b2e4`). NO stage opened or closed. Scan: 3 open decisions, SEC **26** (none opened this session), D **140** / DOCFIX 207 / BUNDLEFIX 053.
- **THE dc0 SNAP PROXY IS LIVE.** `check dc0` PASS exit 0, 16 assertions, re-verified independently of the agent that installed it -- and **a real snap payload fetched through it** (HTTP 206, first bytes `hsqs`). No longer fixture-green. BUG-3 confirmed by measurement (no assertion changed); BUG-4 fixed at BOTH sites where the review had named one.
- **D-139 ADOPTED** -- two rulings taken in separate exchanges: IPv6-only on the east-west planes, and the whole plane carve moved to GUA on the octet map VR0 and Willamette already use.
- **THEN ITS DECIDING REASON WAS REFUTED BY MY OWN VERIFICATION** -- glibc 2.35 implements RFC **3484**, not 6724, so ULA and GUA are EQUAL at precedence 40. Put back to the operator rather than quietly kept; ruling CONFIRMED on its other grounds and the dead rationale STRUCK IN PLACE, not deleted.
- **RULING A IS NOT ACHIEVABLE at current charm revisions** -- `ceph-osd` fails a v6-only plane in BOTH directions, `hacluster` still ships `ip_version: ipv4`, OVN documents encap as IPv4-only. Narrowed by ruling ("B plus C") to a storage+replication experiment plus the upstream fix; LP draft written, operator files it.
- **>>> IPv6 IS PROVEN ON THE dc0 NODE PLANES <<<** 6/6 addresses live on the NICs, 0% loss node-to-node on all six planes, ND resolving from a COLD neighbour table. Gate **G19** built AND run live. **IPv6 is also already load-bearing: node time sync runs over `fd50:840e:74e2:220::6`, stratum 3.**
- **THE RECORDED ROOT CAUSE OF THE v4-ONLY CONTAINERS WAS WRONG.** MAAS has 100% of every v6 `/64` available; the real limit is juju taking `addrs[0]` from an unsorted query, family-blind, with no knob (LP #1723240, open since 2017). Corrected per GA-R1 C2.
- **THE "JUJU CLIENT BLOCKER" WAS NOT ONE** -- the client is live and registered on the rack, exactly where D-138 puts it. Corrects this repo's own 2026-07-31 finding 9d.
- **D-139's OWN EXECUTION LIST WAS DEFECTIVE AND IS REPLACED.** `dc-node-v6-carve.py` pivots on IPv4 existing, so run after v4 removal it would carve four fewer planes per node **and exit clean**.
- **OWNED:** my BUG-1 fix was wrong (a secondary alias is never the kernel's chosen source); I scoped the v6 experiment wrong (ceph couples storage+replication); I stated an agent's input source wrongly; I scoped a research agent with no repo path, so 355 lines landed in `/tmp` and needed rescuing; and one commit went red on ASCII-only em-dashes.
- Gauntlet **ALL GREEN (96)**, repo-lint 0 fail, `d139-gua-carve` 71/71, `dc-node-v6-verify` 55/55. **voffice1's clone is 36 commits BEHIND** -- no loss, but a live hazard on the Plane-2 host.
- **AMENDED AFTER THE BOOKEND (2026-08-01):** both DC containment VMs resized **416 -> 480 GiB** through tofu (operator: *"Option 2 look sgood me"*), **128 GiB swap** added (operator-run), and preflight gained **gate P8, substrate drift** (harness 33 -> 38, proven live at zero diff). Host used **809 -> 63 GiB**. Found: dc0's MAAS region and juju controller have `autostart=disable`. Bodies: changelog items 21-22.
- **NEXT:** apex CREATE-only push (tool built, independently reviewed, dry-run byte-identical), then the bundle deploy -- its blockers are cleared. `network-get` on a v6-only bound space is still unmeasured and gates the v4-removal experiment. Sweep: `docs/audit/queued-findings-20260801-stage5-ipv6-d139.txt` (**6 FIRST SURFACE**). Body: `docs/changelog-20260731-snap-proxy-apply-ipv6.md`. Status ONLY in CURRENT-STATE.md.
