# Factory GPT Sol Model — request

audience: AI coding agents first.

status: DONE
task IDs: #1–#8, #12
source request: Owner requested a critical, production-grade Factory integration for ChatGPT Sol: provider `gpt`; efforts `medium|high|xhigh|pro`; default `pro`. Initial attachment-only, `ask-gpt --factory`, and command-JSON ideas were proposals to challenge, not requirements to preserve. Owner selected mediated Pi tools and existing model-ID syntax.

## Goal

Ship installed `factory-gpt` backed by existing Factory Pi loop and local `solwebd`: `coding_agent: pi`, `model: gpt/sol-web-<effort>`, `thinking: off`; Pro default; typed failures, collision-proof sessions, deterministic context accounting, owner-visible trace, no hidden fallback.

Authoritative design: `docs/specs/2026-08-11-factory-gpt-model-design.md`.

## Scope split

Two independently testable request lanes share this contract:

- `docs/plans/2026-08-11-factory-gpt-provider-runtime.md` — gptbridge service control, provider registration/extension, wrapper, protocol/session/context compatibility. Factory may build this lane because it excludes `modules/harness/factory/**`.
- `docs/plans/2026-08-11-factory-gpt-engine-integration.md` — Factory child metadata, typed attempt receipts/retry policy, presets, trace/UI. Authorized non-Factory worker MUST build this lane; Factory self-write protection remains unchanged.

Integrate only after both lane suites pass against same contract. NEVER let two writers share worktree.

## Contract

Target invocation:

```text
factory-gpt [--effort medium|high|xhigh|pro] <adw> <prompt-or-path>
```

Target Factory config:

```yaml
coding_agent: pi
model: gpt/sol-web-pro
thinking: off
```

Required provider models:

```text
gpt/sol-web-medium
gpt/sol-web-high
gpt/sol-web-xhigh
gpt/sol-web-pro
```

Default effort: `pro`. Unsupported effort fails before session creation.

Session wire:

```text
source: PiRequest.session_id
env: OVERDECK_PI_CLIENT_SESSION
value: v1.<base64url-no-padding(SHA-256(UTF-8 session_id))>
header: X-Overdeck-Client-Session
validation: ^v1\.[A-Za-z0-9_-]{43}$
```

Provider failure:

```text
ProviderFailure = {
  kind: capped | unavailable | protocol | context_overflow | timeout | cancelled,
  detail: string,
  retry_after_seconds?: int,
  resume_at?: string
}
```

Context contract:

```text
contextWindow = 120000
maxTokens = 16000
estimated_tokens(text) = len(text.encode("utf-8"))
reject when cumulative_input + cumulative_output + reserved_output > 120000
```

Count exact rendered system/user/tool/schema text, visible assistant output, 16 tokens/message, 8 tokens/tool definition. Initial input ceiling is 104000 only when accumulated output is zero. Billing/cost remains `unavailable`; numeric zero in Pi catalog is schema metadata only.

## Behavior

- Pi remains sole host tool/session authority. Preserve existing `agent_pi.run`, tool schemas, Report correction, process-group timeout, permissions, gates, and `Literal["pi", "claude_code"]`.
- Sol has mediated host influence through enabled Pi tools. NEVER execute final prose or bespoke command JSON.
- Keep `ask-gpt` and `/gpt-orchestrator` unchanged.
- Headerless CCR/`sw.sh`/ask-gpt clients retain content-derived session keys. Factory requests MUST carry valid session header.
- Different Pi sessions remain isolated under byte-identical prompts/tools; retries preserve identity; compaction replay opens fresh browser conversation when required.
- Provider token is command-resolved by `!solwebctl token`; never automatically propagated through argv, prompts, traces, provider dumps, or tool env. Same-user tools can still read state; do not claim strong secret isolation.
- `solwebctl` requires enrolled local account/profile/machine identity and verified daemon PID/start ticks/generation. `ok=true` only for ready, matching, authenticated, uncapped state with requested minimum seats.
- `factory-gpt` ensures minimum one seat. Existing larger pool is accepted; presets never imply seat count.
- Required Factory models are subset of daemon advertisement. `sol-web-instant` remains legacy-only.
- Cap/logged-out/protocol/unavailable failures do not immediate-retry or fallback. Cap receipt includes `resume_at`; Factory has no deferred scheduler.
- Cancellation stops Pi immediately; abandoned browser turn releases seat within `TURN_TIMEOUT=300s`; no later host tool executes.
- `/factory` shows provider/model/effort, session, account/host, queue/active/cap, tool events, protocol/Report repair, typed failure, context estimate, unavailable billing.
- Workstation only. Remote/buildbox/k3s topology fails before dispatch.

