Asi parallel-subagent-split

Parallel Subagent Split Skill

install
source · Clone the upstream repo
git clone https://github.com/plurigrid/asi
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/plurigrid/asi "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/parallel-subagent-split" ~/.claude/skills/plurigrid-asi-parallel-subagent-split && rm -rf "$T"
manifest: skills/parallel-subagent-split/SKILL.md
source content

Parallel Subagent Split Skill

Categorical formalism for parallel agent decomposition with GF(3) conservation

Version: 1.0.0 Trit: +1 (PLUS - generative/splitting) Domain: distributed-systems, category-theory, agent-architecture


Overview

Parallel Subagent Split models the decomposition of a single agent into multiple parallel subagents using categorical constructs:

                    ┌─────────────┐
                    │   Agent     │
                    │  (unified)  │
                    └──────┬──────┘
                           │ split (+1)
           ┌───────────────┼───────────────┐
           ▼               ▼               ▼
    ┌──────────┐    ┌──────────┐    ┌──────────┐
    │ Agent₁   │    │ Agent₂   │    │ Agent₃   │
    │ trit:-1  │    │ trit:0   │    │ trit:+1  │
    └──────────┘    └──────────┘    └──────────┘
           │               │               │
           └───────────────┼───────────────┘
                           │ join (-1)
                           ▼
                    ┌─────────────┐
                    │   Result    │
                    │ (merged)    │
                    └─────────────┘

Three Categorical Models

1. Coproduct: Agent₁ + Agent₂ + Agent₃ (Parallel Split)

Semantics: Either/Or choice — exactly one agent handles each task.

        ι₁          ι₂          ι₃
Agent₁ ───→ Agent₁ + Agent₂ + Agent₃ ←─── Agent₃
                      ↑
                      │ι₂
                   Agent₂

Universal property: For any f₁, f₂, f₃ to X,
∃! [f₁, f₂, f₃]: Agent₁ + Agent₂ + Agent₃ → X
using Catlab.CategoricalAlgebra

@present SchAgentCoproduct(FreeSchema) begin
    Agent₁::Ob; Agent₂::Ob; Agent₃::Ob
    Coproduct::Ob
    
    ι₁::Hom(Agent₁, Coproduct)
    ι₂::Hom(Agent₂, Coproduct)
    ι₃::Hom(Agent₃, Coproduct)
    
    # Attribute for trit assignment
    Trit::AttrType
    trit₁::Attr(Agent₁, Trit)  # -1
    trit₂::Attr(Agent₂, Trit)  # 0
    trit₃::Attr(Agent₃, Trit)  # +1
end

Use Case: Task routing where only one subagent should handle each request.


2. Product: Agent₁ × Agent₂ × Agent₃ (Synchronized Join)

Semantics: AND combination — all agents must complete before merge.

                    Agent₁ × Agent₂ × Agent₃
                     /         |         \
                  π₁          π₂          π₃
                   ↓           ↓           ↓
                Agent₁     Agent₂     Agent₃

Universal property: For any g with maps to all agents,
∃! ⟨g₁, g₂, g₃⟩: X → Agent₁ × Agent₂ × Agent₃
@present SchAgentProduct(FreeSchema) begin
    Agent₁::Ob; Agent₂::Ob; Agent₃::Ob
    Product::Ob
    
    π₁::Hom(Product, Agent₁)
    π₂::Hom(Product, Agent₂)
    π₃::Hom(Product, Agent₃)
    
    # Synchronization barrier
    SyncState::AttrType
    sync::Attr(Product, SyncState)
end

Use Case: Consensus protocols where all subagents must agree.


3. Monoidal Tensor: Agent₁ ⊗ Agent₂ ⊗ Agent₃ (Independent Parallel)

Semantics: Truly independent execution — no shared context.

Agent₁ ⊗ Agent₂ ⊗ Agent₃

