Back to skills
SkillHub ClubShip Full StackFull Stack

dynamic-sufficiency

Causal state gating via ε-machine. Coworld observer that prevents action

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
10
Hot score
84
Updated
March 20, 2026
Overall rating
C3.8
Composite score
3.8
Best-practice grade
C61.1

Install command

npx @skill-hub/cli install plurigrid-asi-dynamic-sufficiency

Repository

plurigrid/asi

Skill path: skills/dynamic-sufficiency

Causal state gating via ε-machine. Coworld observer that prevents action

Open repository

Best for

Primary workflow: Ship Full Stack.

Technical facets: Full Stack.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: plurigrid.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install dynamic-sufficiency into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/plurigrid/asi before adding dynamic-sufficiency to shared team environments
  • Use dynamic-sufficiency for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: dynamic-sufficiency
description: Causal state gating via ε-machine. Coworld observer that prevents action
version: 1.0.0
---


# Dynamic Sufficiency Skill

## World/Coworld Awareness

| Role | Skill | Function |
|------|-------|----------|
| **World** (+1) | gay-mcp | Generates deterministic color streams |
| **Coordinator** (0) | skill-dispatch | Routes to GF(3) triads |
| **Coworld** (-1) | dynamic-sufficiency | **THIS SKILL** - gates action on coverage |

> *"No action without sufficient witness. The ε-machine observes, the gate permits."*

## Skills as World-Generating Self-Improvising Memories

```
                    ┌─────────────────────────────────────┐
                    │     AUTOPOIETIC SKILL LOOP         │
                    └─────────────────────────────────────┘
                                     │
                    ┌────────────────┼────────────────┐
                    ▼                ▼                ▼
              ┌──────────┐    ┌──────────┐    ┌──────────┐
              │ dynamic- │    │  skill-  │    │  skill-  │
              │sufficiency│    │ dispatch │    │installer │
              │  MINUS   │◀──▶│  ERGODIC │◀──▶│   PLUS   │
              │   (-1)   │    │    (0)   │    │   (+1)   │
              └────┬─────┘    └────┬─────┘    └────┬─────┘
                   │               │               │
                   ▼               ▼               ▼
              ┌──────────┐    ┌──────────┐    ┌──────────┐
              │  GATE    │    │  ROUTE   │    │  LOAD    │
              │ action   │    │ to triad │    │  skills  │
              └────┬─────┘    └────┬─────┘    └────┬─────┘
                   │               │               │
                   └───────────────┼───────────────┘
                                   ▼
                         ┌──────────────────┐
                         │   WORLD MEMORY   │
                         │  (ε-machine +    │
                         │   observations)  │
                         └────────┬─────────┘
                                  │
                    ┌─────────────┼─────────────┐
                    ▼             ▼             ▼
              ┌──────────┐  ┌──────────┐  ┌──────────┐
              │ Observe  │  │  Learn   │  │ Improve  │
              │ outcome  │  │  domain  │  │  world   │
              │          │  │ mappings │  │  model   │
              └──────────┘  └──────────┘  └──────────┘
```

### Core Insight

Skills are not static knowledge. They are:

1. **WORLD-GENERATING**: Each skill generates a local world model for its domain
2. **SELF-IMPROVISING**: Skills learn from observations via ε-machine updates  
3. **MEMORIES**: Crystallized patterns of successful action that persist and evolve

The sufficiency triad forms a closed **autopoietic loop**:
```
dynamic-sufficiency (-1) ⊗ skill-dispatch (0) ⊗ skill-installer (+1) = 0 ✓
```

### Variational Bound on Action

```
min(sufficiency) ≤ action ≤ max(fanout)
```

- **dynamic-sufficiency** GATES: Prevents action without skills (lower bound)
- **max-fanout-gadget** FANS OUT: Maximizes parallel action (upper bound)

Together they form a variational bound ensuring both safety and maximum parallelism.

