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
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,34 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).

---

## [0.2.10] — 2026-08-05

### Changed
- **Pins now point at the commits their version labels claim.** The
`action-mirror` and `provenance-mirror` pins were both commented `# v0.2.0`
while sitting **two commits before** that tag. The two missing commits were
the org migration (`bhyi4/` → `mirror-stack/`) and the SPEC v1.1 reference
update, so an install got pre-migration docs under a v0.2.0 label — a claim
the installer had no way to see was false.
- `measure-mirror` pin bumped **v0.28.1 → v0.29.0**: adds ㉘
`subspace_claim_check` (a declaration auditor, holdout PASS 22/22 after two
sealed kills), `Finding.data`, and a fix for a latent `KeyError` in the
finding formatters on `INFO`/`N/A` levels.

### Added
- `tests/test_pins_are_releases.py` — each pin's SHA is checked against the
GitHub tag its comment names, plus offline checks that every pin is a full
40-hex lowercase SHA and carries a version label. Skips when the API is
unreachable rather than failing offline. Reverse-verified: restoring the old
`action-mirror` pin makes it fail.

### Note
This server exposes a curated 19 tools, not all 28 measure-mirror probes —
`mm_verify` is the umbrella for the rest. ㉘ is reachable through it; no new
tool was added, by design.

---

## [0.2.9] — 2026-07-21

### Changed
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.9"
__version__ = "0.2.10"
25 changes: 16 additions & 9 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,28 @@ build-backend = "setuptools.build_meta"

[project]
name = "mirror-stack-mcp"
version = "0.2.9"
version = "0.2.10"
description = "Unified MCP server for the Mirror Stack — claims, actions, provenance + verify-all in one server"
readme = "README.md"
requires-python = ">=3.10"
license = { text = "Apache-2.0" }
authors = [{ name = "Mother Seara", email = "ruinbl82@gmail.com" }]
dependencies = [
"mcp>=1.0",
# Pinned to exact commits for reproducible installs. An unpinned git dep silently
# 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/mirror-stack/measure-mirror@abe0c19d3894c1278a9bf5281a45946ef0e1eea4", # v0.28.1 — power_check honors alpha/target_power (was hardcoded 1.96/0.842: text-vs-number lie); v0.28.0 SECURITY verify_chain prev_seal (SPEC §4 rule 3); v0.27.0 full 64-hex seals; v0.26 ㉗ prereg_lint
"action-mirror @ git+https://github.com/mirror-stack/action-mirror@fa1fc4998936e6bf179b5c14825fb09e78a36087", # v0.2.0 — full 64-hex SHA-256 seals (security; legacy 16-hex verify via prefix match); v0.1.0 ledgers conform to MIRROR-SPEC v1.0
"provenance-mirror @ git+https://github.com/mirror-stack/provenance-mirror@321f84abc4ffd91da43a5ebc866b5ce02cf3c276", # v0.2.0 — full 64-hex SHA-256 seals (security; legacy 16-hex verify via prefix match); v0.1.0 ledgers conform to MIRROR-SPEC v1.0
"mcp>=1.0,<2", # upper bound: mcp 2.x removed mcp.server.fastmcp (same fix as yeoul)
# Pinned to exact RELEASE commits for reproducible installs. An unpinned git
# dep silently 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.
#
# Each SHA below is the commit its version TAG points at, verified against
# the GitHub tag API by tests/test_pins_are_releases.py. The previous
# action-mirror and provenance-mirror pins were labelled "v0.2.0" while
# sitting TWO commits before that tag, so an install got the pre-org-migration
# docs (stale `bhyi4/` self-references, SPEC v1.0) under a v0.2.0 label.
"measure-mirror @ git+https://github.com/mirror-stack/measure-mirror@b3b6dec8893c8e645ad0c9d50d08fc3c9417eee1", # v0.29.0 — ㉘ subspace_claim_check (declaration auditor, holdout PASS 22/22); Finding.data; INFO/N-A formatter crash fixed; CHANGELOG + catalog-count sync guards
"action-mirror @ git+https://github.com/mirror-stack/action-mirror@15b0c5b775e916a22d837ddc0714e85aef7ec74e", # v0.2.0 — full 64-hex SHA-256 seals (security; legacy 16-hex verify via prefix match); org migration + SPEC v1.1 references
"provenance-mirror @ git+https://github.com/mirror-stack/provenance-mirror@2d1d52d280352567410d140270f7714571bf784f", # v0.2.0 — full 64-hex SHA-256 seals (security; legacy 16-hex verify via prefix match); org migration + SPEC v1.1 references
]

[project.optional-dependencies]
Expand Down
61 changes: 61 additions & 0 deletions tests/test_pins_are_releases.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Every git pin must point at the commit its version label claims.

Found by a family-wide consistency audit: the `action-mirror` and
`provenance-mirror` pins were both commented `# v0.2.0` while sitting TWO
commits *before* the v0.2.0 tag. The two missing commits were the org migration
(`bhyi4/` → `mirror-stack/`) and the SPEC v1.1 reference update — so anyone
installing this package got pre-migration docs under a v0.2.0 label.

A pin whose comment names a release it does not point to is a lie the installer
cannot see, which is exactly the defect class this stack exists to catch.
"""
import json
import re
import urllib.error
import urllib.request
from pathlib import Path

import pytest

ROOT = Path(__file__).resolve().parents[1]
PIN_RE = re.compile(
r'"(?P<pkg>[a-z-]+) @ git\+https://github\.com/(?P<owner>[\w-]+)/'
r'(?P<repo>[\w-]+)@(?P<sha>[0-9a-f]{40})",\s*#\s*(?P<ver>v?[\d.]+)')


def _pins():
text = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
pins = [m.groupdict() for m in PIN_RE.finditer(text)]
assert pins, "no git pins parsed from pyproject.toml — did the format change?"
return pins


def test_every_pin_declares_a_version():
for pin in _pins():
assert pin["ver"], f"{pin['pkg']} pin has no version comment"


def test_pin_shas_are_full_length_and_lowercase():
"""A short SHA is ambiguous and an uppercase one breaks string comparison."""
text = (ROOT / "pyproject.toml").read_text(encoding="utf-8")
for sha in re.findall(r'git\+https://github\.com/[\w-]+/[\w-]+@([0-9a-fA-F]+)', text):
assert len(sha) == 40, f"pin {sha} is not a full 40-hex commit SHA"
assert sha == sha.lower(), f"pin {sha} is not lowercase"


@pytest.mark.parametrize("pin", _pins(), ids=lambda p: p["pkg"])
def test_pin_matches_its_tagged_release(pin):
url = f"https://api.github.com/repos/{pin['owner']}/{pin['repo']}/tags"
try:
with urllib.request.urlopen(url, timeout=15) as r:
tags = json.load(r)
except (urllib.error.URLError, TimeoutError) as e: # offline / rate limited
pytest.skip(f"GitHub tags unreachable for {pin['repo']}: {e}")
want = pin["ver"] if pin["ver"].startswith("v") else "v" + pin["ver"]
match = [t for t in tags if t["name"] == want]
assert match, (
f"{pin['pkg']} pin claims {want} but that tag does not exist "
f"(tags: {[t['name'] for t in tags][:5]})")
assert match[0]["commit"]["sha"] == pin["sha"], (
f"{pin['pkg']} pin is commented {want} but points at {pin['sha'][:8]}, "
f"while {want} is {match[0]['commit']['sha'][:8]}")
Loading