This plugin reads your work session and your memory stores to consolidate durable facts. Because it touches transcripts and persistent memory, here is exactly what it does, what it never does, and how to report a problem.
- Reads (local only): the current project's git log, the repo memory docs
(
MEMORY.md/AGENTS.md/CLAUDE.md), Claude Code's per-project auto-memory (resolved by StoreContext — default~/.claude/projects/<slug>/memory/), domain canonicals under~/.claude/consolidate-memory/domains/<domain>/facts/(legacy~/.claude/memory/is a read-only migration source), and — via the bundledextract_signals.py— the tail signal of the active session transcript (it streams the.jsonl; it never bulk-loads or copies it). - Writes (local only): memory fact files + index in the native store and the
project's enrolled domain (unenrolled projects cannot write canonicals), plus
operational state under plugin-data. Not all writes wait for Phase 4: Phase 1
--pull/--harvestreplicate already-approved canonicals and usage ledgers before the Phase-4 proposal. Authoring, deletion, promotion, migration, and committed-doc edits stay report-then-apply. - Never: makes network calls, sends telemetry, or transmits any of your data
anywhere. Every script is Python 3 stdlib only — no third-party packages, no
pip install.
- No code execution surface: no
eval/exec/os.system/shell=True. The only external process isgit(read-only:rev-parse,log), invoked with a fixed argument list (never a shell string). - Argument-injection guard: the commit SHA read from the on-disk state file is
validated as hex before being passed to
git(memory_status._valid_sha), so a tampered state file cannot injectgitoptions. - Secrets firewall at retrieval:
extract_signals.pydrops any session turn that contains a credential-shaped value to a label — the verbatim secret never reaches a memory file (repo docs are committed; auto-memory persists). It records a pointer, never a value.distill_scan.py(the workflow-recurrence reader) is the other transcript consumer and applies the sameextract_signals._looks_secretfirewall, at the point of EMISSION (v0.1.58): a credential-shaped command still counts into its command-CLASS template (so recurrence stays accurate) and into ascanned.secrets_omittedtransparency counter, but its raw text can never surface — its displaysamplebecomes an omission label, and every emitted template is screened through the same firewall (on the_norm'd form, so a zero-width-split secret is caught) before it can become a row or a chain endpoint. - Bounded input: transcript turns are length-capped (
_PROBE_CAP= 4000 chars) before regex classification (defense-in-depth); the regexes have no catastrophic backtracking — each alphanumeric run and its required separator are disjoint, so there's no ambiguity to blow up — and the length cap bounds worst-case matching regardless. - Filesystem safety:
sync_global.py --gconly deletes files marked as managed mirrors (global_ref:) whose canonical is gone — never project-authored facts — and defaults to report-only (deletion requires--apply).
Only plugins/consolidate-memory/ is packaged. Your personal memory store (memory/
at the repo root) is gitignored and never published — verify with
git ls-tree -r --name-only origin/main | grep memory (only memory/.gitkeep).
tests/, security/, and operational state stay outside the plugin directory.
- Pin what you install. Install from the marketplace via Git
(
/plugin marketplace add Zenetusken/consolidate-memory); a tagged release or pinnedshagives reproducible installs. - Plugins are copied to a local cache on install and run from there; this plugin adds
exactly one hook — a SessionStart beacon (matchers
startup+resume, 2 s timeout) that may inject at most one read-only advisory line — and no MCP servers and no background processes: skill + scripts + that one advisory hook.
These attacker stories are what ADRs 008–016 close. They are not a pentest
report; they are the stories cm doctor / mutating commands must fail closed on.
- Newly cloned / malicious unenrolled repo. Must not create or pull
cross-project canonicals.
unknownis local-only. - Corrupt
control.sqlite. Mutations and cross-project reads refuse; the SessionStart beacon stays silent;cm doctornamesregistry_state. - Enroll switch leaving always-loaded pointers.
enrollrefuses a silent domain switch.move-domain/unenrolljournal a revoke (clean mirrors deleted, local edits quarantined undernative/quarantine/). - Classify/lock race. Pull records plan-time hashes; dest-verify-before-delete; a local edit under lock re-classifies to stop/conflict/quarantine, never overwrite.
- Crash mid-publish. Journal v3: no origin delete until every dest hash matches; registry COMMIT precedes journal complete.
- Migrate rollback of an edited file. Hash-aware: edited-after-apply files are conflicts, never deleted.
- Unenrolled projects are local-only (ADR 008). They cannot create or pull cross-project canonicals. Enroll into a named domain to share.
forgetis lazy acknowledgment: other projects drop the mirror on their next--pull/--gc --apply. Offline clones keep bytes until they run.- Completed
journal.sqliterows from before 0.3.5 may still contain fact bodies (bytes_b64/text) untilcm journal compact(also run fromcm data compact). New operations do not. Export archives a redacted copy. - A crafted project/local
autoMemoryDirectorycannot select another project's store; a dedicated namespace requirescm project grant-native. User/managed settings may still name an absolute dir. - A corrupt/locked
control.sqliterefuses mutations and cross-project reads. The SessionStart beacon stays silent on registry failure. - Public 1.0 remains HOLD.
- Every release tag is SSH-signed (
release.sh --finalizerefuses withoutuser.signingkey); the committed public key (.github/release-tag.pub) is the trust anchor — the CIverifyjob runsgit verify-tagon every pushed tag. Threat model:mainis PR-only (branch ruleset) and the pubkey is committed, so a tag the key did not sign cannot pass the gate. - The tag-triggered workflow also re-runs the strict plugin validates (marketplace + both plugins) and publishes SLSA build provenance plus a stdlib-generated SPDX SBOM to the GitHub Release.
Please open a GitHub security advisory or a private issue at https://github.com/Zenetusken/consolidate-memory. Do not include real credentials in reports. We aim to acknowledge within a few days.