otto 2.0.0.pre3 → 2.0.0.pre7

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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -1
  3. data/.github/workflows/claude-code-review.yml +1 -1
  4. data/.github/workflows/code-smells.yml +146 -0
  5. data/.gitignore +4 -0
  6. data/.pre-commit-config.yaml +2 -2
  7. data/.reek.yml +99 -0
  8. data/CHANGELOG.rst +90 -0
  9. data/CLAUDE.md +74 -540
  10. data/Gemfile +4 -2
  11. data/Gemfile.lock +58 -19
  12. data/README.md +49 -1
  13. data/changelog.d/20251103_235431_delano_86_improve_error_logging.rst +15 -0
  14. data/changelog.d/20251109_025012_claude_fix_backtrace_sanitization.rst +37 -0
  15. data/examples/advanced_routes/README.md +137 -20
  16. data/examples/authentication_strategies/README.md +212 -19
  17. data/examples/backtrace_sanitization_demo.rb +86 -0
  18. data/examples/basic/README.md +61 -10
  19. data/examples/error_handler_registration.rb +136 -0
  20. data/examples/logging_improvements.rb +76 -0
  21. data/examples/mcp_demo/README.md +187 -27
  22. data/examples/security_features/README.md +249 -30
  23. data/examples/simple_geo_resolver.rb +107 -0
  24. data/lib/otto/core/configuration.rb +15 -20
  25. data/lib/otto/core/error_handler.rb +138 -8
  26. data/lib/otto/core/file_safety.rb +2 -2
  27. data/lib/otto/core/freezable.rb +2 -2
  28. data/lib/otto/core/middleware_stack.rb +2 -2
  29. data/lib/otto/core/router.rb +61 -8
  30. data/lib/otto/core/uri_generator.rb +2 -2
  31. data/lib/otto/core.rb +2 -0
  32. data/lib/otto/design_system.rb +2 -2
  33. data/lib/otto/env_keys.rb +61 -12
  34. data/lib/otto/helpers/base.rb +2 -2
  35. data/lib/otto/helpers/request.rb +8 -3
  36. data/lib/otto/helpers/response.rb +2 -2
  37. data/lib/otto/helpers/validation.rb +2 -2
  38. data/lib/otto/helpers.rb +2 -0
  39. data/lib/otto/locale/config.rb +2 -2
  40. data/lib/otto/locale/middleware.rb +160 -0
  41. data/lib/otto/locale.rb +10 -0
  42. data/lib/otto/logging_helpers.rb +273 -0
  43. data/lib/otto/mcp/auth/token.rb +2 -2
  44. data/lib/otto/mcp/protocol.rb +2 -2
  45. data/lib/otto/mcp/rate_limiting.rb +2 -2
  46. data/lib/otto/mcp/registry.rb +2 -2
  47. data/lib/otto/mcp/route_parser.rb +2 -2
  48. data/lib/otto/mcp/schema_validation.rb +2 -2
  49. data/lib/otto/mcp/server.rb +2 -2
  50. data/lib/otto/mcp.rb +2 -0
  51. data/lib/otto/privacy/config.rb +2 -0
  52. data/lib/otto/privacy/geo_resolver.rb +199 -29
  53. data/lib/otto/privacy/ip_privacy.rb +2 -0
  54. data/lib/otto/privacy/redacted_fingerprint.rb +18 -8
  55. data/lib/otto/privacy.rb +2 -0
  56. data/lib/otto/response_handlers/auto.rb +2 -0
  57. data/lib/otto/response_handlers/base.rb +2 -0
  58. data/lib/otto/response_handlers/default.rb +2 -0
  59. data/lib/otto/response_handlers/factory.rb +2 -0
  60. data/lib/otto/response_handlers/json.rb +2 -0
  61. data/lib/otto/response_handlers/redirect.rb +2 -0
  62. data/lib/otto/response_handlers/view.rb +2 -0
  63. data/lib/otto/response_handlers.rb +2 -2
  64. data/lib/otto/route.rb +4 -4
  65. data/lib/otto/route_definition.rb +42 -15
  66. data/lib/otto/route_handlers/base.rb +2 -0
  67. data/lib/otto/route_handlers/class_method.rb +18 -25
  68. data/lib/otto/route_handlers/factory.rb +2 -2
  69. data/lib/otto/route_handlers/instance_method.rb +8 -5
  70. data/lib/otto/route_handlers/lambda.rb +8 -20
  71. data/lib/otto/route_handlers/logic_class.rb +23 -6
  72. data/lib/otto/route_handlers.rb +2 -2
  73. data/lib/otto/security/authentication/auth_failure.rb +2 -2
  74. data/lib/otto/security/authentication/auth_strategy.rb +11 -4
  75. data/lib/otto/security/authentication/route_auth_wrapper.rb +230 -78
  76. data/lib/otto/security/authentication/strategies/api_key_strategy.rb +2 -0
  77. data/lib/otto/security/authentication/strategies/noauth_strategy.rb +2 -0
  78. data/lib/otto/security/authentication/strategies/permission_strategy.rb +2 -0
  79. data/lib/otto/security/authentication/strategies/role_strategy.rb +2 -0
  80. data/lib/otto/security/authentication/strategies/session_strategy.rb +2 -0
  81. data/lib/otto/security/authentication/strategy_result.rb +6 -5
  82. data/lib/otto/security/authentication.rb +2 -2
  83. data/lib/otto/security/authorization_error.rb +73 -0
  84. data/lib/otto/security/config.rb +2 -2
  85. data/lib/otto/security/configurator.rb +17 -2
  86. data/lib/otto/security/csrf.rb +2 -2
  87. data/lib/otto/security/middleware/csrf_middleware.rb +11 -1
  88. data/lib/otto/security/middleware/ip_privacy_middleware.rb +31 -11
  89. data/lib/otto/security/middleware/rate_limit_middleware.rb +2 -0
  90. data/lib/otto/security/middleware/validation_middleware.rb +15 -0
  91. data/lib/otto/security/rate_limiter.rb +2 -2
  92. data/lib/otto/security/rate_limiting.rb +2 -2
  93. data/lib/otto/security/validator.rb +2 -2
  94. data/lib/otto/security.rb +3 -0
  95. data/lib/otto/static.rb +2 -2
  96. data/lib/otto/utils.rb +27 -2
  97. data/lib/otto/version.rb +3 -3
  98. data/lib/otto.rb +174 -14
  99. data/otto.gemspec +7 -3
  100. metadata +24 -15
  101. data/benchmark_middleware_wrap.rb +0 -163
  102. data/changelog.d/20251014_144317_delano_54_thats_a_wrapper.rst +0 -36
  103. data/changelog.d/20251014_161526_delano_54_thats_a_wrapper.rst +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: abc8be5b60ac2a33d84dd0b89aec0e30797d36a995fdc26db7d9d2e65b28553f
