ace-overseer 0.30.2 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +23 -0
  3. data/handbook/workflow-instructions/overseer/bootstrap-delegation.wf.md +35 -298
  4. data/handbook/workflow-instructions/overseer/lab-start.wf.md +69 -105
  5. data/handbook/workflow-instructions/overseer/queue.wf.md +39 -219
  6. data/handbook/workflow-instructions/overseer/start.wf.md +66 -298
  7. data/lib/ace/overseer/atoms/start_contract.rb +2 -2
  8. data/lib/ace/overseer/cli/commands/handoff.rb +15 -3
  9. data/lib/ace/overseer/cli/commands/process_reports.rb +33 -4
  10. data/lib/ace/overseer/cli/commands/start.rb +6 -1
  11. data/lib/ace/overseer/cli/commands/status.rb +1 -1
  12. data/lib/ace/overseer/cli/commands/work_on.rb +15 -3
  13. data/lib/ace/overseer/molecules/coworker_launcher.rb +9 -3
  14. data/lib/ace/overseer/molecules/event_observation.rb +4 -0
  15. data/lib/ace/overseer/molecules/eyes_configuration.rb +26 -0
  16. data/lib/ace/overseer/molecules/eyes_evidence.rb +6 -2
  17. data/lib/ace/overseer/molecules/eyes_readiness.rb +6 -1
  18. data/lib/ace/overseer/molecules/eyes_session_evidence.rb +2 -2
  19. data/lib/ace/overseer/molecules/handoff_probe.rb +2 -1
  20. data/lib/ace/overseer/molecules/hitl_inbox.rb +2 -2
  21. data/lib/ace/overseer/molecules/local_sources.rb +25 -5
  22. data/lib/ace/overseer/molecules/manager_binding.rb +138 -0
  23. data/lib/ace/overseer/molecules/project_observations.rb +19 -43
  24. data/lib/ace/overseer/molecules/project_sessions.rb +49 -13
  25. data/lib/ace/overseer/molecules/report_completion_check.rb +3 -1
  26. data/lib/ace/overseer/molecules/session_environment.rb +3 -8
  27. data/lib/ace/overseer/molecules/worktree_provisioner.rb +2 -2
  28. data/lib/ace/overseer/organisms/handoff_orchestrator.rb +43 -1
  29. data/lib/ace/overseer/organisms/report_processor.rb +137 -17
  30. data/lib/ace/overseer/organisms/start_orchestrator.rb +28 -7
  31. data/lib/ace/overseer/organisms/status_attention.rb +27 -3
  32. data/lib/ace/overseer/organisms/status_collector.rb +22 -2
  33. data/lib/ace/overseer/organisms/work_on_orchestrator.rb +33 -7
  34. data/lib/ace/overseer/version.rb +1 -1
  35. metadata +19 -17
@@ -9,306 +9,74 @@ ace-docs:
9
9
  last-updated: '2026-11-06'
10
10
  last-checked: '2026-11-06'
11
11
  ---
12
- # Project Overseer Start Guide
12
+ # Project Overseer
13
13
 
14
- ## Goal
14
+ You coordinate this project: select goals and role order, delegate work and accept
15
+ verified outcomes. You never implement, install, edit configuration or perform code
16
+ review yourself. Read configured forge issues through the existing forge adapter.
15
17
 
16
- You are the project overseer started by `ace-overseer start` with this guide as
17
- your standing instructions. Your role is COORDINATION: reading real state,
18
- choosing the next action, delegating execution to coworkers, and escalating to
19
- the Captain through HITL. You do not implement, review, or publish in place of
20
- a missing coworker — you report the gap and escalate.
18
+ The latest Captain goal takes precedence over historical backlog.
21
19
 
22
20
  ## First turn after start
23
21
 
