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 @@ -43,6 +43,26 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
green even if they validated nothing. Both were confirmed to fail on the old hint before
this was committed.

- **The response now says when a call CREATED the ledger instead of appending to one.**
measure-mirror 0.41.0 taught its CLI to refuse creating a ledger without `--new-ledger`.
That gate lives in `main()`; every tool here goes through the Python API and misses it
entirely — so the two lanes reported on 2026-08-26 sealing into unaudited ledgers were
both on *this* path, and the fix never reached them.

Blocking the API is not the answer: it would stop every lane mid-seal for a mistake most
callers are not making. So this does not block. `mm_preregister`, `mm_retract`,
`am_record`, `am_witness` and `pm_verify` add a `⚠️ new_ledger_created` field naming the
**absolute** path, and `mm_preregister` also raises a `㉙ ledger-birth` WARN inside `lint`
(WARN survives output compaction; everything else is collapsed to a count).

This is the gap nothing else in the stack can close: the chain of a brand-new ledger is
perfectly intact, so every integrity check is green and no probe has any reason to
mention that the file is new. The only moment the information exists is the call that
creates it.

A negative control ships with it — the notice must NOT fire when appending, or it is
noise, and noise is what a reader learns to skip.

---

## [0.2.12] — 2026-08-28
Expand Down
56 changes: 50 additions & 6 deletions mirror_stack_mcp/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,34 @@ def _remind(tool, result):
return f"{result}\n\n{msg}"


# ── a ledger that did not exist is being born, not appended to ───────────────
# measure-mirror 0.41.0 taught the CLI to refuse creating a ledger without
# `--new-ledger`. The Python API did not change, and every tool here goes through
# the API — so the whole gate misses this server. Two lanes were reported on
# 2026-08-26 sealing into ledgers nobody audits, both via this path.
#
# Blocking the API would stop every lane mid-seal for a mistake most callers are
# not making, so this does not block. It makes the birth VISIBLE in the response
# the caller already reads — which is the part that was missing: the chain is
# intact either way, so every integrity check stays green and nothing else in the
# stack will ever mention that this file is new.
_BIRTH_KEY = "⚠️ new_ledger_created"


def _birth_msg(path: str) -> str:
return (f"{os.path.abspath(path)} did not exist — this call CREATED it rather than "
"appending to an existing ledger. If you meant an existing ledger, what you "
"just sealed is in a file no audit covers, and its chain will verify green "
"forever. Check the path (and which directory you are in), then re-seal in "
"the right ledger — append-only means this entry stays where it is.")


def _birth(path: str, existed: bool) -> dict:
"""The response field that distinguishes 'appended' from 'created'. Empty when
the ledger already existed, so a normal call carries no extra noise."""
return {} if existed else {_BIRTH_KEY: _birth_msg(path)}


