agents/openai.yaml
interface:
display_name: "AI Coding Agents — Settings Policy"
short_description: "Design settings and policy layers"
default_prompt: "Use $ai-coding-agents-settings-policy to design source precedence, managed policy, env controls, or runtime settings validation for a coding-agent runtime."
data/sources.json
{
"metadata": {
"skill": "ai-coding-agents-settings-policy",
"title": "AI Coding Agents Settings Policy - Sources",
"description": "Official documentation and implementation references for settings precedence, managed policy, schema validation, and safe runtime application",
"last_updated": "2026-07-11",
"updated": "2026-07-11",
"total_sources": 8,
"version": "1.1"
},
"categories": {
"official_documentation": [
{
"name": "Claude Code Documentation",
"url": "https://code.claude.com/docs/en",
"type": "documentation",
"relevance": "Primary product documentation for configuration and managed runtime behavior",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "Claude Code Settings Reference",
"url": "https://code.claude.com/docs/en/settings",
"type": "reference",
"relevance": "Complete settings key listing. Precedence verified 2026-07-11: managed > CLI flags > .claude/settings.local.json > .claude/settings.json (project) > ~/.claude/settings.json (user). Managed-settings-only keys confirmed: allowManagedPermissionRulesOnly, allowManagedHooksOnly, allowManagedMcpServersOnly, claudeMd, disableSideloadFlags, forceRemoteSettingsRefresh, blockedMarketplaces, forceLoginMethod/OrgUUID/GatewayUrl, allowAllClaudeAiMcps, allowedChannelPlugins, deniedMcpServers. disableAutoMode, disableAgentView, disableBundledSkills, autoUpdatesChannel exist in all scopes (not managed-only). cleanupPeriodDays default is 30 (min 1). strictPluginOnlyCustomization, policyHelper, and parentSettingsBehavior could not be verified as of 2026-07-11 and were removed from this skill's claims.",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": true
},
{
"name": "JSON Schema",
"url": "https://json-schema.org/",
"type": "specification",
"relevance": "Reference for machine-validated settings contracts and schema publication",
"update_frequency": "quarterly",
"access": "free",
"add_as_web_search": true
}
],
"implementation_references": [
{
"name": "Claude Code GitHub Repository",
"url": "https://github.com/anthropics/claude-code",
"type": "repository",
"relevance": "Closed-source product page and issue tracker for Claude Code; not the implementation source — use code.claude.com/docs/en for authoritative behavior docs",
"update_frequency": "weekly",
"access": "free",
"add_as_web_search": false
},
{
"name": "Codex CLI Repository",
"url": "https://github.com/openai/codex",
"type": "repository",
"relevance": "Cross-runtime comparison point for agent CLI configuration layers",
"update_frequency": "monthly",
"access": "free",
"add_as_web_search": false
},
{
"name": "OpenAI Codex Config Layer Source",
"url": "https://github.com/openai/codex/blob/9f42c89c0112771dc29100a6f3fc904049b2655f/codex-rs/config/src/state.rs",
"type": "repository_source",
"relevance": "Pinned first-party source for config layer stack, managed requirements, loader overrides, disabled layers, and config debug metadata",
"update_frequency": "pinned",
"access": "free",
"add_as_web_search": false
},
{
"name": "OpenAI: Running Codex safely at OpenAI",
"url": "https://openai.com/index/running-codex-safely/",
"type": "safety_post",
"relevance": "May 2026 first-party source for enterprise control surfaces, configuration management, sandboxing, and agent-aware telemetry",
"update_frequency": "static",
"access": "free",
"add_as_web_search": true
},
{
"name": "Anthropic: Building Effective Agents",
"url": "https://www.anthropic.com/engineering/building-effective-agents",
"type": "guide",
"relevance": "High-level reference for keeping policy and runtime control boundaries explicit",
"update_frequency": "quarterly",
"access": "free",
"add_as_web_search": true
}
]
}
}
learnings.consolidated.md
# ai-coding-agents-settings-policy — Consolidated Learnings
Curated, dated, committed memory for this skill. Pruned from raw `learnings.md` via `agents-skills-feedback-loop/scripts/consolidate.py`. Human-approved.
Cap: 60 entries. When exceeded, promote durable rules to `references/`.
## Filter Override
<!-- Add 2-4 bullets that sharpen what counts as a learning for this skill. Leave empty to use the default filter from agents-skills-feedback-loop/references/learnings-format.md. -->
## Patterns That Work
## Mistakes to Avoid
## Domain Knowledge
## Open Questions
## Consolidated Principles
learnings.md
# ai-coding-agents-settings-policy — Learnings
## Patterns That Work
- [2026-07-11] When one repo's `.claude/settings.local.json` has a defect, sweep every repo: `find ~/Projects -maxdepth 4 -path '*/.claude/settings*.json' -not -path '*/node_modules/*'` then batch-check with `jq`. A copied "maximum autonomy" template had spread the same invalid `mcp__*` rule and `bypassPermissions` mode to 7 of 17 repos.
- [2026-07-11] Global `~/.claude/settings.json` hygiene: with bare tool allows (`Read`, `Bash`) present, path- and prefix-scoped variants of the same tool are dead weight — prune to either the bare rule or the specific ones, never both, so the file shows what is actually granted.
## Mistakes to Avoid
- [2026-07-11] Settings templates copied between repos carry defects with them; fixing only the repo where the warning surfaced leaves the same issue live everywhere else the template landed. Treat any settings bug as a fleet-wide search, not a single-file fix.
- [2026-07-11] Legacy duplicate keys accumulate silently: `voiceEnabled: true` alongside `voice: {enabled: true}` — the flat boolean is the deprecated form; keep the structured object.
## Domain Knowledge
- [2026-07-11] Settings precedence: user → project (`.claude/settings.json`) → local (`.claude/settings.local.json`), later overrides earlier — so a project-local `defaultMode` beats the global one, and cleaning the global file changes nothing in repos with local overrides.
- [2026-07-11] Invalid permission rules in settings files do not fail loading; they are skipped with a per-session startup warning, so a broken rule can sit unnoticed for months while appearing to grant access.
- [2026-07-11] `skipDangerousModePermissionPrompt` and `skipAutoPermissionPrompt` suppress the one-time bypass/auto opt-in dialogs machine-wide — combined with project-level `bypassPermissions` this removes every reminder that a repo runs unguarded.
- [2026-07-11] Web-verify audit of this skill (`code.claude.com/docs/en/settings`, `code.claude.com/docs/en/hooks`, `learn.chatgpt.com/docs/config-file/config-reference`, all checked 2026-07-11): (1) `settings-precedence-table.md` and the SKILL.md Quick Reference had local/project/user inverted — corrected to the verified order `managed > CLI flags > local > project > user`; (2) three Claude Code managed-policy keys (`strictPluginOnlyCustomization`, `policyHelper`, `parentSettingsBehavior`) could not be verified in current docs and were removed rather than kept as unsourced claims; (3) `disableAutoMode`/`disableAgentView`/`disableBundledSkills`/`autoUpdatesChannel` were mislabeled as managed-only — they exist in all settings scopes, policy just makes them non-negotiable; `autoUpdatesChannel` valid values are `"latest"`/`"stable"`, not `"stable"`/`"preview"`/`"disabled"`; (4) Codex `approval_policy` value was `"unless-trusted"` in this skill but current docs use `"untrusted"`; (5) Codex named profiles are separate `$CODEX_HOME/<name>.config.toml` files, not `[profiles.NAME]` tables inside `config.toml` as previously shown. `cleanupPeriodDays` default of 30 (min 1) was confirmed accurate and added as an explicit fact to guard against the same error propagating here.
## Open Questions
## Consolidated Principles
references/deferred-tool-policy-layer.md
# Deferred Tool Policy Layer
ToolSearch is the runtime mechanism through which a model discovers and loads deferred tools. Whether a tool is deferred — and whether the model is allowed to invoke ToolSearch at all — is governed by the settings layer, not by the tool itself.
## Table of Contents
- [What "deferred" means](#what-deferred-means)
- [Settings that govern deferral](#settings-that-govern-deferral)
- [How the settings layer gates ToolSearch](#how-the-settings-layer-gates-toolsearch)
- [Policy decision flow](#policy-decision-flow)
- [Interaction with the tool registry](#interaction-with-the-tool-registry)
- [Example: managed policy restricts ToolSearch to MCP tools only](#example-managed-policy-restricts-toolsearch-to-mcp-tools-only)
- [Example: fully open configuration (development)](#example-fully-open-configuration-development)
- [What ToolSearch does NOT control](#what-toolsearch-does-not-control)
- [Anti-patterns](#anti-patterns)
- [Related](#related)
## What "deferred" means
A deferred tool is not visible to the model on turn one. Its schema is withheld from the tool list sent with the initial system prompt. To use it, the model must first call `ToolSearch` with a query that matches the tool's description; the runtime then loads the full schema and makes the tool callable.
This has two effects:
1. **Prompt-cache economics**: a smaller up-front tool list means a more stable prompt-cache prefix. Adding or removing deferred tools does not invalidate the cache for always-loaded tools.
2. **Policy gating**: the ability to trigger deferral, and the ability to call ToolSearch, can be controlled by settings.
## Settings that govern deferral
| Setting key | Type | Default | Description |
|-------------|------|---------|-------------|
| `toolsearch_enabled` | boolean | `true` | Whether the model can call ToolSearch at all. When `false`, deferred tools are invisible for the session. |
| `toolsearch_scope` | enum | `"all"` | Which tool categories can be discovered via ToolSearch. Values: `"all"`, `"mcp_only"`, `"builtins_only"`, `"none"`. |
| `defer_by_default` | boolean | `false` | Whether unrecognized or late-registered tools are deferred automatically unless marked `alwaysLoad`. |
| `always_load_overrides` | list[string] | `[]` | Tool names that must appear on turn one regardless of their `shouldDefer` flag. |
| `toolsearch_min_trust_class` | string | `"user"` | Minimum trust class required to enable ToolSearch. See [`plugin-only-restriction-recipes.md`](plugin-only-restriction-recipes.md). |
## How the settings layer gates ToolSearch
The precedence rules in [`settings-precedence-table.md`](settings-precedence-table.md) apply. Managed policy wins over user settings:
```json
// managed_policy/tools.json
{ "toolsearch_enabled": false, "toolsearch_scope": "none" }
```
When this policy is active:
- `ToolSearch` is not included in the turn-one tool list.
- Deferred tools cannot be loaded.
- Tools that would normally be deferred remain entirely invisible.
- Tools marked `alwaysLoad: true` are still loaded — they are independent of ToolSearch.
## Policy decision flow
```
Is toolsearch_enabled = false?
├── YES → ToolSearch is not offered; deferred tools stay hidden for this session
└── NO → ToolSearch is offered; proceed
What is toolsearch_scope?
├── "none" → Same as toolsearch_enabled = false
├── "mcp_only" → Only MCP-sourced tools can be discovered
├── "builtins_only"→ Only built-in tools can be discovered
└── "all" → All deferred tools can be discovered (default)
Is the calling source's trust class ≥ toolsearch_min_trust_class?
├── NO → ToolSearch is blocked; log a trust-class violation
└── YES → ToolSearch is available to the model
```
## Interaction with the tool registry
At session startup the tool pool is assembled as follows:
1. All `alwaysLoad` tools are included in the initial tool list.
2. All `shouldDefer` tools are registered in the deferred pool but withheld from the initial list.
3. If `toolsearch_enabled = false`, the deferred pool is frozen — tools in it cannot be loaded regardless of model behavior.
4. `ToolSearch` itself is an `alwaysLoad` built-in when `toolsearch_enabled = true`. It is the only tool that can promote deferred tools to callable.
```
Initial tool list (sent to model turn 1):
[alwaysLoad tools]
+ ToolSearch (if enabled)
Deferred pool (hidden from model):
[shouldDefer tools]
→ promoted one at a time as ToolSearch loads them
```
## Example: managed policy restricts ToolSearch to MCP tools only
```json
// managed_policy/toolsearch.json
{
"toolsearch_scope": "mcp_only",
"toolsearch_min_trust_class": "policy"
}
```
Effect:
- The model can call ToolSearch.
- ToolSearch only returns schemas for MCP-sourced tools.
- Built-in deferred tools remain invisible.
- A user or project file attempting `"toolsearch_scope": "all"` is blocked (min_trust_class = "policy").
## Example: fully open configuration (development)
```json
// user settings.json
{
"toolsearch_enabled": true,
"toolsearch_scope": "all",
"defer_by_default": true
}
```
Effect:
- All tools that declare `shouldDefer: true` are deferred.
- `defer_by_default: true` also defers tools that do not explicitly declare a defer preference.
- The model sees `ToolSearch` and can discover any deferred tool.
## What ToolSearch does NOT control
- Whether a tool is permitted to execute (that is [`ai-coding-agents-permissions`](../../ai-coding-agents-permissions/SKILL.md)).
- Whether a tool exists in the registry (that is tool-pool assembly, see [`tool-registry-and-pool-assembly.md`](../../ai-coding-agents-tools/references/tool-registry-and-pool-assembly.md)).
- The ordering of `alwaysLoad` tools in the initial list (that is prompt-cache ordering, same reference).
## Anti-patterns
- Gating ToolSearch from inside the ToolSearch implementation itself. The settings layer should prevent ToolSearch from being offered to the model — not have ToolSearch refuse to run after being called.
- Allowing low-trust sources (local settings, project files) to override `toolsearch_min_trust_class` downward. Trust-class rules apply to this setting the same way they apply to all plugin-only surfaces.
- Confusing "ToolSearch disabled" with "deferred tools disabled." `alwaysLoad` tools are never affected by ToolSearch state.
## Related
- [`settings-precedence-table.md`](settings-precedence-table.md) — Full source-precedence table
- [`plugin-only-restriction-recipes.md`](plugin-only-restriction-recipes.md) — Trust-class enforcement for plugin surfaces
- [`../../ai-coding-agents-tools/references/deferred-loading-execution-and-remote-results.md`](../../ai-coding-agents-tools/references/deferred-loading-execution-and-remote-results.md) — ToolSearch execution pipeline
- [`../../ai-coding-agents-tools/references/deferral-eligibility-decision-tree.md`](../../ai-coding-agents-tools/references/deferral-eligibility-decision-tree.md) — When a tool should be deferred
references/openai-codex-managed-config-and-requirements.md
# OpenAI Codex Managed Config And Requirements
Source snapshot: OpenAI Codex commit `9f42c89c0112771dc29100a6f3fc904049b2655f` (2026-05-24), especially `codex-rs/config/src/state.rs`, `codex-rs/config/src/loader`, and `docs/config.md`.
Web sources checked 2026-05-25:
- OpenAI, "Running Codex safely at OpenAI", May 8, 2026: https://openai.com/index/running-codex-safely/
- Codex configuration docs entrypoint: https://developers.openai.com/codex
Re-verified 2026-07-11 against `learn.chatgpt.com/docs/config-file/config-reference`. Two details from the May snapshot had drifted and are corrected inline below: `approval_policy` uses `"untrusted"` (not `"unless-trusted"`), and named profiles are separate `$CODEX_HOME/<name>.config.toml` files, not `[profiles.NAME]` tables inside `config.toml`. `allow_managed_hooks_only` in `requirements.toml` (see "Managed Hooks Only" below) was independently re-confirmed as current.
## Table Of Contents
- [Design Goal](#design-goal)
- [Layer Stack](#layer-stack)
- [Requirements Are Constraints](#requirements-are-constraints)
- [Managed Hooks Only](#managed-hooks-only)
- [Debuggability](#debuggability)
- [Known Traps](#known-traps)
## Design Goal
Separate user preferences from organization requirements. Codex's config loader keeps a stack of config layers, records source metadata, and carries requirements that constrain the derived runtime config.
## Layer Stack
Codex models config as entries with:
- source name
- parsed TOML
- raw TOML when available
- version/fingerprint
- disabled reason
- associated `.codex/` folder for project-level config
- hook config folder override for linked worktrees
The layer stack is ordered from lowest to highest precedence. Keep this explicit so debug output can explain why a setting won.
## Requirements Are Constraints
Codex distinguishes config layers from requirements. A managed requirement is not just a higher-precedence preference; it is a constraint that later config derivation must obey.
Use this distinction in new runtimes:
- config says "what this user/project wants"
- requirements say "what this environment permits"
- policy failures should be surfaced before tools execute
## Managed Hooks Only
The current Codex docs note `allow_managed_hooks_only = true` in `requirements.toml`: user, project, and session hooks are ignored while managed hooks remain allowed. This setting is requirements-only; placing it in normal user config must not activate it.
This is a good pattern for high-risk surfaces:
- allow organization-managed automation
- suppress user/project-provided hooks in locked environments
- make the lock visible in config debug output
## Debuggability
A production settings system needs a debug surface that can show:
- loaded layers
- disabled layers and reasons
- raw source path or managed source class
- active profile
- requirements source
- startup warnings
- hook folder used for each layer
Without this, policy support turns into guesswork.
## Known Traps
- Treating managed config as just "another config file" instead of a constraint source.
- Letting a user config key enable a requirements-only security mode.
- Hiding disabled layers, which makes operators think a setting was ignored randomly.
- Resolving hooks from the wrong worktree when project config is linked.
- Applying config reloads without re-sanitizing permissions, hooks, and plugin surfaces.
references/plugin-only-restriction-recipes.md
# Plugin-Only Restriction Recipes
A plugin-only restriction is a settings flag or feature that can only be enabled by certain trusted settings sources. Not every source is equally trusted to unlock plugin surfaces.
## Table of Contents
- [The Core Problem](#the-core-problem)
- [Trust Classes](#trust-classes)
- [Recipes](#recipes)
- [Invariants](#invariants)
- [Anti-patterns](#anti-patterns)
- [Related](#related)
## The Core Problem
Without source-aware trust, a user who edits their local `settings.local.json` can enable a plugin surface that an operator intended to be locked to managed policy. The merge layer must track *which source set a value* so trust rules can be enforced at apply time, not just at read time.
## Trust Classes
| Trust class | Sources included | Can enable plugin surfaces? |
|-------------|-----------------|----------------------------|
| `policy` | `distro_policy`, `managed_policy` | Yes — full plugin authority |
| `user` | `user` settings file, CLI flags | Only if `distro_policy` / `managed_policy` have not restricted plugins |
| `project` | `project` settings file | Only for scoped project-level plugin activations if allowed by user/policy |
| `local` | `local` settings file | Never — lowest trust; plugin enables from local are ignored |
## Recipes
### Recipe 1 — Lock a plugin surface to policy-class sources only
Scenario: your runtime ships a `network_search` built-in that is off by default. You want it enabled only when an operator explicitly allows it via managed policy.
**Settings schema:**
```json
{
"network_search": {
"type": "boolean",
"default": false,
"plugin_only": true,
"min_trust_class": "policy"
}
}
```
**Merge-time enforcement:**
```python
def apply_value(key, value, source_trust_class, schema):
field = schema[key]
if field.get("plugin_only") and field.get("min_trust_class") == "policy":
if source_trust_class not in ("policy",):
# Silently ignore; do not error (user should not see "permission denied")
return
runtime_state[key] = value
```
**Result:** a user setting `network_search = true` in their `~/.config/agent/settings.json` has no effect unless managed policy also allows it.
### Recipe 2 — Allow project-scoped plugin activation (but not local)
Scenario: your runtime has a `code_execution_sandbox` plugin. You want teams to be able to opt into it by adding it to the checked-in project settings, but individual developers should not be able to enable it machine-locally in their gitignored file.
**Settings schema:**
```json
{
"code_execution_sandbox": {
"type": "boolean",
"default": false,
"plugin_only": true,
"min_trust_class": "project"
}
}
```
**Trust ordering for this recipe:**
```
policy (enables)
user (enables, subject to policy ceiling)
project (enables — min_trust_class = "project")
local (BLOCKED — below min_trust_class)
```
**Audit log entry** (required for plugin activations):
```json
{
"event": "plugin_surface_activated",
"key": "code_execution_sandbox",
"value": true,
"source": "project",
"source_file": "<repo>/.agent/settings.json",
"trust_class": "project",
"timestamp": "2026-04-27T10:00:00Z"
}
```
### Recipe 3 — Graduated plugin rollout (managed policy + user opt-in)
Scenario: you are rolling out a new `deep_codebase_index` plugin. You want managed policy to opt specific teams in, and then individual users within those teams to be able to further customize it.
**Step 1 — managed policy enables the surface:**
```json
// managed_policy/plugins.json
{ "deep_codebase_index": { "enabled": true, "max_index_size_mb": 500 } }
```
**Step 2 — user can narrow (but not broaden):**
```json
// user settings.json
{ "deep_codebase_index": { "max_index_size_mb": 100 } } ← valid: narrows
{ "deep_codebase_index": { "max_index_size_mb": 1000 } } ← REJECTED: exceeds policy ceiling
```
**Ceiling enforcement at merge time:**
```python
def merge_numeric_with_ceiling(key, user_val, policy_val):
if policy_val is not None:
return min(user_val, policy_val) # user can narrow, not broaden
return user_val
```
### Recipe 4 — Detecting and logging trust-class violations
Rather than silently ignoring low-trust plugin enables, log them so operators can diagnose misconfigured settings files:
```python
def apply_value_with_logging(key, value, source_trust_class, schema, logger):
field = schema[key]
min_class = field.get("min_trust_class")
if field.get("plugin_only") and not trust_satisfies(source_trust_class, min_class):
logger.warning(
"plugin_surface_blocked",
key=key,
requested_value=value,
source_trust_class=source_trust_class,
required_trust_class=min_class,
)
return # Do not apply
runtime_state[key] = value
```
Surface these log entries in the effective-settings debug view so operators can see "this setting was requested but blocked."
## Invariants
- Plugin-only trust is enforced at **apply time**, after merging — not at read time.
- Trust-class violations must be **silent to the user** (no error thrown) but **logged for operators**.
- The effective-settings view must show which trust class won for plugin-surface keys.
- Local settings (`settings.local.json`) must never enable plugin surfaces. This is unconditional.
- An operator-locked plugin surface can be narrowed by lower-trust sources but never broadened.
## Anti-patterns
- Checking trust class only when writing a settings file, not when applying merged values. Users can edit files directly.
- Allowing `local` settings to enable plugin surfaces "for developer convenience." That is a security boundary, not a convenience setting.
- Treating all boolean flags the same regardless of whether they unlock execution or network surfaces.
- Not logging trust-class blocks — silent failures make debugging impossible.
## Related
- [`settings-precedence-table.md`](settings-precedence-table.md) — Full source-precedence table with override examples
- [`settings-source-precedence-and-managed-policy.md`](settings-source-precedence-and-managed-policy.md) — Source model and managed policy architecture
- [`deferred-tool-policy-layer.md`](deferred-tool-policy-layer.md) — How ToolSearch trigger is settings-gated
references/settings-precedence-table.md
# Settings Precedence Table
Concrete reference for the merge order of settings sources in a coding-agent runtime. The order below is highest-to-lowest precedence: a source higher in the table wins over any source below it.
## Table of Contents
- [Precedence Order (Highest → Lowest)](#precedence-order-highest--lowest)
- [Override Examples](#override-examples)
- [Rules](#rules)
- [Debugging effective settings](#debugging-effective-settings)
- [Cache invalidation per layer](#cache-invalidation-per-layer)
- [Related](#related)
## Precedence Order (Highest → Lowest)
| Layer | Source | Editable? | Override rule | Example |
|-------|--------|-----------|---------------|---------|
| **1. distro_policy** | Binary-baked manifest (Custom Distro) | No — immutable without new binary | Wins over everything; cannot be narrowed at runtime | Enterprise distro pins `provider = "anthropic"` and locks `allow_network_tools = false` |
| **2. managed_policy** | Drop-in `.json` files in a managed directory | No — operator-written | Wins over all user-controlled sources; multiple drop-ins merge by filename sort order | IT policy drops `disallow_bash = true`; user cannot re-enable it |
| **3. CLI flags** | `--flag value` on invocation | Per-invocation | Wins over file-based sources for the current process lifetime only | `--model <model-id>` overrides user's configured default |
| **4. local** | `<repo>/.agent/settings.local.json` (gitignored) | Yes — machine-local | Wins over project and user; highest-precedence editable file | Developer overrides `lsp_path` for their machine |
| **5. project** | `<repo>/.agent/settings.json` (checked in) | Yes — shared with team | Wins over user; shared across all contributors | Project pins `max_tokens = 8192` for CI consistency |
| **6. user** | `~/.config/agent/settings.json` (global user file) | Yes — user-owned | Lowest user-editable layer | User sets `theme = "dark"` globally |
| **7. defaults** | Compiled-in defaults | No — code-defined | Applied when no other source specifies a value | Default `timeout_ms = 30000` |
Verified reference point: Claude Code's own precedence is `managed > CLI flags > .claude/settings.local.json > .claude/settings.json (project) > ~/.claude/settings.json (user)` (`code.claude.com/docs/en/settings`, checked 2026-07-11). Local is the most specific file a contributor controls, so it should beat both project and user — the inverse ordering (user or project beating local) is the single most common precedence mistake in this domain; verify against current docs before trusting memory on this point.
## Override Examples
### Managed policy overrides user
```json
// managed_policy/company.json (operator-written)
{ "allow_network_tools": false }
// user settings.json (user-written)
{ "allow_network_tools": true } ← LOSES — managed_policy wins
```
### CLI flag overrides project
```bash
# project settings.json: { "model": "<project-default-model-id>" }
claude --model <requested-model-id> # CLI flag wins for this invocation
```
### Local overrides project for shared keys
```json
// project settings.json
{ "max_tokens": 8192 }
// local settings.local.json
{ "max_tokens": 4096 } ← WINS — local layer wins for this key
```
Note: project only wins for keys that local does not specify. Precedence applies per-key, not per-file — if local is silent on a key, project's value (or user's, if project is also silent) takes effect.
### Distro policy cannot be widened by managed policy
```
distro_policy: { allow_plugins: false }
managed_policy: { allow_plugins: true } ← LOSES — distro is immutable ceiling
```
### Multiple managed drop-ins merge by sort order, last wins within policy layer
```
managed_policy/00_base.json: { "timeout_ms": 10000 }
managed_policy/10_security.json: { "timeout_ms": 5000 } ← wins (sorts later)
managed_policy/20_team.json: { "allow_bash": false } ← additive
```
Effective: `{ "timeout_ms": 5000, "allow_bash": false }`
## Rules
- Precedence is **per-key**: for each setting key, find the highest layer that specifies it and use that value.
- CLI flags are **transient**: they apply for the current process only and are not written back to any file.
- Managed policy is **source-aware**: the merge engine must track which layer set each key so the effective-settings debug view can explain "why can't I change this."
- Distro policy is a **ceiling**, not a floor: managed policy can only narrow within the distro envelope.
- **Plugin-only restrictions** are an additional axis: see [`plugin-only-restriction-recipes.md`](plugin-only-restriction-recipes.md).
- **ToolSearch gating** is a policy-layer concern: see [`deferred-tool-policy-layer.md`](deferred-tool-policy-layer.md).
## Debugging effective settings
A well-built effective-settings view must show:
```
Key: allow_bash
Effective: false
Won by: managed_policy/10_security.json
Overrode: user (true), project (unset), local (unset)
```
Without source attribution, operators cannot explain constraint sources to users. "It's locked by policy" with no file path is not acceptable in production runtimes.
## Cache invalidation per layer
| Layer changed | What to invalidate |
|---------------|--------------------|
| distro_policy | Full settings cache (restart required in most runtimes) |
| managed_policy drop-in added/removed | managed-policy cache + merged cache |
| CLI flag change | Merged cache only (flags are not persisted) |
| user file | user-layer cache + merged cache |
| project file | project-layer cache + merged cache |
| local file | local-layer cache + merged cache |
Never invalidate all caches for a single-file change. Targeted invalidation is required for correct reload sequencing.
## Related
- [`settings-source-precedence-and-managed-policy.md`](settings-source-precedence-and-managed-policy.md) — Source model and managed policy architecture
- [`plugin-only-restriction-recipes.md`](plugin-only-restriction-recipes.md) — Which sources can enable plugin surfaces
- [`deferred-tool-policy-layer.md`](deferred-tool-policy-layer.md) — How ToolSearch trigger is settings-gated
- [`settings-validation-and-safe-runtime-application.md`](settings-validation-and-safe-runtime-application.md) — Validation and runtime application
references/settings-source-precedence-and-managed-policy.md
# Settings Source Precedence And Managed Policy
## Table Of Contents
- [Core Pattern](#core-pattern)
- [Source Layers](#source-layers)
- [Precedence](#precedence)
- [Managed Settings As Base Plus Drop-Ins](#managed-settings-as-base-plus-drop-ins)
- [Plugin-Only Customization Policy](#plugin-only-customization-policy)
- [Source Labels Matter](#source-labels-matter)
- [Design Rules To Reuse](#design-rules-to-reuse)
## Core Pattern
Model runtime settings as layered sources with explicit precedence.
From the April 2026 `claude_code` snapshot:
- `utils/settings/constants.ts` defines the canonical source list
- `utils/settings/settings.ts` loads and merges sources
- managed settings support a base file plus sorted drop-ins
- policy can restrict entire customization surfaces to admin-trusted sources only
## Source Layers
The runtime uses these sources:
- user settings
- project settings
- local gitignored settings
- CLI flag settings
- managed policy settings
Important rule from the source:
- managed policy and flag settings are always included
- editable-source restrictions do not remove those layers
This is a strong pattern for coding-agent CLIs because org policy and explicit CLI overrides should not disappear behind user preferences.
## Precedence
`SETTING_SOURCES` documents that later sources override earlier ones.
Keep this model explicit:
- user
- project
- local
- flag
- policy
If the target runtime needs a different order, document it once and reuse the same ordering everywhere:
- file loading
- UI labels
- conflict explanations
- settings export
This `user → project → local → flag → policy` list is deliberately the same relative order as Claude Code's verified precedence (`~/.claude/settings.json` < `.claude/settings.json` < `.claude/settings.local.json` < CLI flags < managed policy — see [`settings-precedence-table.md`](settings-precedence-table.md)). Local outranking project is the detail auditors get backwards most often; do not "simplify" it to user/project/local alphabetical or size order without checking the target runtime's own docs.
## Managed Settings As Base Plus Drop-Ins
`loadManagedFileSettings()` loads:
- one base managed settings file
- zero or more alphabetically sorted drop-ins
Pattern to reuse:
- one admin-owned base file for defaults
- drop-in fragments for independent teams or policies
- deterministic alphabetical merge order
This avoids one giant central file and still keeps precedence predictable.
## Plugin-Only Customization Policy
`pluginOnlyPolicy.ts` models `strictPluginOnlyCustomization`.
Reusable pattern:
- some customization surfaces can be locked to admin-trusted sources only
- trusted sources can include:
- managed policy
- built-ins
- plugins gated by separate marketplace or trust controls
- user, project, local, and flag sources are blocked for those surfaces
This is useful for:
- commands
- skills
- hooks
- MCP configuration
when organizations want controlled extensibility without fully disabling plugins.
## Source Labels Matter
The runtime keeps distinct source-display helpers:
- lowercase inline labels
- capitalized UI labels
- short display names
Copy this idea.
Users need to understand whether a rule came from:
- user settings
- project settings
- current session
- CLI arguments
- enterprise policy
without reading raw config files.
## Design Rules To Reuse
- Keep a single canonical ordered list of settings sources.
- Treat policy and CLI overlays as special sources, not just more files.
- Use managed base files plus drop-ins for admin control.
- Allow policy to lock specific customization surfaces to trusted sources only.
- Make source origin visible in UI and diagnostics.
references/settings-validation-and-safe-runtime-application.md
# Settings Validation And Safe Runtime Application
## Table Of Contents
- [Parse And Validate At The Boundary](#parse-and-validate-at-the-boundary)
- [Preserve Files, Skip Bad Fragments](#preserve-files-skip-bad-fragments)
- [Safe Environment Controls](#safe-environment-controls)
- [Runtime Application Path](#runtime-application-path)
- [Validation Schema Design](#validation-schema-design)
- [Change Notification](#change-notification)
- [Design Rules To Reuse](#design-rules-to-reuse)
## Parse And Validate At The Boundary
The settings layer uses:
- JSON parsing
- schema validation
- targeted filtering for invalid permission rules
- helpful error formatting with paths and suggestions
This is a strong pattern for agent runtimes:
- parse once
- validate once
- convert to a normalized typed structure
- keep runtime code away from raw config blobs
## Preserve Files, Skip Bad Fragments
`parseSettingsFile()` plus `filterInvalidPermissionRules()` show a useful compromise:
- invalid files can remain on disk
- invalid fragments are ignored when safe
- warnings explain what was skipped
Why this matters:
- one malformed rule should not brick the whole runtime
- users need actionable diagnostics
- the runtime should remain usable while the config is fixed
## Safe Environment Controls
`managedEnvConstants.ts` splits env handling into:
- provider-managed env vars that settings must not override
- dangerous shell-related settings
- safe env vars that can be applied before trust dialogs
This is worth copying directly as a control model:
- host-owned provider routing should not be replaceable by user settings
- secrets, endpoints, proxies, and shell helpers need stricter treatment
- only a whitelist of low-risk env vars should auto-apply under managed policy
## Runtime Application Path
`applySettingsChange()` is the key reference.
It:
- reloads settings from disk
- reloads permission rules
- refreshes hook snapshots
- re-derives permission context
- strips unsafe permissions again where needed
- transitions plan or auto mode state
- updates app state in one place
Reusable rule:
- apply settings changes through one central runtime function
- recompute derived state
- avoid scattered ad hoc listeners mutating independent subsystems
## Validation Schema Design
`utils/settings/types.ts` shows several useful patterns:
- backward-compatible schema evolution guidance
- explicit permission schema
- exact-one-of validation for allowlists and denylists
- optional fields for future growth
- exported schema URL for machine-readable tooling
This is stronger than informal config docs because:
- editors can validate settings automatically
- migration rules stay close to the schema
- admin and user tooling can reuse the same contract
## Change Notification
The settings comments indicate:
- caches are reset before listeners run
- both interactive and headless paths use the same application logic
Keep that pattern:
- filesystem or remote notifications should invalidate caches first
- listeners should always read fresh settings
- UI and SDK modes should not have divergent settings-application paths
## Design Rules To Reuse
- Validate at the settings boundary, not deep inside runtime code.
- Preserve files but ignore bad fragments when safe.
- Use env whitelists and provider-managed stripping for dangerous configuration.
- Recompute derived runtime state through one application path.
- Publish a schema and keep backward compatibility explicit.
SKILL.md
---
name: ai-coding-agents-settings-policy
description: "Designs settings and policy layers for coding-agent runtimes. Use when modeling source precedence, managed policy, env controls, or runtime settings validation."
compatibility: Portable core. Works on Claude Code and Codex.
version: "1.1"
last_validated: 2026-07-11
---
# AI Coding Agents Settings Policy
Use this skill to design or review the settings and policy layer of a coding-agent runtime: settings sources, precedence, managed policy, validation, safe environment handling, and runtime application of settings changes.
This skill owns configuration and policy architecture for coding-agent runtimes. For project memory in `AGENTS.md` or `CLAUDE.md`, use [`../agents-memory/SKILL.md`](../agents-memory/SKILL.md).
## ASCII Flow
```text
settings sources
defaults + managed policy + user + project + local + flags + env
|
v
precedence merge
deterministic order + source provenance + plugin-only restrictions
|
v
validation
schema + forbidden keys + unsafe env + unknown options
|
v
runtime application
provider policy + tools + permissions + plugins + UI + cache invalidation
|
v
audit record
effective settings without secrets
```
## Quick Reference
| Question | Read | Outcome |
|----------|------|---------|
| How should settings sources and policy precedence work? | [`references/settings-source-precedence-and-managed-policy.md`](references/settings-source-precedence-and-managed-policy.md) | Source model, merge order, managed policy, plugin-only restrictions |
| How should settings be validated and applied safely? | [`references/settings-validation-and-safe-runtime-application.md`](references/settings-validation-and-safe-runtime-application.md) | Schema validation, invalid-rule handling, safe env controls, runtime re-application |
| What are the exact override examples for each settings layer? | [`references/settings-precedence-table.md`](references/settings-precedence-table.md) | Full precedence table: managed > CLI flags > local > project > user, with concrete examples and cache invalidation |
| Which sources can enable plugin surfaces? | [`references/plugin-only-restriction-recipes.md`](references/plugin-only-restriction-recipes.md) | Trust-class recipes for locking, scoping, and logging plugin-surface activations |
| How is ToolSearch gated by settings policy? | [`references/deferred-tool-policy-layer.md`](references/deferred-tool-policy-layer.md) | Settings keys, policy decision flow, and interaction with tool-pool assembly |
| How does OpenAI Codex separate config layers from managed requirements? | [`references/openai-codex-managed-config-and-requirements.md`](references/openai-codex-managed-config-and-requirements.md) | Layer stack, requirements constraints, managed-hooks-only mode, debug surfaces |
## When To Use
- Design a settings system for a coding-agent CLI or runtime
- Separate user, project, local, flag, and managed policy sources
- Review how managed settings should override user configuration
- Define which environment variables or customization surfaces are safe to accept
- Apply runtime settings changes without restarting the whole process
## Use Other Skills
| Need | Use Instead |
|------|-------------|
| Broader coding-agent architecture | [`../ai-coding-agents/SKILL.md`](../ai-coding-agents/SKILL.md) |
| Tool approval and permission modes | [`../ai-coding-agents-permissions/SKILL.md`](../ai-coding-agents-permissions/SKILL.md) |
| Plugin package architecture | [`../ai-coding-agents-plugins/SKILL.md`](../ai-coding-agents-plugins/SKILL.md) |
| AGENTS.md or CLAUDE.md repo memory | [`../agents-memory/SKILL.md`](../agents-memory/SKILL.md) |
| Generic config or schema design outside agent runtimes | [`../software-devtools/SKILL.md`](../software-devtools/SKILL.md) |
## Default Workflow
1. **Define source layers.** Separate user, shared project, local gitignored, CLI-flag, and managed-policy sources.
2. **Freeze precedence.** Document one merge order and keep it consistent across disk reads, UI display, and runtime application.
3. **Make managed policy authoritative.** Policy layers should override user-controlled settings and bypass local customization where required.
4. **Validate at the boundary.** Parse, coerce, and validate settings before they mutate runtime state.
5. **Fail soft on invalid fragments.** Preserve the file on disk, but ignore invalid pieces when safe to do so.
6. **Protect dangerous env and customization surfaces.** Whitelist what can be applied automatically; strip or gate anything that could redirect providers or execute shell code.
7. **Separate cache tiers.** Keep merged settings, per-source reads, and parsed-file caches distinct so invalidation is targeted and explainable.
8. **Apply changes through one runtime path.** Re-read settings, snapshot hooks or dependent callbacks, reload dependent subsystems, and update app state through a single function.
9. **Test hostile cases.** Cover malformed JSON, invalid permission rules, drop-in conflicts, managed overrides, and live settings-change notifications.
## Host Rules
- Keep source precedence explicit and stable.
- Always include managed-policy and CLI-flag sources even when users restrict editable sources.
- Distinguish editable sources from read-only policy and flag overlays.
- Prefer schema validation plus targeted filtering over all-or-nothing rejection when individual rules are bad.
- Keep dangerous environment settings and shell-like helper values behind explicit trust gates.
- Apply settings changes by recomputing derived runtime state, not by mutating scattered subsystems in place.
- Treat plugin-only customization policy as source-aware. Not every settings source is equally trusted to enable plugin surfaces.
- Sanitize persisted permission rules after reload before they become active state.
## Build Order
1. Define settings sources and immutable precedence.
2. Define the canonical merged schema and validation path.
3. Add managed-policy and CLI-flag overlays.
4. Add dangerous-surface filtering for env and helper values.
5. Split merged, per-source, and parsed-file caches.
6. Implement one runtime re-application path.
7. Add reload sequencing for caches, hook snapshots, plugins, and UI state.
## Core Invariants
- Precedence must be identical in disk reads, UI views, and runtime application.
- Managed policy overrides user settings even when users can edit local files.
- Invalid fragments should not corrupt valid configuration.
- Dangerous customization surfaces require explicit trust rules.
- Runtime state should be recomputed from merged settings, not patched piecemeal.
- Cache invalidation must respect which layer changed: parsed file, source layer, or merged result.
## Failure Modes
- Different parts of the runtime seeing different precedence orders.
- Managed drop-ins loading after user config but before flags in one code path and not another.
- Invalid permission rules poisoning the whole settings load.
- Reload side effects applying out of order across cache resets, hooks, and app state.
- Plugin-only restrictions differing by source because trust class was lost during merge.
- Safe-looking env overrides redirecting providers or shell helpers unexpectedly.
## Minimal Viable Version
- One source-precedence table.
- One merged schema and validation pass.
- One managed-policy overlay.
- One filter for dangerous env or helper surfaces.
- One source-aware trust rule for plugin-only or privileged customization.
- One central function that reapplies derived runtime state after settings change.
## What Strong Implementations Add
- Read-only versus editable source distinctions in UI and runtime.
- Plugin-only customization restrictions.
- Fine-grained invalid-fragment filtering instead of all-or-nothing rejection.
- Distinct caches for parsed files, per-source layers, and merged effective settings.
- Explicit sequencing for hook snapshots, cache resets, and state reapplication.
- Auditability explaining which source won for any effective value.
## Known Traps
- Letting different subsystems invent their own precedence rules and ending up with settings that disagree between runtime, UI, and policy enforcement.
- Treating environment overrides as harmless configuration even when they bypass managed policy or expand the trust boundary.
- Reapplying one changed field in place instead of rebuilding the derived state that depends on source layering, hooks, and permissions.
- Using one shared cache for parse results, merged settings, and source-specific state, which makes invalidation unreliable.
- Hiding policy wins from the effective-settings view and making debugging impossible for operators.
## Common Anti-Patterns
- Letting every subsystem define its own precedence order.
- Treating flags, policy, and user files as one merge layer.
- Reapplying only the changed field instead of rebuilding derived state.
- Using one undifferentiated settings cache for parse results, source layers, and merged output.
- Accepting arbitrary env overrides because they are “just config.”
- Hiding managed-policy wins from the effective settings view.
## Claude Code: Precedence, Managed Policy, and Cleanup (2026, web-verified)
Verified against `code.claude.com/docs/en/settings` and `code.claude.com/docs/en/hooks` on 2026-07-11. The Claude Code precedence order is the reference implementation for the `settings-precedence-table.md` model in this skill:
```
managed (highest, cannot be overridden)
> CLI flags (session-only)
> .claude/settings.local.json (gitignored; allow rules apply without a trust dialog)
> .claude/settings.json (project, checked in; requires the trust dialog)
> ~/.claude/settings.json (user, lowest)
```
The most common audit mistake is inverting the last three: local is the **most** specific and highest-precedence editable file, not the least. A clean `~/.claude/settings.json` changes nothing in a repo that carries a conflicting `.claude/settings.local.json`.
`cleanupPeriodDays` (any editable scope; default `30`, minimum `1`) controls how many days of session files and other application data Claude Code deletes at startup. Setting it to `0` is a validation error, not "disable cleanup." When auditing a fleet for stale-session accumulation or unexpected data loss, check this key before assuming a bug — 30 days is the shipped default and a lower value elsewhere is a deliberate override, not drift.
### Managed-settings-only keys
These keys are honored **only** when set in managed/enterprise policy; the same key in user or project settings is silently ignored. This is the strongest lockdown class — verify this distinction before telling an operator "just set X in your project settings," because for this table that advice is wrong.
| Key | Type | Effect |
|-----|------|--------|
| `allowManagedPermissionRulesOnly` | boolean | User/project permission rules (`allow`/`ask`/`deny`) are ignored; only managed-settings rules apply |
| `allowManagedHooksOnly` | boolean | Only managed hooks, SDK hooks, and plugin hooks force-enabled via managed `enabledPlugins` load; user/project/other-plugin hooks are blocked |
| `allowManagedMcpServersOnly` | boolean | Only the managed `allowedMcpServers` allowlist is respected; `deniedMcpServers` still merges from all sources on top of it |
| `claudeMd` | string | Organization-managed CLAUDE.md-style instructions injected as memory; ignored if set anywhere other than managed/policy settings |
| `disableSideloadFlags` | boolean | Rejects `--plugin-dir`, `--plugin-url`, `--agents`, and `--mcp-config` CLI flags at startup — closes the loophole where a user could otherwise sideload past `strictKnownMarketplaces` for a single run |
| `forceRemoteSettingsRefresh` | boolean | Blocks CLI startup until remote managed settings are freshly fetched; the CLI exits on fetch failure rather than falling back to a cached or absent policy |
| `blockedMarketplaces` | array | Blocklist enforced on marketplace add and on every plugin install/update/refresh/auto-update, so a marketplace added before the policy existed still loses access |
| `forceLoginMethod` | `"claudeai"` \| `"console"` \| `"gateway"` | Restricts login to one account class |
| `forceLoginOrgUUID` | string or array of UUIDs | Requires login to a specific Anthropic organization (or any of a listed set) |
| `allowAllClaudeAiMcps` | boolean | Loads claude.ai connectors alongside a deployed `managed-mcp.json`, which otherwise takes exclusive control and suppresses them |
| `allowedChannelPlugins` / `deniedMcpServers` | array | Allow/deny lists for channel plugins and MCP servers respectively; the deny list always wins over the allow list, even against managed servers |
### Keys that exist in all scopes but are commonly set via managed policy
Do not describe these as "managed-only" — a user or project file can set them too; policy is simply the layer that makes the choice non-negotiable for that user or repo.
| Key | Type | Effect |
|-----|------|--------|
| `disableAutoMode` | `"disable"` | Prevents auto permission mode from being activated; removed from the Shift+Tab cycle and rejects `--permission-mode auto` at startup |
| `disableAgentView` | boolean | Turns off background agents and agent view (`claude agents`, `--bg`, `/background`) |
| `disableBundledSkills` | boolean | Removes bundled skills/workflows entirely; `/init`-style built-in commands stay typable but hidden from the model |
| `autoUpdatesChannel` | `"latest"` (default) \| `"stable"` | Release channel; `"stable"` trails by about a week and skips versions with major regressions. There is no `"disabled"` channel value — to stop auto-updates entirely, set the `DISABLE_AUTOUPDATER` env var instead |
A prior version of this skill listed `strictPluginOnlyCustomization`, `policyHelper`, and `parentSettingsBehavior` as managed-policy keys. None of the three could be verified against current docs on re-check (2026-07-11) — they have been removed rather than carried forward as unverified claims. If a runtime you are auditing needs "all customization must come through plugins" or "policy context injected into the system prompt" behavior, treat it as a custom control you are designing, not an existing Claude Code key, until you find it in current docs yourself.
### Hook events relevant to settings and policy
| Event | Trigger | Use Case |
|-------|---------|----------|
| `ConfigChange` | Fires when a configuration file changes during a session, covering user, project, local, and managed settings | Audit trail, policy-compliance checks, external notification on settings drift |
| `MessageDisplay` | Fires while assistant message text is displayed | Display-only — no blocking or decision control; can replace displayed text via `hookSpecificOutput.displayContent` but never changes the transcript or what the model sees. Do not use it for enforcement; use `PreToolUse`/`PermissionRequest` for that. |
Both events follow the standard hook lifecycle alongside `PreToolUse`, `PostToolUse`, `SessionStart`, and `SessionEnd`, and are available in user, project, and managed-policy hook sources.
## OpenAI Codex: Config Home, File Layering, and Approval-Mode TOML Key
Re-verified against `learn.chatgpt.com/docs/config-file/config-reference` on 2026-07-11. This supersedes an earlier version of this section that was pinned to a May 2026 source snapshot and had drifted on two points — see corrections below.
### CODEX_HOME and Config File Location
`$CODEX_HOME` is the base directory for all Codex user state, defaulting to `~/.codex/` (`%USERPROFILE%\.codex\` on Windows). The primary user config file is `$CODEX_HOME/config.toml`.
Operators can relocate all user state by setting `$CODEX_HOME` — no config file edits required. Managed/enterprise configuration and CLI flags sit above this in precedence and are not affected by `$CODEX_HOME`.
### Config Layering (named profiles) — corrected
Named profiles are **separate files next to `config.toml`**, not a `[profiles.NAME]` table inside it: `$CODEX_HOME/<profile-name>.config.toml`, selected at launch with `--profile <profile-name>`. A prior version of this skill showed `[profiles.strict]` / `[profiles.ci]` tables inside one `config.toml` — that syntax is not current and should not be used as a template.
```toml
# $CODEX_HOME/ci.config.toml
approval_policy = "never"
[sandbox_workspace_write]
network_access = false
```
The active profile is selected via `--profile ci`, letting one Codex install serve multiple use-case contexts (interactive dev, CI, enterprise review) without one file trying to hold every mode.
### Approval-Mode TOML Key — corrected value
```toml
# ~/.codex/config.toml
approval_policy = "on-request" # default; see AskForApproval enum in ai-coding-agents-permissions
```
Verified valid values (2026-07-11): `"untrusted"`, `"on-request"`, `"never"`, or a granular object (sub-keys for `sandbox_approval`, `rules`, `mcp_elicitations`, `request_permissions`, `skill_approval`). `"on-failure"` still appears but is deprecated. **`"unless-trusted"` is not a valid value** — a prior version of this skill used that spelling; if you find it in an older Codex install or a stale example elsewhere, treat it as the pre-rename form of `"untrusted"` and do not propagate the old spelling into new configs.
### Precedence, including project scope
Current documented order (highest first): managed/enterprise configuration > CLI flags > project-scoped `.codex/config.toml` > profile file (via `--profile`) > user-level `$CODEX_HOME/config.toml`. A fixed set of security-critical keys — provider selection, authentication, notification routing, telemetry routing, and the approval/sandbox trio (`approval_policy`, `sandbox_mode`, `sandbox_workspace_write`) — are deliberately ignored if set in project-scoped config, so a compromised or careless repo cannot loosen its own sandbox. Treat "which keys project config is allowed to touch" as a threat-modeling question, not an oversight, when designing an equivalent for another CLI.
## Cross-Platform Patterns (Goose)
Goose introduces two settings-layer patterns beyond the standard user/project/flag/managed-policy stack: **custom distros as policy-delivery mechanism**, and **`.goosehints`-style narrative project hints** as a distinct source class.
### Custom distros as a read-only policy source
Goose's Custom Distributions (see `ai-coding-agents-release-distribution`) bake an allowlist, provider pinning, and branding into the binary itself. From the settings layer's perspective, this is a *new source class*: read-only, above CLI flags in precedence, immutable at runtime.
- **Pattern:** add `distro_policy` to the source-precedence table as the highest immutable layer (above `managed_policy`, which can be updated without a binary change). Effective-settings UI must attribute wins to the distro layer explicitly so operators can explain "why can't I change this."
- **Anti-pattern:** encoding enterprise restrictions through managed-policy files shipped alongside the open-source binary. Users can rename, move, or delete those files; distro policy cannot be bypassed without swapping the binary.
- **Recipe:** add a distro source class with a frozen manifest, rendered in `--version` output and visible in the effective-settings debug view. Managed-policy layers can still narrow further but cannot broaden beyond the distro envelope.
### `.goosehints` — narrative project-hint layer
Goose loads `.goosehints` as a per-project narrative file (similar to `AGENTS.md` / `CLAUDE.md`). This is *not* structured settings — it is unstructured guidance for the agent about the project. But it is a source layer in the sense that the agent consumes it deterministically at session start.
- **Pattern:** model project-narrative hints as a distinct source class, separate from typed settings. It has its own trust model (user-editable), its own precedence (always loaded, low priority), and its own invalidation (file-watch triggers re-read).
- **Anti-pattern:** treating narrative hints as "just settings" and applying schema validation to prose. Or treating them as entirely separate and duplicating source-precedence logic for them.
- **Recipe:** the settings layer acknowledges project-narrative as a source with explicit lifecycle hooks (read, invalidate, merge into agent context). Concrete formats — `AGENTS.md`, `CLAUDE.md`, `.goosehints`, `.cursorrules` — are implementations of that source class. The `agents-memory` skill owns the content side; the settings-policy layer owns the source-loader plumbing.
## Navigation
### References
- [`references/settings-source-precedence-and-managed-policy.md`](references/settings-source-precedence-and-managed-policy.md) — Source layering, managed policy, and customization restrictions
- [`references/settings-validation-and-safe-runtime-application.md`](references/settings-validation-and-safe-runtime-application.md) — Validation, safe env handling, and runtime application
- [`references/settings-precedence-table.md`](references/settings-precedence-table.md) — Concrete precedence table with override examples (managed > CLI flags > local > project > user)
- [`references/plugin-only-restriction-recipes.md`](references/plugin-only-restriction-recipes.md) — Trust-class enforcement for plugin-surface activations
- [`references/deferred-tool-policy-layer.md`](references/deferred-tool-policy-layer.md) — How ToolSearch trigger is settings-gated
- [`references/openai-codex-managed-config-and-requirements.md`](references/openai-codex-managed-config-and-requirements.md) — OpenAI Codex config layer stack, managed requirements, requirements-only settings, and policy debug surfaces
### Data
- [`data/sources.json`](data/sources.json) — Primary documentation and implementation references for settings and policy guidance
### Related Skills
- [`../ai-coding-agents-permissions/SKILL.md`](../ai-coding-agents-permissions/SKILL.md) — Permission modes and rule handling
- [`../ai-coding-agents-plugins/SKILL.md`](../ai-coding-agents-plugins/SKILL.md) — Plugin-only customization and extension policy
- [`../agents-memory/SKILL.md`](../agents-memory/SKILL.md) — Project instruction files rather than runtime settings
## Fact-Checking
- Known bugs, regressions, framework/compiler/runtime footguns, and version-specific crash or workaround guidance must be verified against current primary web sources before being treated as current fact.
- These patterns are grounded in a local April 2026 `claude_code` source snapshot. Re-check upstream code or docs before relying on volatile runtime details.
- Managed policy, safe-env rules, and live settings application are implementation-specific. Preserve the control model, but verify the exact field names and trust semantics in the target runtime.
## Learnings Loop
Before applying this skill on a non-trivial task, read `learnings.consolidated.md` in this directory (and `learnings.md` if present).
After applying it, if you encountered a pattern worth remembering, a mistake worth preventing, or a domain fact that surprised you, append one dated bullet to `learnings.md` via `agents-skills-feedback-loop/scripts/append_learning.py`. Do not modify `SKILL.md` itself.