Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
124 commits
Select commit Hold shift + click to select a range
e3d99ce
chore: rename crates from template to tinybrowser
senamakel Aug 21, 2026
e366c8f
chore: files changed Cargo.toml,crates/tinybrowser-bus/Cargo.toml,cra…
senamakel Aug 21, 2026
67e9e3f
feat(greeting): add support for named greetings
senamakel Aug 21, 2026
2f0da0b
fix(session): handle missing session type in session creation
senamakel Aug 21, 2026
4bf2dbe
fix(session): correct session type field ordering in serialization
senamakel Aug 21, 2026
767de4e
fix(page): handle missing page type in deserialization
senamakel Aug 21, 2026
8cd033e
fix(test): update test to match new page behavior
senamakel Aug 21, 2026
5facf8c
fix(action): remove unused `types` module
senamakel Aug 21, 2026
ee3f167
fix(action): correct test assertion for bus action
senamakel Aug 21, 2026
1316b72
fix(snapshot): handle empty snapshot list in restore
senamakel Aug 21, 2026
2a914e2
fix(output): handle empty output gracefully
senamakel Aug 21, 2026
4e113b8
fix(errors): add missing error variant for invalid bus state
senamakel Aug 21, 2026
4454b86
fix(bus): correct version module path in lib.rs
senamakel Aug 21, 2026
21cdccc
chore(deps): update Cargo.lock with new dependencies for tinybrowser …
senamakel Aug 21, 2026
4ddac79
fix(greeting): handle empty name in greeting module
senamakel Aug 21, 2026
4ef8af9
fix(cdp): handle missing response in WebSocket message parsing
senamakel Aug 21, 2026
6e13071
fix(cdp): handle missing `sessionId` in CDP response
senamakel Aug 21, 2026
25bb6ba
fix(cdp): handle missing endpoint in endpoint lookup
senamakel Aug 21, 2026
00b3d15
fix(cdp): handle missing browser process gracefully
senamakel Aug 21, 2026
b53d8ec
test(cdp): add test module and remove unused public export
senamakel Aug 21, 2026
1cb5952
fix(session): enforce policy check on ref updates
senamakel Aug 21, 2026
a3263fa
fix(session): handle missing session file gracefully
senamakel Aug 21, 2026
a3c4a7e
fix(session): correct test assertion for session timeout
senamakel Aug 21, 2026
4736fe7
fix(snapshot): handle empty snapshot in render
senamakel Aug 21, 2026
9c1931d
fix(snapshot): handle edge case in snapshot restoration
senamakel Aug 21, 2026
4225a47
fix(script): handle empty script input gracefully
senamakel Aug 21, 2026
f355edb
fix(interact): handle empty path in resolve
senamakel Aug 21, 2026
9ae6d99
fix(keys): handle key release events in key event processing
senamakel Aug 21, 2026
06b8729
fix(interact): handle empty input in interactive mode
senamakel Aug 21, 2026
dbced7b
chore(deps): update test dependencies to latest compatible versions
senamakel Aug 21, 2026
a621c4f
fix(extract): handle missing script extraction gracefully
senamakel Aug 21, 2026
bb9b2d5
fix(extract): handle missing file extension in content type detection
senamakel Aug 21, 2026
243a8d1
fix(capture): handle empty store path in capture configuration
senamakel Aug 21, 2026
c9212ae
fix(capture): correct test assertion for empty capture result
senamakel Aug 21, 2026
446db2c
fix(engine): handle empty search query in URL parsing
senamakel Aug 21, 2026
fdd7811
feat(tinybrowser): replace template crate with browser engine
senamakel Aug 21, 2026
44290d3
chore(bus): remove `#[non_exhaustive]` from all bus types
senamakel Aug 21, 2026
c3421e5
fix(interact): handle missing script key gracefully
senamakel Aug 21, 2026
e134086
feat(tinybus_module): replace template greeting with full browser ser…
senamakel Aug 21, 2026
92b123e
fix(store): handle missing session refs in capture store
senamakel Aug 21, 2026
a16df28
chore: suppress clippy warnings in test modules
senamakel Aug 21, 2026
95115f8
chore: files changed crates/tinybrowser/src/tinybus_module/test.rs
senamakel Aug 21, 2026
711bda8
chore(examples): remove outdated example files
senamakel Aug 21, 2026
499a401
test(tinybrowser): replace template stubs with real public‑API tests
senamakel Aug 21, 2026
bcfbd2f
fix(tinybus_module): correct test assertion for module initialization
senamakel Aug 21, 2026
3c6c63b
fix(snapshot): handle empty snapshot list in render
senamakel Aug 21, 2026
2243048
feat(core): add initial module structure for browser automation
senamakel Aug 21, 2026
c42c9a8
feat(bus): derive Default on bus types and replace manual impls
senamakel Aug 21, 2026
f4faacc
fix(tinybrowser): pass error by reference to bus error conversion
senamakel Aug 21, 2026
ca01dcf
refactor(interact): extract inline action logic into dedicated helper…
senamakel Aug 21, 2026
cdd1482
chore: reformat long lines to comply with style guide
senamakel Aug 21, 2026
a21330e
refactor: replace push_str with write! for string formatting
senamakel Aug 21, 2026
9473f39
fix(engine): expand error documentation for navigate method
senamakel Aug 21, 2026
25b7994
fix(script): normalise non-breaking spaces and zero-width characters …
senamakel Aug 21, 2026
838b20b
fix(script): use single quotes in comment for consistency
senamakel Aug 21, 2026
434791c
feat(tinybrowser): add base64 dev-dependency for live chrome tests
senamakel Aug 21, 2026
bd0428c
feat(cdp): preserve and diagnose browser startup banner on failure
senamakel Aug 21, 2026
4705621
test(live-chrome): make live tests fail instead of skip when no brows…
senamakel Aug 21, 2026
acc9cdb
test(live_chrome): reformat long lines for readability
senamakel Aug 21, 2026
6dbf059
chore(cdp): replace string literal with concat! for sandbox error mes…
senamakel Aug 21, 2026
cfebd05
test(live-chrome): switch tests from data URLs to loopback HTTP server
senamakel Aug 21, 2026
b2f9c06
test(live_chrome): replace data URLs with a loopback HTTP server
senamakel Aug 21, 2026
f88b9a2
test(live_chrome): await page serve in live navigation test
senamakel Aug 21, 2026
3da3e8d
ci: rename project from template to tinybrowser
senamakel Aug 21, 2026
0d20ff1
fix(release): update release package name to tinybrowser
senamakel Aug 21, 2026
bcf9e90
feat(tinybrowser): add over_the_bus example
senamakel Aug 21, 2026
181cda4
feat(cdp): add environment variable for extra browser launch flags
senamakel Aug 21, 2026
f6b2b6e
fix(cdp): backtick AppArmor in doc comment
senamakel Aug 21, 2026
816da12
fix(snapshot): clarify sequence field semantics
senamakel Aug 21, 2026
3eb6b91
chore(tinybrowser): switch live tests from feature flag to environmen…
senamakel Aug 21, 2026
73c62c6
feat(docs): replace template documentation with tinybrowser module docs
senamakel Aug 21, 2026
2d838d2
docs(AGENTS.md): rename project from template to tinybrowser
senamakel Aug 21, 2026
6c26dbb
docs(roadmap, readme): update project documentation from template to …
senamakel Aug 21, 2026
d4a0de8
docs: add integration guide for OpenHuman platform
senamakel Aug 21, 2026
7d996c9
docs: replace example retry policy docs with browser module spec and …
senamakel Aug 21, 2026
ae46063
fix(repo): update repository URL and plan description
senamakel Aug 21, 2026
deddcbe
fix(issue-template): update security report URL
senamakel Aug 21, 2026
a83db28
feat(ci): enable live browser tests in CI workflow
senamakel Aug 21, 2026
377a44c
test(bus): add tests for id conversions, ordering, and screenshot types
senamakel Aug 21, 2026
ebf80f1
test(cdp): add integration tests for launch and endpoint resolution
senamakel Aug 21, 2026
c1c88dd
fix(test): update test to match new error message for non-browser pro…
senamakel Aug 21, 2026
b9dc126
feat(tests): add live integration tests for browser actions
senamakel Aug 21, 2026
701cbf2
feat(snapshot): scope accessibility tree to a selector when one is given
senamakel Aug 21, 2026
89c327d
fix(test): simplify empty tree assertion
senamakel Aug 21, 2026
1831639
test(interact): add tests for punctuation and named key parsing
senamakel Aug 21, 2026
4f36a8b
fix(keys): stop treating a space character as a key chord
senamakel Aug 21, 2026
be2e7f2
feat(cdp): add fake-browser test harness and CDP client tests
senamakel Aug 21, 2026
0656c45
test(live-chrome): add integration tests for locator, navigation, and…
senamakel Aug 21, 2026
5e19fb4
fix(tests): remove mobile emulation from viewport size test
senamakel Aug 21, 2026
c56fbb0
fix(test): compare JSON number as f64 to avoid type mismatch
senamakel Aug 21, 2026
702afea
test(tinybus-module): add live integration tests for the tinybus module
senamakel Aug 21, 2026
4336b43
refactor(cdp): extract browser resolution logic for testability
senamakel Aug 21, 2026
7be0390
fix(cdp): make resolve_executable visible to integration tests
senamakel Aug 21, 2026
95a69e1
feat(cdp): make profile directory creation and startup timeout testable
senamakel Aug 21, 2026
598b7e5
fix(cdp): make launch_within visible to test code
senamakel Aug 21, 2026
db096b5
test(cdp): use a script instead of a shell command for the timeout test
senamakel Aug 21, 2026
f8d53f3
fix(cdp): use exec in test script to avoid orphaned sleep process
senamakel Aug 21, 2026
7b0808d
test(cdp): record elapsed time in timeout test
senamakel Aug 21, 2026
676a0f8
refactor(cdp): replace hardcoded timeout with variable in launch
senamakel Aug 21, 2026
6a10f5d
refactor(test): move imports to module level in test files
senamakel Aug 21, 2026
bb07f18
feat(tinybrowser): handle network errors gracefully in basic example
senamakel Aug 21, 2026
00b628e
refactor(tinybus): replace clippy suppression with explicit future
senamakel Aug 21, 2026
d44ea89
test(tinybus-module): replace brittle session-list assertions with id…
senamakel Aug 21, 2026
37e6d3d
fix(cdp): reorder Linux browser candidates to prefer non-snap paths
senamakel Aug 21, 2026
ffd112a
ci(workflow): add explicit browser selection for live tests
senamakel Aug 21, 2026
52c0607
feat(session): add input navigation tracking and event subscription
senamakel Aug 21, 2026
9bb6b89
fix(extract, interact): distinguish null responses and wait for navig…
senamakel Aug 21, 2026
d1531f7
test(tinybrowser): add live tests for click navigation semantics
senamakel Aug 21, 2026
38a1429
feat(tinybrowser): add scratch diagnostic example
senamakel Aug 21, 2026
2eb84d5
refactor(interact): replace event subscription with document status s…
senamakel Aug 21, 2026
8ca0682
refactor(session): replace lifecycle-event wait with polling for navi…
senamakel Aug 21, 2026
4120a41
fix(session): flatten nested if-let in readiness polling loop
senamakel Aug 21, 2026
45ee217
feat(session): replace polling with event-driven navigation detection
senamakel Aug 21, 2026
9d72545
chore(tinybrowser): remove scratch diagnostic example
senamakel Aug 21, 2026
1a8ac0d
fix(test): add explicit submit button to form submission test
senamakel Aug 21, 2026
b13f9df
feat(interact): include unmodifiedText in keyDown dispatch
senamakel Aug 21, 2026
27fd13b
feat(tinybrowser): add scratch key example
senamakel Aug 21, 2026
ad82858
chore: files changed crates/tinybrowser/examples/scratch_key.rs
senamakel Aug 21, 2026
15e39bf
chore: files changed crates/tinybrowser/examples/scratch_key.rs
senamakel Aug 21, 2026
3f60c45
fix(test): improve assertion message for form submission test
senamakel Aug 21, 2026
6dacc9a
test(live_chrome): add diagnostic polling after form submission
senamakel Aug 21, 2026
ba3c29b
fix(session): increase input navigation grace period and add diagnost…
senamakel Aug 21, 2026
1df7018
fix(session): reduce navigation grace period and improve settle detec…
senamakel Aug 21, 2026
523c0c1
fix(session): clarify the rationale for the input navigation grace pe…
senamakel Aug 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
blank_issues_enabled: true
contact_links:
- name: Security reports
url: https://github.com/tinyhumansai/rust-template/security/policy
url: https://github.com/tinyhumansai/tinybrowser/security/policy
about: Please do not report vulnerabilities through public issues.
53 changes: 46 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,20 @@ jobs:
rust:
name: Rust
runs-on: ubuntu-latest
env:
# A browser module is not meaningfully tested without a browser, and the
# coverage gate below is measured over the same run: with these unset, the
# protocol conversation would be both unexercised and uncovered, which is
# precisely the half that cannot be checked any other way.
#
# GitHub's Ubuntu runners ship Google Chrome at a path the module already
# looks in, so there is nothing to install.
TINYBROWSER_LIVE_TESTS: "1"
# Runners restrict unprivileged user namespaces, so Chrome has no usable
# sandbox here. Accepting that in a disposable CI VM is a different
# decision from applying it on somebody's host, which is why the module
# reports the condition rather than working around it by default.
TINYBROWSER_CHROME_ARGS: --no-sandbox
steps:
- uses: actions/checkout@v7
with:
Expand All @@ -38,6 +52,31 @@ jobs:

