Process Seam - alexherrero/agentm GitHub Wiki

Process seam reference

The memory↔process client seam (scripts/process_seam.py) gives you a small, read-only, graceful-no-op view. You call this seam from a process (like the crickets development-lifecycle phases today, or the V5-9 MCP server tomorrow). You use it instead of reaching into the memory engine's internals. It exports three functions. It composes these functions only from the DC-7-frozen public memory readers (resolve_project, resolve_active_plan, active_plan_paths, project_state_root, is_available β€” harness_state_dir retired under agentm-vault plan 15). It never uses a write path. The importable Python module forms the contract ([LC-1]). The python -m entrypoint gives you a convenience shim for non-Python shell callers.

Note

R0.9 (agentmEngine#2): A third function, recall_here, was retired. It went dead when the V5-3 vault-backend removal made it always return "". It has no live caller. The crickets' documenter sub-agent uses harness_memory.py's own documenter-context CLI verb instead.

⚑ Quick Reference

Function Signature Returns Memory absent β†’
offer_save_here offer_save_here(context, candidate) list[dict] β€” [enriched_candidate] (advisory; never persists) []
state_path state_path(context, which) Path β€” active plan/progress/tracker path repo-local <project_root>/.harness/<file> (never None)
project_path project_path(context, which) Path β€” the project's tasks/, designs/ or desk/ directory (named, never created) raises NoProjectHome (CLI exit 1) β€” no repo-local degrade

Important

Read-only invariant. The seam performs no writes. It imports the engine's public readers. It never imports or calls any write path. This makes "the seam is read-only" literally true. The offer_save_here function is advisory ([LC-2]). It returns save candidates. Persistence stays on the existing /memory save path (harness_memory.offer_save / the offer-save CLI verb).

The shared context dict

You pass a context dict (or None) to each function. The keys are optional:

Key Used by Meaning Default
cwd all three project root the process cwd
project project_path a project named by slug, for one with no repo checkout; it takes the place of cwd (none)
phase passed through by offer_save_here dev-loop phase, attached to the candidate if present (none)
plan state_path named-plan slug ("foo" β†’ PLAN-foo.md / progress-foo.md) the active/default plan

offer_save_here(context, candidate)

You use this to surface what could be saved from this context. You do this without saving it ([LC-2]). The function enriches the candidate with the resolved project slug and the vault target. It copies the candidate. It never mutates it. This lets you invoke the existing /memory save path.

Parameter Type Detail
context dict | None cwd selects the project root; phase is passed through onto the candidate if present.
candidate dict (or any) The caller's proposed save β€” shaped like the offer-save verb's inputs (e.g. {"kind", "slug", "body", "confidence"?, "confidence_reason"?}). Passed through, not validated: the seam stays minimal and lets the caller decide. A non-dict is wrapped as {"body": candidate}.
Condition Result
Memory present and a project resolves [enriched_candidate] β€” the candidate plus project (slug), an optional passed-through phase, and target (the vault path as a string, or None).
Memory / vault absent, no project resolves, or candidate empty [].

It never persists data. It imports no write path. It calls no write path. You (the caller) or the engine's reflection judge save-worthiness. The seam deliberately avoids judging this.

state_path(context, which)

You use this to resolve the harness state path for which in the current context. It wraps resolve_project and active_plan_paths (agentm-vault plan 15 dropped harness_state_dir from this composition β€” the seam now reads the same task-layout resolution resolve_active_plan returns). This gives you V5-10 named-plan awareness for free.

Parameter Type Detail
context dict | None cwd selects the project root; plan (optional) names a plan via resolve_active_plan's explicit-arg path.
which str "plan", "progress" or "tracker" β€” which file of the active plan. The tracker sits beside the pair: tracker.md inside a task directory, or tracker-<name>.md beside a repo-local pair.
Condition Result
A synced backend resolves A task's file β€” the already-absolute path inside its tasks/<name>/ directory (agentm-vault plans 09/15; there is no more vault-side _harness/ to fall through to).
No vault / memory configured Repo-local degrade <project_root>/.harness/<file> ([LC-3]) β€” never None.
which not "plan"/"progress"/"tracker" Raises ValueError β€” a caller bug, distinct from the absent-memory degrade.
.harness/active-plan marker present but dangling / names an unsafe slug Propagates harness_memory.ActivePlanError / ValueError β€” not swallowed.
context names no plan and the project keeps its plans in numbered tasks (agentm-vault plan 10) Propagates harness_memory.TaskNameRequired β€” the CLI shim answers exit 4, nothing on stdout, one line on stderr; an in-process caller catches it and asks which task.

Warning

The corrupt-marker case acts as a deliberate loud-fail safety property (V5-10 Risk #7). It stays distinct from the absent-memory degrade. You could mis-bind the worker to another plan if it silently degraded there. The seam lets the exception propagate. It does not fall back to repo-local.

project_path(context, which)

You use this to find one of a project's homes, so a plugin never composes a project layout itself (agentm-vault, resolve-the-project-homes). The project is the one bound to cwd through its .harness/project.json, or the one project names by slug. The answer is the project directory the resolver already names, with which joined onto it, so it is the same on the vault-root and memory-root layouts. It is returned whether or not the directory exists yet, and nothing is created.

Parameter Type Detail
context dict | None cwd selects the checkout; project names a project by slug instead.
which str "tasks", "designs" or "desk" β€” where a project's tasks, its designs, and its machine files (the board mirror, features.json, project.json, session markers, packs) live.
Condition Result
A synced backend resolves the project <project directory>/<which> β€” for example …/projects/demo/desk.
No project is bound to the checkout, no synced backend is active, or the project or the device is in local state mode Raises process_seam.NoProjectHome β€” the CLI shim answers exit 1 with the reason on stderr. A repo-local .harness/ holds no tasks, designs or desk, so there is no [LC-3] degrade here.
which is not a home, or the slug is not a single path component Raises ValueError β€” the CLI shim answers exit 2.

A project named by slug has no checkout, so only the device-level state_mode applies to it: a .harness/.project-mode marker in whatever repo the process happens to stand in is never read in its place.

python -m entrypoint

This acts as a thin shell shim ([LC-1]). You use it to expose the same functions to non-Python hosts. It always exits 0 on the graceful-no-op paths β€” the absent-memory degrade never wedges your process. state-path also exits 4 on one deliberate, non-graceful case: a bare call on a project that keeps its plans in numbered tasks, which has no singleton to answer with (agentm-vault plan 10). That's a refusal for the caller to act on, not a wedge β€” see the TaskNameRequired row above.

Subcommand Flags Emits
state-path which (positional: plan/progress/tracker), --cwd, --plan the resolved path on stdout
project-path which (positional: tasks/designs/desk), --cwd or --project SLUG (not both) the path on stdout, exit 0; exit 1 with the reason on stderr when the project has no vault home; exit 2 on a usage error or an unsafe slug
offer-save-here --cwd, --phase, --kind (req), --slug (req), --body-file (- = stdin), --confidence, --confidence-reason the advisory candidate list as indented JSON
python3 scripts/process_seam.py state-path plan
python3 scripts/process_seam.py project-path desk --project movies-tv-games
python3 scripts/process_seam.py offer-save-here --kind decision --slug foo --body-file -

The Python module forms the contract. The entrypoint gives you a convenience for shell callers.

Related

  • Named plans β€” the resolve_active_plan precedence, the task layout, the exit-4 bare-call refusal state_path wraps, and list-plans --project SLUG for a project with no checkout.
  • Memory↔process seam β€” This explains why the seam exists. It details the one-way dependency. It outlines the graceful-no-op philosophy.
  • CI gates β€” This shows the check-one-way-imports gate's process-seam rule. This rule enforces the one-way edge. (CONS-1 merged the former standalone check-process-seam-import-direction.sh into this config-driven Python checker).
  • AgentMemory context payload β€” This defines the read-only memory contract the seam composes over.
  • AgentM HLD β€” V5 unbundling β€” This records the decision that introduced the seam concept.
⚠️ **GitHub.com Fallback** ⚠️