Trellis Documentation

TypeScript agentic-coding & ai-workflow repository by @mindfold-ai. AI agent rules & system prompts with 15k stars.

14,990 stars TypeScript TypeScript 10 files · ~24,222 tokens #agentic-coding#ai-workflow#claudecode#codex#harness
RAW Doc

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

bash
# 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-name

See the Quick Start and Supported Platforms guides for setup details.

How to Use

The workflow is simple:

  1. Describe what you want in natural language.
  2. Brainstorm with the AI one question at a time until the PRD is clear, then implementation begins.
  3. Let it run — the AI calls Trellis Implement and auto-checks the result against specs, lint, type-check, and tests.
  4. Type /trellis:finish-work when 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:

  1. Plan — trellis-brainstorm walks through requirements one question at a time and writes prd.md. Research-heavy items go to a trellis-research sub-agent. The result is curated specs + research files referenced from implement.jsonl / check.jsonl.
  2. Implement — a trellis-implement sub-agent writes code from the PRD with the curated context auto-injected, no git commit.
  3. Verify — a trellis-check sub-agent reviews the diff against specs and runs lint, type-check, and tests, self-fixing where it can.
  4. Finish — a final check runs, then trellis-update-spec promotes 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) and
TRELLIS_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

bash
# Start dev server
mintlify dev

# Custom port
mintlify dev --port 3333

File 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 main branch 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:

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

  1. Wrap each CJK character in <b> tags
  2. CSS forces <b> to display:inline-block;width:2ch — exactly 2 monospace columns
  3. Use dangerouslySetInnerHTML to bypass MDX parsing issues

CSS in styles.css:

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:

mdx

/* Detailed source-code truncated for AI context efficiency. */

text
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

json
{
  "$schema": "https://mintlify.com/docs.json"
}

Always include the schema for editor autocomplete.


Core Configuration

Theme and Branding

