Get Started

Get Started

Install the CLI, wire up your editor, give your agent the skill, and write your first sidecar on one real change. Trellis is useful one file at a time — you do not have to convert a codebase to begin.

What’s in the box

Trellis is a .trellis sidecar format plus a single Go binary that turns those sidecars into working tooling. Today that binary gives you:

  • A dependency graph over what every unit Provides: and Consumes:, with queries for dependents, blast radius, and orphaned contracts.
  • A linter — ten rules plus drift detection, with an override mechanism for the cases you’ve consciously accepted.
  • A language server (trellis lsp) that powers diagnostics-as-you-type, hover, and jump-to-definition in your editor.
  • Policy packs that enforce architectural rules (layer_dependencies, stability_tiers) across the graph.
  • An agent skill that makes your AI assistant read the sidecar before it touches the code.

You don’t need all of it on day one. The fastest useful path is: install the CLI, turn on editor support, write one sidecar, and use it during one real change.

Install the CLI

The toolchain is a Go application (Go 1.23+). Build it from source and put it on your $PATH:

git clone https://github.com/norlinga/trellis.git
cd trellis
go build -o ./trellis ./cmd/trellis
sudo install -m 0755 ./trellis /usr/local/bin/trellis   # or any $PATH dir

trellis --help

If you’d rather let Go fetch and install it directly:

go install github.com/norlinga/trellis/cmd/trellis@latest

The binary is one command with a few subcommands. The ones you’ll reach for most:

trellis graph build .          # discover sidecars; summarize the graph + unresolved consumes
trellis lint .                 # run all rules over every sidecar under this path
trellis lsp                    # speak the Language Server protocol over stdio (editors use this)

Graph queries take sidecar paths — the .trellis file, not the source file:

trellis graph deps app/services/create_subscription.rb.trellis        # what this unit depends on
trellis graph dependents app/services/create_subscription.rb.trellis  # who depends on it
trellis graph orphans .                                                # Provides: with no consumer

Run trellis <command> --help for the full surface, and see the GitHub repository for the spec and release notes.

Set up your editor

Every editor integration just spawns trellis lsp over stdio, so the only prerequisite is the trellis binary on $PATH. Once connected you get diagnostics as you type, hover (a Consumes: handle shows the provider’s feature, summary, and source file), and jump-to-definition (F12 on a Consumes: handle jumps to the Provides: that owns it).

Cross-file hover and jump-to-definition only resolve when you open the folder, not a single file — the server indexes the workspace at startup.

Neovim

Requires Neovim 0.10+ and nvim-lspconfig. Drop the project’s trellis.lua into your runtime (e.g. ~/.config/nvim/lua/trellis.lua) and load it:

require("trellis").setup()

That registers *.trellis as filetype trellis and attaches trellis lsp automatically. Open a sidecar and diagnostics appear inline; :LspInfo should list the client as attached.

For tree-sitter-grade highlighting, install the grammar from tree-sitter-trellis via nvim-treesitter — the editors/neovim/README.md in the repo has the install_info snippet.

VS Code / Cursor

The repo ships a client extension under editors/vscode/ (Cursor consumes VS Code extensions natively, so the same .vsix works in both). Build and sideload it:

cd editors/vscode
npm install
npm run compile
npx @vscode/vsce package
code --install-extension trellis-vscode-0.1.1.vsix

You get TextMate syntax highlighting, the LSP client (diagnostics in the Problems panel and inline), hover, and go-to-definition. Point the trellis.executable setting at an absolute path if trellis isn’t on the $PATH VS Code launched with.

Give your agent the skill

The agent skill is a short behavioral document that shapes how your AI assistant works in Trellis-aware code. It enforces the workflow gates: search for an existing unit before creating a new one, read the sidecar before modifying a file, and surface invariant or scope violations before writing code.

Add TRELLIS_SKILL.md to your project. For Claude Code, place it under .claude/ or reference it from CLAUDE.md; for other agents, drop it wherever that agent collects skills or rules (.cursor/rules/, a generic .agents/ directory, etc.). It’s deliberately short — its job isn’t to explain Trellis, it’s to give the agent a checklist it runs before every meaningful action.

Start with one unit

You do not need to sidecar an entire codebase before Trellis pays off. A single .trellis file next to a single source file already helps a reviewer — or an agent — understand what that unit is for before touching it.

Pick a unit that already has a recognizable job: a service object, a class with a narrow responsibility, a module with a stable public function, a worker with a clear contract. Don’t start with the most chaotic file in the system; Trellis teaches best when the first example is coherent.

Before you create a new unit, check whether something like it already exists. Build the graph and look for a Provides: handle that already covers the behavior:

trellis graph build .

