Appendix C - 6. Cross-source coherence lints
The Structure of Cross-source coherence lints — its shape at a glance:
The lint reads both sources and checks the declared relation between them. Agreement passes; any divergence fails the build.
flowchart LR
A[(Source A)] --> G{Coherence lint}
B[(Source B)] --> G
G -->|relation holds| Pass([build proceeds])
G -->|sources diverge| Fail([build blocked])
Accessible description: the coherence lint reads two independent sources and checks the declared relation between them — for instance, A's fields are a subset of B's. When the relation holds the build proceeds; when the sources diverge the build is blocked.
Projected from the catalogue entry validation-and-conformance / Cross-source coherence lints.
On this page: Intent · Motivation · Applicability · Structure · Sample Code · Consequences · Example use within DocAble · Related Patterns
Intent
Intent — Lints that assert two or more independent sources agree (config record ↔ sample JSON, registry ↔ its consumers, enum ↔ its uses), catching drift between sources that each look valid on their own.
Motivation
Some invariants live between files: a config record and its sample JSON must list the same fields; a registry and its consumers must share the same keys; an enum and its handlers must stay in step. Each file is individually valid, so the bug is invisible per-file, but together they disagree. The canonical case: a new field on a config record that is missing from the sample defaults to false at deserialization and silently collapses batching to single-call in prod. The failure is cross-source drift, recurring whenever paired sources evolve independently.
Applicability
- The paired sources are both machine-readable at lint-time.
- A declared consistency relation (subset, equality, one-to-one) between them.
- A lint that reads both and knows how to diff them.
Structure
The Structure diagram appears at the top of this page.
Sample Code
A coherence lint is a relation checked across two sources read at lint time. Here the relation is subset: every field declared on a config record must appear in the shipped sample, or the missing field deserializes to a default and silently changes behaviour. A single-source lint can never catch this — the defect lives only in the disagreement.
import sys
def coherence(record_fields: set[str], sample_fields: set[str]) -> list[str]:
"""Relational invariant: every field on the config record must appear in the
sample. A field present in code but absent from the sample deserializes to a
silent default — the bug lives between the files, not in either one."""
return [f"config field '{f}' missing from sample — will default silently"
for f in sorted(record_fields - sample_fields)]
if __name__ == "__main__":
# `fields_of` reads a source into its declared field set (record, sample, ...)
findings = coherence(fields_of("config_record"), fields_of("config_sample"))
for f in findings:
print(f"DRIFT: {f}")
sys.exit(1 if findings else 0)
Consequences
- The relation must be specified correctly — a wrong relation produces false failures or false confidence.
- Lint-time coupling. Reading several sources at once couples the lint to all of their shapes.
- Pairs must be registered — an unpaired source that should agree with another isn't checked until someone declares the relation.
Example use within DocAble
- The config-field ⊆ sample lints and the
ConfigDeserialization_*companion tests. - Registry-agreement lints (a registry and its consumers must list the same keys).
- The meta-file consistency discipline (read the meta-file; never embed a snapshot).
Related Patterns
- See also (sibling) — semantic-lints: per-source structural lints; this family is the relational complement.
- See also (cross-target) — meta-model-consumption: the agent side's "read the substrate, don't hardcode." Coherence lints enforce that two substrates stay consistent.
- Layer — with the other validation-and-conformance checks over the artifact.