- uses: Swatinem/rust-cache@v2

# Named explicitly rather than left to the module's own discovery order.
# The runner has several browsers on it, including a snap-packaged
# `/usr/bin/chromium` that starts, says nothing, and is timed out — and a
# build should not go red because the search order changed under it. This
# also prints which browser was chosen, so a future failure says so in the
# log instead of costing a round trip to find out.
- name: Select a browser for the live tests
run: |
set -euo pipefail
for candidate in \
/usr/bin/google-chrome \
/usr/bin/google-chrome-stable \
/opt/google/chrome/chrome \
/usr/bin/chromium-browser \
/usr/bin/chromium
do
[ -x "$candidate" ] || continue
echo "selected $candidate"
"$candidate" --version || true
echo "TINYBROWSER_CHROME=$candidate" >> "$GITHUB_ENV"
exit 0
done
echo "no browser on this runner; the live tests cannot run" >&2
exit 1

- name: Check formatting
run: cargo fmt --all -- --check

Expand All @@ -54,27 +93,27 @@ jobs:
run: cargo test

# `cargo build --all-targets` only *compiles* an example. `AGENTS.md`
# promises `cargo run -p template --example basic` works, and a compiled
# promises `cargo run -p tinybrowser --example basic` works, and a compiled
# example can still fail on its first line.
- name: Run the bundled example
run: cargo run -p template --example basic
run: cargo run -p tinybrowser --example basic

