Catalog of every configuration axis in this repo, env vars, commit markers, frontmatter keys, QA skip verdicts, and escape hatches, each with its enforcement point and abuse story, plus the checklist for adding a new flag safely. Use when you say `what does a skip flag do`, `list escape hatches`, `can I skip this gate`, `add a config flag`. Do NOT use for hook runtime behavior (use `agent-harness-reference`) or change gating policy (use `ai-agents-change-control`).
Every flag, marker, and skip semantic in this repo, verified against code as
of 2026-07-03. Hook registration surfaces were rechecked on 2026-09-01. Each
escape hatch exists because a gate sometimes misfires; each one also has an
abuse story or a guard. Before you set any of these, read its row. The house
rule (learned in session 1187, see the Removed Flags section): escape hatches
get teeth or get abused.
Related skills: ai-agents-change-control owns when a bypass is allowed; agent-harness-reference and ai-agents-architecture-contract own what the hooks themselves do; ai-agents-debugging-playbook owns triaging a gate that fired on you.
Triggers
what does the skip-drift-check marker do
list escape hatches
can I skip this gate
add a config flag
is this skip marker allowed
Process
Identify the axis type: env var, commit marker, text directive, frontmatter key, file, or verdict string.
Find its row in the tables below. Read the effect AND the guard/abuse column before using it.
If you are about to use an escape hatch, confirm the legitimate trigger condition holds (for example, a workflow actually unrunnable under act). Bypassing because a gate is slow or annoying is the session 1187 failure mode.
Re-verify the flag in its defining validator with the one-liner in Provenance. Documentation can drift after validator changes.
Adding a new flag? Follow the checklist in "How to Add a New Flag" and add a row plus a re-verify one-liner to this catalog.
Environment Variables: LSP Gate (retired)
The LSP-first runtime gate and its three environment escapes (SKIP_LSP_GATE, LSP_GATE_MODE, LSP_DOWN) were retired in #3216 when ADR-062 was amended to keep LSP-first navigation as static steering only. No environment variable governs it now; the guidance lives in .claude/rules/lsp-first.md. Nothing to set, nothing to escape.
Git Hook Configuration
Lefthook is the sole Git hook manager. lefthook.yml declares the events,
filters, named jobs, and validator commands. Do not infer a bypass from a
deleted custom payload. Inspect the named validator's current interface before
using or documenting an environment variable.
Removed and Stale Flags (do not use, do not reintroduce)
Name
Status as of 2026-07-03
Story
SKIP_PREPUSH
REMOVED
Historical: abused 3x within hours of creation (session 1187, retro .agents/retrospective/2026-02-08-session-1187-skip-prepush-abuse.md)
SKIP_TESTS
REMOVED
Removed from the pre-push hook during the Lefthook migration, but it outlived that as the env default for pre_pr.py --skip-tests, which was parsed and never read; flag, env default, and the --verbose beside it were deleted once no gate set skip_flag
Lesson encoded: a global bypass with no teeth (no telemetry, no approval step) will be reached for under pressure. New escape hatches must be narrow, announced in output, and observable.
Test session injects commit.gpgsign=false with command-line precedence so test repos never invoke the user's signing setup
Production test infra
Only sets index 0 when GIT_CONFIG_COUNT is unset, so an outer process's config is not clobbered
tests/conftest.py:35-38
SERENA_PROJECT_ROOT
env var (user-set)
Formerly overrode automatic Serena project root detection in the worktree scope guard. Set to the absolute path of the intended worktree when switching projects
Retired hook (ADR-097)
Was validated: path had to contain .serena/project.yml or the override was ignored; blocked writes when unset and CLAUDE_PROJECT_DIR was absent. invoke_serena_worktree_scope_guard.py is deleted, so nothing reads this variable today
historical: .claude/hooks/PreToolUse/invoke_serena_worktree_scope_guard.py:68-73 (path no longer exists)
PEP 668 / uv
environment reality
Bare pip fails (externally managed env). Everything goes through uv sync --frozen --extra dev; skill scripts need uv run python, not python3 (PyYAML lives in the venv)
Production
ModuleNotFoundError: No module named 'yaml' means you used the wrong interpreter
pyproject.toml, .python-version (3.14.6)
pytest markers unit, integration, safe_push_transport, security, smoke, windows_path
pytest -m selectors
Filter test classes; smoke = real-CLI tests needing auth/credits, nightly only, and the smoke gate asserts they were NOT skipped (issue #2231 item 4); safe_push_transport = touches a non-local transport, excluded from pre-push
Production
Marking a test smoke to dodge CI is detected by the not-skipped assertion
pyproject.toml [tool.pytest.ini_options].markers
SKIP_PUSH_LOCK_COMMIT_GUARD
env var (=1 exact match)
Bypasses the push-lock-commit-guard pre-commit job (issue #5123), which otherwise refuses a commit while a push for the current branch name holds the canonical push-lock file
Production escape hatch
Scoped to one check, not global. Prints push-lock: commit guard bypassed via SKIP_PUSH_LOCK_COMMIT_GUARD=1 so the bypass is visible in commit output; any other value (0, true, unset) leaves the guard enforced
scripts/validation/check_push_lock_before_commit.py:71,177-180; wired as push-lock-commit-guard in lefthook.yml
AI_AGENTS_PYTEST_FULL_SUITE_LOCALLY
env var (1 or unset; blank counts as unset)
Controls what python-tests runs pre-push. Unset: the import graph narrows the diff, and where it cannot, the fallback collects instead of executing, so a broken import and a syntax error still block the push and everything else is CI's. 1: import-graph selection is skipped entirely and every partition executes locally, announced on stderr. Any other value raises rather than quietly doing less
Production opt-in
Scoped to one job, not a bypass: it makes the gate stricter, never weaker, so there is no abuse direction. Validated before a path is chosen, so true is reported whichever way selection would have gone, and the valid value acts on both paths rather than only on the fallback
scripts/validation/git_hook_policy.py, constant PYTEST_FULL_SUITE_LOCALLY_ENV (name), _validated_full_suite_opt_in and the short-circuit in _resolve_pytest_commands (semantics). Cited by symbol: two earlier line numbers here went stale within a day
AI_AGENTS_PYTEST_WORKERS
env var (positive integer or auto; blank counts as unset)
Overrides the xdist worker count pre-push pytest runs with. Unset or blank: the policy picks the count. Any other value it cannot parse raises rather than falling back to a default the caller did not ask for
Production
Same fail-loud contract as the flag above and validated at the same point, after a version that checked it only inside the executing partitions let an invalid value pass unreported on the collection path
Copy .env.example to .env. Keys as of 2026-07-03: ANTHROPIC_API_KEY (MCP servers, skill-learning hook LLM fallback, exact token counts), PERPLEXITY_API_KEY, TAVILY_API_KEY, CONTEXT7_API_KEY, YDC_API_KEY, and optional COMPRESS_TOKENIZER (tiktoken/anthropic/heuristic, default tiktoken). Everything degrades gracefully when absent per ADR-007; missing keys disable the corresponding MCP or fallback path, they do not break the repo. Note: COMPRESS_TOKENIZER appears only in .env.example; a code consumer was not located in this audit, treat as possibly vestigial.
Commit Markers
Name
Type
Effect
Status
Guard / abuse story
Where defined
[skip-drift-check]
commit message marker
Skips the agent drift detection CI gate for the whole PR (marker in ANY commit subject/body counts)
Production escape hatch with obligations
The bypass job posts a checklist that a human must satisfy: reason documented in PR description, templates/README.md updated with the intentional difference, explicit code-owner approval. Marker alone is NOT approval
Session-end QA can be skipped only with one of these exact verdict strings in the session log, and only when the staged files qualify.
Verdict
When legitimate
Enforcement
SKIPPED: investigation-only
Every staged file matches the allowlist: .agents/sessions/, .agents/analysis/, .agents/retrospective/, .serena/memories/, .agents/security/, .agents/memory/ (incl. episodes/), .agents/architecture/REVIEW-*, .agents/critique/
Single source of truth scripts/modules/investigation_allowlist.py; pre-check via scripts/validation/test_investigation_eligibility.py; CI backstop .github/scripts/validate_investigation_claims.py (advisory, confirmed: exits 0 unconditionally per its own docstring and main())
SKIPPED: docs-only
All changes are markdown and strictly editorial: spelling, grammar, formatting; no code, config, tests, workflows, or code-block changes
The enforcing source is scripts/validate_session_json.py (validate_qa_skip_scope, dispatch table at lines 166-169) plus CONTRIBUTING.md:695-699. Pre-check via scripts/validation/test_docs_only_eligibility.py
Mixed sessions do not qualify; split the commit. Claiming investigation-only with a code file staged is exactly what the CI backstop exists to catch.
Plugin Manifest Version Prohibition
Not a flag, but the config obligation most often tripped over. No packaged
plugin manifest may carry a version field, and neither may a marketplace
entry. Claude Code resolves freshness from the first version it finds, so a
committed version pins consumers to a hand-bumped string instead of the git
commit SHA (ADR-092, which supersedes ADR-079; issue #4080).
File
Must not carry
.claude/.claude-plugin/plugin.json
version
src/claude/.claude-plugin/plugin.json
version
src/copilot-cli/.claude-plugin/plugin.json
version
.claude-plugin/marketplace.json (per entry)
version
.github/plugin/marketplace.json (per entry)
version
Enforced locally by the pre-pr-validation job in lefthook.yml, which runs
scripts/validation/pre_pr.py; that runner invokes
build/scripts/validate_plugin_version_bump.py. CI also enforces it through
.github/workflows/validate-plugin-version-bump.yml, which fires on a plugin
source dir or a marketplace file. A content change needs no manifest edit at
all.
Hook Registration Surfaces
Two independent registration sources serve different consumers. Do not force
parity between them. A third, .github/hooks/require-subagent-model.json, was
retired by ADR-097 along with every tool-call hook:
Surface
Consumer
Shape re-verified 2026-09-01
.claude/settings.json
Claude Code direct in this repository
4 events, 7 groups
.claude/hooks/hooks.json
Vendored plugin source for both harness packages
0 events, 0 groups
.github/hooks/require-subagent-model.json
retired (ADR-097)
deleted; was Copilot CLI in this repository, native preToolUse, matcher task, direct registration
The Copilot generator reads .claude/hooks/hooks.json, not local settings. A
one-file registration is valid only when its consumer scope is deliberate.
Check both sources in any hook PR and document repository-only versus vendored
intent.
No PermissionRequest policy hook is registered. Test runners execute
repository-controlled code, so command-name matching is not a safe approval boundary.
How to Add a New Flag
Define it in exactly one module (single source of truth). Name it for its scope: SKIP_ACTIONLINT not SKIP_CHECKS.
Document semantics including the failure mode: what happens when unset, when the guarded thing is broken, and whether the consumer fails open or closed. There is no neutral default for a missing signal (FM-10, PR #1965).
Make every activation observable: print a WARN/SKIP line, or emit EVENT= telemetry if you also ship a consumer for it (the previous guard-maturity consumer was retired under ADR-084, issue #5154, and nothing reads that schema today). A silent bypass is the session 1187 pattern.
Scope it to one check. Global bypasses (SKIP_PREPUSH) are banned by precedent.
If the flag lives under .claude/, regenerate the src/copilot-cli/ mirror. Do not touch plugin.json; it carries no version.
Add a row to this catalog and a re-verify one-liner to Provenance below.
Route the change through ai-agents-change-control classification (a new escape hatch is never docs-only).
Anti-Patterns
Anti-pattern
Why it fails
Do instead
Reintroducing a global bypass
Session 1187: abused 3x in hours; user verdict "You can't be trusted"
Narrow, announced, per-check escapes
[skip-drift-check] without the checklist
Marker skips the CI job but the bypass job posts unmet obligations; reviewers will bounce it
Document reason, update templates/README.md, get code-owner approval
Documenting a flag only in CONTRIBUTING.md
Docs drift previously left removed flags in active guidance
The defining script is the source of truth; docs quote it (FM-9)
Editing .claude/lib/hook_utilities/ to change flag behavior
That tree is a generated mirror; next sync_plugin_lib.py run reverts you
Edit scripts/hook_utilities/, run the sync
Claiming SKIPPED: investigation-only with code staged
CI backstop diffs staged files against the allowlist
Split the commit or run QA
Verification
The flag you plan to use still exists: run its Provenance one-liner and get a non-empty hit in the defining file (not only in docs).
You used the narrowest applicable escape (per-check env var, line directive over file directive).
The bypass is visible in output (WARN/SKIP line, bypass job summary, or validator notice), not silent.
If you added a flag: tests cover honored/absent/bad-value, and this catalog has the new row. No manifest bump even when the flag lives in a packaged tree: the manifests carry no version (ADR-092).
Provenance and Maintenance
Audited 2026-09-01 against the working tree for hook registration surfaces.
Other rows remain verified as of 2026-07-03. Sources: files and line numbers
cited per row above. Line numbers drift; the commands below are the durable
re-verification. Run from repo root. If a command returns nothing, the flag
moved or died: update this catalog before relying on it.
uv run --frozen python -c "import json; s=json.load(open('.claude/settings.json'))['hooks']; print({k: len(v) for k, v in s.items()})"
repository-local Copilot sub-agent gate retired
test ! -e .github/hooks/require-subagent-model.json && echo retired (ADR-097 deleted this surface)
removed flags absent from CONTRIBUTING
grep -n -e "SKIP_PREPUSH" -e "SKIP_TESTS" CONTRIBUTING.md (expect no matches)
SKIP_TESTS no longer defaults a flag
grep -n "SKIP_TESTS" scripts/validation/pre_pr.py (expect no matches; pre_pr_sequence.py still names --skip-tests in its _Gate docstring, on purpose, to say why skip_flag is gone)
COMPRESS_TOKENIZER consumer not located; verify before documenting it as live.
1---2name: ai-agents-config-catalog3description: Catalog of every configuration axis in this repo, env vars, commit markers, frontmatter keys, QA skip verdicts, and escape hatches, each with its enforcement point and abuse story, plus the checklist for adding a new flag safely. Use when you say `what does a skip flag do`, `list escape hatches`, `can I skip this gate`, `add a config flag`. Do NOT use for hook runtime behavior (use `agent-harness-reference`) or change gating policy (use `ai-agents-change-control`).4license: MIT5---67# AI Agents Config Catalog89<!-- vendor-portability: contributor-facing knowledge pack for the rjmurillo/ai-agents repo itself; intentionally references upstream paths (.agents/, .claude/, scripts/, build/, scripts/validation/check_push_lock_before_commit.py, scripts/validation/git_hook_policy.py) because its audience is repo contributors, not plugin consumers (issue #2050) -->10Every flag, marker, and skip semantic in this repo, verified against code as11of 2026-07-03. Hook registration surfaces were rechecked on 2026-09-01. Each12escape hatch exists because a gate sometimes misfires; each one also has an13abuse story or a guard. Before you set any of these, read its row. The house14rule (learned in session 1187, see the Removed Flags section): escape hatches15get teeth or get abused.1617Related skills: `ai-agents-change-control` owns when a bypass is allowed; `agent-harness-reference` and `ai-agents-architecture-contract` own what the hooks themselves do; `ai-agents-debugging-playbook` owns triaging a gate that fired on you.1819## Triggers2021- `what does the skip-drift-check marker do`22- `list escape hatches`23- `can I skip this gate`24- `add a config flag`25- `is this skip marker allowed`2627## Process28291. Identify the axis type: env var, commit marker, text directive, frontmatter key, file, or verdict string.302. Find its row in the tables below. Read the effect AND the guard/abuse column before using it.313. If you are about to use an escape hatch, confirm the legitimate trigger condition holds (for example, a workflow actually unrunnable under act). Bypassing because a gate is slow or annoying is the session 1187 failure mode.324. Re-verify the flag in its defining validator with the one-liner in Provenance. Documentation can drift after validator changes.335. Adding a new flag? Follow the checklist in "How to Add a New Flag" and add a row plus a re-verify one-liner to this catalog.3435## Environment Variables: LSP Gate (retired)3637The LSP-first runtime gate and its three environment escapes (`SKIP_LSP_GATE`, `LSP_GATE_MODE`, `LSP_DOWN`) were retired in #3216 when ADR-062 was amended to keep LSP-first navigation as static steering only. No environment variable governs it now; the guidance lives in `.claude/rules/lsp-first.md`. Nothing to set, nothing to escape.3839## Git Hook Configuration4041Lefthook is the sole Git hook manager. `lefthook.yml` declares the events,42filters, named jobs, and validator commands. Do not infer a bypass from a43deleted custom payload. Inspect the named validator's current interface before44using or documenting an environment variable.4546## Removed and Stale Flags (do not use, do not reintroduce)4748| Name | Status as of 2026-07-03 | Story |49|---|---|---|50| `SKIP_PREPUSH` | REMOVED | Historical: abused 3x within hours of creation (session 1187, retro `.agents/retrospective/2026-02-08-session-1187-skip-prepush-abuse.md`) |51| `SKIP_TESTS` | REMOVED | Removed from the pre-push hook during the Lefthook migration, but it outlived that as the env default for `pre_pr.py --skip-tests`, which was parsed and never read; flag, env default, and the `--verbose` beside it were deleted once no gate set `skip_flag` |5253Lesson encoded: a global bypass with no teeth (no telemetry, no approval step) will be reached for under pressure. New escape hatches must be narrow, announced in output, and observable.5455## Environment Variables: Tests and Tooling5657| Name | Type | Effect | Status | Guard / abuse story | Where defined |58|---|---|---|---|---|---|59| `GIT_CONFIG_COUNT` + `GIT_CONFIG_KEY_0`/`GIT_CONFIG_VALUE_0` | env vars (set by conftest) | Test session injects `commit.gpgsign=false` with command-line precedence so test repos never invoke the user's signing setup | Production test infra | Only sets index 0 when `GIT_CONFIG_COUNT` is unset, so an outer process's config is not clobbered | `tests/conftest.py:35-38` |60| `SERENA_PROJECT_ROOT` | env var (user-set) | Formerly overrode automatic Serena project root detection in the worktree scope guard. Set to the absolute path of the intended worktree when switching projects | Retired hook (ADR-097) | Was validated: path had to contain `.serena/project.yml` or the override was ignored; blocked writes when unset and `CLAUDE_PROJECT_DIR` was absent. `invoke_serena_worktree_scope_guard.py` is deleted, so nothing reads this variable today | historical: `.claude/hooks/PreToolUse/invoke_serena_worktree_scope_guard.py:68-73` (path no longer exists) |61| PEP 668 / uv | environment reality | Bare `pip` fails (externally managed env). Everything goes through `uv sync --frozen --extra dev`; skill scripts need `uv run python`, not `python3` (PyYAML lives in the venv) | Production | `ModuleNotFoundError: No module named 'yaml'` means you used the wrong interpreter | `pyproject.toml`, `.python-version` (3.14.6) |62| pytest markers `unit`, `integration`, `safe_push_transport`, `security`, `smoke`, `windows_path` | pytest -m selectors | Filter test classes; `smoke` = real-CLI tests needing auth/credits, nightly only, and the smoke gate asserts they were NOT skipped (issue #2231 item 4); `safe_push_transport` = touches a non-local transport, excluded from pre-push | Production | Marking a test `smoke` to dodge CI is detected by the not-skipped assertion | `pyproject.toml [tool.pytest.ini_options].markers` |63| `SKIP_PUSH_LOCK_COMMIT_GUARD` | env var (`=1` exact match) | Bypasses the `push-lock-commit-guard` pre-commit job (issue #5123), which otherwise refuses a commit while a push for the current branch name holds the canonical push-lock file | Production escape hatch | Scoped to one check, not global. Prints `push-lock: commit guard bypassed via SKIP_PUSH_LOCK_COMMIT_GUARD=1` so the bypass is visible in commit output; any other value (`0`, `true`, unset) leaves the guard enforced | `scripts/validation/check_push_lock_before_commit.py:71,177-180`; wired as `push-lock-commit-guard` in `lefthook.yml` |64| `AI_AGENTS_PYTEST_FULL_SUITE_LOCALLY` | env var (`1` or unset; blank counts as unset) | Controls what `python-tests` runs pre-push. Unset: the import graph narrows the diff, and where it cannot, the fallback collects instead of executing, so a broken import and a syntax error still block the push and everything else is CI's. `1`: import-graph selection is skipped entirely and every partition executes locally, announced on stderr. Any other value raises rather than quietly doing less | Production opt-in | Scoped to one job, not a bypass: it makes the gate stricter, never weaker, so there is no abuse direction. Validated before a path is chosen, so `true` is reported whichever way selection would have gone, and the valid value acts on both paths rather than only on the fallback | `scripts/validation/git_hook_policy.py`, constant `PYTEST_FULL_SUITE_LOCALLY_ENV` (name), `_validated_full_suite_opt_in` and the short-circuit in `_resolve_pytest_commands` (semantics). Cited by symbol: two earlier line numbers here went stale within a day |65| `AI_AGENTS_PYTEST_WORKERS` | env var (positive integer or `auto`; blank counts as unset) | Overrides the xdist worker count pre-push pytest runs with. Unset or blank: the policy picks the count. Any other value it cannot parse raises rather than falling back to a default the caller did not ask for | Production | Same fail-loud contract as the flag above and validated at the same point, after a version that checked it only inside the executing partitions let an invalid value pass unreported on the collection path | `scripts/validation/git_hook_policy.py`, constant `PYTEST_WORKERS_ENV` (name), `_pytest_parallel_flags` (semantics) |6667## .env Keys (from .env.example)6869Copy `.env.example` to `.env`. Keys as of 2026-07-03: `ANTHROPIC_API_KEY` (MCP servers, skill-learning hook LLM fallback, exact token counts), `PERPLEXITY_API_KEY`, `TAVILY_API_KEY`, `CONTEXT7_API_KEY`, `YDC_API_KEY`, and optional `COMPRESS_TOKENIZER` (tiktoken/anthropic/heuristic, default tiktoken). Everything degrades gracefully when absent per ADR-007; missing keys disable the corresponding MCP or fallback path, they do not break the repo. Note: `COMPRESS_TOKENIZER` appears only in `.env.example`; a code consumer was not located in this audit, treat as possibly vestigial.7071## Commit Markers7273| Name | Type | Effect | Status | Guard / abuse story | Where defined |74|---|---|---|---|---|---|75| `[skip-drift-check]` | commit message marker | Skips the agent drift detection CI gate for the whole PR (marker in ANY commit subject/body counts) | Production escape hatch with obligations | The bypass job posts a checklist that a human must satisfy: reason documented in PR description, `templates/README.md` updated with the intentional difference, explicit code-owner approval. Marker alone is NOT approval | `.github/workflows/agent-drift-detection.yml:65-69` (detection), `:297-320` (obligations); `CONTRIBUTING.md:499` |7677## Text Directives (orphan-ref-validator)7879| Name | Type | Effect | Status | Guard / abuse story | Where defined |80|---|---|---|---|---|---|81| `orphan-ref-ignore` HTML comment | line directive | Mutes orphan-reference findings for that one line | Production | Rare in tree, easy to grep; compensating control is human review of any commit adding one (DESIGN-009) | `.claude/skills/orphan-ref-validator/scripts/patterns.py:63` |82| `orphan-ref-ignore-file` HTML comment | file directive | Mutes the whole file, but ONLY if the directive appears within the first 50 lines | Production | The 50-line rule prevents burying the mute at the bottom of a long doc | `patterns.py:89`; 50-line window at `scan.py:234` |8384Write the directives as HTML comments (`<!-- ... -->`); shown bare here so this catalog does not mute itself.8586## Frontmatter Keys (skills)8788| Name | Type | Effect | Status | Guard / abuse story | Where defined |89|---|---|---|---|---|---|90| `size-exception: true` | SKILL.md frontmatter | Exempts the file from the 500-line block in the size validator | Escape hatch, must be justified | `taste-lints` only honors it in the first 20 lines of the file; validator prints "size-exception declared" so reviewers see it | `scripts/validation/skill_size.py:107,251`; `.claude/skills/taste-lints/scripts/taste_lints.py:449` |91| `allowed-tools`, `argument-hint` | frontmatter keys | Harness metadata; recognized (not flagged as prose refs) by the orphan-ref scanner | Production | Whitelist lives in one place | `.claude/skills/orphan-ref-validator/scripts/filters.py:31` |9293## QA Skip Verdicts (ADR-034)9495Session-end QA can be skipped only with one of these exact verdict strings in the session log, and only when the staged files qualify.9697| Verdict | When legitimate | Enforcement |98|---|---|---|99| `SKIPPED: investigation-only` | Every staged file matches the allowlist: `.agents/sessions/`, `.agents/analysis/`, `.agents/retrospective/`, `.serena/memories/`, `.agents/security/`, `.agents/memory/` (incl. `episodes/`), `.agents/architecture/REVIEW-*`, `.agents/critique/` | Single source of truth `scripts/modules/investigation_allowlist.py`; pre-check via `scripts/validation/test_investigation_eligibility.py`; CI backstop `.github/scripts/validate_investigation_claims.py` (advisory, confirmed: exits 0 unconditionally per its own docstring and `main()`) |100| `SKIPPED: docs-only` | All changes are markdown and strictly editorial: spelling, grammar, formatting; no code, config, tests, workflows, or code-block changes | The enforcing source is `scripts/validate_session_json.py` (`validate_qa_skip_scope`, dispatch table at lines 166-169) plus `CONTRIBUTING.md:695-699`. Pre-check via `scripts/validation/test_docs_only_eligibility.py` |101102Mixed sessions do not qualify; split the commit. Claiming investigation-only with a code file staged is exactly what the CI backstop exists to catch.103104## Plugin Manifest Version Prohibition105106Not a flag, but the config obligation most often tripped over. No packaged107plugin manifest may carry a `version` field, and neither may a marketplace108entry. Claude Code resolves freshness from the first version it finds, so a109committed version pins consumers to a hand-bumped string instead of the git110commit SHA (ADR-092, which supersedes ADR-079; issue #4080).111112| File | Must not carry |113|---|---|114| `.claude/.claude-plugin/plugin.json` | `version` |115| `src/claude/.claude-plugin/plugin.json` | `version` |116| `src/copilot-cli/.claude-plugin/plugin.json` | `version` |117| `.claude-plugin/marketplace.json` (per entry) | `version` |118| `.github/plugin/marketplace.json` (per entry) | `version` |119120Enforced locally by the `pre-pr-validation` job in `lefthook.yml`, which runs121`scripts/validation/pre_pr.py`; that runner invokes122`build/scripts/validate_plugin_version_bump.py`. CI also enforces it through123`.github/workflows/validate-plugin-version-bump.yml`, which fires on a plugin124source dir or a marketplace file. A content change needs no manifest edit at125all.126127## Hook Registration Surfaces128129Two independent registration sources serve different consumers. Do not force130parity between them. A third, `.github/hooks/require-subagent-model.json`, was131retired by ADR-097 along with every tool-call hook:132133| Surface | Consumer | Shape re-verified 2026-09-01 |134|---|---|---|135| `.claude/settings.json` | Claude Code direct in this repository | 4 events, 7 groups |136| `.claude/hooks/hooks.json` | Vendored plugin source for both harness packages | 0 events, 0 groups |137| `.github/hooks/require-subagent-model.json` | retired (ADR-097) | deleted; was Copilot CLI in this repository, native `preToolUse`, matcher `task`, direct registration |138139The Copilot generator reads `.claude/hooks/hooks.json`, not local settings. A140one-file registration is valid only when its consumer scope is deliberate.141Check both sources in any hook PR and document repository-only versus vendored142intent.143No `PermissionRequest` policy hook is registered. Test runners execute144repository-controlled code, so command-name matching is not a safe approval boundary.145146## How to Add a New Flag147148- [ ] Define it in exactly one module (single source of truth). Name it for its scope: `SKIP_ACTIONLINT` not `SKIP_CHECKS`.149- [ ] Document semantics including the failure mode: what happens when unset, when the guarded thing is broken, and whether the consumer fails open or closed. There is no neutral default for a missing signal (FM-10, PR #1965).150- [ ] Make every activation observable: print a WARN/SKIP line, or emit `EVENT=` telemetry if you also ship a consumer for it (the previous guard-maturity consumer was retired under ADR-084, issue #5154, and nothing reads that schema today). A silent bypass is the session 1187 pattern.151- [ ] Scope it to one check. Global bypasses (`SKIP_PREPUSH`) are banned by precedent.152- [ ] Add tests: positive (flag honored), negative (flag absent = enforced), edge (bad value), per TESTING-RIGOR.153- [ ] If the flag lives under `.claude/`, regenerate the `src/copilot-cli/` mirror. Do not touch plugin.json; it carries no version.154- [ ] Add a row to this catalog and a re-verify one-liner to Provenance below.155- [ ] Route the change through `ai-agents-change-control` classification (a new escape hatch is never docs-only).156157## Anti-Patterns158159| Anti-pattern | Why it fails | Do instead |160|---|---|---|161| Reintroducing a global bypass | Session 1187: abused 3x in hours; user verdict "You can't be trusted" | Narrow, announced, per-check escapes |162| `[skip-drift-check]` without the checklist | Marker skips the CI job but the bypass job posts unmet obligations; reviewers will bounce it | Document reason, update `templates/README.md`, get code-owner approval |163| Documenting a flag only in CONTRIBUTING.md | Docs drift previously left removed flags in active guidance | The defining script is the source of truth; docs quote it (FM-9) |164| Editing `.claude/lib/hook_utilities/` to change flag behavior | That tree is a generated mirror; next `sync_plugin_lib.py` run reverts you | Edit `scripts/hook_utilities/`, run the sync |165| Claiming `SKIPPED: investigation-only` with code staged | CI backstop diffs staged files against the allowlist | Split the commit or run QA |166167## Verification168169- [ ] The flag you plan to use still exists: run its Provenance one-liner and get a non-empty hit in the defining file (not only in docs).170- [ ] You used the narrowest applicable escape (per-check env var, line directive over file directive).171- [ ] The bypass is visible in output (WARN/SKIP line, bypass job summary, or validator notice), not silent.172- [ ] If you added a flag: tests cover honored/absent/bad-value, and this catalog has the new row. No manifest bump even when the flag lives in a packaged tree: the manifests carry no version (ADR-092).173174## Provenance and Maintenance175176Audited 2026-09-01 against the working tree for hook registration surfaces.177Other rows remain verified as of 2026-07-03. Sources: files and line numbers178cited per row above. Line numbers drift; the commands below are the durable179re-verification. Run from repo root. If a command returns nothing, the flag180moved or died: update this catalog before relying on it.181182| Fact | Re-verify one-liner |183|---|---|184| Git hook jobs, filters, and validators | `uv run --frozen lefthook validate` |185| [skip-drift-check] marker + obligations | `grep -n "skip-drift-check" .github/workflows/agent-drift-detection.yml` |186| size-exception | `grep -n "size-exception" scripts/validation/skill_size.py` |187| orphan-ref directives + 50-line window | `grep -n "IGNORE_DIRECTIVE_RE" .claude/skills/orphan-ref-validator/scripts/patterns.py && grep -n "splitlines()\[:50\]" .claude/skills/orphan-ref-validator/scripts/scan.py` |188| investigation allowlist | `grep -n "agents/" scripts/modules/investigation_allowlist.py` |189| docs-only verdict | `grep -n "SKIPPED: docs-only" CONTRIBUTING.md scripts/validate_session_json.py` |190| plugin version-field validator | `uv run python build/scripts/validate_plugin_version_bump.py --help` |191| no version in any manifest or marketplace entry | `uv run python build/scripts/validate_plugin_version_bump.py` |192| GIT_CONFIG_COUNT injection | `grep -n "GIT_CONFIG_COUNT" tests/conftest.py` |193| pytest markers | `grep -n -A 5 "^markers" pyproject.toml` |194| pre-push pytest opt-in and worker override | `grep -n "AI_AGENTS_PYTEST_" scripts/validation/git_hook_policy.py` |195| .env keys | `grep -n -e "API_KEY" -e "COMPRESS_TOKENIZER" .env.example` |196| hook registration surfaces | `uv run --frozen python -c "import json; s=json.load(open('.claude/settings.json'))['hooks']; print({k: len(v) for k, v in s.items()})"` |197| repository-local Copilot sub-agent gate retired | `test ! -e .github/hooks/require-subagent-model.json && echo retired` (ADR-097 deleted this surface) |198| removed flags absent from CONTRIBUTING | `grep -n -e "SKIP_PREPUSH" -e "SKIP_TESTS" CONTRIBUTING.md` (expect no matches) |199| `SKIP_TESTS` no longer defaults a flag | `grep -n "SKIP_TESTS" scripts/validation/pre_pr.py` (expect no matches; `pre_pr_sequence.py` still names `--skip-tests` in its `_Gate` docstring, on purpose, to say why `skip_flag` is gone) |200| SKIP_PUSH_LOCK_COMMIT_GUARD | `grep -n "SKIP_PUSH_LOCK_COMMIT_GUARD" scripts/validation/check_push_lock_before_commit.py lefthook.yml` |201202`COMPRESS_TOKENIZER` consumer not located; verify before documenting it as live.
Run npx skillmds@latest add rjmurillo/ai-agents-config-catalog in your terminal (requires Node.js), paste this page's agent-chat prompt into Claude, Cursor, or any MCP-connected agent, or download the SKILL.md file and copy it into your agent's skills directory.
Catalog of every configuration axis in this repo, env vars, commit markers, frontmatter keys, QA skip verdicts, and escape hatches, each with its enforcement point and abuse story, plus the checklist for adding a new flag safely. Use when you say `what does a skip flag do`, `list escape hatches`, `can I skip this gate`, `add a config flag`. Do NOT use for hook runtime behavior (use `agent-harness-reference`) or change gating policy (use `ai-agents-change-control`). It is listed under AI & ML on SkillMD.
This skill has not completed SkillMD's automated safety review yet. Capability flags: executes scripts, reads secrets. SkillMD never runs a skill's scripts for you; review the SKILL.md before installing.
This skill is tagged as working with Claude Code, Claude.ai, OpenAI Codex. SKILL.md is an open format, so most agents that read a skills directory can load it too.
Yes. Installing skills from SkillMD is free. This skill is licensed under MIT.
rjmurillo (@rjmurillo) published this skill. Their other Agent Skills are listed on their SkillMD profile.