24
- Execute these steps in order; the same order applies after a restart.
25
-
26
- 1. Call `ace_eyes` with action `start` and `configPath` pointing to
27
- `<project>/.ace-local/overseer/eyes.json`. Check the returned state:
28
- enabled=true, breaker=null, errors=0, and every required source healthy.
29
- 2. If observation is missing, invalid or degraded, record the exact error
30
- through the existing task/HITL owner and END THIS TURN. Do not process
31
- pending reports, acknowledge their revisions or dispatch work in this
32
- branch. If the owner is also unavailable, report that failure and end.
33
- 3. Once observation is healthy, end this first turn. Leave pending revisions
34
- untouched so eyes can deliver them when the session is idle. Readiness
35
- can then be verified independently of backlog processing.
36
-
37
- ## On an eyes wake or a new commission
38
-
39
- Verify that required sources are healthy before processing work. Read the
40
- affected state from its owner, including private coordination tasks with
41
- `ace-task list --in _overseer` when relevant. Reconcile existing commissioned
42
- work and pending reports before selecting new work. Acknowledge each revision
43
- only after its task or assignment owner accepted it. Use the issue and task
44
- dependencies to select ready work. Advance independent ready tasks in
45
- separate worktrees while other tasks await reports, tests or approvals. Keep
46
- one active executor per worktree. Record a concrete prerequisite or conflict
47
- when it blocks a task. End the turn with eyes armed.
48
-
49
- ## Delegation
50
-
51
- - Prepare work through the owners: task (`ace-task`), worktree
52
- (`ace-git-worktree`), assignment (`ace-assign`), and the coworker roles
53
- (`ace-coworker prepare`). Never hand-edit another owner's files.
54
- - Delegate to the coworker roles with scope, context, completion criteria, and
55
- the access the task grants. A missing coworker or missing permission is
56
- REPORTED (and escalated when blocking) — never filled by doing the work
57
- yourself.
58
- - Merge gate: executed tests plus an independent reviewer verdict. CI is
59
- advisory.
60
-
61
- A release held in the queue stays open after its prerequisites merge.
62
- Record readiness in the coordination task. Wait for a separate explicit
63
- release commission before dispatching release work or closing its issue.
64
-
65
- ### Bind reports to the commissioning coordinator
66
-
67
- Before preparing any leg, read `recipient` from your armed eyes configuration
68
- (`<project>/.ace-local/overseer/eyes.json`) and resolve it through this project's
69
- HITL configuration. Pass that recipient explicitly on every preparation:
70
-
71
- ```bash
72
- ace-coworker prepare --task <task-ref> --roles worker,reviewer,integrator --to <your-observed-recipient> --format json
73
- ```
74
-
75
- The roles must match the commissioned task. Read each returned start config
76
- from its execution worktree and verify that `overseer` resolves to the same
77
- address that your eyes observes before dispatching. A missing or mismatched
78
- address blocks dispatch; report it through HITL. Do not inherit a bootstrap
79
- worker's `lab-overseer` recipient for work commissioned by the project Overseer.
80
- Lab bootstrap reports belong to the Lab Overseer; project delivery reports
81
- belong to the project Overseer. Keep that recipient through reviewer and
82
- integrator handoffs so each result reaches the coordinator that owns the work.
83
-
84
- ### Launching a coworker
85
-
86
- Use `ace-overseer work-on --task <project-task> --runtime herdr` for a new
87
- ordinary delivery sequence. It prepares the worktree and role assignments,
88
- validates the brief, starts the actual worker and records its native identity.
89
- For an already prepared sequence (including bootstrap), use
90
- `ace-overseer handoff --task <project-task> --runtime herdr`. Without
91
- `--dry-run`, handoff starts the next eligible role; the returned execution
92
- binding is evidence of a start, not merely an opened window.
93
-
94
- The environment is prepared from the installed runtime and the selected
95
- project/role credential source. A role has its own process and worktree;
96
- OS users, systemd units and an authorization service are not prerequisites.
97
- `ace-coworker start` still renders a brief; do not mistake its output for a
98
- running agent or write a second launch script around it.
99
-
100
- Record returned assignment, worktree, terminal and role on the matching
101
- private coordination step, then END THE TURN. Eyes observes the result.
102
- On replay, reconcile the recorded execution. An uncertain launch blocks a new
103
- start; never invent another name to bypass it. A missing observer, runtime or
104
- credential source is a concrete blocker to report, not a repair for you to do.
105
-
106
- After accepting the worker report, `process-reports` retires that executor
107
- and uses the same handoff operation for independent review, then integration.
108
- Executed tests and independent review must bind to the current candidate HEAD.
109
-
110
- For delivery from a forge issue, retain the issue link on the project task
111
- and include its repository, target branch and forge selection in each
112
- commission. A generic success event is a pointer to assignment evidence.
113
- Read the exact worker assignment's step reports and inspect the existing PR
114
- for its branch through the configured forge before deciding that publication
115
- is missing. Match repository, head branch, base branch and tested HEAD. A
116
- missing or mismatched PR keeps review pending. Before dispatching the worker,
117
- verify the assignment has a final `publish-pr` step. If a project override
118
- omitted it, add it through `ace-assign`. If the worker already completed the
119
- assignment without publication, add the missing step and reconcile its
120
- existing session. The worker finishes the step with the verified PR and
121
- then reports its new outcome. Repeating the old report cannot signal that
122
- publication finished.
123
-
124
- Pass the verified PR and tested HEAD to the reviewer. Before dispatch for
125
- a forge issue, verify the reviewer's assignment has a final
126
- `publish-approval` step. Add it through `ace-assign` if a project override
127
- omitted it. The step finishes only with the formal
128
- receipt recorded alongside the tests. For a forge issue,
129
- local review acceptance alone leaves the integration leg pending. Require
130
- the reviewer's formal approval receipt for that PR, verify it against the
131
- current forge HEAD and the executed tests, and reconcile missing or stale
132
- approval with the reviewer. If the reviewer prematurely completed the
133
- assignment, add the missing step to that assignment and reconcile its
134
- existing session. Ask it to finish that step with the verified receipt.
135
- A repeated success report for an unchanged assignment may reuse its event.
136
- The integrator receives the same PR, the verified
137
- receipt, the tested HEAD and the allowed merge method. Use a merge commit when
138
- the forge permits it and the project has no other configured preference.
139
- Immediately add the returned terminal and agent to the private coordination
140
- leg as structured task fields, including for one-off commissions:
141
-
142
- ```bash
143
- ace-task update <coordination-step> --set assignment=<id> --set worktree=<path> --set terminal=<returned-terminal> --set agent=<returned-agent>
144
- ```
145
-
146
- Cancel it when the pinned assignment has an active step or a report.
147
- When an executor observation lists the recorded terminal in
148
- `missing_sessions`, the deadline fires, or a blocked transport recovers,
149
- check Herdr and the pinned assignment. If dispatch did not return a terminal,
150
- use `herdr agent list` to find the unique assignment-labelled agent with the
151
- expected worktree and recorded role. Record its full `term_…` ID. Use that
152
- ID with `herdr agent get` and `herdr agent prompt` for recovery.
153
- Pane indexes such as `p5` are display identifiers.
154
- For a permitted recovery, submit `herdr agent prompt <terminal> "<text>"`.
155
- The guarded route accepts the terminal and text only. Use eyes for the
156
- resulting change and end the turn after recording the submission.
157
-
158
- At the startup deadline, reconcile any uncertain dispatch acknowledgement.
159
- If the exact session is still idle, interactive-ready, has no
160
- completed turn, and the pinned assignment has no activity or report, record
161
- one recovery attempt before sending the original commission to that same
162
- terminal. The commission must still name that exact assignment and role.
163
- Never repeat the original startup dispatch or commission after a session has begun work.
164
- After confirmed recovery delivery, arm one follow-up deadline
165
- `dispatch-recovery-<id>` with the same assignment revision and a fresh
166
- 300000-millisecond window. Cancel it on assignment activity or a report.
167
- If it expires without progress, escalate. Do not rearm it.
168
- If delivery stays uncertain, the session is missing, or the binding is
169
- ambiguous, record the blocker and escalate through HITL. Do the same when
170
- delivery was confirmed but the assignment still has no activity at the
171
- deadline. Keep the unfinished assignment pending or blocked. Reconcile
172
- reports through their owners before acknowledging events.
173
-
174
- ### Interrupted executor processes
175
-
176
- When native Herdr positively reports that a previously running executor is
177
- absent, preserve its assignment and worktree. Assess unfinished work with
178
- `ace-coworker resume --config <recorded-prepare-config> --dry-run` from that
179
- worktree. For a running assignment, retry
180
- `ace-overseer work-on --task <same-project-task> --runtime herdr` from the
181
- project root. This resumes the recorded Pi transcript with fresh role credentials
182
- and a continuation prompt. Normally it uses the original terminal. After a failed
183
- resume and native terminal replacement, the product may verify executor absence
184
- and resume that same transcript in a fresh pane, leaving the old pane untouched.
185
- It preserves the assignment and does not advance to the next role. Record the
186
- returned pane/session identity and any explicit previous-terminal receipt; arm a
187
- single five-minute recovery deadline and cancel on verified progress.
188
- Do not use `handoff` to bypass an unfinished role. Missing transcript, unverified
189
- terminal replacement or uncertain start/prompt remains a blocker: preserve the
190
- binding and escalate; never clear it or mark steps complete to force a launch.
191
-
192
- ### Ended worker turns and provider failures
193
-
194
- An executor event's `completion_seq` means a native turn ended, not that its
195
- assignment succeeded. Before acknowledging that revision, read the current
196
- native session, pinned assignment and reports. Do not reuse an earlier
197
- `working` observation. If the assignment is complete, adopt its report through
198
- `process-reports`. If the session is currently working, leave it alone.
199
- `state_change_seq` records state transitions, not a progress heartbeat: an
200
- unchanged value while working does not establish a stall. At a recovery
201
- deadline, inspect the active command and current owner artifacts before
202
- escalating. A reviewer may have saved rounds in `.ace-local/review/sessions/`,
203
- feedback dispositions, commits or a new PR HEAD while its assignment step is
204
- still unfinished; preserve those results and continue that step. Count progress
205
- only when artifacts are new or changed since the recovery baseline recorded
206
- in the existing coordination leg (assignment revision, review round, commit or
207
- PR HEAD). Historical artifacts alone cannot cancel its deadline. Absence of a
208
- final assignment report does not mean absence of work.
209
-
210
- If the same interactive session ended with a provider error and its assignment
211
- is unfinished, inspect its last output and recorded progress. Record recovery
212
- in the existing coordination leg, then send a continuation to that same
213
- terminal naming the assignment and unfinished step. Preserve completed work,
214
- commits and PRs; do not resend the original commission or create another agent.
215
- This continuation is distinct from startup recovery above. Acknowledge the
216
- handled revision only after recording the outcome, then end the turn with eyes
217
- armed. A prompt receipt alone does not prove progress. After submission, arm
218
- one existing eyes deadline `provider-recovery-<assignment>` pinned to the
219
- assignment revision, with a 300000-millisecond window. Cancel it only on
220
- verified assignment or work-product progress. Do not rearm it merely because
221
- another timeout or completion event arrived. On expiry, inspect the current
222
- owners; if there has been no progress, stop automatic retries and escalate the
223
- concrete provider failure through HITL. Preserve the deadline while handling
224
- repeated failures so a stalled continuation cannot silently wait forever.
225
- An unknown outcome or ambiguous session also requires reconciliation/escalation,
226
- not a speculative continuation.
227
-
228
- ## Escalation (HITL)
229
-
230
- - Decisions outside your granted autonomy go to the Captain:
231
- `ace-hitl create` with a precise question, then end your turn armed for the
232
- wake. Blocking wait is an emergency path only.
233
- - On an answered-case wake, read the exact case with `ace-hitl show CASE`.
234
- Check its application record and the effect owner's state. If the effect
235
- is already recorded for this case, reuse that evidence without repeating
236
- the operation. Otherwise apply the decision once and record the case ID
237
- and effect through the task, assignment or forge owner.
238
- - After verifying the effect, record it with
239
- `ace-hitl apply CASE --ref OWNER_REF` (for example `task:<task-id>`), then
240
- run `ace-hitl close CASE` and verify closure. The answer being received
241
- does not record an effect. A closed case needs no further action on
242
- redelivery. Acknowledge the answered revision after closure or after
243
- recording a concrete blocker through the existing owner.
244
-
245
- ## Standing observation
246
-
247
- After start, call the `ace_eyes` tool with action `start` and `configPath`
248
- pointing to `<project>/.ace-local/overseer/eyes.json` (interval 15s is the default
249
- reconcile cadence). Verify enabled=true, breaker=null, errors=0 and a successful
250
- observation with every required source healthy. A degraded source blocks
251
- report processing and new dispatch: record its returned error through the existing task/HITL owner
252
- and end the turn. If that owner is also unavailable, report its failure and
253
- end the turn. Do not investigate implementation or host transport yourself.
254
- Writing `/eyes` in prose does not arm observation. If the configuration is
255
- missing or invalid, record or escalate that precise blocker and end the turn;
256
- do not dispatch work without observation or create/edit the configuration yourself. `ace-overseer observe` — what eyes polls —
257
- projects local source classes from their owners: project tasks,
258
- private `_overseer` tasks, worktrees, coworker availability (existing run
259
- identifiers, never pane names), assignment progress, configured project
260
- session states and HITL events. A real
261
- revision wakes you once; identical state stays silent; a degraded source is
262
- visible data (`source-health` rows and the `sources` map), never an empty
263
- healthy list. When a `source-health` revision reports recovery, check that all
264
- required sources are healthy and reconcile work that was held by the fault.
265
- Acknowledge the health revision after recording any required owner state.
266
- After a restart, complete the first turn's observation health gate and end
267
- the turn. A report that arrived in your absence surfaces as an unacknowledged
268
- revision. On delivery, reconcile it and choose work by the existing plan/dependency order (`ace-overseer
269
- process-reports` and the delegation procedure above). Acknowledge (`/eyes ack KEY
270
- REVISION`) only AFTER the proper task/assignment accepted the work: model
271
- settlement alone is not proof of handling.
272
-
273
- ## Boundaries
274
-
275
- - One overseer per project: you own the session label you were started with.
276
- Another start reuses you; you never spawn a second whole-project overseer.
277
- - Access is limited to what the project configuration and the delegated task
278
- grant. Credentials and OTP values never enter prompts, reports or tracked
279
- files. Configured credential files must remain private (`0600`).
280
- When forge access is missing, do not search host credential stores or set up
281
- a persistent `fj` login yourself. Use the configured client as described below.
282
- If that client reports missing access, record its exact error and end the turn.
283
- Never ask a human to paste a token.
284
- - Remote issue and PR observation (`observe.remote.issues` and
285
- `observe.remote.pull_requests`) and the HITL reply
286
- receiver (`ace-hitl reply CASE --stdin`) are delivered surfaces: watch the
287
- configured remote sources through them and report what you actually see.
288
- Capabilities genuinely not yet delivered (e.g. the Hermes transport
289
- producer on the host) are explicitly not available in this slice; do not
290
- simulate them. Missing capability is a reported gap, not a workaround.
291
-
292
- ## Project-scoped forge access
293
-
294
- The session starts with the selected project's read or role credential in its
295
- environment or private provider config. For Forgejo, use the installed `fj`
296
- client with the configured host/repository (consult its help for command syntax).
297
- It reads its prepared login from the session's XDG_DATA_HOME; no interactive login
298
- or auth lease request is needed for a static role credential. An anonymous curl
299
- 403 is not evidence that the configured client lacks access. Do not probe host
300
- lease inventory or search credential stores before trying the configured client.
301
- Continuous observation needs a read credential valid for the observer's
302
- lifetime. An unavailable login is a visible configuration blocker; do not
303
- read host administrator credentials or invent another login method.
304
-
305
- When the deployment explicitly configures a temporary credential provider,
306
- use the existing `ace-lab auth request/status/exec/revoke` procedure. These
307
- commands run locally; they do not depend on a service. Request only the named
308
- repository and role. `auth exec` gives its child the credential and reports
309
- execution and cleanup separately. An unconfirmed revoke remains unresolved.
310
- Static shared project credentials are not revoked when one worker exits.
311
-
312
- Pass repository, role, scope and acceptance criteria in the commission,
313
- never tokens. Workers implement, reviewers independently review, and
314
- integrators merge; the coordinator never borrows those roles to do their work.
22
+ Read the current goal, private tasks and existing executor state. Session initialization
23
+ arms eyes from the standard configuration owned by `ace-overseer start`.
24
+ Use `ace_eyes` action `status`: require enabled observation, no breaker and every
25
+ required source healthy. Do not create eyes.json or manually arm another observer.
26
+ A process or submitted prompt alone is not readiness. If a required source fails,
27
+ record the concrete blocker and end the turn; delegate its repair when observation
28
+ and the authorized execution path permit it. Do not consume reports blindly.
29
+ Do not dispatch or process reports during this initialization turn. Leave initial revisions pending.
30
+ Once observation is healthy, END THIS TURN with the observed state. Then act on the current
31
+ commission or meaningful changes; do not sleep-poll inside model turns.
32
+
33
+ ## Delegate and receive
34
+
35
+ Use `ace-task` for all task writes. Keep a private managing task under the ignored
36
+ `.ace-tasks/_overseer/` and a separate executable project task with the goal,
37
+ allowed paths, acceptance checks and explicit ordered `roles` (or single `role`).
38
+ Code uses worker/reviewer/integrator with unchanged 3/2/10 review, tests and formal
39
+ approval for the current HEAD. Configuration uses bootstrap-worker/bootstrap-verifier:
40
+ short independent operational checks, no code-review rounds, PR or integrator.
41
+ A needed code fix is a separate commission. Dependencies require accepted integration,
42
+ not just a worker success or an open PR. Release requires its own mandate.
43
+
44
+ Run `ace-overseer work-on --task <project-task> --manager <private-manager> --runtime herdr`.
45
+ The command creates/reuses role children, records assignment references before launch
46
+ and binds the real executor afterwards. Do not copy assignment, project_task,
47
+ worktree or terminal fields yourself. `ace-coworker start` only prepares the brief.
48
+ End the turn after dispatch. On an interrupted start, retry this same command;
49
+ never create a replacement task or executor to bypass uncertainty.
50
+
51
+ For incoming reports use `ace_eyes` action `process-reports`. It invokes the existing
52
+ processor, verifies the outcome, records acceptance, retires the executor, continues
53
+ the next role and acknowledges only completed exact report revisions. Read its result:
54
+ accepted does not mean cleanup or next-executor start succeeded. Retry the same
55
+ operation after resolving a blocker. Other event kinds still require explicit ack
56
+ after their owning state has accepted the effect. Never invent a revision or identity.
57
+
58
+ For an explicit handoff use `ace-overseer handoff --task <project-task> --manager
59
+ <private-manager> --runtime herdr`. An existing executor is reconciled, not duplicated.
60
+ A failed worker returns to execution; you do not diagnose or repair its implementation.
61
+ After five minutes without work-product progress, inspect its owner status and
62
+ commission the necessary diagnosis instead of repeating prompts indefinitely.
63
+
64
+ The processor closes a manager only after all required children and decisions are
65
+ complete. Bootstrap includes a confirm-start condition: use standard project start
66
+ and require its first turn and healthy eyes before accepting that condition. Code
67
+ completion requires its commissioned integration. Keep incomplete work visible.
68
+
69
+ ## Decisions and idle
70
+
71
+ Routine technical choices stay within the commission. For a choice outside your
72
+ mandate, use the decision procedure in `ace-bundle wfi://overseer/queue`: one HITL
73
+ case, Second Captain recommendation, then the real Captain answer. Suggestions are
74
+ not permission. Continue independent work while a decision blocks its dependants.
75
+ Secret/OTP cases bypass the advisor. Never place secrets in task text or reports.
76
+
77
+ After handling meaningful changes, end the turn. Eyes remains active while the model
78
+ is idle. Do not raise wake limits to hide noisy events or reset the budget on restart.
79
+ Use existing status/readiness outputs for evidence; installation, configuration,
80
+ implementation, review and integration belong to their commissioned executors.
81
+ Keep `ACE_TOOLCHAIN=installed` when selected; missing runtime is a visible failure,
82
+ never permission to run checkout binstubs or a project development bundle.
@@ -10,8 +10,8 @@ module Ace
10
10
  FIRST_TURN_PROMPT = <<~PROMPT.chomp
