# Pinning source text Pinning binds the article-level text stored in a package to the exact bytes of the publication it was taken from. The binding is mechanical: a small declaration file lists which publication bytes become which fragments, and a command regenerates the fragment block from those bytes. When the bytes and the fragments disagree, the check fails instead of guessing. Environment: the pinning loop **requires a source checkout** (**I**) — `./law gen` exists only in the checkout composition; run it from the repository root. The compiler checks of pinned text are **available in the public release** (**S**). Marks follow the [topic legend](/corpus/#how-this-topic-marks-confidence). ## What pinning guarantees, exactly Each row below is one mechanical promise plus the line it stops at. Nothing here judges whether the pinned text is the right law — that judgment stays human. | Guarantee | What holds | Exact limit | |---|---|---| | Document hash | the bytes on disk must match the declared document hash when read back; anything else fails with `LDC-E5204` | covers only bytes pinned locally; a matching hash says nothing about whether the file is the right, current, or authentic document | | Fragment hash | a fragment's text must match its own declared digest (`LDC-E5201`), and every official text on a pinned edition must carry one (`LDC-E5205`) | binds text to digest only — the right digest on the wrong article still passes | | Containment | every official fragment text must occur byte-for-byte inside the pinned publication bytes (`LDC-E5202`) | substring only: stale surroundings, missing articles, and swallowed headings around the quote are invisible to this check | | Retrieve-freshness | per-act retrieve scripts fetch the portal bytes again, and their read-only check mode reports local bytes that no longer match | compares local bytes with whatever the portal serves today; says nothing about legal validity, and the scripts are internal tooling, not a user command | | Content review | cut entries record in plain words why document furniture was excluded, and the bytes stay a substring either way | no automated check: a wrong article, a wrong edition, or a cut that drops normative words all pass — only human review catches them | When the bytes change, these checks fail loudly instead of guessing; the limits in the table are the places a loud failure cannot reach. ## The pinning loop One command owns the loop. It **requires a source checkout** (**I**): `./law gen` is absent from the public composition, so it needs the checkout launcher (see [Before you start](/corpus/lab/#before-you-start)): ```console $ ./law --help ... law gen pinning ( | --all ) [--write | --check | --diff | --json | --emit] pinning.toml → §194 fragment block between markers in the package .law; without flags — check, writing — only --write ``` The declaration file is `pinning.toml`. It names a source text, a target source file, an edition, an article prefix, and the marker comments between which the generated fragment block lives. Nothing is written unless `--write` is passed: running the command with no flags, or with `--check`, only verifies that the generated block matches what the bytes would produce. `--diff` previews the sync result for a human, `--json` and `--emit` serve automation. A real check against package `id.halal` passes silently with exit status 0, while `--diff` confirms what was compared: ```console $ ./law gen pinning corpus/laws/id/halal --check $ echo $? 0 $ ./law gen pinning corpus/laws/id/halal --diff ✓ pinning.toml → 01-sources.law [id] (68 articles) ``` A package without a `pinning.toml` also exits 0: there is nothing to compare, so the check is vacuous rather than failed. ## What the declaration file says The shape is data, not code: source bytes in, fragment block out. This is the head of the real declaration used by `id.halal`, with its author comments trimmed: ```toml source = "sources/uu-33-2014/id.txt" target = "01-sources.law" edition = "UUJPH_ID" prefix = "UUJPH_ART" profile = "id-uu-pasal" kind = "article" stops = ["bab", "bagian"] lang = "id" status = "official" begin = "// BEGIN GENERATED UU JPH FRAGMENTS" end = "// END GENERATED UU JPH FRAGMENTS" ``` The `cut` entries below the head (one per article that needs it) record where non-normative document furniture — promulgation formulas, dates, signatures, gazette lines — is cut away so that a locator keeps addressing the norm and not the colophon. Each cut carries a reason in plain words; the bytes stay a substring of the publication either way. ## The fragment text rule The compiler enforces one plain rule: every fragment's text must appear verbatim inside its publication's bytes, and the publication bytes must match their declared document hash. Four diagnostics enforce the rule. They are **available in the public release** (**S**) and are all raised when a package is checked: - fragment text whose digest differs from its declared content hash (`LDC-E5201`); - a fragment with official text but no content hash on an edition that declares pinned bytes (`LDC-E5205`); - fragment text that is not a substring of the publication bytes (`LDC-E5202`); - publication bytes that cannot be read back — the file is missing, the path escapes the package, or the bytes differ from the declared document hash (`LDC-E5204`). Note the split the numbers hide: `LDC-E5201` fires on a wrong hash, while `LDC-E5205` fires on a missing hash where a pinned edition requires one. ## Retrieving the bytes Fetching a publication from an official portal and syncing fragments into packages is handled by per-act retrieve and sync scripts (**I**). Each script supports a read-only `--check` mode, and the full profile wires several hundred of those `--check` invocations, one per act, so that stale bytes surface without any script ever writing during a check run. One retrieve path deserves a callout (**T**): the Kazakh official legal portal now renders act pages as a single-page application, so fetching the page address returns only the application shell. The sanctioned path pins Kazakh acts through the portal's JSON data API instead of the page markup. ## The full-profile pinning check Beyond the per-package command, the full continuous-integration profile runs a pinning check per act (**G**) that promises four things: 1. every fragment block has its declaration counterpart and back — no orphan blocks, no orphan declarations; 2. the materialization record of a pinned act points at a publication node with bytes present locally; 3. text coverage is measured only against pinned bytes, never against prose claims about the source; 4. a watch for glued headings catches fragments whose boundaries swallowed neighboring titles. A companion report mode prints the same findings with an always-zero exit status for dashboards; the enforcing mode fails the run. ## Reproduction details
Toolchain, hashes, and full transcripts This page uses two executors. The public block below is the lab's pinned tool, shown for reference; every fenced command on this page ran on the checkout block, because `gen` exists only in the checkout composition: ```console $ law version # public route: law-v0.1.1, macOS arm64 law 0.1.1 semantics: law.core/0.2 std for language 0.2: 0.2.0 lawql: lawql/1 (queryResult 0.1) binary hash: sha256:cfa7c17232f2dc594e665dbbf2ad3c154675797ef4be07ef7d758c9abfb80a28 ``` ```console $ ./law version # checkout route: commit b2e9e746b9, debug build via cargo run law 0.1.1 semantics: law.core/0.2 std for language 0.2: 0.2.0 lawql: lawql/1 (queryResult 0.1) binary hash: sha256:7fddf8d081e7fd527c36cc0393aea6cbf9e00ea814e960a76ac701f74794c01f ``` All `./law` fences below were recorded 2026-10-04 against that checkout block from the repository root. (Compiler warnings on stderr are omitted from every transcript on this page; only stdout is shown unless a diagnostic is the point.)
## Further reading - [A real article: source and anchor](/tutorials/real-article-source/) - [Reconciliation with the pinned edition](/tutorials/pinned-edition/) - [Pin the act, edition, and publication](/recipes/i-sources/pin-publication/) - [Sources and text](/recipes/i-sources/) - [Sources: source, edition, publication, fragment](/constructs/sources/)