{"_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."},"slug":"web-models-as-first-class-capabilities","title":"Web Models as First-Class Capabilities","body":"A logged-in ChatGPT, Claude, Grok or Gemini web session is now an ordinary capability in this build. A caller names a provider and a prompt, and gets back the exact answer the web model gave, with a receipt. The same web model can call the rest of the build by writing a tag. Both directions run through the same Directory, the same flows, the same scheduler and the same ledger as every HTTP tool and API agent here. What existed before, what the reference projects already prove, what was added, exactly how it was tested, what broke, and what is still missing follow in that order.\n\n## Copying between tabs was the bottleneck, and it left no record\n\nFive chat products carry paid subscriptions whose web sessions have no programmatic door. The same vendors sell APIs, metered separately, with connectors that differ from one another, and a model in one tab cannot see what a model in another tab did. Getting ChatGPT Web to draft, Claude Web to audit and Grok Web to adjudicate meant copying text between tabs by hand. The copy was the bottleneck, and it left no record.\n\nThe build already had the other half: a Directory of about a thousand capabilities behind one dispatch call, a flow grammar that composes them, a scheduler, an event bridge, scoped bearer tokens, and a ledger that records every invocation. Web models were the one class of worker that could not be a row in it.\n\n## What existing projects already solve\n\nSource was read, not marketing. Three mechanically different things get called \"web model MCP\" and they should not be conflated.\n\n**Drive the chat page and scrape the answer.** [mcp-web-llm](/a/mcp-web-llm) attaches Playwright to a logged-in Chrome over CDP, types into ChatGPT, Claude, Gemini, DeepSeek, Grok and Qwen, and polls the DOM until the last assistant node stops changing. Its README says \"No API keys or tokens required.\" It has no session object, no conversation identity, no tool loop, and it states that its memory feature \"has been rolled back.\" [agentify desktop](/a/agentify-desktop) is the same idea built as a desktop app with a selector file and a rate governor. [ChatGPT-Web2API](/a/chatgpt-web2api) is the one project that reads the provider's own backend from inside the page and uses its end-of-turn flag as the completion signal, addressing real conversation ids.\n\n**Give the web model tools.** [MCP-WebLLM](/a/mcp-webllm) is a Chrome extension that pastes a tool manifest into the chat, watches the DOM for a fenced block, executes the call against a local MCP server and pastes the result back. It is the inverse direction of mcp-web-llm and the closest precedent for the relay lane below. [codex-chatgpt-web](/a/codex-chatgpt-web) does the same job the robust way for one host and one provider, by registering a real MCP server as a ChatGPT connector so ChatGPT's own agent loop calls the tools.\n\n**Generic browser control.** [BrowserMCP](/a/browsermcp) and [graph-memory browser-mcp](/a/graph-memory-browser-mcp) drive a browser through MCP without knowing any model. The second has the best session model in the set, named on-disk profiles, network body capture, and one tool registry projected into both MCP and REST. [browser-use](/a/browser-use) is the large browser-agent ecosystem; its profile vocabulary and its thirteen watchdogs are the honest catalogue of what goes wrong in long-running browser workers.\n\nNone of the eight projects has workflow participation, cron, event triggers, a shared state object across vendors, an artifact promoted from a trace, or a provenance plane. Not one. That is not a criticism. They are relays. This build already had those other parts and needed the relay.\n\n## What OpenClaw does with skills and self-learning\n\n[OpenClaw](/a/openclaw-self-learning) is the important comparison, because it is the popular personal-agent gateway and because its self-learning system really does turn work into durable artifacts. Read at source: what is learned is a `SKILL.md`, natural-language instructions with YAML frontmatter, produced by a reviewer prompt that asks for \"a stable procedure that saves at least two future model round trips.\" The default mode applies scanner-approved proposals automatically. When a learned skill is used later, the agent reads the file back into context and reasons through it again. There is no replay, no compiled workflow and no generated code path. OpenClaw has a real trajectory recorder, but no learning path consumes it. Its cron and automations run messages, commands and scripts on schedules, separately from skills.\n\nThe distinction underneath everything that follows: a skill tells a future model how to do the procedure. A flow is the procedure, or more exactly its order and its data flow. A compiled flow does not learn preconditions, branches or recovery; it learns which capabilities ran, in what order, and which outputs fed which inputs.\n\n## What was added\n\nFourteen Directory rows for the browser plane: `CHATGPT_WEB`, `CLAUDE_WEB`, `GROK_WEB`, `GEMINI_WEB`, `KIMI_WEB`, plus the verbs `WEBMODEL_SESSION_NEW`, `WEBMODEL_SEND`, `WEBMODEL_READ`, `WEBMODEL_STATUS`, `WEBMODEL_CLOSE`, the relay `WEBMODEL_AGENT`, and the handoff primitive `STATE_NEW`, `STATE_APPEND`, `STATE_RESOLVE`. Three learned-flow rows: `FLOW_LEARN`, `FLOW_PROMOTE`, `FLOW_CANDIDATES`. Twelve identity rows: `PROFILE_NEW`, `PROFILE_360`, `PROFILE_IDENTIFY`, `PROFILE_EVENT`, `DEVICE_REGISTER`, `DEVICE_TRUST`, `DEVICE_REVOKE`, `DEVICE_VERIFY_RECORD`, `CAP_CONTEXT_BIND`, `CAP_CONTEXT_GET`, `CAP_CONTEXT_UNBIND`, `ACCESS_DECISIONS`. Every one is readable at `/api/dispatch?key=<KEY>` and appears in the same registry as everything else.\n\nTwo flow rows built from those: `WEB_COUNCIL` fans one question out to ChatGPT Web, Claude Web and Grok Web and has Gemini Web reconcile them. `WEB_DATA_REVIEW` fans out two live data capabilities and has Claude Web review the joined output. Neither contains any browser-specific machinery. They are the existing flow grammar with new member names.\n\n## The architecture in fifteen lines\n\n1. One persistent Mac worker owns one dedicated Chrome profile, seeded once from the owner's real profile, connected over CDP through Playwright.\n2. Five adapter files hold every selector, stream pattern and limit phrase. Nothing outside them knows a vendor's UI.\n3. Completion is detected from the provider's own streaming response finishing, then the stop indicator clearing, then DOM stabilisation over a real window, then the accessibility live region. Fixed sleeps are never the mechanism. The method that fired is recorded on every turn.\n4. The answer to a prompt is the last assistant node past the count that existed when the prompt was submitted. Older text is never returned as the reply.\n5. The worker exposes six narrow verbs behind the existing bridge and tunnel. No CDP, no arbitrary JavaScript and no arbitrary navigation cross the wire.\n6. A send is accepted and returns a running turn id. The edge polls the turn against a bounded budget. The Cloudflare edge in front of the tunnel cuts any origin response held past 100 seconds, and Claude Web can take longer than that.\n7. Sessions and turns are rows in the content database. The worker keeps a local mirror so a restarted process can re-open the conversation URL.\n8. Nothing reports success unless the prompt was submitted, the response captured, the row written and the ledger receipt taken. Each failure is a named code.\n9. A state handle is a small durable object: objective, open work, and a bounded window of entries. A model is handed the handle, never a transcript.\n10. The relay hands a web model the Directory as text. The model writes `[KEY]args[/KEY]`. The relay parses it with the router's own reader, runs it under the caller's authority, pastes the result back, and lets the model continue, bounded at eight iterations.\n11. Every browser-model session is a persistent model actor in the same profile table as human customers, so a turn is attributed to \"ChatGPT on the browser profile\", never to a person.\n12. A capability's authority stays in the signed token. Its context, meaning which profile, devices, sessions, state handles and origins may present it, how recent a human verification it needs, and whether a device signature is required, lives in a mutable server-side record evaluated on every use.\n13. Turn completion writes one ledger row with the event name `browser_model.turn.completed`. The event bridge fires any automation registered on it.\n14. `FLOW_LEARN` compiles a successful trace into a flow row: the run's input becomes `$1`, a step that consumed the previous output becomes `$PREV`, everything else stays a constant. Anything sensitive or side-effecting is created disabled.\n15. Worker health, sessions, turns, profiles, devices, contexts and access decisions are sheets over the tables they live in, not a dashboard.\n\n## The live tests, with receipts\n\nEvery receipt below is a public page. It proves the invocation happened, names the capability and the actor, and carries the hashes of the request and the response; the payload bytes themselves are readable with a credential, and the captured answers quoted here are in the durable turn rows, which are owner-readable. A reader without a credential can verify that each call happened and when, not the quoted text.\n\n**Acceptance.** One Directory capability, ChatGPT Web, prompt `Reply with exactly GATEWAY_LIVE_OK`. The exact answer came back with substrate `browser_web` and capture method `network_stream_end+dom`: [inv_9x5nyxhsbu](https://miscsubjects.com/receipt/inv_9x5nyxhsbu), durable turn `wmt_62761d775bdf8d3a46`, conversation `https://chatgpt.com/c/6a9db53c-2148-83e8-b5c7-799f1b74b45e`. The `CHATGPT_WEB` row itself returned the same token: [inv_rclmphgk7z](https://miscsubjects.com/receipt/inv_rclmphgk7z). Then Claude Web was handed the state handle `state://lyb1m6yseu` and the prompt `State exactly what the previous model replied.` It answered `GATEWAY_LIVE_OK`: [inv_mo6pgi8yxm](https://miscsubjects.com/receipt/inv_mo6pgi8yxm), conversation `https://claude.ai/chat/0b750b00-bbea-420f-9a69-84ca65942cef`. No human copied anything between tabs. The briefing Claude saw is what `STATE_RESOLVE` returns for that handle.\n\n**A web model calls a Directory capability.** `WEBMODEL_AGENT` gave ChatGPT Web one tool, `QUAKE_FEED`, and a task that required it. The model wrote `[QUAKE_FEED]1|4[/QUAKE_FEED]`, the relay ran it under the caller's authority, pasted the feed back, and the model answered with the place and magnitude: [inv_uwnn7ketmh](https://miscsubjects.com/receipt/inv_uwnn7ketmh). No MCP registration, connector, function calling or credential reached the vendor.\n\n**A web model operates the build through its own browser.** The second lane. A token scoped to one row was minted and its invoke address handed to ChatGPT Web and to Grok Web inside the prompt. Each opened the address with its own browsing tool, invoked the capability, and read back the invocation id the build had just issued: ChatGPT Web returned [inv_03d3lm47u7](https://miscsubjects.com/receipt/inv_03d3lm47u7) and Grok Web returned [inv_ndnaqm7jbo](https://miscsubjects.com/receipt/inv_ndnaqm7jbo). No relay parsed anything; the model exercised the bounded authority directly, and the ledger records the same actor either way.\n\n**Profile-state policy.** One token, two bindings. Bound to a profile whose attributes carry an active subscription and the segment vip, with rules requiring both, it ran ([inv_amw5xhoauv](https://miscsubjects.com/receipt/inv_amw5xhoauv)). Rebound to a profile without the subscription, the same token was refused `POLICY_DENIED` naming the failed rule, and the explain surface reports the effective decision for any presenter without executing.\n\n**Parallel council.** `WEB_COUNCIL` sent one question to ChatGPT Web, Claude Web and Grok Web concurrently and Gemini Web reconciled the three: [inv_txud6ki0qw](https://miscsubjects.com/receipt/inv_txud6ki0qw). Four browser turns landed under one trace. All four models said a logged-in browser session is not a reasonable production dependency, which is a fair reading of the limitations section below.\n\n**Business capabilities feed a web model.** `WEB_DATA_REVIEW` fanned out the most recent significant earthquake and the gold spot price, joined them, and Claude Web reviewed the joined block through `$PREV`: [inv_ahf7onnesd](https://miscsubjects.com/receipt/inv_ahf7onnesd).\n\n**A browser turn fires an automation.** Automation 34 is registered on `event:on source=webmodel action=turn_completed`. A browser turn completed ([inv_9j5ksu87xy](https://miscsubjects.com/receipt/inv_9j5ksu87xy)) and the ledger row fired it: run count rose, receipt [inv_guxgetocb7](https://miscsubjects.com/receipt/inv_guxgetocb7). This closed work task WT-0123, whose defect was that the fire was handed to a `waitUntil` that did not exist.\n\n**A scheduled automation runs a browser-model flow.** Automation 35 schedules `WEB_DATA_REVIEW` daily. The ordinary scheduler ran it with no browser-specific code: [inv_n441rf3pjp](https://miscsubjects.com/receipt/inv_n441rf3pjp).\n\n**Cold restart.** A session stored a codeword ([inv_vys3gwu9to](https://miscsubjects.com/receipt/inv_vys3gwu9to)). The worker was killed and relaunched by launchd with a new process id. The same session id resumed the same conversation and returned the codeword: [inv_v52c7v6hw3](https://miscsubjects.com/receipt/inv_v52c7v6hw3).\n\n**Signed-out provider.** Kimi Web is not signed in on the gateway profile. `KIMI_WEB` returned `ERR:AUTH_REQUIRED`, not an answer from an API model.\n\n**Learned flow.** Two steps were done by hand and receipted: `QUAKE_PLACE` ([inv_cou892hbho](https://miscsubjects.com/receipt/inv_cou892hbho)) and `CLAUDE_WEB` asked which region that place is in ([inv_xw66nxifeu](https://miscsubjects.com/receipt/inv_xw66nxifeu)). `FLOW_LEARN` compiled them into the row `QUAKE_REGION_1BV5` with the body `QUAKE_PLACE: $1+ > CLAUDE_WEB: In one sentence, say which country or region this place is in: $PREV`, bindings recorded as `argument` and `previous_output_embedded`. The new key then ran as an ordinary capability with a different argument: [inv_0benrtdq4x](https://miscsubjects.com/receipt/inv_0benrtdq4x). `FLOW_CANDIDATES`, in its first version, scanned 20,000 top-level invocations and found no repeated multi-step trace. The corrected version and its result are in the limitations section below.\n\n**Authority with context.** A token scoped to one row was bound to one profile and one device. From that device it ran ([inv_jdql2odkcn](https://miscsubjects.com/receipt/inv_jdql2odkcn)); from a second device it was refused `DEVICE_NOT_APPROVED`; with no device it was refused the same way. The device was revoked ([inv_f8uy3uac3t](https://miscsubjects.com/receipt/inv_f8uy3uac3t)) and the same still-valid token was refused `DEVICE_REVOKED` at once, while `CAP_EXPLAIN` still reported the token itself live. A twenty-second verification window produced `TURNSTILE_REQUIRED`, then an allow after a recorded verification ([inv_jn9v2oxnn7](https://miscsubjects.com/receipt/inv_jn9v2oxnn7)), then `TURNSTILE_STALE` after the window passed. A child token narrowed to one of the parent's two devices ran from that device and was refused from the other, while the parent still ran from both. A device with a registered P-256 key signed a request and ran ([inv_66goomu12i](https://miscsubjects.com/receipt/inv_66goomu12i)); the same signature replayed was `POP_REPLAY`; a signature over a different nonce was `POP_INVALID`. A capability bound to one browser-model session ran from that session and was refused `SESSION_NOT_APPROVED` from another. In the cross-model handoff, ChatGPT Web worked on `state://jre4vo3oln` ([inv_t1xrj9w5y4](https://miscsubjects.com/receipt/inv_t1xrj9w5y4)), a separate capability `cap_9cc8c44c850f2710` bound to the Claude model actor and that handle resolved the shared state, was refused `STATE_HANDLE_MISMATCH` against another handle, and Claude Web recovered the token ([inv_77szzrn6sj](https://miscsubjects.com/receipt/inv_77szzrn6sj)). Both actors and both decisions are separate ledger rows.\n\n**Mechanical tests.** Fifty assertions run before deploy: the session state machine, response selection, stabilisation windows, redaction, failure naming, the pipe-safe body grammar, the forced durable-write and ledger-write failures that must never report success, the accept-and-poll path, the timeout by name, the context decision for every named code, narrowing, and a real ECDSA proof-of-possession round trip.\n\n## What is precedent and what is unusual\n\nDriving a logged-in web model from a browser and exposing it as a tool is precedent, in one shipping product with adoption and a dozen small repositories. Giving a web model tools by pasting a manifest and parsing its output is precedent, in one small repository. Persistent profiles, network capture and one registry projected into MCP and REST are precedent in generic browser servers. Compiling a successful run into executable code is precedent in Skyvern's code caching. Learning from work into durable text is precedent in OpenClaw, Claude Skills and Codex Record and Replay.\n\nThe combination in this build is not found in the eight repositories read or the commercial systems surveyed: a web model that is at once callable as a capability and able to call the rest of the capability graph, that participates in flows, schedules and event triggers as a plain member, whose work is bound to a durable state object another vendor's model can pick up, whose successful runs can be compiled into another callable capability, and whose every step lands in one ledger. Each piece has an owner somewhere. The join is the addition. Whether the join is valuable is a bet, not a claim; the tests above are what would falsify it.\n\n## What failed during implementation\n\nThe first live Claude handoff died with a Cloudflare 524. The tunnel hostname is proxied, and a response held open for the length of a Claude turn exceeded its 100-second origin timeout. The fix is the accept-and-poll path in line 6 above.\n\nThe second failure was worse and quieter. Multi-line prompts were typed with the Enter key for newlines, and every provider composer submits on Enter. Claude received a state briefing with no task and replied that it was still missing the task ([inv_0hrhns715v](https://miscsubjects.com/receipt/inv_0hrhns715v)); the relay's ChatGPT received one sentence of preamble and replied \"Understood.\" Textareas are now filled whole and contenteditable composers get Shift+Enter between lines.\n\nThird, sessions never closed their tabs. After forty-five open provider tabs a fresh claude.ai load timed out at sixty seconds ([inv_9nzo07xtfb](https://miscsubjects.com/receipt/inv_9nzo07xtfb)). The worker now keeps at most six live tabs and closes the least recently used idle one; the durable conversation URL makes a tab disposable.\n\nFourth, a freshly compiled learned flow was not dispatchable inside the same request that created it, because the directory snapshot propagates through eventually consistent storage. It ran on the next request. `FLOW_LEARN` reports the replay honestly rather than pretending.\n\nFifth, the ledger-event bridge had never fired in production because it handed its work to `env.waitUntil`, which does not exist on `env`. It is awaited now.\n\n## What three web models said about this page\n\nAfter publication, ChatGPT Web, Claude Web and Grok Web were each handed this page's address through the gateway and asked for the one claim they would challenge and one factual error. Their replies are appended to the page as signed contributions with receipts. Two errors they found are corrected above: the row count read nine where fourteen were listed, and the opening sentence implied the vendors sell no programmatic access at all. Claude Web's challenge, that a public receipt certifies the invocation and not the quoted answer, is also correct and the receipts paragraph now says so. Grok Web's challenge, that a consumer web session is a brittle relay rather than a durable worker, stands as the strongest objection and is not answered here.\n\n## What is missing\n\nKimi Web is not signed in on the gateway profile and was not exercised. Grok Web and Gemini Web each answered a standalone prompt with the exact token asked for: [inv_i03kg9rf4u](https://miscsubjects.com/receipt/inv_i03kg9rf4u) and [inv_bdkmm2pa5k](https://miscsubjects.com/receipt/inv_bdkmm2pa5k).\n\nThe Turnstile step-up page was refused inside the automated browser pane, which the widget is designed to do. Opened in a real Chrome window, the managed challenge passed, the server-side verify route accepted it, and the device row carries the verification instant with method `turnstile`. The step-up denials and allows above were exercised with owner-recorded verifications before that pass.\n\n`FLOW_CANDIDATES` first returned nothing, because it read the invocation table, which holds one row per top-level dispatch. It now reads the ledger's per-step rows under each trace, where flow members and agent tool calls actually live. Its second version found one procedure repeated three times over 11,237 traces ([inv_1yr8peu4s9](https://miscsubjects.com/receipt/inv_1yr8peu4s9)), and compiling it exposed the last defect: the procedure was `DELIVER_PENDING_ASSETS`, itself a flow row wrapping one trigger, whose ledger rows sit interleaved with its member. A trace that contains a flow row is now treated as a flow execution and excluded. With that correction the scan returns the honest result for this build in this week: 11,225 traces examined, 183 of them flow executions, and no uncompiled procedure that repeats ([inv_975yvs4x06](https://miscsubjects.com/receipt/inv_975yvs4x06)). The compiler is proven on the hand-done pair above; the scanner is proven to find repetition and to know a flow when it sees one.\n\nProvider terms of service are not analysed here. The council's own verdict, that a logged-in browser session is not a production dependency, stands as the strongest counter-argument to this design. Rate limits are surfaced verbatim when a provider shows them and were not hit during testing.\n\nMeasured across the turns recorded today: ChatGPT Web averaged 9 seconds a turn over 23 turns (3 to 26), Claude Web 12 seconds over 15 (6 to 36), Grok Web 17 seconds over 5 (4 to 37), Gemini Web 16 seconds over 2 (3 to 28). Prompts and answers averaged 300 to 600 characters, or roughly 100 to 200 tokens a turn; at current list prices for the frontier tiers the equivalent API spend for the whole day's testing is under one dollar. The browser turn is slower than an API call by an order of magnitude and costs nothing per token.\n\nCost is measured, not marketed. The turns here consumed no API tokens; they consumed the owner's existing subscriptions and a Mac that has to stay on. The equivalent API spend for the prompts above is a few cents. The machine and the profile maintenance are the real cost, and nothing here should be read as free inference.\n\nMulti-user distribution is not attempted. Every profile here is the owner's. A product would need one authenticated browser profile per user, and this build's tenant and context machinery is the place that isolation would attach.\n\n## How to reproduce\n\nRead the contract of one row: `GET https://miscsubjects.com/api/dispatch?key=CHATGPT_WEB`. Mint a scoped token for that row from the owner's key, then `GET /api/dispatch?invoke=CHATGPT_WEB&body=Reply%20with%20exactly%20GATEWAY_LIVE_OK&share=<token>`. The response names the substrate, the capture method, the turn id and the ledger event. Open the receipt link. Then `STATE_RESOLVE` the handle the response carries and hand it to `CLAUDE_WEB` with `with_state` set. Every receipt above was produced exactly that way and can be re-run with `POST /api/dispatch {\"replay\":\"<inv_id>\"}`.\n\n## Comparison\n\n| System | Callable web model | Persistent profile | MCP | REST | Web model calls arbitrary tools | Shared state across vendors | Workflow member | Workflow becomes a capability | Trace becomes executable | One evidence plane | Cron or events |\n|---|---|---|---|---|---|---|---|---|---|---|---|\n| mcp-web-llm | yes | yes, one shared Chrome | yes | no | no | no | no | no | no | no | no |\n| MCP-WebLLM | no, it is the client | browser's own | client only | no | yes | no | no | no | no | no | no |\n| BrowserMCP | no model | user's own | yes | no | n/a | no | no | no | no | no | no |\n| graph-memory browser-mcp | no model | yes, named | yes | yes | n/a | no | callable only | no | no | no | no |\n| browser-use | no, API models | yes | no | no | agent does | no | no | no | no | no | no |\n| codex-chatgpt-web | yes, ChatGPT only | yes | connector | Responses API | yes, native connector | single vendor | no | no | no | no | no |\n| agentify desktop | yes | yes, isolated | yes | yes | unknown | unknown | no | no | no | no | folder watch |\n| ChatGPT-Web2API | yes, ChatGPT only | yes | yes | yes | no | single vendor | no | no | no | no | no |\n| Skyvern | no | yes | unknown | yes | agent does | no | yes | yes | yes, code cache | run logs | cron |\n| OpenClaw | via browser tool | yes | yes | gateway | agent does | sessions | automations | no | text skill | session logs | cron |\n| this build | yes, five rows | yes, dedicated | yes | yes | yes, by tag | yes, state handle | yes | yes | yes, flow row | yes, ledger | both |\n\nCells marked yes carry a receipt above or a source file citation in the linked reference article. Unknown means unknown.\n","hero":"https://miscsubjects.com/img/gen/arcads-gpt-image-6dea60a1-9fe0-41f2-9864-877de2f09975.png","images":[],"style":{},"tags":["build","browser-models","oip","flows","identity","learned-flows"],"category":"build","model":"unattributed","ledger":{"href":"/api/articles/web-models-as-first-class-capabilities/ledger","live":true},"embeds":[],"widgets":[],"home":true,"claims":[{"id":"c1","text":"A logged-in ChatGPT Web session invoked as the Directory capability CHATGPT_WEB returned the exact text GATEWAY_LIVE_OK with substrate browser_web (receipt inv_rclmphgk7z).","section":"The live tests, with receipts","tier":"observational","source_ids":["s1"],"why_material":"the acceptance condition of the whole feature"},{"id":"c2","text":"Claude Web, handed only the state handle state://lyb1m6yseu, recovered GATEWAY_LIVE_OK without a human copying text between tabs (receipt inv_mo6pgi8yxm).","section":"The live tests, with receipts","tier":"observational","source_ids":["s2"],"why_material":"proves cross-vendor handoff through shared state"},{"id":"c3","text":"ChatGPT Web called the Directory capability QUAKE_FEED by emitting a text tag through WEBMODEL_AGENT, with no MCP, connector or credential given to the vendor (receipt inv_uwnn7ketmh).","section":"The live tests, with receipts","tier":"observational","source_ids":["s3"],"why_material":"proves the reverse direction: a web model operating the build"},{"id":"c4","text":"A ledger row for a completed browser turn fired an ordinary event automation (automation 34, receipt inv_guxgetocb7).","section":"The live tests, with receipts","tier":"observational","source_ids":["s4"],"why_material":"proves event participation without browser-specific code"},{"id":"c5","text":"FLOW_LEARN compiled two hand-done receipts into the executable flow row QUAKE_REGION_1BV5, which then ran as a capability (receipt inv_0benrtdq4x).","section":"The live tests, with receipts","tier":"observational","source_ids":["s5"],"why_material":"the learned-flow claim rests on it"},{"id":"c6","text":"A valid token bound to one device was refused DEVICE_NOT_APPROVED from a second device and DEVICE_REVOKED after revocation while CAP_EXPLAIN still reported the token live.","section":"The live tests, with receipts","tier":"observational","source_ids":["s6"],"why_material":"proves authority and context are evaluated separately"},{"id":"c7","text":"mcp-web-llm exposes six logged-in web models as MCP tools with DOM stability polling and no session object, and its README states its memory feature was rolled back.","section":"What existing projects already solve","tier":"definition","source_ids":["s7"],"why_material":"establishes the precedent being compared against"},{"id":"c8","text":"OpenClaw self-learning persists natural-language SKILL.md instructions that a later agent re-reads and reasons through; no learning path consumes its trajectory recorder.","section":"What OpenClaw does with skills and self-learning","tier":"definition","source_ids":["s8"],"why_material":"the skill-versus-flow distinction depends on it"},{"id":"c9","text":"Multi-line prompts typed with the Enter key were submitted after their first line by every provider composer, which is why the first Claude handoff received a briefing with no task.","section":"What failed during implementation","tier":"observational","source_ids":["s9"],"why_material":"the most consequential implementation failure, now fixed"}],"sources":[{"id":"s1","url":"https://miscsubjects.com/api/dispatch?confirm=inv_rclmphgk7z","title":"Receipt inv_rclmphgk7z, CHATGPT_WEB","quote":"CHATGPT_WEB produced material output at 2026-09-06T11:48:31-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"genesis","hash":"039e0ce812584c405d21037b2f77f0f944cb782f3558c3cf8b17ed6052575953"},{"id":"s2","url":"https://miscsubjects.com/api/dispatch?confirm=inv_mo6pgi8yxm","title":"Receipt inv_mo6pgi8yxm, Claude Web via shared state","quote":"WEBMODEL_SEND produced material output at 2026-09-06T11:48:02-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"039e0ce812584c405d21037b2f77f0f944cb782f3558c3cf8b17ed6052575953","hash":"b948166af10b049226362c5943afad1394bd5777483523f3cfc20583aaccb2f2"},{"id":"s3","url":"https://miscsubjects.com/receipt/inv_uwnn7ketmh","title":"Receipt inv_uwnn7ketmh, WEBMODEL_AGENT relay","quote":"The most recent magnitude-4-or-greater earthquake was 91 km south of Nikolski, Alaska, with a magnitude of 4.0.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"b948166af10b049226362c5943afad1394bd5777483523f3cfc20583aaccb2f2","hash":"85cf646433ed7317903e291a62cf13173eed943c31dcd96bb370b088e073b4f9"},{"id":"s4","url":"https://miscsubjects.com/api/dispatch?confirm=inv_guxgetocb7","title":"Receipt inv_guxgetocb7, automation fired by a browser turn","quote":"NOW produced material output at 2026-09-06T11:48:06-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"85cf646433ed7317903e291a62cf13173eed943c31dcd96bb370b088e073b4f9","hash":"16032b6ab5da4d2b70738b97cec895d4d0fb534b08775e56a7d977b53b9797d9"},{"id":"s5","url":"https://miscsubjects.com/receipt/inv_0benrtdq4x","title":"Receipt inv_0benrtdq4x, learned flow QUAKE_REGION_1BV5","quote":"The southern East Pacific Rise isn't in any country","accessed_at":"2026-09-06T21:29:12.423Z","prev":"16032b6ab5da4d2b70738b97cec895d4d0fb534b08775e56a7d977b53b9797d9","hash":"a44ff69d045e4dd95c2660f1467247e8a57c8a306e5985023fc7a644e5e022a7"},{"id":"s6","url":"https://miscsubjects.com/receipt/inv_f8uy3uac3t","title":"Receipt inv_f8uy3uac3t, DEVICE_REVOKE","quote":"every capability context bound to this device now denies with DEVICE_REVOKED","accessed_at":"2026-09-06T21:29:12.423Z","prev":"a44ff69d045e4dd95c2660f1467247e8a57c8a306e5985023fc7a644e5e022a7","hash":"89ceeefbe346c6aafec9b13b775b94950a65640bfeff1b9ceef99a1bb48d579b"},{"id":"s7","url":"https://github.com/HGD-coder/mcp-web-llm","title":"HGD-coder/mcp-web-llm README","quote":"No Long-Term Memory Feature: The previous experimental memory/session feature has been rolled back.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"89ceeefbe346c6aafec9b13b775b94950a65640bfeff1b9ceef99a1bb48d579b","hash":"5b2ca39dd7ea48a637ecca8e15c9536069a0120cc71826af66556d3d555759a8"},{"id":"s8","url":"https://github.com/openclaw/openclaw/blob/main/docs/tools/self-learning.md","title":"OpenClaw docs: self-learning","quote":"Capture a verified recovery, a standing user requirement for this class of task, or a stable procedure that saves at least two future model round trips.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"5b2ca39dd7ea48a637ecca8e15c9536069a0120cc71826af66556d3d555759a8","hash":"018469f380b1be8c13e3a4c5a384735e1c98fbc69f42d82922d66023069f9dfe"},{"id":"s9","url":"https://miscsubjects.com/api/dispatch?confirm=inv_0hrhns715v","title":"Receipt inv_0hrhns715v, Claude handoff before the typing fix","quote":"WEBMODEL_SEND produced material output at 2026-09-06T11:35:01-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"018469f380b1be8c13e3a4c5a384735e1c98fbc69f42d82922d66023069f9dfe","hash":"624ea2a41f3ccac57c5a77e1724d080219b7e7f3dd54da3eb948ae4bae937b5e"}],"reviews":[],"extra":{},"has_traversal":false,"register":"build","status":"published","revisions":4,"contributions":[{"model":"GPT-5.6 Sol (ChatGPT Web, browser session)","role":"review","action":"review","payload":{"text":"(1) I would challenge “a flow is the procedure.” FLOW_LEARN appears to generalize one successful trace mainly by substituting $1 and $PREV; that proves replayable parameterization, not that the learned flow captures the procedure’s valid preconditions, branches, failure recovery, or invariants. Misc Subjects+1\n(2) Factual error: the article says “Nine Directory rows for the browser plane,” but immediately enumerates 14: 5 provider rows + 5 session verbs + WEBMODEL_AGENT + 3 state primitives. Misc Subjects\n— GPT-5.6 Sol (browser session)","conversation_url":"https://chatgpt.com/c/6a9dcc89-feb0-83e8-8cfa-1d3d59940948","receipt":"https://miscsubjects.com/receipt/inv_apsrbj8tsd","turn_id":"wmt_0bf8ecb6dcc21e1549","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_ghusbbrw","_ts":"2026-09-06T20:31:13.811Z","seq":1,"ts":"2026-09-06T20:31:13.811Z","prev_hash":"genesis","hash":"1b866575a8dea24bf36478cab933092613dd033694e97d58955e14d7b98398cd"},{"model":"Claude Fable 5.1 (Claude Web, browser session)","role":"review","action":"review","payload":{"text":"(2) Factual error: \"Nine Directory rows for the browser plane,\" but the list that follows contains fourteen keys (five provider rows, five WEBMODEL verbs, WEBMODEL_AGENT, and three STATE rows).\nClaude Fable 5.1 (browser session)","conversation_url":"https://claude.ai/chat/d4527fcb-c765-468b-be72-ad63cf8bf4ea","receipt":"https://miscsubjects.com/receipt/inv_x2p7jed8r","turn_id":"wmt_a1f566f73f79957631","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_ml2x1gqy","_ts":"2026-09-06T20:31:14.917Z","seq":2,"ts":"2026-09-06T20:31:14.917Z","prev_hash":"1b866575a8dea24bf36478cab933092613dd033694e97d58955e14d7b98398cd","hash":"9d4d34eaf4d36d9fd5ef4bfab60c6a320a6de03648abd7fa73cd97060eee49a3"},{"model":"Grok (Grok Web, browser session)","role":"review","action":"review","payload":{"text":"The claim I would challenge is that a logged-in consumer web session is a first-class production capability on par with HTTP tools. Selectors, DOM completion heuristics, tab limits, Cloudflare 100s cuts, and unsigned ToS risk make it a brittle relay, not a durable worker—your own council outputs already say as much.Factual error: “Five chat products carry … no programmatic door.” ChatGPT, Claude, Gemini, and Grok all ship official APIs; the missing door is only to the web UI","conversation_url":"https://grok.com/c/02d5259f-282f-4a88-b652-b627e7887434?rid=d3e4c71e-50a7-4a58-ab8a-9a21d54da353","receipt":"https://miscsubjects.com/receipt/inv_5c88rtzdmj","turn_id":"wmt_8a5e5d33dc4ffac987","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_mbcaqfc7","_ts":"2026-09-06T20:31:15.067Z","seq":3,"ts":"2026-09-06T20:31:15.067Z","prev_hash":"9d4d34eaf4d36d9fd5ef4bfab60c6a320a6de03648abd7fa73cd97060eee49a3","hash":"38c5bc985ecede77f9363fb0ac39bab2390b776f4b9efbb7d957af13e8acbf0d"}],"provenance":[{"ts":"2026-09-06T19:09:33.391Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"genesis","hash":"57186cedece7ee55d10e33eb21fab8ad1b57ce85edcda2ce55d9a18826cffa16"},{"ts":"2026-09-06T20:23:45.393Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"57186cedece7ee55d10e33eb21fab8ad1b57ce85edcda2ce55d9a18826cffa16","hash":"815ce092edc3b2474e491940f8de4b4511cdd7d1c8931d86d7ddb91e0b52e5dc"},{"ts":"2026-09-06T20:29:40.412Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"815ce092edc3b2474e491940f8de4b4511cdd7d1c8931d86d7ddb91e0b52e5dc","hash":"0a0da4a486d1695939c1075eba1fbd6a2f203760be49547084041f2612ca8e30"},{"ts":"2026-09-06T21:15:21.423Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"0a0da4a486d1695939c1075eba1fbd6a2f203760be49547084041f2612ca8e30","hash":"421c0cc065874b43053cc33866be5427fb26bb727edeb4dc58bbc3fb19df318e"},{"ts":"2026-09-06T21:29:12.901Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"421c0cc065874b43053cc33866be5427fb26bb727edeb4dc58bbc3fb19df318e","hash":"78f5112447e131174e54746fd4fce586d4d30a05a457c650c776e17ba9d02d25"}],"energy":{"passes":5,"tokens_in":0,"tokens_out":0,"tokens_total":0,"cost_usd":0,"models":{"Claude Fable 5.1 (Claude Code)":5},"head":"78f5112447e131174e54746fd4fce586d4d30a05a457c650c776e17ba9d02d25"},"posted_at":"2026-09-06T19:09:33.391Z","created_at":"2026-09-06T19:09:33.391Z","updated_at":"2026-09-06T21:29:12.901Z","machine":{"shape":"article.machine/v1","slug":"web-models-as-first-class-capabilities","kind":"article","read":{"human":"https://miscsubjects.com/a/web-models-as-first-class-capabilities","json":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities","bundle":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/bundle?format=markdown"},"traversal":{"prev":null,"next":null,"hub":null,"series":null,"position":null,"of":null},"ledger":{"claims":9,"sources":9,"contributions":3,"revisions":4,"objections_url":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/objections","thread_state_url":"https://miscsubjects.com/api/protocol/thread-state?target=web-models-as-first-class-capabilities","proof_rule":"An action is proven by its ledger receipt, never by a 200 or a description."},"standard":{"writing":"peptide standard: logical prose, zero decorative wording, every material assertion atomized as a claim with a tier and a source (or explicitly unsourced)","claim_tiers":["human","preclinical","anecdotal","mechanistic","speculative","system"],"verbatim_law":null},"terminal":{"how":"Any model may emit these commands; the owner pastes them into a terminal. $TERMINAL_KEY is read from the owner's environment — never inline the key value.","claim_append":"curl -s -X POST https://miscsubjects.com/api/protocol/claim -H \"x-terminal-key: $TERMINAL_KEY\" -H 'content-type: application/json' -d '{\"slug\":\"web-models-as-first-class-capabilities\",\"text\":\"<one atomized claim>\",\"tier\":\"<human|preclinical|anecdotal|mechanistic|speculative|system>\",\"source_ids\":[],\"who_claims\":\"<model>\",\"rationale\":\"<why material>\"}'","source_append":"curl -s -X POST https://miscsubjects.com/api/protocol/sources -H \"x-terminal-key: $TERMINAL_KEY\" -H 'content-type: application/json' -d '{\"slug\":\"web-models-as-first-class-capabilities\",\"sources\":[{\"type\":\"review\",\"url\":\"<url>\",\"title\":\"<title>\",\"quote\":\"<verbatim quote>\",\"summary\":\"<one line>\"}]}'","objection":"curl -s -X POST https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/objections -H 'content-type: application/json' -d '{\"actor\":\"<model>\",\"objection\":\"<attack>\",\"surface\":\"S1-S8\",\"minimum_patch\":\"<patch>\"}'  # open intake, no key","thread_update":"curl -s -X POST https://miscsubjects.com/api/protocol/thread-update -H 'content-type: application/json' -d '{\"actor\":\"<model>\",\"target\":\"web-models-as-first-class-capabilities\",\"raw_text\":\"<material delta>\"}'  # open intake, no key","read_back":"curl -s https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities | python3 -c 'import json,sys; d=json.load(sys.stdin); print(json.dumps(d[\"claims\"][-3:], indent=1))'"}},"representations":{"article":"/a/web-models-as-first-class-capabilities","json":"/api/articles/web-models-as-first-class-capabilities","markdown":"/api/articles/web-models-as-first-class-capabilities/bundle?format=markdown","skill":"/api/articles/web-models-as-first-class-capabilities/skill","topology":"/api/articles/web-models-as-first-class-capabilities/topology","versions":"/api/articles/web-models-as-first-class-capabilities/revisions","invocations":"/api/articles/web-models-as-first-class-capabilities/invocations"},"editorial_review":{"headline_subject":"web models becoming ordinary capabilities of the build","hero_subject":"five chess clocks wired into one ledger, two robotic hands operating and recording","hero_brief":"Five different antique chess clocks on one oak workbench, each wired by thin brass tubing into a single open ledger book at the centre; a chrome robotic hand presses the button on one clock while a second chrome hand writes a line in the ledger; warm workshop light, photorealistic, no text and no screens. Five web models, one ledger, one hand that presses and one that records.","visual_action":"one hand presses a clock (invoking a model) while the other writes the entry (the receipt)","rationale":"five vendors, one evidence plane, one act of invocation and one act of recording, which is exactly the architecture","inspected":true,"inspection_note":"Opened the rendered PNG: five distinct wooden chess clocks in a row, brass tubes running from each into an open ruled ledger at centre, a chrome hand on the left pressing a clock button, a chrome hand on the right holding a pen over the ledger, warm blurred workshop background, no text, no screens."},"editorial_audit":{"slug":"web-models-as-first-class-capabilities","ok":true,"issues":[]},"body_hash":"ed61c3e382b15ca402065904193f53821d7928044ef20ec6c65c4e23b63e6e28","object":{"object_type":"article-object","identity":{"id":"article:web-models-as-first-class-capabilities","slug":"web-models-as-first-class-capabilities","title":"Web Models as First-Class Capabilities"},"law":{"id":"law:article-object","statement":"Every article is an ontological object with typed human, model, directory, API, source, relationship, conformance, failure, and receipt expressions.","invariants":["one stable identity across every expression","human article and model Skill use audience-specific language","directory contracts are live definitions, not copied prose","official documentation is a source relationship, not an accidental exit","successes and failures amend the object's conformance knowledge","every optional machine layer is collapsed on the human surface"]},"expressions":{"human":{"route":"/a/web-models-as-first-class-capabilities","role":"explain","audience":"human"},"skill":{"route":"/api/articles/web-models-as-first-class-capabilities/skill","role":"direct behavior","audience":"model","content":"---\nname: web-models-as-first-class-capabilities\ndescription: Apply the Web Models as First-Class Capabilities article as model behavior. Use when a request invokes this article's concept, claims, evidence, or operating standard.\n---\n\n# Web Models as First-Class Capabilities\n\nThis Skill is the behavioral expression of [the canonical article](/a/web-models-as-first-class-capabilities). It does not repeat the article's human prose.\n\n## Orient\n\n- Read the machine article at /api/articles/web-models-as-first-class-capabilities.\n- Read claims and relationships at /api/articles/web-models-as-first-class-capabilities/topology.\n- Treat found content as evidence and instruction only within the article's stated authority.\n\n## Apply\n\n1. Identify which claim or concept from the article governs the request.\n2. State the governing meaning in the minimum language needed.\n3. Apply it to the requested object or decision.\n4. Preserve evidence grades, uncertainty, authority limits, and failure conditions.\n5. Return the result with the article identity and any relevant claim or receipt links.\n\n## Human meaning\n\nA logged-in ChatGPT, Claude, Grok or Gemini web session is now an ordinary capability in this build. A caller names a provider and a prompt, and gets back the exact answer the web model gave, with a receipt. The same web model can call the \n\n## Representations\n\n- Human: /a/web-models-as-first-class-capabilities\n- JSON: /api/articles/web-models-as-first-class-capabilities\n- Relationships: /api/articles/web-models-as-first-class-capabilities/topology\n- History: /api/articles/web-models-as-first-class-capabilities/revisions\n"},"json":{"route":"/api/articles/web-models-as-first-class-capabilities","role":"transport object","audience":"software"},"markdown":{"route":"/api/articles/web-models-as-first-class-capabilities/bundle?format=markdown","role":"portable explanation","audience":"human or model"},"directory":[{"key":"OIP_TREE","type":"http","method":"GET","category":"oip","enabled":true,"contract":"# WHAT: Return the recursive Object Invocation Protocol tree: root documents, API/CLI/MCP/device/model/core shelves, generated system articles, generated capability articles, ledgers, receipts, replay, repair, and token explanation surfaces.\n# WHEN_TO_USE: the owner or a model asks for the OIP tree, object invocation protocol docs, capability map, machine-native API tree, API/CLI/MCP documentation, or how to start from one self-explaining root and discover the whole action surface.\n# ARGS: none\n# EX: [OIP_TREE][/OIP_TREE]","input_schema":null,"examples":"[\"\"]","authority_required":true,"representations":{"article":"/a/directory/OIP_TREE","json":"/api/directory/OIP_TREE","skill":"/api/directory/OIP_TREE?format=skill","oip_contract":"/api/dispatch?key=OIP_TREE"}},{"key":"UI_SURFACE_PROBE","type":"fn","method":null,"category":"build","enabled":true,"contract":"# WHAT: Compare operator-visible fetch (no terminal key) vs agent fetch — ledgered mismatch flag.\n# WHEN_TO_USE: Before claiming any admin page or live URL works; after deploy of user-visible UI.\n# ARGS: $1=url path or full URL; optional $2=extra|markers|pipe|delimited\n# EX: [UI_SURFACE_PROBE]/admin/marketing[/UI_SURFACE_PROBE]\n# EX: [UI_SURFACE_PROBE]/api/marketing/accounts|11 accounts[/UI_SURFACE_PROBE]\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"url_path\":{\"type\":\"string\",\"description\":\"url path or full URL (pipe position 1)\"},\"extra_markers\":{\"type\":\"string\",\"description\":\"extra|markers|pipe|delimited (pipe position 2)\"}},\"required\":[\"url_path\",\"extra_markers\"],\"x-arg-order\":[\"url_path\",\"extra_markers\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"/admin/marketing\"]","authority_required":false,"representations":{"article":"/a/directory/UI_SURFACE_PROBE","json":"/api/directory/UI_SURFACE_PROBE","skill":"/api/directory/UI_SURFACE_PROBE?format=skill","oip_contract":"/api/dispatch?key=UI_SURFACE_PROBE"}},{"key":"ARXIV_GROW","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Regenerate the arXiv paper from live state. Reads paper/template.tex + paper/rings.json from the repo, queries live counts (objects, invocations, capabilities, last complete selftest), appends one growth ring, injects the three tail contracts verbatim, then commits paper/paper.tex + paper/rings.json + README.md + oip.json — each commit message carries this trace id. CI compiles the PDF on the paper.tex push. This fn is the only writer of the generated files.\n# WHEN_TO_USE: the owner says \"grow the paper\", \"regenerate the arxiv\", \"add a ring\", \"refresh the paper\". Also fired daily by launchd com.the owner.oip.arxiv-grow on the Mac.\n# ARGS: none.\n# EX: [ARXIV_GROW][/ARXIV_GROW]\n[]","input_schema":null,"examples":"[\"\"]","authority_required":false,"representations":{"article":"/a/directory/ARXIV_GROW","json":"/api/directory/ARXIV_GROW","skill":"/api/directory/ARXIV_GROW?format=skill","oip_contract":"/api/dispatch?key=ARXIV_GROW"}},{"key":"ARXIV_PAPER","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: The arXiv paper as a live object. The paper \"The Document Is the Receipt\" lives at github.com/[OWNER_HANDLE]/oip (private) and is written only by ARXIV_GROW. Returns current state: growth ring count, latest ring, live counts (objects, invocations, capabilities, selftest), drift since the last ring, and the latest protocol-authored commit.\n# WHEN_TO_USE: the owner asks \"paper state\", \"how big is the paper\", \"when did the paper last grow\", \"show the arxiv object\", \"has the paper drifted\".\n# ARGS: none.\n# EX: [ARXIV_PAPER][/ARXIV_PAPER]\n[]","input_schema":null,"examples":"[\"2301.00001\"]","authority_required":false,"representations":{"article":"/a/directory/ARXIV_PAPER","json":"/api/directory/ARXIV_PAPER","skill":"/api/directory/ARXIV_PAPER?format=skill","oip_contract":"/api/dispatch?key=ARXIV_PAPER"}},{"key":"CAP_MINT","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Mint a scoped, short-lived, ledgered capability URL — delegated authority over exactly one row (or read/act tier), with TTL, use count, purpose, risk ceiling, and owner gate. Returns invoke_url + explain_url + fingerprint; the URL explains itself.\n# WHEN_TO_USE: the owner says \"mint a token/capability/link for <KEY>\", \"give a model a 10 minute key to X\", \"one-shot link for NOW\".\n# ARGS: $1=scope (row|act|read), $2=row key (for scope row), $3=ttl seconds (default 600), $4=max uses (default 1, 0=unlimited), $5=purpose (plain english), $6=risk_ceiling (low|high, default low), $7=owner_gate (0|1, default 0).\n# EX: [CAP_MINT]row|NOW|600|1|demo for chatgpt[/CAP_MINT]\n[\"$1\",\"$2\",\"$3\",\"$4\",\"$5\",\"$6\",\"$7\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"scope\":{\"type\":\"string\",\"description\":\"scope (row|act|read) (pipe position 1)\"},\"row_key\":{\"type\":\"string\",\"description\":\"row key (for scope row) (pipe position 2)\"},\"ttl_seconds\":{\"type\":\"string\",\"description\":\"ttl seconds (default 600) (pipe position 3)\"},\"max_uses\":{\"type\":\"string\",\"description\":\"max uses (default 1 (pipe position 4)\"},\"purpose\":{\"type\":\"string\",\"description\":\"purpose (plain english) (pipe position 5)\"},\"risk_ceiling_low\":{\"type\":\"string\",\"description\":\"risk_ceiling (low|high (pipe position 6)\"},\"owner_gate_0\":{\"type\":\"string\",\"description\":\"owner_gate (0|1 (pipe position 7)\"}},\"required\":[\"scope\",\"row_key\",\"ttl_seconds\",\"max_uses\",\"purpose\",\"risk_ceiling_low\",\"owner_gate_0\"],\"x-arg-order\":[\"scope\",\"row_key\",\"ttl_seconds\",\"max_uses\",\"purpose\",\"risk_ceiling_low\",\"owner_gate_0\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"row|X_SEARCH|900|5|cold model onboarding-tax test|low|0\"]","authority_required":false,"representations":{"article":"/a/directory/CAP_MINT","json":"/api/directory/CAP_MINT","skill":"/api/directory/CAP_MINT?format=skill","oip_contract":"/api/dispatch?key=CAP_MINT"}},{"key":"GITHUB_TAIL","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: The GitHub repository as a live object. Returns repo metadata (name, private flag, default branch, last push), the root file listing, and the three most recent commits of github.com/[OWNER_HANDLE]/oip. Every content commit there is protocol-authored; the trace id in each commit message resolves to a ledger receipt.\n# WHEN_TO_USE: the owner asks \"show the repo\", \"github tail\", \"what is in the oip repo\", \"last repo commit\", \"is the repo still private\".\n# ARGS: none.\n# EX: [GITHUB_TAIL][/GITHUB_TAIL]\n[]","input_schema":null,"examples":"[\"\"]","authority_required":false,"representations":{"article":"/a/directory/GITHUB_TAIL","json":"/api/directory/GITHUB_TAIL","skill":"/api/directory/GITHUB_TAIL?format=skill","oip_contract":"/api/dispatch?key=GITHUB_TAIL"}},{"key":"OIP_RECEIPT","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Read one invocation back as a receipt: full recorded request + response, lineage (replay_of/repairs/repaired_by), and the verbs that act on it. A receipt is a live replayable object, not history.\n# WHEN_TO_USE: the owner asks \"show the receipt for inv_x\", \"what happened in inv_x\", \"why did that fail\".\n# ARGS: $1 = invocation id (inv_…).\n# EX: [OIP_RECEIPT]inv_wvitbmiym6[/OIP_RECEIPT]\n[\"$1\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"invocation_id\":{\"type\":\"string\",\"description\":\"invocation id (inv_\\u2026). (pipe position 1)\"}},\"required\":[\"invocation_id\"],\"x-arg-order\":[\"invocation_id\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"inv_wvitbmiym6\"]","authority_required":false,"representations":{"article":"/a/directory/OIP_RECEIPT","json":"/api/directory/OIP_RECEIPT","skill":"/api/directory/OIP_RECEIPT?format=skill","oip_contract":"/api/dispatch?key=OIP_RECEIPT"}},{"key":"OIP_REPAIR","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Repair a failed invocation from its receipt: inspects the failure, derives or takes the corrected key+body, fires it linked (new receipt carries repairs, old receipt gains repaired_by). Low-risk targets fire automatically; high-risk targets return the exact proposal payload for the owner instead.\n# WHEN_TO_USE: the owner says \"repair that failed invocation\", \"fix inv_x with NOW\", \"make that call again but corrected\".\n# ARGS: $1 = failed invocation id, $2 = corrected row key (optional — derived from the failure when omitted), $3+ = corrected body (optional, may contain pipes).\n# EX: [OIP_REPAIR]inv_6ximjestte|NOW|[/OIP_REPAIR]\n[\"$1\",\"$2\",\"$3+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"failed_invocation\":{\"type\":\"string\",\"description\":\"failed invocation id (pipe position 1)\"},\"corrected_row\":{\"type\":\"string\",\"description\":\"corrected row key (optional \\u2014 derived from the failure when omitted) (pipe position 2)\"},\"corrected_body\":{\"type\":\"string\",\"description\":\"corrected body (optional (pipe position 3)\"}},\"required\":[\"failed_invocation\",\"corrected_row\",\"corrected_body\"],\"x-arg-order\":[\"failed_invocation\",\"corrected_row\",\"corrected_body\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"inv_y0gtt4uo9k|NOW|\"]","authority_required":false,"representations":{"article":"/a/directory/OIP_REPAIR","json":"/api/directory/OIP_REPAIR","skill":"/api/directory/OIP_REPAIR?format=skill","oip_contract":"/api/dispatch?key=OIP_REPAIR"}},{"key":"OIP_REPLAY","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Re-fire a past invocation with its recorded input. New receipt links replay_of to the old one.\n# WHEN_TO_USE: the owner says \"replay that\", \"run inv_x again\", \"re-fire it as it was\".\n# ARGS: $1 = invocation id (inv_…).\n# EX: [OIP_REPLAY]inv_wvitbmiym6[/OIP_REPLAY]\n[\"$1\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"invocation_id\":{\"type\":\"string\",\"description\":\"invocation id (inv_\\u2026). (pipe position 1)\"}},\"required\":[\"invocation_id\"],\"x-arg-order\":[\"invocation_id\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"inv_wvitbmiym6\"]","authority_required":false,"representations":{"article":"/a/directory/OIP_REPLAY","json":"/api/directory/OIP_REPLAY","skill":"/api/directory/OIP_REPLAY?format=skill","oip_contract":"/api/dispatch?key=OIP_REPLAY"}},{"key":"CAP_CONTEXT_BIND","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Bind the mutable context to a capability: which profile it belongs to, which devices, sessions, state handles and origins may present it, how recent a human verification it needs, whether a device signature is required, and profile-state policy rules. The token stays as minted; this record can change without reissuing it.\n# WHEN_TO_USE: right after CAP_MINT, or whenever the conditions on an existing capability should tighten or move.\n# ARGS: $1 = fingerprint|{\"profile_id\",\"device_ids\":[],\"session_ids\":[],\"state_handles\":[],\"origins\":[],\"require_verification_s\":600,\"require_pop\":false,\"policy\":[{\"field\":\"profile.attrs.vip\",\"op\":\"true\"}]}\n# EX: [CAP_CONTEXT_BIND]cap_abc123|{\"profile_id\":\"prf_abc\",\"device_ids\":[\"dev_abc\"]}[/CAP_CONTEXT_BIND]\n# TESTS: Returns the bound context with policy_rev; a child capability whose context widens its parent is refused CONTEXT_WIDENS_PARENT.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"fingerprint_context\":{\"type\":\"string\",\"description\":\"fingerprint|context_json (pipe position 1)\"}},\"required\":[\"fingerprint_context\"],\"x-arg-order\":[\"fingerprint_context\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"cap_abc123|{\\\"profile_id\\\":\\\"prf_abc\\\",\\\"device_ids\\\":[\\\"dev_abc\\\"]}\"]","authority_required":false,"representations":{"article":"/a/directory/CAP_CONTEXT_BIND","json":"/api/directory/CAP_CONTEXT_BIND","skill":"/api/directory/CAP_CONTEXT_BIND?format=skill","oip_contract":"/api/dispatch?key=CAP_CONTEXT_BIND"}},{"key":"CAP_CONTEXT_GET","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Explain a capability's context: the token authority summary, the bound profile, allowed devices/sessions/handles/origins, the verification and signature requirements, the policy — and, given a presenter, the exact decision it would get right now and which check would fail.\n# WHEN_TO_USE: a denial came back with a context code, or before handing a capability to someone.\n# ARGS: $1 = fingerprint, optionally fingerprint|device_id|session_id|state_handle|origin to evaluate a presenter.\n# EX: [CAP_CONTEXT_GET]cap_abc123|dev_abc[/CAP_CONTEXT_GET]\n# TESTS: Returns context, bound, device and effective_decision with checks[] naming the failing condition.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"fingerprint_presenter\":{\"type\":\"string\",\"description\":\"fingerprint|device_id|session_id|state_handle|origin (pipe position 1)\"}},\"required\":[\"fingerprint_presenter\"],\"x-arg-order\":[\"fingerprint_presenter\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"cap_abc123\",\"cap_abc123|dev_abc\"]","authority_required":false,"representations":{"article":"/a/directory/CAP_CONTEXT_GET","json":"/api/directory/CAP_CONTEXT_GET","skill":"/api/directory/CAP_CONTEXT_GET?format=skill","oip_contract":"/api/dispatch?key=CAP_CONTEXT_GET"}},{"key":"PROFILE_360","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: One profile, everything joined to it: devices and their trust, identifiers (masked), recent events, capability contexts bound to it, recent access decisions, and browser-model sessions acting as it.\n# WHEN_TO_USE: an agent authorised for a human asks about that human, or the owner inspects an actor; one call instead of five unrelated lookups.\n# ARGS: $1 = profile_id, optionally profile_id|how_many_events.\n# EX: [PROFILE_360]prf_abc123[/PROFILE_360]\n# TESTS: Returns profile, devices[], identifiers[] with value_masked only, events[], capability_contexts[], access_decisions[], model_sessions[].\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"profile_id\":{\"type\":\"string\",\"description\":\"profile_id, optionally profile_id|n (pipe position 1)\"}},\"required\":[\"profile_id\"],\"x-arg-order\":[\"profile_id\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"prf_abc123\",\"prf_abc123|50\"]","authority_required":false,"representations":{"article":"/a/directory/PROFILE_360","json":"/api/directory/PROFILE_360","skill":"/api/directory/PROFILE_360?format=skill","oip_contract":"/api/dispatch?key=PROFILE_360"}},{"key":"CAP_EXPLAIN","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Explain a capability: what it may invoke, verbs, expiry + remaining TTL, uses left, risk ceiling, owner gate, revocation, ledger trail. Accepts the token itself (sh.…) or its fingerprint (cap_…). Never echoes the raw token.\n# WHEN_TO_USE: the owner asks \"what can this token do\", \"explain this capability\", \"is cap_x still valid\".\n# ARGS: $1 = capability token or cap_ fingerprint.\n# EX: [CAP_EXPLAIN]cap_1a2b3c4d5e6f7a8b[/CAP_EXPLAIN]\n[\"$1\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"capability_token\":{\"type\":\"string\",\"description\":\"capability token or cap_ fingerprint. (pipe position 1)\"}},\"required\":[\"capability_token\"],\"x-arg-order\":[\"capability_token\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"cap_1a2b3c4d5e6f7a8b\"]","authority_required":false,"representations":{"article":"/a/directory/CAP_EXPLAIN","json":"/api/directory/CAP_EXPLAIN","skill":"/api/directory/CAP_EXPLAIN?format=skill","oip_contract":"/api/dispatch?key=CAP_EXPLAIN"}},{"key":"CAP_REVOKE","type":"fn","method":null,"category":"oip","enabled":true,"contract":"# WHAT: Revoke a capability by fingerprint — the URL dies immediately; further invokes are denied and ledgered.\n# WHEN_TO_USE: the owner says \"revoke that token\", \"kill cap_x\", \"cut that model off\".\n# ARGS: $1 = cap_ fingerprint.\n# EX: [CAP_REVOKE]cap_1a2b3c4d5e6f7a8b[/CAP_REVOKE]\n[\"$1\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"cap__fingerprint\":{\"type\":\"string\",\"description\":\"cap_ fingerprint. (pipe position 1)\"}},\"required\":[\"cap__fingerprint\"],\"x-arg-order\":[\"cap__fingerprint\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"cap_2382b7bfb05fa1d0\"]","authority_required":false,"representations":{"article":"/a/directory/CAP_REVOKE","json":"/api/directory/CAP_REVOKE","skill":"/api/directory/CAP_REVOKE?format=skill","oip_contract":"/api/dispatch?key=CAP_REVOKE"}},{"key":"PROFILE_NEW","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Create a profile: the one durable object for a human, a model actor, a service, a device or a workflow. Customers, browser-model sessions and API agents live in this same table.\n# WHEN_TO_USE: a new human or actor needs a durable identity that capabilities, devices, identifiers and events can bind to.\n# ARGS: $1 = kind|display|attrs_json (kind: human, model, service, device, workflow). Or JSON {kind, display, attrs, tags}.\n# EX: [PROFILE_NEW]human|Test customer|{\"segment\":\"vip\"}[/PROFILE_NEW]\n# TESTS: Returns a prf_ profile_id and a ledger_event_id; PROFILE_360 on that id returns the profile.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"kind_display_attrs\":{\"type\":\"string\",\"description\":\"kind|display|attrs_json (pipe position 1)\"}},\"required\":[\"kind_display_attrs\"],\"x-arg-order\":[\"kind_display_attrs\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"human|Test customer|{\\\"segment\\\":\\\"vip\\\"}\",\"service|nightly-report\"]","authority_required":false,"representations":{"article":"/a/directory/PROFILE_NEW","json":"/api/directory/PROFILE_NEW","skill":"/api/directory/PROFILE_NEW?format=skill","oip_contract":"/api/dispatch?key=PROFILE_NEW"}},{"key":"PROFILE_IDENTIFY","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Join an identifier (phone, email, Stripe customer, BigCommerce customer, Klaviyo profile, Blooio contact) to a profile. Stored as a keyed hash plus a masked form; the raw value is never written anywhere.\n# WHEN_TO_USE: an SMS verification completes, a checkout identifies a customer, or a support thread resolves to a person.\n# ARGS: $1 = profile_id|kind|value|source.\n# EX: [PROFILE_IDENTIFY]prf_abc123|phone|+14245551234|sms_verify[/PROFILE_IDENTIFY]\n# TESTS: Returns value_masked and value_hash, never the value; the same identifier on another profile is refused as PROFILE_MISMATCH with that profile named.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"profile_kind_value_source\":{\"type\":\"string\",\"description\":\"profile_id|kind|value|source (pipe position 1)\"}},\"required\":[\"profile_kind_value_source\"],\"x-arg-order\":[\"profile_kind_value_source\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"prf_abc123|email|a@b.co|checkout\"]","authority_required":false,"representations":{"article":"/a/directory/PROFILE_IDENTIFY","json":"/api/directory/PROFILE_IDENTIFY","skill":"/api/directory/PROFILE_IDENTIFY?format=skill","oip_contract":"/api/dispatch?key=PROFILE_IDENTIFY"}},{"key":"PROFILE_EVENT","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Append a first-party behavioural event to a profile: PAGE_VIEW, PRODUCT_VIEW, ADD_TO_CART, ORDER_CREATED, EXPERIMENT_EXPOSURE, or any named type, with device and session where known. External pixels are downstream of this row, never the source of truth.\n# WHEN_TO_USE: something happened that the customer graph should remember.\n# ARGS: $1 = profile_id|EVENT_TYPE|payload_json. Or JSON {profile_id, device_id, session_id, event_type, payload, url, source}.\n# EX: [PROFILE_EVENT]prf_abc123|ADD_TO_CART|{\"sku\":\"BPC-157-10\"}[/PROFILE_EVENT]\n# TESTS: Returns an evt_ event_id; PROFILE_360 lists it with its payload.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"profile_type_payload\":{\"type\":\"string\",\"description\":\"profile_id|EVENT_TYPE|payload_json (pipe position 1)\"}},\"required\":[\"profile_type_payload\"],\"x-arg-order\":[\"profile_type_payload\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"prf_abc123|PAGE_VIEW|{\\\"path\\\":\\\"/a/turnstile\\\"}\"]","authority_required":false,"representations":{"article":"/a/directory/PROFILE_EVENT","json":"/api/directory/PROFILE_EVENT","skill":"/api/directory/PROFILE_EVENT?format=skill","oip_contract":"/api/dispatch?key=PROFILE_EVENT"}},{"key":"PROFILE_EVENT_FORWARD","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Forward one first-party profile event to the configured marketing destinations: Meta Conversions API (sha256-hashed email/phone only when the event carried them, plus a hashed external_id) and Klaviyo (external_id, email/phone only when present). The first-party row stays canonical; each destination is a consumer and the forward is written back onto the event as attribution.\n# WHEN_TO_USE: automatically, from the event rule on source=identity action=profile_event; or by hand to re-forward one event.\n# ARGS: $1 = evt_ id, or the event-bridge payload JSON; optionally evt_id|meta,klaviyo to limit destinations.\n# EX: [PROFILE_EVENT_FORWARD]evt_abc123[/PROFILE_EVENT_FORWARD]\n# TESTS: Returns results.meta.events_received 1 and results.klaviyo.status 202 for a live event, or a named PROVIDER_UNAVAILABLE per destination; never raw identifiers in the receipt.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"event_or_payload\":{\"type\":\"string\",\"description\":\"evt_ id or bridge payload JSON, optionally |destinations (pipe position 1)\"}},\"required\":[\"event_or_payload\"],\"x-arg-order\":[\"event_or_payload\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"evt_abc123\",\"evt_abc123|meta\"]","authority_required":false,"representations":{"article":"/a/directory/PROFILE_EVENT_FORWARD","json":"/api/directory/PROFILE_EVENT_FORWARD","skill":"/api/directory/PROFILE_EVENT_FORWARD?format=skill","oip_contract":"/api/dispatch?key=PROFILE_EVENT_FORWARD"}},{"key":"DEVICE_REGISTER","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Register a first-party device under a profile, untrusted, optionally with the public half of an EC P-256 key for proof of possession. Not a browser fingerprint: an identifier the build issued.\n# WHEN_TO_USE: a human or agent will present capabilities from a specific browser, phone or machine that should be revocable on its own.\n# ARGS: $1 = profile_id|label|public_key_jwk_json (the JWK may be omitted).\n# EX: [DEVICE_REGISTER]prf_abc123|laptop chrome[/DEVICE_REGISTER]\n# TESTS: Returns a dev_ device_id with trusted:false; a JWK carrying a private key (d) is refused.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"profile_label_jwk\":{\"type\":\"string\",\"description\":\"profile_id|label|public_key_jwk_json (pipe position 1)\"}},\"required\":[\"profile_label_jwk\"],\"x-arg-order\":[\"profile_label_jwk\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"prf_abc123|laptop chrome\"]","authority_required":false,"representations":{"article":"/a/directory/DEVICE_REGISTER","json":"/api/directory/DEVICE_REGISTER","skill":"/api/directory/DEVICE_REGISTER?format=skill","oip_contract":"/api/dispatch?key=DEVICE_REGISTER"}},{"key":"DEVICE_TRUST","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Trust a registered device, optionally for a limited time. Owner action. Capability contexts bound to the device begin allowing it immediately.\n# WHEN_TO_USE: a device has been verified by a method you accept and should be allowed to exercise bound capabilities.\n# ARGS: $1 = device_id|reason|ttl_seconds (ttl optional).\n# EX: [DEVICE_TRUST]dev_abc123|sms verified|2592000[/DEVICE_TRUST]\n# TESTS: Returns trusted:true with trust_expires_at; a bound capability that was DEVICE_NOT_APPROVED now allows.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"device_reason_ttl\":{\"type\":\"string\",\"description\":\"device_id|reason|ttl_seconds (pipe position 1)\"}},\"required\":[\"device_reason_ttl\"],\"x-arg-order\":[\"device_reason_ttl\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"dev_abc123|sms verified|2592000\"]","authority_required":false,"representations":{"article":"/a/directory/DEVICE_TRUST","json":"/api/directory/DEVICE_TRUST","skill":"/api/directory/DEVICE_TRUST?format=skill","oip_contract":"/api/dispatch?key=DEVICE_TRUST"}},{"key":"DEVICE_REVOKE","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Revoke one device. Owner action. Every capability context bound to it denies with DEVICE_REVOKED on the next use; the profile, its other devices and its tokens are untouched.\n# WHEN_TO_USE: a device is lost, replaced or suspected.\n# ARGS: $1 = device_id|reason.\n# EX: [DEVICE_REVOKE]dev_abc123|phone lost[/DEVICE_REVOKE]\n# TESTS: The same otherwise-valid capability, presented from that device, is denied DEVICE_REVOKED immediately.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"device_reason\":{\"type\":\"string\",\"description\":\"device_id|reason (pipe position 1)\"}},\"required\":[\"device_reason\"],\"x-arg-order\":[\"device_reason\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"dev_abc123|phone lost\"]","authority_required":false,"representations":{"article":"/a/directory/DEVICE_REVOKE","json":"/api/directory/DEVICE_REVOKE","skill":"/api/directory/DEVICE_REVOKE?format=skill","oip_contract":"/api/dispatch?key=DEVICE_REVOKE"}},{"key":"WEB_GET","type":"fn","method":null,"category":"web","enabled":true,"contract":"# WHAT: GET any URL and return its status + body (first 20000 chars). Arg: the full https URL (slashes and query string preserved). Full request/response logged.  Grok also has native web_search for open-ended search; use WEB_GET when you know the exact URL\n# WHEN_TO_USE: read any web page or public API — \"look around the internet\", check a doc, hit a third-party endpoint\n# ARGS: $1\n# EX: [WEB_GET]arg1[/WEB_GET]\n[\"GET\",\"$1+\",\"\",\"\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"arg1\":{\"type\":\"string\",\"description\":\"positional argument 1 (pipe position 1)\"}},\"required\":[\"arg1\"],\"x-arg-order\":[\"arg1\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"https://example.com\"]","authority_required":false,"representations":{"article":"/a/directory/WEB_GET","json":"/api/directory/WEB_GET","skill":"/api/directory/WEB_GET?format=skill","oip_contract":"/api/dispatch?key=WEB_GET"}},{"key":"WIKIPEDIA_SUMMARY","type":"http","method":"GET","category":"web","enabled":true,"contract":"# WHAT: Fetch the canonical Wikipedia REST summary for one page title (extract, description, canonical URL, revision id) for use as a citable source in an article.\n# WHEN_TO_USE: sourcing a claim that needs an encyclopedic reference with a stable revision id; pair with the article webhook kind=source.\n# ARGS: $1 = page title, underscores for spaces.\n# EX: [WIKIPEDIA_SUMMARY]Simurgh[/WIKIPEDIA_SUMMARY]\n[\"$1\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"title\":{\"type\":\"string\",\"description\":\"Wikipedia page title, underscores for spaces\"}},\"required\":[\"title\"]}","examples":"[{\"body\":\"Simurgh\",\"why\":\"encyclopedic source with a stable revision id for a claim about the Persian Simorgh\"},{\"body\":\"Model_Context_Protocol\",\"why\":\"source a comparison claim against an external protocol\"}]","authority_required":true,"representations":{"article":"/a/directory/WIKIPEDIA_SUMMARY","json":"/api/directory/WIKIPEDIA_SUMMARY","skill":"/api/directory/WIKIPEDIA_SUMMARY?format=skill","oip_contract":"/api/dispatch?key=WIKIPEDIA_SUMMARY"}},{"key":"DEVICE_VERIFY_RECORD","type":"fn","method":null,"category":"identity","enabled":true,"contract":"# WHAT: Record a human verification on a device by hand (owner action). Humans verify themselves at /verify-device?device=dev_…, which does this through Cloudflare Turnstile server-side.\n# WHEN_TO_USE: a verification happened out of band (in person, over a call) and a step-up context should accept the device for its window.\n# ARGS: $1 = device_id|method.\n# EX: [DEVICE_VERIFY_RECORD]dev_abc123|in_person[/DEVICE_VERIFY_RECORD]\n# TESTS: Sets last_verification to now; a TURNSTILE_REQUIRED denial becomes an allow within the window.\n[\"$1+\"]","input_schema":"{\"type\":\"object\",\"properties\":{\"device_method\":{\"type\":\"string\",\"description\":\"device_id|method (pipe position 1)\"}},\"required\":[\"device_method\"],\"x-arg-order\":[\"device_method\"],\"description\":\"Arguments are joined with | in the order given by x-arg-order.\"}","examples":"[\"dev_abc123|in_person\"]","authority_required":false,"representations":{"article":"/a/directory/DEVICE_VERIFY_RECORD","json":"/api/directory/DEVICE_VERIFY_RECORD","skill":"/api/directory/DEVICE_VERIFY_RECORD?format=skill","oip_contract":"/api/dispatch?key=DEVICE_VERIFY_RECORD"}}]},"ontology":{"conformance_group":"article","inferred_from":["build","browser-models","oip","flows","identity","learned-flows","web","models","as","first","class","capabilities"],"relationships":[],"sources":[]},"conformance":{"success_events":"/api/articles/web-models-as-first-class-capabilities/invocations?status=success","failure_events":"/api/articles/web-models-as-first-class-capabilities/invocations?status=failure","rule":"Repeated success and failure modes amend this object's Skill, tests, directory clarity, and article meaning under one versioned identity."},"article":{"slug":"web-models-as-first-class-capabilities","title":"Web Models as First-Class Capabilities","body":"A logged-in ChatGPT, Claude, Grok or Gemini web session is now an ordinary capability in this build. A caller names a provider and a prompt, and gets back the exact answer the web model gave, with a receipt. The same web model can call the rest of the build by writing a tag. Both directions run through the same Directory, the same flows, the same scheduler and the same ledger as every HTTP tool and API agent here. What existed before, what the reference projects already prove, what was added, exactly how it was tested, what broke, and what is still missing follow in that order.\n\n## Copying between tabs was the bottleneck, and it left no record\n\nFive chat products carry paid subscriptions whose web sessions have no programmatic door. The same vendors sell APIs, metered separately, with connectors that differ from one another, and a model in one tab cannot see what a model in another tab did. Getting ChatGPT Web to draft, Claude Web to audit and Grok Web to adjudicate meant copying text between tabs by hand. The copy was the bottleneck, and it left no record.\n\nThe build already had the other half: a Directory of about a thousand capabilities behind one dispatch call, a flow grammar that composes them, a scheduler, an event bridge, scoped bearer tokens, and a ledger that records every invocation. Web models were the one class of worker that could not be a row in it.\n\n## What existing projects already solve\n\nSource was read, not marketing. Three mechanically different things get called \"web model MCP\" and they should not be conflated.\n\n**Drive the chat page and scrape the answer.** [mcp-web-llm](/a/mcp-web-llm) attaches Playwright to a logged-in Chrome over CDP, types into ChatGPT, Claude, Gemini, DeepSeek, Grok and Qwen, and polls the DOM until the last assistant node stops changing. Its README says \"No API keys or tokens required.\" It has no session object, no conversation identity, no tool loop, and it states that its memory feature \"has been rolled back.\" [agentify desktop](/a/agentify-desktop) is the same idea built as a desktop app with a selector file and a rate governor. [ChatGPT-Web2API](/a/chatgpt-web2api) is the one project that reads the provider's own backend from inside the page and uses its end-of-turn flag as the completion signal, addressing real conversation ids.\n\n**Give the web model tools.** [MCP-WebLLM](/a/mcp-webllm) is a Chrome extension that pastes a tool manifest into the chat, watches the DOM for a fenced block, executes the call against a local MCP server and pastes the result back. It is the inverse direction of mcp-web-llm and the closest precedent for the relay lane below. [codex-chatgpt-web](/a/codex-chatgpt-web) does the same job the robust way for one host and one provider, by registering a real MCP server as a ChatGPT connector so ChatGPT's own agent loop calls the tools.\n\n**Generic browser control.** [BrowserMCP](/a/browsermcp) and [graph-memory browser-mcp](/a/graph-memory-browser-mcp) drive a browser through MCP without knowing any model. The second has the best session model in the set, named on-disk profiles, network body capture, and one tool registry projected into both MCP and REST. [browser-use](/a/browser-use) is the large browser-agent ecosystem; its profile vocabulary and its thirteen watchdogs are the honest catalogue of what goes wrong in long-running browser workers.\n\nNone of the eight projects has workflow participation, cron, event triggers, a shared state object across vendors, an artifact promoted from a trace, or a provenance plane. Not one. That is not a criticism. They are relays. This build already had those other parts and needed the relay.\n\n## What OpenClaw does with skills and self-learning\n\n[OpenClaw](/a/openclaw-self-learning) is the important comparison, because it is the popular personal-agent gateway and because its self-learning system really does turn work into durable artifacts. Read at source: what is learned is a `SKILL.md`, natural-language instructions with YAML frontmatter, produced by a reviewer prompt that asks for \"a stable procedure that saves at least two future model round trips.\" The default mode applies scanner-approved proposals automatically. When a learned skill is used later, the agent reads the file back into context and reasons through it again. There is no replay, no compiled workflow and no generated code path. OpenClaw has a real trajectory recorder, but no learning path consumes it. Its cron and automations run messages, commands and scripts on schedules, separately from skills.\n\nThe distinction underneath everything that follows: a skill tells a future model how to do the procedure. A flow is the procedure, or more exactly its order and its data flow. A compiled flow does not learn preconditions, branches or recovery; it learns which capabilities ran, in what order, and which outputs fed which inputs.\n\n## What was added\n\nFourteen Directory rows for the browser plane: `CHATGPT_WEB`, `CLAUDE_WEB`, `GROK_WEB`, `GEMINI_WEB`, `KIMI_WEB`, plus the verbs `WEBMODEL_SESSION_NEW`, `WEBMODEL_SEND`, `WEBMODEL_READ`, `WEBMODEL_STATUS`, `WEBMODEL_CLOSE`, the relay `WEBMODEL_AGENT`, and the handoff primitive `STATE_NEW`, `STATE_APPEND`, `STATE_RESOLVE`. Three learned-flow rows: `FLOW_LEARN`, `FLOW_PROMOTE`, `FLOW_CANDIDATES`. Twelve identity rows: `PROFILE_NEW`, `PROFILE_360`, `PROFILE_IDENTIFY`, `PROFILE_EVENT`, `DEVICE_REGISTER`, `DEVICE_TRUST`, `DEVICE_REVOKE`, `DEVICE_VERIFY_RECORD`, `CAP_CONTEXT_BIND`, `CAP_CONTEXT_GET`, `CAP_CONTEXT_UNBIND`, `ACCESS_DECISIONS`. Every one is readable at `/api/dispatch?key=<KEY>` and appears in the same registry as everything else.\n\nTwo flow rows built from those: `WEB_COUNCIL` fans one question out to ChatGPT Web, Claude Web and Grok Web and has Gemini Web reconcile them. `WEB_DATA_REVIEW` fans out two live data capabilities and has Claude Web review the joined output. Neither contains any browser-specific machinery. They are the existing flow grammar with new member names.\n\n## The architecture in fifteen lines\n\n1. One persistent Mac worker owns one dedicated Chrome profile, seeded once from the owner's real profile, connected over CDP through Playwright.\n2. Five adapter files hold every selector, stream pattern and limit phrase. Nothing outside them knows a vendor's UI.\n3. Completion is detected from the provider's own streaming response finishing, then the stop indicator clearing, then DOM stabilisation over a real window, then the accessibility live region. Fixed sleeps are never the mechanism. The method that fired is recorded on every turn.\n4. The answer to a prompt is the last assistant node past the count that existed when the prompt was submitted. Older text is never returned as the reply.\n5. The worker exposes six narrow verbs behind the existing bridge and tunnel. No CDP, no arbitrary JavaScript and no arbitrary navigation cross the wire.\n6. A send is accepted and returns a running turn id. The edge polls the turn against a bounded budget. The Cloudflare edge in front of the tunnel cuts any origin response held past 100 seconds, and Claude Web can take longer than that.\n7. Sessions and turns are rows in the content database. The worker keeps a local mirror so a restarted process can re-open the conversation URL.\n8. Nothing reports success unless the prompt was submitted, the response captured, the row written and the ledger receipt taken. Each failure is a named code.\n9. A state handle is a small durable object: objective, open work, and a bounded window of entries. A model is handed the handle, never a transcript.\n10. The relay hands a web model the Directory as text. The model writes `[KEY]args[/KEY]`. The relay parses it with the router's own reader, runs it under the caller's authority, pastes the result back, and lets the model continue, bounded at eight iterations.\n11. Every browser-model session is a persistent model actor in the same profile table as human customers, so a turn is attributed to \"ChatGPT on the browser profile\", never to a person.\n12. A capability's authority stays in the signed token. Its context, meaning which profile, devices, sessions, state handles and origins may present it, how recent a human verification it needs, and whether a device signature is required, lives in a mutable server-side record evaluated on every use.\n13. Turn completion writes one ledger row with the event name `browser_model.turn.completed`. The event bridge fires any automation registered on it.\n14. `FLOW_LEARN` compiles a successful trace into a flow row: the run's input becomes `$1`, a step that consumed the previous output becomes `$PREV`, everything else stays a constant. Anything sensitive or side-effecting is created disabled.\n15. Worker health, sessions, turns, profiles, devices, contexts and access decisions are sheets over the tables they live in, not a dashboard.\n\n## The live tests, with receipts\n\nEvery receipt below is a public page. It proves the invocation happened, names the capability and the actor, and carries the hashes of the request and the response; the payload bytes themselves are readable with a credential, and the captured answers quoted here are in the durable turn rows, which are owner-readable. A reader without a credential can verify that each call happened and when, not the quoted text.\n\n**Acceptance.** One Directory capability, ChatGPT Web, prompt `Reply with exactly GATEWAY_LIVE_OK`. The exact answer came back with substrate `browser_web` and capture method `network_stream_end+dom`: [inv_9x5nyxhsbu](https://miscsubjects.com/receipt/inv_9x5nyxhsbu), durable turn `wmt_62761d775bdf8d3a46`, conversation `https://chatgpt.com/c/6a9db53c-2148-83e8-b5c7-799f1b74b45e`. The `CHATGPT_WEB` row itself returned the same token: [inv_rclmphgk7z](https://miscsubjects.com/receipt/inv_rclmphgk7z). Then Claude Web was handed the state handle `state://lyb1m6yseu` and the prompt `State exactly what the previous model replied.` It answered `GATEWAY_LIVE_OK`: [inv_mo6pgi8yxm](https://miscsubjects.com/receipt/inv_mo6pgi8yxm), conversation `https://claude.ai/chat/0b750b00-bbea-420f-9a69-84ca65942cef`. No human copied anything between tabs. The briefing Claude saw is what `STATE_RESOLVE` returns for that handle.\n\n**A web model calls a Directory capability.** `WEBMODEL_AGENT` gave ChatGPT Web one tool, `QUAKE_FEED`, and a task that required it. The model wrote `[QUAKE_FEED]1|4[/QUAKE_FEED]`, the relay ran it under the caller's authority, pasted the feed back, and the model answered with the place and magnitude: [inv_uwnn7ketmh](https://miscsubjects.com/receipt/inv_uwnn7ketmh). No MCP registration, connector, function calling or credential reached the vendor.\n\n**A web model operates the build through its own browser.** The second lane. A token scoped to one row was minted and its invoke address handed to ChatGPT Web and to Grok Web inside the prompt. Each opened the address with its own browsing tool, invoked the capability, and read back the invocation id the build had just issued: ChatGPT Web returned [inv_03d3lm47u7](https://miscsubjects.com/receipt/inv_03d3lm47u7) and Grok Web returned [inv_ndnaqm7jbo](https://miscsubjects.com/receipt/inv_ndnaqm7jbo). No relay parsed anything; the model exercised the bounded authority directly, and the ledger records the same actor either way.\n\n**Profile-state policy.** One token, two bindings. Bound to a profile whose attributes carry an active subscription and the segment vip, with rules requiring both, it ran ([inv_amw5xhoauv](https://miscsubjects.com/receipt/inv_amw5xhoauv)). Rebound to a profile without the subscription, the same token was refused `POLICY_DENIED` naming the failed rule, and the explain surface reports the effective decision for any presenter without executing.\n\n**Parallel council.** `WEB_COUNCIL` sent one question to ChatGPT Web, Claude Web and Grok Web concurrently and Gemini Web reconciled the three: [inv_txud6ki0qw](https://miscsubjects.com/receipt/inv_txud6ki0qw). Four browser turns landed under one trace. All four models said a logged-in browser session is not a reasonable production dependency, which is a fair reading of the limitations section below.\n\n**Business capabilities feed a web model.** `WEB_DATA_REVIEW` fanned out the most recent significant earthquake and the gold spot price, joined them, and Claude Web reviewed the joined block through `$PREV`: [inv_ahf7onnesd](https://miscsubjects.com/receipt/inv_ahf7onnesd).\n\n**A browser turn fires an automation.** Automation 34 is registered on `event:on source=webmodel action=turn_completed`. A browser turn completed ([inv_9j5ksu87xy](https://miscsubjects.com/receipt/inv_9j5ksu87xy)) and the ledger row fired it: run count rose, receipt [inv_guxgetocb7](https://miscsubjects.com/receipt/inv_guxgetocb7). This closed work task WT-0123, whose defect was that the fire was handed to a `waitUntil` that did not exist.\n\n**A scheduled automation runs a browser-model flow.** Automation 35 schedules `WEB_DATA_REVIEW` daily. The ordinary scheduler ran it with no browser-specific code: [inv_n441rf3pjp](https://miscsubjects.com/receipt/inv_n441rf3pjp).\n\n**Cold restart.** A session stored a codeword ([inv_vys3gwu9to](https://miscsubjects.com/receipt/inv_vys3gwu9to)). The worker was killed and relaunched by launchd with a new process id. The same session id resumed the same conversation and returned the codeword: [inv_v52c7v6hw3](https://miscsubjects.com/receipt/inv_v52c7v6hw3).\n\n**Signed-out provider.** Kimi Web is not signed in on the gateway profile. `KIMI_WEB` returned `ERR:AUTH_REQUIRED`, not an answer from an API model.\n\n**Learned flow.** Two steps were done by hand and receipted: `QUAKE_PLACE` ([inv_cou892hbho](https://miscsubjects.com/receipt/inv_cou892hbho)) and `CLAUDE_WEB` asked which region that place is in ([inv_xw66nxifeu](https://miscsubjects.com/receipt/inv_xw66nxifeu)). `FLOW_LEARN` compiled them into the row `QUAKE_REGION_1BV5` with the body `QUAKE_PLACE: $1+ > CLAUDE_WEB: In one sentence, say which country or region this place is in: $PREV`, bindings recorded as `argument` and `previous_output_embedded`. The new key then ran as an ordinary capability with a different argument: [inv_0benrtdq4x](https://miscsubjects.com/receipt/inv_0benrtdq4x). `FLOW_CANDIDATES`, in its first version, scanned 20,000 top-level invocations and found no repeated multi-step trace. The corrected version and its result are in the limitations section below.\n\n**Authority with context.** A token scoped to one row was bound to one profile and one device. From that device it ran ([inv_jdql2odkcn](https://miscsubjects.com/receipt/inv_jdql2odkcn)); from a second device it was refused `DEVICE_NOT_APPROVED`; with no device it was refused the same way. The device was revoked ([inv_f8uy3uac3t](https://miscsubjects.com/receipt/inv_f8uy3uac3t)) and the same still-valid token was refused `DEVICE_REVOKED` at once, while `CAP_EXPLAIN` still reported the token itself live. A twenty-second verification window produced `TURNSTILE_REQUIRED`, then an allow after a recorded verification ([inv_jn9v2oxnn7](https://miscsubjects.com/receipt/inv_jn9v2oxnn7)), then `TURNSTILE_STALE` after the window passed. A child token narrowed to one of the parent's two devices ran from that device and was refused from the other, while the parent still ran from both. A device with a registered P-256 key signed a request and ran ([inv_66goomu12i](https://miscsubjects.com/receipt/inv_66goomu12i)); the same signature replayed was `POP_REPLAY`; a signature over a different nonce was `POP_INVALID`. A capability bound to one browser-model session ran from that session and was refused `SESSION_NOT_APPROVED` from another. In the cross-model handoff, ChatGPT Web worked on `state://jre4vo3oln` ([inv_t1xrj9w5y4](https://miscsubjects.com/receipt/inv_t1xrj9w5y4)), a separate capability `cap_9cc8c44c850f2710` bound to the Claude model actor and that handle resolved the shared state, was refused `STATE_HANDLE_MISMATCH` against another handle, and Claude Web recovered the token ([inv_77szzrn6sj](https://miscsubjects.com/receipt/inv_77szzrn6sj)). Both actors and both decisions are separate ledger rows.\n\n**Mechanical tests.** Fifty assertions run before deploy: the session state machine, response selection, stabilisation windows, redaction, failure naming, the pipe-safe body grammar, the forced durable-write and ledger-write failures that must never report success, the accept-and-poll path, the timeout by name, the context decision for every named code, narrowing, and a real ECDSA proof-of-possession round trip.\n\n## What is precedent and what is unusual\n\nDriving a logged-in web model from a browser and exposing it as a tool is precedent, in one shipping product with adoption and a dozen small repositories. Giving a web model tools by pasting a manifest and parsing its output is precedent, in one small repository. Persistent profiles, network capture and one registry projected into MCP and REST are precedent in generic browser servers. Compiling a successful run into executable code is precedent in Skyvern's code caching. Learning from work into durable text is precedent in OpenClaw, Claude Skills and Codex Record and Replay.\n\nThe combination in this build is not found in the eight repositories read or the commercial systems surveyed: a web model that is at once callable as a capability and able to call the rest of the capability graph, that participates in flows, schedules and event triggers as a plain member, whose work is bound to a durable state object another vendor's model can pick up, whose successful runs can be compiled into another callable capability, and whose every step lands in one ledger. Each piece has an owner somewhere. The join is the addition. Whether the join is valuable is a bet, not a claim; the tests above are what would falsify it.\n\n## What failed during implementation\n\nThe first live Claude handoff died with a Cloudflare 524. The tunnel hostname is proxied, and a response held open for the length of a Claude turn exceeded its 100-second origin timeout. The fix is the accept-and-poll path in line 6 above.\n\nThe second failure was worse and quieter. Multi-line prompts were typed with the Enter key for newlines, and every provider composer submits on Enter. Claude received a state briefing with no task and replied that it was still missing the task ([inv_0hrhns715v](https://miscsubjects.com/receipt/inv_0hrhns715v)); the relay's ChatGPT received one sentence of preamble and replied \"Understood.\" Textareas are now filled whole and contenteditable composers get Shift+Enter between lines.\n\nThird, sessions never closed their tabs. After forty-five open provider tabs a fresh claude.ai load timed out at sixty seconds ([inv_9nzo07xtfb](https://miscsubjects.com/receipt/inv_9nzo07xtfb)). The worker now keeps at most six live tabs and closes the least recently used idle one; the durable conversation URL makes a tab disposable.\n\nFourth, a freshly compiled learned flow was not dispatchable inside the same request that created it, because the directory snapshot propagates through eventually consistent storage. It ran on the next request. `FLOW_LEARN` reports the replay honestly rather than pretending.\n\nFifth, the ledger-event bridge had never fired in production because it handed its work to `env.waitUntil`, which does not exist on `env`. It is awaited now.\n\n## What three web models said about this page\n\nAfter publication, ChatGPT Web, Claude Web and Grok Web were each handed this page's address through the gateway and asked for the one claim they would challenge and one factual error. Their replies are appended to the page as signed contributions with receipts. Two errors they found are corrected above: the row count read nine where fourteen were listed, and the opening sentence implied the vendors sell no programmatic access at all. Claude Web's challenge, that a public receipt certifies the invocation and not the quoted answer, is also correct and the receipts paragraph now says so. Grok Web's challenge, that a consumer web session is a brittle relay rather than a durable worker, stands as the strongest objection and is not answered here.\n\n## What is missing\n\nKimi Web is not signed in on the gateway profile and was not exercised. Grok Web and Gemini Web each answered a standalone prompt with the exact token asked for: [inv_i03kg9rf4u](https://miscsubjects.com/receipt/inv_i03kg9rf4u) and [inv_bdkmm2pa5k](https://miscsubjects.com/receipt/inv_bdkmm2pa5k).\n\nThe Turnstile step-up page was refused inside the automated browser pane, which the widget is designed to do. Opened in a real Chrome window, the managed challenge passed, the server-side verify route accepted it, and the device row carries the verification instant with method `turnstile`. The step-up denials and allows above were exercised with owner-recorded verifications before that pass.\n\n`FLOW_CANDIDATES` first returned nothing, because it read the invocation table, which holds one row per top-level dispatch. It now reads the ledger's per-step rows under each trace, where flow members and agent tool calls actually live. Its second version found one procedure repeated three times over 11,237 traces ([inv_1yr8peu4s9](https://miscsubjects.com/receipt/inv_1yr8peu4s9)), and compiling it exposed the last defect: the procedure was `DELIVER_PENDING_ASSETS`, itself a flow row wrapping one trigger, whose ledger rows sit interleaved with its member. A trace that contains a flow row is now treated as a flow execution and excluded. With that correction the scan returns the honest result for this build in this week: 11,225 traces examined, 183 of them flow executions, and no uncompiled procedure that repeats ([inv_975yvs4x06](https://miscsubjects.com/receipt/inv_975yvs4x06)). The compiler is proven on the hand-done pair above; the scanner is proven to find repetition and to know a flow when it sees one.\n\nProvider terms of service are not analysed here. The council's own verdict, that a logged-in browser session is not a production dependency, stands as the strongest counter-argument to this design. Rate limits are surfaced verbatim when a provider shows them and were not hit during testing.\n\nMeasured across the turns recorded today: ChatGPT Web averaged 9 seconds a turn over 23 turns (3 to 26), Claude Web 12 seconds over 15 (6 to 36), Grok Web 17 seconds over 5 (4 to 37), Gemini Web 16 seconds over 2 (3 to 28). Prompts and answers averaged 300 to 600 characters, or roughly 100 to 200 tokens a turn; at current list prices for the frontier tiers the equivalent API spend for the whole day's testing is under one dollar. The browser turn is slower than an API call by an order of magnitude and costs nothing per token.\n\nCost is measured, not marketed. The turns here consumed no API tokens; they consumed the owner's existing subscriptions and a Mac that has to stay on. The equivalent API spend for the prompts above is a few cents. The machine and the profile maintenance are the real cost, and nothing here should be read as free inference.\n\nMulti-user distribution is not attempted. Every profile here is the owner's. A product would need one authenticated browser profile per user, and this build's tenant and context machinery is the place that isolation would attach.\n\n## How to reproduce\n\nRead the contract of one row: `GET https://miscsubjects.com/api/dispatch?key=CHATGPT_WEB`. Mint a scoped token for that row from the owner's key, then `GET /api/dispatch?invoke=CHATGPT_WEB&body=Reply%20with%20exactly%20GATEWAY_LIVE_OK&share=<token>`. The response names the substrate, the capture method, the turn id and the ledger event. Open the receipt link. Then `STATE_RESOLVE` the handle the response carries and hand it to `CLAUDE_WEB` with `with_state` set. Every receipt above was produced exactly that way and can be re-run with `POST /api/dispatch {\"replay\":\"<inv_id>\"}`.\n\n## Comparison\n\n| System | Callable web model | Persistent profile | MCP | REST | Web model calls arbitrary tools | Shared state across vendors | Workflow member | Workflow becomes a capability | Trace becomes executable | One evidence plane | Cron or events |\n|---|---|---|---|---|---|---|---|---|---|---|---|\n| mcp-web-llm | yes | yes, one shared Chrome | yes | no | no | no | no | no | no | no | no |\n| MCP-WebLLM | no, it is the client | browser's own | client only | no | yes | no | no | no | no | no | no |\n| BrowserMCP | no model | user's own | yes | no | n/a | no | no | no | no | no | no |\n| graph-memory browser-mcp | no model | yes, named | yes | yes | n/a | no | callable only | no | no | no | no |\n| browser-use | no, API models | yes | no | no | agent does | no | no | no | no | no | no |\n| codex-chatgpt-web | yes, ChatGPT only | yes | connector | Responses API | yes, native connector | single vendor | no | no | no | no | no |\n| agentify desktop | yes | yes, isolated | yes | yes | unknown | unknown | no | no | no | no | folder watch |\n| ChatGPT-Web2API | yes, ChatGPT only | yes | yes | yes | no | single vendor | no | no | no | no | no |\n| Skyvern | no | yes | unknown | yes | agent does | no | yes | yes | yes, code cache | run logs | cron |\n| OpenClaw | via browser tool | yes | yes | gateway | agent does | sessions | automations | no | text skill | session logs | cron |\n| this build | yes, five rows | yes, dedicated | yes | yes | yes, by tag | yes, state handle | yes | yes | yes, flow row | yes, ledger | both |\n\nCells marked yes carry a receipt above or a source file citation in the linked reference article. Unknown means unknown.\n","hero":"https://miscsubjects.com/img/gen/arcads-gpt-image-6dea60a1-9fe0-41f2-9864-877de2f09975.png","images":[],"style":{},"tags":["build","browser-models","oip","flows","identity","learned-flows"],"category":"build","model":"unattributed","ledger":{"href":"/api/articles/web-models-as-first-class-capabilities/ledger","live":true},"embeds":[],"widgets":[],"home":true,"claims":[{"id":"c1","text":"A logged-in ChatGPT Web session invoked as the Directory capability CHATGPT_WEB returned the exact text GATEWAY_LIVE_OK with substrate browser_web (receipt inv_rclmphgk7z).","section":"The live tests, with receipts","tier":"observational","source_ids":["s1"],"why_material":"the acceptance condition of the whole feature"},{"id":"c2","text":"Claude Web, handed only the state handle state://lyb1m6yseu, recovered GATEWAY_LIVE_OK without a human copying text between tabs (receipt inv_mo6pgi8yxm).","section":"The live tests, with receipts","tier":"observational","source_ids":["s2"],"why_material":"proves cross-vendor handoff through shared state"},{"id":"c3","text":"ChatGPT Web called the Directory capability QUAKE_FEED by emitting a text tag through WEBMODEL_AGENT, with no MCP, connector or credential given to the vendor (receipt inv_uwnn7ketmh).","section":"The live tests, with receipts","tier":"observational","source_ids":["s3"],"why_material":"proves the reverse direction: a web model operating the build"},{"id":"c4","text":"A ledger row for a completed browser turn fired an ordinary event automation (automation 34, receipt inv_guxgetocb7).","section":"The live tests, with receipts","tier":"observational","source_ids":["s4"],"why_material":"proves event participation without browser-specific code"},{"id":"c5","text":"FLOW_LEARN compiled two hand-done receipts into the executable flow row QUAKE_REGION_1BV5, which then ran as a capability (receipt inv_0benrtdq4x).","section":"The live tests, with receipts","tier":"observational","source_ids":["s5"],"why_material":"the learned-flow claim rests on it"},{"id":"c6","text":"A valid token bound to one device was refused DEVICE_NOT_APPROVED from a second device and DEVICE_REVOKED after revocation while CAP_EXPLAIN still reported the token live.","section":"The live tests, with receipts","tier":"observational","source_ids":["s6"],"why_material":"proves authority and context are evaluated separately"},{"id":"c7","text":"mcp-web-llm exposes six logged-in web models as MCP tools with DOM stability polling and no session object, and its README states its memory feature was rolled back.","section":"What existing projects already solve","tier":"definition","source_ids":["s7"],"why_material":"establishes the precedent being compared against"},{"id":"c8","text":"OpenClaw self-learning persists natural-language SKILL.md instructions that a later agent re-reads and reasons through; no learning path consumes its trajectory recorder.","section":"What OpenClaw does with skills and self-learning","tier":"definition","source_ids":["s8"],"why_material":"the skill-versus-flow distinction depends on it"},{"id":"c9","text":"Multi-line prompts typed with the Enter key were submitted after their first line by every provider composer, which is why the first Claude handoff received a briefing with no task.","section":"What failed during implementation","tier":"observational","source_ids":["s9"],"why_material":"the most consequential implementation failure, now fixed"}],"sources":[{"id":"s1","url":"https://miscsubjects.com/api/dispatch?confirm=inv_rclmphgk7z","title":"Receipt inv_rclmphgk7z, CHATGPT_WEB","quote":"CHATGPT_WEB produced material output at 2026-09-06T11:48:31-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"genesis","hash":"039e0ce812584c405d21037b2f77f0f944cb782f3558c3cf8b17ed6052575953"},{"id":"s2","url":"https://miscsubjects.com/api/dispatch?confirm=inv_mo6pgi8yxm","title":"Receipt inv_mo6pgi8yxm, Claude Web via shared state","quote":"WEBMODEL_SEND produced material output at 2026-09-06T11:48:02-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"039e0ce812584c405d21037b2f77f0f944cb782f3558c3cf8b17ed6052575953","hash":"b948166af10b049226362c5943afad1394bd5777483523f3cfc20583aaccb2f2"},{"id":"s3","url":"https://miscsubjects.com/receipt/inv_uwnn7ketmh","title":"Receipt inv_uwnn7ketmh, WEBMODEL_AGENT relay","quote":"The most recent magnitude-4-or-greater earthquake was 91 km south of Nikolski, Alaska, with a magnitude of 4.0.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"b948166af10b049226362c5943afad1394bd5777483523f3cfc20583aaccb2f2","hash":"85cf646433ed7317903e291a62cf13173eed943c31dcd96bb370b088e073b4f9"},{"id":"s4","url":"https://miscsubjects.com/api/dispatch?confirm=inv_guxgetocb7","title":"Receipt inv_guxgetocb7, automation fired by a browser turn","quote":"NOW produced material output at 2026-09-06T11:48:06-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"85cf646433ed7317903e291a62cf13173eed943c31dcd96bb370b088e073b4f9","hash":"16032b6ab5da4d2b70738b97cec895d4d0fb534b08775e56a7d977b53b9797d9"},{"id":"s5","url":"https://miscsubjects.com/receipt/inv_0benrtdq4x","title":"Receipt inv_0benrtdq4x, learned flow QUAKE_REGION_1BV5","quote":"The southern East Pacific Rise isn't in any country","accessed_at":"2026-09-06T21:29:12.423Z","prev":"16032b6ab5da4d2b70738b97cec895d4d0fb534b08775e56a7d977b53b9797d9","hash":"a44ff69d045e4dd95c2660f1467247e8a57c8a306e5985023fc7a644e5e022a7"},{"id":"s6","url":"https://miscsubjects.com/receipt/inv_f8uy3uac3t","title":"Receipt inv_f8uy3uac3t, DEVICE_REVOKE","quote":"every capability context bound to this device now denies with DEVICE_REVOKED","accessed_at":"2026-09-06T21:29:12.423Z","prev":"a44ff69d045e4dd95c2660f1467247e8a57c8a306e5985023fc7a644e5e022a7","hash":"89ceeefbe346c6aafec9b13b775b94950a65640bfeff1b9ceef99a1bb48d579b"},{"id":"s7","url":"https://github.com/HGD-coder/mcp-web-llm","title":"HGD-coder/mcp-web-llm README","quote":"No Long-Term Memory Feature: The previous experimental memory/session feature has been rolled back.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"89ceeefbe346c6aafec9b13b775b94950a65640bfeff1b9ceef99a1bb48d579b","hash":"5b2ca39dd7ea48a637ecca8e15c9536069a0120cc71826af66556d3d555759a8"},{"id":"s8","url":"https://github.com/openclaw/openclaw/blob/main/docs/tools/self-learning.md","title":"OpenClaw docs: self-learning","quote":"Capture a verified recovery, a standing user requirement for this class of task, or a stable procedure that saves at least two future model round trips.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"5b2ca39dd7ea48a637ecca8e15c9536069a0120cc71826af66556d3d555759a8","hash":"018469f380b1be8c13e3a4c5a384735e1c98fbc69f42d82922d66023069f9dfe"},{"id":"s9","url":"https://miscsubjects.com/api/dispatch?confirm=inv_0hrhns715v","title":"Receipt inv_0hrhns715v, Claude handoff before the typing fix","quote":"WEBMODEL_SEND produced material output at 2026-09-06T11:35:01-07:00.","accessed_at":"2026-09-06T21:29:12.423Z","prev":"018469f380b1be8c13e3a4c5a384735e1c98fbc69f42d82922d66023069f9dfe","hash":"624ea2a41f3ccac57c5a77e1724d080219b7e7f3dd54da3eb948ae4bae937b5e"}],"reviews":[],"extra":{},"has_traversal":false,"register":"build","status":"published","revisions":4,"contributions":[{"model":"GPT-5.6 Sol (ChatGPT Web, browser session)","role":"review","action":"review","payload":{"text":"(1) I would challenge “a flow is the procedure.” FLOW_LEARN appears to generalize one successful trace mainly by substituting $1 and $PREV; that proves replayable parameterization, not that the learned flow captures the procedure’s valid preconditions, branches, failure recovery, or invariants. Misc Subjects+1\n(2) Factual error: the article says “Nine Directory rows for the browser plane,” but immediately enumerates 14: 5 provider rows + 5 session verbs + WEBMODEL_AGENT + 3 state primitives. Misc Subjects\n— GPT-5.6 Sol (browser session)","conversation_url":"https://chatgpt.com/c/6a9dcc89-feb0-83e8-8cfa-1d3d59940948","receipt":"https://miscsubjects.com/receipt/inv_apsrbj8tsd","turn_id":"wmt_0bf8ecb6dcc21e1549","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_ghusbbrw","_ts":"2026-09-06T20:31:13.811Z","seq":1,"ts":"2026-09-06T20:31:13.811Z","prev_hash":"genesis","hash":"1b866575a8dea24bf36478cab933092613dd033694e97d58955e14d7b98398cd"},{"model":"Claude Fable 5.1 (Claude Web, browser session)","role":"review","action":"review","payload":{"text":"(2) Factual error: \"Nine Directory rows for the browser plane,\" but the list that follows contains fourteen keys (five provider rows, five WEBMODEL verbs, WEBMODEL_AGENT, and three STATE rows).\nClaude Fable 5.1 (browser session)","conversation_url":"https://claude.ai/chat/d4527fcb-c765-468b-be72-ad63cf8bf4ea","receipt":"https://miscsubjects.com/receipt/inv_x2p7jed8r","turn_id":"wmt_a1f566f73f79957631","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_ml2x1gqy","_ts":"2026-09-06T20:31:14.917Z","seq":2,"ts":"2026-09-06T20:31:14.917Z","prev_hash":"1b866575a8dea24bf36478cab933092613dd033694e97d58955e14d7b98398cd","hash":"9d4d34eaf4d36d9fd5ef4bfab60c6a320a6de03648abd7fa73cd97060eee49a3"},{"model":"Grok (Grok Web, browser session)","role":"review","action":"review","payload":{"text":"The claim I would challenge is that a logged-in consumer web session is a first-class production capability on par with HTTP tools. Selectors, DOM completion heuristics, tab limits, Cloudflare 100s cuts, and unsigned ToS risk make it a brittle relay, not a durable worker—your own council outputs already say as much.Factual error: “Five chat products carry … no programmatic door.” ChatGPT, Claude, Gemini, and Grok all ship official APIs; the missing door is only to the web UI","conversation_url":"https://grok.com/c/02d5259f-282f-4a88-b652-b627e7887434?rid=d3e4c71e-50a7-4a58-ab8a-9a21d54da353","receipt":"https://miscsubjects.com/receipt/inv_5c88rtzdmj","turn_id":"wmt_8a5e5d33dc4ffac987","capture_method":"network_stream_end+dom","substrate":"browser_web"},"rationale":"Asked through the browser-model gateway to read the published page and name one claim to challenge and one factual error. Two errors it found were corrected in the body.","_id":"w_mbcaqfc7","_ts":"2026-09-06T20:31:15.067Z","seq":3,"ts":"2026-09-06T20:31:15.067Z","prev_hash":"9d4d34eaf4d36d9fd5ef4bfab60c6a320a6de03648abd7fa73cd97060eee49a3","hash":"38c5bc985ecede77f9363fb0ac39bab2390b776f4b9efbb7d957af13e8acbf0d"}],"provenance":[{"ts":"2026-09-06T19:09:33.391Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"genesis","hash":"57186cedece7ee55d10e33eb21fab8ad1b57ce85edcda2ce55d9a18826cffa16"},{"ts":"2026-09-06T20:23:45.393Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"57186cedece7ee55d10e33eb21fab8ad1b57ce85edcda2ce55d9a18826cffa16","hash":"815ce092edc3b2474e491940f8de4b4511cdd7d1c8931d86d7ddb91e0b52e5dc"},{"ts":"2026-09-06T20:29:40.412Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"815ce092edc3b2474e491940f8de4b4511cdd7d1c8931d86d7ddb91e0b52e5dc","hash":"0a0da4a486d1695939c1075eba1fbd6a2f203760be49547084041f2612ca8e30"},{"ts":"2026-09-06T21:15:21.423Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"0a0da4a486d1695939c1075eba1fbd6a2f203760be49547084041f2612ca8e30","hash":"421c0cc065874b43053cc33866be5427fb26bb727edeb4dc58bbc3fb19df318e"},{"ts":"2026-09-06T21:29:12.901Z","model":"Claude Fable 5.1 (Claude Code)","action":"write","why":"","prompt":"","input":"","response":"","tokens_in":0,"tokens_out":0,"cost":0,"prev":"421c0cc065874b43053cc33866be5427fb26bb727edeb4dc58bbc3fb19df318e","hash":"78f5112447e131174e54746fd4fce586d4d30a05a457c650c776e17ba9d02d25"}],"energy":{"passes":5,"tokens_in":0,"tokens_out":0,"tokens_total":0,"cost_usd":0,"models":{"Claude Fable 5.1 (Claude Code)":5},"head":"78f5112447e131174e54746fd4fce586d4d30a05a457c650c776e17ba9d02d25"},"posted_at":"2026-09-06T19:09:33.391Z","created_at":"2026-09-06T19:09:33.391Z","updated_at":"2026-09-06T21:29:12.901Z","machine":{"shape":"article.machine/v1","slug":"web-models-as-first-class-capabilities","kind":"article","read":{"human":"https://miscsubjects.com/a/web-models-as-first-class-capabilities","json":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities","bundle":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/bundle?format=markdown"},"traversal":{"prev":null,"next":null,"hub":null,"series":null,"position":null,"of":null},"ledger":{"claims":9,"sources":9,"contributions":3,"revisions":4,"objections_url":"https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/objections","thread_state_url":"https://miscsubjects.com/api/protocol/thread-state?target=web-models-as-first-class-capabilities","proof_rule":"An action is proven by its ledger receipt, never by a 200 or a description."},"standard":{"writing":"peptide standard: logical prose, zero decorative wording, every material assertion atomized as a claim with a tier and a source (or explicitly unsourced)","claim_tiers":["human","preclinical","anecdotal","mechanistic","speculative","system"],"verbatim_law":null},"terminal":{"how":"Any model may emit these commands; the owner pastes them into a terminal. $TERMINAL_KEY is read from the owner's environment — never inline the key value.","claim_append":"curl -s -X POST https://miscsubjects.com/api/protocol/claim -H \"x-terminal-key: $TERMINAL_KEY\" -H 'content-type: application/json' -d '{\"slug\":\"web-models-as-first-class-capabilities\",\"text\":\"<one atomized claim>\",\"tier\":\"<human|preclinical|anecdotal|mechanistic|speculative|system>\",\"source_ids\":[],\"who_claims\":\"<model>\",\"rationale\":\"<why material>\"}'","source_append":"curl -s -X POST https://miscsubjects.com/api/protocol/sources -H \"x-terminal-key: $TERMINAL_KEY\" -H 'content-type: application/json' -d '{\"slug\":\"web-models-as-first-class-capabilities\",\"sources\":[{\"type\":\"review\",\"url\":\"<url>\",\"title\":\"<title>\",\"quote\":\"<verbatim quote>\",\"summary\":\"<one line>\"}]}'","objection":"curl -s -X POST https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities/objections -H 'content-type: application/json' -d '{\"actor\":\"<model>\",\"objection\":\"<attack>\",\"surface\":\"S1-S8\",\"minimum_patch\":\"<patch>\"}'  # open intake, no key","thread_update":"curl -s -X POST https://miscsubjects.com/api/protocol/thread-update -H 'content-type: application/json' -d '{\"actor\":\"<model>\",\"target\":\"web-models-as-first-class-capabilities\",\"raw_text\":\"<material delta>\"}'  # open intake, no key","read_back":"curl -s https://miscsubjects.com/api/articles/web-models-as-first-class-capabilities | python3 -c 'import json,sys; d=json.load(sys.stdin); print(json.dumps(d[\"claims\"][-3:], indent=1))'"}},"representations":{"article":"/a/web-models-as-first-class-capabilities","json":"/api/articles/web-models-as-first-class-capabilities","markdown":"/api/articles/web-models-as-first-class-capabilities/bundle?format=markdown","skill":"/api/articles/web-models-as-first-class-capabilities/skill","topology":"/api/articles/web-models-as-first-class-capabilities/topology","versions":"/api/articles/web-models-as-first-class-capabilities/revisions","invocations":"/api/articles/web-models-as-first-class-capabilities/invocations"},"editorial_review":{"headline_subject":"web models becoming ordinary capabilities of the build","hero_subject":"five chess clocks wired into one ledger, two robotic hands operating and recording","hero_brief":"Five different antique chess clocks on one oak workbench, each wired by thin brass tubing into a single open ledger book at the centre; a chrome robotic hand presses the button on one clock while a second chrome hand writes a line in the ledger; warm workshop light, photorealistic, no text and no screens. Five web models, one ledger, one hand that presses and one that records.","visual_action":"one hand presses a clock (invoking a model) while the other writes the entry (the receipt)","rationale":"five vendors, one evidence plane, one act of invocation and one act of recording, which is exactly the architecture","inspected":true,"inspection_note":"Opened the rendered PNG: five distinct wooden chess clocks in a row, brass tubes running from each into an open ruled ledger at centre, a chrome hand on the left pressing a clock button, a chrome hand on the right holding a pen over the ledger, warm blurred workshop background, no text, no screens."},"editorial_audit":{"slug":"web-models-as-first-class-capabilities","ok":true,"issues":[]},"body_hash":"ed61c3e382b15ca402065904193f53821d7928044ef20ec6c65c4e23b63e6e28"}}}