deployangel 0.1.12 → 0.1.14

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: c3835d3c334a91d9ad7a75a3247ede4797a27e207b123f684bc1563fffd33cb0
4
- data.tar.gz: c578f30dc3f828dabce95b7c4e48682b1248d306b0982d50483f17d3c3cf57c8
3
+ metadata.gz: ff074e43a43cb39394f9c21d99c1c004c55f68ac7c5a54467acc49b4f0ce1753
4
+ data.tar.gz: 57f860c0f680e70f46f12756f3f638307cb13ec6aa4131e5ebaac0e1cfe16494
5
5
  SHA512:
6
- metadata.gz: 5a8e5b23a9d1a06732add016e3d31e813d08a10afc13016408c26944112098948476cfe51d7c39c60710c9f411cf6d0af01d145a4bd0a125ebcc90728d06b695
7
- data.tar.gz: 7dea64425c2cd98cda899277a2561d67dde2d0871e38156c9efad654769fdcb6198ab94089d56fad0d72c7e15e48300d5b40d5018851491c6378043b435bb470
6
+ metadata.gz: 10d7c45fac648fedae41d360d845b3f874be6026bd331e77d063eaf65a036b4f0ba616a0c74df6e18ede073df87789fee71ac84910902d8456306c2ccb3bb0af
7
+ data.tar.gz: b68c68f332fe45d48be53f3d47e0b5c8bd41b012adf9deb6fc654e4ba9489ed20c3b4b790f6a860723f962ea8774b11082f0a1ce4f0c2f74bb9379b55d0937bf
data/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.14 (2026-10-06)
4
+
5
+ - `deployangel plan` separates what clearance waits on ("Needed to clear")
6
+ from changed and rarely used paths that are only worth running, and
7
+ `deployangel verify` and the GitHub Actions job summary list only what's
8
+ needed. The MCP tool `get_exercise_plan` describes the server's new
9
+ `needed` flag on each item.
10
+
11
+ ## 0.1.13 (2026-10-06)
12
+
13
+ - The MCP tools `list_deployments` and `list_late_regressions` return their
14
+ lists inside an object (`{"deployments": [...]}`, `{"late_regressions":
15
+ [...]}`). MCP requires a tool's structured content to be an object, so
16
+ clients such as Claude Code rejected the bare lists before the agent saw
17
+ them.
18
+
3
19
  ## 0.1.12 (2026-10-06)
4
20
 
5
21
  - `deployangel plan` and the MCP tool `get_exercise_plan` say what stands
data/README.md CHANGED
@@ -387,9 +387,20 @@ choose with `--format=text|json`. Exit codes:
387
387
  ### MCP server
388
388
 
389
389
  ```bash
390
- claude mcp add deployangel -- bundle exec deployangel mcp
390
+ claude mcp add deployangel -- bundle exec deployangel mcp # Claude Code
391
391
  ```
392
392
 
393
+ For Codex, in `~/.codex/config.toml`:
394
+
395
+ ```toml
396
+ [mcp_servers.deployangel]
397
+ command = "bundle"
398
+ args = ["exec", "deployangel", "mcp"]
399
+ ```
400
+
401
+ Any other MCP client (Cursor, VS Code, Zed, ...) runs the same command. The
402
+ server needs `DEPLOYANGEL_API_TOKEN` in the environment it starts in.
403
+
393
404
  Tools: `get_verification`, `wait_for_verification` (up to 5 minutes per call),
394
405
  `get_exercise_plan`, `list_deployments`, `get_exception`,
395
406
  `list_late_regressions`, and `register_deployment` when the token allows it.
