From 5c7f84b43798b51b4d7833a0aca6e72a6c14e350 Mon Sep 17 00:00:00 2001 From: Guillermo Montero Date: Thu, 17 Sep 2026 16:05:04 +0200 Subject: [PATCH] fix(tea-plugin): surface a one-time session-cost notice at plugin activation --- CHANGELOG.md | 2 ++ src/bmad_loop/data/plugins/tea/tea_plugin.py | 33 ++++++++++++++++++++ tests/test_plugin_tea.py | 20 ++++++++++++ 3 files changed, 55 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5259aee07..f8ed8f6ff 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 a one-time notice at activation about the extra agent sessions its default settings add per story. + - 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..c81992960 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 @@ -42,6 +43,19 @@ # advisory by design and are deliberately absent — they are never gate-enforced. GATE_STEPS = ("trace", "nfr", "review") +# The six step-enable settings that gate the extra agent sessions TEA injects +# per story. All default to true (see plugin.toml); when every one of them is +# still at that default, validate() surfaces a one-time cost notice (see +# _warn_if_defaults_costly) -- visibility only, never a default change. +COST_SETTINGS = ( + "td_enabled", + "atdd_enabled", + "automate_enabled", + "trace_enabled", + "nfr_enabled", + "review_enabled", +) + # Verdicts that escalate a blocking gate. PASS / WAIVED (an explicit human # approval to proceed) / an unknown-or-not-evaluated verdict never block. BLOCKING_VERDICTS = frozenset({"FAIL", "CONCERNS"}) @@ -138,6 +152,7 @@ def validate(self, policy: Any) -> None: "require_tea = false under [plugins.tea] in .bmad-loop/policy.toml " "to run the TEA workflows advisory-only without it." ) + self._warn_if_defaults_costly() # --------------------------------------------------------- enforcement @@ -191,6 +206,24 @@ def on_pre_commit(self, ctx: "HookContext") -> None: def _require_tea(self) -> bool: return bool(self.settings.get("require_tea", True)) + def _warn_if_defaults_costly(self) -> None: + """One-time, non-blocking notice on stderr: when all six step-enable + settings (COST_SETTINGS) are still at their true default, enabling tea + injects 6 extra agent sessions per story (3 post_dev_phase + 3 + pre_commit_gate). Never raises and never changes a default -- this is a + visibility fix only; see docs/tea-plugin-guide.md for the cost + breakdown and how to disable steps that aren't needed.""" + if not all(bool(self.settings.get(key, True)) for key in COST_SETTINGS): + return # an operator already opted out of at least one step + sys.stderr.write( + "plugin 'tea': all six step settings (td_enabled, atdd_enabled, " + "automate_enabled, trace_enabled, nfr_enabled, review_enabled) are " + "at their true default -- enabling tea this way adds 6 extra agent " + "sessions per story (3 post_dev_phase + 3 pre_commit_gate). See " + "docs/tea-plugin-guide.md for the cost breakdown and how to disable " + "steps you don't need.\n" + ) + def _project_root(self) -> Path: """The project root, resolved the way the engine resolves it: the run's working directory (``bmad-loop``'s ``--project`` defaults to cwd, and the diff --git a/tests/test_plugin_tea.py b/tests/test_plugin_tea.py index 38442b5c8..b1cacf6dc 100644 --- a/tests/test_plugin_tea.py +++ b/tests/test_plugin_tea.py @@ -263,6 +263,26 @@ def test_readiness_skipped_when_require_tea_false(project, monkeypatch): _tea_instance(require_tea=False).validate(Policy()) # no raise +def test_readiness_emits_cost_notice_when_defaults_in_effect(project, monkeypatch, capsys): + # All six step-enable settings still at their true default -> validate() + # surfaces the one-time cost notice on stderr, and still activates normally. + install_tea(project) + monkeypatch.chdir(project.project) + _tea_instance(require_tea=True).validate(Policy()) # no raise + err = capsys.readouterr().err + assert "6 extra agent sessions per story" in err + assert "docs/tea-plugin-guide.md" in err + + +def test_readiness_skips_cost_notice_when_a_step_is_disabled(project, monkeypatch, capsys): + # An operator who already opted out of at least one step (e.g. disabled + # ATDD) no longer has all six settings at default -> no notice. + install_tea(project) + monkeypatch.chdir(project.project) + _tea_instance(require_tea=True, atdd_enabled=False).validate(Policy()) # no raise + assert capsys.readouterr().err == "" + + def test_engine_construction_fails_fast_without_tea(project, monkeypatch): """The engine runs registry.validate() at startup; an enabled tea plugin with no TEA install fails construction before any story runs."""