Bind a manual sign-off to the tree it was given against
F63, found live on run 19. A manual check on these projects is a human reading what the code prints. RecordPlanPhaseVerification asked only whether a sign-off for that plan and phase existed, and one exists forever, so rerunning a phase's automated checks at a new commit carried the human half along with it. The rig proved it twice: two operator commits and two re-verification requests, each coming back verified without anyone looking. The reducer now records which tree the human confirmed, the record carries it forward as provenance, and a run whose commit does not match it waits for the human again. A sign-off given before any run has no confirmed tree and still counts, so the ordinary ordering is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CVbaKucEYBjMqVeUgJUsc1
@@ -0,0 +1,17 @@
|
|||||||
|
# orchestra ui mockups
|
||||||
|
|
||||||
|
`final/` contains the nine accepted/current screen directions:
|
||||||
|
|
||||||
|
1. dashboard
|
||||||
|
2. task detail
|
||||||
|
3. terminal live pane
|
||||||
|
4. tasks
|
||||||
|
5. decisions
|
||||||
|
6. workers
|
||||||
|
7. projects
|
||||||
|
8. review
|
||||||
|
9. settings
|
||||||
|
|
||||||
|
`iterations/` contains every generated mockup from the design session, including superseded variants.
|
||||||
|
|
||||||
|
`orchestra-ui-spec.md` is the accompanying implementation/design specification.
|
||||||
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
@@ -55,6 +55,13 @@ type PlanPhaseRecord struct {
|
|||||||
// EvidenceRef is the CAS ref of the captured command output.
|
// EvidenceRef is the CAS ref of the captured command output.
|
||||||
EvidenceRef string `json:"evidence_ref,omitempty"`
|
EvidenceRef string `json:"evidence_ref,omitempty"`
|
||||||
At time.Time `json:"at"`
|
At time.Time `json:"at"`
|
||||||
|
// ManualAtSHA is the tree a human was actually looking at when they signed
|
||||||
|
// this phase off. A manual check on most projects is a human reading
|
||||||
|
// output, so a sign-off establishes something about one tree and nothing
|
||||||
|
// about the next one (F63). Rerunning the automated half re-establishes it
|
||||||
|
// at the new commit; the manual half has to be given again, and this is
|
||||||
|
// what makes the difference visible instead of assumed.
|
||||||
|
ManualAtSHA string `json:"manual_at_sha,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stale reports whether the tree has moved since this phase was verified. A
|
// Stale reports whether the tree has moved since this phase was verified. A
|
||||||
|
|||||||
@@ -124,7 +124,12 @@ func RecordPlanPhaseVerification(s *store.Store, project registry.Project, taskI
|
|||||||
// was established.
|
// was established.
|
||||||
record.Status = domain.PlanPhaseAwaitingManual
|
record.Status = domain.PlanPhaseAwaitingManual
|
||||||
}
|
}
|
||||||
if record.Status != domain.PlanPhaseInProgress && manuallySignedOff(s, t, phaseID) {
|
// Carry the confirmed tree forward as provenance. Without it a second
|
||||||
|
// rerun would compare against nothing and re-inherit the sign-off.
|
||||||
|
if prior, ok := t.PlanPhase(phaseID); ok {
|
||||||
|
record.ManualAtSHA = prior.ManualAtSHA
|
||||||
|
}
|
||||||
|
if record.Status != domain.PlanPhaseInProgress && manuallySignedOff(s, t, phaseID, record) {
|
||||||
record.Status = domain.PlanPhaseVerified
|
record.Status = domain.PlanPhaseVerified
|
||||||
}
|
}
|
||||||
if ref, err := s.PutArtifact(verificationEvidence(runs)); err == nil {
|
if ref, err := s.PutArtifact(verificationEvidence(runs)); err == nil {
|
||||||
@@ -135,7 +140,7 @@ func RecordPlanPhaseVerification(s *store.Store, project registry.Project, taskI
|
|||||||
payload := map[string]any{
|
payload := map[string]any{
|
||||||
"plan_ref": record.PlanRef, "phase_id": record.PhaseID, "status": string(record.Status),
|
"plan_ref": record.PlanRef, "phase_id": record.PhaseID, "status": string(record.Status),
|
||||||
"commands": record.Commands, "exit_codes": record.ExitCodes, "at_sha": record.AtSHA,
|
"commands": record.Commands, "exit_codes": record.ExitCodes, "at_sha": record.AtSHA,
|
||||||
"evidence_ref": record.EvidenceRef, "at": record.At,
|
"evidence_ref": record.EvidenceRef, "at": record.At, "manual_at_sha": record.ManualAtSHA,
|
||||||
}
|
}
|
||||||
if t.Lease != nil {
|
if t.Lease != nil {
|
||||||
payload["harness_id"], payload["lease_epoch"] = t.Lease.HarnessID, t.Lease.Epoch
|
payload["harness_id"], payload["lease_epoch"] = t.Lease.HarnessID, t.Lease.Epoch
|
||||||
@@ -149,9 +154,20 @@ func RecordPlanPhaseVerification(s *store.Store, project registry.Project, taskI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// manuallySignedOff reports whether a human has already approved this exact
|
// manuallySignedOff reports whether a human has already approved this exact
|
||||||
// phase of this exact plan. The subject carries both, so a later "looks good"
|
// phase of this exact plan, against the tree this run examined. The subject
|
||||||
// on an unrelated thread cannot satisfy a gate nobody was discussing.
|
// carries plan and phase, so a later "looks good" on an unrelated thread
|
||||||
func manuallySignedOff(s *store.Store, t domain.Task, phaseID string) bool {
|
// cannot satisfy a gate nobody was discussing.
|
||||||
|
//
|
||||||
|
// The tree matters as much as the subject (F63). A sign-off is a human saying
|
||||||
|
// they read what this code prints; an edit afterwards can change exactly that.
|
||||||
|
// A record whose ManualAtSHA names a different commit is therefore not signed
|
||||||
|
// off, and waits for the human again. A sign-off given before any run has no
|
||||||
|
// confirmed tree to compare against and still counts, which keeps the ordinary
|
||||||
|
// ordering unchanged.
|
||||||
|
func manuallySignedOff(s *store.Store, t domain.Task, phaseID string, record domain.PlanPhaseRecord) bool {
|
||||||
|
if record.ManualAtSHA != "" && record.ManualAtSHA != record.AtSHA {
|
||||||
|
return false
|
||||||
|
}
|
||||||
intent, err := s.EffectiveIntent(t.ID)
|
intent, err := s.EffectiveIntent(t.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import (
|
|||||||
|
|
||||||
const shaOne = "1111111111111111111111111111111111111111"
|
const shaOne = "1111111111111111111111111111111111111111"
|
||||||
const shaTwo = "2222222222222222222222222222222222222222"
|
const shaTwo = "2222222222222222222222222222222222222222"
|
||||||
|
const shaThree = "3333333333333333333333333333333333333333"
|
||||||
|
|
||||||
func planProject() registry.Project {
|
func planProject() registry.Project {
|
||||||
p := registry.Project{
|
p := registry.Project{
|
||||||
@@ -251,6 +252,14 @@ func TestLegacyPlanIsExplicitlyNonProgressable(t *testing.T) {
|
|||||||
// signOff records a human decision bound to one phase of one plan, which is
|
// signOff records a human decision bound to one phase of one plan, which is
|
||||||
// the only thing that satisfies a manual verification gate.
|
// the only thing that satisfies a manual verification gate.
|
||||||
func signOff(t *testing.T, s *store.Store, taskID, subject string) {
|
func signOff(t *testing.T, s *store.Store, taskID, subject string) {
|
||||||
|
t.Helper()
|
||||||
|
signOffFrom(t, s, taskID, subject, "signoff-"+subject)
|
||||||
|
}
|
||||||
|
|
||||||
|
// signOffFrom names the comment the sign-off came from. Two sign-offs on one
|
||||||
|
// subject are a real sequence once a rerun sends a phase back to the human,
|
||||||
|
// and provenance is unique per comment.
|
||||||
|
func signOffFrom(t *testing.T, s *store.Store, taskID, subject, externalID string) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
task, _ := s.Task(taskID)
|
task, _ := s.Task(taskID)
|
||||||
if err := s.Append(domain.Event{
|
if err := s.Append(domain.Event{
|
||||||
@@ -259,9 +268,60 @@ func signOff(t *testing.T, s *store.Store, taskID, subject string) {
|
|||||||
Payload: mustJSONBytes(t, map[string]any{
|
Payload: mustJSONBytes(t, map[string]any{
|
||||||
"decision_id": domain.NewID(), "kind": "answer", "subject": subject,
|
"decision_id": domain.NewID(), "kind": "answer", "subject": subject,
|
||||||
"value": "manual steps confirmed",
|
"value": "manual steps confirmed",
|
||||||
"source": map[string]any{"provider": "gitea", "external_id": "signoff-" + subject},
|
"source": map[string]any{"provider": "gitea", "external_id": externalID},
|
||||||
}),
|
}),
|
||||||
}); err != nil {
|
}); err != nil {
|
||||||
t.Fatalf("sign off: %v", err)
|
t.Fatalf("sign off: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// F63, found live on run 19. A manual sign-off says a human read what this
|
||||||
|
// code prints. An edit afterwards can change exactly that, so rerunning the
|
||||||
|
// automated half at a new commit must not carry the human half with it.
|
||||||
|
func TestASignOffDoesNotSurviveTheTreeItWasGivenAgainst(t *testing.T) {
|
||||||
|
s, project, id := planWith(t, twoPhasePlan)
|
||||||
|
run := []VerificationRun{{Command: []string{"go", "test", "./internal/..."}, ExitCode: 0}}
|
||||||
|
if _, err := RecordPlanPhaseVerification(s, project, id, "phase-2", shaOne, run); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
task, _ := s.Task(id)
|
||||||
|
signOff(t, s, id, domain.PlanPhaseSubject(task.PlanRef, "phase-2"))
|
||||||
|
task, _ = s.Task(id)
|
||||||
|
rec, _ := task.PlanPhase("phase-2")
|
||||||
|
if rec.Status != domain.PlanPhaseVerified || rec.ManualAtSHA != shaOne {
|
||||||
|
t.Fatalf("sign-off did not bind to the tree it read: %+v", rec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The tree moves and the phase is re-verified. The commands pass again;
|
||||||
|
// the human has not seen the new output.
|
||||||
|
if _, err := RecordPlanPhaseVerification(s, project, id, "phase-2", shaTwo, run); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
task, _ = s.Task(id)
|
||||||
|
rec, _ = task.PlanPhase("phase-2")
|
||||||
|
if rec.Status != domain.PlanPhaseAwaitingManual {
|
||||||
|
t.Fatalf("status = %q at a tree the human never saw, want awaiting_manual_verification", rec.Status)
|
||||||
|
}
|
||||||
|
if rec.ManualAtSHA != shaOne {
|
||||||
|
t.Fatalf("the confirmed tree was lost: %+v", rec)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A second rerun must not re-inherit it either, which is what carrying
|
||||||
|
// ManualAtSHA forward is for.
|
||||||
|
if _, err := RecordPlanPhaseVerification(s, project, id, "phase-2", shaThree, run); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
task, _ = s.Task(id)
|
||||||
|
rec, _ = task.PlanPhase("phase-2")
|
||||||
|
if rec.Status != domain.PlanPhaseAwaitingManual {
|
||||||
|
t.Fatalf("a second rerun re-inherited the sign-off: %q", rec.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Signing off again, on the tree that is now current, verifies it.
|
||||||
|
signOffFrom(t, s, id, domain.PlanPhaseSubject(task.PlanRef, "phase-2"), "signoff-second")
|
||||||
|
task, _ = s.Task(id)
|
||||||
|
rec, _ = task.PlanPhase("phase-2")
|
||||||
|
if rec.Status != domain.PlanPhaseVerified || rec.ManualAtSHA != shaThree {
|
||||||
|
t.Fatalf("a fresh sign-off did not verify the current tree: %+v", rec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -272,6 +272,9 @@ func (s *Store) apply(e domain.Event) error {
|
|||||||
}
|
}
|
||||||
if p.Subject == domain.PlanPhaseSubject(rec.PlanRef, rec.PhaseID) {
|
if p.Subject == domain.PlanPhaseSubject(rec.PlanRef, rec.PhaseID) {
|
||||||
t.PlanProgress.Phases[i].Status = domain.PlanPhaseVerified
|
t.PlanProgress.Phases[i].Status = domain.PlanPhaseVerified
|
||||||
|
// Record which tree the sign-off was about, so a later run
|
||||||
|
// at a different commit cannot inherit it (F63).
|
||||||
|
t.PlanProgress.Phases[i].ManualAtSHA = rec.AtSHA
|
||||||
t.Version = e.Version
|
t.Version = e.Version
|
||||||
s.replaceTask(e.TaskID, t)
|
s.replaceTask(e.TaskID, t)
|
||||||
}
|
}
|
||||||
|
|||||||
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.6 MiB |
|
After Width: | Height: | Size: 1.4 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 1.3 MiB |
@@ -0,0 +1,635 @@
|
|||||||
|
# orchestra web control ui
|
||||||
|
|
||||||
|
status: mockup specification
|
||||||
|
app: orchestra
|
||||||
|
shared design system: ethos
|
||||||
|
accent: signal violet `#8F7AE5`
|
||||||
|
motif: routing fork
|
||||||
|
primary operator: single infrastructure operator
|
||||||
|
|
||||||
|
## 1. purpose
|
||||||
|
|
||||||
|
orchestra's web ui is an operator console for understanding and steering agent work without reconstructing state from logs, tmux sessions, gitea, or the event stream.
|
||||||
|
|
||||||
|
it has three jobs:
|
||||||
|
|
||||||
|
1. show what needs the operator now.
|
||||||
|
2. show what orchestra believes and what an agent is actually doing.
|
||||||
|
3. expose intervention and forensic detail without making the default screens noisy.
|
||||||
|
|
||||||
|
it is not a generic project-management product, an analytics dashboard, or a chat-first interface.
|
||||||
|
|
||||||
|
## 2. ethos application
|
||||||
|
|
||||||
|
orchestra inherits the ethos shell and neutral system unchanged.
|
||||||
|
|
||||||
|
### fingerprint
|
||||||
|
|
||||||
|
- accent: signal violet `#8F7AE5`
|
||||||
|
- motif: routing fork
|
||||||
|
- accent use: active navigation, focus, selected phase, primary operator action, routing/transition marks
|
||||||
|
- motif use: app mark, phase transitions, routing state, empty states, one hero/detail moment
|
||||||
|
|
||||||
|
suggested token block:
|
||||||
|
|
||||||
|
```css
|
||||||
|
[data-app="orchestra"] {
|
||||||
|
--accent: #8F7AE5;
|
||||||
|
--accent-hi: #A291EC;
|
||||||
|
--accent-dim: rgba(143, 122, 229, 0.14);
|
||||||
|
--accent-line: rgba(143, 122, 229, 0.32);
|
||||||
|
--accent-glow: rgba(143, 122, 229, 0.22);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### type
|
||||||
|
|
||||||
|
- geist sans: labels, task names, explanations, buttons, headings, operator-authored prose
|
||||||
|
- geist mono: task ids, epochs, worker ids, pane ids, timestamps, percentages, context counts, revisions, paths, refs, sha values, lease durations
|
||||||
|
- machine values must never silently use sans for visual convenience
|
||||||
|
|
||||||
|
### surfaces
|
||||||
|
|
||||||
|
- page room: `--bg-0`
|
||||||
|
- normal panel: `--bg-1`
|
||||||
|
- selected/raised content: `--bg-2`
|
||||||
|
- hover: `--bg-3`
|
||||||
|
- borders: 1px `--line`, `--line-hi` only where selection or risk needs stronger separation
|
||||||
|
- no blur or glass
|
||||||
|
- accent never washes a whole content panel
|
||||||
|
|
||||||
|
### motion
|
||||||
|
|
||||||
|
all ui transitions use ethos mechanical motion: 130–170 ms, `cubic-bezier(0.2, 0, 0, 1)`, no spring or bounce.
|
||||||
|
|
||||||
|
## 3. shared shell
|
||||||
|
|
||||||
|
### desktop
|
||||||
|
|
||||||
|
- 64px vertical icon rail
|
||||||
|
- 56px top bar
|
||||||
|
- main content fills remaining viewport
|
||||||
|
- global `cmd+k` command/search surface in the top bar
|
||||||
|
- right side of top bar shows only high-value machine state: orchestra revision, live state, current time
|
||||||
|
|
||||||
|
primary navigation:
|
||||||
|
|
||||||
|
1. dashboard
|
||||||
|
2. tasks
|
||||||
|
3. decisions
|
||||||
|
4. workers
|
||||||
|
5. projects
|
||||||
|
6. review
|
||||||
|
7. settings
|
||||||
|
|
||||||
|
terminal is entered from a task or worker and does not need permanent primary navigation.
|
||||||
|
|
||||||
|
### mobile
|
||||||
|
|
||||||
|
- rail becomes bottom navigation with no more than five primary items
|
||||||
|
- secondary pages move behind task/project drill-down or header menus
|
||||||
|
- machine readouts relocate into detail screens rather than disappearing
|
||||||
|
- terminal remains a dedicated full-screen view
|
||||||
|
|
||||||
|
## 4. information hierarchy
|
||||||
|
|
||||||
|
orchestra should visually distinguish five classes of information.
|
||||||
|
|
||||||
|
| class | meaning | treatment |
|
||||||
|
|---|---|---|
|
||||||
|
| operator authority | human decision, correction, constraint | high prominence, accent signal |
|
||||||
|
| verified orchestra state | lifecycle, lease, phase, review, refs | neutral surfaces, machine values in mono |
|
||||||
|
| agent claims | proposed plan, handoff text, self-reported risk | visibly labeled as agent-supplied |
|
||||||
|
| historical/superseded | stale plan, previous handoff, old decision | muted, never visually dominant |
|
||||||
|
| fault/attention | blocked, refused, stale, mismatch | status color + explicit reason |
|
||||||
|
|
||||||
|
raw events and implementation details are drill-down evidence, not default-page content.
|
||||||
|
|
||||||
|
## 5. dashboard
|
||||||
|
|
||||||
|
mockup: `final/01-dashboard.png`
|
||||||
|
|
||||||
|
purpose: answer "what needs me, what is running, and is the system healthy?"
|
||||||
|
|
||||||
|
### layout
|
||||||
|
|
||||||
|
three primary regions only:
|
||||||
|
|
||||||
|
1. **needs your attention**
|
||||||
|
- trajectory gate
|
||||||
|
- blocking human decision
|
||||||
|
- pr awaiting review
|
||||||
|
- closed-without-merge/operator-required state
|
||||||
|
- one line explaining why each item needs the operator
|
||||||
|
|
||||||
|
2. **running tasks**
|
||||||
|
- task name/id
|
||||||
|
- current phase
|
||||||
|
- worker/harness
|
||||||
|
- last real progress
|
||||||
|
- context occupancy
|
||||||
|
- lease timing
|
||||||
|
- compact routing-fork phase indicator
|
||||||
|
|
||||||
|
3. **system state and capacity**
|
||||||
|
- workers online/available
|
||||||
|
- quota status
|
||||||
|
- active leases
|
||||||
|
- router queue/rejection state
|
||||||
|
- source reconciliation
|
||||||
|
- herdr health
|
||||||
|
|
||||||
|
### rules
|
||||||
|
|
||||||
|
- no disk, cpu, ram, or generic host-monitor metrics unless they directly block orchestration
|
||||||
|
- no vanity charts
|
||||||
|
- health should show actual counts/times, not vague "running" indicators
|
||||||
|
- dashboard actions should navigate to the relevant task/review/worker rather than becoming an operator-control surface itself
|
||||||
|
|
||||||
|
## 6. tasks list
|
||||||
|
|
||||||
|
mockup: `final/04-tasks.png`
|
||||||
|
|
||||||
|
purpose: browse all work under orchestra and locate a task quickly.
|
||||||
|
|
||||||
|
### groups
|
||||||
|
|
||||||
|
- needs attention
|
||||||
|
- running
|
||||||
|
- waiting/queued
|
||||||
|
- completed
|
||||||
|
- failed
|
||||||
|
|
||||||
|
### row fields
|
||||||
|
|
||||||
|
- task title/id
|
||||||
|
- project
|
||||||
|
- current phase
|
||||||
|
- compact routing-fork phase path
|
||||||
|
- worker/harness when assigned
|
||||||
|
- last update/progress
|
||||||
|
- context occupancy for active sessions
|
||||||
|
- status
|
||||||
|
|
||||||
|
### filters
|
||||||
|
|
||||||
|
- project
|
||||||
|
- phase
|
||||||
|
- worker/harness
|
||||||
|
- status
|
||||||
|
- attention state
|
||||||
|
|
||||||
|
avoid turning this into a kanban board. orchestra's lifecycle is stateful and event-backed; the list should expose state, not encourage arbitrary drag-and-drop mutation.
|
||||||
|
|
||||||
|
## 7. task detail
|
||||||
|
|
||||||
|
mockup: `final/02-task-detail.png`
|
||||||
|
|
||||||
|
purpose: provide one calm page answering "what are we doing?", "what is happening?", and "where are we in the workflow?"
|
||||||
|
|
||||||
|
### top area
|
||||||
|
|
||||||
|
- title
|
||||||
|
- task id
|
||||||
|
- project
|
||||||
|
- current phase
|
||||||
|
- steer/correct primary action
|
||||||
|
- secondary actions menu
|
||||||
|
|
||||||
|
### what we're doing
|
||||||
|
|
||||||
|
show only:
|
||||||
|
|
||||||
|
- goal
|
||||||
|
- acceptance summary
|
||||||
|
- latest effective human decisions when present
|
||||||
|
- active constraints when present
|
||||||
|
|
||||||
|
full contract, all decisions, and historical versions open in drill-down views.
|
||||||
|
|
||||||
|
### live execution
|
||||||
|
|
||||||
|
show only:
|
||||||
|
|
||||||
|
- worker/harness
|
||||||
|
- pane
|
||||||
|
- lease remaining
|
||||||
|
- context occupancy
|
||||||
|
- last progress
|
||||||
|
- one-line current activity
|
||||||
|
|
||||||
|
links:
|
||||||
|
|
||||||
|
- open live pane
|
||||||
|
- worker logs
|
||||||
|
- launch context
|
||||||
|
- git diff
|
||||||
|
|
||||||
|
### workflow
|
||||||
|
|
||||||
|
render the ace-fca path visibly:
|
||||||
|
|
||||||
|
`frame → research → plan → implement → review → pr → merged`
|
||||||
|
|
||||||
|
completed phases are quiet green, current phase uses the orchestra accent, future phases remain neutral.
|
||||||
|
|
||||||
|
phase detail, artifacts, refusal history, and raw events belong in drill-downs rather than the default task screen.
|
||||||
|
|
||||||
|
## 8. live pane / terminal
|
||||||
|
|
||||||
|
mockup: `final/03-terminal-live-pane.png`
|
||||||
|
|
||||||
|
purpose: show the actual tmux/herdr pane, not a reconstructed log view.
|
||||||
|
|
||||||
|
### behavior
|
||||||
|
|
||||||
|
- read-only by default
|
||||||
|
- exact pane dimensions and terminal rendering preserved when possible
|
||||||
|
- fit mode may scale visually but must not alter the underlying pane
|
||||||
|
- scrollback inspection must not disturb the live cursor
|
||||||
|
- `take control` explicitly enables keyboard forwarding
|
||||||
|
- strong visual state when control is captured
|
||||||
|
- escape or a fixed shortcut releases control
|
||||||
|
|
||||||
|
### why this screen exists
|
||||||
|
|
||||||
|
it must reveal real tui state such as:
|
||||||
|
|
||||||
|
- input still sitting in the editor
|
||||||
|
- queued input
|
||||||
|
- paste placeholders
|
||||||
|
- permission/approval dialogs
|
||||||
|
- `/clear`
|
||||||
|
- `@HANDOFF.md`
|
||||||
|
- agent busy/idle presentation
|
||||||
|
- tmux status line and pane identity
|
||||||
|
|
||||||
|
### surrounding chrome
|
||||||
|
|
||||||
|
keep it minimal:
|
||||||
|
|
||||||
|
- worker/pane identity
|
||||||
|
- read-only/control state
|
||||||
|
- fit/copy/scrollback controls
|
||||||
|
- lease epoch/timing
|
||||||
|
- context occupancy
|
||||||
|
- last verified progress
|
||||||
|
- pane/worker health
|
||||||
|
- small live event strip for orchestra-originated interactions and confirmations
|
||||||
|
|
||||||
|
when a pane is lost, preserve the last captured frame and state the exact loss time and lease consequence.
|
||||||
|
|
||||||
|
## 9. decisions list
|
||||||
|
|
||||||
|
mockup: `final/05-decisions.png`
|
||||||
|
|
||||||
|
purpose: inspect durable human authority and approvals across tasks.
|
||||||
|
|
||||||
|
this is not the human attention queue; that is review.
|
||||||
|
|
||||||
|
### list fields
|
||||||
|
|
||||||
|
- decision id
|
||||||
|
- short decision text
|
||||||
|
- task
|
||||||
|
- phase
|
||||||
|
- source/provenance
|
||||||
|
- requested/recorded time
|
||||||
|
- status: active, superseded, waiting, approved, rejected, auto
|
||||||
|
- operator/actor
|
||||||
|
|
||||||
|
### filters
|
||||||
|
|
||||||
|
- active/superseded
|
||||||
|
- kind: decision, correction, constraint, answer, scope change
|
||||||
|
- project
|
||||||
|
- task
|
||||||
|
- actor/source
|
||||||
|
|
||||||
|
### detail
|
||||||
|
|
||||||
|
opening a decision should show:
|
||||||
|
|
||||||
|
- exact value
|
||||||
|
- subject
|
||||||
|
- provenance
|
||||||
|
- supersedes/superseded-by links
|
||||||
|
- event sequence
|
||||||
|
- effective authority impact
|
||||||
|
|
||||||
|
machine provenance is mono; the human decision text remains sans.
|
||||||
|
|
||||||
|
## 10. workers
|
||||||
|
|
||||||
|
mockup: `final/06-workers.png`
|
||||||
|
|
||||||
|
purpose: answer "where can work run and is that execution machinery usable?"
|
||||||
|
|
||||||
|
### worker list fields
|
||||||
|
|
||||||
|
- worker/harness identity
|
||||||
|
- online/idle/busy/offline/degraded
|
||||||
|
- harness type
|
||||||
|
- current task when leased
|
||||||
|
- phase
|
||||||
|
- lease or heartbeat age
|
||||||
|
|
||||||
|
intentionally omit generic infrastructure metrics such as disk, memory, cpu, and uptime unless orchestra directly uses them for eligibility.
|
||||||
|
|
||||||
|
### worker detail
|
||||||
|
|
||||||
|
- current session/task
|
||||||
|
- pane id
|
||||||
|
- lease epoch/remaining
|
||||||
|
- last verified progress
|
||||||
|
- worker revision
|
||||||
|
- herdr status
|
||||||
|
- last heartbeat
|
||||||
|
- projects declared by the worker
|
||||||
|
- declared capabilities if orchestra actually consumes them
|
||||||
|
|
||||||
|
### capabilities
|
||||||
|
|
||||||
|
capabilities must come from worker registration/project configuration, never be guessed from host inspection.
|
||||||
|
|
||||||
|
the ui labels them **declared capabilities** and can expose registration provenance.
|
||||||
|
|
||||||
|
### registration
|
||||||
|
|
||||||
|
registration is worker-driven:
|
||||||
|
|
||||||
|
1. worker starts with coordinator credentials/configuration
|
||||||
|
2. worker announces identity, revision, harnesses, projects, capacity and declared capabilities
|
||||||
|
3. coordinator records it and heartbeat updates liveness
|
||||||
|
|
||||||
|
`add worker` should therefore be a registration guide/bootstrap flow, not a magical browser-side creation of a remote process.
|
||||||
|
|
||||||
|
## 11. projects
|
||||||
|
|
||||||
|
mockup: `final/07-projects.png`
|
||||||
|
|
||||||
|
purpose: group long-running repository/workflow configuration and its active work.
|
||||||
|
|
||||||
|
### project list
|
||||||
|
|
||||||
|
- project name
|
||||||
|
- short description
|
||||||
|
- state
|
||||||
|
- active/total task count
|
||||||
|
- attention/decision count
|
||||||
|
- last meaningful activity
|
||||||
|
|
||||||
|
avoid aggregate artifact counts or infrastructure metrics unless they help choose an action.
|
||||||
|
|
||||||
|
### project detail
|
||||||
|
|
||||||
|
- description
|
||||||
|
- source/repository identity
|
||||||
|
- configured worker/harness affinity
|
||||||
|
- workflow phases
|
||||||
|
- trajectory-gate policy
|
||||||
|
- quality gate
|
||||||
|
- human reconcile source
|
||||||
|
- safe-operation policy
|
||||||
|
- active tasks
|
||||||
|
- recent meaningful events
|
||||||
|
|
||||||
|
configuration edits belong in the project detail, while global defaults remain in settings.
|
||||||
|
|
||||||
|
## 12. review
|
||||||
|
|
||||||
|
mockup: `final/08-review.png`
|
||||||
|
|
||||||
|
purpose: the operator inbox: **what needs me to look at something and judge?**
|
||||||
|
|
||||||
|
workers show machinery. review shows human attention.
|
||||||
|
|
||||||
|
### review item types
|
||||||
|
|
||||||
|
- trajectory decision
|
||||||
|
- human blocker / decision request
|
||||||
|
- code/pr review
|
||||||
|
- ai review requiring operator override
|
||||||
|
- operator-required lifecycle state
|
||||||
|
- closed pr without merge
|
||||||
|
- information/fyi, optionally hidden by default
|
||||||
|
|
||||||
|
### list fields
|
||||||
|
|
||||||
|
- type
|
||||||
|
- task/item
|
||||||
|
- why it is here
|
||||||
|
- project
|
||||||
|
- priority
|
||||||
|
- age
|
||||||
|
|
||||||
|
worker/pane is secondary metadata, not a primary column.
|
||||||
|
|
||||||
|
### detail drawer/page
|
||||||
|
|
||||||
|
show the minimum evidence needed to act:
|
||||||
|
|
||||||
|
- why this needs the operator
|
||||||
|
- effective task goal/constraints relevant to the decision
|
||||||
|
- proposed options when applicable
|
||||||
|
- relevant plan/diff/review evidence
|
||||||
|
- provenance and age
|
||||||
|
- comments/context drill-down
|
||||||
|
|
||||||
|
primary action names must match the lifecycle action: make decision, answer blocker, review pr, request changes, retry, etc.
|
||||||
|
|
||||||
|
snooze/defer may exist for attention management but must not silently mutate underlying task truth.
|
||||||
|
|
||||||
|
## 13. settings
|
||||||
|
|
||||||
|
mockup: `final/09-settings.png`
|
||||||
|
|
||||||
|
purpose: configure orchestra itself, project-independent defaults, integrations, policies, and operator/security settings.
|
||||||
|
|
||||||
|
### tabs
|
||||||
|
|
||||||
|
1. general
|
||||||
|
2. appearance
|
||||||
|
3. notifications
|
||||||
|
4. access & security
|
||||||
|
5. integrations
|
||||||
|
6. agents & tools
|
||||||
|
7. policies
|
||||||
|
8. advanced
|
||||||
|
|
||||||
|
### general
|
||||||
|
|
||||||
|
- instance name/environment
|
||||||
|
- locale/time display
|
||||||
|
- default task preferences
|
||||||
|
- default workflow policy
|
||||||
|
- event/artifact retention
|
||||||
|
- storage/backup state only if orchestra owns it
|
||||||
|
|
||||||
|
### appearance
|
||||||
|
|
||||||
|
- theme: system/dark/light
|
||||||
|
- ethos remains fixed; no arbitrary shell redesign controls
|
||||||
|
- accessibility and reduced motion
|
||||||
|
|
||||||
|
### access & security
|
||||||
|
|
||||||
|
- surface tokens/status
|
||||||
|
- trusted review actors
|
||||||
|
- ignored actors
|
||||||
|
- operator sessions
|
||||||
|
- agent-surface auth
|
||||||
|
- secret/config source status without exposing secret values
|
||||||
|
|
||||||
|
### integrations
|
||||||
|
|
||||||
|
- gitea
|
||||||
|
- vikunja
|
||||||
|
- notification sinks
|
||||||
|
- source reconciliation state
|
||||||
|
|
||||||
|
show endpoint/identity and last successful interaction in mono.
|
||||||
|
|
||||||
|
### agents & tools
|
||||||
|
|
||||||
|
- harness definitions
|
||||||
|
- adapter/runtime configuration
|
||||||
|
- tmux/herdr ownership
|
||||||
|
- default launch behavior
|
||||||
|
- context occupancy thresholds
|
||||||
|
|
||||||
|
### policies
|
||||||
|
|
||||||
|
- work phase defaults
|
||||||
|
- trajectory gate
|
||||||
|
- bounded decision request budget
|
||||||
|
- retry policy
|
||||||
|
- reconcile failure handoff threshold
|
||||||
|
- quality/review/submission policy
|
||||||
|
|
||||||
|
### advanced
|
||||||
|
|
||||||
|
- event-store diagnostics
|
||||||
|
- snapshots/replay
|
||||||
|
- raw configuration
|
||||||
|
- migration/status information
|
||||||
|
- destructive operator actions in a visually separated danger zone
|
||||||
|
|
||||||
|
## 14. global command surface
|
||||||
|
|
||||||
|
`cmd+k` is the shared ethos command surface and should handle navigation plus safe operator actions.
|
||||||
|
|
||||||
|
examples:
|
||||||
|
|
||||||
|
- open task by id/title
|
||||||
|
- open worker/pane
|
||||||
|
- show decisions for task
|
||||||
|
- steer/correct active task
|
||||||
|
- answer current blocker
|
||||||
|
- retry failed task
|
||||||
|
- open review item
|
||||||
|
|
||||||
|
high-risk lifecycle actions require explicit confirmation and should not be the first fuzzy-search result for ordinary text.
|
||||||
|
|
||||||
|
## 15. status grammar
|
||||||
|
|
||||||
|
status is compact and consistent across screens.
|
||||||
|
|
||||||
|
recommended semantic colors:
|
||||||
|
|
||||||
|
- green: verified healthy/completed/accepted
|
||||||
|
- violet: current/selected/orchestra-controlled active state
|
||||||
|
- amber: waiting/degraded/operator attention
|
||||||
|
- red: failed/refused/danger
|
||||||
|
- gray: idle/unknown/historical
|
||||||
|
|
||||||
|
color never carries the entire meaning; every state also has text/iconography.
|
||||||
|
|
||||||
|
## 16. evidence drill-down
|
||||||
|
|
||||||
|
any important derived claim should be traceable without ssh.
|
||||||
|
|
||||||
|
from relevant detail screens the operator should be able to reach:
|
||||||
|
|
||||||
|
- event(s) that established the state
|
||||||
|
- exact generated launch context
|
||||||
|
- sealed research/plan/review/submission artifact refs
|
||||||
|
- git sha/ref
|
||||||
|
- worker journal observations
|
||||||
|
- input confirmation receipt
|
||||||
|
- pane capture/terminal
|
||||||
|
- source comment/review provenance
|
||||||
|
|
||||||
|
raw evidence is accessible but not visible by default.
|
||||||
|
|
||||||
|
## 17. copy rules
|
||||||
|
|
||||||
|
- labels describe what the operator controls, not implementation class names
|
||||||
|
- machine values remain exact and honest
|
||||||
|
- errors state what failed and what consequence follows
|
||||||
|
- no "something went wrong"
|
||||||
|
- no spinner where a queue depth, heartbeat age, poll timestamp, retry time, or progress value exists
|
||||||
|
|
||||||
|
examples:
|
||||||
|
|
||||||
|
- `source reconcile failed · retry at 14:06:00`
|
||||||
|
- `worker heartbeat 42s old · ineligible after 60s`
|
||||||
|
- `phase request refused · next phase is research`
|
||||||
|
- `pane lost at 13:42:18 · lease expires in 09:14`
|
||||||
|
|
||||||
|
## 18. responsive behavior
|
||||||
|
|
||||||
|
### tablet/mobile priorities
|
||||||
|
|
||||||
|
keep visible:
|
||||||
|
|
||||||
|
1. attention state
|
||||||
|
2. task title/phase
|
||||||
|
3. latest human authority
|
||||||
|
4. live execution state
|
||||||
|
5. primary action
|
||||||
|
|
||||||
|
collapse or drill down:
|
||||||
|
|
||||||
|
- long acceptance lists
|
||||||
|
- complete workflow history
|
||||||
|
- detailed lease metadata
|
||||||
|
- worker journal
|
||||||
|
- raw events
|
||||||
|
- project configuration
|
||||||
|
|
||||||
|
terminal becomes its own full-screen route and should not be squeezed into a card.
|
||||||
|
|
||||||
|
## 19. implementation order
|
||||||
|
|
||||||
|
recommended web-ui build order:
|
||||||
|
|
||||||
|
1. ethos shell + orchestra tokens/motif
|
||||||
|
2. dashboard
|
||||||
|
3. tasks list
|
||||||
|
4. task detail
|
||||||
|
5. live pane
|
||||||
|
6. review inbox
|
||||||
|
7. decisions list/detail
|
||||||
|
8. workers
|
||||||
|
9. projects
|
||||||
|
10. settings
|
||||||
|
11. global command surface
|
||||||
|
12. evidence/raw-event drill-down
|
||||||
|
|
||||||
|
build each screen against real orchestra api data rather than static ui-only state as early as practical.
|
||||||
|
|
||||||
|
## 20. verification
|
||||||
|
|
||||||
|
for every screen:
|
||||||
|
|
||||||
|
- render and visually inspect pixels
|
||||||
|
- check desktop and <=640px mobile layout
|
||||||
|
- assert no horizontal overflow
|
||||||
|
- verify mono/sans split visibly
|
||||||
|
- verify accent is signal-only
|
||||||
|
- verify no blur/backdrop-filter exists
|
||||||
|
- verify empty/error/loading states show real machine state
|
||||||
|
- compare implemented screen against the corresponding mockup as direction, not pixel-perfect contract
|
||||||
|
|
||||||
|
mockups communicate hierarchy, density, and interaction intent. orchestra state and the ethos design laws are the actual specification.
|
||||||