ace-overseer 0.15.5 → 0.16.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 94c89137756a33173502e2c4fd33bccd546544e9f9f6b407a98a7e50b471145e
4
- data.tar.gz: 905b9f9838c3de93b5713ac9981c6c8bacc3b45aeb5bcaab6158af1c87ccc042
3
+ metadata.gz: 9f1239c62289b213199cac3a316f72301a82477c6d688a70d991eea76880f2cc
4
+ data.tar.gz: 4b847ed09a40f56ad43fc52b74db62325230f8c2cd393f2a175491fe21fd262f
5
5
  SHA512:
6
- metadata.gz: a0f8a6c0eb6ed7c5073b14abb8c89e927056cb6a350a9145a88f851ae364397cc86d3a886906b16efbfbc1be98b7e67da9570ef09348a3af72fbc6919a23da2e
7
- data.tar.gz: 641a1c96465712f92cd307ee430381ffe54fe7051571c04e5350d6df27fd83d8b887cddf4749b2043500df0a21ee8a3fe6d7c1a59905697973cede81fcd7c84b
6
+ metadata.gz: 6babe8200aa8f1c2275218d038dc6764a1d64e2af1a252659a38f0445ffd1c8bacfd23be81ae62c99eb22b37be34577a30fa4dc1ec2532a8a995d0c4339b4518
7
+ data.tar.gz: 668e65a1f12a922b02f1884d1e51eaec6aaf3bfd8fe5647911b1731ce51d4070dab43e79a98069ae8b93ef9fb7c357d168bc05d9af905cd1bbaa070d91f15812
@@ -0,0 +1,13 @@
1
+ ---
2
+ # WFI Sources Protocol Configuration for ace-overseer gem
3
+ # This enables workflow discovery from the installed ace-overseer gem
4
+
5
+ name: ace-overseer
6
+ type: gem
7
+ description: Overseer workflow instructions from ace-overseer gem
8
+ priority: 10
9
+
10
+ config:
11
+ relative_path: handbook/workflow-instructions
12
+ pattern: "*.wf.md"
13
+ enabled: true
data/CHANGELOG.md CHANGED
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ### Added
11
+ - Added an explicit Lab runtime adapter for listing projects and agents, preparing and dispatching Works, forwarding prompts over stdin, starting exact-head reviews, and stopping workers.
12
+
13
+ ### Changed
14
+ - Kept tmux as the default runtime while allowing `work-on`, `status`, and dry-run `prune` to delegate through the system-owned `/usr/local/bin/lab` boundary.
15
+
16
+
17
+ ## [0.16.0] - 2026-09-20
18
+
19
+ ### Added
20
+ - Shipped the overseer lifecycle workflow so `wfi://overseer` resolves for gem consumers: the gem now registers `.ace-defaults/nav/protocols/wfi-sources/ace-overseer.yml` (ace-hitl pattern) and ships real workflow instructions for `work-on`, `status`, and `prune` in `handbook/workflow-instructions/overseer.wf.md`, with prune-safety and status-truth executed-check contracts as first-class non-negotiable steps.
21
+ - Added a packaging test proving the built gem ships both the workflow-instructions payload and the wfi source registration so a future gemspec glob change cannot silently drop them.
22
+
23
+ ## [0.15.6] - 2026-09-02
24
+
25
+ ### Technical
26
+ - Included in the coordinated all-package patch release preparation for ACE monorepo review.
10
27
  ## [0.15.5] - 2026-08-12
11
28
 
12
29
  ### Technical
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  <div align="center">
2
2
  <h1> ACE - Overseer </h1>
3
3
 
4
- One command to provision a worktree, open a tmux window, and start working on a task.
4
+ One command surface for local tmux work and isolated Lab workers.
5
5
 
6
6
  <img src="../docs/brand/AgenticCodingEnvironment.Logo.XS.jpg" alt="ACE Logo" width="480">
7
7
  <br><br>
@@ -20,6 +20,10 @@
20
20
 
21
21
  Starting task work means creating a worktree, opening a tmux window, and preparing an assignment - three manual steps before you even begin coding. ace-overseer collapses that into a single command, tracks what is running where, and cleans up finished worktrees so nothing lingers. You can jump straight to a focused worktree any time with a single invocation.
22
22
 
23
+ On a configured Lab machine, the same CLI can prepare and dispatch the Lab's
24
+ system-owned `Work` objects. The default remains the existing tmux runtime;
25
+ using Lab always requires an explicit `--runtime lab` or a Lab-only command.
26
+
23
27
  ## How It Works
24
28
 
25
29
  1. Resolve task refs and create a scoped worktree.
