Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions .claude/workflows/mm4-release-audit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
export const meta = {
name: 'mm4-release-audit',
description: 'MM v4 P6: release-gate audit before tagging v4.0.0 — parallel skeptics on secrets, packaging/install/entry-points, import+shim correctness, breaking-change/migration completeness -> SHIP/HOLD verdict.',
whenToUse: 'Phase 6 of the v4 program — the final safety gate before the v4.0.0 PyPI release.',
phases: [
{ title: 'Audit', detail: '4 parallel release-risk skeptics' },
{ title: 'Verdict', detail: 'synthesize SHIP/HOLD with any crit/high blockers' },
],
}

const REPO = 'G:/_OneDrive/OneDrive/Desktop/Py Apps/memorymaster'

const COMMON =
'Repo: ' + REPO + ', branch omni/p6-release (verify with git branch --show-current). This is the FINAL release gate before tagging v4.0.0 and publishing to PyPI. The codebase completed a 5-phase v4 consolidation (reliability, 138->7 subpackage restructure with shims, ingest governance + sensitivity hardening, multi-agent contract, surfaces). Full suite is green (2873 passed), CI green on main. ' +
'RULES: READ-ONLY — do NOT modify code, do NOT touch the live memorymaster.db, do NOT commit/push. Report findings with severity (crit/high/med/low). A release-BLOCKER is crit or high ONLY. Ground every finding in evidence (file:line, a command you ran). Be proportionate — this is a release gate, not a nitpick hunt; do not invent blockers. PYTHONIOENCODING=utf-8 for python subprocesses.';

phase('Audit')
const FINDINGS_SCHEMA = {
type: 'object', additionalProperties: false,
required: ['dimension', 'blockers', 'non_blocking', 'evidence', 'verdict'],
properties: {
dimension: { type: 'string' },
blockers: { type: 'array', items: { type: 'string' }, description: 'crit/high issues that should HOLD the release (empty if none)' },
non_blocking: { type: 'array', items: { type: 'string' }, description: 'med/low issues to note for post-release' },
evidence: { type: 'array', items: { type: 'string' }, description: 'commands run + results that ground the verdict' },
verdict: { type: 'string', enum: ['ship', 'hold'] },
},
}
const SKEPTICS = [
{ key: 'secrets', prompt:
'AUDIT DIMENSION: secrets in the shipped artifact. Build the sdist+wheel (python -m build OR pip wheel . into a temp dir; if build not installed, inspect what setuptools would package via the MANIFEST/package-data + git ls-files). Grep the would-be-shipped files for real credentials/tokens/keys/private IPs/personal absolute paths (NOT test fixtures with obviously-fake values). Confirm memorymaster.db / *.db / .env are NOT in the package. Confirm the sensitivity filter module ships. A real secret or the live DB in the package = CRIT blocker.' },
{ key: 'packaging', prompt:
'AUDIT DIMENSION: packaging + install + entry points. In a CLEAN temp venv, pip install the repo (pip install . or -e .[mcp]); confirm it installs without error; run each of the 5 console entry points minimally (memorymaster --help, memorymaster-mcp import, memorymaster-dashboard import, memorymaster-steward import, memorymaster-setup import / --help); confirm "import memorymaster" + each subpackage (core/stores/recall/govern/knowledge/surfaces/bridges) imports clean. pyproject metadata sane (version, deps, classifiers, license). A broken install or a dead entry point = HIGH blocker. Clean up the venv.' },
{ key: 'shims', prompt:
'AUDIT DIMENSION: backward-compat import shims (the restructure moved 138 modules; old paths must still resolve for one minor version). Write a throwaway script that imports a representative sample of OLD top-level paths (memorymaster.service, memorymaster.storage, memorymaster.context_hook, memorymaster.wiki_engine, memorymaster.steward, memorymaster.mcp_server, memorymaster.llm_budget, memorymaster.models, memorymaster.security) and asserts each resolves (via sys.modules alias) to the NEW subpackage module object. Any old path that ImportErrors = HIGH blocker (silent breakage for existing importers). Also confirm the installed production hooks reference paths that still resolve.' },
{ key: 'migration', prompt:
'AUDIT DIMENSION: breaking-change + migration completeness for a 3.28 -> 4.0 MAJOR bump. Verify the CHANGELOG/README document the new subpackage import paths + the deprecation of old paths. Check there is NO un-migrated DB schema break (migrations 0001-0007 present; a 3.28 DB opens cleanly under 4.0 — test by pointing init_db at a fresh tmp DB and confirming it builds). Confirm nothing in the public CLI/MCP surface was REMOVED without a shim (compare entry points + @mcp.tool list to 3.28). A silent removed-public-API or a schema break with no migration = HIGH blocker.' },
]
async function batched(items, make, size) {
const out = []
for (let i = 0; i < items.length; i += size) out.push(...await parallel(items.slice(i, i + size).map((it) => () => make(it))))
return out
}
const findings = await batched(SKEPTICS, (s) =>
agent(COMMON + '\n\n' + s.prompt, { label: 'audit:' + s.key, phase: 'Audit', schema: { ...FINDINGS_SCHEMA } }), 4)
const valid = findings.filter(Boolean)
log('audit: ' + valid.length + '/4 skeptics returned; holds=' + valid.filter(f => f.verdict === 'hold').length)

