Memory Process Seam - alexherrero/agentm GitHub Wiki
Why a process β a dev-loop phase, an MCP server β talks to the memory engine through a small stable client rather than reaching into engine internals, and why that client dependency is one-directional and gate-enforced. The seam concept was introduced by the AgentM HLD β V5 unbundling; this note explains the shape it took. For the function-by-function contract, see Process seam.
The V5 unbundling moved the phase loop (Setup Β· Plan Β· Work Β· Review Β· Release Β· Bugfix) out of agentm and into the companion crickets plugins, leaving agentm as the durable-state substrate and memory engine those phases run on. That split created a question it did not answer: how does a phase β now living in a different repo β read recall context or resolve where PLAN.md lives?
The tempting answer is "import harness_memory and call whatever you need." That couples every consumer to the engine's private shape: a phase would reach for an internal helper, the engine would refactor it, and the phase would break β across a repo boundary, where the breakage is hardest to see coming. The seam is the deliberate alternative. It is a single small client (scripts/process_seam.py) exposing exactly two operations a process actually needs:
- offer save here β "here's something I might save; tell me where it would go" (advisory only β it never saves).
-
state path here β "where do this project's
PLAN.md/progress.md/tracker.mdlive?"
(A third operation, recall here, was retired in R0.9 β it delegated to a V5-3 stub that always returned empty; no live crickets consumer called it.)
Every one of those composes only the engine's frozen public readers (the DC-7 surface: resolve_project, resolve_active_plan, active_plan_paths, is_available β harness_state_dir retired under agentm-vault plan 15). A consumer that needs something the frozen API lacks needs a separate engine change β it can't quietly widen the seam. The engine keeps the freedom to refactor everything behind that public surface; the process only ever sees the seam.
The seam imports the engine. The engine must never import the seam back.
This is not a style preference β it is what keeps the seam a seam. The whole point is a clean layering: memory is the lower, stable substrate; the process is the upper, swappable layer that depends on it. A back-edge β any memory-engine module importing process_seam β would invert that. It would turn a one-way client dependency into a cycle, and worse, it would let an engine change reach up into process concerns: the exact coupling the seam exists to prevent. Once the engine knows about its callers, "refactor freely behind the public surface" stops being true, and the V5 unbundling's clean substrate/plugin split quietly rots.
process (crickets phases, V5-9 MCP server)
β imports
βΌ
process_seam.py ββimportsβββΊ harness_memory (frozen public readers)
β² β
βββββββββββ NEVER ββββββββββββ
(engine must not import the seam)
So the rule is stated plainly ([LC-4]): memory never imports the process. And because a rule that only lives in prose decays, it ships as an executable gate, not a guideline.
check-process-seam-import-direction.sh is the enforcement. It scans agentm's Python automation surfaces for any module that imports process_seam and fails on a hit. The reasoning that makes a simple scan sufficient: within agentm, the seam's only legitimate importer is its own test suite. The designed consumers β the crickets phases, the future MCP server β live outside this repo ([LC-5]). So any in-repo importer that isn't a test is, by construction, the engine reaching for the seam β a forbidden back-edge. The gate excludes the tests (they import by design) and carries an explicit, currently-empty SEAM_CONSUMERS allowlist, so the day a reviewed in-repo process-side consumer appears, the back-edge question gets answered in the open rather than by a silent gate edit. It rides the local battery (check-all.sh) and runs cross-OS via the auto-discovered unit-tests step, since its subprocess tests live in test_process_seam.py. See CI gates for the row.
A subtle pairing reinforces the layering: agentm defines and ships the seam, but its consumers adopt it crickets-side in a separate plan ([LC-5]). The producer and the consumers are deliberately on opposite sides of the repo boundary β the same boundary the one-way rule protects.
The seam is built so a memory-absent install still works. agentm must run on machines without a vault mounted β CI, a fresh device, a contributor who never set one up. If the seam hard-failed when memory was unavailable, every process that called it would wedge, and the optional memory engine would become a hard dependency through the back door.
So every function degrades rather than raising on the absent-memory path:
-
offer_save_herereturns[]when memory is absent or there's nothing to offer. -
state_pathdegrades to repo-local<project_root>/.harness/<file>([LC-3]) β neverNoneβ so a vault-less repo still resolves its plan and progress files.
There is one deliberate exception to the degrade, and it is a safety property, not an oversight: when state_path finds a present-but-corrupt .harness/active-plan marker β dangling, or naming an unsafe slug β it lets the exception propagate (V5-10 Risk #7). Silently degrading there could mis-bind a worker to the wrong plan, which is worse than a loud failure. The distinction is the whole design in miniature: absent memory degrades quietly; corrupt state fails loudly.
The other half of the same philosophy is the read-only stance. The seam never writes β offer_save_here is advisory, returning candidates the caller hands to the existing /memory save path ([LC-2]). A read-only client that degrades gracefully is one a process can call from anywhere without first checking whether memory exists, and without any risk that calling it mutates the vault.
-
Process seam β the function-by-function reference (signatures, degrade contracts, the
python -mentrypoint). -
CI gates β the
check-process-seam-import-directiongate enforcing the one-way edge. - AgentM HLD β V5 unbundling β the decision that split the phase loop out and introduced the seam concept.
- Single-repo (vault-less) state mode β the broader "runs without a vault" stance the seam's graceful degrade participates in.