## Delivery ownership

Factory agents MUST NOT modify `modules/harness/factory/**`. Keep protection unchanged.

- Provider-runtime lane: Factory worker allowed only files named by its lane request.
- Engine-integration lane: authorized non-Factory worker.
- UI changes MUST obey `.claude/skills/od-ui-dev/SKILL.md`; reuse existing deck-ui components/tokens; no new primitive.
- Local infrastructure installs and real installed-entrypoint proof precede landing.
- Landing/deployment follows repo procedure; no hand-written merge/push cascade.

## Out of scope

- `coding_agent: gpt`, `agent_gpt.py`, generic backend abstraction.
- `ask-gpt --factory`, repository ZIP/attachment protocol, action/command JSON.
- Nested `/gpt-orchestrator` or Claude Code/CCR runner as primary path.
- Automatic cap deferral, effort downgrade, provider fallback, remote browser/profile copy.
- Strong same-user token isolation or broader Pi sandbox redesign.
- Factory permission weakening or unrelated refactor.

## Acceptance

Deterministic kill gate MUST pass before Factory behavior mutation:

- installed Pi discovers all four models in default and account-materialized directories;
- bearer auth; session header interpolation/redaction; two identical concurrent sessions stay isolated;
- keepalive → tool call → tool result → final Report;
- exact `429|503|502`, timeout, cancellation, retry count, cap metadata receipt;
- cumulative input+output estimate drives compaction and fresh replay;
- missing billing usage displays unavailable, never measured zero.

Required clean commands:

```text
python3 -m pytest modules/gptbridge/tests/ -q
python3 -m pytest modules/harness/factory/tests/ -q
pnpm --filter web test
pnpm --filter web typecheck
pnpm --filter web build
```

Primary model proof uses scripted fake browser/provider. Raw live Sol probe is forbidden. Installed proof covers symlinks, `solwebctl` identity/readiness, Pi discovery, Pro-default wrapper mapping, and fake-endpoint Factory run.

## Preserved WIP

- Worktree: `/home/user/Projects/overdeck/.worktrees/factory-gpt-model`
- Branch: `wt/factory-gpt-model`
- Base at creation: `2fddbc9da`
- Shared main has unrelated WIP; NEVER copy, restore, stash, clean, or mutate it.
- Design advisor verdict: APPROVED after three repair passes; no owner decisions remain.

## Constraints

- Every changed line traces to this request.
- No stubs, silent fallback, ignored warning, or unmeasured live-model claim.
- Factory phase/retry/permission/trace changes require full Factory suite.
- Deployment must preserve existing headerless Sol consumers and `sol-web-instant`.

## Execution steps

Factory planner owns decomposition inside provider-runtime lane. Main orchestrator owns cross-lane sequencing: provider compatibility contract first; authorized producer integration against proved contract; integrated gates/install; land/deploy. This section defines ownership only—NEVER duplicate planner task graph here.

## Current receipt

- 2026-08-12 canonical deploy clone advanced to `77bb38175`, which contains security fix `b1d445a27`; installed controller matches source byte-for-byte. Installed focused security suite `18 passed`. Provider authenticated/ready as `chatgpt@alex.org.il`; active workload preserved (`queue=9`, `active=1`), so no disruptive restart was performed. Task #12 DONE.
- 2026-08-12 canonical completion: deploy-clone WIP preserved as local branch `wip/deploy-clone-rescue-20260812`, commit `f707aff3` (9 files, 359 insertions/433 deletions); no content discarded. Canonical deploy completed exit 0 and activated current main `77bb38175340e66973d598b0770dec706081c07f`; deploy clone clean and exact `origin/main` match. Exact stale candidate systemd unit `run-p2542832-i253860984.service` was stopped after cgroup/source proof. Canonical deploy-root daemon now ready/authenticated, PID `3858420`, version `77bb38175340e66973d598b0770dec706081c07f`, all four Factory GPT models advertised. Task #2 complete. Deploy emitted pre-existing shim-drift hazards in unrelated worktrees; no live shim drift from main.

