Skip to content

Guard mode → enabled/disabled + guard.* → failsafe.* (backward-compatible)#6

Open
Tombar wants to merge 30 commits into
mainfrom
mode-namespace-rename
Open

Guard mode → enabled/disabled + guard.* → failsafe.* (backward-compatible)#6
Tombar wants to merge 30 commits into
mainfrom
mode-namespace-rename

Conversation

@Tombar

@Tombar Tombar commented Jun 9, 2026

Copy link
Copy Markdown
Member

Summary

Renames the guard-mode vocabulary to describe the guard's state instead of the agent's capability, introduces a boolean policy interface, and modernizes the Rego namespace — all backward-compatible (no existing user policy or script breaks).

  • Canonical mode read / read & writeenabled / disabled (mode files, FAILSAFE_MODE, mode get, statusline, badges). Default is enabled.
  • New policy fact input.failsafe_enabled (bool) is the primary interface; bundled policies gate on the fail-safe form not input.failsafe_enabled == false (a missing/garbled field stays protected, never fail-open). input.mode is retained with legacy read / read & write values, so existing input.mode == "read" policies keep working.
  • Rego namespace guard.*failsafe.* with dual-namespace back-compat: the engine already evaluates each module at its own declared package, so legacy package guard.user / guard.repo files still evaluate; only the advisory validator changed to accept both.
  • Rich mode set aliases: enabled/enable/on/closed/close/lock/ro/r/read/safe and disabled/disable/off/open/unlock/rw/w/write/sudo.
  • On-disk migration + fail-safe: a single Canonicalize choke point migrates legacy mode files and maps any unknown/empty value to enabled.
  • MCP tool descriptions + serverInfo.name (guardfailsafe), CLI/toggle, audit log, all docs, and the doc-validation harness updated; CHANGELOG.md added.

Supersedes #2 — this branch contains the doc-validation harness and the rename; close #2 when this merges.

Security invariants (all verified by tests)

  1. Fail-safe gate not input.failsafe_enabled == false at all 7 bundled sites; the unsafe != false form appears nowhere.
  2. A fact missing failsafe_enabled blocks a mutation (TestFailsafeGate_MissingField_Blocks).
  3. Default: "enabled" + Canonicalize unknown → enabled (no fail-open default).
  4. Legacy package guard.user + input.mode == "read" still blocks (TestLegacyGuardUserPolicy_BlocksWithInputMode).
  5. The 3 failsafe_enabled:false corpus fixtures (03/23/26) keep correct, non-inverted expectations.
  6. Full normalizeMode alias-polarity matrix test.

Test Plan

  • go test ./... — 12/12 packages pass
  • failsafe test ./test/corpus — 30/30 pass
  • make validate-docs — 34/34 (2 known local skips) against the freshly-built binary
  • Final whole-branch security review — READY, all 6 invariants pass
  • CI green on push (doc-validation + ci.yml)

🤖 Generated with Claude Code

Tombar and others added 28 commits June 16, 2026 07:04
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…s assert)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ut never used)

Found by the doc-validation harness (test/docs/iterm.bats pyflakes check).
The iTerm2 toggle registers its RPC off `connection`; `app` was dead code.
… distros

- tmux list-keys: accept C-M-t or M-C-t (older tmux reorders modifiers);
  assert the stable toggle-path + #{pane_id} first.
- luacheck: --no-color so ANSI escapes don't split the '0 errors' substring.
- bats --print-output-on-failure for diagnosability.
…ngs REPORT

REPORT.md records per-claim PASS/STATIC/LIVE-MANUAL results and the one doc bug
the harness caught and fixed (iterm.md unused async_get_app).
…dation

demo.sh: guided, narrated walkthrough (failsafe ENABLED/DISABLED framing) to
run in a WezTerm window while screen-recording.
manual-test-plan.md: 19-check live GUI checklist (WezTerm/iTerm/tmux/statusline)
with recording + KeyCastr + troubleshooting notes.

Local only — held back from PR #2 pending the read→enable/disable rename.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…lves

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fe.* namespace (dual-namespace)

- Rewrites 7 mode-gating sites in kubectl/helm/aws/terraform.rego from
  input.mode == "read" to not input.failsafe_enabled == false, so a missing
  or undefined field BLOCKS (fail-safe) rather than failing open.
- Renames all bundled package declarations guard.bundled.* → failsafe.bundled.*
  (git, failsafe, kubectl, helm, aws, terraform).
- Updates validator to accept both guard.* and failsafe.* namespaces via
  layerSuffix() helper; .failsafe.rego accepts guard.repo OR failsafe.repo.
- Adds TDD security tests in internal/embed/policies_failsafe_test.go proving
  missing failsafe_enabled → BLOCK (fail-safe property).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…fo failsafe

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… legacy guard.user back-compat case

- Add failsafe_enabled:true to 27 fact.json files (mode=read) and
  failsafe_enabled:false to 3 (mode="read & write": 03, 23, 26)
  so bundled policies' `not input.failsafe_enabled == false` gate
  correctly allows mutations in read & write mode.
- Update corpus 30 expected.json reason_contains from
  "blocked in read mode" to "blocked while failsafe is enabled"
  to match the renamed reason string in kubectl.rego.
- Rename 5 corpus dirs: lairguard-* -> failsafe-* (25-29).
- Add TestLegacyGuardUserPolicy_BlocksWithInputMode to
  internal/embed/policies_failsafe_test.go: compiles a hand-written
  package guard.user module with input.mode == "read" gate and proves
  it fires alongside the bundled failsafe.bundled.* namespace,
  guaranteeing legacy user policies keep working (dual-namespace
  back-compat proof).
All 30 corpus cases pass; go test ./... green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…bled fact (mode legacy)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tombar and others added 2 commits June 16, 2026 07:04
…an, legacy mode back-compat

Update all doc-validation bats assertions from the old read/read-&-write vocabulary to
the renamed enabled/disabled canonical values:

- helpers.bats: mode-file round-trip uses "disabled"
- crosscutting.bats: chain-order writes disabled/enabled; missing-file default is
  "enabled"; rego meta-test replaced with two assertions — (a) all deny rules use
  `not input.failsafe_enabled == false` and (b) no rule compares `input.mode ==`;
  back-compat test asserts `mode get | cut -f1` returns `enabled`/`disabled`; alias
  round-trip updated to new on/off/rw/ro/sudo/lock vocabulary
- statusline.bats: glyph/label assertions → `🔒 enabled` / `🔓 disabled`
- wezterm.bats: toggle expects `disabled` (from default enabled); badge grep updated
  to `(mode == "disabled") and " off " or " on "`; sudo-timeout checks `echo enabled`
- tmux.bats: toggle flip expects disabled/enabled; status-script test checks `🔒 on`;
  no-script toggle expects "disabled"
- iterm.bats: read_mode default assertion → "enabled"; toggle flips to "disabled"
- extract.bats: status-indicator block check → `🔒 on`
- live-gui/demo.sh: status() compares "disabled" (drops the legacy bridge comment)
- live-gui/manual-test-plan.md: all expect strings → enabled/disabled, on/off badges
- REPORT.md: per-claim table and doc-bugs section updated

Also fixes a doc bug in docs/toggle/tmux.md: sudo-timeout prose said `echo read`
(stale); corrected to `echo enabled`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Tombar Tombar force-pushed the mode-namespace-rename branch from c9caa7a to 4c001ae Compare June 16, 2026 13:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant