ruby_llm-browser 0.1.0 → 0.2.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.
@@ -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
@@ -47,6 +47,7 @@ BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle install
47
47
  BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rspec
48
48
 
49
49
  bundle exec rake build
50
+ bin/verify-release --candidate # for unreleased development candidates
50
51
  bundle exec ruby bin/verify-package --rails --audit
51
52
  ```
52
53
 
@@ -101,10 +102,16 @@ made for `0.1.0`.
101
102
  ## Releasing a version
102
103
 
103
104
  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`.
105
+ documentation version in `docs/_config.yml` to the same value. Keep `released: false`
106
+ while preparing a candidate. Set it to `true` and update `stable_version` when the
107
+ owner authorizes a release. The documentation builder uses release tags
108
+ for the stable site and builds the working tree under `/next/`. Add a matching release
109
+ entry to `CHANGELOG.md` and `docs/_reference/release-history.md`.
110
+
111
+ During candidate development, keep changes under `Unreleased` in the changelog and
112
+ run `bin/verify-release --candidate`. Before tagging, move them to a dated release
113
+ section and run `bin/verify-release` without that flag. The tag workflow requires
114
+ released metadata; local candidate verification cannot enable publication.
108
115
 
109
116
  Run the checks above, review the package contents and push the completed commit to
110
117
  `main`. Then create and push a matching tag, for example `v0.2.0` for gem version
@@ -127,5 +134,5 @@ workflow `release.yml`, and environment `release` before the first tag.
127
134
  ## Next steps
128
135
 
129
136
  - 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.