> *"The ε-machine is the minimal model sufficient to statistically reproduce the observed data."*
> — Crutchfield & Young, Santa Fe Institute

**Version**: 1.0.0  
**Trit**: -1 (MINUS - Validator/Gatekeeper)  
**Core Principle**: **Never undertake an action without verified skill sufficiency**

---

## Theoretical Foundation (Santa Fe Institute)

This skill implements **Computational Mechanics** from the Santa Fe Institute to ensure agents never act without sufficient capabilities:

### 1. Causal States (Crutchfield-Young)

**Definition**: Causal states partition the space of possible tasks into equivalence classes where:

```
Task T₁ ~ Task T₂  ⟺  Pr(Success | Skills, T₁) = Pr(Success | Skills, T₂)
```

Two tasks are equivalent if they require the **same skill profile** for successful completion.

### 2. ε-Machine (Minimal Sufficient Model)

The **ε-machine** is the minimal set of skills required for optimal task execution:

```
ε-machine: S → S × Skills
```

Where:
- **S** = Set of causal states (task equivalence classes)
- **Skills** = Skill symbols required for state transitions
- The ε-machine is **minimal** and **sufficient**

### 3. Effective Complexity (Gell-Mann-Lloyd)

```
Effective Complexity Y = K(regularities)
                       = Total AIC - Shannon Entropy of incidentals

Skill Complexity = min{ |Skills| : Skills sufficient for task class T }
```

### 4. Predictive Information (Bialek-Nemenman-Tishby)

```
I_pred = Information from past → future
       = I[Task History : Task Success | Loaded Skills]

For K-dimensional skill space:
  I_pred ≈ (K/2) log N
```

The **dimension K** of the skill space determines predictive sufficiency.

---

## Sufficiency Verification Protocol

### Pre-Action Gate

**MANDATORY**: Before ANY action, the agent MUST verify sufficiency:

```python
def pre_action_gate(action: Action, loaded_skills: Set[Skill]) -> Verdict:
    """
    Gate that prevents action without sufficient skills.
    
    Returns:
        PROCEED: Sufficient skills loaded
        LOAD_MORE: Specific skills needed
        ABORT: Insufficient and unrecoverable
    """
    required = infer_required_skills(action)
    coverage = compute_coverage(required, loaded_skills)
    
    if coverage.is_sufficient():
        return Verdict.PROCEED
    
    missing = coverage.missing_skills()
    if can_dynamically_load(missing):
        return Verdict.LOAD_MORE(missing)
    
    return Verdict.ABORT(reason=f"Missing critical skills: {missing}")
```

### Causal State Inference

```python
class CausalStateInference:
    """Infer causal state (task class) from action specification."""
    
    def __init__(self):
        self.state_cache = {}  # Memoize state assignments
        
    def infer_state(self, action: Action) -> CausalState:
        """
        Partition action into equivalence class based on skill requirements.
        
        Uses hierarchical features:
        1. Domain (code, data, web, system)
        2. Operation type (read, write, transform, verify)
        3. Complexity class (O(1), O(n), O(n²), etc.)
        4. Tool requirements (bash, read, edit, mcp)
        """
        features = self.extract_features(action)
        return CausalState(
            domain=features.domain,
            operation=features.operation,
            complexity=features.complexity,
            tool_profile=features.tools,
            skill_signature=self.skill_signature(features)
        )
    
    def skill_signature(self, features) -> Tuple[str, ...]:
        """Canonical skill tuple for this causal state."""
        return tuple(sorted(features.required_skills))
```

### ε-Machine Construction

