rails-informant 0.1.0 → 0.1.1

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: 485e2e93c123b0e19bdf0fead39f92d8043e16c14cf9eaf6bb22f60a71b49ad4
4
- data.tar.gz: c4a3dd9090e8ce91b5d8cd161aafc5428971425e7690a17fe3ff2aa80ba1e31a
3
+ metadata.gz: 0efea7b3e9161f6868f46fa2996fc426ad132eb3cb0b5a814963447472ff83ff
4
+ data.tar.gz: 2b73bff8211ebd79de66056c91321ec3a79989c8f38f415235d2f9e0d49fdc4c
5
5
  SHA512:
6
- metadata.gz: 6f029e95ff0427c4e08732693cfa63cdc452ced3643a0785235e8bb64e0ab11c9dd7f4657c3bbe3aa59e8c50cecdc08abc8bff530fa2d096348c2279255ccc57
7
- data.tar.gz: 1f7d8a4a9da13db4fe53e2bff1a34e6b5147299c088e26e55d1ac80940982d7b2776f787456c5b20721601816a7163e9d58d3a5738dc1e8170e2d3b36b27419b
6
+ metadata.gz: f6b50ea3c2c678ae9239a89846bba3759bdfb57c08e2b12724c43125529a08892a6a45d9d5c1e9cfb5d72eac77dbd18534b075f4043a23cdcac184b7f1bde9c4
7
+ data.tar.gz: 8e9c3fcfea6562d5d925b6824a27d50da602c2a72c3f87e673aa74a0922ad9025e15da4f35df7adcefd88dc095c5c336f3c1601c9350290627d80989c957db9e
@@ -27,7 +27,13 @@ When implementing a fix:
27
27
  5. Verify test passes
28
28
  6. Commit + open draft PR
29
29
  7. Call `mark_fix_pending` with fix_sha, original_sha, and fix_pr_url
30
- (The server auto-resolves when the fix is deployed)
30
+
31
+ ## Verify Deployed Fixes
32
+
33
+ After a deploy, run `verify_pending_fixes` to confirm fix_pending errors are resolved.
34
+ This checks if each fix_sha is an ancestor of the deployed code using git locally.
35
+ Check `get_informant_status` — if `deploy_sha` has changed since you last checked, a deploy has occurred.
36
+ Ensure the local repo is up to date (`git fetch`) before running verification.
31
37
 
32
38
  ## Important Notes
33
39
 
@@ -39,19 +39,14 @@ module RailsInformant
39
39
  )
40
40
  end
41
41
 
42
- initializer "rails_informant.detect_deploy" do
42
+ initializer "rails_informant.log_pending_fixes" do
43
43
  config.after_initialize do
44
44
  next unless RailsInformant.server_mode?
45
45
 
46
- current_sha = RailsInformant.current_git_sha
47
- next unless current_sha
46
+ count = RailsInformant::ErrorGroup.where(status: "fix_pending").count
47
+ next if count.zero?
48
48
 
49
- now = Time.current
50
- RailsInformant::ErrorGroup
51
- .where(status: "fix_pending")
52
- .where.not(original_sha: current_sha)
53
- .in_batches(of: 100)
54
- .update_all(status: "resolved", resolved_at: now, fix_deployed_at: now, updated_at: now)
49
+ Rails.logger.info "[Informant] #{count} error(s) awaiting fix verification"
55
50
  rescue ActiveRecord::StatementInvalid
56
51
  # Table may not exist yet during initial migration
57
52
  end
@@ -6,10 +6,11 @@ module RailsInformant
6
6
 
7
7
  ## Triage Workflow
8
8
  1. Check `get_informant_status` for overview counts by status
9
- 2. List unresolved errors with `list_errors(status: "unresolved")`
10
- 3. Pick the highest-impact error
11
- 4. Investigate with `get_error` (includes up to 10 recent occurrences)
12
- 5. For errors with many occurrences, use `list_occurrences` to paginate through all of them
9
+ 2. If fix_pending count > 0 in the status response, run `verify_pending_fixes` to check deployed fixes
10
+ 3. List unresolved errors with `list_errors(status: "unresolved")`
11
+ 4. Pick the highest-impact error
12
+ 5. Investigate with `get_error` (includes up to 10 recent occurrences)
13
+ 6. For errors with many occurrences, use `list_occurrences` to paginate through all of them
13
14
 
14
15
  ## Assessment Criteria
15
16
  Prioritize by: frequency (occurrence count), impact (affects critical paths),
@@ -23,7 +24,8 @@ module RailsInformant
23
24
  duplicate → unresolved
24
25
 
25
26
  ## Resolution Strategies
26
- - Clear fix available → write fix, call `mark_fix_pending` with commit SHAs
27
+ - Clear fix available → write fix, call `mark_fix_pending` with commit SHAs. After deploy, run `verify_pending_fixes` to confirm and resolve.
28
+ - Pending fixes deployed → `verify_pending_fixes` checks git ancestry and resolves verified fixes
27
29
  - Not actionable → `annotate_error` with reason, then `ignore_error`
28
30
  - Same root cause as another → `mark_duplicate` with target ID
29
31
  - Needs context → `annotate_error` with findings
@@ -60,7 +62,8 @@ module RailsInformant
60
62
  Tools::MarkDuplicate,
61
63
  Tools::MarkFixPending,
62
64
  Tools::ReopenError,
63
- Tools::ResolveError
65
+ Tools::ResolveError,
66
+ Tools::VerifyPendingFixes
64
67
  ].freeze
65
68
 
66
69
  def self.build(config)
@@ -13,7 +13,11 @@ module RailsInformant
13
13
 
14
14
  def self.call(server_context:, environment: nil)
