From a6932bf3c4799cbecb980b359831d5390b837a6f Mon Sep 17 00:00:00 2001 From: dormouse-bot <287024035+dormouse-bot@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:44:53 +0000 Subject: [PATCH 1/2] Correct stale comments in the relay push path, managed voice, the native direct-peer test, and the browser specs Co-Authored-By: Claude --- .github/workflows/security-audit.yaml | 4 +-- docs/specs/dor-browser.md | 6 ++--- docs/specs/dor-cli.md | 2 +- dor/src/commands/send.ts | 6 ++--- hosted/server/entitlement.ts | 2 +- lib/src/host/managed-voice-host.ts | 2 +- .../host/remote/native-direct-peer.test.ts | 26 +++++++++---------- lib/src/lib/managed-voice-engine.ts | 2 +- relay/src/app.ts | 4 +-- relay/src/push.ts | 3 ++- relay/src/setup-token.ts | 2 +- remote-lib-common/src/security/acl.ts | 2 +- scripts/pairing-walkthrough/README.md | 2 +- website/src/lib/tut-runner.ts | 2 +- 14 files changed, 33 insertions(+), 32 deletions(-) diff --git a/.github/workflows/security-audit.yaml b/.github/workflows/security-audit.yaml index a03869f5e..bb4ea71db 100644 --- a/.github/workflows/security-audit.yaml +++ b/.github/workflows/security-audit.yaml @@ -95,8 +95,8 @@ jobs: timeout-minutes: 40 # The AUDIT_PAT (read-only Administration + Secrets + Environments) # lives in this environment, whose deployment-branch-policy admits - # only `main` and `v*` tags — refs that the §3 rulesets reserve to - # admins. A bot-pushed feature branch cannot reach this job at all + # only `main` and `v*` tags — refs that the `Tag operations` and + # `Merge access` rulesets reserve to admins. A bot-pushed feature branch cannot reach this job at all # (GitHub rejects the run before any step starts), so the PAT # cannot be exfiltrated through a hand-authored workflow on a # non-admin-gated ref. diff --git a/docs/specs/dor-browser.md b/docs/specs/dor-browser.md index 03bf154dc..782de341e 100644 --- a/docs/specs/dor-browser.md +++ b/docs/specs/dor-browser.md @@ -119,7 +119,7 @@ Source of truth: `resolveBrowserViewport` in `dor-lib-common/src/browser-viewpor ## Automated Browser -Dormouse is a viewer and client for the user's installed provider CLI; it neither bundles nor forks a browser. `dor agent-browser` / `dor playwright` extract identity flags, handle `dor-embed-size` and prepare initial sizing ([Viewport presets](#viewport-presets)); native commands then run against the resolved session, and flags Dormouse does not model pass through (`docs/specs/dor-cli.md` → Browser Surface Addressing). The webview's two channels: +Dormouse is a viewer and client for the user's installed provider CLI; it neither bundles nor forks a browser. `dor agent-browser` / `dor playwright` extract identity flags, handle `dor-embed-size` and `dor-diagnostics`, and prepare initial sizing ([Viewport presets](#viewport-presets)); native commands then run against the resolved session, and flags Dormouse does not model pass through (`docs/specs/dor-cli.md` → Browser Surface Addressing). The webview's two channels: ```mermaid flowchart LR @@ -172,7 +172,7 @@ A pane out of sight ([Resource Policy](#resource-policy)) or unmounted parks aft **Must send a local paste as bounded `input_text` messages** (`viewerTextInputs`), never a key pair per character (rationale). Select-all, copy, cut, and tab select or close go through host operations (`edit`, `tab`), since editing chords do not survive CDP input; undo/redo is not emulated. -Source of truth: `lib/src/components/wall/agent-browser-surface-controller.ts` (`Phase`, `driver`), `lib/src/components/wall/AgentBrowserPanel.tsx`, `lib/src/components/wall/agent-browser-input.ts`, `onBrowserLaunchFailed` in `lib/src/components/Wall.tsx`. +Source of truth: `lib/src/components/wall/agent-browser-surface-controller.ts` (`Phase`, `driver`), `lib/src/components/wall/AgentBrowserPanel.tsx`, `lib/src/components/wall/agent-browser-input.ts`, `viewerTextInputs` in `lib/src/lib/platform/browser-automation.ts`, `onBrowserLaunchFailed` in `lib/src/components/Wall.tsx`. ### Viewer Socket @@ -248,7 +248,7 @@ Source of truth: `createPlaywrightProvider` in `lib/src/host/playwright-host.ts` ## Iframe Renderer -`dor iframe ` frames the page's own DOM — zero-lag for human inspection, but agents cannot drive or read it. On hosts with `createIframeProxyUrl`, `IframePanel` frames a per-grant loopback proxy URL; without it, a raw uninstrumented iframe. The desktop playground's fronts only its own viewers (`docs/specs/tutorial.md` → Playground filesystem). +`dor iframe ` frames the page's own DOM — zero-lag for human inspection, but agents cannot drive or read it. On hosts with `createIframeProxyUrl`, `IframePanel` frames a per-grant loopback proxy URL; without it, a raw uninstrumented iframe. The desktop playground's `createIframeProxyUrl` fronts only its own viewers (`docs/specs/tutorial.md` → Playground filesystem). The proxy instruments any `http://` upstream, loopback and remote alike: headers are rewritten per the table below and the shim injected into HTML; HTTP and WebSocket traffic passes through. A site's "do not embed" is overridden, not obeyed (rationale); the sandbox neutralizes JS framebusting. **Must refuse link-local / cloud-metadata address literals (`scheme`) after canonicalizing equivalent spellings** — decimal/octal/hex, short forms, IPv4-mapped IPv6; this checks the hostname literal, not DNS answers, and named targets remain the user's command authority. diff --git a/docs/specs/dor-cli.md b/docs/specs/dor-cli.md index 9f570aa49..03425e57b 100644 --- a/docs/specs/dor-cli.md +++ b/docs/specs/dor-cli.md @@ -256,7 +256,7 @@ Source of truth: `runBrowserCli` in `dor/src/commands/browser-cli.ts`; `BrowserV **Must intercept `dor agent-browser` and `dor playwright` before stricli parses provider arguments.** -- **Exactly one identity flag**: `--key` (default `default`), `--session`, or `--surface`, plus `--workspace`; any two fail. Except the Dormouse-owned `dor-embed-size`, arguments are forwarded to the provider; stdout, stderr and exit status pass through. +- **Exactly one identity flag**: `--key` (default `default`), `--session`, or `--surface`, plus `--workspace`; any two fail. Except the Dormouse-owned `dor-embed-size` and `dor-diagnostics`, arguments are forwarded to the provider; stdout, stderr and exit status pass through. - **Resolution is host-side**: a `--key` or `--surface` takes one `surface.resolveBrowser { provider, key | surface, proposed? }` round trip before the binary runs, answered with a binding `{ session, cwd?, binaryPath? }` (`docs/specs/dor-browser.md` → Managed identity). `proposed` — the caller's cwd and executable — rides only a command that may bind. `--session` and an informational command ask nothing. Outside Dormouse a `--surface` fails. - **After a command that may bind succeeds, `surface.browser { provider, key?, session, cwd, binaryPath, wsPort? }` opens or reuses its Surface**: `dor agent-browser` first reads the stream port itself (`docs/specs/dor-browser.md` → agent-browser), and the host reports Playwright's stream. **The call must wait past `BROWSER_REQUEST_TIMEOUT_MS`**, since the host's answer can queue behind a launch or close of the browser (rationale). A failure there adds a stderr warning without changing the command's success. **Exception: a fixed-DPR Playwright `open` binds before navigating**, and on a DPR mismatch kills a Surface it created and fails unopened. diff --git a/dor/src/commands/send.ts b/dor/src/commands/send.ts index 4a1ac8cb5..2c8eb9d10 100644 --- a/dor/src/commands/send.ts +++ b/dor/src/commands/send.ts @@ -32,9 +32,9 @@ type SendInput = | { kind: 'key'; key: string }; // stricli parses each flag independently, so it can't enforce "no duplicate input -// flags" or "--text before --key". `cli.ts` runs this before stricli parses, next -// to the flag definitions below — keep the recognized-flag list in `sendFlagName` -// in sync with `parameters.flags`. +// flags" or "--text before --key". `cli-core.ts` runs this as `preParse` before +// stricli parses — keep `sendFlagName`'s input and output flags in sync with +// `parameters.flags`. export function validateSendFlags(args: string[]): ParseResult { const flagPositions = new Map(); const positionalStart = args.indexOf('--'); diff --git a/hosted/server/entitlement.ts b/hosted/server/entitlement.ts index 364227e05..a6724c057 100644 --- a/hosted/server/entitlement.ts +++ b/hosted/server/entitlement.ts @@ -9,7 +9,7 @@ if (!/^[^'\\]+$/.test(ADMIN_EMAIL)) throw new Error("ADMIN_EMAIL cannot be inlin /** * The entitlement (docs/specs/pricing.md -> "Checkout and entitlement"): a - * SQL boolean over the `"user"` row aliased `user`, so a query that resolves + * SQL boolean over the `"user"` row aliased by `user` (default `u`), so a query that resolves * a bearer resolves its owner's entitlement in the same statement. Admin-only * until billing ships: the verified `ADMIN_EMAIL`. */ diff --git a/lib/src/host/managed-voice-host.ts b/lib/src/host/managed-voice-host.ts index 2f8af91da..01056438f 100644 --- a/lib/src/host/managed-voice-host.ts +++ b/lib/src/host/managed-voice-host.ts @@ -179,7 +179,7 @@ export function createManagedVoiceHost(options: { const speakUrl = voice === null ? null : voice + MANAGED_VOICE_SPEAK_PATH; const tokenUrl = voice === null ? null : voice + MANAGED_VOICE_TOKEN_PATH; let loaded: Promise | null = null; - // Each edit reads, then rewrites the whole file: two at once would drop a field. + // Each edit derives its record from the loaded one: two at once would drop one's change. const serialize = createSerialQueue(); const clips = createClipCache(); let notEntitled = false; diff --git a/lib/src/host/remote/native-direct-peer.test.ts b/lib/src/host/remote/native-direct-peer.test.ts index 3bfb9f765..2f6840ce7 100644 --- a/lib/src/host/remote/native-direct-peer.test.ts +++ b/lib/src/host/remote/native-direct-peer.test.ts @@ -17,7 +17,7 @@ * installed under `standalone/sidecar`, which is where the shipped bundle finds * it with a bare `require`; `lib` must not depend on it, because `lib` is a * browser bundle root. So these cases inject a plain factory over the polyfill - * this file resolves, and the shipped factory is driven by the last case alone. + * this file resolves, and only the last case tears the shipped factory down. */ import { createSocket, type SocketType } from 'node:dgram'; @@ -99,7 +99,7 @@ const CASE_BUDGET_MS = 45_000; interface Negotiation { /** Whether the channel this attempt describes has come up. */ open(): boolean; - /** Drop this attempt's peers, so a retry does not run beside them. */ + /** Close this run's peers; each case calls it once it is done with them. */ abandon(): void; } @@ -559,17 +559,6 @@ describe('the direct path over the native addon', () => { } }); - /** - * The shipped factory, last because its teardown is the process's. - * - * **A teardown is terminal**: the native threads are gone, so a peer built on - * them is not one this process can use and every later offer declines, - * leaving that session relayed. The other half of the contract — a load that - * fails warns once and declines from then on — is not drivable here: vitest's - * `require` resolves from inside the store, where every workspace package is - * reachable, so the addon cannot be made to not load without replacing the - * module system this case exists to exercise. - */ /** * **What the reliability check does *not* reach on this stack.** `DirectPeer` * refuses a channel that is unordered or partially reliable, and the answerer @@ -601,6 +590,17 @@ describe('the direct path over the native addon', () => { CASE_BUDGET_MS, ); + /** + * The shipped factory, last because its teardown is the process's. + * + * **A teardown is terminal**: the native threads are gone, so a peer built on + * them is not one this process can use and every later offer declines, + * leaving that session relayed. The other half of the contract — a load that + * fails warns once and declines from then on — is not drivable here: vitest's + * `require` resolves from inside the store, where every workspace package is + * reachable, so the addon cannot be made to not load without replacing the + * module system this case exists to exercise. + */ it('builds a peer through a bare require, and declines once torn down', () => { const warn = vi.spyOn(console, 'warn').mockImplementation(() => {}); try { diff --git a/lib/src/lib/managed-voice-engine.ts b/lib/src/lib/managed-voice-engine.ts index 9d2b3e754..cc92c55a4 100644 --- a/lib/src/lib/managed-voice-engine.ts +++ b/lib/src/lib/managed-voice-engine.ts @@ -19,7 +19,7 @@ export interface ManagedVoicePlayback { create(url: string): ManagedVoiceAudio; } -/** Blob URL + `HTMLAudioElement`; the standalone CSP grants `media-src blob:`. */ +/** Blob URL + `HTMLAudioElement`; both hosts' CSPs grant `media-src blob:`. */ export const browserPlayback: ManagedVoicePlayback = { url: (audio) => URL.createObjectURL(new Blob([audio as BlobPart], { type: 'audio/mpeg' })), revoke: (url) => URL.revokeObjectURL(url), diff --git a/relay/src/app.ts b/relay/src/app.ts index b745e4ffd..3cc50e1b9 100644 --- a/relay/src/app.ts +++ b/relay/src/app.ts @@ -169,8 +169,8 @@ export interface AppConfig { readonly vapidPublicKey?: string; /** * Web Push delivery. Injectable for the same reason as `now` — the send route - * is testable without a real push service. `index.ts` supplies the `web-push` - * implementation. + * is testable without a real push service. `start.ts` supplies + * `createWebPushSender`. */ readonly pushSender?: PushSender; /** diff --git a/relay/src/push.ts b/relay/src/push.ts index 3446afb68..713ef051d 100644 --- a/relay/src/push.ts +++ b/relay/src/push.ts @@ -139,7 +139,8 @@ function decodeVapidKey(value: string, name: 'public' | 'private', length: numbe } /** - * Real delivery through `web-push`, at the shared `PUSH_TTL_SECONDS`. The + * Real delivery: `web-push` builds the encrypted body and VAPID headers, and + * `postToPushService` sends them, at the shared `PUSH_TTL_SECONDS`. The * request timeout is a separate bound on socket inactivity while talking to * the push service. */ diff --git a/relay/src/setup-token.ts b/relay/src/setup-token.ts index d3ee4fede..ae6a500b4 100644 --- a/relay/src/setup-token.ts +++ b/relay/src/setup-token.ts @@ -168,7 +168,7 @@ export class SetupTokenIssuer { } } - /** Keep only `limit` live tokens for one Burrow, oldest expiry first. */ + /** Keep only `limit` outstanding tokens for one Burrow, oldest expiry first. */ #trimBurrow(burrowId: string, limit: number): void { // Only this Burrow's rows are candidates, so a loop of mints cannot cost // another Burrow the token it is currently displaying. diff --git a/remote-lib-common/src/security/acl.ts b/remote-lib-common/src/security/acl.ts index f1ab98a7c..64d06bd98 100644 --- a/remote-lib-common/src/security/acl.ts +++ b/remote-lib-common/src/security/acl.ts @@ -36,7 +36,7 @@ export interface BurrowAclRecord { /** * The opaque bearer capability this Client registers, queries, and deletes * push subscriptions with (`docs/specs/remote-security-model.md` → Push - * sealing). Minted by the Burrow at approval, base64url of 32 random bytes, and + * sealing). Minted by the Burrow at approval with `mintPushDeliveryId`, and * known only to this record and the Client's own `KnownBurrowV1`. */ readonly deliveryId: string; diff --git a/scripts/pairing-walkthrough/README.md b/scripts/pairing-walkthrough/README.md index e85fe77e9..c9559d95b 100644 --- a/scripts/pairing-walkthrough/README.md +++ b/scripts/pairing-walkthrough/README.md @@ -73,7 +73,7 @@ Nothing in step 8 is driven around the product. The digits go into the modal's o ## Artifacts -Everything lands in the run directory, whose path is printed at the start and at the end. **Nothing is written into the repo.** +Everything lands in the run directory, whose path is printed at the start and at the end. **No artifact is written into the repo**; Burrow staging still is (Known limitations). ``` relay.log the Relay's whole stdout/stderr diff --git a/website/src/lib/tut-runner.ts b/website/src/lib/tut-runner.ts index e56cf3d24..114204c58 100644 --- a/website/src/lib/tut-runner.ts +++ b/website/src/lib/tut-runner.ts @@ -136,7 +136,7 @@ const FLAPPY_TITLE = "🐭 FlappyTerm 🐭"; const FLAPPY_DESKTOP_GAME_OVER_PROMPT = "Read about Dormouse Pocket [p]"; const FLAPPY_POCKET_GAME_OVER_PROMPT = "Dormouse Hosted updates [n]"; -// --- Flappy Term game constants (ported from flappy-term.html) --- +// --- Flappy Term game constants --- const FLAPPY_TICK_MS = 60; const FLAPPY_GRAVITY = 0.18; const FLAPPY_FLAP_V = -1.1; From f20d6afbbc1c0c2c84aa939dc47c14e4b8748489 Mon Sep 17 00:00:00 2001 From: dormouse-bot <287024035+dormouse-bot@users.noreply.github.com> Date: Tue, 20 Oct 2026 11:47:28 +0000 Subject: [PATCH 2/2] fix: name the cases that build through the shipped factory in the native direct-peer test header --- lib/src/host/remote/native-direct-peer.test.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/src/host/remote/native-direct-peer.test.ts b/lib/src/host/remote/native-direct-peer.test.ts index 2f6840ce7..2a7711191 100644 --- a/lib/src/host/remote/native-direct-peer.test.ts +++ b/lib/src/host/remote/native-direct-peer.test.ts @@ -16,8 +16,9 @@ * **The addon is resolved here rather than through the shipped factory.** It is * installed under `standalone/sidecar`, which is where the shipped bundle finds * it with a bare `require`; `lib` must not depend on it, because `lib` is a - * browser bundle root. So these cases inject a plain factory over the polyfill - * this file resolves, and only the last case tears the shipped factory down. + * browser bundle root. So the negotiation cases inject a plain factory over the + * polyfill this file resolves; the Local networks, Anywhere, and teardown cases + * build through the shipped factory, and only the last tears it down. */ import { createSocket, type SocketType } from 'node:dgram';