# `crates/template-bus` exists so a host can name the payload types
# `crates/tinybrowser-bus` exists so a host can name the payload types
# without compiling the module. That promise is invisible in a diff,
# because a forbidden dependency arrives transitively through a feature
# someone enabled one crate away — so it is asserted rather than
# documented.
#
# The FORWARD form is required. `cargo tree -i <crate> -p template-bus`
# The FORWARD form is required. `cargo tree -i <crate> -p tinybrowser-bus`
# discards the `-p` scope, prints the whole-workspace inverse tree, and
# exits 0 looking clean even when this crate is the one at fault.
- name: Assert the contract crate stays transport-free
run: |
set -euo pipefail
forbidden="$(cargo tree -p template-bus -e normal,build --prefix none \
forbidden="$(cargo tree -p tinybrowser-bus -e normal,build --prefix none \
| grep -Ei 'tinybus|tokio|reqwest|ureq|hyper|rusqlite|git2' || true)"
if [ -n "$forbidden" ]; then
echo "template-bus pulled in a dependency its manifest forbids:" >&2
echo "tinybrowser-bus pulled in a dependency its manifest forbids:" >&2
echo "$forbidden" >&2
echo >&2
echo "The contract is what a host compiles against. It must stay free" >&2
Expand Down Expand Up @@ -128,7 +167,7 @@ jobs:
run: |
set -euo pipefail
msrv="$(cargo metadata --format-version 1 --no-deps \
| jq -r '.packages[] | select(.name == "template") | .rust_version')"
| jq -r '.packages[] | select(.name == "tinybrowser") | .rust_version')"
if [[ -z "$msrv" || "$msrv" == "null" ]]; then
echo "workspace.package.rust-version is not set in Cargo.toml" >&2
exit 1
Expand Down
14 changes: 7 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ permissions:

