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/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.