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
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
2 changes: 1 addition & 1 deletion mirror_stack_mcp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""🪞🔎🪪 Mirror Stack unified MCP server."""
__version__ = "0.2.6"
__version__ = "0.2.7"
6 changes: 3 additions & 3 deletions mirror_stack_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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.
"""

Expand Down Expand Up @@ -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 / "
Expand Down
4 changes: 2 additions & 2 deletions 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 = "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"
Expand All @@ -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)
]
Expand Down
16 changes: 14 additions & 2 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,15 +189,27 @@ 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")
assert r["decision"] == "BLOCK"
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,
Expand Down
Loading