env:
# The workspace member that ships as the loadable module. Its package name is
# the artifact name and the library name; `crates/template-bus` rides along on
# the artifact name and the library name; `crates/tinybrowser-bus` rides along on
# the same inherited version and is not packaged separately.
RELEASE_PACKAGE: template
RELEASE_PACKAGE: tinybrowser

jobs:
prepare:
Expand Down Expand Up @@ -259,7 +259,7 @@ jobs:
macOS) module="target/release/lib${library_name}.dylib" ;;
*) echo "unsupported Unix runner: ${RUNNER_OS}" >&2; exit 1 ;;
esac
cargo run --locked --package template --example verify_module -- "$module"
cargo run --locked --package tinybrowser --example verify_module -- "$module"

- name: Verify Windows module through TinyBus loader
if: ${{ runner.os == 'Windows' }}
Expand All @@ -270,7 +270,7 @@ jobs:
$ErrorActionPreference = 'Stop'
$libraryName = $env:CRATE_NAME.Replace('-', '_')
$module = "target/release/$libraryName.dll"
$verifyRoot = Join-Path $env:RUNNER_TEMP 'template-module-verify'
$verifyRoot = Join-Path $env:RUNNER_TEMP 'tinybrowser-module-verify'
New-Item -ItemType Directory -Force $verifyRoot | Out-Null