- 2026-08-12 security hardening landed `b1d445a27`: unreadable `/proc/<pid>/exe` now yields unverified ownership and is accepted only inside authenticated health flow bound to private token + PID/start ticks/generation; stop remains fail-closed. Regression focused `18 passed`; isolated full provider suite `308 passed, 1 skipped`; candidate installed and authenticated runtime healthy.

- Security review correctly rejected trusting `argv[0]` as executable identity. Final repair treats unreadable `/proc/<pid>/exe` as unavailable evidence while still requiring immutable runtime PID/start ticks plus exact isolated bootstrap, canonical root, and canonical daemon script; spoofable argv executable is explicitly tested. Focused suite: `18 passed`.
- Direct-land source reached `origin/main` at `b8bf5e6d3`. First deploy completed at `cd994e4b`; second canonical deploy was blocked fail-closed because another session left the deploy clone dirty, so that WIP was preserved untouched. Candidate links already resolved to the clean isolated carrier.
- Exact degraded deploy-root generation was stopped through its canonical controller only after PID/start-ticks/argv ownership proof. Installed candidate `solwebctl ensure --seats 1` is now ready, authenticated as `chatgpt@alex.org.il`, advertises all required models, and reports version `b8bf5e6d3ae19e0e6965e391b9ef5828e879e9d0`. Installed Pi lists all four `gpt/sol-web-*` models. Installed wrapper rejects unsupported effort before launch. No raw live-model probe was run.

