Technical Documentation: mindfold-ai/Trellis
ℹ️ Provenance: Multi-Tier Ingestion · CodeWiki Reference
Overview (README.md)
Why Trellis?
| Capability | What it changes |
|---|---|
| Auto-injected specs | Write conventions once in .trellis/spec/, then let Trellis inject the relevant context into each session instead of repeating yourself. |
| Task-centered workflow | Keep PRDs, implementation context, review context, and task status in .trellis/tasks/ so AI work stays structured. |
| Project memory | Journals in .trellis/workspace/ preserve what happened last time, so each new session starts with real context. |
| Team-shared standards | Specs live in the repo, so one person's hard-won workflow or rule can benefit the whole team. |
| Multi-platform setup | Bring the same Trellis structure to 22 AI coding platforms instead of rebuilding your workflow per tool. |
Prerequisites:
- Node.js >= 18
- Python >= 3.9
Quick Start
# 1. Install Trellis
npm install @mindfoldhq/trellis@latest
# 2. Initialize in your repo
trellis init -u your-name
# 3. Or initialize with the platforms you actually use
trellis init --cursor --opencode --codex -u your-nameSee the Quick Start and Supported Platforms guides for setup details.
How to Use
The workflow is simple:
- Describe what you want in natural language.
- Brainstorm with the AI one question at a time until the PRD is clear, then implementation begins.
- Let it run — the AI calls Trellis Implement and auto-checks the result against specs, lint, type-check, and tests.
- Type
/trellis:finish-workwhen the work is done or the session context fills up. Trellis archives the task and updates journals.
How It Works
Trellis runs a 4-phase loop with auto-invoked skills and sub-agents:
- Plan —
trellis-brainstormwalks through requirements one question at a time and writesprd.md. Research-heavy items go to atrellis-researchsub-agent. The result is curated specs + research files referenced fromimplement.jsonl/check.jsonl. - Implement — a
trellis-implementsub-agent writes code from the PRD with the curated context auto-injected, no git commit. - Verify — a
trellis-checksub-agent reviews the diff against specs and runs lint, type-check, and tests, self-fixing where it can. - Finish — a final check runs, then
trellis-update-specpromotes new learnings back into.trellis/spec/so the next session starts smarter.
Resources
| Need | Link |
|---|---|
| Install Trellis in a repo | Quick Start |
| Understand platform differences | Supported Platforms |
| See the workflow in practice | Real-World Scenarios |
| Start from spec templates | Spec Templates |
| Track releases | Changelog |
FAQ
How is Trellis different from CLAUDE.md, AGENTS.md, or .cursorrules?
Those files are useful entry points, but they tend to become monolithic. Trellis adds scoped specs, task PRDs, workflow gates, workspace memory, and platform-aware generated files around them.
Is Trellis only for Claude Code?
No. Trellis is a project layer that works across multiple coding agents and IDEs.
Is Trellis for solo developers or teams?
Both. Solo developers use it for memory and repeatable workflow. Teams get the larger benefit: shared standards, task boundaries, reviewable context, and platform portability.
Do I have to write every spec file manually?
No. Many teams start by letting AI draft specs from existing code and then tighten the important parts by hand. Trellis works best when you keep the high-signal rules explicit and versioned.
Can teams use this without constant conflicts?
Yes. Personal workspace journals stay separate per developer, while shared specs and tasks stay in the repo where they can be reviewed and improved like any other project artifact.
Can I temporarily compare a project with and without Trellis?
Yes. trellis ablate temporarily removes all supported project-owned Trellis
surfaces after creating a verified recovery transaction outside the project.
Start a fresh agent session for the comparison, then run trellis restore to
recover the exact prior state. Use --dry-run to preview either operation.
The private recovery transaction includes exact .trellis task, spec, and
workspace bytes, which may contain user-authored sensitive text, and is kept
until restore verifies successfully.
This is different from trellis uninstall (permanent removal) andTRELLIS_HOOKS=0 (hooks only). Ablation does not launch agents, manage
worktrees, hide Git changes, or remove the global CLI, channel logs, or host
transcripts. If a managed path changes while ablated, restore refuses all
writes until the conflict is resolved.
Star History
Community & Resources
.Trellis Spec Docs Site Docs Index (`.trellis/spec/docs-site/docs/index.md`)
Mintlify Documentation Guidelines
Best practices for writing and maintaining documentation in this Mintlify project.
Overview
This project uses Mintlify as the documentation platform. All content is written in MDX (Markdown + JSX) and configured via docs.json.
Guidelines Index
| Guide | Description | Status |
|---|---|---|
| Directory Structure | File organization and naming conventions | Ready |
| MDX Guidelines | MDX syntax, frontmatter, components | Ready |
| Config Guidelines | docs.json configuration patterns | Ready |
| Plugin Guidelines | Claude Code plugin manifest patterns | Ready |
| Style Guide | Writing style + changelog/release-notes voice | Ready |
| ASCII-Art Alignment | Box-drawing & CJK alignment in diagrams | Ready |
| Sync on Change | Which docs-site pages to update when Trellis workflow / platforms / commands / skills change | Ready |
| Release Lifecycle | docs-site/scripts/ lifecycle (release-only / +beta / +rc), when and how to use them |
Ready |
Quick Reference
Local Development
# Start dev server
mintlify dev
# Custom port
mintlify dev --port 3333File Types
| Extension | Purpose |
|---|---|
.mdx |
Documentation pages |
.json |
Configuration (docs.json) or OpenAPI specs |
.svg/.png |
Images and logos |
Key Files
| File | Purpose |
|---|---|
docs.json |
Main configuration |
index.mdx |
Homepage |
snippets/*.mdx |
Reusable content |
Deployment
- Preview: Automatic on PR creation
- Production: Push to
mainbranch triggers deploy
Language: All documentation should be written in English unless specified otherwise.
.Trellis Spec Docs Site Docs Ascii Art Alignment (`.trellis/spec/docs-site/docs/ascii-art-alignment.md`)
ASCII-Art Diagram Alignment in Mintlify
How to make box-drawing diagrams render correctly in browser, especially with CJK text.
Problem Summary
ASCII-art diagrams using box-drawing characters (─│┌┐└┘┬┤├┼) break in the browser for two reasons:
| Problem | Root Cause | Affected |
|---|---|---|
| Box-drawing chars wider than ASCII | JetBrains Mono (Mintlify default) renders ─│┌ at ~1.8× ASCII width |
All languages |
| CJK chars not exactly 2× ASCII | Browser CJK fallback fonts render Chinese at ~1.7× ASCII instead of terminal-standard 2× | Chinese/Japanese/Korean |
Solution 1: English-Only Diagrams (Font Override)
For diagrams with only ASCII text, switching the code block font to Menlo fixes everything — Menlo renders box-drawing chars at exactly 1× ASCII width.
CSS in styles.css:
pre code {
font-family: Menlo, Monaco, 'Courier New', monospace !important;
}Use standard markdown code blocks (```). No other changes needed.
Solution 2: Chinese CJK Diagrams (2ch Inline-Block)
For diagrams mixing Chinese text with box-drawing borders, no font can guarantee CJK = 2× ASCII in browsers. The solution is CSS-forced widths.
Technique
- Wrap each CJK character in
<b>tags - CSS forces
<b>todisplay:inline-block;width:2ch— exactly 2 monospace columns - Use
dangerouslySetInnerHTMLto bypass MDX parsing issues
CSS in styles.css:
pre.cjk-diagram {
display: block !important; /* Override Mintlify's display:flex on */
padding: 14px 16px;
border-radius: 16px;
font-size: 14px;
line-height: 24px;
font-family: Menlo, Monaco, 'Courier New', monospace;
overflow-x: auto;
white-space: pre;
margin: 16px 0;
background: #f8f8f8;
color: #1e1e1e;
}
.dark pre.cjk-diagram {
background: #1e1e1e;
color: #d4d4d4;
}
pre.cjk-diagram b {
display: inline-block;
width: 2ch;
text-align: center;
font-weight: normal;
}MDX usage:
/* Detailed source-code truncated for AI context efficiency. */
Need ASCII-art diagram in docs?
├── English only?
│ └── Use standard ``` code block + Menlo font override ✓
└── Contains CJK text?
└── Use <pre class="cjk-diagram"> + dangerouslySetInnerHTML
+ wrap each CJK char in <b> tags ✓.Trellis Spec Docs Site Docs Config Guidelines (`.trellis/spec/docs-site/docs/config-guidelines.md`)
Configuration Guidelines
docs.json configuration patterns and conventions.
File Location
Main configuration file: docs.json (project root)
Schema Reference
{
"$schema": "https://mintlify.com/docs.json"
}Always include the schema for editor autocomplete.
Core Configuration
Theme and Branding
{
"theme": "mint",
"name": "Project Name",
"colors": {
"primary": "#16A34A",
"light": "#07C983",
"dark": "#15803D"
},
"favicon": "/favicon.svg",
"logo": {
"light": "/logo/light.svg",
"dark": "/logo/dark.svg"
}
}Color Guidelines
| Color | Purpose | Format |
|---|---|---|
primary |
Main brand color | Hex (e.g., #16A34A) |
light |
Light mode accent | Hex |
dark |
Dark mode accent | Hex |
Navigation
Tab-Based Navigation
{
"navigation": {
"tabs": [
{
"tab": "Guides",
"groups": [
{
"group": "Getting Started",
"pages": ["index", "quickstart", "development"]
}
]
},
{
"tab": "API Reference",
"groups": [
{
"group": "Endpoints",
"pages": ["api-reference/introduction"]
}
]
}
]
}
}Group-Based Navigation
{
"navigation": {
"groups": [
{
"group": "Getting Started",
"pages": ["index", "quickstart"]
},
{
"group": "Guides",
"pages": ["essentials/settings", "essentials/navigation"]
}
]
}
}Page References
| Format | Example |
|---|---|
| Root page | "index" |
| Nested page | "essentials/settings" |
| OpenAPI endpoint | "openapi.json GET /users" |
Collapsible Nested Groups
To make a navigation group collapsible (collapsed by default), use the expanded property on nested groups (groups within groups):
{
"navigation": {
"groups": [
{
"group": "Community",
"pages": [
"showcase/index",
"contribute/index",
{
"group": "Blog",
"expanded": false,
"pages": ["blog/index", "blog/post-one", "blog/post-two"]
}
]
}
]
}
}Warning: The
expandedproperty only works on nested groups (groups within groups). Top-level groups are always expanded and cannot be collapsed. If you putexpanded: falseon a top-level group, it will be silently ignored.
Common Mistake: Trying to collapse a top-level group:
// ❌ WRONG - This does NOT work
{
"group": "Blog",
"expanded": false, // Ignored! Top-level groups can't collapse
"pages": ["blog/index", "blog/post-one"]
}// ✅ CORRECT - Nest it under a parent group
{
"group": "Community",
"pages": [
"showcase/index",
{
"group": "Blog",
"expanded": false, // Works! This is a nested group
"pages": ["blog/index", "blog/post-one"]
}
]
}Global Elements
Anchors (Sidebar Links)
{
"navigation": {
"global": {
"anchors": [
{
"anchor": "Documentation",
"href": "https://example.com/docs",
"icon": "book-open-cover"
},
{
"anchor": "GitHub",
"href": "https://github.com/example",
"icon": "github"
}
]
}
}
}Navbar
{
"navbar": {
"links": [
{
"label": "Support",
"href": "mailto:[email protected]"
}
],
"primary": {
"type": "button",
"label": "Dashboard",
"href": "https://dashboard.example.com"
}
}
}Footer
{
"footer": {
"socials": {
"x": "https://x.com/example",
"github": "https://github.com/example",
"linkedin": "https://linkedin.com/company/example"
}
}
}API Documentation
OpenAPI Integration
{
"navigation": {
"tabs": [
{
"tab": "API Reference",
"groups": [
{
"group": "Endpoints",
"openapi": "api-reference/openapi.json"
}
]
}
]
}
}Contextual Options
Code block right-click menu options:
{
"contextual": {
"options": ["copy", "view", "chatgpt", "claude", "perplexity", "mcp", "cursor", "vscode"]
}
}Internationalization (i18n)
Language-Based Navigation
For multi-language support, use navigation.languages instead of navigation.tabs:
{
"navigation": {
"languages": [
{
"language": "en",
"tabs": [
{
"tab": "Guides",
"groups": [
{
"group": "Getting Started",
"pages": ["index", "quickstart"]
}
]
}
]
},
{
"language": "zh",
"tabs": [
{
"tab": "指南",
"groups": [
{
"group": "开始使用",
"pages": ["zh/index", "zh/quickstart"]
}
]
}
]
}
]
}
}i18n File Structure
docs/
├── index.mdx # English homepage
├── quickstart.mdx # English quickstart
├── zh/
│ ├── index.mdx # Chinese homepage
│ └── quickstart.mdx # Chinese quickstart
└── docs.json # Both languages configuredImportant: Each language needs its own complete navigation structure. Pages in
zh/must be referenced as"zh/pagename"in the navigation.
i18n Naming Conventions
Use consistent naming for common page types across languages:
| English | Chinese | Usage |
|---|---|---|
Overview |
概览 |
Index/landing pages for sections |
Guides |
指南 |
How-to documentation section |
Quickstart |
快速开始 |
Getting started guides |
FAQ |
常见问题 |
Frequently asked questions |
Example frontmatter:
# English (templates/specs-index.mdx)
---
title: 'Overview'
description: 'Spec templates for common stacks'
---
# Chinese (zh/templates/specs-index.mdx)
---
title: '概览'
description: '常见技术栈的规范模板'
---Rule: Don't mix languages in titles. English pages use English titles, Chinese pages use Chinese titles.
i18n for Content Sections (Blog, Changelog, etc.)
When a content section (like Blog) has language-specific posts, separate the content by directory:
docs/
├── blog/ # English blog
│ ├── index.mdx # Lists ONLY English posts
│ ├── post-one.mdx
│ └── post-two.mdx
├── zh/
│ └── blog/ # Chinese blog
│ ├── index.mdx # Lists ONLY Chinese posts
│ ├── post-one.mdx # Same filename, different content
│ └── post-two.mdx
└── docs.jsonNavigation configuration:
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [
{
"group": "Blog",
"pages": ["blog/index", "blog/post-one", "blog/post-two"]
}
]
},
{
"language": "zh",
"groups": [
{
"group": "Blog",
"pages": ["zh/blog/index", "zh/blog/post-one", "zh/blog/post-two"]
}
]
}
]
}
}Common Mistake: Putting all blog posts (both languages) in one
blog/folder and showing them all in both navigations. This clutters each language's view with posts they can't read.
Key principle: Each language's content section should be self-contained — its own directory, its own index, its own navigation entries.
Custom Styling
Override Default Styles
Create styles.css in project root to customize Mintlify appearance:
/* Example: Hide language switcher flag icons */
img[src*='cloudfront.net/flags'] {
display: none !important;
}
/* Example: Customize button styles */
button[aria-haspopup='menu'] > div:first-child {
display: none !important;
}Tip: Use browser DevTools to inspect Mintlify elements and find the right selectors.
Best Practices
DO
- Include
$schemafor autocomplete - Organize navigation logically (user journey)
- Use descriptive group names
- Keep tab count reasonable (2-4 tabs)
DON'T
- Create deeply nested navigation
- Use inconsistent naming between pages and nav
- Forget to add new pages to navigation
- Mix different navigation patterns unnecessarily
Checklist: Adding New Content
- Create the
.mdxfile - Add frontmatter with title and description
- Add page to appropriate group in
docs.json - Verify navigation order makes sense
- Test locally with
mintlify dev
.Trellis Spec Docs Site Docs Directory Structure (`.trellis/spec/docs-site/docs/directory-structure.md`)
Directory Structure
File organization and naming conventions for this Mintlify documentation project.
⚠️ Critical: Manual Navigation Update Required
Mintlify does NOT auto-discover pages. Every new page must be manually added to docs.json.
// docs.json - Must update navigation when adding pages
{
"navigation": {
"languages": [
{
"language": "en",
"groups": [
{
"group": "Spec Templates",
"pages": [
"templates/specs-index",
"templates/specs-node", // ← Add new pages here
"templates/specs-python"
]
}
]
},
{
"language": "zh",
"groups": [
// Same structure for Chinese
]
}
]
}
}Checklist for new pages:
- Create
.mdxfile - Add to English navigation in
docs.json - Create Chinese version in
zh/directory - Add to Chinese navigation in
docs.json
Project Structure
docs/
├── docs.json # Main configuration
├── index.mdx # Homepage
├── quickstart.mdx # Quick start guide
├── development.mdx # Development setup
│
├── essentials/ # Core documentation
│ ├── settings.mdx
│ ├── navigation.mdx
│ ├── markdown.mdx
│ ├── code.mdx
│ ├── images.mdx
│ └── reusable-snippets.mdx
│
├── api-reference/ # API documentation
│ ├── introduction.mdx
│ ├── openapi.json # OpenAPI specification
│ └── endpoint/
│ ├── get.mdx
│ ├── create.mdx
│ ├── delete.mdx
│ └── webhook.mdx
│
├── ai-tools/ # AI tools guides
│ ├── cursor.mdx
│ ├── claude-code.mdx
│ └── devin.mdx
│
├── snippets/ # Reusable content fragments
│ └── snippet-intro.mdx
│
├── images/ # Image assets
│ └── *.png|jpg|gif
│
└── logo/ # Brand assets
├── light.svg
└── dark.svgNaming Conventions
Files
| Type | Convention | Example |
|---|---|---|
| MDX pages | kebab-case.mdx |
getting-started.mdx |
| Directories | kebab-case/ |
api-reference/ |
| Images | kebab-case.png |
hero-image.png |
| Snippets | kebab-case.mdx |
api-key-setup.mdx |
Directory Organization
| Directory | Purpose | When to Use |
|---|---|---|
Root (/) |
Top-level pages | Homepage, quickstart, main entry points |
essentials/ |
Core platform docs | Settings, navigation, markdown syntax |
api-reference/ |
API documentation | Endpoints, OpenAPI specs |
snippets/ |
Reusable content | Content used in multiple pages |
images/ |
Image assets | Screenshots, diagrams |
logo/ |
Brand assets | Light/dark mode logos |
Rules
DO
- Group related pages in directories
- Use descriptive, SEO-friendly file names
- Keep directory nesting shallow (max 2 levels)
- Place reusable content in
snippets/
DON'T
- Use underscores in file names (use hyphens)
- Create deeply nested directories
- Mix different content types in same directory
- Use spaces or special characters in names
Internationalization (i18n) Structure
For bilingual documentation, follow this pattern:
docs/
├── index.mdx # English homepage
├── quickstart.mdx # English quickstart
├── guides/ # English guides
│ └── *.mdx
├── blog/ # English blog (EN posts only)
│ ├── index.mdx # Lists EN posts only
│ └── *.mdx
│
├── zh/ # Chinese content root
│ ├── index.mdx # Chinese homepage
│ ├── quickstart.mdx # Chinese quickstart
│ ├── guides/ # Chinese guides
│ │ └── *.mdx
│ └── blog/ # Chinese blog (ZH posts only)
│ ├── index.mdx # Lists ZH posts only
│ └── *.mdx
│
├── changelog.mdx # Shared (no translation needed)
└── docs.jsoni18n Rules
| Content Type | English Path | Chinese Path |
|---|---|---|
| Main pages | page.mdx |
zh/page.mdx |
| Section pages | section/page.mdx |
zh/section/page.mdx |
| Blog posts | blog/post.mdx |
zh/blog/post.mdx |
| Shared content | changelog.mdx |
changelog.mdx (same file) |
Common Mistake
Don't: Mix languages in one folder
blog/
├── post-en.mdx # ❌ Clutters both language views
└── post-zh.mdxDo: Separate by language directory
blog/
└── post.mdx # ✅ English only
zh/blog/
└── post.mdx # ✅ Chinese onlyAdding New Content
New Page (Bilingual)
| Step | Action | File |
|---|---|---|
| 1 | Create English page | section/page.mdx |
| 2 | Create Chinese page | zh/section/page.mdx |
| 3 | Update EN navigation | docs.json → languages[0].groups |
| 4 | Update ZH navigation | docs.json → languages[1].groups |
| 5 | Test locally | pnpm dev |
⚠️ Steps 3-4 are mandatory. Pages won't appear in sidebar without navigation entries.
New Section
- Create new directory with descriptive name
- Add pages inside the directory
- Create a group in
docs.jsonnavigation (both EN and ZH)
New Snippet
- Create
.mdxfile insnippets/ - Reference with
<Snippet file="filename.mdx" />
.Trellis Spec Docs Site Docs Mdx Guidelines (`.trellis/spec/docs-site/docs/mdx-guidelines.md`)
MDX Guidelines
MDX syntax, frontmatter, and component usage patterns.
Frontmatter
Every MDX file must start with frontmatter:
---
title: 'Page Title'
description: 'Brief description for SEO (150-160 characters)'
---Optional Frontmatter Fields
| Field | Purpose | Example |
|---|---|---|
sidebarTitle |
Shorter title for sidebar | "Quick Start" |
icon |
Navigation icon | "rocket" |
tag |
Badge/label | "NEW" |
mode |
Page mode | "wide" |
Example with All Fields
---
title: 'Getting Started with the API'
description: 'Learn how to authenticate and make your first API call'
sidebarTitle: 'Getting Started'
icon: 'play'
tag: 'Updated'
---Components
Card
Use for navigation links and feature highlights:
<Card title="Quick Start" icon="rocket" href="/quickstart">
Get up and running in 5 minutes.
</Card>Horizontal variant:
<Card title="Start Here" icon="rocket" href="/quickstart" horizontal>
Follow our quickstart guide.
</Card>Columns
Use for multi-column layouts:
<Columns cols={2}>
<Card title="First" icon="star" href="/first">
First card content.
</Card>
<Card title="Second" icon="star" href="/second">
Second card content.
</Card>
</Columns>Tabs
Use for alternative content views:
<Tabs>
<Tab title="npm">```bash npm install package-name ```</Tab>
<Tab title="yarn">```bash yarn add package-name ```</Tab>
</Tabs>Accordion
Use for collapsible content:
<AccordionGroup>
<Accordion title="What is Mintlify?">Mintlify is a documentation platform.</Accordion>
<Accordion title="How much does it cost?">
Free tier available, Pro starts at $300/month.
</Accordion>
</AccordionGroup>CodeGroup
Use for multi-language code examples:
<CodeGroup>
```javascript Node.js
const response = await fetch('/api/users');
```
```python Python
response = requests.get('/api/users')
```
```bash cURL
curl https://api.example.com/users
```
</CodeGroup>Snippet
Use for reusable content:
<Snippet file="api-key-setup.mdx" />Code Blocks
Basic Syntax
```language filename
code here
```With Filename
```javascript app.js
const express = require('express');
const app = express();
```Supported Languages
Common: javascript, typescript, python, bash, json, yaml, sql, go, rust
Callouts
Note
<Note>This is important information.</Note>Warning
<Warning>Be careful with this action.</Warning>Info
<Info>Additional context here.</Info>Tip
<Tip>Helpful suggestion here.</Tip>Common Mistakes
Nested Code Blocks in Templates
Problem: Using escaped backticks to show code block syntax renders broken:
\`\`\`bash
npm install
\`\`\`Solution: Use <CodeGroup> component instead:
<CodeGroup>```bash Install npm install ```</CodeGroup>Duplicate Headings in Template Examples
Problem: Showing multiple template examples with same headings triggers MD024 lint error.
Solution: Add lint disable comment at file start:
---
title: 'Templates'
---
## Template 1
### Project structure
...
## Template 2
### Project structure
...Mixed Block/Inline JSX Closing Tag
Symptom: Whole page renders as A parsing error occurred. Please contact the owner of this website. The title also falls back to the file slug (e.g. "Ch12 multi platform" instead of the frontmatter title), confirming MDX compilation failed for the entire file — not just the block.
Cause: Callout components (<Note>, <Warning>, ``, <Tip>) accept either inline form (tag + content + close on one line) OR block form (tags on own lines). Mixing the two breaks the MDX parser.
<Note>
Don't pick **Full re-initialize** — it overwrites existing config.</Note>
<Note>Don't pick **Full re-initialize** — it overwrites existing config.
</Note><Note>Don't pick **Full re-initialize** — it overwrites existing config.</Note>
<Note>
Don't pick **Full re-initialize** — it overwrites existing config.
</Note>Prevention: Pick one form per callout and keep both tags consistent. When the body contains backtick code spans, bolded text, or more than one sentence, default to the block form for readability.
Bulleted List Inside `` / `` / `` / ``
Symptom: Same as Mixed Block/Inline above — page renders as A parsing error occurred, title falls back to slug.
Cause: Prettier reformats Markdown inside JSX block bodies. Bulleted lists indented under the opening tag get pulled to column 0; the closing tag then gets indented 2 spaces. Mintlify sees the bullets as content outside the <Note> and the closing tag as misplaced. Parse fails.
<Note>
Hook support varies by platform:
- **SessionStart** ships on Claude Code, Cursor, OpenCode...
- **PreToolUse** ships on a smaller subset...
</Note>
<Note>
Hook support varies by platform:
- **SessionStart** ships on Claude Code, Cursor, OpenCode...
- **PreToolUse** ships on a smaller subset...
</Note>Prevention: Don't put bulleted lists inside callouts. Keep the callout to a single inline summary line; place the list outside it.
<Note>Hook support varies by platform and by event — see the per-event matrix below.</Note>
- **SessionStart** ships on Claude Code, Cursor, OpenCode...
- **PreToolUse** ships on a smaller subset...If the bullets really must be inside the callout (rare — usually the inline-summary + list-outside form reads better anyway), use raw HTML <li> instead of Markdown bullets so Prettier won't reformat them.
Why this isn't caught by prettier --check or markdownlint-cli2: both pass on the broken output. Only mintlify broken-links (or rendering the page in a Mintlify dev server) surfaces the parse failure. Worth wiring into CI.
Table Column Alignment
Problem: markdownlint MD060 requires consistent table pipe alignment.
Solution: Ensure all columns align:
| Command | What it does |
| -------- | ------------------------------------------------- |
| `/start` | Start session. Loads context, shows current task. |
| Command | What it does |
| -------- | ------------------------------------------------- |
| `/start` | Start session. Loads context, shows current task. |Best Practices
DO
- Always include
titleanddescriptionin frontmatter - Use components for visual structure (Cards, Tabs)
- Keep descriptions under 160 characters for SEO
- Use appropriate callout types (Note, Warning, Tip)
- Include code examples with language identifiers
- Use
<CodeGroup>when showing code block syntax in examples
DON'T
- Skip frontmatter
- Use raw HTML when components exist
- Write overly long descriptions
- Mix component styles inconsistently
- Leave code blocks without language hints
- Use escaped backticks to show nested code blocks
.Trellis Spec Docs Site Docs Plugin Guidelines (`.trellis/spec/docs-site/docs/plugin-guidelines.md`)
Plugin Guidelines
Claude Code plugin manifest patterns and conventions.
File Location
Plugin configuration directory: .claude-plugin/ (project root)
.claude-plugin/
├── marketplace.json # Marketplace manifest (required)
├── plugin.json # Plugin definition (required)
└── README.md # Installation docs (recommended)marketplace.json Schema
This file defines the marketplace and lists available plugins.
Required Fields
| Field | Type | Description |
|---|---|---|
name |
string | Marketplace identifier |
owner |
object | Owner info with name, email |
metadata |
object | Contains description |
plugins |
array | List of plugin definitions |
Plugin Entry Fields
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Plugin identifier |
source |
string | Yes | Path to plugin (relative) |
description |
string | No | What the plugin does |
author |
object | No | Object with name field |
homepage |
string | No | Plugin homepage URL |
repository |
string | No | Source repository URL |
license |
string | No | License identifier |
keywords |
array | No | Search keywords |
category |
string | No | Plugin category |
tags |
array | No | Descriptive tags |
Example
{
"name": "my-marketplace",
"owner": {
"name": "Your Name",
"email": "[email protected]"
},
"metadata": {
"description": "Description of your marketplace"
},
"plugins": [
{
"name": "my-plugin",
"source": "./path/to/plugin",
"description": "What this plugin does",
"author": {
"name": "Your Name"
},
"category": "workflow",
"tags": ["tag1", "tag2"]
}
]
}plugin.json Schema
This file defines the plugin itself.
Required Fields
| Field | Type | Description |
|---|---|---|
name |
string | Plugin identifier |
version |
string | Semantic version |
description |
string | What the plugin does |
Optional Fields
| Field | Type | Description |
|---|---|---|
author |
object | Object with name and url |
homepage |
string | Plugin homepage URL |
repository |
string | Source repository URL |
license |
string | License identifier |
keywords |
array | Search keywords |
skills |
array | Paths to skills |
agents |
array | Paths to agent files |
commands |
array | Paths to commands |
Example
{
"name": "my-plugin",
"version": "1.0.0",
"description": "What this plugin does",
"author": {
"name": "Your Name",
"url": "https://github.com/you"
},
"homepage": "https://example.com",
"repository": "https://github.com/you/repo",
"license": "MIT",
"keywords": ["keyword1", "keyword2"],
"skills": ["./skills/my-skill"]
}Common Mistakes
Mistake: author as string
Symptom: plugins.0.author: Invalid input: expected object, received string
// ❌ WRONG
{
"author": "Your Name"
}
// ✅ CORRECT
{
"author": {
"name": "Your Name"
}
}Mistake: Missing source field
Symptom: plugins.0.source: Invalid input
// ❌ WRONG - No source
{
"plugins": [
{
"name": "my-plugin",
"description": "..."
}
]
}
// ✅ CORRECT - Include source
{
"plugins": [
{
"name": "my-plugin",
"source": "./path/to/plugin",
"description": "..."
}
]
}Mistake: Missing root-level fields
Symptom: name: expected string, received undefined or owner: expected object, received undefined
// ❌ WRONG - Missing name and owner
{
"plugins": [...]
}
// ✅ CORRECT - Include all root fields
{
"name": "marketplace-name",
"owner": {
"name": "Owner Name",
"email": "[email protected]"
},
"metadata": {
"description": "..."
},
"plugins": [...]
}Mistake: Missing version in plugin.json
Symptom: Validation fails silently or during marketplace install
// ❌ WRONG - No version
{
"name": "my-plugin",
"description": "..."
}
// ✅ CORRECT - Include version
{
"name": "my-plugin",
"version": "1.0.0",
"description": "..."
}Mistake: Component fields as strings instead of arrays
Symptom: agents: Invalid input or similar
// ❌ WRONG - String value
{
"skills": "./skills"
}
// ✅ CORRECT - Array value
{
"skills": ["./skills/my-skill"]
}Mistake: Directory paths for agents
Symptom: Validation fails for agents
// ❌ WRONG - Directory path
{
"agents": ["./agents/"]
}
// ✅ CORRECT - Explicit file paths
{
"agents": [
"./agents/planner.md",
"./agents/reviewer.md"
]
}Note:
skillsandcommandscan use directory paths, butagentsmust use explicit file paths.
Mistake: Adding hooks field
Symptom: Duplicate hooks file detected
// ❌ WRONG - Explicit hooks declaration
{
"hooks": ["./hooks/hooks.json"]
}
// ✅ CORRECT - No hooks field
{
// hooks/hooks.json is auto-loaded by convention
}Warning: Claude Code v2.1+ automatically loads
hooks/hooks.jsonby convention. Adding it explicitly causes a duplicate error.
Validation
Before publishing, validate your plugin:
claude plugin validate .claude-plugin/plugin.jsonOr from within Claude Code:
/plugin validate .Best Practices
DO
- Include
versionin plugin.json - Use object format for
author(not string) - Always include
sourcein plugin entries - Use arrays for component fields (
skills,agents,commands) - Use explicit file paths for
agents - Add a README.md with installation instructions
DON'T
- Use string values for
author - Omit required root fields (
name,owner) - Use directory paths for
agents - Add explicit
hooksfield (auto-loaded by convention) - Forget to validate before publishing
Reference
.Trellis Spec Docs Site Docs Release Lifecycle (`.trellis/spec/docs-site/docs/release-lifecycle.md`)
Docs-Site Release Lifecycle
How
docs-site/is structured acrossrelease-only,release+beta,release+rcstates, and the three scripts that drive transitions.
Convention: root = current stable
The directory layout pins one role per location:
| Path | Role |
|---|---|
docs-site/{start,advanced,...} (root) |
Current stable (latest GA). Default. |
docs-site/beta/{start,advanced,...} |
Active beta cycle (when one exists). |
docs-site/rc/{start,advanced,...} |
Active RC cycle (renamed from beta). |
Non-versioned trees (blog/, showcase/, contribute/, skills-market/, templates/, use-cases/, marketplace/, concepts/, essentials/, api-reference/, ai-tools/, guides/, snippets/, images/, logo/) live only at root and are read by every version.
Version path invariant
Before editing versioned docs, determine which release line the content belongs
to and verify the file path matches that line:
| Target line | Edit path | Do not edit |
|---|---|---|
| Current stable / GA | docs-site/{start,advanced,...} |
docs-site/beta/** or rc/** |
| Active beta | docs-site/beta/{start,advanced,...} |
root docs-site/{start,advanced} |
| Active RC | docs-site/rc/{start,advanced,...} |
root docs-site/{start,advanced} |
| Chinese stable | docs-site/zh/{start,advanced,...} |
docs-site/zh/beta/** or rc/** |
| Chinese beta | docs-site/zh/beta/{start,advanced,...} |
root docs-site/zh/{start,advanced} |
| Chinese RC | docs-site/zh/rc/{start,advanced,...} |
root docs-site/zh/{start,advanced} |
Do not use the version dropdown label in a rendered page as proof of source
scope. Mintlify renders all versions from one repository and docs.json, so the
only reliable source-of-truth is the MDX path plus the matching docs.json
version block.
When beta-only content accidentally lands in root, release users see beta
behavior under the Release selector. Treat that as a release-docs incident, not
as a rendering issue.
Pre-commit audit for versioned changes
Run a path-scope audit before committing workflow, phase, artifact, install, or
platform behavior changes:
cd docs-site
git diff --name-only --cached
# For beta-only behavior, changed files must be under beta/ or zh/beta/.
# For stable behavior, changed files must be root versioned paths or zh/ root
# versioned paths.Then grep for version-specific markers in the opposite tree. Example for a beta
workflow change:
rg -n "task-creation consent|codex-mode|<trellis-workflow>|planning artifact|`design\\.md`|`implement\\.md`" \
start advanced guides zh/start zh/advanced zh/guides -g "*.mdx"The command should return no hits except unrelated filename mentions such astrellis-implement.md.
4 lifecycle states
T0 release-only ← steady state between cycles
│ docs.json: versions = ["Release"]
│ files: root/{start, advanced, ...}
│
▼ start a beta cycle
T1 release + beta
│ docs.json: versions = ["Release", "Beta"]
│ files: root/... + beta/{start, advanced, ...}
│
▼ beta → rc
T2 release + rc
│ docs.json: versions = ["Release", "RC"]
│ files: root/... + rc/{start, advanced, ...} (rename beta/ → rc/)
│
▼ rc → release (GA promote)
T3 release-only ← back to T0; root is the new GA
│ docs.json: versions = ["Release"]
│ files: root/... (rc/* content folded into root, rc/ deleted)
▼Scripts
Three POSIX shell scripts in docs-site/scripts/:
| Script | Transition | What it does |
|---|---|---|
docs-beta-start.sh |
T0 → T1 | Copy versioned content (start/, advanced/, index.mdx) from root → beta/. Mirrors zh/. |
docs-beta-to-rc.sh |
T1 → T2 | git mv beta rc (and zh/beta → zh/rc). Bulk text replace @beta → @rc inside rc/* content. |
docs-promote.sh |
T2 → T3 | Detect dev tree (rc/ preferred over beta/), overwrite root versioned content with dev content, mirror in zh/, git rm dev tree. |
All three are content-copy / rename only. They never touch docs.json or the banner — those follow as manual edits because they're decision-driven.
Manual followups
Each script ends with a checklist of docs.json edits and content scrubs the maintainer must apply before committing. Always:
| After | Edit docs.json |
|---|---|
docs-beta-start.sh |
Add "Beta" version block to versions[]. Add banner. Bump beta install commands to @beta. |
docs-beta-to-rc.sh |
Rename "Beta" label → "RC". Update each page entry beta/* → rc/*. Update banner. |
docs-promote.sh |
Drop the "Beta" / "RC" version block from versions[]. Drop banner. Update navbar href. |
When to use each
| Scenario | Script | Trigger |
|---|---|---|
First beta of a new minor / major (e.g. 0.6.0-beta.0) |
docs-beta-start.sh |
Right before pnpm release:beta for the .0 |
| Beta cycle stabilizing → first RC | docs-beta-to-rc.sh |
Before pnpm release:rc for the -rc.0 |
| RC stable → cut GA | docs-promote.sh |
Before pnpm release:promote |
Subsequent beta / rc patches (-beta.1, -rc.1, ...) |
(none — just write the changelog mdx) | Per-patch; no structural change needed |
Per-patch flow (-beta.1 → -beta.2, -rc.1 → -rc.2, ...): just create changelog/v<version>.mdx (en + zh), add to top of pages list in docs.json, bump navbar href. No script invocation.
One-time historical flip (0.5.0 GA)
Before 0.5.0, the layout was inverted: root/ held the dev content (RC), and release/ held the previous GA archive (0.4.0). At 0.5.0 GA we flipped to the convention above:
- Root content (was
0.5.0-rc.X) became the new stable0.5.0GA in place release/(0.4.0 archive) was deleted (git rm -r); 0.4.x docs remain accessible via thev0.4.0git tagdocs.jsoncollapsed from 2 versions to 1 (Releaseonly)- The 3 scripts were introduced to keep the lifecycle reproducible going forward
After this flip, every future cycle uses the scripts; no further manual restructure should be needed.
First real-cycle run: 0.6.0 GA (2026-06-15)
The 0.5.0 flip created the scripts; 0.6.0 GA was the first cycle that actually exercised the full T0→T1→T2→T3 chain end-to-end with docs-promote.sh. Notes captured below to disambiguate the 0.5.0 precedent (which itself diverges from the standard flow because it was a one-time historical flip).
`docs.json` transformation differs from 0.5.0
| Cycle | Pre-flip state | Right edit pattern |
|---|---|---|
| 0.5.0 (historical flip) | RC block had root paths, Release block had release/* paths (old GA archive) |
Rename RC → Release label, drop the old Release block, git rm -r release/ |
| 0.6.0 (standard flow) | RC block has rc/* paths, Release block has root paths (current GA) |
Drop the RC block (its rc/* paths die when docs-promote.sh deletes the rc/ directory), keep Release as-is (its root paths now serve promoted v0.6 content), set Release.default = true |
Concrete edit list for the standard flow (post-docs-promote.sh, both languages):
delete d['banner']- Drop the
RCversion block fromversions[]entirely - Set
Release.default = true(the flag was onRC) - Insert
changelog/v<NEW_GA>andzh/changelog/v<NEW_GA>at the top of eachReleaseblock'sChangelogpages list - Update
navbar.links[label=Changelog].hrefto/changelog/v<NEW_GA>
First dual-package GA promote
0.6.0 was the first GA where both @mindfoldhq/trellis (CLI) and @mindfoldhq/trellis-core (SDK) ship in lockstep. bump-versions.js promote rewrites both package.json files and the CLI's dependencies["@mindfoldhq/trellis-core"] from workspace:* to the exact version at release time. release-preflight verify-packed-cli exists specifically to catch a divergence here — always run it before pnpm release:promote.
Stale navbar Changelog `href` gotcha
The 0.6.0 cycle shipped 24 betas + 1 RC, and the navbar Changelog href in docs.json was never updated through any of them — it sat at /changelog/v0.6.0-beta.22 at GA prep time. Per-patch beta/rc commits add the new mdx to the nav pages list, but they routinely forget the navbar href. Decision rule: the GA promote PR is the last chance to fix the navbar href, since GA is when readers actually start clicking the top-bar Changelog link.
Pre-ship adversarial verify is load-bearing
The 0.6.0 GA prep flow ran a 10-agent pre-ship verify before pnpm release:promote and it caught 2 RED blockers that would have shipped:
@mindfoldhq/trellis@betaleft in a bundled-skill markdown table after the lifecycle flip- Manifest's
**Bundled skills**section listed 3 of the 4 actually-shipping bundled skills
Both blockers were prose-only (no code defect); both would have only embarrassed-not-broken users. Still, the adversarial verify earned its keep — recommend running an equivalent check on every future GA. The 10 angles (bundled skills + manifest + changelogs en/zh + docs.json + root content + preflight + tests + dogfood + npm-ready) generalize to any subsequent minor.
Gotchas
`docs.json` doesn't auto-update
Scripts only touch content trees. Forgetting the docs.json followup leads to:
T1 → T2: pages still resolve atbeta/...URLs but dropdown labelsRC(404 on click)T2 → T3: staleRCdropdown remains while content is gone (404 on every page)
Always run mintlify dev locally after the script + manual edits to catch routing drift before push.
Banner is sticky
The RC banner has been "📦 Reading **RC** docs (0.5.0-rc.0)..." literally across rc.0–rc.7 because nothing auto-bumps the version inside it. Either:
- Treat the banner as "RC docs in general — check
trellis --versionfor your install" (current pattern), or - Bump it as part of
docs-beta-to-rc.shfollowup (current scripts do not — they only print a reminder)
MDX `` / `` closing tags must NOT be indented
When a <Note> block contains a markdown list, prettier auto-indents the closing tag to align with the list:
<Note>
- bullet
</Note> ← BREAKS Mintlify parser: "Expected closing tag </Note> after end of listItem"
</Note> ← correct: closing tag at column 0If you commit through lint-staged + prettier, expect re-indentation. Manually re-fix and commit, or add a // prettier-ignore if the project later supports it. Always run pnpm dev (mintlify) before pushing changelog mdx with these blocks.
Stash workflow when RC and GA prep overlap
If you're staging GA content (changelog/v0.5.0.mdx, scripts/, release/ deletions) while still needing to ship one more rc.X:
cd docs-site
git stash push -u -m "GA promote prep" # park GA changes
# ... work on rc.X (changelog mdx + docs.json bump) ...
git commit && git push
git stash pop # restore GA prep; resolve docs.json conflictThe docs.json conflict at pop is expected — the rc.X commit added v0.5.0-rc.X at the top of pages list, while the stash had v0.5.0 at the top. Keep both, with v0.5.0 first (GA), then v0.5.0-rc.X, then older entries.
.Trellis Spec Docs Site Docs Style Guide (`.trellis/spec/docs-site/docs/style-guide.md`)
Style Guide
Writing style and content standards for documentation.
Voice and Tone
Guidelines
| Aspect | Guideline |
|---|---|
| Voice | Professional, friendly, direct |
| Tense | Present tense ("Click the button" not "You will click") |
| Person | Second person ("You can..." not "Users can...") |
| Mood | Imperative for instructions ("Run the command") |
Examples
Good:
Run the following command to start the server.
Avoid:
The user should run the following command to start the server.
Page Structure
Standard Page Template
---
title: 'Clear, Action-Oriented Title'
description: 'What the reader will learn or accomplish (150 chars)'
---
Brief introduction paragraph explaining what this page covers.
## First Major Section
Content here...
### Subsection if needed
More detail...
## Second Major Section
Content here...
## Next Steps
Links to related content or next actions.Heading Hierarchy
| Level | Usage |
|---|---|
| H1 | Never use (title comes from frontmatter) |
| H2 | Major sections |
| H3 | Subsections |
| H4 | Rarely needed, avoid if possible |
Writing Guidelines
Titles
| Type | Format | Example |
|---|---|---|
| Page title | Title Case | "Getting Started with the API" |
| Section heading | Sentence case | "Configure your settings" |
| Description | Sentence | "Learn how to set up authentication." |
Technical Architecture Openings
Open technical architecture pages with the system thesis, then immediately ground it in concrete scope, modules, and source-of-truth files. Product positioning is acceptable when it names the architecture being documented; audience storytelling is not.
Good:
Trellis is a Team-level Agent Harness with built-in LLM wiki. In implementation terms, that means two systems share the same project files: the agent harness controls workflow execution, and the LLM wiki stores specs, tasks, research, and journals.
Avoid:
This page is for contributors and fork modders: engineers extending Trellis itself, not end users.
Why: Architecture docs should preserve the conceptual model, but the opening must quickly become a technical map. Audience labels and unsupported positioning prose make the page feel like a pitch instead of a technical contract.
Lists
Use bullet lists for:
- Non-sequential items
- Feature lists
- Requirements
Use numbered lists for:
- Step-by-step instructions
- Ordered processes
- Prioritized items
Code References
- Use backticks for inline code:
npm install - Use code blocks for multi-line code
- Always specify language for syntax highlighting
Content Types
Conceptual Content
Explains what something is and why it matters.
## What is Authentication?
Authentication verifies the identity of users accessing your API.
It ensures that only authorized users can perform actions.Procedural Content
Step-by-step instructions for completing a task.
## Set Up Authentication
1. Navigate to the Dashboard
2. Click **Settings** > **API Keys**
3. Click **Generate New Key**
4. Copy the key and store it securelyReference Content
Technical specifications and API details.
## API Response Codes
| Code | Meaning |
| ---- | ------------ |
| 200 | Success |
| 400 | Bad Request |
| 401 | Unauthorized |
| 500 | Server Error |Architecture Content
Architecture and system-overview pages should explain the request or
workflow path before listing components.
## From one request
Explain how a user action moves through the system:
input -> state lookup -> context selection -> execution -> verification
-> persistence.
## Component recap
Summarize each component after the reader understands why it appears.Why: Component-first pages force readers to memorize names before
they understand the problem each component solves. A workflow-first path
shows when each file, hook, script, or role becomes necessary, then the
recap table reinforces the model.
Reference detail: Put exhaustive field tables, path matrices, and
per-platform configuration in reference pages or appendices. The
architecture page should summarize and link to those references instead
of duplicating them.
Formatting Standards
Emphasis
| Style | Usage | Markdown |
|---|---|---|
| Bold | UI elements, important terms | **text** |
| Italic | Introducing new terms | *term* |
Code |
Commands, file names, code | `code` |
Links
Internal links:
See the [quickstart guide](/quickstart) for setup instructions.External links:
Read the [official documentation](https://example.com/docs).Images
Always include descriptive alt text.
Best Practices
DO
- Start with the most important information
- Use concrete examples
- Keep paragraphs short (3-4 sentences max)
- Include code samples for technical content
- Link to related content
DON'T
- Assume prior knowledge without linking to prerequisites
- Use jargon without explanation
- Write walls of text without visual breaks
- Skip alt text on images
- Use vague language ("simply", "just", "easily")
Changelog / Release Notes Voice
Release notes (docs-site/changelog/v*.mdx and docs-site/zh/changelog/v*.mdx) are read by operators deciding whether to upgrade and by AI agents answering version questions. They are reference docs, not product storytelling. Match the tone of kubectl / Rust release notes, not marketing copy.
Convention: Technical voice
What: Each section opens with one sentence stating what changed, followed by concrete identifiers (file paths, function names, flags, migration entries). Prefer tables, code blocks, and bullets over paragraphs. No rhetorical questions, no emotional language, no filler adverbs.
Why: A user scanning a changelog wants to answer "does this affect me, and what do I do?" in seconds. Narrative background ("then what? then nothing") pushes the actual change further down the page and tells readers how to feel rather than what changed. It also ages badly — in six months the only reader is an AI grepping for phase.py or init.ts:1370, not someone reliving the UX story.
Example (changelog entry):
### Joiner onboarding task
`trellis init` now dispatches on two filesystem flags:
| `.trellis/` | `.trellis/.developer` | Generated task |
|---|---|---|
| missing | n/a | `00-bootstrap-guidelines` (creator, unchanged) |
| present | missing | `00-join-<slug>` (new: joiner flow) |
| present | present | none (same-dev re-init) |
`.trellis/.developer` is the per-checkout signal because it's listed in
`.trellis/.gitignore` and therefore absent on fresh clones.
`.trellis/workspace/<name>/` cannot serve this role — it's committed to git.Related: Best Practices > DON'T > use vague language above. Changelog narrative flourishes are the same anti-pattern at document level.
Don't: Narrative storytelling in changelogs
Problem:
### Joiner 引导任务——新开发者不再进来一脸懵
这个版本之前,新开发者在一个已有 Trellis 项目上第一次跑 `trellis init` 几乎啥都不做:
只往 `.trellis/.developer` 写了个身份文件,然后呢?然后就没然后了。打开 AI 工具面对
的是一片空白,不知道 Trellis 是什么、团队约定在哪、自己该做什么。队友只能反复在
群里解释工作流。
beta.9 起,`trellis init` 按两个文件的存在状态分三种场景派发:Why it's bad:
- Rhetorical questions ("然后呢?然后就没然后了") and emotional framing ("一脸懵", "反复在群里解释") don't help an upgrade decision.
- The actual change (dispatch on two flags → three branches) is buried three paragraphs in.
- Language ages badly. In six months "一脸懵" reads as noise; the dispatch table still holds.
- Title is an outcome statement ("不再进来一脸懵"), not a feature name. Hard to grep for.
Instead:
### Joiner onboarding task
`trellis init` now dispatches on (`.trellis/`, `.trellis/.developer`) presence
to generate three task types: creator bootstrap, joiner onboarding, or no task.Lead with the change. Background (if any) goes into a second paragraph or a collapsed "Why" subsection — not in the opening sentence.
Don't: section bloat
Don't ship these sections in a changelog:
## Tests/## Test Coverage— "847/847 pass" / "5 new regression tests" is commit-message material. Users care about behavior, not test counts.## Internal(default) — refactor function-renames, internal flag flips, spec-file edits. Only include an Internal entry if it changes user-observable behavior in a multi-platform / multi-version setup. Otherwise drop it.## Why/## Background/## Rationale— multi-sentence justifications. If the change isn't clear from a one-sentence opener + table/code, the entry is too vague — split it or trim it. Long-form rationale belongs in the task PRD or commit body.
Length cap: each ### section ≤ ~120 words. Going over means you're explaining instead of describing — trim.
Allowed top-level sections (ordered, skip empty ones): Enhancements (feat), Bug Fixes (fix), Internal (only if user-observable), Upgrade. No empty headings.
Section heading rules
- Use feature names, not outcomes:
### Joiner onboarding task, not### New developers aren't thrown into a black box anymore. - Stable across translations: same technical nouns in both
docs-site/changelog/anddocs-site/zh/changelog/. - Greppable: include exact identifiers users might search for (
task.json,trellis init,/trellis:finish-work).
Backstory goes elsewhere
Product narrative ("why we're doing this", user quotes, design rationale) belongs in:
- Task PRD (
.trellis/tasks/<task>/prd.md) during development - Blog post (
docs-site/blog/) for marketing - Spec decision record (
spec/*/design-decision sections) for lasting architectural choices
The changelog just records what shipped and how users upgrade.
Source-of-Truth Discipline for Code-Level Docs
When a page documents code-level contracts — JSON schemas, CLI subcommand tables, config field lists, file path references, default values — open the source file first and copy the list verbatim (field order, names, defaults) before writing a single line of prose. Don't document from memory, and don't propagate what existing docs already say without re-verifying.
Why
Docs drift silently. task.json schema docs claimed task.py create-pr and rejected status existed for multiple releases — neither was ever in the source. The schema field order was shuffled, comments described behavior the code doesn't implement ("commit hash filled on archive" when archive only writes completedAt). Every one of these is a ~30-second verification away from the truth.
Rule
Before writing or editing code-level reference docs:
- Identify the canonical source — usually a single file (e.g.
task_store.py,init.ts, a Zod schema). Link to it in the doc so reviewers can cross-check. - Copy field order from source. Don't alphabetize, don't reorder "for readability".
- Quote source line numbers in commit messages (
task_store.py:147-172) so the provenance is traceable. - When comments describe "when is this populated" — grep for every writer. If the only writer is
create(), say "written as null; no other code paths update it" — don't invent imaginary lifecycle events. - When multiple writers exist with divergent shapes (e.g.
task_store.pyvsinit.tsvsupdate.ts), either document all variants or document the canonical one and file a code-cleanup task to converge them. Don't paper over divergence with optimistic prose.
Common Mistake: Documenting from prior docs
Symptom: Doc says field X or subcommand Y exists. Reader tries it. Nothing happens.
Cause: The doc was copied from a previous version of itself. The previous version was wrong. The field / subcommand was aspirational, removed, or never merged.
Fix: For every code-level claim, grep the source. If no code path writes/reads it, delete the claim.
Prevention: When editing an existing reference page, treat the prior content as unverified. The only trustworthy version is the source file.
JSONL Context Injection Content
When writing docs, skills, commands, or templates that teach users about implement.jsonl / check.jsonl / research.jsonl entries, enforce this content rule:
Rule
JSONL entries point at spec files (.trellis/spec/**) or the task's research outputs ({TASK_DIR}/research/**). They do NOT point at raw source files (src/services/foo.ts) or raw source directories (packages//).
Why
Sub-agents already have Read / Grep. They pull code on demand. Injecting source files into the prompt:
- Burns tokens in every sub-agent spawn for code that may not even be relevant to this turn
- Makes JSONL entries decay fast (code moves; a path pinned by JSONL is stale the moment you refactor)
- Gives AI a false impression that the injected files are the "authoritative" code, biasing toward them even when better code exists elsewhere
Specs and research, by contrast, are the rules and background the sub-agent needs before touching code. They're the right payload.
Example
# Good — specs + research
{"file": ".trellis/workflow.md", "reason": "Workflow contract"}
{"file": ".trellis/spec/backend/api-module.md", "reason": "API module conventions"}
{"file": ".trellis/tasks/02-27-user-login/research/", "type": "directory", "reason": "Upstream research outputs"}
# Bad — raw code
{"file": "src/services/auth.ts", "reason": "Existing auth patterns"}
{"file": "src/services/", "type": "directory", "reason": "Existing service patterns"}Writer's check
When you add a JSONL example or write a skill that calls task.py add-context:
- Is the path under
.trellis/spec/or{TASK_DIR}/research/? - If you're tempted to point at
src/orpackages/, ask: is this really a rule the agent needs up-front, or just code it could grep for itself when needed?
Tombstone Sections: Delete, Don't Archive
When content becomes obsolete because a feature was removed, delete the content outright. Don't leave a "What was removed in vX.Y" table or a standalone "Appendix X: feature (removed)" page.
Why
Tombstone sections:
- Pollute the TOC and in-page sidebar
- Repeat migration guidance that belongs in the changelog / migration manifest, not the reference docs
- Teach readers to skim-ignore sections — the "noise sections" train them to tune out legit content too
- Accumulate across versions (every release adds one; none ever removes one)
Rule
When a feature is removed:
- Delete the sections / pages that documented it.
- Put the "what to do instead" guidance in one place — the release changelog or migration manifest's
notesfield. Link to it from the top-of-page<Note>for the one release that removes it, then drop the note the release after. - If a former section is heavily cross-referenced, check for incoming links and redirect them; don't keep the tombstone just to preserve URLs.
Example
Wrong (actual regression seen in 0.5 audit):
## Appendix E: worktree.yaml (removed)
This appendix previously documented… Both the pipeline and this file
were removed in 0.5.0-beta.0 along with…Right: delete appendix-e.mdx, remove it from docs.json. Note in the 0.5.0-beta.0 changelog that Multi-Agent Pipeline was removed and worktree.yaml is no longer read.
Managing the Release / Beta Dual Track
The docs-site has two version tracks: Beta (tracks the latest @beta npm release, currently 0.5.x) and Release (tracks the latest stable, currently 0.4.0 GA). Users pick one from the version dropdown. Only part of the content is version-coupled.
What's dual, what's single-source
Lives in release/ and zh/release/ too |
Single-source (top-level only) |
|---|---|
guide/ch01 – ch13 (concepts, commands, platform matrix) |
use-cases/ |
guide/appendix-a – appendix-f |
showcase/ |
guide/index.mdx, index.mdx (landing) |
blog/ |
changelog/ (version entries) |
skills-market/ |
templates/ |
|
contribute/ |
Rule of thumb: anything that describes how Trellis works right now is dual (because that changes per version). Anything marketing, community, or resource-discovery stays single-source — those pages don't change per version, so maintaining two drift-prone copies burns effort for no user benefit.
When in doubt, ask: "Would a 0.4 user genuinely see something different on this page than a 0.5 user?" If no, it's single-source.
Source of truth for platform counts
Platform count drifts across releases (0.4 = 14 platforms, 0.5 = 13 after iFlow removal). Always derive the count from the CLI source for the version you're documenting, not from prior docs.
# For Release track (documents 0.4.0 GA)
git ls-tree v0.4.0 packages/cli/src/configurators/ | grep '\.ts$' \
| awk -F'[\t/]' '{print $NF}' | grep -v 'index\|shared\|workflow'
# For Beta track (documents current HEAD)
ls packages/cli/src/configurators/*.ts | xargs -n1 basename \
| grep -v 'index\|shared\|workflow'The count is (number of configurator files) - (index.ts + shared.ts + workflow.ts).
Per-platform directory layout for a specific version
To document what trellis init -- actually writes in a given version:
git show v0.4.0:packages/cli/src/configurators/qoder.ts | head -60
git ls-tree -r v0.4.0 packages/cli/src/templates/qoder/Don't infer from memory or from current beta templates — 0.5 adds hooks/ and rules/ to platforms that didn't have them in 0.4, so a current-beta listing will mis-describe 0.4.
Orphan changelog files
When a release/changelog/v*.mdx file exists on disk but has no entry in docs.json's Release dropdown, it's dead content — Mintlify doesn't render it in any nav, users can only reach it by guessing a URL. Delete it.
# List orphans (files on disk, not in docs.json)
cd docs-site
for f in release/changelog/*.mdx; do
base=$(basename "$f" .mdx)
grep -q "\"release/changelog/$base\"" docs.json || echo "$f"
doneCommon source: when the track was forked from beta with cp -r, pre-release changelog files came along but the Release dropdown was manually whitelisted to stable versions only. Result: 30+ orphan MDX per locale.
What each track is called
- Beta track =
guide/,changelog/,index.mdx(at the top level) — latest@betacontent - Release track =
release/guide/,release/changelog/,release/index.mdx— snapshot of the latest stable docs.jsonnavigation has separate blocks for Beta and Release. Each language (EN, ZH) has both, so there are 4 navigation blocks total.
Switching content between tracks is a cp -r with manual docs.json updates — there's no automation. Remember to update all four navigation blocks (EN Beta, EN Release, ZH Beta, ZH Release) when restructuring.
When the stable GA bumps
When 0.5 reaches GA:
- Decide whether to promote beta content → release (overwriting the frozen 0.4 snapshot) or keep both tracks. Usually promote.
cp -r guide/ release/guide/(EN) andcp -r zh/guide/ zh/release/guide/(ZH)- Update the release-track Changelog whitelist in
docs.jsonto include the new stable version - Update the version-switcher label in the docs.json navbar (Release = "latest stable" not "0.4")
- Delete pre-release changelog files from
release/changelog/that shouldn't be in the stable-only whitelist - Beta track continues evolving toward the next major (0.6)
Common Mistake: Documenting Release track from current templates
Symptom: You write Release-track docs (documenting 0.4.0 GA) and grab details from the current tree — e.g. cat packages/cli/src/configurators/qoder.ts or ls packages/cli/src/templates/qoder/. Result: the docs claim 0.4.0 generates paths that only exist on 0.5 betas (.qoder/rules/, .qoder/hooks/), or platforms that are 0.5-only get documented as 0.4. User tries to trellis init --qoder on 0.4.0 and sees a completely different layout.
Cause: The current tree is always beta-current. Release track docs must reflect the tagged stable version, which is what users on @stable actually get.
Fix: Always query the tagged version when writing Release docs. Every inline reference to a file path, config shape, or platform capability in the Release track should trace to git show v<stable-version>:... or git ls-tree v<stable-version> ..., never to the working tree.
Prevention: When you open a file to document a Release-track behavior, the first keystroke should be git show v<version>: not cat. If that feels like friction, keep a terminal tab pinned to git log --oneline v<stable-version>^..v<stable-version> so the tag is always in scope.
Common Mistake: Counting platforms from existing docs
Symptom: Docs say Trellis supports N platforms, but the real count is different. Worst case: you inherit a wrong count from prior docs, propagate it, then spend a session re-fixing every mention. (Seen in 0.4.0 Release audit: prior docs claimed 6 platforms, next pass got to 12, actual was 14 — CodeBuddy and Antigravity were quietly shipped and never documented.)
Cause: Docs drift silently. When a platform is added to the CLI, documentation updates are manual and easy to forget. Counting from docs compounds every prior miss.
Fix: The canonical count is git ls-tree v<version> packages/cli/src/configurators/ | grep '\.ts$' | grep -vE 'index|shared|workflow' | wc -l.
Prevention: When a platform count appears in docs (tables, taglines, architecture diagrams, FAQs), cross-check it against the configurator directory for the target version before trusting prior docs. Treat every platform-count claim as unverified until you've run the count yourself.
Quality Checklist
Before publishing:
- Title is clear and descriptive
- Description is under 160 characters
- Headings follow hierarchy (H2 > H3)
- Code examples are tested and correct
- Links are valid and point to correct pages
- Images have alt text
- Content is scannable (lists, tables, short paragraphs)
- For code-level reference pages: every field / subcommand / flag traces to a named source file you opened while writing
- JSONL examples inject specs or research, not raw code
- No "removed in vX.Y" tombstone sections for features already absent
- If you touched a dual-track page (
guide/*,appendix-*,index.mdx), you updated both Beta and Release copies (EN and ZH each = 4 files to touch) - If you touched a single-source page (
use-cases/,showcase/,blog/,skills-market/,templates/,contribute/), you did NOT create a duplicate underrelease/ - Platform counts / lists trace to
git ls-tree v<target-version> packages/cli/src/configurators/, not to prior docs or memory - All four
docs.jsonnavigation blocks (EN Beta / EN Release / ZH Beta / ZH Release) are consistent
.Trellis Spec Docs Site Docs Sync On Change (`.trellis/spec/docs-site/docs/sync-on-change.md`)
Docs-Site Sync Matrix
When Trellis changes workflow, platforms, commands, or skills, which docs-site pages must update in lockstep. A concrete "Audit ALL Writers" checklist for documentation.
Why This Exists
Docs-site is a submodule that lags behind template code. Missing a doc-update on a shipping change produces false claims (docs say "run init-context" when the command has been removed). This has happened — see the init-context-removal task (2026-04-23) where 8 MDX pages still referenced the removed command after one implementation pass.
Rule of thumb: if the change touches packages/cli/src/templates/ or packages/cli/src/migrations/, grep the matrix below before merging.
Version Scope Gate
Before applying any trigger below, decide whether the changed behavior belongs
to stable, beta, or RC docs. The file path must match that decision:
- Stable / GA content: root versioned paths such as
start/**,advanced/**,
and theirzh/**mirrors. - Beta content:
beta/**andzh/beta/**only. - RC content:
rc/**andzh/rc/**only.
Never copy a beta workflow, artifact model, platform contract, or install
instruction into the root versioned paths before GA promotion. Root is what the
Release selector serves.
Required opposite-tree grep
For version-specific changes, grep the tree that should not contain the new
behavior before committing. For example, after a beta-only workflow change:
cd docs-site
rg -n "task-creation consent|codex-mode|<trellis-workflow>|planning artifact|`design\\.md`|`implement\\.md`" \
start advanced guides zh/start zh/advanced zh/guides -g "*.mdx"If this finds the new beta terms in root release docs, stop and move the change
to beta/** / zh/beta/** instead.
Trigger 1: Phase Structure Changes
Scope: any edit to packages/cli/src/templates/trellis/workflow.md that adds/removes a step, renames a phase, or changes required/optional/once tags.
| File (en + zh) | What to sync |
|---|---|
start/install-and-first-task.mdx |
Phase 1/2/3 walkthrough block (around line 215-240 in en) — keep step numbers + action verbs in sync with workflow.md phase index |
start/everyday-use.mdx |
Task lifecycle ASCII diagram + any per-phase bash examples |
advanced/architecture.mdx |
Phase overview diagrams (if present) |
concepts/workflow.mdx (if exists) |
Phase definition sections |
Grep command (docs-site)
cd docs-site && grep -rln "Phase 1\|Phase 2\|Phase 3\|phase-1\|phase-2\|phase-3\|workflow\.md" \
--include="*.mdx" | grep -v "release/\|changelog/\|blog/"In-template routing references (CRITICAL — grep bare step numbers, not just "Phase N.N")
When a step is deleted/renumbered, references to it live in MORE than workflow.md. They use bare-number routing syntax that a "Phase 3.1" grep misses. Past drift: 0.6.1 deleted Phase 3.1 but left continue.md routing check passed → **3.1** (caught by a user, fixed in 0.6.2).
Audit ALL of these, in BOTH the source and any independent copies:
| File | Reference form |
|---|---|
packages/cli/src/templates/common/commands/continue.md |
resume-routing table: status=... → **<step>** |
packages/cli/src/templates/common/bundled-skills/trellis-meta/references/customize-local/change-workflow.md |
resume-at status-transition table: Phase <step> (...) |
packages/cli/src/templates/copilot/prompts/finish-work.prompt.md |
Phase 3 ASCII flow list |
marketplace/workflows/{native,tdd,channel-driven-subagent-dispatch}/workflow.md |
full independent workflow copies — same step body + Phase Index + breadcrumb scope ranges |
Grep patterns that catch bare-number routing (run from repo root):
# bare-number routing arrows / bold step refs / step-N.N anywhere in templates + marketplace
grep -rnE "→ \*\*[0-9]\.[0-9]+\*\*|-> \*\*[0-9]\.[0-9]+\*\*|step [0-9]\.[0-9]+|Phase [0-9]\.[0-9]+|\*\*[0-9]\.[0-9]+\*\*" \
packages/cli/src/templates/ marketplace/ \
--include="*.md" --include="*.toml" --include="*.prompt.md" \
| grep -v "/dist/"Also verify the script tolerates a now-missing step: python3 .trellis/scripts/get_context.py --mode phase --step <deleted> must return a friendly "Step not found", not crash.
Trigger 2: Platform Add / Remove / Rename
Scope: any edit to AI_TOOLS in packages/cli/src/types/ai-tools.ts, _SUBAGENT_CONFIG_DIRS in task_store.py, or platform-tagged blocks in workflow.md.
Add a new platform
| File (en + zh) | What to sync |
|---|---|
ai-tools/.mdx |
NEW FILE — platform-specific setup + quirks page |
ai-tools/index.mdx (if exists) |
List entry for the new platform |
docs.json |
Add navigation entry to both languages[0] (en) and languages[1] (zh) groups |
start/install-and-first-task.mdx |
Platform table (hook-inject vs pull-based vs agent-less) |
advanced/multi-platform.mdx |
Class-1 / Class-2 / agent-less grouping table |
advanced/appendix-d.mdx (platform quirks) |
Add quirks row if any |
release/ mirror copies |
Release-frozen copies update on next release-cut, not immediately |
Remove a platform
| File | What to sync |
|---|---|
ai-tools/.mdx |
Delete the page |
ai-tools/index.mdx |
Remove list entry |
docs.json |
Delete navigation entries (both languages) |
start/install-and-first-task.mdx, advanced/multi-platform.mdx, advanced/appendix-d.mdx |
Remove references |
changelog/<version>.mdx |
Changelog entry documenting the removal |
Rename (e.g. "iFlow" removed) — same as remove + migration note in changelog.
Grep command
cd docs-site && grep -rln "<platform-name>" --include="*.mdx" --include="*.json"Trigger 3: `task.py` Command Add / Remove / Rename
Scope: any edit to task.py subparser registrations or the split modules it dispatches to (task_store.py, task_context.py).
| File (en + zh) | What to sync |
|---|---|
advanced/appendix-b.mdx |
task.py subcommand reference table — add/remove row |
start/everyday-use.mdx |
Task lifecycle flow arrow + per-step bash examples |
advanced/appendix-c.mdx |
If the change affects task.json fields, update schema comments |
Evidence of past drift
The init-context removal (2026-04-23) touched all three files above; the first-pass sweep missed them. Only a follow-up review question ("哪些文档站地方需要更新") caught them.
Grep command
cd docs-site && grep -rln "task\.py <subcommand-name>\|`<subcommand-name>`" --include="*.mdx" \
| grep -v "release/\|changelog/"Trigger 4: Skill Add / Remove / Rename
Scope: any edit to packages/cli/src/templates/common/skills/ or packages/cli/src/templates/{platform}/skills/.
| File (en + zh) | What to sync |
|---|---|
start/everyday-use.mdx |
Skill table at top (around line 15-18) + individual skill description sections |
advanced/appendix-b.mdx |
Skill reference table (if present) |
start/install-and-first-task.mdx |
Phase walkthrough skill names |
| Skill Routing table across workflow docs | Must match workflow.md Skill Routing per-platform splits |
Grep command
cd docs-site && grep -rln "trellis-<skill-name>" --include="*.mdx" \
| grep -v "release/\|changelog/\|blog/"Trigger 5: JSONL / Task Metadata Schema Changes
Scope: any edit to implement.jsonl / check.jsonl seed format, task.json schema, or consumer contracts (hook / prelude / read_jsonl_entries).
| File (en + zh) | What to sync |
|---|---|
advanced/appendix-c.mdx |
task.json schema block — every field has a comment; keep in sync with task_store.py |
start/everyday-use.mdx |
"Seeded on Create, AI Curates in Phase 1.3" section (or whatever replaces it) + sample JSONL blocks |
advanced/architecture.mdx |
Context injection diagrams if present |
concepts/*.mdx |
Seed vs curated row distinction if any conceptual page explains jsonl |
Contract to keep in sync
- Created state: on sub-agent-capable platforms
task.py createwrites emptyimplement.jsonl/check.jsonl— no placeholder row. Platforms that load specs
through skills instead of JSONL (Codex inline mode, Kilo, Antigravity, Devin) get
neither file - Curated row schema:
{"file": "", "reason": "<why>"} - Consumer behavior: row without
fileis skipped by every consumer (hook, prelude, list-context) - Legacy placeholder row
{"_example": "..."}: rejected bytask.py validateand by PR preflight — delete it - READY gate: jsonl with no curated rows → NOT ready (must have at least one curated row)
See .trellis/spec/cli/backend/platform-integration.md → "Agent-Curated JSONL Contract (Phase 1.3)" for the code-side contract.
Trigger 6: Changelog & Migration
Every released version must have:
| File (en + zh) | What to sync |
|---|---|
changelog/v<version>.mdx |
Release notes — list user-visible changes, breaking-change warnings, upgrade steps |
docs.json |
Navigation entry for the new changelog page (both languages) |
release/ tree |
Release-frozen copy — only updated on release-cut, not during develop |
Migration manifests in packages/cli/src/migrations/manifests/ need matching changelog entries. The manifest's changelog + aiInstructions fields are the authoritative text; changelog MDX should link to or paraphrase them.
Trigger 7: `.trellis/config.yaml` Template or Reader Changes
Scope: any edit to packages/cli/src/templates/trellis/config.yaml, config readers under packages/cli/src/templates/trellis/scripts/common/, or configSectionsAdded behavior in update manifests.
| File (en + zh, release + beta) | What to sync |
|---|---|
advanced/configuration.mdx |
Add/remove/rename config keys, defaults, accepted values, and update behavior |
advanced/appendix-a.mdx |
Update the .trellis/config.yaml one-line purpose if the file's responsibility changes |
start/everyday-use.mdx |
Update only if the key affects day-to-day task/session operations |
changelog/v<version>.mdx |
Document user-visible config behavior or migration delivery |
Grep command
cd docs-site && grep -rln "config.yaml\|session_auto_commit\|codex.dispatch_mode\|update.skip" \
--include="*.mdx" --include="docs.json" \
| grep -v "node_modules/"Rule: The template is the source of truth for shipped examples, but advanced/configuration.mdx is the user-facing reference. If a key is supported by code but intentionally absent from the current template (for example, legacy compatibility), the configuration page should say so explicitly instead of silently omitting it.
Bilingual Discipline
Every update under *.mdx must be done in BOTH en/ and zh/ paths. The zh/ tree mirrors the en tree exactly — same file names, same section headings, same order.
Common drift sources
- Edited en, forgot zh →
zh/start/everyday-use.mdxfalls behind by weeks - Navigation entries added to
languages[0]only → page renders in English sidebar only - Code blocks translated (don't translate code — only prose)
Detection
cd docs-site
# Find pages that exist in en but not zh (or vice versa)
diff <(find . -name "*.mdx" -not -path "./zh/*" -not -path "./release/*" -not -path "./node_modules/*" | sed 's|^\./||' | sort) \
<(find zh -name "*.mdx" | sed 's|^zh/||' | sort)Non-zero output = orphan pages. Triage before merge.
Non-Triggers (Don't Update Docs)
| Change | Why no doc update |
|---|---|
| Internal refactor with no user-visible behavior change | No user-facing contract changed |
| Bug fix that restores documented behavior | Docs already describe correct behavior |
| Test additions | Tests aren't user-facing |
| Migration manifest content changes | Already captured by changelog/v<version>.mdx |
Audit Process Before Merging
- Run the grep command from the trigger section that matches your change
- Open each hit — is the page still accurate after your change?
- For any hit that's stale: update both
enandzhversions - If you added/removed pages: update
docs.jsonnavigation in both language trees - If the change impacts multiple triggers (e.g. a removed command + a removed platform), run all relevant greps
Mechanical > heroic. Don't rely on memory or review to catch drift.
--- METRICS ---
- Files Extracted: 10
- Estimated Token Budget: ~24141 tokens
- Canonical Reference: https://codewiki.google/github.com/mindfold-ai/Trellis