Properties:
- Associativity: (A ⊗ B) ⊗ C ≅ A ⊗ (B ⊗ C)
- Unit: I ⊗ A ≅ A ≅ A ⊗ I
- Braiding: A ⊗ B ≅ B ⊗ A (symmetric monoidal)
from discopy import Ty, Box, Diagram, Functor

# Types for agents
Agent1 = Ty('Agent₁')
Agent2 = Ty('Agent₂')  
Agent3 = Ty('Agent₃')
Result = Ty('Result')

# Tensor product = parallel composition
parallel_agents = Agent1 @ Agent2 @ Agent3

# Task boxes
task1 = Box('task₁', Ty(), Agent1)
task2 = Box('task₂', Ty(), Agent2)
task3 = Box('task₃', Ty(), Agent3)

# Parallel split
split = task1 @ task2 @ task3  # : I → Agent₁ ⊗ Agent₂ ⊗ Agent₃

# Merge box
merge = Box('merge', Agent1 @ Agent2 @ Agent3, Result)

# Full diagram: split >> merge
parallel_computation = split >> merge

Use Case: Embarrassingly parallel workloads with no inter-agent communication.


DisCoPy String Diagram Representation

"""
parallel_subagent_split.py - DisCoPy string diagrams for agent splitting
"""
from discopy import *
from discopy.monoidal import Ty, Box, Diagram, Id

# === Type Definitions ===
Task = Ty('Task')
Agent = Ty('Agent')
Result = Ty('Result')

# Trit-labeled agent types
AgentMinus = Ty('A₋')   # trit = -1
AgentZero = Ty('A₀')    # trit = 0
AgentPlus = Ty('A₊')    # trit = +1

# === Splitting Operations ===

class Split(Box):
    """Split single task into three parallel agents."""
    def __init__(self):
        super().__init__(
            name='split',
            dom=Task,
            cod=AgentMinus @ AgentZero @ AgentPlus
        )
        self.trit = +1  # Generative operation

class Merge(Box):
    """Merge three agent results into one."""
    def __init__(self):
        super().__init__(
            name='merge',
            dom=AgentMinus @ AgentZero @ AgentPlus,
            cod=Result
        )
        self.trit = -1  # Consumptive operation

class Process(Box):
    """Individual agent processing with trit assignment."""
    def __init__(self, agent_type: Ty, trit: int):
        name = f'proc_{trit:+d}'
        super().__init__(name=name, dom=agent_type, cod=agent_type)
        self.trit = trit

# === GF(3) Conserving Diagram ===

def make_parallel_diagram():
    """
    Construct full parallel computation diagram.
    
    Task → [Split(+1)] → A₋ ⊗ A₀ ⊗ A₊ → [Merge(-1)] → Result
    
    GF(3) balance: +1 (split) + (-1 + 0 + 1) (agents) + (-1) (merge) = 0 ✓
    """
    # Split task into three
    split = Split()
    
    # Parallel processing (tensor product)
    proc_minus = Process(AgentMinus, -1)
    proc_zero = Process(AgentZero, 0)
    proc_plus = Process(AgentPlus, +1)
    parallel = proc_minus @ proc_zero @ proc_plus
    
    # Merge results
    merge = Merge()
    
    # Compose: split >> parallel >> merge
    return split >> parallel >> merge

def verify_gf3(diagram):
    """Verify GF(3) conservation for diagram."""
    total_trit = 0
    for box in diagram.boxes:
        if hasattr(box, 'trit'):
            total_trit += box.trit
    return total_trit % 3 == 0

# === Trace Operation (Feedback Loops) ===

class TracedAgent(Box):
    """Agent with feedback loop via trace."""
    def __init__(self, inner: Box, feedback_type: Ty):
        # Trace: (A ⊗ X → B ⊗ X) ↦ (A → B)
        self.inner = inner
        self.feedback = feedback_type
        super().__init__(
            name=f'Tr({inner.name})',
            dom=inner.dom,
            cod=inner.cod
        )
    
    def trace(self):
        """Compute traced diagram."""
        # Identity on feedback wire + inner computation
        return self.inner  # Simplified; real trace uses cups/caps

