brainiac-fizzy 0.0.28 → 0.0.30
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 +21 -0
- data/lib/brainiac/plugins/fizzy/cli.rb +66 -1
- data/lib/brainiac/plugins/fizzy/config.rb +20 -0
- data/lib/brainiac/plugins/fizzy/handlers/assignment.rb +85 -9
- data/lib/brainiac/plugins/fizzy/hooks.rb +70 -30
- data/lib/brainiac/plugins/fizzy/version.rb +1 -1
- data/lib/brainiac/plugins/fizzy.rb +16 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8e54fe3a10cd6e39be3f52862b1625e2532be592e5480c11f685423910a75b91
|
|
4
|
+
data.tar.gz: 77cc0b0ce8a518bde93484bf8513f538f9a26f4412479556d80f83de3ae5c545
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 71c5abb12093956521a3ebdf78566d416d1c3ed33293d6c402a5236462b524042af9a259a3cda7fbf1930c80b93e7be68462952aed0f61393a1eb84b055a851a
|
|
7
|
+
data.tar.gz: 1ecc72a231ccef4c9fadbc279fe561628fda23151aad9b8b64afd65cd151d7ec612d95c25daa008958176b0e922b582602689f6c0754a0474643a4216131055c
|
data/README.md
CHANGED
|
@@ -37,6 +37,8 @@ Fizzy configuration lives in `~/.brainiac/fizzy.json` (same as before):
|
|
|
37
37
|
{ "id": "user-id-1", "name": "Andy", "human": true },
|
|
38
38
|
{ "id": "agent-id-1", "name": "Galen", "human": false }
|
|
39
39
|
],
|
|
40
|
+
"uat_agent": false,
|
|
41
|
+
"uat_agent_tag": "uat",
|
|
40
42
|
"boards": {
|
|
41
43
|
"development": {
|
|
42
44
|
"board_id": "your-board-id",
|
|
@@ -51,6 +53,25 @@ Fizzy configuration lives in `~/.brainiac/fizzy.json` (same as before):
|
|
|
51
53
|
}
|
|
52
54
|
```
|
|
53
55
|
|
|
56
|
+
### UAT agent (`uat_agent`)
|
|
57
|
+
|
|
58
|
+
When a PR is merged and its card moves to the UAT column, Brainiac can dispatch
|
|
59
|
+
an LLM agent to write manual testing steps as a card comment. This spends tokens
|
|
60
|
+
on every merge, so it's **disabled by default**. Set `"uat_agent": true` at the
|
|
61
|
+
top level of `fizzy.json` to opt back in globally. The card still moves to UAT
|
|
62
|
+
and gets the PR-merged comment either way — only the agent dispatch is gated.
|
|
63
|
+
|
|
64
|
+
**Per-card opt-in:** Even with `uat_agent` off globally, you can trigger the UAT
|
|
65
|
+
agent for a single card by adding the `uat` tag to it. When the card's PR merges,
|
|
66
|
+
Brainiac fetches the card's live tags and dispatches the UAT agent if the tag is
|
|
67
|
+
present. Customize the tag name with `"uat_agent_tag": "your-tag"`, or set it to
|
|
68
|
+
`false`/`null` to disable per-card overrides entirely. This gives you three
|
|
69
|
+
states:
|
|
70
|
+
|
|
71
|
+
- `uat_agent: true` — dispatch on every merge
|
|
72
|
+
- `uat_agent: false` (default) + `uat` tag on a card — dispatch for that card only
|
|
73
|
+
- `uat_agent: false` + no tag — never dispatch (pure bookkeeping, zero tokens)
|
|
74
|
+
|
|
54
75
|
## What This Plugin Handles
|
|
55
76
|
|
|
56
77
|
| Event | Action |
|
|
@@ -27,6 +27,8 @@ module Brainiac
|
|
|
27
27
|
cmd_setup
|
|
28
28
|
when "board"
|
|
29
29
|
cmd_board(args)
|
|
30
|
+
when "env"
|
|
31
|
+
cmd_env(args)
|
|
30
32
|
else
|
|
31
33
|
print_help
|
|
32
34
|
end
|
|
@@ -126,6 +128,66 @@ module Brainiac
|
|
|
126
128
|
end
|
|
127
129
|
end
|
|
128
130
|
|
|
131
|
+
# --- ephemeral env management ---
|
|
132
|
+
|
|
133
|
+
# `brainiac fizzy env setup <card_number>`
|
|
134
|
+
#
|
|
135
|
+
# Manually set up (or refresh tracking for) an ephemeral Belt env for a
|
|
136
|
+
# card that was already worked — no agent, no tokens. Once tracked, PR
|
|
137
|
+
# updates auto-redeploy it via brainiac-github.
|
|
138
|
+
def cmd_env(args)
|
|
139
|
+
sub = args.shift
|
|
140
|
+
case sub
|
|
141
|
+
when "setup", "add"
|
|
142
|
+
env_setup(args)
|
|
143
|
+
else
|
|
144
|
+
puts "Usage: brainiac fizzy env setup <card_number>"
|
|
145
|
+
puts ""
|
|
146
|
+
puts "Commands:"
|
|
147
|
+
puts " setup <card_number> Configure + deploy an ephemeral Belt env for a"
|
|
148
|
+
puts " card that was already worked (no agent dispatch)."
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def env_setup(args)
|
|
153
|
+
card_number = args.shift
|
|
154
|
+
if card_number.nil? || card_number.strip.empty?
|
|
155
|
+
puts "Usage: brainiac fizzy env setup <card_number>"
|
|
156
|
+
return
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
require "net/http"
|
|
160
|
+
require "uri"
|
|
161
|
+
|
|
162
|
+
server_url = detect_server_url
|
|
163
|
+
uri = URI("#{server_url}/api/fizzy/ephemeral-env/#{card_number}")
|
|
164
|
+
|
|
165
|
+
puts "Setting up ephemeral env for card ##{card_number} (no agent)..."
|
|
166
|
+
begin
|
|
167
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
168
|
+
http.read_timeout = 600 # belt deploy can be slow
|
|
169
|
+
response = http.post(uri.path, "")
|
|
170
|
+
data = begin
|
|
171
|
+
JSON.parse(response.body)
|
|
172
|
+
rescue StandardError
|
|
173
|
+
{}
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
if response.is_a?(Net::HTTPSuccess) && data["status"] == "ok"
|
|
177
|
+
puts "✓ Ephemeral env '#{data["env"]}' ready#{" (was already configured)" if data["already_configured"]}"
|
|
178
|
+
puts " Worktree: #{data["worktree"]}" if data["worktree"]
|
|
179
|
+
puts " URL: #{data["url"]}" if data["url"]
|
|
180
|
+
puts ""
|
|
181
|
+
puts "Tracked in ephemeral_envs.json — PR updates will auto-redeploy it."
|
|
182
|
+
else
|
|
183
|
+
puts "✗ Failed: #{data["reason"] || response.message}"
|
|
184
|
+
end
|
|
185
|
+
rescue StandardError => e
|
|
186
|
+
puts "Could not reach server at #{server_url}: #{e.message}"
|
|
187
|
+
puts "Is the server running? Check with: brainiac status"
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
129
191
|
# --- board setup (split into phases) ---
|
|
130
192
|
|
|
131
193
|
def board_setup
|
|
@@ -629,6 +691,7 @@ module Brainiac
|
|
|
629
691
|
board list List configured boards and their columns
|
|
630
692
|
board assign <project> <board_key> Assign a project to a board
|
|
631
693
|
board columns <board_key> Refresh columns for a board from Fizzy
|
|
694
|
+
env setup <card_number> Set up an ephemeral Belt env for an already-worked card (no agent)
|
|
632
695
|
|
|
633
696
|
Fizzy handles card assignment, comments, @mentions, cross-agent reviews,
|
|
634
697
|
duplicate detection, and planning mode via webhooks.
|
|
@@ -662,11 +725,13 @@ module Brainiac
|
|
|
662
725
|
# When called with args (the words typed so far after the plugin name),
|
|
663
726
|
# returns context-sensitive completions for nested subcommands.
|
|
664
727
|
def self.completions(args = [])
|
|
665
|
-
return %w[board config setup status] if args.empty?
|
|
728
|
+
return %w[board config env setup status] if args.empty?
|
|
666
729
|
|
|
667
730
|
case args[0]
|
|
668
731
|
when "board"
|
|
669
732
|
board_completions(args[1..])
|
|
733
|
+
when "env"
|
|
734
|
+
args.length <= 1 ? %w[setup add] : []
|
|
670
735
|
else
|
|
671
736
|
[]
|
|
672
737
|
end
|
|
@@ -82,6 +82,26 @@ module Brainiac
|
|
|
82
82
|
nil
|
|
83
83
|
end
|
|
84
84
|
|
|
85
|
+
# Whether to dispatch an LLM agent to write UAT testing steps when a
|
|
86
|
+
# PR is merged and the card moves to UAT. Disabled by default to save
|
|
87
|
+
# tokens — set "uat_agent": true in ~/.brainiac/fizzy.json to enable
|
|
88
|
+
# globally. Individual cards can still opt in via the UAT tag even when
|
|
89
|
+
# this is false (see #uat_agent_tag).
|
|
90
|
+
def uat_agent_enabled?
|
|
91
|
+
@config.fetch("uat_agent", false) == true
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# The card tag that opts a single card into UAT agent dispatch even
|
|
95
|
+
# when uat_agent is globally disabled. Defaults to "uat". Set
|
|
96
|
+
# "uat_agent_tag": "some-tag" in fizzy.json to customize, or false/null
|
|
97
|
+
# to disable per-card overrides entirely.
|
|
98
|
+
def uat_agent_tag
|
|
99
|
+
value = @config.fetch("uat_agent_tag", "uat")
|
|
100
|
+
return nil if value == false || value.nil? || value.to_s.strip.empty?
|
|
101
|
+
|
|
102
|
+
value.to_s
|
|
103
|
+
end
|
|
104
|
+
|
|
85
105
|
def authorized?(payload)
|
|
86
106
|
creator_id = payload.dig("creator", "id")
|
|
87
107
|
@authorized_user_ids.include?(creator_id)
|
|
@@ -194,8 +194,13 @@ end
|
|
|
194
194
|
#
|
|
195
195
|
# `belt g environment` is synchronous so the worktree is configured before the
|
|
196
196
|
# agent starts. The actual `belt deploy` runs in the background.
|
|
197
|
+
#
|
|
198
|
+
# When `force: true`, the `deploy` tag requirement is bypassed — the caller is
|
|
199
|
+
# explicitly requesting env setup (e.g. the `brainiac fizzy env setup` CLI or
|
|
200
|
+
# the `/api/fizzy/ephemeral-env/:card` endpoint), so no tag is needed.
|
|
197
201
|
def maybe_create_ephemeral_belt_env(worktree_path:, card_number:, project_key:, tags: nil,
|
|
198
|
-
repo_path: nil, agent_name: nil, fetch_live_tags: false
|
|
202
|
+
repo_path: nil, agent_name: nil, fetch_live_tags: false,
|
|
203
|
+
force: false)
|
|
199
204
|
unless defined?(BeltConfig) && defined?(BeltEnvironment)
|
|
200
205
|
LOG.debug "[EphemeralEnv] Belt utilities not available — skipping"
|
|
201
206
|
return
|
|
@@ -216,14 +221,16 @@ def maybe_create_ephemeral_belt_env(worktree_path:, card_number:, project_key:,
|
|
|
216
221
|
return
|
|
217
222
|
end
|
|
218
223
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
224
|
+
unless force
|
|
225
|
+
resolved_tags = resolve_ephemeral_env_tags(
|
|
226
|
+
tags, card_number: card_number, repo_path: repo_path || worktree_path,
|
|
227
|
+
agent_name: agent_name, fetch_live_tags: fetch_live_tags
|
|
228
|
+
)
|
|
229
|
+
tag_list = tag_names(resolved_tags)
|
|
230
|
+
unless tag_list.include?("deploy")
|
|
231
|
+
LOG.debug "[EphemeralEnv] Card ##{card_number} tags=#{tag_list.inspect} — no deploy tag, skipping"
|
|
232
|
+
return
|
|
233
|
+
end
|
|
227
234
|
end
|
|
228
235
|
|
|
229
236
|
parent_env = BeltConfig.parent_env_for(project_key)
|
|
@@ -266,6 +273,75 @@ def ensure_ephemeral_env_for_comment(ctx, card_number, worktree)
|
|
|
266
273
|
)
|
|
267
274
|
end
|
|
268
275
|
|
|
276
|
+
# Manually set up an ephemeral Belt env for a card that was already worked,
|
|
277
|
+
# without dispatching an agent. Resolves the card's existing worktree from the
|
|
278
|
+
# work item map, configures + deploys the env synchronously, and tracks it in
|
|
279
|
+
# ephemeral_envs.json so PR-update auto-redeploys (brainiac-github) work.
|
|
280
|
+
#
|
|
281
|
+
# Bypasses the `deploy` tag requirement (force: true) — the caller is asking
|
|
282
|
+
# for this explicitly. Returns a result hash suitable for a JSON API response.
|
|
283
|
+
#
|
|
284
|
+
# @param card_number [Integer, String] Fizzy card number
|
|
285
|
+
# @return [Hash] { status:, env:, url:, worktree:, reason: }
|
|
286
|
+
def setup_ephemeral_env_for_card(card_number)
|
|
287
|
+
return { status: "error", reason: "card_number required" } unless card_number
|
|
288
|
+
|
|
289
|
+
unless defined?(BeltConfig) && defined?(BeltEnvironment)
|
|
290
|
+
return { status: "error", reason: "Belt utilities not available" }
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
card_number = card_number.to_s
|
|
294
|
+
entry = work_item_entry_for_card(card_number)
|
|
295
|
+
return { status: "error", reason: "no work item found for card ##{card_number}" } unless entry
|
|
296
|
+
|
|
297
|
+
worktree = entry["worktree"]
|
|
298
|
+
project_key = entry["project"]
|
|
299
|
+
unless worktree && File.directory?(worktree)
|
|
300
|
+
return { status: "error", reason: "worktree missing for card ##{card_number} (#{worktree.inspect})" }
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
env_name = BeltConfig.ephemeral_env_for_card(card_number)
|
|
304
|
+
already = BeltEnvironment.environment_configured?(worktree: worktree, env_name: env_name)
|
|
305
|
+
|
|
306
|
+
maybe_create_ephemeral_belt_env(
|
|
307
|
+
worktree_path: worktree, card_number: card_number,
|
|
308
|
+
project_key: project_key, force: true
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
# After setup, resolve the public URL the same way deployment lookups do.
|
|
312
|
+
url = defined?(deployment_url_for_card) ? (deployment_url_for_card(card_number) || {})[:url] : nil
|
|
313
|
+
|
|
314
|
+
{
|
|
315
|
+
status: "ok",
|
|
316
|
+
env: env_name,
|
|
317
|
+
url: url,
|
|
318
|
+
worktree: worktree,
|
|
319
|
+
already_configured: already
|
|
320
|
+
}
|
|
321
|
+
rescue StandardError => e
|
|
322
|
+
LOG.error "[EphemeralEnv] Manual setup failed for card ##{card_number}: #{e.message}"
|
|
323
|
+
{ status: "error", reason: e.message }
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# Resolve the work item entry for a Fizzy card number from work_items.json.
|
|
327
|
+
# Returns a flattened hash with worktree/branch/project or nil.
|
|
328
|
+
def work_item_entry_for_card(card_number)
|
|
329
|
+
map = load_work_item_map
|
|
330
|
+
match = map.values.find do |entry|
|
|
331
|
+
entry.dig("sources", "fizzy", "card_number").to_s == card_number.to_s
|
|
332
|
+
end
|
|
333
|
+
return nil unless match
|
|
334
|
+
|
|
335
|
+
{
|
|
336
|
+
"worktree" => match["worktree"],
|
|
337
|
+
"branch" => match["branch"],
|
|
338
|
+
"project" => match["project"]
|
|
339
|
+
}
|
|
340
|
+
rescue StandardError => e
|
|
341
|
+
LOG.error "[EphemeralEnv] Could not load work item for card ##{card_number}: #{e.message}" if defined?(LOG)
|
|
342
|
+
nil
|
|
343
|
+
end
|
|
344
|
+
|
|
269
345
|
def belt_app_for_ephemeral?(worktree_path)
|
|
270
346
|
return BeltEnvironment.belt_app?(worktree_path) if defined?(BeltEnvironment) && BeltEnvironment.respond_to?(:belt_app?)
|
|
271
347
|
|
|
@@ -134,36 +134,7 @@ module Brainiac
|
|
|
134
134
|
# PR merged — post comment on Fizzy card, move to UAT, dispatch UAT agent
|
|
135
135
|
def register_pr_merged
|
|
136
136
|
Brainiac.on(:pr_merged) do |ctx|
|
|
137
|
-
|
|
138
|
-
next unless card_number
|
|
139
|
-
|
|
140
|
-
card_info = ctx[:card_info]
|
|
141
|
-
card_agent = card_info["agent"]
|
|
142
|
-
env = Helpers.fizzy_env_for(card_agent)
|
|
143
|
-
repo_path = ctx[:repo_path]
|
|
144
|
-
board_key = card_info.dig("sources", "fizzy", "board_key") || Config.board_key_for_project(ctx[:project_config])
|
|
145
|
-
|
|
146
|
-
# Post PR link comment
|
|
147
|
-
pr_url = ctx[:pr_url]
|
|
148
|
-
pr_title = ctx[:pr_title]
|
|
149
|
-
branch = ctx[:branch]
|
|
150
|
-
comment_body = "<p>PR merged into main: <a href=\"#{pr_url}\">#{pr_title}</a></p>" \
|
|
151
|
-
"<p>Branch: <code>#{branch}</code></p>"
|
|
152
|
-
run_cmd("fizzy", "comment", "create", "--card", card_number.to_s, "--body", comment_body,
|
|
153
|
-
chdir: repo_path, env: env)
|
|
154
|
-
|
|
155
|
-
# Move card to UAT
|
|
156
|
-
Helpers.move_card_to_column(card_number, "uat",
|
|
157
|
-
project_config: ctx[:project_config],
|
|
158
|
-
agent_name: card_agent,
|
|
159
|
-
board_key: board_key)
|
|
160
|
-
record_self_move(card_number)
|
|
161
|
-
|
|
162
|
-
# Clear deployment tracking
|
|
163
|
-
clear_deployment_for_card(card_number) if respond_to?(:clear_deployment_for_card)
|
|
164
|
-
|
|
165
|
-
# Dispatch UAT agent
|
|
166
|
-
dispatch_fizzy_uat_agent(ctx)
|
|
137
|
+
handle_pr_merged(ctx)
|
|
167
138
|
rescue StandardError => e
|
|
168
139
|
LOG.error "[Fizzy] Error in pr_merged hook: #{e.message}" if defined?(LOG)
|
|
169
140
|
end
|
|
@@ -292,6 +263,75 @@ module Brainiac
|
|
|
292
263
|
|
|
293
264
|
# --- Private helpers ---
|
|
294
265
|
|
|
266
|
+
# Body of the :pr_merged hook. Posts the PR link comment, moves the
|
|
267
|
+
# card to UAT, clears deployment tracking, and dispatches the UAT
|
|
268
|
+
# agent when enabled. Extracted from the hook block to keep the
|
|
269
|
+
# registration method within complexity limits.
|
|
270
|
+
def handle_pr_merged(ctx)
|
|
271
|
+
card_number = ctx[:card_number]
|
|
272
|
+
return unless card_number
|
|
273
|
+
|
|
274
|
+
card_info = ctx[:card_info]
|
|
275
|
+
card_agent = card_info["agent"]
|
|
276
|
+
env = Helpers.fizzy_env_for(card_agent)
|
|
277
|
+
repo_path = ctx[:repo_path]
|
|
278
|
+
board_key = card_info.dig("sources", "fizzy", "board_key") || Config.board_key_for_project(ctx[:project_config])
|
|
279
|
+
|
|
280
|
+
# Post PR link comment
|
|
281
|
+
comment_body = "<p>PR merged into main: <a href=\"#{ctx[:pr_url]}\">#{ctx[:pr_title]}</a></p>" \
|
|
282
|
+
"<p>Branch: <code>#{ctx[:branch]}</code></p>"
|
|
283
|
+
run_cmd("fizzy", "comment", "create", "--card", card_number.to_s, "--body", comment_body,
|
|
284
|
+
chdir: repo_path, env: env)
|
|
285
|
+
|
|
286
|
+
# Move card to UAT
|
|
287
|
+
Helpers.move_card_to_column(card_number, "uat",
|
|
288
|
+
project_config: ctx[:project_config],
|
|
289
|
+
agent_name: card_agent,
|
|
290
|
+
board_key: board_key)
|
|
291
|
+
record_self_move(card_number)
|
|
292
|
+
|
|
293
|
+
# Clear deployment tracking
|
|
294
|
+
clear_deployment_for_card(card_number) if respond_to?(:clear_deployment_for_card)
|
|
295
|
+
|
|
296
|
+
# Dispatch UAT agent — off by default to save tokens. Globally
|
|
297
|
+
# enabled via "uat_agent": true, or per-card via the UAT tag
|
|
298
|
+
# (default "uat") even when the global flag is off.
|
|
299
|
+
if uat_agent_dispatch?(card_number, repo_path: repo_path, env: env)
|
|
300
|
+
dispatch_fizzy_uat_agent(ctx)
|
|
301
|
+
else
|
|
302
|
+
log_uat_dispatch_skipped(card_number)
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Log that UAT dispatch was skipped for a card (global flag off, no tag).
|
|
307
|
+
def log_uat_dispatch_skipped(card_number)
|
|
308
|
+
return unless defined?(LOG)
|
|
309
|
+
|
|
310
|
+
LOG.info "[Fizzy] UAT agent disabled (set \"uat_agent\": true in fizzy.json, or add the " \
|
|
311
|
+
"\"#{Config.uat_agent_tag}\" tag to this card) — skipping dispatch for card ##{card_number}"
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Decide whether to dispatch the UAT agent for a merged card.
|
|
315
|
+
# True when globally enabled, OR when the card carries the configured
|
|
316
|
+
# UAT tag (per-card opt-in). Card tags aren't in the pr_merged ctx, so
|
|
317
|
+
# we fetch them live — only when the global flag is off, to avoid an
|
|
318
|
+
# extra API call when it's already enabled.
|
|
319
|
+
def uat_agent_dispatch?(card_number, repo_path:, env: nil)
|
|
320
|
+
return true if Config.uat_agent_enabled?
|
|
321
|
+
|
|
322
|
+
tag = Config.uat_agent_tag
|
|
323
|
+
return false unless tag && card_number && repo_path
|
|
324
|
+
|
|
325
|
+
tags = Helpers.fetch_card_tags(card_number, repo_path: repo_path, env: env)
|
|
326
|
+
return false unless tags
|
|
327
|
+
|
|
328
|
+
has_tag = Helpers.card_has_tag?(tags, tag)
|
|
329
|
+
if has_tag && defined?(LOG)
|
|
330
|
+
LOG.info "[Fizzy] Card ##{card_number} has \"#{tag}\" tag — dispatching UAT agent despite global flag being off"
|
|
331
|
+
end
|
|
332
|
+
has_tag
|
|
333
|
+
end
|
|
334
|
+
|
|
295
335
|
def dispatch_fizzy_uat_agent(ctx)
|
|
296
336
|
card_number = ctx[:card_number]
|
|
297
337
|
card_info = ctx[:card_info]
|
|
@@ -103,8 +103,7 @@ module Brainiac
|
|
|
103
103
|
end
|
|
104
104
|
|
|
105
105
|
# Card index API (duplicate detection)
|
|
106
|
-
app.get "/api/card-index" do
|
|
107
|
-
content_type :json
|
|
106
|
+
app.get "/api/card-index" do content_type :json
|
|
108
107
|
halt 404, { error: "Card index not available" }.to_json unless defined?(CARD_INDEX)
|
|
109
108
|
|
|
110
109
|
query = params["q"]
|
|
@@ -116,6 +115,21 @@ module Brainiac
|
|
|
116
115
|
end
|
|
117
116
|
end
|
|
118
117
|
|
|
118
|
+
# Manually set up an ephemeral Belt env for a card that was already
|
|
119
|
+
# worked — no agent dispatch, no tokens. Configures + deploys the env
|
|
120
|
+
# in the card's existing worktree and tracks it so PR-update
|
|
121
|
+
# auto-redeploys keep it fresh.
|
|
122
|
+
app.post "/api/fizzy/ephemeral-env/:card" do
|
|
123
|
+
content_type :json
|
|
124
|
+
card_number = params["card"]
|
|
125
|
+
result = setup_ephemeral_env_for_card(card_number)
|
|
126
|
+
if result[:status] == "ok"
|
|
127
|
+
result.to_json
|
|
128
|
+
else
|
|
129
|
+
halt 422, result.to_json
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
119
133
|
# Deployment API routes (if deployments are configured)
|
|
120
134
|
return unless defined?(DEPLOYMENTS_CONFIG)
|
|
121
135
|
|