This commit is contained in:
Bryan Ramos 2026-04-02 15:46:38 -04:00
parent 28ab10c58f
commit 7381316e28
12 changed files with 230 additions and 75 deletions

View file

@ -1,4 +1,4 @@
# Global Claude Code Instructions
Rules are modularized in `rules/` and loaded automatically by the generated Claude config.
Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project).
Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema).

View file

@ -65,7 +65,6 @@ just clean # removes generated artifacts: settings.json + claude/ + cod
| `worker-protocol` | Output format, feedback handling, and operational procedures for worker agents |
| `qa-checklist` | Self-validation checklist workers run before returning results |
| `message-schema` | Typed YAML frontmatter envelopes for all inter-agent communication |
| `project` | Instructs agents to check for and ingest a project-specific skill file before starting work |
## Rules
@ -120,6 +119,7 @@ Runtime policy is documented in [spec/agent-runtime-v1.md](spec/agent-runtime-v1
| `SETTINGS.yaml` | `claude/settings.json` | Claude adapter output |
| `SETTINGS.yaml` | `codex/config.toml` | Codex adapter output |
| `TEAM.yaml` + `rules/*.md` | `codex/AGENTS.md` | Codex adapter output |
| `TEAM.yaml` + `skills/*/SKILL.md` | `codex/skills -> ../skills` | Codex adapter output |
| `TEAM.yaml` + `skills/*/SKILL.md` | installed skill dirs | target install output |
All final config files are generated artifacts. The authored protocol sources are `SETTINGS.yaml`, `TEAM.yaml`, and Markdown instruction content. The primary workflows are `nix run .#build` / `nix run .#install` or the equivalent `just` commands.
@ -129,6 +129,7 @@ Narrow compatibility caveats:
- TEAM schema is intentionally rigid/repo-specific in v1. Inventory changes require schema updates in lockstep.
- Claude generated agent frontmatter is normalized by generator serialization (field order/quoting), which may produce non-semantic diffs.
- Codex skill installation is TEAM-authoritative when `TEAM.yaml` is present. Legacy directory fallback is used only when TEAM is absent or unparseable.
- Codex custom-agent files do not preserve every TEAM agent field. `background`, `memory`, and `isolation` have no documented per-agent equivalents in current Codex docs. TEAM `skills` are mapped into per-agent Codex `skills.config` entries.
Shared runtime intent is generated conservatively across tools:
@ -140,7 +141,13 @@ Shared runtime intent is generated conservatively across tools:
| `runtime.approval = guarded-auto` | partially represented | `approval_policy = "untrusted"` |
| `runtime.approval = full-auto` | partially represented | `approval_policy = "never"` |
The adapters do not expose identical config surfaces. For example, Codex does not support Claude-style per-tool `allow` / `deny` / `ask` patterns directly. The shared protocol keeps the intent portable, then adapters derive the closest target behavior. Use target-specific fields only where there is no shared equivalent:
The adapters do not expose identical config surfaces. For example, Codex does not support Claude-style per-tool `allow` / `deny` / `ask` patterns directly. The shared protocol keeps the intent portable, then adapters derive the closest target behavior.
`runtime.approval` and `runtime.network_access` are the primary source of truth. `targets.codex.approval_policy` and `targets.codex.network_access` are compatibility overrides for exceptional cases only. When set, they override the Codex-derived value.
This repo intentionally sets those Codex overrides to `approval_policy: never` and `network_access: true`. The reason is not that Codex has no approval controls at all, but that it lacks Claude-equivalent pattern-level permission controls for tool/path `allow` / `deny` / `ask`. In this repo, Codex therefore runs with a deliberately more permissive top-level policy than the portable runtime defaults.
Use target-specific fields only when you intentionally need a target-only override:
```yaml
targets:
@ -234,7 +241,6 @@ Each project repo can extend the team with local config in `.claude/`:
- `.claude/CLAUDE.md` — project-specific instructions (architecture notes, domain conventions, stack details)
- `.claude/agents/` — project-local agent overrides or additions
- `.claude/skills/project.md` — skill file that agents automatically ingest before starting work (see the `project` skill)
Commit `.claude/` with the project so the team has context wherever it runs.
@ -242,7 +248,7 @@ Commit `.claude/` with the project so the team has context wherever it runs.
Two memory systems coexist:
- **Manual memory** (`.claude/memory/`) — curated context files with YAML frontmatter, indexed by `MEMORY.md`. Loaded as part of the CLAUDE.md hierarchy on every session. Use this for project decisions, user preferences, and reference pointers.
- **Project memory** (`memory/`) — curated context files with YAML frontmatter, indexed by `MEMORY.md`. This is the portable, instruction-level memory source shared across targets.
- **Agent memory** (`.claude/agent-memory/`) — Claude Code's built-in runtime memory, written automatically by agents with `memory: project` scope. Excluded from CLAUDE.md context via `claudeMdExcludes` to avoid polluting the context window.
Commit both directories with the repo so memory persists across machines and sessions.
Commit both directories when used so memory persists across machines and sessions.

View file

@ -47,5 +47,8 @@ targets:
claude_md_excludes:
- .claude/agent-memory/**
codex:
approval_policy: untrusted
network_access: false
# Intentional target override: Codex does not expose Claude-equivalent
# per-tool/path allow/deny/ask controls, so this repo runs Codex in
# full-auto with network enabled by default.
approval_policy: never
network_access: true

View file

@ -25,9 +25,9 @@ agents:
- Grep
- WebFetch
- WebSearch
- Bash
- Write
disallowed_tools: []
disallowed_tools:
- Edit
max_turns: 35
skills:
- conventions
@ -39,7 +39,7 @@ agents:
description: Use after implementation — audits for security vulnerabilities and validates runtime behavior. Builds, tests, and probes acceptance criteria. Never modifies code.
model: sonnet
effort: ""
permission_mode: ""
permission_mode: acceptEdits
tools:
- Read
- Glob
@ -82,17 +82,16 @@ agents:
documenter:
id: documenter
name: documenter
description: Use when asked to write or update documentation — READMEs, API references, architecture overviews, inline doc comments, or changelogs. Reads code first, writes accurate docs. Never modifies source code.
description: Use when asked to write or update documentation — READMEs, API references, architecture overviews, inline doc comments, or changelogs. Reads code first and updates documentation artifacts only.
model: sonnet
effort: high
permission_mode: ""
permission_mode: acceptEdits
tools:
- Read
- Write
- Edit
- Glob
- Grep
- Bash
disallowed_tools: []
max_turns: 20
skills:
@ -136,7 +135,6 @@ agents:
- Read
- Glob
- Grep
- Bash
- WebFetch
- WebSearch
disallowed_tools:
@ -157,7 +155,6 @@ agents:
- Read
- Glob
- Grep
- Bash
- WebFetch
- WebSearch
disallowed_tools:

View file

@ -60,6 +60,22 @@
validate(instance=settings_data, schema=settings_schema)
validate(instance=team_data, schema=team_schema)
# TEAM referenced files must exist on disk.
for agent_id in team_data["agents"]["order"]:
instruction_file = team_data["agents"]["items"][agent_id]["instruction_file"]
if not (root / instruction_file).is_file():
raise FileNotFoundError(f"Missing agent instruction file: {instruction_file}")
for skill_id in team_data["skills"]["order"]:
instruction_file = team_data["skills"]["items"][skill_id]["instruction_file"]
if not (root / instruction_file).is_file():
raise FileNotFoundError(f"Missing skill instruction file: {instruction_file}")
for rule_id in team_data["rules"]["order"]:
source_file = team_data["rules"]["items"][rule_id]["source_file"]
if not (root / source_file).is_file():
raise FileNotFoundError(f"Missing rule source file: {source_file}")
PY
'';
@ -104,6 +120,7 @@
program = "${mkAppScript "install" ''
set -euo pipefail
test -f ./install.sh || { echo "Run this command from the repository root."; exit 1; }
${validateCmd}
${bashBin} ./install.sh
''}/bin/install";
meta.description = "Install generated artifacts into Claude and Codex config directories.";
@ -148,6 +165,22 @@
validate(instance=settings_data, schema=settings_schema)
validate(instance=team_data, schema=team_schema)
# TEAM referenced files must exist on disk.
for agent_id in team_data["agents"]["order"]:
instruction_file = team_data["agents"]["items"][agent_id]["instruction_file"]
if not (root / instruction_file).is_file():
raise FileNotFoundError(f"Missing agent instruction file: {instruction_file}")
for skill_id in team_data["skills"]["order"]:
instruction_file = team_data["skills"]["items"][skill_id]["instruction_file"]
if not (root / instruction_file).is_file():
raise FileNotFoundError(f"Missing skill instruction file: {instruction_file}")
for rule_id in team_data["rules"]["order"]:
source_file = team_data["rules"]["items"][rule_id]["source_file"]
if not (root / source_file).is_file():
raise FileNotFoundError(f"Missing rule source file: {source_file}")
PY
'';

View file

@ -435,11 +435,11 @@ map_default_sandbox_mode() {
# ---------------------------------------------------------------------------
# map_approval_policy — determines Codex approval_policy from shared config
# $1 = Claude permissions.defaultMode value
# $1 = runtime.approval value (manual / guarded-auto / full-auto)
# $2 = optional Codex approval override from shared config
# ---------------------------------------------------------------------------
map_approval_policy() {
local default_mode="$1"
local runtime_approval="$1"
local override="$2"
if [ -n "$override" ] && [ "$override" != "null" ]; then
@ -447,14 +447,7 @@ map_approval_policy() {
return
fi
# Closest intent mapping:
# - plan remains guarded
# - acceptEdits becomes Codex's closest "edit without constant prompts" mode
case "$default_mode" in
plan) echo "on-request" ;;
acceptEdits) echo "untrusted" ;;
*) echo "untrusted" ;;
esac
map_approval_intent_to_codex_policy "$runtime_approval"
}
# ---------------------------------------------------------------------------
@ -560,6 +553,8 @@ generate_codex() {
# Clean and recreate output directories
rm -rf "$CODEX_DIR"
mkdir -p "$CODEX_AGENTS_DIR"
ln -s ../skills "$CODEX_DIR/skills"
echo "Symlinked: $CODEX_DIR/skills -> ../skills"
# Generate agent .toml files from TEAM metadata + markdown instruction body
echo "Generating Codex agent definitions..."
@ -568,6 +563,7 @@ generate_codex() {
[ -n "$agent_id" ] || continue
local name description model effort permission_mode tools disallowed_tools
local agent_skills
local src_file dst_file
name="$(yq -r ".agents.items.${agent_id}.name" "$TEAM_YAML")"
description="$(yq -r ".agents.items.${agent_id}.description" "$TEAM_YAML")"
@ -576,6 +572,7 @@ generate_codex() {
permission_mode="$(yq -r ".agents.items.${agent_id}.permission_mode // \"\"" "$TEAM_YAML")"
tools="$(yq -r ".agents.items.${agent_id}.tools[]" "$TEAM_YAML" | csv_from_yaml_array)"
disallowed_tools="$(yq -r ".agents.items.${agent_id}.disallowed_tools // [] | .[]" "$TEAM_YAML" | csv_from_yaml_array)"
agent_skills="$(yq -r ".agents.items.${agent_id}.skills[]" "$TEAM_YAML")"
src_file="$SCRIPT_DIR/$(yq -r ".agents.items.${agent_id}.instruction_file" "$TEAM_YAML")"
dst_file="$CODEX_AGENTS_DIR/${name}.toml"
@ -599,6 +596,13 @@ generate_codex() {
You do NOT have access to these tools: ${disallowed_tools}"
fi
# TOML multiline basic strings use """ delimiters; reject raw delimiter
# sequences in instruction bodies so generated TOML remains parseable.
if printf '%s' "$developer_instructions" | grep -q '"""'; then
echo "Error: agent instruction contains raw triple quotes (\"\"\") which break TOML in $src_file"
exit 1
fi
# Write TOML output
cat > "$dst_file" <<TOML
name = "${name}"
@ -606,11 +610,35 @@ description = "${description}"
model = "${codex_model}"
model_reasoning_effort = "${codex_effort}"
sandbox_mode = "${codex_sandbox}"
TOML
cat >> "$dst_file" <<TOML
developer_instructions = """
${developer_instructions}
"""
TOML
local skill_id skill_applies enabled
while IFS= read -r skill_id; do
[ -n "$skill_id" ] || continue
skill_applies="$(yq -r ".skills.items.${skill_id}.applies_to[]" "$TEAM_YAML")"
if ! printf '%s\n' "$skill_applies" | grep -qx "codex"; then
continue
fi
enabled=false
if printf '%s\n' "$agent_skills" | grep -qx "$skill_id"; then
enabled=true
fi
cat >> "$dst_file" <<TOML
[[skills.config]]
path = "../skills/${skill_id}/SKILL.md"
enabled = ${enabled}
TOML
done < <(yq -r '.skills.order[]' "$TEAM_YAML")
echo "Generated: $dst_file"
done < <(yq -r '.agents.order[]' "$TEAM_YAML")
@ -620,7 +648,7 @@ TOML
{
echo "# Agent Team Instructions"
echo ""
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema, project)."
echo "Agent-team specific protocols live in skills (orchestrate, conventions, worker-protocol, qa-checklist, message-schema)."
local rule_id rules_file
while IFS= read -r rule_id; do
[ -n "$rule_id" ] || continue
@ -636,14 +664,15 @@ TOML
echo ""
echo "Generating codex/config.toml..."
local default_mode codex_approval_override codex_network_access
local default_mode runtime_approval codex_approval_override codex_network_access
default_mode="$(map_filesystem_intent_to_claude_mode "$(yq -r '.runtime.filesystem' "$SETTINGS_SHARED_YAML")")"
runtime_approval="$(yq -r '.runtime.approval' "$SETTINGS_SHARED_YAML")"
codex_approval_override="$(yq -r '.targets.codex.approval_policy // ""' "$SETTINGS_SHARED_YAML")"
codex_network_access="$(yq -r '.targets.codex.network_access // .runtime.network_access // false' "$SETTINGS_SHARED_YAML")"
local config_sandbox config_approval
config_sandbox="$(map_default_sandbox_mode "$default_mode")"
config_approval="$(map_approval_policy "$default_mode" "$codex_approval_override")"
config_approval="$(map_approval_policy "$runtime_approval" "$codex_approval_override")"
cat > "$CODEX_DIR/config.toml" <<TOML
#:schema https://developers.openai.com/codex/config-schema.json

View file

@ -11,7 +11,6 @@ SKILLS_SRC="$SCRIPT_DIR/skills"
RULES_SRC="$SCRIPT_DIR/rules"
TEAM_YAML="$SCRIPT_DIR/TEAM.yaml"
AGENTS_DST="$CLAUDE_DIR/agents"
SKILLS_DST="$CLAUDE_DIR/skills"
RULES_DST="$CLAUDE_DIR/rules"
CLAUDE_MD_SRC="$SCRIPT_DIR/claude/CLAUDE.md"
CLAUDE_MD_DST="$CLAUDE_DIR/CLAUDE.md"
@ -31,12 +30,15 @@ echo "Source: $SCRIPT_DIR"
echo "Target: $CLAUDE_DIR"
echo ""
# Pre-flight: require generated claude/ output before proceeding
if [ ! -d "$SCRIPT_DIR/claude" ]; then
echo "Error: claude/ not found. Run ./generate.sh first."
# Pre-flight: build fresh generated outputs before proceeding.
if [ ! -f "$SCRIPT_DIR/generate.sh" ]; then
echo "Error: generate.sh not found."
exit 1
fi
echo "Generating fresh artifacts before install..."
bash "$SCRIPT_DIR/generate.sh"
# Ensure ~/.claude exists
mkdir -p "$CLAUDE_DIR"
@ -82,6 +84,22 @@ create_symlink() {
echo "Linked: $dst -> $src"
}
ensure_directory() {
local dst="$1"
local name="$2"
if [ -L "$dst" ]; then
echo "Removing existing symlink: $dst"
rm "$dst"
elif [ -f "$dst" ]; then
local backup="${dst}.backup.$(date +%Y%m%d%H%M%S)"
echo "Backing up existing $name file to: $backup"
mv "$dst" "$backup"
fi
mkdir -p "$dst"
}
# Symlink a single file
create_file_symlink() {
local src="$1"
@ -165,11 +183,95 @@ resolve_skill_dir_from_team() {
printf '%s\n' "$skill_dir"
}
install_team_skills_for_target() {
local target="$1"
local dst_root="$2"
local label_prefix="$3"
ensure_directory "$dst_root" "$label_prefix skills"
local skill_dir skill_name skill_dst skill_id skill_dir_path
local expected_skills_tmp
expected_skills_tmp="$(mktemp)"
cleanup_skill_symlinks() {
local expected_file="$1"
local existing_path existing_name
for existing_path in "$dst_root"/*; do
[ -e "$existing_path" ] || [ -L "$existing_path" ] || continue
[ -L "$existing_path" ] || continue
existing_name="$(basename "$existing_path")"
if [ -s "$expected_file" ] && grep -Fxq "$existing_name" "$expected_file"; then
continue
fi
echo "Removing stale symlink: $existing_path"
rm "$existing_path"
done
}
if [ -f "$TEAM_YAML" ]; then
local team_skills_tmp
team_skills_tmp="$(mktemp)"
if ! list_team_skills_for_target "$target" > "$team_skills_tmp" 2>/dev/null; then
echo "Warning: TEAM.yaml exists but could not be parsed; falling back to directory-based ${label_prefix} skill install."
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
printf '%s\n' "$skill_name" >> "$expected_skills_tmp"
done
cleanup_skill_symlinks "$expected_skills_tmp"
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
create_symlink "$skill_dir" "$dst_root/$skill_name" "${label_prefix} skill: $skill_name"
done
rm -f "$team_skills_tmp"
rm -f "$expected_skills_tmp"
return
fi
while IFS= read -r skill_id; do
[ -n "$skill_id" ] || continue
printf '%s\n' "$skill_id" >> "$expected_skills_tmp"
done < "$team_skills_tmp"
cleanup_skill_symlinks "$expected_skills_tmp"
while IFS= read -r skill_id; do
[ -n "$skill_id" ] || continue
skill_dir_path="$(resolve_skill_dir_from_team "$skill_id" || true)"
if [ -z "$skill_dir_path" ]; then
echo "Warning: TEAM.yaml skill '$skill_id' has no valid instruction_file directory; skipping."
continue
fi
create_symlink "$skill_dir_path" "$dst_root/$skill_id" "${label_prefix} skill: $skill_id"
done < "$team_skills_tmp"
rm -f "$team_skills_tmp"
rm -f "$expected_skills_tmp"
return
fi
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
printf '%s\n' "$skill_name" >> "$expected_skills_tmp"
done
cleanup_skill_symlinks "$expected_skills_tmp"
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
create_symlink "$skill_dir" "$dst_root/$skill_name" "${label_prefix} skill: $skill_name"
done
rm -f "$expected_skills_tmp"
}
create_symlink "$AGENTS_SRC" "$AGENTS_DST" "agents"
create_symlink "$SKILLS_SRC" "$SKILLS_DST" "skills"
create_symlink "$RULES_SRC" "$RULES_DST" "rules"
create_file_symlink "$CLAUDE_MD_SRC" "$CLAUDE_MD_DST" "CLAUDE.md"
create_file_symlink "$SETTINGS_SRC" "$SETTINGS_DST" "settings.json"
install_team_skills_for_target "claude" "$CLAUDE_DIR/skills" "claude"
# Codex CLI integration (optional — only if codex/ output exists)
CODEX_DIR="$HOME/.codex"
@ -181,36 +283,7 @@ if [ -d "$SCRIPT_DIR/codex" ]; then
# Skills: symlink each skill directory into ~/.codex/skills/
# (Can't replace the whole directory — .system/ must remain intact)
mkdir -p "$CODEX_DIR/skills"
if [ -f "$TEAM_YAML" ]; then
team_codex_skills_tmp="$(mktemp)"
if ! list_team_skills_for_target "codex" > "$team_codex_skills_tmp" 2>/dev/null; then
echo "Warning: TEAM.yaml exists but could not be parsed; falling back to directory-based Codex skill install."
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_name" "codex skill: $skill_name"
done
rm -f "$team_codex_skills_tmp"
else
# TEAM is authoritative when present, including the explicit zero-skills case.
while IFS= read -r skill_id; do
[ -n "$skill_id" ] || continue
skill_dir="$(resolve_skill_dir_from_team "$skill_id" || true)"
if [ -z "$skill_dir" ]; then
echo "Warning: TEAM.yaml skill '$skill_id' has no valid instruction_file directory; skipping."
continue
fi
create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_id" "codex skill: $skill_id"
done < "$team_codex_skills_tmp"
rm -f "$team_codex_skills_tmp"
fi
else
# Legacy fallback only when TEAM.yaml is absent.
for skill_dir in "$SKILLS_SRC"/*/; do
skill_name="$(basename "$skill_dir")"
create_symlink "$skill_dir" "$CODEX_DIR/skills/$skill_name" "codex skill: $skill_name"
done
fi
install_team_skills_for_target "codex" "$CODEX_DIR/skills" "codex"
# Generated agents
if [ -d "$SCRIPT_DIR/codex/agents" ]; then

View file

@ -10,3 +10,4 @@
- Use `MEMORY.md` in that directory as the index (one line per entry pointing to a file)
- Memory files use frontmatter: `name`, `description`, `type` (user/feedback/project/reference)
- Commit `memory/` with the repo so memory persists across machines and sessions
- Tool-specific runtime memory (for example `.claude/agent-memory/`) is optional and does not replace `memory/` as the project source of truth

View file

@ -149,10 +149,12 @@
"on-request",
"untrusted",
"never"
]
],
"description": "Codex-only compatibility override. Prefer runtime.approval as the portable source of truth."
},
"network_access": {
"type": "boolean"
"type": "boolean",
"description": "Codex-only compatibility override. Prefer runtime.network_access as the portable source of truth."
}
}
}

View file

@ -367,7 +367,7 @@
"inventory_skills": {
"type": "object",
"additionalProperties": false,
"description": "Skill inventory for protocol v1. This schema enforces exact order, exact keys, and key/id equality for the current repository inventory.",
"description": "Skill inventory for protocol v1. This schema enforces exact order, exact keys, and key/id equality for the current repository inventory. The fixed v1 inventory does not include a separate 'project' skill entry.",
"required": [
"order",
"items"

View file

@ -21,7 +21,7 @@ Version 1 standardizes:
- portable tool classes
- protected path rules
- dangerous shell command prompts
- target-specific escape hatches only when the target exposes settings with no shared equivalent
- a narrow set of target-specific escape hatches for compatibility overrides
Version 1 does not attempt to standardize:
@ -50,13 +50,20 @@ Version 1 does not attempt to standardize:
### `targets`
Target blocks are escape hatches, not the main schema. Use them only where a runtime exposes a knob with no shared equivalent.
Target blocks are escape hatches, not the main schema.
Current target-specific fields:
- `targets.claude.claude_md_excludes`
- `targets.codex.approval_policy`
- `targets.codex.network_access`
- `targets.codex.approval_policy` (optional override of derived approval)
- `targets.codex.network_access` (optional override of derived network access)
Authority rules:
- `runtime.approval` and `runtime.network_access` are the portable source of truth.
- Codex target fields exist for explicit compatibility overrides and should normally be omitted.
- When Codex target fields are set, they intentionally override the derived Codex value.
- In this repo, `targets.codex.approval_policy` and `targets.codex.network_access` are intentionally set so Codex runs with `approval_policy = "never"` and network enabled by default. This is a deliberate target-specific compatibility choice, not an accidental divergence.
## Adapter rules
@ -81,15 +88,16 @@ Lossiness:
- `runtime.filesystem = read-only` -> `sandbox_mode = "read-only"`
- `runtime.filesystem = workspace-write` -> `sandbox_mode = "workspace-write"`
- `runtime.approval = manual` -> `approval_policy = "on-request"`
- `runtime.approval = guarded-auto` -> `approval_policy = "untrusted"`
- `runtime.approval = full-auto` -> `approval_policy = "never"`
- `runtime.approval = manual` -> `approval_policy = "on-request"` (unless overridden)
- `runtime.approval = guarded-auto` -> `approval_policy = "untrusted"` (unless overridden)
- `runtime.approval = full-auto` -> `approval_policy = "never"` (unless overridden)
- `runtime.network_access` -> `[sandbox_workspace_write].network_access`
Lossiness:
- Codex does not expose Claude-style per-tool `allow` / `deny` / `ask` pattern controls in `config.toml`.
- Protected paths and dangerous command prompts are therefore only partially representable in Codex config today.
- Codex does expose coarse approval controls, including `approval_policy` and documented granular approval categories, but not the same pattern-level permission model Claude exposes.
## Compatibility contract

View file

@ -79,9 +79,10 @@ Each agent entry includes metadata required for adapter generation:
Each skill entry includes lightweight metadata and content reference:
- `id`
- `name`
- `description`
- `instruction_file`
- optional target/install metadata
- target/install metadata (`applies_to`, `install_mode`)
Skill prose remains in `skills/*/SKILL.md`.
@ -110,6 +111,7 @@ Current target behavior:
- `codex/config.toml`
- `codex/AGENTS.md`
- `codex/agents/*.toml`
- `codex/skills` symlinked to the shared skill directories for relative `skills.config` references
## Validation Requirements
@ -128,6 +130,7 @@ TEAM validation enforces schema + runtime checks for:
- Existing YAML frontmatter in `agents/*.md` may remain for editorial continuity, but generation does not use it for team metadata.
- Output diffs that are purely formatting-related are acceptable; semantic behavior changes are not unless explicitly documented.
- TEAM schema is intentionally rigid/repo-specific in v1; inventory additions/removals require schema updates in lockstep.
- Agent metadata is not fully portable across targets. Current Codex custom-agent docs cover session-style fields such as `model`, `model_reasoning_effort`, `sandbox_mode`, `mcp_servers`, and `skills.config`, but do not document per-agent equivalents for TEAM's `background`, `memory`, or `isolation` fields.
## Out of Scope