DashboardopencodeSession 0DlXL0GFYSW4

Session 0DlXL0GFYSW4

Full ID: 0DlXL0GFYSW4N8WrE
Messages: 26
Distillations: 7
Time range: 2026-09-15 22:52:20 — 2026-09-15 23:06:05

Conversation (26 messages)

Perform an independent, adversarial, strictly READ-ONLY correctness review of the frozen V2 interrupted-session recovery candidate in /home/byk/Code/opencode.

Immutable evidence:

  • Base commit: ab9408c81cc9916177641ae8e9f08624fc27b5b9
  • Canonical full patch: /tmp/opencode/session-recovery-final.patch
  • Required patch SHA-256: b670599f8dbb4d4e14a74691a286622077c87e4d5c96a9acd3e2843c2c01072c
  • SEA artifact: /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
  • Required artifact SHA-256: 8e567b2cce03c95263df6fd06d80de6e143858d521e70712f5dc97bbfeb3355b
  • Candidate consists only of the 32 files represented by that patch. Never inspect mutable worktree contents as source of truth; inspect the patch and base Git objects. You may hash/read the named artifact. Do not edit, format, test, build, commit, start services, or mutate VCS.
  • Do not use lore/recall. The required repo-setup skill is unavailable; read repository instructions as needed without changing files.

Review all changed files and trace lifecycle end-to-end. Core invariants:

  • Restart never replays an uncertain provider request or old local tool; it reconciles unknown outcomes and admits/promotes a fresh deterministic queued continue Step.
  • Never use SessionPrompt; Core never imports Server.
  • Never recover a live lease or an execution superseded by a newer assistant.
  • Exact Session/execution/owner and unexpired lease fence every durable mutation; expired owners never renew/revive.
  • Ordinary acquisition never replaces expired work before recovery reconciles it.
  • Step/tool/compaction continuation, explicit cancellation, Context Epoch, promotion, durable publication, recovery outbox, and ordinary wake intent remain atomic/replay-safe.
  • Initial execution intent and exact resume:false->true retry are durable; historical v1 PromptAdmitted without the optional field remains admit-only; promoted retry never wakes an unrelated admit-only input. ...
