# Changelog 2026-08-05 -- Task #2: D-020 vault-metal-only amendment committed + provider-bundle-check harness reconciled

Session scope (GA-R2, one changelog per session). Picks up the durability debt the
2026-08-05 (part 2) close left explicitly open: three uncommitted files on the vcloud
working tree (the RULED-but-uncommitted D-020 vault-metal-only amendment) plus a RED
provider-bundle-check harness (6 fail) and repo-lint L10. All offline; no live-cloud
mutation this session. Advisor-reviewed before commit.

Branch: `dc-dc-stage5-preconditions`. Stage 5 dc0 remains OPEN.

## Context (measured this session, not carried from prose)

- HEAD `a5cf01a` did NOT contain the D-020 2026-08-05 amendment despite its commit
  message naming "vault metal-only" -- the work was uncommitted on disk. Verified:
  `git show HEAD:docs/design-decisions.md | grep -c "D-020 -- AMENDMENT (2026-08-05)"` = 0.
- BUNDLEFIX-053 (committed `ae06c72`) folded the FULL 13-subordinate HA chain
  (`vault-hacluster` included) and `num_units: 3` into `bundle.yaml`. Confirmed:
  13 `:ha` relations + 13 hacluster subordinates in `bundle.yaml`; `num_units: 3` on
  mysql/ovn-central/ceph-mon (so the harness rolesep builder must be num_units-aware).
- Real checker output MEASURED on the merged dc0 deploy input: `12 clustered VIP(s)
  ... (0 dual-family)`, `13 hacluster principal(s) all carry a VIP`, vault excluded
  from the triple count via its own metal-only [ok] line. Dual fixture: `12 ... (12
  dual-family)`. These are the numbers the harness assertions were re-pointed to.

## Items

