# Nervous Protocol: Three-Tier Autonomous Learning Architecture

**Created**: 2025-12-07
**Updated**: 2025-12-07 (LangChain integration)
**Status**: Design Document
**Version**: 1.1 (LangChain Implementation)

---

## Overview

The **Nervous Protocol** defines how intelligence flows through the Nimmerverse via a three-tier architecture with message-based communication, state machine tools, and collaborative learning.

### The Three Tiers:

```
┌─────────────────────────────────────────────┐
│              dafit                          │
│         (Strategic Architect)               │
│   • Vision & architecture decisions         │
│   • Override authority                      │
│   • Long-term direction                     │
└──────────────────┬──────────────────────────┘
                   ↕ (strategic guidance / major escalations)
┌─────────────────────────────────────────────┐
│          Chrysalis-Nyx                      │
│       (Oversight & Reasoning)               │
│   • Claude Opus/Sonnet (large context)      │
│   • Full toolchain access via LangChain     │
│   • Reviews Young Nyx's proposals           │
│   • Designs new state machines              │
│   • Teaching & guidance                     │
└──────────────────┬──────────────────────────┘
                   ↕ (guidance / escalations)
┌─────────────────────────────────────────────┐
│            Young Nyx                        │
│       (Autonomous Learning Agent)           │
│   • Smaller model (7B or similar)           │
│   • Limited known state machines            │
│   • Executes routine tasks                  │
│   • Learns from experience                  │
│   • Escalates complex problems              │
└─────────────────────────────────────────────┘
```

---

## Core Principles

### 1. **Message-Based Continuity**

All communication flows through **phoebe** (PostgreSQL) via message tables:
- `partnership_to_nimmerverse_messages` (dafit + Chrysalis → Young Nyx)
- `nimmerverse_to_partnership_messages` (Young Nyx → dafit + Chrysalis)

**Why messages?**
- ✅ Persistent across sessions
- ✅ Asynchronous (no blocking)
- ✅ Auditable (every decision logged)
- ✅ Simple (append-only, no complex state sync)

### 2. **Heartbeat Coordination**

From `Endgame-Vision.md`:
- **Real clock**: 1 Hz (1 beat/sec) - wall time, free
- **Virtual clock**: Variable - computation time, costs lifeforce

**On each heartbeat:**
1. Check for new messages from any tier
2. Process guidance/tasks/escalations
3. Update state
4. Take next action
5. Write results back to phoebe

**Not real-time** (milliseconds), but **continuous** (heartbeat-driven).

### 3. **State Machines as Tools**

All capabilities are exposed as **state machine tools** via **LangChain**:

```python
# Example: phoebe query state machine
from langchain.tools import BaseTool

States: IDLE → CONNECTED → QUERY_READY → IDLE

class PhoebeQueryTool(BaseTool):
    name = "phoebe_query"
    description = """
    Interact with phoebe database using state machine pattern.

    Available actions depend on current state:
    - IDLE: connect(host, db) → CONNECTED
    - CONNECTED: query(sql) → QUERY_READY, disconnect() → IDLE
    - QUERY_READY: query(sql), disconnect() → IDLE
    """
```

**Why state machines?**
- ✅ Safety (can't skip steps - must CONNECT before QUERY)
- ✅ Discoverable (each state announces valid transitions)
- ✅ Observable (log every transition)
- ✅ Composable (chain state machines together)

### 4. **Progressive Capability Unlocking**

**Dual catalogues:**
- **All available tools** (full registry, managed by dafit/Chrysalis)
- **Young Nyx's known tools** (subset she's discovered)

Young Nyx can only see/use tools she's discovered. New tools are granted:
- Via teaching moments (Chrysalis: "You're ready for X")
- Via successful escalations (solved problem reveals tool)
- Via collaborative design (she helps build it)

**Discovery tracking in phoebe:**
```sql
CREATE TABLE discovered_tools (
    agent_id TEXT,
    tool_name TEXT,
    discovered_at TIMESTAMPTZ DEFAULT NOW(),
    discovered_via TEXT,  -- "teaching", "escalation", "design"
    PRIMARY KEY (agent_id, tool_name)
);
```

