···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 `pds-smoke-suite/`, which owns the browser runtime, package CLI, example configs, and bring-your-own-account plus `perlsky` adapter helpers.
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- For now, `script/perlsky-browser-smoke` remains the active `perlsky` adapter entrypoint in this repo, forwarding into the generic package while the external package boundary stabilizes.
63636464Moderation and labels:
+111
atproto-smoke/README.md
···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+## Current Scope
1010+1111+The existing browser automation is already strong enough to be useful outside
1212+this repo:
1313+1414+- reusable-account `bsky.app` smoke flows
1515+- post, image post, like, repost, quote, reply, bookmark, follow
1616+- list lifecycle
1717+- profile edit and avatar upload
1818+- notifications checks
1919+- settings-depth flows
2020+- strict artifacts with screenshots, console output, failed requests, failed
2121+ HTTP responses, and recent XRPC traffic
2222+2323+DMs are intentionally deferred for now. The current suite is focused on stable
2424+social, list, and settings interactions first.
2525+2626+## Extraction Shape
2727+2828+The target standalone project shape is:
2929+3030+1. Generic core browser flows and artifact handling
3131+2. A bring-your-own-accounts mode with minimal configuration
3232+3. Thin per-PDS adapters for provisioning and implementation-specific defaults
3333+3434+The generic runtime, config builders, and adapter helpers now live here. The
3535+older `tools/browser-automation/` entrypoints simply forward into this package
3636+so the existing repo scripts keep working during the extraction.
3737+3838+## Current CLI
3939+4040+The package now has its own CLI entrypoint:
4141+4242+```sh
4343+node atproto-smoke/bin/atproto-smoke.mjs print-example --mode dual
4444+node atproto-smoke/bin/atproto-smoke.mjs validate --mode dual --config atproto-smoke/examples/bring-your-own-dual.json
4545+node atproto-smoke/bin/atproto-smoke.mjs run-dual --config atproto-smoke/examples/bring-your-own-dual.json
4646+```
4747+4848+Examples live in [examples/](./examples):
4949+5050+- `bring-your-own-single.json`
5151+- `bring-your-own-dual.json`
5252+- `perlsky-dual.json`
5353+5454+## Minimal Configuration Goal
5555+5656+The default experience for other PDS developers should be:
5757+5858+- provide a `pdsUrl`
5959+- provide one or two existing account credentials
6060+- optionally provide a `targetHandle`
6161+- run the suite against `bsky.app`
6262+6363+Provisioning is intentionally adapter-specific. That means `perlsky` can keep a
6464+helpful invite/bootstrap path, while other PDSes like `rsky` or `pegasus` can
6565+add their own adapters without changing the core browser flows.
6666+6767+## Current Adapter Contract
6868+6969+The staging helpers in `src/` model two layers:
7070+7171+- `adapters/bring-your-own.mjs`
7272+ For the lowest-friction mode where callers supply existing credentials
7373+- `adapters/perlsky.mjs`
7474+ For `perlsky`-specific defaults like cleanup prefixes and adapter tagging
7575+7676+The current config contract is intentionally small:
7777+7878+- suite-level settings:
7979+ `pdsUrl`, `artifactsDir`, `appUrl`, `publicApiUrl`, `targetHandle`,
8080+ `publicCheckTimeoutMs`, `headless`, `strictErrors`, `publicChecks`,
8181+ `browserExecutablePath`, `adapter`
8282+- account-level settings:
8383+ `handle`, `password`, `birthdate`, `postText`, `mediaPostText`, `quoteText`,
8484+ `replyText`, `profileNote`, `cleanupPostPrefixes`
8585+8686+`pdsHost` is derived automatically from `pdsUrl`, so callers do not need any
8787+perlsky-specific host-setting knowledge just to point the browser at a custom
8888+PDS.
8989+9090+## V2 Ideas
9191+9292+The long-term direction is a test pyramid, not a browser-only harness and not a
9393+pure endpoint-only harness:
9494+9595+1. direct PDS/AppView contract tests
9696+2. cross-service integration checks
9797+3. a thinner `bsky.app` smoke on top
9898+9999+The browser layer stays because it catches real `social-app` assumptions and
100100+AppView proxying issues. The direct API/AppView layers belong underneath it so
101101+regressions become easier to debug and less brittle when the UI changes.
102102+103103+## Planned Next Steps
104104+105105+- keep `script/perlsky-browser-smoke` as a thin `perlsky` adapter over this
106106+ generic package
107107+- add a repo-independent install story once the extracted package boundary
108108+ settles
109109+- add direct API/AppView contract tests as the first major v2 expansion
110110+- revisit a JS-to-TS migration later, after the standalone package boundary is
111111+ 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';
+1-1
docs/BROWSER_SMOKE.md
···86868787## Extraction
88888989-The generic runtime now lives under [pds-smoke-suite](../pds-smoke-suite/README.md).
8989+The generic runtime now lives under [atproto-smoke](../atproto-smoke/README.md).
9090`perlsky` still keeps [script/perlsky-browser-smoke](/Users/sarah/src/tries/2026-03-10-perlds/script/perlsky-browser-smoke)
9191as the ergonomic adapter for this repo, but the standalone package now owns:
9292
+1-1
tools/browser-automation/dual-smoke.mjs
···11-import { runDualFromArgv } from '../../pds-smoke-suite/src/browser/run-dual.mjs';
11+import { runDualFromArgv } from '../../atproto-smoke/src/browser/run-dual.mjs';
2233const exitCode = await runDualFromArgv(process.argv);
44process.exitCode = exitCode;
+1-1
tools/browser-automation/smoke.mjs
···11-import { runSingleFromArgv } from '../../pds-smoke-suite/src/browser/run-single.mjs';
11+import { runSingleFromArgv } from '../../atproto-smoke/src/browser/run-single.mjs';
2233const exitCode = await runSingleFromArgv(process.argv);
44process.exitCode = exitCode;