v3 workflow: intent, phases, review, submission, enforcement, burn-in
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>
This commit is contained in:
@@ -44,6 +44,11 @@ credential: its `build` object is the coordinator provenance. `GET
|
||||
/v1/federation/workers` shows every worker's `build`, supported projects, and
|
||||
worker-local health without SSH.
|
||||
|
||||
Build both binaries with `deploy/build.sh`, which stamps them from one commit
|
||||
and refuses a dirty tree. A burn-in run must never pair a new coordinator with
|
||||
an old worker, and matching revisions are how that is checked rather than
|
||||
assumed.
|
||||
|
||||
For the Docker coordinator deployment, provide the same provenance as build
|
||||
arguments (the Dockerfile intentionally cannot read `.git` from its build
|
||||
context):
|
||||
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
#!/bin/sh
|
||||
# Build the coordinator and the worker from one commit, with one stamp, so a
|
||||
# burn-in run can never pair a new coordinator with an old worker. Both
|
||||
# binaries then report the same revision at /v1/admin/diagnostics and in the
|
||||
# worker's registration, which is what makes deployed identity evidence rather
|
||||
# than assumption.
|
||||
#
|
||||
# Usage: deploy/build.sh [outdir]
|
||||
set -eu
|
||||
out=${1:-./build}
|
||||
cd "$(dirname "$0")/.."
|
||||
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||
echo "refusing to stamp a dirty tree with a commit revision" >&2
|
||||
exit 1
|
||||
fi
|
||||
rev=$(git rev-parse HEAD)
|
||||
built=$(git show -s --format=%cI HEAD)
|
||||
flags="-s -w -X orchestra/internal/buildinfo.Revision=$rev -X orchestra/internal/buildinfo.Time=$built -X orchestra/internal/buildinfo.Dirty=false"
|
||||
mkdir -p "$out"
|
||||
go build -trimpath -ldflags="$flags" -o "$out/orchestra" ./cmd/orchestra
|
||||
go build -trimpath -ldflags="$flags" -o "$out/orchestra-worker" ./cmd/orchestra-worker
|
||||
echo "$rev"
|
||||
@@ -16,6 +16,20 @@
|
||||
"worktree_root": "/var/lib/orchestra/worktrees/correx", // Optional per-project worktree dir.
|
||||
// Overrides global ORCHESTRA_WORKTREE_ROOT.
|
||||
"quality_gate": "go test ./... && go vet ./..." // Worker runs this before deterministic delivery.
|
||||
,
|
||||
// Cognitive phase path. Omit for the default
|
||||
// frame -> research -> plan -> implement -> review. A phase left out is
|
||||
// skipped, which is how a trivial project runs frame/implement/review.
|
||||
"work_phases": ["frame", "research", "plan", "implement", "review"],
|
||||
// Phase transitions the human must confirm before work continues. The
|
||||
// task blocks with block_reason "trajectory_gate" and the decision
|
||||
// packet arrives on the usual notification surfaces. Any reply is
|
||||
// recorded as a human decision and outranks the sealed plan.
|
||||
"trajectory_gate": { "plan_to_implement": "required" },
|
||||
// Per-task budget for bounded questions to the human. Default 6. Once
|
||||
// spent, the task blocks with block_reason "operator_required" and a
|
||||
// reply no longer resumes it, so a task cannot become an interview.
|
||||
"human_decisions": { "max_requests_per_task": 6 }
|
||||
},
|
||||
{
|
||||
"id": "maven",
|
||||
@@ -35,6 +49,7 @@
|
||||
{
|
||||
"id": "mainframe-claude-1", // Unique herdr id.
|
||||
"machine_id": "mainframe", // Which machine (above) this herdr runs on.
|
||||
"backend": "herdr", // Pane backend: "herdr" (default) or "tmux" (Claude only).
|
||||
// "address" omitted: falls back to the parent machine's address (used here since
|
||||
// this herdr's harness listens on the machine's default port).
|
||||
"harness": "claude", // Harness adapter to use: "claude" | "codex" | "opencode".
|
||||
@@ -50,10 +65,9 @@
|
||||
{
|
||||
"id": "satellite-claude-1",
|
||||
"machine_id": "satellite",
|
||||
"address": "10.0.0.11:9245", // Explicit override: this herdr listens on a non-default
|
||||
// port on its machine (e.g. multiple herdrs per machine).
|
||||
"backend": "tmux", // tmux entries are always worker-owned, including on the
|
||||
// coordinator machine; the worker reports local health.
|
||||
"harness": "claude",
|
||||
"protocol": "1",
|
||||
"capabilities": ["code"],
|
||||
"concurrency": 1,
|
||||
"quota_limit_5h": 20,
|
||||
|
||||
@@ -17,9 +17,40 @@ ORCHESTRA_PORT=9145
|
||||
# ORCHESTRA_FEDERATION_ADMIT_TOKEN=<homesrv-admission-secret>
|
||||
# ORCHESTRA_WORKER_HERDR_ID=workpc-opencode
|
||||
# ORCHESTRA_WORKER_HARNESS=opencode
|
||||
# Pane/process backend. Defaults to herdr. tmux is currently supported only
|
||||
# for Claude Code workers; Codex and OpenCode should remain on herdr.
|
||||
# ORCHESTRA_WORKER_BACKEND=herdr
|
||||
# ORCHESTRA_WORKER_HERDR=/home/orchestra/.config/herdr/herdr.sock
|
||||
# With ORCHESTRA_WORKER_BACKEND=tmux, ORCHESTRA_WORKER_HERDR is not used.
|
||||
# A bare value selects an isolated tmux -L socket; an absolute path selects -S.
|
||||
# ORCHESTRA_WORKER_TMUX_SOCKET=orchestra
|
||||
# Optional Claude executable override; defaults to resolving `claude` in PATH.
|
||||
# ORCHESTRA_WORKER_HARNESS_COMMAND=/home/orchestra/.local/bin/claude
|
||||
# ORCHESTRA_WORKER_STATE=/var/lib/orchestra-worker/state.json
|
||||
# ORCHESTRA_GIT_REMOTE=origin
|
||||
#
|
||||
# One worker process can serve several harnesses (several multiplexers and
|
||||
# several agents) by declaring them in a JSON file. The variables above then
|
||||
# describe nothing: each entry carries its own backend and harness. Every
|
||||
# declared harness is a separate federation identity, because the coordinator
|
||||
# authorizes lease calls by comparing the worker id against the lease's harness
|
||||
# id, so each needs its own token and registers separately.
|
||||
# ORCHESTRA_WORKER_HARNESS_CONFIG_FILE=/etc/orchestra/harnesses.json
|
||||
# [
|
||||
# {"id":"workpc-claude","harness":"claude","backend":"tmux",
|
||||
# "tmux_socket":"orchestra","command":"/home/kami/.local/bin/claude"},
|
||||
# {"id":"workpc-opencode","harness":"opencode","backend":"herdr",
|
||||
# "herdr":"127.0.0.1:9247"}
|
||||
# ]
|
||||
# Tokens belong in the environment rather than that file. Each id maps to
|
||||
# ORCHESTRA_WORKER_TOKEN_<ID>, uppercased with every other character underscored:
|
||||
# ORCHESTRA_WORKER_TOKEN_WORKPC_CLAUDE=<per-identity-secret>
|
||||
# ORCHESTRA_WORKER_TOKEN_WORKPC_OPENCODE=<per-identity-secret>
|
||||
# Each identity keeps its own state file. Set the directory holding them, or a
|
||||
# per-entry "state" path; the default derives one per id next to the worktrees.
|
||||
# ORCHESTRA_WORKER_STATE_DIR=/var/lib/orchestra-worker
|
||||
# ORCHESTRA_WORKER_ID still names the process for the single-harness form, where
|
||||
# it must equal ORCHESTRA_WORKER_HERDR_ID.
|
||||
ORCHESTRA_MACHINE_ID=homesrv # required when the registry has multiple machines
|
||||
|
||||
# Static project/machine/herdr topology (registry.Load). Required for
|
||||
@@ -70,6 +101,30 @@ ORCHESTRA_CONTEXT_WINDOW=200000
|
||||
#ORCHESTRA_GITEA_REPO=
|
||||
#ORCHESTRA_GITEA_WEBHOOK_SECRET=
|
||||
|
||||
# Human-input reconciliation. When a Gitea source is configured, issue
|
||||
# comments are imported as human decisions immediately before every lease,
|
||||
# and a source that cannot be read refuses the lease instead of letting a
|
||||
# successor resume from an older intent. Set to "off" only to keep leasing
|
||||
# while a source is known down; agents then run without newer comments.
|
||||
#ORCHESTRA_HUMAN_RECONCILE=off
|
||||
# How many consecutive failed turn-boundary reconciles ask the running session
|
||||
# to hand off. Default 3. The successor's pre-lease reconcile then fails closed
|
||||
# while the source is still down, so the task waits instead of running on
|
||||
# intent Orchestra cannot refresh.
|
||||
#ORCHESTRA_RECONCILE_FAILURE_HANDOFF=3
|
||||
|
||||
# Base branch for pull requests created by `task pr`. Defaults to master. The
|
||||
# pull request is created or updated for the task branch orchestra/<task-id>,
|
||||
# never duplicated: a repeated submission refreshes the same review.
|
||||
#ORCHESTRA_PR_BASE=master
|
||||
|
||||
# Whose words on a submitted pull request may reopen a task. ACTORS is an
|
||||
# allow-list of forge logins; empty trusts anyone not ignored, which is only
|
||||
# safe on a private forge with no bots. IGNORE_ACTORS always loses. Comments at
|
||||
# or before the submission never reopen anything.
|
||||
#ORCHESTRA_REVIEW_ACTORS=kami
|
||||
#ORCHESTRA_REVIEW_IGNORE_ACTORS=gitea-actions,orchestra-bot
|
||||
|
||||
# --- Delivery (notify-only surfaces) ---
|
||||
# Telegram: both required together.
|
||||
#ORCHESTRA_TELEGRAM_BOT_TOKEN=
|
||||
@@ -95,6 +150,16 @@ ORCHESTRA_WEB_PASSWORD_HASH=
|
||||
#ORCHESTRA_UI_INSECURE_COOKIE=1
|
||||
#ORCHESTRA_MCP_TOKEN=
|
||||
#ORCHESTRA_MAVEN_TOKEN=
|
||||
# The credential an in-pane coding session may hold. It buys read access plus
|
||||
# the three request endpoints (approval, decision-request, deferred) and
|
||||
# nothing else: phase, review, submission, completion and lease changes are
|
||||
# refused for this surface at the endpoint and again at the bus. This is the
|
||||
# ONLY Orchestra credential that may enter an agent pane; the forge, Vikunja,
|
||||
# worker and operator tokens must stay in the worker.
|
||||
#ORCHESTRA_AGENT_TOKEN=
|
||||
# The turn-boundary endpoint's own token (/v1/harness/turn), authenticated in
|
||||
# the handler rather than by the surface gate.
|
||||
#ORCHESTRA_HARNESS_TOKEN=
|
||||
# Inbound bearer tokens for the notify-only surfaces, separate from the
|
||||
# credentials used to *send* (ORCHESTRA_TELEGRAM_BOT_TOKEN, ORCHESTRA_NTFY_TOKEN).
|
||||
# S12: ORCHESTRA_NTFY_TOKEN used to serve both roles, so configuring ntfy
|
||||
|
||||
Reference in New Issue
Block a user