ask-solid_errors 0.1.0

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f5fc53842cb442db8d1ca3b2545515ad9d4763bcdeec8dc93c375f3901c2b12f
4
+ data.tar.gz: 9f024d03059f3c96864991ef633d9d98ef86808c569d5c21094804f3a6d420c5
5
+ SHA512:
6
+ metadata.gz: 7f48fdce7fe85b1b43f74d9c6720e159541984a1f9ce8cf4855cfa9fecbf97302d12d5c355bab1110d850ea674b1537ee59bbbde7f6eaf7fe760f8cac12c1431
7
+ data.tar.gz: 34bd2e2cbcb8ba7049c09de62c5bd60c4fd9ad858b79afe1aa647af0cfe1f158765b7cb86189e46e47bf9b14e7d2b04567e07d54e5183c56232e1cf1f1ad44af
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kaka Ruto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,26 @@
1
+ # ask-solid_errors
2
+
3
+ SolidErrors — error tracking stored in your Rails database
4
+
5
+ ## Installation
6
+
7
+ ```ruby
8
+ gem "ask-solid_errors"
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```ruby
14
+ client = Ask::SolidErrors.client
15
+ # ... use the client according to its API
16
+ ```
17
+
18
+ ## Development
19
+
20
+ ```bash
21
+ bundle exec rake test
22
+ ```
23
+
24
+ ## License
25
+
26
+ MIT
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module SolidErrors
5
+ # Returns a client proxy for querying SolidErrors.
6
+ #
7
+ # No authentication is needed — SolidErrors runs in the same database as the
8
+ # Rails app. The proxy delegates to +SolidErrors::Error+ and
9
+ # +SolidErrors::Occurrence+ models, returning +ActiveRecord::Relation+
10
+ # objects that can be further chained by the agent.
11
+ #
12
+ # @example
13
+ # Ask::SolidErrors.recent(limit: 5)
14
+ # Ask::SolidErrors.unresolved
15
+ # Ask::SolidErrors.by_class("ActiveRecord::RecordNotFound")
16
+ #
17
+ # @return [ClientProxy] a proxy wrapping +SolidErrors::Error+
18
+ # @raise [LoadError] if the +solid_errors+ gem is not installed
19
+ def self.client
20
+ ensure_solid_errors_loaded!
21
+ ClientProxy.new
22
+ end
23
+
24
+ # Convenience — return the most recently recorded errors.
25
+ #
26
+ # @param limit [Integer] number of errors to return (default: 10)
27
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
28
+ def self.recent(limit: 10)
29
+ client.recent(limit: limit)
30
+ end
31
+
32
+ # Convenience — find a single error by its primary key.
33
+ #
34
+ # @param id [Integer, String] error record ID
35
+ # @return [SolidErrors::Error]
36
+ # @raise [ActiveRecord::RecordNotFound] if the record does not exist
37
+ def self.find(id)
38
+ client.find(id)
39
+ end
40
+
41
+ # Convenience — return all unresolved errors.
42
+ #
43
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
44
+ def self.unresolved
45
+ client.unresolved
46
+ end
47
+
48
+ # Convenience — return all resolved errors.
49
+ #
50
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
51
+ def self.resolved
52
+ client.resolved
53
+ end
54
+
55
+ # Convenience — filter errors by exception class name.
56
+ #
57
+ # @param klass [String] exception class name (e.g. "ActiveRecord::RecordNotFound")
58
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
59
+ def self.by_class(klass)
60
+ client.where(exception_class: klass)
61
+ end
62
+
63
+ # Convenience — filter errors by severity level.
64
+ #
65
+ # @param severity [String] severity level ("error", "warning", "info")
66
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
67
+ def self.by_severity(severity)
68
+ client.where(severity: severity)
69
+ end
70
+
71
+ # Convenience — search errors whose message contains the given text.
72
+ #
73
+ # @param query [String] search text
74
+ # @return [ActiveRecord::Relation<SolidErrors::Error>]
75
+ def self.search(query)
76
+ client.where("message LIKE ?", "%#{client.sanitize_sql_like(query)}%")
77
+ end
78
+
79
+ # Convenience — return the occurrence count for an error.
80
+ #
81
+ # @param error [SolidErrors::Error, Integer] error record or its ID
82
+ # @return [Integer]
83
+ def self.occurrence_count(error)
84
+ client.occurrence_count(error)
85
+ end
86
+
87
+ # Wraps +SolidErrors::Error+ and delegates query methods to it while
88
+ # providing a clean interface for AI agents.
89
+ class ClientProxy < BasicObject
90
+ def initialize
91
+ @model = ::SolidErrors::Error
92
+ end
93
+
94
+ # Delegate all known query methods to the SolidErrors model.
95
+ def method_missing(name, ...)
96
+ @model.public_send(name, ...)
97
+ rescue ::ActiveRecord::NoDatabaseError => e
98
+ ::Kernel.raise e, "SolidErrors database is not configured. " \
99
+ "Run `bin/rails solid_errors:install:migrations db:migrate` first."
100
+ rescue ::ActiveRecord::StatementInvalid => e
101
+ if e.message.include?("solid_errors")
102
+ ::Kernel.raise e, "SolidErrors table does not exist. " \
103
+ "Run `bin/rails solid_errors:install:migrations db:migrate` first."
104
+ else
105
+ ::Kernel.raise e
106
+ end
107
+ end
108
+
109
+ def respond_to_missing?(name, include_private = false)
110
+ @model.respond_to?(name, include_private) || super
111
+ end
112
+
113
+ # Return the most recent errors.
114
+ def recent(limit: 10)
115
+ @model.order(created_at: :desc).limit(limit)
116
+ end
117
+
118
+ # Return all unresolved errors.
119
+ def unresolved
120
+ @model.unresolved.order(created_at: :desc)
121
+ end
122
+
123
+ # Return all resolved errors.
124
+ def resolved
125
+ @model.resolved.order(created_at: :desc)
126
+ end
127
+
128
+ # Return the occurrence count for an error.
129
+ def occurrence_count(error)
130
+ error = @model.find(error) unless error.is_a?(::ActiveRecord::Base)
131
+ error.occurrences.count
132
+ end
133
+ end
134
+
135
+ # Raise +LoadError+ with a clear message when the +solid_errors+ gem
136
+ # is not available.
137
+ def self.ensure_solid_errors_loaded!
138
+ return if defined?(::SolidErrors)
139
+
140
+ raise ::LoadError, "The `solid_errors` gem is not installed. " \
141
+ "Add `gem 'solid_errors'` to your Gemfile and run `bundle install`."
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module SolidErrors
5
+ # Human-readable description of the SolidErrors service context.
6
+ DESCRIPTION = "SolidErrors — error tracking stored in your Rails database"
7
+
8
+ # Gem name for the SolidErrors tracker.
9
+ GEM_NAME = "solid_errors"
10
+
11
+ # Quick-start Ruby code snippet for agents to copy-paste.
12
+ QUICK_START = <<~RUBY
13
+ errors = Ask::SolidErrors.recent(limit: 10)
14
+ errors.map { |e| { id: e.id, class: e.exception_class, message: e.message.truncate(200) } }
15
+
16
+ # Or get full details:
17
+ error = Ask::SolidErrors.find(id)
18
+ error.backtrace
19
+ error.context
20
+
21
+ # Or work with occurrences:
22
+ error = Ask::SolidErrors.find(id)
23
+ error.occurrences.each do |occ|
24
+ puts occ.parsed_backtrace
25
+ end
26
+ RUBY
27
+ end
28
+ end
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module SolidErrors
5
+ # Structured error knowledge for AI agents working with SolidErrors.
6
+ #
7
+ # Provides human-readable guidance for common Rails exception classes,
8
+ # error severity levels, and database-related issues encountered when
9
+ # querying SolidErrors records.
10
+ module Errors
11
+ # Common Rails exception classes and actionable guidance.
12
+ EXCEPTIONS = {
13
+ "ActiveRecord::RecordNotFound" => {
14
+ message: "A record was not found by its primary key or finder.",
15
+ action: "Check that the ID exists in the database. The record may have been deleted."
16
+ },
17
+ "ActiveRecord::RecordInvalid" => {
18
+ message: "A record failed validation.",
19
+ action: "Inspect the record's errors to see which validations failed."
20
+ },
21
+ "ActiveRecord::RecordNotSaved" => {
22
+ message: "A record could not be saved due to base errors or callbacks.",
23
+ action: "Check for before_save/before_create callbacks that return false."
24
+ },
25
+ "ActiveRecord::RecordNotDestroyed" => {
26
+ message: "A record could not be destroyed due to before_destroy callbacks.",
27
+ action: "Check for before_destroy callbacks that return false."
28
+ },
29
+ "ActiveRecord::StatementInvalid" => {
30
+ message: "An SQL statement failed to execute.",
31
+ action: "Check the SQL statement and database schema. The query may reference a missing column or table."
32
+ },
33
+ "ActiveRecord::ConnectionNotEstablished" => {
34
+ message: "No database connection is available.",
35
+ action: "Check database configuration and that the database server is running."
36
+ },
37
+ "ActiveRecord::Migration::PendingMigrationError" => {
38
+ message: "There are pending database migrations.",
39
+ action: "Run `bin/rails db:migrate` to apply pending migrations."
40
+ },
41
+ "ActiveRecord::NoDatabaseError" => {
42
+ message: "The database does not exist.",
43
+ action: "Run `bin/rails db:create` to create the database."
44
+ },
45
+ "ActiveRecord::Deadlocked" => {
46
+ message: "A database deadlock was detected.",
47
+ action: "Retry the transaction. Ensure consistent lock ordering in concurrent operations."
48
+ },
49
+ "ActiveRecord::LockWaitTimeout" => {
50
+ message: "A database lock wait timeout expired.",
51
+ action: "Reduce contention on the locked resource or increase the lock timeout."
52
+ },
53
+ "ActionController::RoutingError" => {
54
+ message: "No route matches the requested URL.",
55
+ action: "Check the URL and your routes file. Run `bin/rails routes` to see available routes."
56
+ },
57
+ "ActionController::ParameterMissing" => {
58
+ message: "A required parameter was not provided.",
59
+ action: "Check the required parameters for the action and ensure they are included in the request."
60
+ },
61
+ "ActionController::InvalidAuthenticityToken" => {
62
+ message: "The CSRF authenticity token is invalid.",
63
+ action: "Ensure forms include the CSRF token. This can happen after a session expires."
64
+ },
65
+ "ActiveSupport::MessageVerifier::InvalidSignature" => {
66
+ message: "A signed message has an invalid signature.",
67
+ action: "The data may have been tampered with, or the secret key base has changed since signing."
68
+ },
69
+ "Net::ReadTimeout" => {
70
+ message: "An external HTTP request timed out while reading the response.",
71
+ action: "Check the external service availability and response time. Consider increasing the timeout."
72
+ },
73
+ "Net::OpenTimeout" => {
74
+ message: "An external HTTP connection timed out.",
75
+ action: "Check network connectivity and that the external service is reachable."
76
+ },
77
+ "SystemExit" => {
78
+ message: "The process was asked to exit by calling exit or abort.",
79
+ action: "This may be intentional (e.g., abort in a callback). Check the exit code."
80
+ },
81
+ "SignalException" => {
82
+ message: "The process received a signal (e.g., SIGTERM, SIGINT).",
83
+ action: "The process was interrupted externally. This is expected during deploys or shutdowns."
84
+ },
85
+ "NoMemoryError" => {
86
+ message: "The system ran out of memory.",
87
+ action: "Reduce memory usage or increase available memory. Check for memory leaks."
88
+ }
89
+ }.freeze
90
+
91
+ # Error severity levels used by SolidErrors and how to respond.
92
+ SEVERITIES = {
93
+ "error" => {
94
+ description: "🔥 An actual error occurred that requires investigation.",
95
+ action: "Prioritize review. Check the backtrace and context for the root cause."
96
+ },
97
+ "warning" => {
98
+ description: "⚠️ An unexpected condition occurred but the request completed.",
99
+ action: "Review when convenient. May indicate an edge case or degraded experience."
100
+ },
101
+ "info" => {
102
+ description: "ℹ️ An informational event was logged.",
103
+ action: "No immediate action needed. Useful for auditing and debugging."
104
+ }
105
+ }.freeze
106
+
107
+ # Database-related guidance for SolidErrors configuration.
108
+ DATABASE = {
109
+ table_name: "solid_errors",
110
+ occurrences_table_name: "solid_errors_occurrences",
111
+ migration_instructions: "Run `bin/rails solid_errors:install:migrations` then `bin/rails db:migrate`",
112
+ table_structure: {
113
+ "solid_errors" => [
114
+ "exception_class (text) — the exception class name (e.g. ActiveRecord::RecordNotFound)",
115
+ "message (text) — the exception message",
116
+ "severity (text) — error, warning, or info",
117
+ "source (text, nullable) — source of the error",
118
+ "resolved_at (datetime, nullable) — when the error was resolved",
119
+ "fingerprint (string, unique) — SHA256 hash for deduplication"
120
+ ],
121
+ "solid_errors_occurrences" => [
122
+ "error_id (integer) — FK to solid_errors.id",
123
+ "backtrace (text) — the exception backtrace",
124
+ "context (json) — structured context data from the error"
125
+ ]
126
+ }
127
+ }.freeze
128
+
129
+ # Look up guidance for a Rails exception class name.
130
+ #
131
+ # @param exception_class [String] The exception class name (e.g. "ActiveRecord::RecordNotFound")
132
+ # @return [Hash, nil] A hash with +:message+ and +:action+ keys, or nil if unknown
133
+ def self.for(exception_class)
134
+ EXCEPTIONS[exception_class]
135
+ end
136
+
137
+ # Describe a severity level.
138
+ #
139
+ # @param severity [String] severity level ("error", "warning", "info")
140
+ # @return [Hash, nil] Description of the severity, or nil if unknown
141
+ def self.severity_description(severity)
142
+ SEVERITIES[severity.to_s]
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ask
4
+ module SolidErrors
5
+ VERSION = "0.1.0"
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ask/solid_errors/version"
4
+ require_relative "ask/solid_errors/context"
5
+ require_relative "ask/solid_errors/client"
6
+ require_relative "ask/solid_errors/error_guide"
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ask-solid_errors
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kaka Ruto
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: minitest
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.25'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.25'
26
+ - !ruby/object:Gem::Dependency
27
+ name: mocha
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '3.1'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '13.0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '13.0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: ask-core
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '0.1'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.1'
68
+ description: Error context for agents via SolidErrors for the ask-rb ecosystem.
69
+ email:
70
+ - kaka@myrrlabs.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - LICENSE
76
+ - README.md
77
+ - lib/ask-solid_errors.rb
78
+ - lib/ask/solid_errors/client.rb
79
+ - lib/ask/solid_errors/context.rb
80
+ - lib/ask/solid_errors/error_guide.rb
81
+ - lib/ask/solid_errors/version.rb
82
+ homepage: https://github.com/ask-rb/ask-solid_errors
83
+ licenses:
84
+ - MIT
85
+ metadata: {}
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '3.2'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubygems_version: 4.0.3
101
+ specification_version: 4
102
+ summary: SolidErrors — error tracking stored in your Rails database
103
+ test_files: []