Building the documentation¶
The site renders the repository's canonical Markdown and specification. A generated staging directory preserves their relative paths; it is not a second editable documentation tree.
Build and check¶
With Nix flakes enabled, run:
nix build .#docs
nix flake check --no-eval-cache
nix run .#docs-check
nix run .#browser-check
The documentation package and checks use the locked shared MkDocs toolchain. The documentation check inspects rendered internal links, section anchors, navigation, speech data and theme assets. Separate model and simulator checks exercise the proved Lean model and its finite corpus, and the model check cross-checks the compiled axiom report against the theorem manifest; none proves application behavior. See the model ledger and simulation for their exact scope.
The browser check launches the pinned Chromium build, serves the standalone simulator on a temporary loopback port, and executes its manual-control and story assertions. The canonical runner is tools/browser-check.mjs; the earlier callback under simulator/ is retained as historical browser evidence. Screenshots use a temporary writable directory; no browser download or external service is required during execution. Set KEEP_BROWSER_EVIDENCE=1 to retain successful browser evidence. CI's build gate realizes the packages, executing checks, and development-shell inputs before the downstream verification jobs.
Ordinary binaries and explicit test builds¶
As a release operator, I build ordinary singular and open-datum with
the manual Cabal test-harness flag disabled. Both executable closures use
the ordinary registry CLI modules. The release archive and publication
commands select these ordinary outputs.
As a contributor, I exercise crash windows and provider-answer loss with
the separately named test outputs. Their registry CLI modules retain the
original hold/drop controls and SINGULAR_HARNESS_TRIE_TRACE, an independent
observation of the trie capability's actual operations. That diagnostic is
a hidden test input and its evidence belongs to the test build.
nix build ./offchain#singular
nix build ./applications/open-datum/offchain#open-datum
nix build ./offchain#singular-test-harness
nix build ./applications/open-datum/offchain#open-datum-test-harness
nix run .#release-build-control
nix run .#demo1-cli-test-harness
The build control strips and inspects both ordinary compiled executables, reads the actual resolved registry CLI flag, and deliberately rejects the hook-bearing test artifacts and opposite flag polarity. CI runs the ordinary extracted-release journey separately from the test journey's unchanged recovery, race, node-loss and trie-observation assertions. CLI recovery and conformance backend controls select the explicit test composition. The separate replay-fault project remains a deliberately altered test build; its named refusal proof retains its published pending cases. Test outputs are never substituted into the ordinary release verifier or published as ordinary release artifacts.
flowchart TB
Source[Registry CLI source] -->|flag disabled| Ordinary[Ordinary binaries]
Ordinary -->|inspect bytes and flags| Exclusion[Release build control]
Exclusion -->|checks pass| Journey[Ordinary archive journey]
flowchart TB
Source[Registry CLI source] -->|flag enabled explicitly| Tests[Test binaries]
Tests -->|faults and diagnostics| Recovery[Recovery controls]
Recovery -->|assertions pass| Evidence[Test receipts]
Generated API references¶
The site ships a generated reference for each library a contributor calls into, built from the same revision as the pages that link it. Which code has one, and what stands in for it where there is none:
| Code | Reference on this site |
|---|---|
| The off-chain registry library's public modules | Haddock: the off-chain API reference, including the four modules it re-exports from the public local-services library |
| The Conformance library | Haddock: the Conformance API reference |
The registry validators under onchain/ |
aiken docs from the onchain flake's pinned compiler: the validator API reference |
| The runtime service modules outside the library's extent | None generated; the Runtime service ownership page records which module owns which concern |
| The off-chain and Conformance executables and test suites | None generated; the registry command owners and Conformance suite owners pages link their sources |
The naming validators under naming-onchain/ |
None generated; a separate Aiken project, documented by record value and recovery |
Executables, test suites and the runtime service modules outside the library's extent have no generated pages here. That coverage gap stays visible, exactly as the off-chain reference's own page records it: wider generated coverage is a separate decision for the epic's owner, not one this page takes. Each generated reference carries a manifest binding its pages to the digests of the source it was generated from, and the documentation check fails when a page, its source or its link targets no longer agree.
The repository code inventory¶
just inventory maps every file the tree actually holds to its lint policy
and its format policy — or, for data and artifacts, to a named non-code
class — and fails on anything unmapped. Discovery walks the tree itself
(extensions, shebangs, executable mode and component manifests), so a new
source file in a covered directory is mapped without editing anything,
while a file nobody classified fails with a named diagnostic instead of
passing silently. The same inventory runs over the Nix flake source, where
no Git directory exists, through nix run .#inventory-check; pull-request
CI runs both forms so the two contexts cannot drift. The checked registry
lives in tools/code_inventory.py.
The inventory is a mapping, not enforcement: its report names the CI
carrier that executes each policy, and every lint and format policy is
enforced — no code family is left without a checker, and no source
directory is excluded. just inventory-controls runs the negative and
positive controls: synthetic orphans injected into scratch copies of the
classified tree, each required to produce its intended failure. The
controls never touch the working tree.
Haskell formatting¶
The repository's Haskell trees are formatted with Fourmolu under one house
configuration, fourmolu.yaml at the repository root. Every formatter
entrypoint — the off-chain lint check, the Conformance format check and the
root recipes below — passes that file explicitly, so a missing configuration
fails loudly instead of falling back to Fourmolu's defaults, and there is no
per-tree configuration to drift from it.
nix develop --quiet -c just format # apply to every discovered Haskell source
nix develop --quiet -c just format-check # the same check `just ci` carries
Discovery is dynamic: every Haskell source under offchain/ and
conformance/ — the formerly fenced verifier sources and the evaluation
spike included — with no directory exclusions, so a new file or component
directory is covered with nothing to edit. The Conformance tree gets the
same check in its own Nix source context through nix run .#format-check
from conformance/ (CI job 'Conformance Haskell format and lint check'). just
format-controls runs the negative and positive controls: a source that
Fourmolu's defaults accept but the house configuration rejects must fail
the check — proving the configuration is read — and its formatter correction
must pass it again; a tree without the configuration must fail loudly rather
than format with defaults; a newly tracked component tree outside the old
directories joins the check through the Git index, while ignored untracked
build noise never enters it and a tracked file under an ignored path stays.
The controls run over scratch copies and never touch the working tree.
HLint runs over the same discovered extent with no exclusions and no ignore
file: nix run .#lint from offchain/ (CI job 'Off-chain lint') checks both
formats and hints, and nix run .#hlint-check from conformance/ runs
beside the Conformance format check.
Other languages¶
Every other code family the inventory discovers is linted and format-checked by one command, over the inventory's own extent, with the tools the development shell pins:
nix develop --quiet -c just lint # the check `just ci` carries
nix develop --quiet -c just lint-fix # apply the formatters in place
nix develop --quiet -c just lint-controls # planted defects must fail it
| Family | Format | Lint |
|---|---|---|
| Nix | nixfmt | statix, deadnix, Conformance lock equal to the off-chain lock |
| Python | ruff format | ruff check |
| Shell | shfmt -i 2 -ci -bn |
shellcheck |
| JavaScript, CSS | Biome (biome.json) |
Biome recommended rules |
| justfiles | just --fmt |
the same parse |
| Workflow YAML | yamlfmt | actionlint with shellcheck |
| Lean, HTML templates | style rules | style rules |
Lean and HTML have no general formatter here, so the explicit style rules
are the policy: no tab, no carriage return, no trailing whitespace, and
exactly one final newline. Aiken sources are checked by aiken fmt --check
inside the onchain/ and naming-onchain/ flake checks, with the Aiken
each flake pins; formatting leaves every compiled validator and its hash
unchanged. Generated files — the shipped simulator page, the Lean mirror
and the cage vectors — are regenerated from their formatted sources, and
their freshness checks stay the policy. A code family nobody checks fails
just lint instead of passing silently.
Edit and serve¶
The development shell has a separate CI build because packaged builds do not exercise it:
nix develop --quiet -c just ci
nix develop --quiet -c just serve-docs
just build-docs and just serve-docs stage the generated API references
from the packaged site build, nix build .#docs, so the local site links
the same generated pages the packaged site and its checks carry; the
Markdown pages still reload live as they are edited.
To serve the packaged output without a live-reload editor, run the docs-serve tool (marked not-a-guarantee) with the port as its argument and open http://127.0.0.1:8000.
Publication and accessibility¶
Pull requests publish to a live preview with the candidate revision recorded alongside the generated site. Preview publication does not post PR comments. The default branch publishes through workflow-mode GitHub Pages after merge; the same documentation build supplies both routes.
The Terminal theme follows the system light/dark preference until a reader chooses with the toggle, and remembers that choice. Diagrams follow the same palette and open at full size in a viewer. Each section has a play button that speaks the recorded narration of its paragraphs in the Puck voice. The voice says the page text word for word: the speech companions are written by the extraction tool, which reads the prose of each rendered page, and tables, code blocks and diagrams are not spoken. The documentation check fails when a paragraph changed without its recording. just extract-speech rewrites the companions after a page edit and just narrate records the paragraphs that changed; recording needs a DeepInfra key. Diagrams are Mermaid, rendered by the copy the shared toolchain pins as a fixed-output Nix input and served from the site itself; no page loads a script or stylesheet from outside the site, and the site check fails if one does.
Documentation releases¶
The documentation version is the one version.txt, the release manifest and the changelog agree on; a prepared version is not evidence that an archive was tagged or published. Release-please's simple strategy updates those three surfaces together for later versions, and Nix requires the version and manifest to agree. These are documentation versions, not deployed protocol versions.
After a successful main-branch gate, repository-scoped App credentials let release-please prepare a release PR whose changes trigger normal CI. It plans PRs only: release creation is disabled because upstream release creation posts a PR comment. Merging the release PR prepares the version but does not tag or publish it. An operator-authorized v<version> tag on that merged release PR's exact commit triggers publication. The commit must belong to main, and tag, manifest and version must agree.
The tag workflow builds and checks a Nix-produced documentation archive and checksum, publishes them to the matching GitHub release, downloads and verifies the uploaded bytes, then changes the release PR's pending label to tagged. It accepts a pending or already-tagged matching PR so interrupted publication can be retried. It does not post PR comments. CI has a manual recovery trigger; no release workflow or tag is dispatched merely by adding this automation.
Build the documentation archive locally with nix build .#docs-release, and run version, workflow and artifact checks with nix run .#release-check. The archive contains the rendered site and speech data plus a complete review workspace under artifacts/review/: model and theorem sources, finite corpora, scenarios, simulator and replay sources, checkers, and a minimal flake with its exact lock. The inner artifacts/SHA256SUMS authenticates every review input plus the separately served corpus and identity files; the outer SHA256SUMS authenticates the archive.
Packaging checks alone do not establish reproduction. Extract the built singular-docs-<version>.tar.gz into a fresh directory, change to that directory, run sha256sum --check artifacts/SHA256SUMS, then run nix run --no-write-lock-file ./artifacts/review#check. Nix may acquire the exact locked toolchain when it is not cached; the model, scenario, simulator, replay, checker, and configuration inputs all come from the archive. Provenance comes from the validated tag and build binding. Published releases, each produced by this workflow from a tag on main, are listed at https://github.com/lambdasistemi/singular/releases.
The initial empty-manifest behavior and version override follow the release-please manifest documentation. Publishing remains a separate action from reviewing this documentation.