From efb676fd95fe29480a7e3d54020c4d3bb4ef6649 Mon Sep 17 00:00:00 2001 From: wolverinaton Date: Sat, 20 Jun 2026 16:23:08 -0300 Subject: [PATCH 1/2] chore(p6): release-gate audit workflow --- .claude/workflows/mm4-release-audit.js | 65 ++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .claude/workflows/mm4-release-audit.js diff --git a/.claude/workflows/mm4-release-audit.js b/.claude/workflows/mm4-release-audit.js new file mode 100644 index 0000000..3954b07 --- /dev/null +++ b/.claude/workflows/mm4-release-audit.js @@ -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 } From 9e2eb864cd3492d34ee10e44469dbae89edfa396 Mon Sep 17 00:00:00 2001 From: wolverinaton Date: Sat, 20 Jun 2026 16:49:14 -0300 Subject: [PATCH 2/2] =?UTF-8?q?release:=20v4.0.0=20=E2=80=94=20v4=20consol?= =?UTF-8?q?idation=20(reliability/restructure/governance/security/agents/s?= =?UTF-8?q?urfaces)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Version 3.28.0 -> 4.0.0. CHANGELOG documents the full v4 story + migration note (imports change to subpackage paths, DB unchanged, old paths shimmed). Release-gate audit fixes (both HIGH, would have shipped to public PyPI): - packages.find scoped to memorymaster* — the wheel now ships ONLY the library (was sweeping tests/scripts/benchmarks/cloned into site-packages, colliding on 'import tests'/'import scripts'). - scrubbed a hardcoded home-lab IP (192.168.100.186 -> localhost/env) from benchmarks + scripts; bench_recall_latency.py path now env/relative. Gate: 2873 passed, ruff clean; clean wheel verified (RECORD top-level = memorymaster only, 0 IP). --- CHANGELOG.md | 65 +++++++++++++++++++++++++ benchmarks/longmemeval_vector_runner.py | 2 +- memorymaster/__init__.py | 2 +- pyproject.toml | 8 ++- scripts/bench_recall_latency.py | 9 +++- scripts/index_claims_to_qdrant.py | 4 +- 6 files changed, 83 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b16ae9..c04b67b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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.` +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.` → 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 diff --git a/benchmarks/longmemeval_vector_runner.py b/benchmarks/longmemeval_vector_runner.py index 6ddb0d3..9611d90 100644 --- a/benchmarks/longmemeval_vector_runner.py +++ b/benchmarks/longmemeval_vector_runner.py @@ -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 diff --git a/memorymaster/__init__.py b/memorymaster/__init__.py index 1c18bef..6f81187 100644 --- a/memorymaster/__init__.py +++ b/memorymaster/__init__.py @@ -2,4 +2,4 @@ __all__ = ["__version__"] -__version__ = "3.28.0" +__version__ = "4.0.0" diff --git a/pyproject.toml b/pyproject.toml index 34c0391..d8f447e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"}] @@ -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 = [ diff --git a/scripts/bench_recall_latency.py b/scripts/bench_recall_latency.py index 241efcb..deb736f 100644 --- a/scripts/bench_recall_latency.py +++ b/scripts/bench_recall_latency.py @@ -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 diff --git a/scripts/index_claims_to_qdrant.py b/scripts/index_claims_to_qdrant.py index 68d4968..7fe530d 100644 --- a/scripts/index_claims_to_qdrant.py +++ b/scripts/index_claims_to_qdrant.py @@ -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): @@ -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",