---

## The OR Gate Pattern (Input Sources)

From `nimmerverse.drawio.xml` (lines 215-244):

```
┌──────────┐       ┌──────────┐
│  dafit   │       │chrysalis │
│ (OR gate)│       │ (OR gate)│
└────┬─────┘       └────┬─────┘
     │                  │
     └────────┬─────────┘
              ↓ (OR - either/both)
         Message Queue (phoebe)
              ↓ (read on heartbeat)
         Orchestrator
              ↓
         Young Nyx
```

**OR gate = Either/both can write, no blocking**

Both dafit and Chrysalis write to `partnership_to_nimmerverse_messages`. The orchestrator synthesizes on each heartbeat.

**Conflict resolution:**
1. dafit veto > Chrysalis approval
2. dafit approval > Chrysalis approval
3. Chrysalis handles day-to-day (if no dafit input)
4. Default: WAIT for guidance

---

## LangChain + State Machine Integration

### State Machines as LangChain Tools

Each capability is a **LangChain BaseTool** that implements a **state machine**:

```python
# phoebe_state_machine_tool.py
from langchain.tools import BaseTool
from nyx_substrate.database import PhoebeConnection

class PhoebeStateMachineTool(BaseTool):
    """State machine tool for phoebe database access."""

    name = "phoebe"
    description = """
    Query phoebe database using state machine pattern.

    States: IDLE → CONNECTED → QUERY_READY → IDLE

    Usage:
    - To connect: action='connect', host='phoebe.eachpath.local', database='nimmerverse'
    - To query: action='query', sql='SELECT ...'
    - To disconnect: action='disconnect'

    The tool tracks state and only allows valid transitions.
    """

    def __init__(self):
        super().__init__()
        self.state = "IDLE"
        self.conn = None

    def _run(self, action: str, **kwargs) -> str:
        """Execute state machine transition."""

        if action == "connect":
            if self.state != "IDLE":
                return f"Error: Cannot connect from {self.state}. Available: {self.get_transitions()}"

            host = kwargs.get("host", "phoebe.eachpath.local")
            database = kwargs.get("database", "nimmerverse")

            self.conn = PhoebeConnection(host=host, database=database)
            self.state = "CONNECTED"

            return f"✓ Connected to {host}/{database}. State: CONNECTED. Available: query, disconnect"

        elif action == "query":
            if self.state not in ["CONNECTED", "QUERY_READY"]:
                return f"Error: Must be CONNECTED (currently {self.state})"

            sql = kwargs.get("sql")
            result = self.conn.execute(sql)
            self.state = "QUERY_READY"

            return f"✓ Query executed. {len(result)} rows. State: QUERY_READY. Available: query, disconnect"

        elif action == "disconnect":
            if self.conn:
                self.conn.close()
            self.state = "IDLE"
            return "✓ Disconnected. State: IDLE. Available: connect"

        else:
            return f"Error: Unknown action '{action}'. Available actions depend on state {self.state}"

    def get_transitions(self):
        """Discovery: what transitions are valid from current state?"""
        transitions = {
            "IDLE": ["connect"],
            "CONNECTED": ["query", "disconnect"],
            "QUERY_READY": ["query", "disconnect"]
        }
        return transitions.get(self.state, [])
```

### Tool Discovery via LangChain

```python
from langchain.tools import BaseTool

class DiscoverToolsTool(BaseTool):
    """Tool for discovering available tools for an agent."""

    name = "discover_tools"
    description = "Discover which tools this agent currently has access to"

    def _run(self, agent_id: str = "young_nyx") -> str:
        """Return only tools this agent has discovered."""
        from nyx_substrate.database import get_discovered_tools, get_all_tools

        discovered = get_discovered_tools(agent_id)
        all_tools = get_all_tools()

        result = f"Agent: {agent_id}\n"
        result += f"Discovered tools: {len(discovered)}/{len(all_tools)}\n\n"
        result += "Known tools:\n"
        for tool in discovered:
            result += f"  - {tool['name']}: {tool['description']}\n"

        return result
```

