The map that moved the bridge

Three flood-response teams divide a river corridor into overlapping zones. North Team maps the upstream road network in its own grid. East Team maps the bridge approaches in another. South Team maps the evacuation route in a third. Each team checks its hand-off with the next. Shared landmarks line up. The radio operator therefore reports that the three maps are consistent.

Then the command centre stitches the maps into one operational view. The bridge has moved thirty metres.

The first reaction is practical: average the coordinates. That feels reasonable because each local survey is accurate within its own zone. Yet averaging assumes that all three teams are already speaking the same coordinate language. They are not. North-to-East adds a ten-metre origin shift. East-to-South adds another ten. South-to-North, according to its own conversion sheet, subtracts only ten. Travel around the loop and the same landmark returns ten metres away from itself.

No team supplied an obviously absurd number. No single hand-off looked illegal in isolation. The failure lived in the composition of hand-offs. The maps did more than disagree about a value. Their translation rules failed to close.

This is the local-to-global problem in its most useful form. Local descriptions can be individually valid and pairwise interpretable while the whole still lacks a coherent state. The remedy is not a more confident average. It is a structure that records what each region can say, how its claims restrict to an overlap, and whether all translation paths agree. That structure is a sheaf.

The seam is where meaning changes

Imagine two weather stations ten kilometres apart. One reports 20. The other reports 68. A naive consistency check sees a gap of forty-eight units. A typed check asks a prior question: what does each number mean? The first is Celsius. The second is Fahrenheit. Once both are translated onto the overlap variable, air temperature in Celsius, the discrepancy vanishes.

The important object is not either number. It is the map that carries each local description into a shared comparison space. A sheaf makes that map explicit.

Figure 1.Overlapping maps either stitch or tear
Illustrative
Three organic map patches with matching and mismatching seams The upper row shows three overlapping patches whose river, road and bridge align across overlaps. The lower row shows the same local patches with a translation drift that creates a visible tear at the final seam. Translations close One loop accumulates drift tear at final seam same local pieces different whole
Local richness does not determine global coherence. The decisive information lies at the seams and in the way seam translations compose. Accessible description: three map patches align in the upper example, while a shifted third patch creates a visible gap in the lower example.

From patches to a sheaf

Start with a space divided into regions. A region may be geographic, temporal, organisational or conceptual. To each region U, assign the descriptions that are valid there. A description might be a continuous function, a database record, a coordinate frame, a probability distribution or a set of permitted system states.

If V sits inside U, there is a restriction map from descriptions on U to descriptions on V. Restriction answers a concrete question: what does this larger claim say when viewed only on the smaller overlap?

A presheaf records regions, local descriptions and restrictions. A sheaf adds two conditions. Compatible pieces must glue into a global description, and that global description must be unique. The formal definition says that when local sections agree after restriction to every overlap, there exists exactly one section over their union that restricts back to them. This is the standard sheaf condition.[1]

Figure 2.The anatomy of gluing
Conceptual synthesis
The sheaf axiom is not a voting rule. It is a typed existence-and-uniqueness contract for compatible local descriptions. Accessible description: a three-stage sequence moves from two local sections, through overlap comparison, to one unique glued section.
Decisive mechanism

Gluing succeeds because every local description is compared only after both sides have been carried into the same overlap space. Raw equality is neither required nor usually meaningful. What must agree are the restricted descriptions.

Worked example 1 · A temperature seam

Station A says 20 °C. Station B says 68 °F. The overlap variable is Celsius. A restricts by the identity map. B restricts by (F − 32) × 5/9. Both produce 20. The sections glue. If the translator omits −32, B restricts to 37.8. The local readings did not change, but the assignment no longer forms a compatible family.

This corrects a common slogan. It is misleading to say that “even compatible local sections of a sheaf might not glue”. For an actual sheaf, compatibility over a cover is exactly what guarantees unique gluing. Failure appears because the supplied local assignment is incompatible, because the proposed restriction architecture is only a presheaf, because an extension problem has a non-zero obstruction, or because the model forgot information needed for uniqueness.

Evidence status

The gluing axiom is established mathematics. Cellular sheaves make the idea finite and computable by attaching vector spaces and maps to cells in a complex.[2] The use of sheaves for sensor integration, network analysis and data fusion is a published applied programme, not a proof that every integration problem should use sheaf theory.[3]

The argument in one pass
Chart · flow
flowchart LR
  A["The map that moved the bridge"] --> B["The seam is where meaning changes"]
  B --> C["The loop that local checks<br/>cannot see"]
  C --> D["Noise, ambiguity and false<br/>agreement"]
  D -. "re-test" .-> A
  classDef first fill:#0a1424,stroke:#007D68,stroke-width:2px,color:#e6f3ff
  classDef step fill:#0a1424,stroke:#3f7da0,stroke-width:1px,color:#e6f3ff
  class A first
  class B,C,D step
A structural route through Sheaves: local truth and global consistency: from The map that moved the bridge to Prove the reports inhabit one…. Reading use: follow the relation, then return to the surrounding argument for evidence and limits.

The loop that local checks cannot see

A chain of translations is forgiving. Choose a value at the first node, apply each local rule, and every later node receives a value. Nothing forces the final result to revisit its starting point. A loop is different. It creates a counterfactual route back to the same place. The composed translation must return the original value.

Figure 3.One closing edge turns propagation into a test
Illustrative
A chain becomes a cycle in four stages The first three stages show values propagating from A to B to C. The fourth adds a closing edge from C to A, revealing a one-unit closure error. A choose xA = 0 B xB = 2 C xC = 5 +2 +3 closing rule −4 0 → 2 → 5 → 1 the same node returns one unit away
A tree propagates constraints. A cycle audits their composition. The closing edge reveals a defect that was previously unobservable. Accessible description: values move from A equals zero, to B equals two, to C equals five, then a closing rule returns to A equals one rather than zero.