4
- data.tar.gz: 003a6c148c47011bbdc185c0035d9788f319b34eaa4bd2067cf06199749abf46
3
+ metadata.gz: 69a46d80f1fcc3f2472c44554fd7102645226e264befdd23af9871ad4f5fafaf
4
+ data.tar.gz: 54d8b433d49b549e17d3406aa011b6f4f80c50eefc5a6fa51dde1772fde8b8f2
5
5
  SHA512:
6
- metadata.gz: 0b27bf0132a8648a0b7ba14c33e1c1553196447a34a826463572c2fb005c644cdcbadd9987b1b71787c41cc70332771034ac52bda39e76aaa2947c0b73138470
7
- data.tar.gz: c45a50e6420a3a6a072abdbb03177228132359cc52193fdcbac230ea0b6f6aac8c86613190d4cb6be192125db6999510467a3a4a5abe0f1fee46e07e202448aa
6
+ metadata.gz: b518140043ad7ab983fb99e85ac9643ca4527385930d77a4b01e2b241b7212854fbc5e8875bc58b17e3d17c66fcfc6455b85587d023ceec13006876bec79a0d9
7
+ data.tar.gz: f64b842b58acc746ad39d58eac0fb57c245f1db29755f89d259d64bd127db3043034a9e8ada0ee19c04d969ec1bb34b515f5dfc1fcf36c80ceaeb9123ac2de6e
@@ -45,7 +45,7 @@ jobs:
45
45
  bundler-cache: ${{ !matrix.experimental }}
46
46
 
47
47
  - name: Setup tmate session