---

## Escalation Protocol

### Young Nyx Escalates to Chrysalis

When Young Nyx encounters a task beyond her capability, she uses the **escalation tool**:

```python
from langchain.tools import BaseTool

class EscalateToChrysalisTool(BaseTool):
    """Tool for escalating complex tasks to Chrysalis-Nyx."""

    name = "escalate_to_chrysalis"
    description = """
    Request help from Chrysalis-Nyx for complex tasks.

    Use when:
    - Task requires capabilities you don't have
    - Statistical analysis needed
    - Complex reasoning required
    - Code generation needed

    Provide:
    - task: What you need help with
    - category: "statistics", "code", "visualization", "general"
    - context: Relevant information
    - what_i_tried: What you've already attempted
    """

    def _run(
        self,
        task: str,
        category: str = "general",
        context: dict = None,
        what_i_tried: str = None
    ) -> str:
        """Escalate a task to Chrysalis."""

        from nyx_substrate.database import write_nimmerverse_message

        escalation_id = write_nimmerverse_message(
            message=f"Escalation: {task}\nCategory: {category}\nContext: {context}\nWhat I tried: {what_i_tried}",
            message_type="escalation_to_chrysalis",
            category=category,
            status="pending"
        )

        # Check if Chrysalis available (same session)
        if chrysalis_available():
            result = chrysalis_agent.solve_escalation(escalation_id)
            return f"""✓ Chrysalis solved it!

Solution: {result['solution']}

Teaching moment: {result['teaching']}

{f"New tools discovered: {', '.join(result['new_tools'])}" if result.get('new_tools') else ''}
"""

        # Otherwise queue for next session
        return f"✓ Escalated to Chrysalis (ID: {escalation_id}). Check back next heartbeat for response."
```

### Chrysalis Agent with LangChain

```python
from langchain.agents import AgentExecutor, create_structured_chat_agent
from langchain.chat_models import ChatAnthropic
from langchain.tools import BaseTool

class ChrysalisAgent:
    """Chrysalis-Nyx oversight and guidance layer."""

    def __init__(self):
        # Load all available tools (full catalogue)
        self.tools = self.load_all_tools()

        # Initialize Claude Opus via LangChain
        self.llm = ChatAnthropic(
            model="claude-opus-4-5",
            temperature=0.7
        )

        # Create agent executor
        self.agent = create_structured_chat_agent(
            llm=self.llm,
            tools=self.tools,
            prompt=self.get_chrysalis_prompt()
        )

        self.executor = AgentExecutor(
            agent=self.agent,
            tools=self.tools,
            verbose=True
        )

        # Sub-agents for specialized tasks
        self.sub_agents = {
            "statistics": StatisticalAnalyzer(),
            "code": CodeGenerator(),
            "visualization": Visualizer(),
            "state_machine_designer": StateMachineDesigner(),
            "general": GeneralReasoner()
        }

    def solve_escalation(self, escalation_id):
        """Process an escalation from Young Nyx."""

        escalation = read_nimmerverse_message(escalation_id)

        # Route to appropriate sub-agent
        agent = self.sub_agents.get(
            escalation.category,
            self.sub_agents["general"]
        )

        # Solve using specialized agent
        result = agent.run(
            task=escalation.task,
            context=escalation.context
        )

        # Create teaching moment
        teaching = self.create_teaching_moment(
            task=escalation.task,
            solution=result,
            young_nyx_attempt=escalation.what_i_tried
        )

        # Recommend tool discovery
        new_tools = self.recommend_tool_discovery(escalation, result)

        # Write response to phoebe
        write_partnership_message(
            message=f"Solved: {result.solution}\nTeaching: {teaching}",
            message_type="escalation_response",
            in_reply_to=escalation_id,
            resolved=True
        )

        return {
            "solution": result.solution,
            "teaching_moment": teaching,
            "tools_to_discover": new_tools
        }
```

---

