Chapters

DOC-05 / Technical reference · Chapter 11

Skills, Agents & Hooks

How the harness wires skills, delegable sub-agents and event hooks to enforce doctrine.

Overview

The mothership's harness is wired around a .claude/ directory. Three mechanisms coexist there: the skills (invocable capabilities), the delegable sub-agents, and the event hooks that enforce doctrine — commit-on-the-fly, prod/email safeguards, memory injection.

                 .claude/
  user        ──▶  settings.json (versioned)  +  settings.local.json (local)
  / Atlas          hooks PreToolUse / PostToolUse / Stop / UserPromptSubmit...

  skills/<name>/SKILL.md  ──▶ invoked via the Skill tool
  agents/<nickname>.md    ──▶ delegated via the Agent tool
       │                         │
       ▼                         ▼
  scripts/hook-*.sh        facades + memory injection
  (safeguards, nudges)

Two settings files coexist:

FileGit-versionedContents
.claude/settings.jsonyeshooks PreToolUse/PostToolUse/Stop/UserPromptSubmit, env, permissions.defaultMode
.claude/settings.local.jsonno (local)permissions.allow, hooks SessionStart/UserPromptSubmit/PreToolUse/PostToolUse, autoMode

Both hook sets are cumulative: for a given event (e.g. PreToolUse matcher Bash), the commands from both files run. This is why the Python safeguards live in the local file and the shell nudges in the versioned one.

Skills

Each skill is a .claude/skills/<name>/ directory holding a SKILL.md — either bare markdown, or with a --- description: ... --- frontmatter. An auto/ subfolder gathers engine-internal skills not meant for manual invocation.

Catalogue by category

CategorySkillRole
Audit / healthauditFull audit of a site (infra healthcheck, public pages, SEO, perf, security, DB) — score /100.
audit-hard-floorAnti-rot check of the immutable P0 whitelist — verifies every named path / section still exists.
audit-personasDrift of the 30 personas vs the current stack; snapshot into sy_personas_drift_history.
statusFull check of system state.
context-statusState of the current session's context.
Infra / hostsinfraInfra audit of a host machine (availability / load / services / SSL / backups).
securitySecurity audit of a host (mothership or tenant).
backup-verifyChecks freshness + integrity of a host's local backups.
upgradeNon-interactive APT upgrade of a host with a Docker hold.
botsAnalyses bot hits collected server-side.
gscQueries the Google Search Console API (opportunities).
Chantier / steeringchantierChantier steering (doctrine "1 chantier = N travaux"); sources ps_ac_chantier + ps_ac_chantier_travail; supports new-skeleton (atomic creation).
ideeCreates a brainstorm idea (sy_brainstorm).
revueThe Place des Armes review (each agent reports in).
montessoriPost-chantier lesson via the Montessori agent → draft in the Vault's inbox folder.
Email / inboxinboxReads / searches emails in ps_ac_inbox_emails via the facade.
inbox-directDirect IMAP fallback, read-only, does not write to the DB.
inbox-findErgonomic IMAP wrapper — fetch + .eml, lists attachments without extracting (scan-first).
scan-attachmentAV scan of an attachment before opening — dedicated facade (Mitnick agent).
propal-verifQA of a commercial email proposal before sending (pool of 4 agents).
Memory / knowledgerecallSemantic recall (pgvector RAG) over memory, the Vault and ps_ac_{cicatrices,doctrine,chantier} (1024-dim embeddings, cosine).
zettelSplits a monolithic document into atomic notes for the Obsidian Vault.
dictionnaireAdds terms to the technical dictionary.
victoireEngraves a victory into ps_ac_cicatrices (kind='victory').
refresh-personaRefreshes an agent persona via an LLM + 3-way diff + review (UPDATE sy_agents).
Finance / businessbankConsults accounts / transactions via the banking facade.
bank-import-n26Manual import of bank transactions (ps_ac_bank_transactions).
maltFetches prospect conversations from the freelance platform.
PublishingpublishPublishes a blog article on the showcase site.

DB sources of truth

Skills do not store business data: they hit the PostgreSQL tables (schema vaisseau_mere_ac). Tables explicitly cited in the SKILL.md files:

  • chantierps_ac_chantier, ps_ac_chantier_travail
  • recallps_ac_cicatrices, ps_ac_doctrine, ps_ac_chantier
  • victoireps_ac_cicatrices (kind='victory')
  • ideesy_brainstorm
  • audit-personassy_personas_drift_history
  • refresh-personasy_agents
  • inboxps_ac_inbox_emails
  • bank-import-n26ps_ac_bank_transactions

A pre-invoke hook reads sy_skill.size_bytes to decide whether to suggest a lazy load (3-tier) rather than injecting a large SKILL.md in full.

