brainiac-basecamp 0.0.9 → 0.0.11

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f3b4d7d9e99a700f2a533b48110071025bde9eb69b55633acffd4125f88c5540
4
- data.tar.gz: 63635c64597b65ed6e0a69e90511010d6975c418249c7a181d9a0349bded7dcc
3
+ metadata.gz: d4d21875fecdd7aa33718a8db5fa446800e8a85924490b6716a455b211d79483
4
+ data.tar.gz: 9601289488c260e891a6df6da2b63be3e9965c9c8235d11c4366622d27a24d0b
5
5
  SHA512:
6
- metadata.gz: 37d7d00ab786c8f99318d7098b8a700d8428d0c3db1da4ceaa1040aa924b627b0b886a928cb7f6f1661e35f578403ae4b8911b87a5c48c6bc67a9c0ed5f61c02
7
- data.tar.gz: a798eb76376698539c16c54ecdfea9d597893e0f0630c035e9e473401e45b43c0357e4cb6ef8717df704c98543f2bace4754b5f2db6dc97677a46ee8782b2ff9
6
+ metadata.gz: f24339950cce469195888d59fbc7ddecb0b75f9a7f8279df58cc2bb681637916525341ce8b9fc86c2f38bb9e8b1718a27c9f164d14afa4acff9376f4394da3eb
7
+ data.tar.gz: 39d9128c65180e56fe30e59d6b384dbc9ce2b7648f8586498b88e7e44b4c516fcea6d06b279bc1956373f84f1a8602aa1fb697699a40b98e699b2c6ef2f97c59
@@ -214,16 +214,12 @@ module Brainiac
214
214
  # All gates have reviewed — dispatch implementation agent to address ALL feedback
215
215
  LOG.info "[Basecamp:Hooks] All gates responded for card ##{card_number} — dispatching fixes" if defined?(LOG)
216
216
  task["status"] = "in_flight"
217
+ task["dispatched_at"] = Time.now.iso8601
217
218
  task.delete("changes_debounce_started")
218
219
  save_epic_state(epic)
219
220
 
220
- # Re-assign card to implementation agent to trigger dispatch
221
- impl_agent = epic["agent"]
222
- fizzy_user_id = Orchestrator.send(:resolve_fizzy_user_id, impl_agent)
223
- if fizzy_user_id
224
- LOG.info "[Basecamp:Hooks] Re-assigning card ##{card_number} to #{impl_agent} for fixes" if defined?(LOG)
225
- safe_assign_card(card_number, fizzy_user_id)
226
- end
221
+ # Spawn agent directly safe_assign_card won't work if already assigned
222
+ dispatch_impl_directly(epic, task)
227
223
  else
228
224
  # Wait for remaining gates to respond
229
225
  responded = (task["gate_approvals"]&.size || 0) + (task["changes_requested_by"]&.size || 0)
@@ -246,11 +242,11 @@ module Brainiac
246
242
  if task_reloaded && task_reloaded["status"] == "in_review" && task_reloaded["changes_requested_by"]&.any?
247
243
  LOG.info "[Basecamp:Hooks] Debounce timeout for card ##{card_number} — forcing dispatch" if defined?(LOG)
248
244
  task_reloaded["status"] = "in_flight"
245
+ task_reloaded["dispatched_at"] = Time.now.iso8601
249
246
  task_reloaded.delete("changes_debounce_started")
250
247
  save_epic_state(epic_reloaded)
251
- # Re-assign card to trigger dispatch
252
- fizzy_user_id = Orchestrator.send(:resolve_fizzy_user_id, epic_reloaded["agent"])
253
- safe_assign_card(card_number, fizzy_user_id) if fizzy_user_id
248
+ # Spawn agent directly safe_assign_card won't work if already assigned
249
+ dispatch_impl_directly(epic_reloaded, task_reloaded)
254
250
  end
255
251
  rescue StandardError => e
256
252
  LOG.error "[Basecamp:Hooks] Debounce dispatch failed: #{e.message}" if defined?(LOG)
@@ -728,14 +724,123 @@ module Brainiac
728
724
  end
729
725
  end
730
726
 
