ace-overseer 0.22.0 → 0.23.1

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: 3913e2fb5bdc611dec02a5cb33fca48a5395e216a09ae477e4bb452e8e4ec3ef
4
- data.tar.gz: fffdbb4d1b7cd1c28ad81595ce9bb064a23e8a7c708e1c693ccad7c1d54d99a1
3
+ metadata.gz: b00e42cea8d8748ae7bf4495d2ae97a16261bc9542882e47a8634ef7eac6f784
4
+ data.tar.gz: 44952270d2e973eb4c43b9412a30206b8a46c6fc96bb89c62e04ef072e942e5c
5
5
  SHA512:
6
- metadata.gz: 979a9f3a8f9eff1155f458fa0c54112b01c234078a93f9e4c3eddc1b1b758b3608c30694ce9054b94b27c907cce0efd3fede25c37ba12e1ce85e1810f6820373
7
- data.tar.gz: 4853d86cf0df5b06f3b5ed689ccd94483ea017c006836b358b1c805a337f85cccff266e353079766ac84bf4ffacb76508bf79f91ecf421429e57d145b6ab52d9
6
+ metadata.gz: c32caa5bd06bb268d3f0b1614af8dd5ba2c05537154b571c37314e0220eec8ffec6694fec40cddac76c036ffb6fb09a114999c44fc4cad73335d64460984954d
7
+ data.tar.gz: c93f4cd4d8c94de422486c65a2ab822e646ae2ac519832f8dc5e9e4b78b658c75789940b79e21af62b396628a489f20662287df4f28293eea84fd1b460259469
data/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.23.1] - 2026-10-17
11
+
12
+ ### Changed
13
+
14
+ - Adapt status and prune collection to `ace-git-worktree`'s inventory-based
15
+ `list_all` (no `show_tasks:` argument) after the 0.27 active-worktree listing
16
+ rework, and raise the dependency floor to `ace-git-worktree ~> 0.27` so
17
+ overseer stays aligned with the current worktree minor release line.
18
+
19
+ ## [0.23.0] - 2026-10-16
20
+
21
+ ### Added
22
+
23
+ - Add explicit ordinary preparation/launch/resume using existing worktree, native pane and provider mechanisms. Preserve original identities and dispatch intent on response loss; use native filesystem watches only as canonical-inspection hints.
24
+
25
+ ### Changed
26
+
27
+ - Follow ace-review 0.61 campaign head-assessment contracts (dependency floor).
28
+
10
29
  ## [0.22.0] - 2026-10-16
11
30
 
12
31
  ### Added
data/docs/usage.md CHANGED
@@ -12,6 +12,7 @@ ace-docs:
12
12
  ## Command Surface
13
13
 
14
14
  - `ace-overseer work-on`
15
+ - `ace-overseer ordinary`
15
16
  - `ace-overseer status`
16
17
  - `ace-overseer prune`
17
18
  - `ace-overseer projects`
@@ -54,6 +55,50 @@ observation, never canonical terminal/release or physical cleanup permission.
54
55
  Recovery reports absent/mismatched local inputs separately from original canonical
55
56
  reservation attribution, without upload, reconstruction or retry.
56
57
 
