ace-assign 0.55.2 → 0.56.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 +4 -4
- data/.ace-defaults/assign/presets/work-on-task-ace-development.yml +59 -0
- data/.ace-defaults/assign/presets/work-on-task-auto-merge.yml +47 -0
- data/.ace-defaults/assign/presets/work-on-task-in-place.yml +42 -0
- data/CHANGELOG.md +7 -0
- data/lib/ace/assign/cli/commands/status.rb +11 -0
- data/lib/ace/assign/cli.rb +1 -0
- data/lib/ace/assign/molecules/evidence_calculator.rb +134 -0
- data/lib/ace/assign/version.rb +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2735356d8b6ca0c85cd065008a49099def711664666f2e6280419b4226609c9b
|
|
4
|
+
data.tar.gz: 18e4449c7bf1807bab4cfa84b8d3b7ae79d206fa6fabd56835df7e2483dbd4b5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7562bb372cd8ef57978616874139b403a78d71fc81ed35311426cba26c8fadafd1880564fc7ab7c4bae33ed2c6b0b5c2b4acc6058690fc723cf618158a637e60
|
|
7
|
+
data.tar.gz: 920c4a01f45060226ae96d75567d1a189c61471f8f88980f2ee986d6e1a9ecab620b13ff7c602230c7a2d44f850bf48e4a8c59770e2345ed03a56ba9f45f87a1
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# work-on-task-ace-development preset
|
|
2
|
+
# Complete ACE framework development pipeline preset (batching, releases, demo recording, retro)
|
|
3
|
+
|
|
4
|
+
name: work-on-task-ace-development
|
|
5
|
+
description: Full ACE development pipeline with batching, releases, demo recording, and retrospectives
|
|
6
|
+
|
|
7
|
+
parameters:
|
|
8
|
+
taskrefs:
|
|
9
|
+
required: true
|
|
10
|
+
type: array
|
|
11
|
+
description: Task references
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: onboard
|
|
15
|
+
number: "000"
|
|
16
|
+
workflow: wfi://onboard
|
|
17
|
+
instructions:
|
|
18
|
+
- "Load project context for the task batch: {{taskrefs}}."
|
|
19
|
+
|
|
20
|
+
- name: verify-test-suite
|
|
21
|
+
number: "012"
|
|
22
|
+
workflow: wfi://assign/verify-test-suite
|
|
23
|
+
instructions:
|
|
24
|
+
- "Verify test coverage and performance across batch tasks: {{taskrefs}}."
|
|
25
|
+
|
|
26
|
+
- name: release-minor
|
|
27
|
+
number: "020"
|
|
28
|
+
instructions:
|
|
29
|
+
- Release all modified packages touched by this batch.
|
|
30
|
+
|
|
31
|
+
- name: create-pr
|
|
32
|
+
number: "030"
|
|
33
|
+
workflow: wfi://github/pr/create
|
|
34
|
+
instructions:
|
|
35
|
+
- "Create a pull request summarizing batch tasks: {{taskrefs}}."
|
|
36
|
+
|
|
37
|
+
- name: review-valid-1
|
|
38
|
+
number: "040"
|
|
39
|
+
context: fork
|
|
40
|
+
sub_steps: [review-pr, apply-feedback, release]
|
|
41
|
+
instructions:
|
|
42
|
+
- Execute valid review cycle.
|
|
43
|
+
|
|
44
|
+
- name: record-demo
|
|
45
|
+
number: "145"
|
|
46
|
+
instructions:
|
|
47
|
+
- "Record a demo showing the feature implemented in this PR."
|
|
48
|
+
|
|
49
|
+
- name: mark-tasks-done
|
|
50
|
+
number: "155"
|
|
51
|
+
workflow: wfi://task/update
|
|
52
|
+
instructions:
|
|
53
|
+
- "Mark each top-level task in {{taskrefs}} as done and archive it."
|
|
54
|
+
|
|
55
|
+
- name: create-retro
|
|
56
|
+
number: "160"
|
|
57
|
+
skill: as-retro-create
|
|
58
|
+
instructions:
|
|
59
|
+
- "Create a retrospective for the batch work on: {{taskrefs}}."
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# work-on-task-auto-merge preset
|
|
2
|
+
# Advanced authorized guarded auto-merge task delivery preset
|
|
3
|
+
|
|
4
|
+
name: work-on-task-auto-merge
|
|
5
|
+
description: Work on a task with PR, review cycles, and advance authorization for guarded auto-merge
|
|
6
|
+
|
|
7
|
+
parameters:
|
|
8
|
+
taskrefs:
|
|
9
|
+
required: true
|
|
10
|
+
type: array
|
|
11
|
+
description: Task references
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: onboard
|
|
15
|
+
number: "000"
|
|
16
|
+
workflow: wfi://onboard
|
|
17
|
+
instructions:
|
|
18
|
+
- "Load project context for the task: {{taskrefs}}."
|
|
19
|
+
|
|
20
|
+
- name: create-pr
|
|
21
|
+
number: "030"
|
|
22
|
+
workflow: wfi://github/pr/create
|
|
23
|
+
instructions:
|
|
24
|
+
- "Create a pull request summarizing task: {{taskrefs}}."
|
|
25
|
+
|
|
26
|
+
- name: review-valid-1
|
|
27
|
+
number: "040"
|
|
28
|
+
context: fork
|
|
29
|
+
sub_steps: [review-pr, apply-feedback]
|
|
30
|
+
instructions:
|
|
31
|
+
- Execute valid review cycle and apply feedback.
|
|
32
|
+
|
|
33
|
+
- name: push-to-remote
|
|
34
|
+
number: "140"
|
|
35
|
+
instructions:
|
|
36
|
+
- Push changes with force-with-lease.
|
|
37
|
+
|
|
38
|
+
- name: auto-merge
|
|
39
|
+
number: "150"
|
|
40
|
+
instructions:
|
|
41
|
+
- "Guarded auto-merge authorized PR when checks pass."
|
|
42
|
+
|
|
43
|
+
- name: mark-tasks-done
|
|
44
|
+
number: "155"
|
|
45
|
+
workflow: wfi://task/update
|
|
46
|
+
instructions:
|
|
47
|
+
- "Mark task {{taskrefs}} done during final delivery."
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# work-on-task-in-place preset
|
|
2
|
+
# In-place single checkout task delivery preset
|
|
3
|
+
|
|
4
|
+
name: work-on-task-in-place
|
|
5
|
+
description: Work on a task in the current checkout with reviewed, verified, scoped commit (no worktree, PR, or merge)
|
|
6
|
+
|
|
7
|
+
parameters:
|
|
8
|
+
taskrefs:
|
|
9
|
+
required: true
|
|
10
|
+
type: array
|
|
11
|
+
description: Task references
|
|
12
|
+
|
|
13
|
+
steps:
|
|
14
|
+
- name: onboard
|
|
15
|
+
number: "000"
|
|
16
|
+
workflow: wfi://onboard
|
|
17
|
+
instructions:
|
|
18
|
+
- "Load project context for the task: {{taskrefs}}."
|
|
19
|
+
|
|
20
|
+
- name: verify-test-suite
|
|
21
|
+
number: "012"
|
|
22
|
+
workflow: wfi://assign/verify-test-suite
|
|
23
|
+
instructions:
|
|
24
|
+
- "Verify test coverage for task in current checkout: {{taskrefs}}."
|
|
25
|
+
|
|
26
|
+
- name: review-valid-1
|
|
27
|
+
number: "040"
|
|
28
|
+
context: fork
|
|
29
|
+
sub_steps: [review-pr, apply-feedback]
|
|
30
|
+
instructions:
|
|
31
|
+
- Execute valid review cycle and apply feedback in current checkout.
|
|
32
|
+
|
|
33
|
+
- name: commit-in-place
|
|
34
|
+
number: "050"
|
|
35
|
+
instructions:
|
|
36
|
+
- "Create one reviewed, verified, scoped commit in the current authorized checkout."
|
|
37
|
+
|
|
38
|
+
- name: mark-tasks-done
|
|
39
|
+
number: "060"
|
|
40
|
+
workflow: wfi://task/update
|
|
41
|
+
instructions:
|
|
42
|
+
- "Mark task {{taskrefs}} done during final delivery."
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.56.0] - 2026-08-13
|
|
11
|
+
### Added
|
|
12
|
+
- Calculate exact-head evidence for status
|
|
13
|
+
- Add outcome-based task presets
|
|
14
|
+
### Fixed
|
|
15
|
+
- Resolve PR 316 findings and finalize cleanup proofs
|
|
16
|
+
|
|
10
17
|
## [0.55.2] - 2026-08-12
|
|
11
18
|
|
|
12
19
|
### Technical
|
|
@@ -83,6 +83,13 @@ module Ace
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def status_to_h(assignment, state, active_steps, next_step, target:, scope_root:)
|
|
86
|
+
# Extract auto_merge config if present in assignment settings or preset
|
|
87
|
+
auto_merge = false
|
|
88
|
+
preset_name = Ace::Assign::Molecules::PresetInferrer.infer_from_assignment(assignment)
|
|
89
|
+
auto_merge = true if preset_name && preset_name.start_with?("work-on-task-auto-merge")
|
|
90
|
+
|
|
91
|
+
evidence = Ace::Assign::Molecules::EvidenceCalculator.calculate(auto_merge: auto_merge)
|
|
92
|
+
|
|
86
93
|
payload = {
|
|
87
94
|
assignment: {
|
|
88
95
|
id: assignment.id,
|
|
@@ -95,6 +102,10 @@ module Ace
|
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
payload[:next_step] = step_to_h(next_step, effective_fork_provider: effective_fork_provider_for(next_step, scoped_fork_metadata_step(state, next_step, target.scope, scope_root))) if active_steps.empty? && next_step
|
|
105
|
+
|
|
106
|
+
# Merge exact head evidence into root payload
|
|
107
|
+
payload.merge!(evidence)
|
|
108
|
+
|
|
98
109
|
payload
|
|
99
110
|
end
|
|
100
111
|
|
data/lib/ace/assign/cli.rb
CHANGED
|
@@ -33,6 +33,7 @@ require_relative "molecules/skill_assign_source_resolver"
|
|
|
33
33
|
require_relative "molecules/fork_session_launcher"
|
|
34
34
|
require_relative "molecules/tmux_control_surface_runner"
|
|
35
35
|
require_relative "molecules/preset_inferrer"
|
|
36
|
+
require_relative "molecules/evidence_calculator"
|
|
36
37
|
|
|
37
38
|
# Organisms
|
|
38
39
|
require_relative "organisms/assignment_executor"
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "ace/support/fs"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module Assign
|
|
9
|
+
module Molecules
|
|
10
|
+
# Computes exact-head evidence receipts and canonical decision digests for assignment delivery.
|
|
11
|
+
class EvidenceCalculator
|
|
12
|
+
def self.calculate(pr_number: nil, auto_merge: false)
|
|
13
|
+
new.calculate(pr_number: pr_number, auto_merge: auto_merge)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def calculate(pr_number: nil, auto_merge: false)
|
|
17
|
+
root = Ace::Support::Fs::Molecules::ProjectRootFinder.find_or_current
|
|
18
|
+
|
|
19
|
+
if pr_number.nil?
|
|
20
|
+
pr_output, s = Open3.capture2("ace-git", "pr", "--format", "json", chdir: root)
|
|
21
|
+
if s.success?
|
|
22
|
+
begin
|
|
23
|
+
require "json"
|
|
24
|
+
pr_data = JSON.parse(pr_output)
|
|
25
|
+
pr_number = pr_data["number"]
|
|
26
|
+
rescue StandardError
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
head, _s = Open3.capture2("git", "rev-parse", "HEAD", chdir: root)
|
|
32
|
+
head = head.to_s.strip
|
|
33
|
+
|
|
34
|
+
tree, _s = Open3.capture2("git", "rev-parse", "HEAD^{tree}", chdir: root)
|
|
35
|
+
tree = tree.to_s.strip
|
|
36
|
+
|
|
37
|
+
diff_output, _s = Open3.capture2("git", "diff", "HEAD", chdir: root)
|
|
38
|
+
changed_scope_digest = Digest::SHA256.hexdigest(diff_output.to_s)
|
|
39
|
+
|
|
40
|
+
review_receipt = evaluate_review_receipt(root, head)
|
|
41
|
+
release_receipt = evaluate_release_receipt(root, head)
|
|
42
|
+
feedback_state = "terminal"
|
|
43
|
+
|
|
44
|
+
merge_decision = evaluate_merge_decision(
|
|
45
|
+
auto_merge: auto_merge,
|
|
46
|
+
review_receipt: review_receipt,
|
|
47
|
+
release_receipt: release_receipt,
|
|
48
|
+
feedback_state: feedback_state
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
canonical_payload = [
|
|
52
|
+
pr_number || "none",
|
|
53
|
+
head,
|
|
54
|
+
tree,
|
|
55
|
+
changed_scope_digest,
|
|
56
|
+
review_receipt,
|
|
57
|
+
release_receipt,
|
|
58
|
+
feedback_state,
|
|
59
|
+
merge_decision
|
|
60
|
+
].join(":")
|
|
61
|
+
|
|
62
|
+
decision_digest = Digest::SHA256.hexdigest(canonical_payload)
|
|
63
|
+
|
|
64
|
+
{
|
|
65
|
+
pr: pr_number || "none",
|
|
66
|
+
head: head,
|
|
67
|
+
tree: tree,
|
|
68
|
+
changed_scope_digest: changed_scope_digest,
|
|
69
|
+
review_receipt: review_receipt,
|
|
70
|
+
release_receipt: release_receipt,
|
|
71
|
+
feedback_state: feedback_state,
|
|
72
|
+
merge_decision: merge_decision,
|
|
73
|
+
decision_digest: decision_digest
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def evaluate_review_receipt(root, current_head)
|
|
80
|
+
session_dir = File.join(root, ".ace-local", "review", "sessions")
|
|
81
|
+
return "missing" unless Dir.exist?(session_dir)
|
|
82
|
+
|
|
83
|
+
reviews = Dir.glob(File.join(session_dir, "*", "metadata.yml"))
|
|
84
|
+
return "missing" if reviews.empty?
|
|
85
|
+
|
|
86
|
+
latest = reviews.max_by { |f| File.mtime(f) }
|
|
87
|
+
session_dir = File.dirname(latest)
|
|
88
|
+
|
|
89
|
+
# Review must actually be executed, not just prepared
|
|
90
|
+
return "missing" unless File.exist?(File.join(session_dir, "report.md")) || File.exist?(File.join(session_dir, "report.json"))
|
|
91
|
+
|
|
92
|
+
begin
|
|
93
|
+
require "yaml"
|
|
94
|
+
metadata = YAML.safe_load_file(latest, permitted_classes: [Time, Date]) || {}
|
|
95
|
+
return "stale" unless metadata["head"] == current_head
|
|
96
|
+
rescue StandardError
|
|
97
|
+
return "missing"
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
"current"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def evaluate_release_receipt(root, current_head)
|
|
104
|
+
session_dir = File.join(root, ".ace-local", "release", "sessions")
|
|
105
|
+
return "missing" unless Dir.exist?(session_dir)
|
|
106
|
+
|
|
107
|
+
releases = Dir.glob(File.join(session_dir, "*", "metadata.yml"))
|
|
108
|
+
return "missing" if releases.empty?
|
|
109
|
+
|
|
110
|
+
latest = releases.max_by { |f| File.mtime(f) }
|
|
111
|
+
begin
|
|
112
|
+
require "yaml"
|
|
113
|
+
metadata = YAML.safe_load_file(latest, permitted_classes: [Time, Date]) || {}
|
|
114
|
+
return "stale" unless metadata["head"] == current_head
|
|
115
|
+
rescue StandardError
|
|
116
|
+
return "missing"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
"current"
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def evaluate_merge_decision(auto_merge:, review_receipt:, release_receipt:, feedback_state:)
|
|
123
|
+
return "report-only" unless auto_merge
|
|
124
|
+
|
|
125
|
+
if review_receipt == "current" && release_receipt == "current" && feedback_state == "terminal"
|
|
126
|
+
"authorized"
|
|
127
|
+
else
|
|
128
|
+
"approval-required"
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
data/lib/ace/assign/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-assign
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.56.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-08-
|
|
10
|
+
date: 2026-08-13 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.
|
|
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.
|
|
53
|
+
version: '0.18'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: ace-support-nav
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -228,6 +228,9 @@ files:
|
|
|
228
228
|
- ".ace-defaults/assign/presets/quick-implement.yml"
|
|
229
229
|
- ".ace-defaults/assign/presets/release-only.yml"
|
|
230
230
|
- ".ace-defaults/assign/presets/work-on-docs.yml"
|
|
231
|
+
- ".ace-defaults/assign/presets/work-on-task-ace-development.yml"
|
|
232
|
+
- ".ace-defaults/assign/presets/work-on-task-auto-merge.yml"
|
|
233
|
+
- ".ace-defaults/assign/presets/work-on-task-in-place.yml"
|
|
231
234
|
- ".ace-defaults/assign/presets/work-on-task.yml"
|
|
232
235
|
- ".ace-defaults/nav/protocols/skill-sources/ace-assign.yml"
|
|
233
236
|
- ".ace-defaults/nav/protocols/wfi-sources/ace-assign.yml"
|
|
@@ -303,6 +306,7 @@ files:
|
|
|
303
306
|
- lib/ace/assign/models/step.rb
|
|
304
307
|
- lib/ace/assign/molecules/assignment_discoverer.rb
|
|
305
308
|
- lib/ace/assign/molecules/assignment_manager.rb
|
|
309
|
+
- lib/ace/assign/molecules/evidence_calculator.rb
|
|
306
310
|
- lib/ace/assign/molecules/fork_session_launcher.rb
|
|
307
311
|
- lib/ace/assign/molecules/preset_inferrer.rb
|
|
308
312
|
- lib/ace/assign/molecules/queue_scanner.rb
|