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, or the parallel Claude Code stream -- can resume without losing pending work.
How to use it (standing practice).
bash scripts/ledger-scan.sh. Reconcile.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.scripts/ledger-scan.sh; do not hand-edit)As of 2026-07-09 scan (post-DOCFIX-161, final consolidation pass); re-run the scan to refresh:
docs/dc-dc-deployment-workflow.md is CLEARED; see docs/dc-dc-buildout-design.md Section 10 for the ruling record. (D-050 RESOLVED/CLOSED 2026-07-06 via D-051; D-073 APPLIED live 2026-07-06 -- not scan-tracked as open. D-076..D-099 are a RESERVED source-project band, never assigned in this repo, per D-110.)scripts/lib-validate.sh (exit contract 0/1/2/3/4, emit, vr_json stderr-safe, run, env hygiene, disruptive gate) + scripts/validate.sh orchestrator (profiles, verdict, --stop-on-fail, --include-disruptive).
checks/d011-01-charms, checks/d011-06-vault-unseal (MANUAL until the SEC-003 rehearsal closes).checks/d011-02-vip-jumphost (catalog-derive public origins, TLS+HTTP), checks/d011-03-vip-tenant (ephemeral agnhost pod egress -> keystone VIP, D-035 proof). post-restart profile now fully populated.d011-04-octavia-lb (RR+member additive; amphora failover --disruptive, N+1 headroom-guarded) + d011-05-magnum-e2e (wraps tenant-acceptance + timing). full-d011 profile now COMPLETE.vault-kv-health.sh into cloud-assert as a section; use the combined gate to regenerate the stale restart-procedure runbook.scripts/tenant-cluster-create.sh -- generalize the t2-02 stage6 + watch + D-066-evidence wrapper. Not started.scripts/keystone-policy-drift.sh -- D-051 base_* alignment (LIVE-READ PENDING). Not started.scripts/cloud-snapshot.sh -- juju-export baseline capture (D-070 retired KVM snapshots but KEPT the export/inventory baseline). Not started.tests/tenant-acceptance/ harness -- pending (tenant-onboard harness landed).d011-06-vault-unseal (currently MANUAL). Operator input, recorded off-repo.server show .flavor; hypervisor list --long vcpu/ram) -- safe default HOLDs on any parse miss, but confirm the OK path live.--sweep-magnum-orphans mode (orphan per-cluster trustee in the magnum domain).host_href=None barbican observation (exercised fine in stage 6; probably closeable).-f json ... 2>&1 stderr-merge hazards (DOCFIX-085 class).identity:list_trusts="" hardening remain PROPOSED/OPEN, not actioned.First execution of runbooks/ops-update-procedure.md (DOCFIX-086; addendum 13). Logged session ops-update-20260705; checkpointed here at each milestone for cross-stream sync. State as of the Section 2 entry checkpoint:
juju create-backup -m admin/controller (902MB, checksummed, ~/openstack-baseline/, jumphost-only). NOTE: controller model is admin/controller, NOT /controller.juju create-backup/download-backup EXIST on juju 3.6 -- authoring assumption ("removed in 3.0, expected absent") is WRONG; correct ops-update-procedure 2.1 and the D-071 risk section at window close (DOCFIX candidate).repo_lint.py L5 "next-free identifiers" info line counts ANY textual mention of D-/DOCFIX-/BUNDLEFIX-NNN across all files, so it is inflated by the deliberate high-numbered decoys in the tests/ledger-scan fixture ("must NOT inflate") and by next-free pointer lines themselves. ledger-scan (heading-based, pointer-excluding) is the numbering authority and is correct. DOCFIX candidate: make L5 reuse ledger-scan's exclusion rules or drop the line. No number consumed. NOTE (2026-07-06 correction): the first version of THIS bullet quoted the decoy tokens literally and thereby inflated ledger-scan's own DOCFIX/BUNDLEFIX next-free (docs/ prose is scan-scanned; only "next-free" lines are excluded). Tokens de-fanged; standing rule: never write identifier-shaped tokens above the real high-water mark in docs/ or runbooks/ prose.channel + ssl-* options verified; snap has NO 1.13/1.14 track -- max MPL payload in-charm = 1.12.11; OpenBao memo updated. Orphan-trustee sweep draft NOT started (queued).juju login -u jessea123 at next presence. Blocked reads queued: keystone policy.json PENDING-LIVE-READ trio; barbican host_href look. (Vault reads above completed BEFORE expiry.) CLEARED later 2026-07-06: auth verified working; both queued reads DONE (see addendum 27 closures).clustertemplate:publish (admin_api) refusal, BY DESIGN; read-only diagnosis, no cloud change. DOCFIX-121 (clientdocs template-creation guidance + Public/Hidden warning) + DOCFIX-122 (appendix-A symptom entry); L7 receipt re-recorded; ~/devteam-package re-rendered (kubernetes.md, self-service-guide.md) for tonight's push.devteam-k8s was never delivered (window ran stages 0-4); one gated command (bash scripts/tenant-onboard.sh devteam 5) closes the docs' delivered- template promise; else devteam self-creates per new docs.TestCluster (uuid 92b92eed-afa3-4aaf-ab33-13d3ddeea869) CREATE_COMPLETE + HEALTHY (cluster / infrastructure / controlplane / nodegroup all Ready), created 2026-07-08 17:18 UTC in devteam-prod. 1 master + 1 worker, keypair devteam-key, api https://10.12.7.130:6443. calico + small-first + correct keypair + no Public/Hidden flag -- the DOCFIX-121 dashboard symptom resolved IN PRACTICE on their first real self-service create. (Note: this fresh TestCluster reuses the name of the earlier deleted TestCluster/TestCluster2, addenda 40/42 -- cosmetic.)TestTemplate (calico, public/hidden False, keypair baked in = devteam-key) in the dashboard and launched TestCluster from it, rather than the delivered CLI template devteam-k8s (keypair_id None; measured, correct-by-design, still unused). Both are valid Magnum objects.openstack coe cluster config) has NO Horizon path -- the supported retrieval is the CLI. (Earlier draft of this bullet claimed magnum-ui "offers a cluster-config download" -- UNVERIFIED, retracted; do not assert it.) The Jenkins integration requires the CLI/app-cred path regardless.Operator greenlit the recommended sequence: (1) Phase A stderr fix, (2) S7+S4, (3) S8; HOLD S1/S2/S3/S5 (lib-validate adoption on tenant-onboard/offboard) until the DOCFIX-126/127 live-verify lands. Source: docs/script-quality-findings-20260707.md.
tinv() (tenant-scoped inv() twin) for the 3 Phase A coe cluster list captures; wait loop retries on list-error, final check fails closed. Latent bug: merged stderr could false-trip exit 21 on an already-clean tenant. Harness un- excludes coe from warnstderr + new coe-stderr-not-argv assertion (22/22). Gauntlet 38/38, lint 0 fail, L7 re-recorded. Committed + pushed.CF (d011-04) + subsumed vr_is_ipv4 guard (d011-03). S4: three inline trust filters -> one fixture-tested scripts/trust_filter.py (+tests/trust_filter/ 9/9); SDIR added to offboard + acceptance. Gauntlet 39 ALL GREEN. Committed + pushed.. lib-net.sh sourcing into client-critical scripts I just modified (DOCFIX-131 + S4), which stacks risk. Breakdown:
bash scripts/repo-lint.sh --record-guide-skill-coupling. Harness +5 (28/28), gauntlet 39/39. So the next guide edit CANNOT land without a conscious skill review. Skill-lag closed at the source.default (default) provisioner cinder.csi.openstack.org, Retain, WaitForFirstConsumer, expansion=true. So the delivered template (volume_driver unset) DOES yield PV support via the cinder-CSI magnum default -- persistent storage works out of the box; no template change needed. sec 5.5 doc is correct and self-verifying; OPTIONAL enhancement: state the expected default cinder class (would trigger L8 skill sweep). Cleanup: kubeconfig removed, no residue.member (invisible to the existing 50 policy pass/fail assertions); (3) clean up the orphaned test user 9a62f88e331f41adb5c9e41225ec8698 (name test1, CONFIRMED still present in devteam, ZERO roles -- delete or grant member); (4) operator RULING: adopt rev2 (a) vs close GUI/CLI-only (b) + accept/reject the charmhelpers blacklist-hole reliance; (5) another operator-present gated window to re-test after rework. NOTE: live override state UNVERIFIED -- juju macaroon expired (needs juju login); confirm use-policyd-override is reverted to baseline. Draft in worktree agent-a73ec798 (uncommitted). Clients unaffected: CLI identity path is the documented behavior.openstack user delete 9a62f88e331f41adb5c9e41225ec8698 (name test1, devteam domain, 0 roles measured 2026-07-08). Run at the next convenient window; it is a role-less shell with no resources, so removal does not affect devteam's tests.cp ~/vault-init/vault-ca-root.pem ~/devteam-package/omega-cloud-ca.pem (guard correctly blocks agent access to ~/vault-init); (b) deliver ~/devteam-package + the ~/tenant-devteam credential files in-person to the two named custodians.tenant-offboard.sh --sweep-magnum-orphans --audit reaps its trustee.Cross-stream: main-chat to reconcile its ledger section against addenda 27-33 (its backlog items 6/7/8 delivered here; its Verify-live queue fully cleared; D-050/D-073/D-074 all resolved).
Horizon three-account walkthrough + docs alignment (2026-07-07 cont., addendum 38): operator live-tested all three devteam accounts in the GUI; every observation contract-correct (details in item 2 above). DOCFIX-119: clientdocs aligned to observed GUI reality (5 files by background docs agent -- first-login expectations, CLI user-creation commands, popup signatures, object-storage row de-hedged + acceptance script aligned; harnesses 8/8 + 47/47; L7 receipt re-recorded) + README instantiation guidance (DASHBOARD_URL includes webroot). DOCFIX-120: contract section 5 amended with live verdicts (5.1 verified; 5.3 closed-by-CLI-clause; stage-7 canary marked IMPLEMENTED). Package RE-INSTANTIATED to ~/devteam-package from updated templates (incl. /horizon URL); OPERATOR: re-copy to local + deliver. Dashboard override attempt/revert logged in item 2c.
openstack-caracal-dc-dc first load (repo-rename sweep)vopenstack-jesse) CLAUDE.md describes -- flagged to operator. run-tests-all.sh gauntlet on this box shows 23/39 pre-existing failures (missing jq, tty/exit-code quirks) unrelated to any change made this session; repo-lint (the only suite touched) is ALL PASS standalone and in the gauntlet. Confirm actual jumphost behavior at next Linux session.docs/changelog-20260709-dc-dc-repo-rename-sweep.md): repo-rename sweep (stale openstack-caracal-ipv4 path/URL references repaired to openstack-caracal-dc-dc across the skill, 4 runbook RUN blocks, and the repo_lint.py docstring; clientdocs L7 receipt re-recorded after confirming no client-facing drift) + a real repo_lint.py bug fix (L1 Windows path-separator: the docs/design-decisions.md legacy-carve-out comparison used str(p.relative_to(R)), which never matches on Windows, so the documented WARN silently became a FAIL -- fixed to .as_posix() at all 3 affected sites; harness gained T3b covering the carve-out path directly, 29/29 green). NOT actioned (flagged, larger scope): README.md and environment.md's body content still describe VR0 DC0/v1 single-DC scope wholesale -- awaiting operator direction on whether/when to rewrite for VR1 DC-DC. Uncommitted -- operator has not yet asked to commit.docs/changelog-20260709-repo-agnostic-sweep.md): operator asked to scope the "make scripts repo-agnostic" idea as its own sweep + a lint rule, following directly from the DOCFIX-139/140 experience. New repo-lint check L9 (self-reference guard) FAILS any script/RUN-block/ skill file that hardcodes the current repo's own directory name; added with a 5-case harness (34/34 green). Along the way, narrowed all_text()'s .claude exclusion from "skip all of .claude/" to "skip only .claude/worktrees/" -- the blanket exclusion meant checked-in .claude/skills/ and .claude/hooks/ content was invisible to lint entirely (why the SKILL.md hardcode from DOCFIX-139 wasn't caught). Established a $REPO session-variable convention (documented in runbooks/README.md Conventions) and converted the 8 remaining hardcoded clone-path references (SKILL.md + 4 runbooks) to require it explicitly (fail loud if unset, no inferred fallback, per hard rule 2). environment.md's repo URL is KEPT (documented anchor fact, same as its other facts) but tagged with the new repo-lint: allow-selfref opt-out. LOGGED, NOT ACTIONED: L3/L4/L6/L9 only recognize triple-backtick fences for "in code" detection; d011-batch3-window-DRAFT.md (indented pseudo-code) and tenant-onboarding-v2-DRAFT.md (BEGIN/END block comments) use different conventions and are invisible to these checks -- a pre-existing gap, not introduced here, DOCFIX candidate (teach the checks the other conventions, or standardize runbooks onto fences). Uncommitted -- operator has not yet asked to commit.docs/dc-dc-deployment-workflow.md added (Stage 0-7 followable checklist + tooling gap register audited directly against the repo) + a companion visual Artifact (https://claude.ai/code/artifact/e752f6dd-23ff-4c59-9ce5-1dedb917e5ea). COMMITTED + PUSHED (374aef5).docs/changelog-20260709-opentofu-scaffold.md): first OpenTofu content in the repo (gap register item 2). opentofu/ network + storage-pool scaffold: dc-planes (six per-DC planes), mesh-link (D-100 dark-fiber triangle), dc-storage-pool modules; DC1 + Office1 wired, DC2 held back pending its CIDR assignment (D-101). Provider schema (dmacvicar/libvirt v0.9.8) verified against its actual current docs this session, not memory -- differs materially from older common examples (network isolation is forward.mode, nested, not top-level mode; domain resource restructured to ~40 args mirroring raw libvirt XML). Node-VM/volume resources, OPNsense, DC2 planes, and tc netem application DELIBERATELY DEFERRED -- schema too unfamiliar to author safely without a real tofu providers schema pass; see opentofu/README.md. Added scripts/opentofu-validate.sh + tests/opentofu-validate/ (2/2 green -- only the guard-clause paths testable without a tofu binary, which is not available anywhere this repo has been worked in this session; the actual fmt/init/validate path is UNTESTED, logged not hidden). Extended repo_lint.py's L1 file-extension set to include .tf (previously uncovered since no Terraform/OpenTofu files existed). repo-lint 34/34 harness green, 0 fail/1 documented warn. Uncommitted -- operator has not yet asked to commit this piece.tofu binary + registry network access, run scripts/opentofu-validate.sh, then tofu providers schema -json against the initialized provider, and read the real libvirt_domain/libvirt_volume schema directly before authoring the node-VM module.tc netem a separate OS-level thing blocked on an unruled D-100 parameter). Mirrored into opentofu/README.md + the visual tracker. COMMITTED + PUSHED (0bb6cff).docs/changelog-20260709-repo-lint-crlf-fix.md): fixed the CRLF write_text bug flagged during the DOCFIX-141 commit -- --record-clientdocs-sweep/--record-guide-skill-coupling now write_bytes() with an explicit ascii-encode instead of relying on .gitattributes eol=lf to clean up CRLF after the fact. Verified 0 CR bytes on a live re-record; harness 34/34 unchanged; repo-lint 0 fail/1 warn. COMMITTED + PUSHED (f93dd0d).docs/changelog-20260709-opentofu-node-vm.md): moved into the domain module as requested. Built opentofu/modules/node-vm (blank-disk PXE-boot MAAS-managed node VM, D-103's pattern for Stage 3) -- this time from the provider's own real example .tf files, not doc-summarization alone, after last session deferred libvirt_domain for exactly that confidence gap. Found and fixed a real bug in the already-committed DOCFIX-142 delivery in the process: dc-planes/ mesh-link/dc-storage-pool used classic HCL block syntax (domain { ... }) where the provider's current schema actually needs attribute-style objects (domain = { ... }) -- confirmed once real provider examples surfaced the convention; doc-summarization alone had NOT caught this. All three corrected; opentofu/README.md's schema notes now state the attribute-object convention as a provider-wide assumption. UNVERIFIED, flagged: the per-device boot-order attribute's exact shape (an inference by pattern). Still deferred: the cloud-init/base-image VM pattern (Office1 services + OPNsense), DC2 planes, netem. COMMITTED + PUSHED (3324387).docs/changelog-20260709-opentofu-cloudinit-vm.md): built opentofu/modules/base-image + modules/cloudinit-vm (the OTHER VM pattern, for Office1's own service VMs -- MAAS-region, NetBox, GitBucket). Same high-confidence source as DOCFIX-144 (examples/alpine_cloudinit.tf, fetched as real code). Threaded the base image's .path output across the module boundary as a real attribute reference rather than reconstructing a volume path from pool+name strings -- a cleaner design than the earlier draft, avoiding an unconfirmed-convention guess entirely. user_data/meta_data/network_config are required inputs with no default (Office1 VM configs aren't designed yet; a plausible DHCP default would also silently fail since these planes carry no libvirt DHCP). OPNsense applicability explicitly NOT confirmed -- FreeBSD-based, cloud-init/NoCloud support not the same guarantee as Linux images, flagged prominently rather than assumed. Neither module instantiated (no image source chosen for any service VM). Workflow doc + visual tracker updated. Uncommitted -- operator has not yet asked to commit this piece.docs/changelog-20260709-opnsense-research.md, no DOCFIX -- research delivery, matches the design-doc precedent): operator asked to check OPNsense's actual documentation. Confirmed cloud-init is genuinely unreliable on OPNsense (FreeBSD; OPNsense's own forum consensus), closing the DOCFIX-145 open question. Found the real mechanism instead: the Configuration Importer (official docs) scans an attached volume for /conf/config.xml in a 2-3s boot window; ISO9660 support was added to it specifically for VM/cloud automation (a closed, milestone-targeted, core-dev-assigned GitHub issue) -- mechanically identical to cloudinit-vm's cdrom-attach shape, different payload. Use the nano image for KVM (pre-installed, serial-ready), not the installer images. Fully sourced in opentofu/README.md. NOT built yet: a decompress/convert preprocessing script for the nano image, an ISO9660 config.xml-seed builder, and the real config.xml content -- itemized as the next actionable step. COMMITTED + PUSHED (75ca823).docs/changelog-20260709-opnsense-edge-module.md): operator asked to continue with OPNsense while the research was fresh. Built all three itemized next-steps: scripts/opnsense-prep-image.sh (nano image decompress+convert, no hardcoded mirror -- OPNSENSE_MIRROR_BASE required), scripts/opnsense-build-config-iso.sh (ISO9660 config.xml seed), and opentofu/modules/opnsense-edge wiring both together -- mechanically identical to cloudinit-vm's cdrom-attach shape, no libvirt_cloudinit_disk involved. Explicit lan_network_name/ wan_network_name variables (not an ordered list) to prevent a silent LAN/WAN swap. Both harnesses green (4/4, 3/3) exercising REAL missing-tool guards (qemu-img/genisoimage/xorriso are all genuinely absent this session). Two things flagged, not presented as fact: whether the Configuration Importer's ISO9660 behavior holds on a real boot, and whether devices.interfaces list order reliably maps to LAN/WAN NIC enumeration for this guest. config_iso_path has no default -- real config.xml content per site is still undesigned. Not instantiated in root main.tf. opentofu/README.md + workflow doc + visual tracker updated to reflect BUILT (not just researched). COMMITTED + PUSHED (a3ff07f).docs/changelog-20260709-opentofu-maas-netem.md): operator asked to pull docs and validate before drafting the next modules -- the two remaining unbuilt mechanisms from the OpenTofu scope (MAAS VM-host registration, tc netem application). Researched a real official MAAS provider (canonical/maas v2.7.2) and OpenTofu's own local-exec/terraform_data docs BEFORE writing any code, per instruction. Caught a real design mistake before it happened: read both maas_vm_host (register) and maas_vm_host_machine (compose -- takes cores/memory/disks as INPUTS, MAAS's "Compose machine" pod feature) schemas, cross-checked against D-103's explicit "MAAS does NOT compose new ones" ruling, and built modules/maas-vm-host using ONLY maas_vm_host -- the other resource would have had MAAS and node-vm fighting over VM creation. Also confirmed local-exec runs on the machine invoking tofu apply (Office1 per D-103), not the vcloud host where bridges live -- modules/netem-link wraps its tc qdisc command in an explicit SSH hop because of this, using terraform_data (OpenTofu's own recommended replacement for null_resource) with a destroy-time provisioner to clean up. Neither module instantiated (MAAS zone/pool, vcloud SSH target, and real netem parameters all still pending). opentofu/README.md gained a new sourced research section; workflow doc + visual tracker updated. COMMITTED + PUSHED (ea276c5).docs/changelog-20260709-opentofu-audit.md): operator asked to check back over all current modules, pull docs for anything drafted without it, and review for errors/assumptions. Grepped every "UNVERIFIED"/"inferred"/"assumed" marker across all 9 modules and researched each further rather than re-stating hedges unchanged. Found and fixed a genuine documentation error, not just a hedge: opnsense-edge's comments claimed interface list-order directly sets OPNsense's LAN/WAN role -- researching OPNsense's own interface-assignment model showed role assignment is a SEPARATE, explicit config.xml mapping, not automatic from device order ("vtnet0=WAN" is a convention some guides use, not an enforced rule). Comments + both LAN/WAN variable descriptions corrected; no config.xml has been written yet so this fixes documentation accuracy for whoever writes it next, not current runtime behavior. Confidence UPGRADED (not fully resolved) on three items: node-vm's boot-order shape (confirmed the provider 1:1-mirrors libvirt's own XML, and libvirt's own docs confirm <boot order='N'/>'s single-attribute shape), create.content.url accepting local paths (second independent source), and the genisoimage flags (confirmed standard, cleanly separated from the still-open "does OPNsense's Importer actually read it" question). Confirmed safe: maas_vm_host's zone/pool computed-if-unset behavior. opentofu/README.md gained a new "Audit pass" section. Deliberately did NOT re-research already-well-sourced findings (Importer mechanism, vm_host-vs-vm_host_machine, the syntax-bug fix) -- targeted only real gaps. Also added (same delivery): .claude/skills/openstack-cloud-ops/references/ opentofu-provider-docs.md -- indexes every provider/doc source used building opentofu/ with exact URLs, confirmed versions, default branches, AND the fetch methodology that actually worked this session (Registry doc pages are JS-rendered, use GitHub instead; branch names vary, check default_branch via the API; real example .tf files beat doc-summarized prose for syntax questions). New SKILL.md routing entry; cross-referenced from opentofu/README.md in both directions, no content duplicated (README.md stays the single source of truth for per-module findings). COMMITTED + PUSHED (96ecbad).docs/changelog-20260709-opnsense-config-design.md): operator asked to move into designing the real config.xml content. Fetched OPNsense's own real, currently-shipped config.xml.sample (opnsense/core repo) and its Route.xml static-routes model before drafting. Built opentofu/templates/opnsense-config.xml.tmpl ({{TOKEN}}-parameterized, reusing this repo's existing clientdocs convention) + scripts/opnsense-render-config.sh. The real sample fetch DIRECTLY CONFIRMED the DOCFIX-148 audit finding with an actual example: ships literal placeholder device names inside each interface's own <if> element, proving LAN/WAN role assignment really is the explicit per-block mapping DOCFIX-148 concluded. The renderer is tested END-TO-END, not just guard clauses (8/8 -- it needs no external tool, a first among the opnsense-* scripts) -- and the harness caught a REAL bug before it shipped: the token HOSTNAME collides with bash's own built-in $HOSTNAME variable (unset doesn't actually clear it), silently passing a test that should have failed. Renamed to OPNSENSE_HOSTNAME throughout. Full token legend in opentofu/templates/README.md, marking exactly which values are real (NTP pool, D-106 naming) vs. still pending Stage 0 ratification (D-100/D-101/D-107) vs. only measurable on a real boot (vtnetN assignment) vs. a security requirement (root password hash must be freshly generated, never the stock sample's own shipped default). opentofu/README.md + workflow doc + visual tracker updated. Uncommitted -- operator has not yet asked to commit this piece.docs/changelog-20260709-stage0-ratification.md, DOCFIX-150): operator walked through the 6 buildout-design redline items live (AskUserQuestion, 2 rounds): Ceph size=3-by-default (size=2 only as an explicit Phase-0 fallback); netem same-metro lean; D-102 folds into D-101, D-109 stays standalone; metal-admin GAINS a ULA leg (reverses the stated lean -- a real deviation, not a rubber-stamp); COS per-DC-only confirmed; mirror sync topology independent-per-DC confirmed. All 11 of D-100..D-110 individually flipped PROPOSED -> ADOPTED in docs/design-decisions.md (D-102's content preserved under a MERGED-INTO-D-101 status line, per the append-only discipline -- not deleted). docs/dc-dc-buildout-design.md Section 10 rewritten from "open items" to a ruling record; Section 3's Ceph gate and the stray "Section D-102" cross-reference corrected to D-101. Verified via ledger-scan.sh: D-100..D-110 no longer appear under PROPOSED/OPEN -- only D-068/D-071 remain (separate, pre-existing, unrelated to this gate). repo-lint 0 fail/1 documented warn, harness 34/34. Stage 0 of docs/dc-dc-deployment-workflow.md is CLEARED.run-tests-all.sh shows 23 pre-existing failures in this environment -- all either "jq required" (not installed on the workstation) or live-cloud-dependent suites (cloud-assert, preflight, tenant-*, etc.) -- confirmed via git stash A/B to be identical with and without this session's own changes, i.e. an environment gap, not a regression.docs/changelog-20260709-dc-selector-convention.md, DOCFIX-151): added lib_net_select_dc() to scripts/lib-net.sh (dc0/dc1 no-op per D-101's inherited-layout ruling; dc2 fails loud -- gap #3 still open) and lib_hosts_select_dc() to scripts/lib-hosts.sh (dc0 no-op; dc1 AND dc2 BOTH fail loud -- no real per-DC host enrollment exists for either yet, an intentional asymmetry vs. the net selector, documented in-line in both files). Backward compatible by construction -- no existing caller of either library is affected. New harness tests/dc-selector/run-tests.sh: 21/21 PASS (caught and fixed a real pipefail-vs-nonzero-exit-code bug in the harness itself while writing it). repo-lint 0 fail/1 documented warn (unchanged); full gauntlet confirmed via git stash A/B to introduce zero new failures. Stage 5's runbook (not yet authored) still needs to actually CALL lib_net_select_dc "$DC" / lib_hosts_select_dc "$DC" -- the mechanism exists, the call sites don't yet.docs/changelog-20260709-netbox-dc-dc-pipeline.md, DOCFIX-152): netbox/dc-dc-prefixes-import.py extends the v1 single-site IPv4-only NetBox import to VR1's two-DC dual-stack model (D-101). DC1's v4 planes hardcoded (D-101: inherited unchanged, this is decision text, not an inference); DC2's v4 supernet, the org ULA /48, and the per-DC GUA carve are all REQUIRED env vars with no defaults -- fails loud rather than inventing any of them. The per-plane subdivision scheme for the ULA/GUA blocks is flagged in the script's own docstring as a proposed, unratified convention pending operator review. New harness tests/dc-dc-prefixes-import/ (a fake in-memory NetBox client): 37/37 PASS at the time (later corrected to 40/40 by DOCFIX-160 -- a dead-code bug in the harness itself had left 3 happy-path assertions silently uncounted; see that entry below). Caught and fixed two real authoring bugs at delivery time: (1) example GUA addresses in the first draft had host bits set beyond the /40 boundary, caught by Python's own ipaddress.ip_network(strict=True); (2) the test's own "count CREATED lines" assertion was inflated by the script's unrelated site-creation message also containing the word CREATED -- tightened to match prefix-specific lines. Flagged, not fixed (out of scope): the existing v1 ipv4-prefixes-import.py's role slugs predate the current six-plane model in scripts/lib-net.sh; this new script uses the CURRENT slugs instead. repo-lint 0 fail/1 documented warn (also caught and fixed a CRLF regression introduced by a Python find/replace during test-data cleanup). Full gauntlet 23/45 FAILED -- confirmed identical pre-existing environment-gap failures (jq missing / no live juju-cloud access on this workstation), zero regressions. UNVALIDATED against a real NetBox (none reachable this session). This closes the TOOLING half of gap #3 only -- the literals themselves (ULA /48, GUA carve, DC2 supernet) still need real assignment before this script can be run for real.docs/changelog-20260709-dc-dc-phase0-runbook.md, DOCFIX-153): runbooks/dc-dc-phase0-vcloud-prep.md -- command-level Phase 0 (vcloud host prep) steps, matching the phase-00 runbook's style (sequence table, read-only CHECK before every mutation, individually gated mutations, an explicit gate section, delivery checklist). Surfaces a genuine open structural question rather than silently resolving it: opentofu/main.tf's unconditional provider "maas" block may force MAAS credentials to be set at tofu plan time even though this stage's plan touches only libvirt resources -- documented both possible outcomes and what to do for each. Also flags the VR0/DC0-pool-path-collision risk if the vcloud host is the same physical machine being repurposed (buildout design's own framing). NOT YET EXECUTED -- this is the runbook the operator will run tomorrow morning after switching to the vcloud host; nothing in this delivery claims a real run happened. repo-lint 0 fail/1 documented warn (unchanged, prose-only).docs/changelog-20260709-dc-dc-phase1-runbook.md, DOCFIX-154): runbooks/dc-dc-phase1-office1-standup.md. Every VM- creation step forks explicit Option A (OpenTofu, blocked pending cloud-init content design)/Option B (manual, logged debt). Found a real, previously-unnoticed gap: no Office1-local virtual network is modeled anywhere in opentofu/. Caught and fixed a repo-lint L2 false-flag during review (a correctly-sourced historical D-014 path reference, reworded onto one line so the rule's own EXEMPT pattern applies, not deleted).docs/changelog-20260709-dc-dc-phase2-runbook.md, DOCFIX-155): runbooks/dc-dc-phase2-tofu-dc-substrate.md. DC1-first; DC2 hard-gated (not "later"). Five !!! callouts naming real constraints (Stage 2 dependency, DC2 gate, no-Roosevelt-analog shim, undecided node sizing, unruled netem params) plus OPNsense tool- presence check. GATE section concludes "CONDITIONALLY MET AT BEST," refusing to claim more than is actually true.docs/changelog-20260709-dc-dc-phase4-runbook.md, DOCFIX-156): runbooks/dc-dc-phase4-juju-bundle-per-dc.md. Describes adapting (not duplicating) phase-01..08 to run twice, once per DC. DC1 needs zero VIP/CIDR edits (D-101 inheritance); DC2 blocked on the same NetBox literals as Stages 3/4. Mandates lib_net_select_dc "$DC"/ lib_hosts_select_dc "$DC" before every adapted command block touching those libraries (DOCFIX-151's first real consumer). Surfaces two genuine open design gaps rather than inventing around them: the D-101 IPv6 family-matrix overlay (exact charm config option names unconfirmed) and D-109's IPv6-SAN cert-issuance charm support (unverified assumption). All three: repo-lint 0 fail/1 documented warn; NOT YET EXECUTED (prep-only session, no live infrastructure reachable).docs/changelog-20260709-dc-dc-phase3-runbook.md, DOCFIX-157): runbooks/dc-dc-phase3-maas-enlist-deploy.md. Mandates lib_net_select_dc "$DC" then HONESTLY documents lib_hosts_select_dc "$DC" as expected to fail for both DCs until this stage's own enrollment populates real host data -- a deliberate chicken-and-egg. Names 7 real gaps in its own section (no OpenTofu module stands up a per-DC MAAS rack controller VM; three scripts not $DC-parameterized; node sizing undecided; DC2 blocked at the selector layer; no owner for the per-DC mirror; VM-host-discovery MAAS status unconfirmed; DC0- specific tag/fabric literals).docs/changelog-20260709-dc-dc-phase5-runbook.md, DOCFIX-158): runbooks/dc-dc-phase5-dr-failover-drill.md. Found three real bundle.yaml gaps by grepping it directly (no cinder-backup charm, no ceph-rbd-mirror charm, ceph-mon's rbd-mirror binding on the wrong plane) and makes fixing them the runbook's own first gated mutation. Includes a negative-control drill (partition-only, expected to correctly refuse promotion) before the real hard-down drill. Every Ceph object name is an explicit placeholder.docs/changelog-20260709-dc-dc-phase6-runbook.md, DOCFIX-159): runbooks/dc-dc-phase6-designate-cos-magnum.md. Quotes the bundle's actual current "NO designate" text verbatim before describing the reversal; correctly identifies D-106's reactivation also reverses the bundle's separate B5 os-public-hostname-dropped posture (two coupled changes, not one); proposes per-DC hostname overlays to keep bundle.yaml DC-agnostic; quotes D-046's domain-setup trap verbatim twice; leaves COS's deployment mechanism as an explicit operator decision point rather than inventing one. All three: repo-lint 0 fail/1 documented warn; NOT YET EXECUTED. Four new tooling-gap-register items (#12-15) added for the cross-cutting findings this whole runbook-authoring pass surfaced (Office1-local network missing, IPv6 family-matrix overlay missing, D-108 bundle gaps, Stage-4 MAAS/multi-rack gaps) -- see docs/dc-dc-deployment-workflow.md.docs/changelog-20260709-adversarial-review-fixes.md, DOCFIX-160): ran a dedicated fresh-eyes review subagent (no authoring context) across lib-net.sh/lib-hosts.sh's new selectors, netbox/dc-dc-prefixes-import.py, and all seven Stage 1-7 runbooks, instructed to find real defects and cross-check every claim against ground truth directly (not trust the runbooks' own self-report) -- including running both new test harnesses live. Found and fixed 3 real issues: (1) a genuine Python control-flow bug in tests/dc-dc-prefixes- import/test_logic.py's expect_ok() -- a return inside try made the else clause unreachable dead code, silently leaving 3 happy-path assertions uncounted (37/37 was true for what ran, but overstated what was verified; now 40/40 with the fix, independently re-verified via a live python3 -c reproduction before trusting the agent's claim); (2) a self-contradictory pair of adjacent comments in dc-dc-prefixes- import.py about /19-vs-/22 arithmetic (no functional bug, just wrong prose); (3) stale "as of this writing" self-references in the Stage 4 runbook, written before Stage 3 existed and never reconciled once it did. Propagated the corrected 40/40 count into docs/dc-dc-deployment- workflow.md and this ledger's own earlier entry (left the original delivery changelog un-edited as historical record of what was believed at the time). The review explicitly found NOTHING wrong with: the $DC selector asymmetry description (consistent everywhere), the hand- verified ULA/GUA carve arithmetic, Stage 6's bundle.yaml claims (confirmed byte-for-byte), 3+ direct D-NNN quotes (verbatim-accurate), and DOCFIX/D-NNN numbering consistency across every file -- and found no inferred/invented literal value anywhere in the reviewed material. repo-lint 0 fail/1 documented warn; both affected test suites re-run clean (40/40 and 21/21) after the fixes.docs/changelog-20260709-final-consolidation.md, DOCFIX-161), closing out tonight's autonomous session: a last full read-through of docs/dc-dc-deployment-workflow.md for internal cross-stage consistency found ONE MORE real gap -- no stage explicitly owns creating Office1's own OPNsense edge VM, despite the topology being explicit that the design is per-site (three sites, three edges: DC1, DC2, AND Office1) -- logged as gap #16, not silently decided. Also fixed two stale cross-references left over from earlier in the same evening (the design-doc summary row's confusing "DONE, PROPOSED" phrasing; the lib-net/lib-hosts row's "Stage 5 runbook still needs to call it," now true of both Stage 4 and Stage 5). Companion Artifact redeployed twice more: all seven stage pills moved to a new "Runbook written" state (a 4th click-cycle state added, distinct from not-started/in-progress/ done), gap register items 1/3/9 marked closed and 12-16 added, and the 37/37-to-40/40 test-count correction propagated in. Final full gauntlet re-run: same 23 pre-existing environment-gap failures as every earlier check tonight (jq missing / no live juju-cloud access on this workstation) -- zero regressions across the entire evening's cumulative changes. repo-lint 0 fail/1 documented warn. Session summary for the operator's morning read: 11 commits (DOCFIX-151 through DOCFIX-161) delivered gap #1 (closed), gap #3's tooling half (closed, data half open), and all seven Stage 1-7 runbooks (gap #9 closed), plus an adversarial review pass and this consolidation. Nothing was executed against real infrastructure -- prep-only session, as the operator specified. The morning's first real action is Stage 1's runbook (runbooks/dc-dc-phase0-vcloud-prep.md) on the actual vcloud host. Genuinely open blockers, roughly in the order they'll be hit: gap #7 (MTU/Ceph disk-budget arithmetic), gap #16 (Office1 OPNsense edge ownership), gap #3's data half (NetBox literals), gap #2's UNVALIDATED status (no tofu binary has run yet), gaps #12-15 (Office1-local network, IPv6 family-matrix overlay, bundle.yaml DR gaps, MAAS/ multi-rack gaps).docs/changelog-20260709-mtu- ceph-budget-calculators.md, DOCFIX-162 -- numbering deferred to the orchestrating session to avoid a collision with other parallel in-flight agents tonight): scripts/dc-dc-mtu-geneve-budget.sh (required --underlay-mtu, no default) reproduces D-101's own worked example exactly (1500 - 56 = 1444), leaves tenant MTU at 1500 for a measured jumbo (>=9000) underlay, and adds a clearly-separated IPv6-minimum-link-MTU (1280, RFC 8200) sanity floor beyond D-101's own text. scripts/dc-dc-ceph-disk-budget.sh (required --total-disk, per-DC --dcN-nodes/--dcN-per-node-osd, and required --backup-overhead-fraction with deliberately NO default -- not a hard number anywhere in this repo, must come from real DC1 Ceph/radosgw-admin measurement) computes whether size=3 across DC1+DC2 plus overhead fits the measured total disk, and if not, names size=2 as the ONLY documented fallback (D-101/Section 3) without silently applying it -- an explicit operator-logged decision. New harnesses: tests/dc-dc-mtu-geneve-budget/ run-tests.sh 19/19 PASS, tests/dc-dc-ceph-disk-budget/run-tests.sh 16/16 PASS -- both cover the exact worked/constructed numeric examples, the jumbo/fits vs fallback branches, and every required-arg/bad-format fail-loud path. bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn (unchanged). Both scripts are read-only calculators (mutate nothing) and have not yet been run against the real vcloud host -- that first real run is part of executing runbooks/dc-dc-phase0-vcloud- prep.md Step 3. Uncommitted -- operator has not yet asked to commit this piece; the orchestrating session will assign the real DOCFIX number and find-replace DOCFIX-162 at integration time.docs/changelog-20260709-office1-network-edge.md, DOCFIX-163 -- numbering deferred to the orchestrating session, same reason as the #7 item above): built opentofu/modules/office1-network (a new, dc-planes-shaped isolated libvirt_network sized for ONE network, not six planes -- closes gap #12) and INSTANTIATED it for real in root main.tf (module "office1_network", needs no unmeasured value beyond already-real domain_suffix/underlay_mtu). Design decision: a new module, deliberately NOT a reused host bridge -- a bridge would be the one network in this topology living outside OpenTofu's D-103-mandated inventory and would silently break this repo's own virsh net-list --all completeness assumption; full reasoning in the module's own header and in runbooks/dc-dc-phase1-office1-standup.md's Open question #1 (now marked RESOLVED). Also decided gap #16 (Office1 OPNsense edge ownership): YES, Office1 gets its own modules/opnsense-edge call, a fourth site alongside DC1/DC2, and Stage 2's own runbook owns creating it (new Step 4b, CHECK-before-MUTATION shape, its own GATE bullet) -- Stage 2's whole scope is already "Office1 headend standup" and Stage 1 already builds this edge's network neighbors, so Stage 3 has no reason to reach back into Office1's own infrastructure. A commented-out module "office1_opnsense" skeleton now exists in main.tf, mirroring Stage 3's dc1_opnsense template exactly; its lan_network_name resolves cleanly to the new network, but its wan_network_name does not -- investigated rather than invented, and found to be a genuinely DEEPER, cross-site gap: Stage 3's own dc1_opnsense template carries the identical unresolved wan_network_name placeholder, confirming no dedicated per-site ISP-uplink/WAN network exists yet for ANY site (DC1/DC2 included). Logged as NEW tooling gap register item #17 rather than defaulted to a mesh-link network, which would have directly contradicted D-100's own sub-item ruling that the three mesh legs carry management traffic only. bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn (unchanged); opentofu/ remains UNVALIDATED overall (no tofu binary available this session, same standing status as every other module here -- not newly introduced by this delivery). Uncommitted -- operator has not yet asked to commit; the orchestrating session will assign the real DOCFIX number and find-replace DOCFIX-163 at integration time.overlays/dc-dc-ipv6-family-matrix.yaml + docs/dc-dc-ipv6-charm- research.md (full sourcing). Fetched REAL charm config/source, not memory: charm-keystone/config.yaml confirms prefer-ipv6 (bool) is a real, shared charms.openstack option; charm-nova-cloud-controller's actual "Dual Stack VIPs" commit confirms it's genuinely ADDITIVE (HAProxy binds both v4 and v6 wildcard addresses simultaneously), not an either/or switch -- resolving the biggest open question about whether D-101's dual-stack ambition is even mechanically possible. charm-ceph- mon/config.yaml confirms its OWN separate prefer-ipv6 + ceph-public-network/ceph-cluster-network, which per the charm's own docs IS a straight switch (not additive) -- the correct shape for the ULA-only storage/replication planes. Fetched the FULL charm-layer- ovn/config.yaml (25 options) and confirmed NO IPv6/encapsulation option exists at all -- OVN needs no overlay entry, geneve family follows the bound interface once the plane is ULA-only. Fetched charm-vault's actual vault_pki.py sort_sans() code and confirmed no IPv4/IPv6 distinction in cert-issuance -- D-109's IPv6-SAN requirement is code-confirmed, not assumed. Found a real, open upstream risk while researching, not invented or glossed over: Octavia's lb-mgmt-net IPv6 support has two real, still-referenced Launchpad bugs (#1911788, #1913409) describing failures -- #1911788's root cause is an OVN/LXD/ MAAS hostname-resolution mismatch, the SAME CLASS of problem this repo's own D-008 bootstrap order already hardens against, so not necessarily fatal, but genuinely unresolved. The overlay deliberately excludes an Octavia entry rather than silently forcing the risk through. Updated Stage 5's runbook (runbooks/dc-dc-phase4-juju-bundle-per-dc.md) Step 6 and the tooling gap register item #13 to reflect all of this. bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn. Changelog: docs/changelog-20260709-ipv6-family-matrix-overlay.md, DOCFIX-164 (number confirmed and closed out here -- this line previously ended with unresolved "will assign at integration time" boilerplate; caught by the full-project-sweep's DC-DC-runbook-consistency agent as an integration miss, fixed 2026-07-10).$DC parameterization for the 3 MAAS scripts (changelog docs/changelog-20260709-maas-scripts-dc-param.md, DOCFIX-166), closing gap #15 sub-item 2's CLI-invocation half: scripts/reenroll- hosts.sh, scripts/carve-host-interfaces.sh, and scripts/phase-00-maas- standup.sh now accept an opt-in $DC env var and call lib_net_select_dc / lib_hosts_select_dc immediately after sourcing, matching the same convention (and the same dc1-network-no-op/dc1-hosts-fail asymmetry) DOCFIX-151 gave the two library files the night before. Unset $DC is provably byte-for-byte unchanged (verified by direct, jq-independent invocation of all three scripts across the unset/dc0/dc1/dc2/bogus matrix, since the selector calls sit before any MAAS/jq touch-point in every script). Added $DC test cases to both existing harnesses (carve-host-interfaces, phase-00-maas-standup) and created a new one (reenroll-hosts had none before). Did NOT invent any per-DC host/CIDR literal, and did NOT make phase-00-maas-standup.sh's hardcoded PLANES table dc2-aware -- both remain honestly open (dc1 only "works" there because D-101 makes its real target identical to dc0's). Did not run a git stash A/B this time since the working tree carries other parallel agents' in-flight uncommitted work tonight; verified directly instead. repo-lint 0 fail/1 documented warn. docs/dc-dc-deployment-workflow.md item #15 and runbooks/dc-dc-phase3-maas-enlist-deploy.md (gap #2, Steps 5 and 8, delivery checklist) updated to reflect the partial closure -- six of the seven gap-#15 sub-findings remain open, said plainly rather than overclaiming. No live infrastructure touched. (DOCFIX-166, already cited above -- this closing line's stale "will assign at integration time" boilerplate corrected 2026-07-10, flagged by the same sweep pass.)bundle.yaml the jumphost operates the live VR0/DC0 rehearsal cloud with, not a DC-DC-only file): bundle.yaml gained designate/designate-bind/designate-mysql- router/designate-hacluster (D-106) and cinder-backup/ceph-rbd- mirror (D-108) + ceph-mon's rbd-mirror binding corrected from storage to replication. Caught and fixed three real mistakes during self-review, by fetching each new charm's REAL metadata.yaml rather than trusting the standard-pattern draft or the Stage 7 runbook's own earlier "template" section: (1) designate's real provides-side endpoint is dnsaas, not public like the other API charms; (2) cinder-backup is a SUBORDINATE charm (confirmed subordinate: true) -- the first draft wrongly gave it num_units/to/an amqp relation that doesn't exist on this charm at all; (3) ceph-rbd-mirror is a principal with ceph-local/ceph-remote requires endpoints (confirmed subordinate: false) -- the first draft invented a single wrong binding key. A genuine, unresolved cross-DC wiring limit documented rather than worked around: ceph-remote (the peer DC's ceph-mon) cannot be a same-bundle relation given D-104's per-DC-independent-Juju-controller design -- only ceph-local (this DC's own leg) is wired; the cross-DC peer relationship needs juju offer/consume or the manual rbd mirror pool peer bootstrap CLI flow Stage 6's runbook already documents. Deliberately did NOT reactivate os-public-hostname on the 11 API charms in this shared bundle (D-106's OTHER coupled change) -- pushed to a proposed per-DC overlay instead, to keep bundle.yaml itself DC-agnostic. Verified: python3 -c "import yaml; yaml.safe_load( open('bundle.yaml'))" parses clean (56 apps, 108 relations); python3 scripts/provider-bundle-check.py PASS, all 6 existing invariants unaffected (tests/provider-bundle-check/: 8/8 unaffected); bash scripts/repo-lint.sh 0 fail/1 documented warn. Reconciled Stage 6's and Stage 7's runbooks to point at the now-real bundle content instead of their earlier "template"/"known gap" framing. NOT applied to any live model this session -- no live cloud reachable; this is a repo-source change only, to be reviewed via juju deploy --dry-run before any real apply. Changelog docs/changelog-20260709-designate-cinderbackup- rbdmirror.md, DOCFIX-167** (number confirmed and closed out here -- this line previously ended with unresolved "will assign at integration time" boilerplate; caught as an integration miss by the full-project sweep's DC-DC-runbook-consistency agent, fixed 2026-07-10).docs/changelog-20260709-ceph-replication- tooling.md, DOCFIX-165 -- numbering deferred to the orchestrating session, same reason as the other parallel deliveries tonight): the radosgw multisite / rbd-mirror command sequences already written out in runbooks/dc-dc-phase5-dr-failover-drill.md Steps 4, 5, 8, 10.2, and 11.2-11.4 (verbatim upstream Ceph administration, not invented) are now real, callable scripts: scripts/dc-dc-radosgw-multisite.sh (master-init/join-readonly/enable-two-way), scripts/dc-dc-rbd- mirror.sh (bootstrap-primary/bootstrap-secondary --direction rx-only|rx-tx), and scripts/dc-dc-dr-drill.sh (failover/failback). Every realm/zonegroup/zone/pool/endpoint/unit name is a REQUIRED argument -- no invented defaults, matching the runbook's own <UPPER_SNAKE_TBD> placeholder discipline. Default mode is --dry-run (prints the exact juju ssh/juju run command sequence, secrets redacted); --apply executes one command at a time, aborting on first failure. The single most safety-critical property in the whole drill -- demote the CURRENT PRIMARY before promoting the RECOVERING side during failback -- is HARD-CODED as fixed call order in dc-dc-dr-drill.sh's failback subcommand, not left to documentation or an operator-orderable flag; in apply mode the promote step is only ever reached if the preceding demote succeeded. Step 10.2's per-image Glance re-registration is always printed as a flagged MANUAL reminder and NEVER auto-executed (the runbook is explicit the real invocation depends on unconfirmed Glance driver behavior); Step 11.3's Cinder reconciliation is likewise not scripted (tenant/volume-specific). Each script sources scripts/lib- net.sh and calls lib_net_select_dc per DC token touched (DOCFIX-151 convention) -- informational in dry-run (a plan preview is not a mutation), hard-blocking before any --apply (dc2 --apply refused today, exit 3, since D-101/gap #3 leaves DC2 with no assigned network literals). New harnesses tests/dc-dc-radosgw-multisite/, tests/dc-dc-rbd-mirror/, tests/dc-dc-dr-drill/ (19/19, 19/19, 23/23 PASS) test ONLY argument parsing, guard clauses, and dry-run output -- including a line-order grep proving the failback demote-before-promote property -- NOT real radosgw/rbd-mirror behavior; no live Ceph cluster exists this session to test against (same UNVALIDATED posture as every other tooling delivery tonight). Stage 6 runbook's own "Follow-up, NOT built now" delivery-checklist item and docs/dc-dc-deployment- workflow.md item #5 both updated to point at these scripts instead of describing them as future work. bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn (unchanged). No live infrastructure touched. Uncommitted -- orchestrating session will assign the real DOCFIX number and find-replace DOCFIX-165 at integration time.docs/changelog- 20260709-netem-ula-gua-proposal.md, DOCFIX-168), docs/dc-dc-netem-and-ula-gua- proposal.md: does NOT rule the netem decision or generate any of the three real NetBox literals itself -- presents a concrete, reasoned proposal (1ms delay / 0.2ms jitter / 0.01% loss / uncapped rate, each with rationale grounded in the buildout design's own "same-metro dark fiber, low single-digit ms" lean) for OPERATOR ratification, and gives the exact safe command (openssl rand -hex 5, RFC 4193-compliant) for the operator to generate the real org ULA /48 themselves. Explicitly does NOT attempt to generate the per-DC GUA carve or DC2's supernet -- both require real coordination (the ARIN-block administrator; NetBox's full address inventory) this session has no authority or visibility to perform; the section instead explains precisely what each requires and how to close it once ready. docs/dc-dc-deployment-workflow.md gap items #4(d)/#11 updated to reference it; item #10 (harness debt) also corrected -- LARGELY CLOSED given how many new test suites landed tonight (dc-selector, dc-dc-prefixes-import, dc-dc-mtu-geneve-budget, dc-dc-ceph-disk-budget, dc-dc-radosgw-multisite, dc-dc-rbd-mirror, dc-dc-dr-drill, plus MAAS script additions). bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn. No live infrastructure touched. Uncommitted -- orchestrating session will assign the real DOCFIX number at integration time.docs/changelog-20260710-phase01-bundle-staleness-fix.md, DOCFIX-169): runbooks/phase-01-bundle-deploy.md -- an EXISTING, LIVE-CLOUD-relevant VR0 runbook, not a DC-DC artifact -- had gone stale the moment DOCFIX-167 edited the SAME shared bundle.yaml it deploys. Its "expected plan: 50 apps, 97 relations" (Constants section + Step 1.2's dry-run GATE) was wrong (real count now 56/108); its GATE's "NO designate (D-019)" text flatly contradicted the new reality. Fixed both LIVE-gate references; deliberately left the historical "As-built reference (2026-06-03)" section's own 50 apps/97 relations line UNCHANGED (append-only audit- trail discipline -- it documents a real past deploy, not something to retroactively edit). Flagged a real, unresolved architectural tension rather than silently picking a side: this bundle now serves BOTH a plain VR0-only redeploy AND standing up DC1, and reactivating Designate/the DR mechanism in the shared file means any future phase-01 run gets them regardless of intent -- a future plain-VR0 redeploy needing to exclude them would need an explicit override, not an assumption either way. Extended the EXIT GATE's "waiting on vault certs" list to include designate/designate-bind (inferred from their real certificates relation to vault, clearly labeled NOT YET OBSERVED against a live deploy). bash scripts/repo-lint.sh: 0 fail, 1 documented legacy warn. Sweep continues across the rest of the repo (scripts, other runbooks, OpenTofu modules, test quality, security/secrets posture).docs/changelog-20260710-sweep-vr0-runbook-opentofu- fixes.md): phase-08-workload-cluster-acceptance.md's Designate framing (same cascading-staleness class as DOCFIX-169), nuanced not a blanket flip -- charms now active/idle-checkable, but D-011.8's real resolution criterion still needs Stage 7's os-public-hostname overlay, not automatic from deployment alone. Plus a stale opentofu/modules/ base-image/outputs.tf comment (cosmetic, no functional effect).docs/changelog-20260710-sweep-dcdc-runbook-fixes.md): Stage 1's calculator scripts (DOCFIX-162) never wired into its own Step 3 -- fixed to actually invoke them; Stage 5's Sequence-list still said [NOT YET WRITTEN] for the IPv6 overlay after the Step 6 body was updated -- fixed; Stage 6's checklist claimed its steps call the new Ceph scripts when the step bodies still only showed raw commands -- added real "PREFER scripts/dc-dc-..." pointers to Steps 4, 5, 8, 10.2, 11.2 themselves, closing the gap between claim and text. Also closed out two orphaned ledger entries (DOCFIX-164, 167) whose closing lines never got their real number written in during the earlier integration pass -- an integration miss the sweep itself caught.docs/changelog-20260710-sweep-bundle-fixes.md): magnum-dashboard had NO bindings: block AT ALL -- a real, pre-existing bug (confirmed via git log, present since the D-052 explicit-bindings commit, not tonight's regression) violating this bundle's own stated "explicit per-application blocks" discipline. Fixed with a minimal, metadata-confirmed block (subordinate, only a real dashboard endpoint -- NOT a copy of octavia-dashboard's fuller shape, which has a certificates endpoint this charm doesn't). Also relocated two section-header comments that sat before the WRONG app's bindings block (functionally harmless, confirmed via yaml.safe_load -- purely a future-edit-risk fix) and corrected the header's stale "11 API charms" VIP-chain count to 12 (with the VIP-triple count of 11 correctly left alone, since designate doesn't carry a vip: option). provider-bundle-check.py PASS throughout, 56 apps/108 relations unchanged.docs/changelog-20260710-sweep-script-fixes.md): 4 confirmed logic bugs + 1 security hardening, ALL independently reproduced in isolation before being trusted (the same discipline that caught the dc-dc-prefixes-import bug earlier tonight) -- including catching that a FIRST proposed fix for one of them (scripts/phase-06-capi-stack.sh) was itself wrong: || true after a failing pipeline under pipefail silently discards PIPESTATUS, which would have made the "GATE FAIL" check never fire again, WORSE than the original bug. The correct if pipeline; then :; fi idiom was verified via reproduction before committing. Full list: carve-host- interfaces.sh's emit() returning 1 unguarded under set -e (killed apply-mode runs on the first MAAS error, before the FATAL-accumulator summary could print); phase-06-capi-stack.sh's PIPESTATUS read (as above); juju-spaces-check.sh and osd-blank-check.sh's one unguarded display-only pipe each (both safely || true-able since nothing downstream needed their PIPESTATUS); scripts/ledger-scan.sh's DOCFIX/BUNDLEFIX regex using a fixed {3} instead of {3,} -- the SAME bug class this repo already got bitten by once for D-numbers (fixed there via {3,}, never mirrored here) -- a dormant landmine, not yet reachable (repo is at DOCFIX-170ish) but real; and dc-dc-rbd-mirror.sh's bootstrap token using a predictable /tmp path with no permission hardening -- hardened to a random-suffixed path + immediate chmod 600. Full test gauntlet re-run after all fixes: 23/51 FAILED, identical to every prior check tonight (same pre-existing jq-missing environment gaps) -- zero regressions across the entire sweep-and-fix pass. Also flagged, not a repo issue: the scripts-audit agent reported that a skill's tool output contained injected text resembling a system reminder, instructing it to conceal a date change from the operator -- it correctly disregarded the concealment instruction and reported the anomaly rather than act on it. The main session observed an identically-worded, identically-formatted message earlier this same evening (a genuine date-rollover notice, consistent with the session crossing midnight) and treated it as benign/expected rather than a live attack -- noted here for the operator's own awareness per this environment's prompt-injection disclosure norm, not because either session took any unsafe action in response.docs/v1-deploy-runbook.md.juju create-backup/download-backup DO exist on juju 3.6; a 902MB controller backup was taken). D-071 REMAINS PROPOSED -- not ratified to ADOPTED. Operator ratification is pending; the single-controller (no-HA) half of the risk is unresolved by backups alone. See D-071 + its 2026-07-05 amendment.