137
+ - Follow the [roadmap to 1.0]({% link _reference/roadmap.md %}) for proposed version scope.
138
+ 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,100 @@
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
+ Commit each completed change with its docs and checks. Keep the next work item in the
32
+ [roadmap]({% link _reference/roadmap.md %}); do not create a second task ledger.
33
+
34
+ ## Review the Offline Workbench
35
+
36
+ ```sh
37
+ BUNDLE_GEMFILE=gemfiles/browser.gemfile bundle install
38
+ bin/workbench
39
+ # Open http://127.0.0.1:4024, then Ctrl-C to stop.
40
+ ```
41
+
42
+ The single fixture in `dev/workbench.rb` runs the real Ruby tools with FakeBackend,
43
+ a deterministic resolver, and an in-memory operation store. Inspect and Extract show
44
+ their returned hashes and fake start/cleanup counts. Enter an unapproved host to
45
+ exercise a denial, run offline Doctor, or check that Stagehand remains blocked. The
46
+ Stagehand diagnostic uses synthetic fixture credentials and never calls the provider.
47
+
48
+ The host serves local HTML and CSS, escapes returned content, requires a form token,
49
+ and accepts loopback Host values only. It is a development fixture, not a production
50
+ controller or a new public gem API. It does not load the requested page or use provider
51
+ credentials. The fixture and browser libraries are excluded from the gem.
52
+
53
+ ## Run Browser Tests
54
+
55
+ ```sh
56
+ bin/check-browser
57
+ # Or include browser tests after the offline checks:
58
+ bin/check --browser
59
+ ```
60
+
61
+ Use installed Chrome. `CHROME_BIN` can select its binary; `CHROMEDRIVER` can select an
62
+ installed matching driver to avoid Selenium Manager's driver resolution. Screenshots
63
+ go into the printed, ignored `tmp/browser/` directory. The runner fails if tests fail
64
+ or no screenshot is produced. CI retains fixture screenshots for seven days.
65
+
66
+ Browser scenarios cover inspect/extract, denial before dispatch, keyboard recovery at
67
+ a 390px device viewport, and offline/blocked-Stagehand Doctor output. Capture screenshots before closing the
68
+ active session, including on assertion failures. Inspect failure artifacts before
69
+ retrying. Screenshots support visual review; assertions establish the expected behavior.
70
+
71
+ The Puma host and Selenium port probe bind loopback. Browser test HTTP clients allow
72
+ loopback only; this is not a Chrome network firewall. No live provider request is part
73
+ of this suite. Tests close their driver sessions, and Puma ends with the test process.
74
+ Stop manual previews and verify no task-owned service remains after your review.
75
+
76
+ ## Verify a Candidate Package
77
+
78
+ ```sh
79
+ bin/check --browser --package
80
+ bin/verify-release --candidate
81
+ docs/bin/build.sh
82
+ ```
83
+
84
+ The package option builds the gem and runs `bin/verify-package --rails --audit`. It
85
+ installs into an empty GEM_HOME outside the checkout, resolves dependencies from the
86
+ registry, runs shipped examples, tests the generated migration, and audits the activated
87
+ runtime. Refresh the advisory database first with `bundle exec bundle-audit check --update`.
88
+ Package verification needs registry access; offline test success cannot replace it.
89
+
90
+ `bin/verify-release --candidate` checks version alignment, the Unreleased changelog,
91
+ the unreleased docs flag, and package exclusions. Without `--candidate`, it requires
92
+ released docs and a dated changelog; the tag workflow never passes the candidate flag.
93
+ The install verifier copies the input artifact before testing and prints that snapshot's
94
+ SHA-256. A later build cannot replace the file under verification.
95
+
96
+ Record the exact artifact hash, dependency versions, checks, and not-run boundaries in
97
+ the candidate validation note. Local browser tests prove fixture application wiring.
98
+ They do not establish real provider egress, session TTL, cleanup, billing, external
99
+ adoption, remote CI success, or publication. Follow the
100
+ [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,170 @@
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 inspect status, cancel work, and recover orphaned sessions.
18
19
 
19
- ## Install the Adapter
20
+ ## Installing the adapter
20
21
 
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:
22
+ Your application supplies Rails 8, Active Record, Active Job, and a database adapter.
23
+ They are not runtime dependencies of the plain-Ruby gem.
23
24
 
24
25
  ```ruby
25
26
  require "ruby_llm/browser/rails"
26
27
  ```
27
28
 
28
- ```sh
29
+ ```bash
29
30
  bin/rails generate ruby_llm:browser:install
30
31
  bin/rails db:migrate
31
32
  ```
32
33
 
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.
34
+ The generator creates operations, approvals, and per-actor reservation-lock tables.
35
+ SQLite and PostgreSQL are accepted by the store, but only SQLite concurrency is tested
36
+ locally. MySQL is not supported by this adapter's partial indexes/locking contract.
37
+ Use a database timeout appropriate to your workload. Database lock failures fail closed;
38
+ they must not be handled by retrying a browser call with a new operation ID.
35
39
 
36
- ## Keep Authorization in the Host
40
+ `Schema.create!` is for disposable fixtures, not production migrations or schema upgrades.
41
+ The [offline Rails example](https://github.com/yorzi/ruby_llm-browser/blob/main/examples/rails_adapter.rb) requires no full Rails app.
37
42
 
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.
43
+ ## Configuring the trusted context factory
42
44
 
43
- ## Schedule Recovery
45
+ ```ruby
46
+ # config/initializers/ruby_llm_browser.rb
47
+ require "ruby_llm/browser/rails"
48
+
49
+ browser_budget = RubyLLM::Browser::Budget.new(max_seconds: 30, max_steps: 6)
50
+ RubyLLM::Browser::Rails.context_factory = lambda do |actor_id:, run_id:|
51
+ # Resolve authenticated host records and recheck authorization, consent and revocation.
52
+ # Build policy/backend from those records, never from job or model-supplied credentials.
53
+ RubyLLM::Browser::Context.new(
54
+ actor_id: actor_id, run_id: run_id,
55
+ backend: qualified_backend, source_policy: policy,
56
+ operation_store: RubyLLM::Browser::Rails.operation_store,
57
+ budget: browser_budget
58
+ )
59
+ end
60
+ ```
61
+
62
+ `qualified_backend` and `policy` above are host objects, not global APIs supplied by the
63
+ gem. The factory must use the **same operation-store instance and budget limits** as
64
+ the queued reservation. Construct different budgets per run only from trusted records
65
+ that the factory can retrieve again. An optional approval store does not enable Act in
66
+ jobs; `OperationJob` accepts only `inspect` and `extract`.
67
+
68
+ ## Enqueuing a read-only operation
69
+
70
+ ```ruby
71
+ operation = RubyLLM::Browser::Rails::OperationJob.enqueue(
72
+ actor_id: current_user.id.to_s, run_id: research_run.id.to_s,
73
+ kind: :inspect, url: "https://docs.example.com/",
74
+ budget: browser_budget, idempotency_key: "research-#{research_run.id}"
75
+ )
76
+ ```
77
+
78
+ For extraction, pass `kind: :extract` and a host-pinned JSON `schema:`. Optional
79
+ `instruction:` and `max_bytes:` have the same meaning as on the tools.
80
+
81
+ One actor/run/key identifies one exact payload and budget. A changed payload using the
82
+ same key is rejected. Repeated enqueue calls may add duplicate queue deliveries, but
83
+ they attach to the same durable operation. Starting is atomically claimed once. A job
84
+ that sees a started, unknown, pending-cleanup, or terminal operation cannot start it
85
+ again. No automatic browser retry is configured.
86
+
87
+ Reservation and queue insertion are separate transactions. If enqueueing fails, retain
88
+ the returned operation identity when available and use the same idempotency key after
89
+ investigation. Schedule the reaper to reclaim expired reservations that never started.
44
90
 
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.
91
+ Configure `RubyLLM::Browser::Rails.job_queue = :browser` if you need a separate queue.
92
+ Jobs suppress argument logging, but queues still persist URLs, instructions and schemas.
93
+ Protect queue access, storage, retention and encryption in the host application.
94
+
95
+ ## Reading status and progress
96
+
97
+ ```ruby
98
+ store = RubyLLM::Browser::Rails.operation_store
99
+ operation = store.fetch!(operation_id)
100
+ # Authorize this read against the authenticated actor/run before returning anything.
101
+ summary = operation.summary
102
+ ```
103
+
104
+ `summary` omits provider session IDs and budget internals. It reports state, steps,
105
+ bytes, fence, timestamps, error code and cancellation flag. It is coarse progress,
106
+ not a percentage-complete promise. No result body is durably stored by the job; the
107
+ host must explicitly implement authorized result storage/delivery if needed. Do not
108
+ rely on an Active Job return value after asynchronous execution.
109
+
110
+ The stores are trusted server APIs, not tenant-safe controllers. They expose unscoped
111
+ fetch/sweep methods for workers; never expose them directly to model arguments or HTTP.
112
+ Tool progress callbacks are feature-detected on RubyLLM 2.1+, and contain no raw URL.
113
+
114
+ ## Cancelling and reaping
115
+
116
+ ```ruby
117
+ store.cancel(operation_id, actor_id: current_user.id.to_s,
118
+ run_id: research_run.id.to_s, now: Time.now.utc)
119
+ RubyLLM::Browser::Rails::Reaper.new(backend: qualified_backend).sweep
120
+ ```
121
+
122
+ Cancellation of a never-started reservation is terminal immediately. Otherwise it sets
123
+ `cancel_requested`, fences stale workers, and remains `cleanup_pending` with quota held.
124
+ It cannot undo an in-flight provider request. A returned session ID is persisted even
125
+ if cancellation raced with session creation.
126
+
127
+ Run a reaper regularly in an independent scheduler, not only in the operation worker's
128
+ ensure block. A reaper fences workers **before** reconciliation/cleanup, tries to end
129
+ known sessions even when provider reconciliation is unavailable, and reports `failed`
130
+ rather than claiming a database write succeeded. `ttl_seconds:` is an optional positive
131
+ idle threshold, not proof that the provider's billable session has expired.
132
+
133
+ Stagehand exposes no correlation endpoint here. An unknown start without a session ID
134
+ remains unknown and holds quota until the host supplies provider-confirmed evidence.
135
+ Alert on unknown/pending/failed entries. Never free quota just because enough local time
136
+ passed. An unknown action outcome remains in the result/error code even if session
137
+ cleanup succeeds; do not replay it.
138
+
139
+ One reaper/backend instance must cover only operations belonging to that provider
140
+ configuration. The current table has no backend credential-configuration binding.
141
+ Multi-provider/multi-credential deployments need a host-scoped store or a future schema
142
+ extension before a global sweep is safe.
143
+
144
+ ## Upgrading an earlier development snapshot
145
+
146
+ If you installed the generator from an earlier development snapshot, preserve your data
147
+ and add an explicit migration for these new fields instead of rerunning `create_table`
148
+ or dropping the database:
149
+
150
+ ```ruby
151
+ class HardenBrowserBookkeeping < ActiveRecord::Migration[8.1]
152
+ def change
153
+ create_table :ruby_llm_browser_actors, id: :string do |t|
154
+ t.integer :sequence, null: false, default: 0
155
+ end
156
+ add_column :ruby_llm_browser_operations, :cancel_requested, :boolean, default: false, null: false
157
+ add_column :ruby_llm_browser_operations, :request_digest, :string
158
+ end
159
+ end
160
+ ```
49
161
 
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.
162
+ Adapt this migration if any additions already exist. Drain workers during the upgrade.
163
+ Legacy rows without request digests are not payload-bound, so do not reuse their
164
+ idempotency keys for new work. Inspect earlier terminal cancellations for sessions that
165
+ may not have been confirmed closed before reclaiming them.
52
166
 
53
- ## Next Steps
167
+ ## Next steps
54
168
 
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.
169
+ - Review [the API]({% link _reference/api-and-configuration.md %}) for result interpretation and budget semantics.
170
+ - Review [release blockers]({% link RELEASE_AUDIT.md %}) before production deployment.
data/docs/_config.yml CHANGED
@@ -94,16 +94,20 @@ exclude:
94
94
  - Gemfile
95
95
  - Gemfile.lock
96
96
  - README.md
97
+ - API.md
98
+ - RAILS.md
97
99
  - RELEASE_ACTION_PLAN.md
98
100
  - SPEC_BASELINE.md
99
101
  - WORK.md
102
+ - evidence
100
103
 
101
104
  # The language of code examples; `project-docs` uses it for new pages.
102
105
  code_language: ruby
103
106
 
104
107
  # Values that change between releases. Pages read them as {{ site.version }}
105
- # and {{ site.examples.NAME }}. Keep `released: false` on main; the docs builder
106
- # reads a release's version from its matching Git tag.
107
- released: false
108
- version: "0.1.0"
108
+ # and {{ site.examples.NAME }}. Candidates remain unreleased; the docs builder
109
+ # reads stable docs from the latest release tag and main under /next/.
110
+ released: true
111
+ version: "0.2.0"
112
+ stable_version: "0.2.0"
109
113
  examples: {}
@@ -0,0 +1,68 @@
1
+ ---
2
+ layout: default
3
+ title: Checking Configuration
4
+ nav_order: 5
5
+ description: Diagnose the configured backend without opening a session or making a provider request.
6
+ ---
7
+
8
+ # {{ page.title }}
9
+
10
+ {{ page.description }}
11
+
12
+ After reading this guide, you will know:
13
+
14
+ - How to check offline configuration without provider credentials.
15
+ - Which checks apply to each backend.
16
+ - Why a local pass cannot qualify a live deployment.
17
+
18
+ ## Check an Offline Backend
19
+
20
+ ```ruby
21
+ require "ruby_llm/browser"
22
+
23
+ report = RubyLLM::Browser::Doctor.new(backend: RubyLLM::Browser::FakeBackend.new).report
24
+ puts report[:mode] # => offline
25
+ puts report[:ok] # => true
26
+ puts report[:summary]
27
+ # => configuration checks passed; live provider guarantees are not verified
28
+ ```
29
+
30
+ Doctor checks RubyLLM compatibility and the fake backend's capabilities. It does not
31
+ load the Stagehand SDK, inspect provider credentials or endpoint overrides, or open a
32
+ session. You do not need provider credentials to run offline checks.
33
+
34
+ ## Interpret the Report
35
+
36
+ Use `ok` to decide whether configuration checks passed. Use `mode` to identify which
37
+ backend the report describes, not whether that backend is supported. The existing
38
+ `summary` and `checks` fields remain available. Each check has `name`, `status`, and
39
+ `detail`; statuses are `pass`, `warn`, `info`, or `fail`. Any failure makes `ok` false.
40
+
41
+ | Mode | Meaning | Checks |
42
+ | --- | --- | --- |
43
+ | `offline` | FakeBackend with fake server mode | RubyLLM and backend capabilities; no provider environment or SDK checks |
44
+ | `live` | Stagehand or another non-offline backend | RubyLLM and backend capabilities; Stagehand also checks its SDK and environment |
45
+ | `unconfigured` | No backend supplied | RubyLLM and a failed backend/egress check |
46
+
47
+ A custom backend does not depend on Stagehand diagnostics. Its capability checks
48
+ still enforce unverified egress, an unpinned local server, and unapproved custom-header
49
+ capabilities as failures. A backend's enforced-egress assertion is not independent
50
+ evidence of its deployment behavior.
51
+
52
+ ## Diagnose Stagehand Without Calling It
53
+
54
+ Pass your host-created Stagehand backend to `Doctor.new(backend:)`. It reports `live`
55
+ mode and a failed `egress_isolation` check because the built-in adapter is unsupported.
56
+ Having an installed SDK or environment credentials cannot enable live operations.
57
+
58
+ Stagehand diagnostics report the SDK version, endpoint override names, and credential
59
+ presence in the supplied environment without printing values. These observations do
60
+ not configure the backend or replace explicit constructor credentials. Doctor never
61
+ calls the provider to test credentials, billing, egress, or session cleanup.
62
+
63
+ ## Next Steps
64
+
65
+ - Read [live-backend qualification]({% link _advanced/live-backend-qualification.md %})
66
+ for the deployment evidence needed before live use.
67
+ - Use [development and testing]({% link _advanced/development-and-testing.md %}) to
68
+ exercise diagnostics in the offline browser workbench.
@@ -41,8 +41,7 @@ schema = {
41
41
  "required" => [ "title" ], "additionalProperties" => false
42
42
  }
43
43
 
44
- inspect_tool = RubyLLM::Browser::Inspect.new(context: context)
45
- extract_tool = RubyLLM::Browser::Extract.new(context: context, schema: schema)
44
+ inspect_tool, extract_tool = RubyLLM::Browser.tools(context: context, schema: schema)
46
45
 
47
46
  puts inspect_tool.execute(url: "https://docs.example.com/")[:status] # => ok
48
47
  puts extract_tool.execute(url: "https://docs.example.com/")[:data] # => {"title"=>"Example"}
@@ -54,6 +53,23 @@ instances on a configured RubyLLM chat. The host should select the context and s
54
53
  the model supplies only the URL, optional instruction, and an optional positive
55
54
  `max_bytes` limit.
56
55
 
56
+ ## Register the Read-only Pair
57
+
58
+ Once your host has a context and schema, register both tools in one call:
59
+
60
+ ```ruby
61
+ chat.with_tools(*RubyLLM::Browser.tools(context: context, schema: schema))
62
+ ```
63
+
64
+ The factory returns fresh Inspect and Extract instances in that order. Both use the
65
+ same context; Extract keeps its own immutable schema copy. It never includes Act,
66
+ loads credentials, or starts an operation. A missing context or schema is rejected
67
+ by the existing constructors. Schema contents are checked by policy when Extract
68
+ runs, before a session starts.
69
+
70
+ You can still construct either tool separately with `Inspect.new(context:)` or
71
+ `Extract.new(context:, schema:)`. That remains useful when a chat needs only one tool.
72
+
57
73
  ## Inspect a Page
58
74
 
59
75
  `Inspect` navigates to one approved URL and asks the backend for observations. It does
@@ -8,5 +8,7 @@
8
8
  url: /api-reference/
9
9
  collections:
10
10
  - reference
11
+ - title: "What's New"
12
+ url: /whats-new-in-0-2/
11
13
  - title: Releases
12
14
  url: https://github.com/yorzi/ruby_llm-browser/releases