# Session changelog -- 2026-07-27 -- Stage-5 grounding audit

ONE changelog for this session (GA-R2/D1). Branch `dc-dc-stage5-grounding-audit`
off post-merge `main` at `71519a2`. Status claims live ONLY in
`docs/CURRENT-STATE.md`; this file records what CHANGED and how to revert it.

Operator direction: a full reconciliation and grounding from a fresh session
before Stage 5 opens -- current status, the changes required to reach the
as-built, configuration versus project goal, readiness to enter the next stage
error-free, then a review of the upcoming stages. Run autonomously while the
operator was away, with rulings QUEUED not adopted.

---

## 1. Session-scoped permission expansion (settings.local.json -- GITIGNORED)

The operator offered a broad exception; a SCOPED READ-ONLY expansion was taken
instead, on the grounds that the task is read-only by construction and the `ask`
rules ARE the enforcement of hard rule 3 (CLAUDE.md says not to work around
them). Design: the allow list enumerates READ verbs, the ask list enumerates
MUTATING verbs, and anything matching neither falls through and FAILS SAFE --
logged UNMEASURED, never assumed absent.

Added (allow): read-only shell tooling (`sed -n`, `head`, `tail`, `wc`, `sort`,
`uniq`, `comm`, `diff`, `find`, `stat`, `cut`, `nl`, `file`, `column`,
`tee docs/audit/*`); read-only `virsh` (local + quoted remote variants);
`ssh voffice1 *maas admin * read*` and `query-power-state`; the per-DC artifact
and rack checkers; `Write`/`Edit` on `docs/**`; `git commit`/`git push`;
`WebSearch` + eight upstream doc domains.
Replaced (ask): the five broad `ssh voffice1 "maas admin <noun> *` rules, which
walled READ calls because `ask` beats `allow` regardless of specificity, with 23
verb-scoped MUTATION asks. Quoted variants used throughout, per the 2026-07-26
finding that a quote breaks pattern matching.
NOT granted and not requested: jumphost `sudo`, `tofu apply|destroy|import|state
rm`, any `maas admin` write, `juju deploy|add-model|bootstrap`, `opnsense-api.sh
POST`, `scripts/* apply|install`, `*--commit*`, `rm *`. `Edit(.claude/settings*.json)`
deliberately LEFT as `ask` so the session could not widen its own permissions
unattended. Deny list and the PreToolUse guard untouched.
Deliberately NOT requested: blanket `Bash(cat *)` -- the guard's NEVER list covers
`vault-init/`, `as-executed/`, `tenant-*/`, `*-cred.txt`, `appcred`, `*.pem` but
NOT the `~/vr1-*-creds/` key files, so a blanket `cat` would open exactly the
hazard CLAUDE.md forbids.

**REVERT: delete the added allow entries and restore the five original
`ssh voffice1 "maas admin <noun> *` ask rules. The file is gitignored, so
`docs/audit/stage5-grounding-audit-scope-20260727.md` section 6 is its only
durable record. REMOVE THE BLOCK AT SESSION CLOSE.**

Flagged NOT fixed (hard rule 1): `Bash(awk *)`, `Bash(grep *)` and
`Bash(python3 *)` were ALREADY broadly allowed and can each read a `~/*-creds/`
key file without tripping the guard. Pre-existing gap, not introduced here.

**Known remaining wall for a future session: there is no `Bash(timeout *)` allow
rule.** Lens 1's `timeout 240 bash scripts/preflight.sh` fell through to the
auto-mode classifier and was denied, even though bare `bash scripts/preflight.sh`
is allowed -- the `timeout` prefix means no rule MATCHES. Same failed-to-match
class as the 2026-07-26 quoted-`sudo` finding, NOT a classifier override. Add
`Bash(timeout * bash scripts/*)` (or drop the prefix) if a future session needs it.

## 2. Deliverables (all new files)

- `docs/audit/stage5-grounding-audit-scope-20260727.md` -- the charter, committed
  BEFORE any agent was spawned so a session that died mid-run still left the plan.
- `docs/audit/stage5-readiness-20260727.md` -- the ordered precondition checklist.
- `docs/audit/stage5-committee-raw-20260727.md` -- all 7 lenses verbatim, kept
  separate from synthesis so synthesis cannot quietly reshape a lens's claim.