phase('Verdict')
const VERDICT_SCHEMA = {
type: 'object', additionalProperties: false,
required: ['ship', 'blockers', 'non_blocking_summary', 'rationale'],
properties: {
ship: { type: 'boolean', description: 'true = clear to tag v4.0.0 and publish; false = HOLD' },
blockers: { type: 'array', items: { type: 'string' }, description: 'the crit/high issues that must be fixed before release (empty if ship)' },
non_blocking_summary: { type: 'array', items: { type: 'string' }, description: 'med/low to carry as post-release follow-ups' },
rationale: { type: 'string' },
},
}
const verdict = await agent(
COMMON + '\n\nROLE: synthesize the release verdict from the 4 skeptics:\n' + JSON.stringify(valid) + '\n\n' +
'SHIP only if there are ZERO crit/high blockers across all dimensions. Independently sanity-check any claimed blocker before accepting it (do not HOLD on a misdiagnosis; do not SHIP past a real one). Return ship=true/false, the deduped blocker list, the non-blocking follow-ups, and your rationale.',
{ label: 'release-verdict', phase: 'Verdict', schema: VERDICT_SCHEMA })

return { findings: valid, verdict }
65 changes: 65 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,71 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [4.0.0] - 2026-06-20

**The v4 consolidation — MemoryMaster becomes a top-tier governed memory layer.**
A six-phase program turning the production-proven prototype into a reliable,
governed, distributable release. No data migration is required: an existing
3.x SQLite database opens unchanged, and **every old `memorymaster.<module>`
import path keeps working** via deprecation shims for one minor version.

### Reliability (P1)

- **Single-writer "WAL-Discipline".** Retired the corruption class that hit the
project on 2026-06-05: the legacy `openclaw-sync.sh` scp-over-live-DB path is
hard-disabled, the hottest write path (`verbatim_store`) gained a busy_timeout,
and a steward **integrity phase** now runs `wal_checkpoint(TRUNCATE)` every
cycle (the WAL had grown to 1.44 GB with no checkpoint ever), `quick_check`
daily with a promotion freeze on failure, `foreign_key_check`, and weekly
`VACUUM INTO` snapshots (keep-3). ~26 ad-hoc `sqlite3.connect` sites were
unified through one pragma envelope (`open_conn`/`connect_ro`).
- **Cold `init_db` 16 s → 0.09 s** via the `MEMORYMASTER_INITDB_FASTPATH`
user_version stamp; recall read path no longer takes a write lock
(`MEMORYMASTER_WAL_DISCIPLINE`). Both flags default off; legacy path intact.
- New `repair-fk` and `qdrant-reconcile` maintenance jobs.

### Governance & security (P3)

- **Intake policy as code** (`core/intake_policy.py`) at the `service.ingest`
chokepoint, after the (unchanged) sensitivity filter: `source_agent`
attribution, session-state/heartbeat rejection (the telemetry-flood class),
per-agent quotas, and a distilled-per-stop cap. Additive and env-configurable.
- **Closed three sensitivity-filter bypasses** — `dream_bridge`, the
`llm_steward` cycle-insert, and `transcript_miner` (which used a literal scan
that missed base64/encoded secrets) now all run the canonical
`scan_text_for_findings` sweep before any write.
- The steward can now **absorb correction-mining** as a `run_cycle` phase
(`MEMORYMASTER_STEWARD_RULE_MINING`, default off).

### Multi-agent contract (P4)

- **`docs/INTEGRATING.md`** documents the 3-beat contract (session-start fetch /
on-demand recall / session-end distilled ingest) with per-agent-class
reference implementations; new `scripts/agent_session_end_ingest.py` turnkey
Codex/generic session-end ingest; a **per-agent provenance** dashboard panel.

