Fascicle never dictates your app's shape — but the apps that stay easy to change all converge on the same one. The blueprint is that shape, distilled from the reference apps and from production consumers: one composition layer, markdown prompts, normalized module contracts, and boundaries enforced by lint rules instead of memory.
Building an agent on fascicle — or pointing a coding agent at the task? Start here. This page is the summary; the full spec lives in docs/blueprint.md.
One file — call it flow.ts — that the reader opens to see the entire topology, written in fascicle vocabulary only: sequence, parallel, branch, loop, scope, step, model_call. Everything that is not shape — string formatting, IO, state transitions, extraction — lives in sibling modules and is plugged in as plain functions.
Steps are plain values: anything with the same Step<i, o> type swaps for anything else. That plug-and-play property is only worth something if your app has a place where the blocks are visible. Scattered through business logic, the topology exists only in the reader's head. Gathered into one layer, the topology is the file — and swapping a block is editing one line of it.
Fascicle has three useful adoption tiers. Pick the smallest one that keeps your topology legible — and pick it per subsystem, not per project. A deterministic CLI can be tier 1 while its eval harness is tier 2.
A plain async pipeline; model calls are leaf functions behind a small app-owned port. Fascicle owns provider portability, schema enforcement, retries, and cost — you never adopt the composition algebra.
Fascicle's loop / sequence at the top and nothing else; each step delegates to a phase module. Trajectory, resume, abort, and cost caps fall out for free.
Fascicle owns the whole topology; you write leaf functions only. The full blueprint — the standard layout, stage factories, state readers — assumes this tier.
The names matter less than the contracts. What kills legibility is not calling the file pipeline.ts instead of flow.ts — it is a message builder implemented inline in the flow, or a model_call hidden in a service.
| MODULE | OWNS | MUST NOT CONTAIN |
|---|---|---|
| {{ c.name }} | {{ c.owns }} | {{ c.not }} |
Every one of these earned its place in the blueprint by the pain of its presence in a real codebase. The fix is in each card.
The consumers that stayed clean all enforce the blueprint with ast-grep rules in CI: create_engine confined to engine.ts, no imperative loops in the composition layer, fascicle value imports confined to the files allowed to know about it.
Working, tested copies of all three rules ship in the reference app — copy the directory into your own app as a starting point. Each rule is a few lines, and each turns an architecture review comment into a build failure.
Copy the rules →Composable TypeScript toolkit for agentic workflows. Compose LLM calls, tools, and plain functions into a Step<i, o>.