···5858- DMs are intentionally deferred from the current browser-smoke tranche; see `docs/BROWSER_SMOKE.md` for current scope and rationale.
5959- Fresh-account creation is still available through the explicit `bootstrap-*` commands, but it is no longer the normal path for repeated browser smoke runs.
6060- Detailed browser-smoke workflow, current interaction coverage, and the env-gated `prove` wrapper live in `docs/BROWSER_SMOKE.md`.
6161-- Extraction work toward a cross-PDS standalone package now lives in `atproto-smoke/`, which owns the browser runtime, package CLI, example configs, and bring-your-own-account plus `perlsky` adapter helpers.
6262-- `script/perlsky-browser-smoke` now prefers an external sibling checkout at `../atproto-smoke` when present, and falls back to the in-repo `atproto-smoke/` copy. Set `PERLSKY_BROWSER_SUITE_ROOT` to point it at any other checkout explicitly.
6161+- Extraction work toward a cross-PDS standalone package now lives in the `atproto-smoke` repo, which owns the browser runtime, package CLI, example configs, and bring-your-own-account plus `perlsky` adapter helpers.
6262+- `script/perlsky-browser-smoke` expects a standalone checkout at `../atproto-smoke` by default. Set `PERLSKY_BROWSER_SUITE_ROOT` to point it at any other checkout explicitly.
6363+- The shared smoke runtime now applies a bounded per-step timeout (`stepTimeoutMs`, default `120000`) so late browser stalls fail with artifacts instead of hanging forever.
63646465Moderation and labels:
6566
···11-# atproto-smoke
22-33-This directory is the extraction staging area for a standalone `bsky.app`
44-compatibility smoke suite that can be used by multiple PDS implementations, not
55-just `perlsky`. The browser runtime now lives here, while the old
66-`tools/browser-automation/*` paths remain as thin compatibility wrappers for
77-the current `perlsky` workflow.
88-99-## Quickstart
1010-1111-```sh
1212-npm install
1313-npx playwright install chromium
1414-node bin/atproto-smoke.mjs print-example --mode dual > config.json
1515-$EDITOR config.json
1616-node bin/atproto-smoke.mjs run-dual --config config.json
1717-```
1818-1919-For the lowest-friction path, point the suite at an existing PDS and two
2020-existing accounts. The package is intentionally adapter-friendly, but
2121-bring-your-own accounts are the default path for non-Perl PDS implementations.
2222-2323-## Current Scope
2424-2525-The existing browser automation is already strong enough to be useful outside
2626-this repo:
2727-2828-- reusable-account `bsky.app` smoke flows
2929-- post, image post, like, repost, quote, reply, bookmark, follow
3030-- list lifecycle
3131-- profile edit and avatar upload
3232-- notifications checks
3333-- settings-depth flows
3434-- strict artifacts with screenshots, console output, failed requests, failed
3535- HTTP responses, and recent XRPC traffic
3636-3737-DMs are intentionally deferred for now. The current suite is focused on stable
3838-social, list, and settings interactions first.
3939-4040-## Extraction Shape
4141-4242-The target standalone project shape is:
4343-4444-1. Generic core browser flows and artifact handling
4545-2. A bring-your-own-accounts mode with minimal configuration
4646-3. Thin per-PDS adapters for provisioning and implementation-specific defaults
4747-4848-The generic runtime, config builders, and adapter helpers now live here. The
4949-older `tools/browser-automation/` entrypoints simply forward into this package
5050-so the existing repo scripts keep working during the extraction.
5151-5252-## Current CLI
5353-5454-The package now has its own CLI entrypoint:
5555-5656-```sh
5757-node atproto-smoke/bin/atproto-smoke.mjs print-example --mode dual
5858-node atproto-smoke/bin/atproto-smoke.mjs validate --mode dual --config atproto-smoke/examples/bring-your-own-dual.json
5959-node atproto-smoke/bin/atproto-smoke.mjs run-dual --config atproto-smoke/examples/bring-your-own-dual.json
6060-```
6161-6262-Examples live in [examples/](./examples):
6363-6464-- `bring-your-own-single.json`
6565-- `bring-your-own-dual.json`
6666-- `perlsky-dual.json`
6767-6868-## Minimal Configuration Goal
6969-7070-The default experience for other PDS developers should be:
7171-7272-- provide a `pdsUrl`
7373-- provide one or two existing account credentials
7474-- optionally provide a `targetHandle`
7575-- run the suite against `bsky.app`
7676-7777-Provisioning is intentionally adapter-specific. That means `perlsky` can keep a
7878-helpful invite/bootstrap path, while other PDSes like `rsky` or `pegasus` can
7979-add their own adapters without changing the core browser flows.
8080-8181-## Current Adapter Contract
8282-8383-The staging helpers in `src/` model two layers:
8484-8585-- `adapters/bring-your-own.mjs`
8686- For the lowest-friction mode where callers supply existing credentials
8787-- `adapters/perlsky.mjs`
8888- For `perlsky`-specific defaults like cleanup prefixes and adapter tagging
8989-9090-The current config contract is intentionally small:
9191-9292-- suite-level settings:
9393- `pdsUrl`, `artifactsDir`, `appUrl`, `publicApiUrl`, `targetHandle`,
9494- `publicCheckTimeoutMs`, `headless`, `strictErrors`, `publicChecks`,
9595- `browserExecutablePath`, `adapter`
9696-- account-level settings:
9797- `handle`, `password`, `birthdate`, `postText`, `mediaPostText`, `quoteText`,
9898- `replyText`, `profileNote`, `cleanupPostPrefixes`
9999-100100-`pdsHost` is derived automatically from `pdsUrl`, so callers do not need any
101101-perlsky-specific host-setting knowledge just to point the browser at a custom
102102-PDS.
103103-104104-## V2 Ideas
105105-106106-The long-term direction is a test pyramid, not a browser-only harness and not a
107107-pure endpoint-only harness:
108108-109109-1. direct PDS/AppView contract tests
110110-2. cross-service integration checks
111111-3. a thinner `bsky.app` smoke on top
112112-113113-The browser layer stays because it catches real `social-app` assumptions and
114114-AppView proxying issues. The direct API/AppView layers belong underneath it so
115115-regressions become easier to debug and less brittle when the UI changes.
116116-117117-In other words: this project should eventually answer both "does my PDS return
118118-the right protocol shapes?" and "does it still behave correctly through
119119-`bsky.app` and AppView-backed reads?".
120120-121121-## Planned Next Steps
122122-123123-- keep `script/perlsky-browser-smoke` as a thin `perlsky` adapter over this
124124- generic package
125125-- add a repo-independent install story once the extracted package boundary
126126- settles
127127-- add direct API/AppView contract tests as the first major v2 expansion
128128-- revisit a JS-to-TS migration later, after the standalone package boundary is
129129- stable
···11-export * from './config.mjs';
22-export * from './adapters/bring-your-own.mjs';
33-export * from './adapters/perlsky.mjs';
44-export * from './browser/run-single.mjs';
55-export * from './browser/run-dual.mjs';
66-export * from './cli.mjs';
+11-4
docs/BROWSER_SMOKE.md
···7373- HTTP failures
7474- recent XRPC traffic
75757676+The generic runtime also applies a per-step timeout (`stepTimeoutMs`, default
7777+`120000`) so late browser stalls fail as bounded smoke errors instead of
7878+hanging indefinitely.
7979+7680## Test Suite Wrapper
77817882The browser smoke is available from `prove`, but it is intentionally opt-in:
···86908791## Extraction
88928989-The generic runtime now lives under [atproto-smoke](../atproto-smoke/README.md).
9393+The generic runtime now lives in the standalone `atproto-smoke` repo:
9494+9595+- `https://github.com/aliceisjustplaying/atproto-smoke`
9696+- `https://tangled.org/alice.mosphere.at/atproto-smoke`
9797+9098`perlsky` still keeps [script/perlsky-browser-smoke](/Users/sarah/src/tries/2026-03-10-perlds/script/perlsky-browser-smoke)
9199as the ergonomic adapter for this repo, but the standalone package now owns:
92100···98106This keeps the current `perlsky` workflow stable while making extraction to a
99107repo-independent package much more straightforward.
100108101101-The wrapper now prefers an external sibling checkout at `../atproto-smoke`
102102-when present, and otherwise falls back to the in-repo `atproto-smoke/` copy.
103103-Set `PERLSKY_BROWSER_SUITE_ROOT` to force a specific checkout.
109109+The wrapper now expects a standalone `atproto-smoke` checkout, either at the
110110+default sibling path `../atproto-smoke` or via `PERLSKY_BROWSER_SUITE_ROOT`.
104111105112## Notes
106113