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>
8.8 KiB
Handoff — orchestra v3 intent/context/workflow foundation
Written 2026-08-26. Read this with CLAUDE.md and AUDIT.md. Everything below
was verified with go build ./... && go vet ./... && go test ./... passing, 20
test packages, at the end of the session.
What this session built
Eight units, in this order. Each one landed complete with tests before the next started.
- Human decision events plus a reducer.
- Source cursors and reconcile-before-launch.
- Turn-boundary reconciliation for a live lease.
internal/agentctxas the single renderer.- ace-fca phases with sealed artifacts.
- Federated turn decision, then the trajectory gate.
- Bounded grilling.
- Independent review,
task prenforcement, then human review reflection.
The loop now closes:
task → frame → research → plan → implement → ai review → task pr
→ human review ─┬─ comments → implementation
└─ merge → completed
The invariants that hold, and where they are enforced
| Invariant | Enforced at |
|---|---|
| Human decisions outrank contract, plan, and handoff | domain.ReduceIntent, rendering order in agentctx.Build |
| Supersession is explicit, never inferred from subject | domain.ReduceIntent |
| Reduction is order-independent and replay-deterministic | domain.ReduceIntent, sorted by (At, ID) |
| No ownership begins without reconciling human input | Store.PreLease, called inside Store.Lease, the only minter of TaskLeased |
| Live sessions see corrections at a verified turn boundary | Coordinator.TurnDecision, Coordinator.RemoteTurn |
| One renderer decides what an agent sees | agentctx.Build, sole caller of every context section |
| A phase reads sealed artifacts, never prior conversations | agentctx.renderSealed, phase table |
| Leaving research or plan requires a sealed artifact | Store.Append, WorkPhaseChanged branch |
| Only Orchestra changes phase | operations.AdvanceWorkPhase, domain.CanTransitionPhase |
| One question at a time, bounded, with a finite budget | operations.RequestHumanDecision |
| Review is independent of the implementation | agentctx.Build excludes handoff and research in review phase |
| Review is bound to one commit | Task.ReviewTargetSHA, Task.ReviewSatisfied |
| Submission requires gate sha == review sha == head sha | domain.CheckSubmission |
| Submission is idempotent and not completion | Task.Submitted, StateInReview |
| Only the bound pull request can move its task | operations.ReflectSubmission |
| Completion requires an actual merge | operations.ReflectSubmission, domain.CompletionReceipt |
Package map for the new work
internal/agentctx/ the only renderer of model-facing context
internal/human/ human input, reconciliation, trust, PR observations
internal/review/ review evidence, findings, reviewer instructions
internal/workphase/ sealed research and plan artifacts
internal/domain/ decision.go, decision_request.go, workphase.go, submission.go
internal/operations/ workphase.go, trajectory.go, human_decision.go,
review.go, submission.go, reflect.go
internal/store/ cursor.go, plus projections in store.go
internal/provider/ gitea_comments.go, gitea_pr.go
New event types, all validated in domain.ValidateEvent: HumanDecisionRecorded,
HumanDecisionSuperseded, WorkPhaseChanged, DeferredFindingRecorded,
ReviewRecorded, TaskSubmitted, TaskChangesRequested.
New task state: in_review. New block reasons: trajectory_gate,
human_decision, operator_required.
Things that will bite the next session
bootstrapPromptandtaskLaunchPromptare gone. Do not reintroduce a second renderer.grep 'prompt = ' internal cmdshould only findhandoffPromptandconventionsPromptininternal/herdr, which request output rather than supply authority.herdr.Sessionis no longer comparable with==. It carriesDeliveredDecisions []string.cmd/orchestra-worker/main.go:584usesreflect.DeepEqual.- The reflector must not move behind
PreLease. An in-review task cannot be leased, so a pre-lease hook cannot observe the feedback that reopens it. go vetcatchest.Context()becausego.moddeclares go1.22 while the toolchain is newer. Usecontext.Background()in tests.- Two pre-existing gofmt offenders,
internal/provider/provider.goandinternal/webui/webui.go, are untouched by this work. Ignore them or fix them deliberately. - The router still swallows lease errors with a bare
continue(internal/router/router.go:197). That cost real debugging time this session when aPreLeaserefusal looked like "no candidates".
Deliberate gaps, with reasons
- Turn-boundary reconcile failure is non-fatal. Recorded in
MonitorHealth.Sessions[id].LastError, and the turn continues. Blocking would freeze live sessions during a source outage without making their intent less stale. The escalation toprepare_handoffafter repeated failures is deliberately not built; wait for evidence. - The federated worker has no turn-decision path of its own. It reports its
locally-evaluated verdict to
POST /v1/federation/turnand delivers what comes back. It has noprepare_handoffescalation either. - Admission for grilling is stated, not enforced. Orchestra cannot mechanically tell a repo-answerable question from a real one without another semantic judge. What is enforced: bounds, budget, one-at-a-time, and Orchestra owning the lifecycle.
- Vikunja is not marked in-review on submission. No Vikunja mutation client
exists in this repo.
Task.Submissionholds everything a reflector needs. - Deferred findings do not become follow-up tasks yet.
operations.DeferredFindingslists them; creating tasks is a separate step. - The review endpoint requires a full-control surface, so a gated agent surface cannot seal a review. That is the conservative default until capability restrictions land.
GiteaPublisheris untested against a live Gitea. Its PR create, update, read, and comment paths are shaped from the API docs, not from a live probe. Verify against the real instance before trusting them.
New configuration
deploy/config.example.jsonc, per project:
"work_phases": ["frame", "research", "plan", "implement", "review"],
"trajectory_gate": { "plan_to_implement": "required" },
"human_decisions": { "max_requests_per_task": 6 }
deploy/orchestra.env.example:
ORCHESTRA_HUMAN_RECONCILE=off # disable pre-lease reconciliation
ORCHESTRA_PR_BASE=master # pull request base branch
ORCHESTRA_REVIEW_ACTORS=kami # who may reopen a submitted task
ORCHESTRA_REVIEW_IGNORE_ACTORS=... # bots, always loses
Nothing in this session's work changes behaviour on a deployment with no Gitea source configured. Reconciliation, submission publishing, and reflection all stay inert without one.
Next unit: capability restrictions
This is the last item in the build order, and its point is narrow: the workflow exists now, so what remains is stopping an agent from stepping around it.
Agent sessions should get:
repo read/write
tests, build
task checkpoint
task decision-request
task deferred
They should not get:
vikunja mutation token
gitea administrative token
direct lifecycle mutation
task completion API
push or pull-request creation
phase transition
review sealing
submission
Orchestra keeps: push policy, pull request creation, task status, session state, decision persistence, phase transitions, review sealing, submission, and completion.
Prompt rules stay advisory. The enforcement is capability boundaries plus legal
transitions, which is what internal/authz already models. Start there:
CapabilityFor currently gives mcp and maven GatedWrite, and CanEmit
allows only ApprovalRequested at that level. Decide which of the new event
types an agent surface may emit, then make the endpoints agree.
Two concrete questions to settle first:
- Should a reviewing agent seal its own review through a gated surface, or should the worker seal it on the agent's behalf? Today it needs full control.
- Does the harness token surface (
/v1/harness/turn) need its own capability level, distinct frommcp?
Acceptance proofs worth reading before changing anything
internal/integration/acefca_test.go— four proofs: phase boundaries carry authority, the trajectory gate correction outranks the sealed plan, a blocking question resumes with the answer on top, review is independent, and a submitted task is not reassigned.internal/integration/reconcile_launch_test.go— reconciliation is upstream of every agent start, and an unreachable source refuses the lease.internal/operations/reflect_test.go— the full human loop from rejection to merge.