@@ -402,7 +413,8 @@ A quiet app can take hours to clear a release. `deployangel plan` (or the
402
413
  clearance, in the numbers of the rule it's judged by, and what to exercise
403
414
  against production so it clears sooner: normally active routes short of
404
415
  their runs, routes this release changed that haven't run, and critical
405
- flows. Requests to them count like any traffic. Routes that change data
416
+ flows. It separates what clearance waits on from changed and rarely used
417
+ paths that are only worth running. Requests to them count like any traffic. Routes that change data
406
418
  are marked; use a test account for them, or ask first. Report what you
407
419
  ran with the `deployangel check` command the plan gives you. A passing
408
420
  check labels what it covered; only the requests themselves count as
@@ -122,18 +122,37 @@ module DeployAngel
122
122
  shortfall.each { |line| lines << " #{line}" }
123
123
  end
124
124
  items = Array(plan["items"])
125
- if items.any?
126
- lines << (EXERCISABLE.include?(plan["status"]) ? "Exercise against production:" : "Optional:")
127
- items.each { |item| lines << " #{item_line(item)}" }
128
- lines << "Use a test account, or ask first, for routes marked [changes data]." if items.any? { |item| item["mutating"] }
125
+ if EXERCISABLE.include?(plan["status"])
126
+ needed, extra = items.partition { |item| needed?(item) }
127
+ item_group(lines, "Needed to clear, exercise against production:", needed)
128
+ item_group(lines, "Also worth running, not needed to clear (changed or rarely used, watched on first use):", extra)
129
+ else
130
+ item_group(lines, "Optional:", items)
129
131
  end
132
+ lines << "Use a test account, or ask first, for routes marked [changes data]." if items.any? { |item| item["mutating"] }
130
133
  lines << "Then report it: #{plan["report_with"]}" if plan["report_with"]
131
134
  lines.join("\n")
132
135
  end
133
136
 
137
+ # Only what clearance waits on: the rest is in `deployangel plan`.
134
138
  def exercisable_items(document)
135
139
  plan = document["exercise_plan"] || {}
136
- EXERCISABLE.include?(plan["status"]) ? Array(plan["items"]).map { |item| item_line(item) } : []
140
+ return [] unless EXERCISABLE.include?(plan["status"])
141
+
142
+ Array(plan["items"]).select { |item| needed?(item) }.map { |item| item_line(item) }
143
+ end
144
+
145
+ # Whether clearance waits on the item. Servers older than the needed
146
+ # flag listed normally active items first, so count those.
147
+ def needed?(item)
148
+ item.key?("needed") ? item["needed"] : item["reason"] == "normally_active"
149
+ end
150
+
151
+ def item_group(lines, heading, items)
152
+ return if items.empty?
153
+
154
+ lines << heading
155
+ items.each { |item| lines << " #{item_line(item)}" }
137
156
  end
138
157
 
139
158
  def shortfall_lines(shortfall)
@@ -22,7 +22,8 @@ module DeployAngel
22
22
  Findings are deterministic evidence; any "investigation" field is AI inference. These tools cannot
23
23
  change production. Do not roll back or change production without explicit approval.
24
24
  If a release isn't cleared yet, get_exercise_plan says what to exercise against production so it
25
- clears sooner. Only act on status "exercisable" or "waiting_for_activity"; for "warm_up" or
25
+ clears sooner. Items with needed true are what clearance waits on; the rest are only worth running.
26
+ Only act on status "exercisable" or "waiting_for_activity"; for "warm_up" or
26
27
  "no_baseline" nothing you run can clear it. Exercise routes marked mutating only with a test account
27
28
  or after asking. Report what you ran with the plan's report_with command (deployangel check).
28
29
  TEXT
@@ -102,7 +103,8 @@ module DeployAngel
102
103
  )),
103
104
  tool("get_exercise_plan", "What stands between a release and clearance, and what to exercise against " \
104
105
  "production so it clears sooner: normally active routes short of their runs, routes this release changed " \
105
- "that haven't run, and critical flows. Routes marked mutating change data: use a test account or ask first. " \
106
+ "that haven't run, and critical flows. needed marks what clearance waits on; the rest are only worth running. " \
107
+ "Routes marked mutating change data: use a test account or ask first. " \
106
108
  "Your requests count as ordinary traffic; report what you ran with report_with. Defaults to the current git HEAD.",
107
109
  TARGET_PROPERTIES),
108
110
  tool("list_deployments", "Recent deployments with their verification state and verdict.",
@@ -137,9 +139,11 @@ module DeployAngel
137
139
  when "get_verification" then waiter.wait(target(arguments), wait: false).then { |o| verification_content(o) }
138
140
  when "wait_for_verification" then wait_content(arguments)
139
141
  when "get_exercise_plan" then waiter.wait(target(arguments), wait: false).then { |o| plan_content(o) }
140
- when "list_deployments" then @client.deployments(limit: arguments.fetch("limit", 10))
142
+ # MCP requires structured content to be an object, never a bare list.
143
+ when "list_deployments" then { "deployments" => @client.deployments(limit: arguments.fetch("limit", 10)) }
141
144
  when "get_exception" then @client.exception(arguments.fetch("fingerprint") { raise ArgumentError, "fingerprint is required" })
142
- when "list_late_regressions" then @client.late_regressions(since: arguments["since"], limit: arguments.fetch("limit", 10))
145
+ when "list_late_regressions"
146
+ { "late_regressions" => @client.late_regressions(since: arguments["since"], limit: arguments.fetch("limit", 10)) }
143
147
  when "register_deployment"
144
148
  raise ArgumentError, "register_deployment is not available for this token" unless scopes.include?("deployments")
145
149
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeployAngel
4
- VERSION = "0.1.12"
4
+ VERSION = "0.1.14"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: deployangel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.12
4
+ version: 0.1.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Owens