Skip to content

v4.39.0 — merge-scale memory: one Open Thread per file

Choose a tag to compare

@acn-ericlaw acn-ericlaw released this 01 Sep 18:57
· 2 commits to main since this release
Immutable release. Only release title and notes can be modified.
b8fdecf

Highlights

  • Merge conflicts in shared memory are now removed structurally, not resolved. As team
    adoption grew, memory/continuity.md merge conflicts became regular business,
    concentrated in ## Open Threads (multi-line blocks appended and edited at one anchor)
    and the last_session scalar (bumped by nearly every session). This release takes the
    conflict surfaces away instead of automating their resolution — no merge-time machinery,
    no per-clone registration, nothing to keep in sync. Design record:
    docs/DESIGN-merge-scale.md.
  • One thread, one file. Each Open Thread lives in its own
    memory/open-threads/thread-<id>.md — filename = the thread's fact id (stable for its
    lifetime), content = exactly the bullet block that previously sat in continuity. Parallel
    work on different threads cannot conflict (different files). Both sides editing the
    same thread still conflicts per-file — that is the MERGE.md Tier 2 human gate,
    preserved by design and pinned by a mutation-checked test: a clean merge there would
    be a silent fork, the exact failure the never-pick-a-winner invariant forbids.
  • The directory is the index, like sessions/: no index file (an index line per thread
    would recreate the add/add conflict one line at a time). ls memory/open-threads/ lists
    the threads; the checkbox in each file is its state.
  • last_session is gone — it was fully derivable (newest sessions/ filename; the
    agent name is that log's **Agent:** header) and the most frequent scalar conflict.
  • Archive files union-merge (memory/archive/*.md merge=union in .gitattributes,
    git-native): concurrent review sweeps' appends keep both sides; the one wrong case — a
    reactivation's removal resurrected — is caught deterministically by the existing
    [both] / [over-archived] ERRORs.
  • With credit to Roland Heusser (#27): his field artifact (two stacked branches, both
    appending a thread and bumping last_session) motivated the shape of this release, his
    [duplicate-state-key] check ships essentially as written, and his merge-rehearsal test
    pattern pins the new layout's behavior. The PR's merge driver itself was closed after
    review findings (verified against the branch; recorded in the design doc), with a
    scalars-only driver pre-scoped as the follow-up if scalar-bump conflicts persist.

What's changed

  • Layout: memory/open-threads/ (one thread-<id>.md per Open Thread); continuity's
    ## Open Threads section is now a pointer note; last_session removed from Project
    State; memory/archive/*.md merge=union in .gitattributes; reviews run serialized
    (REVIEW.md: up-to-date default branch, committed promptly).
  • Scripts (all three built-in pairs, python + node at byte parity): memory-lint
    treats thread files as live fact surfaces (footers, checkbox pinning, conflict-marker and
    secret scans, closed-thread bloat) and gains three checks — [thread-file] (one block
    per file, filename = footer id), [duplicate-id] (an id exists exactly once across the
    live layer — the silent-fork backstop for same-id creation collisions), and
    [duplicate-state-key] (a Project State scalar set twice). refresh-metadata refreshes
    thread-file footers in place (filenames never churn). archive-fact sweeps a thread by
    moving its block to the quarter archive + INDEX.md and deleting the file.
  • Contract test: tests/test_thread_layout_merge.sh — four real-git-merge cases:
    different-threads merge clean; same-thread edits conflict (the load-bearing case);
    archive union keeps both sides; and a mutation check (stripping the union attribute must
    restore the conflict).
  • Lockstep: DECAY.md §1/§12, REVIEW.md, MERGE.md, .agent/schema.md (new
    memory/open-threads/ section), ENABLE.md 5b/5c/7b, MIGRATE.md §D, both
    memory/PROTOCOL.md copies (activation reads the thread files; the close ritual creates
    them; no last_session), all four bootstrap "still scan" notes, templates, MANIFEST.md
    (new row + two semantic steps, including the mandated protocol-propagation row),
    UPGRADE.md ladder row + 4.38.1 → 4.39.0 rung, CHANGELOG, README, example fixtures
    annotated (real fixtures left unfalsified), mkdocs nav.
  • Dogfooded: this repo's own 35 threads migrated (23 verbatim extractions + 12 legacy
    items given minted ids per the 3.0.0 backfill rule); continuity shrank 387 → ~130 lines.
  • Suites: memory-lint 67 ×2, refresh-metadata 6 ×2, archive-fact 9 ×2, reconcile 38 ×2
    • --check-manifest OK; live-tree lint 0 errors / 0 warnings, byte-identical across
      runtimes.

Upgrading an enabled repo

Say "AI enable this repo" as usual — Mode B reconciles against MANIFEST.md (doc
re-copies, the three updated skill dirs, the .gitattributes union rule via
sentinel-merge, the memory/open-threads/ seed), then two semantic steps do the judgment
half: (1) thread migration — every Open Thread block moves verbatim out of
continuity.md into its own thread-<id>.md (a legacy thread with no footer gets one
minted first), the section body becomes the pointer note, and the last_session line is
deleted; (2) protocol re-copy/arbitrate — a still-stock memory/PROTOCOL.md is
re-copied, a customized one arbitrated per ENABLE.md §5i. memory-lint gates the
migration: [thread-file] and [duplicate-id] fail on a mis-named file or an id left
behind. Full steps: the 4.38.1 → 4.39.0 rung in UPGRADE.md.

Full details

  • CHANGELOG.md — Version 4.39.0
  • docs/DESIGN-merge-scale.md — the design record, including the evaluated-and-rejected
    merge-driver alternative and what was deliberately deferred
  • UPGRADE.md — the 4.38.1 → 4.39.0 rung