IF THE PROJECT PATH IS /workspace/ — YOU ARE IN A CCY CONTAINER.
- NEVER run Ansible playbooks in the container
- Only edit and commit — then tell the user to deploy on their HOST system
- CCY version bump required when modifying
files/var/local/claude-yolo/claude-yolo
Full container rules and the retired ctrl+z patch: CLAUDE/ContainerRules.md
This is the #1 principle of this project. It is non-negotiable.
- Exit immediately on errors — Use
set -ein all bash scripts - No silent failures — Every error must stop execution with clear message
- NEVER skip and continue — If an operation should succeed, FAIL on error
- NEVER decouple dependent operations — If task B depends on task A, failure in A must prevent B
- ❌
failed_when: false— PROHIBITED unless annotated with# FAIL-FAST-OK: <reason> - ❌
ignore_errors: true— PROHIBITED unless annotated with# FAIL-FAST-OK: <reason> - ❌ "Skip and warn" pattern — NEVER use
debugto warn and continue - ✅ Probe-then-fail pattern —
failed_when: falseis OK when registered result is explicitly checked
This is a public repository. Never commit personal information, credentials, hardcoded paths, or sensitive data. Always use Ansible variables, placeholders, and Vault encryption.
Full security rules, vault management, and pre-commit checks: CLAUDE/SecurityRules.md
ALL system changes MUST go through Ansible playbooks. Never perform manual file copies, installations, service management, or configuration edits.
STRICT IAC — NO MANUAL FIX. DO NOT SUGGEST A MANUAL/QUICK FIX, EVEN AS A "do this now" shortcut alongside the playbook. ONLY IAC: fix it in the playbook and tell the user to run the playbook. Nothing else.
Full IaC workflow (edit → playbook → deploy → test): CLAUDE/InfrastructureAsCode.md
Never accept a missing dependency. Never paper over it. Surface it loudly and fix it via IaC.
When a script, QA gate, or workflow errors with "tool not found" (semgrep not found, pdm: command not found, which: no foo in …, etc.):
- ❌ DO NOT install the tool manually (
pipx install foo,dnf install foo,npm i -g foo). - ❌ DO NOT make the script tolerate the missing tool (skip-if-absent,
|| true, advisory-only mode). - ❌ DO NOT dismiss the gap as "pre-existing environment issue" or "unrelated to this task" — it is now your task.
- ✅ DO grep the playbooks for an existing install task — if one exists, instruct the user to re-run that play.
- ✅ DO add the dependency to the relevant playbook if it is missing, then ask the user to run that play.
The host's tool inventory is owned by Ansible. A missing tool is an IaC gap, not a runtime fallback to engineer around. This rule applies to host tools, CCY container tools, and anything else the repo depends on.
Run ./scripts/qa-all.bash before every commit touching Bash or Python files. Run ESLint for extension JavaScript.
qa-all.bash is mechanical and passes green on work that is structurally wrong. Run the qa-reviewer agent as the required final step of every plan, and to review any PR or branch diff — it catches misplaced work in the IaC graph, a new playbook that should have been an edit, jargon naming, missing version bumps, plan/docs drift, and verification that does not exercise the code path it vouches for.
Full QA reference (scripts, what they check, limitations): CLAUDE/QA.md
Push after every commit. The GitHub remote is this repository's backup; a commit that exists only in a local checkout is not saved. A lost disk, a discarded container, or a deleted worktree takes unpushed commits with it. Never end a session with a branch ahead of its remote, and never hold commits back to batch them. This is standing authorisation: no need to ask before a plain git push of the current branch. Force-pushes remain the user's call.
When providing diagnostic commands to users, always use --no-pager, | cat, or | head. Never open pagers or editors.
Full rules and examples: CLAUDE/DebugCommands.md
Scripts a human runs and is prompted by (extensionless executables and executed *.bash files under files/home/.local/bin/) MUST follow the standard UX rules: strict validation, friendly recovery — validate strictly, but on a recoverable input mistake show a clear error and re-prompt in a bounded loop instead of aborting. Reserve hard aborts for genuinely unrecoverable states. Sourced libraries and non-interactive/Ansible-invoked helpers are out of scope (those stay fully non-interactive and fail fast).
Full rules, scope, and canonical patterns: CLAUDE/InteractiveScripts.md
A script or function's stdout is its return value — the one thing a caller
would $(capture). Everything that is not that value (status, progress,
"switching…", prompts, warnings, diagnostics) goes to stderr with >&2.
Mixing chatter into stdout silently pollutes $(cmd) captures and breaks
jq/read downstream — often only on a conditional code path. This applies to
bash executables, generated bash written into user dotfiles by playbooks,
embedded shell: blocks, scripts/, and helpers/ Python (parsed marker lines
= stdout; diagnostics = stderr). Help/status/report commands whose entire job is
to print for a human are the exception — their text IS the payload.
Full rule, decision procedure, patterns, and review checklist: CLAUDE/StderrHygiene.md
Use Podman wherever possible — it is the better system. Reach for Docker only when a tool genuinely needs it for compatibility or legacy reasons, and understand that Docker is significantly less secure than Podman.
- Podman (rootless) — default for everything. CCY, devtools, ad-hoc work.
- Docker (rootful) — compatibility mode only, e.g. DDEV.
dockergroup = root-equivalent. - LXC (rootful) — VM-like full-system containers with systemd inside.
New playbooks needing a container engine must use the container_engine variable (default podman), not hardcode an engine.
Full role split, coexistence, and FAQ: CLAUDE/ContainerEngines.md
- YAGNI — Don't add features until actually needed. No speculative code. Delete unused code.
- DRY — Extract common patterns. Use variables for repeated values. Reference, don't duplicate.
- Idempotent — All operations safe to run multiple times. Use
creates, conditionals, declarative state. - Security First — Never hardcode secrets. Validate inputs. Least privilege. No credentials in logs.
- Self-documenting — Clear names over comments. Comments explain WHY, not what.
Full Ansible style rules (playbook structure, markers, packages, services, variables, tasks): CLAUDE/AnsibleStyle.md
Never let plan state lag behind the work it tracks. When code work completes, advances, or invalidates a plan task, the corresponding plan file changes must be committed too — ideally in the same commit as the code, but at minimum within the same session.
This rule exists to prevent drift between plan state and code state. It is not a restriction on when plans can be committed.
- Committing a brand-new plan on its own, with no related code yet — fine and recommended so the plan is tracked immediately
- Committing plan research, decision-gate notes, or status updates on their own
- Committing plan progress in the same commit as the code that implements it (preferred when both change in one session)
- Committing code that completes plan tasks while leaving the plan file unchanged on disk
- Leaving an untracked
CLAUDE/Plan/NNN-…/directory after committing related work - Marking tasks ✅ in conversation but not in the plan file
- Bundling unrelated plan edits with unrelated code changes — split them into separate commits
git status # Look for untracked CLAUDE/Plan/ dirs and unstaged plan edits
git add CLAUDE/Plan/NNN-description/ # Stage plan alongside related codeIf git status shows plan files modified by your session, decide before committing: stage them with the related code, or make a separate plan-only commit. Do not leave them dangling.
The topic files below are linked, not @-imported: an @-import is re-inlined into
every session whether or not it is relevant, while a link costs nothing until followed.
So that nothing is missed by not being resident, .claude/rules/*.md carry paths: globs
and load on demand whenever a matching file is touched. Each rule is a thin pointer to
the topic file that owns the fact — never a copy.
| Touching… | Loads | Which points at |
|---|---|---|
playbooks/, tasks/, vars/, environment/, any *.yml |
ansible-editing.md |
AnsibleStyle, InfrastructureAsCode, helpers |
any *.bash, scripts/, files/**/bin/ |
bash-scripts.md |
StderrHygiene, InteractiveScripts, QA |
files/var/local/claude-yolo/ |
ccy-version-bump.md |
ContainerRules — the mandatory version bump |
helpers/, tests/helpers/, any *.py |
python-helpers.md |
helpers/CLAUDE.md, the ruff pin |
environment/, vault.bash, *_vars/ |
secrets-and-vault.md |
SecurityRules, ExampleValues |
extensions/, gnome-shell dirs |
gnome-extensions.md |
GnomeShell, the ESLint requirement |
scripts/qa-*.bash, ruff.toml, workflows |
qa-gates.md |
QA.md, and how these gates have failed before |
When you add a fact that only matters for certain paths, add it to the rule, not here.
CLAUDE.md is resident in every session; the rules are not.
Each of these is the single source of truth for its subject. Follow the row you need.
| File | Content |
|---|---|
| ContainerRules.md | CCY container detection, version bump, retired ctrl+z patch |
| ContainerEngines.md | Podman/Docker/LXC role split; when to use which; security trade-offs |
| InfrastructureAsCode.md | Ansible-only workflow, prohibited manual actions |
| AnsibleStyle.md | Playbook structure, markers, packages, services, variables |
| SecurityRules.md | Public repo warning, vault management, pre-commit checks |
| ExampleValues.md | Reserved example IPs/emails/hostnames the secret scanner whitelists |
| QA.md | QA scripts reference, what to run when |
| DebugCommands.md | Non-interactive command rules for user diagnostics |
| InteractiveScripts.md | Human-friendly interactive script rules (validate strictly, retry on loop) |
| StderrHygiene.md | Diagnostics → stderr; stdout is the captured payload (bash + Python) |
| GnomeShell.md | GNOME Shell extension development (Wayland, ESLint, APIs) |
| PlanWorkflow.md | Planning workflow and plan document structure |
| PlanTriage.md | How to establish facts: plan-local triage scripts (probes go IN the script) |
| PlanJournalling.md | JOURNAL/ entry grammar and the append-only discipline |
| PlanScriptStandards.md | Plan-folder orchestrator rules (R1–R14) and _planlib.inc.bash |
| AgentNotes.md | Working practices and project gotchas (feedback + project knowledge) |
User-facing docs (installation, architecture, playbooks, troubleshooting) are in docs/. See docs/README.md for the full index.
This project uses claude-code-hooks-daemon for automated safety and workflow enforcement.
After editing .claude/hooks-daemon.yaml — restart the daemon using the hooks-daemon skill:
- Restart: use the
hooks-daemonskill with argsrestart - Health check: use the
hooks-daemonskill with argshealth
Important:
/hooks-daemonis a skill (slash command), not a bash command. Invoke it using the Skill tool, e.g.Skill(skill="hooks-daemon", args="restart"). Do NOT attempt to run/hooks-daemonas a bash command — it will fail.
Key files:
.claude/hooks-daemon.yaml— handler configuration (enable/disable handlers).claude/hooks/handlers/— project-specific custom handlers
Documentation: .claude/hooks-daemon/CLAUDE/LLM-INSTALL.md
The handlers listed below are active in this project. Read this section to avoid triggering unnecessary blocks.
When a tool is blocked by a handler, do not stop working. Read the block reason, modify your approach, and continue with your task.
A file written through Bash is not seen by the content guards that run BEFORE the write. The PreToolUse handlers below that inspect what a file CONTAINS, or where it lives, key on the Write and Edit tools — so a >, >>, tee or a cat <<EOF heredoc reaches disk unexamined by them: no block, no advisory, no record. A Bash write that drew no complaint is NOT a write that passed those checks — use Write/Edit for file content and they apply.
The LINTERS are the exception, and they DENY. lint_on_edit and validate_eslint_on_write do run on a file a Bash command AUTHORS — a redirect, tee, a heredoc — so unparseable Python or failing TypeScript is reported however it reached disk. The write has already landed, so the denial is a failure report to repair with Edit, not a rollback. A file the command merely RELOCATES (cp, mv, install, dd) is never linted: those bytes were already on disk, so blaming the copy would report a defect the command did not introduce.
The handlers that judge a Bash COMMAND — destructive git, sed, pipes, permissions, curl | sh — are unaffected and still cover you.
Full detail on any rule: bin/hooks-daemon explain-rule <ID>.
| ID | Blocked | Why | Fix |
|---|---|---|---|
| R-ABSOLUTE-PATH-REQUIRED | Read/Write/Edit file_path requires absolute path |
Ambiguous about the current working directory and can target the wrong file | Use an absolute path starting with / |
| R-GIT-MERGE-SQUASH | git merge --squash |
Severs ancestry -- git branch -d refuses the branch forever | Use git merge --no-ff instead |
| R-GH-PR-MERGE-SQUASH | gh pr merge --squash |
Severs ancestry -- git branch -d refuses the branch forever | Use gh pr merge --merge instead |
| R-GH-PR-MERGE-REBASE | gh pr merge --rebase |
Severs ancestry -- git branch -d refuses the branch forever | Use gh pr merge --merge instead |
| R-ASK-USER-QUESTION-UNJUSTIFIED | AskUserQuestion without ASKING BECAUSE: prefix |
Asking pauses the session for a question the daemon cannot verify was necessary | State the assumed answer in output text and proceed, or retry every question prefixed ASKING BECAUSE: <reason> |
| R-BASH-SAFE-MODE-PRELUDE-MISSING | a sequenced Bash invocation with no set safety prelude |
Errors in earlier statements can be silently ignored | Add set -euo pipefail at the top, or gate explicitly with &&/` |
| R-COMMENT-CHANGELOG | changelog narrative in a code comment | A comment describes CURRENT STATE; history belongs elsewhere | Move it to git, a changelog file, or the plan's JOURNAL/ |
| R-COMMENT-SIZE | a comment growing past its configured size limit | Comments should describe current state, not accumulate | Shorten the comment, or declare MUST_EXCEED_COMMENT_SIZE_BECAUSE |
| R-CURL-PIPE-SHELL | `curl | wget ... | bash |
| R-DAEMON-DIR-CD | cd into .claude/hooks-daemon/ |
Daemon CLI commands must be run from PROJECT ROOT, causing path confusion otherwise | Run daemon commands from project root, e.g. bin/hooks-daemon status |
| R-GIT-RESET-HARD | git reset --hard |
Permanently destroys all uncommitted changes | Ask the user to run it manually |
| R-GIT-CLEAN-FORCE | git clean -f |
Permanently deletes untracked files | Ask the user to run it manually |
| R-GIT-CHECKOUT-DISCARD | git checkout -- <file> / git checkout . |
Discards local changes to file(s) permanently | Ask the user to run it manually |
| R-GIT-RESTORE | git restore <file> |
Discards local changes to files permanently (--staged/-S is allowed) |
Ask the user to run it manually |
| R-GIT-STASH-DROP | git stash drop |
Permanently destroys a stashed change | Ask the user to run it manually |
| R-GIT-STASH-CLEAR | git stash clear |
Permanently destroys all stashed changes | Ask the user to run it manually |
| R-GIT-PUSH-FORCE | git push --force |
Can overwrite remote history and destroy team members' work | Ask the user to run it manually, or coordinate and use --force-with-lease |
| R-GIT-BRANCH-FORCE-DELETE | git branch -D |
Force-deletes a branch without checking if it has been merged | Use git branch -d first (refuses unmerged branches); ask the user for -D |
| R-GIT-COMMIT-AMEND | git commit --amend |
Rewrites the previous commit, creating messy history and potential data loss | Create a new commit instead |
| R-DOCS-QA-COMMIT | a git commit violates a block-level docs QA staged-tree check | Most doc rot that matters at commit time is cross-file drift a single-file edit hook cannot see | Fix the content per each finding's remediation below and amend the commit |
| R-DOCS-QA-EDIT | a documentation Write/Edit violates a block-level docs QA check | A finding only denies the write when it is BLOCK severity AND the resolved mode for that check is block | Fix the content per each finding's remediation below and retry |
| R-ERROR-HIDING | an error-hiding pattern (bare except, | true, empty catch, _ = err, ...) | |
| R-FLAGGABLE-CONTENT-CHANNEL | a content-revealing git/grep command shape over a flaggable path | It would reveal flaggable content inside routine command output, with no deliberate Read at all | Delegate the WHOLE review to the quarantine subagent instead |
| R-GH-ISSUE-VIEW-NO-COMMENTS | gh issue view without --comments |
Issue comments contain critical context, clarifications and updates not in the issue body | Add --comments, or include comments in --json fields |
| R-GH-PR-VIEW-NO-COMMENTS | gh pr view without --comments |
PR comments contain review feedback and discussion context not in the PR body | Add --comments, or include comments in --json fields |
| R-GIT-MESSAGE-BACKTICK | an unescaped backtick in a double-quoted git commit/tag message | Bash performs command substitution inside double quotes -- the span is EXECUTED, not quoted | Use single quotes, or git commit -F |
| R-GIT-STASH-PUSH | git stash / git stash push / git stash save |
Stashes get forgotten, lost, and block git pull | Use git commit instead — WIP commits are fine |
| R-GH-AUTO-CLOSE-KEYWORD | a GitHub closing keyword + issue reference in a git/gh message | Auto-closes the referenced issue/PR the moment the commit reaches the default branch, and cannot be disabled repository-side | Use a non-closing reference instead, e.g. Addresses #123 |
| R-LOCK-FILE-EDIT | Direct Write/Edit of a package manager lock file |
Lock files are generated artifacts; manual edits create checksum mismatches and broken dependency graphs | Use the package manager commands instead (e.g. npm install, cargo update) |
| R-LSP-SYMBOL-LOOKUP | a symbol-like Grep/Bash grep lookup | LSP tools give semantic ~50ms code intelligence; grep is slow and imprecise | Use goToDefinition/findReferences/workspaceSymbol/hover/documentSymbol instead |
| R-PIP-BREAK-SYSTEM-PACKAGES | pip install --break-system-packages |
Bypasses PEP 668 protection and can corrupt the system Python installation | Use a virtual environment or pip install --user instead |
| R-PLAN-NUMBER-DISCOVERY | a bash discovery scan (ls/find/sort+tail) for the next plan number | Misses subdirectories like Completed/ and disagrees across branches | Use the printed next plan number, or the git counter directly |
| R-PLAN-FOLDER-MKDIR | mkdir <plan-dir>/NNNNN-name (hand-creating a plan folder) |
Claims a plan number the moment the folder appears, but nothing records the claim until PLAN.md is written | Use the mkplan.bash scaffolder instead |
| R-PLAN-QA-COMMIT | a git commit violates a block-level plan QA cross-file invariant | Most plan rot is cross-file and a single-file edit hook cannot see it | Amend the commit to also stage what each finding's remediation names below |
| R-PLAN-QA-EDIT | a PLAN.md/README.md Write/Edit violates a block-level plan QA check | Plan QA linting catches issues you can fix immediately, before they reach commit | Fix the content per each finding's remediation below and retry |
| R-PLAN-TIME-ESTIMATE | Time estimates not allowed in plan documents | Time estimates in plans create false expectations and pressure | Break work into concrete tasks and implementation steps; let the user decide scheduling |
| R-QA-SUPPRESSION | a QA suppression directive (noqa, type: ignore, eslint-disable, ...) | Suppression comments hide real problems and create technical debt | Fix the underlying issue; do not suppress the warning |
| R-ROOT-RECURSION-CATASTROPHIC | grep -r/find/rg/... rooted at /, /proc, /sys, /home, /root, ~, $HOME |
Walks the entire filesystem and can pin every CPU core for hours | Scope the search to the project (e.g. rg -l "pattern" .) |
| R-SEC-CODE-INJECTION | eval, exec, new Function, __import__, instance_eval, yaml.load |
Dynamic execution of a string as code | Avoid dynamic code execution; use safe parsing/import alternatives |
| R-SEC-CMD-INJECTION | os.system, subprocess(..., shell=True), shell_exec, proc_open, Runtime.exec, Process.Start, IO.popen |
Shell command construction from untrusted input enables command injection | Use argument-list APIs (no shell=True) instead of shell string concatenation |
| R-SEC-DESERIALISATION | pickle.load, Marshal.load, unserialize, ObjectInputStream, XMLDecoder, BinaryFormatter |
Deserialising untrusted data can execute arbitrary code | Use a safe serialisation format (e.g. JSON) instead |
| R-SEC-XSS | innerHTML, dangerouslySetInnerHTML, document.write, template.HTML/JS/URL |
Injects unescaped content into the DOM/output, enabling XSS | Use the framework's safe templating/escaping APIs |
| R-SEC-HARDCODED-CREDS | AWS access keys, GitHub tokens, Stripe keys, private key blocks | Hardcoded credentials leak via source control history and code review | Use environment variables, never hardcode credentials |
| R-SEC-UNSAFE-MEMORY | Rust from_raw_parts, transmute |
Bypasses Rust's memory/type safety guarantees | Use safe conversions (as, From/Into) or validated slice operations |
| R-STAGED-LINT-FAILURE | a staged file fails the cheap syntax check at commit time | lint_on_edit only ever runs at Write/Edit time, so a git add of pre-existing content skips it entirely | Fix the failing file(s) above and re-stage before committing |
| R-SUDO-PIP-INSTALL | sudo pip install |
Conflicts with the OS package manager and can corrupt system Python | Use a virtual environment or pip install --user instead |
| R-INSTRUCTION-IMPLEMENTATION-LOG | implementation logs (e.g. 'created the file X', 'added the class Y') | Instruction files hold permanent instructions, not a log of past edits | Remove the log sentence; put implementation history in git or a plan JOURNAL/ |
| R-INSTRUCTION-STATUS-INDICATOR | status indicators (e.g. checkmark + 'Complete', 'Done', 'Success', 'Fixed') | A completion emoji records a moment in time, not a permanent fact | Remove the status marker; instruction files describe the project, not its history |
| R-INSTRUCTION-TIMESTAMP | timestamps (ISO dates such as 2024-03-15) | A dated entry is a log line, and instruction files are not a log | Remove the date; if it is genuinely load-bearing, put it in git history |
| R-INSTRUCTION-LLM-SUMMARY | LLM summaries (section headings such as '## Summary', '## Key Points', '## Overview') | A summary heading is the shape an LLM's own turn-report takes, not project documentation | Remove the heading and fold any durable content into the surrounding instructions |
| R-INSTRUCTION-TEST-OUTPUT | test output counts (e.g. '42 tests passed', '1 test failed') | A test run's result is a point-in-time fact, not a stable instruction | Remove the count; CI already reports this on every run |
| R-INSTRUCTION-FILE-LISTING | changelog-style file listings (e.g. 'created src/Service/Foo.php') | A file path preceded by a past-tense action verb is changelog narrative | Remove the log line; a bare path reference used as documentation stays allowed |
| R-INSTRUCTION-CHANGE-SUMMARY | change summaries (e.g. 'Added 15 lines', 'Removed 8 lines') | A line-count delta describes one diff, not a stable instruction | Remove the summary; the diff itself is preserved in git |
| R-INSTRUCTION-COMPLETION-INDICATOR | completion indicators (e.g. 'ALL DONE!', 'Task complete!', 'Finished task') | A completion phrase announces a session's end, not a fact about the project | Remove the phrase; instruction files should never celebrate finishing a task |
| R-VERIFICATION-RESULT-NOT-CONSUMED | a verifier followed by a mutator with nothing consuming the result | The verifier can fail and the mutator would still run | Gate with &&, an explicit exit-code check, or set -euo pipefail |
| R-ARTIFACT-PUBLISH | publishing an artefact via the Artifact tool |
The page lives OUTSIDE the project and the repository cannot audit or retract it | Write the file locally and tell the user its path, or ask a human to publish |
| R-CHMOD-WORLD-WRITABLE | chmod 777/chmod a+w/chmod o+w |
Allows anyone to read, write, and execute, bypassing all file permission security | Use least-privilege permissions instead (755/644/600) |
| R-MARKDOWN-WRONG-LOCATION | MARKDOWN FILE IN WRONG LOCATION — a new .md file written to an unrecognised location |
Markdown files must follow project organization rules | Move it into an allowed location, or configure extra_allowed_markdown_paths |
| R-MARKDOWN-UNTRACKED-MEMORY | UNTRACKED CLAUDE MEMORY IS DISABLED FOR THIS PROJECT — a write to ~/.claude/projects/*/memory/*.md |
That knowledge is per-checkout, un-reviewed, and invisible to teammates — it drifts from the repo and bypasses code review | Document it in tracked project docs instead (CLAUDE.md, .claude/rules/*.md, docs/) |
| R-MARKDOWN-PLAN-SYNC | a .claude/settings.json plansDirectory out of sync with the daemon's plan_workflow config |
Plan workflow requires plansDirectory to match daemon config to redirect writes correctly | Fix .claude/settings.json's plansDirectory key, then restart your session |
| R-NPM-PIPED-COMMAND | a piped npm run/npx command |
Piping npm/npx commands is pointless — llm: cache files hold the full data | Run the plain command, then query the cache file with jq |
| R-NPM-NON-LLM-COMMAND | a raw npm run/npx command when llm: wrappers exist |
llm: commands provide LLM-friendly, machine-readable output | Use the project's npm run llm:* equivalent instead |
| R-PIPE-TO-TAIL | ` | tail` | Truncates output and causes information loss |
| R-PIPE-TO-HEAD | ` | head` | Truncates output and causes information loss |
| R-QUARANTINE-ARTEFACT-READ | reading a quarantined *-opus-security-DETAIL* artefact into the coordinator |
A DETAIL artefact holds raw flaggable substance meant for a human or another quarantine agent only | Read the paired *-opus-security-SUMMARY* artefact instead |
| R-SECRET-READ | Read/Write/Edit/NotebookEdit/Grep targeting a protected path | The file's contents must NEVER be read into context by any route — not Read, not Bash, not an interpreter one-liner, not a copy | Use bin/hooks-daemon secret-meta <path> for metadata, or ask the user |
| R-SECRET-BASH-MENTION | a Bash command whose text mentions a protected path | The file's contents must NEVER be read into context by any route — not Read, not Bash, not an interpreter one-liner, not a copy | Use bin/hooks-daemon secret-meta <path> for metadata, or ask the user |
| R-SECRET-SCRIPT-AUTHOR | a script authored via Write/Edit whose content references a protected path | The file's contents must NEVER be read into context by any route — not Read, not Bash, not an interpreter one-liner, not a copy | Use bin/hooks-daemon secret-meta <path> for metadata, or ask the user |
| R-SED-FILE-MODIFICATION | sed |
Claude gets sed syntax wrong regularly and a single error can destroy hundreds of files | Use the Edit tool (or parallel Haiku agents with Edit for bulk changes) |
| R-SENSITIVE-PUBLIC-PATTERN | content matching a configured public pattern | The pattern is a named, safe-to-disclose signal (a path, a placeholder, profanity, ...) | Remove or replace the matched text before retrying |
| R-SENSITIVE-SECRET-TERM | content matching a configured blocked term | A gitignored secret word list term was found in this write | Ask the user what the cited entry covers, then remove the matching text |
| R-TDD-TEST-FIRST | creating a production source file without its test file | TDD requires the test file to exist before the source file | Create the test file first (RED), then the source file (GREEN) |
| R-WORKTREE-FILE-COPY | cp/mv/rsync between a worktree and the main repo |
Defeats worktree isolation, bypasses git tracking, and can nuke untracked work in the target directory | cd into the worktree, commit, then git merge back |
| R-WRITE-CLOBBER | Write to an existing file you have not read this session |
You cannot know what you are destroying, so you could not report the loss even afterwards | Read the file then retry, or use Edit for a targeted change |
| R-LINT-FAILURE | a written/authored file that fails its language's lint check | The write has already landed on disk; this is a failure report, not a rollback | Fix the reported problems with Edit — do not re-Write the file from scratch |
| R-ESLINT-ERRORS | a written/authored TS/TSX file with reported ESLint errors | The write has already landed on disk; this is a failure report, not a rollback | Fix the reported problems with Edit (npx eslint <file> --fix clears most) |
| R-ESLINT-TIMEOUT | an ESLint run that did not finish within the configured timeout | This handler DENIES on a timeout — unlike lint_on_edit, which allows | Investigate why ESLint is slow (config, project size); retry the edit |
| R-ESLINT-RUN-FAILURE | an ESLint invocation that failed to run at all | ESLint could not be launched (exception raised invoking it) | Check the ESLint wrapper/tsx setup, then retry the edit |
| R-FAILSAFE-CRON-SUPPRESSED | A delivered failsafe-cron tick, while a 'blocked only on human input' marker is live | Every tick against a session blocked only on human input is a guaranteed no-op model turn | Nothing to do -- this is expected. Send a real message to clear the marker and resume ticks |
| R-STOP-QA-FAILURE | Stopping while the last QA tool run's own output indicated failure | QA failures detected in the last QA tool run | Fix the failures, re-run the QA tool, and continue without stopping |
| R-STOP-TAUTOLOGICAL-QUESTION | Stopping behind a rhetorical continue/confirmation question | The answer is obvious -- yes, continue the already-planned work now | Resume the next unit of work immediately; STOPPING BECAUSE: does not exempt this |
| R-STOP-AFTER-TOOL-ERROR | Stopping right after an unresolved tool_use_error | The correct action is to address the cause and retry, not stop | Address the tool_use_error's cause (e.g. Read before Edit/Write) and retry |
| R-STOP-CONFIRMATION-QUESTION | Stopping to ask an obvious confirmation question | The daemon auto-continues through confirmation-style questions | Proceed with the remaining work; stop with STOPPING BECAUSE: only if truly stuck |
| R-STOP-NO-REASON | Stopping without a STOPPING BECAUSE: explanation | The stop hook enforces intentional stops | Prefix your stop message with STOPPING BECAUSE: , or keep working |
| R-STOP-GOAL-LEDGER | Stopping while ledgered plan(s) are still In Progress | The daemon-side goal ledger owes a goal for EVERY In Progress plan, not only the newest /goal condition | Continue the listed plan(s), or stop with STOPPING BECAUSE: naming why each cannot proceed |
One line each; these fire with their own guidance when relevant. Full text: bin/hooks-daemon explain-handler <name>.
- daemon_restart_verifier — restart the daemon before committing
- flaggable_work_advisor — delegate flaggable work BEFORE reading it
- agent_isolation_advisor — isolate concurrent agents
- dispatch_declaration — declare where a subagent's reports go
- plan_workflow — PLAN.md, supporting docs and JOURNAL/ obey DIFFERENT contracts
- system_paths — do not edit deployed system files directly
- ansible_enforcement — no direct system management commands
- background_process_tracker — backgrounded processes are tracked
- command_hints — advisory reminders after specific commands
- git_hooks_executable_fixer — auto-fixes non-executable git hooks
- markdown_table_formatter — markdown tables are auto-aligned
- budget_exhaustion_detector — hidden agent budgets are surfaced
- goal_injection — plan-start goal signal for the ccy supervisor
- recovery_cron_advisor — failsafe recovery cron lifecycle advisory
- ccy_supervisor_integrity — keep the ccy supervisor properly set up
- git_upstream_checker — additive fetch + pull/cleanup advice on session start
- hook_registration_checker — hooks configuration policy
- plan_qa_sweep — plan-tree drift report at session start
- plan_workflow_asset_checker — plan tooling provisioning alert
- project_handler_load_checker — project protection degraded alert
- secret_file_hygiene_checker -- on-disk hygiene for protected paths
- tool_disable_advisor — declared never-want tools are checked at session start
- docs_qa_sweep — documentation drift report at session start
- model_fallback_detector — silent model substitution is surfaced
- idle_housekeeping_advisory — report-first idle housekeeping (beta, opt-in)
- standing_authorisations — a project can record a standing request
- auto_approve_reads — gated on bypassPermissions mode
- subagent_report_size_blocker — write large reports to a file
- worktree_create — semantic worktree naming
- nitpick.dismissive_language — do not deflect or prematurely halt
- nitpick.hedging_language — the guessing is the defect, not the wording