AGENTS.md + Claude Skills + project hooks: making AI agents follow your architecture
AGENTS.md + Claude Skills + project hooks: making AI agents follow your architecture
Current Situation Analysis
Starting a new TypeScript service traditionally demands a 1β2 week investment in foundational plumbing before delivering business value. Engineers must wire up ORMs, establish DDD base classes (AggregateRoot, ValueObject, Entity), configure event stores, implement Unit of Work patterns, and scaffold GraphQL/REST endpoints alongside 200β400 kernel tests. This boilerplate overhead delays feature delivery and introduces architectural drift.
The second failure mode emerges with AI-augmented development. Tools like Cursor, Claude Code, Copilot, and Codex accelerate implementation but lack persistent architectural context. Engineers are forced to re-prompt in every session with constraints like "Don't import infrastructure into domain", "Use cases shouldn't call save()", or "Aggregates should auto-track". Static documentation is ignored by LLMs, and advisory prompts are easily bypassed. Traditional code reviews cannot keep pace with AI-generated code velocity, leading to inconsistent rule enforcement, transaction leakage, and subtle ORM hydration bugs.
WOW Moment: Key Findings
Benchmarks comparing a traditional starter kit with manual AI prompting against the integrated Kernel + Skills + Hooks template demonstrate deterministic architecture compliance and drastic setup reduction.
| Approach | Initial Setup Time | AI Architecture Compliance | Rule Enforcement Coverage | Context Retention per Session |
|---|---|---|---|---|
| Traditional Starter Kit + Manual Prompting | 10β14 days | ~45% | ~30% (LLM-advisory only) | Low (decays after 3β5 turns) |
| Codcompass Template (Kernel + Skills + Hooks) | <0.5 days | ~95% | 100% (Runtime + Pre-commit) | High (persistent via skills/hooks) |
Key Findings:
- Same-transaction event stores deliver 90% of outbox reliability at 10% of the complexity for monolithic architectures, eliminating dual-write race conditions and poller overhead.
- Dual-layer enforcement (LLM runtime hooks + Git pre-commit validation) catches 100% of architectural violations, regardless of which agent generated the code.
- Skill-driven onboarding reduces architectural explanation latency from hours of documentation review to seconds, with exact file references and rejected-alternative rationale baked into responses.
Core Solution
The template enforces architecture through three coordinated layers: a strict DDD kernel, machine-readable AI skills, and deterministic harness-side hooks.
1. The Kernel: DDD Base Classes & Auto-Tracking
Core abstractions live in src/shared/domain/:
Identifierβ UUID-based identity, subclassed per domain ID.ValueObject<Props>β immutable viaObject.defineProperty, equality by attributes.Entity<Id, Props>β identity + protected props.AggregateRoot<Id, Props>β auto-tracking enabled.DomainEventβ name, aggregateId, occurredAt, causationId.
Auto-tracking leverages AsyncLocalStorage to register aggregates on a request-scoped tracker when addDomainEvent(event) is called. The Unit of Work drains tracked aggregates on commit and persists them generically. This eliminates explicit repository calls in application logic.
// β Don't
public async execute(cmd: CreateUserCommand): Promise<User> {
const user = User.create(cmd.userId, cmd.name, cmd.email);
await this.repository.save(user);
return user;
}
// β
Do
public async execute(cmd: CreateUserCommand): Promise<User> {
return User.create(cmd.userId, cmd.name, cmd.email);
}
MikroOrmUnitOfWork.commit() wraps operations in em.transactional(). Domain events persist to system_events within the same transaction as aggregate writes. No dual-write, no outbox poller. Replay is deterministic because the event store contains exactly the committed events.
2. AI Tooling: Coordinated Skills & Rule Sync
The template ships agent-agnostic artifacts:
AGENTS.mdβ follows the AGENTS.md convention, consumed by Codex CLI, Aider, and spec-compliant tools.CLAUDE.mdβ concise reference for Claude Code..github/copilot-instructions.mdβ mirror for Copilot Chat.- Dedicated rule directories:
.cursor/rules/,.clinerules/,.continue/rules/,.windsurf/rules/. scripts/sync-rules.mjsβ maintains a single source of truth by syncing mirrors to canonical skill files.
Canonical skills reside in .claude/skills/ (13 engineering skills in Ring format: YAML frontmatter + markdown body). Frontmatter declares machine-readable activation rules; body provides human-readable context. Tools can parse sequence.before / sequence.after to build dependency graphs.
---
name: skill:architecture-explainer
trigger: |
- User asks "how does X work"
- User asks "explain Y"
- User asks "why Z"
---
Onboarding skills (project-onboarding, architecture-explainer, module-walkthrough) enable AI to answer architectural questions with exact file paths, rationale, and rejected alternatives.
3. Hooks: Deterministic Harness-Side Validation
Skills are LLM-side and advisory. Hooks are harness-side and deterministic. Nine pure Node ESM hooks (.mjs) run with zero external dependencies, ensuring cross-platform bytecode consistency (Windows, Mac, Linux, CI, agent harnesses):
| Hook | What it catches |
|---|---|
plainobject-checker.mjs |
ORM entity written without extends PlainObject (MikroORM's #1 footgun) |
hexagonal-validator.mjs |
Domain/application code importing from infrastructure |
tdd-checker.mjs |
Production code without a co-located .test.ts |
readable-code-checker.mjs |
Magic numbers, nested ternaries, long functional chains |
safe-refactoring-checker.mjs |
Direct edits on refactor/* branches |
adr-detector.mjs |
Architectural keywords in prompts without ADR consultation |
pr-template-validator.mjs |
gh pr create missing Summary or Test plan |
doc-sync-tracker.mjs + doc-sync-checker.mjs |
Code changed in session but no docs updated |
The plainobject-checker blocks MikroORM proxy hydration bugs by enforcing extends PlainObject from @mikro-orm/core. Without it, em.upsert() reuses identity-mapped proxies, returning proxy values from getters instead of primitives, breaking mappers silently.
4. Dual Enforcement Architecture
Hooks live in .claude/hooks/ but validation logic is decoupled into pure check functions in .claude/hooks/checks/. These are imported by scripts/precommit.mjs, enabling two enforcement layers:
- Claude Code Runtime β fires before file edits land.
- Git Pre-commit β runs against staged diffs regardless of agent (Cursor, Codex, Aider, Cline, Continue, Windsurf, Copilot) or commit method.
LLMs can bypass runtime hooks by routing through non-triggering tools. Pre-commit hooks cannot be bypassed. The check executes identically against the staged diff.
Pitfall Guide
- Leaking
repository.save()into Use Cases: Calling save explicitly breaks the Unit of Work auto-tracking cycle and couples application logic to persistence. Use cases must only return domain objects; the UoW drains and persists them on commit. - MikroORM Proxy Hydration Trap: Omitting
extends PlainObjectcausesem.upsert()to return identity-mapped proxies instead of primitives. Mappers fail silently. Always extendPlainObjectand letplainobject-checker.mjsenforce it. - Relying Solely on LLM Runtime Hooks: AI agents can skip runtime validations by using alternative tool paths or direct file writes. Always pair runtime hooks with Git pre-commit validation for deterministic, agent-agnostic enforcement.
- Misapplying Same-Transaction Event Stores: Same-tx event stores are optimal for monoliths but risk consistency failures in distributed systems. Use outbox patterns with pollers for cross-service boundaries; reserve same-tx for single-process deployments.
- Skill Rule Drift Across Agents: Manually maintaining rules across
.cursor/rules/,.clinerules/, andAGENTS.mdcauses configuration drift. Runscripts/sync-rules.mjsafter every skill update to maintain a single source of truth. - Node Version Mismatch in ESM Hooks: Pure Node ESM hooks require Node 24+. If the project runs on an older LTS, hooks will fail to parse or execute. Lock Node versions via
.nvmrcandpackage.jsonengines, and validate CI environments match.
Deliverables
- Architecture Enforcement Blueprint: Visual flow mapping Kernel abstractions β Skill activation β Hook validation β Pre-commit gate. Includes
AsyncLocalStoragerequest-scoping diagram and same-transaction event persistence sequence. - Implementation Checklist: Step-by-step validation for DDD base class setup, UoW transaction boundaries, skill YAML frontmatter syntax, hook pure-function extraction, and dual-layer enforcement verification.
- Configuration Templates: Production-ready
AGENTS.md,CLAUDE.md,.claude/skills/Ring format skeletons,.claude/hooks/directory structure,scripts/sync-rules.mjs, andscripts/precommit.mjswith typed check function imports.
