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>
100 lines
2.7 KiB
Go
100 lines
2.7 KiB
Go
package store
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"orchestra/internal/authz"
|
|
"orchestra/internal/domain"
|
|
)
|
|
|
|
func decisionPayload(t *testing.T, id, kind, subject, value string, supersedes ...string) []byte {
|
|
t.Helper()
|
|
p := map[string]any{
|
|
"decision_id": id, "kind": kind, "subject": subject, "value": value,
|
|
"source": map[string]any{"provider": "gitea", "external_id": "issue-1#c7"},
|
|
}
|
|
if len(supersedes) > 0 {
|
|
p["supersedes"] = supersedes
|
|
}
|
|
b, err := json.Marshal(p)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// A decision must land while the task is leased — that is the whole point,
|
|
// since the human corrects work already in flight — without touching task
|
|
// state or the current lease.
|
|
func TestDecisionAppendsUnderLiveLeaseWithoutDisturbingProjection(t *testing.T) {
|
|
dir := t.TempDir()
|
|
s, err := Open(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := s.Append(created("create")); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.Lease("task-1", "h1", time.Minute); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
before, _ := s.Task("task-1")
|
|
|
|
e := domain.Event{
|
|
ID: "e-d1", Type: domain.EventHumanDecisionRecorded, TaskID: "task-1",
|
|
Version: before.Version + 1, At: time.Now().UTC(),
|
|
Payload: decisionPayload(t, "d1", "correction", "strategy", "use b"),
|
|
Surface: string(authz.Web), SchemaVersion: domain.CurrentEventSchema,
|
|
}
|
|
if err := s.Append(e); err != nil {
|
|
t.Fatalf("decision rejected under live lease: %v", err)
|
|
}
|
|
after, _ := s.Task("task-1")
|
|
if after.State != before.State {
|
|
t.Fatalf("state changed %s -> %s", before.State, after.State)
|
|
}
|
|
if after.Lease == nil || *after.Lease != *before.Lease {
|
|
t.Fatalf("lease changed: %+v -> %+v", before.Lease, after.Lease)
|
|
}
|
|
if after.Description != before.Description || after.LifecyclePhase != before.LifecyclePhase {
|
|
t.Fatalf("contract fields changed: %+v", after)
|
|
}
|
|
|
|
intent, err := s.EffectiveIntent("task-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(intent.Decisions) != 1 || intent.Decisions[0].Value != "use b" {
|
|
t.Fatalf("standing set = %+v", intent.Decisions)
|
|
}
|
|
if intent.Decisions[0].Source.ExternalID != "issue-1#c7" {
|
|
t.Fatalf("provenance lost: %+v", intent.Decisions[0].Source)
|
|
}
|
|
|
|
// Same answer after a restart replay, from the log alone.
|
|
reopened, err := Open(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
replayed, err := reopened.EffectiveIntent("task-1")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(replayed.Decisions) != 1 || replayed.Decisions[0].ID != "d1" {
|
|
t.Fatalf("replayed standing set = %+v", replayed.Decisions)
|
|
}
|
|
}
|
|
|
|
func TestEffectiveIntentUnknownTask(t *testing.T) {
|
|
s, err := Open(t.TempDir())
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.EffectiveIntent("nope"); !errors.Is(err, domain.ErrNotFound) {
|
|
t.Fatalf("want ErrNotFound, got %v", err)
|
|
}
|
|
}
|