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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +89 -0
  3. data/lib/aidp/cli/checkpoint_command.rb +198 -0
  4. data/lib/aidp/cli/config_command.rb +71 -0
  5. data/lib/aidp/cli/enhanced_input.rb +2 -0
  6. data/lib/aidp/cli/first_run_wizard.rb +8 -7
  7. data/lib/aidp/cli/harness_command.rb +102 -0
  8. data/lib/aidp/cli/jobs_command.rb +3 -3
  9. data/lib/aidp/cli/mcp_dashboard.rb +4 -3
  10. data/lib/aidp/cli/models_command.rb +661 -0
  11. data/lib/aidp/cli/providers_command.rb +223 -0
  12. data/lib/aidp/cli.rb +45 -464
  13. data/lib/aidp/config.rb +54 -0
  14. data/lib/aidp/daemon/runner.rb +2 -2
  15. data/lib/aidp/debug_mixin.rb +25 -10
  16. data/lib/aidp/execute/agent_signal_parser.rb +22 -0
  17. data/lib/aidp/execute/async_work_loop_runner.rb +2 -1
  18. data/lib/aidp/execute/checkpoint_display.rb +38 -37
  19. data/lib/aidp/execute/interactive_repl.rb +2 -1
  20. data/lib/aidp/execute/prompt_manager.rb +4 -4
  21. data/lib/aidp/execute/repl_macros.rb +2 -2
  22. data/lib/aidp/execute/steps.rb +94 -1
  23. data/lib/aidp/execute/work_loop_runner.rb +238 -19
  24. data/lib/aidp/execute/workflow_selector.rb +4 -27
  25. data/lib/aidp/firewall/provider_requirements_collector.rb +262 -0
  26. data/lib/aidp/harness/ai_decision_engine.rb +35 -2
  27. data/lib/aidp/harness/config_manager.rb +5 -10
  28. data/lib/aidp/harness/config_schema.rb +8 -0
  29. data/lib/aidp/harness/configuration.rb +40 -2
  30. data/lib/aidp/harness/enhanced_runner.rb +25 -19
  31. data/lib/aidp/harness/error_handler.rb +23 -73
  32. data/lib/aidp/harness/model_cache.rb +269 -0
  33. data/lib/aidp/harness/model_discovery_service.rb +259 -0
  34. data/lib/aidp/harness/model_registry.rb +201 -0
  35. data/lib/aidp/harness/provider_factory.rb +11 -2
  36. data/lib/aidp/harness/runner.rb +5 -0
  37. data/lib/aidp/harness/state_manager.rb +0 -7
  38. data/lib/aidp/harness/thinking_depth_manager.rb +202 -7
  39. data/lib/aidp/harness/ui/enhanced_tui.rb +8 -18
  40. data/lib/aidp/harness/ui/enhanced_workflow_selector.rb +0 -18
  41. data/lib/aidp/harness/ui/progress_display.rb +6 -2
  42. data/lib/aidp/harness/user_interface.rb +0 -58
  43. data/lib/aidp/init/runner.rb +7 -2
  44. data/lib/aidp/message_display.rb +0 -46
  45. data/lib/aidp/planning/analyzers/feedback_analyzer.rb +365 -0
  46. data/lib/aidp/planning/builders/agile_plan_builder.rb +387 -0
  47. data/lib/aidp/planning/builders/project_plan_builder.rb +193 -0
  48. data/lib/aidp/planning/generators/gantt_generator.rb +190 -0
  49. data/lib/aidp/planning/generators/iteration_plan_generator.rb +392 -0
  50. data/lib/aidp/planning/generators/legacy_research_planner.rb +473 -0
  51. data/lib/aidp/planning/generators/marketing_report_generator.rb +348 -0
  52. data/lib/aidp/planning/generators/mvp_scope_generator.rb +310 -0
  53. data/lib/aidp/planning/generators/user_test_plan_generator.rb +373 -0
  54. data/lib/aidp/planning/generators/wbs_generator.rb +259 -0
  55. data/lib/aidp/planning/mappers/persona_mapper.rb +163 -0
  56. data/lib/aidp/planning/parsers/document_parser.rb +141 -0
  57. data/lib/aidp/planning/parsers/feedback_data_parser.rb +252 -0
  58. data/lib/aidp/provider_manager.rb +8 -32
  59. data/lib/aidp/providers/adapter.rb +2 -4
  60. data/lib/aidp/providers/aider.rb +264 -0
  61. data/lib/aidp/providers/anthropic.rb +206 -121
  62. data/lib/aidp/providers/base.rb +123 -3
  63. data/lib/aidp/providers/capability_registry.rb +0 -1
  64. data/lib/aidp/providers/codex.rb +75 -70
  65. data/lib/aidp/providers/cursor.rb +87 -59
  66. data/lib/aidp/providers/gemini.rb +57 -60
  67. data/lib/aidp/providers/github_copilot.rb +19 -66
  68. data/lib/aidp/providers/kilocode.rb +35 -80
  69. data/lib/aidp/providers/opencode.rb +35 -80
  70. data/lib/aidp/setup/wizard.rb +555 -8
  71. data/lib/aidp/version.rb +1 -1
  72. data/lib/aidp/watch/build_processor.rb +211 -30
  73. data/lib/aidp/watch/change_request_processor.rb +128 -14
  74. data/lib/aidp/watch/ci_fix_processor.rb +103 -37
  75. data/lib/aidp/watch/ci_log_extractor.rb +258 -0
  76. data/lib/aidp/watch/github_state_extractor.rb +177 -0
  77. data/lib/aidp/watch/implementation_verifier.rb +284 -0
  78. data/lib/aidp/watch/plan_generator.rb +95 -52
  79. data/lib/aidp/watch/plan_processor.rb +7 -6
  80. data/lib/aidp/watch/repository_client.rb +245 -17
  81. data/lib/aidp/watch/review_processor.rb +100 -19
  82. data/lib/aidp/watch/reviewers/base_reviewer.rb +1 -1
  83. data/lib/aidp/watch/runner.rb +181 -29
  84. data/lib/aidp/watch/state_store.rb +22 -1
  85. data/lib/aidp/workflows/definitions.rb +147 -0
  86. data/lib/aidp/workflows/guided_agent.rb +3 -3
  87. data/lib/aidp/workstream_cleanup.rb +245 -0
  88. data/lib/aidp/worktree.rb +19 -0
  89. data/templates/aidp-development.yml.example +2 -2
  90. data/templates/aidp-production.yml.example +3 -3
  91. data/templates/aidp.yml.example +57 -0
  92. data/templates/implementation/generate_tdd_specs.md +213 -0
  93. data/templates/implementation/iterative_implementation.md +122 -0
  94. data/templates/planning/agile/analyze_feedback.md +183 -0
  95. data/templates/planning/agile/generate_iteration_plan.md +179 -0
  96. data/templates/planning/agile/generate_legacy_research_plan.md +171 -0
  97. data/templates/planning/agile/generate_marketing_report.md +162 -0
  98. data/templates/planning/agile/generate_mvp_scope.md +127 -0
  99. data/templates/planning/agile/generate_user_test_plan.md +143 -0
  100. data/templates/planning/agile/ingest_feedback.md +174 -0
  101. data/templates/planning/assemble_project_plan.md +113 -0
  102. data/templates/planning/assign_personas.md +108 -0
  103. data/templates/planning/create_tasks.md +52 -6
  104. data/templates/planning/generate_gantt.md +86 -0
  105. data/templates/planning/generate_wbs.md +85 -0
  106. data/templates/planning/initialize_planning_mode.md +70 -0
  107. data/templates/skills/README.md +2 -2
  108. data/templates/skills/marketing_strategist/SKILL.md +279 -0
  109. data/templates/skills/product_manager/SKILL.md +177 -0
  110. data/templates/skills/ruby_aidp_planning/SKILL.md +497 -0
  111. data/templates/skills/ruby_rspec_tdd/SKILL.md +514 -0
  112. data/templates/skills/ux_researcher/SKILL.md +222 -0
  113. 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(number)
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
- # Create error result to pass to handle_failure
91
- error_result = {
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
- error: e.message,
94
- error_class: e.class.name,
95
- message: "Exception during harness execution: #{e.message}"
96
- }
97
-
98
- # Handle as failure (posts comment, updates state) but DON'T re-raise
99
- handle_failure(issue: issue, slug: slug, result: error_result)
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, recorded, and reported to GitHub
108
+ # The error has been logged and recorded internally
103
109
  end
104
110
 
105
111
  private
106
112
 
107
- def ensure_plan_data(number)
108
- data = @state_store.plan_data(number)
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
- # Post error comment
113
- error_comment = <<~COMMENT
114
- #{COMMENT_HEADER}
115
-
116
- ❌ Automated change request processing failed: #{e.message}
117
-
118
- Please review the requested changes manually or retry by re-adding the `#{@change_request_label}` label.
119
- COMMENT
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, use_harness: false)
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