# LX-04: Multi-Agent Support Framework

**Status:** Phase 1 + 1.5 Complete, Ready for Phase 2
**Priority:** High
**Category:** Language/Agent eXtensions
**Created:** 2025-12-29
**Updated:** 2025-12-30
**Based on:** LX-02 (research), LX-03 Phase 1 (docs), Framework Review
**Supersedes:** LX-03 Phase 2+ (template generation)
**Reduction:** 15 days → 6 days (leverages existing infrastructure)

## Summary

Design a minimal, pragmatic multi-agent support leveraging existing Invar infrastructure (manifest.toml, sync_templates, region system) rather than building a new framework.

## Key Findings (2025-12-30 Review)

### Correct Version Flow

```
src/invar/templates/              ← SSOT (Single Source of Truth)
        │
        ├── invar dev sync        (Invar project, syntax=mcp)
        │
        └── invar init/update     (User projects, syntax=cli)
                │
                ▼
       Project files              ← Generated/synced
```

**Fix Required:** `.invar/context.md` incorrectly states "INVAR.md is the source". The templates are the true source.

### CLAUDE.md / INVAR.md Content Duplication

| Content | CLAUDE.md | INVAR.md | Issue |
|---------|-----------|----------|-------|
| Check-In | Full (16 lines) | Full (18 lines) | **Duplicate** |
| Visible Workflow | Partial | Full | Partial duplicate |
| Core/Shell | Summary table | Full + decision tree | OK (layered) |
| Contract Rules | None | Full (44 lines) | OK (INVAR.md only) |

**Recommendation:** Remove duplicates from CLAUDE.md, keep only references to INVAR.md.

### Content Gap Analysis (If Agent Only Reads CLAUDE.md)

| Source | Lines | What Agent Misses |
|--------|-------|-------------------|
| **INVAR.md** | 310 | Six Laws, Contract Rules (44 lines), Core/Shell Examples, USBV details |
| **context.md** | 163 | Task Router, Lessons Learned, Tool Priority |
| **examples/** | ~400 | Working code patterns |

**Critical Missing Content:**

| Content | Location | Impact |
|---------|----------|--------|
| **Lambda Signature Rule** | INVAR.md:106-118 | Agents write `@pre(lambda x: ...)` for `def f(x, y)` → ERROR |
| **@post Scope Limitation** | INVAR.md:136-146 | Agents write `@post(lambda r: r > x)` → ERROR (`x` not accessible) |
| **Task Router** | context.md:30-42 | Agents skip reading examples before coding |
| **Core/Shell Examples** | INVAR.md:64-103 | Agents guess patterns instead of copying working code |

**Contract Rules That Agents Miss (INVAR.md:104-147):**

```python
# WRONG (agent common mistake) - Lambda missing parameter
@pre(lambda x: x >= 0)
def calc(x: int, y: int = 0): ...

# CORRECT - Lambda must include ALL params
@pre(lambda x, y=0: x >= 0)
def calc(x: int, y: int = 0): ...

# WRONG - @post cannot access function parameters
@post(lambda result: result > x)  # 'x' not available!

# CORRECT - @post only sees 'result'
@post(lambda result: result >= 0)
```

**Conclusion:** These rules MUST be inlined in CLAUDE.md critical section.

### Agent Ecosystem (Updated)

| Agent | Prompt File | Skills | MCP | Hooks | Integration |
|-------|-------------|--------|-----|-------|-------------|
| **Claude Code** | CLAUDE.md | .claude/skills/ ✅ | ✅ | Bash | Native |
| **Pi** | CLAUDE.md ✅ | .claude/skills/ ✅ | ❌ | TypeScript | Native |
| **Cursor** | .cursor/rules/*.mdc | ❌ | ✅ | JSON | MCP |
| **Aider** | CONVENTIONS.md | ❌ | ❌ | lint-cmd | Lint Hook |

**Key Insight:** Pi reads CLAUDE.md and .claude/skills/ directly. Only hooks need Pi-specific files.

## Existing Infrastructure to Leverage

### 1. Template System (Already Complete)

```
src/invar/templates/
├── manifest.toml           ← File ownership & sync rules
├── protocol/INVAR.md       ← Protocol source
├── config/CLAUDE.md.jinja  ← Jinja2 with syntax variable
├── skills/*/SKILL.md.jinja ← Skills with syntax variable
└── hooks/*.sh.jinja        ← Bash hooks
```

### 2. Sync Engine (Already Complete)

```python
# DX-56: Unified sync engine
sync_templates(path, SyncConfig(
    syntax="mcp",                    # or "cli"
    inject_project_additions=True,  # or False
))
```

### 3. Region System (Already Complete)

```markdown
<!--invar:critical-->   ← Always update
<!--invar:managed-->    ← Update managed, preserve user
<!--invar:project-->    ← Inject from project-additions.md
<!--invar:user-->       ← Preserve user content
<!--invar:skill-->      ← Skill content
<!--invar:extensions--> ← User extensions for skills
```

## What's Missing (Minimal Additions)

### 1. Pi TypeScript Hooks

```
src/invar/templates/hooks/pi/
└── invar-guard.ts.jinja
```

**Content:**
```typescript
import type { HookAPI } from "@mariozechner/pi-coding-agent/hooks";

const BLOCKED = [/^pytest\b/, /^python\s+-m\s+pytest/, /^crosshair\b/];
const ALLOWED = [/--pdb/, /--cov/, /--debug/];

export default function(pi: HookAPI) {
  pi.on("tool_call", async (event, ctx) => {
    if (event.toolName !== "bash") return;
    const cmd = (event.input.command as string).trim();

    if (!BLOCKED.some(p => p.test(cmd))) return;
    if (ALLOWED.some(p => p.test(cmd))) return;

    return {
      block: true,
      reason: "Use `{{ syntax == 'mcp' and 'invar_guard()' or 'invar guard' }}` instead."
    };
  });
}
```

### 2. Cursor Rules Template

```
src/invar/templates/config/cursor.mdc.jinja
```

**Content:**
```markdown
---
description: Invar verification rules
globs: ["**/*.py"]
alwaysApply: true
---