```python
class EpsilonMachine:
    """
    Minimal sufficient model for task → skill mapping.
    
    Properties:
    - Minimal: No redundant states
    - Sufficient: All information for prediction preserved
    - Unique: Up to isomorphism
    """
    
    def __init__(self, skill_registry: SkillRegistry):
        self.states: Dict[CausalState, Set[Skill]] = {}
        self.transitions: Dict[(CausalState, Action), CausalState] = {}
        self.registry = skill_registry
        
    def add_observation(self, action: Action, skills_used: Set[Skill], success: bool):
        """Learn from observed action-skill-outcome triples."""
        state = self.infer_state(action)
        
        if success:
            # These skills were sufficient for this state
            if state not in self.states:
                self.states[state] = set()
            self.states[state].update(skills_used)
        else:
            # Mark state as requiring additional skills
            self.states[state].add(INSUFFICIENT_MARKER)
    
    def minimal_sufficient_skills(self, action: Action) -> Set[Skill]:
        """Return minimal skill set sufficient for action."""
        state = self.infer_state(action)
        
        if state in self.states:
            return self.states[state] - {INSUFFICIENT_MARKER}
        
        # Infer from similar states
        similar = self.find_similar_states(state)
        return self.intersection_of_skills(similar)
    
    def statistical_complexity(self) -> float:
        """
        C_μ = -Σ p(s) log p(s)
        
        The entropy of the causal state distribution.
        Higher = more complex task space.
        """
        state_counts = Counter(self.states.keys())
        total = sum(state_counts.values())
        probs = [c / total for c in state_counts.values()]
        return -sum(p * log2(p) for p in probs if p > 0)
```

---

## Skill Coverage Metrics

### Fisher Information Metric

The **Fisher metric** measures how distinguishable skill configurations are:

```python
def fisher_metric(skill_config_1: Set[Skill], 
                  skill_config_2: Set[Skill],
                  task_distribution: Distribution) -> float:
    """
    g(θ₁, θ₂) = E[(∂log p / ∂θ₁)(∂log p / ∂θ₂)]
    
    Measures information-geometric distance between skill configurations.
    """
    # Symmetric difference weighted by task frequency
    diff = skill_config_1.symmetric_difference(skill_config_2)
    
    weighted_distance = sum(
        task_distribution[skill] * skill.information_content
        for skill in diff
    )
    
    return weighted_distance
```

### Sufficiency Invariance

A skill configuration is **sufficient** if:

```
I(Task; Outcome | Skills) = I(Task; Outcome | All_Skills)
```

The loaded skills capture all predictive information about success.

### Coverage Score

```python
def coverage_score(action: Action, loaded_skills: Set[Skill]) -> CoverageResult:
    """
    Compute sufficiency coverage for an action.
    
    Returns:
        score: 0.0 (insufficient) to 1.0 (fully sufficient)
        missing: List of missing skills with priority
        excess: Skills loaded but not needed
    """
    required = epsilon_machine.minimal_sufficient_skills(action)
    
    covered = loaded_skills & required
    missing = required - loaded_skills
    excess = loaded_skills - required
    
    # Weight by skill criticality
    covered_weight = sum(s.criticality for s in covered)
    total_weight = sum(s.criticality for s in required)
    
    score = covered_weight / total_weight if total_weight > 0 else 1.0
    
    return CoverageResult(
        score=score,
        is_sufficient=(score >= SUFFICIENCY_THRESHOLD),
        missing=sorted(missing, key=lambda s: -s.criticality),
        excess=excess
    )
```

---

## Task → Skill Mapping (ε-Machine States)

### Domain-Specific Causal States

| Causal State | Required Skills | Trit Sum |
|--------------|-----------------|----------|
| `code:haskell:mcp` | `[ghc, mcp-builder, gay-mcp]` | 0 |
| `code:julia:acset` | `[julia-gay, acsets, specter-acset]` | 0 |
| `code:clojure:repl` | `[babashka, cider-clojure, clj-kondo-3color]` | 0 |
| `verify:spi` | `[spi-parallel-verify, polyglot-spi, bisimulation-game]` | 0 |
| `web:scrape` | `[firecrawl, exa, read-web-page]` | N/A |
| `file:transform` | `[read, edit_file, create_file]` | N/A |

