Files
orchestra/HANDOFF-2026-07-31.md
T
kami 7f12c7fc37 v3 workflow: intent, phases, review, submission, enforcement, burn-in
The v3 stack, previously an uncommitted working tree, plus this session's two
units and the burn-in instrument. This commit is the burn-in build identity:
coordinator and worker must both report this revision before a task is created.

Workflow (earlier sessions, uncommitted until now): human decision events and
reduction, source cursors and reconcile-before-launch, turn-boundary
reconciliation, internal/agentctx as the single renderer, ace-fca phases with
sealed artifacts, the trajectory gate, bounded grilling, independent review,
task pr enforcement, and human review reflection.

Capability restrictions at the agent boundary: an authz.Agent surface at
GatedWrite may ask and may not act. It also fixes two bugs the unit exposed --
gated surfaces could not reach the two endpoints written for them, and
RequestHumanDecision would block an unowned task while rejecting a question
from the session that did own it.

Turn-boundary reconcile-failure escalation: a streak of consecutive failures
asks the session to hand off, fenced on the lease epoch, with reconcile_failure
as a real handoff reason. The worker was dropping the coordinator's verdict on
the floor; it now acts on it.

Burn-in: herdr.WriteLaunchContext dumps the exact agentctx.Build result to
<worktree>/.orchestra/launch.md at every launch, local and federated. BURNIN.md
is the runbook. deploy/build.sh stamps both binaries from one commit.

go build, go vet and go test ./... pass, 20 packages.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 18:31:20 +04:00

176 lines
8.1 KiB
Markdown