731
- def mark_in_review(epic, card_number)
732
- task = epic["tasks"].find { |t| t["fizzy_card"] == card_number.to_i }
733
- return unless task
727
+ # Dispatch the implementation agent directly (spawns via run_agent) to address
728
+ # changes_requested feedback. Used by resume and health-check when the agent is
729
+ # no longer running but the Fizzy card is still assigned (so safe_assign_card
730
+ # would bail out with "already assigned, skipping").
731
+ #
732
+ # @param epic [Hash] Epic state
733
+ # @param task [Hash] Task within the epic
734
+ # @return [void]
735
+ def dispatch_impl_directly(epic, task)
736
+ card_number = task["fizzy_card"]
737
+ agent_name = epic["agent"]
738
+ pr_number = task["pr_number"]
739
+ project_key = task["project"]
740
+ card_key = "changes-requested-#{card_number}"
741
+
742
+ # Guard: don't spawn a duplicate if a session is already running for this task
743
+ session_alive = if defined?(session_active?)
744
+ session_active?(card_key)
745
+ elsif Object.respond_to?(:session_active?, true)
746
+ Object.send(:session_active?, card_key)
747
+ else
748
+ false
749
+ end
750
+
751
+ if session_alive
752
+ LOG.info "[Basecamp:Hooks] Session already active for #{card_key} — skipping dispatch" if defined?(LOG)
753
+ return
754
+ end
734
755
 
735
- task["status"] = "in_review"
736
- task["review_started_at"] = Time.now.iso8601
737
- epic["updated_at"] = Time.now.iso8601
738
- save_epic_state(epic)
756
+ LOG.info "[Basecamp:Hooks] Direct-dispatching #{agent_name} for changes_requested on card ##{card_number}" if defined?(LOG)
757
+
758
+ # Get project config
759
+ projects_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "projects.json")
760
+ projects = File.exist?(projects_file) ? JSON.parse(File.read(projects_file)) : {}
761
+ project_config = projects[project_key]
762
+
763
+ unless project_config
764
+ LOG.error "[Basecamp:Hooks] No project config for '#{project_key}' — cannot direct-dispatch" if defined?(LOG)
765
+ return
766
+ end
767
+
768
+ repo_path = project_config["repo_path"]
769
+ unless repo_path
770
+ LOG.error "[Basecamp:Hooks] No repo_path for project '#{project_key}' — cannot direct-dispatch" if defined?(LOG)
771
+ return
772
+ end
773
+
774
+ # Find the worktree for this card
775
+ work_items_file = File.join(ENV.fetch("BRAINIAC_DIR", File.join(Dir.home, ".brainiac")), "work_items.json")
776
+ worktree_path = repo_path # Default to main repo
777
+ if File.exist?(work_items_file)
778
+ work_items = JSON.parse(File.read(work_items_file))
779
+ work_item = work_items.values.find { |wi| wi.dig("sources", "fizzy", "card_number") == card_number }
780
+ worktree_path = work_item["worktree"] if work_item&.dig("worktree")
781
+ end
782
+
783
+ # Build prompt
784
+ changers = (task["changes_requested_by"] || []).join(", ")
785
+ prompt = <<~PROMPT
786
+ ## Changes Requested — Fizzy Card ##{card_number}
787
+
788
+ Review gates have requested changes on your PR#{pr_number ? " ##{pr_number}" : ""}.
789
+ Reviewers who requested changes: #{changers}
790
+
791
+ Your job:
792
+ 1. Read the review feedback: #{pr_number ? "`gh pr view #{pr_number} --comments`" : "check the Fizzy card comments"}
793
+ 2. Address all requested changes
794
+ 3. Commit and push your fixes
795
+
796
+ After pushing, update the Fizzy card with a brief status comment noting what you fixed.
797
+ PROMPT
798
+
799
+ # Resolve GitHub App token so agent's `gh` commands run as their bot identity
800
+ github_repo = project_config&.dig("github_repo")
801
+ agent_env = github_repo ? ReviewGate.send(:resolve_agent_github_env, agent_name, github_repo) : {}
802
+
803
+ # Spawn the agent directly
804
+ pid = nil
805
+ log_file = nil
806
+
807
+ begin
808
+ pid, log_file = method(:run_agent).call(
809
+ prompt,
810
+ project_config: project_config,
811
+ chdir: worktree_path,
812
+ log_name: "changes-requested-#{card_number}",
813
+ agent_name: agent_name,
814
+ source: :basecamp,
815
+ card_number: card_number,
816
+ env: agent_env
817
+ )
818
+ rescue NameError
819
+ if Object.respond_to?(:run_agent, true)
820
+ pid, log_file = Object.send(:run_agent,
821
+ prompt,
822
+ project_config: project_config,
823
+ chdir: worktree_path,
824
+ log_name: "changes-requested-#{card_number}",
825
+ agent_name: agent_name,
826
+ source: :basecamp,
827
+ card_number: card_number,
828
+ env: agent_env)
829
+ else
830
+ LOG.warn "[Basecamp:Hooks] run_agent not available — direct impl dispatch skipped" if defined?(LOG)
831
+ return
832
+ end
833
+ end
834
+
835
+ # Register session for waybar visibility
836
+ if pid
837
+ if defined?(register_session)
838
+ register_session(card_key, pid, log_file: log_file, agent_name: agent_name)
839
+ elsif Object.respond_to?(:register_session, true)
840
+ Object.send(:register_session, card_key, pid, log_file: log_file, agent_name: agent_name)
841
+ end
842
+ LOG.info "[Basecamp:Hooks] Spawned #{agent_name} (pid #{pid}) for changes_requested on card ##{card_number}" if defined?(LOG)
843
+ end
739
844
  end
