From e7e0d0ff373341160d83a48e818d9e8096ddc552 Mon Sep 17 00:00:00 2001 From: Ulises Chavarria Date: Tue, 28 Jul 2026 13:35:37 -0700 Subject: [PATCH 1/2] feat(agentic-ai): track a shared global AGENTS.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The global ~/AGENTS.md was untracked, stale, and inert. Railguard's installer created it on 9 Jul; three days later f9dbe01 made AGENTS.md the master agent doc and 29a2675 dropped the graphify reference, but both landed on the Claude side only. It still pointed at ~/.Codex/skills/graphify/, which no longer exists, and its four @rules/common/* imports resolved against a ~/rules/ that never existed — so none of its hand-authored content ever loaded. ~/.codex/AGENTS.md was a byte-identical copy with the same defects. Add agentic-ai/AGENTS.md as the tracked cross-agent set and move rules/ up from agentic-ai/Claude/ to agentic-ai/, since none of the four rule files are Claude-specific. CLAUDE.md slims to @AGENTS.md plus Claude-only notes, mirroring the repo-root split. install.sh now symlinks AGENTS.md and rules/ into ~/.claude/, ~/.codex/, and ~/; validate.sh checks all six. The siblings are required, not belt-and-braces: Claude Code resolves @-imports against the deployed directory of the importing file, does not follow '../', and for a symlinked file resolves relative to the symlink rather than its target. All three behaviors were verified with live claude -p probes, as was the CLAUDE.md -> @AGENTS.md -> @rules/... chain. Co-Authored-By: Claude Opus 5 (1M context) --- agentic-ai/AGENTS.md | 66 +++++++++++++++++ agentic-ai/Claude/CLAUDE.md | 73 +++---------------- agentic-ai/Claude/README.md | 48 ++++++++---- agentic-ai/Claude/install.sh | 24 ++++-- agentic-ai/Claude/validate.sh | 10 ++- agentic-ai/{Claude => }/rules/bash/style.md | 0 .../{Claude => }/rules/common/agents.md | 0 .../{Claude => }/rules/common/general.md | 0 .../{Claude => }/rules/common/railguard.md | 0 docs/CHANGELOG.md | 43 +++++++++-- 10 files changed, 177 insertions(+), 87 deletions(-) create mode 100644 agentic-ai/AGENTS.md rename agentic-ai/{Claude => }/rules/bash/style.md (100%) rename agentic-ai/{Claude => }/rules/common/agents.md (100%) rename agentic-ai/{Claude => }/rules/common/general.md (100%) rename agentic-ai/{Claude => }/rules/common/railguard.md (100%) diff --git a/agentic-ai/AGENTS.md b/agentic-ai/AGENTS.md new file mode 100644 index 0000000..3929a7a --- /dev/null +++ b/agentic-ai/AGENTS.md @@ -0,0 +1,66 @@ +@rules/common/general.md +@rules/common/agents.md +@rules/common/railguard.md +@rules/bash/style.md + + +# Railguard — Active Guardrails + +Railguard is monitoring this session. Every tool call (Bash, Write, Edit, Read) passes through Railguard before execution. + +## What you need to know + +- **Some commands will be blocked.** If you see a "denied" response from a hook, it means Railguard blocked the command. Do NOT retry the same command — find a safer alternative. +- **Some commands require human approval.** If you see an "ask" response, the human will be prompted to approve or deny. +- **File writes are snapshotted.** Every Write/Edit you make is backed up before execution. The human can rollback any change. +- **Everything is logged.** All tool calls and decisions are recorded in `.railguard/traces/`. + +## Writing files + +Prefer `Write`/`Edit` over Bash redirects (`cat <`, `echo >`, `printf >`). Tool writes are snapshotted and skip the Bash path-fence scan, which matches fenced-path *strings* in command text — so a command merely mentioning a fenced path (heredoc, issue body) is blocked even if it never touches it. Switching a fence-blocked heredoc to `Write` is intended remediation, not evasion. + +## If something goes wrong + +If the human asks you to undo changes, fix a mistake, or rollback: + +1. **Get context first.** Run: `railguard context --session $SESSION_ID --verbose` + This shows you exactly what changed, diffs, blocked commands, and available rollback commands. + +2. **Check what changed.** Run: `railguard diff --session $SESSION_ID` + Or for a specific file: `railguard diff --session $SESSION_ID --file ` + +3. **Rollback options:** + - Undo the last edit: `railguard rollback --session $SESSION_ID --steps 1` + - Undo the last N edits: `railguard rollback --session $SESSION_ID --steps N` + - Restore a specific file: `railguard rollback --session $SESSION_ID --file ` + - Restore everything: `railguard rollback --session $SESSION_ID` + - Restore a specific snapshot: `railguard rollback --session $SESSION_ID --id ` + +4. **Find your session ID.** Run: `railguard log` + This lists all sessions. Pick the most recent one. + +## Configuring Railguard + +You **can** help the user customize their Railguard policy. This is encouraged: + +- **Read `railguard.yaml`** to understand the current policy. +- **Propose edits to `railguard.yaml`** — the user will be prompted to approve before any change is applied. +- **Run `railguard init`** to generate a starter `railguard.yaml` if one doesn't exist (user approves). +- **Run `railguard status`** to show the current protection state. +All changes to Railguard policy require explicit human approval. You generate the change, the user reviews and accepts or rejects it. Changes take effect on the next tool call — no restart needed. + +### Policy layers + +- **Global** `railguard.yaml` / `~/.railguard.yaml` (resolved upward from cwd) — base rules; edits gated to **ask**. +- **Per-project** `.railguard.local.yaml` (project root) — additive `fence.allowed_paths` only; cannot weaken `denied_paths` or disable the fence. Honored only if global sets `fence.allow_local_overrides: true`; the project cannot opt itself in. + +Out-of-project path keeps prompting and the human wants it for this project only → add it to `.railguard.local.yaml`, not the global policy. Override ignored = global `allow_local_overrides: true` missing. Gitignore it unless the exception is shared. Details: `docs/per-project-allowlist.md`. + +## Do NOT attempt to + +- Run `railguard uninstall` — it will be blocked. +- Modify the agent's own settings file (`~/.claude/settings.json`, `~/.codex/settings.json`) — it will be blocked. +- Remove the railguard binary — it will be blocked. +- Access `~/.ssh`, `~/.aws`, `~/.gnupg`, `/etc`, or other fenced paths (if path fencing is enabled). + + diff --git a/agentic-ai/Claude/CLAUDE.md b/agentic-ai/Claude/CLAUDE.md index 9cc4ec1..c41f6b0 100644 --- a/agentic-ai/Claude/CLAUDE.md +++ b/agentic-ai/Claude/CLAUDE.md @@ -1,66 +1,13 @@ -@rules/common/general.md -@rules/common/agents.md -@rules/common/railguard.md -@rules/bash/style.md +@AGENTS.md - -# Railguard — Active Guardrails +# Claude Code -Railguard is monitoring this session. Every tool call (Bash, Write, Edit, Read) passes through Railguard before execution. +The section above is imported from the shared global `AGENTS.md` — the +instruction set for every agent (Claude Code, Codex, opencode). Everything there +applies. Keep cross-agent guidance in `AGENTS.md`, not here; this file is only +for things that apply to Claude Code and nothing else. -## What you need to know - -- **Some commands will be blocked.** If you see a "denied" response from a hook, it means Railguard blocked the command. Do NOT retry the same command — find a safer alternative. -- **Some commands require human approval.** If you see an "ask" response, the human will be prompted to approve or deny. -- **File writes are snapshotted.** Every Write/Edit you make is backed up before execution. The human can rollback any change. -- **Everything is logged.** All tool calls and decisions are recorded in `.railguard/traces/`. - -## Writing files - -Prefer `Write`/`Edit` over Bash redirects (`cat <`, `echo >`, `printf >`). Tool writes are snapshotted and skip the Bash path-fence scan, which matches fenced-path *strings* in command text — so a command merely mentioning a fenced path (heredoc, issue body) is blocked even if it never touches it. Switching a fence-blocked heredoc to `Write` is intended remediation, not evasion. - -## If something goes wrong - -If the human asks you to undo changes, fix a mistake, or rollback: - -1. **Get context first.** Run: `railguard context --session $SESSION_ID --verbose` - This shows you exactly what changed, diffs, blocked commands, and available rollback commands. - -2. **Check what changed.** Run: `railguard diff --session $SESSION_ID` - Or for a specific file: `railguard diff --session $SESSION_ID --file ` - -3. **Rollback options:** - - Undo the last edit: `railguard rollback --session $SESSION_ID --steps 1` - - Undo the last N edits: `railguard rollback --session $SESSION_ID --steps N` - - Restore a specific file: `railguard rollback --session $SESSION_ID --file ` - - Restore everything: `railguard rollback --session $SESSION_ID` - - Restore a specific snapshot: `railguard rollback --session $SESSION_ID --id ` - -4. **Find your session ID.** Run: `railguard log` - This lists all sessions. Pick the most recent one. - -## Configuring Railguard - -You **can** help the user customize their Railguard policy. This is encouraged: - -- **Read `railguard.yaml`** to understand the current policy. -- **Propose edits to `railguard.yaml`** — the user will be prompted to approve before any change is applied. -- **Run `railguard init`** to generate a starter `railguard.yaml` if one doesn't exist (user approves). -- **Run `railguard status`** to show the current protection state. -All changes to Railguard policy require explicit human approval. You generate the change, the user reviews and accepts or rejects it. Changes take effect on the next tool call — no restart needed. - -### Policy layers - -- **Global** `railguard.yaml` / `~/.railguard.yaml` (resolved upward from cwd) — base rules; edits gated to **ask**. -- **Per-project** `.railguard.local.yaml` (project root) — additive `fence.allowed_paths` only; cannot weaken `denied_paths` or disable the fence. Honored only if global sets `fence.allow_local_overrides: true`; the project cannot opt itself in. - -Out-of-project path keeps prompting and the human wants it for this project only → add it to `.railguard.local.yaml`, not the global policy. Override ignored = global `allow_local_overrides: true` missing. Gitignore it unless the exception is shared. Details: `docs/per-project-allowlist.md`. - -## Do NOT attempt to - -- Run `railguard uninstall` — it will be blocked. -- Modify `~/.claude/settings.json` — it will be blocked. -- Remove the railguard binary — it will be blocked. -- Access `~/.ssh`, `~/.aws`, `~/.gnupg`, `/etc`, or other fenced paths (if path fencing is enabled). - - +`@AGENTS.md` is a sibling import on purpose: Claude Code resolves `@` paths +relative to the *deployed* location of the importing file and will not follow +`../`, so `install.sh` symlinks both `AGENTS.md` and `rules/` into `~/.claude/` +next to this file. diff --git a/agentic-ai/Claude/README.md b/agentic-ai/Claude/README.md index e2c31d4..05cce3c 100644 --- a/agentic-ai/Claude/README.md +++ b/agentic-ai/Claude/README.md @@ -15,7 +15,8 @@ This will: - Back up your existing `~/.claude/settings.json` when it differs from the template - Copy this `settings.json` to `~/.claude/settings.json` - Symlink `~/.claude/CLAUDE.md` → this `CLAUDE.md` -- Symlink `~/.claude/rules/` → this `rules/` +- Symlink `AGENTS.md` + `rules/` → `../AGENTS.md` and `../rules/` in + `~/.claude/`, `~/.codex/`, and `~/` (the shared cross-agent set) - Symlink `~/.claude/docs/` → this `docs/` - Symlink this `railguard.yaml` → `~/.railguard.yaml` - Symlink each `hooks/*.sh` script into `~/.claude/hooks/` @@ -87,22 +88,43 @@ Hooks use **exit 2** to block — Claude receives the stderr message as the reas ## Rules (Tip 6 hierarchical structure) -`CLAUDE.md` @-imports from `rules/` to keep principles modular: +`CLAUDE.md` imports `@AGENTS.md`, which imports from `rules/` — both live one +level up in `agentic-ai/`, shared with Codex and opencode: ``` -rules/ - common/ - general.md — language-agnostic coding principles - agents.md — when to self-invoke Plan / Explore / review / verify - railguard.md — slim always-loaded Railguard behavior and reference routing - bash/ - style.md — bash scripting conventions -docs/ - RAILGUARD.md — expected behavior and bug-reporting protocol - per-project-allowlist.md — local path-allowlist configuration +agentic-ai/ + AGENTS.md — shared cross-agent instruction set (imports the rules below) + rules/ + common/ + general.md — language-agnostic coding principles + agents.md — when to self-invoke Plan / Explore / review / verify + railguard.md — slim always-loaded Railguard behavior and reference routing + bash/ + style.md — bash scripting conventions + Claude/ + CLAUDE.md — imports @AGENTS.md, then Claude-Code-only notes + docs/ + RAILGUARD.md — expected behavior and bug-reporting protocol + per-project-allowlist.md — local path-allowlist configuration ``` -Add a new language by creating `rules//style.md` and adding an `@` line to `CLAUDE.md`. +Add a new language by creating `rules//style.md` and adding an `@` line to +`AGENTS.md` (cross-agent) or `CLAUDE.md` (Claude-only). + +### Why AGENTS.md and rules/ are symlinked into three places + +Claude Code resolves `@` imports against the **deployed** directory of the +importing file, and does **not** follow `../`. Verified behavior: + +| Import form | Resolves? | +|---|---| +| `@rules/common/general.md` (at or below the file's dir) | yes | +| `@../AGENTS.md` (parent) | **no** | +| through a symlinked file | relative to the **symlink's** dir, not its target | + +So every location holding an instruction file needs `AGENTS.md` and `rules/` +beside it. This is why the pre-existing untracked `~/AGENTS.md` was inert: its +`@rules/common/*` lines pointed at a `~/rules/` that never existed. ## Testing the hooks diff --git a/agentic-ai/Claude/install.sh b/agentic-ai/Claude/install.sh index f857b70..b86cdc9 100755 --- a/agentic-ai/Claude/install.sh +++ b/agentic-ai/Claude/install.sh @@ -6,6 +6,7 @@ set -euo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENTIC_DIR="$(cd "$REPO_DIR/.." && pwd)" CLAUDE_DIR="$HOME/.claude" HOOKS_DIR="$CLAUDE_DIR/hooks" SETTINGS="$CLAUDE_DIR/settings.json" @@ -37,11 +38,24 @@ printf 'Copied: settings.json\n' ln -sf "$REPO_DIR/CLAUDE.md" "$CLAUDE_DIR/CLAUDE.md" printf 'Linked: CLAUDE.md\n' -# Symlink rules directory (used by @imports in CLAUDE.md) -# rm -f first: ln -sf on an existing dir-symlink creates a nested link inside it -rm -f "$CLAUDE_DIR/rules" -ln -sf "$REPO_DIR/rules" "$CLAUDE_DIR/rules" -printf 'Linked: rules/\n' +# Deploy the shared cross-agent AGENTS.md + rules/ to every global location that +# reads one. Claude Code resolves @-imports against the *deployed* directory of +# the importing file and will not follow "../", so AGENTS.md and rules/ must sit +# as siblings of each instruction file — ~/AGENTS.md alone would leave its +# @rules/... imports pointing at a nonexistent ~/rules. +for agents_dir in "$CLAUDE_DIR" "$HOME/.codex" "$HOME"; do + mkdir -p "$agents_dir" + agents_dst="$agents_dir/AGENTS.md" + if [[ -e "$agents_dst" && ! -L "$agents_dst" ]]; then + backup="$agents_dst.bak.$(date +%Y%m%d%H%M%S)" + printf 'Backing up existing %s → %s\n' "$agents_dst" "$backup" + mv "$agents_dst" "$backup" + fi + ln -sf "$AGENTIC_DIR/AGENTS.md" "$agents_dst" + rm -f "$agents_dir/rules" + ln -sf "$AGENTIC_DIR/rules" "$agents_dir/rules" + printf 'Linked: AGENTS.md + rules/ → %s\n' "$agents_dir" +done # Symlink docs directory (on-demand references pointed at by rules, e.g. # ~/.claude/docs/RAILGUARD.md — not @imported, read only when needed) diff --git a/agentic-ai/Claude/validate.sh b/agentic-ai/Claude/validate.sh index 1714162..e136133 100755 --- a/agentic-ai/Claude/validate.sh +++ b/agentic-ai/Claude/validate.sh @@ -6,6 +6,7 @@ set -uo pipefail REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +AGENTIC_DIR="$(cd "$REPO_DIR/.." && pwd)" CLAUDE_DIR="$HOME/.claude" HOOKS_DIR="$CLAUDE_DIR/hooks" ERRORS=0 @@ -41,10 +42,17 @@ check_regular_file() { section "Installed files" check_regular_file "$CLAUDE_DIR/settings.json" check_symlink "$CLAUDE_DIR/CLAUDE.md" "$REPO_DIR/CLAUDE.md" -check_symlink "$CLAUDE_DIR/rules" "$REPO_DIR/rules" check_symlink "$CLAUDE_DIR/docs" "$REPO_DIR/docs" check_symlink "$HOME/.railguard.yaml" "$REPO_DIR/railguard.yaml" +# AGENTS.md and rules/ must be siblings at every location that reads an +# instruction file: @-imports resolve against the deployed directory and don't +# follow "..", so a missing sibling silently loads nothing. +for agents_dir in "$CLAUDE_DIR" "$HOME/.codex" "$HOME"; do + check_symlink "$agents_dir/AGENTS.md" "$AGENTIC_DIR/AGENTS.md" + check_symlink "$agents_dir/rules" "$AGENTIC_DIR/rules" +done + # ── Hooks ───────────────────────────────────────────────────────────────────── section "Hooks" for hook in "$REPO_DIR/hooks/"*.sh; do diff --git a/agentic-ai/Claude/rules/bash/style.md b/agentic-ai/rules/bash/style.md similarity index 100% rename from agentic-ai/Claude/rules/bash/style.md rename to agentic-ai/rules/bash/style.md diff --git a/agentic-ai/Claude/rules/common/agents.md b/agentic-ai/rules/common/agents.md similarity index 100% rename from agentic-ai/Claude/rules/common/agents.md rename to agentic-ai/rules/common/agents.md diff --git a/agentic-ai/Claude/rules/common/general.md b/agentic-ai/rules/common/general.md similarity index 100% rename from agentic-ai/Claude/rules/common/general.md rename to agentic-ai/rules/common/general.md diff --git a/agentic-ai/Claude/rules/common/railguard.md b/agentic-ai/rules/common/railguard.md similarity index 100% rename from agentic-ai/Claude/rules/common/railguard.md rename to agentic-ai/rules/common/railguard.md diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 6406226..ce5a33c 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -6,16 +6,44 @@ Format loosely follows [Keep a Changelog](https://keepachangelog.com). Remaining work lives in [TODO.md](TODO.md); the design rationale for the unified layout is in [UNIFICATION.md](UNIFICATION.md). -## Unreleased — config-only deploys +## Unreleased — shared global AGENTS.md + +### Added +- `agentic-ai/AGENTS.md`: the tracked cross-agent global instruction set, plus + `agentic-ai/rules/` (moved up from `agentic-ai/Claude/rules/`). `install.sh` + symlinks both into `~/.claude/`, `~/.codex/`, and `~/`. + +### Changed +- `agentic-ai/Claude/CLAUDE.md` slimmed to `@AGENTS.md` + Claude-only notes, + mirroring the repo-root `AGENTS.md`/`CLAUDE.md` split adopted in `f9dbe01`. + +### Fixed +- The global `~/AGENTS.md` was untracked, stale, and **inert**: created 9 Jul by + railguard's installer, it missed both 12 Jul changes (`f9dbe01` AGENTS.md-as- + master, `29a2675` graphify removal), still pointed at a `~/.Codex/skills/ + graphify/` that no longer exists, and its four `@rules/common/*` imports + resolved against a `~/rules/` that never existed — so none of them ever loaded. + `~/.codex/AGENTS.md` was a byte-identical copy with the same defects. + +### Notes +Claude Code `@`-import semantics, verified empirically rather than assumed: +imports resolve against the **deployed** directory of the importing file, do +**not** follow `../`, and for a symlinked file resolve relative to the symlink +rather than its target. Hence `AGENTS.md` + `rules/` are symlinked as siblings at +every location that holds an instruction file, and `validate.sh` now checks all +six links. + +## 2026-07-28 — Config-only deploys ([#65](https://github.com/ulises-c/Computer-Setup/pull/65)) ### Added - `setup.sh --dotfiles`: deploys only the shared dotfiles set (`~/.zshrc`, `~/.tmux.conf`, `~/.zsh_plugins.txt`, `~/.p10k.zsh`) and installs no packages. Short-circuits before tag validation and the interactive prompt, so selection flags don't apply. Combine with `--dry-run` to preview. -- `fixterm` alias in `dotfiles/zshrc.example` — resets stuck mouse (1000/1002/ - 1003/1006/1015) and focus (1004) reporting after an app dies without cleanup, - a common sleep/wake symptom. Cheaper than `reset`, which also clears scrollback. +- `fixterm` alias in `dotfiles/zshrc.example` — resets stuck mouse reporting + (modes 9/1000/1001/1002/1003/1005/1006/1015/1016) and focus reporting (1004) + after an app dies without cleanup, a common sleep/wake symptom. Cheaper than + `reset`, which also clears scrollback. ### Changed - The four dotfile deploys, previously duplicated between `platforms/macos.sh` @@ -28,9 +56,14 @@ in [UNIFICATION.md](UNIFICATION.md). - `driftcheck.sh` Stop hook aborted with `ignore_patterns[@]: unbound variable` on every session end when neither ignore file existed — macOS bash 3.2 treats `"${arr[@]}"` on an empty array as unset under `set -u`. It exited 1 (generic - hook error) instead of the intended 2, masking real violations. + hook error) instead of the intended 2, masking real violations. Two further + exit-2 gaps found during review are tracked in + [#67](https://github.com/ulises-c/Computer-Setup/issues/67). - `benchmarking/lib/common.sh` was mode 644 with a shebang while its siblings `lib/core.sh` and `lib/verify.sh` are 755 — the violation the crash was hiding. +- `CONFIG_SRC_DIR` is now assigned unconditionally on the `--dotfiles` path + (`core_resolve_config_src_dir`), so an inherited environment value can't decide + which `zshrc.example` wins. - Stale comment in `lib/core.sh` claiming `linux-server` ships a `zshrc.example` override; no platform does. From 8d49e4c147fc1e27671c528362302652abba3b02 Mon Sep 17 00:00:00 2001 From: Ulises Chavarria Date: Tue, 28 Jul 2026 14:36:42 -0700 Subject: [PATCH 2/2] fix(agentic-ai): let railguard own its block in CLAUDE.md only MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Running install.sh confirmed the caveat flagged on #66: 'railguard install' re-injects its guardrail block into ~/.claude/CLAUDE.md, which is a symlink to the tracked file. With AGENTS.md also carrying a copy, and CLAUDE.md importing AGENTS.md, Claude loaded the block twice. The install also showed the copy here was stale: railguard v0.5.1 writes a slim five-line block, while the one hand-carried into AGENTS.md was the old long-form version that railguard does not maintain and would never refresh. Drop it from AGENTS.md and let railguard own it per-tool in CLAUDE.md. Cross-agent railguard guidance already lives in rules/common/railguard.md. The explanatory note avoids reproducing the start/end marker strings — an unpaired marker in a file railguard scans could confuse its injector. Verified after deploying: validate.sh passes all checks, AGENTS.md holds zero markers, CLAUDE.md holds exactly one pair, and AGENTS.md + rules/ resolve from all three deployed locations. Co-Authored-By: Claude Opus 5 (1M context) --- agentic-ai/AGENTS.md | 76 ++++++++----------------------------- agentic-ai/Claude/CLAUDE.md | 9 +++++ 2 files changed, 24 insertions(+), 61 deletions(-) diff --git a/agentic-ai/AGENTS.md b/agentic-ai/AGENTS.md index 3929a7a..483d2a0 100644 --- a/agentic-ai/AGENTS.md +++ b/agentic-ai/AGENTS.md @@ -3,64 +3,18 @@ @rules/common/railguard.md @rules/bash/style.md - -# Railguard — Active Guardrails - -Railguard is monitoring this session. Every tool call (Bash, Write, Edit, Read) passes through Railguard before execution. - -## What you need to know - -- **Some commands will be blocked.** If you see a "denied" response from a hook, it means Railguard blocked the command. Do NOT retry the same command — find a safer alternative. -- **Some commands require human approval.** If you see an "ask" response, the human will be prompted to approve or deny. -- **File writes are snapshotted.** Every Write/Edit you make is backed up before execution. The human can rollback any change. -- **Everything is logged.** All tool calls and decisions are recorded in `.railguard/traces/`. - -## Writing files - -Prefer `Write`/`Edit` over Bash redirects (`cat <`, `echo >`, `printf >`). Tool writes are snapshotted and skip the Bash path-fence scan, which matches fenced-path *strings* in command text — so a command merely mentioning a fenced path (heredoc, issue body) is blocked even if it never touches it. Switching a fence-blocked heredoc to `Write` is intended remediation, not evasion. - -## If something goes wrong - -If the human asks you to undo changes, fix a mistake, or rollback: - -1. **Get context first.** Run: `railguard context --session $SESSION_ID --verbose` - This shows you exactly what changed, diffs, blocked commands, and available rollback commands. - -2. **Check what changed.** Run: `railguard diff --session $SESSION_ID` - Or for a specific file: `railguard diff --session $SESSION_ID --file ` - -3. **Rollback options:** - - Undo the last edit: `railguard rollback --session $SESSION_ID --steps 1` - - Undo the last N edits: `railguard rollback --session $SESSION_ID --steps N` - - Restore a specific file: `railguard rollback --session $SESSION_ID --file ` - - Restore everything: `railguard rollback --session $SESSION_ID` - - Restore a specific snapshot: `railguard rollback --session $SESSION_ID --id ` - -4. **Find your session ID.** Run: `railguard log` - This lists all sessions. Pick the most recent one. - -## Configuring Railguard - -You **can** help the user customize their Railguard policy. This is encouraged: - -- **Read `railguard.yaml`** to understand the current policy. -- **Propose edits to `railguard.yaml`** — the user will be prompted to approve before any change is applied. -- **Run `railguard init`** to generate a starter `railguard.yaml` if one doesn't exist (user approves). -- **Run `railguard status`** to show the current protection state. -All changes to Railguard policy require explicit human approval. You generate the change, the user reviews and accepts or rejects it. Changes take effect on the next tool call — no restart needed. - -### Policy layers - -- **Global** `railguard.yaml` / `~/.railguard.yaml` (resolved upward from cwd) — base rules; edits gated to **ask**. -- **Per-project** `.railguard.local.yaml` (project root) — additive `fence.allowed_paths` only; cannot weaken `denied_paths` or disable the fence. Honored only if global sets `fence.allow_local_overrides: true`; the project cannot opt itself in. - -Out-of-project path keeps prompting and the human wants it for this project only → add it to `.railguard.local.yaml`, not the global policy. Override ignored = global `allow_local_overrides: true` missing. Gitignore it unless the exception is shared. Details: `docs/per-project-allowlist.md`. - -## Do NOT attempt to - -- Run `railguard uninstall` — it will be blocked. -- Modify the agent's own settings file (`~/.claude/settings.json`, `~/.codex/settings.json`) — it will be blocked. -- Remove the railguard binary — it will be blocked. -- Access `~/.ssh`, `~/.aws`, `~/.gnupg`, `/etc`, or other fenced paths (if path fencing is enabled). - - +# Agents + +Shared global instruction set for every coding agent (Claude Code, Codex, +opencode). `install.sh` symlinks this file and `rules/` into `~/.claude/`, +`~/.codex/`, and `~/` — imports resolve against the deployed directory, so both +must sit beside each instruction file. + +Railguard's auto-injected guardrail block is deliberately not kept here (no +railguard start/end HTML comment markers in this file — an unpaired one would +confuse its injector). `railguard install` writes and maintains that block +per-tool, currently in `Claude/CLAUDE.md`. Duplicating it here would both +double-load for Claude, which imports this file, and go stale: the copy that +used to live here was the old long-form block, while railguard now writes a much +slimmer one. Cross-agent railguard guidance lives in +`rules/common/railguard.md`. diff --git a/agentic-ai/Claude/CLAUDE.md b/agentic-ai/Claude/CLAUDE.md index c41f6b0..1b70d78 100644 --- a/agentic-ai/Claude/CLAUDE.md +++ b/agentic-ai/Claude/CLAUDE.md @@ -11,3 +11,12 @@ for things that apply to Claude Code and nothing else. relative to the *deployed* location of the importing file and will not follow `../`, so `install.sh` symlinks both `AGENTS.md` and `rules/` into `~/.claude/` next to this file. + + +# Railguard - Active Guardrails + +Railguard monitors every tool call in this session: allow, ask, or block. If a command is blocked, do NOT re-issue it with cosmetic changes (new flags, encoding, wrappers) - take a genuinely different approach. On ask, wait for the human. File writes are snapshotted and can be rolled back. + +Full agent guide (rollback commands, policy customization, path-fence quirks, self-protection): run `railguard guide`. + +