ruby_llm-browser 0.1.0 → 0.3.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/CONTRIBUTING.md +8 -1
  4. data/README.md +25 -96
  5. data/docs/API.md +5 -172
  6. data/docs/RAILS.md +5 -162
  7. data/docs/README.md +23 -12
  8. data/docs/RELEASE_ACTION_PLAN.md +28 -13
  9. data/docs/RELEASE_AUDIT.md +19 -0
  10. data/docs/RELEASING.md +24 -12
  11. data/docs/SECURITY_MODEL.md +1 -1
  12. data/docs/WORK.md +22 -0
  13. data/docs/_advanced/development-and-testing.md +107 -0
  14. data/docs/_advanced/live-backend-qualification.md +5 -0
  15. data/docs/_advanced/rails-integration.md +147 -29
  16. data/docs/_advanced/result-delivery.md +119 -0
  17. data/docs/_config.yml +8 -4
  18. data/docs/_core_features/diagnostics.md +68 -0
  19. data/docs/_core_features/inspect-extract.md +18 -2
  20. data/docs/_data/navigation.yml +2 -0
  21. data/docs/_getting_started/getting-started.md +11 -1
  22. data/docs/_getting_started/overview.md +3 -0
  23. data/docs/_getting_started/whats-new-in-0-3.md +48 -0
  24. data/docs/_plugins/versioned_docs.rb +3 -3
  25. data/docs/_reference/api-and-configuration.md +203 -0
  26. data/docs/_reference/api-reference.md +3 -2
  27. data/docs/_reference/release-history.md +3 -1
  28. data/docs/_reference/roadmap.md +171 -0
  29. data/docs/_reference/upgrading.md +48 -14
  30. data/docs/_reference/whats-new-in-0-2.md +46 -0
  31. data/docs/index.md +2 -3
  32. data/examples/plain_ruby.rb +7 -4
  33. data/examples/rails_results.rb +141 -0
  34. data/lib/ruby_llm/browser/doctor.rb +25 -10
  35. data/lib/ruby_llm/browser/rails/operation_job.rb +16 -4
  36. data/lib/ruby_llm/browser/rails/result_delivery_error.rb +22 -0
  37. data/lib/ruby_llm/browser/rails.rb +16 -0
  38. data/lib/ruby_llm/browser/result.rb +3 -3
  39. data/lib/ruby_llm/browser/version.rb +1 -1
  40. data/lib/ruby_llm/browser.rb +11 -0
  41. metadata +11 -2
@@ -7,26 +7,41 @@ llms: false
7
7
 
8
8
  # Release action plan
9
9
 
10
+ This is the historical 0.1.0 release record. Use the
11
+ [roadmap to 1.0]({% link _reference/roadmap.md %}) for upcoming development.
12
+
10
13
  The owner selected the offline-only first release as `0.1.0`, authorized its publication,
11
- and requested tag-based release automation. This plan preserves the live-provider limits
12
- and records the release checks.
14
+ and requested tag-based release automation. Version `0.1.0` is published. This plan records
15
+ the outcome and preserves the live-provider limits for future work.
13
16
 
14
17
  ## Current position
15
18
 