- Provider Factory run `32acfeed` committed repaired runtime `e79024a1d`; integrated as `74e619832` + `08de78d98`.
- Engine lane commits `05fff831f` + `b52a55303` integrated as `125af8e27` + `e53bcaab1`; add/add test conflict preserved both upstream catalog tests and GPT tests.
- Final isolated carrier: `/home/user/Projects/overdeck/.worktrees/factory-gpt-final`, branch `wt/factory-gpt-final`, based on `origin/main` at `e69233ebf`.
- Provider suite is stable across three consecutive integrated runs: each `272 passed, 1 skipped`. Factory suite: `174 passed`. Deterministic Factory GPT provider gate: `2 passed`.
- `install.sh --dry-run --links-only` is mutation-free. Candidate links are live under `~/.local/bin`; installed `factory-gpt` resolves to this worktree and proves Pro-default usage handling with exit 2 on missing required arguments.
- `OVERDECK_DEPLOY_DRY_RUN=1 packaging/deploy-local.sh` completed without mutation and includes GPT link + Pi provider synchronization.
- Web gates passed from final worktree on remote fleet: 403 tests, typecheck 0 errors/warnings/hints, production build clean. Collector suite passed.
- Independent reviews found six concrete blockers: fresh-turn context double-count, omitted tool-block output accounting, duplicate/malformed Pi provider registration, incomplete daemon command ownership, unbounded/noncanonical POST framing, and typed `/v1/ask` errors collapsing after streaming starts. All six causes are repaired with focused regressions (`26 passed`); full provider suite now passes `283 passed, 1 skipped`; full Factory suite remains `174 passed`.
- Candidate Pi discovery resolves all four `gpt/sol-web-*` models from both default candidate directory and isolated account-composed directory. Pi registration now uses documented `registerProvider("gpt", config)`; static duplicate removed.
- Candidate Pi sync is installed. The stale broken `~/.pi/agent/extensions` symlink was atomically repointed to the isolated carrier, then canonical `deckctl sync apply pi` completed with a timestamped `models.json` backup. Installed default and account-materialized Pi discovery each list all four `gpt/sol-web-*` models.
- Final post-install gates pass: provider `283 passed, 1 skipped`; Factory `174 passed`. Deterministic suites cover keepalive, mediated tool call/result/final answer, stable session identity, typed cap/cancellation, exact context accounting, and unavailable billing.
- Full post-merge integrated gate passes: provider `283 passed, 1 skipped`; Factory `174 passed`; web `401 passed`, typecheck 0 errors/warnings/hints, production build passed; collector passed.
- Current-profile enrollment succeeded for `chatgpt@alex.org.il` in virtual mode. Upstream isolated-launch work changed daemon argv to Python `-I -c` and exposed that the original ownership parser accepted only a direct script operand. Focused repair now recognizes only the exact direct `-I <solwebd.py>` or trusted-launch `-I -c <launcher> <solwebd.py> <root>` shapes; independent security review rejected permissive argv scanning, and negative regressions cover `-c`, `-m`, and unrelated-script path smuggling. Focused security suite: `16 passed`.
- Runtime activation remains blocked by a `solwebd` launched from `ask-gpt-isolated-bootstrap`; installed `solwebd` currently exits before writing `~/.overdeck/gptbridge/runtime.json`; no broad process kill or state deletion was used. Full provider suite cannot run cleanly while that process owns port 8791; its sole observed failure was confirmed live-daemon interference in bootstrap listing. Factory suite passes `174 passed` on current `origin/main` (`896bc870d`). Owner correction: main incorrectly inferred the idle `ask-gpt` session still owned this runtime and messaged it without first proving session/process ownership. Next action is inspect the exact PID, parent/cgroup, worktree, branch, and active session mapping; contact only a proven live owner or take over safely if none exists.
- Visibility deployment background receipt failed empty; landed visibility source remains on main, but installed proof is not claimed complete.
- Guarded land ticket `ticket.8bc21141bee44894af1f4c938d5c5541` failed its first candidate test with an opaque command-supervisor exit despite the same full command subsequently passing. Retry was explicitly stopped for handoff; branch/worktree remain preserved.
- Earlier mixed integration carrier was concurrently rewound and retained 52 uncommitted files; it remains preserved untouched.
- Remote web execution pulled stale plan/index content back into worktree after the gate. Both authoritative receipt files were restored together from `0ee711e66`; no source change was reverted.
- Commit-hook notices are pre-existing toolchain gaps: unpinned PATH `ast-grep`/`tsc`, unavailable `jscpd`, and checker runtime over budget. No prevent-band finding was emitted.

- Final integrated gates after current-main merge: provider `307 passed, 1 skipped`; Factory `174 passed`; visibility routing `24 passed`; installed skill contract PASS.
- Direct landed and deployed `cb4877c8`; remote `main` matches. Deployment completed exit 0 and activated the existing web release.
- Installed commands resolve to clean deploy clone. `ask-gpt --help` exposes only human-only `--visible` and no `--mode`; installed `/gpt-orchestrator` skill exposes neither selector. Pi lists all four `gpt/sol-web-*` models.
- Exact stale ask-gpt service and exact hung pre-deploy daemon were terminated only after process/cgroup ownership proof. Installed `solwebctl ensure --seats 1` is healthy, authenticated as `chatgpt@alex.org.il`, advertises all required models, and reports deployed version `cb4877c8a90ba643a603d942430fd9d4c2cd52d4`.
- Wrapper rejects unsupported effort before launch and documents Pro-default invocation. Billing remains unavailable by contract; no live-model probe was used.

- Post-delivery security review flagged `python -s` as allowing interpreter-startup environment injection. Repaired both trusted-launch paths to `python -I -c` with an exact fixed `runpy` bootstrap and trusted root/script operands; tests assert isolated mode and minimal child environment. Clean gates: provider `307 passed, 1 skipped`; Factory `174 passed`. Landed/deployed `c53adf62`; installed provider is ready/authenticated with zero queued/active work and reports version `c53adf62fe40254800eeae6b525984447c9e3dd0`.

## Next executable action

None. Canonical main deployed; owner-visible Factory GPT runtime proves matching deployed version.
