Use issues for bugs and design discussion, and pull requests for changes. Report a suspected vulnerability privately, as SECURITY.md describes.
Find your way around§
blueprint-platform/ the roc-blueprint platform that Blueprint.roc apps use
*.roc setting types, checked values, lowering to the Spec
host/, build.zig Zig host, built into targets/<target>/libhost.a
targets/ linker inputs, none committed: libhost.a is built, the rest fetched (see its README)
blueprint-core/ roc-blueprint-core: Spec, Provider contract, validation, Steps, Value, codec
Project.roc pure normalization, references and provider capability checks
Realise.roc pure planning of a request: what the Core supplies, and what it asks of a provider
Sbom.roc pure SBOM inventory; writes SPDX 3.0.1, SPDX 2.3 and CycloneDX 1.7, reads SPDX 3.0.1, 2.3 and 2.2 and its own CycloneDX
Spdx3.roc, Spdx2.roc the SPDX 3.0.1 and 2.x rules, held to the schemas pinned in tests/sbom/schema/
CycloneDx.roc the CycloneDX 1.7 rules, held to the schema pinned in tests/sbom/schema/
fuzz/ roc-fuzz targets: spec-parse, spec-round-trip, project-validate,
lock-parse, lock-snapshot-parse, lock-snapshot-round-trip,
lock-snapshot-project, sbom-render, sbom-read, build-record-parse,
build-model-compare, nix-native-decode, nix-lock-decode
blueprint-nix/ importable pure Nix provider (depends only on blueprint-core)
NixProvider.roc shared pure request planning and flake rendering
Locks.roc Nix pins <-> the Lock: Sources plus a "nix" hint
tests/ Spec fixtures and golden flakes
blueprint-cli/ the blueprint CLI (basic-cli + weaver), which executes provider plans
Store.roc, Download.roc the per-user store of downloads and the HTTPS client that fills it
StoreRecord.roc the record the store keeps beside a tree, read and written without effects
Bootstrap.roc identity of the compiler archive embedded in each CLI
compilers.roc Bootstrap and StoreRecord as a package for build tooling and tests
Published.roc what a release publishes as the digest of an asset, asked by update
fixtures/consumer/ independent consumer of the Spec and Nix packages
fixtures/overlays/ overlay flakes for scripts/test_scenarios.roc
examples/all-settings/ environments, sources, scoped overlays, tasks and Raw
examples/composition/ imported pure modules: reusable task settings and a program's recipe
examples/artifacts/ runnable source/dependency/workflow example and its Roc scripts
examples/extensions/ Custom blocks; CI checks blueprint refuses them clearly
scripts/ Roc scripts, run from the repository root; their modules are in scripts/src/
test.roc local CI test groups; runs the test_*.roc suites
link_inputs.roc fetch or check the platform's linker inputs
bundle.roc bundle core, the platform and the reporting app into dist/ and smoke-test them
embed_compiler.roc stage and verify one target's compiler archive for embedding
test_bootstrap.roc test embedded compiler installation, repair and concurrency
build_release.roc cross-build the CLI, the SBOM tool and the smoke-test program for each released system
smoke_binary.roc run a built CLI with no Roc and no Python of its own; with --no-nix, with no Nix either
fuzz.roc replay or fuzz the roc-fuzz targets
release_notes.roc render a release's notes from docs/releases/
package_examples.roc archive the examples with a release's platform URL
package_sbom_fixtures.roc archive the SBOM fixture set with its case contract
test_release.roc test that archive against the bundles, served from localhost
docs_snippet.roc write the part of the manual that names the latest release
check_version.roc check a release's version before building and against the public API after
package_docs.roc archive the rendered site
release_assets.roc gather a release's assets and check the names other repositories depend on
test_lock_snapshot.roc check the official lock snapshot consumer against the vendored schema and profile
test_builds.roc, test_workflows.roc real-Nix build and workflow suites on scripts/src/BuildHarness.roc
reproducible_probe.roc compare a producer on independent CI hosts and synthetic CLI patch builds
docs/ the manual: index.adoc includes one chapter per top-level .adoc file
releases/ each release's notes, included by releases.adoc
link-inputs.lock.json the linker-input release the platform links, pinned by content
flake.nix builds blueprint with the pinned Roc; the development shell; the release cross-compiler
The local platform and CLI share blueprint-core, including
Project.validate. The Spec wire format is major 3; older platform bundles are not compatible. The architecture, terminology (Spec,
Provider, Lock, stages) and invariants are defined in
the architecture chapter; keep it in sync with changes.
Keep capabilities provider neutral§
Provider neutrality is a lasting design constraint, including for a future executor developed within Blueprint. Review shared-contract changes against it:
Describe the capability and guarantee independently of the current Nix implementation. Keep native syntax, command lines and metadata in the provider; keep identity semantics explicit and provider-qualified.
Show how another deterministic system could fulfil the contract. A provider that cannot fulfil it must fail before effects, rather than silently weaken isolation or reproducibility.
Preserve the boundary between committed intent and pins, pure inspection, captured execution evidence, disposable materialization and the host’s constrained effect execution.
Exercise changed provider boundaries with an independent contract fixture using its own arguments, identities and metadata. Renaming a Nix fixture is not evidence of another implementation.
Document unsupported capabilities and remaining routing or registration constraints. Distinguish a reusable contract from an implemented backend.
Protocol changes need a shared semantic reason and the existing versioning rules. They must not make every provider adopt one ecosystem’s native format. Production support for a second provider and general resolver composition remain follow-up work tracked in the additional-provider follow-up.
Review changes against the ownership table in the architecture chapter. Scripts own build algorithms; the pure core owns validation and plans; the host owns effects; providers own native execution and caches. Identity fields must say whether they identify input content, a prepared build or measured output. Do not reconstruct a build graph from independent JSON reports when a shared plan/result type can serve the interface.
Set up§
Full development and CI require x86_64 Linux with Nix and flakes enabled.
nix develop
gives two Roc compilers from
roc-overlay, Zig 0.16 and the
programs the scripts start: coreutils, git, curl, tar, gzip, zip, unzip and gh.
On Linux it also supplies Bubblewrap, util-linux and Python for isolated
runner tests and a restrictive-umask launcher. Nothing else uses Python: the
product, the reporting app and every conformance check run in Roc.
roc is the product compiler, the nightly in
blueprint-cli/compiler-version: it builds and tests the CLI, the packages and
the platform, and a released blueprint embeds it. roc-stable is the
nightly rocStableTag names in flake.nix: the scripts in scripts/ start
with #!/usr/bin/env roc-stable and run on it, so they keep working while the
product moves to a newer nightly. The scripts reach the product compiler
through ROC, or roc on PATH. This shell is the one definition of the toolchain:
every CI job runs its scripts through nix develop -c, so what passes in the
shell is what CI runs. blueprint is not in the shell, so entering it never
compiles the CLI; build it as below, or use nix run ..
The shell stages this host’s pinned compiler archive as the ignored file
blueprint-cli/compiler-archive. Roc imports its bytes into the CLI at build
time. Outside the shell, stage an archive first with
scripts/embed_compiler.roc <system>; the script checks its size and digest
against Bootstrap.roc. Keep the complete archive: macOS Roc needs the
darwin/ sysroot beside its executable.
blueprint-cli/main.roc uses the released basic-cli platform by URL. Roc
downloads it on first use outside Nix; the Nix blueprint package fetches the same
archive by hash, so its sandboxed build needs no network.
Build and test§
scripts/link_inputs.roc fetch # the musl runtime files the platform links
(cd blueprint-platform && zig build) # libhost.a or host.lib for all five hosts
roc test blueprint-core/main.roc # Spec round trips and format tests
roc test --opt=interpreter blueprint-cli/main.roc # CLI, provider and core expects
roc build blueprint-cli/main.roc --output=./blueprint
(cd examples/all-settings && ../../blueprint run --help) # try the CLI
scripts/test.roc # all local CI test groups
scripts/test.roc unit cli # or only some groups: static, unit,
# cli, scenarios, builds, package, glue, fuzz
scripts/fuzz.roc 300 # fuzz each target for 5 minutes
The CLI unit tests use interpreter mode because the pinned compiler’s native test backend crashes on the large embedded byte constant. Integration tests run the compiled executable.
The .roc files in scripts/ are Roc programs; they start other programs by
argument list, not through a shell. Run one from the repository root with the
pinned roc on PATH, or as "$ROC" scripts/test.roc.
The platform’s linker inputs are not committed. scripts/link_inputs.roc fetch
downloads the release pinned in link-inputs.lock.json into .cache/link-inputs/,
verifies it and installs the files beside libhost.a; nothing that evaluates a
Blueprint.roc against the local platform links without them.
scripts/link_inputs.roc check verifies what is installed without the network.
See
blueprint-platform/targets/README.md.
scripts/test.roc runs the repository’s CI test groups. Its groups are static
(formatting; no tracked object file, archive or import library; the CLI’s use of
the Provider contract; the compiler downloads Bootstrap.roc records, one
for each system the CLI runs on; each workflow’s
permissions and checkouts; the release notes chapter against the notes files;
the flake’s list of core modules and its version), unit (the platform’s, the scripts', the CLI’s
and the consumer’s expects, the configuration fixtures and the Roc the
manual shows), cli,
scenarios (test_scenarios.roc, test_roc_packages.roc and the golden
flakes parsed by Nix), builds (test_builds.roc and test_workflows.roc),
package (the flake’s package and development shell, the packaged CLI building
an artifact with only Nix on PATH, and a dry run of what a release ships:
core and the platform bundled and smoke-tested, the examples archived and the
archive tested; see The two bundles),
glue (scripts/check_glue.roc: the host’s committed
roc_platform_abi.zig is what roc glue writes for the platform with the
product compiler; scripts/check_glue.roc --update rewrites it)
and fuzz (each target’s committed corpus, replayed, which
takes seconds; .github/workflows/fuzz.yml fuzzes each target for five minutes
every week, starting from the inputs earlier runs found, which it keeps in the
Actions cache through FUZZ_CORPUS, and uploads any crashing input). scenarios and builds are the
real-Nix suites, split into two groups of about the same length so that CI runs
them as two jobs at the same time.
Two additional CI jobs run scripts/reproducible_probe.roc on independent
Ubuntu 24.04 and 22.04 hosts. One uses the release binary; the other compiles
a synthetic next-patch CLI with the same runner pins. Each builds and
rebuilds the same selected-input producer. A dependent job compares their
actual derivation, outputs and locked-source provenance.
scripts/test_config.roc runs the compiler on generated Blueprint.roc
fixtures: the platform’s own rules, which only a compiler run can show (a
setting given twice or not at all, in Lower.roc, and each checked name’s
from_quote), a few core rules and bounds as witnesses that shared validation
surfaces at compile time, and that settings composed from an imported module
emit the same Spec as inline ones. Every other rule of Project.validate has an
expect in blueprint-core/Project.roc. The bundle gate repeats a few of the
fixtures against the served platform, plus the all-settings example.
It then compiles the manual’s Roc (scripts/src/DocSnippets.roc): every
[source,roc] block of docs/ and docs/releases/, and the first program of
the Get started chapter, against the platform in the working tree; see
Write the manual.
scripts/test_cli.roc exercises the built CLI with and without a
configuration, checks validation and help/version handling, and records Nix
argv to verify shell selection and task arguments without entering a shell.
It holds what only a run can show: the order of checks before any effect, how
often the configuration is evaluated, exact provider argv and error text. Rules
that a pure expect in blueprint-core or blueprint-nix already holds are
not repeated there.
The all-settings integration tests separately run tasks through real Nix.
scripts/test_scenarios.roc writes projects and runs them through the real
CLI and real Nix: the all-settings and extensions examples, noncommutative
overlays in both orders with inheritance, an unselected overlay’s native
missing-package failure and a declared overlay that throws if evaluated,
system-scoped tools on Linux and macOS with the package-target rejection of an
unscoped one, renamed commands, and a task that names a script by path, as
scripts/check_all.roc, which starts by its roc-stable shebang on a platform
RocPackages supplies and imports a module beside it, and zig-overlay and
rust-overlay as overlays, each supplying an exact release named in a quoted
attribute, and configured tools: a fixture overlay’s package says which
options it was built with, and rust-overlay evaluates a toolchain with two
targets and a component. It also runs nix develop --debug on a
staged flake and requires Nix’s own report that its bash comes from the locked
package source, with no registry lookup,. A project it writes pins its inputs to the
revisions in fixtures/consumer/inputs.lock and flake.lock, and its
Blueprint.lock is read-only and compared after every step.
fixtures/consumer/main.roc holds the staged bytes and supplied-lock
preservation as expects. Parse fuzzing also checks successful semantic
normalization for idempotence.
SBOMs are SPDX 3.0.1 JSON-LD, with SPDX 2.3 and CycloneDX 1.7 renderings of
the same inventory for consumers that read only those, and conformance is
checked in Roc, with no other validator. blueprint-core/Spdx3.roc,
Spdx2.roc and CycloneDx.roc hold the classes or objects, members,
enumerations and prose rules of each format, and scripts/src/SbomTest.roc
holds them to the digest-pinned schemas in tests/sbom/schema/ (the published
SPDX 3.0.1 JSON schema, the SPDX 2.3 schema and the CycloneDX 1.7 schema):
every class, member and enumeration the validators accept is the schema’s own.
The same expects require that each committed fixture in tests/sbom/fixtures/
is what the renderers write now (regenerate them with
scripts/test_sbom.roc fixtures tests/sbom/fixtures), that all three
renderings of a fixture read back to one package graph, and that the
standalone tool’s committed inputs (input/, source-import/), the case
contract (CONTRACT.md) and the file list of the release asset
(scripts/src/SbomFixtures.roc) are what the suite states (regenerate the
first three with scripts/test_sbom.roc inputs, source-import and
contract). scripts/test_sbom.roc binding
then runs the built scripts/sbom.roc against read-only inputs: the native
case, and the source supplier import’s accepted targets, bound renderings
and refusals as source-import/expected.json states them. The
sbom-render fuzz target generates inventories with arbitrary text and
graphs: each must render to valid, canonical SPDX 3.0.1, SPDX 2.3 and
CycloneDX 1.7, read back from each to exactly its own packages and
relationships, with CycloneDX’s standard fields (dependencies, scopes, hashes,
purls, licenses, suppliers) derived from the inventory, and the validators
must reject a rendering once one of a list of SPDX 3.0.1 or CycloneDX rule
violations is injected into it. It also strips Blueprint’s properties from
the CycloneDX rendering, which must then import as plain CycloneDX to a
projection of the inventory, and adds files to the SPDX renderings, which
must read to the same package graph with the files counted as gaps.
sbom-read mutates the fixtures of every format, including what Syft and
Trivy write (tests/sbom/fixtures/upstream/): reading must never crash, what
it accepts must be a sound package graph, and it must import as an upstream
SBOM unless the import is refused for a developer path, a credential, its
size or a subject digest that is not a SHA-256.
scripts/test_builds.roc runs real sandboxed builds of artifacts,
dependencies and sources, including host-file and TCP isolation with positive
host controls, fail-closed runner checks, exact argv, immutable locks,
freshness and relocation. It also checks what Nix copies from the project
(bytes, the owner’s execute bit, raw names, exclusions, refused symlinks and
special files) and that an undeclared tool is not found. The exact standalone
runner’s argv, input and output checks run in an isolated Linux test harness;
an unisolated invocation must refuse execution before user code runs. Runtime
namespace witnesses are private to each invocation and do not enter the
derivation. Stubbed CLI tests verify their contents, cleanup and separation
from stable recipe bytes.
scripts/test_workflows.roc runs real ordered task/build workflows, nested
repetitions, failure stops, snapshot/dependency freshness, repeated
locked-source verification, whole-closure preflight and immutable authority,
including out-of-tree layouts, and then a temporary copy of the complete
artifacts example. Both are Roc scripts on the harness in
scripts/src/BuildHarness.roc, and the tasks and builds of their fixtures are
Roc scripts run as roc-stable through RocPackages. Their inputs are pinned
in fixtures/consumer/inputs.lock and fixtures/roc-inputs.lock.json; one
first step fetches them and every later Nix call is recorded and run with
--offline. See fixtures/builds/README.md.
scripts/test_update.roc checks local-source preflight, authority observation,
concurrent publication, named updates and what an unsupported lock is told. scripts/test_isolation.roc checks what a build stages
about its caller: the namespace identities, the CLI’s own path as the runner,
and the refusals when either cannot be used. Both put a failing
python3 on PATH: the CLI itself must not use a host Python.
scripts/test_programs.roc runs projects whose environments fetch programs
and expect tools of the machine. Nothing selects how they run but their own
Blueprint.roc: one with no Nix tool must never start the nix stub. The
downloads come from a server the script runs on the loopback address, the
store is a directory of the test’s own, and the lock entries are written by
the test. It checks what is kept in the store and under which names, that a
second run makes no request, that a stored artifact with other bytes is
removed with its tree and fetched again, two runs at once from an empty store,
every refusal (a missing expected program, a program the lock does not hold or
holds under other URLs, another length or digest, each URL’s failure, an
archive with a link out of its tree), the caller’s own shell, and parity: the
same task with one Nix tool added prints the same from the same store file,
and only then starts Nix. Its last cases let blueprint update write the
lock: with no nix on PATH for a project that only fetches, with nothing
asked when nothing changed, for a named program alone, after a URL list
changed, and for release assets. A download URL must be https://, with one
exception made for these tests: http://127.0.0.1:<port>/, which no other
machine can serve. A loopback URL of the shape
/<owner>/<repo>/releases/download/<tag>/<asset> is a release asset, and the
same server describes its release at /repos/<owner>/<repo>/releases/tags/<tag>,
as GitHub’s API does for a GitHub URL. That is the whole seam: no variable
or flag redirects the question. Through it a digest computed here is compared
with a published one, another system’s is recorded unfetched, a published
digest that differs stops the update, a question that is refused or answered
with something else leaves every digest computed and says so once, and a
download missing for one system says how to declare that.
It ends with blueprint requirements, asked with no nix, no lock, an empty
store and no server: the report is the same, a declared and unused Nix source
requires no provider, and nothing is written.
These are Roc scripts, run from the repository root with ./blueprint
built; ROC names the compiler. They replace every tool the CLI runs with one
program, scripts/stubs/tool.roc, which each test builds once and installs
under several names (roc-record, roc-wire, roc-probe, nix, guix,
python3, readlink). A copy decides what to be from its own file name and
how to behave from STUB_* variables, and records each invocation as a file
holding one line of JSON in <name>-calls/ under its working directory. Its
header lists what each name does. scripts/src/CliHarness.roc holds what the
three tests share.
scripts/test_roc_packages.roc is a Roc script, like link_inputs.roc. It
runs the real CLI and Nix with a Roc package cache of its own in a temporary
directory, and refuses to run if that directory lies inside ~/.cache. A
task’s bundles are downloaded by blueprint itself from the releases their
URLs name. It checks publication into an empty cache, that a second run
writes nothing, that an existing package is left alone and an incomplete one
replaced, that failures leave nothing behind, that a project with no build
locks no bundle and its task still downloads them, and that a sandboxed build
resolves its bundles from the lock with no network while the same build
without one of them does not.
Normal execution tests explicitly initialize authority with update first.
The complete artifacts example is executed in a temporary copy by
the workflow suite (scripts/test_workflows.roc).
Keep fuzz coverage with the code§
Every reader of bytes that Blueprint did not write in the same process has a
fuzz target in blueprint-core/fuzz/: committed authority (the Spec:
spec-parse, spec-round-trip and project-validate; the Lock:
lock-parse), reports it reads back (the lock snapshot: lock-snapshot-parse,
lock-snapshot-round-trip and lock-snapshot-project; SBOMs: sbom-read and
sbom-render), native tool output a provider decodes (Nix derivation, build
and dev-shell JSON: nix-native-decode; the native lock graph and the Nix
pins in the Lock: nix-lock-decode) and local records (build records:
build-record-parse and build-model-compare).
A reader has a mutation target with a seeded corpus and a dictionary. A value with a generator also has a generation target for its round-trip and idempotence properties.
A pull request that adds or changes such a reader adds or extends its target in the same change. The
staticgroup keeps the target matrix of.github/workflows/fuzz.ymlequal to the directories ofblueprint-core/fuzz/, so CI cannot forget a new target. Reviewers ask of every new parser what reads it, and which target covers it.A corpus holds one valid document per schema version, one seed per refusal the parser names, and every crashing input ever found, committed under
corpus/with a name that says what it found. The replay on every commit then guards it for good.CI replays the corpora on every commit, which takes seconds. The weekly workflow fuzzes each target for 300 seconds in parallel jobs and keeps what it finds in the Actions cache, so each week starts where the last ended.
scripts/fuzz.roc SECONDS [TARGET...]runs a longer campaign on request. The weekly budget is set infuzz.yml, and changing it is a deliberate decision recorded there.Only pure code is fuzzable: a reader that lives in the CLI app moves to a pure module in
blueprint-coreor an adapter before it can have a target.
The job with no Nix§
Every suite above runs where Nix is installed, and replaces it with a stub
where it must not be started. One job of .github/workflows/ci.yml runs on
machines that have no Nix at all: no-nix, on ubuntu-latest,
ubuntu-24.04-arm, macos-latest, macos-15-intel and windows-latest, with the binaries and platform hosts
the build job produced and no step that installs anything. It runs
dist/smoke-<system> --no-nix dist/blueprint-<system>, the NoNix mode of
scripts/src/SmokeBinary.roc, which:
refuses to start when
nixis onPATH, and leaves norocthere;writes a project whose compiler is
Programs([Fetch("roc-stable", Recipes.roc_nightly(...))])of the nightly inblueprint-cli/compiler-version, withExpect(["git"])and the Roc packages of a basic-cli script, against this checkout’s platform;gives the binary a store and a Roc package cache of its own, empty;
runs
blueprint updateand requires that the embedded compiler was installed exactly once, for readingBlueprint.rocand asroc-stabletogether;runs the shared compiler as
roc-stable versionand requires its actual response to matchblueprint compiler-version;requires that
blueprint requirements --jsonnames no provider;runs the script as a task, which the embedded compiler builds, links and runs. On macOS that is the test that
rocfinds thedarwindirectory of its archive through the link it is started by;runs the task again and requires that nothing is downloaded, and that no flake was generated.
The same job then runs the cross-built SBOM tool of its system,
dist/blueprint-sbom-<system>, over tests/sbom/fixtures/input
(.github/actions/sbom-tool): inspect must reproduce the committed
inventory, bind writes the sample artifact’s sidecars and check holds
them, all with no Roc on the machine.
It reaches GitHub for the nightly and the Roc packages, and asks GitHub’s API
once for the nightly’s published digests; a refusal there is a notice, not a
failure. To run it on a machine that has Nix, hide Nix from PATH:
roc build scripts/smoke_binary.roc --output=/tmp/smoke
env PATH=/usr/bin:/bin /tmp/smoke --no-nix ./blueprint
Write the manual§
The manual is the AsciiDoc in docs/. index.adoc is a book that includes
one chapter per top-level .adoc file; .github/workflows/docs.yml renders
it as a site and a PDF with the shared
build-docs
action, on every pull request, and deploys the site from main. The action is pinned to a reviewed roc-automation main revision; the renderer
is released as tools-0.6.0. flake.nix checks the released
renderer binary’s SHA-256 for each supported host. Rendering uses the native
Roc utility. API generation uses this repository’s pinned Roc compiler.
To render the same outputs locally, inside nix develop:
scripts/docs_snippet.roc # docs/generated/, from the latest release; needs a logged-in gh
renderer=$(nix build .#roc-docs --no-link --print-out-paths)/bin/roc-docs
"$renderer" build --workspace "$PWD" --pdf-filename roc-blueprint.pdf \
--pdf-image-dpi 240 --site-url https://lukewilliamboswell.github.io/roc-blueprint/ \
--docs-version main --api-entrypoint blueprint-platform/main.roc \
--roc-command "$(command -v roc)"
The site is .docs-out/site/ and the PDF .docs-out/roc-blueprint.pdf;
neither is committed. Both workflows use 240 DPI for PDF image preparation
so the architecture diagram retains detail when zoomed. Publication links use explicit URLs; this action and
renderer pair uses site-url for publication links and Markdown discovery.
Keep PSV table cells on complete physical lines. HTML honours the declared column proportions; this renderer release’s PDF sizes earlier columns to their content and gives the last column the remaining space. Keep long explanations together: the overview combines each source and its version pin, and the configuration checks use labelled paragraphs. Stage responsibilities use separate input/output, execution and invariant tables. Review both outputs after changing a table; column proportions alone do not establish that its PDF is readable.
Stable explicit anchors preserve links when a heading’s wording changes.
Invariant headings use words before their numbers because numeric-leading
headings exceed this release’s native inline profile. The historical
use-this-release-2 release anchor remains explicit. Prepared figures replace
render-time Mermaid and JPEG conversion, with their source and asset digests
recorded below. These are authoring changes, rather than renderer patches.
Native images are non-interlaced RGB/RGBA PNGs. The manual’s illustration is
converted from the original JPEG; its source hash and conversion command are
in docs/images/blueprint-illustration.provenance.json. The architecture flow
uses the checked-in published SVG and its prepared PNG, with provenance in
docs/images/blueprint-flow.provenance.json. Its Mermaid source stays in
architecture.adoc; docs/prepared-diagrams.json binds its exact source and
PNG hashes. Update the figure and registration together when editing the
Mermaid block. These prepared assets let ordinary documentation builds run
without a browser, image converter or diagram tool.
The manual describes the tool as it is: one recommended way to do each thing, in the present tense. What changed between versions belongs in the release notes, and what is unfinished in an issue.
The Roc in the manual is compiled by scripts/test.roc unit, so a block must
be one of five things:
a whole
Blueprint.roc, startingapp [, whose platform is written as a placeholder;settings as they stand inside
config, complete enough to check by themselves: an environment a task uses is in the block. A tool of the sourcenixmay be written without thePackages("nix", ...)line;[source,roc,snippet=core-module], pure imports and declarations checked inside a package against the repository’s core modules;[source,roc,snippet=refused], settings the compiler must refuse, for a mistake the text explains;[source,roc,snippet=unchecked], which is not compiled: use it only for a line of some other program or for settings that cannot stand alone.Each chapter opens with one
= Titleand is also rendered as a page of its own, so it cannot rely on attributes set inindex.adoc.Link to another chapter as
xref:chapter.adoc#section-id[text], always with a section’s ID and text. Section IDs come from headings, lowercase with hyphens.Give tables explicit column widths.
A release’s notes are
docs/releases/<version>.adoc, included fromreleases.adoc, newest first; see Release.