feat(core): make path-local instruction discovery durable

Replace synthetic-message instruction injection with a durable
discovery projection so discovered AGENTS.md files survive compaction,
forks, reverts, and restarts.

- Add the instruction_file table and the durable
  session.instructions.discovered event; projection stores each
  discovered path and content with its owning assistant-message
  boundary and durable discovery order.
- Fold discovered files into the core/instructions source through
  InstructionDiscovery, absorbing SessionInstructions. Completed
  compaction rebaselines restate discovered instructions instead of
  summarizing them away.
- Re-read discovered files live at each observation so mid-session
  edits reach the model; the frozen discovery content stands in only
  when a file becomes unreadable.
- Narrate instruction file changes as per-file deltas via diffByKey,
  restating the full set only for pure reorderings.
- Fork copies discoveries within the inherited transcript, committed
  revert removes discoveries past the revert boundary, and Session
  movement clears them so the destination initializes a complete
  baseline.
This commit is contained in:
Kit Langton 2026-07-06 14:22:53 -04:00
commit 0726f09b05
28 changed files with 1166 additions and 598 deletions

View file

@ -16,6 +16,7 @@ import contextEpochAgentMigration from "@opencode-ai/core/database/migration/202
import simplifyIntegrationCredentialsMigration from "@opencode-ai/core/database/migration/20260611192811_lush_chimera"
import simplifySessionInputMigration from "@opencode-ai/core/database/migration/20260622202450_simplify_session_input"
import renameInstructionsMigration from "@opencode-ai/core/database/migration/20260705180000_rename_instructions"
import addInstructionFileMigration from "@opencode-ai/core/database/migration/20260706181957_add_instruction_file"
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
import { EventV2 } from "@opencode-ai/core/event"
@ -168,6 +169,21 @@ describe("DatabaseMigration", () => {
)
})
test("adds the instruction file table", async () => {
await run(
Effect.gen(function* () {
const db = yield* makeDb
yield* db.run(sql`CREATE TABLE session (id text PRIMARY KEY)`)
yield* DatabaseMigration.applyOnly(db, [addInstructionFileMigration])
expect(
yield* db.get(sql`SELECT name FROM sqlite_master WHERE type = 'table' AND name = 'instruction_file'`),
).toEqual({ name: "instruction_file" })
}),
)
})
test("keeps legacy credential fields nullable", async () => {
await run(
Effect.gen(function* () {