Hand off the burn-in: run 1 closed, run 2 ready to start

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-27 00:00:43 +04:00
parent fb7fd234ca
commit c833e0eb62
+135
View File
@@ -0,0 +1,135 @@
# Handoff — burn-in, run 1 closed, run 2 not started
Written 2026-08-26, late. Read with `BURNIN.md` (the runbook and the findings
ledger), `AUDIT.md`, and `CLAUDE.md`. This session stopped adding workflow
features and started running real tasks. Everything below was observed live, not
inferred.
## Deployed state
| Half | Revision | Evidence |
|---|---|---|
| Coordinator, homesrv container | `77a2b32` | `docker logs orchestra-api` prints it at startup, `/readyz` ready |
| Worker, workpc systemd | `77a2b32` | `journalctl -u orchestra-worker`, both harnesses serving |
Burn-in identity is `77a2b32`. Both halves report it, which was the precondition
for a conformance run. `deploy/build.sh [outdir] [rev]` builds both from a
detached worktree of that revision, so a shared checkout cannot leak
uncommitted work into a stamped binary.
Deploying the coordinator:
```sh
ssh kami@192.168.1.104
repo=/home/kami/apps/orchestra; rev=$(git -C $repo rev-parse HEAD)
bt=$(git -C $repo show -s --format=%cI $rev); tree=/tmp/orchestra-build-$rev
git -C $repo worktree add --detach --quiet $tree $rev
docker build --build-arg BUILD_REVISION=$rev --build-arg BUILD_TIME=$bt \
--build-arg BUILD_DIRTY=false -f $tree/Dockerfile.api \
-t orchestra-web-ui-orchestra-api $tree
git -C $repo worktree remove --force $tree
cd /home/kami/docker-apps/orchestra-web-ui
docker compose up -d --no-deps --force-recreate orchestra-api
```
Do not use `docker compose build`: its context is the live checkout, which is
shared. Installing the worker needs root, so it is always the operator's step.
## Another session owns 18 uncommitted paths
An auth and frontend session worked in this same checkout and has finished, but
its work is **uncommitted**: `internal/authn/*`, `web/*`,
`internal/webui/assets/*`, `AGENTS.md`, `deploy/DEPLOYMENT.md`,
`deploy/orchestra.env.example`, `AUDIT.md`, and an extension to
`deploy/build.sh` that also builds `cmd/orchestra-user`. Do not commit those on
its behalf, and do not use `git add -A`. Commit by path.
The deployed coordinator does not contain that work. Deploying it means
committing it first.
One thing to check: this session's first commit, `7f12c7f`, predates that
discipline and used `git add -A`. It swept in whatever was uncommitted then,
including `web/src` and `internal/authn`.
## Where the burn-in stands
**Run 1 is closed as a diagnostic, not a conformance run.** Task
`06G3YR34117MAYT6KEAC9RJHD0` from issue `kami/test-e2e#1` needed three manual
leases, and its authority was rendered before the issue-body fix. It was
released with reason `abandoned diagnostic run` and sits at `attempt: 3`, so the
router will fail it.
What it proved anyway: `.orchestra/launch.md` renders authority order, the
ambiguity ladder, the phase brief, the standing decision set and a verified git
state; an opencode agent respected `frame` and changed no code; and a bad base
checkout nacked cleanly back to `queued` with no orphan pane.
**Run 2 has not started.** Every precondition is met except creating the task.
## Do this first
1. Create one ordinary issue in `kami/test-e2e`, with a real body. The body is
the goal and acceptance; the title alone is not enough.
2. **Do not lease it by hand.** Autonomous assignment is the thing under test.
3. Watch, in this order:
- `GET /v1/router/health` — if the task is not assigned within a minute, this
names the gate that refused it. That is the finding; stop there.
- `journalctl -u orchestra-worker -f` — launch, nack or error.
- `cat /tmp/test-e2e-worktrees/<task-id>/.orchestra/launch.md` — the highest
value artifact. Ask only whether the agent knows what the task wants, what
was most recently decided, which phase it is in, what is merely history,
and what to do next.
4. Compare that against what the model does. Classify before changing anything:
authority, context-selection, lifecycle, adapter/harness, model-following, or
operator-policy. A prompt change is only ever the answer to a
model-following failure.
Success criteria for run 2, in order: issue ingested, router assigns
`workpc-opencode` on its own, `Store.Lease`, pre-lease reconcile against
`gitea:test-e2e` only, agent launches, `launch.md` carries a body-derived goal
and acceptance, `frame` behaves, and the phase advances with no manual
lifecycle intervention.
## Findings ledger
Fixed this session, all verified by test and most seen live:
- **F1**, authority: a federated worker could not read `GET /v1/tasks/<id>/intent`, so every federated launch died with a 401. `2753a8d`.
- **F2**, authority: `Gitea.event` dropped the issue body, so every Gitea task ran on its title with `Acceptance: Not stated.` `09e572f`.
- **F3**, hygiene: the launch dump left `.orchestra/` untracked in a worktree whose own context claimed a clean tree. `a0209a2`.
- **F5**, lifecycle: every router eligibility gate now records a `router.Rejection` at `GET /v1/router/health`, including tasks skipped for retry backoff. No gate was weakened. `0ead6d2`, `4fbf3ac`.
- **F7**, security: an unset token means no check, so a full-control surface without a credential was an open control plane. Startup now refuses. `ORCHESTRA_TUI_TOKEN` is set. `0ead6d2`.
- **F8**, correctness: reconciliation is bound to `task.Source`, so a comment on one repository can no longer become an authoritative decision for a same-numbered task in another. `0ead6d2`.
- **F11**, outage: `ValidateEvent` compared `until_ns` against `time.Now()`, so an expired lease event failed validation and `store.Open` refused the log tail. The API was in a restart loop. Validation is time-independent now. `77a2b32`.
Open, in the order this session would take them:
- **F6**, observability: worker health reports `active_task: null` while the store shows the task leased and herdr shows a live agent in its worktree.
- **F4**, adapter quality: `harness "opencode" has no session-file resolver`, so activity and thrash triggers are permanently degraded on opencode. Acceptable for a boring-success baseline; milestone and occupancy paths still work.
- **F9**, lifecycle: releasing a leased task through `POST /v1/tasks/<id>/release` requires supplying `harness_id` and `lease_epoch`, because `Store.Append` fences the event and the endpoint passes the body through. There is no operator escape hatch.
- **F10**, hygiene: a coordinator-side release under a live worker leaves the worker renewing a lease it no longer holds.
- **Unset gated tokens**: `ORCHESTRA_MCP_TOKEN`, `ORCHESTRA_MAVEN_TOKEN` and `ORCHESTRA_AGENT_TOKEN` are all empty, so those surfaces need no credential. Bounded by capability to reads plus the three request endpoints, which is why startup does not refuse. Worth closing.
## Things that will bite
- **`snapshot.json` lags the event log.** It showed 30 tasks while `/v1/tasks`
showed 34. Read the API or `events.jsonl`, never the snapshot, when checking
live state.
- **The MCP surface is a read-only diagnostic today.** `curl -H
'X-Orchestra-Surface: mcp' .../v1/tasks` works with no credential, which is
how this session read live state without the operator login. Mutations need
the TUI token from `.env` on homesrv.
- **`/tmp` is tmpfs.** `/tmp/test-e2e` (a **non-bare** clone, origin
`ssh://git@gitea.kvmx.ru:2222`) and `/tmp/test-e2e-worktrees` do not survive a
reboot, and `/tmp` has a 10-day sweep. A bare clone fails with `fast-forward
base checkout: fatal: this operation must be run in a work tree`.
- **The three queued `correx` tasks will never lease.** No worker declares that
project. `/v1/router/health` says so plainly now.
- **23 blocked test-e2e tasks are July leftovers.** They have been inert across
hours of `ResumeAnsweredBlockers`, so they are not a hazard, only noise.
- **A pane can push.** Panes run as `kami`, and pushing uses kami's registered
SSH identity over port 2222. Isolation needs a separate unix user, not an
environment scrub.
- **Ingest is now `kami/test-e2e`, not `correx`.** `ORCHESTRA_GITEA_REPO` was
switched for the burn-in; the previous `.env` is kept as
`.env.pre-burnin-20260826`. The project id must equal the repo name.