From trails
Build with the Trails framework — define trail contracts, wire CLI/MCP trailheads, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding trailheads, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.
How this skill is triggered — by the user, by Claude, or both
Slash command
/trails:trailsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Contract-first TypeScript framework. Define a trail once with typed input, Result output, examples, and metadata — then trailhead it on CLI, MCP, HTTP, or WebSocket without drift.
examples/cli-command.mdexamples/composition.mdexamples/express-handler.mdexamples/mcp-tool.mdexamples/patterns.mdreferences/architecture.mdreferences/cli-surface.mdreferences/common-pitfalls.mdreferences/contract-patterns.mdreferences/error-taxonomy.mdreferences/getting-started.mdreferences/mcp-surface.mdreferences/migration-checklist.mdreferences/testing-patterns.mdtemplates/composition.mdtemplates/trail.mdContract-first TypeScript framework. Define a trail once with typed input, Result output, examples, and metadata — then trailhead it on CLI, MCP, HTTP, or WebSocket without drift.
// 1. Define a trail
const greet = trail('greet', {
input: z.object({ name: z.string().describe('Who to greet') }),
output: z.object({ message: z.string() }),
intent: 'read',
examples: [{ name: 'Basic', input: { name: 'World' }, expected: { message: 'Hello, World!' } }],
blaze: (input) => Result.ok({ message: `Hello, ${input.name}!` }),
});
// 2. Collect into topo
const app = topo('myapp', greetModule);
// 3. Blaze on trailheads
trailhead(app); // CLI — from @ontrails/cli/commander
await trailhead(app); // MCP — from @ontrails/mcp
// 4. Headless execution (no trailhead needed)
const result = await run(app, 'greet', { name: 'Alice' });
// 5. Test
testAll(app); // Examples + governance in one line
Use these terms — they are non-negotiable in Trails codebases.
| Term | Meaning | Not this |
|---|---|---|
trail | Unit of work (atomic or composite) | handler, action |
cross | Composition declaration and runtime verb | workflow, route |
topo | Trail collection | registry |
blaze | Open trails on a trailhead | serve, mount |
trailhead | Transport connector (CLI, MCP, HTTP) | transport |
metadata | Trail annotations | tags |
warden | Governance enforcement | linter |
(input, ctx) => Result. Default choice.crosses: [...], uses ctx.cross().Dotted, lowercase, verb-last: entity.show, math.add, search. Dots become CLI subcommands and MCP tool name segments.
Every field gets .describe() — this becomes --help text, MCP descriptions, and form labels.
input: z.object({
name: z.string().describe('Entity name to look up'),
limit: z.number().default(20).describe('Maximum results'),
})
Required for MCP and HTTP trailheads. Define what Result.ok returns.
| Field | Effect |
|---|---|
intent: 'read' | Safe, no side effects. MCP: readOnlyHint. |
intent: 'destroy' | Irreversible. CLI: auto-adds --dry-run. MCP: destructiveHint. |
idempotent: true | Safe to retry. |
Each example is both documentation AND a test case:
expected: { ... } — deep equalserror: 'NotFoundError' — asserts error typeSee contract-patterns.md for detailed patterns. Copy from trail.md or composition.md.
Adding a trailhead is a trailhead() call, not an architecture change. The framework derives everything from the trail contract.
CLI: Flags from Zod, subcommands from dotted IDs, exit codes from error taxonomy.
import { trailhead } from '@ontrails/cli/commander';
trailhead(app);
MCP: Tool names from trail IDs, JSON Schema from Zod, annotations from metadata.
import { trailhead } from '@ontrails/mcp';
await trailhead(app);
HTTP: Routes from trail IDs (dots become path segments), verbs from intent, error responses from taxonomy.
import { trailhead } from '@ontrails/http/hono';
await trailhead(app, { port: 3000 });
See the CLI trailhead docs, the MCP trailhead docs, and the HTTP trailhead docs for derivation details.
Provisions declare infrastructure dependencies — databases, API clients, caches — as first-class primitives alongside trails and events.
Define a provision with provision():
const db = provision('db.main', {
create: (svc) => Result.ok(openDatabase(svc.env?.DATABASE_URL)),
dispose: (conn) => conn.close(),
health: (conn) => conn.ping(),
mock: () => createInMemoryDb(),
});
The create factory receives ProvisionContext (env, cwd, workspaceRoot only — not the full TrailContext). Provisions are singletons, resolved once per process and cached.
Declare on trails with provisions: [...]:
const search = trail('search', {
provisions: [db],
input: z.object({ query: z.string() }),
output: z.array(z.object({ id: z.string(), title: z.string() })),
blaze: async (input, ctx) => {
const conn = db.from(ctx);
return Result.ok(await conn.search(input.query));
},
});
Access via db.from(ctx) (typed, preferred) or ctx.provision<Database>('db.main') (dynamic escape hatch).
Test with zero config — provisions with mock factories auto-resolve in testAll(app). Override explicitly when needed:
testAll(app, () => ({ provisions: { 'db.main': createSpecialTestDb() } }));
Governance: The warden enforces provision-declarations (usage matches declarations) and provision-exists (provision IDs resolve in the topo).
See contract-patterns.md for declaration patterns and testing-patterns.md for mock strategies.
testAll(app) runs the full governance suite in one line:
TDD workflow: Define trail with examples → run tests (red) → implement (green) → refactor.
Edge cases go in testTrail(trail, scenarios). Use createCrossContext() to mock ctx.cross for composite trail unit tests. Trailhead integration uses createCliHarness() / createMcpHarness().
See testing-patterns.md for the full testing API.
13 error classes, deterministic mapping to exit codes, HTTP status, and JSON-RPC codes:
| Category | Classes | Exit | HTTP | Retry |
|---|---|---|---|---|
| validation | ValidationError, AmbiguousError | 1 | 400 | No |
| not_found | NotFoundError | 2 | 404 | No |
| conflict | AlreadyExistsError, ConflictError | 3 | 409 | No |
| permission | PermissionError | 4 | 403 | No |
| timeout | TimeoutError | 5 | 504 | Yes |
| rate_limit | RateLimitError | 6 | 429 | Yes |
| network | NetworkError | 7 | 502 | Yes |
| internal | InternalError, AssertionError | 8 | 500 | No |
| auth | AuthError | 9 | 401 | No |
| cancelled | CancelledError | 130 | 499 | No |
Use the most specific class. Return Result.err(new XError(...)), never throw.
See error-taxonomy.md for constructor signatures and patterns. See common-pitfalls.md for anti-patterns.
Converting existing code to Trails:
testAll()See migration-checklist.md for the detailed checklist.
The warden enforces conventions and detects drift:
trails warden # Convention checks
trails warden --drift # Contract drift vs lock file
Key rules: no throw in blaze functions, no trailhead imports, crosses declarations match ctx.cross() calls, provision declarations match db.from(ctx) / ctx.provision() calls, output schemas present, .describe() on fields.
| Reference | Content |
|---|---|
| getting-started.md | Full install-to-test walkthrough |
| architecture.md | Hexagonal model, package gates, data flow |
| contract-patterns.md | ID naming, schema design, example authoring |
| CLI trailhead docs | Flag derivation, output modes, exit codes |
| MCP trailhead docs | Tool naming, annotations, progress |
| testing-patterns.md | testAll, testTrail, harnesses |
| error-taxonomy.md | All 13 error classes with signatures |
| common-pitfalls.md | 9 anti-patterns with fixes |
| migration-checklist.md | Step-by-step conversion guide |
| trail.md | Annotated trail skeleton |
| composition.md | Annotated composite trail skeleton |
| patterns.md | Before/after: common transformation patterns |
| express-handler.md | Before/after: Express routes → trails |
| cli-command.md | Before/after: Commander commands → trails |
| mcp-tool.md | Before/after: MCP tool handlers → trails |
| composition.md | Before/after: direct calls → ctx.cross |
npx claudepluginhub outfitter-dev/trails --plugin trailsBuilds end-to-end type-safe APIs with tRPC — routers, procedures, middleware, subscriptions, and Next.js/React integration patterns.
Builds tRPC APIs with Zod validation, middleware chaining, Vertical Slice architecture, and domain error handling.
Generates vertical traces and RED contract tests from spec.md, tracing API scenarios through layers (API→Handler→Service→DB) with parameter transformations. Supports delta updates for spec changes.