Skip to content

Evidence Bindings

The recurring objection to an architecture model is the fair one: how do I know it’s still true? A diagram drifts from the code silently, and by the time anyone notices, nobody trusts it enough to fix it.

ArchLang’s answer is not to generate the model from code — Chapter 30 explains why derived architecture is a picture of your imports, not of your design. The answer is a citation. A module names the file ranges that attest it, and a host that has the repository can check whether that citation still holds.

The model asserts. The repository attests. Nothing is derived from code.

module Checkout {
sources: "src/checkout/index.ts:12-88", "src/checkout/tax.ts:5-40"
}

There is no new construct here, and that is deliberate. sources is a well-known field (Chapter 9) like latency — ordinary structured data that a named tool interprets. So everything you already know about fields applies unchanged:

  • refinement and override at the type or instance level (Chapter 19),
  • cascade / append propagation (Chapter 18),
  • merging across in <Module> { … } extension blocks,
  • and it crosses the wire — JSON export, the viewer, the diff — with no serializer change.

An entry is one of three shapes, repo-root-relative:

FormMeans
"src/tax.ts"the whole file
"src/tax.ts:40"a single line
"src/tax.ts:12-88"a line range, 1-based and inclusive

A path on its own attests that a file existed once. To attest that the model is true of a revision, the repository and the commit are pinned in package.archspace — peers of version: and widgets: (Chapter 12):

package: acme.shop
version: "1.4.0"
repo: "https://github.com/acme/shop"
commit: "a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0"

commit: is required whenever repo: is set. Scoping the pin to the package — rather than writing a repository beside every binding — is what keeps a bare path unambiguous however many dependencies the package has.

A commit: naming a branch or tag is still checked, but warns: a branch moves, so verified would be a claim about whatever it points at today rather than about a fixed revision.

VerdictMeans
verifiedthe path is a blob at the pinned commit, and the range fits inside it
brokenthe citation no longer holds — the file is gone, the range runs past the end of it, or the entry doesn’t parse
unverifiednothing could check it — no repository, no pin, or the wrong repository

unverified is never a pass. An unchecked citation is never displayed as a checked one, and the board draws no badge at all for it — a node the viewer could not check must look exactly like one that cites nothing. The inspector still lists the citations either way, so a reader can tell “no evidence” from “evidence, unchecked”.

A citation that cannot parse is broken, not merely unchecked. An unreadable range would otherwise pass every bounds test vacuously, which is the one way a citation could claim more than it proves.

What a host checks is mechanical: the path is a blob at the pinned commit, the range fits inside that blob, and the checkout’s origin remote is the declared repository. Whether the code at those lines really is the module is a judgement for a reviewer — and the verdict is the input to that judgement, not a replacement for it.

Terminal window
archlang evidence ./architecture --repo=../shop
https://github.com/acme/shop @ a1b2c3d
✓ Checkout src/checkout/index.ts:12-88
✗ Checkout src/checkout/tax.ts:5-40
· Ledger src/ledger.ts
warning: 'Checkout' cites 'src/checkout/tax.ts' up to line 40, but that
file is 22 lines long at the pinned commit.
1 verified, 1 broken, 1 unverified

It exits non-zero only on broken. A binding nobody could check is reported and does not fail the build — the asymmetry is the point: a broken binding is a defect the author can fix, an unverifiable one usually means the machine running the check has no repository.

Evidence is also a phase of the combined gate, so CI needs no extra step:

Terminal window
archlang check ./architecture # validate + policy-check + format --check + evidence

The seven diagnostics are all warning severity, and the three repository-level ones suppress the per-entry noise they would otherwise cause:

CodeFires when
EVIDENCE_MALFORMEDan entry isn’t path / path:line / path:start-end
EVIDENCE_PIN_MISSINGbindings exist but repo: or commit: is absent
EVIDENCE_PIN_NOT_A_COMMITcommit: names a branch or tag
EVIDENCE_UNVERIFIEDthe pinned commit could not be read
EVIDENCE_ORIGIN_MISMATCHthe checkout’s origin isn’t the declared repository
EVIDENCE_PATH_MISSINGa cited path isn’t in the repository at that commit
EVIDENCE_RANGE_OUT_OF_FILEthe range runs past the end of the file

A verdict needs a host that can read the repository

Section titled “A verdict needs a host that can read the repository”

The engine is git-free. It verifies a snapshot the host hands it, and derives nothing from the contents of that snapshot:

  • archlang evidence and archlang check supply the snapshot for a local checkout (Chapter 21).
  • Studio supplies it for a repository the reader never cloned, because it already owns the repository.
  • The plain viewer supplies nothing, so nothing is verified — and, per the rule above, nothing is badged.

Where verdicts are supplied, a corner badge marks the module. It is drawn from the one composed scene tree, so the live board and an exported SVG or PNG carry it identically, and the inspector’s citations link to the pinned commit.

  • Cite the load-bearing range, not the directory. "src/checkout/" is not a claim anyone can check meaningfully; "src/checkout/index.ts:12-88" is.
  • Prefer a whole file to a stale range. A range that drifts by four lines is broken every week. If the module is the file, cite the file.
  • Bump the pin with the model. commit: is what makes the citation an assertion about a revision; a stale pin verifies against history and quietly stops telling you anything.
  • A citation is readable without any repository. One ⌘F row per citation, so “which module claims this file?” is answerable from the model alone — including by someone who has only the .arch files.