- `docs/audit/queued-rulings-20260727.md` -- 11 Stage-5-blocking + 4 standing
  questions, GA-R5 shape, blank utterance lines. NONE adopted.
- `docs/audit/stage5-live-measurement-20260727.txt` -- this session's measurements.
- `docs/audit/outer-plan-20260727-stage5-audit.txt`,
  `docs/audit/preflight-20260727-stage5-audit.txt`,
  `docs/audit/stage5-creds-privileged-20260727.txt` -- captures.

REVERT: `git revert` the three audit commits (`514f0aa`, `6d511e8`, `fb8f6bc`,
`de11bf4`); every file is new.

## 3. CURRENT-STATE edits (GA-R1 amendment C2 -- measurement corrects the record
in the capture's own commit; also what repo-lint L10 requires)

- Section 7 pin table: Juju row corrected to the MEASURED 3.6.27 on voffice1
  (was 3.6.25). In-channel patch refresh, which D-071 permits -- policy-compliant
  drift, not an incident, but it is the client that will bootstrap the controller.
- Section 7: NEW row recording that the `openstack` client is ABSENT on BOTH
  hosts while ten Stage-5/6/7 scripts invoke it. No prior surface carried this.
- Section 5: convergence history extended with the three-root ZERO-DIFF
  re-confirmation and the validity proof behind the inner pair.
- D-137 block: the 2026-07-27 privileged re-run recorded as a RE-CONFIRMATION of
  the 2026-07-26 run, not a newly-closed item (an overclaim corrected mid-session).
- Section 1: the audit's verdict and pointers to the five artifacts.
- **Gate row G3 CORRECTED** -- the one mechanical fix taken. See below.

REVERT: each edit is additive except G3; restore G3's prior cell text from
`git show de11bf4^:docs/CURRENT-STATE.md`.

## 4. The single mechanical fix (deliberately chosen, not opportunistic)

Gate row G3 read OPEN with "FREEZE holds for un-gated surfaces", contradicted by
its OWN cited evidence file (`docs/audit/phase6-exit-runs-20260719.md:84,91-92`
records the re-signature PASS and "G3 + G11 CLOSED") and by row G11 and section
11. No ruling was required -- two surfaces already declared it closed. The
consequential half was the FREEZE clause, not the state token: left standing it
would have blocked the very DOCFIX batch this audit queues.

REVERT: restore the prior cell text (see 3).

## 5. Logged, NOT executed (hard rule 1)

The 21-item DOCFIX remediation batch in Phase 3 of the readiness doc, and every
gate-integrity fix in Phase 4. Nearly every runbook fix interlocks with an
unanswered ruling, so landing them now would encode assumptions. No D-, DOCFIX-
or BUNDLEFIX- number was assigned; next-free is unchanged at D 138 / DOCFIX 205 /
BUNDLEFIX 053, correctly, because nothing was remediated.

## 6. Owned errors, recorded rather than quietly corrected

- The per-DC artifact checkers were first run LOCALLY on vcloud, producing a
  comprehensive FAIL with every probe MISS -- output indistinguishable from
  "never installed". Both scripts are designed to be PIPED TO THE RACK. Corrected
  by direct functional probe; lens 2 independently ran the canonical piped form
  and got PASS on all three. The scripts were not at fault.
- The first exit-code reading of those checkers captured `tail`'s status through
  a pipe, not the script's. Re-measured: both correctly exit 1.
- The measurement capture initially waved P3's "33 pins, 0 fail, 33 warn" through
  as expected. Lens 4 established the CAUSE was a missing `juju` binary, so ZERO
  pins are verified against charmhub. The count was expected; the cause was not.
  Capture amended.
- The privileged credential sweep was first described as closing an item
  outstanding since 2026-07-26. It was not -- that sweep completed on 07-26.
  Today's run RE-CONFIRMS it against the current 82-row matrix.

## 7. Committee mechanics

Seven read-only lenses, each under a contract requiring: no mutating shape; the
`claim / evidence / blocks-Stage-5 / disposition` schema; and any denied or
prompted tool call reported UNMEASURED with its exact command, never folded into
a negative finding. Lens 5 died on an API error with no output and was relaunched
with the interim findings folded in. Lens 1 hit one classifier denial
(`timeout 240 bash scripts/preflight.sh`) and correctly reported it UNMEASURED;
root cause was a missing `Bash(timeout *)` allow rule -- a failed-to-MATCH, the
same class as 2026-07-26, not a classifier override. The audit session had
measured preflight independently, so that gap was closed.

