trails
/trails
Buduje z frameworkiem Trails: definiuje kontrakty, otwiera powierzchnie CLI i MCP, testuje, debuguje i migruje kod.
- Autor
- outfitter-dev ↗
- Repozytorium
- trails ↗
- Licencja
- MIT
- Ostatnia zmiana repozytorium
- 09.11.2026
Ocena i bezpieczeństwo
- Pliki w paczce
- 20
- Skrypty
- 0
- Komentarze HTML
- 0
- Adresy URL w SKILL.md
- 0
Brak sygnałów. Repozytorium zmienione 09.11.2026, bez odwołań do wycofanych modeli i API.
MIT, otwarta: można używać i zmieniać, z zachowaniem informacji o autorze.
Co robi i kiedy
Buduje z frameworkiem Trails: definiuje kontrakty, otwiera powierzchnie CLI i MCP, testuje, debuguje i migruje kod.
Oryginalny opis (EN)
Build with the Trails framework — define trail contracts, open CLI/MCP surfaces, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding surfaces, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.
Jak działa
- Quick Start
- Lexicon
- Package Orientation
- Agent Wayfinding
- Creating Trails
- Surfaces
- Resources
- Testing
- Error Taxonomy
- Migration
Podgląd SKILL.md
---
description: Build with the Trails framework — define trail contracts, open CLI/MCP surfaces, test with examples, debug errors, migrate codebases, run governance. Use when creating trails, adding surfaces, testing, debugging Trails errors, migrating to Trails, running warden, or any work involving @ontrails/* packages.
metadata:
skillset.schema: "1"
trails: 0.2.5
version: 0.4.0
name: trails
---
# Trails
Contract-first TypeScript framework. Define a trail once with typed input, Result output, examples, meta, and an implementation that establishes how it runs — then surface it on CLI, MCP, or HTTP today, with the RPC surface (JSON-RPC 2.0, hosted on WebSocket first) landing on the same contract-first model.
## Quick Start
```typescript
// 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!' } }],
implementation: (input) => Result.ok({ message: `Hello, ${input.name}!` }),
});
// 2. Collect into topo
const graph = topo('myapp', greetModule);
// 3. Open surfaces
await surface(graph); // CLI — from @ontrails/commander
// await surface(graph); // MCP — from @ontrails/mcp
// await surface(graph, { port: 3000 }); // HTTP — from @ontrails/hono or @ontrails/http/bun
// 4. Headless execution (no surface needed)
const result = await run(graph, 'greet', { name: 'Alice' });
// 5. Test
testAll(graph); // Examples + contract suite in one line
```
## Lexicon
Use these terms — they are non-negotiable in Trails codebases.
| Term | Meaning | Not this |
|------|---------|----------|
| `trail` | Unit of work (atomic or composite) | handler, action |
| `compose` | Composition declaration and runtime verb | workflow, route |
| `topo` | Queryable graph of trails, signals, resources, and relationships | registry, collection |
| `implementation` | Authored behavior that establishes how a trail runs from validated input to Result | handler, impl |
| `surface` | The boundary-owned one-liner that opens a graph | serve, mount |
| `graph` | Local name for a topo instance | app, registry |
| `derive` | Produce canonical facts or an intermediate representation from authored truth | operation |
| `render` | Present derived facts through a surface or format | operation |
| `meta` | Trail annotations and ownership data | tags, metadata |
| `warden` | Governance enforcement | linter |
## Package Orientation
Current public packages are lockstep at the same Trails framework version.
- **Core model:** `@ontrails/core` owns Result, errors, trail/signal/entity/topo contracts, resources, layers, execution, validation, and adapter ports.
- **Surfaces:** `@ontrails/commander`, `@ontrails/mcp`, `@ontrails/hono`, and `@ontrails/http/bun` open the same topo on CLI, MCP, Hono HTTP, or Bun-native HTTP. `@ontrails/http` owns shared route derivation, OpenAPI, and the Web Fetch kernel; `@ontrails/cli` owns the framework-agnostic CLI command model; `@ontrails/vite` adapts Trails surfaces for Vite projects.
- **Infrastructure:** `@ontrails/config`, `@ontrails/permits`, `@ontrails/store`, and `@ontrails/drizzle` cover config, authorization, schema-derived stores, and Drizzle SQLite bindings.
- **Observability:** `@ontrails/observability` defines sink contracts; its `/dev` and `/otel` subpaths provide developer-state and OTel support, while `/logtape` and `/pino` are temporary sink adapters.
- **Ecosystem:** `@ontrails/testing` provides contract tests and surface harnesses; `@ontrails/topography` owns TopoGraphs, semantic diffing, lock manifests, topo-store persistence, and Wayfind graph-read query APIs; `@ontrails/warden` owns governance rules.
- **Stable install policy:** Normal 0.x releases use the npm `latest` tag. Use the exact package pins in [Getting Started](references/getting-started.md). The 0.x line permits documented API changes in minor releases; patches preserve compatibility. Existing 1.0 beta ranges require an explicit manifest migration.
## Agent Wayfinding
When saved Topography artifacts can answer a graph question, use Wayfinder before raw text search:
```bash
trails wayfind --overview --root-dir . --json
trails wayfind --trails --intent read --root-dir . --json
trails wayfind <trail-id> --contract --root-dir . --json
trails wayfind <trail-id> --deps --root-dir . --json
trails wayfind <trail-id> --impact --map --root-dir . --json
trails schema wayfind
```
- Start with `trails wayfind --overview` to learn artifact source, freshness, and graph counts.
- Use the selected operator CLI shape for filtered discovery: `trails wayfind --trails`, `--resources`, `--signals`, `--surfaces`, `--trailheads`, `--entities`, `--errors`, or `--adapter <package>`. Attach bounded related facts with `--include adapters`, `errors`, `examples`, `surfaces`, or `versions`. Topography may expose graph-query APIs beyond the operator CLI/MCP selection; check `trails schema wayfind` before constructing shell calls.
- Use `trails schema <command...>` when you need accepted CLI routes, aliases, flags, and schemas before constructing shell calls.
- Use `wayfind.describe` for a full saved entity record and `wayfind.contract` for a trail or version input/output/intent summary.
- Use `wayfind.nearby`, `wayfind.impact`, and `wayfind.diff` for relation context, blast-radius reads, and explicit saved-baseline comparison.
- Use `trails wayfind query "<phrase>"` for indexed text queries. Treat Wayfinder as graph-read only; do not assume semantic search, signposts, or implications exist in v0.
Wayfinder trails are internal by default. Host apps expose selected queries deliberately, usually as read-only operator tools or MCP resources protected by the host's authorization boundary. Fall back to `rg`, qmd, source reads, or a fresh compile when Wayfinder reports missing or stale artifacts, when the task needs source code that Topography does not derive, or when writing artifacts is outside your current authority.
## Creating Trails
### Atomic vs Composite Trails
- **Atomic trail**: does one thing. `(input, ctx) => Result`. Default choice.
- **Composite trail**: composes other trails. Declares `composes: [...]`, uses `ctx.compose()`.
- **Runnable trail**: an authored contract with an implementation. The runtime runs trails, not implementations.
### Trail ID Conventions
Dotted, lowercase, verb-last: `entity.show`, `math.add`, `search`. Dots become CLI subcommands and MCP tool name segments.
### Input Schema
Every field gets `.describe()` — this becomes `--help` text, MCP descriptions, and form labels.
```typescript
input: z.object({
name: z.string().describe('Entity name to look up'),
limit: z.number().default(20).describe('Maximum results'),
})
```
### Output Schema
Required for MCP and HTTP surfaces. Define what Result.ok returns.
### Intent and Flags
| 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. |
### Examples
Each example is both documentation AND a test case:
- **Full match**: `expected: { ... }` — deep equals
- **Schema-only**: no expected — validates against output schema
- **Error match**: `error: 'NotFoundError'` — asserts error type
See [contract-patterns.md](references/contract-patterns.md) for detailed patterns. Copy from [trail.md](templates/trail.md) or [composition.md](templates/composition.md).
## Surfaces
Adding a surface is a `surface()` 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.
```typescript
import { surface } from '@ontrails/commander';
await surface(graph);
```
Use `cli` on a trail only for canonical command overrides or trail-owned aliases that still normalize into the same trail contract. String aliases are sibling leaf aliases (`find` beside `search`); string-array aliases are absolute command paths (`['wf', 'search']`). Author app-owned compatibility routes in an exported `trailsOverlays = [surfaceOverlay({ cli: { 'wf.search': 'wayfind.search' } })]` in the app module, then pass `trailsOverlays` to the CLI surface's `overlays` option. Compile, validate, Wayfinder, and `trails schema` then inspect the same routes the runtime CLI accepts. See [CLI surface](references/cli-surface.md).
Treat aliases, future input mappings, and trailheads as **surface accommodations**: render-level fit adjustments, not alternate behavior. The trail stays the capability. A surface entry is the invocable affordance on a surface; an approach is the way a caller reaches it. Aliases add alternate approaches to the same trail, input mappings normalize surface-shaped input into the same trail input, and trailheads group several trails into one entry while preserving the selected trail ID. Use the ADR-0050 test: if the fit would change intent, permits, errors, outputs, lifecycle, side effects, or hide which trail is running, call it a trail fork and author a distinct or composing trail instead.
Classify surface-fit work before editing:
| Shape | Classification |
| --- | --- |
| One trail, another path, no input reshape | Alias |
| One trail, surface-shaped input that normalizes honestly | Input mapping |
| Many trails, one grouped entry, member trail identity preserved | Trailhead |
| Different intent, permits, errors, outputs, lifecycle, side effects, or hidden member identity | Distinct trail or composing trail |
**MCP**: Tool names from trail IDs, JSON Schema from Zod, annotations from intent, idempotency, and description.
```typescript
import { surface } from '@ontrails/mcp';
await surface(graph);
```
Dense MCP surfaces may use **trailheads** to group related trails into fewer agent-facing tools. A trailhead is surface rendering configuration, not a core `Facet` primitive and not a new domain operation. It groups and selects without merging. Author it in MCP surface options, call it with `{ trail, input }`, and expect successful results as `{ trail, output }` so the underlying trail stays visible.
```typescript
await surface(graph, {
trailheads: {
governance: {
description: 'Run project diagnostics and Warden guidance.',
mcp: { loading: 'deferred' },
trails: ['doctor', 'warden', 'warden.guide'],
},
},
mcpResources: { examples: true, surfaceMap: true },
});
```
Use `trails://surface-map` and per-trail MCP resources for cold context before guessing at grouped affordances. Adapter-kit may validate resolved derived evidence for future surface adapters, but it does not define or author trailheads. Do not invent `facet()`, `overlapsWith`, or adapter-kit `facet` config.
**HTTP**: Routes from trail IDs (dots become path segments), verbs from intent, error responses from taxonomy. Use Hono for framework portability or Bun-native HTTP when you want Bun serving without a third-party runtime; both share the `@ontrails/http` route/fetch kernel.
```typescript
import { surface } from '@ontrails/hono';
await surface(graph, { port: 3000 });
```
```typescript
import { surface } from '@ontrails/http/bun';
await surface(graph, { port: 3000 });
```
The RPC surface is hosted on WebSocket and speaks JSON-RPC 2.0; it is not a "WebSocket surface". See the CLI surface docs, the MCP surface docs, and the HTTP package docs for derivation details.
## Resources
Resources declare infrastructure dependencies — databases, API clients, caches — as first-class primitives alongside trails and signals.
**Define** a resource with `resource()`:
```typescript
const db = resource('db.main', {
create: (resourceCtx) => Result.ok(openDatabase(resourceCtx.env?.DATABASE_URL)),
dispose: (conn) => conn.close(),
health: (conn) => conn.ping(),
mock: () => createInMemoryDb(),
});
```
The `create` factory receives `ResourceContext` (`env`, `cwd`, `workspaceRoot`, and validated `config` when the resource declares a config schema — not the full `TrailContext`). Resources are singletons, resolved once per process and cached.
**Declare** on trails with `resources: [...]`:
```typescript
const search = trail('search', {
resources: [db],
input: z.object({ query: z.string() }),
output: z.array(z.object({ id: z.string(), title: z.string() })),
implementation: 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.resource<Database>('db.main')` (dynamic escape hatch).
**Test** with zero config — resources with `mock` factories auto-resolve in `testAll(graph)`. Mark live-only dependencies with `unmockable: { reason }` and provide explicit overrides for examples or contracts that need them.
```typescript
testAll(graph, () => ({ resources: { 'db.main': createSpecialTestDb() } }));
```
**Governance:** The warden enforces `resource-declarations` (usage matches declarations) and `resource-exists` (resource IDs resolve in the topo).
See [contract-patterns.md](references/contract-patterns.md) for declaration patterns and [testing-patterns.md](references/testing-patterns.md) for mock strategies.
## Testing
`testAll(graph)` runs the full contract suite in one line:
1. Topo validation (composes, schemas, signals, resources)
2. Example execution (every example as an assertion)
3. Contract checks (output matches schema)
4. Detour verification (targets exist)
**TDD workflow**: Define trail with examples → run tests (red) → implement (green) → refactor.
Edge cases go in `testTrail(trail, scenarios)`. Use `createComposeContext()` to mock `ctx.compose` for composite trail unit tests. Surface integration uses `@ontrails/testing/cli`, `@ontrails/testing/mcp`, `@ontrails/testing/http`, and `@ontrails/testing/surface-parity`.
See [testing-patterns.md](references/testing-patterns.md) for the full testing API.
## Error Taxonomy
17 fixed-category error classes across 10 categories, plus the dynamic `RetryExhaustedError` wrapper, with 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, VersionNotSupportedError | 2 | 404 | No |
| conflict | AlreadyExistsError, ConflictError | 3 | 409 | No |
| permission | PermissionError, PermitError | 4 | 403 | No |
| timeout | TimeoutError | 5 | 504 | Yes |
| rate_limit | RateLimitError | 6 | 429 | Yes |
| network | NetworkError | 7 | 502 | Yes |
| internal | InternalError, DerivationError, RecoverableCompletionError, AssertionError | 8 | 500 | No |
| auth | AuthError | 9 | 401 | No |
| cancelled | CancelledError | 130 | 499 | No |
`RetryExhaustedError` is dynamic: it wraps another `TrailsError`, inherits the wrapped error's category for surface mappings, and always reports `retryable: false`.
Use the most specific class. Return `Result.err(new XError(...))`, never throw.
See [error-taxonomy.md](references/error-taxonomy.md) for constructor signatures and patterns. See [common-pitfalls.md](references/common-pitfalls.md) for anti-patterns.
## Migration
Converting existing code to Trails:
1. Inventory handlers (routes, CLI commands, MCP tools)
2. Extract Zod input/output schemas
3. Convert implementations to return Result (replace throw/console.log/process.exit)
4. Compose into topo, open surfaces
5. Add examples, run `testAll()`
6. Run warden for governance
See [migration-checklist.md](references/migration-checklist.md) for the detailed checklist.
## Governance
The warden enforces conventions and detects drift:
```bash
trails warden # Convention checks
trails warden --lock cached --no-lock-mutation # Governance against cached lock data
trails compile # Regenerate a standalone or current-app trails.lock
trails compile --app my-app # Regenerate one configured app lock from a workspace root
trails validate # Verify the current app, or every app from a workspace root
trails validate --app my-app # Verify one configured app from a workspace root
```
Each lock-owning app has its own root `trails.lock`. A configured workspace derives its app set from `workspace.apps`; it never owns an aggregate workspace-root lock.
For the current generated rule index, read [warden-guide.md](references/warden-guide.md) instead of relying on copied rule prose.
## References
| Reference | Content |
|-----------|---------|
| [getting-started.md](references/getting-started.md) | Full install-to-test walkthrough |
| [architecture.md](references/architecture.md) | Hexagonal model, package boundaries, data flow |
| [contract-patterns.md](references/contract-patterns.md) | ID naming, schema design, example authoring |
| CLI surface docs | Flag derivation, output modes, exit codes |
| MCP surface docs | Tool naming, annotations, progress |
| [http-surface.md](references/http-surface.md) | Route derivation, OpenAPI, Hono, Bun-native HTTP, fetch kernel |
| [testing-patterns.md](references/testing-patterns.md) | testAll, testTrail, harnesses |
| [error-taxonomy.md](references/error-taxonomy.md) | Error classes and signatures |
| [warden-guide.md](references/warden-guide.md) | Generated Warden rule guidance from the live manifest |
| [common-pitfalls.md](references/common-pitfalls.md) | 12 anti-patterns with fixes |
| [migration-checklist.md](references/migration-checklist.md) | Step-by-step conversion guide |
| [trail.md](templates/trail.md) | Annotated trail skeleton |
| [composition.md](templates/composition.md) | Annotated composite trail skeleton |
| [patterns.md](examples/patterns.md) | Before/after: common transformation patterns |
| [express-handler.md](examples/express-handler.md) | Before/after: Express routes → trails |
| [cli-command.md](examples/cli-command.md) | Before/after: Commander commands → trails |
| [mcp-tool.md](examples/mcp-tool.md) | Before/after: MCP tool handlers → trails |
| [composition.md](examples/composition.md) | Before/after: direct calls -> ctx.compose |
Pliki w paczce (20)
- LICENSE.txt 1 KB
- SKILL.md 18 KB
- examples/cli-command.md 3 KB
- examples/composition.md 4 KB
- examples/express-handler.md 3 KB
- examples/mcp-tool.md 3 KB
- examples/patterns.md 2 KB
- references/architecture.md 11 KB
- references/cli-surface.md 5 KB
- references/common-pitfalls.md 7 KB
- references/contract-patterns.md 8 KB
- references/error-taxonomy.md 5 KB
- references/getting-started.md 8 KB
- references/http-surface.md 4 KB
- references/mcp-surface.md 6 KB
- references/migration-checklist.md 4 KB
- references/testing-patterns.md 10 KB
- references/warden-guide.md 13 KB
- templates/composition.md 4 KB
- templates/trail.md 4 KB
Wersje
W bazie nie ma innych skilli o nazwie trails.
Migawka bazy z 13.11.2026 · wersja paczki 8bf742bc3e89 (skrót SHA-256 całej paczki)
Pobrania w ostatnich 30 dniach: 0 · ranking
Zgłoś problem z tym skillem: szkodliwa treść, błędny opis albo prośba autora o usunięcie.
Podobne skille
- 100warden-skill
Pokazuje, jak używać Warden CLI lokalnie do analizy zmian w kodzie: polecenia, konfiguracja warden.toml, własne skille, triggery i rozwiązywanie problemów. - 86api-surface
Sprawdza powierzchnie API i MCP, gdy użytkownik prosi o inspekcję. - 90adding-an-operation
Prowadzi przez ścieżkę operacji od katalogu do handlera, sklepu, CLI, MCP i testów. - 100melonjs-particles-and-trails
Obsługuje efekty cząsteczek i ślady ruchu w melonJS: konfiguracja ParticleEmitter, emisja seryjna i strumieniowa, referenceSpace, ruch 3D cząsteczek i renderable Trail. - 100warden
Uruchamia Warden do analizy zmian w kodzie przed commitem: sprawdzanie zmian, konfiguracja warden.toml, własne skille i triggery. - 100warden
Uruchamia Warden do analizy zmian w kodzie przed commitem: sprawdzanie zmian, konfiguracja warden.toml, własne skille i triggery.