# Changelog -- 2026-07-27 -- repackage the openstack-cloud-ops .skill artifact

Session scope: regenerate the committed `.skill` upload artifact from the
authoritative skill dir, ahead of standing up a troubleshooting clone for the
devteam on a SEPARATE cloud (`openstack-caracal-ipv4` test env). Packaging +
changelog only -- NO cloud mutation, NO script change, NO runbook change, no
skill CONTENT edit. CURRENT-STATE deliberately untouched (no status-bearing
surface changed: no `docs/audit/` file, no **Status: line, no `| G<n> |` gate
row -- L10 not triggered).

Branch base: post-merge `main` (`6495cfb`), which was verified to carry the
2026-07-27 skill sweep -- `git merge-base --is-ancestor 39e8988 origin/main`
returned 0, and `.claude/` + `skills/` are byte-identical between `origin/main`
and `dc-dc-stage5-preconditions`. This matters because the derived artifact must
not be built from a base older than its source.

## Item 1 -- `skills/openstack-cloud-ops/openstack-cloud-ops.skill` REGENERATED

The committed upload artifact was last written 2026-07-05 (`35cfe41`) and had
gone 22 days stale against `.claude/skills/openstack-cloud-ops/`, which is the
single source of truth (changelog-20260703-process-hardening, items at :304 and
:349-350: "edit under .claude/skills/, repackage the .skill from it").

Measured staleness of the OLD artifact:

- `SKILL.md` 129 lines packaged vs **325** authoritative -- it predates the
  GA-R1..R7 record-authority section, the VR1 DC-DC routing rows, the D-128
  operating model, the D-135 per-DC artifact-strategy invariant, the
  DOCFIX-200 "Stage 4 hands off READY nodes" ruling, and the four
  audit-derived invariants (finding-vs-conclusion, citation-as-existence-claim,
  ruled-is-not-built, a-checker-that-cannot-fail).
- **Two reference files were absent entirely**: `platform-traps.md` (517 lines
  -- the per-tool trap + verbatim-error index) and `opentofu-provider-docs.md`
  (154 lines).
- `environment.md`, `operating-discipline.md`, `script-authoring.md`,
  `troubleshooting.md` all differed.
- Consequential content defect: the packaged frontmatter `description` names
  `openstack-caracal-ipv4` as "the repository this skill operates against". The
  2026-07-05 build predates BOTH the D-110 seed of this repo (2026-07-09) and
  the repo-agnostic sweep of the same week
  (`changelog-20260709-repo-agnostic-sweep`), so the literal was correct WHEN
  BUILT. It is wrong NOW, and not because of a rename: per D-110,
  `openstack-caracal-ipv4` is the separate, still-live SOURCE project (VR0 v1,
  IPv4-only) this repo was seeded FROM at `8813efc` -- with its own D-series
  ceiling at D-075 and the D-076..D-099 band reserved to it. The authoritative
  copy is repo-agnostic ("the repository this skill operates against") per the
  L9 self-ref guard. So an upload of the stale package points the operating
  skill at a DIFFERENT PROJECT's repo, which is a live confusion risk right now:
  that is the same repo backing the devteam's test env.

Build (deterministic by construction, so a rebuild from unchanged sources is
byte-identical and drift is detectable by hash):

    STAGE=<tmp>; cp -r .claude/skills/openstack-cloud-ops "$STAGE/openstack-cloud-ops"
    find "$STAGE" -exec touch -h -t 202607270000.00 {} +
    cd "$STAGE" && zip -r -X -q out.zip openstack-cloud-ops

Verification captured this session:

- Internal layout preserved against the old package: a top-level
  `openstack-cloud-ops/` prefix dir, NOT a bare `./SKILL.md` (a bare layout
  mis-mounts on claude.ai upload). `unzip -l` = 9 entries / 7 files
  (was 5 files), 104171 bytes uncompressed.
- Sources verified ASCII + LF before packing: 0 non-ASCII lines and 0 CR lines
  across all 7 files.
