---
name: operational-safety
description: Rules for safe file operations and project-scoped temporary directories
version: "1.0"
created: 2026-02-27
---

# Operational Safety Rules

## Rule 1: Use Native Tools for File Operations

All file read, write, search, and edit operations MUST use the native tools (`Read`, `Write`, `Edit`, `Glob`, `Grep`), never shell commands (`cat`, `echo`, `sed`, `awk`, `find`). If a native tool exists for the operation, use it unconditionally — agents must not fall back to Bash for convenience. See `devforgeai/specs/context/anti-patterns.md` Category 1 and `.claude/rules/core/critical-rules.md` Rule 2.

---

## Rule 2: Project-Scoped Temporary Files

All temporary files MUST be written under the project root using the pattern `tmp/{story-id}/` (relative path, resolved from the project root — verify `CLAUDE.md`/`package.json` exists at CWD first). Rationale: system `/tmp/` is not reliably shared across WSL/Windows/Linux; story-id naming keeps artifacts traceable to the workflow that produced them.

**Enforcement:** `pre-tmp-path-block.sh` (PreToolUse[Bash], `exit 2`) blocks Bash commands that redirect-to/`mkdir`/`touch`/`tee` system `/tmp/`. Read-only inspections (`cat`/`grep`/`ls`/etc.) and `cp`/`mv` (where `/tmp/` may be the source) are explicitly allowed; see ADR-093 for the documented regex limitations. No bypass.

---

## Rule 3: Sanctioned Operational Edits

Two enforcement mechanisms exist for the dual-path operational directories
(`.claude/skills/`, `.claude/agents/`, `.claude/commands/`, `.claude/rules/`):

- **`post-edit-write-check.sh`** (PostToolUse[Edit|Write]) — advisory WARNING after every operational write. Always active.
- **`pre-dual-path-write-guard.sh`** (PreToolUse[Edit|Write]) — preventative BLOCK on operational paths unless a bypass is satisfied (Pattern A marker or Pattern B env var). Defined by ADR-076; bypass mechanism by ADR-081. Always-on as of 2026-05-23 — the original `DEVFORGEAI_DUAL_PATH_GUARD_ENFORCE` env-var rollout gate was removed after empirical evidence that Claude Code's settings.json `env` block does not propagate into hook subshells, making the gate a silent no-op for the ADR-081 Step C window (PR #97 → fix).

A remediation cycle that must edit an operational file directly may scope a bypass to a **single tool invocation** using ONE of the patterns below:

### Pattern A — Marker file (ADR-081, recommended for Edit/Write tool calls)