If a plausible match turns up, extend that unit instead of duplicating it. If nothing matches, create the source file and its sidecar together. The sidecar lives next to the source with .trellis appended:

app/services/create_subscription.rb
app/services/create_subscription.rb.trellis

Write the sidecar

Write the sidecar first, and keep it small and honest. The goal isn’t to restate the implementation — it’s to capture intent, boundaries, and the constraints that must survive a rewrite.

@owner: BillingTeam
@stability: stable
@since: 2026-05-27
@reviewed: 2026-05-27

Feature: Create Subscription
  "Creates a user's subscription and charges the initial payment method."

  Provides:
    - Subscription.create(user, plan_id) -> Subscription | raises PaymentError
    - Event: subscription.created

  Consumes:
    - PaymentGateway.charge(token, amount) -> ChargeResult
    - UserRecord (must respond to: id, email, payment_token)

  Invariants:
    - A user MUST NOT have two active subscriptions for the same plan
    - Charges SHALL be idempotent on (user_id, plan_id, idempotency_key)

  OutOfScope:
    - Refunds (handled by RefundService)
    - Plan upgrades (handled by ChangeSubscriptionPlan)

  Scenario (happy-path): Successful subscription creation
    Given a user with a valid payment token
    When subscription creation is requested for a plan
    Then a Subscription record is created
    And the payment gateway is charged

  Scenario (negative): Declined card
    Given a user with a declined payment method
    When subscription creation is requested
    Then it MUST raise PaymentError
    And no Subscription record is created

That snippet is highlighted by the same TextMate grammar that ships in the editor extension — not faked for the page. Capitalized RFC 2119 words (MUST, MUST NOT, SHALL, SHOULD, MAY) are load-bearing in invariants and scenario steps. The first structured token of each Provides: / Consumes: entry is its handle — the stable graph identity. Keep handles exact and free of implementation locations; if you want to anchor one to a line or label, use @source("line:42-68") or @source("label:CHARGE").

Keep four questions in mind as you write:

  1. What does this unit provide to the rest of the system?
  2. What does it consume or rely on?
  3. What must remain true even if the implementation changes (its invariants)?
  4. What does not belong here?

Use it during a real change

The sidecar isn’t done when the file exists. It’s done when it changes how the work gets done. Take one real task and use the sidecar before editing code:

  1. Read the sidecar in full.
  2. Check whether the change belongs inside the unit’s stated scope.
  3. If it touches an Invariant, decide explicitly whether you’re preserving or changing it.
  4. If it adds a dependency, update Consumes:.
  5. If it alters the contract or boundaries, update the sidecar in the same pull request — and bump @reviewed:.

Then lint the sidecar you touched (lint takes the sidecar path):

trellis lint app/services/create_subscription.rb.trellis

This is the moment that matters. Trellis earns its place only if it affects decisions before code is written.

What good looks like

Your first experiment succeeded if it produced any of these:

  • the agent extended an existing unit instead of creating a duplicate
  • a reviewer caught intent drift that would have been easy to miss in code alone
  • the sidecar revealed that a requested change was actually out of scope
  • a test plan got clearer because the invariants were explicit
  • the unit’s dependencies became easier to reason about

The win is not ceremony. It’s better targeting and better restraint.

What to avoid

Common failure modes on a first attempt:

  • writing implementation notes instead of intent
  • documenting every private detail
  • inventing invariants that aren’t actually enforced
  • leaving OutOfScope: empty when the boundary matters
  • treating the sidecar as finished once created, even after the code changed

A good sidecar is concise, reviewable, and slightly abstracted from the source. If it just paraphrases the code line by line, it isn’t doing its job.

Roll out gradually

If the first unit proves useful, expand by subsystem, not all at once:

  1. Add sidecars to a few high-value units in one subsystem.
  2. Require agents to read those sidecars before modifying those files.
  3. Review code and sidecar diffs together.
  4. Run trellis lint in CI.
  5. Watch for duplicate features, weak boundaries, and stale intent.
  6. Only later add policy packs (layer_dependencies, stability_tiers) to enforce architecture across the graph.

The adoption path is intentionally file-by-file.

Where things stand

The format and on-disk artifacts — sidecar grammar, policy file shape, diagnostic codes — are v0.1 stable. The CLI (graph + lint), the language server, the policy packs, the tree-sitter grammar, and the agent skill are all shipping today. Pre-v1, rule thresholds and lint calibration will keep moving with real-world feedback, so pin a known-good build for CI. The current status table lives in the repository README.

Next reading

  • The whitepaper for the full argument and tooling model.
  • The FAQ for the strongest objections and the honest responses.
  • The glossary for precise definitions of Provides, Consumes, Invariants, and OutOfScope.
  • The GitHub repository for the spec, the editor integrations, and release announcements.