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:
2026-08-26 18:31:20 +04:00
parent 97a9c65302
commit 7f12c7fc37
78 changed files with 16417 additions and 352 deletions
+87 -8
View File
@@ -1,8 +1,87 @@
import type { Detail, Overview } from './types'
function sessionExpired(r:Response){if(r.status===401)window.dispatchEvent(new Event('orchestra:unauthorized'))}
async function request<T>(path:string, init?:RequestInit):Promise<T>{const r=await fetch(path,{credentials:'same-origin',headers:{'Content-Type':'application/json',...init?.headers},...init});if(!r.ok){sessionExpired(r);throw new Error(await r.text())}return r.json() as Promise<T>}
async function text(path:string){const r=await fetch(path);if(!r.ok)throw new Error(await r.text());return r.text()}
async function upload(body:string){const r=await fetch('/v1/artifacts',{method:'POST',headers:{'Content-Type':'text/markdown'},body});if(!r.ok)throw new Error(await r.text());return (await r.json() as {ref:string}).ref}
async function login(username:string,password:string){const r=await fetch('/v1/ui/session',{method:'POST',credentials:'same-origin',headers:{'Content-Type':'application/json'},body:JSON.stringify({username,password})});if(!r.ok)throw new Error(await r.text())}
async function logout(){const r=await fetch('/v1/ui/session',{method:'DELETE',credentials:'same-origin'});if(!r.ok)throw new Error(await r.text())}
export const api={login,logout,overview:()=>request<Overview>('/v1/ui/overview'),detail:(id:string)=>request<Detail>('/v1/ui/tasks/'+id),artifact:(ref:string)=>text('/v1/ui/artifacts/'+ref),upload,create:(body:unknown)=>request('/v1/ui/tasks',{method:'POST',body:JSON.stringify(body)}),action:(id:string,action:string,body={})=>request<Detail>(`/v1/ui/tasks/${id}/actions/${action}`,{method:'POST',body:JSON.stringify(body)})}
import type { CreatedEvent, Detail, Overview } from './types'
function sessionExpired(response: Response) {
if (response.status === 401) {
window.dispatchEvent(new Event('orchestra:unauthorized'))
}
}
async function responseError(response: Response) {
const message = (await response.text()).trim()
return new Error(message || `${response.status} ${response.statusText}`)
}
async function request<T>(path: string, init?: RequestInit): Promise<T> {
const response = await fetch(path, {
credentials: 'same-origin',
...init,
headers: init?.body
? { 'Content-Type': 'application/json', ...init.headers }
: init?.headers,
})
if (!response.ok) {
sessionExpired(response)
throw await responseError(response)
}
return response.json() as Promise<T>
}
async function text(path: string) {
const response = await fetch(path, { credentials: 'same-origin' })
if (!response.ok) {
sessionExpired(response)
throw await responseError(response)
}
return response.text()
}
async function upload(body: string) {
const response = await fetch('/v1/artifacts', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'text/markdown' },
body,
})
if (!response.ok) {
sessionExpired(response)
throw await responseError(response)
}
return ((await response.json()) as { ref: string }).ref
}
async function login(username: string, password: string) {
const response = await fetch('/v1/ui/session', {
method: 'POST',
credentials: 'same-origin',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ username, password }),
})
if (!response.ok) throw await responseError(response)
}
async function logout() {
const response = await fetch('/v1/ui/session', {
method: 'DELETE',
credentials: 'same-origin',
})
if (!response.ok) throw await responseError(response)
}
export const api = {
login,
logout,
overview: () => request<Overview>('/v1/ui/overview'),
detail: (id: string) => request<Detail>(`/v1/ui/tasks/${id}`),
artifact: (ref: string) => text(`/v1/ui/artifacts/${ref}`),
upload,
create: (body: unknown) =>
request<CreatedEvent>('/v1/ui/tasks', {
method: 'POST',
body: JSON.stringify(body),
}),
action: (id: string, action: string, body: object = {}) =>
request<Detail>(`/v1/ui/tasks/${id}/actions/${action}`, {
method: 'POST',
body: JSON.stringify(body),
}),
}