16
- - Hardening baseline: `0ec34d4`.
17
- - Expanded remote CI is green: seven test jobs and the package/install/advisory job.
19
+ - Hardening baseline: `0ec34d4`; release workflow fix: `8f0e8e7`.
20
+ - Expanded remote CI passed before release. Tagged verification and publication passed in
21
+ [release run 39110148539](https://github.com/yorzi/ruby_llm-browser/actions/runs/39110148539).
22
+ - RubyGems lists [ruby_llm-browser 0.1.0](https://rubygems.org/gems/ruby_llm-browser).
23
+ Publication used the registered trusted publisher for `yorzi/ruby_llm-browser`,
24
+ `.github/workflows/release.yml`, and environment `release`; MFA publication is recorded.
25
+ - The [GitHub Release](https://github.com/yorzi/ruby_llm-browser/releases/tag/v0.1.0)
26
+ includes the gem and `SHA256SUMS`. The gem SHA-256 is
27
+ `0159bf5b4d50fc1a5d129ce8e6d8235ff9a5181704d5e8168c2facb429808ed3`.
28
+ - The post-release Pages build passed in
29
+ [Docs run 39110265391](https://github.com/yorzi/ruby_llm-browser/actions/runs/39110265391).
30
+ The [custom-domain docs site]({% link index.md %}) shows `0.1.0 (stable)`.
31
+ - The first publish attempt failed before upload because the action's optional
32
+ experimental attestation patch activated Ruby's default zlib before Bundler. The
33
+ unpublished tag was moved to a fix that disables that patch; OIDC publication remains.
18
34
  - Owner decision on 2026-10-18: first release is read-only; defer live Act and mark the
19
35
  built-in Stagehand backend unsupported until egress and cleanup are qualified.
20
36
  - No live provider calls are authorized or were run for this decision.
21
37
  - Independent H1 and owner H2 are complete for exact candidate Q
22
38
  `22d488987517b10dcd96d2713ed3d47e8818b36b`. H2 approves handoff, not RubyGems publication.
23
- - The RubyGems page for `ruby_llm-browser` returned 404 during the pre-release check on
24
- 2026-10-18; that did not reserve the name. The resolved 26-gem runtime license inventory
25
- matches installed gemspec metadata; Stagehand is covered by its pinned source MIT text.
26
- - The owner later instructed publication of gem version `0.1.0`; that instruction is
27
- separate from H2's handoff approval. RubyGems MFA is enabled on the signed-in publisher
28
- account. Trusted publishing must match this repository's `release.yml` workflow and
29
- `release` environment.
39
+ - The pre-release exact-name check returned 404 on 2026-10-18; it did not reserve the
40
+ name. Version `0.1.0` is now registered on RubyGems. The resolved 26-gem runtime license
41
+ inventory matches installed gemspec metadata; Stagehand is covered by its pinned source
42
+ MIT text.
43
+ - The owner instructed publication separately from H2's handoff approval. The gem requires
44
+ MFA, and RubyGems records the successful publication as MFA-protected.
30
45
 
31
46
  ## Release scope
32
47
 
@@ -42,9 +57,9 @@ exact Q `22d488987517b10dcd96d2713ed3d47e8818b36b`; the approved Specs candidate
42
57
  | --- | --- | --- |
43
58
  | Release scope / Act | H2 approved the read-only scope and KPI proposal against exact Q. Live Act remains a later-version feature. | Keep the Stagehand adapter disabled and preserve the H2 decision in release docs. |
44
59
  | Provider qualification | No live call is authorized for the first release. A later live release needs a named deployment and approval of data-processing/billing owners. | Record the unsupported egress and cleanup guarantees. Do not use a host assertion as qualification. |
45
- | Name and account | The owner authorized publication as `ruby_llm-browser`; the account has MFA enabled. | Recheck name availability at publication and use the pending trusted publisher configured for this repo and workflow. |
60
+ | Name and account | The owner authorized publication as `ruby_llm-browser`; version `0.1.0` is registered and MFA-protected. | Use the existing trusted publisher for future tags; do not add a long-lived RubyGems API key to GitHub. |
46
61
  | Formal approval | H1 and H2 are complete for exact Q; C/R registration and implementation baseline receipt remain pending. | Preserve the historical approval. Do not represent H2 as RubyGems authorization; the owner's later release instruction supplies that authorization. |
47
- | Publication | Owner instructed publication of package version `0.1.0`. | Verify the matching tag, run the release workflow, then record the RubyGems, GitHub Release and Pages outcomes. |
62
+ | Publication | Version `0.1.0` is published under tag `v0.1.0`; GitHub Release assets and stable Pages are live. | For each later version, align gem/docs/changelog metadata and push the matching tag. The release workflow verifies tests, advisories and package contents before OIDC publishing; Docs deploys after success. |
48
63
 
49
64
  ## Provider questions to resolve before live tests
50
65
 
@@ -156,3 +156,22 @@ does not authorize provider calls, make Stagehand supported, or implement live A
156
156
  approved Specs Q remains identified by its historical candidate label `0.3.0-draft`.
157
157
  RubyGems publication and GitHub Pages deployment results are recorded separately from
158
158
  this pre-release audit snapshot.
159
+
160
+ ## Publication outcome (2026-10-18)
161
+
162
+ The owner authorized publishing the offline-only `0.1.0` package after the earlier audit
163
+ snapshot. The RubyGems trusted publisher is registered for repository `yorzi/ruby_llm-browser`,
164
+ workflow `release.yml`, and environment `release`. The corrected tagged workflow
165
+ [39110148539](https://github.com/yorzi/ruby_llm-browser/actions/runs/39110148539) passed
166
+ tagged-package verification and published the gem with GitHub OIDC. RubyGems lists
167
+ [version 0.1.0](https://rubygems.org/gems/ruby_llm-browser), published with MFA metadata;
168
+ the [GitHub Release](https://github.com/yorzi/ruby_llm-browser/releases/tag/v0.1.0)
169
+ contains the gem and checksum. The custom-domain [documentation site]({% link index.md %})
170
+ is deployed and shows `0.1.0 (stable)`.
171
+
172
+ The first tag run, [39109944660](https://github.com/yorzi/ruby_llm-browser/actions/runs/39109944660),
173
+ failed before upload because the action's optional experimental attestation patch loaded
174
+ RubyGems before Bundler and activated Ruby's default zlib. The unpublished tag was moved
175
+ to a workflow fix that disables that optional patch; OIDC publishing remains active.
176
+ This release does not close provider qualification: no live egress, TTL, billing, or
177
+ cleanup calls were made. Stagehand remains unsupported, and full live Act remains deferred.
data/docs/RELEASING.md CHANGED
@@ -17,6 +17,10 @@ After reading this guide, you will know:
17
17
 
18
18
  ## Compatibility evidence
19
19
 
20
+ The source repository's `docs/evidence/V0_3_VALIDATION.md` records local 0.3.0 checks.
21
+ `docs/evidence/V0_3_RELEASE.md` records the exact remote CI, published artifact and
22
+ hosted documentation results. The earlier audit below is historical evidence.
23
+
20
24
  The 2026-10-18 audit runs on Ruby **3.3.5**, arm64 macOS, with Stagehand **3.21.0**,
21
25
  JSONSchemer **2.5.0**, and sqlite3 **2.10.0**. Full offline suites cover:
22
26
 
@@ -40,6 +44,7 @@ bundle exec rake
40
44
  bundle exec bundle-audit check --update
41
45
  bundle exec ruby examples/plain_ruby.rb
42
46
  bundle exec ruby examples/rails_adapter.rb
47
+ bundle exec ruby examples/rails_results.rb
43
48
 
44
49
  BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle install
45
50
  BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle exec rspec
@@ -47,6 +52,7 @@ BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle install
47
52
  BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rspec
48
53
 
49
54
  bundle exec rake build
55
+ bin/verify-release --candidate # for unreleased development candidates
50
56
  bundle exec ruby bin/verify-package --rails --audit
51
57
  ```
52
58
 
@@ -76,10 +82,10 @@ Verify all of the following:
76
82
  - Core require loads no Rails, Active Record, Active Job, or Stagehand client.
77
83
  - The optional Rails require loads on demand.
78
84
  - The generated migration and all README-linked guides are shipped.
79
- - Both examples run using the installed artifact.
85
+ - All shipped examples run using the installed artifact.
80
86
  - The artifact contains no keys, cookies, dumps, databases, lockfiles, or test fixtures.
81
87
  - Dependency/license inventory and known-advisory check match the resolved versions.
82
- - Record the artifact SHA-256, exact versions, commands and date in the WORK ledger.
88
+ - Record the artifact SHA-256, exact versions, commands and date in the candidate validation note.
83
89
 
84
90
  ## Release boundaries
85
91
 
@@ -101,18 +107,24 @@ made for `0.1.0`.
101
107
  ## Releasing a version
102
108
 
103
109
  For a later version, update the gem version in `lib/ruby_llm/browser/version.rb` and the
104
- documentation version in `docs/_config.yml` to the same value. Keep `released: false` on
105
- `main`; the documentation builder uses release tags for the stable site and builds the
106
- working tree under `/next/`. Add a matching release entry to `CHANGELOG.md` and
107
- `docs/_reference/release-history.md`.
110
+ documentation version in `docs/_config.yml` to the same value. Keep `released: false`
111
+ while preparing a candidate. Set it to `true` and update `stable_version` when the
112
+ owner authorizes a release. The documentation builder uses release tags
113
+ for the stable site and builds the working tree under `/next/`. Add a matching release
114
+ entry to `CHANGELOG.md` and `docs/_reference/release-history.md`.
115
+
116
+ During candidate development, keep changes under `Unreleased` in the changelog and
117
+ run `bin/verify-release --candidate`. Before tagging, move them to a dated release
118
+ section and run `bin/verify-release` without that flag. The tag workflow requires
119
+ released metadata; local candidate verification cannot enable publication.
108
120
 
109
121
  Run the checks above, review the package contents and push the completed commit to
110
- `main`. Then create and push a matching tag, for example `v0.2.0` for gem version
111
- `0.2.0`:
122
+ `main`. Then create and push a matching tag, for example `v{{ site.version }}` for gem version
123
+ `{{ site.version }}`:
112
124
 
113
125
  ```bash
114
- git tag -a v0.2.0 -m "Release 0.2.0"
115
- git push origin v0.2.0
126
+ git tag -a v{{ site.version }} -m "Release {{ site.version }}"
127
+ git push origin v{{ site.version }}
116
128
  ```
117
129
 
118
130
  The `Release Gem` workflow rejects a tag that does not match both the gem and docs
@@ -127,5 +139,5 @@ workflow `release.yml`, and environment `release` before the first tag.
127
139
  ## Next steps
128
140
 
129
141
  - Review the requirement evidence in the source repository's `docs/WORK.md` and the [remaining audit findings]({% link RELEASE_AUDIT.md %}).
130
- - Follow the source repository's `docs/RELEASE_ACTION_PLAN.md` for the selected scope and
131
- future version workflow.
142
+ - Follow the [roadmap to 1.0]({% link _reference/roadmap.md %}) for proposed version scope.
143
+ The source repository's `docs/RELEASE_ACTION_PLAN.md` preserves the 0.1.0 release record.
@@ -113,5 +113,5 @@ qualification. Cleanup and TTL are best-effort, and cost values are advisory.
113
113
 
114
114
  ## Next steps
115
115
 
116
- - Follow [Rails recovery]({% link RAILS.md %}) to schedule cleanup and protect status access.
116
+ - Follow [Rails recovery]({% link _advanced/rails-integration.md %}) to schedule cleanup and protect status access.
117
117
  - Review [the release audit]({% link RELEASE_AUDIT.md %}) for evidence and remaining blockers.
data/docs/WORK.md CHANGED
@@ -242,6 +242,28 @@ the tag's stable documentation only after that release workflow succeeds. The fi
242
242
  is pushed only after the trusted publisher is registered for this repository and the
243
243
  `release.yml` workflow under the `release` GitHub environment.
244
244
 
245
+ ### 0.1.0 publication receipt (2026-10-18)
246
+
247
+ The first tagged workflow run failed before the gem upload because the action's optional
248
+ experimental attestation patch loaded RubyGems before Bundler and activated Ruby 3.3's
249
+ default zlib instead of the locked patched zlib. No gem was published by that attempt.
250
+ The release action now has attestations disabled; trusted OIDC publishing remains enabled.
251
+ The unpublished `v0.1.0` tag was moved to the workflow-fix commit before retrying.
252
+
253
+ | Check | Result |
254
+ | --- | --- |
255
+ | Tagged release workflow | PASS: [run 39110148539](https://github.com/yorzi/ruby_llm-browser/actions/runs/39110148539); tagged package verification and RubyGems publish jobs succeeded |
256
+ | RubyGems | PASS: [ruby_llm-browser 0.1.0](https://rubygems.org/gems/ruby_llm-browser); published by `yorzi/ruby_llm-browser` via `.github/workflows/release.yml` and `release` environment; MFA publication is recorded |
257
+ | Gem SHA-256 | `0159bf5b4d50fc1a5d129ce8e6d8235ff9a5181704d5e8168c2facb429808ed3` |
258
+ | GitHub Release | PASS: [v0.1.0](https://github.com/yorzi/ruby_llm-browser/releases/tag/v0.1.0) contains the `.gem` file and `SHA256SUMS` |
259
+ | Documentation Pages | PASS: [run 39110265391](https://github.com/yorzi/ruby_llm-browser/actions/runs/39110265391); the [custom-domain docs site]({% link index.md %}) shows `0.1.0 (stable)` |
260
+ | Live provider egress, TTL, billing and cleanup | NOT RUN; Stagehand remains unsupported until provider qualification |
261
+ | Full live Act protocol | NOT IMPLEMENTED; offline simulation only, deferred to a later version |
262
+
263
+ The published gem metadata reports MFA publication. No long-lived RubyGems API key was
264
+ added to GitHub; the release workflow uses the registered trusted publisher and GitHub
265
+ OIDC. The first failed run was [39109944660](https://github.com/yorzi/ruby_llm-browser/actions/runs/39109944660).
266
+
245
267
  ## Commit log
246
268
 
247
269
  Small, meaningful commits land one piece at a time. See `git log` for the authoritative
@@ -0,0 +1,107 @@
1
+ ---
2
+ layout: default
3
+ title: Development and Testing
4
+ nav_order: 3
5
+ description: Verify changes with offline contracts, installed packages, and a local browser workbench.
6
+ ---
7
+
8
+ # {{ page.title }}
9
+
10
+ {{ page.description }}
11
+
12
+ After reading this guide, you will know:
13
+
14
+ - How to run the local verification loop.
15
+ - How to review browser behavior and retain fixture screenshots.
16
+ - What offline, browser, and installed-package checks establish.
17
+
18
+ ## Run the Offline Checks
19
+
20
+ ```sh
21
+ bin/setup
22
+ bin/check
23
+ ```
24
+
25
+ Start a behavior change with a focused failing spec. Implement the smallest public
26
+ contract, update its guide, then run `bin/check`. The command runs specs and RuboCop,
27
+ the RubyLLM 2.0 and Rails 8.0 alternate bundles, and the plain-Ruby and Rails examples.
28
+ Install each alternate bundle first if needed. Database suites run sequentially.
29
+ RuboCop uses the writable `tmp/rubocop` cache. Default tests block unmocked HTTP.
30
+
31
+ The Rails result suite executes serialized jobs in separate processes against temporary
32
+ SQLite files. It verifies reads after worker exit, actor/run isolation, conflicting
33
+ delivery rejection, persistence failures before and after commit, and the crash gap
34
+ between browser execution and host storage. HTTP stays blocked in those subprocesses.
35
+ A frozen 0.2.0 migration verifies preservation of existing rows and indexes. These
36
+ checks exercise host result delivery without adding a second browser fixture.
37
+
38
+ Commit each completed change with its docs and checks. Keep the next work item in the
39
+ [roadmap]({% link _reference/roadmap.md %}); do not create a second task ledger.
40
+
41
+ ## Review the Offline Workbench
42
+
43
+ ```sh
44
+ BUNDLE_GEMFILE=gemfiles/browser.gemfile bundle install
45
+ bin/workbench
46
+ # Open http://127.0.0.1:4024, then Ctrl-C to stop.
47
+ ```
48
+
49
+ The single fixture in `dev/workbench.rb` runs the real Ruby tools with FakeBackend,
50
+ a deterministic resolver, and an in-memory operation store. Inspect and Extract show
51
+ their returned hashes and fake start/cleanup counts. Enter an unapproved host to
52
+ exercise a denial, run offline Doctor, or check that Stagehand remains blocked. The
53
+ Stagehand diagnostic uses synthetic fixture credentials and never calls the provider.
54
+
55
+ The host serves local HTML and CSS, escapes returned content, requires a form token,
56
+ and accepts loopback Host values only. It is a development fixture, not a production
57
+ controller or a new public gem API. It does not load the requested page or use provider
58
+ credentials. The fixture and browser libraries are excluded from the gem.
59
+
60
+ ## Run Browser Tests
61
+
62
+ ```sh
63
+ bin/check-browser
64
+ # Or include browser tests after the offline checks:
65
+ bin/check --browser
66
+ ```
67
+
68
+ Use installed Chrome. `CHROME_BIN` can select its binary; `CHROMEDRIVER` can select an
69
+ installed matching driver to avoid Selenium Manager's driver resolution. Screenshots
70
+ go into the printed, ignored `tmp/browser/` directory. The runner fails if tests fail
71
+ or no screenshot is produced. CI retains fixture screenshots for seven days.
72
+
73
+ Browser scenarios cover inspect/extract, denial before dispatch, keyboard recovery at
74
+ a 390px device viewport, and offline/blocked-Stagehand Doctor output. Capture screenshots before closing the
75
+ active session, including on assertion failures. Inspect failure artifacts before
76
+ retrying. Screenshots support visual review; assertions establish the expected behavior.
77
+
78
+ The Puma host and Selenium port probe bind loopback. Browser test HTTP clients allow
79
+ loopback only; this is not a Chrome network firewall. No live provider request is part
80
+ of this suite. Tests close their driver sessions, and Puma ends with the test process.
81
+ Stop manual previews and verify no task-owned service remains after your review.
82
+
83
+ ## Verify a Candidate Package
84
+
85
+ ```sh
86
+ bin/check --browser --package
87
+ bin/verify-release --candidate
88
+ docs/bin/build.sh
89
+ ```
90
+
91
+ The package option builds the gem and runs `bin/verify-package --rails --audit`. It
92
+ installs into an empty GEM_HOME outside the checkout, resolves dependencies from the
93
+ registry, runs shipped examples, tests the generated migration, and audits the activated
94
+ runtime. Refresh the advisory database first with `bundle exec bundle-audit check --update`.
95
+ Package verification needs registry access; offline test success cannot replace it.
96
+
97
+ `bin/verify-release --candidate` checks version alignment, the Unreleased changelog,
98
+ the unreleased docs flag, and package exclusions. Without `--candidate`, it requires
99
+ released docs and a dated changelog; the tag workflow never passes the candidate flag.
100
+ The install verifier copies the input artifact before testing and prints that snapshot's
101
+ SHA-256. A later build cannot replace the file under verification.
102
+
103
+ Record the exact artifact hash, dependency versions, checks, and not-run boundaries in
104
+ the candidate validation note. Local browser tests prove fixture application wiring.
105
+ They do not establish real provider egress, session TTL, cleanup, billing, external
106
+ adoption, remote CI success, or publication. Follow the
107
+ [release checklist]({% link RELEASING.md %}) before releasing.
@@ -34,6 +34,11 @@ built-in Stagehand backend as unsupported: `egress_isolation` remains `:unverifi
34
34
  the Engine refuses to start a session. The host cannot override this status with a
35
35
  constructor option. A custom backend must enforce its own browser-side network boundary.
36
36
 
37
+ The fake report has `mode: :offline` and skips provider credential and SDK checks.
38
+ Stagehand reports `mode: :live` even though it remains unsupported; mode describes
39
+ the backend, not its qualification. See [Checking Configuration]({% link _core_features/diagnostics.md %})
40
+ for the report fields and checks applied to each backend.
41
+
37
42
  Browserbase's documented [Allowed Domains feature](https://docs.browserbase.com/platform/browser/security/allowed-domains)
38
43
  applies to top-frame navigations. It does not block iframe or subresource requests such
39
44
  as scripts, images, and XHR. That setting alone cannot qualify provider egress. Any future
@@ -1,56 +1,174 @@
1
1
  ---
2
2
  layout: default
3
- title: Running work in Rails
3
+ title: Rails Jobs and Recovery
4
4
  nav_order: 1
5
- description: Run durable inspect and extract operations with the Rails 8 adapter.
5
+ redirect_from:
6
+ - /RAILS/
7
+ description: Run bounded read-only operations with durable state and independently scheduled cleanup.
6
8
  ---
7
9
 
8
- # {{ page.title }}
10
+ # Rails jobs and recovery
9
11
 
10
- {{ page.description }}
11
- {: .fs-6 .fw-300 }
12
+ Run bounded read-only operations with durable state and independently scheduled cleanup.
12
13
 
13
14
  After reading this guide, you will know:
14
15
 
15
- * Which framework versions and components the adapter expects.
16
- * How to install its tables.
17
- * What the host must own in background jobs and cleanup.
16
+ - How to install the adapter's tables.
17
+ - How to build a freshly authorized job context with matching budgets.
18
+ - How to deliver results to host storage.
19
+ - How to inspect status, cancel work, and recover orphaned sessions.
18
20
 
19
- ## Install the Adapter
21
+ ## Installing the adapter
20
22
 
21
- The adapter is optional. Add Rails 8 Active Record and Active Job to the host application,
22
- then require the Rails entry point and install its migration:
23
+ Your application supplies Rails 8, Active Record, Active Job, and a database adapter.
24
+ They are not runtime dependencies of the plain-Ruby gem.
23
25
 
24
26
  ```ruby
25
27
  require "ruby_llm/browser/rails"
26
28
  ```
27
29
 
28
- ```sh
30
+ ```bash
29
31
  bin/rails generate ruby_llm:browser:install
30
32
  bin/rails db:migrate
31
33
  ```
32
34
 
33
- The generator creates durable operation and approval tables plus per-actor reservation
34
- locks. The jobs accept `inspect` and `extract`; Act is not available in background jobs.
35
+ The generator creates operations, approvals, and per-actor reservation-lock tables.
36
+ SQLite and PostgreSQL are accepted by the store, but only SQLite concurrency is tested
37
+ locally. MySQL is not supported by this adapter's partial indexes/locking contract.
38
+ Use a database timeout appropriate to your workload. Database lock failures fail closed;
39
+ they must not be handled by retrying a browser call with a new operation ID.
35
40
 
36
- ## Keep Authorization in the Host
41
+ `Schema.create!` is for disposable fixtures, not production migrations or schema upgrades.
42
+ The [offline Rails example](https://github.com/yorzi/ruby_llm-browser/blob/main/examples/rails_adapter.rb) requires no full Rails app.
37
43
 
38
- The context factory runs in the worker and must reload trusted records, recheck actor and
39
- run authorization, and use the same operation store and budget limits as the reservation.
40
- Never put provider credentials or authorization decisions in model arguments or job
41
- payloads. Protect queue storage because URLs, instructions, and schemas are persisted.
44
+ ## Configuring the trusted context factory
42
45
 
43
- ## Schedule Recovery
46
+ ```ruby
47
+ # config/initializers/ruby_llm_browser.rb
48
+ require "ruby_llm/browser/rails"
49
+
50
+ browser_budget = RubyLLM::Browser::Budget.new(max_seconds: 30, max_steps: 6)
51
+ RubyLLM::Browser::Rails.context_factory = lambda do |actor_id:, run_id:|
52
+ # Resolve authenticated host records and recheck authorization, consent and revocation.
53
+ # Build policy/backend from those records, never from job or model-supplied credentials.
54
+ RubyLLM::Browser::Context.new(
55
+ actor_id: actor_id, run_id: run_id,
56
+ backend: qualified_backend, source_policy: policy,
57
+ operation_store: RubyLLM::Browser::Rails.operation_store,
58
+ budget: browser_budget
59
+ )
60
+ end
61
+ ```
62
+
63
+ `qualified_backend` and `policy` above are host objects, not global APIs supplied by the
64
+ gem. The factory must use the **same operation-store instance and budget limits** as
65
+ the queued reservation. Construct different budgets per run only from trusted records
66
+ that the factory can retrieve again. An optional approval store does not enable Act in
67
+ jobs; `OperationJob` accepts only `inspect` and `extract`.
68
+
69
+ ## Enqueuing a read-only operation
70
+
71
+ ```ruby
72
+ operation = RubyLLM::Browser::Rails::OperationJob.enqueue(
73
+ actor_id: current_user.id.to_s, run_id: research_run.id.to_s,
74
+ kind: :inspect, url: "https://docs.example.com/",
75
+ budget: browser_budget, idempotency_key: "research-#{research_run.id}"
76
+ )
77
+ ```
78
+
79
+ For extraction, pass `kind: :extract` and a host-pinned JSON `schema:`. Optional
80
+ `instruction:` and `max_bytes:` have the same meaning as on the tools.
81
+
82
+ One actor/run/key identifies one exact payload and budget. A changed payload using the
83
+ same key is rejected. Repeated enqueue calls may add duplicate queue deliveries, but
84
+ they attach to the same durable operation. Starting is atomically claimed once. A job
85
+ that sees a started, unknown, pending-cleanup, or terminal operation cannot start it
86
+ again. No automatic browser retry is configured.
87
+
88
+ Reservation and queue insertion are separate transactions. If enqueueing fails, retain
89
+ the returned operation identity when available and use the same idempotency key after
90
+ investigation. Schedule the reaper to reclaim expired reservations that never started.
44
91
 
45
- Run `RubyLLM::Browser::Rails::Reaper` on a separate schedule. Worker cleanup alone cannot
46
- recover sessions left by a killed process. Do not free quota for an unknown or pending
47
- session based only on elapsed time. The job does not durably store result bodies, so the
48
- host must implement authorized result storage and delivery if it needs them.
92
+ Configure `RubyLLM::Browser::Rails.job_queue = :browser` if you need a separate queue.
93
+ Jobs suppress argument logging, but queues still persist URLs, instructions and schemas.
94
+ Protect queue access, storage, retention and encryption in the host application.
95
+
96
+ ## Reading status and progress
97
+
98
+ ```ruby
99
+ store = RubyLLM::Browser::Rails.operation_store
100
+ operation = store.fetch!(operation_id)
101
+ # Authorize this read against the authenticated actor/run before returning anything.
102
+ summary = operation.summary
103
+ ```
104
+
105
+ `summary` omits provider session IDs and budget internals. It reports state, steps,
106
+ bytes, fence, timestamps, error code and cancellation flag. It is coarse progress,
107
+ not a percentage-complete promise. To persist a result body, opt in to
108
+ `RubyLLM::Browser::Rails.result_delivery`. The callback receives `operation:` and
109
+ `result:` after execution and cleanup. Your application owns storage and authorized
110
+ retrieval. Read [Rails result delivery]({% link _advanced/result-delivery.md %}) for
111
+ failure handling and recovery. Do not rely on an Active Job return value after
112
+ asynchronous execution.
113
+
114
+ The stores are trusted server APIs, not tenant-safe controllers. They expose unscoped
115
+ fetch/sweep methods for workers; never expose them directly to model arguments or HTTP.
116
+ Tool progress callbacks are feature-detected on RubyLLM 2.1+, and contain no raw URL.
117
+
118
+ ## Cancelling and reaping
119
+
120
+ ```ruby
121
+ store.cancel(operation_id, actor_id: current_user.id.to_s,
122
+ run_id: research_run.id.to_s, now: Time.now.utc)
123
+ RubyLLM::Browser::Rails::Reaper.new(backend: qualified_backend).sweep
124
+ ```
125
+
126
+ Cancellation of a never-started reservation is terminal immediately. Otherwise it sets
127
+ `cancel_requested`, fences stale workers, and remains `cleanup_pending` with quota held.
128
+ It cannot undo an in-flight provider request. A returned session ID is persisted even
129
+ if cancellation raced with session creation.
130
+
131
+ Run a reaper regularly in an independent scheduler, not only in the operation worker's
132
+ ensure block. A reaper fences workers **before** reconciliation/cleanup, tries to end
133
+ known sessions even when provider reconciliation is unavailable, and reports `failed`
134
+ rather than claiming a database write succeeded. `ttl_seconds:` is an optional positive
135
+ idle threshold, not proof that the provider's billable session has expired.
136
+
137
+ Stagehand exposes no correlation endpoint here. An unknown start without a session ID
138
+ remains unknown and holds quota until the host supplies provider-confirmed evidence.
139
+ Alert on unknown/pending/failed entries. Never free quota just because enough local time
140
+ passed. An unknown action outcome remains in the result/error code even if session
141
+ cleanup succeeds; do not replay it.
142
+
143
+ One reaper/backend instance must cover only operations belonging to that provider
144
+ configuration. The current table has no backend credential-configuration binding.
145
+ Multi-provider/multi-credential deployments need a host-scoped store or a future schema
146
+ extension before a global sweep is safe.
147
+
148
+ ## Upgrading an earlier development snapshot
149
+
150
+ If you installed the generator from an earlier development snapshot, preserve your data
151
+ and add an explicit migration for these new fields instead of rerunning `create_table`
152
+ or dropping the database:
153
+
154
+ ```ruby
155
+ class HardenBrowserBookkeeping < ActiveRecord::Migration[8.1]
156
+ def change
157
+ create_table :ruby_llm_browser_actors, id: :string do |t|
158
+ t.integer :sequence, null: false, default: 0
159
+ end
160
+ add_column :ruby_llm_browser_operations, :cancel_requested, :boolean, default: false, null: false
161
+ add_column :ruby_llm_browser_operations, :request_digest, :string
162
+ end
163
+ end
164
+ ```
49
165
 
50
- SQLite concurrency is exercised locally. PostgreSQL still needs separate qualification,
51
- and MySQL is not supported by the adapter's locking contract. See the [full Rails guide]({% link RAILS.md %}) for a context factory, enqueueing, status access, migration recovery, and reaper details.
166
+ Adapt this migration if any additions already exist. Drain workers during the upgrade.
167
+ Legacy rows without request digests are not payload-bound, so do not reuse their
168
+ idempotency keys for new work. Inspect earlier terminal cancellations for sessions that
169
+ may not have been confirmed closed before reclaiming them.
52
170
 
53
- ## Next Steps
171
+ ## Next steps
54
172
 
55
- * [Bounding operation work]({% link _core_features/operation-lifecycle.md %}) - understand budgets and state transitions.
56
- * [Qualifying a live backend]({% link _advanced/live-backend-qualification.md %}) - review provider requirements.
173
+ - Review [the API]({% link _reference/api-and-configuration.md %}) for result interpretation and budget semantics.
174
+ - Review [release blockers]({% link RELEASE_AUDIT.md %}) before production deployment.