## 8. RULINGS TAKEN (operator returned; one exchange each, GA-R5)

Fourteen rulings, two withdrawals, one gate opened. Ruling AUTHORITY is each
D-number's Status block in `docs/design-decisions.md`, or `docs/CURRENT-STATE.md`
for the OPS ones. `docs/audit/queued-rulings-20260727.md` records the questions as
put and the exact utterances.

| # | Outcome | Recorded as |
|---|---|---|
| R1 | ceph-osd gets a real second disk, 4 storage nodes/DC (8 volumes, not 18) | D-121 AMENDMENT |
| R2 | Dual-stack as ruled | D-101 RULING NOTE |
| R2a | **WITHDRAWN** -- literals already assigned under D-111 | -- |
| R3 | Finish the jumbo underlay (two lagging segments) | D-101 RULING NOTE |
| R4 | DC-aware band tool; v4 + FIP now, v6 after the carve | D-134 AMENDMENT |
| R5 | Designate deploys at Stage 5, configured at Stage 7 | D-106 RULING NOTE |
| R6 | VIP gaps first, then the HA overlay whole | D-121 RULING NOTE |
| R7 | Per-DC independent Octavia PKI | D-109 AMENDMENT |
| R8 | Octavia creates and owns its own IPv6 network | D-101 RULING NOTE |
| R8a | Extend the o-hm0 verifier to compare MTUs | D-101 sub-ruling (OPS) |
| R9 | Derive lib-net's dc1 arm from the overlay + drift check | D-119 AMENDMENT |
| R10 | Fix the stale clone; all working directories current | CURRENT-STATE (OPS) |
| R11 | vault `.61`, designate `.62`, dual-family triples | D-020 AMENDMENT |
| R12 | Fold time verification into G17; make its check falsifiable | **EXECUTED** in the G17 row |
| R13 | Register-first credential fixes, no new tool | D-137 SUB-RULING 6 |
| R14 | **WITHDRAWN** -- the register already did what the question assumed it could not | -- |
| R15 | All three gate floors, harness MANIFEST not a count | CURRENT-STATE (OPS) |
| G18 | OPENED -- IPAM apex completeness, BLOCKING, deferred until the cloud is live | CURRENT-STATE gate table |

REVERT: each ruling is one commit, `420275f`..`10b1abc`; revert individually. R12
also EXECUTED a change to the G17 gate row -- reverting it restores the prior
`[V]` text.

NO NUMBER CONSUMED: next-free stays D 138 / DOCFIX 205 / BUNDLEFIX 053, because
every ruling's execution remains LOGGED NOT EXECUTED.

## 9. SESSION-CLOSE SWEEP + SKILL SWEEP

Sweep capture: `docs/audit/queued-findings-20260727-stage5-audit.txt`. It found
THREE stale surfaces the audit itself created (readiness doc's 13 stale
NEEDS-RULING markers, queued-rulings' "nothing is adopted" header, six blank
utterance lines) -- all fixed in `5dd7b2c`.

SKILL SWEEP done although NO STAGE CLOSED, on operator direction. Folded into
`.claude/skills/openstack-cloud-ops/SKILL.md`:
- **A FINDING IS AN OBSERVATION, NOT A CONCLUSION** -- measure before putting it to
  the operator; four of the first six questions changed shape, three were withdrawn
  or re-scoped.
- **A CITATION IS AN EXISTENCE CLAIM; ONLY ITS CONTENT IS EVIDENCE** -- both bugs
  behind the v4-only lb-mgmt draft failed on reading, and the conclusion inverted.
- **RULED IS NOT BUILT** -- D-134's bands and D-020's vault VIP were both ruled and
  never implemented, and passed every gate for weeks.
Into `references/script-authoring.md`: the repo-lint L5 heading trap, the
commit-gated-on-lint pattern, and the space-aligned `.tsv`.
Into `references/operating-discipline.md`: the probe PATH determines the answer.
Snapshot REGENERATED as
`.claude/skills/openstack-cloud-ops-consolidated-20260727.md` -- 1695 lines,
ASCII/LF byte-verified, all five new items present.

REVERT: `git revert` the close commit; the skill edits are additive and the
snapshot is a derived artifact regenerable from any commit.

