Procedural memories are first-class category: procedure items stored under memoryDir/procedures/YYYY-MM-DD/ as markdown (same persistence path as other memories via StorageManager.writeMemory). They capture ordered steps (human-editable in the body) plus YAML frontmatter for lifecycle, provenance, and review state.
This feature tracks issue #519.
Also indexed from the repo README (Features + Configuration), Getting started (Next steps), and Config reference (Procedural memory section) so operators and agents see the procedural.enabled gate without opening this page first.
Everything behavioral is gated by plugin config procedural.enabled (default true since issue #567 PR 4/5; previously false). When explicitly disabled (false, "0", "no", or "off"):
- Direct extraction does not emit new procedure memories.
- Intent-gated recall does not inject a procedure section.
- The nightly miner MCP entry returns without writing files.
Operators who want to stay opt-out must set procedural.enabled: false explicitly. Mirror the same keys under openclaw.plugin.json / host config as for other Engram-style toggles.
If you are upgrading from a Remnic build where procedural memory shipped disabled (pre-#567), no action is required — existing memories and trajectory records continue to work. Fresh installs and any config that omits procedural.enabled now enable the feature using the safer-by-default thresholds from slice 3 (successFloor=0.75, lookbackDays=14, recallMaxProcedures=2). The lift number justifying the flip is documented in docs/benchmarks/procedural-recall.md and captured in the committed packages/bench/baselines/procedural-recall-baseline.json artifact.
MemoryCategoryincludes"procedure".- Default taxonomy exposes a
proceduresbucket (priority between principles and entities). category-dirmapsprocedure→procedures/.
When the extractor proposes category: "procedure", the extraction judge requires at least two steps and explicit trigger-style phrasing before the memory is accepted. Failed checks drop the candidate rather than downgrading silently.
On prompts that look like starting hands-on work (deploy, ship, open a PR, run tests, etc.), the orchestrator may inject a ## Relevant procedures block built from active procedure files only. pending_review miner suggestions are not injected by default.
Relevant config keys include:
procedural.recallMaxProcedures— cap on injected procedure previews. Default2(lowered from the earlier3in issue #567 PR 3/5 to keep procedural injection from crowding out other recall sections once the feature is enabled by default).
See also: Advanced retrieval and Retrieval pipeline.
The procedural mining + recall defaults are tuned so the feature stays safe when it is enabled by default in the slice-4 config flip:
| Key | Default | Notes |
|---|---|---|
procedural.minOccurrences |
3 |
At least three clustered trajectories before a candidate procedure is emitted. Set to 0 to disable mining entirely. |
procedural.successFloor |
0.75 |
Miner promotion requires ≥ 75% trajectory success. Raised from 0.7 in #567 PR 3 to reduce false positives. |
procedural.autoPromoteOccurrences |
8 |
When auto-promote is on, pending_review procedures wait for eight occurrences before becoming active. |
procedural.lookbackDays |
14 |
Trajectory miner window. Lowered from 30 in #567 PR 3 so mined procedures stay recent. |
procedural.recallMaxProcedures |
2 |
Cap on injected procedure previews per recall. Lowered from 3 in #567 PR 3. |
Operators who need to override any of these should do so explicitly; all fields accept CLI-style string inputs and JSON numbers.
A dedicated miner clusters causal trajectory records (bounded lookback by recordedAt / lookbackDays) and can write status: pending_review procedure candidates. Promotion to active respects optional auto-promote rules and avoids clobbering user-edited bodies.
Automation is not part of runMemoryGovernance. Use the MCP tool engram.procedure_mining_run (and optional cron registration mirroring other nightly jobs) so procedural mining stays isolated from shadow/apply governance.
Operators can inspect procedural memory health via three matched surfaces that all return the same ProcedureStatsReport JSON shape (schema v1):
- CLI:
remnic procedural stats [--format json|text] [--memory-dir <path>] - HTTP:
GET /engram/v1/procedural/stats?namespace=<optional> - MCP tool:
remnic.procedural_stats(with legacy aliasengram.procedural_stats), argument{ namespace?: string }
The report includes:
counts— procedure files by status:total,active,pending_review,rejected,quarantined,superseded,archived,other.recent—lastWriteAt(ISO 8601 ornull),writesLast7Days(exclusive upper bound per CLAUDE.md rule 35), andminerSourcedcount (procedures whosesource=procedure-miner).config— snapshot of the activeprocedural.*config so the caller can confirm the gate state and threshold floor in the same payload.
All three surfaces are read-only and namespace-scoped (CLAUDE.md rule 42). The HTTP endpoint resolves the namespace through the same layer used by /recall/explain and /trust-zones/status; the MCP tool uses the authenticated principal. The CLI reads from whatever memoryDir the current config / active-space resolves to, or an explicit --memory-dir override.
The remnic patterns command group exposes the pattern-reinforcement output written by the maintenance job (PR 2/4). Both subcommands read from the active memoryDir and require no extra config.
Lists memories whose reinforcement_count > 0, sorted by count descending.
remnic patterns list [--limit N] [--category cat1,cat2] [--since ISO] [--format text|markdown|json]| Flag | Description | Default |
|---|---|---|
--limit N |
Maximum rows to show (positive integer) | 50 |
--category list |
Comma-separated category filter | all categories |
--since ISO |
Only include memories reinforced on or after this ISO 8601 timestamp | all time |
--format fmt |
Output format: text, markdown, or json |
text |
Shows the full reinforcement picture for a single canonical: reinforcement count, last_reinforced_at, derived_from provenance chain (page-version refs stamped by PR 2/4), canonical body, and cluster members (memories whose supersededBy points at this canonical).
remnic patterns explain <memoryId> [--format text|markdown|json]Exits with code 1 and a descriptive error if <memoryId> is not found or has no reinforcement_count > 0.
Invalid flag values (--format xml, --limit 0, --since not-a-date) throw a listed-options error rather than silently defaulting (CLAUDE.md rule 51).
The procedural-recall benchmark in @remnic/bench scores:
- Task initiation gate — deterministic intent classification vs. labeled prompts.
- Procedure section gate — temp
memoryDirround-trip: whether a non-null recall section is produced when expected (feature on/off and non-task prompts).
Run a quick pass:
npm run bench:run -- --quick procedural-recall