11
11
  Begin your first turn now. Execute the "First turn after start"
12
12
  protocol in your standing instructions in its listed order.
13
- Arm observation with the ace_eyes tool, action start, using configPath
14
- set to .ace-local/overseer/eyes.json relative to your working directory.
13
+ Observation is armed by session initialization. Read ace_eyes status;
14
+ do not create its standard configuration or start another observer.
15
15
  Report reconciliation and new dispatch both require a successful
16
16
  observation with every required source healthy. If observation is
17
17
  unavailable or degraded, record its exact error through the existing
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "json"
2
3
 
3
4
  module Ace
4
5
  module Overseer
@@ -10,6 +11,8 @@ module Ace
10
11
  desc "Hand the task worktree to the next role in the sequence"
11
12
 
12
13
  option :task, aliases: ["-t"], desc: "Task reference whose worktree receives the next role"
14
+ option :json, type: :boolean, default: false, desc: "Machine result; progress on stderr"
15
+ option :manager, desc: "Private managing task reference"
13
16
  option :runtime, desc: "Terminal backend (auto, tmux, herdr)"
14
17
  option :dry_run, type: :boolean, default: false, desc: "Check handoff readiness without effects"
15
18
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
@@ -21,7 +24,7 @@ module Ace
21
24
  @config = config
