# Spawn Gate Node-Side Parity (MC #10548)

# Spawn Gate Node-Side Parity (MC #10548)

## Context — Why This Exists

**Problem:** Pi-orchestrator spawns agents internally at Step 4.6 (~line 4291 in `pi-orchestrator.js`) without going through Claude Code's Task dispatch path. This meant PreToolUse Bash hooks (`~/.claude/hooks/pre-dispatch-gate.sh`) never fired, creating a bypass where internal spawns skipped Mehanik clearance verification.

**Solution:** MC #10548 implemented Node-side spawn gate parity — a JavaScript enforcement layer (`~/system/kernel/spawn-gate.js`) that mirrors all 9 checks from the Bash gate, called directly by pi-orchestrator before every agent spawn.

**Genesis:** Pi-orchestrator hardening Talas 2 (parent thread #10043 reform). Dependency on δ #10551 `worktree_company_enforcer.js` (completed).

## Architecture — Dual Gate System

<table id="bkmrk-gate-location-lines-"><thead><tr><th>Gate</th><th>Location</th><th>Lines</th><th>Trigger</th></tr></thead><tbody><tr><td>**Bash Gate**</td><td>`~/.claude/hooks/pre-dispatch-gate.sh`</td><td>163</td><td>Claude Code Task/Agent dispatches (PreToolUse hook)</td></tr><tr><td>**Node Gate**</td><td>`~/system/kernel/spawn-gate.js`</td><td>853</td><td>Pi-orchestrator internal spawns (Step 4.6)</td></tr></tbody></table>

**Shared enforcement:** Both gates implement the same 9-check validation sequence. Checks 1-3 existed in spawn-gate.js before this MC; checks 4-9 were added to achieve full parity.

## The 6 New Checks (MC #10548 Scope)

### Check 4: Marker TTL (`checkMarkerTTL`, line 143)

Verifies Mehanik clearance has not expired. Reads `expires_at` field from `/tmp/mehakin-cleared-{taskId}` marker and compares to current time. Rejects if expired or missing.

### Check 5: Marker Schema (`checkMarkerSchema`, line 175)

Validates all 22 required marker fields are present:

- task\_id, timestamp, expires\_at
- project\_path, blueprint\_read, deploy\_map\_read, deploy\_path\_summary
- ceo\_item\_count, approved\_subtask\_count, scope\_ceiling\_compliance
- estimated\_cost\_usd, cost\_approval\_required, skill\_audit\_summary
- blueprint\_score, blueprint\_threshold\_applied, risk\_level
- hallucination\_surface, resource\_lock\_state, cross\_product\_check
- tool\_contract\_check, agent\_registry\_check, final\_decision

### Check 6: Scope Ceiling (`checkScopeCeiling`, line 196)

Deterministic arithmetic enforcement: `approved_subtask_count ≤ ceo_item_count + 2`. Prevents scope creep beyond Mehanik-approved ceiling.

### Check 7: Tool Contract (`checkToolContract`, line 226)

Research-class agent dispatches (datavera, sentinel-\*) must include a `TOOL_CONTRACT:` block in the prompt. Exempts prompts with tool literal references (`discover.js`, `lightrag.js`, `mc.js`, `web-search.sh`) as implementation context, not research requests.

**Fix on failure:**

```
node ~/system/tools/wrap-with-tool-contract.js --agent research --tools web-search.sh
```

### Check 8: Agent Registry (`checkAgentRegistry`, line 258)

Agent slug must exist in `~/system/agents/specialist-mapping.json`. Bootstrap-exempt agents skip this check:

- mehanik
- devils-advocate
- validator

### Check 9: Blueprint Advisory (`checkBlueprintAdvisory`, line 311)

**WARN-ONLY, FAIL-OPEN.** Checks `blueprint_score < blueprint_threshold_applied`. On failure, writes warning to `/tmp/spawn-gate-warnings.log` and stderr, but does NOT block dispatch. Bypassed by `[CEO_OVERRIDE]` in prompt.

## Wiring — Pi-Orchestrator Integration

**Location:** `~/system/kernel/pi-orchestrator.js` lines 4291-4316 (Step 4.6)

```

// Step 4.6: AAOS Spawn Gate — full 9-check parity with pre-dispatch-gate.sh (MC #10548)
if (_aaosSpawnGate && typeof _aaosSpawnGate.runGate === 'function') {
  const _taskPromptContext = `${task.title || ''} ${task.description || ''}`;
  const gateResult = await _aaosSpawnGate.runGate(task.id, 'pi-orchestrator', task.priority || 'M', _taskPromptContext);

  if (!gateResult.allowed) {
    const priority = (task.priority || 'M').toUpperCase();
    if (priority === 'H' || priority === 'M') {
      log('error', `Task #${task.id} SPAWN GATE BLOCKED: ${gateResult.reason}`, { taskId: task.id });
      execFileSync('node', [MC_SCRIPT, 'block', String(task.id), `spawn-gate: ${gateResult.reason}`], { timeout: 5000 });
      return; // HARD BLOCK — do not proceed to spawn
    }
    log('warn', `Task #${task.id} SPAWN GATE warned (L-priority): ${gateResult.reason}`, { taskId: task.id });
  }
}
```

**On FAIL (H/M priority):**

1. Log error
2. Call `mc.js block <taskId> "spawn-gate: {reason}"`
3. Return early (no agent spawn)

**On FAIL (L priority):** Log warning, proceed with spawn.

**Fallback:** Lines 4316+ preserve legacy `check()` path for compatibility if `runGate()` is unavailable (pre-MC#10548 deployments).

## Current State

**Status:** Code deployed COLD. Pi-orchestrator daemon is STOPPED.

**Activation:** Requires MC #10542 reactivation go/no-go decision (separate CEO approval, ZAKON PI2 verification pending).

## Test Plan

**Test suite:** `~/system/tests/spawn-gate.test.js` (23 tests)

**Coverage:** Each new check has PASS + FAIL path tests.

**Run tests:**

```
node --test ~/system/tests/spawn-gate.test.js
```

**Expected output:** 23 PASS, 0 FAIL

## Validation

**Proveo verdict:** `/tmp/proveo-10548-spawn-gate-validation.json` — PASS 6/6 AC

## Related Documentation

- [Pi-Orchestrator Operations](https://docs.alai.no/books/runbooks/page/pi-orchestrator-operations)
- MC #10548 task record
- Parent reform thread: MC #10043 (Pi-orch hardening Talas 2)
- ZAKON PI2: Deploy verification protocol