ace-task 0.37.4 → 0.37.6
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 +13 -0
- data/lib/ace/task/cli/commands/github_sync.rb +11 -3
- data/lib/ace/task/molecules/github_issue_sync_adapter.rb +17 -6
- data/lib/ace/task/molecules/task_frontmatter_validator.rb +10 -3
- data/lib/ace/task/organisms/task_manager.rb +39 -5
- data/lib/ace/task/version.rb +1 -1
- data/lib/ace/task.rb +1 -0
- metadata +18 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5979cedf5bba02d87a871387ecaa348ba33ebb86ac583eb6341889f74ad05d6
|
|
4
|
+
data.tar.gz: c3b2cd69ddff0bd6b557ce9606f496efe9a9c915e02b2fb4e971c120eef2bde5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4c59cf04b650430acc2f6e59c3659dc62330dcc3383444e4040d35890134445f3b4d47fce150f62be1d1a9d55d66f70809e7fbe0c83da14105cf8cbc156ea2fd
|
|
7
|
+
data.tar.gz: de3fb447f8fbdb4c802a4ccc68b0476680d89cd78161434cf48ef2528e045830606a90a04a6b96bd633e6bca64acfab0588519efa571af8acdb56ebbd3254498
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.37.6] - 2026-09-27
|
|
11
|
+
|
|
12
|
+
### Technical
|
|
13
|
+
- Stub the GitHub sync adapter during pending replay in tests.
|
|
14
|
+
|
|
15
|
+
## [0.37.5] - 2026-09-26
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Best-effort GitHub sync: updates run without `gh`; offline updates flag tasks with `github_sync_pending` frontmatter, and `ace-task github-sync --pending` replays the missed issue syncs once gh is installed and authenticated.
|
|
19
|
+
- gh availability gate (`IssueSync.available?` via ace-git-github): ownership validation and post-update issue sync run only when gh is installed and authenticated, restoring full checks when it is.
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Require the declared `ace-git-github` dependency so updates and archive moves of tasks with linked GitHub issues no longer fail with "GitHub issue sync primitives are unavailable".
|
|
10
23
|
|
|
11
24
|
## [0.37.4] - 2026-09-02
|
|
12
25
|
|
|
@@ -16,25 +16,27 @@ module Ace
|
|
|
16
16
|
|
|
17
17
|
example [
|
|
18
18
|
"q7w # Sync one task",
|
|
19
|
-
"--all # Sync all linked tasks"
|
|
19
|
+
"--all # Sync all linked tasks",
|
|
20
|
+
"--pending # Replay tasks flagged with github_sync_pending (offline sync)"
|
|
20
21
|
]
|
|
21
22
|
|
|
22
23
|
argument :ref, required: false, desc: "Task reference (full ID, short ref, or suffix)"
|
|
23
24
|
option :all, type: :boolean, aliases: %w[-a], desc: "Sync all linked tasks"
|
|
25
|
+
option :pending, type: :boolean, desc: "Sync only tasks flagged with github_sync_pending"
|
|
24
26
|
|
|
25
27
|
option :quiet, type: :boolean, aliases: %w[-q], desc: "Suppress non-essential output"
|
|
26
28
|
option :verbose, type: :boolean, aliases: %w[-v], desc: "Show verbose output"
|
|
27
29
|
option :debug, type: :boolean, aliases: %w[-d], desc: "Show debug output"
|
|
28
30
|
|
|
29
31
|
def call(ref: nil, **options)
|
|
30
|
-
all = options[:all]
|
|
32
|
+
all = options[:all] || options[:pending]
|
|
31
33
|
|
|
32
34
|
if !all && (ref.nil? || ref.strip.empty?)
|
|
33
35
|
raise Ace::Support::Cli::Error.new("Provide a task reference or use --all")
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
manager = Ace::Task::Organisms::TaskManager.new
|
|
37
|
-
result = manager.github_sync(ref: ref, all: all)
|
|
39
|
+
result = manager.github_sync(ref: ref, all: options[:all], pending: options[:pending])
|
|
38
40
|
raise Ace::Support::Cli::Error.new("Task '#{ref}' not found") if result.nil?
|
|
39
41
|
print_failures(result[:failures])
|
|
40
42
|
|
|
@@ -42,8 +44,14 @@ module Ace
|
|
|
42
44
|
raise Ace::Support::Cli::Error.new(
|
|
43
45
|
"GitHub sync incomplete: synced #{result[:synced]}, failed #{result[:failed]}, skipped #{result[:skipped]}"
|
|
44
46
|
)
|
|
47
|
+
elsif options[:pending] && result[:synced].to_i.zero? && result[:pending].to_i.zero?
|
|
48
|
+
puts "No pending GitHub syncs"
|
|
45
49
|
elsif all
|
|
46
50
|
puts "GitHub sync complete: synced #{result[:synced]} linked task(s), skipped #{result[:skipped]} task(s)"
|
|
51
|
+
pending = result[:pending].to_i
|
|
52
|
+
warn "gh unavailable: #{pending} task(s) remain flagged for github-sync --pending" if pending.positive?
|
|
53
|
+
elsif result[:pending].to_i.positive?
|
|
54
|
+
puts "GitHub sync skipped (gh unavailable); flagged for github-sync --pending"
|
|
47
55
|
elsif result[:synced].positive?
|
|
48
56
|
puts "GitHub sync complete: #{result[:task_id]}"
|
|
49
57
|
else
|
|
@@ -4,19 +4,29 @@ module Ace
|
|
|
4
4
|
module Task
|
|
5
5
|
module Molecules
|
|
6
6
|
# Bridges ace-task lifecycle events to reusable ace-git issue sync primitives.
|
|
7
|
+
# GitHub sync is best-effort: when gh is unavailable, operations no-op and
|
|
8
|
+
# callers record the pending sync on the task instead.
|
|
7
9
|
class GithubIssueSyncAdapter
|
|
8
10
|
UNAVAILABLE_MESSAGE = "GitHub issue sync primitives are unavailable. " \
|
|
9
|
-
"
|
|
11
|
+
"Update ace-git-github to a version providing them."
|
|
10
12
|
|
|
11
13
|
CANDIDATE_INTEGRATIONS = [
|
|
12
|
-
["Ace::Git::
|
|
13
|
-
["Ace::Git::
|
|
14
|
-
["Ace::Git::Molecules::GithubIssueSync", :sync_task],
|
|
15
|
-
["Ace::Git::Molecules::IssueSync", :sync_task]
|
|
14
|
+
["Ace::Git::Github::IssueSync", :validate_link!],
|
|
15
|
+
["Ace::Git::Github::IssueSync", :sync_task]
|
|
16
16
|
].freeze
|
|
17
17
|
|
|
18
|
+
# True when GitHub sync should run: the integration resolves and reports
|
|
19
|
+
# gh as installed and authenticated.
|
|
20
|
+
def available?
|
|
21
|
+
receiver, = resolve_integration(:sync_task)
|
|
22
|
+
return false unless receiver
|
|
23
|
+
|
|
24
|
+
receiver.respond_to?(:available?) ? receiver.available? : false
|
|
25
|
+
end
|
|
26
|
+
|
|
18
27
|
def validate_link!(issue_id:, previous_task: nil)
|
|
19
28
|
return unless issue_id.to_i.positive?
|
|
29
|
+
return unless available?
|
|
20
30
|
|
|
21
31
|
receiver, method_name = resolve_integration(:validate_link!)
|
|
22
32
|
raise UNAVAILABLE_MESSAGE unless receiver && method_name
|
|
@@ -33,7 +43,8 @@ module Ace
|
|
|
33
43
|
current_issue_ids = [task.metadata["github_issue"]].compact.map(&:to_i).uniq
|
|
34
44
|
previous_issue_ids = [previous_task&.metadata&.[]("github_issue")].compact.map(&:to_i).uniq
|
|
35
45
|
issue_ids = (current_issue_ids + previous_issue_ids).uniq
|
|
36
|
-
return {synced: 0, issues: []} if issue_ids.empty?
|
|
46
|
+
return {synced: 0, issues: []} if issue_ids.empty? || !available?
|
|
47
|
+
|
|
37
48
|
|
|
38
49
|
receiver, method_name = resolve_integration(:sync_task)
|
|
39
50
|
raise UNAVAILABLE_MESSAGE unless receiver && method_name
|
|
@@ -136,15 +136,22 @@ module Ace
|
|
|
136
136
|
|
|
137
137
|
def validate_github_fields(frontmatter, file_path, issues)
|
|
138
138
|
linked = frontmatter["github_issue"]
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
unless linked.is_a?(Integer) && linked.positive?
|
|
139
|
+
unless linked.nil? || (linked.is_a?(Integer) && linked.positive?)
|
|
142
140
|
issues << {
|
|
143
141
|
type: :error,
|
|
144
142
|
message: "Invalid GitHub issue ID '#{linked}' in github_issue (expected positive integer)",
|
|
145
143
|
location: file_path
|
|
146
144
|
}
|
|
147
145
|
end
|
|
146
|
+
|
|
147
|
+
pending = frontmatter["github_sync_pending"]
|
|
148
|
+
return if pending.nil? || pending == true || pending == false
|
|
149
|
+
|
|
150
|
+
issues << {
|
|
151
|
+
type: :error,
|
|
152
|
+
message: "Invalid github_sync_pending value (expected boolean)",
|
|
153
|
+
location: file_path
|
|
154
|
+
}
|
|
148
155
|
end
|
|
149
156
|
end
|
|
150
157
|
end
|
|
@@ -227,11 +227,12 @@ module Ace
|
|
|
227
227
|
created_subtask
|
|
228
228
|
end
|
|
229
229
|
|
|
230
|
-
def github_sync(ref: nil, all: false)
|
|
231
|
-
raise ArgumentError, "Provide --all or a task reference" if !all && (ref.nil? || ref.strip.empty?)
|
|
230
|
+
def github_sync(ref: nil, all: false, pending: false)
|
|
231
|
+
raise ArgumentError, "Provide --all or a task reference" if !all && !pending && (ref.nil? || ref.strip.empty?)
|
|
232
232
|
|
|
233
|
-
if all
|
|
233
|
+
if all || pending
|
|
234
234
|
tasks = list(in_folder: "all")
|
|
235
|
+
tasks = tasks.select { |t| t.metadata["github_sync_pending"] } if pending
|
|
235
236
|
linked_tasks = tasks.select { |t| linked_issue_id(t) }
|
|
236
237
|
results = linked_tasks.map { |task| sync_linked_issues_for(task, reason: "manual-sync") }
|
|
237
238
|
return summarize_manual_sync_results(results, skipped: tasks.length - linked_tasks.length)
|
|
@@ -482,13 +483,43 @@ module Ace
|
|
|
482
483
|
return sync_result_for(task: task, issues: issue_ids, success: true, reason: reason) if issue_ids.empty?
|
|
483
484
|
|
|
484
485
|
adapter = Molecules::GithubIssueSyncAdapter.new
|
|
486
|
+
unless adapter.available?
|
|
487
|
+
mark_github_sync_pending(task)
|
|
488
|
+
@last_update_note = "GitHub sync skipped (gh unavailable); flagged for 'ace-task github-sync --pending'"
|
|
489
|
+
return sync_result_for(task: task, issues: issue_ids, success: true, reason: reason).merge(offline: true)
|
|
490
|
+
end
|
|
491
|
+
|
|
485
492
|
adapter.sync_task(task: task, reason: reason, previous_task: previous_task)
|
|
493
|
+
clear_github_sync_pending(task)
|
|
486
494
|
sync_result_for(task: task, issues: issue_ids, success: true, reason: reason)
|
|
487
495
|
rescue StandardError => e
|
|
488
|
-
|
|
496
|
+
mark_github_sync_pending(task)
|
|
497
|
+
@last_update_note = "GitHub sync warning for task #{task&.id}: #{e.message}; flagged for 'ace-task github-sync --pending'"
|
|
489
498
|
sync_result_for(task: task, issues: issue_ids, success: false, reason: reason, error: e.message)
|
|
490
499
|
end
|
|
491
500
|
|
|
501
|
+
# Flag the task so the missed GitHub sync survives offline work and can be
|
|
502
|
+
# replayed with 'ace-task github-sync --pending'.
|
|
503
|
+
def mark_github_sync_pending(task)
|
|
504
|
+
return unless task&.file_path && File.exist?(task.file_path)
|
|
505
|
+
|
|
506
|
+
Ace::Support::Items::Molecules::FieldUpdater.update(
|
|
507
|
+
task.file_path, set: {"github_sync_pending" => true}
|
|
508
|
+
)
|
|
509
|
+
rescue StandardError
|
|
510
|
+
nil
|
|
511
|
+
end
|
|
512
|
+
|
|
513
|
+
def clear_github_sync_pending(task)
|
|
514
|
+
return unless task&.file_path && File.exist?(task.file_path)
|
|
515
|
+
|
|
516
|
+
Ace::Support::Items::Molecules::FieldUpdater.update(
|
|
517
|
+
task.file_path, set: {"github_sync_pending" => nil}
|
|
518
|
+
)
|
|
519
|
+
rescue StandardError
|
|
520
|
+
nil
|
|
521
|
+
end
|
|
522
|
+
|
|
492
523
|
def sync_result_for(task:, issues:, success:, reason:, error: nil)
|
|
493
524
|
{
|
|
494
525
|
task_id: task&.id,
|
|
@@ -508,9 +539,12 @@ module Ace
|
|
|
508
539
|
}
|
|
509
540
|
end
|
|
510
541
|
|
|
542
|
+
pending = results.count { |result| result[:offline] }
|
|
543
|
+
|
|
511
544
|
{
|
|
512
|
-
synced: results.length - failures.length,
|
|
545
|
+
synced: results.length - failures.length - pending,
|
|
513
546
|
failed: failures.length,
|
|
547
|
+
pending: pending,
|
|
514
548
|
skipped: skipped,
|
|
515
549
|
failures: failures
|
|
516
550
|
}
|
data/lib/ace/task/version.rb
CHANGED
data/lib/ace/task.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.37.
|
|
4
|
+
version: 0.37.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Michal Czyz
|
|
8
8
|
bindir: exe
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-09-
|
|
10
|
+
date: 2026-09-27 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: ace-support-core
|
|
@@ -85,14 +85,28 @@ dependencies:
|
|
|
85
85
|
requirements:
|
|
86
86
|
- - "~>"
|
|
87
87
|
- !ruby/object:Gem::Version
|
|
88
|
-
version: '0.
|
|
88
|
+
version: '0.24'
|
|
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.
|
|
95
|
+
version: '0.24'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: ace-git-github
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '0.1'
|
|
103
|
+
type: :runtime
|
|
104
|
+
prerelease: false
|
|
105
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0.1'
|
|
96
110
|
- !ruby/object:Gem::Dependency
|
|
97
111
|
name: ace-support-cli
|
|
98
112
|
requirement: !ruby/object:Gem::Requirement
|