agent-orchestrator/docs/status.md

99 lines
4.6 KiB
Markdown

# LCM + Session Manager — status & roadmap
Where the lane stands, what's left, and where to plug in.
## Branch model
`feat/lcm-sm-contracts` is the **lane integration branch**: each sub-PR below
branched off it and merged **into** it. The whole lane lands on `main` as one
unit once it's ready. Sub-PRs were reviewed against the integration branch;
the eventual lane→main merge is a single cumulative review.
## Done — implementation complete (behind fakes)
| Area | What landed | PR |
|------|-------------|----|
| Skeleton | `backend/` (Go) + `frontend/` (Electron/TS) | #1 (on `main`) |
| Contracts + CI | `domain/` + `ports/`; Go + gitleaks workflows | #2 |
| Pure DECIDE core | the deciders + anti-flap quarantine + exhaustive truth-table tests | #4 |
| LCM — pipeline | `Apply*` pipeline, per-session serialization, store integration, composition rules, detecting-memory lifecycle | #5 |
| LCM — reactions | reaction table + escalation engine + real `TickEscalations` | #6 |
| Session Manager | spawn / kill / restore / cleanup / list, eager rollback, worktree-remove safety | #7 |
`gofmt` / `go build` / `go vet` / `go test -race` all green across `domain`,
`domain/decide`, `lifecycle`, and `session`. The `decide` core is at 100%
statement coverage; the impl packages cover the load-bearing logic including the
error/rollback paths.
### Build & test
```
cd backend
gofmt -l . # must print nothing
go build ./...
go vet ./...
go test -race ./...
go test -cover ./...
```
## Not done — the integration phase
Everything above runs against **in-memory fakes**. Making it a live system means
swapping fakes for real adapters (built by other lanes) behind the existing
ports, and resolving the carried-forward items below.
### Carried-forward items (must be addressed as real adapters land)
- **`react()` out-of-lock dispatch.** Reactions fire after the per-session lock
releases (deliberate, so a busy-waiting send-to-agent doesn't hold the mutex).
Under a live daemon with concurrent observers this can dispatch on a stale
snapshot / out of order. Give `react()` a per-session ordering (a small react
queue) or re-check the triggering state before dispatching. Documented in
`lifecycle/reactions.go`.
- **`ExpectedRevision` optimistic-concurrency is unused.** The in-process
per-session mutex covers a single daemon. Multi-writer or CDC-driven setups
must use the `LifecyclePatch.ExpectedRevision` CAS the contract already exposes.
- **Store `Seed` + `Get` need a real implementation.** The Session Manager added
two record-with-identity methods to `LifecycleStore`; the real persistence
layer must implement them (create-with-identity that rejects an existing id;
full-record read by id). Documented in `ports/outbound.go`.
### Real adapters needed (other lanes)
| Port | Real adapter | Owning lane |
|------|--------------|-------------|
| `LifecycleStore` | persistence layer (flat-file/KV + atomic write + lock + CDC) | persistence |
| `SCMFacts` producer | SCM poller (batch PR/CI/review enrichment) | SCM |
| `Runtime` / `Agent` / `Workspace` | tmux runtime, claude-code/codex agent, git-worktree workspace | coding-agents |
| `Notifier` | desktop/Slack notifier | notifications |
| `AgentMessenger` | tmux inject with busy-detect + delivery verify | coding-agents |
| `SessionManager` consumer | backend API (routes/controllers) + OpenAPI | API |
### Open cross-lane contract questions
- **SCM facts** — does `SCMFacts` match what the poller can cheaply produce
(batch enrichment, CI log tail as a pointer)?
- **Persistence** — is `LifecycleStore` + `LifecyclePatch` the right boundary?
Per-session lock vs. the `ExpectedRevision` CAS?
- **API** — is the `SessionManager` interface + the `Session` read-model
OpenAPI-friendly?
### Land the lane → `main`
A final cumulative review of `feat/lcm-sm-contracts` vs. `main`, then merge the
complete lane in one unit.
## Where to plug in (for someone picking this up)
- **Implementing a real adapter?** Write it to satisfy the matching interface in
`ports/`, then construct the `lifecycle.Manager` / `session.Manager` with it in
place of the fake. Nothing in `domain`/`lifecycle`/`session` should need to
change.
- **Changing decision behavior?** It lives in `domain/decide` (pure) — add a
truth-table case first; nothing there does I/O.
- **Adding a reaction?** Extend the table in `lifecycle/reactions.go` and map the
triggering status in `reactionEventFor`.
- **Don't** persist the display status, conclude death outside the probe
pipeline, or `rm -rf` a still-registered worktree — see the invariants in
[architecture.md](architecture.md#7-load-bearing-invariants).