Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
1263288
fix(opencode): make project memory process safe
LeXwDeX Aug 11, 2026
2494b45
docs(memory): add ProjectMemoryAuthority redo plan from d7b011738
LeXwDeX Aug 12, 2026
baf8598
docs(memory): fold plan-review findings; add P0 spec phase + resume p…
LeXwDeX Aug 12, 2026
16f1e1b
docs(memory): recreate ADR-0004 + CONTEXT authority glossary (P0 prop…
LeXwDeX Aug 12, 2026
5704eea
fix(project): repoint workflow+permission FK on identity upgrade (Occ…
LeXwDeX Aug 12, 2026
4b18989
docs(memory): adopt Occam minimal path (§10); reject elaborate ADR-00…
LeXwDeX Aug 12, 2026
7b30af3
docs(memory): close Occam #3/#4 as non-gaps; defer #2 (cascade) — pat…
LeXwDeX Aug 12, 2026
9f78858
fix(memory): make Memory fail-closed inert under the shared global id…
LeXwDeX Aug 12, 2026
419ac45
fix(memory): harden identity migration — residue tolerance, content m…
LeXwDeX Aug 12, 2026
216f649
fix(memory): close admission/lifecycle review findings — full-snapsho…
LeXwDeX Aug 12, 2026
f6fc23e
fix(worktree): make list() non-destructive and move worktree cleanup …
LeXwDeX Aug 12, 2026
5506755
test(memory): pin store resilience — corrupt-manifest fail-closed, it…
LeXwDeX Aug 12, 2026
8c91cbd
fix(memory): serialize MEMORY config file writers per file; pin cross…
LeXwDeX Aug 12, 2026
2382186
docs(memory): align CONTEXT.md and redo plan with the shipped Occam d…
LeXwDeX Aug 12, 2026
c5584bb
fix(memory): close Round 3/4 P2 findings — identity-lock protocol, sc…
LeXwDeX Aug 12, 2026
3dd5999
fix(memory): hold identity fence across the full retirement seam (MEM…
LeXwDeX Aug 12, 2026
7055931
fix(memory): single authority for the memory-identity fence protocol …
LeXwDeX Aug 12, 2026
580c624
fix(worktree): fail removal closed when the identity retires mid-remo…
LeXwDeX Aug 12, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CONTEXT-MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ Read the context documents relevant to the code or decision under review. Do not
| --- | --- | --- |
| Session Runtime and Client Contract | [`CONTEXT.md`](CONTEXT.md) | `packages/opencode/src/session`, `packages/opencode/src/system-context`, `packages/protocol`, `packages/client`, `packages/sdk` |
| Workflow Orchestration | [`packages/opencode/src/dag/CONTEXT.md`](packages/opencode/src/dag/CONTEXT.md) | `packages/opencode/src/dag`, workflow tool, DAG template validation and packaging |
| Project Memory | [`packages/opencode/src/memory/CONTEXT.md`](packages/opencode/src/memory/CONTEXT.md) | `packages/opencode/src/memory`, Memory-owned worktree lifecycle integration |

## Contexts created lazily

Expand Down
275 changes: 275 additions & 0 deletions docs/memory-authority-redo-plan-2026-08-12.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/opencode/src/format/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ export const layer = Layer.effect(
const dir = yield* InstanceState.directory
const result = yield* appProcess
.run(
ChildProcess.make(replaced[0]!, replaced.slice(1), {
ChildProcess.make(replaced[0], replaced.slice(1), {
cwd: dir,
env: item.environment,
extendEnv: true,
Expand Down
67 changes: 67 additions & 0 deletions packages/opencode/src/memory/CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Project Memory Context

Project Memory preserves user-confirmed, durable human context for one Project. It is not a code index, task tracker, instruction source, or general model-writable store.

## User principles (confirmed 2026-08-12)

- **One shared Memory per Project.** Worktrees hold no Memory of their own; they all share the Project's single Memory.
- **Memory never forks.** Memory is core, topic-typed content; worktrees (small PRs) must not branch it into per-worktree copies.
- **An identity upgrade is imperceptible.** When a repo gains its first remote (root → first-remote identity), the user's Memory endures seamlessly — nothing the user notices is lost, moved, or forked.

## Authority structure (Occam path, adopted 2026-08-12)

The domain runs on the existing seams; the elaborate `ProjectMemoryAuthority` redesign (ADR-0004) was **Rejected**. The authoritative pieces are:

- **MemoryStore** (`store.ts`) — generation+manifest persistence for Topics. Strict reads (`readSnapshot`/`inspectTopics`) fail closed on a corrupt or missing generation; the runtime read (`readTopics`) is lenient and projects empty.
- **MemoryConfig** (`config.ts`) — the unversioned `.opencode/memory.jsonc` policy. Writes serialize on a per-file cross-process flock (`memory-config:<file>`).
- **MemoryAdmission** (`admission.ts`) — the single legacy-input seam: scans one Project snapshot, reconciles once, caches only conflict-free results.
- **MemoryIdentityMigration** (`identity-migration.ts`) — `migrateHome(oldID, newID)`: rename when the target is absent, merge-then-remove otherwise; fails closed on conflict or an unread source.
- **Worktree guard** (`worktree/index.ts`) — `list()` is a pure observation path; `remove`/`reset` reconcile legacy memory fail-closed against the full directory snapshot and always invalidate the admission cache first.
- **Project identity migration** (`project/project.ts` `migrateProjectId`) — memory first, then the DB transaction that repoints session/workspace/workflow/permission references before deleting the old row.

## Glossary

| Term | Meaning |
| --- | --- |
| Project Memory | The authoritative durable Topic set owned by one Project identity and shared by all of that Project's worktrees. |
| Memory Home | The Project-scoped persistence boundary for Project Memory, keyed by Project identity (`memory/projects/<hash(id)>`). |
| Topic | A bounded structured collection of confirmed preferences, decisions, or terms with controller-owned metadata. |
| Legacy Worktree Memory | Memory files stored inside a checkout by an older runtime. They are migration inputs, never a second authoritative store. |
| Memory Conflict | A case where legacy and Project Memory claim the same logical identity with different **content**, or where legacy configuration differs from the effective Project configuration. Controller metadata drift is not a conflict. |
| Project Configuration | The MEMORY policy owned by the Project's primary directory (`.opencode/memory.jsonc`). It is unversioned; writes are serialized per file, not atomic with Topics. |
| Memory Admission | The single legacy input seam that scans one Project snapshot, reconciles it once, and caches only conflict-free results. |
| Identity upgrade | The one-way transition when a repo gains a durable identity (root → first-remote, or a changed remote). Memory is migrated before the old Project row is deleted; nothing is forked. |
| Global identity | The shared fallback identity of commit-less repositories. Memory is fail-closed **inert** under it: one Project = one Memory, and a shared bucket would leak across repos and orphan at the first commit. |

## Invariants

- One Project identity has one authoritative Project Memory.
- Two worktrees of the same Project cannot form independent Memory namespaces; Memory never forks per worktree.
- Current user input and higher-priority instructions always override retrieved Memory.
- The controller owns persistence, metadata, migration, limits, and atomicity; models only propose bounded semantic actions.
- Migration writes a durable authoritative copy before treating a legacy copy as consumed.
- A Memory Conflict is explicit and fail-closed; no component silently chooses or overwrites conflicting durable context. Content equality ignores controller-owned metadata (`last_matched_at`, `match_count`, `revision`, `updated_at`).
- Removing or resetting a worktree cannot imply deleting Project Memory, and never deletes the user's worktree directory as a side effect of registration cleanup.
- Removing Project Memory requires a separate Project retention decision.
- Runtime reads never perform ad-hoc legacy migration; they consume a Project snapshot admitted by Memory Admission.
- Memory is inert under the global identity and for uninitialized projects; activation requires a real, initialized identity.
- Identity upgrade migrates Memory first, repoints every Project-owned reference (session, workspace, workflow, permission), and only then retires the old row. A successor permission that collides on `(project_id, action, resource)` wins; the duplicate is dropped, never wedged.
- A missing Memory Home is empty. A corrupt or dangling Home fails closed on strict reads and migration (the source is never deleted unread); the lenient runtime read projects it as empty rather than erroring.
- Worktree `list()` observes and never mutates: it does not prune git admin data or drop registrations for merely-prunable entries. Destructive cleanup belongs to `remove`/`reset`, which prove each case first.
- Worktree `remove`/`reset` reconcile legacy memory fail-closed against the **complete** directory snapshot (primary + every registered sandbox) and always invalidate the admission cache before rescanning; they never trust a cached clean result.
- Legacy files are re-read and compared immediately before deletion; content that changed after the scan is preserved and surfaced as a conflict.
- Every writer of a MEMORY config file serializes on the file's cross-process lock; byte-atomicity is not undermined by whole-document last-writer-wins.

## Boundaries

- Worktree lifecycle assembles the directory snapshot and invalidates the admission cache before reconciling; it does not own Topic persistence or Project retention.
- Session runtime may retrieve and attach bounded Memory context, but it does not own Topic persistence.
- Codebase discovery belongs to codebase-memory facilities and is rejected from Project Memory.
- Source-Home retention/GC after migration is a deferred product decision; the current behavior is migrate-then-remove.

## Decisions

- [ADR-0001: Project identity owns Memory](docs/adr/0001-project-owned-memory.md)
- [ADR-0002: Project Memory commits are versioned and process-safe](docs/adr/0002-project-memory-commit-protocol.md)
- [ADR-0003: Legacy Memory enters through Project admission](docs/adr/0003-memory-admission.md)
- [ADR-0004: Project Memory authority owns identity and commits](docs/adr/0004-project-memory-authority.md) — **Rejected (2026-08-12)** in favor of the Occam path recorded in `docs/memory-authority-redo-plan-2026-08-12.md` §10.
Loading
Loading