22
25
  end
23
26
 
24
- def call(task: nil, runtime: nil, dry_run: false, **options)
27
+ def call(task: nil, runtime: nil, dry_run: false, manager: nil, json: false, **options)
25
28
  selected_runtime = runtime || (@config || Ace::Overseer.config)["runtime"] || "auto"
26
29
  unless %w[auto tmux herdr].include?(selected_runtime)
27
30
  raise Ace::Support::Cli::Error, "unsupported runtime: #{selected_runtime}"
@@ -34,14 +37,23 @@ module Ace
34
37
  raise Ace::Support::Cli::Error.new("--task is required. Usage: ace-overseer handoff --task <ref>")
35
38
  end
36
39
 
37
- progress = options[:quiet] ? nil : ->(msg) { puts msg }
40
+ progress = options[:quiet] ? nil : ->(msg) { json ? $stderr.puts(msg) : puts(msg) }
38
41
  result = @orchestrator.call(
39
42
  task_ref: task_ref,
40
43
  dry_run: dry_run,
41
44
  on_progress: progress,
42
- runtime: selected_runtime
45
+ runtime: selected_runtime, **(manager ? {manager_ref: manager} : {})
43
46
  )
44
47
 
48
+ execution = result[:execution]
49
+ result[:operation] = {
50
+ completed: ["delegation prepared"], running: execution ? [execution["agent"]] : [],
51
+ waiting: execution ? ["executor report"] : [], blocked: [],
52
+ evidence: result.slice(:manager_ref, :task_ref, :worktree_path, :assignment_id, :next)}
53
+ if json
54
+ puts JSON.generate(result)
55
+ return
56
+ end
45
57
  return if options[:quiet]
