Dashboard › opencode › Session 1dhwJSISRq8q
1dhwJSISRq8qNI9uVPerform a frozen, read-only adversarial correctness review of repository /home/byk/Code/opencode using ONLY immutable Git objects. Exact base SHA: ab9408c81cc9916177641ae8e9f08624fc27b5b9. Exact head SHA: dc661f30cb2055ad6248fdc59b14998d834497be. Exact binary patch SHA-256: e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb. Built artifact path/hash is supporting evidence only: packages/opencode/dist/opencode-linux-x64/bin/opencode SHA-256 70a250ea7a1f6dba86d329cf09f728db00af41035975d3fb38a548ff7c9003d2. Do not read the worktree, mutate files, run services, or touch databases. Inspect every changed file via git show <sha>:<path> and git diff <base> <head>. Verify ancestry, diff-check, patch scope, event/projector replay semantics, execution-claim lifecycle, all provider and tool termination paths, compaction overflow reset, startup composition, duplicate scans, supersession, migrations, and tests. Explicitly determine whether any provider implementation can dispatch before phase becomes unknown, whether any normal terminal path leaves a claim, whether graceful user interruption can accidentally trigger restart recovery, and whether tests prove the real preconditions. Classify findings as PASS, CONCERN, or MUST-FIX with precise HEAD file:line evidence. If blocked, quote exact command/error. Return a substantive report and end with exactly MERGE or DO-NOT-MERGE.
I’ll review only the two named Git objects and derived diffs. The required repo-setup skill is unavailable in this session, so I cannot load it; I will not substitute worktree reads for it.
ab9408c81cc9916177641ae8e9f08624fc27b5b9 091bb97b0c3b82d103748fbc9380a15ca9d325aa8df22a9a2604dd8438070ee2 - M .lore.md M packages/core/schema.json M packages/core/src/database/migration.gen.ts A packages/core/src/database/migration/20260914170650_session-recovery.ts A packages/core/src/database/migration/20260914214636_session-execution.ts A packages/core/src/database/migration/20260914222526_session-execution-phase.ts M packages/core/src/database/schema.gen.ts M packages/core/src/session/compaction.ts M packages/core/src/session/execution/local.ts M packages/core/src/session/message-updater.ts M packages/core/src/session/projector.ts A packages/core/src/session/recovery.ts M packages/core/src/session/runner/llm.ts M packages/core/src/session/runner/publish-llm-event.ts M packages/core/src/session/sql.ts A packages/core/test/session-execution-local.test.ts M packages/core/test/session-projector.test.ts M packages/core/test/session-runner-tool-events.test.ts M packages/core/test/sessio...
e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb - e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb - .lore.md packages/core/schema.json packages/core/src/database/migration.gen.ts packages/core/src/database/migration/20260914170650_session-recovery.ts packages/core/src/database/migration/20260914214636_session-execution.ts packages/core/src/database/migration/20260914222526_session-execution-phase.ts packages/core/src/database/schema.gen.ts packages/core/src/session/compaction.ts packages/core/src/session/execution/local.ts packages/core/src/session/message-updater.ts packages/core/src/session/projector.ts packages/core/src/session/recovery.ts packages/core/src/session/runner/llm.ts packages/core/src/session/runner/publish-llm-event.ts packages/core/src/session/sql.ts packages/core/test/session-execution-local.test.ts packages/core/test/session-projector.test.ts packages/core/test/session-runner-tool-events.test.ts packages/core/test/session-runner.te...
===== packages/schema/src/session-event.ts =====
1 export * as SessionEvent from "./session-event"
2
3 import { Schema } from "effect"
4 import { optional } from "./schema"
5 import { Event } from "./event"
6 import { ProviderMetadata, ToolContent } from "./llm"
7 import { Delivery } from "./session-delivery"
8 import { Model } from "./model"
9 import { DateTimeUtcFromMillis, NonNegativeInt, RelativePath } from "./schema"
10 import { FileAttachment, Prompt } from "./prompt"
11 import { SessionID } from "./session-id"
12 import { Location } from "./location"
13 import { SessionMessage } from "./session-message"
14 import { Revert } from "./revert"
15
16 export { FileAttachment }
17
18 export const Source = Schema.Struct({
19 start: NonNegativeInt,
20 end: NonNegativeInt,
21 text: Schema.String,
22 }).annotate({
23 identifier: "session.next.event.source",
24 })
...
===== packages/core/src/session/sql.ts =====
1 import { sqliteTable, text, integer, index, primaryKey, real, uniqueIndex } from "drizzle-orm/sqlite-core"
2 import * as DatabasePath from "../database/path"
3 import { ProjectTable } from "../project/sql"
4 import type { SessionMessage } from "./message"
5 import type { Prompt } from "./prompt"
6 import type { SessionInput } from "./input"
7 import type { Snapshot } from "../snapshot"
8 import { PermissionV1 } from "../v1/permission"
9 import { ProjectV2 } from "../project"
10 import type { SessionSchema } from "./schema"
11 import type { MessageID, PartID, SessionV1 } from "../v1/session"
12 import { WorkspaceV2 } from "../workspace"
13 import { Timestamps } from "../database/schema.sql"
14 import type { SystemContext } from "../system-context/index"
15 import { AgentV2 } from "../agent"
16 import type { Revert } from "@opencode-ai/schema/revert"
17
...
===== packages/core/src/session/projector.ts =====
1 export * as SessionProjector from "./projector"
2
3 import { and, desc, eq, gt, or, sql } from "drizzle-orm"
4 import { DateTime, Effect, Layer, Schema } from "effect"
5 import { Database } from "../database/database"
6 import { EventV2 } from "../event"
7 import { makeGlobalNode } from "../effect/app-node"
8 import { SessionEvent } from "./event"
9 import { SessionV1 } from "../v1/session"
10 import { WorkspaceTable } from "../control-plane/workspace.sql"
11 import { SessionMessage } from "./message"
12 import { SessionMessageUpdater } from "./message-updater"
13 import { SessionInput } from "./input"
14 import { WorkspaceV2 } from "../workspace"
15 import {
16 MessageTable,
17 PartTable,
18 SessionInputTable,
19 SessionMessageTable,
20 SessionRecoveryTable,
21 SessionTable,
22 } from "./sql"
23 import type { ...
...output truncated...
Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a237b78f001uV5MzDo0DZsILQ
256 const publish = (event: LLMEvent, outputPaths: ReadonlyArray<string> = []) =>
257 withPublication(publisher.publish(event, outputPaths))
258 let overflowFailure: ProviderErrorEvent | undefined
259 const providerStream = Stream.suspend(() => llm.stream(request)).pipe(
260 Stream.runForEach((event) =>
261 Effect.gen(function* () {
262 if (overflowFailure || publisher.hasProviderError()) return
263 if (LLMEvent.is.providerError(event)) {
264 if (isContextOverflowFailure(event) && !publisher.hasAssistantStarted()) {
265 overflowFailure = event
266 return
267 }
268 }
269 yield* publish(event)
270 if (event.type !== "tool-call" || event.providerExecuted) ...
1 import {
2 LLM,
3 LLMClient,
4 LLMError,
5 LLMEvent,
6 Message,
7 SystemPart,
8 isContextOverflowFailure,
9 type ProviderErrorEvent,
10 } from "@opencode-ai/llm"
11 import { Cause, DateTime, Effect, FiberSet, Layer, Option, Semaphore, Stream } from "effect"
12 import { and, eq } from "drizzle-orm"
13 import { AgentV2 } from "../../agent"
14 import { Config } from "../../config"
15 import { Database } from "../../database/database"
16 import { EventV2 } from "../../event"
17 import { Location } from "../../location"
18 import { ModelV2 } from "../../model"
19 import { PermissionV2 } from "../../permission"
20 import { ProviderV2 } from "../../provider"
21 import { QuestionV2 } from "../../question"
22 import { SystemContext } from "../../system-context/index"
23 import { SystemContextRegistry } from "../../system-context/registry"
24 import { SkillGuidance } from ...
1 export * as SessionCompaction from "./compaction"
2
3 import { LLM, LLMError, LLMEvent, Message, type LLMRequest, type Model } from "@opencode-ai/llm"
4 import { DateTime, Effect, Stream } from "effect"
5 import type { Config } from "../config"
6 import type { EventV2 } from "../event"
7 import { SessionEvent } from "./event"
8 import { SessionMessage } from "./message"
9 import { SessionSchema } from "./schema"
10 import { Token } from "../util/token"
11
12 const DEFAULT_BUFFER = 20_000
13 const DEFAULT_KEEP_TOKENS = 8_000
14 const TOOL_OUTPUT_MAX_CHARS = 2_000
15 const SUMMARY_OUTPUT_TOKENS = 4_096
16 const SUMMARY_TEMPLATE = `Output exactly the Markdown structure shown inside <template> and keep the section order unchanged. Do not include the <template> tags in your response. ...
===== packages/server/src/routes.ts =====
1 import { Database } from "@opencode-ai/core/database/database"
2 import { LayerNode } from "@opencode-ai/core/effect/layer-node"
3 import { httpClient } from "@opencode-ai/core/effect/app-node-platform"
4 import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
5 import { EventV2 } from "@opencode-ai/core/event"
6 import { Credential } from "@opencode-ai/core/credential"
7 import { PermissionSaved } from "@opencode-ai/core/permission/saved"
8 import { PtyTicket } from "@opencode-ai/core/pty/ticket"
9 import { SessionV2 } from "@opencode-ai/core/session"
10 import { SessionExecution } from "@opencode-ai/core/session/execution"
11 import { LocationServiceMap } from "@opencode-ai/core/location-service-map"
12 import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
13 import { SessionRecovery } from "@opencode-ai/core/session/recovery"
...
1 import { describe, expect } from "bun:test"
2 import { Deferred, Effect, Exit, Scope } from "effect"
3 import { eq } from "drizzle-orm"
4 import { Database } from "@opencode-ai/core/database/database"
5 import { Project } from "@opencode-ai/core/project"
6 import { ProjectTable } from "@opencode-ai/core/project/sql"
7 import { AbsolutePath } from "@opencode-ai/core/schema"
8 import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
9 import { SessionSchema } from "@opencode-ai/core/session/schema"
10 import { SessionExecutionTable, SessionTable } from "@opencode-ai/core/session/sql"
11 import { testEffect } from "./lib/effect"
12
13 const sessionID = SessionSchema.ID.make("ses_execution_local")
14 const it = testEffect(Database.layerFromPath(":memory:"))
15
16 const insertSession = Effect.gen(function* () {
17 const { db } = yield* Database.Service
18 yield* db
19 ...
diff --git a/packages/core/test/session-projector.test.ts b/packages/core/test/session-projector.test.ts
index 7ebcd97314..80f5a8669f 100644
--- a/packages/core/test/session-projector.test.ts
+++ b/packages/core/test/session-projector.test.ts
@@ -1,5 +1,5 @@
import { describe, expect } from "bun:test"
-import { DateTime, Effect, Schema } from "effect"
+import { DateTime, Effect, Exit, Schema } from "effect"
import { asc, eq, sql } from "drizzle-orm"
import { Database } from "@opencode-ai/core/database/database"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
@@ -17,15 +17,28 @@ import { SessionMessage } from "@opencode-ai/core/session/message"
import { Prompt } from "@opencode-ai/core/session/prompt"
import { SessionMessageUpdater } from "@opencode-ai/core/session/message-updater"
import { SessionProjector } from "@opencode-ai/core/session/projector"
+import { SessionRecovery } from "@opencode-ai/core/session/recovery"
import { SessionExecution } from ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/event.ts:369: function publishEvent<D extends Definition>(definition: D, event: Payload<D>, commit?: PublishOptions["commit"]) {
1 export * as EventV2 from "./event"
2
3 import { Cause, Context, Effect, Layer, Option, PubSub, Queue, Schema, Stream } from "effect"
4 import { Event } from "@opencode-ai/schema/event"
5 import type { Data, Definition, Payload } from "@opencode-ai/schema/event"
6 import { and, asc, eq, gt, inArray } from "drizzle-orm"
7 import { Database } from "./database/database"
8 import { EventSequenceTable, EventTable } from "./event/sql"
9 import { Location } from "./location"
10 import { makeGlobalNode } from "./effect/app-node"
11 import { isDeepStrictEqual } from "node:util"
12 import { Durable } from "@opencode-ai/schema/durable-event-manifest"
13
14 export const ID = Event.ID
15 export type ID = import("@opencode-ai/schema/event").ID
16 export type { Data, Definition, Payload } from "@opencode-ai/schema/event"
17
18 export type Subscriber<D extends Definition = Definition> = (event: Payload<D>) => ...
packages/core/test/session-runner.test.ts 3475 packages/core/test/session-runner-tool-events.test.ts 138 packages/core/schema.json 2418 .lore.md 607
...output truncated... Full output saved to: /home/byk/.local/share/opencode/tool-output/tool_0a239a139001yEshqu12Np4k8A -<!-- lore:01a07ee1-d0a7-7c46-84c0-a24e1b830766 --> -* **ERR\_UNSUPPORTED\_NODE\_MODULES\_TYPE\_STRIPPING bun-pty**: Trap: Bun typechecks and smoke tests make \`bun-pty@0.4.10\` look Node-compatible, but its runtime \`main\` is \`src/index.ts\`; the Node SEA refuses TypeScript stripping inside \`node\_modules\` with \`ERR\_UNSUPPORTED\_NODE\_MODULES\_TYPE\_STRIPPING\`. Fix: use the \`script(1)\` process adapter or another compiled Node-safe PTY API. \`@opencode-ai/pty\` is not an in-process replacement because it exports only \`binaryPath\`. - <!-- lore:019f89da-59f8-7ebd-bac6-06414ffc0a5c --> * **git push fails: Volta Node shim missing from husky pre-push PATH**: Trap: \`git push\` to a fork (e.g. ...
diff --git a/packages/core/schema.json b/packages/core/schema.json
index d0eeeebd5c..072f4434d9 100644
--- a/packages/core/schema.json
+++ b/packages/core/schema.json
@@ -1,8 +1,10 @@
{
"version": "7",
"dialect": "sqlite",
- "id": "f14a9b18-8207-487e-a3d3-227e629ba9ad",
- "prevIds": ["169a0f0f-d58f-479f-b024-fa1c7b9a09db"],
+ "id": "57591e35-6f8f-4cda-932c-6a08e85caae7",
+ "prevIds": [
+ "9401e0ed-b1e3-4557-bc94-1c9b29e1e09c"
+ ],
"ddl": [
{
"name": "workspace",
@@ -60,6 +62,10 @@
"name": "session_context_epoch",
"entityType": "tables"
},
+ {
+ "name": "session_execution",
+ "entityType": "tables"
+ },
{
"name": "session_input",
"entityType": "tables"
@@ -68,6 +74,10 @@
"name": "session_message",
"entityType": "tables"
},
+ {
+ "name": "session_recovery",
+ "entityType": "tables"
+ },
{
"name": "session",
"entityType": "tables"
@@ -920,6 +930,66 @@
...
diff --git a/packages/core/test/session-runner.test.ts b/packages/core/test/session-runner.test.ts
index cc58b43b29..5ddc34f457 100644
--- a/packages/core/test/session-runner.test.ts
+++ b/packages/core/test/session-runner.test.ts
@@ -31,7 +31,7 @@ import { SessionMessage } from "@opencode-ai/core/session/message"
import { Prompt } from "@opencode-ai/core/session/prompt"
import { SessionProjector } from "@opencode-ai/core/session/projector"
import { SessionExecution } from "@opencode-ai/core/session/execution"
-import { SessionRunCoordinator } from "@opencode-ai/core/session/run-coordinator"
+import { SessionExecutionLocal } from "@opencode-ai/core/session/execution/local"
import { SessionRunner } from "@opencode-ai/core/session/runner"
import * as SessionRunnerLLM from "@opencode-ai/core/session/runner/llm"
import { SessionRunnerModel } from "@opencode-ai/core/session/runner/model"
@@ -43,6 +43,7 @@ import { ConfigCompaction } from "@opencode-ai/core/config/compaction"
import ...
2860 output: Schema.Struct({}),
2861 execute: (_, context) =>
2862 questions.ask({ sessionID: context.sessionID, questions: [] }).pipe(Effect.as({}), Effect.orDie),
2863 }),
2864 })
2865 yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Ask then stop" }), resume: false })
2866
2867 requests.length = 0
2868 responses = [
2869 [
2870 LLMEvent.stepStart({ index: 0 }),
2871 LLMEvent.toolCall({ id: "call-question", name: "question", input: {} }),
2872 LLMEvent.stepFinish({ index: 0, reason: "tool-calls" }),
2873 LLMEvent.finish({ reason: "tool-calls" }),
2874 ],
2875 [],
2876 ]
2877
2878 const run = yield* session.resume(sessionID).pipe(Effect.exit, Effect.forkChild)
2879 let pending = yield* questions.list()
2880 while (pending.length === 0) {
2881 yield* Effect.yieldNow
...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:42:import { ConfigCompaction } from "@opencode-ai/core/config/compaction"
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:46: SessionExecutionTable,
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:102:const compactModel = Model.make({
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:103: id: "compact",
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:220: compaction: new ConfigCompaction.Info({
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:1034: it.effect("rebuilds the baseline directly after completed compaction", () =>
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-runner.test.ts:1044: const compactionID = ...
1065 ["Replacement context"],
1066 ])
1067 yield* replaySessionProjection(sessionID)
1068 yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Third" }), resume: false })
1069 yield* session.resume(sessionID)
1070 }),
1071 )
1072
1073 it.effect("automatically compacts into a completed summary and retained recent turn", () =>
1074 Effect.gen(function* () {
1075 yield* setup
1076 const session = yield* SessionV2.Service
1077 response = fragmentFixture("text", "text-first", ["Earlier answer"]).completeEvents
1078 yield* session.prompt({
1079 sessionID,
1080 prompt: Prompt.make({ text: "Earlier question ".repeat(180) }),
1081 resume: false,
1082 })
1083 yield* session.resume(sessionID)
1084
1085 currentModel = compactModel
1086 requests.length = 0
1087 responses = [
1088 fragmentFixture("text", ...
1307 it.effect("interrupts overflow recovery while the summary provider is running", () =>
1308 Effect.gen(function* () {
1309 const session = yield* setupOverflowRecovery
1310 responses = [
1311 [LLMEvent.providerError({ message: "prompt too long", classification: "context-overflow" })],
1312 fragmentFixture("text", "text-summary", ["## Objective\n- Interrupted"]).completeEvents,
1313 ]
1314 const firstGate = yield* Deferred.make<void>()
1315 const summaryGate = yield* Deferred.make<void>()
1316 streamGate = firstGate
1317 yield* session.prompt({ sessionID, prompt: Prompt.make({ text: "Continue" }), resume: false })
1318 const run = yield* session.resume(sessionID).pipe(Effect.forkChild)
1319 while (requests.length < 1) yield* Effect.yieldNow
1320 streamGate = summaryGate
1321 yield* Deferred.succeed(firstGate, undefined)
1322 while (requests.length < 2) ...
1 import { expect, test } from "bun:test"
2 import { Effect, Schema, Stream } from "effect"
3 import { LLMEvent } from "@opencode-ai/llm"
4 import { EventV2 } from "@opencode-ai/core/event"
5 import { SessionEvent } from "@opencode-ai/core/session/event"
6 import { SessionMessage } from "@opencode-ai/core/session/message"
7 import { SessionV2 } from "@opencode-ai/core/session"
8 import { ModelV2 } from "@opencode-ai/core/model"
9 import { ProviderV2 } from "@opencode-ai/core/provider"
10 import { createLLMEventPublisher } from "@opencode-ai/core/session/runner/publish-llm-event"
11
12 const sessionID = SessionV2.ID.make("ses_tool_event_test")
13 const base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB"
14
15 const capture = () => {
16 const published: Array<{ readonly type: string; readonly data: unknown }> = []
17 const events = EventV2.Service.of({
18 publish: (definition, data) =>
19 ...
1 export * as SessionRunCoordinator from "./run-coordinator"
2
3 import { Deferred, Effect, Exit, Fiber, FiberSet, Scope } from "effect"
4
5 /** Serializes execution for each key while allowing different keys to run concurrently. */
6 export interface Coordinator<Key, E> {
7 /** Snapshots keys with an execution owned by this coordinator. */
8 readonly active: Effect.Effect<ReadonlySet<Key>>
9 /** Starts execution while idle or joins the active execution. */
10 readonly run: (key: Key) => Effect.Effect<void, E>
11 /** Registers one coalesced follow-up after newly recorded work. */
12 readonly wake: (key: Key) => Effect.Effect<void>
13 /** Stops active execution and waits for its cleanup. */
14 readonly interrupt: (key: Key) => Effect.Effect<void>
15 }
16
17 type Entry<E> = {
18 readonly done: Deferred.Deferred<void, E>
19 owner?: Fiber.Fiber<void, never>
20 pendingWake: ...
395 yield* events.publish(SessionEvent.AgentSwitched, {
396 sessionID: input.sessionID,
397 messageID: SessionMessage.ID.create(),
398 timestamp: yield* DateTime.now,
399 agent: input.agent,
400 })
401 }),
402 switchModel: Effect.fn("V2Session.switchModel")(function* (input) {
403 const session = yield* result.get(input.sessionID)
404 if (
405 session.model?.providerID === input.model.providerID &&
406 session.model.id === input.model.id &&
407 (session.model.variant ?? "default") === (input.model.variant ?? "default")
408 )
409 return
410 yield* events.publish(SessionEvent.ModelSwitched, {
411 sessionID: input.sessionID,
412 messageID: SessionMessage.ID.create(),
413 timestamp: yield* DateTime.now,
414 model: input.model,
415 })
416 ...
1 #!/usr/bin/env bun
2
3 import { $ } from "bun"
4 import fs from "fs/promises"
5 import os from "os"
6 import path from "path"
7 import { pathToFileURL } from "url"
8 import { parseArgs } from "util"
9
10 const root = path.resolve(import.meta.dirname, "../../..")
11 const snapshot = path.join(root, "packages/core/schema.json")
12 const tsDir = path.join(root, "packages/core/src/database/migration")
13 const registry = path.join(root, "packages/core/src/database/migration.gen.ts")
14 const schema = path.join(root, "packages/core/src/database/schema.gen.ts")
15 const args = parseArgs({
16 args: process.argv.slice(2),
17 options: {
18 check: { type: "boolean" },
19 name: { type: "string" },
20 },
21 })
22
23 if (args.values.check) {
24 await check()
25 process.exit(0)
26 }
27
28 await generate()
29
30 async function generate() {
31 ...
1 export * as DatabaseMigration from "./migration"
2
3 import { sql } from "drizzle-orm"
4 import { Effect, Semaphore } from "effect"
5 import type { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
6 import { migrations } from "./migration.gen"
7 import schema from "./schema.gen"
8
9 type Database = EffectDrizzleSqlite.EffectSQLiteDatabase
10 type Transaction = Parameters<Parameters<Database["transaction"]>[0]>[0]
11 const lock = Semaphore.makeUnsafe(1)
12
13 export type Migration = {
14 id: string
15 up: (tx: Transaction) => Effect.Effect<void, unknown>
16 }
17
18 export function apply(db: Database) {
19 return lock.withPermit(
20 Effect.gen(function* () {
21 const tables = yield* db.all<{ name: string }>(
22 sql`SELECT name FROM sqlite_master WHERE type = 'table' AND name NOT LIKE 'sqlite_%'`,
23 )
24 if (tables.some((table) ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260303231226_add_workspace_fields.ts:8: yield* tx.run(`ALTER TABLE \`workspace\` ADD \`type\` text NOT NULL;`) dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260507164347_add_workspace_time.ts:8: yield* tx.run(`ALTER TABLE \`workspace\` ADD \`time_used\` integer NOT NULL DEFAULT 0;`) dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260510033149_session_usage.ts:8: yield* tx.run(`ALTER TABLE \`session\` ADD \`cost\` real DEFAULT 0 NOT NULL;`) dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260510033149_session_usage.ts:9: yield* tx.run(`ALTER TABLE \`session\` ADD \`tokens_input\` integer DEFAULT 0 NOT NULL;`) dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/database/migration/20260510033149_session_usage.ts:10: yield* tx.run(`ALTER TABLE \`session\` ADD \`tokens_output\` ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:10:import { migrations } from "@opencode-ai/core/database/migration.gen"
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:61: yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:84: yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:98: yield* DatabaseMigration.applyOnly(db, [workspaceNameMigration])
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:188: yield* DatabaseMigration.applyOnly(db, [contextEpochAgentMigration])
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/database-migration.test.ts:207: yield* DatabaseMigration.applyOnly(db, ...
fatal: path 'packages/opencode/dist/opencode-linux-x64/bin/opencode' exists on disk, but not in 'dc661f30cb2055ad6248fdc59b14998d834497be'
OK .lore.md OK packages/core/schema.json OK packages/core/src/database/migration.gen.ts OK packages/core/src/database/migration/20260914170650_session-recovery.ts OK packages/core/src/database/migration/20260914214636_session-execution.ts OK packages/core/src/database/migration/20260914222526_session-execution-phase.ts OK packages/core/src/database/schema.gen.ts OK packages/core/src/session/compaction.ts OK packages/core/src/session/execution/local.ts OK packages/core/src/session/message-updater.ts OK packages/core/src/session/projector.ts OK packages/core/src/session/recovery.ts OK packages/core/src/session/runner/llm.ts OK packages/core/src/session/runner/publish-llm-event.ts OK packages/core/src/session/sql.ts OK packages/core/test/session-execution-local.test.ts OK packages/core/test/session-projector.test.ts OK packages/core/test/session-runner-tool-events.test.ts OK packages/core/test/session-runner.test.ts OK packages/opencode/src/server/routes/instance/httpapi/server.ts OK ...
1 import { describe, expect, test } from "bun:test"
2 import { $ } from "bun"
3 import { fileURLToPath } from "url"
4 import path from "path"
5 import { SqliteClient } from "@effect/sql-sqlite-bun"
6 import { EffectDrizzleSqlite } from "@opencode-ai/effect-drizzle-sqlite"
7 import { Effect, Layer } from "effect"
8 import { eq, inArray, sql } from "drizzle-orm"
9 import { DatabaseMigration } from "@opencode-ai/core/database/migration"
10 import { migrations } from "@opencode-ai/core/database/migration.gen"
11 import workspaceNameMigration from "@opencode-ai/core/database/migration/20260410174513_workspace-name"
12 import sessionUsageMigration from "@opencode-ai/core/database/migration/20260510033149_session_usage"
13 import normalizeStoragePathsMigration from "@opencode-ai/core/database/migration/20260601010001_normalize_storage_paths"
14 import sessionMessageProjectionOrderMigration from ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-projector.test.ts:105: error: { type: "unknown", message: "Provider Step outcome unknown after server restart" },
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-projector.test.ts:120: error: { type: "unknown", message: "Provider Step outcome unknown after server restart" },
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-projector.test.ts:122: { type: "text", id: "recovery:execution", text: "Provider Step outcome unknown after server restart" },
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-projector.test.ts:168: error: { type: "unknown", message: "Provider Step outcome unknown after server restart" },
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/test/session-projector.test.ts:200: yield* SessionRecovery.recover().pipe(Effect.provideService(SessionExecution.Service, ...
231 * **Prior-process running tools**: Trap: replaying a tool still projected as \`running\` looks like continuation because its call and input are durable, but the prior process may already have completed the side effect. Fix: before the next provider request, durably fail every abandoned local tool with exact error \`Tool execution interrupted\`; never silently re-execute it, because outcome and side-effect completion are unknown. 475 * **Never silently replay ambiguous or partial side effects**: During retries, compaction, restart, or continuation recovery, rely only on explicit durable state and never silently replay ambiguous or partial provider/tool side effects. Persist intent and progress before execution; distinguish durable facts from process-local ownership and advisory wakeups. Claim unfinished sessions atomically, separately from unrelated ownership fields, with duplicate-safe multi-node fencing when clustered. ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/input.ts:136: isNull(SessionInputTable.promoted_seq),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/input.ts:170:export const hasPending = Effect.fn("SessionInput.hasPending")(function* (
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/input.ts:181: isNull(SessionInputTable.promoted_seq),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/input.ts:257: isNull(SessionInputTable.promoted_seq),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/input.ts:279: isNull(SessionInputTable.promoted_seq),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/projector.ts:489: or(gt(SessionInputTable.admitted_seq, boundary.seq), gt(SessionInputTable.promoted_seq, boundary.seq)),
dc661f30cb2055ad6248fdc59b14998d834497be:packages/core/src/session/runner/llm.ts:456: const hasSteer = yield* ...
AGENTS.md CONTRIBUTING.md packages/app/AGENTS.md packages/app/e2e/AGENTS.md packages/app/e2e/performance/AGENTS.md packages/codemode/AGENTS.md packages/core/src/tool/AGENTS.md packages/desktop/AGENTS.md packages/effect-drizzle-sqlite/AGENTS.md packages/llm/AGENTS.md packages/opencode/AGENTS.md packages/opencode/src/server/routes/instance/httpapi/AGENTS.md packages/opencode/src/session/llm/AGENTS.md packages/opencode/test/AGENTS.md packages/opencode/test/server/AGENTS.md packages/schema/AGENTS.md packages/session-ui/AGENTS.md packages/stats/AGENTS.md packages/ui/AGENTS.md
===== AGENTS.md =====
1 - To regenerate the legacy JavaScript SDK, run `./packages/sdk/js/script/build.ts`.
2 - 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.
3 - 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.
4 - The default branch in this repo is `dev`.
5 - Local `main` ref may not exist; use `dev` or `origin/dev` for diffs.
6
7 ## Branch Names
8
9 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/`.
10
11 Examples: `session-recovery`, `fix-scroll-state`, `regenerate-sdk`. ...
dc661f30cb2055ad6248fdc59b14998d834497be:packages/schema/src/session-event.ts:197: type: "session.next.step.interrupted", dc661f30cb2055ad6248fdc59b14998d834497be:packages/schema/src/session-event.ts:208: type: "session.next.step.recovered",
1 export * as DurableEventManifest from "./durable-event-manifest"
2
3 import { Event } from "./event"
4 import { SessionEvent } from "./session-event"
5 import { SessionV1 } from "./session-v1"
6
7 export const SessionDurable = {
8 definitions: Event.durable(SessionEvent.DurableDefinitions),
9 schema: SessionEvent.Durable,
10 } as const
11
12 export const Durable = Event.durable([
13 ...SessionV1.Event.Definitions.filter((definition) => definition.durable !== undefined),
14 ...SessionEvent.DurableDefinitions,
15 ])
1 import { Effect } from "effect"
2 import type { DatabaseMigration } from "../migration"
3
4 export default {
5 id: "20260603040000_session_message_projection_order",
6 up(tx) {
7 return Effect.gen(function* () {
8 // Pre-launch Session projections were written before durable event persistence
9 // became unconditional, so they cannot be assigned truthful aggregate order.
10 yield* tx.run(`DELETE FROM \`session_message\`;`)
11 yield* tx.run(`ALTER TABLE \`session_message\` ADD COLUMN \`seq\` integer NOT NULL;`)
12 yield* tx.run(`DROP INDEX IF EXISTS \`session_message_session_type_time_created_id_idx\`;`)
13 yield* tx.run(`CREATE INDEX \`session_message_session_seq_idx\` ON \`session_message\` (\`session_id\`,\`seq\`);`)
14 yield* tx.run(
15 `CREATE INDEX \`session_message_session_type_seq_idx\` ON \`session_message\` (\`session_id\`,\`type\`,\`seq\`);`,
...
385 test("resets incompatible projected Session messages before adding sequence order", async () => {
386 await run(
387 Effect.gen(function* () {
388 const db = yield* makeDb
389 yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY)`)
390 yield* db.run(
391 sql`CREATE TABLE message (id text PRIMARY KEY, session_id text NOT NULL, time_created integer NOT NULL, time_updated integer NOT NULL, data text NOT NULL)`,
392 )
393 yield* db.run(
394 sql`CREATE TABLE part (id text PRIMARY KEY, message_id text NOT NULL, session_id text NOT NULL, time_created integer NOT NULL, time_updated integer NOT NULL, data text NOT NULL)`,
395 )
396 yield* db.run(sql`CREATE TABLE event (id text PRIMARY KEY, seq integer NOT NULL)`)
397 yield* db.run(
398 sql`CREATE TABLE session_message (id text PRIMARY KEY, session_id text NOT NULL, type text NOT NULL, ...
PASS
ab9408c81cc9916177641ae8e9f08624fc27b5b9dc661f30cb2055ad6248fdc59b14998d834497begit merge-base returned the stated base; ancestry passed.git diff --check passed.git diff --binary <base> <head> | sha256sum returned the required e414f577c79eab2ae49be387b2f6fb48b013f9d8a61618d5298d62229bf8e4eb.git show <head>:<path>..lore.md. It contains no binary blob.The required repo-setup skill was unavailable. I instead read the immutable HEAD copies of applicable AGENTS.md and CONTRIBUTING.md.
The built artifact is not an immutable object and was not read. Exact Git result:
fatal: path 'packages/opencode/dist/opencode-linux-x64/bin/opencode' exists on disk, but not in 'dc661f30cb2055ad6248fdc59b14998d834497be'
saferunTurnAttempt invokes automatic compaction before changing the execution phase:
packages/core/src/session/runner/llm.ts:224-225unknown only at packages/core/src/session/runner/llm.ts:304-312.dependencies.llm.stream(...) at packages/core/src/session/compaction.ts:202-211.Therefore yes, a provider implementation can dispatch before the phase becomes unknown. The main provider stream is correctly deferred with Stream.suspend at llm.ts:259, but proactive compaction bypasses that protection.
A crash during the compaction request leaves a safe claim. Recovery then reports a generic execution interruption and can issue the same compaction request again, despite its prior outcome being unknown.
The automatic-compaction test at packages/core/test/session-runner.test.ts:1073-1143 checks request content ...
Date: Sep 14, 2026 * 🔴 (23:29) `packages/core/test/database-migration.test.ts` test `resets incompatible projected Session messages before adding sequence order` creates legacy tables `session`, `message`, `part`, `event`, and `session_message`, plus indexes `session_message_session_time_created_id_idx` and `session_message_session_type_time_created_id_idx`. * 🔴 (23:29) The migration test inser…
Date: Sep 14, 2026 * 🔴 (23:25) `packages/core/test/database-migration.test.ts` test `defaults missing workspace names while preserving legacy workspace data` creates legacy workspace `wrk_legacy` with `type="remote"`, `branch="main"`, `directory="/repo"`, `extra="{}"`, and `project_id="proj_legacy"`; after `DatabaseMigration.applyOnly(db, [workspaceNameMigration])`, it expects `name=""` while pr…
Date: Sep 14, 2026 * 🔴 (23:22) `packages/core/test/session-runner-tool-events.test.ts` defines `capture()` around `createLLMEventPublisher(...)` with `sessionID = SessionV2.ID.make("ses_tool_event_test")`, agent `"build"`, model ID `"model"`, provider ID `"provider"`, and no-op `startCommit` / `terminalCommit` callbacks using `Effect.void`. * 🔴 (23:22) `packages/core/test/session-runner-tool-ev…
Date: Sep 14, 2026 * 🔴 (23:21) In `packages/core/test/session-runner.test.ts`, the test `durably fails blocked local tools when a provider turn is interrupted` now queries `SessionExecutionTable` through `Database.Service` and verifies the active row for `session_id = sessionID` has `phase: "unknown"` before calling `session.interrupt(sessionID)`. * 🔴 (23:21) The interrupted blocked-tool test e…
Date: Sep 14, 2026 * 🔴 (23:21) User directed that AI cassettes always replay by default. Intentional recording requires `RECORD=true`, the listed provider credentials, and `CI` unset; CI forces replay. Cassettes must remain pretty-printed JSON because compact output makes multi-interaction changes difficult to audit. * 🔴 [enforced-workflow] (23:21) User requires evidence-driven, safety-first wo…
Date: Sep 14, 2026 * 🔴 (23:21) `packages/core/src/event.ts` exports `EventV2` from `"./event"` and defines `ID = Event.ID`, `type ID = import("@opencode-ai/schema/event").ID`, `Subscriber<D extends Definition = Definition> = (event: Payload<D>) => Effect.Effect<void>`, and `Unsubscribe = Effect.Effect<void>`. * 🔴 (23:21) `packages/core/src/event.ts` imports `Cause`, `Context`, `Effect`, `Layer`…
Date: Sep 14, 2026 * 🔴 (23:20) Added `packages/core/test/session-execution-local.test.ts`, using `testEffect(Database.layerFromPath(":memory:"))`, `sessionID = SessionSchema.ID.make("ses_execution_local")`, and an in-memory `ProjectTable`/`SessionTable` fixture rooted at `/project`. * 🔴 (23:20) `packages/core/test/session-execution-local.test.ts` adds exactly 3 `SessionExecutionLocal` lifecycle…
Date: Sep 14, 2026 * 🔴 (23:20) User specified a pending architecture requirement: “Replace local ownership with durable multi-node ownership when clustered.” * 🔴 (23:20) User specified that processes which never serve the HTTP API—specifically CLI and scripts—should not pay the associated cost at module load. * 🔴 (23:20) Supplied session-execution code documents that exactly one `llm.stream(re…
Date: Sep 14, 2026 * 🔴 (23:19) User supplied truncated tool output whose full contents were saved at `/home/byk/.local/share/opencode/tool-output/tool_0a237b78f001uV5MzDo0DZsILQ`. * 🔴 (23:19) Visible stream-processing code defines `publish = (event: LLMEvent, outputPaths: ReadonlyArray<string> = []) => withPublication(publisher.publish(event, outputPaths))`, initializes `overflowFailure: Provid…
Date: Sep 14, 2026 * 🔴 (23:19) User stated the session-projection rule: “A newer turn supersedes stale incomplete rows; never resume an older assistant projection.” This exact comment appears at `packages/core/src/session/projector.ts:142` and again in another visible concatenated source fragment at line 22. * 🔴 (23:19) User supplied partially elided `packages/core/src/session/projector.ts`; vi…
Date: Sep 14, 2026 * 🔴 (23:19) User supplied additional partially elided source output beginning with `packages/core/src/session/sql.ts`; visible imports include `sqliteTable`, `text`, `integer`, `index`, `primaryKey`, `real`, and `uniqueIndex` from `drizzle-orm/sqlite-core`, plus `DatabasePath`, `ProjectTable`, `SessionMessage`, `Prompt`, `SessionInput`, `Snapshot`, `PermissionV1`, `ProjectV2`,…
Date: Sep 14, 2026 * 🔴 (23:18) Immutable Git-object verification showed HEAD `dc661f30cb2055ad6248fdc59b14998d834497be` has tree `261416647ae3fcff79038dddd095b0c8136e0f45` and direct parent `ab9408c81cc9916177641ae8e9f08624fc27b5b9`, confirming the requested base is HEAD’s parent. * 🔴 (23:18) HEAD commit metadata: author `Burak Yigit Kaya <byk@sentry.io>`, date `2026-09-14T23:15:29+00:00`, subj…
Date: Sep 14, 2026 * 🔴 [requested-review] (23:16) User requested a frozen, read-only adversarial correctness review of repository `/home/byk/Code/opencode` using ONLY immutable Git objects. * 🔴 (23:16) User specified exact base SHA `ab9408c81cc9916177641ae8e9f08624fc27b5b9` and exact head SHA `dc661f30cb2055ad6248fdc59b14998d834497be`. * 🔴 (23:16) User specified exact binary patch SHA-256 `e41…