fix(harness): add automatic TaskCompleted producer (B3, partial)

POST /v1/harness/complete lets a Claude Code Stop hook report task
completion instead of relying on a human hitting the manual endpoint.
The hook only fires on an explicit .orchestra-report.md marker (not
every turn boundary); the server builds the receipt itself from the
real transcript via herdr.ClaudeUsage rather than trusting a
self-reported number. Codex/opencode producers and the turn-decision
endpoint are still unbuilt — see AUDIT.md/progress.md for scope.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01W1rkJ2hBMybnJctPbcy4tT
This commit is contained in:
kami
2026-07-27 21:24:17 +04:00
parent 63cda5557e
commit f8a397a14c
4 changed files with 167 additions and 3 deletions
+31
View File
@@ -527,6 +527,37 @@ adapter.go:**
`go build ./...`, `go vet ./...`, `go test ./...` all pass after these
changes.
## B3 — partial fix, 2026-07-27
Added `POST /v1/harness/complete` (`cmd/orchestra/main.go`) — the first
automatic `TaskCompleted` producer. Design: a Claude Code Stop hook
(`deploy/hooks/orchestra-stop.sh`) runs on every turn boundary but only POSTs
when the agent has written `.orchestra-report.md` at the worktree root —
that file is the explicit "I'm done" signal, since Stop fires on every pause
and treating every stop as completion would be wrong (this is exactly the
distinction Phase 2 items 12, the `continue`/`prepare_handoff`/`rotate_now`/
`refuse` turn-decision endpoint, are meant to own — that endpoint is still
unbuilt, so there is currently no plane-side signal telling the agent when to
rotate vs. finish; the marker-file convention is a stopgap that only covers
the completion half).
The handler builds the `receipt` server-side from `herdr.ClaudeUsage` against
the transcript path the hook supplies (same local-filesystem assumption as
`CLIAdapter.Occupancy` — doesn't hold for a session hosted on a herdr that
isn't local to the machine running orchestra, i.e. the federation-fork
caveat applies here too) rather than trusting a self-reported number, and
uploads the report body via `Store.PutArtifact` for `report_ref`. Event is
appended with `Surface: string(authz.System)` hardcoded in Go — not read from
a request header — consistent with B8 (system must never be
header-controlled); gated instead by an optional `ORCHESTRA_HARNESS_TOKEN`
bearer check.
**Not done:** Codex/opencode completion producers (only Claude wired), the
turn-decision endpoint itself, and no test — `cmd/orchestra/main.go` has zero
handler test coverage of any kind (everything is inline in `main()`), so this
follows the existing gap rather than introducing an isolated test harness for
one handler.
**Still open from B5** (not attempted this pass — larger, needs design, not
just a method-name swap):
- `agent.prompt` inline `wait` on `CLIAdapter.Lease` (still `wait=0`, per B5's