Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/guides/dev/cli-internals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
46 changes: 46 additions & 0 deletions docs/guides/user/customizing-with-agents-md.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <name>`.
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
Expand Down
74 changes: 49 additions & 25 deletions docs/guides/user/customizing-with-skills.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[medium] architectural-misalignment

The PR reverses the documented skill precedence model: the old text says repo skills with the same name as built-in skills replace the built-in; the new text says built-in skills take precedence and repo skills are silently ignored. ADR 0035 describes the customized/ overlay mechanism (which the new docs preserve correctly), but the claim about project-level .claude/skills/ being shadowed by personal-level CLAUDE_CONFIG_DIR/skills/ needs verification against Claude Code actual skill resolution behavior.

Suggested fix: Verify Claude Code actual skill precedence (personal > project). If confirmed, add a brief note explaining that this precedence is a Claude Code runtime behavior, distinct from the ADR 0035 overlay mechanism.

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.
Comment on lines +88 to +91

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this precedence behavior is accurate, i.e. repo skills with colliding names are silently ignored, then I feel like this should also track a follow-up to log a warning when a repo skill is shadowed, otherwise users have no way to know their skill isn't running.


### 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-name>/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 |
|-------|-------|---------|
Expand All @@ -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
Expand Down
89 changes: 89 additions & 0 deletions docs/runtimes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
└── <repo-name>/ ← 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/<name>.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/<repo>/CLAUDE.md │
│ /sandbox/workspace/<repo>/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: <repo>/.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)
Loading