@@ -42,6 +46,22 @@ ace-overseer work-on --task 8q4 --preset work-on-task
42
46
  ace-overseer status --watch
43
47
  ```
44
48
 
49
+ **Operate Lab workers** - prepare a reviewed Work, dispatch it to a configured
50
+ agent, and inspect it without teaching ACE how to manage containers or Herdr:
51
+
52
+ ```bash
53
+ ace-overseer prepare --runtime lab --project nervus \
54
+ --source lab-plan:oauth-callback --work W142 \
55
+ --planner admin-agy --title "OAuth callback validation"
56
+ ace-overseer work-on --runtime lab --work W142 --agent builder-codex
57
+ ace-overseer status --runtime lab --project nervus
58
+ ```
59
+
60
+ Lab owns worktrees, credentials, Podman, and Herdr. Overseer decides what to
61
+ run and delegates only through the absolute `/usr/local/bin/lab` boundary.
62
+ Use `ace-overseer prune WORK... --runtime lab --dry-run` to preview exact Work
63
+ destruction, then repeat with `--yes` to delegate it to Lab.
64
+
45
65
  **Clean up finished work** - [`ace-overseer prune`](docs/usage.md#ace-overseer-prune) removes completed worktrees safely. It checks three conditions before removing: assignment completed, task marked done, and git working tree clean. Use `--dry-run` to preview what would be pruned, `--force` for worktrees that fail safety checks, or `--assignment` to prune a single stale assignment.
46
66
 
47
67
  **Customize the workflow** - the full pipeline is defined in two layers: the [assignment preset](../ace-assign/.ace-defaults/assign/presets/work-on-task.yml) controls which steps run and in what order (onboard, implement, test, release, review, PR), while each step references a [workflow instruction](../ace-task/handbook/workflow-instructions/task/work.wf.md) that defines how it executes. Browse [available presets](../ace-assign/.ace-defaults/assign/presets/) or create your own to tailor the pipeline to your project.
data/docs/usage.md CHANGED
@@ -3,8 +3,8 @@ doc-type: user
3
3
  title: ace-overseer Usage
4
4
  purpose: Full CLI reference for ace-overseer commands and options.
5
5
  ace-docs:
6
- last-updated: 2026-03-22
7
- last-checked: 2026-03-22
6
+ last-updated: 2026-08-30
7
+ last-checked: 2026-08-30
8
8
  ---
9
9
 
10
10
  # Usage
@@ -14,6 +14,12 @@ ace-docs:
14
14
  - `ace-overseer work-on`
15
15
  - `ace-overseer status`
16
16
  - `ace-overseer prune`
17
+ - `ace-overseer projects`
18
+ - `ace-overseer agents`
19
+ - `ace-overseer prepare`
20
+ - `ace-overseer prompt`
21
+ - `ace-overseer review`
22
+ - `ace-overseer stop`
17
23
 
18
24
  ## `ace-overseer work-on`
19
25
 
@@ -23,8 +29,11 @@ Invocation: `ace-overseer work-on --task <task-ref>`.
23
29
 
24
30
  Options:
25
31
 
26
- - `--task`, `-t` (required): task reference(s); repeatable and comma-separated values supported
32
+ - `--task`, `-t` (required for tmux): task reference(s); repeatable and comma-separated values supported
27
33
  - `--preset`, `-p`: assignment preset name
34
+ - `--runtime`: `tmux` (default) or `lab`
35
+ - `--work`: existing Lab Work ID; required with `--runtime lab`
36
+ - `--agent`: configured Lab agent ID; required with `--runtime lab`
28
37
  - `--quiet`, `-q`: suppress non-essential output
29
38
  - `--debug`, `-d`: show debug output
30
39
  - `--help`, `-h`: show help
@@ -41,6 +50,8 @@ Options:
41
50
 
42
51
  - `--format`: output format (`table`, `json`)
43
52
  - `--watch`, `-w`: auto-refresh dashboard
53
+ - `--runtime`: `tmux` (default) or `lab`
54
+ - `--project`: filter Lab Works by project
44
55
  - `--quiet`, `-q`: suppress non-essential output
45
56
  - `--debug`, `-d`: show debug output
46
57
  - `--help`, `-h`: show help
@@ -61,6 +72,7 @@ Options:
61
72
  - `--force`, `-f`: force-remove unsafe worktrees
62
73
  - `--yes`, `-y`: skip interactive confirmation
63
74
  - `--dry-run`: list prune candidates only
75
+ - `--runtime`: `tmux` (default) or `lab`; Lab accepts dry-run only and delegates destruction to the exact `lab work destroy WORK --confirm` command
64
76
  - `--quiet`, `-q`: suppress non-essential output
65
77
  - `--debug`, `-d`: show debug output
66
78
  - `--help`, `-h`: show help
@@ -73,6 +85,32 @@ Check dashboard: `ace-overseer status`.
73
85
 
74
86
  Preview then prune: `ace-overseer prune --dry-run`, then `ace-overseer prune --yes`.
75
87
 
88
+ ## Lab Runtime
89
+
90
+ Lab commands are available only where `/usr/local/bin/lab` is installed. ACE
91
+ does not read Lab credentials and does not call Podman or Herdr directly.
92
+
93
+ - `ace-overseer projects`: list registered Lab projects.
94
+ - `ace-overseer agents`: list registered Lab agents and concurrency limits.
95
+ - `ace-overseer prepare --runtime lab --project PROJECT --source KIND:ID --work WORK --planner AGENT --title TITLE`: create a reviewed Work and its isolated worktree.
96
+ - `ace-overseer work-on --runtime lab --work WORK --agent AGENT`: reserve the agent, create or reuse its Herdr workspace, and dispatch it.
97
+ - `ace-overseer status --runtime lab [--project PROJECT] [--format table|json]`: show Lab Work state. Continuous status lives in each project Herdr session, so `--watch` is intentionally rejected for Lab.
98
+ - `ace-overseer prompt --work WORK --file PATH`: forward prompt text from a file to the Work pane. Piped stdin is also supported; prompt text is never passed as a process argument.
99
+ - `ace-overseer review --work WORK --pr NUMBER`: prepare an exact-head admin review checkout and pane.
100
+ - `ace-overseer stop --work WORK`: stop the assigned process without destroying Work state.
101
+ - `ace-overseer prune WORK... --runtime lab --dry-run`: preview exact Work destruction commands; rerun with `--yes` to delegate each destruction to Lab.
102
+
103
+ Example:
104
+
105
+ ```bash
106
+ ace-overseer prepare --runtime lab --project nervus \
107
+ --source nervus-thread:67611c0b-f44c-4ac4-ae4e-55773b175617 \
108
+ --work W321 --planner admin-agy --title "Reviewed task title"
109
+ ace-overseer work-on --runtime lab --work W321 --agent builder-codex
110
+ ace-overseer prompt --work W321 --file .ace-local/prompts/W321.md
111
+ ace-overseer status --runtime lab --project nervus --format json
112
+ ```
113
+
76
114
  ## Public Verification Paths
77
115
 
78
116
  Use these user-visible checks when validating behavior end-to-end:
@@ -1,16 +1,126 @@
1
1
  ---
2
+ name: overseer-workflow
3
+ description: Orchestrate task worktrees with ace-overseer (work-on, status, prune) under binding prune-safety and status-truth executed-check contracts
4
+ allowed-tools: Bash, Read
2
5
  doc-type: workflow
3
6
  title: Overseer Workflow
4
- purpose: overseer workflow
7
+ purpose: Binding process contract for the overseer lifecycle (work-on, status, prune) with executed-check contracts for prune safety and status truth.
5
8
  ace-docs:
6
- last-updated: 2026-03-12
7
- last-checked: 2026-03-21
9
+ last-updated: '2026-09-20'
10
+ last-checked: '2026-09-20'
8
11
  ---
9
12
 
10
13
  # Overseer Workflow
11
14
 
15
+ ## Goal
16
+
17
+ Orchestrate task worktrees with `ace-overseer` (work-on, status, prune) as the
18
+ binding process contract. Two executed-check contracts in this workflow are
19
+ non-negotiable: **prune safety** and **status truth**. They exist because a
20
+ described or remembered check is how worktrees and commits get silently
21
+ destroyed and stale blockers masquerade as state. Every step below is executed,
22
+ never assumed.
23
+
12
24
  ## Instructions
13
25
 
14
- 1. For work-on flows, run `ace-overseer work-on --task $ARGUMENTS`.
15
- 2. Use `ace-overseer status` to inspect active worktrees.
16
- 3. Use `ace-overseer prune --dry-run` before destructive cleanup, then `ace-overseer prune --yes` when confirmed.
26
+ Run the loaded workflow as the source of truth and execute it end-to-end
27
+ instead of only summarizing it.
28
+
29
+ ### 1) Work-on: start focused task work
30
+
31
+ Provision a worktree, open a tmux window, and prepare the assignment:
32
+
33
+ ```bash
34
+ ace-overseer work-on --task <task-ref>
35
+ ```
36
+
37
+ - Repeatable/comma-separated refs fan out into the same batch: `ace-overseer work-on --task 230 --task 231,232`
38
+ - Optional preset: `ace-overseer work-on --task <task-ref> --preset <preset-name>`
39
+ - After launch, switch to the tmux window and run `/ace-assign-drive`.
40
+ - Lab runtime variant (reserved agent + Herdr workspace): `ace-overseer work-on --runtime lab --work <work-id> --agent <agent-id>` -- `--task`/`--preset` are not supported with the Lab runtime.
41
+
42
+ Never improvise worktree provisioning by hand when `work-on` exists; ad-hoc
43
+ worktrees bypass the assignment lifecycle that `status` and `prune` rely on.
44
+
45
+ ### 2) Status: inspect active worktrees
46
+
47
+ ```bash
48
+ ace-overseer status # table dashboard
49
+ ace-overseer status --format json # machine-readable snapshot
50
+ ```
51
+
52
+ Lab runtime: `ace-overseer status --runtime lab [--project <project>]` (no
53
+ `--watch`; continuous status lives in each project Herdr session).
54
+
55
+ #### Status truth (non-negotiable executed check)
56
+
57
+ Recorded blockers are **claims, not state**. During any status review:
58
+
59
+ 1. For each pending owner-blocked task, look for an executable non-secret
60
+ check that can confirm or refute the blocker, and run it when one exists.
61
+ 2. Close or correct stale tasks **in the same change** -- do not accept a
62
+ recorded blocker as state and do not defer the correction to a later pass.
63
+ 3. Report what you executed (command + observed output) as the evidence for
64
+ each kept or closed blocker. A described or remembered check is never
65
+ sufficient.
66
+
67
+ ### 3) Prune: remove finished worktrees safely
68
+
69
+ Always preview before destructive cleanup:
70
+
71
+ ```bash
72
+ ace-overseer prune --dry-run
73
+ ```
74
+
75
+ Then apply only when confirmed:
76
+
77
+ ```bash
78
+ ace-overseer prune --yes
79
+ ```
80
+
81
+ - Targeted: `ace-overseer prune <task-ref|folder>...` or `ace-overseer prune --assignment <assignment-id>`
82
+ - Lab runtime: `ace-overseer prune <work-id>... --runtime lab --dry-run`, rerun with `--yes` to delegate each destruction to Lab.
83
+
84
+ #### Prune safety (non-negotiable executed check)
85
+
86
+ Prune/removal of a worktree or branch happens **only after an executed
87
+ preservation proof** for every commit that would be removed:
88
+
89
+ 1. Prove the work is already merged into its base:
90
+
91
+ ```bash
92
+ git merge-base --is-ancestor <work-head> <base>
93
+ ```
94
+
95
+ Exit code 0 proves every commit on the work branch is contained in the base.
96
+
97
+ 2. If the work was migrated elsewhere (squash-merge, cherry-pick, hand-off),
98
+ prove the copy exists with a subject-level search in the successor
99
+ repository:
100
+
101
+ ```bash
102
+ git -C <successor-repo> log --all --grep "<subject>"
103
+ ```
104
+
105
+ The search must return the commit(s) carrying the work. No output means no
106
+ proof.
107
+
108
+ 3. A described or remembered proof is never sufficient -- run the command and
109
+ observe the result in this session.
110
+
111
+ 4. Any commit without a proven copy **blocks the prune** for that worktree.
112
+ Report the blocked candidate (ref, head SHA, missing proof) to the operator;
113
+ never silently drop it and never force past a failed proof.
114
+
115
+ ## Non-Negotiable Contracts Summary
116
+
117
+ | Contract | Claim | Executed proof |
118
+ |--------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
119
+ | Prune safety | "The work is preserved" | `git merge-base --is-ancestor <work-head> <base>` (rc=0), or `git -C <successor-repo> log --all --grep "<subject>"` returning the migrated commit |
120
+ | Status truth | "The task is blocked on the owner" | An executable non-secret check run in this session; stale tasks closed/corrected in the same change |
121
+
122
+ ## Success Criteria
123
+
124
+ - Work starts only through `ace-overseer work-on` (or the Lab-runtime equivalent), never through ad-hoc manual worktree provisioning.
125
+ - Every status review re-verified pending owner-blocked tasks with an executed check and corrected stale state in the same change.
126
+ - Every pruned worktree/branch had an executed preservation proof; candidates without proof were reported as blocked, not removed.
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+ module Ace
4
+ module Overseer
5
+ module CLI
6
+ module Commands
7
+ class Agents < Ace::Support::Cli::Command
8
+ include Ace::Support::Cli::Base
9
+ desc "List configured Lab agents"
10
+ def initialize(client: nil)
11
+ super()
12
+ @client = client || Molecules::LabClient.new
13
+ end
14
+ def call(**)
15
+ puts JSON.pretty_generate(@client.call("agents", "--json"))
16
+ rescue => error
17
+ raise Ace::Support::Cli::Error.new(error.message)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ module Ace
3
+ module Overseer
4
+ module CLI
5
+ module Commands
6
+ class Prepare < Ace::Support::Cli::Command
7
+ include Ace::Support::Cli::Base
8
+ desc "Prepare a Work through the Lab runtime"
9
+ option :runtime, required: true, desc: "Runtime (lab)"
10
+ option :project, required: true, desc: "Lab project slug"
11
+ option :source, required: true, desc: "Source KIND:ID"
12
+ option :work, required: true, desc: "Work ID"
13
+ option :planner, required: true, desc: "Planner agent ID"
14
+ option :title, required: true, desc: "Reviewed Work title"
15
+ def initialize(client: nil)
16
+ super()
17
+ @client = client || Molecules::LabClient.new
18
+ end
19
+ def call(runtime:, project:, source:, work:, planner:, title:, **)
20
+ raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "lab"
21
+ puts @client.call(
22
+ "work", "prepare", project,
23
+ "--source", source,
24
+ "--work", work,
25
+ "--planner", planner,
26
+ "--title", title,
27
+ json: false
28
+ )
29
+ rescue Ace::Support::Cli::Error
30
+ raise
31
+ rescue => error
32
+ raise Ace::Support::Cli::Error.new(error.message)
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ require "json"
3
+ module Ace
4
+ module Overseer
5
+ module CLI
6
+ module Commands
7
+ class Projects < Ace::Support::Cli::Command
8
+ include Ace::Support::Cli::Base
9
+ desc "List configured Lab projects"
10
+ def initialize(client: nil)
11
+ super()
12
+ @client = client || Molecules::LabClient.new
13
+ end
14
+ def call(**)
15
+ puts JSON.pretty_generate(@client.call("project", "list", "--json"))
16
+ rescue => error
17
+ raise Ace::Support::Cli::Error.new(error.message)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+ module Ace
3
+ module Overseer
4
+ module CLI
5
+ module Commands
6
+ class Prompt < Ace::Support::Cli::Command
7
+ include Ace::Support::Cli::Base
8
+ desc "Prompt a running Lab Work from stdin"
9
+ option :work, required: true, desc: "Work ID"
10
+ option :file, desc: "Read prompt text from a file instead of stdin"
11
+ def initialize(client: nil, input: $stdin)
12
+ super()
13
+ @client = client || Molecules::LabClient.new
14
+ @input = input
15
+ end
16
+ def call(work:, file: nil, **)
17
+ if file
18
+ prompt = File.read(file)
19
+ else
20
+ if @input.respond_to?(:tty?) && @input.tty?
21
+ raise Ace::Support::Cli::Error, "prompt text is required via --file or stdin"
22
+ end
23
+ prompt = @input.read
24
+ end
25
+ raise Ace::Support::Cli::Error, "prompt text must contain non-whitespace characters" if prompt.strip.empty?
26
+
27
+ puts @client.call("work", "prompt", work, stdin_data: prompt, json: false)
28
+ rescue => error
29
+ raise Ace::Support::Cli::Error.new(error.message)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -17,15 +17,24 @@ module Ace
17
17
  option :dry_run, type: :boolean, default: false, desc: "Show candidates only"
18
18
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
19
19
  option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
20
+ option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
20
21
 
21
- def initialize(orchestrator: nil, input: $stdin, output: $stdout)
22
+ def initialize(orchestrator: nil, input: $stdin, output: $stdout, lab_client: nil)
22
23
  super()
23
24
  @orchestrator = orchestrator || Organisms::PruneOrchestrator.new
24
25
  @input = input
25
26
  @output = output
27
+ @lab_client = lab_client || Molecules::LabClient.new
26
28
  end
27
29
 
28
30
  def call(**options)
31
+ runtime = options.fetch(:runtime, "tmux")
32
+ if runtime == "lab"
33
+ prune_lab(**options)
34
+ return
35
+ end
36
+ raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
37
+
29
38
  Atoms::RepoGuard.ensure_repo!
30
39
 
31
40
  targets = Array(options[:targets] || [])
@@ -65,6 +74,28 @@ module Ace
65
74
 
66
75
  private
67
76
 
77
+ def prune_lab(**options)
78
+ raise Ace::Support::Cli::Error, "--assignment is not supported with Lab runtime; provide exact Work IDs" if options[:assignment]
79
+ raise Ace::Support::Cli::Error, "--force is not supported with Lab runtime" if options[:force]
80
+
81
+ works = Array(options[:targets]).map(&:to_s)
82
+ raise Ace::Support::Cli::Error, "provide at least one exact Lab Work ID to prune" if works.empty?
83
+
84
+ if options[:dry_run]
85
+ return if options[:quiet]
86
+
87
+ works.each { |work| puts "Would destroy Lab Work #{work}: /usr/local/bin/lab work destroy #{work} --confirm" }
88
+ return
89
+ end
90
+
91
+ raise Ace::Support::Cli::Error, "Lab prune requires --yes after reviewing --dry-run" unless options[:yes]
92
+
93
+ works.each do |work|
94
+ result = @lab_client.call("work", "destroy", work, "--confirm", json: false)
95
+ puts result unless options[:quiet]
96
+ end
97
+ end
98
+
68
99
  def print_assignment_result(result)
69
100
  candidate = result[:assignment_candidate]
70
101
  if result[:dry_run]
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+ module Ace
3
+ module Overseer
4
+ module CLI
5
+ module Commands
6
+ class Review < Ace::Support::Cli::Command
7
+ include Ace::Support::Cli::Base
8
+ desc "Start an exact-head Lab review"
9
+ option :work, required: true, desc: "Work ID"
10
+ option :pr, required: true, type: :integer, desc: "Forgejo PR number"
11
+ def initialize(client: nil)
12
+ super()
13
+ @client = client || Molecules::LabClient.new
14
+ end
15
+ def call(work:, pr:, **)
16
+ puts @client.call("work", "review", work, "--pr", pr, json: false)
17
+ rescue => error
18
+ raise Ace::Support::Cli::Error.new(error.message)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -15,14 +15,30 @@ module Ace
15
15
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
16
16
  option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
17
17
  option :watch, aliases: ["-w"], type: :boolean, default: false, desc: "Auto-refresh dashboard"
18
+ option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
19
+ option :project, desc: "Filter Lab status by project"
18
20
 
19
- def initialize(collector: nil, config: nil)
21
+ def initialize(collector: nil, config: nil, lab_client: nil)
20
22
  super()
21
23
  @collector = collector || Organisms::StatusCollector.new
22
24
  @config = config
25
+ @lab_client = lab_client || Molecules::LabClient.new
23
26
  end
24
27
 
25
- def call(format:, **options)
28
+ def call(format:, runtime: "tmux", project: nil, **options)
29
+ if runtime == "lab"
30
+ if options[:watch]
31
+ raise Ace::Support::Cli::Error, "Lab watch runs in the project Herdr status pane; omit --watch"
32
+ end
33
+ arguments = ["work", "status"]
34
+ arguments.concat(["--project", project]) unless project.to_s.empty?
35
+ arguments << "--json" if format == "json"
36
+ output = @lab_client.call(*arguments, json: false)
37
+ puts output unless options[:quiet]
38
+ return
39
+ end
40
+ raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
41
+
26
42
  Atoms::RepoGuard.ensure_repo!
27
43
  return if options[:quiet]
28
44
 
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module Ace
3
+ module Overseer
4
+ module CLI
5
+ module Commands
6
+ class Stop < Ace::Support::Cli::Command
7
+ include Ace::Support::Cli::Base
8
+ desc "Stop a Lab Work"
9
+ option :work, required: true, desc: "Work ID"
10
+ def initialize(client: nil)
11
+ super()
12
+ @client = client || Molecules::LabClient.new
13
+ end
14
+ def call(work:, **)
15
+ puts @client.call("work", "stop", work, json: false)
16
+ rescue => error
17
+ raise Ace::Support::Cli::Error.new(error.message)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -9,18 +9,34 @@ module Ace
9
9
 
10
10
  desc "Create worktree, open tmux window, and prepare assignment"
11
11
 
12
- option :task, aliases: ["-t"], type: :array, required: true,
12
+ option :task, aliases: ["-t"], type: :array,
13
13
  desc: "Task reference(s), repeatable and comma-separated (e.g., 230 --task 231,232)"
14
14
  option :preset, aliases: ["-p"], desc: "Assignment preset name"
15
+ option :runtime, default: "tmux", desc: "Runtime (tmux, lab)"
16
+ option :work, desc: "Existing Lab Work ID"
17
+ option :agent, desc: "Configured Lab agent ID"
15
18
  option :quiet, aliases: ["-q"], type: :boolean, default: false, desc: "Suppress non-essential output"
16
19
  option :debug, aliases: ["-d"], type: :boolean, default: false, desc: "Show debug output"
17
20
 
18
- def initialize(orchestrator: nil)
21
+ def initialize(orchestrator: nil, lab_client: nil)
19
22
  super()
20
23
  @orchestrator = orchestrator || Organisms::WorkOnOrchestrator.new
24
+ @lab_client = lab_client || Molecules::LabClient.new
21
25
  end
22
26
 
23
- def call(task: nil, preset: nil, **options)
27
+ def call(task: nil, preset: nil, runtime: "tmux", work: nil, agent: nil, **options)
28
+ if runtime == "lab"
29
+ if task || preset
30
+ raise Ace::Support::Cli::Error, "--task and --preset are not supported with Lab runtime"
31
+ end
32
+ raise Ace::Support::Cli::Error, "--work and --agent are required for Lab runtime" if work.to_s.empty? || agent.to_s.empty?
33
+
34
+ output = @lab_client.call("work", "dispatch", work, "--agent", agent, json: false)
35
+ puts output unless options[:quiet]
36
+ return
37
+ end
38
+ raise Ace::Support::Cli::Error, "unsupported runtime: #{runtime}" unless runtime == "tmux"
39
+
24
40
  Atoms::RepoGuard.ensure_repo!
25
41
 
26
42
  task_refs = normalize_task_refs(task)
@@ -5,6 +5,12 @@ require "ace/core"
5
5
  require_relative "cli/commands/work_on"
6
6
  require_relative "cli/commands/status"
7
7
  require_relative "cli/commands/prune"
8
+ require_relative "cli/commands/projects"
9
+ require_relative "cli/commands/agents"
10
+ require_relative "cli/commands/prepare"
11
+ require_relative "cli/commands/prompt"
12
+ require_relative "cli/commands/review"
13
+ require_relative "cli/commands/stop"
8
14
 
9
15
  module Ace
10
16
  module Overseer
@@ -16,7 +22,13 @@ module Ace
16
22
  REGISTERED_COMMANDS = [
17
23
  ["work-on", "Work on a task in isolated worktree"],
18
24
  ["status", "Show status of task worktrees"],
19
- ["prune", "Remove stale task worktrees"]
25
+ ["prune", "Remove stale task worktrees"],
26
+ ["projects", "List configured Lab projects"],
27
+ ["agents", "List configured Lab agents"],
28
+ ["prepare", "Prepare a Lab Work"],
29
+ ["prompt", "Prompt a Lab Work from stdin"],
30
+ ["review", "Start a Lab Work review"],
31
+ ["stop", "Stop a Lab Work"]
20
32
  ].freeze
21
33
 
22
34
  HELP_EXAMPLES = [
@@ -28,6 +40,12 @@ module Ace
28
40
  register "work-on", Commands::WorkOn
29
41
  register "status", Commands::Status
30
42
  register "prune", Commands::Prune
43
+ register "projects", Commands::Projects
44
+ register "agents", Commands::Agents
45
+ register "prepare", Commands::Prepare
46
+ register "prompt", Commands::Prompt
47
+ register "review", Commands::Review
48
+ register "stop", Commands::Stop
31
49
 
32
50
  version_cmd = Ace::Support::Cli::VersionCommand.build(
33
51
  gem_name: "ace-overseer",
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "open3"
5
+
6
+ module Ace
7
+ module Overseer
8
+ module Molecules
9
+ class LabClient
10
+ BINARY = "/usr/local/bin/lab"
11
+
12
+ def initialize(runner: Open3)
13
+ @runner = runner
14
+ end
15
+
16
+ def call(*arguments, stdin_data: nil, json: true)
17
+ stdout, stderr, status = @runner.capture3(
18
+ BINARY,
19
+ *arguments.map(&:to_s),
20
+ stdin_data: stdin_data.to_s
21
+ )
22
+ raise Error, stderr.to_s.strip.empty? ? "Lab command failed" : stderr.to_s.strip unless status.success?
23
+
24
+ return stdout unless json
25
+
26
+ JSON.parse(stdout)
27
+ rescue Errno::ENOENT
28
+ raise Error, "Lab runtime unavailable: #{BINARY} is not installed"
29
+ rescue JSON::ParserError => error
30
+ raise Error, "Lab returned invalid JSON: #{error.message}"
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Overseer
5
- VERSION = "0.15.5"
5
+ VERSION = "0.16.0"
6
6
  end
7
7
  end
data/lib/ace/overseer.rb CHANGED
@@ -16,6 +16,7 @@ require_relative "overseer/atoms/repo_guard"
16
16
  require_relative "overseer/atoms/preset_resolver"
17
17
  require_relative "overseer/atoms/status_formatter"
18
18
  require_relative "overseer/molecules/worktree_provisioner"
19
+ require_relative "overseer/molecules/lab_client"
19
20
  require_relative "overseer/molecules/tmux_window_opener"
20
21
  require_relative "overseer/molecules/assignment_launcher"
21
22
  require_relative "overseer/molecules/worktree_context_collector"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ace-overseer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.5
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-08-12 00:00:00.000000000 Z
10
+ date: 2026-09-22 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-cli
@@ -43,14 +43,14 @@ dependencies:
43
43
  requirements:
44
44
  - - "~>"
45
45
  - !ruby/object:Gem::Version
46
- version: '0.17'
46
+ version: '0.18'
47
47
  type: :runtime
48
48
  prerelease: false
49
49
  version_requirements: !ruby/object:Gem::Requirement
50
50
  requirements:
51
51
  - - "~>"
52
52
  - !ruby/object:Gem::Version
53
- version: '0.17'
53
+ version: '0.18'
54
54
  - !ruby/object:Gem::Dependency
55
55
  name: ace-support-fs
56
56
  requirement: !ruby/object:Gem::Requirement
@@ -71,42 +71,42 @@ dependencies:
71
71
  requirements:
72
72
  - - "~>"
73
73
  - !ruby/object:Gem::Version
74
- version: '0.55'
74
+ version: '0.56'
75
75
  type: :runtime
76
76
  prerelease: false
77
77
  version_requirements: !ruby/object:Gem::Requirement
78
78
  requirements:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
- version: '0.55'
81
+ version: '0.56'
82
82
  - !ruby/object:Gem::Dependency
83
83
  name: ace-git
84
84
  requirement: !ruby/object:Gem::Requirement
85
85
  requirements:
86
86
  - - "~>"
87
87
  - !ruby/object:Gem::Version
88
- version: '0.22'
88
+ version: '0.23'
89
89
  type: :runtime
90
90
  prerelease: false
91
91
  version_requirements: !ruby/object:Gem::Requirement
92
92
  requirements:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
- version: '0.22'
95
+ version: '0.23'
96
96
  - !ruby/object:Gem::Dependency
97
97
  name: ace-git-worktree
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '0.21'
102
+ version: '0.22'
103
103
  type: :runtime
104
104
  prerelease: false
105
105
  version_requirements: !ruby/object:Gem::Requirement
106
106
  requirements:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
- version: '0.21'
109
+ version: '0.22'
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: ace-task
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -201,6 +201,7 @@ extensions: []
201
201
  extra_rdoc_files: []
202
202
  files:
203
203
  - ".ace-defaults/nav/protocols/skill-sources/ace-overseer.yml"
204
+ - ".ace-defaults/nav/protocols/wfi-sources/ace-overseer.yml"
204
205
  - ".ace-defaults/overseer/config.yml"
205
206
  - CHANGELOG.md
206
207
  - LICENSE
@@ -221,14 +222,21 @@ files:
221
222
  - lib/ace/overseer/atoms/repo_guard.rb
222
223
  - lib/ace/overseer/atoms/status_formatter.rb
223
224
  - lib/ace/overseer/cli.rb
225
+ - lib/ace/overseer/cli/commands/agents.rb
226
+ - lib/ace/overseer/cli/commands/prepare.rb
227
+ - lib/ace/overseer/cli/commands/projects.rb
228
+ - lib/ace/overseer/cli/commands/prompt.rb
224
229
  - lib/ace/overseer/cli/commands/prune.rb
230
+ - lib/ace/overseer/cli/commands/review.rb
225
231
  - lib/ace/overseer/cli/commands/status.rb
232
+ - lib/ace/overseer/cli/commands/stop.rb
226
233
  - lib/ace/overseer/cli/commands/work_on.rb
227
234
  - lib/ace/overseer/models/assignment_prune_candidate.rb
228
235
  - lib/ace/overseer/models/prune_candidate.rb
229
236
  - lib/ace/overseer/models/work_context.rb
230
237
  - lib/ace/overseer/molecules/assignment_launcher.rb
231
238
  - lib/ace/overseer/molecules/assignment_prune_safety_checker.rb
239
+ - lib/ace/overseer/molecules/lab_client.rb
232
240
  - lib/ace/overseer/molecules/prune_safety_checker.rb
233
241
  - lib/ace/overseer/molecules/tmux_window_opener.rb
234
242
  - lib/ace/overseer/molecules/worktree_context_collector.rb