Adds backend/internal/integration with five end-to-end tests that hydrate the real lifecycle.Manager + session.Manager against a tmp SQLite store and exercise the full pipeline through the DB triggers and the CDC poller: - TestHappyPath_Spawn_PR_Kill — spawn -> SCM PR observation (open + CI passing) -> kill; asserts canonical row, pr row, and change_log event types (session_created/_updated, pr_created, pr_check_recorded). - TestRestoreRoundTrip_PreservesMetadata — spawn, kill, close store, reopen same DB path, hydrate fresh LCM/SM, Restore(); asserts AgentSessionID and the rest of SessionMetadata survive across the daemon restart. - TestCIFailureAndRecovery_NudgeThenClears — failing CI observation drives the CI-failed reaction nudge with the log tail injected; passing CI observation switches to approved-and-green human notify; pr_checks history reads back the failure (the brake's source of truth). - TestDetectingPersistsAcrossRestart — failed probe parks the session in detecting with detecting_* columns populated, round-trips across a close/reopen, alive probe clears the quarantine memory. - TestCDCPollerReceivesAllStages — drives the real cdc.Poller; asserts the trigger pipeline emits each expected event_type and seq is monotonic. Wiring gap fixed (minimal): goose v3 keeps baseFS/logger/dialect as package-level globals, so two concurrent sqlite.Open() calls — uncommon in production but normal under -race with t.Parallel() — race on goose.SetBaseFS/SetLogger/SetDialect inside migrate(). Added a process-level sync.Mutex around the migrate() call. ~11 lines, no signature changes. Scope notes (the task brief assumed a fancier architecture than what actually shipped in PR #37): - No outbox / consumer_offsets / janitor exist on main — the change_log table IS the durable, ordered source of truth (see cdc/event.go), so the brief's janitor-watermark step is skipped. - No reaction_trackers table / ReactionStore port — trackers are in-memory per lifecycle/reactions.go; persistence-round-trip there is N/A. - No revision column / Upsert(rec, eventType) — write-mutex serialises and change_log.seq orders, so the assertions land on event_type + seq, not on a per-row revision counter. All 219 tests pass under -race across 18 packages. lifecycle/fakes_test.go is untouched; existing unit tests still drive the in-memory fake. |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| docs | ||
| frontend | ||
| .gitignore | ||
| README.md | ||
README.md
agent-orchestrator
Rewrite of the agent-orchestrator: a long-running Go backend daemon (backend/)
paired with an Electron + TypeScript frontend (frontend/).
See docs/ for architecture and status — start with the
Lifecycle Manager + Session Manager lane in docs/architecture.md.
Backend daemon
The Go binary in backend/ is the HTTP daemon — a loopback-only
sidecar the Electron supervisor will spawn (Phase 1c). Phase 1a landed the
skeleton: chi router, middleware stack (recoverer → request-id → logger →
real-ip), /healthz + /readyz, atomic running.json PID/port handshake,
graceful shutdown on SIGINT/SIGTERM.
Run
cd backend
go run . # binds 127.0.0.1:3001 with all defaults
AO_PORT=3019 go run . # override per invocation
Health check:
curl localhost:3001/healthz # {"status":"ok"}
curl localhost:3001/readyz # {"status":"ready"}
Configuration (env only)
The bind host is always 127.0.0.1: the daemon is a loopback-only sidecar
and binding any other interface would be a security regression, so the host
is intentionally not env-configurable.
| Var | Default | Purpose |
|---|---|---|
AO_PORT |
3001 |
bind port; fails fast if taken |
AO_REQUEST_TIMEOUT |
60s |
per-request timeout (Go duration) |
AO_SHUTDOWN_TIMEOUT |
10s |
graceful-shutdown hard cap |
AO_RUN_FILE |
<UserConfigDir>/agent-orchestrator/running.json |
PID + port handshake path |
Test
cd backend
gofmt -l . && go build ./... && go vet ./... && go test -race ./...