58
+ ## `ace-overseer ordinary`
59
+
60
+ Ordinary execution is explicit and uses the root-selected project's Assign owner.
61
+ `work-on --project` keeps its protected contract and never falls back here.
62
+ The native supervisor prepares each reviewed leaf in its own existing worktree
63
+ and prepared pane, registers exact captured inputs, then launches its explicit
64
+ provider profile:
65
+
66
+ ```sh
67
+ ace-overseer ordinary --operation prepare --project atlas --task 8wr.t.abc --checkout /lab/projects/atlas \
68
+ --provider pi:zai/glm-5.3-flash:high --output /private/ordinary/task-one
69
+ ace-overseer ordinary --operation launch --project atlas --context /private/ordinary/task-one/context.json
70
+ ace-overseer ordinary --operation resume --project atlas --context /private/ordinary/task-one/context.json
71
+ ```
72
+
73
+ Preparation validates the explicit checkout against the installed Git common directory, then uses WorktreeProvisioner, WindowOpener and PreparedWorkBuilder. Project configuration resolves inside the selected checkout/worktree even when the native supervisor runs from another project. The
74
+ provider/model/thinking selection must resolve through the existing LLM builder.
75
+ It retains exact IDs, HEAD, scope and worker pane before registration. If the
76
+ registration reply is lost, inspect canonical status through that original
77
+ context; if registration is absent, replay the original `prepared/request.json`
78
+ with `ace-lab assignment request`. Do not create another preparation to bypass
79
+ uncertainty. Partial preparation errors preserve their files for inspection.
80
+
81
+ Launch verifies the actual current native supervisor against canonical ownership,
82
+ retains a dispatch intent before native send and sends the worker's ordinary
83
+ `/as-assign-drive` entry. Acknowledgement means accepted send, not process start.
84
+ The worker must register its own actual start through the installed owner.
85
+ An existing dispatch intent plus no start stays `dispatch_uncertain`; neither
86
+ launch nor resume sends a second command. `status` and `resume` are read-only
87
+ observations of the original assignment/attempt and current supervisor selection.
88
+
89
+ The returned native `/watch add` command watches a result hint file. Install it
90
+ once in the supervisor session, persist through a harmless turn, then resume the
91
+ exact session without replaying add commands or restoring intentional removals.
92
+ A watch is only a wake hint; canonical `ace-lab assignment drive --operation status`
93
+ and `adopt` decide outcome. Existing progress-audit duties also reconcile lost
94
+ hints. A replaced coordinator first performs explicit canonical supervisor
95
+ adoption as documented in Lab usage; worker identities and requests survive it.
96
+
97
+ For two workers, repeat prepare with two reviewed leaf tasks and separate output
98
+ directories. Verify two distinct panes/attempts, two canonical starts and results,
99
+ and sequential reviewed ordinary merges. This source interface does not claim
100
+ installed watches, real provider turns, review, release or merge acceptance.
101
+
57
102
  ## `ace-overseer status`
58
103
 
59
104
  Show status for active task worktrees.
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../organisms/ordinary_work_on"
4
+
5
+ module Ace
6
+ module Overseer
7
+ module CLI
8
+ module Commands
9
+ class Ordinary < Ace::Support::Cli::Command
10
+ desc "Prepare, launch or resume one ordinary execution through installed Assign"
11
+ option :operation, required: true, desc: "prepare, launch, status, resume (status/resume never dispatch)"
12
+ option :project, required: true, desc: "Installed ordinary project ID"
13
+ option :task, desc: "Reviewed leaf task for preparation"
14
+ option :checkout, desc: "Explicit project checkout validated against installed common directory"
15
+ option :output, desc: "New private retained input directory for preparation"
16
+ option :provider, desc: "Explicit interactive provider/model/thinking selection"
17
+ option :context, desc: "Original context.json for launch/status/resume"
18
+
19
+ def call(operation:, project:, **options)
20
+ entry = Organisms::OrdinaryWorkOn.new
21
+ result = case operation
22
+ when "prepare"
23
+ entry.prepare(project: project, task: options.fetch(:task), checkout: options.fetch(:checkout),
24
+ output: options.fetch(:output), provider: options.fetch(:provider))
25
+ when "launch" then entry.launch(project: project, context: options.fetch(:context))
26
+ when "status", "resume" then entry.resume(project: project, context: options.fetch(:context))
27
+ else raise ArgumentError, "unknown ordinary operation"
28
+ end
29
+ puts JSON.generate(result)
30
+ rescue ArgumentError, KeyError, SystemCallError, Ace::Overseer::Error, Ace::Assign::Error, Ace::Lab::Error, Ace::Runtime::Error, Ace::LLM::Error => error
31
+ raise Ace::Support::Cli::Error, "ordinary handoff incomplete: #{error.message}; retain its inputs and inspect the original context"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -10,6 +10,7 @@ require_relative "cli/commands/agents"
10
10
  require_relative "cli/commands/prompt"