def add_feedback_loop(diagram, feedback_state: Ty):
    """
    Add trace for stateful feedback.
    
    Trace turns: A ⊗ S → B ⊗ S  into  A → B
    where S is the state/feedback wire.
    """
    # Cup and cap for feedback
    cup = Box('cup', Ty(), feedback_state @ feedback_state)
    cap = Box('cap', feedback_state @ feedback_state, Ty())
    
    # Construct traced diagram
    # This models iterative refinement across subagent generations
    return TracedAgent(diagram, feedback_state)

# === Drawing ===

def draw_parallel_split():
    """Generate SVG of parallel split diagram."""
    diagram = make_parallel_diagram()
    diagram.draw(
        figsize=(10, 6),
        path='parallel_split.svg',
        fontsize=12
    )
    return diagram

# === Example Usage ===

if __name__ == "__main__":
    # Build diagram
    diagram = make_parallel_diagram()
    
    # Verify GF(3)
    assert verify_gf3(diagram), "GF(3) conservation violated!"
    print("✓ GF(3) conserved")
    
    # Show structure
    print(f"Domain: {diagram.dom}")
    print(f"Codomain: {diagram.cod}")
    print(f"Boxes: {[b.name for b in diagram.boxes]}")
    
    # Draw (if matplotlib available)
    try:
        draw_parallel_split()
        print("✓ Diagram saved to parallel_split.svg")
    except ImportError:
        print("⚠ matplotlib not available for drawing")

GF(3) Conservation Across Splits

Trit Accounting

OperationTritRole
Split+1Creates new agents (generative)
Agent₋-1Validator/checker role
Agent₀0Coordinator/neutral
Agent₊+1Generator/creator role
Merge-1Consumes agents (destructive)

Conservation Law

Σ(trits) ≡ 0 (mod 3)

Example:
  split(+1) + agent₋(-1) + agent₀(0) + agent₊(+1) + merge(-1)
= +1 + (-1) + 0 + (+1) + (-1)
= 0 ≡ 0 (mod 3) ✓

SplitMix64 Seed Distribution

def split_seeds(parent_seed: int, n_children: int = 3) -> list:
    """
    Deterministically derive child seeds from parent.
    Each child gets independent but reproducible stream.
    """
    GOLDEN = 0x9E3779B97F4A7C15
    seeds = []
    for i in range(n_children):
        child_seed = (parent_seed + GOLDEN * (i + 1)) & 0xFFFFFFFFFFFFFFFF
        seeds.append(child_seed)
    return seeds

# Trit assignment from seed
def seed_to_trit(seed: int) -> int:
    """Map seed to trit via modular arithmetic."""
    return (seed % 3) - 1  # → -1, 0, or +1

Trace Operation for Feedback Loops

Categorical Trace

In a traced monoidal category, the trace operation models feedback:

Tr_X : Hom(A ⊗ X, B ⊗ X) → Hom(A, B)

For agents: Tr_State(f) takes a stateful agent and produces a stateless one
by "looping" the state back.

Implementation

from discopy.ribbon import Ty, Box, Cup, Cap

State = Ty('State')  # Feedback wire

class IterativeAgent(Box):
    """Agent that refines via feedback loop."""
    
    def __init__(self, base_agent: Box, max_iterations: int = 3):
        self.base = base_agent
        self.max_iter = max_iterations
        super().__init__(
            name=f'Iter({base_agent.name})',
            dom=base_agent.dom,
            cod=base_agent.cod
        )
    
    def as_traced_diagram(self):
        """
        Construct traced diagram for iteration.
        
        Uses cup/cap to create feedback loop:
        
             ┌─────────────┐
         A ──┤             ├── B
             │   Agent     │
             │   ┌───┐     │
             │   │   │ S   │
             └───┴───┴─────┘
                 └───┘ (trace)
        """
        # Augment with state wire
        augmented = self.base  # base: A → B
        
        # Create feedback via cap-cup
        # cap: () → S ⊗ S
        # cup: S ⊗ S → ()
        
        return augmented  # Simplified