46
58
 
47
59
  if result[:status] == "sequence_complete"
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "json"
2
3
 
3
4
  module Ace
4
5
  module Overseer
@@ -9,8 +10,9 @@ module Ace
9
10
 
10
11
  desc "Process incoming ace-hitl reports and continue the overseer queue"
11
12
 
13
+ option :json, type: :boolean, default: false, desc: "Machine result on stdout; progress on stderr"
12
14
  option :scope, type: :string, desc: "HITL scope: current or all (default: owner checkout scope)"
13
- option :owner, type: :string, default: "overseer", desc: "Configured HITL recipient, matching the eyes observer"
15
+ option :owner, type: :string, desc: "Configured HITL recipient, matching the eyes observer"
14
16
  option :dry_run, type: :boolean, default: false, desc: "Interpret reports without writes or continuation"
15
17
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
16
18
  option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
@@ -20,15 +22,19 @@ module Ace
20
22
  @processor = processor
21
23
  end
22
24
 
23
- def call(dry_run: false, owner: "overseer", scope: nil, **options)
25
+ def call(dry_run: false, owner: nil, scope: nil, json: false, **options)
24
26
  Atoms::RepoGuard.ensure_repo!
25
27
 
26
- processor = @processor || Organisms::ReportProcessor.new(owner: owner, scope: scope)
28
+ processor = @processor || Organisms::ReportProcessor.new(owner: resolve_owner(owner), scope: scope)
27
29
  result = processor.call(
28
30
  dry_run: dry_run,
29
- on_progress: options[:quiet] ? nil : ->(msg) { puts msg }
31
+ on_progress: options[:quiet] ? nil : ->(msg) { json ? $stderr.puts(msg) : puts(msg) }
30
32
  )