Sub-agents

Seven <nickname>.md files are delegable via the Agent tool. Each file is generated from the DB by a script that reads the ps_ac_agents view and writes between AUTO-PERSONA-START / AUTO-PERSONA-END markers. The managed zone is never hand-edited.

ps_ac_agents is a read-only view over the base table sy_agents (30 rows). The physical source of truth is therefore sy_agents: refresh-persona writes to it, the generator reads the view — consistent, no two-table divergence.

Each agent's frontmatter: name, description (delegation criterion), tools, model.

NicknameCodenameRoleAllowed toolsCognitive frame
brunelinfraDevOps / Infrastructure (Docker, nginx, SSL, DNS, VPS)Bash, Read, Edit, Write, Grep, GlobMaximum load
turingbackendBackend SRE (headless e-commerce, modules, Python, PG integrity)Bash, Read, Edit, Write, Grep, GlobDeterminism
eamesfrontendFrontend (Nuxt / Vue / Tailwind / Design System, hub pages)Bash, Read, Edit, Write, Grep, GlobFunctionalism
lovelaceqaQA — last prod gate, Playwright acceptance, regressionsBash, Read, Grep, Glob (no write)Pre-mortem
mitnicksecuriteOffensive/defensive security, attachment scan, secret detection, OWASPBash, Read, Grep, Glob (no write)Attacker
otletseo-techniqueTechnical SEO (JSON-LD, sitemap, CWV, 301, AI indexability)Bash, Read, Edit, Write, Grep, GlobInterlinking
nightingaleclient-successCustomer Success — drafts client comms, NEVER sends directlyRead, Grep, Glob (read-only)Vital signs

Verifiable observations:

  • lovelace and mitnick have no Edit/Write (control roles, no mutation).
  • nightingale does not even have Bash — strictly read, consistent with the rule "the AI never speaks directly to clients".
  • Known drift: for some agents the managed zone may show a historical DB stack (older Nuxt versions, an older SQL engine) while the manual zone describes the current stack. This is exactly what audit-personas / refresh-persona track. Source of truth = the base table sy_agents, not the .md.

Delegation doctrine: Atlas self-applies the chantier doctrine (≥ 2 distinct agents for a tenant scope) and delegates via the Agent tool. Mitnick in attachment-scan mode is tooling, not a team recruitment.

Hooks

Event map

SessionStart        (local)     reset context + brief + scar injection
UserPromptSubmit    json+local   DB schema sync  +  email check
PreToolUse  Bash    json+local   shell nudges    +  python safeguards
PreToolUse  Agent   json+local   reactor         +  agent scar injection
PreToolUse  Skill   (json)       pre-invoke (suggests lazy-load for large SKILL.md)
PreToolUse  Edit|Write (local)   scar injection
PostToolUse Bash    (json)       react-log  +  deploy-preprod post-commit
PostToolUse Agent   (json)       reactor + react-log + agent telemetry
PostToolUse Edit|Write... (json) track-session-edits
PostToolUse ""      (local)      context-hook (token counting)
Stop                (json)       release-lock → scar → uncommitted-warn → chantier-sync

SessionStart

Three commands (local file), each with a short timeout:

  1. Resets the session's context counter.
  2. Session brief: reads a pre-computed report (ps_ac_audit_reports, type daily_meet, produced by a scheduled task) and enriches it live (git, crontab, client emails, backlog, scar). SQL piped via docker exec, no npm dependency.
  3. Injects the relevant scars into the startup context.

There is no SessionStart hook in the versioned settings.json — it lives only in the local file.

UserPromptSubmit

  • settings.json: syncs the DB schema dump (.claude/db_schema.md).
  • local file: checks incoming emails (inbox facade, short timeout, silent and non-blocking on failure).

PreToolUse — Bash matcher

settings.json (nudges, in execution order):

ScriptEffect
check-db-mutation.shblocks a DB mutation (UPDATE/INSERT/ALTER … ps_ac_*) without an aligned code commit in the last 5 minutes (runtime equivalent of "code BEFORE db").
hook-pre-deploy-autocommit.shauto-commits before a ./deploy.
hook-chantier-skeleton-nudge.shwarns on raw SQL INSERT into a chantier instead of the atomic creation path.
hook-pre-commit-transcript-scan.shanti-leak scan before commit.

A back-office auth audit script exists on disk but is referenced in no settings*.json → a dormant script, not to be assumed active.

Local file (Python safeguards, exit 2 = blocking):

