Skip to content
Back to skills

Fr Isolation

BSecurity

Run work in an isolated workspace: git worktree + devcontainer, driven through the `fr isolation` CLI (exec-bridge). Use when feature work must not touch the base repo, when fr-brainstorming or fr-goal needs a workspace, when the operator says "isolate this" / "run it in the container", or to clean up after a merged PR. devcontainer mode needs a profile; host/external modes are docker-less.

  • 2 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added October 19, 2026
ai-agentsgoshellbashdockergit

Works with

  • claude code
  • cursor
  • cli

Security analysis

B75/100
  • criticalPipes output to a shell interpreter

Pro scans all 2 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add derio-net/super-fr --skill fr-isolation --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Fr Isolation?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Fr Isolation
[![Security: B — Skills Directory](https://www.skillsdirectory.com/api/skills/derio-net-fr-isolation/badge)](https://www.skillsdirectory.com/skills/derio-net-fr-isolation)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: fr-isolation
description: >
  Run work in an isolated workspace: git worktree + devcontainer, driven through
  the `fr isolation` CLI (exec-bridge). Use when feature work must not touch the
  base repo, when fr-brainstorming or fr-goal needs a workspace, when the operator
  says "isolate this" / "run it in the container", or to clean up after a merged
  PR. devcontainer mode needs a profile; host/external modes are docker-less.
---

# fr-isolation

A workspace contract, not just a worktree: a git worktree OUTSIDE the repo (`~/.cache/fr/worktrees/<main-checkout>/<branch>`),
commands in the profile's devcontainer, base repo untouched while the run is live. Plain shell, any agent or human.

**Announce at start:** "I'm using fr-isolation to run this work isolated."

## Hard requirements

- Inside a git repo. **devcontainer mode** (default) needs ≥1 profile (`.devcontainer/<profile>/devcontainer.json`); missing →
  exit 2 pointing at fr-init. NEVER proceed unisolated; offer the fr-init interview (pause, resume).
- **host-worktree** (`FR_ISOLATION_TARGET=worktree`, docker-less; the default in a Claude Code cloud session): host env as-is, NO profile/secrets; restart/stats refuse, stop/rebuild no-op.
- **external** mode (valid preparer-written `.fr-isolation` marker, `mode:external`): fr adopts the container's checkout — `up --branch`
  ensures the branch in place; restart/stop/rebuild/stats refuse, gc reports (the container's owner runs them).
- Any other `FR_ISOLATION_TARGET` value fails closed (`devcontainer|worktree`). **It selects the mode at `up` only:** every later command
  follows the workspace's recorded mode, so agents, hooks and fresh shells need no env; only `up`, `gc`'s sweep and a reaped `verify-merge` read it.

## Lifecycle

```bash
fr isolation up --branch <b> [--profile <name>] [--session <id>] [--print-path]  # worktree + container; --print-path: last stdout line = path
fr isolation exec --branch <b> -- CMD ...                                         # every build/test/run; resumes a stopped container
fr isolation status [--branch ...] [--session <id>] [--format json] [--stats] [--push-check]  # state + bound sessions
fr isolation attach|detach --session <id> [--repo <path>] [--branch ...]         # bind/unbind a harness session
fr isolation restart [--branch ...] [--force]                                     # bounce a wedged container, worktree kept
fr isolation stop [--branch ...]                                                  # free a container's resources; everything else kept
fr isolation rebuild [--branch ...] [--no-cache]                                  # recreate the container from the (changed) profile
fr isolation down --branch <b> | --worktree <path> | --all [--dry-run] [--yes] [--force] [--no-preserve]  # teardown; --all lists blast radius first
fr isolation gc [--repo <path>] [--dry-run] [--format json]                       # reconcile fr-owned workspaces, ALL three modes
```

- `up` (devcontainer mode) resolves the profile (flag → repo default from `.devcontainer/fr-profiles.yaml` → sole profile), creates the
  worktree under the MAIN checkout's name (even from inside another worktree), ensures the host secrets env-file, starts the container
  with the base repo's `.git` mounted at the same absolute path. Re-running `up` on a live workspace keeps its record and sessions.

| Situation (the container is disposable, the worktree is the work) | Verb | Worktree, uncommitted files, run record | In-container installs |
|---|---|---|---|
| wedged container | `restart` | kept | kept |
| pause the work, free RAM/CPU | `stop`; the next `exec` resumes it (stderr notice) | kept | kept |
| profile changed (feature, image, env) | `rebuild` (`--no-cache` for a clean image) | kept | lost (container recreated) |
| different profile, or the work is done | `down` (+ `up --profile <p>`); mid-work that needs `--force`, so only the operator can ask for it | worktree removed; uncommitted files lost except fr's records under `docs/superpowers/` (preserved) | lost |

- **Branch base (#322, #438):** a local `<B>` is reused (behind/diverged from `origin/<B>` → `WARNING`, never rebased); else an existing
  `origin/<B>` is fetched and checked out; else a NEW branch is cut from freshly-fetched `origin/<default>`, never the base repo's HEAD. `--base
  <ref>` = verbatim, no fetch (`--base HEAD` forks the checkout); `--no-fetch` = LOCAL refs. No remote → local HEAD + `WARNING`; origin unreachable
  → the last-fetched `origin/<B>` or a cold start, each with a `WARNING`; `origin/<B>` present but unfetchable with no local ref, or `--base` beside
  an existing `origin/<B>` → refused (exit 2).

## Exec-bridge discipline

- EVERY build/test/lint/run command goes through `fr isolation exec -- ...`; file
  edits happen in the worktree directly (host-visible), execution in-container.
- Credential boundary (devcontainer mode): the container sees only the profile's
  env-file (`~/.config/fr/secrets/<repo>/<profile>.env`), NO SSH identity (#377).
  ALL git-host I/O (push/fetch, PR/MR creation, `gh`/`glab`/`tea` reads in
  `status`/`down`/`gc`) runs on the HOST outside `exec`; `status --push-check` previews.
- Pre-push guard (#320): a push to a branch whose PR is `MERGED`/`CLOSED` is
  denied by `fr-merged-pr-push-guard.sh` — cherry-pick onto `main` or a fresh PR.
- The harness resets cwd to base each call, so host-side git/gh is compound `cd
  <worktree> && …`; the guard allows a leading `cd` under `~/.cache/fr/worktrees`
  / temp (#279); `/add-dir` (#281) persists a bare `cd`. Never run base commands.
- `up` writes a gitignored `.fr-isolation` marker (`mode` records the mode) that
  the `fr-isolation-required` PreToolUse hook reads to ALLOW edits; tracked files
  in an fr-enabled base clone are blocked (`FR_BASE_OK=1` / `.fr-isolation-allow`
  escape). `down` removes it. See that rule (#328).

## Session bindings (traceability)

Which harness session holds which workspace — traceability only; the edit gate never reads a binding. `attach` records
`{session_id, harness, attached_at}` in the workspace state (source of truth) plus a derived index `~/.cache/fr/sessions/<id>.json`
(`FR_SESSIONS_DIR`; keys `session_id, harness, repo_root, branch, worktree, profile, attached_at`), one binding per session.
`up --session` binds in the same call; `down` detaches everyone AFTER a successful teardown (a refused `down` keeps bindings);
`gc` prunes stale indexes. Claude transports (plugin hooks, always exit 0): `fr-session-bind.sh` (PostToolUse Bash; parses
`fr isolation up|exec|down …`, leading `cd` folded), `fr-session-unbind.sh` (SessionEnd), `fr-worktree-create.sh` (`claude --worktree` /
`EnterWorktree` → `up --session --print-path`, branch `wt/<name>`; `agent-*` keep Claude's default `<repo>/.claude/worktrees/`
shape), `fr-worktree-remove.sh` (`down --worktree`; a refusal keeps the workspace). Hermes has no bind transport yet.

**Status line** (spec 2026-09-14): `scripts/fr-statusline-segment.sh` answers "which branch, and is this session in fr-isolation?" —
shell+jq+git, never fr. Input: status-line JSON on stdin, or `--cwd <dir>` (no stdin). A live binding wins; else the cwd's branch, `fr`
when the cwd's toplevel is an fr workspace. `--format plain` (state `fr`/`none`, `branch: <b>`/ `no branch`, `worktree: <path>`/`no
fr-isolation`), `ansi` (the two rows, green when fr, purple when not), `oneline` (`fr:<branch>`/`<branch>`, 40-char slot). The v1 `iso:`
hint and worktree gauge are gone. **Claude Code:** set `statusLine.command` to `bash
~/.claude/plugins/cache/derio-net--super-fr/super-fr/current/scripts/fr-statusline-claude.sh`, symlink it (`ln -s <that path>
~/.claude/statusline.sh`; the script finds the segment next to its real file), or in your own script: `rows=$(printf '%s' "$data" | bash
<scripts>/fr-statusline-segment.sh --format ansi)`, line 2 = row 1 + ` | ~/cwd`, line 3 = row 2. **Hermes:** pending
NousResearch/hermes-agent#109596 — `display.status_bar.fields: [..., custom]`, `custom_command: "bash <super-fr
checkout>/plugins/super-fr/scripts/fr-statusline-segment.sh --format oneline --cwd ."` (`fr hermes install` copies hooks only, not
scripts). **OpenCode:** no status-line hook (anomalyco/opencode#37464); run `--format oneline --cwd <dir>` from a tmux/herdr status bar.

## Cleanup contract — worktree + container PERSIST after PR creation (back-loaded manual phases push there)

- **gc auto-reconciles merged work, in every mode.** Fires detached on every `up`/`down` (host-wide, no daemon, ≤1 stale): tears down
  MERGED-PR and content-merged workspaces, retires state records whose worktree is gone, removes empty repo folders + stale session indexes,
  and (devcontainer only) reaps orphaned containers / `vsc-*` images. Open-PR, dirty, no-PR work: never touched. **external** only reports.
- **Ownership boundary.** gc acts only where fr ownership is provable (state record, cache, label); a foreign `git worktree add` is invisible.
- **Post-merge cleanup is `fr pickup --branch <b>`'s brief** (verify-merge, archive, commit/push, PR; built from git alone), ending in **`down`, the immediate lever — never run alone in its place**: it verifies container + worktree gone before dropping state (never leaked);
  refuses an open PR, a dirty worktree (#435), content not on `origin/<default>` (#467).
- **`--force` is operator-requested-and-informed only.** It bypasses all three. An agent must never reach for it on its own initiative — only after
  the operator asks — and first names what would be destroyed: `git worktree remove --force` drops the worktree and fr's record; the branch
  and its commits stay; uncommitted changes do not, except fr's records under `docs/superpowers/` (run cursor, journals), preserved unless
  `--no-preserve`. Restore is additive, only when the next `up --branch` re-creates the worktree: a file changed since teardown is a reported
  conflict (copy kept); recorded deletions are reported, never re-applied; an unrelated re-created branch gets nothing (set aside). A
  refusal names the active run it would end. **Prose with no tripwire.**

## Recovery (#341) and failure handling

- **Wedged or broken container:** `restart [--force]` (installs kept), then `rebuild` (worktree kept) — never `down --force` + `up`.
- **Orphaned pipeline sentinel** (base commands denied, no workspace to `cd` into): the guard heals **per sentinel** — once EVERY workspace
  the session bound is gone it retires that one and fails open (#472); unbound (fresh/legacy) stays armed (#529). `down --all` is repo-wide: `--dry-run` first; another session's workspace needs `--yes`.
- `devcontainer up` failures surface verbatim (missing Docker, a broken profile, an absent secrets file): report and stop, never degrade to a weaker mode.

Files in this skill

  • .source124 B
  • SKILL.md10.6 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.