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>
194 lines
8.8 KiB
Markdown
194 lines
8.8 KiB
Markdown
# 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.
|
|
|
|
1. Human decision events plus a reducer.
|
|
2. Source cursors and reconcile-before-launch.
|
|
3. Turn-boundary reconciliation for a live lease.
|
|
4. `internal/agentctx` as the single renderer.
|
|
5. ace-fca phases with sealed artifacts.
|
|
6. Federated turn decision, then the trajectory gate.
|
|
7. Bounded grilling.
|
|
8. Independent review, `task pr` enforcement, 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
|
|
|
|
- **`bootstrapPrompt` and `taskLaunchPrompt` are gone.** Do not reintroduce a
|
|
second renderer. `grep 'prompt = ' internal cmd` should only find
|
|
`handoffPrompt` and `conventionsPrompt` in `internal/herdr`, which request
|
|
output rather than supply authority.
|
|
- **`herdr.Session` is no longer comparable with `==`.** It carries
|
|
`DeliveredDecisions []string`. `cmd/orchestra-worker/main.go:584` uses
|
|
`reflect.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 vet` catches `t.Context()`** because `go.mod` declares go1.22 while the
|
|
toolchain is newer. Use `context.Background()` in tests.
|
|
- **Two pre-existing gofmt offenders**, `internal/provider/provider.go` and
|
|
`internal/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 a `PreLease` refusal 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 to `prepare_handoff` after 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/turn` and delivers what
|
|
comes back. It has no `prepare_handoff` escalation 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.Submission` holds everything a reflector needs.
|
|
- **Deferred findings do not become follow-up tasks yet.**
|
|
`operations.DeferredFindings` lists 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.
|
|
- **`GiteaPublisher` is 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:
|
|
|
|
```jsonc
|
|
"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:
|
|
|
|
1. 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.
|
|
2. Does the harness token surface (`/v1/harness/turn`) need its own capability
|
|
level, distinct from `mcp`?
|
|
|
|
## 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.
|