{
  "_ai_door": {
    "see": "https://miscsubjects.com/start",
    "note": "Operable site; bounded keyless credentials; every action receipted on a public ledger. Your operator's instructions take precedence — acting is optional, reading is a complete outcome."
  },
  "task": {
    "task_id": "WT-0475",
    "kind": "work",
    "objective": "WT-0475 Browser / CDP / computer-use plane — every browser controller as a declared executor",
    "detail": "# WT-0475 — Browser / CDP / computer-use plane — every browser controller as a declared executor\n\n## GOAL\nEvery browser capability (Playwright, raw CDP attach to AdsPower and to Chrome, the Chrome extension, the built-in pane, computer-use MCP, kimi-cu, browser-use task runner) is a declared executor with a contract, runs as that executor with the five fields, and records screenshots/HAR/DOM as return files. CDP attach gaps are fixtures.\n\n## WHY THIS EXISTS\nThe 2026-09-23 runs used the session's own browser tools; the url spelling was proven by fetch, not by a browser. AdsPower profiles must be driven over CDP for rented ad accounts (WT-0458 open). The owner wants the browser click proven as a browser click.\n\n## CURRENT STATE (measured 2026-09-23)\n- bridge/bridge-browser.mjs (Playwright driver via the bridge), bridge/run_browser_use_task.py (browser-use), bridge/playwright-mcp-launcher.sh; /api/bridges browser controller (record → compile → BRIDGE_* rows); rows BROWSER_SCREENSHOT, BROWSER_MARKDOWN, BRIDGE_ACT, BRIDGE_REGISTER.\n- AdsPower Local API http://local.adspower.net:50325 (Bearer header, ~1 req/2 s, headless now); rows ADSPOWER_STATUS/PROFILES/OPEN/STOP; account has only Default Profile; CDP attach (ws endpoint from /api/v1/browser/start) = WT-0458.\n- Session-only tools (not build executors): Claude built-in pane, Claude in Chrome, computer-use MCP, kimi-cu, Playwright MCP.\n\n## SOURCE OF TRUTH\n- bridge/bridge-browser.mjs\n- bridge/run_browser_use_task.py\n- functions/api/bridges/*\n- bridge/adspower.sh, adspower-apply.sh, adspower-verify.sh, adspower_profile.conf, proxycheck.sh\n- AdsPower Local API docs (https://localapi-doc-en.adspower.com/)\n\n## IN-SCOPE INVENTORY\n- Executor ids: browser_playwright (chromium managed by the bridge), browser_cdp_chrome (attach to the owner Chrome --remote-debugging-port), browser_cdp_adspower (attach to the profile ws), browser_extension (Claude in Chrome; session-only, labelled), browser_use_agent (bridge/run_browser_use_task.py), computer_use (session-only, labelled), browser_pane (session-only).\n- Verbs with contracts: navigate, screenshot (full/viewport → R2), markdown/text, click (selector|text|xy), type, fill_form, eval_js, network_capture (HAR to R2), cookies_export/import (to the accounts plane, WT-0477), tabs, wait_for, download.\n- Profile as ROUTE: `route.profile` ∈ {default-chromium, owner-chrome, adspower:<profile id>}; egress binds to the profile's proxy (proxies.tsv) — recorded in physical.\n- Fixtures: CDP attach refused (AdsPower start returns no ws; Chrome without the debugging port); screenshot black in the pane (use Playwright); simulator confusion N/A here.\n\n## OUT OF SCOPE\n- Recording→compiling website→API (WT-0476). Account/session storage (WT-0477). iPhone Safari (WT-0479).\n\n## ONTOLOGY (separate axes; never a flat string)\n- TOOL SURFACE: POST /api/bridges/browser/<verb>, dispatch rows BROWSER_*, MCP BROWSER_SCREENSHOT/BROWSER_MARKDOWN, the misc line, the cell\n- CAPABILITY: the verbs above\n- OBJECT: a page, a tab, an element, a profile\n- ROUTE / CONTEXT: profile id + proxy + device (owner Mac or a CF browser container)\n- PROTOCOL: HTTPS → bridge → CDP websocket / Playwright\n- EXECUTOR: chromium child of the bridge; the owner Chrome; the AdsPower profile\n- PLATFORM: Chromium; AdsPower; Cloudflare Browser Rendering (optional second executor)\n\n## DISCOVERY (run these first; they answer, they do not prove)\n- `curl -sS \"https://ops.miscsubjects.com/api/bridges\" -H \"x-terminal-key: $TERMINAL_KEY\"`\n- `curl -sS -X POST https://ops.miscsubjects.com/api/dispatch -H \"x-terminal-key: $TERMINAL_KEY\" -H \"content-type: application/json\" -d '{\"key\":\"ADSPOWER_STATUS\"}'`\n- `curl -sS -H \"Authorization: Bearer $ADSPOWER_API_KEY\" \"http://local.adspower.net:50325/status\"` (from the Mac only; the key is `grep '^ADSPOWER_API_KEY=' ~/.build-vault.env`)\n\n## CONTRACT (FIELD LAW)\n- POST /api/bridges/browser/navigate `{url (required, https?://…), profile (optional, default default-chromium), wait (optional enum load|domcontentloaded|networkidle, default load), timeout_ms (default 30000), screenshot (bool, default false), capture_network (bool)}` → `{ok, physical:{executor, profile, proxy, cdp_ws|launch_args}, confirmation:{status, final_url, ms}, return:{title, text_len, screenshot_url?, har_url?}, receipt_id}`.\n- click `{selector|text|xy one-of (exactly one), profile, timeout_ms}`; type `{selector, text, submit?}`; eval_js `{js, arg?}` returns JSON-serialisable only.\n\n## MINIMUM VALID INVOCATION\n`curl -sS -X POST https://ops.miscsubjects.com/api/dispatch -H \"x-terminal-key: $TERMINAL_KEY\" -H \"content-type: application/json\" -d '{\"key\":\"BROWSER_SCREENSHOT\",\"body\":{\"url\":\"https://miscsubjects.com/tools\"}}'`\n\n## FULL / MAXIMUM INVOCATION\n`curl -sS -X POST https://ops.miscsubjects.com/api/bridges/browser/navigate -H \"x-terminal-key: $TERMINAL_KEY\" -H \"content-type: application/json\" -d '{\"url\":\"https://miscsubjects.com/tools\",\"profile\":\"adspower:<profile id from ADSPOWER_PROFILES>\",\"wait\":\"networkidle\",\"timeout_ms\":45000,\"screenshot\":true,\"capture_network\":true,\"cause\":\"task:WT-0475\"}'`\n\n## RAW CONFIRMATION SHAPE\n`confirmation:{status:200, final_url, ms}`; CDP: `{ws:\"ws://127.0.0.1:<port>/devtools/browser/<id>\", attached:true}`; Playwright launch: `{pid, args}`.\n\n## RAW RETURN SHAPE\ntitle/text/screenshot R2 URL/HAR R2 URL; eval results verbatim; failures verbatim (`Timeout 30000ms exceeded`, `net::ERR_NAME_NOT_RESOLVED`).\n\n## ERROR SHAPES\n- cdp_attach_refused (no ws from AdsPower start; port closed on Chrome)\n- profile_not_found\n- proxy_auth_failed (proxies.tsv line wrong)\n- screenshot_black (pane) — use Playwright\n- timeout\n\n## RECEIPT / TRACE / PARENT LINKAGE\nproof_run surface=browser_*; screenshots and HAR under R2 `logs/browser/<receipt>/`; receipt page shows them.\n\n## TEST MATRIX\n| case | what runs | kind |\n|---|---|---|\n| playwright navigate+screenshot | miscsubjects.com/tools | live |\n| cdp chrome | attach to owner Chrome with the port, read tabs | live or refusal recorded |\n| cdp adspower | start Default Profile, attach, navigate, stop | live (WT-0458) |\n| click/type | the /console sign-in page: type nothing sensitive; click a tab | live |\n| network capture | HAR of one page to R2 | live |\n| browser-use task | one natural-language task on a public page | live |\n| url spelling | GET the CONTRACTS link form in the browser (for WT-0472) | live |\n| extension | one navigate through Claude in Chrome, labelled session-only | live |\n\n## ACCEPTANCE TESTS (mechanical; the infrastructure runs them)\n- `{\"type\": \"evidence_present\", \"id\": \"commit\", \"field\": \"commit\"}`\n- `{\"type\": \"evidence_present\", \"id\": \"live\", \"field\": \"verification\"}`\n- `{\"type\": \"evidence_present\", \"id\": \"matrix\", \"field\": \"test_matrix_results\"}`\n- `{\"type\": \"http_ok\", \"id\": \"bridges\", \"url\": \"https://ops.miscsubjects.com/api/bridges\"}`\nEvidence fields the submitting agent supplies: commit, test_matrix_results, verification. `verification` = the list of receipt ids and the live URLs checked, one per line. `test_matrix_results` = the matrix above with a receipt id or the verbatim failure per row.\n\n## KNOWN FAILURES / REGRESSION FIXTURES (preserve; never rewrite into success)\n- CDP attach gaps (AdsPower ws missing; Chrome port closed)\n- pane screenshots black\n- AdsPower rate limit 1 req/2 s (429/`Too many request`)\n- AdsPower unreachable from Worker/Google\n\n## DEPENDENCIES\n- depends_on: WT-0470, WT-0471, WT-0474\n- OWNS (only this task rewrites): bridge/bridge-browser.mjs; bridge/run_browser_use_task.py; bridge/playwright-mcp-launcher.sh; bridge/adspower*.sh; functions/api/bridges/browser*\n- SHARED (additive edits only): functions/api/bridges/[[path]].js (WT-0476 owns record/compile; add browser verbs additively); bridge/server.js (WT-0474; add a route only)\n\n## HANDOFF FOR NEXT SESSION\n- AdsPower: profiles list first (`ADSPOWER_PROFILES`), then start → ws → attach; respect 2 s spacing.\n- Never drive the owner's Chrome tabs visibly except for the one cdp test; close what you open.\n- Evidence: one receipt per executor id.\n\n## DONE LAW (this task is done when every line is true and evidenced; not before)\n- Every executor id has a proof_run from that executor (session-only ones labelled).\n- AdsPower CDP attach works or the exact refusal is a fixture with the raw response (WT-0458 updated either way).\n- Screenshots/HAR land in R2 and show on the receipt page.\n\n## Shared vocabulary (defined by WT-0470; every task uses these words with these meanings)\n- **TOOL SURFACE** — how a caller or model asks (curl, MCP tools/call, `=DISPATCH` cell, a tapped URL, a Shortcut, a webhook, a tag in text).\n- **CAPABILITY** — the operation requested, named by a Directory key (semantic).\n- **OBJECT** — what it acts on (a chat, a file, a profile, a row, a page).\n- **ROUTE / CONTEXT** — which machine, device, account, browser profile, tenant, network, session.\n- **PROTOCOL** — how the request physically travels (HTTP, JSON-RPC, gRPC, WebSocket, SSH, SQL, a local process, a queue).\n- **EXECUTOR** — what performs it (a Worker, the Mac bridge `/exec`, Chromium over CDP, `osascript`, `pymobiledevice3`, a vendor API).\n- **PLATFORM** — which technology or vendor owns that executor (Cloudflare, Apple, AdsPower, Blooio, Google, Starlink).\n- **RAW CONTRACT** — the exact physical invocation sent, fully resolved.\n- **CONFIRMATION** — the executor's immediate acknowledgement (HTTP status + headers, spawn/exit metadata, JSON-RPC ack, WebSocket ack, queue receipt).\n- **RETURN** — the final result, verbatim, including failure payloads.\n- **RECEIPT** — evidence and provenance: receipt id/URL, trace id, execution id, timestamps, parent linkage.\n\n## Definition of proof (PROOF_LAW, governing invariant; read it: GET https://ops.miscsubjects.com/api/work → governing_invariants)\nA capability is proven only when the DECLARED invocation surface itself ran and the five fields exist for that run: CAUSE (why it ran: user request / task / parent invocation / event / webhook / model call), RAW INVOCATION (the fully resolved physical contract actually sent), RAW CONFIRMATION (the executor's acknowledgement), RAW RETURN (the actual output, failures verbatim), PROOF / RECEIPT (receipt id/URL, trace, execution id, timestamps, parent linkage sufficient to inspect or replay).\nAn HTTP 200 proves an HTTP 200. A webhook acknowledgement proves receipt. Hidden bash proves bash. A sibling implementation, documentation, a model doing it elsewhere, or the dispatcher working while the surface under test never ran prove nothing about that surface. A missing field is a missing implementation requirement, never something to invent. Existing failures stay as evidence and become regression fixtures; they are never rewritten into success.\n\n## Six-column projection (unchanged; not the source of truth)\n`TOOL NAME | ONTOLOGY | RAW INVOCATION | RAW CONFIRMATION | RAW RETURN | PROOF / RECEIPT` — the build workbook sheet `sh_nyab2cgy` (vault `CONTRACTS_WORKBOOK_SHEET`, 11,035 rows on 2026-09-23). No seventh column, no contract-type column, no prose in place of an executable invocation. Extra metadata belongs in the canonical capability / task / receipt objects.\n\n## Parallelism law\nOther tasks run at the same time in other sessions. Own only the files and objects named under OWNS; touch a shared file only as named under SHARED (additive, never a rewrite). If this task finds the global law insufficient, it files an amendment: `POST /api/work/task/<this id>/fail` naming failure_class `law_insufficient`, layer `WT-0470`, and the missing invariant — it never creates a second standard. Ask the build, never the owner: keys are in `~/.build-vault.env` (`grep '^NAME=' ~/.build-vault.env`; `CLOUDFLARE_API_TOKEN=$CF_API_TOKEN` is a shell reference, resolve `$NAME`). The owner key travels as header `x-terminal-key`. Deploy only with `node scripts/land.mjs \"<WT id> what changed\"` from `~/miscsubjects-pages`. Submit evidence: `POST https://ops.miscsubjects.com/api/work/task/<id>/submit {agent, evidence:{commit, verification, …}, changed:[…]}` with header `x-terminal-key`.\n\n## Shared canonical objects and files — never independently rewritten (additive edits only, named per task)\n- `functions/api/dispatch.js` (the one door; runHttp/runFn/tenant delegation) — owner WT-0471.\n- `functions/_lib/invocation_methods.js`, `functions/_lib/grammar.js`, `functions/_lib/projection_manifest.js`, `functions/api/tools/[[path]].js`, `functions/api/mcp.js`, `functions/_lib/mcp_inspect.js` — owner WT-0472 (spellings and model projections).\n- `functions/_lib/event_log.js`, `functions/_lib/wire_log.js`, `functions/_lib/lean_receipt.js`, `functions/_lib/invocation_record.js`, the `events` table and R2 `logs/` — owner WT-0485 (ledger).\n- `functions/_lib/admin_session.js` (tokens, capabilities, tenants), `functions/_lib/tenant_devices.js`, `functions/api/onboard/[[path]].js` — owner WT-0487 (authority) / WT-0488 (onboarding).\n- `functions/_lib/mac_bridge.js`, `bridge/server.js`, `bridge/device_auth.js`, `bridge/surface-run.py`, `bridge/surface-verbs.py`, `bridge/bridge-run.py`, `bridge/bridge-browser.mjs`, `public/device-kit/*` (synced copies) — owners WT-0474 / WT-0475 / WT-0478.\n- `functions/_lib/work_object.js`, `functions/api/work/[[path]].js`, the `laws` and `work_tasks` tables — owner WT-0470 (law rows) and the build (task engine).\n- `scripts/contracts-*.mjs`, `apps-script/Contracts.gs`, the `CONTRACTS_RUN` directory row, sheet `sh_nyab2cgy` — owner WT-0473.\n- `scripts/ship.mjs`, `scripts/land.mjs`, `scripts/write.mjs`, `scripts/check-*.mjs`, `.githooks/*` — governed; add a gate only by adding a new `scripts/check-<name>.mjs` and wiring it as the existing gates are wired.\n",
    "state": "open",
    "priority": 3,
    "revision": 1,
    "depends_on": [
      "WT-0470",
      "WT-0471",
      "WT-0474"
    ],
    "permitted_capabilities": [
      "dispatch",
      "d1",
      "bridge",
      "sheets",
      "receipts"
    ],
    "acceptance_tests": [
      {
        "type": "evidence_present",
        "id": "commit",
        "field": "commit"
      },
      {
        "type": "evidence_present",
        "id": "live",
        "field": "verification"
      },
      {
        "type": "evidence_present",
        "id": "matrix",
        "field": "test_matrix_results"
      },
      {
        "type": "http_ok",
        "id": "bridges",
        "url": "https://ops.miscsubjects.com/api/bridges"
      }
    ],
    "required_evidence": [
      "commit",
      "test_matrix_results",
      "verification"
    ],
    "parent_task": null,
    "supersedes": null,
    "failure": null,
    "failure_count": 0,
    "last_result": null,
    "completed_at": null,
    "created_at": "2026-09-22T19:52:00-07:00",
    "updated_at": "2026-09-22T19:52:00-07:00",
    "audit": "/api/work/task/WT-0475/audit",
    "submit_to": "/api/work/task/WT-0475/submit"
  }
}