diff --git a/CHANGELOG.md b/CHANGELOG.md index ec82804..1817f23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,26 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). --- +## [0.2.7] — 2026-07-21 + +### Changed +- **measure-mirror pin `13077df` → `690c27e` (v0.26.0 → v0.27.0)**. Picks up, in order: + - **v0.27.0 — full 64-hex SHA-256 seals** (security, SPEC v1.1): 16-hex truncated + seals allowed a birthday-search (~2³² hashes) to forge two entries sharing one seal + and swap them post-seal. Legacy 16-hex seals keep verifying (prefix match) — no + ledger migration. The stack's L1 chain check inherits the wider digest. + - **v0.26.1 — ㉗ `prereg_lint` false-positive fix**: an audit of the probe against 64 + real ledgers found ⑫c was reading `baseline` as the chance floor → **44 spurious + FAILs = 44 wrong compute-gate BLOCKs**. It now uses a declared `chance` only, so a + below-chance gate BLOCK requires `chance=` on the preregistration. +- Docs: "bar at/below chance" → "bar at/below **declared** chance" (README, connect-time + DISCIPLINE, `mm_prereg_lint` reminder). + +### Fixed +- **`test_compute_gate_blocks_on_lint_fail_below_chance_bar`** now declares `chance=` + (the corrected contract) + new `test_compute_gate_does_not_block_on_baseline_alone` + pins the false-positive guard: `pass < baseline` with no declared `chance` must GO. + ## [0.2.6] — 2026-07-21 ### Added diff --git a/README.md b/README.md index b85259a..2599839 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Windsurf, any MCP client. | `mm_audit` | 🪞 | audit a result vs its sealed registration | | `mm_power_check` | 🪞 | false-negative guard — is n big enough? (design-time) | | `mm_falsifiability_check` | 🪞 | Popper gate — kill-condition registered & not tripped? | -| `mm_prereg_lint` | 🪞 | seal *quality* lint (㉗) — leaked kill-condition, bar at/below chance, unstructured kill, low n, undeclared pre-seal checks; the compute gate BLOCKs on a lint FAIL | +| `mm_prereg_lint` | 🪞 | seal *quality* lint (㉗) — leaked kill-condition, bar at/below declared chance, unstructured kill, low n, undeclared pre-seal checks; the compute gate BLOCKs on a lint FAIL | | `mm_leakage_check` | 🪞 | train∩test contamination | | `mm_multiseed_check` | 🪞 | unstable signal / lucky seed | | `mm_retract` | 🪞 | chain-linked retraction (cannot be silently deleted) | diff --git a/mirror_stack_mcp/__init__.py b/mirror_stack_mcp/__init__.py index 25a4c25..1ead46a 100644 --- a/mirror_stack_mcp/__init__.py +++ b/mirror_stack_mcp/__init__.py @@ -1,2 +1,2 @@ """🪞🔎🪪 Mirror Stack unified MCP server.""" -__version__ = "0.2.6" +__version__ = "0.2.7" diff --git a/mirror_stack_mcp/server.py b/mirror_stack_mcp/server.py index a9b1a6c..e765dd2 100644 --- a/mirror_stack_mcp/server.py +++ b/mirror_stack_mcp/server.py @@ -43,7 +43,7 @@ neutral-control / manipulation-check / positive-control). 2. Power: is n big enough to detect the effect? mm_power_check (design-time). 3. Lint the seal — mm_prereg_lint (auto in the mm_preregister response). A FAIL - (kill-condition leaked into the metric field, bar at/below chance) means the + (kill-condition leaked into the metric field, bar at/below declared chance) means the automated checks can't fire: fix and re-seal under a NEW claim_id. BEFORE reporting (verify before speaking): @@ -70,7 +70,7 @@ NORMATIVE FORMAT: the ledger format is defined by MIRROR-SPEC v1.0 (measure-mirror docs/SPEC.md, ratified & frozen 2026-07-02) — these tools are a reference implementation. KNOWN ILLUSIONS: before trusting a surprising result (good OR bad), scan the catalog of -measurement illusions (measure-mirror catalog/, 45 real sealed cases: gaming, self-catch, +measurement illusions (measure-mirror catalog/, 46 real sealed cases: gaming, self-catch, false-negative guards, contamination). When you retract, consider adding a specimen. """ @@ -138,7 +138,7 @@ def _compact(findings): "mm_falsifiability_check": "🪞 If the kill-condition tripped (FAIL), the claim is falsified by " 'its OWN criterion → mm_retract it. If it did not trip, that\'s "not refuted", not "proven".', "mm_prereg_lint": "🪞 Lint judges the seal's QUALITY, not just its presence. A FAIL here " - "(kill-condition leaked into `metric`, or a bar at/below chance) means the automated " + "(kill-condition leaked into `metric`, or a bar at/below declared chance) means the automated " "checks can't fire — fix and re-seal under a NEW claim_id (first-write-wins). Run this " "right before spending compute; it's the cheap machine-check that saves a KILL.", "mm_preflight": "🪞 This is a primitive — the MCP only judges GO/BLOCK. YOUR launcher / " diff --git a/pyproject.toml b/pyproject.toml index 346002d..f71f925 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "mirror-stack-mcp" -version = "0.2.6" +version = "0.2.7" description = "Unified MCP server for the Mirror Stack — claims, actions, provenance + verify-all in one server" readme = "README.md" requires-python = ">=3.10" @@ -16,7 +16,7 @@ dependencies = [ # tracks each mirror's default branch, so an install made before a mirror's release # freezes on whatever was HEAD then and never refreshes — that is how a local install # ran stale measure-mirror 0.14.3 long after 0.15.1 landed. Bump these on purpose. - "measure-mirror @ git+https://github.com/bhyi4/measure-mirror@13077df8c2616e0559742b2d97c37e2459d7906c", # v0.26.0 — ㉗ prereg_lint pre-compute seal-quality probe + pre_seal_checks (#28; required by mm_prereg_lint/gate lint wiring); v0.25.0 catalog v1.8 + anchor probes ㉔㉕ A2; v0.24.0 grounding ㉑㉒㉓ + A1 + "measure-mirror @ git+https://github.com/bhyi4/measure-mirror@690c27e9645878576f7284bdcc9b7cafdf7c0f53", # v0.27.0 — full 64-hex SHA-256 seals (security, SPEC v1.1); v0.26.1 ㉗ prereg_lint false-positive fix (declared-chance floor); v0.26.0 ㉗ prereg_lint probe + pre_seal_checks (required by mm_prereg_lint/gate wiring); v0.24~0.25 grounding/anchor probes ㉑–㉕ "action-mirror @ git+https://github.com/bhyi4/action-mirror@fd46e90c3e3f0706792c261316f4699d7a6799d8", # v0.1.0 (ledgers conform to MIRROR-SPEC v1.0) "provenance-mirror @ git+https://github.com/bhyi4/provenance-mirror@8edbbfd94772d3718de93271c1f80297c0476043", # v0.1.0 (ledgers conform to MIRROR-SPEC v1.0) ] diff --git a/tests/test_server.py b/tests/test_server.py index e7b60dc..ce92ec2 100644 --- a/tests/test_server.py +++ b/tests/test_server.py @@ -189,8 +189,9 @@ def test_preregister_response_carries_auto_lint(tmp_path): def test_compute_gate_blocks_on_lint_fail_below_chance_bar(tmp_path): led = tmp_path / "mm.jsonl" - # well-formed kill fields, but the pass bar sits below chance → lint FAIL - s.mm_preregister(str(led), "c1", metric="acc", baseline=0.5, pass_threshold=0.45, + # well-formed kill fields, but the pass bar sits at/below the DECLARED chance → FAIL. + # (chance must be declared — `baseline` alone is a comparison arm, not the floor.) + s.mm_preregister(str(led), "c1", metric="acc", chance=0.5, pass_threshold=0.45, kill_threshold={"metric": "acc", "threshold": 0.4, "direction": "below"}) r = s.mm_preflight(str(led), "c1", gate="compute") @@ -198,6 +199,17 @@ def test_compute_gate_blocks_on_lint_fail_below_chance_bar(tmp_path): assert "prereg-lint FAIL" in " ".join(r["reasons"]) +def test_compute_gate_does_not_block_on_baseline_alone(tmp_path): + led = tmp_path / "mm.jsonl" + # pass=0.45 < baseline=0.5 but NO declared chance → must GO (A3 false-positive guard). + s.mm_preregister(str(led), "c1", metric="acc", baseline=0.5, pass_threshold=0.45, + min_n=200, + kill_threshold={"metric": "acc", "threshold": 0.4, + "direction": "below"}) + r = s.mm_preflight(str(led), "c1", gate="compute") + assert r["decision"] == "GO" + + def test_compute_gate_go_passes_lint_check(tmp_path): led = tmp_path / "mm.jsonl" s.mm_preregister(str(led), "c1", metric="acc", min_n=240,