{{ critical_rules }}

## Contract Rules (Critical)

### Lambda Signature
\```python
# ❌ WRONG: Lambda only takes first parameter
@pre(lambda x: x >= 0)
def calculate(x: int, y: int = 0): ...

# ✅ CORRECT: Lambda must include ALL parameters
@pre(lambda x, y=0: x >= 0)
def calculate(x: int, y: int = 0): ...
\```

## MCP Tools

| Task | Tool |
|------|------|
| Verify | `invar_guard()` |
| Signatures | `invar_sig(target)` |
| Entry points | `invar_map()` |
```

### 3. manifest.toml Extensions

```toml
# Add agent configuration
[agents]
claude = { enabled = true }
pi = { enabled = false, hooks_lang = "typescript" }
cursor = { enabled = false, rules_format = "mdc" }

# Add new templates
[templates]
".pi/hooks/invar-guard.ts" = { src = "hooks/pi/invar-guard.ts.jinja", type = "jinja", agent = "pi" }
".cursor/rules/invar.mdc" = { src = "config/cursor.mdc.jinja", type = "jinja", agent = "cursor" }
```

### 4. CLI Extension

```bash
# Add --agent flag to init
invar init --agent pi          # Enable Pi hooks
invar init --agent cursor      # Enable Cursor rules
invar init --agent claude,pi   # Multiple agents
```

## Content Optimization (Phase 1)

### Problem: CLAUDE.md/INVAR.md Duplication

Current Check-In content exists in both files (~95% duplicate).

### Solution: Reference, Don't Copy

**Before (CLAUDE.md):**
```markdown
## Check-In (DX-54)

Your first message MUST display:
...
[16 lines of content]
```

**After (CLAUDE.md):**
```markdown
## Check-In

> See [INVAR.md#check-in](./INVAR.md#check-in-required) for protocol.

Display: `✓ Check-In: [project] | [branch] | [clean/dirty]`
Then read `.invar/context.md`.
```

### Inline Critical Rules (MUST Add to CLAUDE.md)

Add to `<!--invar:critical-->` section in `CLAUDE.md.jinja`:

```markdown
## ⚡ Critical Rules

| Always | Remember |
|--------|----------|
| **Verify** | `invar_guard` — NOT pytest, NOT crosshair |
| **Core** | `@pre/@post` + doctests, NO I/O imports |
| **Shell** | Returns `Result[T, E]` from `returns` library |
| **Flow** | USBV: Understand → Specify → Build → Validate |

### Contract Rules (CRITICAL)

```python
# ❌ WRONG: Lambda must include ALL parameters
@pre(lambda x: x >= 0)
def calc(x: int, y: int = 0): ...

# ✅ CORRECT: Include defaults too
@pre(lambda x, y=0: x >= 0)
def calc(x: int, y: int = 0): ...

