| 2026-07-28 |

LIVE: node IPv6 carve both DCs -- 108 links, IPv4 untouched, 18 Ready unchanged
...
Operator-gated dc0 then dc1. 54 applied per DC, 0 errors, read-back 54/54 each.
IPv6 links 0 -> 108; IPv4 links 108 unchanged; 18 Ready unchanged. Both
dc-node-v6-carve gates PASS and dc-plane-ipam still reads 24/24 on both DCs.
The octet mirror holds across all six planes per node -- superb-piglet .121 ->
::121 everywhere, big-trout .100 -> ::100. enp2s0 correctly received nothing: the
D-100 raw provider NIC has no v4 link to mirror, and br-ex carries provider-public
instead, on the node /64 rather than the VIP /64. That falls out of deriving from
"already carries v4" rather than naming planes in a table.
The gate discriminated rather than agreeing with what it found -- it flipped dc0
to PASS while dc1 still read FAIL, before dc1 was carved.
This closes the node half of step 3, which was the correction the operator's
octet-mirror question surfaced: v6 does not arrive with the subnet, because MAAS
mode=static means an explicitly configured address.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

Node IPv6 acquisition RULED (MAAS static assignment); STEP 3 COMPLETE
...
Operator, exact utterance: "MAAS static assignment is correct". Recorded on the
D-134 R4 amendment. The question offered MAAS static assignment, DHCPv6, or
SLAAC/RA -- materially different, only the first already satisfied.
It CLOSES work rather than opening it: the v6 carve already applied is sufficient
for node addressing; the rack-bridge v6 legs are NOT needed and were not applied,
so dc-rack-net.sh's LEGS table stays v4-only; dhcpd6 staying off is correct rather
than a gap; and no plane needs a router advertising RAs, so the DC edges take no
new role. It mirrors how v4 node addressing already works here.
RESOLVES U17. Its "the rack bridges need v6 too" was a correct observation on the
data path, but under static assignment nothing consumes a rack v6 leg, so the
widening it proposed does not follow -- the observation held, the conclusion did
not, which is this project's standing lesson about lens findings.
Verification owed at Stage-5 first boot, since nodes are powered off: PROPOSED,
not adopted, a fourth G17 assertion that a booted node carries a global v6 address
from its plane's /64 in the ruled ::100-::200 band. Amending a gate row is the
operator's call, so G17's three assertions are unchanged here.
STEP 3 COMPLETE. MAAS, lib-net and the apex all carry the ruled values. D-134's
bands and the D-020/R11 VIP set are now artifacts rather than prose. Machines 18
Ready + 2 Deployed unchanged across every mutation. Gauntlet ALL GREEN (83).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

LIVE: apex populated -- bands + 156 VIP objects, both DCs, read-back clean
...
D-136 option (D) rules the apex be populated first and the renderer generated from
it. Applied both DCs: 12 ip-ranges + 78 ip-addresses each, read-back 12/12 and
78/78, errors=0, idempotent re-run plans zero. Apex ip-ranges 3 -> 27,
ip-addresses 4 -> 160 (IPv6 0 -> 78), 156 VIP objects.
Derived rather than hardcoded except the ruled app->octet map: planes from
lib-net, v6 prefixes read from the apex itself, VIP legs computed as the three
plane bases + octet (not VIP_PREFIX_*, which lib-net unsets for dc1 per R9), and
the v6 host part as the textual octet mirror with the provider leg in the
dedicated GUA VIP /64.
vault .61 and designate .62 included though ruled-but-not-built -- reserving a
planned address is what an IPAM apex is for, and it spares a second reconciliation
when the renderer emits them.
CORRECTION TO D-136: its prose says sandbox-fidelity-check.py "gates every apex
reconciliation this coupling depends on". Measured, that script compares two JSON
dumps and touches no NetBox -- it validates the SEEDER against the upstream draft,
not apex writes. Read-back is the real verification for a write. The extension is
still owed for the sandbox loop but was not a blocker here.
A live-vs-dump shape difference bit the first version: the live API returns
scope.name as the display name ("VR1 DC0") with scope.slug as the site key, while
the repo's dumps normalise name to the slug. Built against the dump, the tool
matched zero prefixes live and REFUSED rather than silently writing nothing -- the
refusal is what surfaced it. Now matches slug-then-name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