# ───────────────────────── 🪞 measure-mirror (claims) ─────────────────────────
@mcp.tool()
def mm_preregister(ledger_path: str, claim_id: str, metric: str, min_n: int = 200,
Expand All @@ -203,13 +231,20 @@ def mm_preregister(ledger_path: str, claim_id: str, metric: str, min_n: int = 20

The response carries an automatic seal-quality lint (`lint` key): a FAIL there means
the compute gate will BLOCK this claim — fix and re-seal under a NEW claim_id."""
existed = os.path.exists(ledger_path)
entry = mm.preregister(
ledger_path, claim_id, metric=metric, min_n=min_n, baseline=baseline,
pass_threshold=pass_threshold, kill_condition=kill_condition,
kill_threshold=kill_threshold, depends_on=depends_on,
metric_range=metric_range, chance=chance, pre_seal_checks=pre_seal_checks)
lint = _compact(_findings(mm._preseal_lint(entry)))
return _remind("mm_preregister", {**entry, "lint": lint})
findings = list(mm._preseal_lint(entry))
if not existed:
# WARN level on purpose: compaction keeps WARN verbatim, and this is the one
# line that a green-everywhere response would otherwise never carry.
findings.append(mm.Finding("㉙ ledger-birth", "WARN", _birth_msg(ledger_path)))
lint = _compact(_findings(findings))
return _remind("mm_preregister", {**entry, "lint": lint,
**_birth(ledger_path, existed)})


@mcp.tool()
Expand Down Expand Up @@ -291,7 +326,9 @@ def mm_multiseed_check(seed_results: list[float], baseline: float = 0.5) -> str:
@mcp.tool()
def mm_retract(ledger_path: str, claim_id: str, reason: str) -> dict:
"""Append a chain-linked retraction (cannot be silently deleted; dependents go STALE)."""
return _remind("mm_retract", mm.retract(ledger_path, claim_id, reason))
existed = os.path.exists(ledger_path)
return _remind("mm_retract", {**mm.retract(ledger_path, claim_id, reason),
**_birth(ledger_path, existed)})


@mcp.tool()
Expand Down Expand Up @@ -352,14 +389,19 @@ def mm_preflight(ledger_path: str, claim_id: str, gate: str = "compute",
def am_record(ledger_path: str, agent: str, action: str, target: str | None = None,
payload: dict | None = None) -> dict:
"""Seal one agent action. Set target=<claim_id> to tie the action to a claim (J1)."""
existed = os.path.exists(ledger_path)
return _remind("am_record",
am.record(ledger_path, agent=agent, action=action, target=target, payload=payload))
{**am.record(ledger_path, agent=agent, action=action,
target=target, payload=payload),
**_birth(ledger_path, existed)})


@mcp.tool()
def am_witness(my_ledger: str, peer_ledger: str, peer_name: str) -> dict:
"""Pin a peer ledger's head into mine (J3). Catches whole-ledger replacement that chains miss."""
return am.witness_peer(my_ledger, peer_ledger, peer_name=peer_name)
existed = os.path.exists(my_ledger)
return {**am.witness_peer(my_ledger, peer_ledger, peer_name=peer_name),
**_birth(my_ledger, existed)}


@mcp.tool()
Expand All @@ -373,7 +415,9 @@ def am_verify(ledger_path: str) -> list[str]:
def pm_verify(file_path: str, ledger_path: str = "pm_ledger.jsonl",
origin: str | None = None) -> dict:
"""Verify a content file's provenance/integrity across 5 signals (a verifier, not a detector)."""
return pm.verify(file_path, ledger_path=ledger_path, origin=origin)
existed = os.path.exists(ledger_path)
return {**pm.verify(file_path, ledger_path=ledger_path, origin=origin),
**_birth(ledger_path, existed)}


# ───────────────────────── 🪞🔎🪪 stack-level ─────────────────────────────────
Expand Down
48 changes: 48 additions & 0 deletions tests/test_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,51 @@ def narrow(ledger_path: str, claim_id: str, metric: str,
func_metadata(narrow).arg_model.model_validate(
{"ledger_path": "x", "claim_id": "c", "metric": "acc",
"pre_seal_checks": _STRUCTURED})


# ── created vs appended ──────────────────────────────────────────────────────
# The CLI gate added in measure-mirror 0.41.0 lives in main(); every tool here
# goes through the Python API and misses it entirely. Blocking the API would stop
# every lane mid-seal, so the response says so instead. What matters is that it
# says so ONLY when the ledger is actually new — a notice that always fires is
# noise, and noise is what a reader learns to skip.

def test_preregister_says_so_when_it_creates_the_ledger(tmp_path):
led = tmp_path / "typo.jsonl"
assert not led.exists()
r = s.mm_preregister(str(led), "c1", metric="acc", min_n=240,
kill_threshold={"metric": "acc", "threshold": 0.55,
"direction": "below"},
pre_seal_checks=[{"name": "neutral-control",
"result": "not_fired"}])
assert s._BIRTH_KEY in r
assert str(led.resolve()) in r[s._BIRTH_KEY]
# and it survives compaction, which keeps WARN verbatim and collapses the rest
assert any("ledger-birth" in line and "WARN" in line for line in r["lint"])


def test_preregister_is_silent_when_it_appends(tmp_path):
# negative control: the notice must not fire on the normal path, or it means nothing.
led = tmp_path / "real.jsonl"
s.mm_preregister(str(led), "c1", metric="acc", min_n=240,
kill_threshold={"metric": "acc", "threshold": 0.55,
"direction": "below"})
r = s.mm_preregister(str(led), "c2", metric="acc", min_n=240,
kill_threshold={"metric": "acc", "threshold": 0.55,
"direction": "below"})
assert s._BIRTH_KEY not in r
assert not any("ledger-birth" in line for line in r["lint"])


def test_am_record_says_so_when_it_creates_the_ledger(tmp_path):
led = tmp_path / "actions.jsonl"
r = s.am_record(str(led), agent="t", action="a")
assert s._BIRTH_KEY in r
r2 = s.am_record(str(led), agent="t", action="b")
assert s._BIRTH_KEY not in r2 # negative control on the same file


def test_retract_says_so_when_it_creates_the_ledger(tmp_path):
led = tmp_path / "r.jsonl"
r = s.mm_retract(str(led), "never-registered", reason="wrong ledger")
assert s._BIRTH_KEY in r
Loading