The smallest obstruction you can calculate mentally

Let each node hold one real number. The edge from A to B says that B is two units above A. The edge from B to C says that C is three units above B. The final edge says that A is four units below C.

xB − xA = 2
xC − xB = 3
xA − xC = −4

Add the left sides. Every variable cancels, so the result must be zero. Add the right sides and the result is one. No assignment can satisfy all three equations. The contradiction is not located at one node. It is carried by the cycle.

Worked example 2 · Distributing a one-unit tear

Anchor A at zero and fit B and C by least squares. The best values are approximately xB = 1.667 and xC = 4.333. Each edge misses its target by one third. The fitted state is useful as a repair proposal, but it is not an exact gluing. The residual did not disappear. It was distributed.

Figure 4.The triangle carries a one-unit obstruction
Illustrative numerical example
Triangle of affine constraints and least-squares residuals A triangle connects A, B and C. Required offsets sum to one around the loop. The best fit spreads a residual of minus one third across each edge. A B C anchor 0 best fit 1.667 best fit 4.333 A → B requires +2 fit residual −⅓ B → C requires +3 fit residual −⅓ C → A requires −4 fit residual −⅓ cycle sum: 2 + 3 − 4 = 1
Least squares can propose the smallest repair, but the non-zero cycle sum remains the reason exact gluing is impossible. Accessible description: a labelled triangle shows the three required offsets, best-fit node values and equal one-third residuals.

What cohomology adds

Write all node values as a vector x. Let the operator δ compute the difference demanded on every edge. In the homogeneous case, a global section satisfies δx = 0. In the affine case above, the target edge offsets form a vector b, and a global assignment must satisfy:

δx = b

The equation has a solution exactly when b lies in the image of δ. On a graph, adding a node potential changes edge offsets by a coboundary. What remains after every such local correction is the cohomology class of the cycle defect. For the triangle, the class is witnessed by the sum around the loop.

Cohomology is therefore not a mystical global summary. It records a defect that no redistribution of local coordinates can remove. In higher-dimensional settings the algebra is richer, but the causal intuition survives: some conflicts live on overlaps, while others appear only when overlap relations are composed around a closed structure.

Boundary condition

A non-zero cohomological witness can certify an obstruction in the model being analysed. A zero witness need not prove that every desired global object exists. In the sheaf-theoretic treatment of contextuality, for example, a non-vanishing Čech cohomology class is sufficient but not necessary for contextuality.[7] The diagnostic has a declared scope.

Cycles are not the enemy

It would be easy to conclude that cycles create inconsistency. They do not. A consistent cycle closes exactly and supplies valuable redundancy. A tree can always propagate relative values, even when one translator is wrong. With no alternative path, the error is invisible. The cycle adds a testable equality between two routes.

Database theory shows a related structural divide. For relational and bag-valued schemas, acyclic hypergraphs enjoy a local-to-global property: pairwise consistency can guarantee global consistency. Cyclic schemas require more care, and for bag consistency the decision problem becomes NP-complete for fixed cyclic schemas in the cited result.[9] This does not mean every cycle is computationally hard. It means topology changes what local checks can establish.

Published comparison

Abramsky and Brandenburger model quantum contextuality as an obstruction to a global section over a measurement cover.[6] Distributed-systems research has likewise used contextuality to characterise failures of global consistency among relative execution traces.[12] The shared structure is local descriptions plus overlap constraints. The physical interpretation does not transfer automatically.

Noise, ambiguity and false agreement

Exact equality is a poor operating assumption for sensors, human reports and learned models. Measurements drift. Timestamps round. Language mappings lose detail. A useful local-to-global method must say more than “glues” or “does not glue”. It must quantify mismatch without erasing its structure.

The residual lives on the overlap

For a cellular sheaf on a graph, each node may hold a vector in its own local space. Each edge has a shared comparison space. The restriction map from an endpoint to an edge translates that endpoint’s state into the shared space. For an edge e joining u and v, define the mismatch:

re = Rv→exv − Ru→exu
  • xuthe local state proposed at node u
  • Ru→ethe typed translation from node u into edge e’s overlap space
  • rethe disagreement after both endpoints have been translated into the same space

Stack all residuals into δx. A weighted inconsistency energy is:

E(x) = Σe we ‖re‖² = ‖W1/2δx‖²

Every weight we is an assumption about confidence or consequence. The equation is useful only when the local spaces, translations, norms and weights are justified. Under those assumptions, minimising E finds the state that least violates the overlap constraints. The associated sheaf Laplacian is L = δ*Wδ. Its kernel is the space of exact global sections in the linear setting, while its spectrum describes aspects of how disagreement propagates.[5]

Figure 5.Averaging reduces visible residual but cannot cancel cycle drift
Synthetic triangle model
Minimum inconsistency energy grows quadratically with cycle drift A parabola shows that the minimum squared residual is zero only when the cycle drift is zero. Points at drift minus three to plus three are labelled with the resulting minimum energy. cycle drift d minimum energy d² / 3 −3 −2 −1 0 1 2 3 0 1 2 3 d = 1 gives E = ⅓ the tear is smaller, not gone only d = 0 glues exactly the curve touches zero once
For the equal-weight triangle, the best fit spreads a closure drift d across three edges, giving minimum squared energy d²/3. The curve measures repair cost, not truth. Accessible description: a symmetric parabola touches zero at zero drift and rises as positive or negative cycle drift grows.

Robinson’s consistency radius provides a related approach for sheaves of pseudometric spaces. It measures disagreement among restricted local assignments, supports consistency filtrations and is stable under specified perturbations.[4] The practical lesson is broader than any one norm: noise should soften equality, not erase the topology of where disagreement occurs.