### GF(3) Conservation in Skill Loading

Skills are loaded in **triads** to maintain GF(3) = 0:

```
MINUS (-1): Validators (spi-parallel-verify, polyglot-spi)
ERGODIC (0): Coordinators (gay-mcp, triad-interleave)
PLUS (+1): Generators (unworld, topos-generate)

Loading constraint: Σ trit(skill) ≡ 0 (mod 3)
```

---

## Implementation

### Sufficiency Gate Decorator

```python
from functools import wraps
from typing import Callable, Set

SUFFICIENCY_THRESHOLD = 0.95

def require_sufficiency(min_coverage: float = SUFFICIENCY_THRESHOLD):
    """
    Decorator that gates function execution on skill sufficiency.
    
    Usage:
        @require_sufficiency(min_coverage=0.9)
        def complex_action(params):
            ...
    """
    def decorator(func: Callable):
        @wraps(func)
        def wrapper(*args, **kwargs):
            # Infer action from function signature
            action = Action.from_callable(func, args, kwargs)
            
            # Get currently loaded skills
            loaded = get_loaded_skills()
            
            # Check coverage
            coverage = coverage_score(action, loaded)
            
            if not coverage.is_sufficient:
                # Attempt dynamic loading
                for skill in coverage.missing:
                    if can_load(skill):
                        load_skill(skill)
                
                # Recheck
                coverage = coverage_score(action, get_loaded_skills())
                
                if not coverage.is_sufficient:
                    raise InsufficientSkillsError(
                        f"Cannot execute {func.__name__}: "
                        f"coverage={coverage.score:.2%}, "
                        f"missing={coverage.missing}"
                    )
            
            # Record in ε-machine for learning
            try:
                result = func(*args, **kwargs)
                epsilon_machine.add_observation(action, loaded, success=True)
                return result
            except Exception as e:
                epsilon_machine.add_observation(action, loaded, success=False)
                raise
        
        return wrapper
    return decorator
```

### Pre-Message Hook

```python
class SufficiencyHook:
    """
    Hook that runs before every agent message.
    
    Ensures sufficient skills are loaded before ANY action.
    """
    
    def __init__(self, epsilon_machine: EpsilonMachine):
        self.em = epsilon_machine
        self.skill_loader = SkillLoader()
    
    def pre_message(self, message: str, context: Context) -> PreMessageResult:
        """
        Analyze message and ensure sufficiency before processing.
        """
        # 1. Infer likely actions from message
        predicted_actions = self.predict_actions(message, context)
        
        # 2. Compute unified skill requirement
        required_skills = set()
        for action in predicted_actions:
            required_skills.update(
                self.em.minimal_sufficient_skills(action)
            )
        
        # 3. Check current coverage
        loaded = self.skill_loader.get_loaded()
        coverage = self.compute_coverage(required_skills, loaded)
        
        # 4. Dynamic loading if needed
        if not coverage.is_sufficient:
            to_load = self.prioritize_loading(coverage.missing)
            for skill in to_load:
                self.skill_loader.load(skill)
            
            # Update coverage
            loaded = self.skill_loader.get_loaded()
            coverage = self.compute_coverage(required_skills, loaded)
        
        return PreMessageResult(
            proceed=coverage.is_sufficient,
            loaded_skills=loaded,
            coverage=coverage,
            causal_state=self.em.infer_state(predicted_actions[0]) if predicted_actions else None
        )
    
    def predict_actions(self, message: str, context: Context) -> List[Action]:
        """
        Predict what actions the message will require.
        
        Uses:
        - Keyword extraction (e.g., "create file" → file:write)
        - Context analysis (e.g., .hs file → code:haskell)
        - History patterns (e.g., previous similar messages)
        """
        actions = []
        
        # Keyword-based inference
        keywords = {
            'create': Action(operation='write'),
            'edit': Action(operation='transform'),
            'read': Action(operation='read'),
            'verify': Action(operation='verify'),
            'test': Action(operation='verify'),
            'search': Action(operation='search'),
            'web': Action(domain='web'),
            'haskell': Action(domain='code', language='haskell'),
            'julia': Action(domain='code', language='julia'),
            'mcp': Action(tool='mcp'),
            'gay': Action(skill='gay-mcp'),
            'spi': Action(skill='spi-parallel-verify'),
        }
        
        message_lower = message.lower()
        for keyword, action_template in keywords.items():
            if keyword in message_lower:
                actions.append(action_template)
        
        return actions or [Action(operation='general')]
```

