Cohomological Obstructions
When sheaf descent fails, JuGeo classifies the failure as a cohomological obstruction. The degree of the obstruction — H⁰, H¹, H², or H∞ — determines what kind of repair is possible and how hard it is.
Cohomology Classification
JuGeo uses the language of Čech cohomology on the semantic site \((\mathcal{C}, J)\) to classify descent failures. Given a covering family \(\mathcal{U} = \{U_i \to U\}_{i \in I}\) and the judgment sheaf \(\mathcal{F}\), the Čech complex is:
The cohomology groups \(\check{H}^n(\mathcal{U}, \mathcal{F})\) measure qualitatively different kinds of failure. JuGeo computes an approximation of these groups and assigns one of four obstruction degrees to each descent failure. Each degree corresponds to a distinct structural problem that requires a different remediation strategy.
Missing Local Section
At least one element \(U_i\) of the cover has no valid local section. The property does not hold locally at some piece of the code. This is the most common and most directly fixable obstruction — repair or annotate the implementation at \(U_i\).
Compatibility Failure
All local sections exist, but they disagree on at least one intersection \(U_i \times_U U_j\). The 1-cochain of mismatches is a cocycle but not a coboundary — a genuine global obstruction. Requires interface reconciliation between two components.
Higher Coherence Failure
Local sections and pairwise compatibilities both exist, but triple intersections \(U_{ijk}\) fail to cohere. Arises from three-way interface conflicts; rare in practice but common in heavily parametric code (typeclass hierarchies, multiple inheritance chains).
Undecidable Obstruction
The compatibility condition reduces to an undecidable problem (e.g.,
general program equivalence or the halting problem). JuGeo cannot compute
the cohomology group and classifies the obstruction as H∞.
Only FORMAL_PROOF evidence or manual annotation can resolve it.
Practical frequency. In the JuGeo evaluation benchmark H⁰ obstructions account for approximately 72% of failures, H¹ for 24%, H² for 3%, and H∞ for 1%. The overwhelmingly common case is a missing local section — a function that simply does not satisfy the required property. Most automated repair can therefore focus on H⁰ cases.
Obstruction Class Fields
Every obstruction is represented as an ObstructionClass dataclass.
The fields encode both the mathematical data (which degree, which cochain) and
the engineering data (source locations, repair hints, backpressure score) needed
to present actionable diagnostics to the developer.
| Field | Type | Description |
|---|---|---|
| degree | ObstructionDegree | H0, H1, H2, or HINF. Determines which repair strategies apply. |
| coordinate | Coordinate | The object \(U\) where descent was attempted (e.g., a module or class). |
| cover | list[Coordinate] | The covering family \(\{U_i\}\) used for the descent attempt. |
| failing_indices | list[int] | list[tuple[int,int]] | Indices into cover of the failing local sections (H0) or pairs (H1/H2). |
| cochain | Cochain | The raw Čech cochain data: maps each failing index to a mismatch formula. |
| is_cocycle | bool | Whether \(\delta c = 0\) holds. True indicates a genuine cohomological obstruction; false means it may be resolvable by cover refinement. |
| representative | Formula | None | A canonical representative formula for the obstruction class, if computable. |
| repair_hints | list[RepairHint] | Ordered list of suggested code changes to resolve the obstruction, with source locations. |
| backpressure_score | float | Estimated propagation severity through the site, in [0.0, 1.0]. Higher means more downstream judgments are affected. |
| evidence_gaps | list[EvidenceGap] | Evidence channels that could supply a section but have not been queried yet. |
Constructing and inspecting an obstruction manually:
from jugeo.geometry.descent import (
CohomologyClass,
DescentObstruction,
OverlapCondition,
OverlapStatus,
RepairFrontier,
)
obs = DescentObstruction(
coordinate="mymod.payments",
violated_overlaps=(
OverlapCondition(
left_coordinate="mymod.payments.validate",
right_coordinate="mymod.payments.charge",
overlap_coordinate="mymod.payments.validate∩mymod.payments.charge",
status=OverlapStatus.VIOLATED,
),
),
repair_frontier=RepairFrontier(
missing_evidence=("spec:payments-charge",),
weakened_claims=("align validate/charge currency assumptions",),
),
cohomology_class=CohomologyClass(
cocycle_data={"validate∩charge": {"currency": {"left": "USD", "right": "EUR"}}}
),
)
print(obs.violation_count)
print(obs.violated_pairs())
print(obs.cohomology_class.cocycle_data)
Repair Frontiers
A repair frontier is the minimal set of code locations that, if modified, would eliminate a given obstruction. JuGeo computes repair frontiers by working backward from the obstruction through the antecedent DAG of judgments — finding the earliest points where an intervention would propagate forward and resolve the mismatch.
For H⁰ obstructions:
- The frontier is the set of functions or classes that lack the required local section.
- JuGeo suggests adding or strengthening a postcondition annotation, or fixing the implementation to satisfy the required property.
- Minimum effort: tighten the local claim, re-run
jugeo prove, and inspect the new obstruction data.
For H¹ obstructions:
- The frontier is the set of call-sites where the caller's precondition disagrees with the callee's postcondition at an intersection.
- JuGeo suggests either: (a) strengthening the caller's precondition, (b) weakening the callee's postcondition, or (c) introducing a boundary adapter.
- The lowest-effort option is usually (a): restrict the inputs so both sides agree.
For H² obstructions:
- The frontier spans three components. JuGeo identifies which triple \((i, j, k)\) causes the triple intersection to fail and suggests a refactoring that separates the conflicting concerns.
Computing the frontier programmatically:
from jugeo.geometry.descent import RepairFrontier
frontier = RepairFrontier(
missing_evidence=("runtime trace:payments.validate",),
weakened_claims=("weaken charge() postcondition on accepted currencies",),
suggested_refinements=("split payments cover into auth/charge",),
estimated_cost=2.5,
)
print(frontier.total_items())
for category, item in frontier.prioritized_items():
print(category, item)
Backpressure System
An obstruction at a low-level utility function does not exist in isolation — every function that depends on that utility inherits an unresolved antecedent obligation. JuGeo's backpressure system quantifies this propagation by assigning a score \(\beta \in [0, 1]\) to each obstruction that measures how broadly its effects cascade through the semantic site.
Edge weights \(w(f)\) depend on the morphism kind: direct call edges carry
weight 1.0, import/module edges 0.5, and containment edges 0.8. Scores are
normalised to \([0, 1]\) across the full obstruction report and displayed in
the jugeo prove CLI output.
JuGeo recognises five canonical backpressure families, each suggesting a different response:
Score 0.0 – 0.1
Obstruction affects only one or two direct callers. Fix at leisure; low urgency.
Score 0.1 – 0.3
Affects a small local cluster. Fix before merging the affected module.
Score 0.3 – 0.6
Propagates across subsystem boundaries. Include in the current sprint.
Score 0.6 – 0.85
Blocks a large fraction of the module graph. Fix immediately to unblock other work.
Score 0.85 – 1.0
Obstructs a foundational utility used pervasively. All dependent judgments are partial until resolved.
Fix high-pressure obstructions first. When multiple obstructions exist, repair the ones with the highest backpressure score first. Fixing a single CRITICAL obstruction (e.g., in a widely-called utility) can eliminate dozens of downstream partial judgments in one stroke.
Python Examples
Querying the obstruction report for a module:
from jugeo.geometry.covers import Cover
from jugeo.geometry.descent import DescentEngine
from jugeo.geometry.site import Coordinate, CoordinateKind
cover = Cover(
target=Coordinate("payments", kind=CoordinateKind.MODULE),
patches=(
Coordinate(("payments", "validate"), kind=CoordinateKind.FUNCTION),
Coordinate(("payments", "charge"), kind=CoordinateKind.FUNCTION),
),
overlaps=(("payments/validate", "payments/charge"),),
)
sections = {
"payments/validate": {"currency": "USD", "status": "ok"},
"payments/charge": {"currency": "EUR", "status": "ok"},
}
result = DescentEngine().run(cover, sections).to_descent_result()
obstruction = result.unwrap_obstruction()
print(obstruction.violation_count)
print(obstruction.violated_pairs())
print(obstruction.cohomology_class.cocycle_data)
Attempting automatic repair:
import subprocess
completed = subprocess.run(
["python3", "-m", "jugeo", "--format", "json", "repair", "payments.py"],
check=True,
capture_output=True,
text=True,
)
print(completed.stdout)
Inspecting the Čech cochain directly:
cohomology = obstruction.cohomology_class
print(cohomology.dimension)
print(cohomology.rank)
print(cohomology.is_trivial())
print(cohomology.cocycle_data)