Skip to content

Releases: alexherrero/agentm

v5.10.0 — Backend-aware harness state via the storage seam

19 Jun 21:13

Choose a tag to compare

MINOR. Harness state I/O is backend-aware again. harness_state_dir / read_state_file / write_state_file route to <vault>/projects/<slug>/_harness/ when a live synced backend is active and gracefully degrade to device-local <project_root>/.harness/ otherwise — reversing ADR 0018 DC-1's device-local-only state I/O while keeping every other V5-3 decision (DC-2–DC-7) intact.

The discriminator is backend.capabilities.sync on the backend resolve_project already returns (duck-typed — the kernel never imports the vault plugin), not the presence of a vault_path config key. Precedence: a .harness/.project-mode=local opt-out wins, then a synced backend → vault, else device-local. This is not a return to the pre-V5-3 vault_path() probe-and-prefer tier — it routes through the V5-6 resolve_project seam.

It fixes the surfacing regression where an operator on a synced backend kept their PLAN/ROADMAP/progress in the vault but the kernel read the empty device-local .harness/, so the SessionStart hook never injected them. Storage-seam no-Path-leak and import-direction gates remain green (the three state functions are outside the gate's routing-function set by design).

Changed

  • Backend-aware harness state — harness_state_dir / read_state_file / write_state_file (ADR 0020, reverses ADR 0018 DC-1). New private helper _state_backend_target(resolution) is the single routing decision: returns (backend, harness_locator, backend_root) when a synced backend should serve state, else None (device-local). Gated by _read_project_mode(resolution) == "local" (opt-out wins) → resolution["backend"].capabilities.sync (any exception → None, graceful degradation). Vault-absent / fresh-install / selection-raises all collapse to device-local through the existing resolve_project graceful path.

  • Session-start hooks co-locate progress.md with the resolved PLAN.md (.sh + .ps1). harness-context-session-start.{sh,ps1} previously hardcoded progress.md to the device-local <cwd>/.harness/, which blocked the singleton injection on a synced backend even after the kernel fix. They now derive progress.md as the sibling of whatever _harness/ the bridge resolved PLAN.md into, falling back to the device-local path only when no singleton plan resolved.

Internal

  • ADR 0020 — Backend-aware harness state. New decision record with DC-1–DC-6, full Consequences (positive / negative / load-bearing assumptions with re-audit triggers) and Related. ADR 0018 gains an Amendment — 2026-06-19 section recording that DC-1 is reversed while DC-2–DC-7 stand.

  • Tests — backend routing coverage. test_harness_memory.py gains TestStateBackendRouting (7 cases) plus a CLI vault-wins-over-stale-repo case with OBSIDIAN_VAULT_SCRIPTS pinned for deterministic plugin selection; test_queue_status_lite.py gains test_synced_backend_returns_vault. Vault fixtures seed LF-only bytes (write_bytes) so the byte-exact VaultBackend read is stable on Windows. CI green across Linux / Mac / Windows.

v5.9.1 — vault-path hygiene: check-no-hardcoded-vault-path gate

19 Jun 18:20

Choose a tag to compare

PATCH. A new CI gate prevents absolute vault-path literals from re-entering the codebase, paired with a prose lock of the runtime-resolve convention in `AGENTS.md`. No behavior changes; no new runtime code; gate count 21→22.

Added

`check-no-hardcoded-vault-path` gate — 22nd gate in `check-all.sh` (`034a846`). Fails if any non-test tracked file embeds an absolute `…/Library/CloudStorage/…` path literal or the retired pre-V5-3 vault root name `…/Obsidian/AgentMemory` as a path component. Shell tilde/variable expansions (`~/Library/CloudStorage`, `$HOME/Library/CloudStorage`) and placeholder notation (`<…>`, `…`) are allowed. Fourteen unit tests. Gate row added to CI-Gates.md.

Internal

`AGENTS.md` — `§ Vault-path convention — resolve, don't recall` (`306cbf0`). New subsection under Conventions: canonical resolver (`harness_memory.vault_path()`), `$MEMORY_VAULT_PATH` escape hatch, and the "why" (machine-specific path segments become silently stale across installs). Running-the-checks paragraph updated to list the 22nd gate.


Full changelog: CHANGELOG.md

v5.9.0 — V5-5: auto_orchestration trigger split

19 Jun 15:46

Choose a tag to compare

MINOR. The auto_orchestration push-surface is split into its three natural owners without changing behavior or raising autonomy. phase_dispatch() in harness_memory is formalized as the write-capable sibling bridge (non-blocking, graceful-skip, kernel-single-writer, _BRIDGE_PHASES = frozenset({"post-work", "post-release"})). auto_orchestration.py is declared the sole writer of _meta/auto-orchestration-state.json, gate-checked by a static assertion in verify-v4.sh. Session-start hooks delegate plan-file discovery through a new list_plan_files() public function + list-plans CLI verb (V5-6 state_mode/Locator-aware). verify-v4.sh fractured to kernel-only (A+E+G, 162→85 lines); new verify-orchestration-briefing.sh holds the PM-half (B+C+D); session-marker + discover-skills scenarios relocated to verify-phases.sh; check-all.sh 20→21 gates. Import-direction gate extended to assert no bridge back-edge. PM-half trigger remains kernel-side until the crickets PM-trigger plan ships (gate lifted — github-projects exists). Gates: 21/21, CI green across Linux/Mac/Windows.

What shipped

  • phase_dispatch() orchestration bridge formalized (a65f901)_BRIDGE_PHASES constant; 4 contract properties codified in docstring; ValueError on unrecognized phase; 21 contract tests in test_orchestration_bridge.py.
  • list_plan_files() + list-plans CLI verb (a7e3bee) — canonical PLAN*.md enumeration; session-start hooks delegate discovery here; routes through harness_state_dir() Locator chain; 13 new tests.
  • Single-writer invariant declared + gate-checked (15da187)auto_orchestration.py declared sole state writer; static assertion in verify-v4.sh segment G.
  • verify-v4.sh fractured (8e4b170) — kernel-only (A+E+G); verify-orchestration-briefing.sh new (PM-half B+C+D); session-marker + discover-skills → verify-phases.sh; check-all.sh 20→21 gates.
  • Bridge back-edge gate (0fcdfb0)check-process-seam-import-direction.sh extended; 3 new tests.
  • Docs: PM-half + Orchestration-Bridge reference (9dffa4d, 4d3622c)Auto-Orchestration.md trigger-ownership section; new wiki/reference/Orchestration-Bridge.md; ADR 0011 DC-1 re-audit trigger filed.

Related

v5.8.0 — V5-7 config-plane: plugin-namespaced vault_path + explicit backend selection

19 Jun 14:20

Choose a tag to compare

MINOR. The kernel no longer owns obsidian-vault's config. vault_path moves from the flat kernel key "vault_path" to "plugins.obsidian-vault.vault_path" — the first plugin-namespaced config key. Existing operators self-heal on first use via a first-read migration. choose_protocol() loses its implicit config-based vault-inference step; vault selection is now always explicit. Resolution chain: config → $MEMORY_VAULT_PATH env var → device-local.

See CHANGELOG.md for the full entry.

Highlights

  • harness_memory.vault_path() now reads plugins.obsidian-vault.vault_path; first-read migration auto-heals legacy configs (writes plugin key + storage.backend=vault)
  • agentm_config --vault-path writes to the plugin-namespaced key; --get/--unset have backward-compat legacy fallback
  • choose_protocol() removes the config-based implicit vault inference; $MEMORY_VAULT_PATH env var remains an explicit escape hatch (step 2 of the 3-step chain)
  • ADR 0013 amended to record the V5-7 config-plane design calls

v5.7.0 — V5-6 routing-plane de-vaulting

19 Jun 04:25

Choose a tag to compare

MINOR. The kernel's routing layer is now backend-agnostic. Three mechanisms that previously built vault_path() / … filesystem paths — resolve_project / _vault_projects_dir, repo_registry, and the state_mode resolver — now speak Locators to the V5-1 storage seam. On the obsidian-vault backend behavior is byte-identical (LC-1). A fresh install with only device-local can now host a project, its harness state, and the repo registry without a vault. state_mode: vault in device config and .project-mode markers is aliased to state_mode: backend at read time — no operator migration required (LC-5). Gate extensions enforce the no-Path-leak and one-way-import-direction invariants on the routing layer. AGENTM_DEVICE_LOCAL_ROOT env-var override added for test isolation in CI environments without the obsidian-vault plugin. This is the third and final leg of the V5 de-vaulting arc. Gates: 20/20, CI green across Linux/Mac/Windows.

Added

  • V5-6 — resolve_project / _vault_projects_dir via seam (b762987). harness_memory._vault_projects_dir signature changed from (vault: Path) -> Path to (backend: StorageBackend) -> Locator; resolve_project returns {slug, project_locator, backend, project_root, layout} instead of {slug, vault_path, project_root, layout}. Callers in process_seam.py and memory_mcp_tools.py updated to use project_locator.key. 10 new tests. LC-7 parallel-run: VaultBackend Locator resolves to same on-disk path.

  • V5-6 — repo_registry onto the seam (3af7fa6). registry_path(vault) -> Path replaced by registry_locator(backend) -> Locator; _vault_or_none() replaced by _backend_or_none() (lazy-imports select_backend()); all five public functions (read_registry, write_registry, register_repo, unregister_repo, list_repos) now take StorageBackend. _mutate_registry drops vault_mutex (held internally by VaultBackend.write()); write_registry adds an explicit content-hash CAS. CLI graceful-skip fires when select_backend() raises, not merely when MEMORY_VAULT_PATH is unset. 10 new/rewritten tests.

  • V5-6 — state_mode: vaultbackend alias (b62e378). _read_config_state_mode and _read_project_mode alias "vault""backend" at read time — one-line guard, no rewrite, no operator migration (LC-5). agentm_config._STATE_MODES adds "backend" as the canonical value; cmd_set_state_mode normalizes "vault""backend" at write time. 5 new/updated tests.

  • V5-6 — gate extensions + conformance suite for routing layer (51b1a32). check-storage-seam-no-path-leak.py Pass 2 checks 8 named routing functions in harness_memory.py and repo_registry.py for pathlib.Path return annotations. check-process-seam-import-direction.sh LC-8 block scans routing files for storage_vault imports. storage_conformance.py gains check_routing_repo_registry() and ROUTING_CHECKS; ConformanceSuite.test_routing_repo_registry() is now inherited by all conformance subclasses. 8 new tests across three gate test files.

  • V5-6 — ADR 0019 + wiki sweep (e185946). ADR 0019 records the three-leg de-vaulting arc completion, all locked design calls (LC-1/4/5/6/7/8), and load-bearing assumptions with re-audit triggers. Decisions.md + decisions/_Sidebar.md updated. device-wide-architecture.md v1.0 entry finalized. Storage-Seam.md routing layer NOTE updated to complete. Single-Repo-State-Mode.md updated: state_mode value "vault""backend" with backward-compat note.

Internal

  • CI test isolation — AGENTM_DEVICE_LOCAL_ROOT + backend mocks (4a28b1c). storage_device_local._default_root() reads $AGENTM_DEVICE_LOCAL_ROOT to redirect the device-local root in CI without the vault plugin. 2 subprocess CLI tests updated to use this env var; 2 in-process tests updated to patch backend_selection.select_backend directly.

  • CI verify-phases.sh vault pass fix (d055423). Set OBSIDIAN_VAULT_SCRIPTS=$REPO/scripts so the vault pass can load the kernel VaultBackend as a stand-in when the crickets obsidian-vault plugin is absent in CI. verify-phases: 32/32 (was 31/32).

Full changelog: See CHANGELOG.md.

v5.6.0 — V5-7 capability-request matching

19 Jun 01:09

Choose a tag to compare

MINOR. select_backend() now accepts a required: Capabilities | None = None keyword parameter. When provided, the resolved backend's .capabilities must satisfy every True flag — a subset check. Mismatch raises CapabilityMismatchError (new StorageSelectionError subclass) naming the backend protocol and all unsatisfied fields. Operators can pre-flight requirements without code via doctor --requires <cap1,cap2>. Zero existing callers updated. Gates: 20/20, CI green across Linux/Mac/Windows.

Added

  • CapabilityMismatchError (03aceb5) — new StorageSelectionError subclass; existing except StorageSelectionError catch sites cover it automatically.
  • select_backend(required=) (03aceb5) — subset-check of resolved backend capabilities; 8 new tests.
  • doctor --requires (5a6f94c) — pre-flight CLI flag; unknown field names rejected before backend construction; 5 new tests.

Internal

  • ADR 0013 amendment: subset-only matching is fail-loud, never a silent downgrade.
  • Closed V5-7 deferral marker in backend_selection.py module docstring.

v5.5.0 — V5-3 storage cutover: device-local is canonical

18 Jun 14:01

Choose a tag to compare

MINOR. The kernel no longer contains a built-in vault backend. State (harness_state_dir, read_state_file, write_state_file) is device-local only; phase_recall returns ""; resolve_documenter_context returns None. The vault is reachable only through the obsidian-vault plugin; a config-file storage.backend=vault with no vault accessible now raises StorageBackendNotInstalledError — never a silent demotion to device-local. Four locked design calls in ADR 0018. Gates: 20/20, CI green across Linux/Mac/Windows.

What shipped

  • V5-3 — lock A2 index invariant (53f96fa). scripts/test_a2_index_invariant.py — gate confirming vector index stays under ~/.cache/agentm/, never inside the synced vault root.
  • V5-3 — vault-root rename tooling (cd78cdd, 1358e1d). scripts/rename-vault-root.sh — Phase A backup+runbook, Phase B string sweep across 36 files. Used to execute the AgentMemory → Agent rename.
  • V5-3 — group rename: personal-private → personal (053217b). 3-part lockstep: kernel _ALWAYS_LOAD_REL, plugin vault_probe.py, 10 skill scripts.
  • V5-3 — delete vault backend from kernel; device-local state only (8cb2b48). harness_memory.pyharness_state_dir/read_state_file/write_state_file unconditional device-local; phase_recall""; resolve_documenter_contextNone.
  • V5-3 — vault_path() fail-loud guard (05e63d3). StorageBackendNotInstalledError raised when storage.backend=vault configured but no vault accessible. $MEMORY_VAULT_PATH env override remains graceful-skip. Six tests in TestVaultPathGuard.
  • V5-3 — ADR 0018; HLD v0.9; ADR 0010/0013 amendments (8ce8824). Seven V5-3 design calls documented. ADR 0010 re-audit trigger #1 closed. ADR 0013 negative bullets resolved.

See also

v5.4.0 — V5-9 memory MCP server

17 Jun 19:07

Choose a tag to compare

MINOR. The memory engine is now reachable from any MCP host — Claude Code, Cursor, Goose, Claude Desktop — through a single local HTTP daemon. Four snake_case tools (memory_search, memory_recall, memory_append, memory_forget), singleton streamable-HTTP broker, static bearer auth, mandatory Origin-validation, and soft-delete. The daemon is writer #2 alongside the CLI, routing all MCP-host writes through the V5-0 vault_lock protocol. Five locked design calls in ADR 0017. Gates: 20/20, CI green across Linux/Mac/Windows.

Added

  • Server skeleton + liveness probe (cfda095) — scripts/memory_mcp_server.py, FastMCP 3.x, 127.0.0.1:7821, /health
  • Four-tool surface (ccdcb98) — memory_search / memory_recall / memory_append / memory_forget; in-memory test client
  • Writer-routing + vault-source-resolution (fb4c4ef) — resolves through V5-1 backend_selection (vault-primary); writer #2 composing full V5-0 mutex/CAS stack; never a hardcoded repo allowlist
  • Security layer (cd8d358) — TokenVerifier (bearer auth), _OriginValidator ASGI middleware (→403 on non-loopback Origin), path-traversal validation
  • Stdio shim + host configs (e895cef) — memory_mcp_stdio_shim.py for Claude Desktop; config snippets for Claude Code / Cursor / Goose / Claude Desktop
  • Operations & docs (c9d79dc) — memory_mcp_doctor.py (4 checks, 13 tests), launchd plist template, install.sh --mcp-server, doctor skill check 4e, ADR 0017, Stand-Up-Memory-MCP-Server how-to, Memory-MCP-Tools reference

Internal

  • Fix check-lib-parity / sync-lib: exclude __pycache__ from checksums (67bf8a7)
  • Fix check_index_root_safe: normalize to POSIX path before sync-marker check — Windows compat (1e11426)

How to connect

See Stand up the memory MCP server in the wiki. Verify with python3 scripts/memory_mcp_doctor.py --live.

Deferred to v1.1: remote tier (outbound tunnel + OAuth 2.1), Windows daemon parity, memory_get fetch verb.

v5.3.0 — V5-11 team-coordinator persona

17 Jun 04:30

Choose a tag to compare

V5-11 — team-coordinator persona

The first composed persona: the team-coordinator reads the vault, computes answers, and hands the operator decision-ready recommendations — where the team stands, which plans are safe to run together, and what order to merge in. Advisory only; zero execution authority. Built on the V5-12 persona tier substrate (v5.2.0).

The one rule that holds everything up: answers are computed, not guessed. Every verdict comes from plain code reading the vault; the model writes the prose on top.

Added

  • personas/team-coordinator.mdkind: persona, requires: [queue_status_lite], enhances: [developer-workflows, github-projects]
  • scripts/plan_graph.py — shared map engine; reads active + queued plans; parses depends_on: / touches: from YAML frontmatter; 17 tests
  • scripts/standup.py — worker-state derivation (building / mergeable / idle, 2h threshold); 13 tests
  • scripts/readiness.py — dep readiness + file-overlap safe-to-run-together; loud degrade when touches: absent; 9 tests
  • scripts/merge_order.py — Kahn topo-sort + git-diff-stat tie-break + alphabetical fallback + cycle detection; 12 tests

62 fixture-backed unit tests, no live vault required. 20/20 check-all.sh gates. CI green Linux / macOS / Windows.

Paired release: agentm v5.2.0 shipped the V5-12 persona tier substrate this persona builds on.

v5.2.0 — V5-8 capability-discovery resolver + V5-12 persona tier

16 Jun 21:07

Choose a tag to compare

V5-8 — capability-discovery resolver: the enhances: runtime

scripts/capability_resolver.py implements the soft-composition runtime: a capability-keyed, graceful-degrade resolver that answers "is capability X present on this host?" by reading installed-plugin manifests as data (never importing plugin code). A primitive declares enhances: [capability-name]; the resolver answers present/absent at load time and the caller degrades cleanly when absent. enhances ∩ requires = ∅ is a hard invariant. The check-capability-resolver-one-way gate asserts the resolver never imports plugin code (ADR 0015 DC-5).

V5-12 — persona tier: kind: persona primitive, check-personas gate, rememberer

Names the missing third architectural tier — a persona is a standing concern that composes capabilities it does not own (arbitrating among them when it composes ≥2), anchored on the neutral substrate, with hard deps (requires:) restricted to substrate-native primitives only. Zero new runtime. Three deliverables: personas/ directory; personas/rememberer.md (the degenerate first persona, naming the memory engine); scripts/check-personas.py (static gate: requires ⊆ substrate-native + no-always-load, wired into check-all.sh + CI). The chief-of-staff (first real persona, V5-11) is gated behind this substrate.

Gates

20/20 local · CI ✅ Linux / Mac / Windows

ADRs

Prior release

v5.1.0 — V5-2 kernel-thinning + #46 token-efficiency