$identity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
Expand Down Expand Up @@ -300,7 +300,7 @@ jobs:

$verifiedModule = Join-Path $verifyRoot "$libraryName.dll"
Copy-Item -LiteralPath $module -Destination $verifiedModule
cargo run --locked --package template --example verify_module -- $verifiedModule
cargo run --locked --package tinybrowser --example verify_module -- $verifiedModule

- name: Assemble Unix module package
if: ${{ runner.os != 'Windows' }}
Expand Down Expand Up @@ -463,7 +463,7 @@ jobs:
verify_root="/opt/${CRATE_NAME}-module-verify"
install -d -m 700 "$verify_root"
install -m 755 "target/release/lib${library_name}.so" "$verify_root/"
cargo run --locked --package template --example verify_module -- \
cargo run --locked --package tinybrowser --example verify_module -- \
"$verify_root/lib${library_name}.so"

- name: Assemble distribution module package
Expand Down Expand Up @@ -587,5 +587,5 @@ jobs:
cargo run --manifest-path vendor/tinybus/Cargo.toml --locked \
--package tinybus --all-features --example github_module_host -- \
"$release_url" "$archive" "$sha256"
cargo run --locked --package template --example verify_github_release -- \
cargo run --locked --package tinybrowser --example verify_github_release -- \
"$release_url" "$archive" "$sha256"
122 changes: 66 additions & 56 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,63 +4,45 @@ This file is the single source of truth for how humans and coding agents work
in this repository. `CLAUDE.md` is a symlink to this file, so every agent reads
the same instructions.

When you generate a new project from this template, keep this file and adapt
the project-specific parts (crate name, module map, feature flags, commands).
Delete guidance that no longer applies rather than leaving it to rot.

## Template Checklist

Do this once, in a single commit, before writing feature code:

- [ ] Rename `crates/template` and `crates/template-bus` to the project's crate
names, and update `name` in each manifest plus the `template-bus` entry in
the root `[workspace.dependencies]`.
- [ ] Set `description`, `keywords`, and `categories` in each manifest, and
`repository` in the root `[workspace.package]`.
- [ ] Rename the crate references in `README.md`, both `src/lib.rs` files,
`crates/template/examples/`, and `crates/template/tests/` (search for
`template` and `template_bus`).
- [ ] Replace the placeholder `greeting` module in both crates with the first
real feature area — payload types in the contract crate, behavior in the
module crate — keeping the `mod.rs` / `types.rs` / `test.rs` layout.
- [ ] Confirm `license` and `LICENSE` match the project's intended license.
- [ ] Update the security contact in `SECURITY.md`.
- [ ] Rename the TinyBus interface, object path, and member constants in
`crates/template-bus/src/names/`, and the matching `provides` / `methods`
declarations in `crates/template/src/tinybus_module/`, while keeping
`vendor/tinybus` pinned.
- [ ] Reset `CONTRACT_VERSION` in `crates/template-bus/src/version/` for the new
contract.
- [ ] Replace `ROADMAP.md` with the real plan, or delete it.
- [ ] Rewrite the "Project Structure" section below to describe this workspace.
This project began as `rust-template` and no longer is one: the template
checklist has been worked through and removed. What remains describes this
workspace.

