brainiac-fizzy 0.0.30 → 0.0.32

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: 8e54fe3a10cd6e39be3f52862b1625e2532be592e5480c11f685423910a75b91
4
- data.tar.gz: 77cc0b0ce8a518bde93484bf8513f538f9a26f4412479556d80f83de3ae5c545
3
+ metadata.gz: 7db11f41fae35db895516e80e9de7e4c8c058523bd8bcc11b5b6a64b00462c41
4
+ data.tar.gz: 8cff0f7ca421e7ca5a20344a83f0f89a0a6cce4102f2daa5cc5d4fc5c2f1a2ac
5
5
  SHA512:
6
- metadata.gz: 71c5abb12093956521a3ebdf78566d416d1c3ed33293d6c402a5236462b524042af9a259a3cda7fbf1930c80b93e7be68462952aed0f61393a1eb84b055a851a
7
- data.tar.gz: 1ecc72a231ccef4c9fadbc279fe561628fda23151aad9b8b64afd65cd151d7ec612d95c25daa008958176b0e922b582602689f6c0754a0474643a4216131055c
6
+ metadata.gz: 66bb5070c162be023a3ed046e9748f4fdf1197841aeb47a07c8b660e3759a48aa01773b587edb68bd762f6ab863280c44578f3ba1cc4c654e4fc9ce74946ccac
7
+ data.tar.gz: 9232d4131f5298d4a3e9190c39c360e0a02296ad05b40dacf12be5b64599517af001b94c56bbd9b032fcfafc89b004e6800f6a2a6372868e20e8dc74da5cd996
@@ -47,8 +47,11 @@ def handle_card_assigned(payload, board_key: nil)
47
47
  worktree_path = setup_assigned_worktree(repo_path, branch, card_internal_id, card_number, project_key, assigned_agent, board_key: board_key)
48
48
 
49
49
  initial_cli = detect_cli_provider(tags: tags)
50
- initial_model = detect_model(project_config, tags: tags)
50
+ initial_model, initial_model_explicit = detect_model_explicit(project_config, tags: tags)
51
51
  initial_effort = detect_effort(project_config, tags: tags)
52
+ # Profile ([profile:X]/[p:X]) can be specified inline in the card title.
53
+ # It's a named env bundle (e.g. an alternate kiro-cli account) — see core profiles.rb.
54
+ initial_profile = parse_inline_tags(title.to_s)[:profile]
52
55
 
53
56
  # Persist initial overrides from card tags to the work item
54
57
  resolve_work_item_overrides(
@@ -66,7 +69,8 @@ def handle_card_assigned(payload, board_key: nil)
66
69
  card_number: card_number, card_internal_id: card_internal_id, title: title, tags: tags,
67
70
  branch: branch, worktree_path: worktree_path, project_config: project_config, project_key: project_key,
68
71
  agent_name: assigned_agent, model: initial_model,
69
- effort: initial_effort, cli_provider_override: initial_cli, board_key: board_key
72
+ effort: initial_effort, cli_provider_override: initial_cli, profile: initial_profile, board_key: board_key,
73
+ explicit_model: initial_model_explicit
70
74
  )
71
75
  end
72
76
 
@@ -136,7 +140,8 @@ def setup_assigned_worktree(repo_path, branch, card_internal_id, card_number, pr
136
140
  end
137
141
 
138
142
  def dispatch_assigned_card(card_number:, card_internal_id:, title:, tags:, branch:, worktree_path:,
139
- project_config:, project_key:, agent_name:, model:, effort:, cli_provider_override:, board_key: nil)
143
+ project_config:, project_key:, agent_name:, model:, effort:, cli_provider_override:,
144
+ profile: nil, board_key: nil, explicit_model: nil)
140
145
  card_context = prefetch_card_context(card_number, repo_path: project_config["repo_path"], agent_name: agent_name)
141
146
  planning_info = detect_planning_mode(text: title, tags: tags, card_internal_id: card_internal_id, card_number: card_number)
142
147
 
@@ -180,7 +185,8 @@ def dispatch_assigned_card(card_number:, card_internal_id:, title:, tags:, branc
180
185
  log_name: "assigned-#{card_number}", model: model, effort: effort,
181
186
  agent_name: agent_name, card_number: card_number, source: :fizzy,
182
187
  source_context: { card_number: card_number, board_key: board_key, dispatched_at: Time.now },
183
- cli_provider: cli_provider_override, env: agent_github_env)
188
+ cli_provider: cli_provider_override, profile: profile, env: agent_github_env,
189
+ explicit_model: explicit_model)
184
190
  register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: agent_name)
185
191
 
186
192
  Thread.new { move_card_to_column(card_number, "right_now", project_config: project_config, agent_name: agent_name, board_key: board_key) }