Four questions that a single score confuses

Suppose an optimiser returns a very small energy. Can the system act? Not yet. Four different questions remain.

Residual asks how far the supplied assignment lies from satisfying the overlap rules. Obstruction asks whether any exact global assignment can satisfy the stated local constraints. Ambiguity asks how many materially different global states remain possible. Stability asks how much the conclusion moves when data or restriction maps are perturbed.

These quantities can vary independently. A tree of precise relative offsets can have zero residual and no cycle obstruction, yet still float by an unknown global origin. A heavily redundant network can determine a unique state but remain fragile if many constraints are almost dependent. A noisy loop can have a small residual but a repeatable non-zero closure error that identifies a biased translator.

Figure 6.A release decision is a gluing problem across typed local worlds
Practitioner synthesis
A consequential “release” is not one model output. It is a global section across local systems whose overlaps carry different kinds of authority. Accessible description: four panels for world state, installed part, authority, and evidence are connected by one shared contract containing identifiers, times, permissions and effects.
Worked example 3 · Synthetic maintenance release

A planning agent proposes reopening a machine after a bearing replacement. The work-order service says the task is complete. The inventory service says bearing B-204 is installed. The authority service records an engineer’s approval for bearing B-203. The effect ledger confirms a successful test run after B-204 was installed.

Every local record is valid in its own system. A dashboard that checks only “task complete”, “approval present” and “test passed” returns green. A sheaf-like overlap contract carries the bearing serial into the authority comparison space. The approval and installed-part sections fail to agree. The correct outcome is not “mostly consistent”. The proposed release lacks a global authorised state.

This boundary experiment matters for human-machine systems. Intent, identity, world state, permitted context, model proposal, authority, typed action, evidence, verified outcome and recovery are not interchangeable fields. Collapsing them into “memory” makes incompatible histories look identical. A global state is only as discriminating as the overlaps it preserves.

Worked example 4 · The lossy overlap

Two laboratories report a pressure profile. Their overlap map retains only the label “safe” or “unsafe”. Both restrict to “safe”, so gluing succeeds. Yet one profile is 2% below the safety limit and the other is 40% below. For a binary dispatch decision, the overlap may be sufficient. For forecasting margin erosion, it destroys decision-relevant information. The issue is not obstruction. It is ambiguity created by a lossy restriction.

Figure 7.Four diagnostics, four different repairs
Practitioner synthesis
DiagnosticQuestionTypical witnessCorrect responseWhat zero does not prove
ResidualHow far is this assignment from the overlap rules?Non-zero edge mismatch or consistency radiusRecalibrate, reweight or inspect noisy sourcesThat an exact global state exists for every required field
ObstructionCan any exact assignment satisfy the constraints?Cycle closure error or non-zero obstruction classRepair a translator, constraint or local claimThat the model’s ontology is true or complete
AmbiguityHow many decision-distinct global states remain?Nullity, unanchored component or lossy overlapAdd an anchor, richer overlap or new measurementThat the selected state is factually correct
StabilityWould a small perturbation change the conclusion?Small spectral margin or high condition sensitivityCollect independent evidence or reduce consequenceThat the stable consensus is not a shared falsehood
Do not compress these diagnostics into one confidence score. Each failure type points to a different intervention. Accessible description: a five-column table distinguishes residual, obstruction, ambiguity and stability by their questions, witnesses, repairs and limits.
Strongest alternative

Constraint solvers, factor graphs, database joins and type systems can solve many local-to-global problems with less machinery. Use them when all variables already inhabit one schema and translations are trivial. Sheaf language earns its cost when local domains differ, overlaps carry explicit maps, coverage changes, or topology itself controls what local checks establish. A decorative sheaf is worse than a clear constraint graph.

Open research hypothesis

A system that localises inconsistency to overlap maps and cycle witnesses will recover faster than one that reports only a global contradiction score. This would be strengthened by lower repair time and fewer unnecessary data rejections in matched failure-injection trials. It would be weakened if ordinary constraint provenance identifies the same repair with equal effort. The smallest useful test is a synthetic network with one biased translator, one noisy source and one lossy overlap.

How the claim changes under pressure
Chart · state
stateDiagram-v2
  [*] --> Claim
  state "From patches to a sheaf" as Claim
  state "The smallest obstruction you<br/>can calculate mentally" as Test
  state "What cohomology adds" as Revision
  Claim --> Test: expose mechanism
  Test --> Decision: evidence survives
  Test --> Revision: rival survives
  Revision --> Claim: narrow or rebuild
  Decision --> [*]
The revision loop begins with From patches to a sheaf and keeps counterexamples inside the reasoning path. Reading use: follow the relation, then return to the surrounding argument for evidence and limits.

The gluing gate

The theory becomes operational when it changes a release decision. The proposed instrument here is a Gluing Assurance Record, or GAR. It is not a new theorem. It is a practitioner synthesis that packages established sheaf and linear-algebraic ideas into one reviewable decision object.

A GAR should be versioned with the candidate boundary, local spaces, restriction maps, observations, weights and decision threshold. Its four core diagnostics are:

  • Compatibility residual: the weighted mismatch of the supplied assignment on each overlap.
  • Cycle obstruction: a path or cohomological witness showing that no exact global assignment satisfies the stated constraints.
  • Ambiguity dimension: the remaining degrees of freedom after all constraints and anchors are applied.
  • Stability margin: the resistance of the conclusion to small perturbations in data or translations.

The record also states the permitted decision. A GAR for “may display a consolidated dashboard” does not authorise “may execute an irreversible action”. The global section is scoped to the variables and relations encoded in the model.

New causal distinction