json
{
  "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

Tab-Based Navigation

json
{
  "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

json
{
  "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):

json
{
  "navigation": {
    "groups": [
      {
        "group": "Community",
        "pages": [
          "showcase/index",
          "contribute/index",
          {
            "group": "Blog",
            "expanded": false,
            "pages": ["blog/index", "blog/post-one", "blog/post-two"]
          }
        ]
      }
    ]
  }
}

Warning: The expanded property only works on nested groups (groups within groups). Top-level groups are always expanded and cannot be collapsed. If you put expanded: false on a top-level group, it will be silently ignored.

Common Mistake: Trying to collapse a top-level group:

json
// ❌ WRONG - This does NOT work
{
  "group": "Blog",
  "expanded": false, // Ignored! Top-level groups can't collapse
  "pages": ["blog/index", "blog/post-one"]
}
json
// ✅ 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

json
{
  "navigation": {
    "global": {
      "anchors": [
        {
          "anchor": "Documentation",
          "href": "https://example.com/docs",
          "icon": "book-open-cover"
        },
        {
          "anchor": "GitHub",
          "href": "https://github.com/example",
          "icon": "github"
        }
      ]
    }
  }
}
json
{
  "navbar": {
    "links": [
      {
        "label": "Support",
        "href": "mailto:[email protected]"
      }
    ],
    "primary": {
      "type": "button",
      "label": "Dashboard",
      "href": "https://dashboard.example.com"
    }
  }
}
json
{
  "footer": {
    "socials": {
      "x": "https://x.com/example",
      "github": "https://github.com/example",
      "linkedin": "https://linkedin.com/company/example"
    }
  }
}

API Documentation

OpenAPI Integration

json
{
  "navigation": {
    "tabs": [
      {
        "tab": "API Reference",
        "groups": [
          {
            "group": "Endpoints",
            "openapi": "api-reference/openapi.json"
          }
        ]
      }
    ]
  }
}

Contextual Options

Code block right-click menu options:

json
{
  "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:

json
{
  "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

text
docs/
├── index.mdx           # English homepage
├── quickstart.mdx      # English quickstart
├── zh/
│   ├── index.mdx       # Chinese homepage
│   └── quickstart.mdx  # Chinese quickstart
└── docs.json           # Both languages configured

Important: 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:

yaml
# 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:

text
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.json

Navigation configuration:

json
{
  "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:

css
/* 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 $schema for 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

  1. Create the .mdx file
  2. Add frontmatter with title and description
  3. Add page to appropriate group in docs.json
  4. Verify navigation order makes sense
  5. 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.

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:

  1. Create .mdx file
  2. Add to English navigation in docs.json
  3. Create Chinese version in zh/ directory
  4. Add to Chinese navigation in docs.json

Project Structure

text
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.svg

Naming 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:

text
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.json

i18n 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

text
blog/
├── post-en.mdx      # ❌ Clutters both language views
└── post-zh.mdx

Do: Separate by language directory

text
blog/
└── post.mdx         # ✅ English only
zh/blog/
└── post.mdx         # ✅ Chinese only

Adding 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

  1. Create new directory with descriptive name
  2. Add pages inside the directory
  3. Create a group in docs.json navigation (both EN and ZH)

New Snippet

  1. Create .mdx file in snippets/
  2. 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:

yaml
---
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

yaml
---
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:

mdx
<Card title="Quick Start" icon="rocket" href="/quickstart">
  Get up and running in 5 minutes.
</Card>

Horizontal variant:

mdx
<Card title="Start Here" icon="rocket" href="/quickstart" horizontal>
  Follow our quickstart guide.
</Card>

Columns

Use for multi-column layouts:

mdx
<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:

mdx
<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:

mdx
<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:

mdx
<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:

mdx
<Snippet file="api-key-setup.mdx" />

Code Blocks

Basic Syntax

markdown
```language filename
code here
```

With Filename

markdown
```javascript app.js
const express = require('express');
const app = express();
```

Supported Languages

Common: javascript, typescript, python, bash, json, yaml, sql, go, rust


Callouts

Note

mdx
<Note>This is important information.</Note>

Warning

mdx
<Warning>Be careful with this action.</Warning>

Info

mdx
<Info>Additional context here.</Info>

Tip

mdx
<Tip>Helpful suggestion here.</Tip>

Common Mistakes

Nested Code Blocks in Templates

Problem: Using escaped backticks to show code block syntax renders broken:

mdx
\`\`\`bash
npm install
\`\`\`

Solution: Use <CodeGroup> component instead:

mdx
<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:

mdx
---
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.

mdx
<Note>
  Don't pick **Full re-initialize** — it overwrites existing config.</Note>


<Note>Don't pick **Full re-initialize** — it overwrites existing config.
</Note>
mdx
<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.

mdx
<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.

mdx
<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:

markdown
| 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 title and description in 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)

text
.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

json
{
  "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

json
{
  "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

json
// ❌ WRONG
{
  "author": "Your Name"
}

// ✅ CORRECT
{
  "author": {
    "name": "Your Name"
  }
}

Mistake: Missing source field

Symptom: plugins.0.source: Invalid input

json
// ❌ 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

json
// ❌ 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

json
// ❌ 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

json
// ❌ WRONG - String value
{
  "skills": "./skills"
}

// ✅ CORRECT - Array value
{
  "skills": ["./skills/my-skill"]
}

Mistake: Directory paths for agents

Symptom: Validation fails for agents

json
// ❌ WRONG - Directory path
{
  "agents": ["./agents/"]
}

// ✅ CORRECT - Explicit file paths
{
  "agents": [
    "./agents/planner.md",
    "./agents/reviewer.md"
  ]
}

Note: skills and commands can use directory paths, but agents must use explicit file paths.

Mistake: Adding hooks field

Symptom: Duplicate hooks file detected

json
// ❌ 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.json by convention. Adding it explicitly causes a duplicate error.


Validation

Before publishing, validate your plugin:

bash
claude plugin validate .claude-plugin/plugin.json

Or from within Claude Code:

text
/plugin validate .

Best Practices

DO

  • Include version in plugin.json
  • Use object format for author (not string)
  • Always include source in 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 hooks field (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 across release-only, release+beta, release+rc states, 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:

bash
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:

bash
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 as
trellis-implement.md.


4 lifecycle states

text
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 stable 0.5.0 GA in place
  • release/ (0.4.0 archive) was deleted (git rm -r); 0.4.x docs remain accessible via the v0.4.0 git tag
  • docs.json collapsed from 2 versions to 1 (Release only)
  • 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):

  1. delete d['banner']
  2. Drop the RC version block from versions[] entirely
  3. Set Release.default = true (the flag was on RC)
  4. Insert changelog/v<NEW_GA> and zh/changelog/v<NEW_GA> at the top of each Release block's Changelog pages list
  5. Update navbar.links[label=Changelog].href to /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:

  1. @mindfoldhq/trellis@beta left in a bundled-skill markdown table after the lifecycle flip
  2. 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 at beta/... URLs but dropdown labels RC (404 on click)
  • T2 → T3: stale RC dropdown 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.

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 --version for your install" (current pattern), or
  • Bump it as part of docs-beta-to-rc.sh followup (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:

mdx
<Note>
- bullet
  </Note>   ← BREAKS Mintlify parser: "Expected closing tag </Note> after end of listItem"
</Note>     ← correct: closing tag at column 0

If 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:

bash
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 conflict

The 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

mdx
---
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:

  1. Step-by-step instructions
  2. Ordered processes
  3. 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.

mdx
## 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.

mdx
## 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 securely

Reference Content

Technical specifications and API details.

mdx
## 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.

mdx
## 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`

Internal links:

mdx
See the [quickstart guide](/quickstart) for setup instructions.

External links:

mdx
Read the [official documentation](https://example.com/docs).

Images

mdx

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):

markdown
### 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:

markdown
### 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:

markdown
### 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/ and docs-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:

  1. 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.
  2. Copy field order from source. Don't alphabetize, don't reorder "for readability".
  3. Quote source line numbers in commit messages (task_store.py:147-172) so the provenance is traceable.
  4. 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.
  5. When multiple writers exist with divergent shapes (e.g. task_store.py vs init.ts vs update.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

jsonl
# 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/ or packages/, 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:

  1. Delete the sections / pages that documented it.
  2. Put the "what to do instead" guidance in one place — the release changelog or migration manifest's notes field. Link to it from the top-of-page <Note> for the one release that removes it, then drop the note the release after.
  3. 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):

markdown
## 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.

bash
# 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:

bash
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.

bash
# 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"
done

Common 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 @beta content
  • Release track = release/guide/, release/changelog/, release/index.mdx — snapshot of the latest stable
  • docs.json navigation 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:

  1. Decide whether to promote beta content → release (overwriting the frozen 0.4 snapshot) or keep both tracks. Usually promote.
  2. cp -r guide/ release/guide/ (EN) and cp -r zh/guide/ zh/release/guide/ (ZH)
  3. Update the release-track Changelog whitelist in docs.json to include the new stable version
  4. Update the version-switcher label in the docs.json navbar (Release = "latest stable" not "0.4")
  5. Delete pre-release changelog files from release/changelog/ that shouldn't be in the stable-only whitelist
  6. 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 under release/
  • Platform counts / lists trace to git ls-tree v<target-version> packages/cli/src/configurators/, not to prior docs or memory
  • All four docs.json navigation 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 their zh/** mirrors.
  • Beta content: beta/** and zh/beta/** only.
  • RC content: rc/** and zh/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:

bash
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)

bash
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):

bash
# 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

bash
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

bash
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

bash
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 create writes empty
    implement.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 file is skipped by every consumer (hook, prelude, list-context)
  • Legacy placeholder row {"_example": "..."}: rejected by task.py validate and 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

bash
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

  1. Edited en, forgot zh → zh/start/everyday-use.mdx falls behind by weeks
  2. Navigation entries added to languages[0] only → page renders in English sidebar only
  3. Code blocks translated (don't translate code — only prose)

Detection

bash
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

  1. Run the grep command from the trigger section that matches your change
  2. Open each hit — is the page still accurate after your change?
  3. For any hit that's stale: update both en and zh versions
  4. If you added/removed pages: update docs.json navigation in both language trees
  5. 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 ---