Skip to content

Commit 67597fa

Browse files
committed
fix: guard work import state transitions
1 parent a724eb4 commit 67597fa

2 files changed

Lines changed: 46 additions & 0 deletions

File tree

src/brigade/work_cmd.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1993,6 +1993,9 @@ def import_promote(
19931993
if item is None:
19941994
print(f"error: import not found: {import_id}", file=sys.stderr)
19951995
return 1
1996+
if item.get("status", "pending") != "pending":
1997+
print(f"error: import is not pending: {item.get('id')} ({item.get('status')})", file=sys.stderr)
1998+
return 2
19961999
text = str(item.get("text") or "").strip()
19972000
if not text:
19982001
print(f"error: import has no text: {import_id}", file=sys.stderr)
@@ -2016,6 +2019,9 @@ def import_dismiss(*, target: Path, import_id: str, reason: str | None = None) -
20162019
if item is None:
20172020
print(f"error: import not found: {import_id}", file=sys.stderr)
20182021
return 1
2022+
if item.get("status", "pending") != "pending":
2023+
print(f"error: import is not pending: {item.get('id')} ({item.get('status')})", file=sys.stderr)
2024+
return 2
20192025
now = _now().isoformat()
20202026
item["status"] = "dismissed"
20212027
item["updated_at"] = now

tests/test_work_cmd.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -942,6 +942,46 @@ def test_work_import_dismiss_marks_import_not_pending(tmp_path, monkeypatch, cap
942942
assert payload["imports"][0]["dismiss_reason"] == "not actionable"
943943

944944

945+
def test_work_import_promote_rejects_non_pending_import(tmp_path, monkeypatch, capsys):
946+
_init_git_repo(tmp_path)
947+
monkeypatch.setattr(
948+
work_cmd,
949+
"_now",
950+
lambda: datetime(2026, 5, 26, 12, 0, 0, tzinfo=timezone.utc),
951+
)
952+
assert work_cmd.import_add(target=tmp_path, text="Dismissed scanner item", source="discord") == 0
953+
import_id = capsys.readouterr().out.split("import: ", 1)[1].splitlines()[0]
954+
assert work_cmd.import_dismiss(target=tmp_path, import_id=import_id) == 0
955+
capsys.readouterr()
956+
957+
assert work_cmd.import_promote(target=tmp_path, import_id=import_id) == 2
958+
assert "import is not pending" in capsys.readouterr().err
959+
960+
imports = json.loads((tmp_path / ".brigade" / "work" / "imports" / "inbox.jsonl").read_text().splitlines()[0])
961+
assert imports["status"] == "dismissed"
962+
assert not (tmp_path / ".brigade" / "work" / "tasks.json").exists()
963+
964+
965+
def test_work_import_dismiss_rejects_non_pending_import(tmp_path, monkeypatch, capsys):
966+
_init_git_repo(tmp_path)
967+
monkeypatch.setattr(
968+
work_cmd,
969+
"_now",
970+
lambda: datetime(2026, 5, 26, 12, 0, 0, tzinfo=timezone.utc),
971+
)
972+
assert work_cmd.import_add(target=tmp_path, text="Promote scanner item", source="slack") == 0
973+
import_id = capsys.readouterr().out.split("import: ", 1)[1].splitlines()[0]
974+
assert work_cmd.import_promote(target=tmp_path, import_id=import_id) == 0
975+
capsys.readouterr()
976+
977+
assert work_cmd.import_dismiss(target=tmp_path, import_id=import_id, reason="late cleanup") == 2
978+
assert "import is not pending" in capsys.readouterr().err
979+
980+
payload = json.loads((tmp_path / ".brigade" / "work" / "imports" / "inbox.jsonl").read_text().splitlines()[0])
981+
assert payload["status"] == "promoted"
982+
assert "dismiss_reason" not in payload
983+
984+
945985
def test_work_brief_includes_pending_imports(tmp_path, monkeypatch, capsys):
946986
_init_git_repo(tmp_path)
947987
monkeypatch.setattr(

0 commit comments

Comments
 (0)