using Pkg
Pkg.activate(joinpath(@__DIR__, ".."))
using FunctorFlowDemocritus Assembly
Local causal graph gluing, repaired global sections, and regrounding
Introduction
The basic democritus_assembly_pipeline block already exposes a sheaf-like local-to-global pattern: glue local claims into a global section, then map that global section back down to local fragments. This vignette adds the missing piece for a fuller CATAGI-style story: repair-aware assembly.
In the example below, local causal fragments come from three partially overlapping views of a labor-market story:
- a policy fragment,
- a household-demand fragment, and
- a labor-outcome fragment.
The assembly step glues them into a global causal graph and then infers repaired cross-fragment claims by taking a small transitive closure. The regrounding step pushes those repaired claims back into the local fragments whose focus vocabularies can express them.
Setup
Build the repaired assembly example
example = build_democritus_assembly_example()
println("Local fragments:")
for (fragment, claims) in example[:local_claims]
println(" ", fragment, " => ", sort(collect(claims)))
end
println("\nFragment focus vocabularies:")
for (fragment, focus) in example[:fragment_focus]
println(" ", fragment, " => ", sort(collect(focus)))
end
println("\nOverlap cover:")
for (region, fragments) in example[:overlap_relation]
println(" ", region, " => ", fragments)
endLocal fragments:
policy => ["minimum wage -> earnings", "minimum wage -> labor costs"]
labor => ["employment -> job quality", "labor costs -> employment"]
household => ["demand -> employment", "earnings -> demand"]
Fragment focus vocabularies:
policy => ["earnings", "employment", "labor costs", "minimum wage"]
labor => ["employment", "job quality", "labor costs", "minimum wage"]
household => ["demand", "earnings", "employment", "minimum wage"]
Overlap cover:
policy_labor => [:policy, :labor]
policy_household => [:policy, :household]
labor_market => [:policy, :household, :labor]
This example uses a custom gluing reducer, democritus_repair_reducer, rather than a plain set union. The reducer first glues the local claims and then adds repaired claims such as minimum wage -> employment that appear only after joining multiple local pieces.
Execute the assembly
result = execute_democritus_assembly_example(example)
summary = summarize_democritus_assembly_example(example)
println("Counts: ", summary["counts"])
println("Coherence / repair loss: ", summary["coherence_loss"])Counts: Dict("repaired_claims" => 5, "global_claims" => 11, "seed_claims" => 6, "local_fragments" => 3)
Coherence / repair loss: 0.4
The assembled global section is keyed by overlap region. In this example the main region is :labor_market.
global_sections = result.values[example[:diagram].ports[:global_output].ref]
println("Global labor-market section:")
for claim in sort(collect(global_sections[:labor_market]))
println(" ", claim)
endGlobal labor-market section:
demand -> employment
demand -> job quality
earnings -> demand
earnings -> employment
employment -> job quality
labor costs -> employment
labor costs -> job quality
minimum wage -> demand
minimum wage -> earnings
minimum wage -> employment
minimum wage -> labor costs
The repaired claims are exactly the claims that appear globally but were absent from the original local fragments.
println("Inferred global repairs:")
for claim in summary["global_inferred_claims"]
println(" ", claim)
endInferred global repairs:
demand -> job quality
earnings -> employment
labor costs -> job quality
minimum wage -> demand
minimum wage -> employment
Reground repairs back to local fragments
The regrounding map sends repaired global claims back into any local fragment whose focus vocabulary contains both endpoints of the claim. That turns the global section into a source of local repair proposals.
println("Fragment-level repairs:")
for (fragment, repairs) in sort(collect(summary["fragment_repairs"]); by=first)
println(" ", fragment, " => ", repairs)
endFragment-level repairs:
household => ["earnings -> employment", "minimum wage -> demand", "minimum wage -> earnings", "minimum wage -> employment"]
labor => ["labor costs -> job quality", "minimum wage -> employment", "minimum wage -> labor costs"]
policy => ["earnings -> employment", "labor costs -> employment", "minimum wage -> employment"]
Notice that the repair loss is non-zero: the regrounded local fragments now contain repaired claims that were not present in the original local sections. That is exactly the signal we want if we interpret Democritus assembly as a local-to-global consistency-and-repair procedure.
Inspect the diagram itself
The example still compiles down to an ordinary FunctorFlow diagram, so it can be inspected, adapted, or replaced with learned morphisms later.
diagram = example[:diagram]
println("Diagram name: ", diagram.name)
println("Ports: ", collect(keys(diagram.ports)))
println("Operations: ", collect(keys(diagram.operations)))
println("Losses: ", collect(keys(diagram.losses)))Diagram name: DemocritusCausalAssembly
Ports: [:input, :relation, :global_output, :regrounded_claims, :coherence_loss]
Operations: [:glue__assemble_global_section, :reground_repaired_claims]
Losses: [:section_repair_loss]
The key categorical pieces are:
- a right Kan gluing operation that builds the global section;
- a regrounding morphism back into local claim space; and
- a section repair loss that measures how much local repair was needed.
Why this matters
This dedicated example pushes the Democritus block beyond a schematic macro:
- it uses a concrete local causal graph story;
- it exposes repaired claims that only appear after global assembly; and
- it shows how those repairs can be propagated back to the local fragments that need them.
That makes the block suitable as a template for multi-document causal synthesis, fragmentary world-model repair, and other local-to-global assembly workflows.