Skip to main content

Customizing the Workflow

/.trellis/workflow.md is the single source of truth for how Trellis runs development. Phase definitions, skill routing, per-turn reminders, and the task.py command catalog all live in this one file. Forking the workflow means editing one markdown file — no Python, no hook code, no re-releasing Trellis. Before 0.5, workflow behavior was scattered across three places: hook Python scripts, configurator TypeScript, and command markdown. Keeping them in sync while forking was painful. 0.5 collapses the three into workflow.md.

What workflow.md controls

Section in workflow.mdWho reads itEffect
## Phase Index + ## Phase 1/2/3AI at session start (via SessionStart hook)Defines the three-phase flow and the step-by-step how-to inside each phase
### Skill RoutingAI at session startMaps user intent → which skill to load (e.g. “wants new feature” → trellis-brainstorm)
### DO NOT skip skillsAI at session startInlines the common excuses for skipping skills and the reasons why they’re wrong
## Workflow State Breadcrumbsinject-workflow-state.py on every UserPromptSubmitPer-turn reminder shown as <workflow-state>…</workflow-state> based on task status
### Task System (task.py command list)AI at session startReference for the 16 task.py subcommands grouped by purpose
All injection paths read workflow.md at runtime — you don’t need to rebuild anything after editing.

Changing the per-turn breadcrumbs

The per-turn <workflow-state> block is what nudges the AI at each message based on the current task’s status field. You can rewrite any of them without touching the hook script.
## Workflow State Breadcrumbs

[workflow-state:no_task]
No active task. If the user describes multi-step work, load trellis-brainstorm skill…
[/workflow-state:no_task]

[workflow-state:planning]
Complete prd.md via trellis-brainstorm skill; then run task.py start.
[/workflow-state:planning]

[workflow-state:in_progress]
Flow: implement → check → update-spec → finish
Check conversation history + git status to determine current step; do NOT skip check.
[/workflow-state:in_progress]

[workflow-state:completed]
User commits changes; then run task.py archive.
[/workflow-state:completed]
Rules:
  • Tag STATUS matches task.json.status. Defaults: planning / in_progress / completed, plus no_task when no task is active.
  • Hyphens and underscores are both allowed in the tag (blocked / in-review / needs_qa etc.).
  • If a status has no matching tag block, the hook falls back to a built-in default — safe to delete blocks you don’t need.
  • Keep each block short (~200 bytes). This is injected every turn; bloat means the AI pays attention cost on every message.

Adding a custom workflow state

Want a blocked state that nudges the AI to escalate instead of guessing?
[workflow-state:blocked]
Task is blocked waiting on external input. Don't start new implementation —
surface the blocker to the user or spawn `trellis-research` if more investigation helps.
[/workflow-state:blocked]
Then set the status on the task by editing task.json directly:
# Switch to a custom status
jq '.status = "blocked"' "$TASK_DIR/task.json" > "$TASK_DIR/task.json.tmp" && mv "$TASK_DIR/task.json.tmp" "$TASK_DIR/task.json"
From the next message onward, the AI sees the blocked breadcrumb injected per turn. Switch it back to in_progress the same way to resume the normal flow. Built-in task.py subcommands only transition between the default statuses (startin_progress, archivecompleted). Custom statuses are plain strings in task.json.status — the breadcrumb system doesn’t require pre-registering them, and task.py list --status <name> can filter by any string.

Changing the skill routing table

The table under ### Skill Routing is what the AI consults when deciding whether to load an auto-trigger skill.
| User intent                                  | Skill                |
|----------------------------------------------|----------------------|
| Wants a new feature / requirement unclear    | trellis-brainstorm   |
| About to write code / start implementing     | trellis-before-dev   |
| Finished writing / want to verify            | trellis-check        |
| Stuck / fixed same bug several times         | trellis-break-loop   |
| Spec needs update                            | trellis-update-spec  |
Add a row for a custom skill you shipped (see Chapter 12: Custom Skills):
| User wants a test plan before writing code   | trellis-test-plan    |
No code change needed — next session, the AI reads the updated table and routes correctly.

Adding or reshaping a Phase

The Phase sections in workflow.md are plain markdown. You can:
  • Add a Phase 4: Review — define steps (4.1, 4.2, …) and their how-to text. Reference it from breadcrumbs ([workflow-state:in_review]).
  • Split Plan into two branches (A/B) — replace step numbering with 1A.1 / 1B.1 naming; the AI follows what’s in the text.
  • Shorten Finish — delete steps you don’t care about (e.g. drop 3.2 debug retrospective).
Keep the Phase Index in sync with the detailed Phase sections — the index is inlined at SessionStart so the AI sees both consistently.
get_context.py --mode phase --step X.Y parses ## Phase X headings + #### X.Y step headings to extract the step body. If you rename or reshape sections, make sure the step anchors still parse (heading depth is significant).

What NOT to edit

A handful of conventions are consumed by scripts and shouldn’t drift:
Don’t changeWhy
The tag format [workflow-state:STATUS]…[/workflow-state:STATUS]inject-workflow-state.py parses this literally
Heading depth for Phase / step (## Phase X + #### X.Y)get_context.py --mode phase --step X.Y depends on it
task.py subcommand names in the task lifecycle listThey must match the actual CLI; renaming in docs doesn’t rename the script
Everything else — phrasing, ordering, adding sections, rewriting how-to text — is fair game.

When your change takes effect

ChangeWhen AI sees it
Breadcrumb textNext user message (re-read on every UserPromptSubmit)
Phase / step bodyNext session (re-read on SessionStart)
Skill routing tableNext session
Forks don’t need to republish anything. Commit your edited workflow.md to the repo and your team picks up the change on the next session start.