---

## Sufficiency Violation Handling

### Violation Levels

| Level | Coverage | Response |
|-------|----------|----------|
| **CRITICAL** | < 50% | ABORT: Refuse to act |
| **WARNING** | 50-80% | LOAD: Attempt dynamic loading |
| **ADVISORY** | 80-95% | PROCEED: Note missing skills |
| **SUFFICIENT** | ≥ 95% | PROCEED: Full capability |

### Error Messages

```python
class InsufficientSkillsError(Exception):
    """Raised when action cannot proceed due to missing skills."""
    
    def __init__(self, action: Action, coverage: CoverageResult):
        self.action = action
        self.coverage = coverage
        
        msg = f"""
╔══════════════════════════════════════════════════════════════════╗
║  SUFFICIENCY VIOLATION                                           ║
╠══════════════════════════════════════════════════════════════════╣
║  Action: {action.summary():<54} ║
║  Coverage: {coverage.score:.1%} (required: ≥95%)                        ║
║                                                                  ║
║  Missing Skills:                                                 ║
"""
        for skill in coverage.missing[:5]:
            msg += f"║    • {skill.name:<56} ║\n"
        
        msg += """║                                                                  ║
║  Resolution:                                                     ║
║    1. Load missing skills: skill load {missing}                  ║
║    2. Use alternative approach with loaded skills                ║
║    3. Request human guidance                                     ║
╚══════════════════════════════════════════════════════════════════╝
"""
        super().__init__(msg)
```

---

## Integration with GF(3) Triadic System

### Skill Triad Completion

When loading skills for sufficiency, complete triads for GF(3) conservation:

```python
def complete_triad(skills_to_load: Set[Skill]) -> Set[Skill]:
    """
    Add skills to complete GF(3) = 0 triads.
    
    Example:
        Input: {spi-parallel-verify (-1), gay-mcp (+1)}
        Output: {spi-parallel-verify (-1), triad-interleave (0), gay-mcp (+1)}
    """
    current_sum = sum(s.trit for s in skills_to_load) % 3
    
    if current_sum == 0:
        return skills_to_load
    
    # Find complementary skill
    needed_trit = (3 - current_sum) % 3 - 1  # Map to {-1, 0, +1}
    
    complementary = find_skill_with_trit(needed_trit)
    return skills_to_load | {complementary}
```

### Sufficiency Triad

The core sufficiency verification triad:

```
dynamic-sufficiency (-1) ⊗ skill-dispatch (0) ⊗ skill-loader (+1) = 0 ✓
```

---

## Commands

```bash
# Check sufficiency for an action
just sufficiency-check action="create haskell mcp server"

# Show ε-machine state
just sufficiency-epsilon

# Compute statistical complexity
just sufficiency-complexity

# Verify GF(3) conservation in loaded skills
just sufficiency-gf3

# Run full sufficiency audit
just sufficiency-audit
```

---

## Configuration

```yaml
# .sufficiency.yaml
sufficiency:
  threshold: 0.95
  
  # Violation responses
  violations:
    critical:
      threshold: 0.50
      response: abort
    warning:
      threshold: 0.80
      response: load_and_retry
    advisory:
      threshold: 0.95
      response: proceed_with_note
  
  # ε-machine learning
  epsilon_machine:
    learn_from_failures: true
    state_cache_ttl: 3600
    
  # GF(3) enforcement
  gf3:
    enforce_triads: true
    auto_complete: true
```