Repairable disagreement and structural impossibility are not the same failure. A residual may shrink when observations are denoised. A cycle obstruction persists until a claim or translation rule changes. Ambiguity requires more information, while fragility requires more independent constraint. Treating all four as low confidence leads to the wrong recovery action.

Figure 8.A stale translator can manufacture a globally authorised story
Synthetic adversarial path
Adversarial path through a stale authority translation World state, authority, proposal and evidence feed a release gate. A stale cache bypasses the current authority overlap. A cycle check between authority version, action time and receipt exposes the bypass. World state part B-204 installed Current authority approval revoked at t2 Effect receipt test completed at t3 Model proposal release machine Stale cache approval valid at t1 Release gate global section? NO: t1 ≠ t2 ≠ t3 proposed bypass authority-version overlap Cycle witness approval version → action time → receipt time → approval version
A stale approval can satisfy a shallow checklist. A versioned overlap cycle exposes that the authority used by the proposal is not the authority valid at execution. Solid lines show executed evidence paths; the dotted coral line is the rejected bypass. Accessible description: four information sources feed a release gate, while a stale approval cache attempts a bypass that is rejected by a time-and-version cycle check.

Run the smallest useful experiment

The interactive lab below uses the three affine translations from the opening example. Each edge specifies how much a value changes when transported to the next node. The closure error is the sum around the oriented cycle. Zero means the offsets can arise from node values. A non-zero result means no exact gluing exists.

Executable lab

Close the triangle

Change any offset. Set C → A to −5 to make the loop close exactly. Values are synthetic.

Cycle closure1.000
Best equal edge residual0.333
DecisionReject

The browser lab exposes one invariant. The companion Python artefact adds weighted least squares, cycle witnesses, anchor conflicts, ambiguity and a spectral stability proxy. It accepts a JSON model in which every directed edge states x_v − x_u = offset.

How to interpret the artefact

A positive exact-gluing result permits only the conclusion that the supplied scalar affine constraints are mutually satisfiable. A non-zero cycle witness identifies a structural conflict. A non-zero ambiguity dimension means the model lacks an absolute anchor for at least one component. A small stability margin warns that relative state is weakly constrained. None of these results establishes factual truth, causal validity, adequate coverage or moral authority.

Python · full runnable artefact
#!/usr/bin/env python3
"""Gluing Assurance Record for scalar affine constraints on a graph.

Each directed edge u -> v encodes the local translation

    x_v - x_u = offset

The analyser separates four questions:
1. obstruction: do translations close around every cycle and agree with anchors?
2. fit: what least-squares state best reconciles noisy constraints?
3. ambiguity: how many connected components remain unanchored?
4. stability: how strongly does the graph constrain relative state?

This is a small teaching implementation. It is not a general sheaf-cohomology
library and should not be used as the sole release control for consequential systems.
"""

from __future__ import annotations

from dataclasses import dataclass
import json
import math
import sys
from pathlib import Path
from typing import Any, Dict, Iterable, List, Mapping, MutableMapping, Sequence, Tuple


@dataclass(frozen=True)
class Edge:
    u: str
    v: str
    offset: float
    weight: float = 1.0
    label: str = ""


@dataclass(frozen=True)
class Anchor:
    node: str
    value: float
    weight: float = 1000.0
    label: str = ""


def _parse_model(raw: Mapping[str, Any]) -> Tuple[List[str], List[Edge], List[Anchor]]:
    nodes = [str(n) for n in raw.get("nodes", [])]
    if not nodes:
        raise ValueError("model.nodes must contain at least one node")
    if len(nodes) != len(set(nodes)):
        raise ValueError("model.nodes contains duplicates")
    node_set = set(nodes)

    edges: List[Edge] = []
    for i, item in enumerate(raw.get("edges", [])):
        u, v = str(item["u"]), str(item["v"])
        if u not in node_set or v not in node_set:
            raise ValueError(f"edge {i} references an unknown node")
        if u == v:
            raise ValueError(f"edge {i} is a self-loop; expand it as an explicit constraint")
        weight = float(item.get("weight", 1.0))
        if weight <= 0:
            raise ValueError(f"edge {i} has non-positive weight")
        edges.append(
            Edge(
                u=u,
                v=v,
                offset=float(item["offset"]),
                weight=weight,
                label=str(item.get("label", f"{u}->{v}")),
            )
        )

    anchors: List[Anchor] = []
    for i, item in enumerate(raw.get("anchors", [])):
        node = str(item["node"])
        if node not in node_set:
            raise ValueError(f"anchor {i} references an unknown node")
        weight = float(item.get("weight", 1000.0))
        if weight <= 0:
            raise ValueError(f"anchor {i} has non-positive weight")
        anchors.append(
            Anchor(
                node=node,
                value=float(item["value"]),
                weight=weight,
                label=str(item.get("label", f"anchor:{node}")),
            )
        )
    return nodes, edges, anchors


def _components(nodes: Sequence[str], edges: Sequence[Edge]) -> Tuple[List[List[str]], Dict[str, int]]:
    adjacency: Dict[str, List[str]] = {n: [] for n in nodes}
    for edge in edges:
        adjacency[edge.u].append(edge.v)
        adjacency[edge.v].append(edge.u)

    components: List[List[str]] = []
    component_of: Dict[str, int] = {}
    for start in nodes:
        if start in component_of:
            continue
        cid = len(components)
        stack = [start]
        component: List[str] = []
        component_of[start] = cid
        while stack:
            node = stack.pop()
            component.append(node)
            for nxt in adjacency[node]:
                if nxt not in component_of:
                    component_of[nxt] = cid
                    stack.append(nxt)
        components.append(component)
    return components, component_of