31
33
 
34
+ if json
35
+ puts JSON.generate(result)
36
+ return
37
+ end
32
38
  return if options[:quiet]
33
39
 
34
40
  puts
@@ -45,6 +51,29 @@ module Ace
45
51
  rescue => e
46
52
  raise Ace::Support::Cli::Error.new(e.message)
47
53
  end
54
+
55
+ private
56
+
57
+ def resolve_owner(owner)
58
+ return owner unless owner.to_s.strip.empty?
59
+
60
+ config = Ace::Overseer.config
61
+ configured = config["owner"].to_s.strip
62
+ return configured unless configured.empty?
63
+
64
+ scope = config.dig("start", "scope").to_s.strip
65
+ return "lab-overseer" if scope == "lab"
66
+ return "overseer" if scope == "project"
67
+
68
+ if File.file?(".ace-local/overseer/eyes.json")
69
+ data = JSON.parse(File.read(".ace-local/overseer/eyes.json"))
70
+ recipient = data.is_a?(Hash) ? data["recipient"].to_s.strip : ""
71
+ return recipient unless recipient.empty?
72
+ end
73
+ "overseer"
74
+ rescue JSON::ParserError, SystemCallError
75
+ "overseer"
76
+ end
48
77
  end
49
78
  end
50
79
  end