ScriptEffect
prod-write guardblocks any Bash command targeting a tenant's prod (host / database / domain markers) with an SQL write pattern → requires a safe prod-write script. Born from an accidental TRUNCATE scar. Anti-false-positive: preprod and non-destructive SELECT/SHOW are explicitly whitelisted, so a smoke check is not blocked.
email-facade guardblocks any bypass of the email facade (raw SMTP/IMAP clients, message construction outside the facade). Only the authorised facade scripts may send/read.
Bash scar injectioninjects scars related to the command (short timeout).

PreToolUse — other matchers

  • Agent: a shell reactor (json) + agent scar injection (local).
  • Skill: a non-blocking pre-invoke (exit 0 always) that suggests a lazy load when sy_skill.size_bytes exceeds a threshold.
  • Edit|Write (local): scar injection.

PostToolUse

  • Bash: react-log + auto-deploy hook after commit (the deploy auto / ship manual asymmetry). The hook maps touched paths to the impacted site. For tenants this is a preprod deploy; for the mothership there is no preprod — ./deploy = a live rebuild, so the word "preprod" in the hook's name is misleading in that case. Skips: the hook skips the deploy if the commit message contains [skip-deploy] / [no-deploy] or starts with wip:, if the commit is pure docs/markdown/Vault (no runtime file touched), or if the working tree is dirty post-commit.
  • Agent: reactor + react-log + agent telemetry.
  • Edit|Write|MultiEdit|NotebookEdit: track-session-edits writes the list of files edited by the session — the basis of the session-aware Stop hook.
  • all tools (local): token / context counting.

Stop — execution order

1. release-chantier-lock   releases the session's chantier lock
2. stop-cicatrice          scar reminder IF closing keywords
                           ("closing", "end of session", "wrapping up"…)
3. uncommitted-warn        BLOCKING: refuses Stop if dirty (files FROM
                           this session) → enforces commit-on-the-fly
4. chantier-sync           reminder to sync chantier ↔ hub DB

Key mechanics of the uncommitted warning:

  • session-aware: filters git status on the files listed by the session → N parallel Claude sessions do not block each other.
  • anti-loop: if stop_hook_active=true (2nd pass), it switches to a non-blocking warning instead of {"decision":"block"}.
  • worker early-return: a sub-claude spawned by the task worker exits silently with exit 0 — it manages its own commit cycle.
  • exclusion of .claude/settings.json (the hook may have edited it).

The stop-cicatrice hook is gated by a keyword in the last user message, otherwise silent; when triggered, it lists the fix(*) / Cicatrice commits since the preprod branch via stderr, then exit 2.

Worker-context convention

A single shared lib under scripts/lib/ is sourced by the four Stop hooks; they exit early when the worker context variable is set (injected by the agent spawner). General rule: a sub-claude spawned by the worker does not trigger the user session hooks (commit-on-the-fly, closing scars).

Permissions & environment

settings.json

"env":         { "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "0", "CLAUDE_CODE_NO_FLICKER": "0" }
"permissions": { "defaultMode": "auto" }

defaultMode: auto = execution without a prompt by default (consistent with the AI's autonomous ./deploy doctrine).

settings.local.json

  • permissions.allow: a long allowlist of pre-approved tools / commands; permissions.deny absent.
  • env: empty.
  • autoMode: a non-standard structured block describing a DB access policy in natural language — allow (read-only SQL on a staging environment), soft_deny (any write to a tenant's prod → explicit confirmation every time), and environment (a reminder not to confuse two VPS of the same tenant: prod vs staging).

This autoMode block is doubled at the executable level by the prod-write guard: the declarative policy + the blocking hook.

Anti-leak

No secret in clear text in the hooks/skills/agents: IMAP/SMTP credentials are referenced by variable name in a gitignored environment file, never by value. The pre-commit transcript scan and the security agent (skill scan-attachment) are the anti-leak / anti-malware nets.

Data model

Summary of the tables touched (schema vaisseau_mere_ac), with their producer and consumer:

TableProducerConsumer
sy_agents (base) / ps_ac_agents (view)manual edit / refresh-personapersona generator → .claude/agents/*.md
ps_ac_chantier, ps_ac_chantier_travailskill chantierchantier-sync / lock hooks
ps_ac_cicatricesskill victoire, stop-cicatrice hookrecall, SessionStart / PreToolUse injection
ps_ac_audit_reports (daily_meet)scheduled audit tasksession brief (SessionStart)
sy_skill (size_bytes)skill indexingskill pre-invoke hook
sy_personas_drift_historyaudit-personasdrift review
sy_brainstormideebrainstorm hub
ps_ac_inbox_emailsinbox facadeskill inbox
ps_ac_bank_transactionsbank-import-n26skill bank

Verified via information_schema: ps_ac_agents is a view over the base table sy_agents. refresh-persona writes sy_agents, the generator reads the view — consistent, not two divergent tables.