740
845
 
741
846
  def auto_merge_and_advance(epic, card_number, ctx)
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Basecamp
6
- VERSION = "0.0.9"
6
+ VERSION = "0.0.11"
7
7
  end
8
8
  end
9
9
  end
@@ -129,25 +129,21 @@ module Brainiac
129
129
  impl_agent = epic["agent"]
130
130
  LOG.info "[Basecamp:HealthCheck] Task ##{card_number} in_flight for #{elapsed.round}s — re-dispatching #{impl_agent}" if defined?(LOG)
131
131
 
132
- # Re-dispatch by re-assigning the card (triggers webhook)
132
+ # Spawn the agent directly safe_assign_card won't work if already assigned
133
133
  task["dispatched_at"] = Time.now.iso8601
134
- fizzy_user_id = Orchestrator.send(:resolve_fizzy_user_id, impl_agent)
135
- if fizzy_user_id
136
- Hooks.send(:safe_assign_card, card_number, fizzy_user_id)
137
- healed_any = true
138
- end
134
+ Hooks.send(:dispatch_impl_directly, epic, task)
135
+ healed_any = true
139
136
  end
140
137
  elsif task["changes_requested_by"]&.any?
141
138
  # No dispatched_at recorded but changes were requested — legacy state.
142
139
  # Always re-dispatch in this case.
143
140
  impl_agent = epic["agent"]
144
141
  LOG.info "[Basecamp:HealthCheck] Task ##{card_number} in_flight with changes_requested but no dispatched_at — re-dispatching #{impl_agent}" if defined?(LOG)
142
+
143
+ # Spawn the agent directly — safe_assign_card won't work if already assigned
145
144
  task["dispatched_at"] = Time.now.iso8601
146
- fizzy_user_id = Orchestrator.send(:resolve_fizzy_user_id, impl_agent)
147
- if fizzy_user_id
148
- Hooks.send(:safe_assign_card, card_number, fizzy_user_id)
149
- healed_any = true
150
- end
145
+ Hooks.send(:dispatch_impl_directly, epic, task)
146
+ healed_any = true
151
147
  end
152
148
 
153
149
  when "in_review"
@@ -329,13 +325,11 @@ module Brainiac
329
325
 
330
326
  # Transition to in_flight so the agent gets the right context
331
327
  task["status"] = "in_flight"
328
+ task["dispatched_at"] = Time.now.iso8601
332
329
  Hooks.send(:save_epic_state, epic)
333
330
 
334
- # Re-assign card to trigger a fresh dispatch via the Fizzy webhook
335
- fizzy_user_id = Orchestrator.send(:resolve_fizzy_user_id, impl_agent)
336
- if fizzy_user_id
337
- Hooks.send(:safe_assign_card, card_number, fizzy_user_id)
338
- end
331
+ # Spawn the agent directly safe_assign_card won't work if already assigned
332
+ Hooks.send(:dispatch_impl_directly, epic, task)
339
333
  else
340
334
  # No changes requested yet — check if gates need (re-)dispatching
341
335
  approvals = task["gate_approvals"]&.size || 0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac-basecamp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.9
4
+ version: 0.0.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis