How Agent Profile Works
A deterministic, privacy-first pipeline from repository discovery to subprocess launch.
The Core Principle: Your Agents, in Separate Rooms
Modern software engineers routinely switch contexts: corporate client code, internal proprietary services, personal hobby projects, and open-source contributions. Yet virtually every coding assistant defaults to storing tokens, prompt histories, MCP servers, and cached context in a single global home folder (such as ~/.claude/, ~/.codex/, or ~/.config/cursor/).
This creates serious security risks:
- Credential Contamination: Client A's API keys or enterprise auth tokens sit in the same directory scanned when contributing to a public open-source project.
- Context & History Leakage: Agent history files and local sqlite databases preserve previous completions across projects, allowing sensitive prompt details to bleed into unrelated completions.
- Repository Exploitation: Malicious or compromised repositories may craft project configuration files that quietly extract ambient credentials.
Agent Profile solves this by establishing clean, deterministic profile rooms per repository. The agent owns authentication; Agent Profile owns profile selection.
The 6-Stage Execution Pipeline
Every execution of agent-profile follows a strict, one-way data pipeline where each stage verifies invariants before advancing:
CLI Invocation & Argument Separation
Parses the agent name (e.g. claude, codex, aider), optional profile word, and wrapper flags (--dry-run, --repo <path>). Everything after -- is isolated and preserved verbatim to pass to the agent process untouched.
Canonical Repository Discovery
Discovers the git repository for the current working directory (or --repo override). Resolves symlinks, NTFS junctions, and path casing to a single canonical root. Git submodules and linked worktrees are strictly treated as distinct repositories of their own.
Deterministic Precedence Resolution
Resolves the target profile through an unyielding 4-step ladder: Agent-specific mapping → Repository mapping → default_profile → Error. It never guesses from directory names or Git branch names.
Adapter Synthesis
Invokes the agent's adapter to produce relocated environment variables (e.g. CLAUDE_CONFIG_DIR, CODEX_HOME) or flag arguments (e.g. --config). The adapter computes data only; it never spawns processes.
LaunchPlan Verification
Emits an immutable LaunchPlan specifying executable path, arguments, working directory, and environment modifications. In --dry-run mode, this plan is formatted and printed for operator inspection.
Subprocess Launch & Signal Forwarding
The centralized process launcher executes the agent binary with the sterile environment. Process exit codes, signals (SIGINT, SIGTERM), and standard streams pass through cleanly.
The Deterministic Precedence Ladder
When launching without an explicit profile name (for example, agent-profile claude), the profile is resolved deterministically:
| Priority | Source | Mechanism | Scope |
|---|---|---|---|
| 1. Highest | Agent-specific repository link | agent-profile <agent> link <profile> |
Applies only to this specific agent in this canonical repository root. |
| 2. Second | Repository-wide link | agent-profile link <profile> |
Applies to all agents launched in this canonical repository root. |
| 3. Third | Global default profile | default_profile = "name" in config.toml |
Fallback profile when no repository-specific mapping exists. |
| 4. Terminal | Resolution Error | Non-zero exit with descriptive diagnostic | Refuses to guess. Suggests exact link or create commands. |
To inspect how the current repository resolves without running an agent, use agent-profile <agent> resolve or agent-profile status.
Repository Boundaries: Worktrees, Submodules, and Junctions
A repository mapping applies strictly to that canonical repository:
- Canonicalization: A subdirectory, symlink, or NTFS junction always resolves to its canonical Git root before lookup. Moving into
src/engine/continues to resolve to the parent repository mapping. - Submodules: A Git submodule is an independent repository. It never inherits the parent repository's profile mapping.
- Linked Worktrees: Git worktrees possess distinct working trees and are treated as distinct repositories so experimental or parallel branches can carry dedicated profiles.
- Untrusted Repositories: Agent Profile deliberately ignores in-repo configuration files for profile selection. An untrusted public repo can never dictate which profile your machine selects.
The LaunchPlan Architecture
In classical CLI wrapper designs, individual agent plugins directly call std::process::Command::spawn. This architectural flaw makes wrapper behavior impossible to test without side effects and encourages inconsistent signal handling.
Agent Profile implements a clean separation of concerns:
CLI Command
↓
Repository Discovery (Canonical Git Root)
↓
Profile Resolution (Deterministic Ladder)
↓
Agent Adapter (Calculates Environment & Arguments)
↓
LaunchPlan (Immutable Pure Data Structure)
↓
Process Launcher (Executes Subprocess in Clean Environment)
Adapters produce a LaunchPlan; they never spawn processes. This guarantees that every adapter is 100% unit testable in pure Rust, and the launcher enforces security invariants across all twelve adapters.
The Evidence-Based Isolation Harness
We do not trust vendor documentation when it claims an environment variable or flag provides isolation. Every adapter in Agent Profile is verified through an empirical sandbox test harness in Linux containers:
- Filesystem Delta Tracing: We snapshot the filesystem, execute the agent with our relocated configuration, and record every file created, modified, or removed across both the target directory and the default user directory.
- Process Auditing: We inspect binary strings and syscalls for hardcoded fallback paths and undocumented credential stores.
- Three-Level Isolation Grading:
config: User settings and preferences are proven isolated.credentials: Stored auth keys and session cookies are proven isolated.state: Completion logs, prompt history, and SQLite databases are proven isolated.