### Surfaces & docs (P5)

- README rewritten with positioning vs mem0/Letta/Zep and a 15-minute quickstart;
generated `docs/MCP-TOOLS.md`; stale audits/experiments archived under
`docs/archive/`; the dashboard's recall-analysis view now renders as a panel.

### Packaging (P6)

- **The published wheel now ships only the `memorymaster` package.** Previously
`setuptools` swept every top-level dir (`tests/`, `scripts/`, `benchmarks/`,
…) into site-packages, polluting the importer's namespace (`import tests`
would collide) — `packages.find` is now scoped to `memorymaster*`.

### Migration

- **Imports:** update `memorymaster.<module>` → the subpackage path
(`memorymaster.core.service`, `memorymaster.stores.storage`,
`memorymaster.recall.context_hook`, `memorymaster.govern.steward`, …). Old
paths still resolve but are deprecated and will be removed in a future minor.
- **Database:** none. 3.x DBs open as-is.
- **Removed:** `memorymaster.skill_evolver` (unreferenced dead module).

### Changed

- **Package restructure (v4 program P2).** The previously-flat ~138 modules
Expand Down
2 changes: 1 addition & 1 deletion benchmarks/longmemeval_vector_runner.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
SCRIPT_DIR = Path(__file__).parent
DATASET = str(SCRIPT_DIR / "longmemeval_oracle.json")
OUTPUT = str(SCRIPT_DIR / "longmemeval_vector_output.jsonl")
QDRANT_URL = "http://192.168.100.186:6333"
QDRANT_URL = "http://localhost:6333"
COLLECTION = "longmemeval_bench"
EMBED_MODEL = "text-embedding-3-small"
EMBED_DIM = 1536
Expand Down
2 changes: 1 addition & 1 deletion memorymaster/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

__all__ = ["__version__"]

__version__ = "3.28.0"
__version__ = "4.0.0"
8 changes: 7 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "memorymaster"
version = "3.28.0"
version = "4.0.0"
description = "Production-grade memory reliability system for AI coding agents. Lifecycle-managed claims with citations, conflict detection, steward governance, and MCP integration."
license = {text = "MIT"}
authors = [{name = "wolverin0"}]
Expand Down Expand Up @@ -65,6 +65,12 @@ ignore = ["E501"] # line length handled by formatter

[tool.setuptools.packages.find]
where = ["."]
# Ship ONLY the library. Without this, find() sweeps every top-level dir
# (tests/, scripts/, benchmarks/, artifacts/, cloned/, docs/, ...) into
# site-packages as importable top-level packages — polluting the user's
# namespace (`import tests` / `import scripts` collide) and bloating the
# wheel. Scoped to memorymaster* so the published artifact is just the package.
include = ["memorymaster*"]

[tool.setuptools.package-data]
memorymaster = [
Expand Down
9 changes: 7 additions & 2 deletions scripts/bench_recall_latency.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,13 @@
import sys
import time

PROJECT_ROOT = r"G:\_OneDrive\OneDrive\Desktop\Py Apps\memorymaster"
DB_PATH = os.path.join(PROJECT_ROOT, "memorymaster.db")
# Resolve the repo root from this file's location (scripts/ is one level down),
# overridable via env — no hardcoded maintainer path.
PROJECT_ROOT = os.environ.get(
"MEMORYMASTER_PROJECT_ROOT",
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
)
DB_PATH = os.environ.get("MEMORYMASTER_DEFAULT_DB", os.path.join(PROJECT_ROOT, "memorymaster.db"))

sys.path.insert(0, PROJECT_ROOT)
os.environ["MEMORYMASTER_DEFAULT_DB"] = DB_PATH
Expand Down
4 changes: 2 additions & 2 deletions scripts/index_claims_to_qdrant.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

python scripts/index_claims_to_qdrant.py \
--db memorymaster.db \
--qdrant-url http://192.168.100.186:6333 \
--qdrant-url http://localhost:6333 \
--collection memorymaster-claims

Environment variables (respected as defaults when flags are omitted):
Expand Down Expand Up @@ -278,7 +278,7 @@ def main() -> int:
ap.add_argument(
"--qdrant-url",
default=os.environ.get("MEMORYMASTER_QDRANT_URL") or DEFAULT_QDRANT_URL,
help="Qdrant REST URL, e.g. http://192.168.100.186:6333",
help="Qdrant REST URL, e.g. http://localhost:6333",
)
ap.add_argument(
"--collection",
Expand Down
Loading