When a send-to-agent reaction was configured (e.g. for ci-failed), the
reactionHandledNotify flag stayed false, causing checkSession() to also
call notifyHuman() immediately — bypassing the retry/escalation workflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two bugs fixed:
1. Transitions with non-info priority (e.g. merge.completed,
review.approved, session.errored) now call notifyHuman() directly
when no reaction config handles notification. Before, these
transitions updated state silently with no human notification.
2. When agent.detectActivity() throws, sessions in stuck/needs_input
state now preserve that state instead of being coerced to "working"
by the fallback at the bottom of determineStatus().
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The event bus was over-engineered for a system with ~50 sessions. The
dashboard can read metadata files + poll GitHub directly. Remove the
EventBus interface, event-bus.ts module, and all emit/subscribe wiring
from session-manager and lifecycle-manager. The reaction engine (the
valuable part) stays — executeReaction now takes sessionId/projectId
directly instead of going through OrchestratorEvent.
Also fix listMetadata() to filter out directories (not just the
"archive" dir by name) so readMetadataRaw() can't crash on non-file
entries.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add -l flag to tmux send-keys for short messages so key-like payloads
(e.g. "Enter", "Escape") are sent as text, not interpreted as keypresses
- Prune states and reactionTrackers maps in pollAll() for sessions that
no longer appear in the session list, preventing unbounded memory growth
and stale retry counters from applying to reused session IDs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Reactions with action: "notify" bypass the auto: false gate so
merge-ready notifications fire for approved-and-green config
- All spawn failure paths now delete the reserved session metadata file
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- kill() and send() prefer handle.runtimeName over project defaults
- spawn() uses O_EXCL atomic file creation to prevent concurrent ID collisions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- kill() uses handle.runtimeName to find the correct runtime plugin
- postCreate cleanup skips destroy when workspacePath is project root
- checkSession reads persisted metadata status on first poll after restart
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- stuck/needs_input sessions recover to working when agent is active
- Project reaction overrides merge with global defaults (not replace)
- summary.all_complete events get info priority (not action)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- summary.all_complete reactions now execute in pollAll() after emission
- workspace.postCreate errors trigger workspace cleanup to prevent leaks
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review fixes:
- metadata: sessionId validation to prevent path traversal
- event-bus: guard appendFileSync, remove unused import
- lifecycle-manager: fix reaction retry counting (track attempts,
clear on status transition), allow retry on send failure instead
of immediate escalation, detect killed sessions in polling loop,
map session.killed to agent-exited reaction
- session-manager: validate status against union, clean up runtime
and workspace on spawn failure, kill with default plugins when
project config is missing
Lint/format fixes:
- Remove unused imports across all test files
- Replace `Function` type with typed callback in tmux tests
- Replace require() with dynamic import() in tests
- Replace dynamic delete with object spread in metadata
- Apply prettier formatting to all files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- metadata: validate sessionId against ^[a-zA-Z0-9_-]+$ to prevent
path traversal attacks
- lifecycle-manager: fix reaction retry/escalation to count attempts
(not just successes), escalate after maxRetries attempts; map
session.killed to agent-exited reaction (was unreachable session.exited)
- session-manager: validate status against known SessionStatus union
(default to "spawning" for unknown values); clean up workspace on
runtime creation failure to prevent resource leaks
- event-bus: guard appendFileSync in try/catch so disk errors don't
crash event emitters
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- session-manager: replace readMetadata with readMetadataRaw (no unsafe casts),
add safeJsonParse for JSON.parse guards, escapeRegex for session prefix,
explicit branch priority, convert from this-based to closure-based methods
- tmux: named buffers (load-buffer -b / paste-buffer -b -d) to prevent
global paste buffer race on concurrent sendKeys; send Escape before text
to clear partial input
- lifecycle-manager: concurrent polling with Promise.allSettled instead of
sequential for-of await, allCompleteEmitted guard to prevent repeated
summary.all_complete events, polling re-entrancy guard, safe reaction
config handling with action/auto checks
- tests: update tmux tests for Escape-first and named buffer changes
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implement the 5 core service modules in packages/core/src/:
- metadata.ts: Flat-file key=value session metadata read/write matching
existing bash script format. Supports read, write, update, delete
(with archive), and list operations.
- event-bus.ts: In-process pub/sub with JSONL append-only persistence.
Supports typed and wildcard listeners, priority inference from event
type, filtered history queries, and a createEvent() helper.
- tmux.ts: Async wrappers around tmux commands using child_process.execFile
(no shell injection). Handles long/multiline messages via load-buffer/
paste-buffer. Covers list, create, send-keys, capture-pane, kill, and
pane TTY discovery.
- session-manager.ts: Session CRUD that orchestrates Runtime + Agent +
Workspace plugins. Full spawn pipeline (workspace → runtime → agent),
list with live runtime checks, kill with cleanup, batch cleanup checking
PR state + issue state + runtime liveness, and message delivery.
- lifecycle-manager.ts: State machine per session with configurable polling
interval. Detects status transitions, emits events, triggers configured
reactions (send-to-agent, notify, auto-merge) with retry counting and
time-based escalation. Routes notifications by priority to configured
notifier plugins.
All services implement interfaces defined in types.ts. Updated index.ts
to export all new modules.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: CI workflow handles Next.js build separately, fix web tsconfig rootDir
- Remove rootDir from web tsconfig (conflicts with Next.js generated .next/types)
- Include .next/types/**/*.ts in web tsconfig
- CI: build non-web packages first, web build allowed to soft-fail
- CI: typecheck excludes web (typechecked by next build)
- CI: test excludes web build dependency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: remove || true from web build — bugbot caught it, web errors should fail CI
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: override rootDir in web tsconfig to "." for Next.js compat
Next.js generates .next/types/ files that must be under rootDir.
Base tsconfig sets rootDir to "src", web needs "." to include both
src/ and .next/types/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Phase 0 complete. Establishes:
- pnpm workspace with 18 packages (core, cli, web, 15 plugins)
- Complete type definitions in packages/core/src/types.ts defining
all 8 plugin slot interfaces (Runtime, Agent, Workspace, Tracker,
SCM, Notifier, Terminal) + core service interfaces
- YAML config loader with Zod validation and sensible defaults
- Plugin registry with built-in discovery
- CLAUDE.md with conventions for spawned agents
All agents can now branch from main and implement their assigned
packages against the interfaces defined in types.ts.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Maps dependency graph, identifies 7 truly independent work streams
after Phase 0 (scaffold + types), with Linear tickets and spawn
commands ready to go.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The human never polls. The system notifies the human. Two-tier event
handling: auto-handle routine issues silently, push notifications only
when human judgment is required. Escalation chains, priority-based
routing, and the dashboard is a drill-down tool, not the primary interface.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comprehensive analysis of 16+ competing agent orchestration tools
(Gas Town, OpenHands, SWE-ReX, Par, claude-flow, etc.) and detailed
architecture design with 8-slot plugin system, session lifecycle state
machine, and human attention optimization patterns.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This file contains the shared orchestrator instructions that ship with
the repo. It's loaded explicitly via --append-system-prompt-file when
launching an orchestrator session, rather than auto-discovered as a
CLAUDE.local.md. Users can still have their own CLAUDE.local.md for
personal overrides.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Import all 18 production orchestrator scripts from ~/. These are direct
copies with hardcoded paths — generalization is the next step. Includes
CLAUDE.local.md with project overview, architecture notes, and roadmap.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>