Write an empty marker file at `<repo-root>/tmp/<marker-name>.marker` immediately before the Edit/Write to the operational path. The marker is consumed on bypass (deleted by the hook), must be **fresh (< 30 seconds old)**, and authorizes exactly one operational Edit/Write. As of 2026-05-23 (post-#111 fix), `<repo-root>` is resolved by walking up from the editing file's path looking for a `.git` entry or `CLAUDE.md` sentinel — so in worktree sessions the marker goes in the **worktree's** `tmp/`, not the main checkout's.

Sanctioned marker names:

| Marker file | Bypasses | Defined by |
|---|---|---|
| `tmp/_operational-edit-bypass.marker` | `pre-dual-path-write-guard.sh` (`.claude/skills/`, `.claude/agents/`, `.claude/commands/`, `.claude/rules/`) | ADR-081 |
| `tmp/_adr-status-update.marker` | `pre-adr-append-only-guard.sh` (`devforgeai/specs/adrs/ADR-NNN-*.md`) | ADR-096 |
| `tmp/_pr-scope-bypass.marker` | `pre-commit-pr-scope-guard.sh` (PR-scope manifest divergence on `git commit`) | ADR-108 |
| `tmp/_phase-gate-bypass.marker` | `phase-gate-write.sh` + `phase-gate-commit.sh` (over-broad STORY scope false-blocking unrelated non-STORY sessions, gh#179) | ADR-109 |

Each marker bypasses ONE hook; markers do NOT cross-authorize — with one exception: `tmp/_phase-gate-bypass.marker` bypasses BOTH gates of the phase-gate family (`phase-gate-write.sh` + `phase-gate-commit.sh`), which encode the single decision "this operation is not part of the resolved dev workflow" (ADR-109).

```
# Step 1: Write the marker (any tool that can create an empty file)
Write(file_path="tmp/_operational-edit-bypass.marker", content="")

# Step 2: Edit/Write the operational file (within 30 seconds of Step 1)
Edit(file_path=".claude/skills/<name>/SKILL.md", ...)
```

This is the only mechanism that reliably bypasses the PreToolUse hook for Edit/Write tool calls, because the env-var-prefix Pattern B below does not propagate into Claude Code's hook process for native Edit/Write tool calls (only for Bash subshell calls).

### Pattern B — Env-var prefix (ADR-076 legacy, Bash invocations only)

For Bash tool calls (e.g., `cp`, `mv`, `sed -i` on operational paths):

```bash
env DEVFORGEAI_ALLOW_OPERATIONAL_EDIT=1 <your-bash-command>
```

A session-wide `export` of that variable is **FORBIDDEN** — it suppresses the warning for every edit, including ordinary `src/` dev work, and opens a silent `src/`↔`.claude/` drift path.

### Audit trail

Active bypasses (both Pattern A and Pattern B) are logged to `devforgeai/logs/post-edit-write.log` with a `BYPASS-MARKER` or `BYPASS-ENV` tag.

**HALT Trigger:** if about to `export DEVFORGEAI_ALLOW_OPERATIONAL_EDIT=1` (Pattern B), set it for longer than one invocation, OR leave a stale marker file in `tmp/` (Pattern A), HALT and AskUserQuestion.

---

## Rule 4: Sandbox-Restricted Git Operations

**Full specification:** RCA-067 REC-5 / Issue-13

The Claude Code sandbox `denyWithinAllow` write list excludes `.claude/agents/`, `.claude/skills/`, `.claude/commands/`, and `.claude/rules/`. Git operations that touch these paths can fail partially — some files update while sandbox-protected files silently fail with "Read-only file system." The exact protected-path set is runtime-determined and may change across Claude Code versions; the 4 directories above are the currently known set.

| Operation | Sandbox Safe? | Notes |
|-----------|--------------|-------|
| `git status` / `log` / `diff` | Safe | Read-only |
| `git add` / `commit` / `push` | Safe | Writes only to `.git/` (or network) |
| `git rebase` | **NOT safe** | Touches working tree incl. protected paths |
| `git restore` | **NOT safe** | Modifies working tree files |
| `git checkout <branch>` / `-b` | **NOT safe** | Switches/creates branch, touches protected paths |
| `git reset --hard` | **NOT safe** | Overwrites working tree files |
| `git pull --rebase` | **NOT safe** | Fetch + rebase, touches protected paths |
| `git stash` | Conditional | `git stash list` safe; `pop`/`apply` may touch protected paths |

**Independent constraint:** several sandbox-unsafe operations (rebase, reset --hard, stash, checkout -b) also require user approval per `.claude/rules/core/git-operations.md` — the two constraints are independent. `DEVFORGEAI_ALLOW_OPERATIONAL_EDIT=1` does NOT bypass the sandbox (it only suppresses the prompt-level warning; the sandbox is OS-level enforcement).

The `sandbox-state-verify.sh` SessionStart hook detects partial state at boot and emits recovery commands. Per-directory detection: `git diff --name-only HEAD -- .claude/agents/ .claude/skills/ .claude/commands/ .claude/rules/`.

**HALT Trigger:** when any operation fails with "Read-only file system" on a protected `.claude/` path, HALT and AskUserQuestion — see CLAUDE.md HALT trigger #11 for the template. Recovery: run the `cp` commands emitted by `sandbox-state-verify.sh` from a non-sandboxed terminal (not Claude Code).