---

## Mathematical Appendix

### Theorem: Minimal Sufficient Skill Set

For any task T with skill requirement function R(T), the ε-machine produces a skill set S* such that:

1. **Sufficiency**: P(Success | S*) = P(Success | All Skills)
2. **Minimality**: ∀ S' ⊂ S*: P(Success | S') < P(Success | S*)
3. **Uniqueness**: S* is unique up to isomorphism

### Proof Sketch

By the Fisher-Neyman factorization theorem, S* is sufficient iff:

```
P(Task | Skills) = h(Task) × g(R(Task), S*)
```

where h doesn't depend on outcome. The ε-machine construction ensures this factorization by partitioning tasks into causal states with identical conditional success probabilities.

---

---

## Narya Compatibility (ADMISSIBILITY REQUIREMENT)

### Effect Typing (GF(3))

| Operation | Trit | Justification |
|-----------|------|---------------|
| `pre_action_gate` | -1 (MINUS) | Read-only verification, gates action |
| `infer_state` | -1 (MINUS) | Classifies task, no mutation |
| `coverage_score` | -1 (MINUS) | Computes metric without side effects |
| `add_observation` | +1 (PLUS) | Updates ε-machine state |
| `load_skill` | +1 (PLUS) | Commits skill to loaded set |
| `complete_triad` | 0 (ERGODIC) | Coordinates GF(3) balance |

### Narya Log Schema

```yaml
narya:
  before: "hash(loaded_skills)"
  after: "hash(loaded_skills_post_gate)"
  delta: "skills_loaded_or_blocked"
  birth: "new_epsilon_machine_observations"
  impact_test: "sufficiency_threshold_crossed?"
```

### Proof Witness Generation

```python
def narya_witness(action, loaded_before, loaded_after, verdict):
    """
    Generate proof of sufficiency gate decision.
    """
    before_hash = hash(frozenset(loaded_before))
    after_hash = hash(frozenset(loaded_after))

    skills_added = loaded_after - loaded_before
    coverage_before = coverage_score(action, loaded_before)
    coverage_after = coverage_score(action, loaded_after)

    return NaryaWitness(
        before=before_hash,
        after=after_hash,
        delta={
            "coverage_delta": coverage_after.score - coverage_before.score,
            "skills_added": list(skills_added),
            "verdict": verdict.name
        },
        trit=-1,  # Gate is MINUS
        birth=skills_added if verdict == Verdict.PROCEED else set(),
        verified=coverage_after.is_sufficient
    )
```

---

## Invariants (ADMISSIBILITY REQUIREMENT)

```yaml
invariants:
  - name: sufficiency_threshold
    predicate: "action proceeds only if coverage >= 0.95"
    scope: per_action
    failure_mode: abort_or_load

  - name: epsilon_machine_minimal
    predicate: "ε-machine has no redundant states"
    scope: per_machine
    failure_mode: state_consolidation

  - name: causal_state_partition
    predicate: "tasks with same skill signature share causal state"
    scope: per_observation
    failure_mode: reclassify

  - name: gf3_triad_complete
    predicate: "loaded skills sum to 0 mod 3"
    scope: per_context
    failure_mode: complete_triad

  - name: statistical_complexity_bounded
    predicate: "C_μ <= log2(|Skills|)"
    scope: per_machine
    failure_mode: log_warning
```

---

## Fibers (ADMISSIBILITY REQUIREMENT)

```yaml
fibers:
  - name: causal_state_fiber
    base: "CausalState"
    projection: "infer_state(action)"

  - name: skill_requirement_fiber
    base: "Action"
    projection: "minimal_sufficient_skills(action)"

  - name: coverage_fiber
    base: "Action × LoadedSkills"
    projection: "coverage_score(action, skills)"

  - name: epsilon_machine_fiber
    base: "ε-Machine"
    projection: "states(machine)"

  - name: triad_fiber
    base: "SkillSet"
    projection: "partition_by_trit(skills)"
```