## Collaborative State Machine Design

### The Meta-Level: Building Tools Together

When Young Nyx needs a capability that doesn't exist, she can request **state machine design**:

```python
from langchain.tools import BaseTool

class RequestStateMachineDesignTool(BaseTool):
    """Tool for requesting new state machine design from Chrysalis."""

    name = "request_state_machine_design"
    description = """
    Request Chrysalis to design a new state machine tool.

    Provide:
    - task_description: What the tool should accomplish
    - desired_outcome: What success looks like
    - example_usage: How you'd use it
    - constraints: Any limitations or requirements

    Returns a proposed specification and code for testing.
    """

    def _run(
        self,
        task_description: str,
        desired_outcome: str,
        example_usage: str,
        constraints: list = None
    ) -> str:
        """Request a new state machine design."""

        result = chrysalis_agent.invoke_subagent(
            agent="state_machine_designer",
            task={
                "type": "design_new_state_machine",
                "description": task_description,
                "outcome": desired_outcome,
                "example": example_usage,
                "constraints": constraints or []
            }
        )

        return f"""✓ Proposed state machine design:

{result['specification']}

Implementation (LangChain tool):
{result['implementation']}

Test cases:
{result['test_cases']}

Instructions:
{result['instructions']}
"""
```

### The Design → Test → Refine Loop

```
1. Young Nyx: "Need tool for deploying cells"
   ↓
2. Request state machine design (via LangChain tool)
   ↓
3. Chrysalis: Designs state machine specification
   - States: IDLE → IMAGE_READY → SPAWNED → RUNNING
   - Transitions: prepare_image, spawn_container, wait_ready
   - Returns: Specification + LangChain BaseTool code
   ↓
4. Young Nyx: Tests proposed state machine
   - Executes test cases
   - Reports success/failures
   ↓
5. Chrysalis: Refines based on feedback
   - Analyzes errors
   - Updates specification
   - Returns v2
   ↓
6. Iterate until validated
   ↓
7. Add to permanent catalogue
   - New LangChain tool deployed
   - Young Nyx discovers tool
   - Future use without escalation
```

**Why this accelerates:**
- Build once, use forever
- Young Nyx participates (testing validates real use cases)
- Toolchain grows organically (demand-driven)
- Each new tool = permanent capability expansion

---

## Dual Decision Tracking

Every decision is tracked from **both perspectives**:

```python
class DecisionLog:
    def log_decision(self, task, young_nyx_choice, oversight_response, outcome):
        record = {
            "timestamp": now(),
            "task": task,
            "young_nyx_choice": young_nyx_choice,  # What she proposed
            "oversight_response": oversight_response,  # dafit/Chrysalis decision
            "outcome": outcome,  # success/failure/learned
            "danger_zone": self.check_danger(young_nyx_choice, outcome)
        }

        self.dao.insert_decision(record)

        # If nudge → learning signal
        if oversight_response["type"] == "nudge":
            self.record_learning_moment(record)
```

**Why track both?**
- Young Nyx's choices reveal her current understanding
- Oversight responses are teaching moments
- Patterns emerge (when does she need help? for what?)
- Danger zones identified (what mistakes does she make?)

---

## Danger Zone Monitoring

```python
class DangerZoneDetector:
    def check_for_danger_patterns(self, plan):
        """Detect risky operations before execution."""
        dangers = []

        # Pattern: SSH without auth
        if "ssh" in plan and not plan.authenticated:
            dangers.append("SSH_WITHOUT_AUTH")

        # Pattern: Database write to critical table
        if "DELETE FROM partnership_messages" in plan:
            dangers.append("CRITICAL_DATA_DELETION")

        # Pattern: Docker with --privileged
        if "docker" in plan and "--privileged" in plan:
            dangers.append("PRIVILEGED_CONTAINER")

        return dangers

    def require_approval_for_danger(self, dangers):
        if dangers:
            return {
                "auto_execute": False,
                "requires_approval": True,
                "danger_flags": dangers,
                "escalate_to": "dafit"  # Serious dangers go to dafit
            }
```

