ace-task 0.39.1 → 0.39.2
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/handbook/workflow-instructions/task/work.wf.md +7 -0
- data/lib/ace/task/molecules/task_doctor_fixer.rb +15 -11
- data/lib/ace/task/molecules/task_family_completion.rb +30 -0
- data/lib/ace/task/organisms/task_manager.rb +22 -29
- data/lib/ace/task/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 425d8beebe8b18f6d816767d85d9f1b0a29c68a470b8c6cf4590629b2028bddd
|
|
4
|
+
data.tar.gz: a28fc844b081cc3f9b969e2ab15a674df2dca08bb30fc48cd90b1741d3024663
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c2be6996b2753ec7ff709cba139a8533e2b746186e7ba3c29b26e51d9361d163e99a022bc5c05a39db372e71ee5a843c64f4f06fbe5b66298e7488b922da27d5
|
|
7
|
+
data.tar.gz: b976035a868e0d4d433b956d41520d5b7a2670aff609754bd975797a6bc6a0417ca1e3fe364d429c65dfe9edfc8c4e9609ad20accfb0db4f2743c8b26ec304a5
|
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.39.2] - 2026-10-06
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- Keep unresolved descendants from completing or archiving task families, return children correctly after legitimate archival, and prevent doctor from inferring completion from archive location.
|
|
15
|
+
|
|
10
16
|
## [0.39.1] - 2026-10-04
|
|
11
17
|
|
|
12
18
|
### Fixed
|
|
@@ -74,6 +74,13 @@ Work through the plan checklist, step by step:
|
|
|
74
74
|
- If a test fails: fix it before moving to the next step
|
|
75
75
|
- If a test failure is undiagnosable after one attempt: stop and report
|
|
76
76
|
|
|
77
|
+
**Diagnostic reruns and contract corrections:**
|
|
78
|
+
- Before rerunning a failure, state the hypothesis and the observation that will distinguish it. Verify the runner's actual selected tests/count, effective configuration path and seed from its report or a narrow configuration probe; command arguments alone are not proof that options reached the child.
|
|
79
|
+
- Resolve configuration against the runner's actual working directory. If it changes directories, use a verified absolute path. A reproduction with a different seed or zero selected tests does not satisfy the intended check.
|
|
80
|
+
- Add bounded diagnostics at the failing boundary before repeating an opaque failure. Preserve earlier failures and distinguish product fixes, fixture corrections and diagnostic-only changes; do not rerun merely to obtain green output or silently increase deadlines.
|
|
81
|
+
- Poll an owned live test handle to its terminal result. Quiet output is not a reason to start a duplicate run. After a meaningful fix or new hypothesis, rerun only the affected checks, then broaden when integration risk requires it.
|
|
82
|
+
- When a contract changes, revise its current authoritative spec and consumer obligations together. Keep superseded decisions in history rather than leaving competing current addenda. A partial source commit cannot close an unverified acceptance criterion.
|
|
83
|
+
|
|
77
84
|
**Task lifecycle:**
|
|
78
85
|
- `draft` status: warn the user that the spec hasn't been reviewed, then continue only with explicit confirmation. In unattended/fork contexts where interactive confirmation is not possible, proceed after marking in-progress -- the assignment creation layer is responsible for blocking draft tasks before they reach this point.
|
|
79
86
|
- Mark in-progress before first change, done after last verification
|
|
@@ -8,6 +8,7 @@ require_relative "../atoms/task_id_formatter"
|
|
|
8
8
|
require_relative "../atoms/task_validation_rules"
|
|
9
9
|
require_relative "../atoms/task_frontmatter_defaults"
|
|
10
10
|
require_relative "task_scanner"
|
|
11
|
+
require_relative "task_family_completion"
|
|
11
12
|
|
|
12
13
|
module Ace
|
|
13
14
|
module Task
|
|
@@ -218,7 +219,9 @@ module Ace
|
|
|
218
219
|
end
|
|
219
220
|
|
|
220
221
|
def fix_archive_status(file_path)
|
|
221
|
-
|
|
222
|
+
log_fix(file_path, "Skipped status repair: archive location does not prove task completion")
|
|
223
|
+
@skipped_count += 1
|
|
224
|
+
false
|
|
222
225
|
end
|
|
223
226
|
|
|
224
227
|
def fix_maybe_terminal(file_path)
|
|
@@ -456,6 +459,16 @@ module Ace
|
|
|
456
459
|
description = "Moved parent task to _archive/ (all subtasks terminal)"
|
|
457
460
|
end
|
|
458
461
|
|
|
462
|
+
source_spec = find_primary_spec(source_dir)
|
|
463
|
+
source_frontmatter, = Ace::Support::Items::Atoms::FrontmatterParser.parse(File.read(source_spec))
|
|
464
|
+
unless TaskFamilyCompletion.new(@root_dir).descendants_terminal?(
|
|
465
|
+
source_dir, id: source_frontmatter["id"]
|
|
466
|
+
)
|
|
467
|
+
log_fix(source_dir, "Skipped archive move: descendants are not all terminal")
|
|
468
|
+
@skipped_count += 1
|
|
469
|
+
return false
|
|
470
|
+
end
|
|
471
|
+
|
|
459
472
|
if @dry_run
|
|
460
473
|
log_fix(source_dir, "Would #{description.downcase}")
|
|
461
474
|
@fixed_count += 1
|
|
@@ -493,16 +506,7 @@ module Ace
|
|
|
493
506
|
end
|
|
494
507
|
|
|
495
508
|
def all_siblings_terminal?(parent_dir, parent_id)
|
|
496
|
-
|
|
497
|
-
subtasks = scanner.scan_subtasks(parent_dir, parent_id: parent_id)
|
|
498
|
-
return false if subtasks.empty?
|
|
499
|
-
|
|
500
|
-
subtasks.all? do |scan_result|
|
|
501
|
-
content = File.read(scan_result.file_path)
|
|
502
|
-
frontmatter, _body = Ace::Support::Items::Atoms::FrontmatterParser.parse(content)
|
|
503
|
-
status = frontmatter.is_a?(Hash) ? frontmatter["status"] : nil
|
|
504
|
-
Atoms::TaskValidationRules.terminal_status?(status.to_s.downcase)
|
|
505
|
-
end
|
|
509
|
+
TaskFamilyCompletion.new(@root_dir).complete?(parent_dir, id: parent_id)
|
|
506
510
|
end
|
|
507
511
|
end
|
|
508
512
|
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "task_scanner"
|
|
4
|
+
require_relative "../atoms/task_validation_rules"
|
|
5
|
+
|
|
6
|
+
module Ace
|
|
7
|
+
module Task
|
|
8
|
+
module Molecules
|
|
9
|
+
# Task-aware traversal excludes evidence folders and the parent's own status.
|
|
10
|
+
class TaskFamilyCompletion
|
|
11
|
+
def initialize(root_dir)
|
|
12
|
+
@scanner = TaskScanner.new(root_dir)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def descendants_terminal?(path, id:)
|
|
16
|
+
@scanner.scan_subtasks(path, parent_id: id).all? do |child|
|
|
17
|
+
content = File.read(child.file_path)
|
|
18
|
+
frontmatter, = Ace::Support::Items::Atoms::FrontmatterParser.parse(content)
|
|
19
|
+
Atoms::TaskValidationRules.terminal_status?(frontmatter["status"].to_s.downcase) &&
|
|
20
|
+
descendants_terminal?(child.dir_path, id: child.id)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def complete?(path, id:)
|
|
25
|
+
@scanner.scan_subtasks(path, parent_id: id).any? && descendants_terminal?(path, id: id)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -9,6 +9,7 @@ require_relative "../molecules/task_resolver"
|
|
|
9
9
|
require_relative "../molecules/task_loader"
|
|
10
10
|
require_relative "../molecules/task_creator"
|
|
11
11
|
require_relative "../molecules/subtask_creator"
|
|
12
|
+
require_relative "../molecules/task_family_completion"
|
|
12
13
|
require_relative "../molecules/task_reparenter"
|
|
13
14
|
require_relative "../molecules/issue_link"
|
|
14
15
|
require_relative "../atoms/task_validation_rules"
|
|
@@ -190,6 +191,13 @@ module Ace
|
|
|
190
191
|
special_folder: scan_result.special_folder)
|
|
191
192
|
return nil unless task
|
|
192
193
|
|
|
194
|
+
if Ace::Support::Items::Atoms::SpecialFolderDetector.normalize(move_to) == "_archive" &&
|
|
195
|
+
!task.subtask? && !family_completion.descendants_terminal?(task.path, id: task.id)
|
|
196
|
+
@last_update_note = "Task #{task.id} was not archived because descendants are not all terminal; " \
|
|
197
|
+
"resolve blocked or unfinished subtasks before archiving. No requested fields were changed."
|
|
198
|
+
return task
|
|
199
|
+
end
|
|
200
|
+
|
|
193
201
|
# Apply field updates if any
|
|
194
202
|
has_field_updates = [set, add, remove].any? { |h| h && !h.empty? }
|
|
195
203
|
reject_issue_metadata_update!(set, add, remove)
|
|
@@ -258,11 +266,11 @@ module Ace
|
|
|
258
266
|
# Auto-archive hook: if a subtask status was set to terminal,
|
|
259
267
|
# check if all siblings are terminal and auto-move parent to archive
|
|
260
268
|
if set && set.key?("status")
|
|
261
|
-
check_auto_archive(task, set["status"]
|
|
269
|
+
check_auto_archive(task, set["status"])
|
|
262
270
|
end
|
|
263
271
|
|
|
264
272
|
# Reload and return updated task
|
|
265
|
-
updated_task =
|
|
273
|
+
updated_task = show(current_id)
|
|
266
274
|
if sync_needed_after_update?(task, updated_task, set: set, add: add, remove: remove, move_to: move_to)
|
|
267
275
|
sync_failed_definitively = false
|
|
268
276
|
with_issue_identity_lock(linked_issue(updated_task) || {}) do
|
|
@@ -847,25 +855,23 @@ module Ace
|
|
|
847
855
|
|
|
848
856
|
# Auto-archive: if a subtask reaches terminal status and all siblings
|
|
849
857
|
# in the parent directory are also terminal, move the parent to archive.
|
|
850
|
-
def check_auto_archive(task, new_status
|
|
851
|
-
|
|
852
|
-
return unless
|
|
858
|
+
def check_auto_archive(task, new_status)
|
|
859
|
+
return unless Atoms::TaskValidationRules.terminal_status?(new_status.to_s.downcase)
|
|
860
|
+
return unless task.subtask?
|
|
853
861
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
return if File.expand_path(parent_dir) == File.expand_path(@root_dir)
|
|
862
|
+
current = show(task.id)
|
|
863
|
+
return unless current && current.special_folder != "_archive"
|
|
857
864
|
|
|
858
|
-
|
|
859
|
-
return unless
|
|
860
|
-
parent_dir, recursive: true
|
|
861
|
-
)
|
|
862
|
-
|
|
863
|
-
parent = load_parent_from_directory(parent_dir, loader)
|
|
864
|
-
return unless parent
|
|
865
|
+
parent = show(current.parent_id)
|
|
866
|
+
return unless parent && family_completion.complete?(parent.path, id: parent.id)
|
|
865
867
|
|
|
866
868
|
archive_parent_via_update(parent)
|
|
867
869
|
end
|
|
868
870
|
|
|
871
|
+
def family_completion
|
|
872
|
+
Molecules::TaskFamilyCompletion.new(@root_dir)
|
|
873
|
+
end
|
|
874
|
+
|
|
869
875
|
def parse_archive_date(task)
|
|
870
876
|
raw = task.metadata["completed_at"] || task.metadata["created_at"]
|
|
871
877
|
return nil unless raw
|
|
@@ -897,10 +903,7 @@ module Ace
|
|
|
897
903
|
}
|
|
898
904
|
end
|
|
899
905
|
|
|
900
|
-
|
|
901
|
-
subtasks = parent_with_subtasks&.subtasks || []
|
|
902
|
-
all_terminal = subtasks.any? &&
|
|
903
|
-
subtasks.all? { |st| Atoms::TaskValidationRules.terminal_status?(st.status.to_s.downcase) }
|
|
906
|
+
all_terminal = family_completion.complete?(parent.path, id: parent.id)
|
|
904
907
|
|
|
905
908
|
unless all_terminal
|
|
906
909
|
@last_update_note = "Subtask #{task.id} was not archived because sibling subtasks are not all terminal."
|
|
@@ -941,16 +944,6 @@ module Ace
|
|
|
941
944
|
archived_parent
|
|
942
945
|
end
|
|
943
946
|
|
|
944
|
-
def load_parent_from_directory(parent_dir, loader)
|
|
945
|
-
parent_id = File.basename(parent_dir).split("-", 2).first
|
|
946
|
-
return nil unless parent_id
|
|
947
|
-
|
|
948
|
-
parent_special = Ace::Support::Items::Atoms::SpecialFolderDetector.detect_in_path(
|
|
949
|
-
parent_dir, root: @root_dir
|
|
950
|
-
)
|
|
951
|
-
loader.load(parent_dir, id: parent_id, special_folder: parent_special)
|
|
952
|
-
end
|
|
953
|
-
|
|
954
947
|
# Value accessor for FilterApplier
|
|
955
948
|
def task_value_accessor(item, key)
|
|
956
949
|
case key
|
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.39.
|
|
4
|
+
version: 0.39.2
|
|
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-10-08 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-core
|
|
@@ -204,6 +204,7 @@ files:
|
|
|
204
204
|
- lib/ace/task/molecules/task_display_formatter.rb
|
|
205
205
|
- lib/ace/task/molecules/task_doctor_fixer.rb
|
|
206
206
|
- lib/ace/task/molecules/task_doctor_reporter.rb
|
|
207
|
+
- lib/ace/task/molecules/task_family_completion.rb
|
|
207
208
|
- lib/ace/task/molecules/task_frontmatter_validator.rb
|
|
208
209
|
- lib/ace/task/molecules/task_loader.rb
|
|
209
210
|
- lib/ace/task/molecules/task_plan_cache.rb
|