Why
MEMORY activation is gated on project.time_initialized, which is stamped only by an event listener: Project.initState subscribes to Command.Event.Executed and calls setInitialized when it sees the init command (packages/opencode/src/project/project.ts:418-424). If the event is missed - subscription not yet established when /init runs, or the command executed through a path that does not reach that instance's event stream - the stamp is never written. The user then gets "Memory is unavailable until the project is initialized - run /init first, then /memory on" and re-running /init can lose the same race again. Real case: a project (git identity from remote URL) whose earlier clone directory was deleted carried time_initialized = NULL forever; the only recovery was manual sqlite surgery or a lucky re-run.
Scope
- Self-heal: when the activation gate finds
time_initialized NULL but the project worktree already has a non-empty AGENTS.md (the artifact /init produces), stamp it automatically instead of reporting the blocker. - Diagnostics: when the gate still blocks,
/memory status (statusReason) includes the actual DB row state (stamp NULL vs set, worktree path, sandboxes list) so stale-identity cases are visible at a glance. - Regression tests: NULL stamp + AGENTS.md present -> activation succeeds; NULL stamp + no AGENTS.md -> blocker message still reported with diagnostics.
Acceptance
- A project with AGENTS.md present can enable MEMORY without re-running
/init (race becomes harmless). - A project never initialized still reports the run-/init-first blocker, now with DB diagnostics.
- Existing memory gate tests keep passing;
specgit finish exits 0.
Why
MEMORY activation is gated on
project.time_initialized, which is stamped only by an event listener:Project.initStatesubscribes toCommand.Event.Executedand callssetInitializedwhen it sees the init command (packages/opencode/src/project/project.ts:418-424). If the event is missed - subscription not yet established when/initruns, or the command executed through a path that does not reach that instance's event stream - the stamp is never written. The user then gets "Memory is unavailable until the project is initialized - run /init first, then /memory on" and re-running/initcan lose the same race again. Real case: a project (git identity from remote URL) whose earlier clone directory was deleted carriedtime_initialized = NULLforever; the only recovery was manual sqlite surgery or a lucky re-run.Scope
time_initializedNULL but the project worktree already has a non-empty AGENTS.md (the artifact/initproduces), stamp it automatically instead of reporting the blocker./memory status(statusReason) includes the actual DB row state (stamp NULL vs set, worktree path, sandboxes list) so stale-identity cases are visible at a glance.Acceptance
/init(race becomes harmless).specgit finishexits 0.