| 2026-08-03 |

D-052 amendment: ovn-central default -> metal-internal (ruled); bundle plane-purpose sweep
...
Operator ruling, exact utterance: 'ovn-central should be in metal-internal.'
Follow-on: 'Complete a bundle sweep for any other bindings that are bound to a
plane that does not match the planes intended purpose.'
ovn-central's '' default binding moves metal-admin -> metal-internal. Its
certificates + ovsdb* endpoints already live on metal-internal; moving the
default makes it single-space for cert resolution, dodging LP #2044324 (the
multi-space ovn-central cert bug this deploy hit). D-052 isolation unchanged for
every other app.
Bundle plane-purpose sweep (all 56 apps, capture
binding-plane-purpose-sweep-20260803.txt): classified every binding against
D-052's plane purposes, verified each flag against the relation topology. Exactly
ONE other real deviation: openstack-dashboard:cluster on metal-admin where D-052
places cluster peers on metal-internal (sole outlier of 14 cluster-carrying apps;
benign but off-intent) -- PROPOSED, not ruled. Four flags verified non-issues:
octavia:ovsdb-cms is dangling (no relation); ceph-radosgw public/object-store/
cluster are gateway endpoints correctly placed.
Not yet applied -- bundle edit + live juju bind are the next gated step.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-141 ADOPTED: IPAM allocations are dual-stack, status-distinguished
...
Operator ruling, exact utterance: "Yes, but you should expand on the record. It
should be noted that when creating future ipam allocations the IPv4 and IPv6
blocks must follow this same structure."
Immediate: the 26 GUA v6 VIP addresses D-139 step 6 created stay in the apex at
status reserved (already their measured state, no NetBox mutation) and are not
promoted to active until the v6 VIPs are live and verified.
Standing rule (the architectural half, hence a D-number under GA-R3 A1):
every IPAM allocation is authored dual-stack and the STATUS field carries the
truth -- v4 active (live), GUA v6 reserved (planned/collision-protected),
superseded ULA deprecated (history). The apex records current reality AND future
plan in one place, legible from status alone: apex driving deploy, not
back-filled to it. Four binding rules: author both families together; status
bears truth never prose; promotion gated on a NAMED capability; never delete a
reserved future-family block (it is the conversion input).
Adjacent to G18 but does not answer it -- D-141 governs allocation STRUCTURE, G18
is the separate lb-mgmt-prefix ruling still deferred-until-live. The promotion
gate for the API-charm v6 VIPs is docs/charm-ip-family-compatibility.md (juju
LP#1723240 + the per-charm fixes), which now cites D-141 as its governing
decision. Roosevelt analog: every DC's IPAM authored dual-stack from day one, v6
reserved until capable, so no DC ever needs a v4->v6 re-carve -- only a status
promotion.
CURRENT-STATE updated same commit (GA-R1 C1); next-free D 141 -> 142.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-08-02 |

CORRECTION: the MAAS region is INSTALLED in the DC, never migrated
...
Operator correction, verbatim: "MAAS region gets installed directly on the DC,
it is not migrated. If you have read this somewhere we need to make sure it is
corrected so future deployments don't follow a regional migration path."
I told the operator a dc0 environment rebuild would 're-run the D-132 region
migration work'. That framing came from two surfaces, and both are now fixed:
SKILL.md carried a standing invariant headed 'A PER-DC MAAS REGION MIGRATION HAS
AN ORDERING THAT IS NOT THE OBVIOUS ONE'. SKILL.md is read BEFORE any runbook, so
a session planning DC3/4/5 would have followed a migration path that does not
exist. Reframed: the region is installed in the DC at standup and nodes enlist
into it first time. The dc0 migration of 2026-07-30 was a ONE-TIME remediation
for machines already enrolled in the Office1 region before D-132 q1 was ruled --
a state no future DC can enter. The block is retained as record only, explicitly
not as a workflow.
design-decisions.md's D-132 amendment says 'Per DC this means: install region +
PostgreSQL; re-enrol and re-commission 9 nodes; recreate the D-133 carve...'.
True only of dc0 and dc1, which already existed. Annotated in place per D-117's
treatment rule rather than rewritten. The region-target parameter half DOES
generalise -- a per-DC region must be addressed explicitly; the re-enrolment half
does not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-135 AMENDMENT 2026-08-02 (b): dc0 converges on the proxy NOW; mirror result is informational only
...
Operator ruling, exact utterance: "Option A, converge. We have gathered the
information we need on the mirror buildout. It will be a future implementation
but will not be used in Roosevelt but much later so keep the information as
informational only with no pin or expectation on a future deployment date."
RULED (A). dc0's artifact strategy becomes the apt caching proxy on the dc1
pattern; both DCs now run ONE strategy. The per-DC origin override is DELETED
rather than repaired -- the proxy forwards whatever URL it is handed, so
cloud:jammy-caracal (the as-built-proven value, defined once behind bundle.yaml's
anchors) works unmodified and the NO_PUBKEY problem does not exist to be solved.
Supersedes the trigger clause of amendment (a), which made convergence
conditional on a rebuild of dc0's artifact service.
The experiment is complete, so the comparison amendment (a) listed as owed at
the rebuild is owed now and is recorded as evidence. Its status is set by this
ruling: INFORMATIONAL ONLY -- no pin, no expected deployment date, not a
Roosevelt item. A later session must not convert it into a pinned item, a
gap-register row, a trigger, or a forward commitment; ledger-scan and the
forward-items register stay clean of it.
dc-mirror.sh is retained for the historical arm and its currency lesson; install
is a deliberate strategy reversal, never a repair. NOT ruled here: whether the
running mirror is torn down and when its 953 GB is reclaimed -- a separate
destructive action needing its own approval.
GA-R5: recorded and pushed before dependent work.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-139 step 6 tool built + adversarially reviewed; 4 defects fixed. Apex NOT yet written.
...
Implements the two 2026-08-02 rulings ("Full step 6 first, then deploy" / "Deprecate both,
delete nothing"): CREATE 26 GUA VIP addresses -> read-back verify -> deprecate 26 ULA
addresses + 9 ULA prefixes. NO delete path anywhere, asserted against the artifact.
Dry run (live apex): CREATE 26 | ALREADY 0 | DEPRECATE-ADDR 26 | DEPRECATE-PFX 9. The 26
CREATE targets diff EXACTLY against the 26 GUA VIP legs in overlays/vr1-dc0-vips.yaml --
every address written is one the deploy configures. --commit has NOT been run.
AN ADVERSARIAL REVIEW RETURNED "FIX FIRST" AND WAS RIGHT ON ALL FOUR COUNTS
(docs/audit/d139-step6-tool-review-20260802.txt). Mapping logic was correct; the gaps were
preconditions and coverage.
DEF-1 CRITICAL -- --dc vr1-dc1 would ORPHAN-CREATE. Targets were computed arithmetically
and never checked to exist. Measured: dc1's GUA carve is incomplete (four provider-public
rows under 2602:f3e2:f03::/48, no :20::/64, no :21::/64), so dc1 planned 26 creates into
non-existent prefixes then deprecated dc1's only authoritative rows, rc=0, no warning. dc0
hid it because all sixteen of its targets happen to exist. Reachable via the other valid
value of a required flag. FIXED + verified live: dc1 refuses, dc0 unchanged at 26/26/9.
DEF-2 CRITICAL -- the apex-IDENTITY guard was gone. It lives in d139-gua-carve.py's main()
(:159-163) and importing a module never runs its main(), so subclassing C.NB inherited the
TRANSPORT and left the SAFETY POSTURE behind: netbox.baldurkeep.com (the v1 reference)
would have connected fine and taken writes. FIXED: identity checked before any network call.
DEF-3 HIGH -- silent under-count. One missing ULA /64 row gave CREATE=13/DEPA=13/DEPP=8 at
exit 0. FIXED: any ULA address claimed by no prefix row refuses. My first fix was itself
wrong and RUNNING it caught that -- it scanned the whole retired /48 and flagged dc1's 26
VIPs while planning dc0; the /48 is SHARED (dc0 :22x, dc1 :32x). A /60 parent deliberately
does not count as coverage: the reviewer's scenario was a missing /64 whose /60 survived.
DEF-4 HIGH -- main() had ZERO coverage; the reviewer hoisted the deprecate loops above the
create phase and the suite reported ALL PASS. FIXED: T16-T18 drive main() through a fake
client that records CALL ORDER, proven by re-running that exact mutation on a copy (T18
goes RED).
TWO OF MY ASSERTIONS COULD NOT FAIL and the review killed both. T13 asserted the ABSENCE of
a string, so a traceback satisfied it -- it passed against a tool file that did not parse;
it now requires a positive, well-formed, DIFFERENT target, and new T15 asserts the tool
parses. T14 grepped ONE file, so adding a delete to the IMPORTED d139-gua-carve.py left it
green; it now covers both.
Corrected in the ruling record (GA-R1 C2): the amendment said the GUA records would be
status=active. Measured: the live ULA VIP records are "reserved", and
dc-plane-apex-import.py:186,200 creates addresses reserved. Also corrected my own
docstring overclaim -- the 26+9 deprecations are reversible, the 26 CREATES are not.
OPEN SCOPE QUESTION, MEASURED, not a tool defect: D-139 says retire the ULA rows "in the
apex AND in MAAS"; this tool is apex-only, so step 6 is NOT complete when it finishes. MAAS
on the dc0 region holds five ULA /64s beside six GUA; four are empty but
fd50:840e:74e2:220::/64 still holds 2 allocated entries. MAAS has no deprecated status for
a subnet, so delete-or-leave is a separate operator decision.
Gates: harness 20/20 (was 14, delta = the 6 cases added); gauntlet ALL GREEN (98, manifest
recorded deliberately 97 -> 98); repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

GA-R5: D-139 step 6 "retire" means DEPRECATE -- nothing is deleted (ruling before the work)
...
Taken in a SEPARATE exchange from the ordering ruling (GA-R5: one decision per exchange --
that one settled WHEN step 6 runs, this settles WHAT it does). Committed and pushed before
any dependent work.
Operator answer, exact utterance: "Deprecate both, delete nothing".
IT HAD TO BE ASKED RATHER THAN INFERRED. D-139's list says "retire the ULA rows" and this
repo has never defined that for an IPAM OBJECT: every netbox/*.py importer uses only
active/container/reserved, "deprecated" appears nowhere, and d139-gua-carve.py:4 says "NO
deletion ever". The word could equally have meant DELETE or MARK-UNUSABLE, and those differ
irreversibly in one direction. Status choices were verified against the LIVE API before the
options were put, not assumed from documentation:
prefixes -> ['container', 'active', 'reserved', 'deprecated']
ip-addresses -> ['active', 'reserved', 'deprecated', 'dhcp', 'slaac']
STEP 6 IS THEREFORE: (1) create 26 GUA VIP ip-addresses (f02:20::50-::62 metal-admin,
f02:21::50-::62 metal-internal) status=active; (2) deprecate the 26 ULA VIP addresses;
(3) deprecate the 9 ULA prefixes. CREATE FIRST, VERIFY, THEN DEPRECATE -- reversed, there
would be an interval in which the apex marks a live VIP's only record unusable.
THE COST THE ORDERING RULING FLAGGED IS WITHDRAWN. It called step 6 the largest new surface
before a deploy because it needed a DELETE path the repo has deliberately never had. There
is now no delete path: CREATE plus STATUS-UPDATE only, never-delete preserved repo-wide,
every step reversible by flipping a status back, migration stays visible in the apex.
Recorded for a later reader: "deprecated" is ADVISORY in NetBox, not enforcing. The
functional protection against handing out an in-use GUA address comes from the 26 GUA
records EXISTING, not from the deprecation.
Tool still NOT BUILT. repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

GA-R5: D-139 step 6 runs IN FULL before the Stage-5 deploy (ruling recorded before the work)
...
Closes the exchange the 2026-08-01 ordering ruling explicitly left owed: "at least step 6 has
a deploy coupling that this ruling does not settle ... needs its own GA-R5 exchange before
Step 4". Committed and pushed BEFORE any dependent work, per GA-R5.
Operator answer, exact utterance: "Full step 6 first, then deploy".
THE 08-01 PREMISE IS NO LONGER TRUE, and the question was put on re-measurement rather than
on that text. It reasoned the VIP overlays "carry v6 VIPs in the ULA range"; measured
2026-08-02, overlays/vr1-dc0-vips.yaml has ZERO fd50: legs and 39 GUA legs, re-rendered onto
GUA on 08-02 with the rack's staged copy brought into line the same day. The deploy INPUT is
already correct; the residual coupling is in the APEX only.
MEASURED (d139-gua-carve.py --dc vr1-dc0, dry run, against the working apex office1-netbox
per DOCFIX-195): CREATE 0 | EXISTS 16 | RETIRE-REPORT 9, dependents 26 ip-addresses / 0
ip-ranges. Step 1 fully applied and idempotent. The 26 were ENUMERATED rather than inferred
from the count: all are VIP records, 13 in fd50:840e:74e2:220::/64 (metal-admin) and 13 in
:221::/64 (metal-internal), octets ::50-::62. The other three retiring /64s hold zero.
CONSEQUENCE: the Stage-5 deploy is blocked on step 6 and nothing else in the D-139 list.
Step 6 = create 26 GUA VIP addresses, delete 26 ULA VIP addresses, retire 9 ULA prefixes.
Steps 4, 5 and 7 remain sequenced after the deploy (7 necessarily -- its network-get
prerequisite needs a deployed unit).
COST, recorded because it is the argument against the option chosen: it needs a repo tool
with a DELETE path against the apex, which this repo has deliberately never had.
d139-gua-carve.py refuses to delete BY DESIGN and that refusal is why the step-1 push was
safe to run. The new tool is the largest new surface introduced immediately before a deploy
and must meet the step-1 standard: independently reviewed, dry-run first, assertions proven
able to FAIL, and CREATE strictly before DELETE so no window exists in which a live VIP is
recorded nowhere.
The tool is NOT YET BUILT. repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

GA-R5 x2: D-135 amended (dc0 converges on the proxy at rebuild); D-140 PINNED (tofu/juju)
...
D-135 amendment. Operator: "if we have to rebuild in dc0 for any reason we will be using
a proxy rather than a full mirror rebuild." The 953 GB mirror is not reconstructed; a
rebuild comes back as an apt caching proxy on the dc1 pattern. This qualifies D-135's
standing "no fallback and no later convergence" -- none while the mirror stands, but a
rebuild converges. Nothing changes today: the running mirror is verified intact and stays.
Owed at the rebuild: capture the mirror arm's outcome BEFORE dismantling it, or the
experiment D-135 existed to run produces nothing. Consequence for the fold: the phase
runbooks can carry ONE artifact strategy instead of branching per DC.
D-140 PINNED. Operator: "I accept the opentofu plan. Hardened dc0 deployment then
opentofu management with a successful tested deployment method from dc0. Something to pin
for the end of deployment, review and add opentofu management to the remaining tasks."
Order: dc0 deploys and hardens -> method tested -> proven method is the translation input
-> reviewed at end-of-deployment. Not converted now because the bundle deploy has never
completed end to end and changing the mechanism first means two unknowns at once.
Value is measurably in the model/config layer, not topology. Cost to price at review: the
provider is resource-shaped not bundle-shaped, against a measured 56 apps / 108 relations.
Provider capabilities must be read, never recalled.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-08-01 |

GA-R5: VPN (:e0) deferred to Roosevelt design time, recorded not left silent
...
Operator utterance: "Defer to Roosevelt design time".
Recorded as a DEFERRAL rather than left open, because an unrecorded hole is exactly how
the OOB omission survived until the operator caught it. Measured basis: VPN is a
RESERVATION at both precedents, never a carved plane -- VR0 DC0 and Willamette hold the
:e0::/60 parent ONLY, no /64 and no IPv4, unlike OOB which holds /60+/64 at both. VR1's
VPN is Tailscale under D-129(iii), addressed from fd7a:115c:a1e0::/48, which this
deployment does not carve, so a VR1 :e0::/60 would reserve space with no occupant.
Consequence stated plainly: VR1's octet map diverges from VR0 and Willamette on exactly
one hextet, by decision, and ruling B's "conforms to an existing org standard" claim
should be read with that documented exception. Nothing in D-139's execution list, the
Stage-5 deploy, or any gate depends on :e0. Trigger: Roosevelt VPN design, alongside
D-132 and D-131 sub-4.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

GA-R5: OOB IPv4 allocation ruled -- dc0 10.12.40.0/22, dc1 10.12.88.0/22
...
Operator utterance: "dc0 10.12.40.0/22, dc1 10.12.88.0/22 (Recommended)", after asking
for a recommendation matching the existing per-DC pattern. Supersedes 10.12.60.0/22 from
amendment (a), which was ruled back into force before the pattern had been measured.
Measured: the dc0->dc1 offset is a consequence, not a rule. dc0 is 4,8,12,16,[20-28
free],32,36; dc1 is contiguous 64..84; the offset runs +60 x4 then +48 x2 because dc1
never reproduced dc0's historical gap. dc0's gap is unusable for a symmetric pair --
20+60=80 and 20+48=68 are both allocated. 40 and 88 are each the next free /22 after
their own DC's highest plane, keeping both runs contiguous at the +48 offset storage
and replication already use.
10.12.60.0/22 returns to the free pool; D-058 stays superseded in full. The v6 half is
built for dc0; the v4 half is ruled and NOT built and gates nothing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

GA-R5: D-139 gains an OOB plane, dual-stack; 10.12.60.0/22 ruled back into force
...
Operator utterances: "make sure to include oob in the dual stack recordings" and
"Dual-stack; rule 10.12.60.0/22 back into force for OOB".
Measured gap: D-139's CARVE table carried 10,11,20,21,30,40,50,80 and no 0xf0 (OOB)
or 0xe0 (VPN) -- design-decisions.md:3066 declared all three out of scope of the
six-plane tool, D-139 brought :80 back and left the other two. The step-1 push would
have written a carve incomplete against the org standard ruling B cites. Held, not run.
The ruling knowingly diverges from both precedents: VR0 and Willamette carry OOB
IPv6-only and a sweep of every apex prefix returns ZERO v4 rows with role oob at any
site. Dual-stack is a deliberate Roosevelt correction (BMC/IPMI is v4 in practice).
Supersedes the "OOB n/a / bare-metal-only concern" row at design-decisions.md:93.
D-058 remains SUPERSEDED in full; only that one row's VALUE is reinstated, under
D-139's authority. Block verified free before ruling. Per-DC v4 split is UNRULED and
deliberately not inferred; the v6 half is complete and the v6-only push is unblocked.
VPN (:e0) is the same omission, left open rather than folded in.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
GA-R5 ruling: the D-139 GUA carve is sequenced BEFORE the Stage-5 deploy
...
Operator answer, exact utterance: "Apex push AND the MAAS/node GUA carve, then deploy".
D-139 execution steps 1-3 run before the bundle deploy, so the cloud is deployed once
on its final GUA addresses and nothing is re-addressed underneath it. The 2026-07-30
standing directive is satisfied in substance rather than literally.
Recorded, not inferred: steps 4-6 were NOT put, and step 6 has a deploy coupling this
ruling does not settle -- the per-DC VIP overlays carry v6 VIPs in the ULA range, so
deploying after steps 1-3 alone leaves GUA nodes with ULA VIPs. Own exchange owed
before Step 4. Neither ruling A nor B is touched; no new D-number (GA-R3: OPS).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

Next steps: nodes released, D-139 execution list replaced, LP draft written
...
(1) TEST NODES RELEASED. t7ymp6 / fg6gxm -> Ready / owner=None, released
individually and read back. Nothing stranded. G19 was run live before the window
closed, which was the whole reason for holding them.
(2) D-139 GAINS A CORRECTION NOTE REPLACING ITS EXECUTION LIST. Neither ruling is
touched. The original list is preserved verbatim inside the note rather than
silently overwritten -- same reason the struck RFC 6724 rationale was kept: a
later reader must be able to see what was wrong or they will re-derive it.
SILENT FAILURE (why this could not wait): dc-node-v6-carve.py assigns v6 "on
every plane WHERE IT ALREADY CARRIES IPv4", keys the subnet off the same MAAS
vlan as that v4 link, derives the host part from the v4 octet, and its loop body
is `if not v4: continue`. Run after v4 removal it carves FOUR FEWER PLANES PER
NODE AND EXITS CLEAN -- and the original list's "remove v4 LAST" wording
actively invited that ordering.
UNSATISFIABLE: "remove v4 LAST, after each is proven" -- per-plane conversion is
atomic, so no such state exists.
UNSAFE STEP FUSION: apex CREATE and RETIRE in one step, with 52 dependent
ip-addresses (the v6 VIP legs) inside the retiring ULA /64s.
Replacement is 7 steps: CREATE-only apex push; MAAS GUA carve ALONGSIDE the ULA;
node statics re-carved WHILE v4 IS STILL PRESENT; Octavia SAN reissue +
lib-net.sh; lb-mgmt carve; re-home the 52 VIPs THEN retire; and v4 removal as a
SEPARATE experimental step scoped to storage + replication TOGETHER per "B plus
C" (together because ceph-osd's prefer-ipv6 sets ms_bind_ipv4=False globally).
Step 7's prerequisite -- network-get on a v6-only bound space -- is stated unmet.
(3) LP DRAFT WRITTEN, NOT FILED: docs/audit/lp-draft-20260801-ceph-osd-ipv6-
static.md. The "C" half of the ruling. A COMMENT on the EXISTING LP #2061836, not
a new bug -- the ceph-mon task is already Fix Committed and only ceph-osd remains
New. Every code claim quoted from the downloaded rev-953 artifact. Claude does not
post to Launchpad; the operator files it (lp-draft-20260721 precedent).
Two things written INTO the draft's operator notes rather than left implicit: do
NOT cite LP #1590598 (Fix Released, a different defect -- the inverted-citation
class); and this bug does NOT block us locally, since setting ceph-public-network
/ ceph-cluster-network to the v6 CIDRs bypasses the buggy call. Overstating our
dependence on an upstream fix would be a bad-faith way to raise its priority.
OWNED: first push attempt went red on L1 -- I used em-dashes in the changelog and
this repo is ASCII-only. Caught by lint before commit, fixed, re-run 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-139 ruling B RECONSIDERED and CONFIRMED; refuted rationale STRUCK in place
...
Operator, exact utterance: "Deciding to hold to gua does not cost anything
operationally and the case for ULA over gua is not very strong. Let's stay with
gua". Recorded as RULING NOTE 2026-08-01 -- D-139.
RULED: ruling B STANDS, unchanged in substance. The GUA carve table is confirmed,
the ULA /48 stays RETIRED for VR1, the D-101/D-111 amendments hold, and Phase-2
execution of D-139 is UNBLOCKED.
WHAT CHANGED IS THE RATIONALE OF RECORD. The RFC 6724 precedence argument is
STRUCK IN PLACE in D-139 -- quoted, with the measurement that kills it and a
pointer to the capture, marked DO NOT CITE IT. Struck rather than deleted on
purpose: deleting it would leave a future session free to re-derive an argument
that has already been considered and refuted. The block also warns against the
near-miss rescue -- glibc 2.35 DOES carry fc00::/7 in default_labels[] (label 6),
which drives SOURCE-selection rules 5/6, a different mechanism from destination
precedence; finding it does not resurrect the struck claim.
Ruling B now rests on two project-constraint arguments -- conformance (Willamette
and VR0 DC0 are already full GUA; VR1 was the outlier) and MINIMIZE DELTA TO
ROOSEVELT -- plus the operator's recorded reasoning that GUA costs nothing
operationally. MADE EXPLICIT because it was not visible before: on glibc 2.35 GUA
buys NO address-selection advantage on the two dual-stack planes; ULA would have
won equally at 40. Anyone later reasoning "GUA was chosen so v6 would beat v4" is
reasoning from the struck argument.
NEW STANDING RULE, recorded on D-139 because it generalises: the repo's citation
rule (open it, check STATUS and DATES) was FOLLOWED here and was NOT ENOUGH. The
citation was real, current, correctly quoted and correctly understood, and still
wrong, because nobody checked whether the IMPLEMENTATION follows the standard.
For a STANDARDS citation, add a third check -- confirm the deployed software
implements it, at the deployed version. An RFC is not a description of your system.
Staged by explicit path; an agent's in-flight files (netbox/d139-gua-carve.py,
tests/d139-gua-carve/, docs/audit/d139-carve-dryrun-20260801.txt) are deliberately
NOT included. repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-139 ADOPTED: VR1 goes IPv6-only east-west, whole carve moves to GUA
...
TWO GA-R5 rulings, taken in SEPARATE exchanges, recorded separately.
A -- family matrix. Operator: "IPv6 on all planes except for metal-admin and
provider-public which will remain dual stack". metal-internal, data-tenant,
storage, replication and lb-mgmt become IPv6-ONLY. Amends D-101's matrix twice:
metal-internal's "datastore east-west stays v4-bound" is superseded, and lb-mgmt
(ruled v6-only by D-101 but NEVER carved anywhere) becomes a first-class plane.
B -- addressing model. Operator: "Full GUA on every plane (Recommended)". Every
plane carves from its DC's GUA /48 on the :10/:11/:20/:21/:30/:40/:50/:80 octet
map VR0 DC0 and the Willamette site ALREADY use -- conforming to an existing org
standard, not inventing one. The ULA /48 fd50:840e:74e2::/48 is RETIRED for VR1.
Amends D-101 and D-111. Deciding reason is measurable: RFC 6724 ranks IPv4-mapped
at precedence 35 and ULA at 3 while GUA falls under ::/0 at 40, so on a dual-stack
plane a ULA leg LOSES address selection to IPv4 and is decorative. SEC-010, D-052,
D-125 and D-107 unchanged -- containment lives at the forwarding layer.
THE RECORDED ROOT CAUSE OF THE v4-ONLY CONTAINERS WAS WRONG AND THIS REPO CARRIED
IT (GA-R1 C2 -- measurement corrects the document). CURRENT-STATE said MAAS "has
nothing to give for v6". Measured, and re-verified independently: subnet
statistics on fd50:840e:74e2:220::/64 returns available_string "100%",
num_available 18446744069414584320. Zero ipranges rows means zero RESTRICTIONS,
not zero availability. dc-plane-ipam.sh:368-372 has carried the correct behaviour
since 2026-07-27, in the tree, contradicting the authoritative doc the whole time.
The real mechanism is juju-side and there is NO knob: EthernetDeviceForBridge
(tag v3.6.27) takes addrs[0] from an UNSORTED query and derives one CIDR -> one
LinkSubnetArgs. gomaasapi documents "Any number of STATIC links can exist on an
interface", so MAAS would accept both families; the limit is juju's. LP #1723240
is this exact symptom, Triaged/Low since 2017, and the theory this repo recorded
is the one its reporter rebutted in-thread. Consequence: on a container plane
DUAL-STACK IS NOT EXPRESSIBLE while v6-only IS -- ruling A is the achievable
configuration, not merely the desired one.
SNAP PROXY: dc0 INSTALLED, RUNNING, check dc0 PASS exit 0 (16 assertions,
re-verified independently). A REAL SNAP PAYLOAD WAS 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 (the review named one); T20b/T41b NEW.
OWNED: my BUG-1 fix was WRONG. ip route get 10.12.8.4 -> src 10.12.8.2; .4 is a
SECONDARY and Linux never auto-selects a secondary as a source, so the rule is
still dead. Not fixed now -- editing the generator turns the LIVE gate red until
install re-runs. T23b annotated so nobody "fixes" the test to a wrong value.
Generalisable: any ACL keyed to a service ALIAS (the whole D-134 utility .4 band)
misses that host's own traffic -- same exposure in dc-mirror.sh, dc-cache-proxy.sh.
OWED, unverified: the deployed jammy /etc/gai.conf, which can override the RFC 6724
default table that ruling B rests on. Nothing of D-139 is executed.
repo-lint 0 fail; tests/dc-snap-proxy 55/55; 4 mutations killed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-31 |

RULED 2026-07-31 (GA-R5) x2: UCA points in-DC; snaps get an in-DC forward proxy
...
Presented together at operator direction ('Yes, both') and answered SEPARATELY,
so neither is a batch adoption. Both OPS under GA-R3; D-107 UNAMENDED.
RULING 1 -- UCA. Operator utterance: 'Point origin/source at the mirrored UCA,
per-DC overlay (Recommended)'. dc0 gets an explicit deb line at
http://10.12.8.4/cloud-archive in the per-DC hand-maintained overlay; the mirror
address is per-DC so it cannot live in bundle.yaml. dc1 UNCHANGED -- its
apt-cacher-ng forwards the upstream URL transparently, and that asymmetry is
D-135's experiment RESULT, not a defect. Measured precondition: the UCA signing
key is already on the nodes, so a raw deb line verifies with no |key suffix.
RULING 2 -- SNAPS. Operator utterance: 'HTTP(S) forward proxy in the DC utility
band + juju snap-https-proxy (Recommended)'. D-107's core statement stays TRUE --
nodes reach an in-DC proxy, not the internet. Closes the D-135 items 2-3 gap for
BOTH DCs with one mechanism rather than widening the mirror-vs-proxy asymmetry.
Placement is build-time engineering; if it takes its own VM the D-134 octet map
needs a ruled octet first, since that map is a standing cross-DC standard.
Committed and pushed BEFORE the dependent work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

Fix the red-lint push: D-101 heading collision + note placement
...
OWNED: the previous commit was pushed with repo-lint RED. 'repo-lint | tail -2 &&'
masks the lint exit code with tail's, so the && proceeded on a FAIL -- the same
silenced-pipeline trap already recorded in this repo (| grep -q under pipefail;
git pull -q &&).
Two defects, both fixed here:
- the note used a '### D-NNN --' heading, which L5 reads as a second DEFINITION
of D-101 (collision). Re-titled to the established
'### RULING NOTE <date> -- D-101:' form the other four notes use.
- it was appended after D-136 instead of beside the other D-101 notes. Moved to
sit immediately after note (a).
Also recorded: repo-lint exits 2 on WARN and 0 only when fully clean, so 'exit 0'
is the WRONG success predicate for this repo -- its standing state carries one
warn (the legacy D-001..018 non-ASCII carve-out). Gate on '0 fail'.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

RULED 2026-07-31 (GA-R5): prefer-ipv6 set on NO application until IPv6 is operational
...
Operator utterance, verbatim: 'Set it false on the seven, keep every v6 VIP leg
(Recommended)'. Standing context from the same exchange: 'We have DC1 to stand
up with the IPv6 configuration changes. Lets continue with the IPv4/6 stand up
on DC0. We will fold in all lessons learned from the DC0 stand up into the DC1
stand up.'
Recorded as a D-101 RULING NOTE 2026-07-31 (b). OPS under GA-R3, no D-number;
D-101's matrix is UNAMENDED and every dual-family VIP is retained. It supersedes
the emission half of note (a) and now covers all thirteen apps.
Measured cause: get_relation_ip() returns early with get_ipv6_addr()[0] when the
option is true, and the LXD containers hold ONLY a link-local v6 while the HOSTS
are fully dual-stacked.
Why this is not abandoning v6: the option is a unit ADDRESS-FAMILY switch, not a
listener switch. HAProxy's :::port bind is gated on the kernel sysctl and
pacemaker selects IPv6addr by family detection. Neither consults it.
HONEST RESIDUAL, recorded not glossed: pacemaker will place a v6 VIP on a
container whose eth0 has no global v6, and whether that leg is ROUTABLE is
UNVERIFIED. Not a regression -- true today -- but it is what the v6 completion
work must close.
GATE CONSEQUENCE: invariant 9b coupled the option to the v6 legs for declaring
charms and would now FAIL. It is RE-POINTED to the new invariant and re-proven,
never deleted; when IPv6 becomes operational it returns to its coupling form and
this note records why it left.
Committed and pushed BEFORE the dependent work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

RULED 2026-07-31 (GA-R5): sync jammy-backports into the dc0 mirror (D-135 amendment)
...
Operator utterance, verbatim: "Sync the backports into the mirror".
Question as presented: 22 of 33 units failed their install hook on one measured
cause -- the mirror serves jammy/jammy-security/jammy-updates 200 and
jammy-backports 404, exactly dc-mirror.sh:179's "jammy triple" scope, while
every node's sources.list carries jammy-backports because the Step-3.5
apt-mirror model-config rewrites EVERY suite to the DC mirror. Options: (a) add
the suite and re-sync, (b) drop backports from the nodes' sources.
Recorded as a D-135 AMENDMENT. OPS under GA-R3, no new D-number; D-135's per-DC
strategy split (dc0 full mirror / dc1 caching proxy) is unchanged.
Cost measured BEFORE the ruling was put: ~1.00 GiB / 461 packages against a
951 GB mirror with 1.8 T free -- ~0.1%.
Recorded as owed, not built: dc-mirror.sh check verifies sync STATUS, not SUITE
COVERAGE against what the deployed image's sources.list requests, which is why
it read PASS throughout. Same class the mirror gate was already fixed for once.
Committed and pushed BEFORE the dependent work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

RULED 2026-07-31 (GA-R5): the six drop prefer-ipv6, the v6 VIP legs STAY
...
Operator utterance, verbatim: "I want it to use IPv6, if there is spam
mechanisms being applied then that is bad. Even if it doesn't cause an issue
now, it might in the future. A clean IPv6 network is better than one with
unusable and possibly future breaking configurations."
That stated a PRINCIPLE rather than selecting an option, so a confirming
exchange was taken rather than adopting an inferred ruling (GA-R5 forbids
adopting from an inferred or reconstructed ruling). Confirming utterance,
verbatim: "Yes -- keep the v6 legs, remove only the option".
Recorded as a D-101 RULING NOTE dated 2026-07-31. OPS under GA-R3 -- a
renderer + gate change, doubt resolves DOWN, no D-number assigned. D-101's
family matrix is UNAMENDED: the dual-stack posture does not change and every
v6 VIP leg is retained.
Effect: barbican, designate, magnum, octavia, placement and vault keep all
three v6 VIP legs each; prefer-ipv6 stops being emitted for them; both DCs,
symmetric.
The SEVEN charms that DO declare the option are NOT covered by this ruling
and were deliberately not bundled into it -- one decision per exchange.
Committed and pushed BEFORE any dependent work.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-30 |
D-132 placement RULED: per-DC MAAS region VM at utility .6; D-134 map extended
...
GA-R5. Operator utterance: "Dedicated region VM per DC at utility .6
(Recommended)".
The region DB must not share fate with the hypervisor running every node it
manages -- also a precondition for D-132 q3 cross-site backup custody, which
stays pinned. The D-134 standing octet map now reads .4 artifact service /
.5 juju controller / .6 MAAS region, binding at every future DC standup.
Build notes carried, not ruled: author BOTH metal-admin and provider-public
legs at creation (the identical under-carve on the juju controller VM cost
three bootstrap attempts today); re-measure capacity before applying.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-132 q1 RULED for VR1: a MAAS region controller per DC; Stage 5 blocked
...
GA-R5, before dependent work. Operator utterance: "Put a MAAS region
controller in each DC".
Third bootstrap got PAST the agent fetch (downloaded attempt 1 -- the
controller carve closed that failure class) and failed at the next layer:
jujud, running ON the node, cannot reach the MAAS region API at
10.10.0.20:5240. Measured: rack reaches it (200, rack-originated, permitted
by SEC-010); the node cannot (forwarded, blocked); and the rack does NOT
proxy it -- nginx listens on 5248 only. All six required flows enumerated;
exactly one crosses the boundary.
The finding underneath: SEC-010 as written is incompatible with the
deployment's own control-plane topology (D-104 controller in-DC + MAAS
region at Office1). D-138 was necessary but NOT sufficient -- it moved the
cloud-facing client into the DC, and jujud is itself a provider client whose
MAAS dependency I did not enumerate when scoping it.
Per-DC regions remove the requirement rather than excepting it, so SEC-010
is PRESERVED UNAMENDED. Single, not HA (utterance is singular); D-132 q2/q3
stay pinned to the next deployment.
Consequence stated plainly: this reopens the MAAS layer of Stages 3 and 4,
both closed and merged. A region owns its own DB; nothing migrates between
regions. Open sub-question blocking the build: where each DC's region lives.
Also landed: both controllers carved symmetric with gw-bearing
provider-public legs. Trap recorded -- MAAS `interface update vlan=`
returned a full JSON object while changing nothing on a Deployed machine.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-138 ADOPTED: the cloud-facing client lives IN the DC; SEC-026 opened
...
GA-R5, before dependent work. Operator utterance: "Move the cloud-facing
client into the DC (Recommended)".
Resolves the contradiction the Stage-5 bootstrap failure exposed: SEC-010 +
D-052 make metal-admin DC-local and forbid the route juju needs, while D-100
names Juju as fiber traffic and D-128 puts the client on voffice1. SEC-010's
"pinning is free" was priced against tools that proxy at the app layer; juju
dials the machine at L3 and was not in scope.
The client moves; the boundary does not. SEC-010, D-052 and D-125 are
UNCHANGED -- nothing punctured, no plane opened. D-128 is amended to exclude
cloud-facing tools. Scope was set by enumeration first: keystone's VIP is on
provider-public, so routing would have opened two planes per DC across a
dozen ports.
SEC-026 opened for the consequence: a MAAS admin-scoped key becomes resident
on a DC-local host, over a region shared by both DCs. Isolation is the
control -- each DC's client host gets ONLY its own credential.
Counters: 22 open SEC, next-free D 139.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

DOCFIX-205: Q1 withdrawn -- D-117 ruled it 2026-07-13; annotate the four it owed
...
The operator asked to rule on queued Q1 (is substrate vr1-dc1 'dc1' by token or
'dc2' by position, for cert/DNS identity). Measured before presenting options.
The question was WITHDRAWN, not ruled: D-117 (ADOPTED 2026-07-13, four days after
D-106) names the supersession in its own Status line -- "Supersedes ... the D-106
dc1/dc2 zone labels" -- and rules the replacement: "The repo's dc1/dc2 labels are
retired in favour of dc0/dc1". vr1-dc0 -> dc0, vr1-dc1 -> dc1.
WHY IT RESURFACED, and this is the reusable half: D-117 ruled that the ADOPTED
decision texts D-101/D-106/D-111/D-115 be ANNOTATED in place. Measured: ZERO of
the four carried any D-117 annotation. It stayed invisible because D-117's OWN
Status line claimed "FULLY EXECUTED BY D-119", while D-119 scopes its discharge
to the SELECTOR half only. A reader checking whether the annotation was owed was
told it was already done. Ruled is not built -- check the artifact.
Landed (operator-approved batch, "All four + code fixes"):
- All four decisions annotated in place, per D-117's own ruled treatment. D-101
gets the hardest one: it carries BOTH namespaces, so it is annotated BY DATE
(dc1 means different datacenters in its two halves -- D-117 TRAP 2).
- D-117's Status line corrected by measurement (GA-R1/C2) to "EXECUTED IN TWO
HALVES", with the standing lesson that a Status line is a CLAIM about
execution, not evidence of it.
- phase-6 EXECUTABLE DEFECT fixed: os-public-hostname was built as
"keystone.omega.${DC}.vr1..." with $DC the D-119 selector, expanding to
omega.vr1-dc0.vr1... -- the region TWICE -- and would have been baked into
Vault-issued SANs at Stage 7. Both labels are now DERIVED from the site token
(${DC%%-*} / ${DC#*-}); verified by running it, not by reading it.
- octavia-pki.sh A12: refusal -> assertion on the full derived zone; a SAN-less
cert now also fails. Harness 21/21 -> 23/23, with T21 REPLACED (not deleted)
and new T21b (the other DC's label in the right region FAILS -- the cross-DC
mix-up region-only checking passed) and T21c (per-DC derivation proven).
- CURRENT-STATE + ledger corrected in the same commit (L10). "BLOCKING" was
wrong as stated: A12 measures INERT and PASSES live on both DCs. F9's reissue
obligation stands.
gauntlet ALL GREEN (89); repo-lint 0 fail; ledger-scan unchanged (3 decisions,
21 SEC, D 138 / BUNDLEFIX 053; DOCFIX 205 -> next-free 206).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-29 |

RULED (GA-R5): each DC's Octavia PKI is generated ON the Plane-2 headend
...
Operator utterance, exact: "Generate on voffice1 (Recommended)". Recorded as a D-109 ruling
note, which is the authority. OPS under GA-R3 -- it fixes an execution location for an
already-ruled artifact, so no new D-number; next-free stays 138.
Found by a closing check rather than by the audit, and it would have bitten within the hour:
the generation was already approved and the handoff written against phase-01 Step 1.0-GEN's own
"RUN -- jumphost" label. Measured before execution, three surfaces disagreed -- that label and
the eighteen host-role=jumphost register rows (bound to vcloud) against dc-dc-phase4's RUN
LOCATION of voffice1, "NOT the vcloud jumphost", which explicitly expects the octavia overlay to
be present there. juju is ABSENT on vcloud and 3.6.27 on voffice1, so following the runbook's
label would have minted the PKI on a host the deploy cannot read it from.
Generate-then-transfer was declined on R7's own posture ground: a second at-rest copy of a CA
private key plus a plaintext passphrase widens the SEC-004 exposure rather than containing it.
Timing was the substance. F3 established both hosts hold nothing, so this is a free path edit;
after the first mint it becomes a key move.
Execution is a separate gated step and is NOT authorised by this ruling.
repo-lint 0 fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

RULED (GA-R5): juju controller takes utility-band .5; the octet map is a standing cross-DC standard
...
D-134 AMENDMENT (2026-07-29) is the authority.
Question as presented: the D-104 controller VM auto-enlisted into MAAS, but
D-134's bands cover no such node -- .4-.49 utility, .50-.99 VIP, .100-.200
NODES split into three OpenStack ROLE sub-bands, none of which a Juju
controller is. A measured occupancy table was put to the operator (dc0
metal-admin has only NINE allocated addresses; .1 held by nothing, .2 rack
leg, .3 D-131 forwarder, .4 mirror/proxy, .5-.49 free and RESERVED in MAAS)
with three options: .5 in the utility band, .103 in the control band, or a
new named sub-band.
Operator answer, exact utterance: "Rule .5 for the juju controller, with the
other utility nodes. These assignments will follow all DC deployments to make
sure standardized configuration is upheld through multiple datacenter stand
ups."
(1) <dc>-juju-01 takes octet .5 on every plane, inside the reserved utility
band so MAAS never auto-allocates there. This resolves the node-vs-utility
tension in favour of FUNCTION: the utility band is now the band for per-DC
INFRASTRUCTURE the OpenStack nodes consume, host-level or MAAS-managed alike.
.100-.200 stays for OpenStack ROLE nodes.
(2) The load-bearing half: the octet map is a STANDING CROSS-DC STANDARD, not
a per-DC choice. Every DC's juju-01 is .5, artifact service .4, rack leg .2,
forwarder .3. A new per-DC infrastructure service takes the next free utility
octet AND THE SAME ONE IN EVERY DC. Divergence between DCs at the same octet
is a DEFECT, not a local decision -- which is what makes dc-plane-ipam.sh
check meaningful across sites rather than merely per-site. Roosevelt analog:
the map transfers, not just the method.
Scope: this assigns the octet and sets the standing rule. It does NOT create
the MAAS record, the juju-controller-<dc> tag, or the tofu MAC pin -- all
still gated. The controller cannot be commissioned yet: power_type measured
EMPTY at enlistment, the same state that blocked all nine role nodes on
2026-07-20.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

D-109 ruling note: the Octavia controller cert gains IPv6 IP SANs
...
Operator ruling (GA-R5), exact utterance: "Yes, add the v6 IP sans."
Recorded as a D-109 RULING NOTE (2026-07-29); OPS under GA-R3 -- a
generator change extending an already-ruled principle to a surface D-109
governs -- so no new D-number and next-free stays 138.
The generator now emits IP.1 = this DC's provider v4 leg and IP.2 = its
provider v6 leg, both derived from the same per-DC overlay under R7's $DC
selector. Verified: dc0 -> 10.12.4.57 + 2602:f3e2:f02:11::57; dc1 ->
10.12.64.57 + 2602:f3e2:f03:11::57; and a v4-only control emits NO v6 SAN,
so the generator stays correct on a tree where the dual-stack ADD has not
landed. Admin and internal legs stay EXCLUDED exactly as before --
DOCFIX-067's design has always been provider-leg-only.
Two repo guards fired and both were right: repo-lint L5 rejected a heading
that LED with the D-number (it reads as a second definition -- the third
time this trap has bitten on this branch), and L10 rejected the change for
touching a design-decisions Status line without CURRENT-STATE in the same
commit.
Observation logged, not acted on: amphorae reach the controller on o-hm0's
charm-generated fc00::/64 ULA, not on any VIP leg, so neither SAN matches
that path. Measured upstream, the controller verifies the amphora by UUID;
the reverse direction was not established from source. Settle by inspection
at the Octavia step rather than assuming.
Gauntlet ALL GREEN (85) on vcloud; repo-lint 0 fail / 611 files.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|

Octavia: R8a BUILT (record was false), two stale surfaces, R7 generator de-frozen
...
Follows an upstream/vendor research pass answering "can Octavia run full
IPv6" -- yes; nothing inside Octavia requires IPv4. charm-octavia creates
the lb-mgmt subnet with ip_version: 6 and has no IPv4 code path at all; the
health manager is v6-capable both directions; the amphora agent binds '::'
and verifies certs by amphora UUID, not IP; and Nova metadata is not a
dependency (config_drive=True). Surviving v4 pressure is soft or external.
R8a BUILT. phase-05-octavia-verify.sh now compares o-hm0's MTU against
lb-mgmt-net's (resolved BY TAG, never by name), fails in either direction,
and REFUSES when a value cannot be read. This closes the LP#2018998
obligation, live on our exact pin after its 2025-12-31 recurrence.
THE DECISION RECORD HAD CLAIMED THIS SHIPPED. design-decisions.md carried a
present-tense "Delivery: the assertion ships in ..." written on the day of
the ruling, while the file held zero MTU references and its last commit
predated the ruling by a month. Corrected in place. Sharper than the usual
ruled-but-not-built class: the decision doc was the false witness.
TWO OWNED ERRORS:
- I concluded "no octavia harness exists" from a NAME grep. The script's
harness has always been tests/phase-05. Duplicate deleted, R8a cases
folded into the real one.
- My first MTU draft ABORTED the script: a $( ) under set -euo pipefail with
inherit_errexit exits the run rather than reaching the refusal branch. The
pre-existing harness I had just declared nonexistent caught it. Same class
as commit 1's IFS word-splitting trap; both now locked by cases.
Two live surfaces still contradicted R8 -- dc-dc-phase4 and the workflow doc
both called lb-mgmt IPv6 "a real, open risk" and recommended keeping it
v4-only, citing the two refuted bugs. A reader would have re-litigated a
closed ruling in the wrong direction. Both superseded in place.
R7 executed: phase-01 Step 1.0-GEN gains a $DC selector; the baked
/CN=VR0 DC0 CA subjects derive from it, and the VIP gate derives this DC's
provider prefix from the same overlay it read the VIP from -- R7's explicit
"read the MERGED input" caveat. dc1's 10.12.64.57 used to hard-ABORT, so no
dc1 artifact could be produced. Verified both DCs, negative control still
aborts.
Logged not built: the controller cert's CN/DNS SANs still carry dc0.vr0
(outside R7's ruled scope, inert while os-public-hostname is unset), and it
gains no IPv6 IP SAN though the VIP is dual-family -- a gap, not a break,
and adding v6 SANs needs its own ruling.
tests/phase-05 14 cases (was 9); gauntlet ALL GREEN (85) on vcloud;
repo-lint 0 fail / 611 files.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HvCyrwvYTTcDYnRErfMsNf
|
| 2026-07-28 |

D-101 governing rationale recorded; node v6 carve scoped; two of my claims corrected
...
The operator's question -- "are we mirroring the assigned IPv6 octet with the last
IPv4 octet like we did before?" -- exposed two errors in what this session recorded.
(i) I said MAAS static assignment "needs only the subnet, which now exists". Wrong:
mode=static means an EXPLICITLY CONFIGURED address, which is how the Stage-4 carve
set the 90 v4 links. Measured -- all 18 Ready nodes carry ZERO IPv6 links, while
their v4 side is correctly octet-mirrored. So VIPs are mirrored (156 apex objects)
and nodes are not; 108 assignments are owed. (ii) v6 gateway_ip and dns_servers are
unset on all 12 v6 subnets, unnamed before I called step 3 complete. That
"complete" is withdrawn -- the MAAS/apex/lib-net population stands, the node layer
is the remaining half.
D-101 gains its GOVERNING RATIONALE, quoted verbatim, because it existed in no repo
surface: IPv6 unless IPv4 is NECESSARY, driven by real IPv4 sizing constraints in
future expansion -- a commercial requirement, not a preference. It records that
v4-first was DELIBERATE RISK REDUCTION so a future session does not read v4
surfaces as neglect and "correct" them; that Roosevelt has full v4 and v6 edge
transport; and that NAT64/DNS64 was considered and REJECTED on that basis -- a shim
with no Roosevelt analog, against ULA planes that are internal by design.
Node v6 carve scoped in docs/audit/node-v6-carve-scope-20260727.md: 108 assignments
mirroring each node's live v4 octet, prior art measured v4-only, blast radius
per-link and reversible, and the operator's own rationale arguing to carve BEFORE
the deploy since v6-unfriendly modules surface far more cheaply on a static
read-back than mid-bundle.
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
|