diff --git a/README.md b/README.md new file mode 100644 index 0000000..53c401a --- /dev/null +++ b/README.md @@ -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. diff --git a/final/01-dashboard.png b/final/01-dashboard.png new file mode 100644 index 0000000..310d883 Binary files /dev/null and b/final/01-dashboard.png differ diff --git a/final/02-task-detail.png b/final/02-task-detail.png new file mode 100644 index 0000000..b22906a Binary files /dev/null and b/final/02-task-detail.png differ diff --git a/final/03-terminal-live-pane.png b/final/03-terminal-live-pane.png new file mode 100644 index 0000000..05aa3e2 Binary files /dev/null and b/final/03-terminal-live-pane.png differ diff --git a/final/04-tasks.png b/final/04-tasks.png new file mode 100644 index 0000000..252b182 Binary files /dev/null and b/final/04-tasks.png differ diff --git a/final/05-decisions.png b/final/05-decisions.png new file mode 100644 index 0000000..b21b90d Binary files /dev/null and b/final/05-decisions.png differ diff --git a/final/06-workers.png b/final/06-workers.png new file mode 100644 index 0000000..7ed5935 Binary files /dev/null and b/final/06-workers.png differ diff --git a/final/07-projects.png b/final/07-projects.png new file mode 100644 index 0000000..f8eea3f Binary files /dev/null and b/final/07-projects.png differ diff --git a/final/08-review.png b/final/08-review.png new file mode 100644 index 0000000..b01fea8 Binary files /dev/null and b/final/08-review.png differ diff --git a/final/09-settings.png b/final/09-settings.png new file mode 100644 index 0000000..3cbd527 Binary files /dev/null and b/final/09-settings.png differ diff --git a/internal/domain/planprogress.go b/internal/domain/planprogress.go index ba5de79..ef00784 100644 --- a/internal/domain/planprogress.go +++ b/internal/domain/planprogress.go @@ -55,6 +55,13 @@ type PlanPhaseRecord struct { // EvidenceRef is the CAS ref of the captured command output. EvidenceRef string `json:"evidence_ref,omitempty"` 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 diff --git a/internal/operations/planprogress.go b/internal/operations/planprogress.go index 4d7a54e..f8c5e5e 100644 --- a/internal/operations/planprogress.go +++ b/internal/operations/planprogress.go @@ -124,7 +124,12 @@ func RecordPlanPhaseVerification(s *store.Store, project registry.Project, taskI // was established. 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 } 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{ "plan_ref": record.PlanRef, "phase_id": record.PhaseID, "status": string(record.Status), "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 { 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 -// phase of this exact plan. The subject carries both, so a later "looks good" -// on an unrelated thread cannot satisfy a gate nobody was discussing. -func manuallySignedOff(s *store.Store, t domain.Task, phaseID string) bool { +// phase of this exact plan, against the tree this run examined. The subject +// carries plan and phase, so a later "looks good" on an unrelated thread +// 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) if err != nil { return false diff --git a/internal/operations/planprogress_test.go b/internal/operations/planprogress_test.go index 7346836..7433285 100644 --- a/internal/operations/planprogress_test.go +++ b/internal/operations/planprogress_test.go @@ -13,6 +13,7 @@ import ( const shaOne = "1111111111111111111111111111111111111111" const shaTwo = "2222222222222222222222222222222222222222" +const shaThree = "3333333333333333333333333333333333333333" func planProject() 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 // the only thing that satisfies a manual verification gate. 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() task, _ := s.Task(taskID) 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{ "decision_id": domain.NewID(), "kind": "answer", "subject": subject, "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 { 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) + } +} diff --git a/internal/store/store.go b/internal/store/store.go index a5cad53..8d886bf 100644 --- a/internal/store/store.go +++ b/internal/store/store.go @@ -272,6 +272,9 @@ func (s *Store) apply(e domain.Event) error { } if p.Subject == domain.PlanPhaseSubject(rec.PlanRef, rec.PhaseID) { 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 s.replaceTask(e.TaskID, t) } diff --git a/iterations/a_dark_mode_ui_dashboard_screenshot_wide_desktop.png b/iterations/a_dark_mode_ui_dashboard_screenshot_wide_desktop.png new file mode 100644 index 0000000..b01fea8 Binary files /dev/null and b/iterations/a_dark_mode_ui_dashboard_screenshot_wide_desktop.png differ diff --git a/iterations/a_dark_themed_desktop_ui_screenshot_web_app_dashb.png b/iterations/a_dark_themed_desktop_ui_screenshot_web_app_dashb.png new file mode 100644 index 0000000..1dc4035 Binary files /dev/null and b/iterations/a_dark_themed_desktop_ui_screenshot_web_app_dashb.png differ diff --git a/iterations/a_dark_themed_ui_dashboard_screenshot_overall_sce.png b/iterations/a_dark_themed_ui_dashboard_screenshot_overall_sce.png new file mode 100644 index 0000000..f8eea3f Binary files /dev/null and b/iterations/a_dark_themed_ui_dashboard_screenshot_overall_sce.png differ diff --git a/iterations/a_dark_themed_ui_dashboard_screenshot_widescreen.png b/iterations/a_dark_themed_ui_dashboard_screenshot_widescreen.png new file mode 100644 index 0000000..30cf18b Binary files /dev/null and b/iterations/a_dark_themed_ui_dashboard_screenshot_widescreen.png differ diff --git a/iterations/a_dark_themed_ui_screenshot_of_a_web_app_dashboard.png b/iterations/a_dark_themed_ui_screenshot_of_a_web_app_dashboard.png new file mode 100644 index 0000000..0a96e36 Binary files /dev/null and b/iterations/a_dark_themed_ui_screenshot_of_a_web_app_dashboard.png differ diff --git a/iterations/a_dark_ui_dashboard_screenshot_app_web_interface.png b/iterations/a_dark_ui_dashboard_screenshot_app_web_interface.png new file mode 100644 index 0000000..b22906a Binary files /dev/null and b/iterations/a_dark_ui_dashboard_screenshot_app_web_interface.png differ diff --git a/iterations/a_detailed_ui_ux_dashboard_mockup_screenshot_in_a.png b/iterations/a_detailed_ui_ux_dashboard_mockup_screenshot_in_a.png new file mode 100644 index 0000000..faee663 Binary files /dev/null and b/iterations/a_detailed_ui_ux_dashboard_mockup_screenshot_in_a.png differ diff --git a/iterations/a_high_detail_dark_ui_screenshot_of_a_task_detail.png b/iterations/a_high_detail_dark_ui_screenshot_of_a_task_detail.png new file mode 100644 index 0000000..a5e3ad9 Binary files /dev/null and b/iterations/a_high_detail_dark_ui_screenshot_of_a_task_detail.png differ diff --git a/iterations/a_high_detail_ui_screenshot_dashboard_mockup_wit.png b/iterations/a_high_detail_ui_screenshot_dashboard_mockup_wit.png new file mode 100644 index 0000000..0f5c132 Binary files /dev/null and b/iterations/a_high_detail_ui_screenshot_dashboard_mockup_wit.png differ diff --git a/iterations/a_high_fidelity_ui_mockup_dashboard_screenshot.png b/iterations/a_high_fidelity_ui_mockup_dashboard_screenshot.png new file mode 100644 index 0000000..7bed727 Binary files /dev/null and b/iterations/a_high_fidelity_ui_mockup_dashboard_screenshot.png differ diff --git a/iterations/a_high_fidelity_ui_mockup_of_an_orchestration_syst.png b/iterations/a_high_fidelity_ui_mockup_of_an_orchestration_syst.png new file mode 100644 index 0000000..252b182 Binary files /dev/null and b/iterations/a_high_fidelity_ui_mockup_of_an_orchestration_syst.png differ diff --git a/iterations/a_high_resolution_dark_mode_application_ui_screens.png b/iterations/a_high_resolution_dark_mode_application_ui_screens.png new file mode 100644 index 0000000..3cbd527 Binary files /dev/null and b/iterations/a_high_resolution_dark_mode_application_ui_screens.png differ diff --git a/iterations/a_wide_dark_themed_ui_screenshot_of_a_web_app_das.png b/iterations/a_wide_dark_themed_ui_screenshot_of_a_web_app_das.png new file mode 100644 index 0000000..b21b90d Binary files /dev/null and b/iterations/a_wide_dark_themed_ui_screenshot_of_a_web_app_das.png differ diff --git a/iterations/a_wide_desktop_ui_screenshot_of_a_dark_themed_orch.png b/iterations/a_wide_desktop_ui_screenshot_of_a_dark_themed_orch.png new file mode 100644 index 0000000..79f54e6 Binary files /dev/null and b/iterations/a_wide_desktop_ui_screenshot_of_a_dark_themed_orch.png differ diff --git a/iterations/a_wide_high_resolution_dark_themed_software_ui_da.png b/iterations/a_wide_high_resolution_dark_themed_software_ui_da.png new file mode 100644 index 0000000..5ad2d55 Binary files /dev/null and b/iterations/a_wide_high_resolution_dark_themed_software_ui_da.png differ diff --git a/iterations/a_wide_high_resolution_dark_ui_screenshot_of_a_de.png b/iterations/a_wide_high_resolution_dark_ui_screenshot_of_a_de.png new file mode 100644 index 0000000..310d883 Binary files /dev/null and b/iterations/a_wide_high_resolution_dark_ui_screenshot_of_a_de.png differ diff --git a/iterations/a_wide_screenshot_of_a_dark_developer_ops_dashboar.png b/iterations/a_wide_screenshot_of_a_dark_developer_ops_dashboar.png new file mode 100644 index 0000000..05aa3e2 Binary files /dev/null and b/iterations/a_wide_screenshot_of_a_dark_developer_ops_dashboar.png differ diff --git a/iterations/dark_workers_management_dashboard.png b/iterations/dark_workers_management_dashboard.png new file mode 100644 index 0000000..7ed5935 Binary files /dev/null and b/iterations/dark_workers_management_dashboard.png differ diff --git a/iterations/orchestra_dark_operations_dashboard.png b/iterations/orchestra_dark_operations_dashboard.png new file mode 100644 index 0000000..e9be470 Binary files /dev/null and b/iterations/orchestra_dark_operations_dashboard.png differ diff --git a/orchestra-ui-spec.md b/orchestra-ui-spec.md new file mode 100644 index 0000000..66e9f27 --- /dev/null +++ b/orchestra-ui-spec.md @@ -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.