Skip to content
Draft
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**
Expand Down
6 changes: 6 additions & 0 deletions src/bmad_loop/data/plugins/tea/tea_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from __future__ import annotations

import json
import sys
from pathlib import Path
from typing import TYPE_CHECKING, Any

Expand Down Expand Up @@ -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:
Expand Down
16 changes: 12 additions & 4 deletions tests/test_plugin_tea.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down