Parity governance¶
Folia was written to replace one Jekyll site, and its repository holds tools that check Folia's output against that site's Jekyll build. This page is for maintainers: where the tools live, where the site's private data lives, and how approvals work. The tools' commands are in the parity tools reference, and what differs from Jekyll is in Differences from Jekyll and known limitations. None of this is needed to use Folia.
The parity tooling¶
crates/parity: the dev-only parity tool (just routes, just markdown, just pages, just
determinism), and the content lint (just liquid). It reads baselines as black boxes and calls
only Folia's public API. markdown holds the Markdown check: dom (html5ever parsing and
normalization), liquid (the template filters excerpts go through), ledger and report.
builds builds the site at an explicit time into a new directory it keeps, with the output's
manifest and a record of the inputs. It always builds without the preview flags, and its
build.json records them as preview (future and unpublished, both false), so that
a preview build is never taken for a baseline's equivalent. document compares whole HTML
documents, and trees of them, by their own rules. pages builds the site through it and compares what
Folia writes with Jekyll's files, pages::entries the documents' dates, tables of contents and code
blocks, and pages::garden the note graph and the broken-link warnings. coverage classifies
every row of the baseline's manifest from what Folia plans and writes, the checks' verdict on each
output, and the ledger's coverage entries. links checks that every internal href and src of
the HTML Folia writes resolves to a planned output, and warnings that every warning Folia gives
is one the site's expected-warnings file expects. determinism builds the site twice through
builds, as of one time, and compares the two builds byte for byte, and each with a recorded
reference.
parity/: tools that check Folia's output against the Jekyll site.
jekyll/folia_dump.rb: the passive Jekyll plugin that dumps Jekyll's intermediate results (see the parity tools reference). Nothing else may live injekyll/, because Jekyll loads every Ruby file there.codemods/check_d001.py: checks that the only output change of the Liquid rewrite of the Liquid in the site's Markdown is the one expected change.check_dump.rb: checks that a dump accounts for every built file and that no date fell back to a file mtime.same_entries.py: compares the sitemap or the Lunr index entry by entry.diff_regions.py: lists where two versions of a text file differ, token by token, for the toolchain comparison's report.site-exclusions.txt: the paths in the site that are Folia's (_folia/, the content lint's workflow), whichscripts/baseline.shand the parity tools' checkout check both leave out, from this one list.accept.py: the acceptance runner's verdict: it finds and verifies the baseline's lockfile, and judges each acceptance criterion from what the checks left, intoaccept.jsonandaccept.md.check_date_shift.py: checks that two baselines differ only by changed last-modified dates.
Scripts behind the parity recipes (the rest of scripts/ is in
development setup):
baseline.sh: builds a baseline, recording its identities, tools and lockfile; with--lockfileand--at, from exactly that lockfile and as of a pinned time.baseline-container.sh: builds one withbaseline.shin a Docker image pinned by digest.toolchain-compare.sh: builds one commit with each toolchain and compares them;toolchain-negative.shshows the container baselines refuse what they must, in the images.compare-builds.sh: compares two baselines.verify-baseline.sh: runs the baseline's acceptance checks on fresh builds, which it keeps; an existing baseline is left as it is.baseline-negative.shshows both scripts refuse changes outside the approved paths, on throwaway repositories with a stand-in for Jekyll (part ofjust check-parity).determinism-negative.sh: shows the determinism check fails on a changed reference and refuses an existing or misplaced directory, a malformed reference and other templates.accept.sh: the acceptance runner behindjust accept: runs every check built so far against a baseline, in order, into one evidence folder.accept-negative.shshows it fails, refuses and records a tree with changes as it should, from a throwaway copy with a stand-in for the checks (part ofjust check-parity).determinism-reference.sh: chooses the determinism reference, fail-closed: A0's forFOLIA_A0_LABEL, another baseline's only when configured and declared its own (just determinism,just determinism-negative,just accept).evidence-provenance.sh: checks an evidence folder keeps its builds' and baselines' records, made from one clean Folia commit, and is (or can be) committed on its own, on that commit; it only reads.evidence-provenance-negative.shruns it on throwaway repositories, one per way of breaking the convention.slice-boundary.sh: checks a step of the vertical slice keeps out of the other step's mode.
A site's private inputs¶
Folia's repository holds the generator and the generic parity tools, never a site's data. What
the parity recipes need of a site is kept by its owner, outside this repository, and named by the
environment; a recipe that needs one refuses to run without it, and just check and CI need
none:
| Variable | What it names |
|---|---|
FOLIA_SITE, FOLIA_BASELINES |
the site's checkout, and where its baselines go |
FOLIA_CONFIG |
Folia's configuration for the site (folia.toml), with the slice's documents |
FOLIA_LEDGER |
the deviation ledger (deviations.toml) |
FOLIA_EXPECTED_WARNINGS |
the warnings Folia is expected to give, each with its decision |
FOLIA_TEMPLATES_SHA256 |
the tree hash of the templates the pages check expects |
FOLIA_A0_LABEL, FOLIA_A0_REFERENCE |
the reference baseline, and its recorded manifest |
FOLIA_TOOLCHAINS, FOLIA_TOOLCHAIN_AT, FOLIA_TOOLCHAIN_DIR |
the toolchain comparison's environments, pinned time and lockfiles |
The site's evidence (its reviewed outputs, the builds' and baselines'
records), its own negative checks (which change copies of the real site and
baseline) and the tests of its ledgers' contents are kept with them. The .gitignore keeps the
reference site's from being added here again.
Owner approvals in the ledger¶
The deviation ledger declares, at its top, the owners: the only names an approval may give.
A waiver or removal (check = "coverage") classifies an output only when it has
status = "accepted", a valid accepted date, a decision and a rationale, and an
accepted_by that is exactly one of owners; a missing or empty owners approves nothing, and
proposed and withdrawn entries classify nothing.
Three more classes are decided the same way, none of them a waiver
(see the coverage classes): intentional (a difference
Folia has on purpose; approved, and stale once the output matches Jekyll's), folia-only (an
output with no Jekyll baseline, so no manifest row; approved) and known (a difference recorded
with status = "recorded" and a rationale, never accepted: it has no approval, and the
run still fails while it stands). An entry of the first two with an unapproved status, or a
known one that claims acceptance, is invalid and classifies nothing.
What the tool checks is the recorded approval, not who wrote it. It cannot tell whether the
owner made or approved a ledger change: anyone who can edit the file can type an owner's name, or
add a name to owners. A change to owners is visible, not prevented: a test kept with the
site's ledger pins the list, and every coverage report records it. So review must confirm, for
each change to owners and
each accepted entry, that the owner actually approved it; an implementer never writes an owner's
name into accepted_by or owners on their own.