---

## MCP Lift (ADMISSIBILITY REQUIREMENT)

```yaml
lift:
  to_mcp:
    tool_name: "sufficiency_gate"
    params:
      - name: "action"
        type: "object"
        description: "Action specification {domain, operation, tools}"
      - name: "loaded_skills"
        type: "array"
        description: "Currently loaded skill names"
      - name: "threshold"
        type: "number"
        description: "Sufficiency threshold (default 0.95)"
    returns:
      type: "object"
      properties:
        verdict: "PROCEED | LOAD_MORE | ABORT"
        coverage: "Coverage score 0.0-1.0"
        missing_skills: "Skills needed for sufficiency"
        causal_state: "Inferred task equivalence class"
        narya_witness: "Proof witness"

  to_acset:
    schema: "SchEpsilonMachine"
    objects: [CausalState, Skill, Action]
    morphisms: [requires, transitions_to]
    attributes: [coverage_score, trit]

  to_olog:
    types: [Action, Skill, CausalState, Verdict]
    aspects: [requires, covers, gates]

descend:
  from_mcp: "parse gate verdict"
  from_acset: "extract skill requirement graph"
```

---

## Condensation Policy (ADMISSIBILITY REQUIREMENT)

```yaml
condensation:
  trigger: "num_causal_states > 100 or state_cache_stale"
  strategy: "merge_equivalent_states"
  pre_condensed:
    - "states with identical skill signatures"
    - "actions with coverage >= 1.0 (fully redundant)"
    - "expired cache entries (TTL exceeded)"
```

---

## Counterexamples

```python
# Insufficient coverage blocks action (expected behavior)
loaded = {"read", "edit"}  # Missing specialized skills
action = Action(domain="code", language="haskell", operation="mcp")

result = pre_action_gate(action, loaded)
assert result == Verdict.ABORT
# Coverage < 50% because haskell + mcp skills not loaded

# GF(3) triad incomplete (counterexample - should be prevented)
skills_unbalanced = {
    Skill("gay-mcp", trit=+1),
    Skill("spi-verify", trit=-1)
}
# Sum = 0, but missing ERGODIC for proper triad
# complete_triad() should add coordinator

# ε-machine learns from failure
epsilon_machine.add_observation(action, loaded, success=False)
# Next time, infer_state(action) → requires more skills
```

---

**Skill Name**: dynamic-sufficiency
**Type**: Pre-Action Verification Gate
**Trit**: -1 (MINUS - Validator)
**Color**: #2626D8 (Blue)
**GF(3) Triad**: `dynamic-sufficiency (-1) ⊗ skill-dispatch (0) ⊗ skill-loader (+1) = 0`
**SFI Foundation**: Computational Mechanics, Effective Complexity, Predictive Information
**Status**: ✅ ADMITTED (all 7 MUST requirements satisfied)



## Scientific Skill Interleaving

This skill connects to the K-Dense-AI/claude-scientific-skills ecosystem:

### Scientific Computing
- **scipy** [○] via bicomodule
  - Hub for numerical/scientific computation

### Bibliography References

- `dynamical-systems`: 41 citations in bib.duckdb

## Cat# Integration

This skill maps to **Cat# = Comod(P)** as a bicomodule in the equipment structure:

```
Trit: 0 (ERGODIC)
Home: Prof
Poly Op: ⊗
Kan Role: Adj
Color: #26D826
```

### GF(3) Naturality

The skill participates in triads satisfying:
```
(-1) + (0) + (+1) ≡ 0 (mod 3)
```

This ensures compositional coherence in the Cat# equipment structure.
dynamic-sufficiency | SkillHub