aidp 0.26.0 → 0.28.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/README.md +89 -0
- data/lib/aidp/cli/checkpoint_command.rb +198 -0
- data/lib/aidp/cli/config_command.rb +71 -0
- data/lib/aidp/cli/enhanced_input.rb +2 -0
- data/lib/aidp/cli/first_run_wizard.rb +8 -7
- data/lib/aidp/cli/harness_command.rb +102 -0
- data/lib/aidp/cli/jobs_command.rb +3 -3
- data/lib/aidp/cli/mcp_dashboard.rb +4 -3
- data/lib/aidp/cli/models_command.rb +661 -0
- data/lib/aidp/cli/providers_command.rb +223 -0
- data/lib/aidp/cli.rb +45 -464
- data/lib/aidp/config.rb +54 -0
- data/lib/aidp/daemon/runner.rb +2 -2
- data/lib/aidp/debug_mixin.rb +25 -10
- data/lib/aidp/execute/agent_signal_parser.rb +22 -0
- data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
- data/lib/aidp/execute/checkpoint_display.rb +38 -37
- data/lib/aidp/execute/interactive_repl.rb +2 -1
- data/lib/aidp/execute/prompt_manager.rb +4 -4
- data/lib/aidp/execute/repl_macros.rb +2 -2
- data/lib/aidp/execute/steps.rb +94 -1
- data/lib/aidp/execute/work_loop_runner.rb +238 -19
- data/lib/aidp/execute/workflow_selector.rb +4 -27
- data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
- data/lib/aidp/harness/ai_decision_engine.rb +35 -2
- data/lib/aidp/harness/config_manager.rb +5 -10
- data/lib/aidp/harness/config_schema.rb +8 -0
- data/lib/aidp/harness/configuration.rb +40 -2
- data/lib/aidp/harness/enhanced_runner.rb +25 -19
- data/lib/aidp/harness/error_handler.rb +23 -73
- data/lib/aidp/harness/model_cache.rb +269 -0
- data/lib/aidp/harness/model_discovery_service.rb +259 -0
- data/lib/aidp/harness/model_registry.rb +201 -0
- data/lib/aidp/harness/provider_factory.rb +11 -2
- data/lib/aidp/harness/runner.rb +5 -0
- data/lib/aidp/harness/state_manager.rb +0 -7
- data/lib/aidp/harness/thinking_depth_manager.rb +202 -7
- data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
- data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
- data/lib/aidp/harness/ui/progress_display.rb +6 -2
- data/lib/aidp/harness/user_interface.rb +0 -58
- data/lib/aidp/init/runner.rb +7 -2
- data/lib/aidp/message_display.rb +0 -46
- data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
- data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
- data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
- data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
- data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
- data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
- data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
- data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
- data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
- data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
- data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
- data/lib/aidp/planning/parsers/document_parser.rb +141 -0
- data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
- data/lib/aidp/provider_manager.rb +8 -32
- data/lib/aidp/providers/adapter.rb +2 -4
- data/lib/aidp/providers/aider.rb +264 -0
- data/lib/aidp/providers/anthropic.rb +206 -121
- data/lib/aidp/providers/base.rb +123 -3
- data/lib/aidp/providers/capability_registry.rb +0 -1
- data/lib/aidp/providers/codex.rb +75 -70
- data/lib/aidp/providers/cursor.rb +87 -59
- data/lib/aidp/providers/gemini.rb +57 -60
- data/lib/aidp/providers/github_copilot.rb +19 -66
- data/lib/aidp/providers/kilocode.rb +35 -80
- data/lib/aidp/providers/opencode.rb +35 -80
- data/lib/aidp/setup/wizard.rb +555 -8
- data/lib/aidp/version.rb +1 -1
- data/lib/aidp/watch/build_processor.rb +211 -30
- data/lib/aidp/watch/change_request_processor.rb +128 -14
- data/lib/aidp/watch/ci_fix_processor.rb +103 -37
- data/lib/aidp/watch/ci_log_extractor.rb +258 -0
- data/lib/aidp/watch/github_state_extractor.rb +177 -0
- data/lib/aidp/watch/implementation_verifier.rb +284 -0
- data/lib/aidp/watch/plan_generator.rb +95 -52
- data/lib/aidp/watch/plan_processor.rb +7 -6
- data/lib/aidp/watch/repository_client.rb +245 -17
- data/lib/aidp/watch/review_processor.rb +100 -19
- data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
- data/lib/aidp/watch/runner.rb +181 -29
- data/lib/aidp/watch/state_store.rb +22 -1
- data/lib/aidp/workflows/definitions.rb +147 -0
- data/lib/aidp/workflows/guided_agent.rb +3 -3
- data/lib/aidp/workstream_cleanup.rb +245 -0
- data/lib/aidp/worktree.rb +19 -0
- data/templates/aidp-development.yml.example +2 -2
- data/templates/aidp-production.yml.example +3 -3
- data/templates/aidp.yml.example +57 -0
- data/templates/implementation/generate_tdd_specs.md +213 -0
- data/templates/implementation/iterative_implementation.md +122 -0
- data/templates/planning/agile/analyze_feedback.md +183 -0
- data/templates/planning/agile/generate_iteration_plan.md +179 -0
- data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
- data/templates/planning/agile/generate_marketing_report.md +162 -0
- data/templates/planning/agile/generate_mvp_scope.md +127 -0
- data/templates/planning/agile/generate_user_test_plan.md +143 -0
- data/templates/planning/agile/ingest_feedback.md +174 -0
- data/templates/planning/assemble_project_plan.md +113 -0
- data/templates/planning/assign_personas.md +108 -0
- data/templates/planning/create_tasks.md +52 -6
- data/templates/planning/generate_gantt.md +86 -0
- data/templates/planning/generate_wbs.md +85 -0
- data/templates/planning/initialize_planning_mode.md +70 -0
- data/templates/skills/README.md +2 -2
- data/templates/skills/marketing_strategist/SKILL.md +279 -0
- data/templates/skills/product_manager/SKILL.md +177 -0
- data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
- data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
- data/templates/skills/ux_researcher/SKILL.md +222 -0
- metadata +47 -1
|
@@ -10,6 +10,8 @@ require_relative "../harness/runner"
|
|
|
10
10
|
require_relative "../harness/state_manager"
|
|
11
11
|
require_relative "../worktree"
|
|
12
12
|
require_relative "../execute/progress"
|
|
13
|
+
require_relative "github_state_extractor"
|
|
14
|
+
require_relative "implementation_verifier"
|
|
13
15
|
|
|
14
16
|
module Aidp
|
|
15
17
|
module Watch
|
|
@@ -27,6 +29,8 @@ module Aidp
|
|
|
27
29
|
def initialize(repository_client:, state_store:, project_dir: Dir.pwd, use_workstreams: true, verbose: false, label_config: {})
|
|
28
30
|
@repository_client = repository_client
|
|
29
31
|
@state_store = state_store
|
|
32
|
+
@state_extractor = GitHubStateExtractor.new(repository_client: repository_client)
|
|
33
|
+
@verifier = ImplementationVerifier.new(repository_client: repository_client, project_dir: project_dir)
|
|
30
34
|
@project_dir = project_dir
|
|
31
35
|
@use_workstreams = use_workstreams
|
|
32
36
|
@verbose = verbose
|
|
@@ -40,7 +44,7 @@ module Aidp
|
|
|
40
44
|
number = issue[:number]
|
|
41
45
|
display_message("🛠️ Starting implementation for issue ##{number}", type: :info)
|
|
42
46
|
|
|
43
|
-
plan_data = ensure_plan_data(
|
|
47
|
+
plan_data = ensure_plan_data(issue)
|
|
44
48
|
return unless plan_data
|
|
45
49
|
|
|
46
50
|
slug = workstream_slug_for(issue)
|
|
@@ -87,27 +91,34 @@ module Aidp
|
|
|
87
91
|
backtrace: e.backtrace&.first(10)
|
|
88
92
|
)
|
|
89
93
|
|
|
90
|
-
#
|
|
91
|
-
|
|
94
|
+
# Record failure state internally but DON'T post error to GitHub
|
|
95
|
+
# (per issue #280 - error messages should never appear on issues)
|
|
96
|
+
@state_store.record_build_status(
|
|
97
|
+
issue[:number],
|
|
92
98
|
status: "error",
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
details: {
|
|
100
|
+
error: e.message,
|
|
101
|
+
error_class: e.class.name,
|
|
102
|
+
workstream: slug,
|
|
103
|
+
timestamp: Time.now.utc.iso8601
|
|
104
|
+
}
|
|
105
|
+
)
|
|
100
106
|
|
|
101
107
|
# Note: We intentionally DON'T re-raise here to allow watch mode to continue
|
|
102
|
-
# The error has been logged
|
|
108
|
+
# The error has been logged and recorded internally
|
|
103
109
|
end
|
|
104
110
|
|
|
105
111
|
private
|
|
106
112
|
|
|
107
|
-
def ensure_plan_data(
|
|
108
|
-
|
|
113
|
+
def ensure_plan_data(issue)
|
|
114
|
+
# First try to extract from the issue that was passed in (already has comments)
|
|
115
|
+
data = @state_extractor.extract_plan_data(issue)
|
|
116
|
+
|
|
117
|
+
# Fallback to local state store if needed (for backward compatibility)
|
|
118
|
+
data ||= @state_store.plan_data(issue[:number])
|
|
119
|
+
|
|
109
120
|
unless data
|
|
110
|
-
display_message("⚠️ No recorded plan for issue ##{number}. Skipping build trigger.", type: :warn)
|
|
121
|
+
display_message("⚠️ No recorded plan for issue ##{issue[:number]}. Skipping build trigger.", type: :warn)
|
|
111
122
|
end
|
|
112
123
|
data
|
|
113
124
|
end
|
|
@@ -419,11 +430,36 @@ module Aidp
|
|
|
419
430
|
return
|
|
420
431
|
end
|
|
421
432
|
|
|
433
|
+
# Verify implementation completeness before creating PR
|
|
434
|
+
verification = @verifier.verify(issue: issue, working_dir: working_dir)
|
|
435
|
+
|
|
436
|
+
unless verification[:verified]
|
|
437
|
+
handle_incomplete_implementation(
|
|
438
|
+
issue: issue,
|
|
439
|
+
slug: slug,
|
|
440
|
+
branch_name: branch_name,
|
|
441
|
+
working_dir: working_dir,
|
|
442
|
+
verification: verification
|
|
443
|
+
)
|
|
444
|
+
return
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
display_message("✅ Implementation verified complete", type: :success)
|
|
448
|
+
|
|
422
449
|
# Check if PR should be created based on VCS preferences
|
|
423
450
|
# For watch mode, default to creating PRs (set to false to disable)
|
|
424
451
|
vcs_config = config_dig(:work_loop, :version_control) || {}
|
|
425
452
|
auto_create_pr = config_value(vcs_config, :auto_create_pr, true)
|
|
426
453
|
|
|
454
|
+
Aidp.log_debug(
|
|
455
|
+
"build_processor",
|
|
456
|
+
"evaluating_pr_creation",
|
|
457
|
+
issue: issue[:number],
|
|
458
|
+
changes_committed: changes_committed,
|
|
459
|
+
auto_create_pr: auto_create_pr,
|
|
460
|
+
gh_available: @repository_client.gh_available?
|
|
461
|
+
)
|
|
462
|
+
|
|
427
463
|
pr_url = if !changes_committed
|
|
428
464
|
Aidp.log_info(
|
|
429
465
|
"build_processor",
|
|
@@ -441,14 +477,37 @@ module Aidp
|
|
|
441
477
|
issue: issue[:number],
|
|
442
478
|
branch: branch_name,
|
|
443
479
|
base_branch: base_branch,
|
|
444
|
-
working_dir: working_dir
|
|
480
|
+
working_dir: working_dir,
|
|
481
|
+
gh_available: @repository_client.gh_available?
|
|
445
482
|
)
|
|
446
483
|
create_pull_request(issue: issue, branch_name: branch_name, base_branch: base_branch, working_dir: working_dir)
|
|
447
484
|
else
|
|
485
|
+
Aidp.log_info(
|
|
486
|
+
"build_processor",
|
|
487
|
+
"skipping_pr_vcs_preference",
|
|
488
|
+
issue: issue[:number],
|
|
489
|
+
auto_create_pr: auto_create_pr
|
|
490
|
+
)
|
|
448
491
|
display_message("ℹ️ Skipping PR creation (disabled in VCS preferences)", type: :muted)
|
|
449
492
|
nil
|
|
450
493
|
end
|
|
451
494
|
|
|
495
|
+
# Record build status BEFORE posting completion comment
|
|
496
|
+
@state_store.record_build_status(
|
|
497
|
+
issue[:number],
|
|
498
|
+
status: "completed",
|
|
499
|
+
details: {branch: branch_name, workstream: slug, pr_url: pr_url}
|
|
500
|
+
)
|
|
501
|
+
|
|
502
|
+
# Remove build label BEFORE posting completion comment
|
|
503
|
+
begin
|
|
504
|
+
@repository_client.remove_labels(issue[:number], @build_label)
|
|
505
|
+
display_message("🏷️ Removed '#{@build_label}' label after completion", type: :info)
|
|
506
|
+
rescue => e
|
|
507
|
+
display_message("⚠️ Failed to remove build label: #{e.message}", type: :warn)
|
|
508
|
+
# Don't fail the process if label removal fails
|
|
509
|
+
end
|
|
510
|
+
|
|
452
511
|
# Fetch the user who added the most recent label
|
|
453
512
|
label_actor = @repository_client.most_recent_label_actor(issue[:number])
|
|
454
513
|
|
|
@@ -456,6 +515,7 @@ module Aidp
|
|
|
456
515
|
pr_line = pr_url ? "\n- Pull Request: #{pr_url}" : ""
|
|
457
516
|
actor_tag = label_actor ? "cc @#{label_actor}\n\n" : ""
|
|
458
517
|
|
|
518
|
+
# Post completion comment LAST - after all other operations complete successfully
|
|
459
519
|
comment = <<~COMMENT
|
|
460
520
|
✅ Implementation complete for ##{issue[:number]}.
|
|
461
521
|
|
|
@@ -466,22 +526,8 @@ module Aidp
|
|
|
466
526
|
COMMENT
|
|
467
527
|
|
|
468
528
|
@repository_client.post_comment(issue[:number], comment)
|
|
469
|
-
@state_store.record_build_status(
|
|
470
|
-
issue[:number],
|
|
471
|
-
status: "completed",
|
|
472
|
-
details: {branch: branch_name, workstream: slug, pr_url: pr_url}
|
|
473
|
-
)
|
|
474
529
|
display_message("🎉 Posted completion comment for issue ##{issue[:number]}", type: :success)
|
|
475
530
|
|
|
476
|
-
# Remove build label after successful completion
|
|
477
|
-
begin
|
|
478
|
-
@repository_client.remove_labels(issue[:number], @build_label)
|
|
479
|
-
display_message("🏷️ Removed '#{@build_label}' label after completion", type: :info)
|
|
480
|
-
rescue => e
|
|
481
|
-
display_message("⚠️ Failed to remove build label: #{e.message}", type: :warn)
|
|
482
|
-
# Don't fail the process if label removal fails
|
|
483
|
-
end
|
|
484
|
-
|
|
485
531
|
# Keep workstream for review - don't auto-cleanup on success
|
|
486
532
|
if @use_workstreams
|
|
487
533
|
display_message("ℹ️ Workstream #{slug} preserved for review. Remove with: aidp ws rm #{slug}", type: :muted)
|
|
@@ -624,6 +670,103 @@ module Aidp
|
|
|
624
670
|
)
|
|
625
671
|
end
|
|
626
672
|
|
|
673
|
+
def handle_incomplete_implementation(issue:, slug:, branch_name:, working_dir:, verification:)
|
|
674
|
+
display_message("⚠️ Implementation incomplete; scheduling additional work.", type: :warn)
|
|
675
|
+
|
|
676
|
+
# Create tasks for missing requirements
|
|
677
|
+
if verification[:additional_work] && !verification[:additional_work].empty?
|
|
678
|
+
create_follow_up_tasks(working_dir, verification[:additional_work])
|
|
679
|
+
end
|
|
680
|
+
|
|
681
|
+
# Schedule another agentic work unit
|
|
682
|
+
enqueue_decider_followup(working_dir)
|
|
683
|
+
|
|
684
|
+
workstream_note = @use_workstreams ? " The workstream `#{slug}` has been preserved for continued work." : " The branch has been preserved for continued work."
|
|
685
|
+
|
|
686
|
+
# Fetch the user who added the most recent label
|
|
687
|
+
label_actor = @repository_client.most_recent_label_actor(issue[:number])
|
|
688
|
+
actor_tag = label_actor ? "cc @#{label_actor}\n\n" : ""
|
|
689
|
+
|
|
690
|
+
# Post status comment
|
|
691
|
+
missing_section = if verification[:missing_items] && !verification[:missing_items].empty?
|
|
692
|
+
"\n### Missing Requirements\n" + verification[:missing_items].map { |item| "- #{item}" }.join("\n")
|
|
693
|
+
else
|
|
694
|
+
""
|
|
695
|
+
end
|
|
696
|
+
|
|
697
|
+
additional_work_section = if verification[:additional_work] && !verification[:additional_work].empty?
|
|
698
|
+
"\n### Additional Work Needed\n" + verification[:additional_work].map { |item| "- #{item}" }.join("\n")
|
|
699
|
+
else
|
|
700
|
+
""
|
|
701
|
+
end
|
|
702
|
+
|
|
703
|
+
comment = <<~COMMENT
|
|
704
|
+
🔄 Implementation in progress for ##{issue[:number]}.
|
|
705
|
+
|
|
706
|
+
#{actor_tag}The automated implementation has made progress but is not yet complete. Additional work is needed to fully address the issue requirements.
|
|
707
|
+
|
|
708
|
+
### Verification Result
|
|
709
|
+
#{verification[:reason]}#{missing_section}#{additional_work_section}
|
|
710
|
+
|
|
711
|
+
The work loop will continue automatically to complete the remaining tasks.#{workstream_note}
|
|
712
|
+
COMMENT
|
|
713
|
+
|
|
714
|
+
@repository_client.post_comment(issue[:number], comment)
|
|
715
|
+
display_message("💬 Posted incomplete implementation status for issue ##{issue[:number]}", type: :info)
|
|
716
|
+
|
|
717
|
+
@state_store.record_build_status(
|
|
718
|
+
issue[:number],
|
|
719
|
+
status: "incomplete",
|
|
720
|
+
details: {
|
|
721
|
+
branch: branch_name,
|
|
722
|
+
workstream: slug,
|
|
723
|
+
verification: verification
|
|
724
|
+
}
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
Aidp.log_info(
|
|
728
|
+
"build_processor",
|
|
729
|
+
"incomplete_implementation",
|
|
730
|
+
issue: issue[:number],
|
|
731
|
+
branch: branch_name,
|
|
732
|
+
workstream: slug,
|
|
733
|
+
missing_items: verification[:missing_items],
|
|
734
|
+
additional_work: verification[:additional_work]
|
|
735
|
+
)
|
|
736
|
+
end
|
|
737
|
+
|
|
738
|
+
def create_follow_up_tasks(working_dir, additional_work)
|
|
739
|
+
return if additional_work.nil? || additional_work.empty?
|
|
740
|
+
|
|
741
|
+
tasklist_file = File.join(working_dir, ".aidp", "tasklist.jsonl")
|
|
742
|
+
FileUtils.mkdir_p(File.dirname(tasklist_file))
|
|
743
|
+
|
|
744
|
+
require_relative "../execute/persistent_tasklist"
|
|
745
|
+
tasklist = Aidp::Execute::PersistentTasklist.new(working_dir)
|
|
746
|
+
|
|
747
|
+
additional_work.each do |task_description|
|
|
748
|
+
tasklist.create(
|
|
749
|
+
description: task_description,
|
|
750
|
+
priority: :high,
|
|
751
|
+
tags: ["auto-generated", "verification"]
|
|
752
|
+
)
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
Aidp.log_info(
|
|
756
|
+
"build_processor",
|
|
757
|
+
"created_follow_up_tasks",
|
|
758
|
+
working_dir: working_dir,
|
|
759
|
+
task_count: additional_work.length
|
|
760
|
+
)
|
|
761
|
+
rescue => e
|
|
762
|
+
Aidp.log_warn(
|
|
763
|
+
"build_processor",
|
|
764
|
+
"failed_to_create_follow_up_tasks",
|
|
765
|
+
error: e.message,
|
|
766
|
+
working_dir: working_dir
|
|
767
|
+
)
|
|
768
|
+
end
|
|
769
|
+
|
|
627
770
|
def stage_and_commit(issue, working_dir: @project_dir)
|
|
628
771
|
commit_created = false
|
|
629
772
|
|
|
@@ -771,6 +914,19 @@ module Aidp
|
|
|
771
914
|
fallback_to_author: label_actor.nil?
|
|
772
915
|
)
|
|
773
916
|
|
|
917
|
+
Aidp.log_debug(
|
|
918
|
+
"build_processor",
|
|
919
|
+
"attempting_pr_creation",
|
|
920
|
+
issue: issue[:number],
|
|
921
|
+
branch_name: branch_name,
|
|
922
|
+
base_branch: base_branch,
|
|
923
|
+
draft: draft,
|
|
924
|
+
assignee: assignee,
|
|
925
|
+
gh_available: @repository_client.gh_available?,
|
|
926
|
+
title: title,
|
|
927
|
+
body_length: body.length
|
|
928
|
+
)
|
|
929
|
+
|
|
774
930
|
output = @repository_client.create_pull_request(
|
|
775
931
|
title: title,
|
|
776
932
|
body: body,
|
|
@@ -789,9 +945,34 @@ module Aidp
|
|
|
789
945
|
branch: branch_name,
|
|
790
946
|
base_branch: base_branch,
|
|
791
947
|
pr_url: pr_url,
|
|
792
|
-
assignee: assignee
|
|
948
|
+
assignee: assignee,
|
|
949
|
+
raw_output: output
|
|
793
950
|
)
|
|
794
951
|
pr_url
|
|
952
|
+
rescue => e
|
|
953
|
+
Aidp.log_error(
|
|
954
|
+
"build_processor",
|
|
955
|
+
"pr_creation_failed",
|
|
956
|
+
issue: issue[:number],
|
|
957
|
+
branch_name: branch_name,
|
|
958
|
+
base_branch: base_branch,
|
|
959
|
+
error: e.message,
|
|
960
|
+
error_class: e.class.name,
|
|
961
|
+
gh_available: @repository_client.gh_available?,
|
|
962
|
+
backtrace: e.backtrace&.first(10),
|
|
963
|
+
assignee: assignee,
|
|
964
|
+
title: title
|
|
965
|
+
)
|
|
966
|
+
display_message("⚠️ Failed to create pull request: #{e.message}", type: :warn)
|
|
967
|
+
|
|
968
|
+
# Continue gracefully - PR creation failure shouldn't crash the processor
|
|
969
|
+
Aidp.log_info(
|
|
970
|
+
"build_processor",
|
|
971
|
+
"continuing_after_pr_failure",
|
|
972
|
+
issue: issue[:number],
|
|
973
|
+
message: "Implementation complete but PR creation failed"
|
|
974
|
+
)
|
|
975
|
+
nil
|
|
795
976
|
end
|
|
796
977
|
|
|
797
978
|
def gather_test_summary(working_dir: @project_dir)
|
|
@@ -12,6 +12,9 @@ require_relative "../execute/prompt_manager"
|
|
|
12
12
|
require_relative "../harness/runner"
|
|
13
13
|
require_relative "../harness/state_manager"
|
|
14
14
|
require_relative "../harness/test_runner"
|
|
15
|
+
require_relative "../worktree"
|
|
16
|
+
require_relative "github_state_extractor"
|
|
17
|
+
require_relative "implementation_verifier"
|
|
15
18
|
|
|
16
19
|
module Aidp
|
|
17
20
|
module Watch
|
|
@@ -32,10 +35,17 @@ module Aidp
|
|
|
32
35
|
def initialize(repository_client:, state_store:, provider_name: nil, project_dir: Dir.pwd, label_config: {}, change_request_config: {}, safety_config: {}, verbose: false)
|
|
33
36
|
@repository_client = repository_client
|
|
34
37
|
@state_store = state_store
|
|
38
|
+
@state_extractor = GitHubStateExtractor.new(repository_client: repository_client)
|
|
35
39
|
@provider_name = provider_name
|
|
36
40
|
@project_dir = project_dir
|
|
37
41
|
@verbose = verbose
|
|
38
42
|
|
|
43
|
+
# Initialize verifier
|
|
44
|
+
@verifier = ImplementationVerifier.new(
|
|
45
|
+
repository_client: repository_client,
|
|
46
|
+
project_dir: project_dir
|
|
47
|
+
)
|
|
48
|
+
|
|
39
49
|
# Load label configuration
|
|
40
50
|
@change_request_label = label_config[:change_request_trigger] || label_config["change_request_trigger"] || DEFAULT_CHANGE_REQUEST_LABEL
|
|
41
51
|
@needs_input_label = label_config[:needs_input] || label_config["needs_input"] || DEFAULT_NEEDS_INPUT_LABEL
|
|
@@ -109,19 +119,14 @@ module Aidp
|
|
|
109
119
|
display_message("❌ Change request processing failed: #{e.message}", type: :error)
|
|
110
120
|
Aidp.log_error("change_request_processor", "Change request failed", pr: pr[:number], error: e.message, backtrace: e.backtrace&.first(10))
|
|
111
121
|
|
|
112
|
-
#
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
begin
|
|
121
|
-
@repository_client.post_comment(pr[:number], error_comment)
|
|
122
|
-
rescue
|
|
123
|
-
nil
|
|
124
|
-
end
|
|
122
|
+
# Record failure state internally but DON'T post error to GitHub
|
|
123
|
+
# (per issue #280 - error messages should never appear on issues)
|
|
124
|
+
@state_store.record_change_request(pr[:number], {
|
|
125
|
+
status: "error",
|
|
126
|
+
error: e.message,
|
|
127
|
+
error_class: e.class.name,
|
|
128
|
+
timestamp: Time.now.utc.iso8601
|
|
129
|
+
})
|
|
125
130
|
end
|
|
126
131
|
|
|
127
132
|
private
|
|
@@ -143,7 +148,7 @@ module Aidp
|
|
|
143
148
|
|
|
144
149
|
def analyze_change_requests(pr_data:, comments:, diff:)
|
|
145
150
|
provider_name = @provider_name || detect_default_provider
|
|
146
|
-
provider = Aidp::ProviderManager.get_provider(provider_name
|
|
151
|
+
provider = Aidp::ProviderManager.get_provider(provider_name)
|
|
147
152
|
|
|
148
153
|
user_prompt = build_analysis_prompt(pr_data: pr_data, comments: comments, diff: diff)
|
|
149
154
|
full_prompt = "#{change_request_system_prompt}\n\n#{user_prompt}"
|
|
@@ -269,6 +274,28 @@ module Aidp
|
|
|
269
274
|
end
|
|
270
275
|
end
|
|
271
276
|
|
|
277
|
+
# Check if PR is linked to an issue - if so, verify implementation completeness
|
|
278
|
+
issue_number = @state_extractor.extract_linked_issue(pr[:body])
|
|
279
|
+
if issue_number
|
|
280
|
+
display_message("🔗 Found linked issue ##{issue_number} - verifying implementation...", type: :info)
|
|
281
|
+
|
|
282
|
+
begin
|
|
283
|
+
issue = @repository_client.fetch_issue(issue_number)
|
|
284
|
+
verification_result = @verifier.verify(issue: issue, working_dir: @project_dir)
|
|
285
|
+
|
|
286
|
+
unless verification_result[:verified]
|
|
287
|
+
handle_incomplete_implementation(pr: pr, analysis: analysis, verification_result: verification_result)
|
|
288
|
+
return
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
display_message("✅ Implementation verified complete", type: :success)
|
|
292
|
+
rescue => e
|
|
293
|
+
display_message("⚠️ Verification check failed: #{e.message}", type: :warn)
|
|
294
|
+
Aidp.log_error("change_request_processor", "Verification failed", pr: pr[:number], error: e.message)
|
|
295
|
+
# Continue with commit/push even if verification fails
|
|
296
|
+
end
|
|
297
|
+
end
|
|
298
|
+
|
|
272
299
|
# Commit and push
|
|
273
300
|
if commit_and_push(pr, analysis)
|
|
274
301
|
handle_success(pr: pr, analysis: analysis)
|
|
@@ -279,7 +306,29 @@ module Aidp
|
|
|
279
306
|
|
|
280
307
|
def checkout_pr_branch(pr_data)
|
|
281
308
|
head_ref = pr_data[:head_ref]
|
|
309
|
+
pr_number = pr_data[:number]
|
|
310
|
+
|
|
311
|
+
# Check if a worktree already exists for this branch
|
|
312
|
+
existing = Aidp::Worktree.find_by_branch(branch: head_ref, project_dir: @project_dir)
|
|
313
|
+
|
|
314
|
+
if existing && existing[:active]
|
|
315
|
+
display_message("🔄 Using existing worktree for branch: #{head_ref}", type: :info)
|
|
316
|
+
Aidp.log_debug("change_request_processor", "worktree_reused", pr_number: pr_number, branch: head_ref, path: existing[:path])
|
|
282
317
|
|
|
318
|
+
# Update @project_dir to point to the worktree
|
|
319
|
+
@project_dir = existing[:path]
|
|
320
|
+
|
|
321
|
+
# Pull latest changes in the worktree
|
|
322
|
+
Dir.chdir(@project_dir) do
|
|
323
|
+
run_git(%w[fetch origin], allow_failure: true)
|
|
324
|
+
run_git(["checkout", head_ref])
|
|
325
|
+
run_git(%w[pull --ff-only], allow_failure: true)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
return
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Otherwise, use the main worktree
|
|
283
332
|
Dir.chdir(@project_dir) do
|
|
284
333
|
# Fetch latest
|
|
285
334
|
run_git(%w[fetch origin])
|
|
@@ -448,6 +497,71 @@ module Aidp
|
|
|
448
497
|
end
|
|
449
498
|
end
|
|
450
499
|
|
|
500
|
+
def handle_incomplete_implementation(pr:, analysis:, verification_result:)
|
|
501
|
+
display_message("⚠️ Implementation incomplete; creating follow-up tasks.", type: :warn)
|
|
502
|
+
|
|
503
|
+
# Create tasks for missing requirements
|
|
504
|
+
if verification_result[:additional_work] && !verification_result[:additional_work].empty?
|
|
505
|
+
create_follow_up_tasks(@project_dir, verification_result[:additional_work])
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
# Record state but do not post a separate comment
|
|
509
|
+
# (verification details will be included in the next summary comment)
|
|
510
|
+
@state_store.record_change_request(pr[:number], {
|
|
511
|
+
status: "incomplete_implementation",
|
|
512
|
+
timestamp: Time.now.utc.iso8601,
|
|
513
|
+
verification_reasons: verification_result[:reasons],
|
|
514
|
+
missing_items: verification_result[:missing_items],
|
|
515
|
+
additional_work: verification_result[:additional_work]
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
display_message("📝 Recorded incomplete implementation status for PR ##{pr[:number]}", type: :info)
|
|
519
|
+
|
|
520
|
+
# Keep the label so the work loop continues (do NOT remove it)
|
|
521
|
+
Aidp.log_info(
|
|
522
|
+
"change_request_processor",
|
|
523
|
+
"incomplete_implementation",
|
|
524
|
+
pr: pr[:number],
|
|
525
|
+
missing_items: verification_result[:missing_items],
|
|
526
|
+
additional_work: verification_result[:additional_work]
|
|
527
|
+
)
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
def create_follow_up_tasks(working_dir, additional_work)
|
|
531
|
+
return if additional_work.nil? || additional_work.empty?
|
|
532
|
+
|
|
533
|
+
tasklist_file = File.join(working_dir, ".aidp", "tasklist.jsonl")
|
|
534
|
+
FileUtils.mkdir_p(File.dirname(tasklist_file))
|
|
535
|
+
|
|
536
|
+
require_relative "../execute/persistent_tasklist"
|
|
537
|
+
tasklist = Aidp::Execute::PersistentTasklist.new(working_dir)
|
|
538
|
+
|
|
539
|
+
additional_work.each do |task_description|
|
|
540
|
+
tasklist.create(
|
|
541
|
+
description: task_description,
|
|
542
|
+
priority: :high,
|
|
543
|
+
source: "verification"
|
|
544
|
+
)
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
display_message("📝 Created #{additional_work.length} follow-up task(s) for continued work", type: :info)
|
|
548
|
+
|
|
549
|
+
Aidp.log_info(
|
|
550
|
+
"change_request_processor",
|
|
551
|
+
"created_follow_up_tasks",
|
|
552
|
+
task_count: additional_work.length,
|
|
553
|
+
working_dir: working_dir
|
|
554
|
+
)
|
|
555
|
+
rescue => e
|
|
556
|
+
display_message("⚠️ Failed to create follow-up tasks: #{e.message}", type: :warn)
|
|
557
|
+
Aidp.log_error(
|
|
558
|
+
"change_request_processor",
|
|
559
|
+
"failed_to_create_follow_up_tasks",
|
|
560
|
+
error: e.message,
|
|
561
|
+
backtrace: e.backtrace&.first(5)
|
|
562
|
+
)
|
|
563
|
+
end
|
|
564
|
+
|
|
451
565
|
def handle_clarification_needed(pr:, analysis:)
|
|
452
566
|
existing_data = @state_store.change_request_data(pr[:number])
|
|
453
567
|
clarification_count = (existing_data&.dig("clarification_count") || 0) + 1
|