7f12c7fc37
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>
176 lines
8.1 KiB
Markdown
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.
|