cyberine
UsageRun recipes

Claude Code memory adapter (CYBERINE_CC_MEMORY) — opt-in

Cyberine's memory is its own: ~/.cyberine/memory/ (the Long-term memory section below), one format, vendor-independent, so the same orchestrator serves Claude, Codex or any other CLI. A Claude Code memory folder is an OPTIONAL adapter on top of it — Cyberine reads and writes one only when the operator names it. Since 2026-09-09 (kmspc, operator decision) there is NO fallback: with nothing configured the bridge resolves no folder, reads nothing and writes nothing. Until then it guessed .claude/memory, then ~/.claude/memory, then autoMemoryDirectory from ~/.claude/settings.json, and every finished run — smokes and worktree agents included — dropped a handoff-cyberine_<slug>.md into the operator's real memory repo: 352 stray files at its root by the day the fallback was removed (the operator deleted them the same day).

Opt in (first valid directory wins; a path inside the project tree is refused):

  1. CYBERINE_CC_MEMORY=<path> in the environment.
  2. A .local/cc-memory file in the project (one line, the path; gitignored).

CYBERINE_CC_MEMORY=0 is the explicit off switch and still wins over a .local/cc-memory file.

Read (preamble): before each dispatch, Cyberine reads the latest handoff or MEMORY.md for THIS project from the opted-in folder and adds a CC MEMORY block to the task prompt, trailing the task itself with an explicit "this is background, not a live conversation" frame. Capped at 1200 chars. Skipped entirely for small/QA-shaped tasks (anything the router does not classify as code-large) — T488 found a full handoff derailed raw-API loop workers into responding as if continuing that session instead of the dispatched task.

Write (handoff): when a run reaches done state, Cyberine writes a compact handoff-cyberine_<slug>.md into the opted-in folder (atomic tmp+rename).

Containment (kd3e2, 2026-09-06; kmspc, 2026-09-09). npm run selftest:memory-containment (in the security gate list) guards these fences:

  1. Nothing configured means nothing touched. resolveCcMemoryFolder returns null with neither the env var nor the .local/cc-memory file, even when ~/.claude/memory exists, and a real RunStore done transition then writes nothing; the smoke proves both, plus both opt-in routes. Separately, ccMemorySuppression() still forces the bridge off for a test-shaped process (CYBERINE_CC_MEMORY=0, a CYBERINE_HOME under tmpdir(), or one containing .ci-cyberine-home), so a smoke that DOES opt in for its own fixture cannot reach a real folder.
  2. Files are keyed by the git toplevel, never the worktree. projectSlug(cwd) resolves git rev-parse --show-toplevel --git-common-dir; a linked worktree under .claude/worktrees/ maps to the main checkout's basename, a non-git directory to its own basename. A run from a worktree therefore lands on the PROJECT's handoff-cyberine_<project>.md. MemoryStore.baseSlug (the episodic store's per-project key) is still plain basename(cwd) — a different namespace with its own compatibility contract, left alone on purpose.
  3. The preamble reads this project only, and nothing younger than the run. readCcPreamble returns '' without a cwd (no more "newest file at the root"), filters on the project slug, and skips any file whose mtime is later than the moment the preamble was requested (buildMemoryPreamble captures that instant) — a smoke that finished a second ago is not context. Among the survivors it picks the newest via pickNewestHandoff (src/store/handoffPick.ts, shared with src/autopilot/handoffBridge.ts's cold-start selection, k0d3k): the last valid YYYYMMDD in the filename wins, mtime only breaks a same-date tie, and an undated file ranks below every dated one — never raw mtime alone, which an rsync or a touch on a stale handoff could otherwise make win. Guarded by npm run selftest:cc-memory-bridge-newest.

Known Limitations and Deferred Work. The handoff Cyberine writes is Cyberine's shape, not the operator's amemoria convention (no scope folder, no index pointer); an operator who opts in gets a flat handoff-cyberine_<slug>.md per project. Converting it to a foreign memory format was the old kmspc and was dropped with the fallback: the file exists only where the operator asked for it, and ~/.cyberine/memory/ is where Cyberine's own record lives.