Skip to content
38 changes: 38 additions & 0 deletions .github/releases/v1.0.43.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
## opencode {VERSION}

{Prerelease/Stable} release from `{branch}` branch. Memory can now be enabled on the first request after repairing a missing project initialization stamp.

---

### 🐛 Bug Fixes

- **Memory activation**: when a git project already has a non-empty AGENTS.md but its initialization stamp is missing, `/memory on` now repairs the stamp and completes activation in the same request. Missing configuration, disabled configuration, and an unavailable configured model follow the normal initialization and model-reselection paths. Projects without initialization evidence remain blocked ([#581](https://github.com/LeXwDeX/OpenCode-GraphAgent/issues/581)).

---

### 🧪 Test Summary

```
Memory, search, server wiring: 116 pass, 0 fail
Activation regression: 5 pass, 0 fail (included above)
CI opencode suite: 4574 pass, 0 fail, 23 existing skips, 1 existing todo
CI workspace typechecks: 29/29 passed
CI DAG coverage gate: 820 pass, existing coverage floors met
CI HttpAPI composite: 3 x 230 pass, 0 fail, 0 skip, 0 missing
CI E2E: Linux 21 pass; Windows 21 pass
lint: 4846 warnings, 0 errors; existing 4850 cap unchanged
```

---

### 🔍 Verification

Local verification used Bun 1.3.14. The activation regression failed before the fix and now checks the first command response, persisted enabled state, replacement model, initialization stamp, and subsequent status. An independent review reran the focused regression and inspected the controller, identity fence, locks, and generation-based persistence. The memory suite also covers admission, identity migration, cross-process persistence, search, model wire format, and production server dependency wiring.

The CI counts above come from the accepted integration candidate `3b0d9da982` in [PR #582](https://github.com/LeXwDeX/OpenCode-GraphAgent/pull/582): [Typecheck and DAG gates](https://github.com/LeXwDeX/OpenCode-GraphAgent/actions/runs/34212878713), [unit, HttpAPI and E2E gates](https://github.com/LeXwDeX/OpenCode-GraphAgent/actions/runs/34212878737). Both Standards and Spec reviews found no blocking implementation findings, and SpecGit acceptance passed before dev integration. A compiled debug CLI also returned `Memory on` on the first request against a fresh isolated database and retained that status after process restart.

Main promotion is separately gated by its current-head CI and SpecGit acceptance. The release workflow validates reference templates and builds Linux, macOS, and Windows archives with SHA256SUMS. Real external model-provider quality and existing user memory content are outside this verification.

---

**Full changelog:** [`{previous_tag}`...`{current_tag}`](https://github.com/LeXwDeX/OpenCode-GraphAgent/compare/{previous_tag}...{current_tag})
11 changes: 6 additions & 5 deletions .specgit.yaml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
version: 1
delivery: main-release-1042
delivery: memory-release
context:
kind: branch
branch: chore/579-main-release-1042
branch: chore/583-memory-release
issues:
- 579
- 583
- 585
issueKinds:
- issue: 579
- issue: 583
kind: kind::chore
pr: 580
pr: 584
11 changes: 7 additions & 4 deletions packages/opencode/src/memory/memory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -841,19 +841,22 @@ export const layer: Layer.Layer<

const setEnabledUnsafe = Effect.fn("Memory.setEnabledUnsafe")(function* (enabled: boolean) {
const initial = yield* configuration()
if (!initial) {
// statusReason can repair a missing init stamp. Re-read before deciding
// activation is blocked so this same command can enable or repair the model.
const ready = initial ?? (enabled ? yield* statusReason().pipe(Effect.andThen(configuration)) : undefined)
if (!ready) {
if (!enabled) return "Memory remains off"
// #350: a /memory on that cannot activate must say WHY — the bare
// "remains off" sent users to guess (real case: an initialized git
// project whose /init stamp was missing looked identical to a
// disabled Memory).
return (yield* statusReason()) ?? "Memory remains off"
}
const value = initial.loaded
? initial
const value = ready.loaded
? ready
: yield* Effect.gen(function* () {
yield* initUnsafe()
return (yield* configuration()) ?? initial
return (yield* configuration()) ?? ready
})
if (!value.loaded) return "Memory remains off" as const
const loaded = value.loaded
Expand Down
39 changes: 39 additions & 0 deletions packages/opencode/test/memory/memory-init-stamp-selfheal.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,42 @@ const layer = Layer.mergeAll(Memory.layer.pipe(Layer.provideMerge(base)), CrossS
const it = testEffect(layer)

describe("memory /init stamp self-heal", () => {
for (const configured of ["missing", "disabled", "unavailable-model"]) {
it.live(
`first enable completes after self-healing with ${configured} project config`,
() =>
Effect.gen(function* () {
const dir = yield* tmpdirScoped({ git: true })
yield* provideInstance(dir)(
Effect.gen(function* () {
const project = yield* Project.Service
const memory = yield* Memory.Service
const config = yield* MemoryConfig.Service
const { project: info } = yield* project.fromDirectory(dir)
fs.writeFileSync(path.join(info.worktree, "AGENTS.md"), "# project guide\n")
if (configured !== "missing") {
yield* config.writeProject(info.worktree, {
schema_version: 1,
enabled: configured === "unavailable-model",
model: configured === "unavailable-model" ? "missing/model" : "openai/gpt-5.2",
topic_limit: 10,
turn_interval: 5,
injection: { max_topics: 3, max_tokens: 1_200 },
})
}

expect(yield* memory.setEnabled(true)).toBe("Memory on")
expect((yield* project.get(info.id))?.time.initialized).toBeDefined()
expect((yield* config.load(info.worktree))?.config.enabled).toBe(true)
expect((yield* config.load(info.worktree))?.config.model).toBe("openai/gpt-5.2")
expect(yield* memory.status()).toBe("Memory on")
}),
).pipe(Effect.provide(testInstanceStoreLayer))
}),
{ timeout: 30_000 },
)
}

it.live(
"statusReason stamps the project when AGENTS.md already exists",
() =>
Expand Down Expand Up @@ -117,6 +153,9 @@ describe("memory /init stamp self-heal", () => {

const reason = yield* memory.statusReason()
expect(reason).toContain("/init")
expect(yield* memory.setEnabled(true)).toContain("/init")
expect(yield* memory.setEnabled(false)).toBe("Memory remains off")
expect((yield* project.get(info.id))?.time.initialized).toBeUndefined()
// #415 diagnostics: the blocker carries the actual row state so a
// stale identity (worktree pointing at a deleted clone) is visible.
expect(reason).toContain("time_initialized")
Expand Down
Loading