def _cycle_witnesses(nodes: Sequence[str], edges: Sequence[Edge]) -> Tuple[Dict[str, float], List[Dict[str, Any]]]:
    """Assign tree potentials and report closure error on each non-tree edge."""
    adjacency: Dict[str, List[Tuple[str, float, int]]] = {n: [] for n in nodes}
    for index, edge in enumerate(edges):
        # Traversing u -> v adds offset; traversing v -> u subtracts it.
        adjacency[edge.u].append((edge.v, edge.offset, index))
        adjacency[edge.v].append((edge.u, -edge.offset, index))

    potentials: Dict[str, float] = {}
    parent_edge: Dict[str, int] = {}
    tree_edges: set[int] = set()

    for root in nodes:
        if root in potentials:
            continue
        potentials[root] = 0.0
        stack = [root]
        while stack:
            node = stack.pop()
            for nxt, delta, edge_index in adjacency[node]:
                if nxt not in potentials:
                    potentials[nxt] = potentials[node] + delta
                    parent_edge[nxt] = edge_index
                    tree_edges.add(edge_index)
                    stack.append(nxt)

    witnesses: List[Dict[str, Any]] = []
    for index, edge in enumerate(edges):
        if index in tree_edges:
            continue
        closure = potentials[edge.v] - potentials[edge.u] - edge.offset
        witnesses.append(
            {
                "edge": edge.label or f"{edge.u}->{edge.v}",
                "u": edge.u,
                "v": edge.v,
                "required_offset": edge.offset,
                "implied_offset": potentials[edge.v] - potentials[edge.u],
                "closure_error": closure,
            }
        )
    return potentials, witnesses


def _anchor_witnesses(
    components: Sequence[Sequence[str]],
    component_of: Mapping[str, int],
    potentials: Mapping[str, float],
    anchors: Sequence[Anchor],
) -> List[Dict[str, Any]]:
    by_component: Dict[int, List[Tuple[Anchor, float]]] = {i: [] for i in range(len(components))}
    for anchor in anchors:
        # The anchor implies a root value for its component.
        implied_root = anchor.value - potentials[anchor.node]
        by_component[component_of[anchor.node]].append((anchor, implied_root))

    witnesses: List[Dict[str, Any]] = []
    for cid, items in by_component.items():
        if len(items) < 2:
            continue
        reference_anchor, reference_root = items[0]
        for anchor, implied_root in items[1:]:
            witnesses.append(
                {
                    "component": cid,
                    "reference": reference_anchor.label,
                    "other": anchor.label,
                    "reference_implied_root": reference_root,
                    "other_implied_root": implied_root,
                    "closure_error": implied_root - reference_root,
                }
            )
    return witnesses


def _solve_linear_system(matrix: List[List[float]], rhs: List[float], tolerance: float = 1e-12) -> List[float]:
    """Solve a dense square system using Gaussian elimination with pivoting."""
    n = len(rhs)
    augmented = [row[:] + [rhs[i]] for i, row in enumerate(matrix)]
    for col in range(n):
        pivot = max(range(col, n), key=lambda r: abs(augmented[r][col]))
        if abs(augmented[pivot][col]) <= tolerance:
            raise ValueError("normal equations are singular after gauge fixing")
        if pivot != col:
            augmented[col], augmented[pivot] = augmented[pivot], augmented[col]
        pivot_value = augmented[col][col]
        for j in range(col, n + 1):
            augmented[col][j] /= pivot_value
        for row in range(n):
            if row == col:
                continue
            factor = augmented[row][col]
            if abs(factor) <= tolerance:
                continue
            for j in range(col, n + 1):
                augmented[row][j] -= factor * augmented[col][j]
    return [augmented[i][n] for i in range(n)]


def _least_squares(
    nodes: Sequence[str],
    edges: Sequence[Edge],
    anchors: Sequence[Anchor],
    components: Sequence[Sequence[str]],
    component_of: Mapping[str, int],
) -> Tuple[Dict[str, float], List[Dict[str, Any]], float, float]:
    index = {node: i for i, node in enumerate(nodes)}
    equations: List[Tuple[List[float], float, float, str, str]] = []

    for edge in edges:
        row = [0.0] * len(nodes)
        row[index[edge.u]] = -1.0
        row[index[edge.v]] = 1.0
        equations.append((row, edge.offset, edge.weight, "edge", edge.label))

    anchored_components = {component_of[a.node] for a in anchors}
    for anchor in anchors:
        row = [0.0] * len(nodes)
        row[index[anchor.node]] = 1.0
        equations.append((row, anchor.value, anchor.weight, "anchor", anchor.label))

    # One gauge equation per unanchored component fixes only the arbitrary origin.
    for cid, component in enumerate(components):
        if cid in anchored_components:
            continue
        row = [0.0] * len(nodes)
        row[index[component[0]]] = 1.0
        equations.append((row, 0.0, 1.0, "gauge", f"gauge:{component[0]}"))

    n = len(nodes)
    normal = [[0.0 for _ in range(n)] for _ in range(n)]
    target = [0.0 for _ in range(n)]
    for row, value, weight, _, _ in equations:
        for i in range(n):
            if row[i] == 0:
                continue
            target[i] += weight * row[i] * value
            for j in range(n):
                if row[j] != 0:
                    normal[i][j] += weight * row[i] * row[j]

    # Tiny diagonal regularisation handles isolated, gauge-fixed numerical cases.
    for i in range(n):
        normal[i][i] += 1e-12
    solution_vector = _solve_linear_system(normal, target)
    solution = {node: solution_vector[index[node]] for node in nodes}

    residuals: List[Dict[str, Any]] = []
    weighted_sum = 0.0
    total_weight = 0.0
    max_abs = 0.0
    for row, value, weight, kind, label in equations:
        if kind == "gauge":
            continue
        predicted = sum(coef * solution_vector[i] for i, coef in enumerate(row))
        residual = predicted - value
        residuals.append(
            {
                "kind": kind,
                "label": label,
                "required": value,
                "predicted": predicted,
                "residual": residual,
                "weight": weight,
            }
        )
        if kind == "edge":
            weighted_sum += weight * residual * residual
            total_weight += weight
        max_abs = max(max_abs, abs(residual))
    rms = math.sqrt(weighted_sum / total_weight) if total_weight else 0.0
    return solution, residuals, rms, max_abs


