diff --git a/CHANGELOG.md b/CHANGELOG.md index 5259aee07..b768a4101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ breaking changes may land in a minor release. ### Added +- The TEA plugin now prints an operator-visible notice when a gate's artifacts can't be found or parsed and the gate fails open. + - Prove real-tmux teardown reaps the exact detached child after identity publication fails (DW-149). - **The accepted-park arm's `_harvest_gate_exclude` join is now graded engine-side** diff --git a/src/bmad_loop/data/plugins/tea/tea_plugin.py b/src/bmad_loop/data/plugins/tea/tea_plugin.py index 4a2d6d870..7f02abbbf 100644 --- a/src/bmad_loop/data/plugins/tea/tea_plugin.py +++ b/src/bmad_loop/data/plugins/tea/tea_plugin.py @@ -29,6 +29,7 @@ from __future__ import annotations import json +import sys from pathlib import Path from typing import TYPE_CHECKING, Any @@ -169,6 +170,11 @@ def on_pre_commit(self, ctx: "HookContext") -> None: for gate in gates: verdict = self._gate_verdict(gate, artifacts_dir) if verdict is None: + print( + f"tea: gate {gate!r} has no parseable verdict under " + f"{artifacts_dir}; failing open (treated as pass, commit proceeds)", + file=sys.stderr, + ) continue # fail-open: missing / unparseable / not-evaluated verdicts[gate] = verdict if verdict in BLOCKING_VERDICTS: diff --git a/tests/test_plugin_tea.py b/tests/test_plugin_tea.py index 38442b5c8..2147d57aa 100644 --- a/tests/test_plugin_tea.py +++ b/tests/test_plugin_tea.py @@ -524,21 +524,29 @@ def test_advisory_gate_never_blocks(project): assert "tea_gates" not in ctx.shared -def test_missing_artifact_is_fail_open(project): - """A blocking gate with no artifact on disk never blocks the commit.""" +def test_missing_artifact_is_fail_open(project, capsys): + """A blocking gate with no artifact on disk never blocks the commit, and the + operator gets a visible stderr notice that the gate failed open.""" ctx = pre_commit_ctx(project) _tea_instance(trace_blocking=True).on_pre_commit(ctx) assert not ctx.vetoed + stderr = capsys.readouterr().err + assert "trace" in stderr + assert "fail" in stderr.lower() -def test_garbled_artifact_is_fail_open(project): - """An unparseable gate artifact never wrongly stops a commit.""" +def test_garbled_artifact_is_fail_open(project, capsys): + """An unparseable gate artifact never wrongly stops a commit, and the operator + gets a visible stderr notice that the gate failed open.""" art = project.project / "_bmad-output" / "test-artifacts" art.mkdir(parents=True, exist_ok=True) (art / "gate-decision.json").write_text("{ this is not json", encoding="utf-8") ctx = pre_commit_ctx(project) _tea_instance(trace_blocking=True).on_pre_commit(ctx) assert not ctx.vetoed + stderr = capsys.readouterr().err + assert "trace" in stderr + assert "fail" in stderr.lower() def test_not_evaluated_verdict_is_fail_open(project):