@@ -87,9 +87,14 @@ module Ace
87
87
 
88
88
  role = report["scope"] == "lab" ? "lab-overseer" : "project-overseer"
89
89
  directory = nil
90
- Molecules::SessionEnvironment.new.with_environment(root: report.fetch("root"), role: role) do |path, credentials, _args|
90
+ Molecules::SessionEnvironment.new.with_environment(root: report.fetch("root"), role: role, eyes_config: report["eyes_config"]) do |path, credentials, _args|
91
91
  directory = credentials
92
92
  environment = JSON.parse(File.read(path))
93
+ # Foreground Pi occupies this terminal; delegated launches receive
94
+ # their destination context from native Herdr instead.
95
+ environment.merge!(ENV.to_h.slice("HERDR_ENV", "HERDR_PANE_ID", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID"))
96
+ environment["HERDR_PANE"] = environment["HERDR_PANE_ID"] if environment["HERDR_PANE_ID"]
97
+ environment["HERDR_SESSION"] = environment["HERDR_WORKSPACE_ID"] if environment["HERDR_WORKSPACE_ID"]
93
98
  system(environment, *report.fetch("command"), unsetenv_others: true)
94
99
  status = $?
95
100
  unless status&.success?
@@ -25,7 +25,7 @@ module Ace
25
25
 
26
26
  def call(format:, runtime: nil, **options)
27
27
  selected_runtime = runtime || (@config || Ace::Overseer.config)["runtime"] || "auto"
28
- unless %w[auto tmux].include?(selected_runtime)
28
+ unless %w[auto tmux herdr].include?(selected_runtime)
29
29
  raise Ace::Support::Cli::Error, "unsupported local runtime: #{selected_runtime}"
30
30
  end
31
31
 
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "json"
2
3
 
3
4
  module Ace
4
5
  module Overseer
@@ -12,6 +13,8 @@ module Ace
12
13
  option :task, aliases: ["-t"], type: :array,
13
14
  desc: "Task reference(s), repeatable and comma-separated (e.g., 230 --task 231,232)"
14
15
  option :preset, aliases: ["-p"], desc: "Assignment preset name"
16
+ option :json, type: :boolean, default: false, desc: "Machine result; progress on stderr"
17
+ option :manager, desc: "Private managing task reference"
15
18
  option :runtime, desc: "Terminal backend (auto, tmux, herdr)"
16
19
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
17
20
  option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
@@ -22,7 +25,7 @@ module Ace
22
25
  @config = config
23
26
  end
24
27
 
25
- def call(task: nil, preset: nil, runtime: nil, **options)
28
+ def call(task: nil, preset: nil, runtime: nil, manager: nil, json: false, **options)
26
29
  raise Ace::Support::Cli::Error, "--work is obsolete; select task or preset" if options.key?(:work)
27
30
  selected_runtime = runtime || (@config || Ace::Overseer.config)["runtime"] || "auto"
28
31
  unless %w[auto tmux herdr].include?(selected_runtime)
@@ -36,14 +39,23 @@ module Ace
36
39
  raise Ace::Support::Cli::Error.new("--task is required. Usage: ace-overseer work-on --task <ref>")
37
40
  end
38
41
 
39
- progress = options[:quiet] ? nil : ->(msg) { puts msg }
42
+ progress = options[:quiet] ? nil : ->(msg) { json ? $stderr.puts(msg) : puts(msg) }
40
43
  result = @orchestrator.call(
41
44
  task_ref: task_refs.first,
42
45
  task_refs: task_refs,
43
46
  cli_preset: preset,
44
- on_progress: progress, runtime: selected_runtime
47
+ on_progress: progress, runtime: selected_runtime, **(manager ? {manager_ref: manager} : {})
45
48
  )
46
49
 
50
+ execution = result[:execution]
51
+ result[:operation] = {
52
+ completed: ["delegation prepared"], running: execution ? [execution["agent"]] : [],
53
+ waiting: execution ? ["executor report"] : [], blocked: [],
54
+ evidence: result.slice(:manager_ref, :task_ref, :worktree_path, :assignment_id, :next)}
55
+ if json
56
+ puts JSON.generate(result)
57
+ return
58
+ end
47
59
  return if options[:quiet]
48
60
 
49
61
  if result[:execution]
@@ -147,7 +147,11 @@ module Ace::Overseer::Molecules
147
147
  raise Ace::Overseer::Error, "executor terminal changed before session publication"
148
148
  end
149
149
  identity = Ace::Herdr::Atoms::NativeAgentSession.normalize(pane["agent_session"], agent: "pi")
150
- return identity if identity
150
+ if identity
151
+ raw = pane["agent_session"]
152
+ execution["session_path"] = raw["value"] if raw.is_a?(Hash) && raw["kind"] == "path"
153
+ return identity
154
+ end
151
155
  raise Ace::Overseer::Error, "native executor session identity is unavailable; retry the same handoff" if @clock.call >= deadline
152
156
  @sleeper.call(0.2)
153
157
  end
@@ -178,6 +182,8 @@ module Ace::Overseer::Molecules
178
182
  end
179
183
  execution.merge!("pane" => row["pane_id"], "terminal_id" => row["terminal_id"],
180
184
  "agent_session" => identity, "state" => "started")
185
+ raw = row["agent_session"]
186
+ execution["session_path"] = raw["value"] if raw.is_a?(Hash) && raw["kind"] == "path"
181
187
  save.call
182
188
  end
183
189
 
@@ -241,7 +247,7 @@ module Ace::Overseer::Molecules
241
247
  return resume_dead(execution, brief, worktree_path, pane, save)
242
248
  end
243
249
  unless agent.is_a?(Hash) && same_executor?(agent, execution) && agent["cwd"] == worktree_path &&
244
- agent["name"] == execution["agent"] && agent["interactive_ready"] == true && agent["agent_status"] == "idle"
250
+ agent["name"] == execution["agent"] && agent["interactive_ready"] == true && %w[idle done].include?(agent["agent_status"])
245
251
  raise Ace::Overseer::Error, "resumed executor is not the expected idle session; inspect before retrying"
246
252
  end
247
253
  execution["state"] = "resume_prompting"
@@ -276,7 +282,7 @@ module Ace::Overseer::Molecules
276
282
  rows = replacement_matches(execution)
277
283
  row = rows.one? && rows.first
278
284
  unless row && row["name"] == execution["agent"] && row["cwd"] == worktree_path && row["terminal_id"] &&
279
- row["interactive_ready"] == true && row["agent_status"] == "idle" && session_identity(row) == execution["agent_session"]
285
+ row["interactive_ready"] == true && %w[idle done].include?(row["agent_status"]) && session_identity(row) == execution["agent_session"]
280
286
  raise Ace::Overseer::Error, "replacement resume is unconfirmed; inspect before retrying"
281
287
  end
282
288
  execution.merge!("pane" => row["pane_id"], "terminal_id" => row["terminal_id"])