Site pipeline
Beyond single-document writing, the repository ships a documentation-site
pipeline (pipeline/, the kronika-site binary): turn a product repository
into documentation content, then validate every page before publication. Brama
authors a typed content plan, never markup, and identifies product concepts in
the terms validator. Evidence, structure and command coverage are checked
mechanically. The pipeline is separate from the kronika CLI's
sync loop.
Stages
kronika-site (pipeline/src/run.mjs) orchestrates detect → author (or load a plan) →
validate → emit, stops at the first failing stage, and prints one final
report, as JSON or, with --text, one path: value line per field:
kronika-site <productRepo> --sources <file> (--standard <writing-standard.md> | --plan <file>) [--out <dir>] [--model <selector>] [--text]
Exit codes: 0 all green, 1 a stage failed, 2 usage error, 69 model
infrastructure down. Each stage module also runs standalone from the
pipeline/ directory, with --help printing its usage:
node src/stages/detect.mjs <productRepo> --sources <docs-sources.json> [--out brief.json] [--text]
node src/stages/validate.mjs --plan plan.json --brief brief.json [--repo path] [--text]
node src/stages/emit.mjs --plan plan.json --out <dir> [--text]
Detect
pipeline/src/stages/detect.mjs reads the product repository's docs-sources.json
— which must declare product and may declare binary, routes, config,
changelog, limits, and a docs map of plain evidence documents — probes
each declared surface, and computes brief.json. Detection never invents a
surface; only declared, probed surfaces count. The completion-gate formula:
| Evidence | Required page kinds |
|---|---|
| always | overview |
CLI binary answers --help |
quick-start, cli-reference |
| HTTP route file readable | api-reference |
| configuration document exists | config-reference |
| CHANGELOG exists | changelog; plus migration iff breaking entries |
| declared limits/quotas exist | limits |
Author
The model authors plan.json through Brama, conforming to the closed
schema in pipeline/schemas/plan.schema.json. The authoring prompt is built
from brief.json, the declared sources, and the writing standard the
caller names with --standard <file>, read whole and injected into the
prompt; the pipeline ships none. Authoring without --standard or
--plan is refused before detection with exit 2, and a standard that
cannot be read stops the stage as author_failed, naming the flag. The endpoint
is BRAMA_URL when set, otherwise the address published in
~/.stado/forwards/brama.local. Neither means an explicit
not_configured refusal naming both declarations; no address is built in.
The selected declaration must be an absolute HTTP(S) base URL without credentials,
query or fragment. An empty or invalid BRAMA_URL is refused by name
without reading the marker; only an unset variable selects the marker, which
undergoes the same validation. Refusals do not echo declaration contents.
Inference redirects are refused at the selected route instead of forwarding
prompts. Body-read failures retain HTTP status and transport cause; malformed
JSON or missing assistant content retains endpoint, status and the complete body.
An unreachable endpoint fails authoring with exit 69. Brama owns
sampling settings, and HTTP failures retain the full response body.
--plan <file> skips authoring, but validation can still call
Brama to judge recurring undeclared words.
Validate
pipeline/src/stages/validate.mjs runs five validators; the report contains
each validator's failures, and all must pass:
An exception inside a validator is a failure of that named validator, with its observed cause in the report. Invalid evidence expressions, unreadable sources and malformed plan fields do not abort reporting for the remaining validators or count as approval. Emission still requires every validator to pass.
Source commands retain complete stdout, stderr and multiline failure details, without a pipeline-selected output byte ceiling. URL evidence failures identify the URL, HTTP status and complete response body, or the transport cause. A failed source read cannot support a publishing claim.
A failed CLI help probe stops authoring before its Brama request, naming the binary, exit result, process error and both complete captured streams. Partial stdout from a failed command is not accepted as authoring evidence.
- claims — every
claim.evidenceoccurs in its named source. - drift — every documented command usage line and flag exists in the
live binary's
--help. Commands are allowlisted: only the product binary, only subcommand words plus--help/--version, nothing shell-interpreted. Long-option tokens must match completely:--output-formatdoes not establish--output. - terms — for undeclared words used on multiple pages, Brama judges whether they name product concepts using the complete page prose as untrusted reference data. A product concept needs a declaration with an existing defining page. An unavailable, malformed or out-of-vocabulary answer fails the terms validator; it does not count as approval.
- structure — the plan validates against the closed schema; there is no page kind outside it to choose.
- coverage — every required page kind is present, and each
command advertised by the binary's
--helphas acli-referencepage atcli/<command>or below it.
Detection requires a readable Commands: section with command
names. CRLF output and a section ending with the response are accepted.
An unreadable section is a CLI failure retaining the complete help text, not
an empty command inventory. Coverage refuses a declared CLI without a successful,
nonempty observed inventory, including older briefs that omitted commands.
Coverage probes the current binary's --help again with the same
parser as detection. Commands added since the brief was recorded still need
their pages. An unavailable binary, failed help command or unreadable command
inventory fails coverage instead of accepting stale brief evidence.
Coverage recursively reads each advertised command's own --help.
Nested commands require paths such as cli/fleet/create, not merely
the parent page. A leaf must expose a Usage: section. Failed or
unreadable child help and repeated ancestor inventories fail with the complete
command path and cause. Missing plan.pages or
brief.requiredKinds is a named coverage failure, not an empty
collection.
Drift resolves leading command words through each parent's observed command inventory. Once a leaf is reached, following positional values are operands, not additional help subcommands. An unadvertised command word at a parent reports that parent's command path and the unresolved word.
Each product invocation on its own line is checked against its own command help, even when comments or another command come first. Backslash-newline continuations remain part of the same invocation; flags on a later command are not compared against the first command's help.
The reusable real-interface journey is node tests/coverage/cli.mjs.
Declare KRONIKA_TEST_SOURCE_REVISION, KRONIKA_TEST_PRODUCT_REPO,
KRONIKA_TEST_PLAN, KRONIKA_TEST_BRIEF,
KRONIKA_TEST_REQUIRED_COMMAND_PAGE, KRONIKA_TEST_SUCCESS_EXIT
and KRONIKA_TEST_FAILURE_EXIT. It uses an authored plan, a real
detected binary and the configured Brama service when terms require judgement.
The baseline must pass all validators; removing a required command page or
naming an unavailable binary must fail coverage. Revisions, digests, commands,
exit statuses and validator output remain in .build/coverage;
only run-owned fixture copies are removed.
There is no prose quality score. The writing standard guides the author; the named validators enforce evidence, vocabulary, structure and coverage.
Emit
pipeline/src/stages/emit.mjs turns a validated plan into a DocPage data module
— docs.ts in the --out directory — for the consuming site's
DocumentationLayout, along with the navigation grouping. Publication is
the consuming site's CI concern: deploy only on all-green.