dc1 FIP pool applied -- R4's ruled scope now complete for BOTH DCs
...
Operator: "Rule dc1 FIP pool 10.12.65.0-10.12.67.254". Validated before recording
-- inside 10.12.64.0/22, 767 addresses (identical in size to dc0's), no overlap
with the D-134 bands. Applied 1/1, read back.
Final state both DCs: check pass=24 fail=0 AND reserve planned=0 skipped=19
errors=0 -- converged and idempotent. ipranges 3 -> 29, both FIP pools reserved.
Machines 18 Ready + 2 Deployed unchanged throughout.
The ruling narrows dc1's deliberate unset list by exactly one pair; the VIP group
(R9: generated from the overlay) and the D-133-retired metal-internal stack stay
unset, now with harness guards so a future edit cannot widen it silently.
Two stale assertions RE-POINTED rather than deleted, per the standing rule against
going green by removing an assertion: dc-selector asserted dc1 unsets
FIP_POOL_START, and dc-plane-ipam T20 asserted dc1 refuses the pool. Both are
replaced with stronger checks -- the ruled value, the still-unset guards, and that
dc1 plans its OWN pool and never dc0's, which is what proves the tool reads a
per-DC value rather than a shared literal.
dc-selector 48 -> 51 checks. Gauntlet ALL GREEN (83).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

LIVE: D-134's bands now EXIST -- both DCs at pass=24 fail=0, from a 6/18 baseline
...
Operator-gated, dependency-ordered: dc1 v6 carve (6 subnets), dc0 reserve (13
ranges), dc1 reserve (12 ranges). Each pre-apply re-verified in the same session;
every write read back. Subnets 18 -> 30, ipranges 3 -> 28. Machines 18 Ready + 2
Deployed unchanged throughout -- no node, no tfstate, no running service touched.
This closes the "RULED IS NOT BUILT" finding for D-134: its band table had been
prose since 2026-07-23 while MAAS held zero reserved ranges.
FINDING -- MAAS already reserves the entire low IPv6 block. Every explicit v6 band
create failed with "Requested reserved range conflicts with an existing range".
Measured via `subnet reserved-ip-ranges`: MAAS auto-reserves ::1-::ffff:ffff on
every IPv6 subnet plus :: per RFC 4291 s2.6.1, with allocatable space starting at
<prefix>:0:1::. The ruled bands sit entirely inside it, so the write is impossible
AND unnecessary. R4's "v6 bands as a second pass" is not executable in MAAS and
need not be; the tool now verifies coverage instead of writing. Execution-level
correction -- R4's intent is satisfied. Same on ULA and GUA.
Harness T19 asserted a literal string the fix removed. Re-pointed at the surviving
invariant (textual 4-99, never hex 4-63) rather than deleted, per the standing rule
against going green by removing an assertion. 25/25, gauntlet ALL GREEN (83).
dc1's FIP pool still correctly REFUSED -- unset by design, so R4 cannot close for
dc1 until ruled.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

LIVE: dc0 v6 plane carve applied -- 6 MAAS subnets, each read back
...
Operator-gated ("Run carve-v6 vr1-dc0 --commit"). dc0 only; dc1 not authorised
and not touched.
Pre-apply re-verified in the same session per the G8 precedent -- plan unchanged
at 6/1/0 -- then applied: 6 applied, 1 skipped, 0 errors, every create READ BACK
on its intended vlan. MAAS subnets 18 -> 24, v6 1 -> 7.
Each v6 plane landed on the SAME vlan as its v4 twin, so the plane is genuinely
dual-stack on one L2 rather than a parallel fabric. The provider GUA VIP /64 was
deliberately not created -- it holds hacluster-managed API VIPs, not node
addresses, so MAAS never allocates from it.
Idempotency proven LIVE rather than only in fixture: --commit ran twice (the
second to read the script's true exit code rather than a pipeline's) and the
post-state carries zero duplicate CIDRs.
Nothing else moved: 18 Ready + 2 Deployed unchanged, no node touched, no tfstate
involved, dc1 still measures 6 v6 planes absent. The gate now reports dc0's six
v6 planes [ok]; its remaining reds are the 12 unreserved D-134 bands.
Changelog item 9 carries the revert, with the 2026-07-21 pod-delete cascade
lesson attached: read occupancy BEFORE deleting a subnet.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

R15(2): the gauntlet pins a harness MANIFEST, completing the R15 gate work
...
tests/HARNESS-MANIFEST (82 names) plus a drift gate in run-tests-all.sh, with
--record-manifest for deliberate re-recording. The zero-floor already existed;
what was missing was any pin on WHICH harnesses ran, so a renamed or deleted
harness was neither run nor failed while the gauntlet still printed ALL GREEN.
The figure 81 existed only as prose.
Proven able to fail on both cases, including the one a bare count cannot catch:
renaming a harness is reported, and renaming one WHILE adding a decoy -- so the
count holds at exactly 82 -- is still caught, naming both the missing and the
unpinned entry. That case is why the ruling chose a manifest over a count.
Sequenced LAST of R15's three deliberately: the ruling warns a manifest must be
seeded from a verified tree, not from whatever is on disk. Seeded here from a
tree with the gauntlet ALL GREEN and repo-lint 0 fail in the same session.
The gate runs only on a full gauntlet -- a filtered run legitimately executes a
subset. A missing manifest is itself a FAIL, since ALL GREEN would otherwise be
unfalsifiable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

R15(3): preflight no longer fails open on an unexpected sub-gate exit
...
Reproduced first, in isolation with the exact note() from preflight.sh:27 --
sub-gate exits 127 (command not found), 126 (not executable), 130 (interrupted)
and 3 (undefined) ALL left "PREFLIGHT: PASS -- clear to add-model / deploy".
This is the gate that authorises the deploy.
rc=2 handled PER GATE rather than blanket-remapped, because R15's stated
rationale is only partly right on measurement: repo-lint and pre-flight-checks
both DOCUMENT rc2 as a legitimate warning, while provider-bundle-check and
creds-matrix use it only for could-not-evaluate. A blanket 2->FAIL would have
turned the standing L1 legacy-ASCII warn into a deploy blocker. channel_assert
uses 2 for BOTH meanings, so it is ambiguous in the checker and kept WARN; its
missing-PyYAML root cause is caught by P2 anyway, which shares it and is strict.
Flagged rather than guessed -- fixing that belongs in channel_assert.py.
Harness 16/16 (was 10): T11-T13 lock the fail-open cases, T14/T15 lock
could-not-evaluate -> FAIL, and T16 locks the non-over-correction (P1 rc=2 stays
WARN). Gauntlet ALL GREEN (82). Real-repo preflight still exits 1 for the same
known reasons with no new failure classes fired.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

R15(1): repo-lint can no longer report PASS over zero files
...
Reproduced before fixing, both cases printing "PASS: repo lint (0 fail, 0 warn)"
with exit 0: `repo_lint.py --recordd` and `repo_lint.py /nonexistent-path`. Only
the two known flags were stripped, so any other --flag became argv[0] i.e. the
ROOT; it resolved nowhere, rglob yielded nothing, and zero findings read as
clean. This is the gate whose "0-fail" every GA-R6 stage close in this project's
history cites, and it could not tell a clean repo from an unexamined one.
Fix: reject unrecognised options (the actual root cause) and >1 positional root;
require the root to be a directory carrying repo markers, refusing rather than
defaulting to success; add a files-scanned floor; and PRINT the files-scanned
count -- Phase 0 found repo-lint emitted no such figure at all, so a reader could
not distinguish a thorough pass from a vacuous one.
The floor SCALES, and a flat one was wrong: the first implementation used a flat
100 and failed 20 of the 47 existing harness cases, because tests/repo-lint
legitimately builds ~7-file fixture repos. The strong floor now applies only to a
full checkout of THIS repo, detected by two files no fixture creates; any other
tree must still scan at least one file. Caught by running the harness.
Harness 54/54 (was 47) -- T48-T54 lock each reproduced defect, including that the
floor does NOT punish a legitimate minimal tree. Gauntlet ALL GREEN (82).
repo-lint on the real repo: 0 fail, 1 standing warn, 596 files scanned.
No consumer breaks -- preflight P1 reads the exit code, not the summary text;
harness regexes match the unchanged "PASS: repo lint" prefix. Both verified.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-27 |

Phase 0 delivery cleanup: session changelog with reverts, and a mis-filed finding corrected
...
Three gaps in the Phase 0 delivery, plus one new measured finding.
1. SESSION CHANGELOG ADDED (GA-R2/D1) -- docs/changelog-20260727-stage5-phase0.md.
It was missing, and it is the only revert surface: no logged window was
opened for this session, so the changelog + capture are the entire
as-executed record of five live mutations (two branch deletions on origin,
two on voffice1, a 68-package install on the region host). Each item now
carries WHAT / WHY / HOW TO REVERT with the pre-state hashes recorded
(39e8988, 61c416e, 57836b1) and the apt purge scoped with a caution that
68 packages were pulled and some are shared.
2. CURRENT-STATE section 7 no longer carries two rows for one component. The
prior "ABSENT ON BOTH HOSTS" state is folded into the single pin row as
history, following the Juju row's in-row supersession precedent. A pin
table asserting both presence and absence of the same client is the same
stale-surface class the audit close-out sweep found the audit itself
creating.
3. A FINDING I MIS-FILED IS CORRECTED BY MEASUREMENT. P4's
"metal-admin gateway=none (want 10.12.8.1)" was dismissed as covered by
readiness item 3.7. It is not -- 3.7 is specifically the VID-103 /
br-internal assertions. Measured from the dc0 rack: 10.12.8.1 answers 0/2
pings with an INCOMPLETE ARP entry (never resolved -- nothing holds it),
against a control ping to the edge at 10.12.4.1 at 0% loss. So MAAS is
right to carry no gateway and lib-net.sh:37's expectation is the defect, a
VR0 inheritance; the D-134 carve ruled .1 gateways for the PROVIDER subnets
only. Logged as P0-5, not fixed (hard rule 1). The dc1 arm (lib-net.sh:149)
carries the same shape at 10.12.68.1.
Also queued for one GA-R5 exchange, no D-number assigned: which host is
authoritative for the gates. R10 (preflight on voffice1) and R15(3) (preflight
stops failing open) executed together make preflight-on-voffice1 permanently
unpassable given P0-2, and R15(2)'s manifest does not reach P0-1 -- so the R15
execution scope needs this answered first.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|