Why Forme?
Every static-site generator I've used has the same shape: a couple of opinions about layouts, a templating language, a thousand plugins, and at some point the realisation that the moment you want to do something the framework didn't anticipate, you have to fork it.
Forme is the opposite bet: keep the substrate tiny and typed, push all the policy out into composable stages.
The substrate
There are exactly five things in the FM01 kernel:
KindDescriptor— what flows on a wire (ContentSource, ContentNode, RenderedPage, …)Stage<In, Out>— a unit that consumes one Kind and produces anotherStageContext— the bundle of capabilities a stage receives (logger, clock, storage, network, …)- Capability strings (
storage:read,filesystem:write, …) gating the dangerous APIs - A typed DAG runtime that wires stages together using the declared Kinds
That's it. No layouts, no templates, no special "this is a blog" shape. The blog is just one configuration of one of many possible pipelines.
What you get
- A blog post is
Markdown → ContentSource → ContentNode → RenderedPage → DeployArtifact. Five stages, each tiny enough to fit on a page. - An RSS feed is a different terminal
Stage<Stream<ContentNode>, Feed>reading the same parsed nodes — no parser fork required. - A printed PDF (tracked by the FM-B017 backend-boundary milestone) is another terminal stage
consuming the same
ContentNodes and emitting aPrintForme. - A search index is a sibling collector that the orchestrator runs in parallel with the renderer.
The point of building the substrate first is that every one of those features stops being a fork-the-framework conversation and starts being a write-a-stage conversation.
Where we are
This blog is the v0 end-to-end demo: prove the substrate, ship a real artifact, then layer the more interesting capabilities (asset extraction, search index, RSS feed, an actual CSS theme) as follow-up PRs that don't touch the parser or the writer.
Tomorrow's post will walk through the kernel piece by piece.