Skip to content

Flatcode

Flatten any repository into a readable .spec tree. One local command, no server, no account.

spex flat new https://github.com/owner/repo

The product is a directory: the clone with its spec tree committed on a flatcode branch, plus the reading. One more command turns it into a static site any host can serve.

Already-flattened examples live at flatcode.spexcode.net.

Why this is a command and not a campaign

An agent reading an unfamiliar codebase and writing its intent down already happens today — but its stopping condition is a person deciding it looks done. That condition does not survive being left alone.

So this loop stops on a measured signal instead, which is the only reason it can run unattended.

The gate

A round is one non-interactive agent turn followed by a reading. The gate is three existing signals, and the agent's own report is not among them:

  • spex spec lint errors must be zero. Integrity, one-govern, id-format, living and mention are structural truths about the tree; any of them failing means the tree is wrong, not merely thin.
  • Coverage must reach the floor. Lint already reports every governed source file no node claims. The uncovered count over the governed-file count is the only honest measure of "is this repository specced yet".
  • spex doctor's altitude and breadth findings become the next round's instructions. They name the nodes that dumped mechanics instead of stating intent — the quality signal, because a tree can satisfy lint completely while reading as a paraphrase of the code.

A round that fails does not retry the same prompt: the findings ARE the next prompt. Rounds are bounded, and exhausting the budget reports a PARTIAL flat naming what still fails, with a non-zero exit. It never reports a success it did not measure.

The governed set is confirmed, not asserted

A foreign repository has no spexcode.json, and lint with no governedRoots/sourceExtensions finds zero source files — which makes coverage vacuously complete and would pass an empty .spec on any repository in the world.

So Flatcode derives the governed roots and source extensions from what the repository actually tracks. But reading the file tree only proposes a governed set: lint applies the product's own source policy on top (its test globs drop tests/, test_* and *.test.* wholesale), so a root can be proposed, written into the config, and then govern nothing.

The proposal is therefore confirmed against lint's own accounting: roots it kept nothing from are dropped, the config is rewritten, the reading is taken again under the config actually in effect, and the reported count is lint's rather than the file walk's. An empty confirmed set is refused rather than gated.

Options

Option What it does
--out <dir> Where the flat is written (default <repo>.flat)
--launcher <name> Which agent runs it. Must name a harness with a non-interactive turn; one without is refused by name rather than quietly swapped
--rounds <n> Round budget (default 6)
--coverage <pct> Coverage floor (default 90)
--lang <code> Language of the spec prose, e.g. --lang zh. Prose only: node ids are directory names and part of URLs, so they stay lowercase ASCII

Preview

spex flat site <flat-dir>

Writes <flat-dir>/site: the read-only graph, one document per node, the .spec archive, and a release manifest with a SHA-256 per file. Plain static files with no backend, so any static host serves it — and a flat that did not converge still previews, its About panel carrying the coverage so a partial tree never reads as a finished one.

The directory is relocatable: everything it names is relative to itself, so the same bytes serve from a domain root or any path prefix.

What gets published is the repository's own spec. The SpexCode workflow nodes spex init seeds under .plugins are machinery the conversion needs, not a reading of the target, so the graph, the documents and the archive all exclude them.

Many repositories on one host

spex flat gallery --out <dir> <flat-dir>…

Assembles flats into one static tree: each at <out>/<owner>/<repo>/, an index page listing them, and gallery.json naming every entry with a SHA-256 of its release manifest — so what a host serves can be compared with what was built.

An entry's path comes from the source the flat read, never from your --out name: two people flattening the same repository must land on the same path.

What it is not

Flatcode does not deploy, does not own a subdomain, and does not authenticate anyone. It produces a directory; hosting is a separate surface with its own trust boundary.