From c4ee5f081c1ec7a0f43b8e2750ab8699d7713b34 Mon Sep 17 00:00:00 2001 From: Wayne Sun Date: Tue, 23 Jun 2026 19:06:04 -0400 Subject: [PATCH] docs: add sandbox layout, skill precedence, and agent rule layering - customizing-with-skills.md: fix skill overloading section to distinguish extend (.claude/skills/) from override (customized/skills/), add skill precedence explanation (personal > project) - customizing-with-agents-md.md: add section on how AGENTS.md composes with agent definitions (different layers, not competing) - runtimes.md: add sandbox workspace layout diagram, agent rule layering diagram, AGENTS.md injection logic, and context file security scanning - cli-internals.md: add cross-reference to runtimes.md for sandbox details Assisted-by: Claude (fix), Gemini (review), Codex (review) Signed-off-by: Wayne Sun --- docs/guides/dev/cli-internals.md | 3 + .../guides/user/customizing-with-agents-md.md | 46 ++++++++++ docs/guides/user/customizing-with-skills.md | 74 +++++++++------ docs/runtimes.md | 89 +++++++++++++++++++ 4 files changed, 187 insertions(+), 25 deletions(-) diff --git a/docs/guides/dev/cli-internals.md b/docs/guides/dev/cli-internals.md index d8046d5fb..57919ad50 100644 --- a/docs/guides/dev/cli-internals.md +++ b/docs/guides/dev/cli-internals.md @@ -394,6 +394,9 @@ SandboxWorkspace = "/sandbox/workspace" SandboxClaudeConfig = "/sandbox/claude-config" ``` +For sandbox workspace layout, agent rule layering, and security scanning +details, see [Agent runtimes](../../runtimes.md). + ### Key Sandbox Operations | Operation | CLI Command | Purpose | diff --git a/docs/guides/user/customizing-with-agents-md.md b/docs/guides/user/customizing-with-agents-md.md index 67e8ac2ef..30431caa2 100644 --- a/docs/guides/user/customizing-with-agents-md.md +++ b/docs/guides/user/customizing-with-agents-md.md @@ -87,6 +87,52 @@ Your repo has a complex domain model and triage often miscategorizes issues: - The `api/` directory is auto-generated from protobuf — never modify it directly. ``` +## How AGENTS.md interacts with agent definitions + +Fullsend agents have two layers of instructions, loaded through different +mechanisms: + +1. **Agent definition** — the system prompt loaded via `--agent `. + Fullsend controls this. It defines the agent's role, task, allowed tools, + model, and which built-in skills to load. Repos cannot modify it. + +2. **Project instructions** — `CLAUDE.md` and `AGENTS.md` auto-loaded from + the working directory. Your repo controls these. They provide conventions, + architecture context, and domain knowledge. + +These layers **compose** — they don't compete. The agent definition sets +*what* the agent does (review code, implement a feature). Your AGENTS.md +sets *how* it should work in your repo (test commands, code style, domain +context). If AGENTS.md contradicts the agent definition, the agent definition +takes precedence. + +### What AGENTS.md can do + +- Guide agent behavior within its defined role (coding conventions, test + strategy, architecture rules) +- Reference repo skills by name — the agent will invoke them if they exist + in `.claude/skills/` +- Provide domain context that helps the agent make better decisions + +### What AGENTS.md cannot do + +- Override the agent definition's tool restrictions (e.g., the review agent + cannot write files regardless of what AGENTS.md says) +- Remove or replace built-in skills — use + [`customized/skills/`](customizing-with-skills.md#overriding-built-in-skills) + for that +- Change the agent's model or execution parameters + +### Injection handling + +When the target repo has no AGENTS.md, fullsend injects an org-level default +from the config repo. When the repo has AGENTS.md but no CLAUDE.md, fullsend +injects a bridge CLAUDE.md that points to AGENTS.md. Both injected files are +hidden from git so agents don't accidentally commit them. + +All repo context files (AGENTS.md, CLAUDE.md, SKILL.md) are scanned for +prompt injection before the agent starts. + ## What not to do - **Don't write agent-specific instructions.** All agents read the same diff --git a/docs/guides/user/customizing-with-skills.md b/docs/guides/user/customizing-with-skills.md index 12fb2e7ac..50bab45a8 100644 --- a/docs/guides/user/customizing-with-skills.md +++ b/docs/guides/user/customizing-with-skills.md @@ -78,31 +78,63 @@ your-repo/ .claude/skills -> ../.agents/skills ``` -## Skill overloading +## Extending agents with repo skills -Each fullsend agent ships with built-in skills. You can **overload** any of -these by providing your own skill with the same name. Your version replaces -the built-in one at runtime — no other configuration needed. +Skills you add to your repository are available to all fullsend agents +alongside the built-in skills. This is the primary way to give agents +domain-specific capabilities — linting rules, deployment checklists, +architecture constraints — without modifying any fullsend configuration. -This is the most precise way to tune agent behavior. An overloaded skill is only -loaded by the agent that uses it, unlike `AGENTS.md` instructions which are -loaded by every agent. +Repo skills **extend** the agent's skill set. They do not replace built-in +skills. If a repo skill has the same name as a built-in skill, the built-in +version takes precedence and the repo version is silently ignored. Use a +unique name to ensure your skill is discoverable. -### How overloading works +### Skill precedence -Fullsend uses a layered content resolution model -([ADR 0035](../../ADRs/0035-layered-content-resolution.md)). At runtime, the -agent's workspace is assembled by copying upstream defaults first, then -overlaying org-level customizations on top. When you provide a skill with the -same name as a built-in one, yours wins. +Fullsend uploads built-in skills to the agent's personal-level config +directory (`CLAUDE_CONFIG_DIR/skills/`). Repo skills live in the project-level +`.claude/skills/` directory. Claude Code resolves name collisions using +precedence: -To overload a skill, create it in your `.fullsend` config repo at -`customized/skills//SKILL.md`. The directory name must match the -built-in skill name exactly. +``` +Personal (CLAUDE_CONFIG_DIR/skills/) > Project (.claude/skills/) + fullsend built-in skills repo skills +``` + +A repo skill with a novel name (no collision) is always available. A repo +skill with a name matching a built-in skill is shadowed — the agent never +sees it. + +### Extension points + +Some agents recognize skill names that do not ship with fullsend. Providing +these unlocks additional capabilities. See each agent's documentation for the +skills it supports — for example, the +[prioritize agent](../../agents/prioritize.md) uses a `customer-research` skill +when available. + +## Overriding built-in skills + +To intentionally **replace** a built-in skill with your own version, use the +`customized/` overlay ([ADR 0035](../../ADRs/0035-layered-content-resolution.md)). +This replaces the skill at the config layer before the agent starts — the +built-in version is never uploaded to the sandbox. + +Create the override in your `.fullsend` config repo (per-org mode) or in +`.fullsend/customized/` in the target repo (per-repo mode). The directory +name must match the built-in skill name exactly: + +``` +customized/skills/code-review/SKILL.md # replaces the built-in code-review +``` + +This is an org-sanctioned operation — it goes through the content overlay +engine, not through project-level skill discovery. ### Built-in skills -These skills ship with fullsend and can be overloaded: +These skills ship with fullsend and can be overridden via `customized/skills/`: | Agent | Skill | Purpose | |-------|-------|---------| @@ -113,14 +145,6 @@ These skills ship with fullsend and can be overloaded: | [Prioritize](../../agents/prioritize.md) | `customer-research` | Customer data gathering for RICE scoring (extension point) | | [Retro](../../agents/retro.md) | `retro-analysis`, `finding-agent-runs` | Workflow analysis and proposal generation | -### Extension points - -Some agents recognize skill names that do not ship with fullsend. Providing -these unlocks additional capabilities. See each agent's documentation for the -skills it supports — for example, the -[prioritize agent](../../agents/prioritize.md) uses a `customer-research` skill -when available. - ## When to use skills vs. AGENTS.md Use **skills** when you need to change how a specific agent performs a specific diff --git a/docs/runtimes.md b/docs/runtimes.md index e33877bfe..ffa5f33c1 100644 --- a/docs/runtimes.md +++ b/docs/runtimes.md @@ -36,8 +36,97 @@ Harness `security.fail_mode` controls whether critical findings **block** the ru A runtime that implements `Runtime` but not `ClaudeHooksBootstrap` (or an equivalent future extension) will **not** install Tirith, SSRF, canary, or other Claude hook scripts. Document what your runtime provides instead. +## Sandbox workspace layout + +The sandbox has two key directories that map to Claude Code's config levels: + +``` +/sandbox/ +├── claude-config/ ← CLAUDE_CONFIG_DIR (personal level) +│ ├── agents/ +│ │ └── review.md Agent definition (--agent loads from here) +│ ├── skills/ +│ │ ├── code-review/SKILL.md Built-in skills (personal level — wins on collision) +│ │ ├── pr-review/SKILL.md +│ │ └── ... +│ └── plugins/ +│ └── ... Plugin state (simplified; see bootstrapPlugins()) +│ +└── workspace/ ← SandboxWorkspace + ├── .env Environment variables (sourced before claude) + ├── .env.d/ Additional env files (host_files expand) + ├── .claude/ + │ ├── hooks/ Security hooks (PreToolUse, PostToolUse) + │ └── settings.json Hook wiring (separate from plugin config) + │ + └── / ← Claude Code's working directory (cd target) + ├── CLAUDE.md Project instructions (repo's own or injected bridge) + ├── AGENTS.md Project rules (repo's own or org default injected) + ├── .claude/skills/ Repo skills (project level — shadowed on collision) + │ └── custom-lint/SKILL.md + └── src/... Target repo source code +``` + +## Agent rule layering + +When `fullsend run` executes an agent, Claude Code loads instructions from +multiple sources. These compose — they occupy different layers, not competing +slots: + +``` +┌────────────────────────────────────────────────────────┐ +│ Layer 1: Agent Definition (system prompt) │ +│ Source: /sandbox/claude-config/agents/.md │ +│ Loaded via: --agent flag │ +│ Controls: role, task, tools, disallowedTools, model, │ +│ built-in skills list │ +│ Authority: highest — repo cannot modify │ +├────────────────────────────────────────────────────────┤ +│ Layer 2: Project Instructions (advisory) │ +│ Source: /sandbox/workspace//CLAUDE.md │ +│ /sandbox/workspace//AGENTS.md │ +│ Loaded via: Claude Code auto-loads from working dir │ +│ Controls: conventions, architecture, domain context │ +│ Authority: advisory — cannot override layer 1 │ +├────────────────────────────────────────────────────────┤ +│ Layer 3: Skills │ +│ Personal: /sandbox/claude-config/skills/ (fullsend) │ +│ Project: /.claude/skills/ (repo) │ +│ Precedence: personal > project (name collision → │ +│ fullsend wins, repo version shadowed) │ +│ Repo skills extend the agent; customized/skills/ │ +│ overrides at the config layer before upload │ +└────────────────────────────────────────────────────────┘ +``` + +### AGENTS.md injection logic + +`run.go` step 8a (`hasAgentsMD()` / `injectClaudeMDPointer()`): + +1. If target repo has no AGENTS.md → inject org-level default from config repo, + add to `.git/info/exclude` +2. If runtime is Claude Code, target repo has AGENTS.md but no CLAUDE.md → + inject bridge CLAUDE.md pointing to AGENTS.md, add to `.git/info/exclude` +3. If target repo has both → use as-is + +### Context file security scanning + +`run.go` steps 8c and 9b: + +Repo context files (CLAUDE.md, AGENTS.md, SKILL.md) are scanned in two +defense-in-depth passes before the agent starts: + +1. **Host-side (Path A, step 8c):** `scanRepoContextFiles()` runs the + `InputPipeline` (unicode normalizer, context injection scanner) on the + host before files enter the sandbox. +2. **Sandbox-side (Path B, step 9b):** `buildScanContextCommand()` runs + `fullsend scan context` inside the sandbox after all files are assembled. + +Critical findings block the run in `fail_mode: closed`. + ## Related docs +- [cli-internals.md](guides/dev/cli-internals.md) — sandbox constants, key sandbox operations - [architecture.md](architecture.md) — Agent Runtime layer - [problems/security-threat-model.md](problems/security-threat-model.md) — threat model and scanner paths - [problems/agent-architecture.md](problems/agent-architecture.md) — pluggable runtimes (#1260, #579, #70)