Files
orchestra/progress.md
T
kami 02d93fb63d docs: track AUDIT.md remediation progress
Record what's landed this pass (B1, B2, B4, B8, S1, S5, S6) and what's
still open, so the next session doesn't have to re-derive it from git log.
2026-07-27 19:44:42 +04:00

238 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Orchestra progress
Updated: 2026-07-27
## AUDIT.md remediation — in progress
Working through `AUDIT.md`'s blocking/secondary defects in order of the
"suggested order of attack." Each item below is landed, tested, and
committed individually; see the git log for the exact commits.
Fixed so far:
- **B2** — adapters were looked up by `session.Harness` (the harness kind,
e.g. `"claude"`) in `Reconcile`/`expire`/`rotate`, but `AdapterFactory.Herdrs`
is keyed by herdr instance id (e.g. `"homesrv-claude"`). Every one of those
call sites silently no-opped. Added `Coordinator.adapterFor`, routed all
four call sites through it. Regression test registers an adapter under a
herdr-id key distinct from the harness kind and asserts rotation fires.
- **B1** — `CLIAdapter.Occupancy` called `a.Usage(s.PaneID)`, but the usage
readers want a filesystem path to session state, not a herdr pane id.
Added `herdr.Session.SessionFile` and per-harness resolution
(`ClaudeSessionFile` by newest-mtime under Claude Code's own project
directory; codex via the existing `CodexActiveUsage` sqlite discovery;
opencode refuses loudly — needs a live session id, not resolvable from the
worktree alone). A missing/unreadable session file is now a hard error,
surfaced via new `SessionHealth.Occupancy`/`OccupancyError` fields on
`GET /v1/tasks/{id}/health`, not a silent zero. **Still needs live
verification against a real Claude Code session** (the spec's own
acceptance bar for this phase) — not possible from this sandbox.
- **B4** — the router counted every `TaskReleased` (including rotation,
which *is* a `TaskReleased` carrying a valid `handoff_ref`) against
`MaxAttempts`, and double-counted by also incrementing on every
subsequent lease. A task that rotated twice hit the default
`MaxAttempts=3` and was killed. Now only a release without a
`handoff_ref` (expiry/crash) advances the counter.
- **B8** — `X-Orchestra-Surface: system` was reachable from an HTTP request
header in both `authz.HTTP` and `main.go`'s `surface` closure (the one
every handler actually calls). Since no deployment sets
`ORCHESTRA_SYSTEM_TOKEN`, this was an unauthenticated full-control bypass
reachable from any LAN caller. Both call sites now downgrade `system` to
`web` before doing anything else with it.
- **S1** — `Brief.From`/`To` and `GitSync.Branch`/`Head`/`Status` all shared
one JSON tag each (Go only honors the first `json:"..."` tag on a
combined field declaration). `go vet ./...` now passes clean.
- **S5** — `Store.Lease`/`ExpireLeases` set `Event.ID` to the task id, so
every lease of a task produced colliding event IDs. Now `domain.NewID()`.
- **S6** — the ingest dedup path returned `nil` (success) without
appending; `main.go` then returned an unrelated event with `201`. Added
`domain.ErrDuplicate` and `Store.TaskBySource`; `POST /v1/tasks` now
returns the existing task with `200` on a duplicate. Updated every other
`Append` caller (Gitea poll/webhook, JSONL ingest) to treat
`ErrDuplicate` as expected rather than a failure — without that, Gitea
polling would error out of its scan loop on the first already-ingested
issue in every batch.
Not yet started: B3 (no `TaskCompleted` producer / no Stop hook), B5/B6
(herdr protocol verification, Layer 3 wiring), B7 (quota projection has no
producer), S2S4, S7S11, and the Phase 0 live-herdr verification step that
several of the above still need. See `AUDIT.md` for the full plan.
## Current state
This is a working Go implementation of `orchestra-spec (1).md`'s Layer 13
(substrate, harness/rotation, continuity) plus a first cut of Layer 4
(surfaces). `go build ./...` and `go test ./...` both pass. The codebase is
small (~4.6k lines across `internal/{domain,store,provider,registry,router,
herdr,orchestrator,continuity,federation,delivery,authz,operations,admin}`
and `cmd/orchestra/main.go`).
Earlier revisions of this file accumulated a long, self-contradictory
chronological log — gaps were listed as open in one section and then claimed
closed in a later section, sometimes inaccurately. This revision replaces
that log with one audited snapshot. Treat prior git history of this file as
session notes, not as ground truth.
### Verified fixed this pass
- **Rotation emitted an invalid `TaskReleased` (the previously reported
highest-priority defect) — now fixed.** `internal/orchestrator.Coordinator.rotate`
built the release payload as `{"handoff_ref","reason"}`, omitting the
`anchor_sha` the spec (§4, §6.2) and `domain.ValidatePayload` require
whenever `handoff_ref` is present. `store.Append` would reject it, the
error was discarded (`if c.Store.Append(e) == nil`), and the lease/session
silently never rotated — the coordinator would just retry next tick with
no visible failure. Fixed by adding `herdr.HeadSHA(worktree)` and having
`rotate` populate `anchor_sha` from the real worktree HEAD before
appending; if the anchor can't be read, rotation now correctly skips that
tick (leaving the lease intact for TTL/next-tick reclaim) instead of
emitting a payload guaranteed to fail validation.
Covered by `internal/orchestrator/rotation_test.go`
(`TestRotationEmitsValidReleaseWithAnchorSHA`), which drives the real
`Coordinator.Monitor` loop against an actual git worktree and asserts the
emitted event passes `domain.ValidatePayload` with the correct SHA — the
previous end-to-end test masked this bug by manually crafting a
replacement `TaskReleased` event after observing the (silently failed)
adapter-side release.
- **The federation worker release endpoint had the same gap.** The
`/v1/federation/workers/{id}/release` handler (cmd/orchestra/main.go)
built `TaskReleased` from a request body with only `handoff_ref`, no
`anchor_sha`. Since a remote worker is the only party with the actual
checkout (§2.1: "validate against the local checkout wherever the harness
runs"), the endpoint now requires and forwards a 40-hex-char `anchor_sha`
in the request body, rejecting the call with 400 otherwise.
### Multi-repo Gitea ingestion (new)
- `provider.Gitea` gained an optional `Project` field and `SourceName()`
(`"gitea"` if unset, `"gitea:<project>"` if set) — the namespaced source
doubles as the `(source,external_id)` dedup key, so issue #7 in two
different repos never collides, and as the reflection dispatch key.
- New `provider.MultiGitea{Sources map[string]Gitea}` implements
`TaskReflector` by looking up `task.Source` and forwarding to the matching
Gitea instance — lets several Gitea repos (one per project) share one
`ReflectingSink`.
- New `provider.GiteaSourceConfig` + `LoadGiteaConfigs(path)` load a JSON
array of `{project,base_url,owner,repo,token,webhook_secret}`.
`main.go` reads this from `ORCHESTRA_GITEA_CONFIG` if set; each source
gets its own poll supervisor (`gitea:<project>`) and webhook path
(`/v1/providers/gitea/webhook/<project>`).
- The legacy single-repo env vars (`ORCHESTRA_GITEA_URL/TOKEN/OWNER/REPO/
WEBHOOK_SECRET`) still work unchanged when `ORCHESTRA_GITEA_CONFIG` is
unset — same unprefixed webhook path, same `project = ORCHESTRA_GITEA_REPO`
tagging, same dedup source `"gitea"` — so existing deployments and
already-configured Gitea webhooks need no changes.
- Added `internal/provider/gitea_test.go` — previously **there were zero
tests exercising the Gitea provider at all** despite progress.md's prior
claim of Gitea webhook/poll test coverage; that claim was not accurate.
New tests cover source-name namespacing, webhook signature
verification/rejection, project tagging, `MultiGitea` dispatch-by-source
(via two `httptest.Server`s, asserting only the right one is hit), and
`LoadGiteaConfigs` validation/duplicate-project rejection.
### Per-project repos (new)
- `registry.Project` gained optional `repo`/`worktree_root` fields. Each
project can now resolve its own git checkout rather than every project
sharing one global `ORCHESTRA_REPO`/`ORCHESTRA_WORKTREE_ROOT` — matches
spec §2.2 ("projects are first-class and extensible... the binding is a
field + a config entry, not a schema change"). `main.go` builds a
`orchestrator.PerProjectGitWorktrees` from the registry, falling back to
the global default for any project that omits these fields, so
single-repo deployments are unaffected. Covered by
`internal/orchestrator/worktrees_test.go`.
### Closed this pass (were open gaps as of the last snapshot)
- **Bus-level authorization.** `authz.AuthorizeEvent` is now enforced inside
`store.Append` itself — the single choke point every event passes through
(HTTP handlers, router, coordinator/rotation, providers, federation relay)
— not just at HTTP handlers. Event schema bumped to v2, which requires
every event to declare a `Surface`; a new `authz.System` surface (full
control) covers internal emitters (router leases/failures, coordinator
releases/blocks, standup advisory/apply). Schema v1 events on disk still
replay (tolerant reader). Covered by `internal/store/store_test.go` and
`internal/router/router_test.go` additions asserting a non-HTTP append
with no/wrong surface is rejected.
- **Dual quota windows.** `router.QuotaAvailability` now tracks a 5-hour
rolling window and a 7-day weekly window independently per harness
(`QuotaWindowLimits{FiveHour, Weekly}`), applying the conservative 80%
rule to each separately — a harness over threshold on either window is
unavailable. Replaces the old single-`Window` field. Covered by new
`router_test.go` cases for weekly-only and 5h-only exhaustion.
- **Turn-boundary detection made observable, not silently optional.**
Rotation still can't force a harness adapter to implement `TurnBoundary`
Face B, but an adapter that fails to answer it now blocks that tick's
release (never treats a failed check as "safe to proceed"), and any
adapter without the capability — or one whose check errors — increments
`MonitorHealth.TurnBoundaryDegraded`, exposed via the coordinator's health
endpoint so degraded-safety operation is visible, not silent.
- **Cross-machine lease correctness has a real test.**
`internal/integration/federation_lease_test.go`
(`TestCrossMachineLeaseAnchorAndQuotaArePerHost`) exercises a lease
claimed through the federation worker HTTP API, validates the anchor
against that worker's own local checkout (not the router's), and asserts
quota is accounted per-host. Spec §9 item 8 said "prove on the first
federated run" — this is that proof for the primitives that exist today
(registration, heartbeat, lease-claim); it does not yet run against two
real physical machines.
- **Fuzz coverage for lifecycle payload validation.**
`internal/domain/fuzz_test.go` adds `FuzzValidatePayload` and
`FuzzValidateEvent` covering all event types (including malformed nested
`receipt`/`knowledge` shapes) — asserts no panic and always a typed error
on adversarial input.
### Believed accurate from prior sessions (spot-checked, not exhaustively re-verified)
- Event log: append-only JSONL, versioned envelope (schema v1), snapshot
load/replay, CAS with content-hash verification at append.
- `domain.ValidatePayload` enforces required fields per event type,
including `expected_version`/`ttl` on `TaskLeased`, `anchor_sha` on
`TaskReleased` (now correctly emitted, see above), `report_ref`+`receipt`
on `TaskCompleted`, and `blocker` on `TaskBlocked`.
- Router: project→affinity→machine resolution, capability match, quota
availability at conservative 80% threshold, derived-importance ordering,
retry-then-`TaskFailed`.
- herdr adapters (Claude/Codex/opencode) with native occupancy readers,
optional `TurnBoundary`/`RotationSignal`/`PaneExit` capability interfaces,
bootstrap/lease/release/kill.
- Continuity: strict handoff schema/validation, CAS save/load, pickup
validation (HEAD match, dirty-file hashes, immutable `TASK.md` hash),
scratch-branch commit/push/pull helpers.
- Provider layer: JSONL watcher, Gitea webhook+poll with HMAC auth,
idempotent `(source,external_id)` dedup, terminal-state reflection,
supervised restart with backoff.
- Federation: worker registration, heartbeat/TTL offline detection, event
cursor polling/ack, lease claim endpoint.
- Authorization: bus-level capability table (notify-only / full / gated) is
applied to lifecycle and approval writes via `AuthorizeEvent`.
- Delivery: Telegram/ntfy fan-out for completion/failure/block/approval
events.
- `/readyz`, `/v1/brief`, `/v1/providers/health`, `/v1/standup` exist and
return real state (not stubs).
## Known open gaps (named, not silently assumed done)
- **Cross-machine lease correctness is proven at the primitive level, not on
real hardware.** `TestCrossMachineLeaseAnchorAndQuotaArePerHost` exercises
the federation worker HTTP API (registration, lease-claim, anchor
validation against the worker's own checkout, per-host quota) inside one
test process. Spec §9 item 8 says "prove on the first federated run" —
that means an actual homesrv/workpc pair over the real mesh, which this
repo cannot exercise by itself. Named here as the one item that needs a
live two-machine run to fully close, not more code.
- **Turn-boundary Face B still degrades to occupancy-only for adapters that
don't implement it**, by design — the spec's Face B is per-harness native
session state (Stop hook / rollout tail / SSE), which this repo can only
wire against a real running herdr+harness pair. The degradation is now
observable (`MonitorHealth.TurnBoundaryDegraded`) and blocks-on-failure
rather than silently proceeding, but whether Claude/Codex/opencode's
native hooks are wired in a live deployment is a deployment-config fact,
not something provable from source alone.
Everything else named as open in the previous snapshot (bus-level
authorization, dual 5h/weekly quota windows, fuzz coverage of lifecycle
payload validation) is now closed — see "Closed this pass" above. Broader
areas (provider layer, continuity, router matching, delivery, federation
registration) were spot-checked against the code and their tests and
matched their described behavior.