## Project Structure

This is a Rust 2024 cargo workspace rooted at a virtual `Cargo.toml`. Every
crate lives under `crates/`, one directory per package, each directory named for
the package it holds. There is no root package: the crate that ships as the
loadable module is `crates/template`, the same as any other member.
the package it holds. There is no root package: the crate a host loads is
`crates/tinybrowser`, the same as any other member.

```text
Cargo.toml # virtual workspace: members, [workspace.package],
# [workspace.dependencies], [workspace.lints]
crates/
├── template-bus/ # the wire contract: what crosses the bus, nothing else
├── tinybrowser-bus/ # the wire contract: what crosses the bus, nothing else
│ ├── README.md # why the contract is its own crate
│ └── src/
│ ├── lib.rs # crate docs + the entire public re-export surface
│ ├── names/ # interface, object path, one constant per member
│ ├── version/ # contract version and the host bind rule
│ └── <family>/ # one directory per payload family
└── template/ # the module: behavior, adapter, and the cdylib
│ ├── session/ # opening, listing, closing a browser
│ ├── page/ # navigation, extraction, evaluation
│ ├── snapshot/ # the accessibility tree and its refs
│ ├── action/ # every interaction, and how an element is named
│ ├── output/ # screenshots and the handle protocol that carries them
│ └── errors/ # the wire error names, and which an agent can act on
└── tinybrowser/ # the engine: behavior, adapter, and the cdylib
├── src/
│ ├── lib.rs # crate docs + public surface, re-exporting the contract
│ ├── error/mod.rs # crate-wide `Error` and `Result<T>`
│ ├── tinybus_module/ # TinyBus interface, ABI exports, integration tests
│ └── <feature>/ # one directory per feature area
│ ├── mod.rs # module docs, wiring, smallest useful public API
│ ├── types.rs # substantial type definitions
│ └── test.rs # module-local unit tests
│ ├── lib.rs # crate docs + public surface, re-exporting the contract
│ ├── error/ # crate-wide `Error` and `Result<T>`
│ ├── engine/ # `Browser`: the facade every bus member calls
│ ├── cdp/ # the socket, the endpoint, and launching a browser
│ ├── session/ # one browser and page, plus policy and refs
│ ├── snapshot/ # accessibility tree in, indented text and refs out
│ ├── interact/ # target resolution, key parsing, the in-page scripts
│ ├── extract/ # reading a page as text, Markdown, or DOM
│ ├── capture/ # screenshots and the held-output store
│ └── tinybus_module/ # TinyBus interface, ABI exports, integration tests
├── tests/ # integration tests against the public API only
└── examples/ # runnable, compiled-in-CI usage examples
vendor/tinybus/ # pinned TinyBus host types and module SDK
Expand All @@ -72,19 +54,37 @@ docs/

### The two-crate split

`crates/template-bus` holds every type that crosses the bus and the names of the
members that carry them. It has no transport, no runtime, and no behavior, and
CI asserts it stays that way. A host that only makes calls depends on it alone.
`crates/tinybrowser-bus` holds every type that crosses the bus and the names of
the members that carry them. It has no transport, no runtime, and no behavior,
and CI asserts it stays that way. A host that only makes calls depends on it
alone — which is the whole point, because that host is a binary that
deliberately does not want a browser stack in its build.

`crates/template` depends on it and re-exports all of it, so
`template::GreetRequest` and `template_bus::GreetRequest` are the *same* type
rather than structural twins. That direction is load-bearing: a parallel set of
payload types for hosts would mean a conversion at every call site that nothing
checks.
`crates/tinybrowser` depends on it and re-exports all of it, so
`tinybrowser::Action` and `tinybrowser_bus::Action` are the *same* type rather
than structural twins. That direction is load-bearing: a parallel set of payload
types for hosts would mean a conversion at every call site that nothing checks.

The rule for deciding where something goes: a payload type describes what a
frame carries and belongs in the contract; anything that answers a frame, holds
a connection, or touches an engine belongs in the module crate.
a connection, or touches a browser belongs in the module crate.

### Where the layers meet

`engine::Browser` is the only thing `tinybus_module` calls, and it is the only
thing that holds sessions and outputs. Everything below it — `cdp`, `session`,
`snapshot`, `interact`, `extract`, `capture` — is private and written against
`session::Session` rather than against the protocol. Two rules keep that from
eroding:

- **No decisions in the adapter.** `tinybus_module` deserializes, calls one
method, and maps an error to a wire name. Anything decided there could only be
tested through a bus, and a Rust caller using `Browser` directly would not get
it.
- **Judgement goes in a pure function.** Snapshot rendering, key parsing, URL
policy, and output chunking are all pure, and all unit-tested without a
browser. If a rule can only be exercised against a live Chrome, it is in the
wrong place.

Add a crate by creating `crates/<name>/` — `members = ["crates/*"]` picks it up
by existing. Inherit `version`, `edition`, `rust-version`, `license`, and
Expand Down Expand Up @@ -114,7 +114,7 @@ broad ones.

Keep public exports centralized in each crate's `src/lib.rs` so downstream users
have one predictable surface. Put shared error variants in
`crates/template/src/error/mod.rs` and return the crate-wide `Result<T>` from
`crates/tinybrowser/src/error/mod.rs` and return the crate-wide `Result<T>` from
fallible public APIs.

## Build And Test
Expand All @@ -133,8 +133,11 @@ Supporting commands:

- `cargo fmt --all` — format before committing.
- `cargo test <filter>` — run a focused subset while iterating.
- `cargo test -p template-bus` — run one crate's suite.
- `cargo run -p template --example basic` — run the bundled example.
- `cargo test -p tinybrowser-bus` — run one crate's suite.
- `cargo run -p tinybrowser --example basic` — run the bundled example.
- `TINYBROWSER_LIVE_TESTS=1 cargo test -p tinybrowser --test live_chrome` — the
end-to-end suite, against a real browser. Opt-in, because a machine without
one would fail it for a reason unrelated to the change.
- `cargo doc --no-deps --all-features` — build the rustdoc CI also builds with
`RUSTDOCFLAGS="-D warnings"`.
- `cargo test --doc` — run doctests alone when editing documentation examples.
Expand Down Expand Up @@ -185,7 +188,7 @@ add one:
- gate anything optional behind a Cargo feature, documented in `Cargo.toml`;
- declare it once in the root `[workspace.dependencies]` when more than one
crate needs it, and take it with `{ workspace = true }`;
- never add one to `crates/template-bus` that pulls in a transport, an async
- never add one to `crates/tinybrowser-bus` that pulls in a transport, an async
runtime, an HTTP client, or a native library — CI fails the build if you do;
- leave a comment above the entry explaining *why* the crate is needed and what
uses it — see the existing entries for the expected tone;
Expand Down Expand Up @@ -226,7 +229,14 @@ new module capability requires more.
for tests) rather than mixing runtimes.
- Tests must be deterministic and independent of network, wall-clock time, and
execution order. Gate any live/network test behind a feature or an env var and
name it `live_*` so it is easy to exclude.
name it `live_*` so it is easy to exclude. Use an env var rather than a Cargo
feature here: the contract command is `cargo test --all-features`, which would
switch a feature on.
- **A live test that skips silently is worse than no test.** `live_chrome`
checks its opt-in once and then fails if it cannot find a browser. A suite that
quietly does nothing reports green for a build in which nothing was checked.
- The unit suites must not need a browser. Anything that would is a sign the
judgement belongs in a pure function that can be tested without one.
- Maintain at least 90% line coverage in every source file. Add or update tests
with every behavior change, and note any deliberately untested edge case in
the pull request description.
Expand Down Expand Up @@ -295,7 +305,7 @@ Releases run from `.github/workflows/release.yml` via a manual
an interrupted release after its version commit and tag exist. The workflow
re-runs the full validation suite, computes the next version, updates
the root `[workspace.package]` version and `Cargo.lock`, commits and tags
`vX.Y.Z`, builds `crates/template` as a TinyBus module for every supported
`vX.Y.Z`, builds `crates/tinybrowser` as a TinyBus module for every supported
platform, pushes, and creates an immutable GitHub release with installable
native packages.

Expand Down
Loading