def _jacobi_eigenvalues(matrix: Sequence[Sequence[float]], tolerance: float = 1e-12) -> List[float]:
    """Eigenvalues of a small real symmetric matrix via Jacobi rotations."""
    n = len(matrix)
    if n == 0:
        return []
    a = [list(row) for row in matrix]
    max_iterations = max(50, 20 * n * n)
    for _ in range(max_iterations):
        p, q, largest = 0, 0, 0.0
        for i in range(n):
            for j in range(i + 1, n):
                value = abs(a[i][j])
                if value > largest:
                    p, q, largest = i, j, value
        if largest < tolerance:
            break
        app, aqq, apq = a[p][p], a[q][q], a[p][q]
        angle = 0.5 * math.atan2(2.0 * apq, aqq - app)
        c, s = math.cos(angle), math.sin(angle)
        for k in range(n):
            if k in (p, q):
                continue
            akp, akq = a[k][p], a[k][q]
            a[k][p] = a[p][k] = c * akp - s * akq
            a[k][q] = a[q][k] = s * akp + c * akq
        a[p][p] = c * c * app - 2.0 * s * c * apq + s * s * aqq
        a[q][q] = s * s * app + 2.0 * s * c * apq + c * c * aqq
        a[p][q] = a[q][p] = 0.0
    return sorted(a[i][i] for i in range(n))


def _stability_margin(nodes: Sequence[str], edges: Sequence[Edge], tolerance: float) -> float:
    """Return the smallest positive eigenvalue of the weighted scalar graph Laplacian."""
    index = {node: i for i, node in enumerate(nodes)}
    n = len(nodes)
    laplacian = [[0.0 for _ in range(n)] for _ in range(n)]
    for edge in edges:
        i, j, w = index[edge.u], index[edge.v], edge.weight
        laplacian[i][i] += w
        laplacian[j][j] += w
        laplacian[i][j] -= w
        laplacian[j][i] -= w
    positive = [ev for ev in _jacobi_eigenvalues(laplacian) if ev > tolerance]
    return min(positive) if positive else 0.0


def analyse_model(
    raw: Mapping[str, Any],
    *,
    tolerance: float = 1e-9,
    review_threshold: float = 0.25,
) -> Dict[str, Any]:
    nodes, edges, anchors = _parse_model(raw)
    components, component_of = _components(nodes, edges)
    potentials, cycle_witnesses = _cycle_witnesses(nodes, edges)
    anchor_witnesses = _anchor_witnesses(components, component_of, potentials, anchors)

    all_obstructions = [abs(w["closure_error"]) for w in cycle_witnesses]
    all_obstructions.extend(abs(w["closure_error"]) for w in anchor_witnesses)
    max_obstruction = max(all_obstructions, default=0.0)
    exactly_gluable = max_obstruction <= tolerance

    solution, residuals, rms_residual, max_residual = _least_squares(
        nodes, edges, anchors, components, component_of
    )
    anchored_components = {component_of[a.node] for a in anchors}
    ambiguity_dimension = sum(1 for cid in range(len(components)) if cid not in anchored_components)
    stability_margin = _stability_margin(nodes, edges, tolerance)

    if not exactly_gluable and max_obstruction > review_threshold:
        decision = "reject_structural_obstruction"
    elif not exactly_gluable:
        decision = "review_near_consistent"
    elif ambiguity_dimension > 0:
        decision = "insufficient_boundary"
    elif stability_margin <= tolerance:
        decision = "review_fragile_or_disconnected"
    else:
        decision = "accept_for_encoded_scope"

    return {
        "gluing_assurance_record": {
            "exactly_gluable": exactly_gluable,
            "max_obstruction": max_obstruction,
            "weighted_rms_residual": rms_residual,
            "max_fitted_residual": max_residual,
            "ambiguity_dimension": ambiguity_dimension,
            "stability_margin": stability_margin,
            "decision": decision,
        },
        "best_fit_state": solution,
        "cycle_witnesses": cycle_witnesses,
        "anchor_witnesses": anchor_witnesses,
        "fitted_residuals": residuals,
        "model_scope": {
            "nodes": len(nodes),
            "edges": len(edges),
            "anchors": len(anchors),
            "components": len(components),
        },
        "limitations": [
            "Only scalar affine edge translations are supported.",
            "Consistency is relative to the supplied nodes, constraints, weights, and anchors.",
            "A zero obstruction does not establish factual truth, causal correctness, or adequate coverage.",
        ],
    }


DEMO_MODEL: Dict[str, Any] = {
    "nodes": ["A", "B", "C"],
    "edges": [
        {"u": "A", "v": "B", "offset": 2.0, "label": "A to B"},
        {"u": "B", "v": "C", "offset": 3.0, "label": "B to C"},
        {"u": "C", "v": "A", "offset": -4.0, "label": "C to A"},
    ],
    "anchors": [{"node": "A", "value": 0.0, "weight": 1000.0, "label": "A origin"}],
}