@@ -286,9 +292,7 @@ end
286
292
  def setup_ephemeral_env_for_card(card_number)
287
293
  return { status: "error", reason: "card_number required" } unless card_number
288
294
 
289
- unless defined?(BeltConfig) && defined?(BeltEnvironment)
290
- return { status: "error", reason: "Belt utilities not available" }
291
- end
295
+ return { status: "error", reason: "Belt utilities not available" } unless defined?(BeltConfig) && defined?(BeltEnvironment)
292
296
 
293
297
  card_number = card_number.to_s
294
298
  entry = work_item_entry_for_card(card_number)
@@ -296,9 +300,7 @@ def setup_ephemeral_env_for_card(card_number)
296
300
 
297
301
  worktree = entry["worktree"]
298
302
  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
303
+ return { status: "error", reason: "worktree missing for card ##{card_number} (#{worktree.inspect})" } unless worktree && File.directory?(worktree)
302
304
 
303
305
  env_name = BeltConfig.ephemeral_env_for_card(card_number)
304
306
  already = BeltEnvironment.environment_configured?(worktree: worktree, env_name: env_name)
@@ -390,7 +392,14 @@ def create_and_deploy_ephemeral_env(worktree_path:, env_name:, parent_env:, card
390
392
  LOG.info "[EphemeralEnv] Configured #{env_name} in worktree, deploying in background"
391
393
 
392
394
  Thread.new do
393
- frontend_only = BeltEnvironment.frontend_only_changes?(worktree: worktree_path)
395
+ # First provisioning of a fresh env must be a full deploy — it stands up
396
+ # S3/CloudFront/backend. The frontend-only shortcut is only safe once the
397
+ # env is actually live (probe the derived public URL). Otherwise `belt
398
+ # deploy frontend` runs against a bucket that doesn't exist yet and fails
399
+ # with "Could not determine S3 bucket."
400
+ frontend_only =
401
+ ephemeral_env_live?(worktree_path, env_name) &&
402
+ BeltEnvironment.frontend_only_changes?(worktree: worktree_path)
394
403
  BeltEnvironment.deploy(worktree: worktree_path, env_name: env_name, frontend_only: frontend_only)
395
404
  rescue StandardError => e
396
405
  LOG.error "[EphemeralEnv] Error deploying '#{env_name}': #{e.message}"
@@ -18,7 +18,7 @@ CommentContext = Struct.new(
18
18
  :project_config, :project_key, :card_number, :worktree,
19
19
  :model, :effort, :deploy_intent, :cli_provider_override,
20
20
  :comment_vars, :card_tags, :worktree_override, :fresh,
21
- :board_key,
21
+ :board_key, :profile, :explicit_model,
22
22
  keyword_init: true
23
23
  )
24
24
 
@@ -110,7 +110,7 @@ def build_comment_context(eventable:, plain_text:, tags:, card_internal_id:, car
110
110
  clean_text = tags[:clean_text]
111
111
 
112
112
  inline_cli = detect_cli_provider(text: plain_text, tags: card_tags)
113
- inline_model = detect_model(project_config, text: plain_text)
113
+ inline_model, inline_model_explicit = detect_model_explicit(project_config, text: plain_text)
114
114
  inline_effort = detect_effort(project_config, tags: card_tags, text: plain_text)
115
115
 
116
116
  # Resolve overrides against the work item — merges stored overrides with inline tags
@@ -141,11 +141,13 @@ def build_comment_context(eventable:, plain_text:, tags:, card_internal_id:, car
141
141
  board_key: board_key,
142
142
  model: effective_model,
143
143
  effort: effective_effort,
144
+ explicit_model: inline_model_explicit,
144
145
  deploy_intent: deploy_intent,
145
146
  cli_provider_override: effective_cli,
146
147
  card_tags: card_tags,
147
148
  worktree_override: resolve_worktree_override(tags, project_config),
148
149
  fresh: tags[:fresh],
150
+ profile: tags[:profile],
149
151
  comment_vars: {
150
152
  "COMMENT_CREATOR" => creator_name || "Unknown",
151
153
  "COMMENT_ID" => comment_id.to_s,
@@ -411,7 +413,8 @@ def dispatch_cross_agent_review(ctx, card_key:, card_number:, card_assigned_agen
411
413
  model: ctx.model, effort: ctx.effort, agent_name: ctx.agent_name,
412
414
  card_number: card_number, comment_id: ctx.comment_id,
413
415
  source: :fizzy, source_context: { card_number: card_number, dispatched_at: Time.now },
414
- cli_provider: ctx.cli_provider_override)
416
+ cli_provider: ctx.cli_provider_override, profile: ctx.profile,
417
+ explicit_model: ctx.explicit_model)
415
418
  return [200, { status: "dispatch_failed", agent: ctx.agent_name, card: card_number }.to_json] unless pid
416
419
 
417
420
  register_session(card_key, pid, log_file: log_file, supersede_key: card_key, agent_name: ctx.agent_name)
@@ -525,6 +528,7 @@ def dispatch_new_mention(ctx, card_key:, card_number:, card_title:, branch:, wor
525
528
  model: ctx.model, effort: ctx.effort, agent_name: ctx.agent_name,
526
529
  card_number: card_number, comment_id: ctx.comment_id,
527
530
  source: :fizzy, cli_provider: ctx.cli_provider_override,
531
+ profile: ctx.profile, explicit_model: ctx.explicit_model,
528
532
  source_context: { card_number: card_number, dispatched_at: Time.now })
529
533
  return [200, { status: "dispatch_failed", agent: ctx.agent_name, card: card_number }.to_json] unless pid
530
534
 
@@ -563,6 +567,7 @@ def dispatch_followup_comment(ctx, card_key:, card_number:, work_dir:)
563
567
  model: ctx.model, effort: effort, agent_name: ctx.agent_name,
564
568
  card_number: card_number, comment_id: ctx.comment_id,
565
569
  source: :fizzy, cli_provider: ctx.cli_provider_override, resume: should_resume,
570
+ profile: ctx.profile, explicit_model: ctx.explicit_model,
566
571
  source_context: {
567
572
  card_number: card_number, card_internal_id: ctx.card_internal_id,
568
573
  deploy_intent: ctx.deploy_intent, board_key: ctx.board_key, dispatched_at: Time.now
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "shellwords"
4
+ require "net/http"
5
+ require "uri"
4
6
 
5
7
  # Deployment environment tracking.
6
8
  # Tracks which dev environments have active card deploys and which are available.
@@ -383,3 +385,40 @@ def parse_tfvars(contents)
383
385
  end
384
386
  vars
385
387
  end
388
+
389
+ # Is a Belt env's frontend actually live? Derives the public URL from the
390
+ # worktree's infrastructure/<env>/terraform.tfvars and does a HEAD request.
391
+ #
392
+ # This is the honest signal for "already provisioned": a live site means the
393
+ # S3 bucket / CloudFront / backend exist, so the frontend-only deploy shortcut
394
+ # is safe. Anything else (never applied, DNS/CloudFront still propagating,
395
+ # connection refused) reads as not-live and callers should do a full deploy.
396
+ #
397
+ # A false negative during CloudFront/DNS/ACM propagation just triggers a
398
+ # (redundant but harmless) full deploy — never a skipped provisioning step.
399
+ def ephemeral_env_live?(worktree_path, env_name, timeout: 5)
400
+ tfvars = File.join(worktree_path, "infrastructure", env_name, "terraform.tfvars")
401
+ return false unless File.exist?(tfvars)
402
+
403
+ url = ephemeral_url_from_tfvars(tfvars)
404
+ return false unless url
405
+
406
+ frontend_url_live?(url, timeout: timeout)
407
+ end
408
+
409
+ # HEAD the URL; treat any 2xx/3xx as live. Any error (refused, timeout,
410
+ # NXDOMAIN, TLS failure) means not live. HEAD keeps us from pulling the bundle.
411
+ def frontend_url_live?(url, timeout: 5)
412
+ uri = URI.parse(url)
413
+ http = Net::HTTP.new(uri.host, uri.port)
414
+ http.use_ssl = (uri.scheme == "https")
415
+ http.open_timeout = timeout
416
+ http.read_timeout = timeout
417
+
418
+ response = http.request_head(uri.request_uri.empty? ? "/" : uri.request_uri)
419
+ code = response.code.to_i
420
+ code >= 200 && code < 400
421
+ rescue StandardError => e
422
+ LOG.info "[Fizzy:Deploy] Frontend probe for #{url} not live: #{e.class}: #{e.message}" if defined?(LOG)
423
+ false
424
+ end
@@ -3,7 +3,7 @@
3
3
  module Brainiac
4
4
  module Plugins
5
5
  module Fizzy
6
- VERSION = "0.0.30"
6
+ VERSION = "0.0.32"
7
7
  end
8
8
  end
9
9
  end
@@ -103,7 +103,8 @@ module Brainiac
103
103
  end
104
104
 
105
105
  # Card index API (duplicate detection)
106
- app.get "/api/card-index" do content_type :json
106
+ app.get "/api/card-index" do
107
+ content_type :json
107
108
  halt 404, { error: "Card index not available" }.to_json unless defined?(CARD_INDEX)
108
109
 
109
110
  query = params["q"]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: brainiac-fizzy
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.30
4
+ version: 0.0.32
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Davis