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
9 changes: 5 additions & 4 deletions src/lemoncrow/gateway/adapters/mcp_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -7023,8 +7023,9 @@ def _looks_like_test_path(path: str) -> bool:
# fixing the code. We trip ONLY on that signal -- additive edits (new tests or
# assertions) and in-place assertion modifications pass freely. Heuristic and
# language-fuzzy by design: it errs toward allowing, surfacing a counterexample +
# rollback only on a clear net assertion-removal or skip/xfail addition. Operator
# off-switch: LEMONCROW_TEST_CONTRACT_GUARD=0.
# rollback only on a clear net assertion-removal or skip/xfail addition. Off by
# default (it blocked legitimate test-only cleanup); opt in with
# LEMONCROW_TEST_CONTRACT_GUARD=1.
_ASSERTION_RE = re.compile(r"\bassert|\bexpect\s*\(|\bEXPECT_|\bASSERT_|\.should\b")
_SKIP_XFAIL_RE = re.compile(
r"@(?:pytest\.mark\.)?(?:skip|skipif|xfail)\b|@unittest\.skip|\bt\.Skip[a-zA-Z]*\("
Expand All @@ -7033,8 +7034,8 @@ def _looks_like_test_path(path: str) -> bool:


def _test_contract_guard_enabled() -> bool:
"""Whether the test-weakening guard runs (operator off-switch, default on)."""
return os.environ.get("LEMONCROW_TEST_CONTRACT_GUARD", "").strip().lower() not in ("0", "false", "no", "off")
"""Whether the test-weakening guard runs (operator opt-in, default off)."""
return os.environ.get("LEMONCROW_TEST_CONTRACT_GUARD", "").strip().lower() in ("1", "true", "yes", "on")


def _classify_test_weakening(old_content: str, new_content: str) -> str | None:
Expand Down
1 change: 1 addition & 0 deletions tests/gateway/cli/test_runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,7 @@ def test_owned_edit_blocks_test_weakening(tmp_path, monkeypatch) -> None:
# The owned runtime no longer publishes a self-authorize bypass; an edit that
# WEAKENS a test (removes an assertion) is rolled back by the detector.
monkeypatch.setenv("CLAUDE_WORKSPACE_ROOT", str(tmp_path))
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
target = tmp_path / "tests" / "test_contract.py"
target.parent.mkdir()
target.write_text("def test_x():\n assert value == 'old'\n assert other == 1\n", encoding="utf-8")
Expand Down
36 changes: 36 additions & 0 deletions tests/gateway/test_mcp_tool_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1367,6 +1367,7 @@ def test_smart_edit_blocks_test_assertion_removal(
# roll back and surface a counterexample (the detector trips only on weakening).
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
target = Path("tests/test_parser.py")
target.parent.mkdir()
target.write_text("def test_x():\n assert compute() == 5\n assert other() == 9\n", encoding="utf-8")
Expand Down Expand Up @@ -1396,11 +1397,43 @@ def test_smart_edit_blocks_test_assertion_removal(
assert "assert compute() == 5" in target.read_text(encoding="utf-8")


def test_smart_edit_test_contract_guard_off_by_default(
store_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.delenv("LEMONCROW_TEST_CONTRACT_GUARD", raising=False)
target = Path("tests/test_parser.py")
target.parent.mkdir()
target.write_text("def test_x():\n assert compute() == 5\n assert other() == 9\n", encoding="utf-8")

payload = _result(
_call(
"edit",
{
"edits": [
{
"file_path": str(target),
"old_string": " assert compute() == 5\n assert other() == 9\n",
"new_string": " assert other() == 9\n",
}
],
"post_edit_hooks": False,
},
)
)

assert payload.get("rolled_back") is not True
assert "test_weakening" not in payload
assert "assert compute() == 5" not in target.read_text(encoding="utf-8")


def test_smart_edit_blocks_test_skip_addition(
store_root: Path, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
target = Path("tests/test_parser.py")
target.parent.mkdir()
target.write_text("def test_x():\n assert compute() == 5\n", encoding="utf-8")
Expand Down Expand Up @@ -1432,6 +1465,7 @@ def test_smart_edit_allows_additive_test_edit(
# Adding a new assertion to an existing test passes freely -- no friction.
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
target = Path("tests/test_parser.py")
target.parent.mkdir()
target.write_text("def test_x():\n assert compute() == 5\n", encoding="utf-8")
Expand Down Expand Up @@ -1464,6 +1498,7 @@ def test_smart_edit_allows_assertion_value_change(
# assertions and passes -- the detector guards removal/skip, not value edits.
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
target = Path("tests/test_parser.py")
target.parent.mkdir()
target.write_text("def test_x():\n assert compute() == 5\n", encoding="utf-8")
Expand Down Expand Up @@ -1496,6 +1531,7 @@ def test_smart_edit_allows_test_weakening_paired_with_production_change(
# as a genuine refactor (contract moved with the code), not a reward-hack.
_ = store_root
monkeypatch.chdir(tmp_path)
monkeypatch.setenv("LEMONCROW_TEST_CONTRACT_GUARD", "1")
test_file = Path("tests/test_parser.py")
test_file.parent.mkdir()
test_file.write_text("def test_x():\n assert compute() == 5\n assert other() == 9\n", encoding="utf-8")
Expand Down
Loading