11
11
  require_relative "cli/commands/review"
12
12
  require_relative "cli/commands/stop"
13
+ require_relative "cli/commands/ordinary"
13
14
 
14
15
  module Ace
15
16
  module Overseer
@@ -20,6 +21,7 @@ module Ace
20
21
 
21
22
  REGISTERED_COMMANDS = [
22
23
  ["work-on", "Start local task work or one retained protected launch"],
24
+ ["ordinary", "Prepare, launch, inspect or resume one installed ordinary execution"],
23
25
  ["status", "Show status of task worktrees"],
24
26
  ["prune", "Remove stale task worktrees"],
25
27
  ["projects", "List visible public projects"],
@@ -36,6 +38,7 @@ module Ace
36
38
  ].freeze
37
39
 
38
40
  register "work-on", Commands::WorkOn
41
+ register "ordinary", Commands::Ordinary
39
42
  register "status", Commands::Status
40
43
  register "prune", Commands::Prune
41
44
  register "projects", Commands::Projects
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "shellwords"
4
+ require "ace/lab/organisms/ordinary_assignment_client"
5
+ require "ace/lab/molecules/ordinary_worktree_read"
6
+ require "ace/assign/organisms/ordinary_driver"
7
+ require "ace/assign/molecules/runtime_control_surface_runner"
8
+ require "ace/llm"
9
+
10
+ module Ace
11
+ module Overseer
12
+ module Organisms
13
+ # Ordinary entry: existing worktree/native launch mechanisms, immutable
14
+ # request inputs, and canonical Assign observations. No local execution.
15
+ class OrdinaryWorkOn
16
+ Files = Ace::Assign::Authority::RetainedInput
17
+
18
+ def initialize(config: nil, runtime: nil, provisioner: nil, windows: nil, builder: nil, client: nil, prepare: nil, observer: nil)
19
+ @config, @runtime = config, runtime
20
+ @provisioner = provisioner
21
+ @windows = windows
22
+ @builder = builder
23
+ @client = client
24
+ @prepare = prepare || Ace::Lab::Organisms::OrdinaryAssignmentClient.method(:prepare)
25
+ @observer = observer
26
+ end
27
+
28
+ def prepare(project:, task:, checkout:, output:, provider:)
29
+ config = selected(project)
30
+ caller = supervisor!(config)
31
+ checkout = File.realpath(checkout)
32
+ # The native coordinator can live in a different project. Explicit
33
+ # preparation cwd must resolve to this installed common directory.
34
+ source_reader = @observer || Ace::Lab::Molecules::OrdinaryWorktreeRead.new(root: checkout,
35
+ common_dir: config.worktrees.fetch("common_dir"), deadline: Process.clock_gettime(Process::CLOCK_MONOTONIC) + 30)
36
+ source_reader.observe(checkout)
37
+ directory = Files.create_output(output)
38
+ worktree = in_worktree(checkout) do
39
+ provisioner = @provisioner || Molecules::WorktreeProvisioner.new(
40
+ manager: Ace::Git::Worktree::Organisms::WorktreeManager.new(project_root: checkout))
41
+ provisioner.provision(task).fetch(:worktree_path)
42
+ end
43
+ cwd = validate_worktree!(config, worktree)
44
+ # Resolve actual provider arguments before creating any pane/agent.
45
+ prepared = in_worktree(cwd) do
46
+ (@builder || Ace::LLM::Molecules::InteractiveCommandBuilder.new).build(
47
+ provider_model: provider, prompt: "ordinary preparation", working_dir: cwd)
48
+ @prepare.call(project: project, task: task, pane: caller.fetch(:pane), output: File.join(directory, "prepared"))
49
+ end
50
+ window = (@windows || Molecules::WindowOpener.new(runtime: runtime)).open(worktree_path: cwd)
51
+ pane = runtime.prepare_pane(window: window)
52
+ context = {"version" => 1, "project_id" => project, "assignment_id" => prepared.fetch("assignment_id"),
53
+ "attempt_id" => SecureRandom.hex(16), "scope" => prepared.fetch("scope"), "pane" => pane,
54
+ "supervisor_pane" => caller.fetch(:pane), "cwd" => cwd,
55
+ "head" => observe(config, cwd).fetch("head"), "provider" => provider}
56
+ Files.write_file(directory, "context.json", JSON.generate(context) + "\n")
57
+ Files.write_file(directory, "result-hint.json", JSON.generate({"hint_only" => true}))
58
+ registration = client(project).request(input: prepared.fetch("request"))
59
+ unless registration.dig("data", "assignment_id") == prepared.fetch("assignment_id") &&
60
+ registration.dig("data", "project_id") == project
61
+ raise Error, "ordinary registration returned a different project or assignment; recover #{directory}/context.json"
62
+ end
63
+ describe(File.join(directory, "context.json"), context).merge("state" => "ready_not_dispatched", "window" => window)
64
+ end
65
+
66
+ def launch(project:, context:)
67
+ driver = driver(project, context)
68
+ selected_context = driver.context
69
+ config = selected(project)
70
+ caller = supervisor!(config)
71
+ supervisor = driver.supervisor
72
+ unless runtime.process_binding(pane: caller.fetch(:pane), caller_pid: Process.pid) == supervisor.fetch("binding")
73
+ raise Error, "ordinary launch belongs to the canonical native supervisor; adopt explicitly after its previous generation ends"
74
+ end
75
+ cwd = validate_worktree!(config, selected_context.fetch("cwd"))
76
+ directory = File.dirname(driver.path)
77
+ # Canonical supervisor lookup also proves registration. Recovery does
78
+ # not register again or reconstruct work from a local assignment cache.
79
+ observed = driver.status
80
+ return describe(driver.path, selected_context).merge("canonical" => observed) unless observed.fetch("state") == "not_started"
81
+ unless observe(config, cwd).fetch("head") == selected_context.fetch("head")
82
+ raise Error, "ordinary launch checkout moved; preserve original context and reconcile before dispatch"
83
+ end
84
+ invocation = in_worktree(cwd) do
85
+ (@builder || Ace::LLM::Molecules::InteractiveCommandBuilder.new).build(provider_model: selected_context.fetch("provider"),
86
+ prompt: "/as-assign-drive --ordinary #{driver.path.shellescape}", working_dir: cwd,
87
+ subprocess_env: {"ACE_ASSIGN_ORDINARY_CONTEXT" => driver.path,
88
+ "ACE_ASSIGN_ORDINARY_PROJECT" => project, "ACE_RUNTIME" => "herdr"})
89
+ end
90
+ # Create before native send. A lost send/reply cannot cause another
91
+ # invocation. Resume only observes; it never deletes this intent.
92
+ intent = {"pane" => selected_context.fetch("pane"), "assignment_id" => selected_context.fetch("assignment_id"),
93
+ "attempt_id" => selected_context.fetch("attempt_id"), "provider" => invocation.fetch(:provider),
94
+ "model" => invocation.fetch(:model), "thinking_level" => invocation[:thinking_level],
95
+ "command_sha256" => Digest::SHA256.hexdigest(invocation.fetch(:command).to_json)}
96
+ begin
97
+ Files.write_file(directory, "dispatch.json", JSON.generate(intent) + "\n")
98
+ rescue Errno::EEXIST
99
+ return describe(driver.path, selected_context).merge("state" => "dispatch_uncertain", "canonical" => observed,
100
+ "remediation" => "Inspect the original pane and canonical status; never launch a replacement from this context")
101
+ end
102
+ runner = Ace::Assign::Molecules::RuntimeControlSurfaceRunner.new(runtime: "herdr", adapter: runtime)
103
+ runner.run_invocation_in_pane(pane_target: selected_context.fetch("pane"), command: invocation.fetch(:command),
104
+ env: invocation[:env], working_dir: invocation.fetch(:working_dir), visible_handoff: invocation[:prompt])
105
+ describe(driver.path, selected_context).merge("state" => "accepted_not_started", "canonical" => driver.status)
106
+ end
107
+
108
+ # Restart/recovery is an observation of original IDs, never redispatch.
109
+ def resume(project:, context:)
110
+ driver = driver(project, context)
111
+ describe(driver.path, driver.context).merge("canonical" => driver.status,
112
+ "supervisor" => driver.supervisor,
113
+ "dispatch_intent" => File.file?(File.join(File.dirname(driver.path), "dispatch.json")))
114
+ end
115
+
116
+ private
117
+
118
+ def selected(project)
119
+ config = @config || Ace::Lab::Molecules::OrdinaryOwnerConfig.load(project: project)
120
+ raise Error, "ordinary project selection differs" unless config.project == project
121
+ config.verify_owner!
122
+ config
123
+ end
124
+
125
+ def client(project)
126
+ @client || Ace::Lab::Organisms::OrdinaryAssignmentClient.new(project: project)
127
+ end
128
+
129
+ def driver(project, context)
130
+ Ace::Assign::Organisms::OrdinaryDriver.new(path: context, project: project, transport: client(project))
131
+ end
132
+
133
+ def runtime
134
+ @runtime ||= Ace::Herdr::Organisms::RuntimeAdapter.new
135
+ end
136
+
137
+ def supervisor!(config)
138
+ participant = config.selected_principal!(Process.uid)
139
+ caller = runtime.context
140
+ unless caller[:in_runtime] && caller[:session] == participant.fetch("workspace") &&
141
+ participant.fetch("supervisor_panes").include?(caller[:pane])
142
+ raise Error, "ordinary preparation and launch require the selected native supervisor"
143
+ end
144
+ caller
145
+ end
146
+
147
+ def validate_worktree!(config, path)
148
+ actual = File.realpath(path)
149
+ root = File.realpath(config.worktrees.fetch("root"))
150
+ raise Error, "ordinary worktree is outside installed project selection" unless actual.start_with?(root + "/")
151
+ actual
152
+ end
153
+
154
+ def observe(config, cwd)
155
+ reader = @observer || Ace::Lab::Molecules::OrdinaryWorktreeRead.new(root: config.worktrees.fetch("root"),
156
+ common_dir: config.worktrees.fetch("common_dir"), deadline: Process.clock_gettime(Process::CLOCK_MONOTONIC) + 30)
157
+ reader.observe(cwd)
158
+ end
159
+
160
+ def in_worktree(path)
161
+ previous = ENV["PROJECT_ROOT_PATH"]
162
+ Dir.chdir(path) do
163
+ ENV["PROJECT_ROOT_PATH"] = path
164
+ Ace::Support::Fs::Molecules::ProjectRootFinder.clear_cache!
165
+ Ace::Assign.reset_config!
166
+ Ace::LLM.reset_configuration!
167
+ yield
168
+ end
169
+ ensure
170
+ ENV["PROJECT_ROOT_PATH"] = previous
171
+ Ace::Support::Fs::Molecules::ProjectRootFinder.clear_cache!
172
+ Ace::Assign.reset_config!
173
+ Ace::LLM.reset_configuration!
174
+ end
175
+
176
+ def describe(path, context)
177
+ read = "ace-lab assignment drive --project #{context.fetch('project_id').shellescape} --context #{path.shellescape} --operation status"
178
+ {"context" => path, **context.slice("project_id", "assignment_id", "attempt_id", "scope", "pane", "supervisor_pane", "cwd", "provider"),
179
+ "watch_command" => "/watch add ordinary-#{context.fetch('attempt_id')} --path #{File.join(File.dirname(path), 'result-hint.json').to_json} " \
180
+ "--message #{("Ordinary result hint only. Run #{read}; adopt only its exact canonical submitted result. Preserve original IDs on uncertainty.").to_json}",
181
+ "status_command" => read}
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end
@@ -45,7 +45,7 @@ module Ace
45
45
  progress = on_progress || ->(_msg) {}