15
15
  with_client(server_context:, environment:) do |client|
16
- text_response(client.status)
16
+ result = client.status
17
+ if result["fix_pending_count"]&.positive?
18
+ result["hint"] = "#{result["fix_pending_count"]} error(s) awaiting fix verification. Run verify_pending_fixes to check if their fixes have been deployed."
19
+ end
20
+ text_response result
17
21
  end
18
22
  end
19
23
  end
@@ -3,7 +3,7 @@ module RailsInformant
3
3
  module Tools
4
4
  class MarkFixPending < BaseTool
5
5
  tool_name "mark_fix_pending"
6
- description "Mark as fix_pending (unresolved → fix_pending) with the fix commit SHA, original SHA, and optional PR URL. The server auto-resolves when the fix is deployed. Valid from: unresolved."
6
+ description "Mark as fix_pending (unresolved → fix_pending) with the fix commit SHA, original SHA, and optional PR URL. After deploy, run verify_pending_fixes to confirm and resolve. Valid from: unresolved."
7
7
  input_schema(
8
8
  properties: {
9
9
  environment: { type: "string", description: "Target environment (defaults to first configured)" },
@@ -0,0 +1,90 @@
1
+ module RailsInformant
2
+ module Mcp
3
+ module Tools
4
+ class VerifyPendingFixes < BaseTool
5
+ tool_name "verify_pending_fixes"
6
+ description "Verify fix_pending errors by checking if their fix_sha is an ancestor of the deployed code. Resolves verified fixes. Requires git locally. Results reflect local git state — run git fetch first to ensure accuracy."
7
+ input_schema(
8
+ properties: {
9
+ auto_resolve: { type: "boolean", description: "Resolve verified fixes automatically (default: true)" },
10
+ environment: { type: "string", description: "Target environment (defaults to first configured)" },
11
+ target_ref: { type: "string", description: "Git ref to verify against (default: deploy_sha from status API)" }
12
+ }
13
+ )
14
+ annotations(read_only_hint: false, destructive_hint: false, idempotent_hint: true)
15
+
16
+ MAX_PAGES = 10
17
+
18
+ class << self
19
+ def call(server_context:, auto_resolve: true, environment: nil, target_ref: nil)
20
+ with_client(server_context:, environment:) do |client|
21
+ return error_response "git is not available. Run this tool from a machine with git installed." unless git_available?
22
+
23
+ ref = target_ref || fetch_deploy_sha(client)
24
+ return error_response "No deploy SHA available. Pass target_ref explicitly." unless ref
25
+ return error_response "Invalid target_ref" if ref.start_with?("-")
26
+
27
+ errors = fetch_all_fix_pending client
28
+ return text_response("message" => "No fix_pending errors found", "verified" => [], "pending" => []) if errors.empty?
29
+
30
+ verified = []
31
+ pending = []
32
+
33
+ errors.each do |error|
34
+ fix_sha = error["fix_sha"]
35
+ if fix_sha && ancestor?(fix_sha, ref)
36
+ verified << error
37
+ else
38
+ pending << error
39
+ end
40
+ end
41
+
42
+ if auto_resolve && verified.any?
43
+ verified.each { |error| client.update_error error["id"], status: "resolved" }
44
+ end
45
+
46
+ text_response(
47
+ "message" => "Verified #{verified.size} of #{errors.size} fix_pending error(s)",
48
+ "deploy_sha" => ref,
49
+ "resolved_count" => auto_resolve ? verified.size : 0,
50
+ "verified" => verified.map { summarize it },
51
+ "pending" => pending.map { summarize it }
52
+ )
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def ancestor?(fix_sha, target_ref)
59
+ system "git", "merge-base", "--is-ancestor", fix_sha, target_ref, out: File::NULL, err: File::NULL
60
+ end
61
+
62
+ def fetch_all_fix_pending(client)
63
+ errors = []
64
+ page = 1
65
+ loop do
66
+ result = client.list_errors status: "fix_pending", page:, per_page: 100
67
+ errors.concat result["data"]
68
+ break unless result.dig("meta", "has_more")
69
+ page += 1
70
+ break if page > MAX_PAGES
71
+ end
72
+ errors
73
+ end
74
+
75
+ def fetch_deploy_sha(client)
76
+ client.status&.dig("deploy_sha")
77
+ end
78
+
79
+ def git_available?
80
+ system "git", "rev-parse", "--git-dir", out: File::NULL, err: File::NULL
81
+ end
82
+
83
+ def summarize(error)
84
+ { "id" => error["id"], "error_class" => error["error_class"], "message" => error["message"], "fix_sha" => error["fix_sha"], "fix_pr_url" => error["fix_pr_url"] }
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -19,4 +19,5 @@ require_relative "mcp/tools/mark_duplicate"
19
19
  require_relative "mcp/tools/mark_fix_pending"
20
20
  require_relative "mcp/tools/reopen_error"
21
21
  require_relative "mcp/tools/resolve_error"
22
+ require_relative "mcp/tools/verify_pending_fixes"
22
23
  require_relative "mcp/server"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-informant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel López Prat
@@ -159,6 +159,7 @@ files:
159
159
  - lib/rails_informant/mcp/tools/mark_fix_pending.rb
160
160
  - lib/rails_informant/mcp/tools/reopen_error.rb
161
161
  - lib/rails_informant/mcp/tools/resolve_error.rb
162
+ - lib/rails_informant/mcp/tools/verify_pending_fixes.rb
162
163
  - lib/rails_informant/middleware/error_capture.rb
163
164
  - lib/rails_informant/middleware/rescued_exception_interceptor.rb
164
165
  - lib/rails_informant/notifiers/notification_policy.rb