gateway · 2026-09-15 22:52:20
result
b670599f8dbb4d4e14a74691a286622077c87e4d5c96a9acd3e2843c2c01072c  /tmp/opencode/session-recovery-final.patch
8e567b2cce03c95263df6fd06d80de6e143858d521e70712f5dc97bbfeb3355b  /home/byk/Code/opencode/packages/opencode/dist/server/opencode-server
commit
.lore.md
packages/app/src/components/titlebar-tab-nav.css
packages/app/src/components/titlebar-tab-nav.tsx
packages/app/src/components/titlebar-tab-rail-state.ts
packages/app/src/components/titlebar-tab-rail.tsx
packages/app/src/components/titlebar-tab-strip.tsx
gateway · 2026-09-15 22:53:54
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
1: diff --git a/packages/core/schema.json b/packages/core/schema.json
2: index d0eeeebd5c..dad07b6ce6 100644
3: --- a/packages/core/schema.json
4: +++ b/packages/core/schema.json
5: @@ -1,8 +1,8 @@
6:  {
7:    "version": "7",
8:    "dialect": "sqlite",
9: -  "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
10: -  "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
11: +  "id": "797e6838-8a41-4793-932a-d200aa8d51fb",
12: +  "prevIds": ["0445bfe8-162e-4719-a49b-23f1210dcacd"],
13:    "ddl": [
14:      {
15:        "name": "workspace",
16: @@ -60,6 +60,10 @@
17:        "name": "session_context_epoch",
18:        "entityType": "tables"
19:      },
20: +    {
21: +      "name": "session_execution",
22: +      "entityType": "tables"
23: +    },
24:      {
25:        "name": "session_input",
26:        "entityType": "tables"
27: @@ -68,6 +72,10 @@
28:        "name": "session_message",
29:        "entityType": ...
gateway · 2026-09-15 22:54:08
result
Found 32 matches
/tmp/opencode/session-recovery-final.patch:
  Line 1: diff --git a/packages/core/schema.json b/packages/core/schema.json

  Line 410: diff --git a/packages/core/src/database/migration.gen.ts b/packages/core/src/database/migration.gen.ts

  Line 421: diff --git a/packages/core/src/database/migration/20260914170650_session-recovery.ts b/packages/core/src/database/migration/20260914170650_session-recovery.ts

  Line 477: diff --git a/packages/core/src/database/schema.gen.ts b/packages/core/src/database/schema.gen.ts

  Line 563: diff --git a/packages/core/src/session.ts b/packages/core/src/session.ts

  Line 595: diff --git a/packages/core/src/session/compaction.ts b/packages/core/src/session/compaction.ts

  Line 684: diff --git a/packages/core/src/session/context-epoch.ts b/packages/core/src/session/context-epoch.ts

  Line 860: diff --git a/packages/core/src/session/execution/claim.ts b/packages/core/src/session/execution/claim.ts

  Line 932: diff --git ...
gateway · 2026-09-15 22:54:15
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
1387: +          timestamp: DateTime.makeUnsafe(row.time_created),
1388: +          messageID: id,
1389: +          prompt: decodePrompt(row.prompt),
1390: +          delivery: row.delivery,
1391: +        },
1392: +        commit ? { commit: () => commit(row) } : undefined,
1393: +      )
1394:        .pipe(
1395:          Effect.catchDefect((defect) =>
1396:            defect instanceof LifecycleConflict
1397: @@ -247,6 +386,7 @@ export const promoteSteers = Effect.fn("SessionInput.promoteSteers")(function* (
1398:    events: EventV2.Interface,
1399:    sessionID: SessionSchema.ID,
1400:    cutoff: number,
1401: +  commit?: (row: typeof SessionInputTable.$inferSelect) => Effect.Effect<void>,
1402:  ) {
1403:    const rows = yield* db
1404:      .select()
1405: @@ -262,13 +402,14 @@ export const promoteSteers = Effect.fn("SessionInput.promoteSteers")(function* (
1406:      ...
gateway · 2026-09-15 22:54:20
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
2524:  export const createLLMEventPublisher = (events: EventV2.Interface, input: Input) => {
2525: +  const publishEvent = <D extends EventV2.Definition>(
2526: +    definition: D,
2527: +    data: EventV2.Data<D>,
2528: +    commit: () => Effect.Effect<void> = input.eventCommit,
2529: +  ) =>
2530: +    definition.durable !== undefined
2531: +      ? events.publish(definition, data, { commit })
2532: +      : commit().pipe(Effect.andThen(events.publish(definition, data)))
2533:    const tools = new Map<
2534:      string,
2535:      {
2536: @@ -73,15 +85,24 @@ export const createLLMEventPublisher = (events: EventV2.Interface, input: Input)
2537:  
2538:    const startAssistant = Effect.fnUntraced(function* () {
2539:      if (assistantMessageID !== undefined) return assistantMessageID
2540: -    assistantMessageID = SessionMessage.ID.create()
2541: +    const created = SessionMessage.ID.create()
2542: ...
gateway · 2026-09-15 22:54:28
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
3674: +            timestamp: DateTime.makeUnsafe(index),
3675: +            executionID: `poison-${index.toString().padStart(3, "0")}`,
3676: +            phase: "ready",
3677: +          }),
3678: +        { discard: true, concurrency: 1 },
3679: +      )
3680: +      yield* db.run(sql`UPDATE session_input SET prompt = '{}' WHERE time_created < ${poisonCount}`)
3681: +      const wakes: string[] = []
3682: +      const execution = SessionExecution.Service.of({
3683: +        ...recoveryExecution,
3684: +        wake: (id) => Effect.sync(() => wakes.push(id)).pipe(Effect.asVoid),
3685: +      })
3686: +
3687: +      yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, execution))
3688: +      expect(wakes).toEqual([])
3689: +      expect(
3690: +        yield* db
3691: +          .select()
3692: +          .from(SessionRecoveryTable)
3693: +          ...
gateway · 2026-09-15 22:54:46
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
4874: +      const request = requests[0]
4875: +      if (!request) return yield* Effect.die("Recovery provider request not found")
4876: +      expect(userTexts(request)).toEqual(["Session execution interrupted by server restart", "continue"])
4877: +      const { db } = yield* Database.Service
4878: +      expect(
4879: +        yield* db
4880: +          .select({ promoted: SessionInputTable.promoted_seq })
4881: +          .from(SessionInputTable)
4882: +          .where(eq(SessionInputTable.id, unrelatedID))
4883: +          .get(),
4884: +      ).toEqual({ promoted: null })
4885: +      expect(yield* db.select({ pending: SessionRecoveryTable.wake_pending }).from(SessionRecoveryTable).get()).toEqual(
4886: +        {
4887: +          pending: false,
4888: +        },
4889: +      )
4890: +      yield* replaySessionProjection(sessionID)
4891: +      expect(yield* db.select({ pending: ...
gateway · 2026-09-15 22:54:52
result
<path>/tmp/opencode/session-recovery-final.patch</path>
<type>file</type>
<content>
5924: +              },
5925:                {
5926:                  "type": "object",
5927:                  "properties": {
5928: @@ -18196,6 +18241,82 @@
5929:                  "required": ["id", "type", "properties"],
5930:                  "additionalProperties": false
5931:                },
5932: +              {
5933: +                "type": "object",
5934: +                "properties": {
5935: +                  "id": {
5936: +                    "type": "string",
5937: +                    "pattern": "^evt_"
5938: +                  },
5939: +                  "type": {
5940: +                    "type": "string",
5941: +                    "enum": ["session.next.step.interrupted"]
5942: +                  },
5943: +                  "properties": {
5944: +                    "type": "object",
5945: +                    "properties": {
5946: +                      "timestamp": {
5947: +    ...
gateway · 2026-09-15 22:55:21
result
fatal: path 'packages/core/src/event/index.ts' does not exist in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
gateway · 2026-09-15 22:55:34
result
packages/core/src/database/database.ts
packages/core/src/database/migration.gen.ts
packages/core/src/database/migration.ts
packages/core/src/database/migration/20260127222353_familiar_lady_ursula.ts
packages/core/src/database/migration/20260211171708_add_project_commands.ts
packages/core/src/database/migration/20260213144116_wakeful_the_professor.ts
packages/core/src/database/migration/20260225215848_workspace.ts
packages/core/src/database/migration/20260227213759_add_session_workspace_id.ts
packages/core/src/database/migration/20260228203230_blue_harpoon.ts
packages/core/src/database/migration/20260303231226_add_workspace_fields.ts
packages/core/src/database/migration/20260309230000_move_org_to_state.ts
packages/core/src/database/migration/20260312043431_session_message_cursor.ts
packages/core/src/database/migration/20260323234822_events.ts
packages/core/src/database/migration/20260410174513_workspace-name.ts
packages/core/src/database/migration/20260413175956_chief_energizer.ts
packa...
gateway · 2026-09-15 22:56:08
result
export * as EventV2 from "./event"

import { Cause, Context, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"
import { Event } from "@opencode-ai/schema/event"
import type { Data, Definition, Payload } from "@opencode-ai/schema/event"
import { and, asc, eq, gt, inArray } from "drizzle-orm"
import { Database } from "./database/database"
import { EventSequenceTable, EventTable } from "./event/sql"
import { Location } from "./location"
import { makeGlobalNode } from "./effect/app-node"
import { isDeepStrictEqual } from "node:util"
import { Durable } from "@opencode-ai/schema/durable-event-manifest"

export const ID = Event.ID
export type ID = import("@opencode-ai/schema/event").ID
export type { Data, Definition, Payload } from "@opencode-ai/schema/event"

export type Subscriber<D extends Definition = Definition> = (event: Payload<D>) => Effect.Effect<void>
export type Unsubscribe = Effect.Effect<void>

export const latestSequence = ...
gateway · 2026-09-15 22:57:07
result
export * as SessionInput from "./input"

import { and, asc, eq, isNull, lte } from "drizzle-orm"
import { DateTime, Effect, Schema } from "effect"
import { Admitted, Delivery } from "@opencode-ai/schema/session-input"
import type { Database } from "../database/database"
import type { EventV2 } from "../event"
import { SessionEvent } from "./event"
import { SessionMessage } from "./message"
import { Prompt } from "./prompt"
import { SessionSchema } from "./schema"
import { SessionInputTable, SessionMessageTable } from "./sql"

type DatabaseService = Database.Interface["db"]

export { Admitted, Delivery }

const decodePrompt = Schema.decodeUnknownSync(Prompt)
const encodePrompt = Schema.encodeSync(Prompt)

const fromRow = (row: typeof SessionInputTable.$inferSelect): Admitted =>
  Admitted.make({
    admittedSeq: row.admitted_seq,
    id: SessionMessage.ID.make(row.id),
    sessionID: SessionSchema.ID.make(row.session_id),
    prompt: decodePrompt(row.prompt),
    delivery: ...
gateway · 2026-09-15 22:58:13
result
import {
  LLM,
  LLMClient,
  LLMError,
  LLMEvent,
  Message,
  SystemPart,
  isContextOverflowFailure,
  type ProviderErrorEvent,
} from "@opencode-ai/llm"
import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
import { AgentV2 } from "../../agent"
import { Config } from "../../config"
import { Database } from "../../database/database"
import { EventV2 } from "../../event"
import { Location } from "../../location"
import { ModelV2 } from "../../model"
import { PermissionV2 } from "../../permission"
import { ProviderV2 } from "../../provider"
import { QuestionV2 } from "../../question"
import { SystemContext } from "../../system-context/index"
import { SystemContextRegistry } from "../../system-context/registry"
import { SkillGuidance } from "../../skill/guidance"
import { ReferenceGuidance } from "../../reference/guidance"
import { ToolRegistry } from "../../tool/registry"
import { ToolOutputStore } from "../../tool-output-store"
import { ...
gateway · 2026-09-15 22:58:52
result
Found 2 matches
/tmp/opencode/session-recovery-final.patch:
  Line 5398:  import { CorsConfig, isAllowedCorsOrigin, type CorsOptions } from "@opencode-ai/server/cors"

  Line 5399:  import { serveUIEffect } from "@/server/shared/ui"
gateway · 2026-09-15 22:59:16
result
packages/core/schema.json
packages/core/src/database/schema.gen.ts
packages/core/src/database/schema.sql.ts
packages/core/src/project/schema.ts
packages/core/src/pty/schema.ts
packages/core/src/schema.ts
packages/core/src/session/schema.ts
packages/core/src/v2-schema.ts
packages/core/test/legacy-event-schema.test.ts
packages/core/test/pty/info-schema.test.ts
packages/core/test/shared-schema.test.ts

7 f14a9b18-8207-487e-a3d3-227e629ba9ad ['169a0f0f-d58f-479f-b024-fa1c7b9a09db']
gateway · 2026-09-15 23:00:31
result
- To regenerate the legacy JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
- After changing the public Protocol or Server `HttpApi`, run `bun run generate` from `packages/client`. Do not edit `src/generated` or `src/generated-effect` directly.
- Keep runtime dependencies directed from Schema to Core and Protocol, then from Core and Protocol to Server. Client runtime code may depend on Schema and Protocol but never Core or Server; `sdk-next` composes Client, Core, and Server.
- The default branch in this repo is `dev`.
- Local `main` ref may not exist; use `dev` or `origin/dev` for diffs.

## Branch Names

Use a short branch name of at most three words, separated by hyphens. Do not use slashes or type prefixes such as `feat/` or `fix/`.

Examples: `session-recovery`, `fix-scroll-state`, `regenerate-sdk`.

## Commits and PR Titles

Use conventional commit-style messages and PR titles: `type(scope): summary`. ...
gateway · 2026-09-15 23:01:15
result
fatal: path 'packages/core/src/database/migration/20260914170650_session-recovery.ts' exists on disk, but not in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
fatal: path 'packages/core/src/session/execution/claim.ts' exists on disk, but not in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
fatal: path 'packages/core/src/session/recovery-id.ts' exists on disk, but not in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
fatal: path 'packages/core/src/session/recovery.ts' exists on disk, but not in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
fatal: path 'packages/core/test/session-execution-local.test.ts' exists on disk, but not in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'
--- packages/core/src/session/recovery.ts
 210:               : !input ||
 211:                   input.session_id !== row.sessionID ||
 212:                   input.promoted_seq !== null ||
 213:                   input.delivery !== "queue" ||
 214:                   Option.isNone(prompt) ||
 215:                   ...
gateway · 2026-09-15 23:01:56
result
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/core/src/session/execution/local.ts:46:export * as SessionExecutionLocal from "./local"
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/opencode/src/server/routes/instance/httpapi/server.ts:68:import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/opencode/src/server/routes/instance/httpapi/server.ts:299:      AppNodeBuilderV1.build(SessionV2.node, [
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/opencode/src/server/routes/instance/httpapi/server.ts:301:        [SessionExecution.node, SessionExecutionLocal.node],
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/opencode/src/session/session.ts:14:import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
ab9408c81cc9916177641ae8e9f08624fc27b5b9:packages/opencode/test/session/compaction.test.ts:614:          Effect.provide(AppNodeBuilder.build(SessionV2.node, ...
gateway · 2026-09-15 23:02:17
result
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { PermissionV1 } from "@opencode-ai/core/v1/permission"
import { Slug } from "@opencode-ai/core/util/slug"
import { SessionV1 } from "@opencode-ai/core/v1/session"
import { serviceUse } from "@opencode-ai/core/effect/service-use"
import path from "path"
import { BackgroundJob } from "@/background/job"
import { Decimal } from "decimal.js"
import type { ProviderMetadata, Usage } from "@opencode-ai/llm"
import { InstallationVersion } from "@opencode-ai/core/installation/version"
import { Database } from "@opencode-ai/core/database/database"
import { EventV2Bridge } from "@/event-v2-bridge"
import { SessionV2 } from "@opencode-ai/core/session"
import * as SessionExecutionLocal from "@opencode-ai/core/session/execution/local"
import { locationServiceMapLayer } from "@opencode-ai/core/location-services"

import { NotFoundError } from "@/storage/storage"
import { eq } from "drizzle-orm"
import { and } from ...
gateway · 2026-09-15 23:02:50
result
 665:   )
 666: 
 667:   it.effect("clears durable execution intent when ordinary input is promoted", () =>
 668:     Effect.gen(function* () {
 669:       yield* setup
 670:       const session = yield* SessionV2.Service
 671:       const message = yield* session.prompt({
 672:         sessionID,
 673:         prompt: Prompt.make({ text: "clear durable wake" }),
 674:         resume: false,
 675:       })
 676:       const { db } = yield* Database.Service
 677:       yield* db.update(SessionInputTable).set({ wake_pending: true }).where(eq(SessionInputTable.id, message.id)).run()
 678:       response = [
 679:         LLMEvent.stepStart({ index: 0 }),
 680:         LLMEvent.stepFinish({ index: 0, reason: "stop" }),
 681:         LLMEvent.finish({ reason: "stop" }),
 682:       ]
 683: 
 684:       yield* session.resume(sessionID)
 685: 
 686:       expect(
 687:         yield* db.select().from(SessionInputTable).where(eq(SessionInputTable.id, message.id)).get(),
 688:       ...
gateway · 2026-09-15 23:03:11
result
 784:   it.effect("never starts a local tool after ownership changes", () =>
 785:     Effect.gen(function* () {
 786:       yield* setup
 787:       const session = yield* SessionV2.Service
 788:       const events = yield* EventV2.Service
 789:       const { db } = yield* Database.Service
 790:       executions.length = 0
 791:       response = [
 792:         LLMEvent.stepStart({ index: 0 }),
 793:         LLMEvent.toolCall({ id: "call-fenced", name: "echo", input: { text: "must not run" } }),
 794:         LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
 795:         LLMEvent.finish({ reason: "tool-calls" }),
 796:       ]
 797:       const unsubscribe = yield* events.listen((event) =>
 798:         event.type !== SessionEvent.Tool.Called.type
 799:           ? Effect.void
 800:           : db
 801:               .update(SessionExecutionTable)
 802:               .set({ id: "tool-successor", owner_id: "successor", expires_at: 60_000 })
 803:               ...
gateway · 2026-09-15 23:03:44
result
---
 680:       })
 681: 
 682:       const recovered = yield* events
 683:         .publish(SessionEvent.Step.Recovered, {
 684:           sessionID,
 685:           timestamp: DateTime.makeUnsafe(1),
 686:           executionID: "cross-session",
 687:           assistantMessageID: assistantID,
 688:           phase: "unknown",
 689:         })
 690:         .pipe(Effect.exit)
 691: 
 692:       expect(Exit.isFailure(recovered)).toBe(true)
 693:       expect(yield* db.select().from(SessionRecoveryTable).all()).toEqual([])
 694:       expect(yield* db.select().from(SessionInputTable).all()).toEqual([])
 695:     }),
 696:   )
 697: 
 698:   it.effect("quarantines a recovery wake behind a newer assistant", () =>
 699:     Effect.gen(function* () {
 700:       const { db } = yield* Database.Service
 701:       const events = yield* EventV2.Service
 702:       const recoveredID = SessionMessage.ID.make("msg_recovery_stale")
 703:       yield* db
 704:         .insert(ProjectTable)
 705:  ...
gateway · 2026-09-15 23:04:09
result
 741:       yield* SessionRecovery.recover().pipe(
 742:         Effect.provideService(
 743:           SessionExecution.Service,
 744:           SessionExecution.Service.of({
 745:             ...recoveryExecution,
 746:             wake: (id) => Effect.sync(() => wakes.push(id)).pipe(Effect.asVoid),
 747:           }),
 748:         ),
 749:       )
 750: 
 751:       expect(wakes).toEqual([])
 752:       expect(yield* db.select().from(SessionRecoveryTable).get()).toMatchObject({
 753:         recovery_error: "Invalid recovery projection identity",
 754:       })
 755:     }),
 756:   )
 757: 
 758:   it.effect("quarantines a malformed claim without blocking a valid claim", () =>
 759:     Effect.gen(function* () {
 760:       const { db } = yield* Database.Service
 761:       const validSession = SessionV2.ID.make("ses_recovery_valid")
 762:       yield* db
 763:         .insert(ProjectTable)
gateway · 2026-09-15 23:04:56
result
 215:     recovery_error: text(),
 216:   },
 217:   (table) => [
 218:     primaryKey({ name: "session_execution_pk", columns: [table.id] }),
 219:     uniqueIndex("session_execution_session_idx").on(table.session_id),
 220:     index("session_execution_owner_idx").on(table.owner_id),
 221:     check("session_execution_phase_check", sql`${table.phase} IN ('ready', 'safe', 'unknown', 'continue')`),
 222:   ],
 223: )
 224: 
 225: export const SESSION_EXECUTION_LEASE_MS = 30_000
 226: 
 227: export const SessionContextEpochTable = sqliteTable("session_context_epoch", {
 228:   session_id: text()
 229:     .$type<SessionSchema.ID>()
 230:     .primaryKey()
 231:     .references(() => SessionTable.id, { onDelete: "cascade" }),
 232:   baseline: text().notNull(),
 233:   snapshot: text({ mode: "json" }).notNull().$type<SystemContext.Snapshot>(),
 234:   baseline_seq: integer().notNull(),
gateway · 2026-09-15 23:06:05

Findings

MUST-FIX — Quarantined recovery continuations can re-enter ordinary queue execution

SessionRecovery.recover quarantines invalid or stale recovery rows only by setting recovery_error (packages/core/src/session/recovery.ts:220-254). It leaves the associated queued continuation input pending.

The local executor then excludes the errored recovery row from exact recovery selection (packages/core/src/session/execution/local.ts:48-56). However, ordinary queue discovery and promotion do not exclude recovery continuations:

  • SessionInput.hasPending: packages/core/src/session/input.ts:304-322
  • SessionInput.promoteNextQueued: packages/core/src/session/input.ts:408-428

Concrete failure sequence:

  1. Step.Recovered creates a deterministic queued continue input.
  2. A newer assistant supersedes its recovery target.
  3. Recovery correctly sets recovery_error: "Invalid recovery projection identity".
  4. A later ordinary prompt or explicit resume wakes the Session.
  5. Local execution skips the errored recovery row as a recovery operation.
  6. Ordinary queue handling still selects and promotes its pending continue input.
  7. The stale continuation reaches the model after the newer assistant.

For malformed continuation prompts, promotion instead fails repeatedly during prompt decoding and can block later queued work.

The existing stale-recovery test stops after asserting the error and lack of immediate wake (packages/core/test/session-projector.test.ts:698-754). It never starts later ordinary work and therefore misses this escape from quarantine.

Recovery continuations must remain reserved for exact recovery promotion and must never enter ordinary queue selection, including after quarantine.

CONCERN — Provider and local-tool dispatch retain an instruction-level fencing gap

Provider dispatch renews and verifies the claim immediately before constructing the provider stream (packages/core/src/session/runner/llm.ts:290). ...

gpt-5.6-sol · 2026-09-15 23:06:05 · ~1.0K tokens

Distillations (7)

Gen 0 2026-09-16 01:18:51 · 1404 tokens

Date: Sep 15, 2026 * 🟡 (22:55) Investigation attempted to inspect `packages/core/src/event/index.ts` at commit `ab9408c81cc9916177641ae8e9f08624fc27b5b9`; Git returned `fatal: path 'packages/core/src/event/index.ts' does not exist in 'ab9408c81cc9916177641ae8e9f08624fc27b5b9'`. * 🟡 (22:56) Repository search identified database files: `packages/core/src/database/database.ts`, `packages/core/src/…

Gen 0 2026-09-16 01:13:08 · 112 tokens

Date: Sep 15, 2026 * 🔴 (22:54) User supplied a test establishing the invariant `"never dispatches a provider after ownership changes"`. * 🔴 (22:54) User supplied a test establishing the invariant `"never publishes streamed output after ownership changes"`. * 🔴 (22:54) User supplied a test establishing the invariant `"never starts a local tool after ownership changes"`. * 🔴 (22:54) User suppli…

Gen 0 2026-09-16 01:06:15 · 91 tokens

Date: Sep 15, 2026 * 🔴 (22:54) User supplied a test establishing the invariant `"never advances the Context Epoch after ownership changes"`. * 🔴 (22:54) User supplied a test establishing the invariant `"never wakes execution when retrying an already promoted prompt"`. * 🔴 (22:54) User supplied a test establishing the invariant `"never arms durable wake for a conflicting execution retry"`.

Gen 0 2026-09-16 01:01:00 · 173 tokens

Date: Sep 15, 2026 * 🔴 (22:54) User supplied `packages/core/test/session-execution-local.test.ts` tests establishing lease/claim invariants: 1. `"never starts a second owner while the lease is live"`, 2. `"never overwrites an expired lease"`, 3. `"never re-enters its own uncertain live lease"`, 4. `"renews heartbeat expiry from the current clock"`, 5. `"never revives an expired claim on heartbea…

Gen 0 2026-09-16 00:56:11 · 868 tokens

Date: Sep 15, 2026 * 🔴 (22:54) User supplied a search result showing exactly 32 diff-file matches in `/tmp/opencode/session-recovery-final.patch`: 1. `packages/core/schema.json` (line 1), 2. `packages/core/src/database/migration.gen.ts` (line 410), 3. `packages/core/src/database/migration/20260914170650_session-recovery.ts` (line 421), 4. `packages/core/src/database/schema.gen.ts` (line 477), 5.…

Gen 0 2026-09-16 00:50:22 · 129 tokens

Date: Sep 15, 2026 * 🔴 (22:54) User stated the session claim invariant: “an expired claim never renews itself.” * 🔴 (22:54) User provided a code diff involving session persistence and execution/recovery logic, including identifiers `session_message`, `session_execution_session_idx`, `SessionContextEpochTable`, `SessionInputTable`, `SessionRecoveryTable`, `wake_pending`, `wakePending`, `promoted…

Gen 0 2026-09-16 00:45:05 · 233 tokens

Date: Sep 15, 2026 * 🔴 (22:53) User provided SHA-256 checksum `b670599f8dbb4d4e14a74691a286622077c87e4d5c96a9acd3e2843c2c01072c` for `/tmp/opencode/session-recovery-final.patch`. * 🔴 (22:53) User provided SHA-256 checksum `8e567b2cce03c95263df6fd06d80de6e143858d521e70712f5dc97bbfeb3355b` for `/home/byk/Code/opencode/packages/opencode/dist/server/opencode-server`. * 🔴 (22:53) User provided a te…