* test(scm): end-to-end integration coverage for SCM observer (#109) Adds backend/internal/integration/scm_observer_test.go, the regression guard for the SCM observer wiring landed in PR #114. Drives scmobserve.Observer.Poll against a real sqlite.Store, a real lifecycle.Manager with a recording messenger spy, and a canned observe/scm.Provider, asserting the full observation -> reducer -> store -> messenger pipeline. Three table-driven subtests, each on its own tmpdir fixture: - A CI-failing observation persists the pr row (provider-neutral columns + semantic hashes), persists pr_checks mirroring the observation, delivers exactly one nudge with the failed-log tail, persists last_nudge_signature, and produces no additional nudge on an identical re-poll. - A Merged: true observation MarkTerminated's the session and sends no nudge. - A branch with no open PR writes nothing and sends no nudge. Closes #109 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> * test(scm): address review — drop string key indirection, document idempotency path - Key cannedSCMProvider.observations/reviews by PR number directly so the fake no longer carries a string key that resembled (but did not actually need to mirror) the observer's internal prKey. Every case in this test uses scmTestRepo, so number alone is unambiguous. - Add an explicit pointer in the CI-failing subtest noting it exercises the hash-match short-circuit in prepareForPersistence; the ETag-driven 304 short-circuit on the same SHA is covered by observe/scm/observer_test.go (Poll_RepoETag304SkipsDetectPR, Poll_CIETagChangeRefreshesWhenRepoUnchanged). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| docs | ||
| frontend | ||
| test/cli | ||
| .envrc | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| README.md | ||
| flake.lock | ||
| flake.nix | ||
| package-lock.json | ||
| package.json | ||
README.md
agent-orchestrator
Rewrite of the agent-orchestrator: a long-running Go backend daemon (backend/)
paired with a placeholder Electron + TypeScript frontend shell (frontend/).
See docs/ for architecture and status — start with the
Lifecycle Manager + Session Service lane in docs/architecture.md.
Backend daemon
The Go backend now has a Cobra-based ao CLI in backend/cmd/ao.
The CLI controls the HTTP daemon — a loopback-only sidecar the Electron
supervisor will also use. The daemon skeleton includes the chi router,
middleware stack (recoverer → request-id → logger → real-ip), /healthz +
/readyz, atomic running.json PID/port handshake, graceful shutdown on
SIGINT/SIGTERM, SQLite storage, CDC polling, and lifecycle/reaper wiring.
Run
cd backend
go run ./cmd/ao start # start the daemon and wait for readiness
go run ./cmd/ao status # inspect PID/port/health/readiness
go run ./cmd/ao stop # gracefully stop the daemon
go run ./cmd/ao daemon # internal daemon entrypoint
go run . # compatibility wrapper; starts the daemon
AO_PORT=3019 go run ./cmd/ao start # override per invocation
Health check:
curl localhost:3001/healthz # includes status/service/pid
curl localhost:3001/readyz # includes status/service/pid
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 |
AO_DATA_DIR |
<UserConfigDir>/agent-orchestrator/data |
SQLite DB, WAL files, and managed state |
Test
npm run lint
# optional deeper backend pass:
cd backend && go test -race ./...