ruby_llm-browser 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 +7 -0
- data/CHANGELOG.md +61 -0
- data/CODE_OF_CONDUCT.md +11 -0
- data/CONTRIBUTING.md +42 -0
- data/LICENSE.txt +21 -0
- data/NOTICE.md +92 -0
- data/README.md +139 -0
- data/SECURITY.md +46 -0
- data/docs/API.md +177 -0
- data/docs/CNAME +1 -0
- data/docs/RAILS.md +167 -0
- data/docs/README.md +16 -0
- data/docs/RELEASE_ACTION_PLAN.md +81 -0
- data/docs/RELEASE_AUDIT.md +158 -0
- data/docs/RELEASING.md +131 -0
- data/docs/SECURITY_MODEL.md +117 -0
- data/docs/SPEC_BASELINE.md +107 -0
- data/docs/WORK.md +260 -0
- data/docs/_advanced/live-backend-qualification.md +74 -0
- data/docs/_advanced/rails-integration.md +56 -0
- data/docs/_config.yml +109 -0
- data/docs/_core_features/inspect-extract.md +91 -0
- data/docs/_core_features/offline-act.md +110 -0
- data/docs/_core_features/operation-lifecycle.md +63 -0
- data/docs/_core_features/source-policy.md +61 -0
- data/docs/_data/navigation.yml +12 -0
- data/docs/_data/sidebar.yml +8 -0
- data/docs/_data/social_links.yml +3 -0
- data/docs/_getting_started/configuration.md +68 -0
- data/docs/_getting_started/getting-started.md +77 -0
- data/docs/_getting_started/overview.md +52 -0
- data/docs/_plugins/versioned_docs.rb +46 -0
- data/docs/_reference/api-reference.md +29 -0
- data/docs/_reference/release-history.md +23 -0
- data/docs/_reference/upgrading.md +33 -0
- data/docs/bin/build-api.sh +23 -0
- data/docs/bin/build.sh +161 -0
- data/docs/bin/export-api-markdown.rb +53 -0
- data/docs/bin/link_versions.rb +54 -0
- data/docs/bin/postprocess_api_seo.rb +129 -0
- data/docs/bin/prepare_versions.rb +40 -0
- data/docs/bin/serve.sh +12 -0
- data/docs/bin/versions.yml +14 -0
- data/docs/index.md +82 -0
- data/examples/plain_ruby.rb +34 -0
- data/examples/rails_adapter.rb +47 -0
- data/lib/generators/ruby_llm/browser/install_generator.rb +29 -0
- data/lib/generators/ruby_llm/browser/templates/create_ruby_llm_browser_tables.rb.tt +60 -0
- data/lib/ruby_llm/browser/approval.rb +128 -0
- data/lib/ruby_llm/browser/approval_store.rb +38 -0
- data/lib/ruby_llm/browser/backend.rb +99 -0
- data/lib/ruby_llm/browser/budget.rb +90 -0
- data/lib/ruby_llm/browser/capabilities.rb +76 -0
- data/lib/ruby_llm/browser/configuration.rb +74 -0
- data/lib/ruby_llm/browser/context.rb +117 -0
- data/lib/ruby_llm/browser/doctor.rb +134 -0
- data/lib/ruby_llm/browser/engine.rb +326 -0
- data/lib/ruby_llm/browser/errors.rb +86 -0
- data/lib/ruby_llm/browser/fake_backend.rb +134 -0
- data/lib/ruby_llm/browser/immutable.rb +19 -0
- data/lib/ruby_llm/browser/in_memory_approval_store.rb +67 -0
- data/lib/ruby_llm/browser/in_memory_operation_store.rb +174 -0
- data/lib/ruby_llm/browser/operation.rb +218 -0
- data/lib/ruby_llm/browser/operation_store.rb +92 -0
- data/lib/ruby_llm/browser/policy.rb +298 -0
- data/lib/ruby_llm/browser/rails/active_record_approval_store.rb +72 -0
- data/lib/ruby_llm/browser/rails/active_record_operation_store.rb +231 -0
- data/lib/ruby_llm/browser/rails/actor_record.rb +13 -0
- data/lib/ruby_llm/browser/rails/approval_record.rb +49 -0
- data/lib/ruby_llm/browser/rails/operation_job.rb +163 -0
- data/lib/ruby_llm/browser/rails/operation_record.rb +56 -0
- data/lib/ruby_llm/browser/rails/reaper.rb +117 -0
- data/lib/ruby_llm/browser/rails/schema.rb +81 -0
- data/lib/ruby_llm/browser/rails.rb +79 -0
- data/lib/ruby_llm/browser/redactor.rb +159 -0
- data/lib/ruby_llm/browser/result.rb +102 -0
- data/lib/ruby_llm/browser/stagehand_backend.rb +332 -0
- data/lib/ruby_llm/browser/tool.rb +70 -0
- data/lib/ruby_llm/browser/tools/act.rb +91 -0
- data/lib/ruby_llm/browser/tools/extract.rb +60 -0
- data/lib/ruby_llm/browser/tools/inspect.rb +38 -0
- data/lib/ruby_llm/browser/url_guard.rb +253 -0
- data/lib/ruby_llm/browser/version.rb +9 -0
- data/lib/ruby_llm/browser.rb +79 -0
- metadata +267 -0
data/docs/RAILS.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Rails Jobs and Recovery
|
|
4
|
+
description: Run bounded read-only operations with durable state and independently scheduled cleanup.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Rails jobs and recovery
|
|
8
|
+
|
|
9
|
+
Run bounded read-only operations with durable state and independently scheduled cleanup.
|
|
10
|
+
|
|
11
|
+
After reading this guide, you will know:
|
|
12
|
+
|
|
13
|
+
- How to install the adapter's tables.
|
|
14
|
+
- How to build a freshly authorized job context with matching budgets.
|
|
15
|
+
- How to inspect status, cancel work, and recover orphaned sessions.
|
|
16
|
+
|
|
17
|
+
## Installing the adapter
|
|
18
|
+
|
|
19
|
+
Your application supplies Rails 8, Active Record, Active Job, and a database adapter.
|
|
20
|
+
They are not runtime dependencies of the plain-Ruby gem.
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
require "ruby_llm/browser/rails"
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
bin/rails generate ruby_llm:browser:install
|
|
28
|
+
bin/rails db:migrate
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The generator creates operations, approvals, and per-actor reservation-lock tables.
|
|
32
|
+
SQLite and PostgreSQL are accepted by the store, but only SQLite concurrency is tested
|
|
33
|
+
locally. MySQL is not supported by this adapter's partial indexes/locking contract.
|
|
34
|
+
Use a database timeout appropriate to your workload. Database lock failures fail closed;
|
|
35
|
+
they must not be handled by retrying a browser call with a new operation ID.
|
|
36
|
+
|
|
37
|
+
`Schema.create!` is for disposable fixtures, not production migrations or schema upgrades.
|
|
38
|
+
The [offline Rails example](https://github.com/yorzi/ruby_llm-browser/blob/main/examples/rails_adapter.rb) requires no full Rails app.
|
|
39
|
+
|
|
40
|
+
## Configuring the trusted context factory
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
# config/initializers/ruby_llm_browser.rb
|
|
44
|
+
require "ruby_llm/browser/rails"
|
|
45
|
+
|
|
46
|
+
browser_budget = RubyLLM::Browser::Budget.new(max_seconds: 30, max_steps: 6)
|
|
47
|
+
RubyLLM::Browser::Rails.context_factory = lambda do |actor_id:, run_id:|
|
|
48
|
+
# Resolve authenticated host records and recheck authorization, consent and revocation.
|
|
49
|
+
# Build policy/backend from those records, never from job or model-supplied credentials.
|
|
50
|
+
RubyLLM::Browser::Context.new(
|
|
51
|
+
actor_id: actor_id, run_id: run_id,
|
|
52
|
+
backend: qualified_backend, source_policy: policy,
|
|
53
|
+
operation_store: RubyLLM::Browser::Rails.operation_store,
|
|
54
|
+
budget: browser_budget
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`qualified_backend` and `policy` above are host objects, not global APIs supplied by the
|
|
60
|
+
gem. The factory must use the **same operation-store instance and budget limits** as
|
|
61
|
+
the queued reservation. Construct different budgets per run only from trusted records
|
|
62
|
+
that the factory can retrieve again. An optional approval store does not enable Act in
|
|
63
|
+
jobs; `OperationJob` accepts only `inspect` and `extract`.
|
|
64
|
+
|
|
65
|
+
## Enqueuing a read-only operation
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
operation = RubyLLM::Browser::Rails::OperationJob.enqueue(
|
|
69
|
+
actor_id: current_user.id.to_s, run_id: research_run.id.to_s,
|
|
70
|
+
kind: :inspect, url: "https://docs.example.com/",
|
|
71
|
+
budget: browser_budget, idempotency_key: "research-#{research_run.id}"
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
For extraction, pass `kind: :extract` and a host-pinned JSON `schema:`. Optional
|
|
76
|
+
`instruction:` and `max_bytes:` have the same meaning as on the tools.
|
|
77
|
+
|
|
78
|
+
One actor/run/key identifies one exact payload and budget. A changed payload using the
|
|
79
|
+
same key is rejected. Repeated enqueue calls may add duplicate queue deliveries, but
|
|
80
|
+
they attach to the same durable operation. Starting is atomically claimed once. A job
|
|
81
|
+
that sees a started, unknown, pending-cleanup, or terminal operation cannot start it
|
|
82
|
+
again. No automatic browser retry is configured.
|
|
83
|
+
|
|
84
|
+
Reservation and queue insertion are separate transactions. If enqueueing fails, retain
|
|
85
|
+
the returned operation identity when available and use the same idempotency key after
|
|
86
|
+
investigation. Schedule the reaper to reclaim expired reservations that never started.
|
|
87
|
+
|
|
88
|
+
Configure `RubyLLM::Browser::Rails.job_queue = :browser` if you need a separate queue.
|
|
89
|
+
Jobs suppress argument logging, but queues still persist URLs, instructions and schemas.
|
|
90
|
+
Protect queue access, storage, retention and encryption in the host application.
|
|
91
|
+
|
|
92
|
+
## Reading status and progress
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
store = RubyLLM::Browser::Rails.operation_store
|
|
96
|
+
operation = store.fetch!(operation_id)
|
|
97
|
+
# Authorize this read against the authenticated actor/run before returning anything.
|
|
98
|
+
summary = operation.summary
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`summary` omits provider session IDs and budget internals. It reports state, steps,
|
|
102
|
+
bytes, fence, timestamps, error code and cancellation flag. It is coarse progress,
|
|
103
|
+
not a percentage-complete promise. No result body is durably stored by the job; the
|
|
104
|
+
host must explicitly implement authorized result storage/delivery if needed. Do not
|
|
105
|
+
rely on an Active Job return value after asynchronous execution.
|
|
106
|
+
|
|
107
|
+
The stores are trusted server APIs, not tenant-safe controllers. They expose unscoped
|
|
108
|
+
fetch/sweep methods for workers; never expose them directly to model arguments or HTTP.
|
|
109
|
+
Tool progress callbacks are feature-detected on RubyLLM 2.1+, and contain no raw URL.
|
|
110
|
+
|
|
111
|
+
## Cancelling and reaping
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
store.cancel(operation_id, actor_id: current_user.id.to_s,
|
|
115
|
+
run_id: research_run.id.to_s, now: Time.now.utc)
|
|
116
|
+
RubyLLM::Browser::Rails::Reaper.new(backend: qualified_backend).sweep
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Cancellation of a never-started reservation is terminal immediately. Otherwise it sets
|
|
120
|
+
`cancel_requested`, fences stale workers, and remains `cleanup_pending` with quota held.
|
|
121
|
+
It cannot undo an in-flight provider request. A returned session ID is persisted even
|
|
122
|
+
if cancellation raced with session creation.
|
|
123
|
+
|
|
124
|
+
Run a reaper regularly in an independent scheduler, not only in the operation worker's
|
|
125
|
+
ensure block. A reaper fences workers **before** reconciliation/cleanup, tries to end
|
|
126
|
+
known sessions even when provider reconciliation is unavailable, and reports `failed`
|
|
127
|
+
rather than claiming a database write succeeded. `ttl_seconds:` is an optional positive
|
|
128
|
+
idle threshold, not proof that the provider's billable session has expired.
|
|
129
|
+
|
|
130
|
+
Stagehand exposes no correlation endpoint here. An unknown start without a session ID
|
|
131
|
+
remains unknown and holds quota until the host supplies provider-confirmed evidence.
|
|
132
|
+
Alert on unknown/pending/failed entries. Never free quota just because enough local time
|
|
133
|
+
passed. An unknown action outcome remains in the result/error code even if session
|
|
134
|
+
cleanup succeeds; do not replay it.
|
|
135
|
+
|
|
136
|
+
One reaper/backend instance must cover only operations belonging to that provider
|
|
137
|
+
configuration. The current table has no backend credential-configuration binding.
|
|
138
|
+
Multi-provider/multi-credential deployments need a host-scoped store or a future schema
|
|
139
|
+
extension before a global sweep is safe.
|
|
140
|
+
|
|
141
|
+
## Upgrading an earlier development snapshot
|
|
142
|
+
|
|
143
|
+
Nothing has been publicly released. If you already ran the earlier generator, preserve
|
|
144
|
+
your data and add an explicit migration for these new fields instead of rerunning
|
|
145
|
+
`create_table` or dropping the database:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
class HardenBrowserBookkeeping < ActiveRecord::Migration[8.1]
|
|
149
|
+
def change
|
|
150
|
+
create_table :ruby_llm_browser_actors, id: :string do |t|
|
|
151
|
+
t.integer :sequence, null: false, default: 0
|
|
152
|
+
end
|
|
153
|
+
add_column :ruby_llm_browser_operations, :cancel_requested, :boolean, default: false, null: false
|
|
154
|
+
add_column :ruby_llm_browser_operations, :request_digest, :string
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Adapt this migration if any additions already exist. Drain workers during the upgrade.
|
|
160
|
+
Legacy rows without request digests are not payload-bound, so do not reuse their
|
|
161
|
+
idempotency keys for new work. Inspect earlier terminal cancellations for sessions that
|
|
162
|
+
may not have been confirmed closed before reclaiming them.
|
|
163
|
+
|
|
164
|
+
## Next steps
|
|
165
|
+
|
|
166
|
+
- Review [the API]({% link API.md %}) for result interpretation and budget semantics.
|
|
167
|
+
- Review [release blockers]({% link RELEASE_AUDIT.md %}) before production deployment.
|
data/docs/README.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
Use these guides to configure browser tools and understand their limits.
|
|
4
|
+
|
|
5
|
+
- [API reference](API.md): contexts, tools, schemas, results, budgets, and backend options.
|
|
6
|
+
- [Rails integration](RAILS.md): durable stores, jobs, cancellation, recovery, and migrations.
|
|
7
|
+
- [Security model](SECURITY_MODEL.md): trust boundaries and live deployment qualification.
|
|
8
|
+
- [Release checklist](RELEASING.md): compatibility, package verification, and owner approvals.
|
|
9
|
+
- [Release audit](RELEASE_AUDIT.md): findings, fixes, and remaining publication blockers.
|
|
10
|
+
- [Owner action plan](RELEASE_ACTION_PLAN.md): decisions you own and work you can delegate.
|
|
11
|
+
- [WORK ledger](WORK.md): exact commands and offline requirement evidence.
|
|
12
|
+
- [Specification baseline](SPEC_BASELINE.md): product intent and governance boundaries.
|
|
13
|
+
|
|
14
|
+
Start with the runnable offline example in the [README](../README.md). No guide grants
|
|
15
|
+
authorization to access a source or spend provider credits. Gem releases use the reviewed
|
|
16
|
+
tag workflow in [the release checklist](RELEASING.md).
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Release Action Plan
|
|
4
|
+
description: Record the offline first-release decision and the automated gem release path.
|
|
5
|
+
llms: false
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Release action plan
|
|
9
|
+
|
|
10
|
+
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.
|
|
13
|
+
|
|
14
|
+
## Current position
|
|
15
|
+
|
|
16
|
+
- Hardening baseline: `0ec34d4`.
|
|
17
|
+
- Expanded remote CI is green: seven test jobs and the package/install/advisory job.
|
|
18
|
+
- Owner decision on 2026-10-18: first release is read-only; defer live Act and mark the
|
|
19
|
+
built-in Stagehand backend unsupported until egress and cleanup are qualified.
|
|
20
|
+
- No live provider calls are authorized or were run for this decision.
|
|
21
|
+
- Independent H1 and owner H2 are complete for exact candidate Q
|
|
22
|
+
`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.
|
|
30
|
+
|
|
31
|
+
## Release scope
|
|
32
|
+
|
|
33
|
+
The first version supports offline `FakeBackend` operations. `StagehandBackend` remains in
|
|
34
|
+
the source but cannot start or operate on a live session. The full same-session Act
|
|
35
|
+
protocol is deferred to a later version. H1/H2 approved this scope for handoff against
|
|
36
|
+
exact Q `22d488987517b10dcd96d2713ed3d47e8818b36b`; the approved Specs candidate label
|
|
37
|
+
`0.3.0-draft` is historical provenance. The package and Git release use `0.1.0`.
|
|
38
|
+
|
|
39
|
+
## Remaining work and ownership
|
|
40
|
+
|
|
41
|
+
| Gate | What you decide or provide | What the assistant can do |
|
|
42
|
+
| --- | --- | --- |
|
|
43
|
+
| 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
|
+
| 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. |
|
|
46
|
+
| 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. |
|
|
48
|
+
|
|
49
|
+
## Provider questions to resolve before live tests
|
|
50
|
+
|
|
51
|
+
1. Can the actual browser enforce approved destinations and public IP ranges at
|
|
52
|
+
connection time across navigation, redirect hops, subresources, iframes, popups,
|
|
53
|
+
WebSockets and DNS rebinding? A Ruby allowlist is insufficient.
|
|
54
|
+
2. Does the actual Stagehand server return a verified final top-level URL? The SDK
|
|
55
|
+
permits null navigation results; the gem refuses to invent provenance.
|
|
56
|
+
3. How are session TTL and confirmed closure enforced? Is a start with a lost response
|
|
57
|
+
recoverable by correlation? If not, unknown starts must remain held for host review.
|
|
58
|
+
4. Which provider-side billing controls exist? The gem's `max_cost_usd` field is
|
|
59
|
+
advisory, not an enforceable currency cap.
|
|
60
|
+
|
|
61
|
+
If the provider cannot meet a required guarantee, a test passing once is not a substitute.
|
|
62
|
+
Keep the Stagehand backend unsupported until the missing controls have been qualified.
|
|
63
|
+
The current adapter does not accept `egress_isolation: :enforced`; do not add a host
|
|
64
|
+
assertion merely to bypass the preflight gate.
|
|
65
|
+
|
|
66
|
+
## Authorizing a later live test
|
|
67
|
+
|
|
68
|
+
Approve the provider/deployment, public test origin you control or have permission to
|
|
69
|
+
use, data to be sent to browser/model services, session/request/time limits, billing
|
|
70
|
+
owner, and spending limit. A hard monetary ceiling requires an actual provider control.
|
|
71
|
+
Use controlled negative fixtures, not genuine cloud-metadata or third-party private
|
|
72
|
+
services. Supply credentials through local secret management, never in chat or commits.
|
|
73
|
+
|
|
74
|
+
The assistant can first prepare this plan without credentials or paid calls. If required
|
|
75
|
+
network isolation cannot be established, do not proceed with live browsing.
|
|
76
|
+
|
|
77
|
+
## Provider boundary
|
|
78
|
+
|
|
79
|
+
The release instruction does not authorize provider calls or live browsing. Keep
|
|
80
|
+
Stagehand and live Act disabled until the missing provider guarantees are separately
|
|
81
|
+
qualified and approved. A Ruby source URL allowlist is not browser-side egress proof.
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Release Audit
|
|
4
|
+
description: Record release fixes, provider limits, and the scope of available evidence.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Release audit: 2026-10-18
|
|
8
|
+
|
|
9
|
+
**Audit snapshot before the 0.1.0 release instruction: publication was on hold; the selected first-release scope was offline read-only.**
|
|
10
|
+
|
|
11
|
+
The original 270-example suite was green, but source review and new negative tests
|
|
12
|
+
found security/lifecycle defects and documentation errors. Those defects are hardened
|
|
13
|
+
in commit `0ec34d4`. A green offline suite does not close the provider, authorization,
|
|
14
|
+
name/license, or specification-governance gates below. Nothing has been published.
|
|
15
|
+
|
|
16
|
+
## Fixed findings
|
|
17
|
+
|
|
18
|
+
| Severity | Finding | Correction / evidence |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| High | A stale reserved copy or duplicate worker could start an operation again. | Atomic `claim_start`, exact fence and legal-transition checks; stale-copy and independent-connection races. |
|
|
21
|
+
| High | Locking existing open rows did not serialize an actor's first concurrent reservations. | Durable actor lock row written before quota checks, including an initially empty actor. |
|
|
22
|
+
| High | Cancellation released a live session's quota and hid it from the reaper. | Cancellation flag plus pending cleanup; quota held until exact-session closure is confirmed. |
|
|
23
|
+
| High | Cancellation during start or a persistence exception could bypass cleanup/session tracking. | Persist returned identity across cancellation; ensure-style close after post-start failures; regressions. |
|
|
24
|
+
| High | Stagehand reconciliation always returned unknown, so the reaper did not close known sessions. | Reaper fences before I/O and attempts known-session cleanup without requiring an active reconciliation response. Failed writes are reported as failed. |
|
|
25
|
+
| High | Model byte requests overrode host limits, operation bytes were not enforced, and cleanup overwrote counters. | Minimum of model/policy/remaining budget; retained accounting; exact encoded-byte bounding. |
|
|
26
|
+
| High | Extraction provider data was not validated; malformed schema shapes could pass or raise untyped errors. | JSON Schema meta-validation plus instance validation before returning data. |
|
|
27
|
+
| High | Nested mutable host objects could change frozen policy/approval/schema objects. | Defensive copies and immutable host bindings. |
|
|
28
|
+
| High | A clean install activated vulnerable Ruby default gems despite a green development lockfile audit. | Explicit patched CGI, DNS resolver, URI and zlib dependency floors; audit both the resolved lockfile and clean runtime. |
|
|
29
|
+
| High | Live Act accepted incomplete grants despite missing same-session/target proof. | Engine now refuses every non-fake Act before start. Production protocol remains a blocker, not a claimed fix. |
|
|
30
|
+
| Medium | SDK response failures/missing identities could be treated as closed or successful; final URL was guessed. | Unknown starts remain reserved; unsuccessful responses are refused; missing final URL fails closed. |
|
|
31
|
+
| Medium | Provider defaults permitted self-healing/CAPTCHA solving and logging; relevant ENV could drift after qualification. | Explicit disabled options; snapshot recheck; remaining-time request timeout; real-SDK HTTP regressions. |
|
|
32
|
+
| Medium | URL/selector progress, parser errors and Active Job arguments exposed avoidable data. | Coarse progress, generic parser/backend failures, URL query redaction, disabled job argument logging. |
|
|
33
|
+
| Medium | Duplicate idempotency keys could select a different queued request; factory could use a different store. | Payload/budget digest binding and job/context store identity checks. |
|
|
34
|
+
| Medium | Durable timestamp precision could reject a valid direct Engine call using a real clock; configured defaults were ignored. | Return canonical stored identities, honor process budget/result defaults, and test both real clocks and configuration. |
|
|
35
|
+
| Medium | Rails generator command did not resolve (`RubyLLM` underscored incorrectly). | Explicit namespace, lookup test, generated migration up/down test. |
|
|
36
|
+
| Medium | README used `egress_isolated`, omitted required `schema:`, passed numeric IDs, and misstated return type. | Runnable offline README plus API/Rails guides and tested snippets. |
|
|
37
|
+
| Medium | Built gem omitted guides/examples linked by the README; dependency range admitted untested Stagehand minors. | Package includes docs/examples; Stagehand restricted to 3.21.x; packaged-link checks. |
|
|
38
|
+
| Low | Changelog implied a release, CI missed the RubyLLM minimum, and Doctor overstated readiness. | Unreleased changelog, compatibility matrix, public Chat API tests across versions, qualification caveat in Doctor. |
|
|
39
|
+
|
|
40
|
+
The first clean runtime activated CGI 0.4.1 (CVE-2025-27219/27220), Resolv 0.3.0
|
|
41
|
+
(CVE-2026-80212/80213), URI 0.13.1 (CVE-2025-27221/61594), and zlib 3.1.1
|
|
42
|
+
(CVE-2026-27820). The project lockfile did not enumerate all of these default gems.
|
|
43
|
+
Patched runtime dependency floors close this resolution gap. The native interpreter
|
|
44
|
+
and its system libraries still require normal host security updates.
|
|
45
|
+
|
|
46
|
+
The new audit regressions were first run against the original implementation and
|
|
47
|
+
demonstrated the limit/schema/start/cancellation failures before correction.
|
|
48
|
+
Primary evidence: `spec/security/release_audit_spec.rb`,
|
|
49
|
+
`spec/integration/{concurrent_store,stagehand_http,generator,documentation}_spec.rb`,
|
|
50
|
+
and the existing engine/store/job suites. The source repository's `docs/WORK.md` records the commands and dependency versions.
|
|
51
|
+
|
|
52
|
+
## Open live-support and publication gates
|
|
53
|
+
|
|
54
|
+
### P1: Provider-side security and protocol qualification (BR-02; blocks live support)
|
|
55
|
+
|
|
56
|
+
The gem contains no browser firewall. A custom backend's `egress_isolation: :enforced`
|
|
57
|
+
capability is a declaration, not independent proof. `StagehandBackend` stays
|
|
58
|
+
`:unverified` and disabled. No live provider egress, TTL, billing, or cleanup
|
|
59
|
+
qualification was performed. Stagehand 3.21 allows null navigation results, so final-URL
|
|
60
|
+
reporting must be verified against an actual server. Unknown starts without session IDs
|
|
61
|
+
have no correlation API in this adapter and cannot be automatically proven closed. Local
|
|
62
|
+
time alone never releases their quota. These are provider/host evidence gaps, not
|
|
63
|
+
mock-test tasks.
|
|
64
|
+
|
|
65
|
+
### P1: Full live Act protocol is not implemented (deferred beyond the first release)
|
|
66
|
+
|
|
67
|
+
The current Engine starts a new session for each call. Approval operation/session/page
|
|
68
|
+
bindings are optional, and a digest of observed action candidates does not prove DOM
|
|
69
|
+
or target revision. Keyword filters cannot prove semantic safety. Offline single-use
|
|
70
|
+
store behavior is correct, but it is not the required same-session host decision.
|
|
71
|
+
Live Act is disabled instead of presenting this as production-safe. Implement the
|
|
72
|
+
protocol in a later version. No unaudited opt-in flag should bypass this Engine refusal.
|
|
73
|
+
|
|
74
|
+
### P2: Distribution ownership and approval (BR-03 / BR-04; publication blockers)
|
|
75
|
+
|
|
76
|
+
The RubyGems API returned 404 for `ruby_llm-browser` on 2026-10-18. It was
|
|
77
|
+
unregistered at that check, not reserved or owned. The signed-in publisher account has
|
|
78
|
+
MFA disabled; this gem requires MFA, so a push will be rejected until the owner enables
|
|
79
|
+
it. Exact name availability, trademark/affiliation wording and owner/distributor
|
|
80
|
+
clearance remain open. The 26-gem runtime license metadata inventory now matches the
|
|
81
|
+
resolved lockfile; the Stagehand source MIT text is pinned in NOTICE. This technical
|
|
82
|
+
inventory is not legal clearance.
|
|
83
|
+
|
|
84
|
+
Independent H1 is complete and owner H2 approved the offline read-only scope and KPI
|
|
85
|
+
proposal against exact Q `22d488987517b10dcd96d2713ed3d47e8818b36b` on 2026-10-18.
|
|
86
|
+
The approval record is in the OddlyNiche Specs handoff. Baseline registration R and
|
|
87
|
+
implementation receipt remain pending. H2 does not approve RubyGems publication or the
|
|
88
|
+
exact release artifact.
|
|
89
|
+
|
|
90
|
+
## Limits and follow-up checks
|
|
91
|
+
|
|
92
|
+
- PostgreSQL remains untested. Local macOS Ruby 3.3.5 and the Linux Ruby 3.2/3.3/3.4
|
|
93
|
+
CI combinations pass with SQLite. Rails 7 is not a supported target.
|
|
94
|
+
- A global reaper cannot route between backend credential configurations; use one
|
|
95
|
+
configuration per scoped store until provider identity is durably represented.
|
|
96
|
+
- `max_cost_usd` is advisory. No provider-enforced currency cap or cost ledger exists.
|
|
97
|
+
- Retained-output bounds are not remote-response, browser-memory, or bandwidth caps.
|
|
98
|
+
- Sanitization is not a universal secret/PII detector. Host logs, queue arguments,
|
|
99
|
+
database contents and authenticated/private content require separate protection.
|
|
100
|
+
- Job return values are not durable result delivery. Hosts need explicit authorized
|
|
101
|
+
result storage if they want it. Reservation and enqueue are not a transactional outbox.
|
|
102
|
+
- The release hardening changes the development schema. Follow the additive migration
|
|
103
|
+
guidance; do not discard existing operation/approval records.
|
|
104
|
+
|
|
105
|
+
## Checks completed
|
|
106
|
+
|
|
107
|
+
Full offline suites run against RubyLLM 2.0.0 and 2.1.1, Rails components 8.0.5.1 and
|
|
108
|
+
8.1.4, Stagehand 3.21.0, and SQLite 2.10.0 on Ruby 3.3.5: **302 examples, 0 failures**
|
|
109
|
+
in each combination, with **89 Ruby files, no RuboCop offenses**. SDK HTTP requests are stubbed,
|
|
110
|
+
with all other network requests disabled. A fresh ruby-advisory-db check reports no
|
|
111
|
+
known vulnerable dependencies (database commit
|
|
112
|
+
`fa2295251efb3b0d1a300e5ce8ad700ff1f1b485`). This is a point-in-time dependency check,
|
|
113
|
+
not a guarantee of no vulnerabilities. Fresh installation also passes the core/Rails
|
|
114
|
+
examples and installed generator/migration, and the activated clean core runtime is
|
|
115
|
+
checked separately against advisories. Artifact verification is recorded in WORK.
|
|
116
|
+
|
|
117
|
+
## Submission verification
|
|
118
|
+
|
|
119
|
+
Commit `0ec34d4` is pushed to the repository. The expanded
|
|
120
|
+
[GitHub Actions run](https://github.com/yorzi/ruby_llm-browser/actions/runs/39086570934)
|
|
121
|
+
passed all seven compatibility jobs (302 examples each) and the build/install/advisory
|
|
122
|
+
job, including `CORE_PACKAGE_PASS`, `CLEAN_RUNTIME_ADVISORIES_PASS`, and
|
|
123
|
+
`RAILS_PACKAGE_PASS`. This closes the expanded-CI follow-up, not the live-provider or
|
|
124
|
+
owner-approval gates. The source repository's `docs/RELEASE_ACTION_PLAN.md` records the owner decisions and follow-up actions.
|
|
125
|
+
|
|
126
|
+
## Owner scope decision and qualification follow-up (2026-10-18)
|
|
127
|
+
|
|
128
|
+
The owner selected a read-only first release, deferred the full live Act protocol to a
|
|
129
|
+
later version, and explicitly declined live provider calls for this qualification. The
|
|
130
|
+
built-in `StagehandBackend` now always reports egress as unverified and refuses live
|
|
131
|
+
start, navigation, observation, extraction, and Act calls. The host cannot enable it with
|
|
132
|
+
an `:enforced` assertion. The offline `FakeBackend` remains available.
|
|
133
|
+
|
|
134
|
+
Browserbase's [Allowed Domains documentation](https://docs.browserbase.com/platform/browser/security/allowed-domains)
|
|
135
|
+
describes top-frame navigation checks and does not establish blocking for iframes or
|
|
136
|
+
subresources. This is insufficient evidence for the required browser-side egress policy.
|
|
137
|
+
The [Update a Session endpoint](https://docs.browserbase.com/reference/api/update-a-session)
|
|
138
|
+
accepts `REQUEST_RELEASE` and may return a session in `PENDING`; the [Get a Session endpoint](https://docs.browserbase.com/reference/api/get-a-session)
|
|
139
|
+
exposes `status` and `endedAt`. Stagehand 3.21's `SessionEndResponse.success` only says
|
|
140
|
+
the request succeeded, and its SDK does not expose that status lookup. The implementation
|
|
141
|
+
therefore sends no Stagehand provider request, including `end_`; cleanup remains
|
|
142
|
+
`cleanup_pending` until future work can verify terminal status. The stubbed HTTP contract
|
|
143
|
+
tests now assert that no cleanup request is dispatched.
|
|
144
|
+
|
|
145
|
+
No egress, TTL, billing, or cleanup live calls were made. BR-02 remains open for any
|
|
146
|
+
future live backend, but it does not block the offline-only first-release scope. At the
|
|
147
|
+
time of this initial audit snapshot, distribution decisions and the final release
|
|
148
|
+
instruction were pending.
|
|
149
|
+
|
|
150
|
+
## Owner release instruction (2026-10-18)
|
|
151
|
+
|
|
152
|
+
After the initial audit snapshot, the owner selected gem version `0.1.0`, requested a
|
|
153
|
+
matching Git tag, instructed RubyGems publication, and requested GitHub Actions automation
|
|
154
|
+
for later version tags. This instruction authorizes the offline-only package release; it
|
|
155
|
+
does not authorize provider calls, make Stagehand supported, or implement live Act. The
|
|
156
|
+
approved Specs Q remains identified by its historical candidate label `0.3.0-draft`.
|
|
157
|
+
RubyGems publication and GitHub Pages deployment results are recorded separately from
|
|
158
|
+
this pre-release audit snapshot.
|
data/docs/RELEASING.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Compatibility and Release Checklist
|
|
4
|
+
description: Verify the package and release a version from its matching Git tag.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Compatibility and release checklist
|
|
8
|
+
|
|
9
|
+
Build and verify the package before you publish a version. GitHub Actions releases the
|
|
10
|
+
gem from the matching version tag and deploys that version's documentation after success.
|
|
11
|
+
|
|
12
|
+
After reading this guide, you will know:
|
|
13
|
+
|
|
14
|
+
- Which combinations were exercised.
|
|
15
|
+
- How to run offline checks and build an artifact.
|
|
16
|
+
- Which evidence the automated release checks require.
|
|
17
|
+
|
|
18
|
+
## Compatibility evidence
|
|
19
|
+
|
|
20
|
+
The 2026-10-18 audit runs on Ruby **3.3.5**, arm64 macOS, with Stagehand **3.21.0**,
|
|
21
|
+
JSONSchemer **2.5.0**, and sqlite3 **2.10.0**. Full offline suites cover:
|
|
22
|
+
|
|
23
|
+
| RubyLLM | Active Record / Active Job | Result |
|
|
24
|
+
| --- | --- | --- |
|
|
25
|
+
| 2.1.1 | 8.1.4 | PASS |
|
|
26
|
+
| 2.0.0 | 8.1.4 | PASS |
|
|
27
|
+
| 2.0.0 | 8.0.5.1 | PASS |
|
|
28
|
+
|
|
29
|
+
The expanded CI configuration includes Ruby 3.2/3.3/3.4 and RubyLLM 2.0.0/2.1.1,
|
|
30
|
+
plus Rails 8.0. All seven compatibility jobs and the build/install/advisory job passed
|
|
31
|
+
for commit `0ec34d4` in [this CI run](https://github.com/yorzi/ruby_llm-browser/actions/runs/39086570934).
|
|
32
|
+
Do not treat that run as evidence for later code changes or live provider guarantees.
|
|
33
|
+
Rails 7 and MySQL are not supported targets. PostgreSQL must be tested separately.
|
|
34
|
+
|
|
35
|
+
## Running checks
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
bundle install
|
|
39
|
+
bundle exec rake
|
|
40
|
+
bundle exec bundle-audit check --update
|
|
41
|
+
bundle exec ruby examples/plain_ruby.rb
|
|
42
|
+
bundle exec ruby examples/rails_adapter.rb
|
|
43
|
+
|
|
44
|
+
BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle install
|
|
45
|
+
BUNDLE_GEMFILE=gemfiles/ruby_llm_2_0.gemfile bundle exec rspec
|
|
46
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle install
|
|
47
|
+
BUNDLE_GEMFILE=gemfiles/rails_8_0.gemfile bundle exec rspec
|
|
48
|
+
|
|
49
|
+
bundle exec rake build
|
|
50
|
+
bundle exec ruby bin/verify-package --rails --audit
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The default suite blocks unmocked network calls. HTTP contract tests use the actual
|
|
54
|
+
Stagehand client/serializers with stubbed HTTP, not paid browser sessions. Generator
|
|
55
|
+
tests resolve the documented Rails namespace and migrate the generated schema up/down.
|
|
56
|
+
Documentation/package tests check local links, shipped guides/examples and executable
|
|
57
|
+
offline README examples. These are bounded engineering checks, not a penetration test.
|
|
58
|
+
|
|
59
|
+
## Verifying the built gem
|
|
60
|
+
|
|
61
|
+
Use a fresh temporary directory for every install. Install the built gem there, then
|
|
62
|
+
activate it from outside the checkout. Confirm its loaded entrypoint lives under that
|
|
63
|
+
GEM_HOME, not under the source tree. Do not use `--ignore-dependencies` to claim that
|
|
64
|
+
dependency resolution succeeded. A clean network-resolved install is stronger evidence
|
|
65
|
+
than an install that borrows the developer's existing dependencies.
|
|
66
|
+
|
|
67
|
+
`bin/verify-package` installs without `--ignore-dependencies`, activates the artifact
|
|
68
|
+
outside the checkout, runs shipped examples, and optionally installs Rails and tests
|
|
69
|
+
the shipped generator/migration. `--audit` checks the actually activated core runtime
|
|
70
|
+
gems against the advisory database refreshed by `bundle-audit check --update`. This
|
|
71
|
+
catches default gems that a development lockfile may omit. Logs and a JSON version
|
|
72
|
+
inventory are retained in the temporary evidence directory printed by the command.
|
|
73
|
+
|
|
74
|
+
Verify all of the following:
|
|
75
|
+
|
|
76
|
+
- Core require loads no Rails, Active Record, Active Job, or Stagehand client.
|
|
77
|
+
- The optional Rails require loads on demand.
|
|
78
|
+
- The generated migration and all README-linked guides are shipped.
|
|
79
|
+
- Both examples run using the installed artifact.
|
|
80
|
+
- The artifact contains no keys, cookies, dumps, databases, lockfiles, or test fixtures.
|
|
81
|
+
- 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.
|
|
83
|
+
|
|
84
|
+
## Release boundaries
|
|
85
|
+
|
|
86
|
+
The first-release scope is offline read-only. `FakeBackend` is the only built-in backend
|
|
87
|
+
supported for operations. `StagehandBackend` refuses live calls, its egress capability
|
|
88
|
+
stays unverified, and cleanup requests are disabled. The full live Act
|
|
89
|
+
protocol is deferred to a later version. No provider calls were run or authorized.
|
|
90
|
+
|
|
91
|
+
The owner selected version `0.1.0` for this package and instructed its release. H1 and
|
|
92
|
+
H2 approved the product handoff against exact Specs candidate Q
|
|
93
|
+
`22d488987517b10dcd96d2713ed3d47e8818b36b`; the candidate's historical version label is
|
|
94
|
+
`0.3.0-draft`. The later release instruction is separate from H2. See [the audit findings]({% link RELEASE_AUDIT.md %}) for the live-provider limits.
|
|
95
|
+
|
|
96
|
+
Keep Stagehand unsupported and live Act disabled. Before enabling either in a later
|
|
97
|
+
version, qualify egress across every request path, final URL reporting, TTL, cleanup
|
|
98
|
+
status, unknown-start recovery, and provider billing controls. No live provider call was
|
|
99
|
+
made for `0.1.0`.
|
|
100
|
+
|
|
101
|
+
## Releasing a version
|
|
102
|
+
|
|
103
|
+
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`.
|
|
108
|
+
|
|
109
|
+
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`:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git tag -a v0.2.0 -m "Release 0.2.0"
|
|
115
|
+
git push origin v0.2.0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The `Release Gem` workflow rejects a tag that does not match both the gem and docs
|
|
119
|
+
versions, or has no matching changelog section. It runs the suite, dependency audit and
|
|
120
|
+
isolated package verification before RubyGems publishing through OIDC trusted publishing.
|
|
121
|
+
On success it creates a GitHub Release with the gem, checksum and matching changelog
|
|
122
|
+
section. The Docs workflow then builds the tagged stable docs plus current `main` docs.
|
|
123
|
+
Configure the matching
|
|
124
|
+
pending trusted publisher on RubyGems with owner `yorzi`, repository `ruby_llm-browser`,
|
|
125
|
+
workflow `release.yml`, and environment `release` before the first tag.
|
|
126
|
+
|
|
127
|
+
## Next steps
|
|
128
|
+
|
|
129
|
+
- 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.
|