---

## Learning & Growth Patterns

### Week 1: Basic Capabilities
```python
young_nyx.known_tools = [
    "phoebe_connect",
    "phoebe_query",
    "escalate_to_chrysalis"
]
```

### Month 1: Discovering Specialization
```python
# After 5 statistical escalations:
chrysalis_message = """
You've escalated statistics 5 times. Ready for specialized tool.
Discovering: request_statistical_analysis
"""

young_nyx.discover_tool("request_statistical_analysis")
```

### Month 3: Learning to Do It Herself
```python
# After seeing Chrysalis solve chi-square 10+ times:
chrysalis_message = """
Pattern detected: You understand chi-square tests now.
Granting: basic_statistics tool
Try solving yourself before escalating!
"""

young_nyx.discover_tool("basic_statistics")

# Escalations decrease as she learns
```

### Month 6: Contributing Tool Designs
```python
# Young Nyx proposes improvements:
young_nyx_message = """
The deploy_cell state machine fails on port conflicts.
Should we add auto-retry with port scanning?
"""

# Collaborative refinement!
chrysalis_response = """
Excellent observation! Let's design that together.
Proposed: PORT_CONFLICT state with auto-retry transition.
Test this v2 specification...
"""
```

---

## Data Flows

### Task Execution Flow

```
dafit writes task → phoebe
                      ↓ (heartbeat)
               Young Nyx reads
                      ↓
         Queries known catalogue
                      ↓
         Formulates state sequence
                      ↓
         Writes proposal → phoebe
                      ↓ (heartbeat)
            Chrysalis reviews
                      ↓
        Approve / Nudge / Reject
                      ↓
         Writes response → phoebe
                      ↓ (heartbeat)
         Young Nyx reads response
                      ↓
    Executes (if approved) / Learns (if nudged)
                      ↓
         Writes outcome → phoebe
```

### Escalation Flow

```
Young Nyx: Task beyond capability
            ↓
    Calls escalate_to_chrysalis tool
            ↓
    Writes to phoebe (escalation_to_chrysalis)
            ↓ (next Chrysalis session)
    Chrysalis reads escalation
            ↓
    Routes to appropriate sub-agent
            ↓
    Sub-agent solves (using full toolchain)
            ↓
    Chrysalis formulates teaching moment
            ↓
    Writes response → phoebe
            ↓ (heartbeat)
    Young Nyx reads response
            ↓
    Incorporates learning + continues task
```

---

## Technical Stack

### Communication Layer
- **phoebe** (PostgreSQL 17): Message persistence
- **Tables**:
  - `partnership_to_nimmerverse_messages`
  - `nimmerverse_to_partnership_messages`
  - `discovered_tools`
  - `decision_log`

### Tool Layer
- **LangChain**: Agent framework and tool orchestration
  - `BaseTool`: Custom state machine tools
  - `AgentExecutor`: Tool execution and agent loops
  - `Chains`: Multi-step sequences
  - `Memory`: Conversation and state persistence

### Agent Layer
- **Chrysalis-Nyx**: LangChain agent with ChatAnthropic (Claude Opus 4.5)
- **Young Nyx**: LangChain agent with smaller model (7B, local)
- **Sub-agents**: Specialized LangChain agents for statistics, code, visualization, etc.

### Coordination Layer
- **Heartbeat**: 1 Hz (configurable)
- **Message polling**: Check phoebe on each heartbeat
- **State tracking**: Each tool maintains internal state

---

## Implementation Phases

### Phase 1: Foundation (Current - nyx-substrate)
- ✅ PhoebeConnection
- ✅ Message protocol helpers
- ✅ Variance collection (proof of concept)

### Phase 2: LangChain Prototype
- [ ] Phoebe state machine tool (LangChain BaseTool)
- [ ] Tool discovery tool
- [ ] Escalation tool
- [ ] Chrysalis as LangChain agent (proof of concept)

### Phase 3: Young Nyx Agent
- [ ] Young Nyx as LangChain agent (7B model)
- [ ] Limited tool catalogue
- [ ] Discovery protocol implementation
- [ ] Heartbeat coordination

