One source of truth. Inherited by every project. Enforced by hooks. Audited weekly.
A trellis gives a climbing plant the structure to grow on. This one does the same for code an AI agent writes — without it the work sprawls and breaks; with it, it grows tall and clean.
Quick start · What you get · Architecture · Repo layout · FAQ
The first time you let a coding agent write production code it feels like a superpower. The tenth time, you notice the corners it's been cutting: pushed straight to main, marked a task "done" when the tests never ran, summarised what it intended to do instead of what it actually did, force-pushed over an hour of your work to "clean things up."
None of those are bugs in the model. They are gaps in the process around the model — the same gaps human teams spent decades closing with branch protection, code review, commit conventions, CI gates, and a thousand small rituals. An agent has none of that scaffolding by default.
Trellis is that scaffolding, opinionated, in one repo, forkable in ten minutes.
| 📜 Parent rules in one place | core-rules/CLAUDE.md is the single source of truth. Every project inherits via symlink — Claude Code through .claude/rules/, Codex through AGENTS.md + .agents/. |
| 🪝 9 canonical hooks, 3 tiers | Fast-local (<3s every turn) · Heavy-gated (on Stop, ≤90s) · Git-boundary (husky / native). Blocks rm -rf ~, force-pushes, direct pushes to main, secrets reads, and "done" without receipts. |
| ✅ A harness-agnostic process-gate skill | One canonical implementation, symlinked into every project. Runs the same way in Claude Code, Codex, claude -p, and CI. Emits a fixed verdict block: MERGEABLE / NEEDS CHANGES / BLOCKED. |
| 🔍 A fleet of 10 scheduled audits | Weekly sweeps for hook drift, --no-verify bypasses, dep CVEs, test rot, registry/blacklist health. Every report is dated markdown — grep, diff, quote in commits. |
| 📐 Rule of Three for evolution | New rules wait in core-rules/deferred.md until a 3rd independent project adopts them. n=2 is the danger zone. |
| 🤖 Codex parity, opt-in or opt-out | Default config enables both harnesses with the same policy intent. Remove "codex" from trellis.config.json if you don't use it. |
The control plane owns the rules. Projects inherit through symlinks. Hooks enforce in the moment. Audits enforce over time. The Rule-of-Three loop keeps the parent layer honest.
Two ways to set up. Pick one.
- Clone this repo.
- Open it in Claude Code or Codex (or paste
AGENT_SETUP.mdinto any agent session with filesystem tools). - The agent interviews you for paths/harness/name, fills placeholders in-place, optionally enables Codex parity, installs the inheritance symlinks, and seeds the canonical hooks.
That's it. No manual sed.
Follow SETUP.md step by step.
Paste AGENT_ONBOARD_PROJECT.md into an agent open inside your customised trellis/. It interviews you, runs scripts/onboard-project.sh, wires the project's CLAUDE.md, updates registry.md, and commits.
Works for new projects, fresh clones of registered projects, and drift repair.
Tier 1 — fast-local < 3s, every agent turn
─────────────────────────────────────────────────────
block-destructive denies rm -rf ~, git push --force, DROP TABLE, .env reads
post-edit-verify lints just the touched file (eslint/ruff/clippy/govet)
session-context injects last session's state on SessionStart
save-context-log writes context-log.md on PreCompact / Stop
post-compact-context re-injects context-log.md after compaction
truncation-check flags >50K-char tool results
Tier 2 — heavy-gated ≤ 90s, on Stop
─────────────────────────────────────────────────────
stop-verify open todos? typecheck? lint? fast tests? → block on any fail
code-review-subagent dispatches a read-only reviewer on edit-heavy turns (≥3 files)
ui-verify boots dev server + screenshots affected route on UI changes
Tier 3 — git-boundary husky or native git hooks
─────────────────────────────────────────────────────
pre-commit lint-staged on staged files
commit-msg Conventional Commits, project-configured scope allowlist
pre-push blocks direct push to main; runs typecheck/lint/tests
Every tier has the same escape hatch — TRELLIS_ALLOW_MAIN_PUSH=1, --no-verify, override env vars — and every escape hatch is noisy. The bypass-tripwire audit surfaces every use within 8 days, and the audit reports persist in git.
Ten scheduled tasks, registered as cron jobs, sweep every project in registry.md:
| Audit | Cadence | What it catches |
|---|---|---|
cross-project-process-audit |
Mon 10:00 | Hook presence, staleness, required files, inheritance wiring |
registry-blacklist-health |
Mon 10:30 | Registry ↔ filesystem ↔ blacklist consistency |
test-health |
Mon 11:00 | Fast suite green/red across the registry, last-green bisect on red |
dep-currency |
Mon 11:30 | Outdated-dep scan: patch / minor / major drift |
parent-hook-drift |
Sun 21:00 | SHA256 canonical-vs-deployed comparison for every hook |
bypass-tripwire |
Weekdays 08:00 | Silent unless someone used --no-verify, force-push, or direct-to-main |
dep-vulnerabilities |
Weekdays 08:30 | CVE / GHSA via native pkg-mgr audit + osv-scanner |
gotchas-rollup |
1st of month | Rule-of-Three engine — promotes n≥3 patterns to parent rules |
audit-report-rollup |
1st of month | Month-over-month trend report across every other audit |
dep-major-upgrade-watch |
1st of month | Framework-tier (Next, React, TS, Node) drift vs. your watchlist |
Reports land in audits/YYYY-MM-DD-<task>.md. Examples (redacted) live in examples/audits/.
.
├── README.md ← you are here
├── SETUP.md ← human-facing setup walkthrough
├── AGENT_SETUP.md ← paste-into-agent prompt that does setup for you
├── AGENT_ONBOARD_PROJECT.md ← onboard a project after Trellis is bootstrapped
├── LICENSE ← MIT
│
├── core-rules/ ← THE PARENT LAYER — what every project inherits
│ ├── CLAUDE.md ← terse parent rules
│ ├── AGENTS.md ← symlink → CLAUDE.md for Codex parity
│ ├── hooks.md ← spec for the 9 canonical hooks (3 tiers)
│ ├── hooks/ ← canonical Claude Code hook implementations
│ ├── codex/ ← canonical Codex hooks.json + hook scripts
│ ├── husky/ ← canonical pre-commit / commit-msg / pre-push
│ ├── skills/process-gate/ ← canonical pre-PR enforcement skill
│ ├── templates/ ← per-project file templates (gotchas, context-log)
│ ├── inheritance.md ← how Claude and Codex inheritance work
│ └── deferred.md ← rules waiting for their 3rd project (Rule of Three)
│
├── registry.md ← list of projects under Trellis management
├── blacklist.md ← projects to skip
│
├── engineering-process.md ← THE MANUAL — narrative source of truth
│
├── scheduled-tasks/ ← 10 audits + 2 drafts; each is prompt + targets
├── scripts/ ← onboard-project, sync-hooks, sync-codex-hooks
├── audits/ ← generated audit reports land here
├── examples/audits/ ← redacted sample reports
└── docs/ ← provenance + LIFT/LEAVE/DEFER recon
Five placeholders. Setup replaces them in-place.
| Placeholder | What it becomes | Example |
|---|---|---|
__TRELLIS_PATH__ |
Absolute path where you cloned this repo | /Users/jane/projects/trellis |
__PROJECTS_ROOT__ |
Absolute path to the parent dir holding your projects | /Users/jane/projects |
__MAINTAINER_NAME__ |
Your name | Jane Doe |
__GITHUB_USER__ |
Your GitHub handle | janedoe |
__USER_HOME__ |
Your home dir (rare — legacy refs only) | /Users/jane |
AGENT_SETUP.md walks any LLM through asking you for these values and substituting them.
- macOS or Linux with
bash,git, andjqonPATH. Hooks degrade gracefully ifjqis missing. - Node.js for projects using husky. (For Unity / Rust / Go / Python-only projects, see
core-rules/inheritance.md→ "Native git hooks".) - Claude Code and/or Codex. Default config enables both. Remove either from
trellis.config.jsonif you don't use it. - Codex hooks opt-in requires Codex CLI with hooks support and
[features] codex_hooks = truein$CODEX_HOME/config.toml.
Why both Claude Code and Codex?
Different harnesses have different strengths and I use both depending on the project. Forcing a single harness across every project would mean either giving up Codex's strengths or giving up Claude Code's. Trellis keeps the policy intent identical across them — same rules, different hook envelopes — so the choice is per-project, not per-process.
Why the Rule of Three?
n=1 is anecdote. n=2 is coincidence dressed as a pattern. n=3 is the cheapest sample size that lets you commit to an abstraction without locking in the wrong shape and the wrong defaults. Unwinding a bad parent rule across five projects is painful. Waiting for the third witness is free.
What if I disagree with a parent rule?
Fork it, edit core-rules/CLAUDE.md, done. The parent layer is yours after you clone. Trellis is opinionated, not prescriptive — the value is the shape (parent + child + hooks + audits + Rule of Three), not any specific rule.
Can I use this without Codex / without Claude Code?
Yes. Remove the harness you don't use from harnesses in trellis.config.json. Onboarding will skip the corresponding artifact tree.
What about projects without package.json (Unity, Rust, Go, Python)?
Native git hooks under .githooks/ with git config core.hooksPath = .githooks. The PR-flow guard (pre-push blocking direct push to main) is the same; husky just isn't the vehicle. See core-rules/inheritance.md → "Native git hooks".
How do I tune the rules?
Per-project overrides live in .claude/hooks/config.sh (and .codex/hooks/config.sh if applicable). The parent hook scripts are read-only; projects point them at their tools via env vars. See hooks.md → "Project overrides".
A longer-form blog post walking through the design decisions, the five principles, and the lessons:
→ Trellis: An Engineering Process for AI Coding Agents
MIT — see LICENSE and docs/PROVENANCE.md for upstream attribution.
Built and maintained by Abhishek Kaushik.
If you fork it, tell me what you change — the Rule of Three only works with three witnesses.