ace-task 0.41.0 → 0.41.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +1 -1
- data/lib/ace/task/molecules/subtask_allocation_lock.rb +23 -0
- data/lib/ace/task/molecules/subtask_creator.rb +29 -26
- data/lib/ace/task/molecules/task_reparenter.rb +31 -25
- data/lib/ace/task/molecules/task_scanner.rb +2 -2
- data/lib/ace/task/organisms/task_manager.rb +1 -1
- data/lib/ace/task/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6e932999b7042c67c443a0221b3460edad42e09711a1f69e018f6a44473065f6
|
|
4
|
+
data.tar.gz: f993abd4008d257ce10e8dc105633294c31a1753fa76e5ea35e8355be3618ee2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6d508f8f9975cc717d0b4b788e824c5e800b5f5f5aae9e36639da1f9d98c9d6b95010371f8091ee18016d7548a23ffd6a7002b194cb4146e96e9dc2b1f8b5094
|
|
7
|
+
data.tar.gz: '08af4e6b32c752ce7253a147e98cdbfb8c5d2a930f8b51e06dcb5798403d85a592dadde99d854946c009cdf4e7774fa3b4e2eadce303d7ebc190d71e39a5c19b'
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.41.1] - 2026-11-10
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Serialize subtask allocation/reparenting across processes and path aliases. Resolve accessible task references without weakening full inventory errors; preserve private ignored-task ownership.
|
|
15
|
+
|
|
10
16
|
## [0.41.0] - 2026-10-17
|
|
11
17
|
|
|
12
18
|
### Added
|
data/README.md
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
> `--git-commit` is refused for Git-ignored tasks, and completed ignored tasks
|
|
22
22
|
> are neither auto-archived nor moved by `doctor --auto-fix`. A lab may prepare
|
|
23
23
|
> `.ace-tasks/_overseer/` (gitignored) and manage it with
|
|
24
|
-
> `ace-task --in _overseer`; the delegation tools are role- and folder-name
|
|
24
|
+
> `ace-task list --in _overseer`; the delegation tools are role- and folder-name
|
|
25
25
|
> agnostic and do not reject management tasks. The canonical flow is
|
|
26
26
|
> [docs/lab-flow.md](../docs/lab-flow.md).
|
|
27
27
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Task
|
|
5
|
+
module Molecules
|
|
6
|
+
# The parent directory inode is shared across processes and path aliases.
|
|
7
|
+
# Lock it across allocation and publication without adding task-store files.
|
|
8
|
+
module SubtaskAllocationLock
|
|
9
|
+
def self.synchronize(parent_path)
|
|
10
|
+
File.open(parent_path, File::RDONLY) do |directory|
|
|
11
|
+
directory.flock(File::LOCK_EX)
|
|
12
|
+
held = directory.stat
|
|
13
|
+
current = File.stat(parent_path)
|
|
14
|
+
unless current.directory? && [held.dev, held.ino] == [current.dev, current.ino]
|
|
15
|
+
raise ArgumentError, "Parent task moved while waiting for subtask allocation; retry"
|
|
16
|
+
end
|
|
17
|
+
yield
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -4,6 +4,7 @@ require "fileutils"
|
|
|
4
4
|
require_relative "../atoms/task_id_formatter"
|
|
5
5
|
require_relative "../atoms/task_frontmatter_defaults"
|
|
6
6
|
require_relative "task_loader"
|
|
7
|
+
require_relative "subtask_allocation_lock"
|
|
7
8
|
|
|
8
9
|
module Ace
|
|
9
10
|
module Task
|
|
@@ -44,32 +45,34 @@ module Ace
|
|
|
44
45
|
)
|
|
45
46
|
raise ArgumentError, "Title is required" if title.nil? || title.strip.empty?
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
48
|
+
SubtaskAllocationLock.synchronize(parent_task.path) do
|
|
49
|
+
paths = destination_paths_for(parent_task, title)
|
|
50
|
+
subtask_id = paths[:id]
|
|
51
|
+
subtask_dir = paths[:dir]
|
|
52
|
+
FileUtils.mkdir_p(subtask_dir)
|
|
53
|
+
|
|
54
|
+
# Build frontmatter
|
|
55
|
+
effective_status = status || @config.dig("task", "default_status") || "pending"
|
|
56
|
+
frontmatter = Atoms::TaskFrontmatterDefaults.build(
|
|
57
|
+
id: subtask_id,
|
|
58
|
+
status: effective_status,
|
|
59
|
+
priority: priority,
|
|
60
|
+
tags: tags,
|
|
61
|
+
created_at: time,
|
|
62
|
+
parent: parent_task.id,
|
|
63
|
+
estimate: estimate,
|
|
64
|
+
remote_issue: remote_issue
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Write spec file
|
|
68
|
+
spec_file = paths[:spec]
|
|
69
|
+
content = build_spec_content(frontmatter: frontmatter, title: title)
|
|
70
|
+
File.write(spec_file, content)
|
|
71
|
+
|
|
72
|
+
# Load and return
|
|
73
|
+
loader = TaskLoader.new
|
|
74
|
+
loader.load(subtask_dir, id: subtask_id, load_subtasks: false)
|
|
75
|
+
end
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
# Non-mutating preview of the directory and spec file a subtask create
|
|
@@ -29,7 +29,7 @@ module Ace
|
|
|
29
29
|
when "none"
|
|
30
30
|
promote_to_standalone(task)
|
|
31
31
|
when "self"
|
|
32
|
-
convert_to_orchestrator(task)
|
|
32
|
+
SubtaskAllocationLock.synchronize(task.path) { convert_to_orchestrator(task) }
|
|
33
33
|
else
|
|
34
34
|
demote_to_subtask(task, parent_ref: target, resolve_ref: resolve_ref)
|
|
35
35
|
end
|
|
@@ -110,6 +110,10 @@ module Ace
|
|
|
110
110
|
raise ArgumentError, "Cannot convert subtask #{task.id} to orchestrator — promote first"
|
|
111
111
|
end
|
|
112
112
|
|
|
113
|
+
unless scan_existing_subtask_chars(task.path, task.id).empty?
|
|
114
|
+
raise ArgumentError, "Task #{task.id} already has subtasks; cannot convert it again"
|
|
115
|
+
end
|
|
116
|
+
|
|
113
117
|
paths = orchestrator_paths(task)
|
|
114
118
|
subtask_id = paths[:id]
|
|
115
119
|
subtask_dir = paths[:dir]
|
|
@@ -155,37 +159,39 @@ module Ace
|
|
|
155
159
|
raise ArgumentError, "Cannot reparent task to itself"
|
|
156
160
|
end
|
|
157
161
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
+
SubtaskAllocationLock.synchronize(parent_task.path) do
|
|
163
|
+
paths = demoted_paths(task, parent_task)
|
|
164
|
+
new_id = paths[:id]
|
|
165
|
+
new_dir = paths[:dir]
|
|
166
|
+
new_spec_basename = paths[:spec_basename]
|
|
162
167
|
|
|
163
|
-
|
|
168
|
+
raise ArgumentError, "Destination already exists: #{new_dir}" if File.exist?(new_dir)
|
|
164
169
|
|
|
165
|
-
|
|
166
|
-
|
|
170
|
+
# Move folder into parent
|
|
171
|
+
FileUtils.mv(task.path, new_dir)
|
|
167
172
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
173
|
+
# Rename spec file
|
|
174
|
+
old_spec_basename = File.basename(task.file_path)
|
|
175
|
+
old_spec_in_new_dir = File.join(new_dir, old_spec_basename)
|
|
176
|
+
new_spec_path = File.join(new_dir, new_spec_basename)
|
|
172
177
|
|
|
173
|
-
|
|
178
|
+
File.rename(old_spec_in_new_dir, new_spec_path) if old_spec_basename != new_spec_basename
|
|
174
179
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
+
# Update frontmatter: new ID, set parent
|
|
181
|
+
update_frontmatter(new_spec_path) do |fm|
|
|
182
|
+
fm["id"] = new_id
|
|
183
|
+
fm["parent"] = parent_task.id
|
|
184
|
+
end
|
|
180
185
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
+
# Descendant identities hang off the parent ID: rewrite their
|
|
187
|
+
# frontmatter and spec filenames so persisted IDs stay authoritative.
|
|
188
|
+
id_map = {task.id => new_id}
|
|
189
|
+
rewrite_descendants(new_dir, old_parent_id: task.id, new_parent_id: new_id, id_map: id_map)
|
|
190
|
+
rewrite_dependency_references(id_map)
|
|
186
191
|
|
|
187
|
-
|
|
188
|
-
|
|
192
|
+
loader = TaskLoader.new
|
|
193
|
+
loader.load(new_dir, id: new_id)
|
|
194
|
+
end
|
|
189
195
|
end
|
|
190
196
|
|
|
191
197
|
# Rewrite dependency references in every task spec after an ID map
|
|
@@ -35,8 +35,8 @@ module Ace
|
|
|
35
35
|
|
|
36
36
|
# Scan for all primary tasks (excludes subtask folders).
|
|
37
37
|
# @return [Array<ScanResult>] Sorted scan results
|
|
38
|
-
def scan
|
|
39
|
-
@scanner.scan.reject { |sr| subtask_folder?(sr.folder_name) }
|
|
38
|
+
def scan(accessible_only: false)
|
|
39
|
+
@scanner.scan(accessible_only: accessible_only).reject { |sr| subtask_folder?(sr.folder_name) }
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Scan and filter by special folder or virtual filter.
|
|
@@ -877,7 +877,7 @@ module Ace
|
|
|
877
877
|
|
|
878
878
|
def resolve_scan_result(ref)
|
|
879
879
|
scanner = Molecules::TaskScanner.new(@root_dir)
|
|
880
|
-
scan_results = scanner.scan
|
|
880
|
+
scan_results = scanner.scan(accessible_only: true)
|
|
881
881
|
resolver = Molecules::TaskResolver.new(scan_results)
|
|
882
882
|
resolver.resolve(ref)
|
|
883
883
|
end
|
data/lib/ace/task/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ace-task
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.41.
|
|
4
|
+
version: 0.41.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-10
|
|
10
|
+
date: 2026-11-10 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-core
|
|
@@ -44,6 +44,9 @@ dependencies:
|
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: '0.15'
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 0.15.17
|
|
47
50
|
type: :runtime
|
|
48
51
|
prerelease: false
|
|
49
52
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -51,6 +54,9 @@ dependencies:
|
|
|
51
54
|
- - "~>"
|
|
52
55
|
- !ruby/object:Gem::Version
|
|
53
56
|
version: '0.15'
|
|
57
|
+
- - ">="
|
|
58
|
+
- !ruby/object:Gem::Version
|
|
59
|
+
version: 0.15.17
|
|
54
60
|
- !ruby/object:Gem::Dependency
|
|
55
61
|
name: ace-support-markdown
|
|
56
62
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -199,6 +205,7 @@ files:
|
|
|
199
205
|
- lib/ace/task/molecules/issue_link.rb
|
|
200
206
|
- lib/ace/task/molecules/issue_sync_adapter.rb
|
|
201
207
|
- lib/ace/task/molecules/path_utils.rb
|
|
208
|
+
- lib/ace/task/molecules/subtask_allocation_lock.rb
|
|
202
209
|
- lib/ace/task/molecules/subtask_creator.rb
|
|
203
210
|
- lib/ace/task/molecules/task_config_loader.rb
|
|
204
211
|
- lib/ace/task/molecules/task_creator.rb
|