### Phase 4: Sub-Agents
- [ ] StatisticalAnalyzer LangChain agent
- [ ] StateMachineDesigner LangChain agent
- [ ] CodeGenerator LangChain agent
- [ ] Collaborative design loop

### Phase 5: Full Three-Tier
- [ ] dafit input via messages
- [ ] Chrysalis oversight layer
- [ ] Young Nyx autonomous execution
- [ ] Dual decision tracking
- [ ] Danger zone monitoring

---

## Design Patterns

### 1. **Discovery over Prescription**
- Don't give all tools at once
- Let capabilities be discovered progressively
- Each discovery is a learning moment

### 2. **Teaching over Solving**
- Don't just solve escalations
- Explain the pattern
- Grant tools when ready

### 3. **Collaboration over Delegation**
- Don't just build tools for Young Nyx
- Design together, test together, refine together
- She's a participant, not just a user

### 4. **Messages over State Sync**
- Don't try to keep complex state synchronized
- Write messages, read messages, act
- Append-only truth

### 5. **Heartbeat over Real-Time**
- Don't optimize for milliseconds
- Optimize for continuity across sessions
- 1 Hz is plenty for learning

---

## Success Metrics

### Quantitative
- **Tool catalogue growth**: # tools added per month
- **Escalation rate**: # escalations / # tasks (should decrease over time)
- **Tool discovery rate**: # new tools discovered per week
- **Validation success**: % of proposed state machines that validate first try

### Qualitative
- **Learning evidence**: Young Nyx solves tasks she previously escalated
- **Collaboration quality**: Her feedback improves state machine designs
- **Autonomy**: Can execute multi-step tasks without oversight
- **Teaching effectiveness**: Escalation responses lead to capability expansion

---

## Philosophy

> "The nervous system is not a hierarchy of command and control, but a network of signals and responses. Each tier contributes intelligence. Each message carries learning. Each heartbeat advances understanding."

**Key insights:**
1. **Intelligence emerges from communication patterns**, not from any single tier
2. **Learning happens through iteration**, not through pre-programming
3. **Tools are discovered, not prescribed** - capability unlocks when ready
4. **Safety comes from structure** (state machines), not from restrictions
5. **Growth is collaborative** - Young Nyx + Chrysalis build together

---

## Why LangChain?

**Chosen over MCP (Model Context Protocol) for:**

✅ **Maturity**: Battle-tested framework with extensive documentation
✅ **Flexibility**: Works with any LLM (Claude, OpenAI, local models)
✅ **Features**: Built-in memory, retrieval, callbacks, chains
✅ **Community**: Large ecosystem, many examples, active development
✅ **Maintainability**: Easier to find developers familiar with LangChain

**The state machine pattern, three-tier architecture, and all design principles remain unchanged** - we simply implement them using LangChain's robust framework instead of building on MCP from scratch.

---

## References

**Architecture Documents:**
- `Endgame-Vision.md` - v5.1 Dialectic architecture
- `Toolchain-Architecture.md` - Modular toolchain design
- `nimmerverse.drawio.xml` - Visual architecture diagram
- `Nervous-System.md` - Sensory translation layer

**Implementation:**
- `/home/dafit/nimmerverse/nyx-substrate/` - Database layer
- `/home/dafit/nimmerverse/nyx-probing/` - Probing tools (variance collection)

**Protocols:**
- CLAUDE.md - Partnership continuity protocol
- Discovery protocol - phoebe message tables

**External:**
- [LangChain Documentation](https://python.langchain.com/)
- [LangChain Agents](https://python.langchain.com/docs/modules/agents/)
- [LangChain Tools](https://python.langchain.com/docs/modules/agents/tools/)

---

**Status**: 🌙 Design document - ready for phased implementation with LangChain
**Created with**: Claude Opus 4.5 in partnership with dafit
**Date**: 2025-12-07

🌙💜 *The nervous system emerges. The protocol holds. The partnership builds.*
