Releases: alexherrero/agentm
v5.10.0 — Backend-aware harness state via the storage seam
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, elseNone(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 existingresolve_projectgraceful path. -
Session-start hooks co-locate
progress.mdwith the resolvedPLAN.md(.sh+.ps1).harness-context-session-start.{sh,ps1}previously hardcodedprogress.mdto the device-local<cwd>/.harness/, which blocked the singleton injection on a synced backend even after the kernel fix. They now deriveprogress.mdas the sibling of whatever_harness/the bridge resolvedPLAN.mdinto, 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-19section recording that DC-1 is reversed while DC-2–DC-7 stand. -
Tests — backend routing coverage.
test_harness_memory.pygainsTestStateBackendRouting(7 cases) plus a CLI vault-wins-over-stale-repo case withOBSIDIAN_VAULT_SCRIPTSpinned for deterministic plugin selection;test_queue_status_lite.pygainstest_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
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
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_PHASESconstant; 4 contract properties codified in docstring;ValueErroron unrecognized phase; 21 contract tests intest_orchestration_bridge.py.list_plan_files()+list-plansCLI verb (a7e3bee) — canonical PLAN*.md enumeration; session-start hooks delegate discovery here; routes throughharness_state_dir()Locator chain; 13 new tests.- Single-writer invariant declared + gate-checked (
15da187) —auto_orchestration.pydeclared sole state writer; static assertion inverify-v4.shsegment G. verify-v4.shfractured (8e4b170) — kernel-only (A+E+G);verify-orchestration-briefing.shnew (PM-half B+C+D); session-marker + discover-skills →verify-phases.sh;check-all.sh20→21 gates.- Bridge back-edge gate (
0fcdfb0) —check-process-seam-import-direction.shextended; 3 new tests. - Docs: PM-half + Orchestration-Bridge reference (
9dffa4d,4d3622c) —Auto-Orchestration.mdtrigger-ownership section; newwiki/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
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 readsplugins.obsidian-vault.vault_path; first-read migration auto-heals legacy configs (writes plugin key +storage.backend=vault)agentm_config --vault-pathwrites to the plugin-namespaced key;--get/--unsethave backward-compat legacy fallbackchoose_protocol()removes the config-based implicit vault inference;$MEMORY_VAULT_PATHenv 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
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_dirvia seam (b762987).harness_memory._vault_projects_dirsignature changed from(vault: Path) -> Pathto(backend: StorageBackend) -> Locator;resolve_projectreturns{slug, project_locator, backend, project_root, layout}instead of{slug, vault_path, project_root, layout}. Callers inprocess_seam.pyandmemory_mcp_tools.pyupdated to useproject_locator.key. 10 new tests. LC-7 parallel-run:VaultBackendLocatorresolves to same on-disk path. -
V5-6 —
repo_registryonto the seam (3af7fa6).registry_path(vault) -> Pathreplaced byregistry_locator(backend) -> Locator;_vault_or_none()replaced by_backend_or_none()(lazy-importsselect_backend()); all five public functions (read_registry,write_registry,register_repo,unregister_repo,list_repos) now takeStorageBackend._mutate_registrydropsvault_mutex(held internally byVaultBackend.write());write_registryadds an explicit content-hash CAS. CLI graceful-skip fires whenselect_backend()raises, not merely whenMEMORY_VAULT_PATHis unset. 10 new/rewritten tests. -
V5-6 —
state_mode: vault→backendalias (b62e378)._read_config_state_modeand_read_project_modealias"vault"→"backend"at read time — one-line guard, no rewrite, no operator migration (LC-5).agentm_config._STATE_MODESadds"backend"as the canonical value;cmd_set_state_modenormalizes"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.pyPass 2 checks 8 named routing functions inharness_memory.pyandrepo_registry.pyforpathlib.Pathreturn annotations.check-process-seam-import-direction.shLC-8 block scans routing files forstorage_vaultimports.storage_conformance.pygainscheck_routing_repo_registry()andROUTING_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.mdupdated.device-wide-architecture.mdv1.0 entry finalized.Storage-Seam.mdrouting layer NOTE updated to complete.Single-Repo-State-Mode.mdupdated:state_modevalue"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_ROOTto 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 patchbackend_selection.select_backenddirectly. -
CI
verify-phases.shvault pass fix (d055423). SetOBSIDIAN_VAULT_SCRIPTS=$REPO/scriptsso the vault pass can load the kernelVaultBackendas 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
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) — newStorageSelectionErrorsubclass; existingexcept StorageSelectionErrorcatch 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.pymodule docstring.
v5.5.0 — V5-3 storage cutover: device-local is canonical
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 theAgentMemory → Agentrename. - V5-3 — group rename: personal-private → personal (
053217b). 3-part lockstep: kernel_ALWAYS_LOAD_REL, pluginvault_probe.py, 10 skill scripts. - V5-3 — delete vault backend from kernel; device-local state only (
8cb2b48).harness_memory.py—harness_state_dir/read_state_file/write_state_fileunconditional device-local;phase_recall→"";resolve_documenter_context→None. - V5-3 —
vault_path()fail-loud guard (05e63d3).StorageBackendNotInstalledErrorraised whenstorage.backend=vaultconfigured but no vault accessible.$MEMORY_VAULT_PATHenv override remains graceful-skip. Six tests inTestVaultPathGuard. - 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
- ADR 0018 — full design record.
- Completed Features — V5-3 — narrative summary.
v5.4.0 — V5-9 memory MCP server
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-1backend_selection(vault-primary); writer #2 composing full V5-0 mutex/CAS stack; never a hardcoded repo allowlist - Security layer (
cd8d358) —TokenVerifier(bearer auth),_OriginValidatorASGI middleware (→403 on non-loopback Origin), path-traversal validation - Stdio shim + host configs (
e895cef) —memory_mcp_stdio_shim.pyfor 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
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.md—kind: persona,requires: [queue_status_lite],enhances: [developer-workflows, github-projects]scripts/plan_graph.py— shared map engine; reads active + queued plans; parsesdepends_on:/touches:from YAML frontmatter; 17 testsscripts/standup.py— worker-state derivation (building / mergeable / idle, 2h threshold); 13 testsscripts/readiness.py— dep readiness + file-overlap safe-to-run-together; loud degrade whentouches:absent; 9 testsscripts/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
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