Repository navigation
Sync with upstream pingdotgg/t3code main (ad4bb6d6fa) - #6
Merged
Merged
Conversation
…in half (pingdotgg#29572) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#29115) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…r approvals (pingdotgg#29567) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…erged (pingdotgg#29595) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…archived (pingdotgg#29582) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…cs (pingdotgg#29100) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…mpty (pingdotgg#29579) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
pingdotgg#29596) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#29105) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…9101) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…them (pingdotgg#29573) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…overwrite (pingdotgg#28351) Co-authored-by: PR Batch Tester <agent@local.test> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ffect 4.1.2 upgrade (pingdotgg#29780) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…ut tail splits an emoji (pingdotgg#29168) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…hanged test (pingdotgg#29839) Co-authored-by: maria-rcks <254055478+maria-rcks@users.noreply.github.com>
…ments early (pingdotgg#29717) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tatus (pingdotgg#29627) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…otgg#27577) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…y Running (pingdotgg#28356) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ed parents (pingdotgg#27951) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…server (pingdotgg#27703) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…gg#25252) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#23641) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…tgg#23033) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…s skip-worktree or assume-unchanged (pingdotgg#23624) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ingdotgg#29741) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…race (pingdotgg#29889) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…g#30025) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… the catalog (pingdotgg#30051) Co-authored-by: maria-rcks <maria@kuuro.net>
…point (pingdotgg#30033) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…go (pingdotgg#30071) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: maria-rcks <maria@kuuro.net>
…he runtime starts (pingdotgg#30079) Co-authored-by: wenshen <wlum0002@student.monash.edu> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…30080) Co-authored-by: Dominik <git@domko.sbs>
…otgg#30174) Co-authored-by: maria-rcks <maria@kuuro.net>
…ingdotgg#30142) Co-authored-by: SAPHID <saphid@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ttle (pingdotgg#29847) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…ngs (pingdotgg#29853) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…t hooks (pingdotgg#29907) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…backports (pingdotgg#30161) Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… lost, as upstream's unconfirmed interrupt, so the user is offered Force stop An unanswered POST cancel (the 5 s Stop bound, or the adapter's shorter request timeout, or a transport failure) failed as a protocol error wrapped in ProviderAdapterInterruptError, which the server retries instead of marking the run unresponsive. Every way a Stop goes unconfirmed now returns ProviderAdapterInterruptTimeoutError and leaves the turn open; a late done still ends it interrupted; a cancel the cell answers with an error keeps ProviderAdapterInterruptError. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Upstream moved 112 first-parent commits past the first merge of this sync (7c22e24). One conflict, resolved by keeping both sides: - ProviderTurnStartService.test.ts: upstream added a resumeFailure harness option and the "another client holds the native thread" test (pingdotgg#21045); the fork's resumeFallbackWithOpenItem option and its open-subagent-rows resume-fallback test stay beside them. The fork's fallback session is checked first, upstream's resumeFailure shares the plain fallback session. Auto-merged files both sides touched (Orchestrator, ProviderTurnStartService, ProviderRuntimeRecoveryService, RestartBackgroundNote, provider-core's ProviderAdapter) keep both changes; the fork's mechanisms are untouched by upstream's edits there. pnpm-lock.yaml took upstream's copy and was regenerated with pnpm install --lockfile-only; its delta over upstream is the same 43 lines as the first merge (provider-cell importer, two workspace links, ws catalog). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t carries its provider session, which the fork's restart check reads at turn start Upstream's new "fails the run instead of replacing a native thread another client holds" test (pingdotgg#21045) opens the harness's resume-fallback session, which has no providerSession. The fork's turn start reads session.providerSession.capabilities (backgroundWorkSurvivesRestart) before it resumes, so the test died there. Real sessions always carry it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…fusal, so a 5xx, the cell's own or a gateway's, leaves the Stop unconfirmed and the user is offered Force stop Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kupiainen
added a commit
that referenced
this pull request
Nov 4, 2026
…e login-era cell — a refused token ends the session's reading, judged for every answer in the one request function and the socket's close under the event permit, without a reconnect loop; the tenant's allowance as the usage limit with its reset time, the runs bound and run bounds as readable failures, and each run's usage as the turn's token usage Re-applies 8e98fbf, 2cc5586 and 823a00d (PR #5 rounds 1-3) on the upstream sync dd2d084, inside its Effect 4.1 / Effect.fn adapter and under upstream's lint rules. A Stop's cancel refines #6's rule: 401 is access loss, any other 4xx a refusal (403 with the cell's reason), every 5xx unconfirmed. One notice-node emission (#6's); the live revoke check requires no socket after the 4401; the allowance check requires the prompt-time usage_limit that vim main sends since vim pingdotgg#53. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
kupiainen
added a commit
that referenced
this pull request
Nov 4, 2026
…ens end the session's reading with what to do, allowance and run bounds reported, run usage as token usage (#5) The fork's cell provider was finished on 2026-10-20 against a cell with no login. This makes it work, and report clearly, against vim `main` (2e7f884 when opened; round 2 rechecked against 4ce3350; round 4 against 25abf41): login and API tokens, 401/403/503, 4401 on a revoked session, suspension, run bounds, the per-tenant daily allowance and runs bound, and per-run usage. The diff stays inside `packages/provider-cell`, its design record, the `live-cell-*` scripts and one cell fixture, so the rebase onto the upstream merge (stream 1) stayed small. **Round 4: rebased onto the upstream sync (fork main dd2d084, #6), see below.** ## What broke against the login-era cell (vim main, login on, before this PR) - A missing, stale or revoked token made the thread fail with "cell provider protocol error: The cell tenant rejected the access token.", with no word on what to do. The probe said the token was rejected even when none was set. - A tenant failing closed (base domain, no `SESSION_SECRET`, 503) showed up as "The cell answered 503 to GET state" in a thread, and as "not a cell tenant" in the probe. - A revoked token (suspension, new invite code) during a run: the socket closes 4401, the reconnect handshake is refused 401, and the adapter reconnected with backoff (up to 10 s) for ever. - A spent daily allowance is refused **at the prompt** (409 `{ runId: null, error }`). The prompt route re-reads the account, so this is the usual path, not the run's `done`. T3 showed it as a provider error ending in "..". - `done.usage` was ignored. - Vim main now retries a model 500 four times and emits each retry as a run-scoped `error`. The adapter published those notices as items without a node, which fails the projection integrity check. The checked-in `cell_wake_after_failure` no longer recorded live: the retries also reorder the child's completion ahead of the run's `done`. Already fine, and verified live: the eight existing scenarios with a token, the mid-run allowance `done` (`usage_limit` + `resetAt`), the runs-bound `done` ("The run did not start: … send the prompt again", thread reusable), and the run-bound `done` texts. Node's fetch and the `ws` client send no `Origin` and no `Sec-Fetch-Site`, so the cell's other-site rule never refuses T3. ## Design - **One error for "the tenant refuses T3 itself"**: `CellAccessError` in `errors.ts`, built from the status and the cell's text. The message names the tenant by host, says the token is missing or was refused and why tokens end, gives the steps (log in at `<base>/login`, Create an API token, Settings → Providers → Cell → Access token), and quotes the cell's reason. The cell's generic "log in at /login, or send … Authorization: Bearer" tail is cut from the quote: the steps say it in T3's terms, and T3's failure redaction would mangle it. The probe (auth `unauthenticated` on 401, `authenticated` when a token's state read succeeds), an attach, a prompt and the socket all use it. Codes: `cell_unauthorized` (401/403), `cell_unavailable` (503). - **Access loss is a terminal state of the session's reader** (reviews 1 and 2 made this hold by construction). It is entered once, by one function (`loseAccess`), under the event permit, and judged in two places only. **The session's one `request` function classifies every answer before its caller sees it**: a 401 to any request (the attach's `GET state`, a prompt, the `GET state` after a lost prompt, a `GET events` page of a replay, a Stop's cancel, a deleted thread's task cancels) enters `loseAccess` right there, whether or not the turn that asked is still in progress, and the caller gets only `CellAccessLost`. If the requesting fiber already holds the permit (a replay, the lost-prompt recovery), the entry happens directly; otherwise it takes the permit. A turn that had already ended gets no second terminal, but the session still reports the error. The other place is **the socket's close handling**: after a 4401 close, or a socket that never opened, its `GET state` answers 401 (handled by `request`) or 403. Entering it discards what is buffered (the turn's candidates, the queued wakes). After it no wake is queued, offered or dispatched (one queueing function, the offer and the dispatch each check the state), nothing already received is read, no request leaves (the request function checks it), the socket is not reopened, and an attach is refused at once. Each turn in progress ends with exactly one terminal, failed with the refusal; every later turn ends at once with it (a start *error* would be shown by the server as its generic "could not start this turn"). A 403/503 refuses one request, not the token: the turn fails with the refusal's message (a Stop: 403 refused with the cell's reason, 503 unconfirmed per #6's rule) and the session reads on. Changing the token rebuilds the instance, so the next message starts clean. - **One admission check on the socket**: after a connection the cell closed with 4401, or one that could not be opened (a refused handshake tells a client nothing), `GET state` is asked once. The event consumer asks it under the permit, after that connection's frames, so a `done` read before the close is applied first. A 401/403 there ends the reading; anything else reconnects with the existing backoff. Ordinary drops cost nothing extra. - **Allowance at the prompt**: a refusal may carry `code: "usage_limit"` and `resetAt`, which the adapter reports as T3's usage limit with the reset time. vim main sends them since vim pingdotgg#53 (25abf41), checked live in round 4; an older cell's words still show, and they name when it renews. - **Usage**: `done.usage` is read leniently (a bad count reports nothing and never makes the `done` unreadable) into `providerTurn.turnTokenUsage`: complete for a completed run, partial otherwise, with `hasSubagents` from the turn's tasks. - **Fake cell**: cell-worded 401s, refused handshakes answered with their status rather than dropped, `revokeToken` (4401 and the stopped run where a run is in progress; idle sockets stay open and deaf, as in the cell), `loginUnset` (503), `usage` on every `done`, `spendAllowance`. The replay transport now records and replays a socket's close code. - **Live tooling**: driver scenarios `access`, `revoke --suspend/--resume <operator token>`, `allowance` and `runs-bound`, each checking its outcome and exiting non-zero otherwise. The probe gains `--token` and 401/503 stand-ins. The scripted API reports usage. - **Docs** (`docs/internals/provider-cell.md`): a new "The login" section (connecting T3 to a deployed cell, what each answer means and what T3 shows, the 30 s policy-cache timing), protocol notes for `usage` and the refusal code, the socket rule, the probe, and a live-check recipe with the login on. ## Review round 1 fixes (2cc5586) Both must-fixes were ways around the refused-token rule. The fix makes the rule hold by construction instead of adding checks at each site. It is the access-loss state described under Design. 1. **A wake buffered during the prompt was dispatched after the prompt's 401.** Before, `loseAccess` cleared the wakes and then released the turn's candidates, which queued and offered the buffered wake again; its dispatch guard did not look at access loss. Now entering the state discards the candidates. The single function that queues a wake, the offer and the dispatch each check the state, so a wake offered *before* the 401 does not dispatch either. Test: `review r1: a prompt refused 401 discards the wakes it buffered and the ones already offered`. 2. **A cancel answered 401 produced no terminal** ("The cell answered 401 to cancel."). Now it enters access loss under the event permit and the turn ends with the refusal as its one terminal. A 403 or 503 on cancel fails the Stop with the actionable `CellAccessError` message and the run goes on. Tests: `review r1: a cancel refused 401 ends the turn with the refusal and the reading`, `a cancel refused 403 or 503 says why and the run goes on`. 3. **Sweep**: `every cell request answered 401 enters access loss once, by one rule` runs every remaining path on a hand-driven socket: `GET state` after a lost prompt, `GET events` during a replay (before the fix this looped reconnecting), 4401 followed by `GET state`, a task's cancel with no turn in progress, and the attach (a retried attach asks nothing). The prompt and Stop paths are covered by the two regression tests above. Each path checks the same things: the session error is entered once, each turn in progress gets one terminal with `cell_unauthorized`, a wake sent afterwards is not read and no continuation is offered, no socket opens past the backoff, no request leaves afterwards, and a later turn fails at once. Negative control: all four tests fail against 8e98fbf (`r2/logs/negative-control.log`: no terminal on cancel 401, a wake offered after the prompt's 401, `ProviderAdapterProtocolError` on a 403 cancel, and the sweep hangs in the `GET events` reconnect loop). ## Review round 2 fix (823a00d) **A prompt left pending, a reset that ended its turn, then the prompt answered 401: no access loss was recorded.** The session then offered and dispatched a wake and sent another prompt. Cause: the prompt's handler returned early on the inactive turn before it looked at the status. Fix: instead of one more check at that call site, the 401 check moved into `request`, which every session request goes through. It runs before any caller logic (turn liveness, candidate release, retries). `tokenRefused`, `loseAccessNow` and the per-caller 401 branches are gone. The permit is now taken through `underPermit`, which records the holding fiber, so a request made under the permit enters access loss without taking the permit a second time. A task cancel's shorter bound is now a `timeoutMs` on the request, so the bound can never cut off the entry into access loss. The socket's post-close `GET state` now goes through the event consumer (`check_access` inbox entry), so it is ordered behind the connection's frames. Test: `a 401 to a request whose turn had already ended still enters access loss, with no second terminal`. It covers the reviewer's ordering (prompt pending → reset ends the turn → 401) and the same ordering for a Stop's cancel and for a `GET events` page (the page before it carries the reset). It also checks that a run's `done` followed by a 4401 close completes the turn before access is lost. Each case checks one session error, no extra terminal, no wake offered or dispatched, no request or socket afterwards, and a later turn failing at once. The driven cell gained `parksCancels()`. Negative control: against 2cc5586's adapter the test fails, timing out on the prompt ordering (no session error ever comes) (`r3/logs/negative-control.log`). ## Round 4: rebase onto the upstream sync (head 8e4f01e, base dd2d084) **How:** re-applied as one new commit on dd2d084, not a commit-by-commit rebase. #6 rewrote the adapter: Effect 4.1, `Effect.fn` conversions, upstream's comment cuts, and the new Stop path. Each of my three commits touched the same Stop and task-cancel regions, so a sequential rebase would have resolved them three times. I did a 3-way merge of the squashed change (cfa469e → 823a00d) against dd2d084: 17 conflict hunks in 9 files. The old commits stay reachable as 8e98fbf / 2cc5586 / 823a00d. The commit message names them. **Reconciled with #6:** - **Stop rule, refined:** a 401 to a Stop's cancel is access loss. `request` enters it, the turn ends with the refusal as its one terminal, and Stop returns. Any other 4xx is a refusal (`ProviderAdapterInterruptError`; a 403 carries the cell's reason as `CellAccessError`), and 409 keeps its meaning. Every 5xx (503 included, which round 3 treated as a refusal), a transport failure or no answer is `ProviderAdapterInterruptTimeoutError`. #6's 5 s cancel + 8 s confirmation bounds are kept. The cancel bound is passed as `request`'s `timeoutMs`, so it never cuts off the entry into access loss. The test `a cancel refused 403 says why, a 503 leaves it unconfirmed, and the run goes on` replaces round 3's 403/503 test. - **Task cancels on deletion:** #6's `Effect.fn` form plus the access-loss guards and `timeoutMs` inside `request`. - **Notice node:** one emission (#6's). My live notice test duplicated #6's driven-socket test, so I dropped it and folded its one extra assertion (the node is a `system` node) into #6's test. - **Close codes:** the `closeCode` mapping moved into #6's NodeWS socket reader. The replay transport still records `ws_closed` with its code. - **`cell_wake_after_failure`:** I kept #6's recording and #6's recipe (`MODEL_RETRY_DELAY_MS=1` in `.dev.vars`, no `CHILD_DELAY_MS`), and dropped my `CHILD_DELAY_MS=15000` text from the docs. I re-recorded it against a fresh vim 25abf41 cell with the login off. It differs from the checked-in transcript only by ids and timestamps (4 retry notices, 22 entries, replay-verified), so this PR no longer changes the fixture. No new fixture, so nothing new to register in CellReplayFixtures. - **Upstream lint:** I cut 11 long comments to ≤ 4 lines (the design stays in docs), used `catchTags`, and un-exported `cellRefusalText` (knip). The four real-clock waits are the absence-of-reconnect checks after access loss. They keep `// oxlint-disable-next-line t3code/no-real-time-wait-in-tests` with main's existing justification ("an absence has no event to await"). There's no cheap event: making them event-driven would need a production hook that exposes the socket loop's end. The fourth is inside the `assertAccessLost` helper, which the rule does not flag. - **`requiresSignIn`: not adopted.** It is a flag on `ProviderAdapterOpenSessionError`, and the cell's openSession makes no request. The tenant is first asked at the thread attach, which needs the project. The server's only use of the flag is to skip the start worker's retries (100+200+400+800 ms). Those retries already send nothing, because the refusal is cached in the session. Adopting it would add a `GET state` to every session open, and it could not cover a 401 that arrives mid-session. - **Round-3 should-fix:** the live `revoke` check now requires `socketsAfterClose === 0`. **`allowance`** now requires the prompt-time `usage_limit` with `resetAt`, which vim main sends since pingdotgg#53. ## Verification Round 4 (head 8e4f01e, on dd2d084; logs `/srv/t3/tmp/ready-2026-11-04/t3code-login-era/r4/logs/`): - Root: `vp run -r --concurrency-limit 4 typecheck` **41/41**. `vp lint --report-unused-disable-directives` exits 0 with main's four warnings and none in touched files. `vp fmt --check` is clean. `pnpm run knip:check` is clean. The pre-commit hook (fmt, lint, knip) passed. - `packages/provider-cell`: `pnpm test` **113 passed** (3 files; #6 main has 97). `pnpm typecheck` (tsc) is clean. - `apps/server`, `CLAUDE_CONFIG_DIR` unset, by path: CellRestartAdoption, CellThreadDeletion, **CellReplayFixtures**, RunExecutionService, ProviderSessionManager, ThreadDeletion, ProviderRuntimeRecoveryService, ProviderTurnStartService, ProjectService.deletion, Orchestrator.control-reads, ManagedPolicy, ProviderTurnControlService, RestartBackgroundNote and ProviderSwitch.integration: **14 files, 274 passed**. - The review r2/r3 scratch orderings (11 tests: late prompt 401 after a reset, late 202 after socket loss, socket/prompt and cancel/done races, concurrent task cancels past their bound under a held permit, closing with an interrupted requester, prompt 403/503, 4401 then 403 vs 503) were inserted temporarily into adapter.test.ts: **13 passed** with the two own `review r1` tests (`review-orderings.log`). - **Live, vim main 25abf41** (built fresh, index.js sha256 3b7e9109…), celld 0.6.2, scripted API, `MODEL_RETRY_DELAY_MS=1`, `bash r4/live/run-live.sh r4`: **35/35 exit 0**. - Login on: probe; `shell`, `interrupt`, `access` (no token, bogus token, fail-closed tenant); `revoke` (4401 at +30 s, **0 sockets after it**, refusal names the suspension, next message fails at once, resume posted); `shell` after re-login; `delete-delegate` (cancel 200, listed cancelled); 6 recordings; `allowance` at the prompt (**409 carries `code: usage_limit`, turn fails as `usage_limit`, `resetAt` 2026-11-05T00:00Z**) and mid-run; `runs-bound`; fail-closed probe. - Login off: probe; `shell`, `interrupt`, `limit`, `delegate`, `reconnect`, `restart`, `attach-retry`, `delete-delegate`; all **7** recordings, `turn_interrupt` and `cell_wake_after_failure` included. - Live usage on the turn: input 4088 / output 59. Round 3 (head 823a00d): - `packages/provider-cell`: `pnpm test` **112 passed** (111 before, +1 sweep test with four orderings). `pnpm typecheck` clean. `vp lint --report-unused-disable-directives` and `vp fmt --check` clean. - `apps/server` cell suites by path: **10 passed** (143 skipped). - **Live against vim main 4ce3350** (the round-2 build; vim main is now 7888f18, but cell code is unchanged since 4ce3350), login on, full `run-live.sh`. Passed: the probe, `shell`/`interrupt`/`limit`/`delegate`/`reconnect`/`restart`/`attach-retry`, `access` (no token, bogus token, fail-closed tenant), `revoke` (refusal names the suspension, next message fails at once, **0 sockets after the 4401**), `shell` after re-login, `delete-delegate` (the cell lists the task as cancelled), allowance at the prompt and mid-run, and `runs-bound`. 24 of 25 exit 0; `turn_interrupt` with the login on fails as before (owner question 3). Round 2 (head 2cc5586): - `packages/provider-cell`: `pnpm test` **111 passed** (107 before, +4). `pnpm typecheck` clean. `vp lint` and `vp fmt --check` clean. - `apps/server` cell suites by path (`CellRestartAdoption`, `CellThreadDeletion`, `OrchestratorReplayFixtures -t cell`): **10 passed**. - **Live against vim main 4ce3350** (built fresh, celld 0.6.2, login on, tenant `default`, API token via `/login` + `/login/token`): probe; `shell`, `interrupt`, `limit`, `delegate`, `reconnect`, `restart`, `attach-retry` with the token; `access` with no token, a bogus one, and against a fail-closed tenant; `revoke` (refusal names the suspension, next message fails at once, **0 sockets after the 4401**); `shell` after re-login; `delete-delegate`; `allowance` at the prompt and mid-run; `runs-bound`. All exit 0. Six of seven recordings pass; `turn_interrupt` with the login on fails as in round 1 (owner question 3). Round 1 (head 8e98fbf, vim main 2e7f884): - `packages/provider-cell`: `pnpm test` went from **95 to 107 passed** (3 files): +2 protocol, +1 scripted socket (4401, then `GET state` 401, nothing after), +9 over the fake cell (probe answers, refused attach, revoke mid-run with no reconnect, prompt 401 on a deaf socket, refused reconnect, allowance refusal, runs bound, notice node, usage). `pnpm typecheck` is clean. - `apps/server`: the cell suites by path (`CellRestartAdoption`, `CellThreadDeletion`, `OrchestratorReplayFixtures -t cell`): **10 passed** before and after, with the refreshed fixture. `pnpm typecheck` shows no diagnostics in `scripts/live-cell-*` or provider-cell. - `vp lint --report-unused-disable-directives` and `vp fmt --check` on the changed files: clean. - **Live, vim main 2e7f884 on celld 0.6.2, login on** (`CELL_BASE_DOMAIN=vim.test`, `SESSION_SECRET`, `INVITE_SECRET`, tenant `default` at the apex, API token minted through `/login` + `/login/token`), full run `run-live.sh r3`. Passed: - the probe; - `shell`, `interrupt`, `limit`, `delegate`, `reconnect`, `restart` and `attach-retry` with the token; - `access` with no token, with a bogus one, and against a fail-closed tenant (no prompt posted in each); - `revoke`: 4401 at +30 s, **0 sockets after it**, the second message ended at once with the refusal, resume posted; - `shell` after re-login, `delete-delegate`; - `allowance` at the prompt (`TENANT_DAILY_TOKENS=1000`) and mid-run (`41500`, `usage_limit` + `resetAt`); - `runs-bound` (`TENANT_MAX_RUNS=1`); - six of seven fixture recordings with the token; - live usage on the turn (`inputTokens 4060, outputTokens 59`). - `cell_wake_after_failure` was re-recorded against that cell (`CHILD_DELAY_MS=15000`, `--write-fixture --version 0.3.0`) and checked in. The transcript carries no token. - `turn_interrupt` records with the login **off** (5/5) but not on (0/5); see owner questions. ## Owner questions (provisional decisions) 1. ~~The allowance refusal's code~~: settled by vim pingdotgg#53 (the prompt's 409 carries `code: "usage_limit"` and `resetAt`), mapped live in round 4. 2. **A refused token ends the session's reading** rather than retrying it. I chose this because a refused token never holds again. Is a session-level stop right, or should later turns retry the attach (costing one more 401 each)? 3. **`turn_interrupt` with the login on**: the shared input presses Stop right after the prompt, which with the login on lands during the first model request's reservation, so the shell row never appears. Adding `waitForTurnItemType: "command_execution"` to the shared input would fix it, but that touches every provider's fixture and `fixtures/index.ts`; I left it. 4. Failure codes `cell_unauthorized` / `cell_unavailable` are new strings. The class stays `provider_error`, since contracts have no auth class. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Round 2 (after review round 1): a second merge brings the branch to upstream
ad4bb6d6fa, and an unanswered Stop cancel now offers Force stop. See Round 2 below; sections 1–3 describe round 1 and still hold.The fork last merged upstream at
325a7e2ae4(2026-10-19, #3). Upstream pingdotgg/t3codemainhas since moved 2,638 first-parent commits, to7c22e24e3f(2026-11-03, "fix(antigravity): rate-limited turns keep their reset time"). The owner wants T3 Code's latest agent harness with the cell provider working on top of it. Upstream's commits rewrite files the fork's pieces live in (deletion planning, turn start, run execution, recovery, the replay harness, the adapter interface), move client registration intopackages/client-runtime, delete or split three files the fork had edited, move to Effect 4.1, and add lint rules the cell code predates.Fix
A merge, not a rebase, as in #3. The merge commit holds only the conflict resolution; the provider-cell port and each adoption follow as separate commits.
1. Merge commit
66c3964411(upstream/main7c22e24 into forkmaincfa469e)32 files conflicted. Each decision:
apps/web/package.json,apps/mobile/package.json@t3tools/client-runtime/provider-clients.apps/mobile/src/lib/providerClients.ts(deleted upstream)apps/web/src/components/settings/providerDriverMeta.tspackages/client-runtime/src/providerClients.ts+package.json(no conflict)cellClientregistered afterpiClient, dep addedapps/server/src/orchestration-v2/builtInProviderAdapterDrivers.ts(deleted upstream, pingdotgg#22557)CellAdapterV2Driverloses its only outside user and becomes private (port commit).apps/server/src/orchestration-v2/testkit/ProviderTurnStartMemory.fixture.mjs(deleted upstream, pingdotgg#22557)apps/server/src/orchestration-v2/testkit/OrchestratorReplayFixtures.integration.test.ts(split upstream, pingdotgg#22717)CellReplayFixtures.integration.test.ts<Provider>ReplayFixtures.integration.test.ts; the cell's seven run from their own file (andcelljoins the runner's guard list in the port commit).apps/server/package.jsonscriptsrecord:cell-replaybeside upstream's newrecord:fx-replay,record:kiro-replay,generate*.package.jsonknip:check--workspace packages/provider-cellknip.jsonclive-cell-*entriesevaluate-thread-titles.tsandmeasure-pr-preview.ts.docs/README.md,docs/user/install.md,docs/user/permission-modes.mdapps/server/src/provider/providerStatusCache.tsfx,kiro, then the fork'spi,muse,cell.apps/server/src/settingsSecrets.tscellClientbeside upstream's newkiroClient.packages/contracts/src/settings.tscellstays default-disabled beside upstream'sfx,kiro.packages/contracts/src/settings.test.tsapps/server/src/provider/model-manifest.jsonupdatedAt, cell entry afterfxpackages/provider-core/src/server/ProviderAdapter.tsforceNewNativeThreadand the fork'sopenNativeSubagentItemsare independent optional inputs.apps/server/src/orchestration-v2/Orchestrator.ts(thread.delete)planDeletion(side chats) +providerThreads,turnItemsin its record list (filtered to open subagent items in0a0394af2c)ProviderSessionManager.ts(failSubscribers)Effect.fnform + the fork'sreason/ProviderSessionReleasedErrorcauseProviderRuntimeRecoveryService.ts(3 hunks)backgroundWorkSurvivesRestart+ upstream's one-line comment; both item filters (surviving native subagent, upstream'slink_request)ProviderTurnStartService.ts(2 hunks)forceNewNativeThreadandopenNativeSubagentItems; upstream moved the restart-note computation earlier (goal-command aware) and the fork'sworkSurvivesRestartis grafted thereRunExecutionService.ts(2 hunks)testkit/ProviderReplayHarness.tsNodeCryptotestkit/fixtures/index.ts,shared.tsProviderRuntimeRecoveryService.test.ts,ProviderSessionManager.test.tsRunExecutionService.test.tsProviderTurnStartService.test.ts(11 hunks)apps/server/src/orchestration-v2/runtimeLayer.ts(auto-merged, broken)layerProviderAdapterRegistryProvided→ProviderAdapterRegistry.layerFromProviderInstanceRegistrypnpm-lock.yamlpnpm install --lockfile-onlyws/@types/wscatalog entries, as at #3. provider-cell's dependencies are unchanged.The fork's comments in these files are cut to upstream's new four-line comment rule.
The fork's three server mechanisms are all kept (
backgroundWorkSurvivesRestart,cancelBackgroundWorkon deletion,ProviderSessionReleasedError): upstream still has no capability for work that survives a restart, no deletion-time hook, and no deliberate-release marker. The nearest upstream pieces (hasPendingBackgroundWork,backgroundTasksEndWithProcess,RestartBackgroundNote.runOutcome) solve other problems.2. Follow-up commits
cba5e77f2d fix(cell): build the cell provider on upstream's Effect 4.1 and its per-provider replay fixture files:NodeSocket.layerWebSocketConstructorWS→NodeWS.layerWebSocketConstructor(same ws-backed constructor that carries the bearer token);CellAdapterV2Driverprivate;cellin the replay runner's guard list.c71ca38741 style(cell): follow upstream's Effect.fn, comment-length and test-wait lint rules:Effect.fn("Owner.op")for the reusable effects, the 38 long comments cut to four lines (the rules stay indocs/internals/provider-cell.md), justified disables on four real-clock waits inadapter.test.tsthat prove an absence or wait on counters the driven cell does not signal,decodeSyncin the testkit. No behaviour change.ca27bf6e1d fix(cell): publish a cell notice's node with it: found by the live run. vim main's cell now retries a transient model failure (upperformance/vim#20) and reports each retry as a run-scopederror; the adapter'ssystem_noticeitem named a node it never published, which fails the projection integrity check. The notice now publishes asystemnode first, as Pi's do; the adapter test checks it (fails without the fix).cell_wake_after_failureis re-recorded live: the four retry notices are the only wire change. The docs' live run setsMODEL_RETRY_DELAY_MS=1(see below).be2232456b feat(cell): report a Stop the cell does not confirm as upstream's unconfirmed interrupt: the adoption below.6672913836 fix(cell): typecheck with tsc: upstream dropped thetsc-rsbinary name;vp run -r typecheckfailed on provider-cell before.614643f7fd style(server): format the two turn-start files the merge resolution left unformatted.7ff5cfdd61 fix(cell): list the cell provider among the drivers a managed policy may name: upstream's new managed policy keeps its own driver list.0a0394af2c fix(server): read only a deleted thread's open native subagent items, not its whole history: upstream's control-reads test requires deletion to read no unrelated history; both deletion paths now read turn items filtered to opensubagentitems.3. Upstream's provider-facing changes since 325a7e2: adopt / skip
interruptTurn,ProviderAdapterInterruptTimeoutError, unresponsive run + Force stop (pingdotgg#27252)done, then failed the turn itself; the manager now cuts it off at 15 s first. Cancel 5 s, confirmation 8 s; unconfirmed Stop returnsProviderAdapterInterruptTimeoutErrorand leaves the turn open (a latedonestill ends it interrupted), so the user decides on Force stop, as with ACP. New test fails on the old code.CellReplayFixtures.integration.test.ts.resetAt, auto-resume and usage refresh (7c22e24, 6f41364, 8298664)class: "usage_limit"with an ISOresetAt(livelimit:resetAt: 2026-11-04T00:05:35Z). Usage refresh needs a usage reader the cell has none of.awaitedbackground tasks /awaitBackgroundTasks(91ea0e1)subagent, which always holds Working.hasPendingBackgroundWorkForThread({ includeSubagents })(07242ab)hasPendingBackgroundWorkis the documented fallback; holding the stream open per thread would be wrong while a wake waits as a new run.forceNewNativeThread(07242ab)background_work.updated,pendingBackgroundWorkonprovider_thread.updatedreasonon completed/interrupted terminals (9b72088)requiresSignInonProviderAdapterOpenSessionError(99ff58a)openSessionmakes no request; a rejected token surfaces later. Login handling is the login-era stream's.maxRuntimeModemanaged-policy ceilingfeedback, removedresponse/reasoningEffortautoCompactThreshold,promptCacheExpiresAt, per-request counting)donenow carriesusage(requests, tokens); reporting it onprovider_turn.updatedis a follow-up (owner question 2).secretSetting.No upstream change adds a required member to an interface provider-cell implements.
Verification
All from
/srv/t3/worktrees/t3code/t3code-sync,TMPDIRunder/srv/t3/tmp/ready-2026-11-04/t3code-sync/tmp, logs in that directory.pnpm install --frozen-lockfile(afterpnpm install --lockfile-onlyregenerated the lock)vp run -r --concurrency-limit 4 typecheckapps/webincluded, which failed on upstream at the last sync)vp lint --report-unused-disable-directivesfrom the rootmarkdownWindowsPaths.ts×2,AntigravityAcpSupport.test.ts,T3ChatAccounts.test.ts)vp fmt --check,vp run knip:checkvp test runinpackages/provider-cellvp test runonCellRestartAdoption,CellThreadDeletion,CellReplayFixturesintegration testsvp test runon RunExecutionService, ProviderSessionManager, ThreadDeletion, ProviderRuntimeRecoveryService, ProviderTurnStartService tests, ProjectService.deletionvp test runinapps/server,CLAUDE_CONFIG_DIRunsetvp test runin eachvp test run src/modelSelection.test.tsinapps/webThe first whole-suite run also failed
ManagedPolicy.test.tsandOrchestrator.control-reads.test.ts, which pass on pure upstream; both were ours and are fixed by7ff5cfdd61(the cell missing from the managed policy's driver list) and0a0394af2c(deletion read the whole turn-item history; now only opensubagentitems, which upstream's test requires).Failing on pure upstream 7c22e24 as well (checked in a second worktree with the same command), not ours:
src/preview/ServerBrowserPage.test.ts(2 tests): Playwright's headless shell is not downloaded on this machine (chromium_headless_shell-1223missing).src/storageCleanup.test.ts"compares the checked-out branch without a full status": the worktree classification differs in this machine's git setup.Live run against the cell of upperformance/vim
main2e7f884 (the round's prebuiltdist), celld 0.6.2dev --clean --no-watchon 9701,.dev.varsOPENAI_API_KEY=sk-test,OPENAI_BASE_URL=http://127.0.0.1:9702/v1,MODEL_RETRY_DELAY_MS=1,scripts/live-cell-api.mjs 9702 20000, followingdocs/internals/provider-cell.md(Testing) step by step (logslive/, driverlive/run-live.sh):live-cell-probe.ts: the live cellready, version 0.3.0, modelgpt-6-astra; the six stand-ins (no protocol, protocol 2, HTML, 404, no API key, closed port) give their expected status and message.live-cell-driver.ts, all eight scenarios exit 0 with the expected projection:shellcompleted;interruptinterrupted afterPOST cancelwithx-run-idanswered 204;limitfailed asusage_limitwith an ISOresetAt, then a second turn completed;delegatethree completed turns (prompt, wake, follow-up);reconnectcompleted after the 25 s hold;restartthe second runtime's continuation took the run back and completed with no cancelled-background note (same projection as at Sync with upstream pingdotgg/t3code main (325a7e2ae4) #3);attach-retrycompleted on the second connection;delete-delegate(API restarted withCHILD_DELAY_MS=30000, run last) sent the task's cancel, the cell answered 200 andGET taskslists it as cancelled, and the stream end logged at INFO "provider event stream ended after its run settled".record-cell-replay-fixture.ts --out <scratch>for all seven fixtures: each wrote and verified by replay (2–9 s each). Compared with the checked-in transcripts, onlycell_wake_after_failurechanged on the wire (the cell's four retryerrorevents), so only it was refreshed with--write-fixture --version 0.3.0(22 entries).cell_wake_after_failurefound the notice-node bug (ca27bf6e1d) and the retry-delay trap (owner question 4); logs of that round inlive-r1/.UI smoke (headless Chrome 152 through playwright-core, the merged server
serve --port 9703with its own--base-dir, telemetry off, web built from this branch): the cell provider added in Settings → Providers with the celld URL (status Available, v0.3.0), a cell thread in a scratch project withgpt-6-astra: the streamed answer, a delegatedreviewsubagent row Running then "Subagent "review" finished" and the wake's reply, and a Stop ("Run interrupted · Stopped by user", 144 ms). Screenshotsui/shots/15-cell-answer.png,16a-subagent-running.png,16b-subagent-done.png,19-stopped.png(plus11-cell-added.png). The server log has no warning or error.Round 2
4. Review fix: an unanswered cancel is an unconfirmed Stop (
377d0d39de)Round 1's Stop adoption returned
ProviderAdapterInterruptTimeoutErroronly when the cell answered the cancel but sent nodonewithin 8 s. WhenPOST cancelitself went unanswered (the 5 s Stop bound, the adapter's shorterrequestTimeoutMs, or a transport failure), the adapter failed with a protocol error wrapped inProviderAdapterInterruptError. The server retries that error instead of marking the run unresponsive, so Force stop was never offered.The rule now, as upstream's Stop bound defines it: every way a Stop can stay unconfirmed returns
ProviderAdapterInterruptTimeoutErrorand leaves the turn open. That covers a cancel the cell never answers (hung, or lost at the transport) and a cancel answered with nodonein time. A latedonestill ends the turn as interrupted. Only a cancel the cell answers with an error status other than 409 counts as a refusal; it keepsProviderAdapterInterruptError. (Narrowed in round 3, section 6: only a 4xx refuses; a 5xx is unconfirmed.) The 409 "another run" handling is unchanged. The adapter's request helper fails only when no answer arrives, so the cancel's failures (Effect.timeout(min(requestTimeoutMs, 5 s))plus the helper's own timeout) all map to the unconfirmed error, logged at WARN with the cause.Regression test in
adapter.test.ts, next to the acknowledged-cancel/no-donetest: "a Stop whose cancel the cell never answers, hung or lost, is reported unconfirmed like a missing done, and a definite refusal keeps its own error". It setsrequestTimeoutMs: 300, below the 5 s bound, so the request helper's timeout is the one that fires. It then runs three Stops on one open turn: a held cancel and a transport-failed cancel both giveProviderAdapterInterruptTimeoutError, and a 500 givesProviderAdapterInterruptError. No terminal is emitted in between, and a late cancelleddoneends the turn as interrupted. The driven cell gainedholdCancel, andanswerCancel(0, …)now fails at the transport. The test fails on0a0394af2c(expected 'ProviderAdapterInterruptError' to equal 'ProviderAdapterInterruptTimeoutError') and passes now. I also reran the reviewer's standalone reproduction (requestTimeoutMs: 10000, so the real 5 s bound applies) against this head. It returnsProviderAdapterInterruptTimeoutErrorafter 5000 ms and the late done ends the turn interrupted (r2/repro/stop-cancel-timeout.log). The Testing doc's Stop paragraph states the rule.5. Second merge commit
1da56ecf60(upstream/mainad4bb6d)Upstream moved 112 first-parent commits past 7c22e24 (more than the 58 to
940afd65e0when the review was written). This is a merge commit on the branch, not a rebase.ProviderTurnStartService.test.ts(the one conflict, 4 hunks)resumeFailureharness option and "fails the run instead of replacing a native thread another client holds" (pingdotgg#21045). The fork'sresumeFallbackWithOpenItemoption and its resume-fallback test stay beside them: the fork's fallback session is chosen first, andresumeFailureshares the plain fallback session.Orchestrator.ts,ProviderTurnStartService.ts,ProviderRuntimeRecoveryService.ts(+ test),RestartBackgroundNote.ts, provider-coreProviderAdapter.tsr2/fork-delta-compare.log).pnpm-lock.yamlpnpm install --lockfile-onlyPort commit
a17ce0d72f fix(server): give upstream's another-client resume test a session that carries its provider session. Upstream's new test died at the fork'ssession.providerSession.capabilitiesread (backgroundWorkSurvivesRestart): the harness's mocked fallback session has noproviderSession, which real sessions always carry and upstream's own code reads later. The fix is in the harness only; it passes on pure upstream and now passes here.Upstream's provider-facing changes in these 112 commits, adopt / skip:
hasPendingBackgroundWorkprobe takes part in upstream's background-work protection: a queuedrestart_and_resumeholds the queue while the cell's tasks run; a context handoff (create_with_handoff, which a driver change selects) proceeds and keeps the busy old session attached instead of detaching it. Upstream'sProviderSwitch.integration.test.tscovers both and passes here.ProviderAdapterThreadInUseError(pingdotgg#21045)subagentThreadTitle. Restart notes are skipped for the cell.Verification (round 2, head
a17ce0d72f)All from
/srv/t3/worktrees/t3code/t3code-sync,TMPDIR=/srv/t3/tmp/ready-2026-11-04/t3code-sync/r2/tmp, logs inr2/.pnpm install --frozen-lockfileinstall.log)vp run -r --concurrency-limit 4 typechecktypecheck-all.log)vp lint --report-unused-disable-directives,vp fmt --check,vp run knip:checkvp test run,vp run typecheckCellReplayFixturestest-cell-integration.log)server-focused.log)CLAUDE_CONFIG_DIRunset)server-suite.log). The 2 failing files (ServerBrowserPage.test.ts: headless shell not downloaded;storageCleanup.test.ts: "compares the checked-out branch without a full status") fail identically on pure upstreamad4bb6d6fa(known-failures-upstream.log).Live run against vim main 2e7f884's cell (fresh copy of the round's
dist, celld 0.6.2--clean --no-watchon 9701,live-cell-api.mjson 9702,MODEL_RETRY_DELAY_MS=1, docs' steps 2–7, scriptr2/live/run-live.sh, summaryr2/live/summary.txt):gpt-6-astra; all six stand-ins give their expected status.shell,interrupt,limit,delegate,reconnect,restart,attach-retry, anddelete-delegate(run last withCHILD_DELAY_MS=30000: cancel answered 200,GET taskslists the task as cancelled).--outrecordings were written and verified by replay (10–23 entries). Normalized for run ids, timestamps and vim main'susagefields, they match the checked-in transcripts, so no--write-fixturerefresh was needed.UI smoke (server
serve --port 9703, web built from this head, headless Chrome on 9704; scratch home copied from round 1 with its cell provider instance): a cell thread withgpt-6-astrastreamed the answer (r2/ui/shots/03-cell-answer.png). A held turn showed Working (04a-working.png), and "Stop generation" settled it as "Run interrupted · Stopped by user" in 119 ms (04b-stopped.png). All servers were stopped by port afterwards.Round 3 (head
6cb702632e)6. Review fix: a 5xx answer to a Stop's cancel is unconfirmed (
6cb702632e)Round 2 still read any non-409 error status as the cell's refusal, so a gateway's 502/503 (the upstream connection broke, the cell unreachable) produced
ProviderAdapterInterruptError. The server retries that error at once and never marks the run unresponsive, so Force stop was never offered.The rule now: only a 4xx answer refuses the cancel. 409 keeps its meaning (the run ended; its
donesettles the turn live or by replay). Any other 4xx isProviderAdapterInterruptError; 401 stays there as today, and its handling as access loss is fork PR #5's. Everything else is unconfirmed: any 5xx, whether the cell's own or a gateway's (neither says whether the run was cancelled, and the transport sees only status and body, so the body is not consulted), a transport failure, no answer within the 5 s bound, or an accepted cancel with nodonewithin 8 s. All of these returnProviderAdapterInterruptTimeoutErrorand leave the turn open. The 5xx case returns at once and logs a WARN. A late cancelleddonestill ends the turn interrupted. One added branch ininterruptTurn(adapter.ts), and the Stop paragraph and test list indocs/internals/provider-cell.mdnow state the rule.Regression: the round-2 Stop test is now "a Stop whose cancel is hung, lost or answered with any 5xx is reported unconfirmed like a missing done, and only a 4xx refuses it". On one open turn it runs a held cancel, a transport failure, 500 (
{"error":"no"}), 502 (HTML gateway page), 503 (plain text) and 504 (a body shaped like the cell's refusal). Each one givesProviderAdapterInterruptTimeoutError. Then a 403 with the cell's refusal body givesProviderAdapterInterruptError. There are seven cancels, all naming run A, and no terminal before a late cancelleddone, which ends the turn interrupted. The 409 case stays covered by "a cancel the cell refuses because another run is active means the run ended: the replayed done settles the turn". Without the new branch the test fails at the first 5xx (500: expected 'ProviderAdapterInterruptError' to equal 'ProviderAdapterInterruptTimeoutError',r3/regression-before.log).Section 5's pingdotgg#30401 row is corrected: only
restart_and_resumeholds the queue; a context handoff proceeds and keeps the busy session.Upstream:
upstream/mainis now66e6217b93, 3 first-parent commits pastad4bb6d6fa, none touchingpackages/provider-core,packages/contractsorapps/server/src/orchestration-v2. Not merged in this round.Verification (round 3, head
6cb702632e)All from
/srv/t3/worktrees/t3code/t3code-sync,TMPDIR=/srv/t3/tmp/ready-2026-11-04/t3code-sync/r3/tmp, logs inr3/.vp test run,vp run typecheckprovider-cell.log)vp run -r --concurrency-limit 4 typechecktypecheck-all.log)vp lint --report-unused-disable-directives,vp fmt --checkon the changed fileslint.log,fmt-check.log); pre-commit hook (fmt, lint, knip) passedCellReplayFixtures+ProviderTurnControlServiceserver-cell.log)server-focused.log)distcopy, celld 0.6.2--clean --no-watchon 9701,live-cell-api.mjs 9702 20000,MODEL_RETRY_DELAY_MS=1)live/probe.out);--scenario interruptexit 0: cancel answered 204, cancelleddone, run and provider turn interrupted,command_executioninterrupted (live/driver-interrupt.out, projection inlive/driver-interrupt/)The whole server suite was not rerun for this one-branch adapter change; round 2's run stands (
r2/server-suite.log).Owner questions (provisional decisions)
Force stop on a cell (from the Stop adoption): T3's Force stop settles the run in T3 and detaches the session; it does not cancel the cell's run, which may go on on the tenant until it ends. Provisional: accepted, since the cell already refused to confirm the cancel; a later turn's resume reads the run back from the thread's head. Should Force stop also try
POST cancelonce more, orcancelBackgroundWork?Token usage: vim main's
doneandstatenow carry the run'susage(requests, input/cached/output/reasoning tokens). Upstream's turn usage shape could show it per turn. Not adopted here (it is a new feature, not a sync need) — a follow-up for the cell provider?Retry notices: each model retry is now a visible notice item in the turn ("The API responded 500: …; trying again in 0.5s (retry 1 of 4)"). Provisional: shown, as the cell reports them. Collapse them, or hide all but the last?
MODEL_RETRY_DELAY_MS=1in the live-run docs: at the cell's default backoff the retries (~8 s) outlast the scripted child (4 s), its completion reaches the socket before the failed run'sdonebehind the fixture's gate, and thecell_wake_after_failurerecording never finishes. Provisional: the docs set the variable for the live run instead of changing the fixture's gate or the scripted API's child delay.Cancel answered 5xx— answered in round 3 by the Stop contract: only a 4xx answer is the cell's definite refusal of a cancel (409 keeps its meaning; 401 as access loss is left to fork PR feat(cell): the cell provider against the login-era cell: refused tokens end the session's reading with what to do, allowance and run bounds reported, run usage as token usage #5). Every 5xx, a transport failure or no answer within the bound is unconfirmed:ProviderAdapterInterruptTimeoutError, turn open, Force stop offered, a latedonestill ends it interrupted. See section 6.Not done
ad4bb6d6fa(fetched 2026-11-04 ~01:51 UTC+2).--write-fixturerefresh of the six fixtures whose wire is unchanged (only the known 202-versus-opener order differs).adapter.test.tscould become event-driven if the driven cell signalled its requests; left for the login-era stream, which owns that harness.🤖 Generated with Claude Code