# ❌ WRONG: @post cannot access parameters
@post(lambda result: result > x)  # 'x' not available!

# ✅ CORRECT: @post only sees 'result'
@post(lambda result: result >= 0)
```
```

**Rationale:** These two rules have the highest agent failure rate. Inlining adds ~15 lines but prevents common errors.

## Implementation Plan

### Phase 1: Content Optimization ✅ Complete

| Task | File | Change | Status |
|------|------|--------|--------|
| 1.1 | `.invar/context.md` | Fixed version flow (templates are SSOT) | ✅ |
| 1.2 | `templates/config/CLAUDE.md.jinja` | Add Contract Rules to critical section (+15 lines) | ✅ |
| 1.3 | `templates/config/CLAUDE.md.jinja` | Simplify Check-In (reference INVAR.md, -11 lines) | ✅ |
| 1.4 | `invar dev sync` | Sync changes to Invar project | ✅ |
| 1.5 | Test | Verified in isolation environment | ✅ |

**Actual Change:** +4 lines in CLAUDE.md (add +15 contract rules, reduce -11 Check-In)

### Phase 1.5: Layered Redundancy ✅ Complete

| Task | File | Change | Status |
|------|------|--------|--------|
| 1.5a | `templates/config/CLAUDE.md.jinja` | Add Task Router reference (+1 line) | ✅ |
| 1.5b | `templates/config/CLAUDE.md.jinja` | Add Core/Shell edge cases (+7 lines) | ✅ |
| 1.5c | `templates/skills/develop/SKILL.md.jinja` | Add Task Router to Entry Actions (+1 line) | ✅ |

**Actual Change:** +10 lines in CLAUDE.md, +1 line in SKILL.md develop

### Phase 2: Pi Support (2 days)

| Task | Output |
|------|--------|
| 2.1 Create Pi TypeScript hook template | `templates/hooks/pi/invar-guard.ts.jinja` |
| 2.2 Extend manifest.toml with agent config | Agent configuration |
| 2.3 Add `--agent pi` to init command | CLI support |
| 2.4 Test Pi blocking pytest | Validation |

### Phase 3: Cursor Support (2 days)

| Task | Output |
|------|--------|
| 3.1 Create Cursor .mdc template | `templates/config/cursor.mdc.jinja` |
| 3.2 Add `--agent cursor` to init | CLI support |
| 3.3 Test Cursor MCP integration | Validation |

**Total: 6 days** (vs original 15 days)

## Architecture Comparison

| Aspect | Original LX-04 | Revised LX-04 |
|--------|----------------|---------------|
| Manifest | New JSON Schema | **Extend manifest.toml** |
| Copy-Sync | Full implementation | **Not needed** (Pi reads .claude/) |
| Templates | Jinja2 system | **Already exists** |
| Sync engine | New implementation | **Use sync_templates()** |
| Region markers | Generation markers | **Use existing <!--invar:*-->** |
| Time estimate | 15 days | **6 days** |

## Testing Strategy

### Test: Agent Link Following (Validated by Design)

**Question:** Will agents follow `[INVAR.md](./INVAR.md)` links?

**Answer:** Unreliably. Design already mitigates this:
- Critical rules inlined in CLAUDE.md
- Task Router uses explicit "STOP and read" prompts
- "**Must read:**" emphasis for examples

**Conclusion:** No runtime test needed. Keep critical rules in CLAUDE.md.

### Test: Task Router Works

1. Request "add a function to core/"
2. Observe: Does agent read `.invar/examples/contracts.py` first?
3. **Success criteria:** Agent shows evidence of reading example before coding

### Test: Pi Integration (Phase 2)

1. Run `invar init --agent pi`
2. Run `pi -p "run: pytest --version"`
3. Observe: Command blocked with "Use invar_guard() instead"?

## Risks and Mitigations

| Risk | Probability | Mitigation |
|------|-------------|------------|
| Agent doesn't follow INVAR.md link | Medium | Keep critical rules in CLAUDE.md |
| Pi TypeScript hook compatibility | Low | Test with Pi 0.30.2+ |
| Cursor .mdc format changes | Low | Simple template, easy to update |

## References

- [Pi Hooks Documentation](https://github.com/badlogic/pi-mono/blob/main/packages/coding-agent/docs/hooks.md)
- [Cursor Rules](https://cursor.com/docs/context/rules)
- [DX-56: Unified Sync Engine](./completed/DX-56-template-sync.md)