### 1. `scripts/provider-bundle-check.py` -- vault metal-only exception, HARDENED (DOCFIX-210)
- WHAT: the vault VIP branch (invariant 9c region) now asserts vault is the metal PAIR
  (metal-admin + metal-internal), NO provider-public leg, **per-DC and family-aware**
  against the `--dc` PROVIDER/ADMIN/INTERNAL bands -- replacing the first draft's
  hardcoded `10.12.4.` literal (which was blind to dc1's `10.12.64.x` provider). The
  metal pair is STILL band-checked (50-99) and STILL registers in `octet_owner` for
  uniqueness. Docstring invariant 2 records the exception.
- WHY: the ruled D-020 amendment (2026-08-05) makes vault the single non-triple
  clustered VIP. **Advisor-caught defect in the first draft:** its `continue` ran
  BEFORE the band check and `octet_owner[h] = n`, silently disarming octet-uniqueness
  for `.61` -- a second app given `.61` would collide with nothing and pass (a gate
  that cannot fail; this repo's named failure mode). PROVEN: the reconstructed draft
  returns rc=0 on a glance-takes-vault's-`.61` input (gap); the fixed checker returns
  rc=1 `VIP last octet .61 shared by glance and vault` (caught).
- REVERT: `git checkout HEAD -- scripts/provider-bundle-check.py` restores the
  no-vault-exception version (vault treated as a normal triple; the live metal-only
  overlay would then FAIL the check -- so revert the overlays in item 3 together).

### 2. `tests/provider-bundle-check/run-tests.sh` -- harness reconciled to 58/0
- WHAT: (a) rolesep fixture builder is now num_units-aware -- 3-unit HA apps land on
  lxd:0/1/2, single-unit on lxd:0, guarded with a `SystemExit` if any `to:` app has
  num_units>3 (only 3 control machines exist). Fixes T9, broken by BUNDLEFIX-053's
  `num_units: 3` against the old `to:[lxd:0]` pin. (b) Count assertions re-pointed to
  the measured values: T16 `12 -> 13` hacluster principals (vault-hacluster now in the
  BASE bundle, dissolving the old T16/T17 pre/post-overlay ordering distinction --
  comment re-pointed; the dc-ha-scaleup.yaml-now-redundant question LOGGED as a Task #1
  finding, not fixed here per hard rule 1); T19 `13 -> 12` clustered VIPs; T21/T45
  `13 -> 12` clustered + `13 -> 12` dual-family (vault leaves the triple count). (c)
  NEW failing-direction fixtures T54 (provider leg on vault FAILS), T55 (triple FAILS),
  T56 (another app taking vault's `.61` octet FAILS -- guards the item-1 defect). Result
  49/6 -> 58/0 ALL PASS.
- WHY: a bundle change breaks the check's harness even when the script is untouched;
  every new assertion must be proven able to FAIL (T54-56 have failing fixtures).
- REVERT: `git checkout HEAD -- tests/provider-bundle-check/run-tests.sh`.

### 3. `overlays/vr1-dc0-vips.yaml` + `overlays/vr1-dc1-vips.yaml` -- vault metal pair, RE-RENDERED
- WHAT: dc0 vault vip -> `"10.12.8.61 10.12.12.61"`; dc1 vault vip the dual-family sextet
  -> the dc1 metal pair `"10.12.68.61 10.12.72.61"` (provider leg + the D-141-deprecated
  `fd50:` ULA metal v6 legs dropped). Both stay a v4 metal pair THROUGH a future D-141
  rule-3 promotion until a new amendment. **These overlays are GENERATED by
  `render-dc-overlays.py` from `render/values/*.yaml` (D-136), NOT hand-editable** -- see
  the FINDING below. Both files were re-rendered from the values files (items 6-7); the diff
  vs HEAD is EXACTLY the one vault line in each, no other drift.
- WHY: implements the D-020 amendment.
- REVERT: revert items 6-7 (values) and re-render, or `git checkout HEAD -- overlays/vr1-dc*-vips.yaml`.

### 6. `scripts/render-dc-overlays.py` -- vault metal-only render exception (DOCFIX-210)
- WHAT: `render()` gains a `name == "vault"` branch that drops the provider-public leg AND
  forces v4 (no v6 append) so vault renders exactly the 2-leg metal pair in BOTH v4 and
  dual-family DCs. Named exception, mirroring provider-bundle-check.py + pre-flight-checks.sh
  -- fires for every DC's values file (the ruled Roosevelt delta), not a per-DC flag a new
  DC could omit.
- WHY: the overlays are rendered artifacts; the vault metal-only change is only lawful
  through the renderer (render-drift forbids hand-editing).
- REVERT: `git checkout HEAD -- scripts/render-dc-overlays.py` (then vault re-renders as a triple).

### 7. `render/values/vr1-dc0-vips.yaml` + `render/values/vr1-dc1-vips.yaml` -- vault comment
- WHAT: vault's per-app `comment:` field updated (dc0) / added (dc1) to the D-020 metal-only
  note. The comment is part of the rendered bytes, so it belongs in the values file, not the
  overlay. Octet 61 unchanged; vault stays in the apps list (T12 count still 13).
- WHY: carries the amendment provenance into the rendered overlay.
- REVERT: `git checkout HEAD -- render/values/vr1-dc0-vips.yaml render/values/vr1-dc1-vips.yaml`.

### 8. `scripts/pre-flight-checks.sh` -- CHECK 1 vault metal-only exception
- WHAT: the awk VIP validator gains app-name tracking (2-space header lines) and a
  `name == "vault"` branch accepting exactly the metal PAIR (metal-admin + metal-internal,
  octet-aligned, in-band, no provider column) as OK. `VIP_COUNT_EXPECT` stays 13 (vault still
  joins the OK count). Without this, vault's 2-leg pair was reported MALFORMED (not 3/6 IPs).
- WHY: this is P4's live VIP validator; it is a fourth consumer of the vault VIP shape.
- REVERT: `git checkout HEAD -- scripts/pre-flight-checks.sh`.

### FINDING (report to operator) -- overlays are RENDERED; render-drift was ALREADY red pre-session
- The overlays are generated from `render/values/*.yaml`; `tests/render-drift` byte-compares
  every values file against its overlay and FAILS with "re-render, do not hand-edit the overlay."
  Task #2's guidance ("edit overlays/vr1-dc1-vips.yaml vault->metal pair") and the part-2
  session's dc0 hand-edit BOTH violated this. **render-drift + render-dc-overlays + pre-flight-checks
  were red at the part-2 close but NOT in its "RED AT CLOSE" list** (which named only repo-lint L10
  + provider-bundle-check). The part-2 dc0 overlay edit was non-reproducible from the moment it was
  made. Resolved here by teaching the renderer (item 6) + values (item 7) and re-rendering, per the
  advisor -- the amendment is RULED, so this is OPS implementation, not a new decision.

### Harness updates for items 6-8
- `tests/render-dc-overlays`: T15 count 13->12 dual-family (vault excluded) + vault metal-only
  [ok]; NEW T15b proof-of-teeth (renaming vault restores the 6-leg sextet -- the exception is
  name-keyed). T3b/T14 heal on re-render. 20/3 -> 24/0.
- `tests/render-drift`: heals on re-render (2/2 -> 4/0).
- `tests/pre-flight-checks`: T27/T28 heal; NEW T28b proof-of-teeth (vault as a triple FAILS
  CHECK 1). 29/2 -> 32/0.

### 4. `docs/design-decisions.md` -- D-020 amendment text finalized
- WHAT: the RULED 2026-08-05 D-020 amendment (vault metal-only) CONSEQUENCE section
  augmented -- dc1 overlay now done (not "when it exists"), the per-DC/family-aware
  checker + retained band/octet gates recorded, and the "stays a v4 metal pair through
  D-141 rule-3" durability note added so a later dual-stack session does not "restore"
  the dropped v6 legs as an oversight.
- WHY: the amendment was operator-ratified ("Ratify as written") but uncommitted.
- REVERT: `git checkout HEAD -- docs/design-decisions.md`.

### 5. `docs/CURRENT-STATE.md` -- D-121 12/14 -> 14/14 + D-020 amendment; L10 satisfied
- WHAT: the D-121 HA scale-up status updated to 14/14 (keystone + vault added), with the
  D-020 vault-metal-only landing recorded. **EVIDENCE GAP DECLARED (GA-R1 rule 2):** the
  14/14 count is OPERATOR-ATTESTED + part-2 records, NOT yet a committed `juju status`
  capture -- flagged OWED, riding the F4 live-verify sweep. Not papered over.
- WHY: L10 requires the design-decisions Status-line change to touch CURRENT-STATE in the
  same commit; and the committed status still read 12/14.
- REVERT: `git checkout HEAD -- docs/CURRENT-STATE.md`.

## Verification (this session)
- `provider-bundle-check` 58/0; `render-dc-overlays` 24/0; `render-drift` 4/0;
  `pre-flight-checks` 32/0. Full gauntlet re-run after the renderer work.
- Draft-vs-fixed collision proof captured (rc=0 draft / rc=1 fixed).
- Re-rendered overlays diff vs HEAD = exactly the one vault line each (no other drift).
- `scripts/repo-lint.sh` -> 0 fail (1 pre-existing L1 non-ASCII warn, unchanged).

## Owed / NOT done this session (out of Task #2 scope)
- Live `juju status -m vr1-dc0` capture backing 14/14 (F4 sweep, next live step).
- F8 ceph-radosgw "Services not running: radosgw" verify; F9 dc0 rack repo-stage staleness.
- Task #1 post-wave bundle/overlay review, incl. whether dc-ha-scaleup.yaml is now redundant.