48
- uses: mxschmitt/action-tmate@7b6a61a73bbb9793cb80ad69b8dd8ac19261834c # v3
48
+ uses: mxschmitt/action-tmate@c0afd6f790e3a5564914980036ebf83216678101 # v3
49
49
  if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_enabled }}
50
50
  with:
51
51
  detached: true
@@ -54,7 +54,7 @@ jobs:
54
54
  - name: Remove claude-review label
55
55
  # Remove label whether success or failure - prevents getting stuck
56
56
  if: always() && github.event.action != 'opened'
57
- uses: actions/github-script@v7
57
+ uses: actions/github-script@v8
58
58
  with:
59
59
  script: |
60
60
  try {
@@ -0,0 +1,146 @@
1
+ # .github/workflows/code-smells.yml
2
+ ---
3
+ name: Code Smells
4
+
5
+ on:
6
+ pull_request:
7
+ branches: [main]
8
+ push:
9
+ branches: [main]
10
+ workflow_dispatch:
11
+
12
+ permissions:
13
+ contents: read
14
+ pull-requests: write # Needed to post comments on PRs
15
+
16
+ jobs:
17
+ reek-analysis:
18
+ name: Reek Code Analysis
19
+ runs-on: ubuntu-24.04
20
+ timeout-minutes: 5
21
+
22
+ steps:
23
+ - name: Checkout code
24
+ uses: actions/checkout@v5
25
+
26
+ - name: Set up Ruby
27
+ uses: ruby/setup-ruby@v1
28
+ with:
29
+ ruby-version: 3.4
30
+ bundler-cache: true
31
+
32
+ - name: Configure Bundler for secure gem installation
33
+ run: |
34
+ bundle config set --local path 'vendor/bundle'
35
+ bundle config set --local deployment 'false'
36
+
37
+ - name: Install dependencies
38
+ run: bundle install --jobs 4 --retry 3
39
+
40
+ - name: Run Reek analysis
41
+ run: |
42
+ echo "=== Running Reek code analysis ==="
43
+ echo "This analysis identifies code smells and potential improvements."
44
+ echo "Results are informational and won't fail the build."
45
+ echo ""
46
+
47
+ # Run reek and capture output (don't fail on warnings)
48
+ # Use success-exit-code to prevent failures from stopping the analysis
49
+ bundle exec reek --format=text --success-exit-code 0 --failure-exit-code 0 || true
50
+
51
+ echo ""
52
+ echo "=== Reek analysis complete ==="
53
+ continue-on-error: true # Don't fail the build on code smells
54
+
55
+ - name: Generate Reek report
56
+ run: |
57
+ echo "=== Generating detailed Reek report ==="
58
+
59
+ # Generate JSON report for potential future processing
60
+ bundle exec reek --format=json --success-exit-code 0 --failure-exit-code 0 > reek-report.json || true
61
+
62
+ # If no JSON was generated, create an empty valid JSON array
63
+ if [ ! -s reek-report.json ]; then
64
+ echo "[]" > reek-report.json
65
+ echo "No code smells detected - created empty report"
66
+ else
67
+ echo "Reek JSON report generated: $(wc -l < reek-report.json) lines"
68
+ echo "Top code smell types found:"
69
+ jq -r '.[].smells[].smell_type' reek-report.json 2>/dev/null | sort | uniq -c | sort -rn | head -10 || echo "Unable to parse JSON report"
70
+ fi
71
+
72
+ # Also generate a human-readable HTML report for easier viewing
73
+ bundle exec reek --format=html --success-exit-code 0 --failure-exit-code 0 > reek-report.html || echo "<!-- No code smells detected -->" > reek-report.html
74
+ continue-on-error: true
75
+
76
+ - name: Upload Reek report as artifact
77
+ uses: actions/upload-artifact@v5
78
+ if: always()
79
+ with:
80
+ name: reek-report
81
+ path: |
82
+ reek-report.json
83
+ reek-report.html
84
+ if-no-files-found: ignore
85
+ retention-days: 30
86
+
87
+ additional-checks:
88
+ name: Additional Quality Checks
89
+ runs-on: ubuntu-24.04
90
+ timeout-minutes: 5
91
+
92
+ steps:
93
+ - name: Checkout code
94
+ uses: actions/checkout@v5
95
+
96
+ - name: Set up Ruby
97
+ uses: ruby/setup-ruby@v1
98
+ with:
99
+ ruby-version: 3.4
100
+ bundler-cache: true
101
+
102
+ - name: Configure Bundler for secure gem installation
103
+ run: |
104
+ bundle config set --local path 'vendor/bundle'
105
+ bundle config set --local deployment 'false'
106
+
107
+ - name: Install dependencies
108
+ run: bundle install --jobs 4 --retry 3
109
+
110
+ - name: Check for TODO/FIXME comments
111
+ run: |
112
+ echo "=== Scanning for TODO/FIXME comments ==="
113
+ echo "This helps track technical debt and action items."
114
+ echo ""
115
+
116
+ # Find TODO/FIXME comments (excluding vendor and tmp directories)
117
+ find . -name "*.rb" -not -path "./vendor/*" -not -path "./tmp/*" | \
118
+ xargs grep -Hn -i -E "(TODO|FIXME|HACK|XXX|NOTE):" 2>/dev/null | \
119
+ head -20 || echo "No TODO/FIXME comments found"
120
+ continue-on-error: true
121
+
122
+ - name: Check Ruby file syntax
123
+ run: |
124
+ echo "=== Checking Ruby syntax ==="
125
+ echo "Validates that all Ruby files have correct syntax."
126
+ echo ""
127
+
128
+ find . -name "*.rb" -not -path "./vendor/*" -not -path "./tmp/*" | \
129
+ while read -r file; do
130
+ if ! ruby -c "$file" > /dev/null 2>&1; then
131
+ echo "Syntax error in: $file"
132
+ ruby -c "$file"
133
+ fi
134
+ done
135
+ continue-on-error: true
136
+
137
+ - name: Check for long lines
138
+ run: |
139
+ echo "=== Checking for long lines (>120 characters) ==="
140
+ echo "Identifies potentially hard-to-read code lines."
141
+ echo ""
142
+
143
+ find . -name "*.rb" -not -path "./vendor/*" -not -path "./tmp/*" | \
144
+ xargs grep -Hn "^.\{121,\}$" | \
145
+ head -10 || echo "No overly long lines found"
146
+ continue-on-error: true
data/.gitignore CHANGED
@@ -11,8 +11,12 @@
11
11
  .*.json
12
12
  !LICENSE.txt
13
13
  !spec/fixtures/*.txt
14
+ !examples/**/*.md
15
+ !README.md
16
+ !CLAUDE.md
14
17
  .ruby-version
15
18
  appendonlydir
19
+ data/
16
20
  etc/config
17
21
  log
18
22
  tmp
@@ -96,12 +96,12 @@ repos:
96
96
 
97
97
  # Commit message issue tracking integration
98
98
  - repo: https://github.com/avilaton/add-msg-issue-prefix-hook
99
- rev: v0.0.12
99
+ rev: v0.0.13
100
100
  hooks:
101
101
  - id: add-msg-issue-prefix
102
102
  stages: [prepare-commit-msg]
103
103
  description: Automatically prefix commits with issue numbers
104
104
  args:
105
105
  - "--default="
106
- - '--pattern=(?:i18n(?=\/)|[a-zA-Z0-9]{0,10}-?[0-9]{1,5})'
106
+ - "--pattern=(i18n(?=/)|([a-zA-Z0-9]{0,10}-?[0-9]{1,5}))"
107
107
  - "--template=[#{}]"
data/.reek.yml ADDED
@@ -0,0 +1,99 @@
1
+ # .reek.yml
2
+ #
3
+ # Reek configuration for Otto
4
+ #
5
+ # Basic commands:
6
+ # bundle exec reek # Analyze all Ruby files
7
+ # bundle exec reek lib/ # Analyze specific directory
8
+ # bundle exec reek lib/otto.rb # Analyze specific file
9
+ # bundle exec reek --help # Show all options
10
+ # bundle exec reek --docs # Open documentation
11
+ #
12
+ # Advanced usage:
13
+ # bundle exec reek --format=html > report.html # Generate HTML report
14
+ # bundle exec reek --format=json # JSON output for CI
15
+ # bundle exec reek --config .reek.yml # Use specific config
16
+ # bundle exec reek --show-docs IrresponsibleModule # Explain specific smell
17
+ # bundle exec reek --failure-exit-code 1 # Exit with error on smells (for CI)
18
+
19
+ ---
20
+ detectors:
21
+ # Disable some detectors for initial adoption
22
+ # You can gradually enable these as you clean up the codebase
23
+
24
+ # Class/Module Structure
25
+ IrresponsibleModule:
26
+ enabled: false # Modules without documentation - start with this disabled
27
+
28
+ # Method Complexity
29
+ TooManyStatements:
30
+ enabled: true
31
+ max_statements: 15 # Default is 5, relaxed for initial adoption
32
+
33
+ TooManyMethods:
34
+ enabled: true
35
+ max_methods: 25 # Default is 15, relaxed for ORMs which often have many methods
36
+
37
+ LongParameterList:
38
+ enabled: true
39
+ max_params: 4 # Default is 3, slightly relaxed
40
+
41
+ # Data Classes and Feature Envy
42
+ DataClump:
43
+ enabled: true
44
+
45
+ FeatureEnvy:
46
+ enabled: true
47
+
48
+ # Control Structure
49
+ NestedIterators:
50
+ enabled: true
51
+ max_allowed_nesting: 2 # Default is 1, relaxed for data processing
52
+
53
+ # Variable and Constant Usage
54
+ UnusedParameters:
55
+ enabled: true
56
+
57
+ InstanceVariableAssumption:
58
+ enabled: true
59
+
60
+ # Naming
61
+ UncommunicativeParameterName:
62
+ enabled: true
63
+ reject:
64
+ - "/^.$/" # Single letter names
65
+ - "/[0-9]$/" # Names ending in numbers
66
+ - "/^_/" # Names starting with underscore (common Ruby pattern)
67
+ accept: []
68
+
69
+ UncommunicativeVariableName:
70
+ enabled: true
71
+ reject:
72
+ - "/^.$/" # Single letter names
73
+ - "/[0-9]$/" # Names ending in numbers
74
+ accept:
75
+ - e # Exception variable
76
+ - id # Common identifier
77
+ - db # Database connection
78
+ - op # Operation
79
+ - io # Input/output
80
+
81
+ UncommunicativeMethodName:
82
+ enabled: true
83
+ reject:
84
+ - "/^.$/" # Single letter method names
85
+ - "/[0-9]$/" # Methods ending in numbers
86
+ accept:
87
+ - "<<" # Common Ruby operator overload
88
+
89
+ # Directory and file exclusions
90
+ exclude_paths:
91
+ - "vendor/**/*.rb"
92
+ - "tmp/**/*.rb"
93
+ - "try/**/*.rb" # Test files using tryouts framework
94
+ - "examples/**/*.rb" # Example code files
95
+ - "bin/*" # Executable scripts
96
+ - "*.gemspec" # Gem specification files
97
+
98
+ # Note: For limiting warnings output, use CLI: bundle exec reek | head -50
99
+ # Note: For failure exit codes, use CLI: bundle exec reek --failure-exit-code 1
data/CHANGELOG.rst CHANGED
@@ -7,6 +7,95 @@ The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.1.0/>`
7
7
 
8
8
  <!--scriv-insert-here-->
9
9
 
10
+ .. _changelog-2.0.0.pre6:
11
+
12
+ 2.0.0.pre6 — TBD
13
+ ================
14
+
15
+ Changed
16
+ -------
17
+
18
+ - **BREAKING**: ``Otto.on_request_complete`` is now an instance method instead of a class method. This fixes duplicate callback invocations in multi-app architectures (e.g., Rack::URLMap with multiple Otto instances). Each Otto instance now maintains its own isolated set of callbacks that only fire for requests processed by that specific instance.
19
+
20
+ **Migration**: Change ``Otto.on_request_complete { |req, res, dur| ... }`` to ``otto.on_request_complete { |req, res, dur| ... }``
21
+
22
+ - **Logging**: Eliminated duplicate error logging in route handlers. Previously, errors produced two log lines ("Handler execution failed" + "Unhandled error in request"). Now produces a single comprehensive error log with all context (handler, duration, error_id). Lambda handlers now use centralized error handling for consistency. #86
23
+
24
+ Fixed
25
+ -----
26
+
27
+ - Fixed issue #84 where ``on_request_complete`` callbacks would fire N times per request in multi-app architectures, causing duplicate logging and metrics
28
+ - Fixed ``Otto.structured_log`` to respect ``Otto.debug`` flag - debug logs are now properly skipped when ``Otto.debug = false``
29
+
30
+ AI Assistance
31
+ -------------
32
+
33
+ - This enhancement was developed with assistance from Claude Code (Opus 4.1)
34
+
35
+ .. _changelog-2.0.0.pre5:
36
+
37
+ 2.0.0.pre5 — 2025-10-21
38
+ =======================
39
+
40
+ Added
41
+ -----
42
+
43
+ - Added ``Otto::LoggingHelpers.log_timed_operation`` for automatic timing and error handling of operations
44
+ - Added ``Otto::LoggingHelpers.log_backtrace`` for consistent backtrace logging with correlation fields
45
+ - Added microsecond-precision timing to configuration freeze process
46
+ - Added unique error ID generation for nested error handler failures (links via ``original_error_id``)
47
+
48
+ Changed
49
+ -------
50
+
51
+ - Timing precision standardization: All timing calculations now use microsecond precision instead of milliseconds. This affects authentication duration tracking and request lifecycle timing. Duration values are now reported in microseconds as integers (e.g., ``15200`` instead of ``15.2``).
52
+ - Request completion hooks API improvement: ``Otto.on_request_complete`` callbacks now receive a ``Rack::Response`` object instead of the raw ``[status, headers, body]`` tuple. This provides a more developer-friendly API consistent with ``Rack::Request``, allowing clean access via ``res.status``, ``res.headers``, and ``res.body`` instead of array indexing.
53
+ - All timing now uses microseconds (``Otto::Utils.now_in_μs``) for consistency
54
+ - Configuration freeze process now logs detailed timing metrics
55
+
56
+ Documentation
57
+ -------------
58
+
59
+ - Added example application demonstrating three new logging patterns (``examples/logging_improvements.rb``)
60
+ - Documented base context pattern for downstream projects to inject custom correlation fields
61
+ - Added output examples for both structured and standard loggers
62
+
63
+ AI Assistance
64
+ -------------
65
+
66
+ - This enhancement was developed with assistance from Claude Code (Opus 4.1)
67
+
68
+ .. _changelog-2.0.0.pre4:
69
+
70
+
71
+ 2.0.0.pre4 — 2025-10-20
72
+ =======================
73
+ Changed
74
+ -------
75
+ - Authentication moved from middleware to RouteAuthWrapper at handler level (executes after routing)
76
+ - RouteAuthWrapper now wraps all routes and provides session persistence, security headers, strategy caching, and pattern matching (exact, prefix, fallback)
77
+ - env['otto.strategy_result'] now guaranteed present on all routes (authenticated or anonymous)
78
+ - Renamed MiddlewareStack#build_app to #wrap (reflects per-request wrapping vs one-time initialization)
79
+
80
+ Removed
81
+ -------
82
+ - AuthenticationMiddleware (executed before routing)
83
+ - enable_authentication! (RouteAuthWrapper handles auth automatically)
84
+ - Defensive nil fallback from LogicClassHandler (no longer needed)
85
+
86
+ Fixed
87
+ -----
88
+ - Session persistence: env['rack.session'] now references same object as strategy_result.session
89
+ - Security headers included on all auth failure responses (401/302)
90
+ - Anonymous routes now receive StrategyResult with IP metadata
91
+
92
+ Documentation
93
+ -------------
94
+ - Updated CLAUDE.md with RouteAuthWrapper architecture
95
+ - Updated env_keys.rb to document strategy_result guarantee
96
+ - Added tests for anonymous route handling
97
+
98
+
10
99
  .. _changelog-2.0.0.pre2:
11
100
 
12
101
  2.0.0.pre2 — 2025-10-11
@@ -60,6 +149,7 @@ AI Assistance
60
149
  - Comprehensive migration of Logic classes and documentation with AI guidance for consistency
61
150
  - Automated test validation and intelligent file organization following Ruby conventions
62
151
 
152
+
63
153
  .. _changelog-2.0.0-pre1:
64
154
 
65
155
  2.0.0-pre1 — 2025-09-10