# Changelog -- 2026-07-24 -- caveman plugin: disable on live host + governance + guardrails

Session scope: acting on a 4-seat council review of the third-party `caveman`
Claude Code plugin (installed + off-by-default-guarded 2026-07-23, commit
338c919). Operator accepted the review recommendations (exact utterance: "I'll
accept your recommendations"). Harness-config + repo-governance only -- NO cloud
mutation, NO runbook change. CURRENT-STATE deliberately untouched (adding a SEC
row is not an L10 trigger; the G14 open-SEC count reconciles at Stage 4 close --
see item 2). Council finding that drove the disable: `enabled:true` runs
third-party `node` on every session start + every prompt REGARDLESS of
`CAVEMAN_DEFAULT_MODE=off` (the env guard gates arming/terse-output, not hook
execution), and the plugin's own honest docs put net token savings at
break-even-to-negative on this host's gated-approval workload -- so a standing
per-turn execution surface on a credential-bearing jumphost was not worth
carrying.

## Item 1 -- caveman DISABLED on the live host (enabled:false, both scopes)

- `~/.claude/settings.json` (user scope, authoritative for vcloud):
  `enabledPlugins."caveman@caveman"` flipped `true` -> `false`. Stops the
  SessionStart + UserPromptSubmit hooks from wiring at the next session start.
- `/home/jessea123/openstack-caracal-dc-dc/.claude/settings.json` (committed
  repo scope): added `enabledPlugins {"caveman@caveman": false}`. Project scope
  outranks user scope (Local > Project > User), so this is a fail-closed,
  travels-with-the-clone off-switch across the operator's workstations --
  mirrors the dual placement used for the env guard.
- `CAVEMAN_DEFAULT_MODE=off` retained in both files as a residual layer; the
  `extraKnownMarketplaces.caveman` entry RETAINED (plugin stays installed) so a
  deliberate re-enable for hardened-workflow deployment testing remains cheap.
- Verified: both settings.json parse (`jq`); user + repo
  `enabledPlugins."caveman@caveman"` = `false`; no `~/.claude/.caveman-active`
  flag (not armed). repo-lint 0 fail / 1 legacy warn (unchanged).
- **Revert:** set both `enabledPlugins."caveman@caveman"` back to `true`
  (repo copy via `git revert`/edit; user copy by hand). Note: re-enabling
  re-incurs the SEC-017 re-verify duty.

## Item 2 -- SEC-017 opened (supply-chain re-verify obligation)

- Added row **SEC-017** to `docs/security-ledger.md` (next-free by grep;
  highest prior = SEC-016). Records the standing third-party per-turn
  code-execution surface, its point-in-time benign verdict at SHA `0d95a81`,
  the not-declaratively-pinnable caveat, the disable disposition, and the
  STANDING DUTY to re-verify the hooks on any re-enable or plugin update.
- Register call = OPS (no D-number): fails GA-R3's Roosevelt-delta / A1 grep
  test (no bare-metal analog of a jumphost output-style plugin); precedent
  D-129 (OPS plugin-install stays OPS). The SEC row is the durable home the
  security-ledger mandate requires ("a row at discovery, never only a note") --
  the prior changelog (338c919) is GA-R2 non-citable/archive-bound, so the
  posture had no durable home before this.
- Count: open SEC rows 12 -> 13. This is NOT an L10 status-couple trigger
  (L10 fires on docs/audit/*, design-decisions Status lines, and session-ledger
  gate rows -- not security-ledger.md), so CURRENT-STATE is untouched; the G14
  count reconciles at Stage 4 close per its existing deferral pattern.
- **Self-measured drift baseline (sha256, 2026-07-24; executing cache copy ==
  git clone at `0d95a81`, only a `.in_use` runtime marker differing):**
  - `src/hooks/caveman-activate.js`        `bc0af3ba327657630e5f6c60be619e6b5394bf03edc29b1384a15f09a1b348bf`
  - `src/hooks/caveman-mode-tracker.js`    `3e34f004d4a9e65609f8c95b47dfe5f0e3a6f2d16b88c7f8af730187ee2fb246`
  - `src/hooks/caveman-config.js`          `f47fe2e6440578eeb20408bc3131b63fb8e1ae4f6ea26dbdcca33e7901e5ee8c`
  - `src/hooks/caveman-stats.js`           `433a345a279c2d54be51864ef77c0a7b19d0f8604b9a441ce0a1eded31f113e8`
  - `src/hooks/cavecrew-model-overrides.js` `9f2601e8551653609f0b9d700c9bd75bffa43747d2db18ffe83fb73de5dc3607`
  - `.claude-plugin/plugin.json`           `b76999bd244fa59c2b8805b1c2b573458e2d065b58e5904fc8e9e545e3dac7a6`
- **Revert:** delete the SEC-017 row from `docs/security-ledger.md`
  (`git revert <sha>` for the commit).

## Item 3 -- guardrails: repo-lint L11 + fail-closed compress prohibition

Council-reviewed guardrail set. (a) a repo-lint hash-baseline of the plugin
files was REJECTED (layering violation -- the plugin lives outside the repo
under `~/.claude`, non-portable across workstations, and duplicates the
plugin's own `checksums.sha256`; drift-on-update belongs in the SEC-017
re-verify duty, not repo-lint). Built (b) and (c):

- **(b) repo-lint L11 -- caveman-compress residue detector.** `scripts/repo_lint.py`
  gains L11: FAIL on any `*.original.md` in the tree (the backup signature of a
  lossy in-place `/caveman-compress` rewrite). Excludes `.git`, `.claude`
  worktrees, `.terraform`, `__pycache__` (reuses the `all_text()` skip set);
  opt-out marker `repo-lint: allow-original-md` per the L9 convention. Docstring
  catalog + `repo-lint.sh` header updated L1-L10 -> L1-L11. Harness
  `tests/repo-lint/run-tests.sh` gains T42 (residue FAILS), T43 (opt-out
  suppresses), T44 (worktree excluded): **45/45 PASS** (was 42). It is a
  POST-HOC detector (the original is already overwritten when the backup
  exists) -- it complements, not replaces, (c).
- **(c) fail-closed compress prohibition.** Mechanism measured read-only:
  `/caveman-compress <file>` is a SKILL that mutates by running
  `python3 -m scripts <file>` from the skill dir (in-place overwrite +
  `<name>.original.md` backup). Two layers added: (1) `.claude/settings.json`
  `deny` rules `Bash(*python3 -m scripts*)` + `Bash(*caveman-compress*)` --
  verified no legit repo usage collides (the strings appear only inside
  `repo_lint.py`'s L11 text, never in an executed argv); (2) a CLAUDE.md
  "Tooling guardrails" section prohibiting the compressor against ANY
  repo-tracked prose file, scoped to the whole record corpus (not a 4-file
  hand-list, which would rot). Note the pre-existing `Skill(*)` ask rule already
  gates the skill invocation itself; the deny is the fail-closed backstop that
  does not depend on that routing.
- Net enforcement stack: plugin DISABLED (item 1) is primary; `Skill(*)` ask +
  the compress `deny` gate invocation; L11 catches residue; CLAUDE.md is the
  human-readable rationale.
- Verification: `jq` valid; all touched files ASCII/LF; repo-lint 0 fail / 1
  legacy warn; `bash scripts/run-tests-all.sh` -> **GAUNTLET: ALL GREEN
  (77 harnesses)**.
- **Revert:** `git revert <sha>` -- removes L11 (+ its 3 harness cases), the two
  `deny` rules, and the CLAUDE.md section together.