Feedback-Aware GF(3)

Trace preserves GF(3):
  If f: A ⊗ S → B ⊗ S has GF(3) = k
  Then Tr_S(f): A → B has GF(3) = k

The feedback wire S contributes 0 net trits (same in, same out).

Integration with Bisimulation Game

Equivalence Checking for Parallel Subagents

from bisimulation_game import BisimulationGame

class ParallelBisimulation:
    """
    Verify that different split strategies produce equivalent results.
    
    System 1: Coproduct split (task routing)
    System 2: Tensor split (independent parallel)
    
    Bisimilar iff no observer can distinguish outcomes.
    """
    
    def __init__(self, seed: int = 1069):
        self.seed = seed
        self.game = BisimulationGame(
            system1=self.coproduct_system,
            system2=self.tensor_system,
            seed=seed
        )
    
    def coproduct_system(self, task):
        """Route task to exactly one agent."""
        agent_id = hash(task) % 3
        return self.agents[agent_id].process(task)
    
    def tensor_system(self, task):
        """Process on all agents, combine results."""
        results = [a.process(task) for a in self.agents]
        return self.merge(results)
    
    def verify_equivalence(self, tasks: list) -> dict:
        """
        Play bisimulation game over task sequence.
        
        Returns verification report with GF(3) accounting.
        """
        log = []
        for task in tasks:
            # Attacker chooses system and makes move
            attacker_move = self.game.attacker_move("s1", task)
            
            # Defender responds
            defender_move = self.game.defender_respond(task)
            
            # Arbiter checks
            conserved = self.game.arbiter_verify()
            
            log.append({
                "task": task,
                "attacker_trit": attacker_move,
                "defender_trit": defender_move,
                "conserved": conserved
            })
        
        return {
            "rounds": len(log),
            "all_matched": all(r["conserved"] for r in log),
            "gf3_total": sum(r["attacker_trit"] + r["defender_trit"] for r in log) % 3,
            "log": log
        }

Bisimulation Diagram

╔═══════════════════════════════════════════════════════════════════╗
║           PARALLEL SPLIT BISIMULATION GAME                        ║
╠═══════════════════════════════════════════════════════════════════╣
║                                                                   ║
║  ROUND 1:                                                         ║
║  ┌─ ATTACKER (S₁: Coproduct) ────────────────────────────────────┐║
║  │ Route task T₁ to Agent₂ (trit: 0)                             │║
║  │ Transition: ι₂(T₁) in Agent₁ + Agent₂ + Agent₃                │║
║  └───────────────────────────────────────────────────────────────┘║
║                                                                   ║
║  ┌─ DEFENDER (S₂: Tensor) ───────────────────────────────────────┐║
║  │ Process T₁ on Agent₁ ⊗ Agent₂ ⊗ Agent₃ in parallel           │║
║  │ Merge results to match ι₂ behavior                            │║
║  │ Response: MATCHED ✓                                            ║
║  └───────────────────────────────────────────────────────────────┘║
║                                                                   ║
║  ┌─ ARBITER ─────────────────────────────────────────────────────┐║
║  │ GF(3): (-1) + 0 + (+1) = 0 ✓                                  │║
║  │ Observational equivalence: VERIFIED                           │║
║  └───────────────────────────────────────────────────────────────┘║
║                                                                   ║
╚═══════════════════════════════════════════════════════════════════╝

ACSet Schema for Agent Decomposition

using Catlab.CategoricalAlgebra
using StructuredDecompositions

@present SchParallelAgents(FreeSchema) begin
    # Objects
    Task::Ob
    Agent::Ob
    Result::Ob
    
    # Morphisms
    assigned_to::Hom(Task, Agent)
    produces::Hom(Agent, Result)
    merged_from::Hom(Result, Result)  # For composition
    
    # Attributes
    TritType::AttrType
    SeedType::AttrType
    StateType::AttrType
    
    trit::Attr(Agent, TritType)
    seed::Attr(Agent, SeedType)
    state::Attr(Agent, StateType)