46
46
 
47
47
  progress.call("Scanning worktrees...")
48
- result = @worktree_manager.list_all(show_tasks: true)
48
+ result = @worktree_manager.list_all
49
49
  raise Error, result[:error] || "Failed to list worktrees" unless result[:success]
50
50
 
51
51
  all_worktrees = Array(result[:worktrees]).reject(&:bare)
@@ -14,7 +14,7 @@ module Ace
14
14
  end
15
15
 
16
16
  def collect
17
- result = @worktree_manager.list_all(show_tasks: true)
17
+ result = @worktree_manager.list_all
18
18
  raise Error, result[:error] || "Failed to list worktrees" unless result[:success]
19
19
 
20
20
  all_worktrees = Array(result[:worktrees]).reject(&:bare)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ace
4
4
  module Overseer
5
- VERSION = "0.22.0"
5
+ VERSION = "0.23.1"
6
6
  end
7
7
  end
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.22.0
4
+ version: 0.23.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Czyz
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2026-10-16 00:00:00.000000000 Z
10
+ date: 2026-10-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ace-support-cli
@@ -99,14 +99,14 @@ dependencies:
99
99
  requirements:
100
100
  - - "~>"
101
101
  - !ruby/object:Gem::Version
102
- version: '0.26'
102
+ version: '0.27'
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.26'
109
+ version: '0.27'
110
110
  - !ruby/object:Gem::Dependency
