type: meta

slug: sol-web-harness

base_branch: wt/gptarm

gate0_mode: strict

preset: default

base_branch_note: origin/main does NOT contain modules/gptbridge/browser.py, chat.py or ask_gpt.py — that work is unlanded on wt/gptarm. Verified with git diff --stat origin/main..wt/gptarm. The plan MUST build on and land into wt/gptarm, never onto main.

land_mode: pr


type: goal

text: Ship a CLI wrapper that opens Claude Code driven by the owner's free ChatGPT web session (Sol), so the real agentic tool loop — file edits, command execution, permissions — runs on chat quota instead of paid API tokens.


type: methodology

text: Measure before speccing: every load-bearing claim in the design doc is a live probe against the real ChatGPT session, never an estimate. Build in the smallest number of modules that pass the deletion test. Verify layers this design does not own (ccr translation) against a stub before touching the browser.


type: intent

text: ask-gpt is one-shot and has no agency. Every agentic CLI already owns a tool loop and can be pointed at an OpenAI-compatible endpoint. So do not write an agent — make the browser conversation look like an OpenAI chat-completions provider, and let Claude Code be the agent.


type: direction

verbatim: you keep spreading files across all computer, all files it must live ~/.overdeck or ~/Projects/overdeck/


verbatim: just put it in my #$PATH i dont care about landing. i need to use it.


verbatim: i ened to use it. i asked 8 hours ago. its TOO LONG FOR A WRAPPER


verbatim: Use workspace-write. Run it only from a project directory you are comfortable exposing. Do not run it from your home directory or filesystem root. Keep important projects under version control. Do not send passwords, API keys, recovery codes, or MFA codes through ChatGPT. Read all commands before allowing ChatGPT to run them (set permissions to 'Always Ask'). Stop the server when it is not being used.


note: User settled the harness fork via AskUserQuestion: Claude Code via ccr. Do NOT re-ask which harness.


note: ChatGPT renders fenced code as DOM nodes — fence markers are ABSENT from innerText. Parse tool calls from `pre code` elements in the reply turn; a markdown-fence regex silently finds nothing.


note: Measured 2026-08-08: 145KB turn-1 send accepted, answered 16.0s, exactly one valid JSON tool call; turn 2 after OBSERVATION also one valid block. Warm turn 10-15s, cold start 27.1s.


note: Measured 2026-08-08: chat.py set_effort works on a conversation that already has a turn, and re-asserting the same value is idempotent — so effort is re-asserted before every send rather than once at open.


note: ccr-up.sh <slug> uses the slug ONLY to name its daemon log; it validates nothing against a provider list, so `bash ccr-up.sh solweb` is safe before the provider entry exists.


type: deferred

text: A real per-adapter concurrency cap in the harness engine

reason: presets/adapters.json has additionalProperties:false and no concurrency key; the engine pool is run-level (src/engine/scheduler.js). Adding one is an engine-contract change, deliberately not bundled with this adapter. The one-seat-per-wave limit is stated in the spec and exposed via /healthz queue instead.


type: gated

category: architecture

needs: Confirm ccr's Anthropic->OpenAI translation forwards `tools` through to a bare provider (no transformer key) and accepts a synthesized tool_calls response.

why: This is the one layer the design owns neither side of. A failure changes the ccr provider entry, not the architecture.

default: proceed

status: RESOLVED

answer: proceed

resolved_by: assistant

source: author

resolution_note: Not a user decision: the record itself states a failure changes the ccr provider entry, not the architecture, so abort was never a real option. Task 3 resolves it empirically with a stub before Task 9 registers the real provider.


type: anchor

what: the contract wrappers/sw.sh must satisfy exactly — exit codes 0/124/2/3/75 are a hard floor


what: existing browser driver: wait_ready, use_chat_surface, set_effort, type_prompt, send, harvest


type: task

seat: coder

tier: medium

desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 1 (protocol.py) for the full contract. Render the tool-call contract plus tool schemas into a preamble; parse reply code blocks into ToolCall/Final/Malformed. Acceptance: modules/gptbridge/python3 -m pytest modules/gptbridge/tests/test_protocol.py -q. Commit only: modules/gptbridge/protocol.py modules/gptbridge/tests/test_protocol.py.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 2 (translate.py) for the full contract. Reconcile stateless messages[] against the stateful browser conversation: session keying, Registry, prefix-diff delivery, turn-envelope rendering. Acceptance: modules/gptbridge/python3 -m pytest modules/gptbridge/tests/test_translate.py -q. Commit only: modules/gptbridge/translate.py modules/gptbridge/tests/test_translate.py.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 3 (ccr passthrough probe) for the full contract. Prove with a stub server that ccr forwards tools to a bare provider and accepts a synthesized tool_calls response. Acceptance: python3 modules/gptbridge/tests/ccr_passthrough_probe.py. Commit only: modules/gptbridge/tests/ccr_passthrough_probe.py.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 4 (usage-cap detection) for the full contract. Add detect_cap to chat.py against DOM captured from the live account; never guess the selector. Acceptance: modules/gptbridge/python3 -m pytest modules/gptbridge/tests/test_cap_detection.py -q. Commit only: modules/gptbridge/chat.py modules/gptbridge/tests/test_cap_detection.py modules/gptbridge/tests/fixtures/cap_dom.html.


tier: high

desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 5 (solwebd daemon) for the full contract. Serve the ChatGPT web session as an OpenAI-compatible endpoint on 127.0.0.1:8791, bearer-gated, serial, effort re-asserted before every send. Acceptance: modules/gptbridge/bin/solwebd --port 8799 & then curl -sf -H "Authorization: Bearer $(<~/.overdeck/gptbridge/solwebd.token)" 127.0.0.1:8799/v1/models lists the six sol-web ids; unauthenticated returns 401. Commit only: modules/gptbridge/solwebd.py modules/gptbridge/bin/solwebd.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 6 (daemon fake-browser integration test) for the full contract. Drive the full two-step tool loop, replay, correction, cap and streaming paths against a scripted browser, spending no real ChatGPT turn. Acceptance: modules/gptbridge/python3 -m pytest modules/gptbridge/tests/test_solwebd_loop.py -q. Commit only: modules/gptbridge/tests/fake_browser.py modules/gptbridge/tests/test_solwebd_loop.py modules/gptbridge/solwebd.py.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 7 (sw.sh wrapper) for the full contract. Ensure daemon, ensure ccr, run Claude Code foreground under timeout -k 5; status JSON on stdout only; exit codes 0/124/2/3/75 per WRAPPER-CONTRACT.md; --timeout defaults 3600. Acceptance: bash modules/harness/wrappers/sw.sh --health. Commit only: modules/harness/wrappers/sw.sh.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 8 (sw exit-code contract test) for the full contract. Assert every row of the exit-code table is reachable with the daemon and ccr stubbed, and that exit 3 dispatches nothing. Acceptance: bash modules/harness/wrappers/sw-exitcodes.test.sh. Commit only: modules/harness/wrappers/sw-exitcodes.test.sh.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 9 (registration) for the full contract. Append the solweb adapter entry and the ccr provider entry; bind any sol-web-* model to at most one seat per wave. Acceptance: bash modules/harness/test/adapters-schema.sh. Commit only: modules/harness/presets/adapters.json.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 10 (ask-gpt daemon client) for the full contract. Probe /healthz and route through solwebd when up so ask-gpt never fights over profile.lock; direct path unchanged when down. Acceptance: modules/gptbridge/python3 -m pytest modules/gptbridge/tests/test_ask_gpt_routing.py -q. Commit only: modules/gptbridge/ask_gpt.py modules/gptbridge/tests/test_ask_gpt_routing.py.


desc: Read docs/plans/2026-08-08-sol-web-harness.md Task 11 (install live and verify end to end) for the full contract. Install so the runtime loads the new code, then invoke the real installed sw and prove the file it was asked to create exists. Acceptance: sw --workspace "$(mktemp -d)" --task-slug live-check --trust "create hello.txt containing hi" exits 0 and hello.txt contains hi. Commit only: modules/gptbridge/install.sh.


note: Gate g1 RESOLVED empirically 2026-08-08: ccr_passthrough_probe.py printed PASS tools_forwarded=1 tool_calls_accepted=true. A bare provider entry with NO transformer key is correct; do not add one.