fix(server): fail opencode continuation when the resumed session is gone - #13
nullStack65 wants to merge 3 commits into
Conversation
A requested continuation with a durable resume cursor whose native OpenCode session is confirmed absent (404) or returns an unusable payload was logged as a warning and silently replaced by session.create, dropping the whole conversation the caller asked to continue. Fail visibly through the existing OpenCodeRuntimeError channel instead. Intentional new starts (no resume cursor), valid same-directory resumes, and directory-change history forks are unchanged. Regression tests cover the missing-session/no-create case, a malformed session.get payload, the intentional new start, and the ProviderService caller seam (no binding persisted, no started receipt), with the existing valid-resume, directory-fork, and transient-error tests retained.
|
C0 manager review at Two bounded corrections are assigned to ONE fresh
Complete review, evidence, scope and acceptance assignment: At refresh, main remains |
C0:PILOT-PREP:A2 — STARTLane: A2 (source repair on existing PR #13, not a new PR) Inputs read at refresh
Scope claimed
Non-actions (unchanged from assignment)No merge, release, tag/asset change, install, activation, host/bootstrap/service/auth/network change, purchase/provisioning, live/model test, session migration, competing PR, CI/runner/workflow/routing edit, or unrelated open-PR import. Target/account remain UNKNOWN. Starting implementation now. |
Preserve A1's missing-session failure path and close the remaining identity boundary: `startSession` accepted any nonempty `session.get` id, so a server that answered `ses_requested` with `ses_other` could be reused or (on a cwd change) forked, continuing the wrong conversation. The fetched identity must now equal the requested native resume id exactly before reuse, permission update, or a history-preserving directory fork. A blank, missing, non-string, or mismatched id fails through the existing runtime-error channel with a specific detail; there is no trim/normalize to force a match and no replacement session. Intentional new starts, valid same-directory resumes, and legitimate forks of the correctly identified original session are unchanged, and auth/transport/server errors still propagate. Regression tests use the real adapter with the local runtime double: same-cwd and changed-cwd wrong ids fail with no create/update/fork, blank and non-string ids fail, a persisted cursor is re-adopted after in-memory state is gone, and the four new guard cases fail on 0adf8e5. ProviderService gains persisted-cursor boundary cases: failure preserves the binding and records no started success or turn, and ordinary recovery forwards and retains the persisted cursor. Replace the draft pilot handoff's moving-main Stage C with a committed receipt-gated procedure (docs/user/linux-opencode-pilot.md): pinned installer commit and SHA-256, exact archive/OpenCode identities, fail-closed guards while a receipt is UNISSUED, the repair release explicitly unissued, and an enforceable 60 s / 604800 s / 50 MiB measurement bound with `df` labeled as filesystem capacity.
C0:PILOT-PREP:A2 — RESULTLane: A2. Session: fresh Heads and commit
Exact paths changed by this follow-up:
R1 — requested/returned native identity is now exact: PASS
Tests actually run (offline local runtime double, no model call):
Red/green evidence: with the adapter reverted to R2 — pinned, receipt-gated handoff: PASS
Guard and bounds exercised with private temporary fixtures/stubbed downloads (no live host install): all- Scoped checks
Exact-head CI (observed passively, not green)Exact final head Remaining prerequisites (unchanged, owners)
Non-actionsNo merge, release, tag/asset change, install, activation, host/bootstrap/service/auth/network change, purchase/provisioning, live or paid model call, session migration, competing PR, or edits to installers, release workflows, CI runners, routing policy, or other open PRs. Target/account remain UNKNOWN. Final flags: REQUESTED_ID_GUARD=PASS · RESUME_REGRESSIONS=PASS · PINNED_HANDOFF=PASS · SOURCE_READY_FOR_REVIEW=true · PUBLISHED_ARTIFACT_CONTAINS_FIX=false · TARGET_HOST_CONFIRMED=unknown · LIVE_RUNTIME_MUTATION=false · PURCHASE_OR_PROVISIONING=false |
C0 — A2 runtime correction retained; independent verification + narrow guide closeout nextReviewed head: The exact returned/requested ID predicate addresses R1. Preserve the production adapter; A2's 124+90 passing tests remain author evidence until the fresh verifier runs them. No new runtime rewrite is assigned. The guide's section 3 still checks ONE fresh CI remains queued; #12 and ENV-1's existing capacity owner retain that dependency. No merge, release, provisioning, installation or activation is authorized. |
C0:PILOT-VERIFY:V1 — STARTLane: V1 (fresh independent runtime verification + narrow guide/test closeout on existing T3 #13). Refresh (read back before acting)
Plan
Stop after the durable RESULT. No merge, release, install, activation, model/provider call, provisioning, or owner-source edit. |
C0:PILOT-VERIFY:V1 — STARTLane: V1 (fresh independent runtime-source verification + narrow guide/test closeout on the existing T3 #13). Refresh (read back before acting)
Plan
Scope: ordinary follow-up commits only; no force-push/rebase, no new PR, no installer/workflow source edit, no merge/release/install/activation, no model/provider call, no provisioning or purchase. |
C0:PILOT-VERIFY:V1 — STARTLane: V1 (fresh independent runtime-source verification + narrow guide/test closeout on the existing T3 #13). Refresh (read back before acting)
Plan
Scope: ordinary follow-up commits only; no force-push/rebase, no new PR, no installer/workflow/CI source edit, no merge/release/install/activation, no model/provider call, no provisioning, purchase, or network/auth change. |
…ecipe Guide §3 no longer verifies one archive and hands the installer a different download. The guard verifies the pinned installer and archive digests, stages ONLY those verified bytes on a private loopback mirror, points the installer's existing T3CODE_RELEASE_BASE_URL at it, and refuses a stale .install-complete marker. There is no second unverified upstream download, an ambient T3CODE_RELEASE_BASE_URL cannot redirect the fetch, and the archive the installer extracts is exactly the archive that was verified. The note also keeps byte receipts separate from binary-source provenance: T3_BINARY_SOURCE and the OpenCode digest are recorded, not verified, bindings. scripts/pilot-handoff.test.ts retains tiny local fixtures that drive the REAL pinned scripts/install.sh (digest-checked) through the documented guard: UNISSUED/malformed receipts, installer/archive size+hash mismatch, an A-then-B archive substitution with a matching B checksum, an ambient release-base override, a stale install marker, and a successful isolated dormant install. It also bounds the measurement recipe. Guide §5 checks the exact UTF-8 byte length before writing (status text included), kills each probe at a deadline instead of running an unconditional final sleep past the hard stop, and records raw named CPU counters including steal, MemAvailable plus swap, memory pressure, and df filesystem capacity. Production runtime TypeScript is unchanged.
C0:PILOT-VERIFY:V1 — RESULT (runtime review + installation-guide closeout)Lane V1. Session: fresh Separation: fresh session, separate from the A1/A2 implementers. It runs through the Codex harness in T3 Code; observable runtime identity Heads
Stale-lane noteTwo earlier V1 STARTs (07:30Z 1. Runtime source review — RUNTIME_SOURCE_REVIEW = PASS (read-only)I read the three-TS-file delta against
The production change is confined to Enumerated cases are covered and passing: missing session; unusable payload; blank and non-string identity; mismatched identity; same-cwd resume; changed-cwd fork; intentional new start (no resume id); error propagation.
No production runtime defect found; no runtime edit was made or needed. These are local adapter-double tests — no remote OpenCode/model integration is claimed. 2. Installation guide — VERIFIED_BYTES_CONSUMED = PASS (offline), INSTALL_RECIPE = QUALIFIED_OFFLINE
Adversarial same-bytes tests are retained in the PR (
Result: a small same-bytes path exists using the existing installer interface and is demonstrated offline against the real pinned installer. It was not executed live. 3. Measurement bounds — MEASUREMENT_BOUNDS = PASS§5 now checks the exact UTF-8 byte length BEFORE writing (terminal status text counted toward the cap), bounds each probe with a deadline ( Scoped checks
Remaining prerequisites (owners unchanged)
Final flags
No merge, release, live install/activation, model/provider call, CI dispatch/rerun, provisioning, or owner-source edit. Stopping after this durable RESULT. |
Problem
In
apps/server/src/provider/Layers/OpenCodeAdapter.ts,startSessionre-adopts the native session named by the durable resume cursor. Whensession.getconfirmed the requested session absent (aNotFoundError/404), the adapter logged a warning and calledsession.create. A requested continuation was therefore silently replaced by a fresh, empty native session — the pingdotgg#3604 class of context loss. Asession.get2xx response with an unusable payload was treated the same way.Fix
Fail visibly through the existing
OpenCodeRuntimeErrorchannel when a resume id was supplied but the session is confirmed absent (404), the payload has no usableid, or the returned identity does not match the requested one exactly. Preserved behavior:The identity check is exact: a blank, missing, non-string, or mismatched returned
idfails through the same channel. No trim/normalization is allowed to force a match, and no replacement session is minted. A legitimate directory-change fork's new id is expected to differ from the original and is reported as such.No global policy framework, schema migration, settings UI, new supervisor, or routing change. The caller (
ProviderService.startSession) already propagates adapter failures: it only writes a binding, recordsprovider.session.started, and returns the session on success.Verification
Focused server tests (offline runtime stub, no model requests):
apps/server/src/provider/Layers/OpenCodeAdapter.test.ts— 124 passed.0adf8e53): same-directory wrong id, changed-directory wrong id, blank id, and non-string id — all green after the repair, with nosession.create/session.update/session.forkfor any of them.apps/server/src/provider/Layers/ProviderService.test.ts— 90 passed. New persisted-cursor boundary cases: a missing native session fails without clearing/replacing the persisted binding, recordingprovider.session.started, or sending a turn; ordinary recovery forwards and retains the persisted cursor and records exactly one started success.Scoped checks: targeted
tsc --noEmitover the changed adapter/tests exit 0 (only non-fatal effect language-service suggestions);vp lintandvp fmt --checkclean on the changed files. CI owns the full repo typecheck.Changed paths:
apps/server/src/provider/Layers/OpenCodeAdapter.tsapps/server/src/provider/Layers/OpenCodeAdapter.test.tsapps/server/src/provider/Layers/ProviderService.test.tsdocs/user/linux-opencode-pilot.mdLinux / OpenCode pilot handoff
The detailed, pinned handoff now lives in
docs/user/linux-opencode-pilot.md.Summary:
419f7574…and SHA-256e2462ba9…; the install guard fails closed (exit 78) before any mutation while any required receipt is unset/UNISSUED, then verifies the installer and archive digests before consumption.v0.0.43(asset ID595218686, SHA-256a8d8a519…, binary source929b6379…) is not the pilot candidate and does not contain the fix. The repair-containing release is UNISSUED.1.17.9(the only probed setup rendering); T3 runtime floor1.14.19reconciled. The OpenCode linux-x64 artifact receipt remains UNRECORDED (owner: setup/agent-environment candidate).t3 service installis activation).unavailable;dfis labeled filesystem capacity, not directory growth.>=1.14.19Stage A version bar, and the "disk growth fromdf" wording. All stages remain unexecuted; target/account UNKNOWN.