# 2026-07-21 -- G12 [V] leg: vr1-dc1 substrate authoring (session changelog)

Session scope: the G12 remaining [V] leg per the D-124 AMENDMENT (2026-07-21):
apex confirm-free -> vr1_dc1 vars -> dc1 substrate authoring -> gated build.
Branch: `dc-dc-g12-dc1-substrate` (off post-Stage-3-merge `main`, per the
stage-close rule). One changelog for the session (GA-R2/D1); every item
carries its revert.

## Item 1 -- dc1 apex confirm-free capture (read-only; the [V] leg's first step)

Read-only GETs on office1-netbox (token used on-host, never printed), captured
to `docs/audit/dc1-apex-confirm-20260721.txt`. Findings:

- The six dc1 plane /22s ALREADY EXIST in the apex, scoped `vr1-dc1`, exactly
  matching the ratified D-124-amendment scheme (provider-public 10.12.64.0/22
  ... replication 10.12.84.0/22, roles per D-101). Confirm-CONSISTENT: the
  assignment landed at the original D-101/D-115 import; the ruled derivation
  matches it.
- Transit 172.31.0.4/30 FREE (container 172.31.0.0/24 + dc0's /30 are its only
  occupants; zero ip-addresses in the /30).
- Uplink 172.30.3.0/24 already registered to vr1-dc1 (D-115 edge import,
  2026-07-17). Rack IP 10.12.68.2 + forwarder .3 free (zero ip-addresses in
  10.12.68.0/22). Site `vr1-dc1` and role `transit` exist.

Consequence: the ONLY apex write still owed for dc1 is the transit /30 + rack
metal-admin IP -- exactly the dc-rack-mgmt-import.py surface (dc0 precedent:
those are the only two objects dc0 registered either; transit endpoint IPs and
the D-131 forwarder alias are NOT apex objects for dc0 and dc1 mirrors that).

Revert: delete the capture file (no state touched).

## Item 2 -- dc-rack-mgmt-import.py: per-site support (vr1-dc0 + vr1-dc1)

What: the importer was dc0-hardcoded (SITE_SLUG/METAL_ADMIN/RACK_DNS/desc
constants). Replaced with a `SITES` map (dc0: 10.12.8.0/22 / vvr1-dc0, ratified
2026-07-16; dc1: 10.12.68.0/22 / vvr1-dc1, D-124 amendment 2026-07-21) selected
by a new REQUIRED `--site {vr1-dc0,vr1-dc1}` flag (env: RACK_SITE). Explicit by
design: one DC's values can never land scoped to another site. An env-supplied
site is validated against SITES in code (argparse `choices` does not validate
env-default values). ROLE_SLUG / CONTAINER / D-120 band offsets stay global
(convention-wide). Also generalized dc0-specific error/usage text and fixed two
stale "(Cloud)" labels on the container messages (the container has been
172.31.0.0/24, not Cloud, since 2026-07-16).

Why: unblocks the one owed dc1 apex write; the D-124 scheme "generalizes
per-leg" and the tool now encodes that instead of forking a dc1 copy.

Harness: `tests/dc-rack-mgmt-import/` extended in the same change -- all CLI
cases pass `--site`, new cases: missing `--site` dies; bogus RACK_SITE env
dies; dc1 happy path (site id binding, /22 mask, dns vvr1-dc1); cross-site
guard (dc0 rack IP under --site vr1-dc1 rejected, zero writes); dc1 band edges
.2/.49 accepted, .1 gateway rejected; missing vr1-dc1 site precondition dies.
Structural pins moved from the retired globals to the SITES map.
**117/117 PASS** (was 96).

Live dry-run vs the real apex (read-only, on office1-netbox):
`docs/audit/dc1-rack-import-dryrun-20260721.txt` -- whole preflight PASS,
plan = would-create exactly 172.31.0.4/30 (role transit, scope vr1-dc1) +
10.12.68.2/22 (dns vvr1-dc1). The `--commit` is a GATED operator step (apex
mutation), not run here.

Revert: `git checkout main -- netbox/dc-rack-mgmt-import.py tests/dc-rack-mgmt-import/`
(restores the dc0-only tool); delete the two capture files. No apex state
touched (dry-run only).
