Architecture Overview
Overall Architecture
┌──────────────────────────────────────────────────────────────┐
│ User │
└─────────────────────────────┬────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Claude Code / Cursor / OpenCode / Codex / Kiro / Gemini / │
│ Qoder / CodeBuddy / Copilot / Droid / Kilo / Antigravity / │
│ Windsurf + .agents/skills/ ecosystem (Amp, Cline, …) │
│ (AI Coding Assistant Interface) │
└─────────────────────────────┬────────────────────────────────┘
│
┌───────────────────┼──────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Commands │ │ Skills │ │ Hooks │
│ /trellis:* │ │ (auto-trigger) │ │ (hook-capable) │
│ │ │ │ │ │
│ start │ │ brainstorm │ │ SessionStart │
│ finish-work │ │ before-dev │ │ PreToolUse │
│ continue │ │ check │ │ UserPrompt │
│ │ │ update-spec │ │ │
│ [all] │ │ break-loop │ │ [see §2.2] │
└───────┬───────┘ └────────┬─────────┘ └────────┬────────┘
│ │ │
└───────────────────┼──────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ .trellis/ Directory │
│ │
│ spec/ workspace/ tasks/ scripts/ │
│ (Specs) (Journals) (Tasks) (Python) │
└─────────────────────────┬────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Sub-agent System │
│ │
│ trellis-research → trellis-implement → trellis-check│
│ │
│ Each sub-agent receives precise context via JSONL │
└─────────────────────────┬────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────┐
│ Your Project Code │
│ (AI generates/modifies code per specs) │
└──────────────────────────────────────────────────────┘
workflow.md: the single source of truth
.trellis/workflow.md is the one file that defines the Plan / Execute / Finish contract — Phase 1 (1.0 task.py create → 1.4 task.py start), Phase 2 (2.1 trellis-implement → 2.2 trellis-check), Phase 3 (3.1 final check → 3.4 /trellis:finish-work). Every other piece of the system pulls from it:
- SessionStart hook auto-injects its TOC + step-level details so AI knows the contract before you type anything.
/trellis:continue reads .current-task + task status, then consults workflow.md to figure out which step the task is on and what the next action should be.
- Skills and sub-agents receive it via JSONL (
workflow.md is the first entry in the default implement.jsonl).
- Task state — default statuses
planning / in_progress / completed (set automatically by task.py create / start / archive) map one-to-one to the three phases in workflow.md. workflow.md has per-status reminder blocks; add custom statuses there when you need more states.
Edit workflow.md to change the workflow for your project; every platform, skill, sub-agent, and command re-reads it on the next session. There is no other place where the workflow is hard-coded.
Session Startup Flow
SessionStart hook fires automatically, so the platform-specific session-start.py / session-start.js injects Trellis context at every session open with no manual step. Describe your task directly; optionally run /trellis:start for an explicit context report.
Codex auto-reads AGENTS.md at the repo root every session. When codex_hooks = true is enabled in ~/.codex/config.toml, the SessionStart hook also runs and behaves identically to the hook-capable tab above. Without the flag, the prelude alone covers the basics.
Kiro delivers Trellis content through its .kiro/ skill files. Agent Hooks are user-configured in Kiro (save triggers, build triggers, etc.); Trellis does not ship any. Run @trellis:start when you want a full context report.
These three platforms have workflows + skills only — no hooks. Manually run the entry workflow (/start.md / .agent/workflows/start.md / /trellis-start); the AI reads workflow.md + get_context.py + spec indexes inline, then asks what you want to do.
When /trellis:start runs (or when a SessionStart hook fires automatically), here’s what happens:
/trellis:start
│
▼
┌────────────────────────────────────────┐
│ Hook or command body: │
│ │
│ Read .trellis/.developer → identity│
│ Read .trellis/workflow.md → workflow│
│ Read workspace/{name}/index → history │
│ Read git log → commits │
│ Read .trellis/tasks/ → tasks │
└───────────────────┬────────────────────┘
│
▼
┌──────────────────────────┐
│ Context injected into AI│
└────────────┬─────────────┘
│
▼
┌──────────────────────────────────┐
│ start.md command runs: │
│ │
│ Step 1: Read workflow.md │
│ Step 2: Run get_context.py │
│ Step 3: Read spec indexes │
│ Step 4: Report & ask user │
└──────────────────────────────────┘
Spec Injection Mechanism
Automatic sub-agent spec injection relies on the platform’s PreToolUse hook (or equivalent). Claude Code, Cursor, OpenCode, CodeBuddy, and Droid support this today. On the other platforms, the main session reads the JSONL files itself and passes the relevant content into sub-agent prompts.
inject-subagent-context (Python on Claude Code, JS on OpenCode) is the core spec-injection engine. When the main session spawns a trellis-* sub-agent, this hook intercepts the call and injects the right JSONL-specified context.
Workflow:
Main session spawns: Task(subagent_type="trellis-implement", prompt="...")
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Hook: inject-subagent-context (runs BEFORE sub-agent) │
│ │
│ The HOOK (not the sub-agent) does the reading: │
│ │
│ 1. Hook reads .trellis/.current-task │
│ → current task directory path │
│ │
│ 2. Hook reads {task_dir}/implement.jsonl │
│ → list of spec files this sub-agent needs │
│ │
│ 3. Hook reads each file referenced in the JSONL │
│ → .trellis/spec/backend/index.md │
│ → .trellis/spec/backend/database-guidelines.md │
│ → ... │
│ │
│ 4. Hook reads prd.md (requirements) and info.md (design) │
│ │
│ 5. Hook concatenates everything and prepends it to the │
│ sub-agent's prompt as "additionalContext" │
└───────────────────────┬──────────────────────────────────────┘
│
▼
The sub-agent wakes up with this already in its prompt
(it does not read any of the files itself):
# trellis-implement Task
## Your Context
=== .trellis/spec/backend/index.md ===
(full content injected by hook)
=== .trellis/spec/backend/database-guidelines.md ===
(full content injected by hook)
=== {task_dir}/prd.md (Requirements) ===
(full content injected by hook)
## Your Task
Implement the feature
JSONL (JSON Lines) files tell the hook which files to inject into each sub-agent’s prompt. Each line is a JSON object:
{"file": ".trellis/workflow.md", "reason": "Project workflow and conventions"}
{"file": ".trellis/spec/backend/index.md", "reason": "Backend development guide"}
{"file": "src/services/auth.ts", "reason": "Existing auth implementation to align with"}
{"file": ".trellis/tasks/02-27-user-login/research/", "type": "directory", "reason": "Research findings"}
Field descriptions:
| Field | Required | Description |
|---|
file | Yes | Relative path to file or directory (relative to project root) |
reason | Yes | Why this file is needed |
type | No | Defaults to "file". In file mode the hook reads the referenced file as-is regardless of extension (source code, JSON, etc. all work). In "directory" mode the hook only picks up .md files inside, sorted by filename, max 20 — non-.md files are skipped. |
What to put in JSONL depends on what the sub-agent needs to execute the task:
- Specs and workflow:
.trellis/workflow.md, .trellis/spec/**/index.md, specific guideline files — shape how the sub-agent writes code.
- Existing code as reference: a single source file (
src/services/auth.ts) is useful when the sub-agent should align with an existing pattern. Prefer single-file entries; directory mode won’t pick up .ts / .py / etc.
- Research findings:
{task_dir}/research/ in directory mode pulls in every .md produced by trellis-research.
Three types of JSONL files:
| File | Used By | Typical Content |
|---|
implement.jsonl | trellis-implement | workflow.md + relevant specs + code pattern examples |
check.jsonl | trellis-check | quality checklist specs + test / lint rules |
research.jsonl | trellis-research | project structure overview + investigation targets |
Practical example (implement.jsonl for a backend task, after brainstorm adds context):
{"file": ".trellis/workflow.md", "reason": "Project workflow and conventions"}
{"file": ".trellis/spec/backend/index.md", "reason": "Backend development guide"}
{"file": ".trellis/spec/backend/database-guidelines.md", "reason": "DB patterns used by this task"}
{"file": ".trellis/tasks/02-27-user-login/research/", "type": "directory", "reason": "Research findings from trellis-research"}
Entries after the first two are task-specific and typically added with task.py add-context.
Injection behavior per sub-agent:
trellis-implement: injects implement.jsonl + prd.md + info.md
trellis-check: injects check.jsonl + prd.md (to understand intent)
trellis-research: injects project structure overview + research.jsonl (optional)
Sub-agent System
Trellis ships three sub-agents, each with a focused role, explicit restrictions, and a JSONL spec injection lane:
| Sub-agent | Role | Restriction | Primary context |
|---|
trellis-research | Codebase / doc search | Read-only | research.jsonl |
trellis-implement | Writing code | Forbidden from git commit | implement.jsonl + prd.md |
trellis-check | Verify + self-fix | Writes code only to fix | check.jsonl + prd.md |
Typical task flow in the main session:
trellis-brainstorm skill
│
│ Clarify scope, draft prd.md, create task directory + JSONL config
│ Spawn trellis-research sub-agent when investigation is needed
│
▼
trellis-before-dev skill
│
│ Read relevant spec files before coding starts
│
▼
Spawn trellis-implement sub-agent
│
│ Hook injects implement.jsonl context
│ Sub-agent writes the code, does not git commit
│
▼
Spawn trellis-check sub-agent
│
│ Hook injects check.jsonl context
│ Runs lint / typecheck / test, compares diff vs. quality checklist
│ Self-fixes within its internal retry loop
│
▼
/trellis:finish-work
│
│ Human validates + commits, then finish-work archives the task
│ and appends a session entry to the workspace journal