Introduces the browser-facing surface and the worker-side protocol that
backs it:
- internal/ui: joined read model plus per-task lifecycle and approval
controls, kept separate from the raw endpoints workers and harnesses
depend on.
- internal/webui + web/: Vite/React app, build output embedded via
go:embed and served as an SPA fallback.
- federation: per-(worker, task) captures with a monotonic revision that
advances only when pane text actually changes, and a command queue
restricted to grant_approval / deny_approval, each bound to the capture
revision the operator acted on.
- orchestra-worker: publishes captures and executes commands only after
re-reading the pane and confirming the revision still matches. Sends
keystrokes only for a visible y/n prompt or OpenCode's fully labelled
selector, and refuses to deny through that selector rather than guess
at unobservable navigation.
This is the ownership boundary AUDIT.md's B14 and B17 call for: approval
becomes an explicit, revision-bound operation executed by the worker that
owns the pane, instead of a side effect of prompting over a
coordinator-driven remote socket.
Also ignores the web build inputs and outputs. node_modules ships vendored
Go packages, so go build and go test walk into it if it is merely
untracked; both node_modules and .node_modules are excluded.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01535A3Y8RtkAi8wYuWhtkEd
functionCommandPalette({close}:{close:()=>void}){constnav=useNavigate(),qc=useQueryClient(),input=useRef<HTMLInputElement>(null),[term,setTerm]=useState('');useEffect(()=>{input.current?.focus()},[]);constchoose=(id:string)=>{if(id==='board')nav('/');if(id==='workers')nav('/workers');if(id==='new'){nav('/');window.dispatchEvent(newEvent('orchestra:new-task'))}if(id==='refresh')qc.invalidateQueries();close()};constcommands=[['board','Go to dispatch board','G then B'],['new','Create a new task','N'],['workers','View worker pool','G then W'],['refresh','Refresh live data','R']]asconst;constmatches=commands.filter(c=>c[1].toLowerCase().includes(term.toLowerCase()));return<divclassName="palette-backdrop"onMouseDown={close}><sectionclassName="palette"role="dialog"aria-modal="true"aria-label="Command palette"onMouseDown={e=>e.stopPropagation()}><inputref={input}value={term}onChange={e=>setTerm(e.target.value)}onKeyDown={e=>{if(e.key==='Escape')close();if(e.key==='Enter'&&matches[0])choose(matches[0][0])}}placeholder="Find a command…"aria-label="Find a command"/><divclassName="palette-list">{matches.map(([id,name,key])=><buttonkey={id}className="palette-command"onClick={()=>choose(id)}><span>{name}</span><kbd>{key}</kbd></button>)}{!matches.length&&<pclassName="palette-empty">Nomatchingcommand.</p>}</div><pclassName="palette-foot"><kbd>↵</kbd>run<kbd>esc</kbd>close</p></section></div>}
functionCreate({close}:{close:()=>void}){constqc=useQueryClient(),nav=useNavigate(),first=useRef<HTMLInputElement>(null),[title,setTitle]=useState(''),[description,setDescription]=useState(''),[project,setProject]=useState('default'),[capability,setCapability]=useState(''),[advanced,setAdvanced]=useState(''),[formError,setFormError]=useState('');useEffect(()=>{first.current?.focus()},[]);constm=useMutation({mutationFn:()=>{letextra:Record<string,unknown>={};if(advanced.trim()){try{extra=JSON.parse(advanced)}catch{returnPromise.reject(newError('Additional fields must be valid JSON.'))}}returnapi.create({...extra,source:'web',external_id:crypto.randomUUID(),project,title,description,capability:capability.split(',').map(x=>x.trim()).filter(Boolean)})},onSuccess:(e:any)=>{qc.invalidateQueries({queryKey:['overview']});nav('/tasks/'+e.task_id)}});constsubmit=(e:React.FormEvent)=>{e.preventDefault();setFormError('');if(title.trim().length<3){setFormError('Give the task a title of at least three characters.');return}if(description.trim().length<10){setFormError('Add enough immutable instructions for an agent to act safely.');return}m.mutate()};return<divclassName="modal-backdrop"onMouseDown={close}><sectionclassName="create modal"role="dialog"aria-modal="true"aria-labelledby="new-task-title"onMouseDown={e=>e.stopPropagation()}><divclassName="modal-heading"><div><pclassName="eyebrow">Dispatchnewwork</p><h2id="new-task-title">Newtask</h2></div><buttonclassName="icon-button"onClick={close}aria-label="Close task creation">×</button></div><p>Givetheharnessaclearobjectiveandimmutableoperatinginstructions.</p><formonSubmit={submit}noValidate><label>Tasktitle<inputref={first}requiredvalue={title}onChange={e=>setTitle(e.target.value)}placeholder="e.g. Add the health endpoint"autoComplete="off"/></label><label>Project<inputrequiredvalue={project}onChange={e=>setProject(e.target.value)}placeholder="Project name"autoComplete="off"/></label><label>Capabilities<small>Optional,comma-separated</small><inputvalue={capability}onChange={e=>setCapability(e.target.value)}placeholder="go, docker"autoComplete="off"/></label><label>Immutableinstructions<textarearequiredvalue={description}onChange={e=>setDescription(e.target.value)}placeholder="Describe the outcome, constraints, and evidence required."/></label><details><summary>Additionalacceptedtaskfields</summary><textareaaria-label="Additional task fields JSON"placeholder={'{"parent":"…","inherent_priority":1,"due":"2026-07-30T12:00:00Z"}'}value={advanced}onChange={e=>setAdvanced(e.target.value)}/></details><divclassName="modal-actions"><buttontype="button"className="quiet-button"onClick={close}>Cancel</button><buttondisabled={m.isPending}>{m.isPending?'Creating task…':'Create task'}</button></div>{(formError||m.error)&&<spanclassName="error"role="alert">{formError||String(m.error)}</span>}</form></section></div>}
functionApproval({approval,onAction,pending}:{approval:PendingApproval;onAction:(a:string)=>void;pending:boolean}){constref=useRef<HTMLDivElement>(null),grant=approval.kind!=='unknown',deny=approval.kind==='shell';useEffect(()=>{constnode=ref.current;if(!node)return;node.querySelector<HTMLButtonElement>('button:not(:disabled)')?.focus();consttrap=(e:KeyboardEvent)=>{if(e.key==='Escape')return;if(e.key!=='Tab')return;constfocus=[...node.querySelectorAll<HTMLElement>('button:not(:disabled)')],i=focus.indexOf(document.activeElementasHTMLElement);if(e.shiftKey&&i<=0){e.preventDefault();focus.at(-1)?.focus()}elseif(!e.shiftKey&&i===focus.length-1){e.preventDefault();focus[0].focus()}};node.addEventListener('keydown',trap);return()=>node.removeEventListener('keydown',trap)},[]);return<divclassName="approval-backdrop"><sectionref={ref}className="approval"role="dialog"aria-modal="true"aria-labelledby="approval-title"><pclassName="eyebrow">Harnessgate</p><h2id="approval-title">Permissionrequired</h2><p>{approval.summary}</p><pre>{approval.command||approval.diff||'The harness prompt could not be parsed safely.'}</pre><small>Pane{approval.pane_id}·capturerevision{approval.capture_revision}·detected{newDate(approval.detected_at).toLocaleString()}</small>{approval.kind==='opencode_once'&&<p>ApprovingsendsEntertoOpenCode’sexplicitlydisplayed<b>Allowonce</b>selection.Rejectisunavailablebecausetheselectedpositioncannotbeverifiedfromthecapture.</p>}{!grant&&<prole="alert">Thispromptcannotbesafelyexecutedbecauseitsexactconfirmationisunknown.</p>}<div><buttondisabled={!grant||pending}onClick={()=>onAction('grant_approval')}>{approval.kind==='opencode_once'?'Allow once':'Approve'}</button><buttondisabled={!deny||pending}title={deny?'':'This selector does not expose a verifiable reject position.'}onClick={()=>onAction('deny_approval')}>Reject</button></div></section></div>}
functionLifecycle({action,mutate,pending}:{action:Action;mutate:(action:string,body?:object)=>void;pending:boolean}){const[value,setValue]=useState('');if(action.id==='handoff')return<buttondisabled={!action.enabled||pending}title={action.reason}onClick={()=>mutate('handoff')}>Requesthandoff</button>;if(action.id==='complete')return<formonSubmit={asynce=>{e.preventDefault();try{constref=awaitapi.upload(value);mutate('complete',{report_ref:ref,receipt:{source:'web',completed_at:newDate().toISOString()}})}catch(err){alert(String(err))}}}><labelclassName="sr-only"htmlFor="completion-report">Completionreport</label><textareaid="completion-report"requiredplaceholder="Completion report, stored as evidence"value={value}onChange={e=>setValue(e.target.value)}/><buttondisabled={!action.enabled||pending}>Completetask</button></form>;constfield=action.id==='block'?'blocker':'reason';return<formonSubmit={e=>{e.preventDefault();mutate(action.id,{[field]:value})}}><labelclassName="sr-only"htmlFor={'action-'+action.id}>{field}</label><inputid={'action-'+action.id}requiredplaceholder={field==='blocker'?'What is blocking progress?':'Reason'}value={value}onChange={e=>setValue(e.target.value)}/><buttondisabled={!action.enabled||pending}title={action.reason||action.needs?.join(',')}>{actionLabel[action.id]||action.id}</button></form>}
functionTaskDetail(){const{taskID=''}=useParams(),qc=useQueryClient();constq=useQuery({queryKey:['task',taskID],queryFn:()=>api.detail(taskID),refetchInterval:3000});constm=useMutation({mutationFn:({action,body}:{action:string;body?:object})=>api.action(taskID,action,body),onSuccess:()=>qc.invalidateQueries({queryKey:['task',taskID]})});if(q.isLoading)return<mainclassName="page loading">Fetchingtaskrecord…</main>;if(q.error)return<mainclassName="page error"role="alert">Taskunavailable:{String(q.error)}</main>;constd=q.data!;return<mainclassName="page"><LinkclassName="back"to="/">←Dispatchboard</Link><divclassName="page-heading"><div><pclassName="eyebrow">Taskrecord</p><h1>{d.task.title||d.task.id}</h1></div><pclassName="machine">{d.task.id}</p></div>{d.session?.pending_approval&&<Approvalapproval={d.session.pending_approval}pending={m.isPending}onAction={a=>m.mutate({action:a})}/>}<divclassName="detail-grid"><divclassName="surface"><h2>Instructions</h2><pclassName="task-description">{d.task.description||'No immutable instructions were recorded.'}</p><divclassName={'task-state state-'+d.task.state}>{label[d.task.state]}</div><h2>Livecapture</h2><pre>{d.session?.capture?.text||d.session?.blocker||'No live capture is available for this task.'}</pre><h2style={{marginTop:20}}>Actions</h2><divclassName="actions">{d.actions.map(a=><Lifecyclekey={a.id}action={a}pending={m.isPending}mutate={(action,body)=>m.mutate({action,body})}/>)}</div>{m.error&&<pclassName="error"role="alert">{String(m.error)}</p>}</div><asideclassName="surface"><h2>Session</h2><ulclassName="meta"><li><span>Project</span><spanclassName="machine">{d.task.project}</span></li><li><span>State</span><spanclassName="machine">{d.task.state}</span></li><li><span>Harness</span><spanclassName="machine">{d.session?.harness_id||'—'}</span></li><li><span>Pane</span><spanclassName="machine">{d.session?.pane_id||'—'}</span></li><li><span>Leaseends</span><spanclassName="machine">{d.session?.lease_until?newDate(d.session.lease_until).toLocaleString():'—'}</span></li></ul>{d.handoff_ref&&<p><LinkclassName="back"to={'/artifacts/'+d.handoff_ref}>Viewhandoff→</Link></p>}{d.report_ref&&<p><LinkclassName="back"to={'/artifacts/'+d.report_ref}>Viewreport→</Link></p>}</aside></div><divclassName="surface"style={{marginTop:20}}><h2>Timeline</h2><olclassName="timeline">{d.events.map(e=><likey={e.id}><span>{e.type}</span><time>{newDate(e.at).toLocaleString()}</time></li>)}</ol></div></main>}
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.