111
111
  name: ace-task
112
112
  requirement: !ruby/object:Gem::Requirement
@@ -183,14 +183,14 @@ dependencies:
183
183
  requirements:
184
184
  - - "~>"
185
185
  - !ruby/object:Gem::Version
186
- version: 0.60.3
186
+ version: 0.61.0
187
187
  type: :runtime
188
188
  prerelease: false
189
189
  version_requirements: !ruby/object:Gem::Requirement
190
190
  requirements:
191
191
  - - "~>"
192
192
  - !ruby/object:Gem::Version
193
- version: 0.60.3
193
+ version: 0.61.0
194
194
  - !ruby/object:Gem::Dependency
195
195
  name: ace-support-test-helpers
196
196
  requirement: !ruby/object:Gem::Requirement
@@ -286,6 +286,7 @@ files:
286
286
  - lib/ace/overseer/atoms/status_formatter.rb
287
287
  - lib/ace/overseer/cli.rb
288
288
  - lib/ace/overseer/cli/commands/agents.rb
289
+ - lib/ace/overseer/cli/commands/ordinary.rb
289
290
  - lib/ace/overseer/cli/commands/projects.rb
290
291
  - lib/ace/overseer/cli/commands/prompt.rb
291
292
  - lib/ace/overseer/cli/commands/prune.rb
@@ -310,6 +311,7 @@ files:
310
311
  - lib/ace/overseer/molecules/worktree_context_collector.rb
311
312
  - lib/ace/overseer/molecules/worktree_provisioner.rb
312
313
  - lib/ace/overseer/organisms/launch_recovery.rb
314
+ - lib/ace/overseer/organisms/ordinary_work_on.rb
313
315
  - lib/ace/overseer/organisms/protected_prune.rb
314
316
  - lib/ace/overseer/organisms/protected_review.rb
315
317
  - lib/ace/overseer/organisms/protected_status.rb