end

@acset_type ParallelAgentGraph(SchParallelAgents, index=[:assigned_to, :produces])

function create_split(parent_seed::UInt64)
    """Create parallel agent graph with GF(3) balanced trits."""
    G = ParallelAgentGraph()
    
    # Create three agents
    child_seeds = split_seeds(parent_seed, 3)
    
    add_part!(G, :Agent, trit=-1, seed=child_seeds[1], state="ready")
    add_part!(G, :Agent, trit=0,  seed=child_seeds[2], state="ready")
    add_part!(G, :Agent, trit=+1, seed=child_seeds[3], state="ready")
    
    # Verify GF(3)
    @assert sum(G[:trit]) % 3 == 0 "GF(3) violated!"
    
    G
end

Structured Decomposition for Agent Hierarchies

using StructuredDecompositions

function decompose_agent_tree(root_task, max_depth::Int)
    """
    Build tree decomposition of agent hierarchy.
    
    Each level splits into 3 subagents.
    Adhesions track shared state between siblings.
    """
    
    # Shape graph: binary tree structure
    shape = StrDecomp.tree_shape(3, max_depth)
    
    # Functor: shape → Category of agent graphs
    function agent_functor(node)
        if is_leaf(node)
            return single_agent_graph(node.seed)
        else
            return split_graph(node.seed, 3)
        end
    end
    
    # Build structured decomposition
    decomp = StrDecomp(shape, agent_functor)
    
    # Verify: sheaf condition ensures consistent merge
    verify_sheaf_condition(decomp)
    
    decomp
end

GF(3) Synergistic Triads

# Parallel split participates in these balanced triads:

bisimulation-game (-1) ⊗ parallel-subagent-split (0) ⊗ triad-interleave (+1) = 0 ✓
structured-decomp (-1) ⊗ parallel-subagent-split (0) ⊗ cognitive-superposition (+1) = 0 ✓
unworld (-1) ⊗ parallel-subagent-split (0) ⊗ gh-interactome (+1) = 0 ✓
acsets-relational-thinking (-1) ⊗ parallel-subagent-split (0) ⊗ specter-acset (+1) = 0 ✓

Commands

# Create parallel split diagram
just parallel-split-diagram SEED N_AGENTS

# Verify GF(3) conservation
just parallel-split-gf3 SEED

# Run bisimulation equivalence check
just parallel-split-bisim SYSTEM1 SYSTEM2

# Generate ACSet for agent graph
just parallel-split-acset SEED

# Draw DisCoPy string diagram
python parallel_subagent_split.py

# Interleave with triad scheduler
just triad-interleave SEED N_TRIPLETS round_robin

Mathematical Summary

ConstructCategoryUniversal PropertyUse Case
Coproduct A₁ + A₂ + A₃SetUnique [f₁,f₂,f₃] to any targetTask routing
Product A₁ × A₂ × A₃SetUnique ⟨f₁,f₂,f₃⟩ from any sourceConsensus sync
Tensor A₁ ⊗ A₂ ⊗ A₃MonCatIndependent parallelEmbarrassingly parallel
Trace Tr_X(f)TracedMonCatFeedback loopIterative refinement

References

  • Patterson et al., "Categorical Data Structures for Technical Computing" (2022)
  • Bumpus et al., "Structured Decompositions" arXiv:2207.06091
  • Joyal, Street, Verity, "Traced Monoidal Categories" (1996)
  • de Felice et al., "DisCoPy: Monoidal Categories in Python" (2020)
  • Milner, "Communication and Concurrency" (1989) — bisimulation games

Skill Name: parallel-subagent-split Type: Distributed Agent Architecture Trit: +1 (PLUS - generative/splitting) GF(3): Conserved via trit accounting Dependencies: discopy, bisimulation-game, triad-interleave, acsets, structured-decomp