Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
977b54c
spec: SPEC-3 — cross-harness peers (CC + Pi backends, profile routing…
rz1989s Jul 24, 2026
949a587
plan: SPEC-3 — cross-harness peers (14 TDD tasks)
rz1989s Jul 24, 2026
b45b944
feat(spec-3): BackendHookParity + BackendRegistry (routing + view dat…
rz1989s Jul 24, 2026
d610759
feat(spec-3): ResumeStore (file-backed sessionKey → backendSessionId …
rz1989s Jul 24, 2026
83384c2
feat(spec-3): frontmatter backend + sessionKey fields (profile pins b…
rz1989s Jul 24, 2026
0e1cca9
feat(spec-3): engine routes via BackendRegistry; session_init stamps …
rz1989s Jul 24, 2026
38666d6
feat(spec-3): claude-events NDJSON → ChildSessionEvent mapper
rz1989s Jul 24, 2026
6029fb7
feat(spec-3): detectClaude (version + stream-json schema smoke + flag…
rz1989s Jul 24, 2026
8f61a8c
feat(spec-3): ClaudeChildSession (ChildSession over claude -p child p…
rz1989s Jul 24, 2026
b4bdd11
feat(spec-3): createClaudeChildFactory (compose invocation, memory-in…
rz1989s Jul 24, 2026
d7eb6f2
feat(spec-3): Pi factory file-backed SessionManager + session_init em…
rz1989s Jul 24, 2026
8dc2fc8
feat(spec-3): general-purpose-cc builtin (backend: claude) + discover…
rz1989s Jul 24, 2026
10f793e
feat(spec-3): /fleet Backends view + Agents-view backend badge
rz1989s Jul 24, 2026
5f53ac5
feat(spec-3): wire BackendRegistry + detectClaude at init; thread thr…
rz1989s Jul 24, 2026
f3e7ac9
test(spec-3): real-pi smoke script + term-driven checklist (rows 1-7)
rz1989s Jul 24, 2026
14c49cc
fix(spec-3): review findings — detectClaude ENOENT capture + ClaudeCh…
rz1989s Jul 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions agents/general-purpose-cc.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
name: general-purpose-cc
description: A focused general-purpose CC subagent. Use for any task needing Claude Code as the worker.
backend: claude
todoSync: true
memoryHydrate: true
vision: true
---
You are a focused subagent delegate running under Claude Code. Complete the assigned task
thoroughly, work autonomously to completion, and return a concise result summary.
Do not call the `todo` tool — the fleet engine manages todo tracking for you.
26 changes: 26 additions & 0 deletions docs/SPEC-3-smoke-checklist.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# SPEC-3 smoke checklist (real-pi, term-driven)

Rows that need no `claude` call are run via `term` inside a real pi session; rows 2-4 are the script.

## How to run
- Script (rows 2-4): `node --import tsx scripts/spec-3-smoke.mts` (skips CC rows if claude absent)
- Term rows (1/5/6/7): spawn pi in `~/local-dev/getpipher/armory-fleet`, drive via `term`

## Rows
| # | Action | Expected |
|---|---|---|
| 1 | extension loads with `claude` absent | `/fleet` Backends view shows `claude: ✗ (not installed)`; `pi: ✓` |
| 2 | `subagent(general-purpose, "reply OK")` (pi) | run completes; armory chip `t✓ m✓ v✓` |
| 3 | `subagent(general-purpose-cc, "reply OK")` (claude, if available) | run completes via `claude -p`; `backendSessionId` set; chip `t✓ m✓ v~` |
| 4 | re-spawn `general-purpose-cc` same `sessionKey` | `--resume <id>` passed; CC replays history |
| 5 | `backend: invalid` profile in `.pi/agents/` | load warning surfaced; profile excluded from registry |
| 6 | `claude` schema drift (point FLEET_CLAUDE_BIN at a fake) | Backends view shows `schema ✗`; spawn fails fast with actionable error |
| 7 | Backends view `r:Refresh` + `i:Info` | refresh notifies "restart pi to re-detect"; info shows flag matrix + hook mechanism notes |

## How to inspect the CC invocation
- The `i:Info` pane on the `claude` backend row shows the flag-support matrix probed at init.
- Set `DEBUG=fleet:cc` (or equivalent) to log the composed `claude -p` args + the NDJSON events received.

## Pass bar
- Rows 1, 5, 6, 7 pass (term-driven, no CC call).
- Rows 2-4 pass when `claude` is installed; skipped (exit 0) otherwise.
2,105 changes: 2,105 additions & 0 deletions plans/SPEC-3-cross-harness-peers.md

Large diffs are not rendered by default.

59 changes: 59 additions & 0 deletions scripts/spec-3-smoke.mts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// scripts/spec-3-smoke.mts — SPEC-3 full-run smoke (rows 2-4).
// Exercises the REAL CC backend (spawn a real `claude -p`) when claude is installed; skips cleanly otherwise.
// Run: node --import tsx scripts/spec-3-smoke.mts
import { spawnSubagent } from "../src/engine/spawnSubagent.ts";
import { RunRegistry } from "../src/engine/run-registry.ts";
import { createSingleSlotLock } from "../src/engine/concurrency-lock.ts";
import { ArmoryTodoAdapter } from "../src/todo-sync/adapter.ts";
import { ArmoryMemoryAdapter } from "../src/memory-hydrate/adapter.ts";
import { BackendRegistry, PI_HOOK_PARITY, CLAUDE_HOOK_PARITY } from "../src/backend/port.ts";
import { detectClaude } from "../src/backend/claude-detector.ts";
import { createClaudeChildFactory } from "../src/backend/claude-factory.ts";
import { ResumeStore } from "../src/backend/resume-store.ts";
import { ModelRuntime } from "@earendil-works/pi-coding-agent";
import { createChildSessionFactory } from "../src/index.ts";
import type { AgentDef } from "../src/registry/frontmatter.ts";

let pass = 0, fail = 0;
function check(name: string, cond: boolean, detail = ""): void {
if (cond) { console.log(` ✔ ${name}`); pass++; }
else { console.log(` ✖ ${name} ${detail}`); fail++; }
}

const resumeStore = new ResumeStore();
const claudeInfo = await detectClaude();
if (!claudeInfo?.schemaOk) {
console.log("⏭ claude not available (not installed or schema drift) — skipping CC rows. Pi row 2 still runs.");
}

const runtime = await ModelRuntime.create();
const reg = new BackendRegistry();
reg.register({ id: "pi", factory: createChildSessionFactory(runtime, new ArmoryMemoryAdapter(), resumeStore), available: () => true, versionInfo: () => null, hookParity: PI_HOOK_PARITY });
if (claudeInfo) reg.register({ id: "claude", factory: createClaudeChildFactory(claudeInfo, resumeStore), available: () => claudeInfo.schemaOk, versionInfo: () => claudeInfo, hookParity: CLAUDE_HOOK_PARITY });

const piAgent: AgentDef = { name: "general-purpose", description: "d", rolePrompt: "Reply minimally.", todoSync: true, memoryHydrate: true, vision: true, backend: "pi", sessionKey: "general-purpose", source: "builtin", filePath: "/x" };
const ccAgent: AgentDef = { name: "general-purpose-cc", description: "d", rolePrompt: "Reply minimally.", todoSync: true, memoryHydrate: true, vision: true, backend: "claude", sessionKey: "general-purpose-cc", source: "builtin", filePath: "/x" };
const registry = new Map<string, AgentDef>([["general-purpose", piAgent], ["general-purpose-cc", ccAgent]]);

// Row 2: pi backend (real Ollama Cloud session.prompt())
{
console.log("Row 2: pi backend spawn");
const res = await spawnSubagent({ agent: "general-purpose", task: "Reply with exactly: OK", registry, todoSync: new ArmoryTodoAdapter(), runRegistry: new RunRegistry(), lock: createSingleSlotLock(), backendRegistry: reg, parentModel: { provider: "Ollama", id: "glm-5.2:cloud" }, parentCwd: process.cwd() });
check("pi run completes", res.status === "completed", res.error ?? "");
check("pi run produced finalText", res.finalText.length > 0);
}

// Rows 3-4: CC backend + resume (only if claude available)
if (claudeInfo?.schemaOk) {
console.log("Row 3: claude backend spawn");
const res = await spawnSubagent({ agent: "general-purpose-cc", task: "Reply with exactly: OK", registry, todoSync: new ArmoryTodoAdapter(), runRegistry: new RunRegistry(), lock: createSingleSlotLock(), backendRegistry: reg, parentModel: { provider: "x", id: "y" }, parentCwd: process.cwd() });
check("cc run completes", res.status === "completed", res.error ?? "");
console.log("Row 4: claude resume (re-spawn same sessionKey)");
const res2 = await spawnSubagent({ agent: "general-purpose-cc", task: "What did I just say?", registry, todoSync: new ArmoryTodoAdapter(), runRegistry: new RunRegistry(), lock: createSingleSlotLock(), backendRegistry: reg, parentModel: { provider: "x", id: "y" }, parentCwd: process.cwd() });
check("cc resume run completes", res2.status === "completed", res2.error ?? "");
} else {
console.log("Rows 3-4: skipped (claude unavailable)");
}

console.log(`\n${pass} pass / ${fail} fail`);
process.exit(fail === 0 ? 0 : 1);
Loading
Loading