def _main(argv: Sequence[str]) -> int:
    if len(argv) > 2:
        print(f"usage: {Path(argv[0]).name} [model.json]", file=sys.stderr)
        return 2
    if len(argv) == 2:
        raw = json.loads(Path(argv[1]).read_text(encoding="utf-8"))
    else:
        raw = DEMO_MODEL
    result = analyse_model(raw)
    print(json.dumps(result, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(_main(sys.argv))

Run with python paper-158-sheaf-consistency-lab.py for the built-in triangle, or pass a JSON file. The implementation uses only the Python standard library.

Input schema, invariants and decision meanings

The top-level JSON object contains nodes, edges and optional anchors. Every edge has u, v, offset, optional weight and optional label. An anchor fixes one node to an absolute value.

The invariant is explicit: for every edge, the candidate state should satisfy x_v − x_u = offset. The cycle analyser constructs a spanning forest and checks each non-tree edge against the offset implied by the tree. The least-squares stage distributes unavoidable mismatch. The ambiguity dimension counts connected components without an anchor. The stability margin is the smallest positive eigenvalue of the weighted scalar graph Laplacian.

accept_for_encoded_scope means exact closure, adequate anchoring and a positive stability margin under this model. insufficient_boundary means the relative constraints glue but do not fix an absolute state. review_near_consistent means a small non-zero obstruction remains. reject_structural_obstruction means the closure defect exceeds the declared review threshold.

Use the result to choose the repair

The practical gate is not “score above threshold”. It is a branching decision. When obstruction is non-zero, identify the smallest witness cycle and inspect its local claims and translations. When obstruction is zero but ambiguity remains, add an anchor or enrich the overlap. When the section is unique but unstable, collect an independent measurement or reduce the action’s irreversibility. Only when the encoded state is compatible, sufficiently determined and stable should the system proceed within its declared authority.

Figure 9.The gluing decision surface
Decision instrument
The same mathematical result supports different operational decisions at different consequence levels. Coherence is necessary for action, but high-consequence action also needs stability, authority and recovery. Accessible description: four quadrants recommend proceeding, strengthening evidence, repairing the model or blocking action according to coherence and consequence.
Scope of the synthesis

Global sections as consistent states, sheaf Laplacians and consistency radii are published constructions.[5][4] Recent work applies cellular sheaves to heterogeneous graph learning and distributed task solvability.[10][11] The GAR, its four-field release gate and the maintenance example are practitioner synthesis. They require domain validation before operational use.

The evidence conversation
Chart · sequence
sequenceDiagram
  participant Q as Question
  participant M as Mechanism
  participant E as Evidence
  participant D as Decision
  Q->>M: The map that moved the bridge
  M->>E: From patches to a sheaf
  E-->>M: compare rival explanations
  E->>D: Prove the reports inhabit one decision-relevant state
  D-->>Q: revise scope or act
Question, mechanism, evidence and decision exchange the burdens developed in Sheaves: local truth and global consistency. Reading use: follow the relation, then return to the surrounding argument for evidence and limits.

Prove the reports inhabit one decision-relevant state

A local report is easy to accept because it arrives with detail. A component can show a timestamp, a confidence score, an explanation and a valid signature. Another component can do the same. Yet the decision-maker does not act on those reports separately. It acts on the state they appear to describe together. The practical question is therefore not whether each source looks respectable. It is whether the sources can inhabit one state after their meanings, times, identities and authorities have been translated onto shared overlaps.

Do not globalise information merely because every pair appears to agree. Pairwise agreement may be checked in different coordinate systems, at different cutoffs or against different versions of authority. It can also be vacuous when the overlap carries too little information. A reliable globalisation decision needs a declared candidate boundary, explicit translation maps, loop closure, enough anchors to remove consequential ambiguity and a stability check suited to the cost of acting.

Begin with the decision, not the database

Suppose a maintenance system must decide whether to release a machine. Start by writing the release proposition in a typed form: machine identity, installed part, inspection result, approver, approval version, valid interval and requested action. This statement defines the candidate global section. The local spaces are then chosen because they supply or constrain one of those fields. The inventory service need not expose its entire schema. The inspection tool need not be treated as an oracle. Each contributes only the section and restriction maps required by the decision.

This reverses a common integration habit. Teams often begin with available data, merge fields and then ask what the combined record means. A sheaf-informed design begins with the global state that would justify action and asks what local evidence would have to agree for that state to exist. The design thereby makes missing overlaps visible. If the inspection receipt has no binding to machine identity, the system lacks a necessary map. If approval is tied to a work-order version but the action contract carries only a work-order number, the apparent join discards the very distinction that protects the decision.

Typed database mappings already show why migrations should preserve structure rather than merely move values.[8] The same discipline applies here, but the target is not a universal enterprise ontology. The target is a decision-sized consistency model. It should include exactly the distinctions whose loss could change the action, no fewer and no more.

The pre-globalisation protocol

Define, translate, close, determine, stress, then act. Define the candidate global state and its boundary. Translate every local section onto each decision-relevant overlap. Close independent routes around cycles. Determine whether more than one global state remains possible. Stress the result under plausible perturbations, stale versions and missing sources. Act only within the authority and reversibility represented by the model.

Choose the repair from the diagnosis

The four fields in the Gluing Assurance Record are deliberately not interchangeable. An obstruction asks whether the encoded descriptions can be true together. Ambiguity asks how many global states remain compatible with them. Instability asks whether small local changes can produce large global changes. Authority asks whether the resulting state may support this action. A system that treats all four as “low confidence” cannot choose a principled repair.

When a cycle obstruction appears, inspect the smallest witness rather than averaging the entire network. One translation may use a different unit, one assertion may refer to the wrong subject or one cache may preserve an obsolete permission. The witness cycle narrows the search to the relations that cannot jointly hold. Database consistency research makes a related point: global consistency of overlapping data collections is not reducible to arbitrary local checks, and structural conditions determine when local consistency is sufficient.[9]

When obstruction is zero but ambiguity remains, a new measurement may be more useful than a better optimiser. Add an anchor, widen an overlap or make the decision less specific. If two possible installed parts fit all current reports, no amount of numerical smoothing can manufacture identification. The correct output is not a confident guess. It is a record of what additional distinction would collapse the remaining alternatives.

When the state is unique but unstable, seek an independent path or reduce consequence. A nearly disconnected network can transmit enough constraints to identify a solution while making that solution extremely sensitive to one weak edge. The spectral margin in the lab is only a scalar proxy, but it expresses the operational intuition: a state held together by one fragile translation deserves a different release rule from a state supported by several independent overlaps.

When the state is coherent, determined and stable but authority is absent, stop anyway. Mathematical gluing does not confer permission. A valid section over facts and receipts can coexist with an invalid action because the actor lacks the capability, purpose or temporal entitlement required to use those facts. Consistency is a property of the encoded descriptions; release is a governed decision.

Worked example 5: the release meeting

Three teams enter a review. Inventory says part P-17 is installed. Inspection says P-17 passed. Governance says work order W-42 is approved. In the old meeting, three green indicators produce a release. In the gluing meeting, the chair asks for the overlap maps. Inventory binds P-17 to machine M-8 at version 12. Inspection binds its test to M-8 at version 11. Governance approves W-42 version 12, but W-42 version 12 replaced P-17 with P-19. Every local statement may be accurate in its own context. There is no section that assigns one installed part and one work-order version across all three contexts.

The repair is not “ask the model again”. The team either re-inspects the version-12 configuration, rolls the work order back under a new authorised action or withholds release. The loop changed the meeting from a vote over green lights into a test of whether the lights describe the same world.

The strongest boundary: coherence is not truth

A perfectly glued map can still be wrong. Three sensors may share the same calibration fault. Three services may copy a false master record. A human team may reach coherent agreement through a common but mistaken assumption. Sheaf consistency detects incompatibility relative to supplied spaces and maps. It does not prove that the spaces represent the world adequately, that the maps are causally justified or that an omitted variable is harmless.

This boundary is not a minor disclaimer. It determines the assurance architecture. A gluing gate should sit beside, not replace, provenance checks, independent measurement, causal validation, adversarial testing and outcome readback. The method is most valuable where local descriptions are heterogeneous and their joins carry risk. It is less informative when all sources are generated by one pipeline, when the cover has no meaningful overlaps, when the relevant relation is intentionally non-deterministic or when the global state changes faster than the observations can be aligned.

There is also a mathematical limit. A chosen cohomological test may expose a particular obstruction without being complete for every form of contextuality. Non-vanishing cohomology can witness contextuality in the Abramsky programme, but vanishing of that obstruction does not universally prove a global model exists.[7] In engineered systems, the analogous discipline is to state which defect the test can reveal, then keep rival generators for false reassurance.

Where not to apply the tool

Do not use the scalar affine lab as a generic truth engine, a consciousness test, a moral-status score or a substitute for domain modelling. It assumes fixed nodes, additive edge translations, declared weights and a snapshot-like state. Nonlinear, probabilistic, temporal or higher-dimensional systems need richer stalks, maps and diagnostics. The teaching artefact is useful precisely because its assumptions are visible.

A hypothesis worth testing

The practitioner hypothesis is that gluing debt predicts integration failure better than pairwise agreement alone. Gluing debt is the unresolved combination of missing translations, non-closing cycles, decision-relevant ambiguity and weak stability at the moment a global state is asserted. It is not a theorem and it should not be converted into a universal score.

The smallest useful study would take a set of synthetic or historically replayable compound-system decisions. For each decision, record pairwise agreement, the GAR fields and whether replay reveals identity mismatch, stale authority, inconsistent state or unstable reconstruction. The hypothesis gains support if GAR defects forecast these failures after controlling for ordinary confidence and source count. It weakens if pairwise checks perform equally well, if the proposed witnesses do not localise repair or if most incidents arise outside the encoded boundary.

A rival explanation is simple data quality: the GAR may work only because it counts missing or inconsistent fields more carefully. To separate the accounts, compare it with a strong schema-validation baseline using the same inputs. Another rival is graph density: better-connected systems may both score well and fail less for reasons unrelated to sheaf structure. Match or model connectivity. Do not apply the hypothesis to domains where no stable candidate global state can be named. The first implementation should be a shadow-mode gate that records witnesses and decisions without blocking live work.

Open research hypothesis

Observation that would strengthen it: structural witnesses repeatedly identify the relation whose correction resolves replayed failures. Result that would weaken it: witnesses are unstable across equivalent encodings or add no predictive value beyond a strong typed-schema baseline. Rival explanation: common-cause data quality or graph connectivity. Smallest implementation: one decision type, three to six local sources, explicit versioned overlaps and offline replay.

What should change on Monday

Before a human or machine combines local outputs into a consequential claim, require a gluing record. Name the candidate world state. Show how each source restricts to shared fields. Preserve version, time, subject and authority in those maps. Test independent paths and cycles. Report ambiguity separately from residual. Scale the stability and recovery burden with irreversibility. Retain the smallest witness whenever the gate refuses a global state.

This changes the release question. Instead of asking, “Do the services agree?” ask, “Is there one decision-relevant state whose restrictions are these reports?” Instead of asking, “Can the discrepancy be averaged?” ask, “Does the discrepancy reveal a broken translation, a missing anchor or ordinary noise?” Instead of asking, “Is the score high enough?” ask, “What conclusion does this exact diagnostic permit, and what remains outside its scope?”

The bridge in the opening case did not move. The coordinate system did. The failure occurred because the join erased that fact. Sheaf thinking makes the seam part of the model. The decision this changes is whether local evidence may be promoted into a global state at all. Promote it only when the translations close, the remaining ambiguity cannot change the decision, the state is stable enough for the consequence and the action remains within authority.