# Orchestra — session notes & next steps, 2026-07-31
Scope of this session: acted on `REVIEW.md` (the 2026-07-30 senior review), then
on seven operator review comments left on Gitea PR #1. Two commits on
`webui-and-audit-reconciliation`, pushed, not merged at time of writing.
**Nothing was deployed. No live herdr or pane was touched.**
---
## What landed
### `56f5aac` — reconcile docs, module graph, token compare, health
| Finding | Action |
|---|---|
| REVIEW.md 1 | `CLAUDE.md`/`AGENTS.md` federation drift fixed; `clients/` tracked (later reversed — see below) |
| REVIEW.md 2 | `progress.md` references removed from `CLAUDE.md`, `AGENTS.md`, `rotation_test.go`, `deploy/hooks/orchestra-codex-poll.sh` |
| REVIEW.md 4 | `web/go.mod` stub added — ends the parent module's package graph at `web/` |
| REVIEW.md 5 | `orchestra-worker` untracked (still on disk); both binaries gitignored |
| REVIEW.md 7 | `subtle.ConstantTimeCompare` in `cmd/orchestra/main.go` |
| REVIEW.md 3 | (already fixed in the review's own pass — `SessionHealth.LastError` + `Observed`) |
### `97a9c65` — delete Design A, hook completion path, retired deploy files
Deleted: `clients/` (3 files), `deploy/hooks/` (3 scripts),
`deploy/config.example.json`, `deploy/orchestra.service`, `deploy/redeploy.sh`,
`deploy/docker-api-entrypoint.sh`, the `/v1/harness/complete` route, the
unmounted `harnessCompletion` handler, and that handler's test.
Changed: `Dockerfile.api` now `ENTRYPOINT ["/app/orchestra"]`; docs updated
across `CLAUDE.md`, `AGENTS.md`, `AUDIT.md`, `deploy/DEPLOYMENT.md`,
`deploy/orchestra.env.example`, `deploy/config.example.jsonc`, `.gitignore`.
---
## Ground truth established this session
Worth keeping — each of these corrected a documented claim.
1. **`orchestra-worker` owns completion, end to end.** It watches for
`.orchestra/done` in the worktree, confirms via `AgentStatus` that the agent
is not busy (a marker alone is intent, not proof), then finalizes and posts
through `/v1/federation/*` with **both** lease epoch and expected version.
`cmd/orchestra-worker/main.go:385-412`.
2. **The `deploy/hooks/` scripts were vestigial, not partly-needed.** They used
a different, older convention (`.orchestra-report.md`) and posted to
`/v1/harness/complete`, which was already a 410 stub — so that path could
not have completed a task. It never fired because the live OpenCode QA run
went through the worker. `/v1/harness/turn` is a separate, still-live
endpoint and was not touched.
3. **Env vars reach the API container via `env_file: .env`** in
`~/docker-apps/orchestra-web-ui/compose.yaml` — that is where the
Gitea/ntfy/web tokens and the bcrypt operator hash live. Only a single file,
`config.jsonc`, is bind-mounted into `/etc/orchestra/` (via
`compose.override.yaml`). CLAUDE.md previously claimed the container mounted
`/etc/orchestra:ro` and that its entrypoint sourced the env file. Both wrong.
4. **`ORCHESTRA_DATA`/`ORCHESTRA_PORT` are baked into the image** at
`Dockerfile.api` line 17, independent of compose — which is why deleting the
entrypoint script is behavior-neutral.
5. **`tea` cannot infer this repo.** `origin` is
`ssh://git@192.168.1.104:2222/...` while the login knows `gitea.kvmx.ru`, so
every `tea` call needs `--login homesrv --repo kami/orchestra`. Also: this
Gitea rejects `tea pr reply` (405) and `tea pr resolve` ("comment is not a
review comment"), so review threads must be answered with a conversation
comment and resolved in the browser.
6. **zsh does not word-split unquoted variables**`$FLAGS` holding
`--login homesrv --repo kami/orchestra` arrives as one argument.
---
## `REVIEW.md` is less reliable than it claims
Its second pass asserts every independently checkable claim held up. Six did
not. Treat it as a strong lead, not a verified record.
1. `AUDIT.md` never contained the false "Design B has zero clients" claim —
`AGENTS.md` was the real second copy.
2. The non-local-herdr guardrail is at `orchestrator.go:312`, not `:309`.
3. The `progress.md` reference list missed
`deploy/hooks/orchestra-codex-poll.sh:6`.
4. Finding 5's "untrack both" was half-actionable; `orchestra` was already
gitignored.
5. Finding 4's blast radius was overstated — `Dockerfile.api` builds
`./cmd/orchestra` by explicit path and `.dockerignore` already excluded
`node_modules`, so it was a local/CI break, never a production-image break.
6. It claims that pass "corrected a comment that still described the retired
`/v1/harness/complete` as handling completion." It had not;
`main.go:556-561` still did, and was fixed in `97a9c65`.
It also missed `TOKEN_MINIMAL_WORKFLOW_PLAN.md` and `WEB_UI_PLAN.md` entirely.
---
## Next steps, in order
### 1. Rebuild the deployed image — nothing above is live yet
```sh
cd /home/kami/docker-apps/orchestra-web-ui
revision=$(git -C /home/kami/apps/orchestra rev-parse HEAD)
build_time=$(date -u +%Y-%m-%dT%H:%M:%SZ)
docker compose -f compose.yaml -f compose.live.yaml build \
--build-arg BUILD_REVISION="$revision" \
--build-arg BUILD_TIME="$build_time" \
--build-arg BUILD_DIRTY=false orchestra-api
docker compose -f compose.yaml -f compose.live.yaml up -d --build
docker logs orchestra-api | tail -30
```
**Watch this one.** It is the first container boot without
`docker-api-entrypoint.sh`. Expected: the process starts, reads config from the
bind-mounted `config.jsonc`, and picks up secrets from `.env`. Confirm
provenance afterwards at `GET /v1/admin/diagnostics` — its `build` object
should match `97a9c65`, not an older revision.
### 2. Vikunja 350 — the single controlled live OpenCode continuity run
Still the project's real blocker: every "Closed" row in `AUDIT.md` rests on unit
tests. Prerequisites:
- Probe herdr reachability **directly**. `main.go` logs connection *failures*
only and never logs success, so absence of a log line means up, not down.
- Check whether the stuck task is still stuck: workspace `wA`, id
`06FT6CKD9Y98AZRX6X8K3QXFZG`, opencode, pane `wA:p1`,
`agent_status: "blocked"`. Code fixes do not unstick an orphaned pane; it
needs a manual kill/restart, and `CLAUDE.md` requires asking before
destructive herdr calls.
### 3. Vikunja 353 — constant-time compares in `internal/federation`
`federation.go:368` (per-worker bearer token, checked on every authenticated
worker call — hottest path), `:343` (admission shared secret). `:346` is a
re-registration equality check, arguably not an auth decision. Use the inline
`subtle.ConstantTimeCompare` idiom from `internal/authz/authz.go:111,241`, and
preserve the empty-token-means-disabled semantics.
Note `internal/provider/provider.go:333` is already correct (`hmac.Equal`).
### 4. Reconcile `TOKEN_MINIMAL_WORKFLOW_PLAN.md` against `AUDIT.md`
534 lines, dated 2026-07-29, status "proposed" — describes the target
unattended workflow (Vikunja ingest → router → worker syncs checkout →
immutable `TASK.md` → deterministic gates → push → Vikunja reflection) with an
explicit model-token boundary. Never audited. `WEB_UI_PLAN.md` is in the same
position. Decide for each: current plan, or obsolete.
### 5. Vikunja 351 — extract `main.go`'s route closures (deferred)
`97a9c65` removed ~80 lines, so this is marginally less pressing. Do it
incrementally, next time a route is added.
---
## Dead code left deliberately in place
`herdr.ClaudeStopHookUsage` (`internal/herdr/occupancy.go:106`) and
`herdr.OpenCodeStatus` (`:263`) have no non-test callers now that the hook
scripts are gone. Kept as quota-source plumbing a future harness path may
want. If nothing claims them, they are a clean deletion.
## Still-open assumption
`REVIEW.md` assumed `clients/herdr-bridge.go` was deployed; the operator
confirmed on 2026-07-31 that it is not, now that workers carry cross-machine
work. The deletion in `97a9c65` rests on that confirmation, not on a probe —
all six herdrs were unreachable as of 2026-07-29 and no probe was run this
session. If a bridge process turns up running on homesrv, it is running from an
installed binary with no source in the repo; recover it from `56f5aac`, which
tracked the files before `97a9c65` removed them.