* feat(api): register PR action route shells (merge + resolve-comments) Adds two 501 Not Implemented route shells for the SCM/PR action lane as specified in issue #21. No business logic — the routes are stubs that return a structured planned body with the embedded OpenAPI spec slice, consistent with the existing route-shell pattern. Routes registered: POST /api/v1/prs/{id}/merge POST /api/v1/prs/{id}/resolve-comments Closes part of #18. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(api): PR action routes — full impl (merge + resolve-comments) Builds the two SCM/PR action routes end-to-end per issue #21: POST /api/v1/prs/{id}/merge POST /api/v1/prs/{id}/resolve-comments **ports/scm.go** — new PRService interface, MergeResult, ResolveResult. **adapters/scm/github** — adds ErrNotMergeable/ErrUnprocessable sentinels to the client (405/409/422 classification) and MergePR / ListUnresolvedThreadIDs / ResolveThread methods to the Provider. **internal/scm/pr_service.go** — concrete PRService over PRProvider. Parses the path ID as a PR number, calls the provider, maps github sentinel errors to domain errors (ErrPRNotFound / ErrPRNotMergeable / ErrPRPreconditions / ErrNothingToResolve). Nil PRService keeps routes registered but returns OpenAPI-backed 501s. **httpd/controllers/prs.go** — real handlers; writePRError maps the four domain errors to 404 / 409 / 422 / 500. **prs_test.go** — httptest coverage: 501 (nil service), 200/404/409/422 for both routes, spec-slice present in 501 body. **scm/pr_service_test.go** — table-driven unit tests with a fake PRProvider. Closes part of #18. Closes #21. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat(api): PR action routes — merge + resolve-comments (#21) Implements POST /api/v1/prs/{id}/merge and POST /api/v1/prs/{id}/resolve-comments. Service logic lives in internal/service/pr (ActionManager interface + ActionService struct). Controllers use the projects pattern — import the service package directly rather than going through a ports interface. Drops the internal/scm intermediary package and the ports/scm.go file added in earlier iterations. Also fixes the ContentLength-based body-decode guard in resolveComments, which silently dropped JSON bodies sent with chunked transfer encoding; now decodes unconditionally and treats io.EOF as an absent body. Closes #21. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(specgen): remove dead path-param entries from schemaNames ControllersProjectIDParam, ControllersSessionIDParam, and ControllersPRIDParam are never matched by the schemaName interceptor — swaggest reflects path-param structs inline rather than as $ref component schemas, so the hook is never called for these types. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(pr): anchor resolve-comments to stated PR when explicit IDs supplied When commentIDs were provided, the parsed PR number was never used — any thread ID could be resolved regardless of which PR was in the URL path. Add a ListUnresolvedThreadIDs existence probe in the else branch so the PR must be reachable before iterating the caller-supplied IDs. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(controllers): exclude io.ErrUnexpectedEOF from isEmptyBody A truncated body (e.g. {"commentIds":["T_1") returns io.ErrUnexpectedEOF, not io.EOF. Treating it as an absent body caused the handler to fall through to "resolve all unresolved threads" instead of returning 400. Only io.EOF (reader returned no bytes) is a genuine empty-body signal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(api): revert to route shell — stubs, no adapter changes - Remove adapter changes (ErrNotMergeable/ErrUnprocessable from client.go, MergePR/ListUnresolvedThreadIDs/ResolveThread from provider.go) - ActionService returns dummy values with TODO; no business logic - Errors (ErrPRNotFound etc.) moved to controllers/errors.go - PR DTOs moved to controllers/dto.go - Remove 501 guards — stub service always wired via NewAPI default Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix: restore client.go, move PR errors to service/pr, fix lint - Restore original client.go alignment (no functional change) - Move PR sentinel errors to service/pr/errors.go - controllers/errors.go now only contains writePRError, referencing prsvc sentinels - Fix schemaNames alignment in specgen/build.go (goimports lint) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(api): replace fake-success stub with 503 when SCM not configured The nil-service fallback was silently injecting a stub that returned fake merge/resolve success, misleading callers when no SCM is wired. Remove the injection; nil Svc now returns 503 SCM_NOT_CONFIGURED. Also inline writePRError into prs.go and delete controllers/errors.go. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(specgen): mark resolve-comments request body as optional reqBody: nil removes the requestBody.required: true annotation so generated SDK clients can omit the body (which resolves all threads). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(prs): align nil-service guard with spec (501) and echo prID in stub Use apispec.NotImplemented (501) instead of 503 so nil-service responses match the OpenAPI spec and generated clients hit the documented 501 branch. Echo prID as PRNumber in the stub Merge to avoid claiming the wrong PR was merged if NewActionService is wired by accident before real impl lands. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| .github/workflows | ||
| backend | ||
| docs | ||
| frontend | ||
| test/cli | ||
| .envrc | ||
| .gitignore | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| README.md | ||
| flake.lock | ||
| flake.nix | ||
| 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 ./...