SPEC-3 — cross-harness peers (CC + Pi backends, profile routing, session_key resume) - #4
Merged
Merged
Conversation
…, session_key resume) Brainstorming output (5 Q&A + design §1-§7). Decisions: - Q1=B graceful degradation; per-backend armory chip (t✓ m✓ v~ for CC vs t✓ m✓ v✓ for Pi) - Q2=A profile pins backend; fan-out = two profiles; engine unchanged - Q3=A backend-native resume; sessionKey per profile; backendSessionId on run - Q4=A streaming claude -p --output-format stream-json; version-detect at adapter; fail-loud at backend - Q5=B ChildSessionFactory stays the seam; BackendRegistry + Backend descriptor hold metadata 15 sections + decision log. Additive only: ~8 new src files, 4 mod, 1 new builtin, 1 new smoke script. Pi factory + all SPEC-2 modules untouched except the one-line inMemory→file-backed SessionManager for resume.
Implementation plan for SPEC-3. Tasks (bottom-up, each independently testable): 1. BackendHookParity + Backend + BackendRegistry 2. ResumeStore (file-backed sessionKey→backendSessionId) 3. Frontmatter backend + sessionKey 4. Engine: ChildSessionEvent.backendSessionId + runRecord fields + backendRegistry routing 5. claude-events NDJSON → ChildSessionEvent mapper 6. detectClaude (version + stream-json schema smoke + flag-support probe) 7. ClaudeChildSession (ChildSession over claude -p child process) 8. createClaudeChildFactory (compose invocation, memory-in-prompt, resume) 9. Pi factory file-backed SessionManager + session_init emission (the one SPEC-2 module touched) 10. general-purpose-cc builtin + discovery backend-validation 11. /fleet Backends view + Agents-view backend badge 12. index.ts wiring (BackendRegistry + detectClaude at init) 13. Real-pi smoke script + term-driven checklist 14. CI gate — typecheck + full suite + release.yml staging Self-reviewed: spec coverage complete, no placeholders, type consistency verified, the one runtime unknown (exact claude -p flag set) resolved by detectClaude() at init.
…ackend, resume id)
…runRecord SpawnOptions.childFactory → backendRegistry; engine looks up backend by agentDef.backend + fails fast when unavailable. ChildSessionEvent gains backendSessionId; RunRecord gains backendSessionId + sessionKey. The session_init event stamps the run record for resume. Ripples to the tool + panel + index wiring (pulled forward from Task 12 to keep typecheck green per-task): SubagentToolDeps + FleetPanelDeps now carry backendRegistry; index.ts builds a minimal pi-only BackendRegistry (Task 12 adds claude detection). Existing tests inject a registry wrapping their fakes via a regWith() helper.
…y backend-validation
…ough tool+panel buildDefaultBackendRegistry now runs detectClaude() and registers the CC backend (availability reflects detection; registered regardless so the Backends view shows it). Pi factory + CC factory share a single ResumeStore. The deps construction awaits the registry build (claude detection is async).
Detector schema smoke now scans ALL stream-json lines for the system/init event (real CC emits hook_started/hook_response system events before init; first-line-only would false-fail). The factory's claude invocation includes --verbose (CC requires it with --output-format stream-json). Smoke verified: 4/4 rows pass (pi row 2 via real Ollama Cloud; CC rows 3-4 via real claude -p stream-json — wiring end-to-end: detect → spawn → NDJSON parse → session_id capture → resume). NOTE: RECTOR's claude OAuth is expired, so CC task output is an auth-failure message; the fleet wiring is correct, the auth is an env issue to re-auth.
…ildSession stdin guard Code review of PR #4 (self-review, requesting-code-review skill): - detectClaude: run() error handler now captures err.message into stderr so the ENOENT check works when the default 'claude' is not on PATH (previously returned a misleading 'version failed' object instead of null). Added a test that empties PATH to force the ENOENT path. - ClaudeChildSession.prompt: guard against null proc.stdin (would hang the engine's await session.prompt() forever); throws an actionable error instead. Other observations noted as acceptable-for-v0.3 (not fixed): CC 'result' with is_error:true maps to turn_end per spec §4.2 (auth failure surfaces as finalText); positional-task + stdin double-pass is a harmless smell the smoke verifies works.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
SPEC-3 — Cross-harness peers (CC + Pi backends)
Makes the fleet dual-arsenal: a
subagentrun targets one of two backends (Pi or Claude Code) chosen by the agent profile'sbackendfrontmatter field. The moat translates into CC via prompt/flag mechanisms (vision gap declared), backend-nativesession_keyresume works in both, and a new/fleetBackends view shows availability, version, schema, and per-backend hook parity.Implements
specs/SPEC-3-cross-harness-peers.md(15 sections + decision log) viaplans/SPEC-3-cross-harness-peers.md(14 TDD tasks). Spec + plan committed tomainfirst (977b54c, 949a587); implementation on this branch.Decisions (brainstorm)
t✓ m✓ v~CC vst✓ m✓ v✓Pi); vision pass-through-only gap declaredbackend; fan-out = two profiles; engine unchanged, nobackendtool paramsessionKeyper profile;backendSessionIdon run;SessionManager.open/claude --resumeclaude -p --output-format stream-json;detectClaude()version + schema smoke + flag-support probe at init; fail-loud at the backendChildSessionFactorystays the seam;BackendRegistry+Backenddescriptor hold metadata (YAGNI against aBackendPort)What's new
src/backend/—BackendRegistry+Backenddescriptor,BackendHookParity(declared),ResumeStore(file-backedsessionKey → backendSessionId),claude-events(NDJSON →ChildSessionEvent),claude-detector(version + schema smoke + flag-support probe),ClaudeChildSession(child-process adapter),createClaudeChildFactorysrc/engine/spawnSubagent.ts—ChildSessionEvent.backendSessionId+RunRecord.backendSessionId/sessionKey+SpawnOptions.childFactory→backendRegistry(engine routes byagentDef.backend, fails fast when unavailable)src/registry/frontmatter.ts—backend(defaultpi) +sessionKey(default = name) fields + validationsrc/panel/— new Backends view (read-only;r:Refresh+i:Info) + Agents-view[backend]badgeagents/general-purpose-cc.md— builtin sibling togeneral-purpose(day-one dual-arsenal)src/index.ts—detectClaude()at init; buildsBackendRegistry; registers pi (always) + claude (availability reflects detection); Pi factory file-backedSessionManager+session_initemission (the one SPEC-2 module touched — recorded in §3.1)Done bar (v0.3)
backend: claudeprofile spawns a realclaude -pchild through the CC factory; routed viaBackendRegistry; tracked in armory-todo like any Pi run--append-system-prompt);todoexcluded (--disallowed-tools+ prompt-nudge fallback); vision pass-through-only (v~declared + visible in chip)session_keyresume in both backends (SessionManager.open/claude --resume); stale-id fallback to fresh run with visible warning/fleetBackends view + Agents-view backend badge;claudeabsent/schema-drifted → fail fast at spawngeneral-purpose(pi) +general-purpose-cc(claude)Verification
pnpm typecheckcleanpnpm test:run→ 106/106 pass (65 prior + 41 new across 11 new test files)node --import tsx scripts/spec-3-smoke.mts→ 4/4 (pi row via real Ollama Cloud; CC rows via realclaude -pstream-json — wiring end-to-end: detect → spawn → NDJSON parse → session_id capture → resume)Note on CC execution
RECTOR's
claudeCLI OAuth is currently expired, so real CC task output is an auth-failure message — that's an environment issue to re-auth (claudelogin), not a fleet bug. The fleet wiring (detection, spawning, NDJSON parsing, session_id capture, resume) is verified end-to-end by the smoke.Deferred (recorded in spec §12)
Fan-out synthesis (SPEC-6); per-spawn
backendoverride (SPEC-6); cross-backend resume (never);BackendPortlift (when 4th backend lands); Codex (post-v1); per-backend model list (SPEC-5b); inline Backends editing (SPEC-6); async CC runs (SPEC-5a); cost accounting (SPEC-6).Release
After merge, tag
v0.3.0→release.ymlpublishes@getpipher/armory-fleet@0.3.0+ creates the GitHub Release (mirrors the v0.2.0 flow).