ADR-0005 — Sync Agent Skills at Session Start via UserPromptSubmit Hook
Status: accepted
Deciders: DeAcero AI Agent Guild
Date: 2026-03-18
Commits: 5f4d78a (feat(sync): add cornerstone sync hook)
Context and Problem Statement
Each project generated from the Cornerstone template receives a static copy of .agents/skills/ at generation time. As the template evolves — new skills added, existing skills improved, protocols updated — generated projects diverge silently. There is no mechanism to notify teams of updates, and requiring a manual pull from the template is friction that teams will ignore.
The problem is guaranteed skill drift: a project generated six months ago may operate with significantly outdated or incorrect agent skills while its team is unaware.
Decision Drivers
- Skills must stay current without requiring manual team intervention
- Sync must not block the developer workflow (must be transparent and fast-path-exit when up to date)
- Sync must fail silently — a missing
ghauth or network issue must never crash a session - The solution must work within Claude Code's existing extension points
- Teams must be able to force a sync when they need the latest skills immediately
Considered Options
- UserPromptSubmit hook — run
sync_agents.shat every session start via Claude Code hooks; throttle to once per 24h - Manual script — document
bash tools/sync_agents.shas a step teams must run periodically - CI job — GitHub Actions workflow on a schedule that PRs skill updates into each generated repo
- Submodule —
.agents/skills/as a git submodule pointing todeagentic/cornerstone - KDB MCP — future: query a central Knowledge Database via MCP at session start
Decision Outcome
Chosen option: Option 1 — UserPromptSubmit hook with 24h throttle.
Rationale:
- Option 2 (manual) will be skipped in practice. Human nature guarantees drift.
- Option 3 (CI job) requires each generated project to be registered centrally, creates noisy PRs, and does not cover local developer sessions.
- Option 4 (submodule) is fragile: submodule state diverges easily,
git pull --recurse-submodulesis non-obvious, and detached HEAD states confuse non-git-expert teams. - Option 5 (KDB MCP) is the correct long-term solution but does not exist yet. This decision explicitly plans for it:
sync_agents.shis designed so that its internal implementation can be replaced with a KDB query without changing its CLI contract. - Option 1 is transparent, automatic, and fails safely. Claude Code's
UserPromptSubmithook fires before the first prompt is processed, making it the ideal slot for session initialization work.
Positive Consequences
- Skills stay current without any team action beyond the initial
gh auth login - Teams that open a project after a long gap automatically get the latest skills
- The 24h throttle means no perceptible delay in normal daily use
--forceflag allows immediate sync when needed
Negative Consequences
- Requires
gh auth loginas a hard prerequisite — teams without access to the privatedeagentic/cornerstonerepo cannot sync - 24h staleness window means a skill fix may not reach a team until the next day
- Sync silently does nothing if
ghis not installed, which may confuse debugging
Implementation Details
tools/sync_agents.sh
Core logic:
# 1. Read .agents/.last_sync timestamp
# 2. If < 24h since last sync and not --force → exit 0 (silent)
# 3. Verify gh auth (gh auth status) → exit 0 silently if not authenticated
# 4. gh repo clone deagentic/cornerstone /tmp/cornerstone-sync --depth=1
# 5. rsync -a --delete /tmp/cornerstone-sync/.agents/skills/ .agents/skills/
# 6. Write current timestamp to .agents/.last_sync
The script stores PROJECT_SLUG="{{cookiecutter.project_slug}}" as a runtime shell variable. This is intentional: at generation time, cookiecutter replaces the Jinja2 expression. At runtime (every sync), the already-rendered value is used by the script.
_copy_without_render requirement
tools/sync_agents.sh must be listed in _copy_without_render in cookiecutter.json. Without this, {{cookiecutter.project_slug}} inside the script would be re-rendered by cookiecutter at generation time and could conflict with other template variables if the script itself were treated as a Jinja2 template. The _copy_without_render directive copies the file as-is, so the already-rendered literal value is preserved.
Hook wiring in .claude/settings.json
{
"hooks": {
"UserPromptSubmit": [{
"matcher": "",
"hooks": [{"type": "command", "command": "bash tools/sync_agents.sh 2>/dev/null || true"}]
}]
}
}
The 2>/dev/null || true wrapper ensures that any error in the sync script — network failure, missing gh, permission denied — is completely swallowed and never surfaces to the developer.
Future evolution path
The sync_agents.sh contract is stable. When the KDB becomes available, only the internal implementation changes:
TODAY FUTURE
───────────────────────────── ─────────────────────────────
sync_agents.sh sync_agents.sh
gh repo clone cornerstone → MCP query → KDB
rsync .agents/skills/ skills + business rules
+ accumulated learnings
The bash tools/sync_agents.sh CLI contract does not change. Projects do not need to be updated.
Pros and Cons of Options
Option 1: UserPromptSubmit hook (chosen)
- Good, because fully automatic — zero team action after initial setup
- Good, because fails silently — never blocks a session
- Good, because designed for future KDB migration (same contract)
- Bad, because requires
gh auth loginprerequisite
Option 2: Manual script
- Good, because no dependencies beyond the script file itself
- Bad, because teams will not run it — drift is guaranteed
Option 3: CI job
- Good, because automatic and does not depend on local
ghauth - Bad, because noisy (PRs per project per sync), requires central project registry
Option 4: Git submodule
- Good, because native git — no extra tooling
- Bad, because submodule drift is the exact same problem in a different form; teams routinely forget
--recurse-submodules
Option 5: KDB MCP (future)
- Good, because skills + business rules + learnings in one query
- Bad, because KDB does not exist yet; blocks on significant infrastructure work
Links
- Implemented in:
{{cookiecutter.project_slug}}/tools/sync_agents.sh - Hook configured in:
{{cookiecutter.project_slug}}/.claude/settings.json - Parent ADR: ADR-0004 (project-level settings)
- Future evolution: docs/future.md — KDB section