- **Round-trip**: `unzip` of the committed artifact `diff -r` against
  `.claude/skills/openstack-cloud-ops/` -> IDENTICAL, byte-for-byte.
- **Determinism**: independent rebuild reproduced
  sha256 `287424fd8ad014db18d9f3609ef4edd2fe434f34fb26c899f67506b68154555d`.
- EOL-normalization checked, since `.gitattributes` pins `*.zip binary` but the
  extension here is `.skill` (see Item 2): working file sha256 == staged blob
  sha256, and `git diff --cached` reports `Bin 22230 -> 50267 bytes`. Git's
  `text=auto` content detection classified it binary, so no normalization
  occurred. The artifact is correct as committed.
- `bash scripts/repo-lint.sh` -> **PASS (0 fail, 0 warn)** on this base.

- **Revert:** `git revert` this commit, or restore the prior blob directly:
  `git checkout 35cfe41 -- skills/openstack-cloud-ops/openstack-cloud-ops.skill`
  (prior sha256 is recoverable from that blob; prior size 22230 bytes). Note
  that reverting restores an artifact that mis-names the repo, per Item 1.

## Item 2 -- findings LOGGED, not executed (hard rule 1)

Both surfaced while packaging. Neither is actioned here; both are put to the
operator rather than fixed mid-step.

1. **`.gitattributes` covers `*.zip binary` but not `*.skill`.** The stanza's
   own comment states the intent -- "Binary artifacts must never be
   EOL-normalized (DOCFIX-071 ships a committed zip)" -- and this file IS a
   committed zip that the pattern misses. It is currently benign (measured
   above: git's content detection catches it), but the protection is
   incidental rather than declared. The 2026-07-05 artifact used compression
   method `store`, which packs markdown RAW -- exactly the case where a
   text-looking blob is most at risk if content detection ever went the other
   way. Minimal fix would be one line: `*.skill binary`.

2. **Nothing gates this artifact's freshness -- which is why it went 22 days
   stale, spanning three stage closes.** The stage-close ritual in `SKILL.md`
   names two derived outputs -- keep `.claude/skills/` current, then regenerate
   the dated consolidated `.md` snapshot -- but does NOT name the `.skill`.
   The snapshot was regenerated 2026-07-27 (`39e8988`); the `.skill` was not.
   This is the repo's own "a checker that cannot fail is not a gate" pattern in
   its weaker form: no checker at all. A `scripts/skill-package.sh` with its
   `tests/skill-package/run-tests.sh` harness, plus a repo-lint rule asserting
   the artifact's CONTENT matches the source dir (assert on content, never on
   existence), would make the staleness impossible rather than noticed. Scoped
   as OPS under GA-R3 -- no Roosevelt delta, fails the A1 grep test.

## Item 3 -- record correction (commit message of `9ffc904`)

The commit body of `9ffc904` (this session's packaging commit, already pushed)
describes the stale artifact's frontmatter literal as "correct when built
(pre-D-110 seed)". That phrasing reads as though `openstack-caracal-ipv4` were a
FORMER NAME of this repo. It is not, and D-110 says so explicitly:
`openstack-caracal-ipv4` is the separate, still-live SOURCE project this repo was
seeded FROM at `8813efc`; its D-series ceils at D-075 and the D-076..D-099 band
is reserved to it and NEVER assigned here.

Corrected in Item 1 above, which is the authoritative statement. The commit
message is left as pushed rather than amended: `git push --force*` is a `deny`
rule in `.claude/settings.json`, and working around a permission rule is barred
by hard rule 3. Append-only correction also matches this repo's verbatim-record
discipline -- the error and its correction both stay visible, which is the
outcome an amend would have destroyed.

Worth keeping, because the misreading is the failure mode the skill already
names: a finding is an observation, not a conclusion. "The packaged description
names a repo that is not this one" was the reliable OBSERVATION; "therefore it
is a stale name for this repo" was an unmeasured CONCLUSION, and one grep of
`docs/design-decisions.md` inverted it.
