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
5 changes: 4 additions & 1 deletion loopx/control_plane/work_items/operator_inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,11 @@ def _pending_events(
) -> list[dict[str, Any]]:
processed_path = inbox / "processed.json"
processed: set[str] = set()
if processed_path.is_file():
try:
processed_payload = _read_mapping(processed_path)
except FileNotFoundError:
pass
else:
if (
processed_payload.get("schema_version")
!= source_contract.processed_schema_version
Expand Down
10 changes: 6 additions & 4 deletions loopx/extensions/lark/event_inbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,10 @@ def load_lark_event_inbox_config(


def _load_processed(path: Path) -> set[str]:
if not path.is_file():
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
return set()
payload = json.loads(path.read_text(encoding="utf-8"))
if (
not isinstance(payload, Mapping)
or payload.get("schema_version") != PROCESSED_SCHEMA_VERSION
Expand Down Expand Up @@ -726,12 +727,13 @@ def inspect_lark_event_inbox(

def _material_review_ledger(inbox: Path) -> tuple[Path, dict[str, Any]]:
path = inbox / "material-review" / "receipts.json"
if not path.is_file():
try:
payload = json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
return path, {
"schema_version": MATERIAL_REVIEW_LEDGER_SCHEMA_VERSION,
"receipts": {},
}
payload = json.loads(path.read_text(encoding="utf-8"))
if (
not isinstance(payload, dict)
or payload.get("schema_version") != MATERIAL_REVIEW_LEDGER_SCHEMA_VERSION
Expand Down
53 changes: 53 additions & 0 deletions tests/extensions/test_operator_inbox_material_review.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,3 +537,56 @@ def blocked_writer(path: Path, payload: object) -> None:
inbox = tmp_path / ".loopx" / "inbox" / "lark" / "material-review"
processed = json.loads((inbox / "processed.json").read_text(encoding="utf-8"))
assert set(processed["message_ids"]) == {"om_material_text", "om_direct"}


def test_processed_state_path_damage_never_reopens_acknowledged_events(
tmp_path: Path,
) -> None:
config = _config(tmp_path)
_ingest(tmp_path, config)
acknowledge_lark_event_inbox(
project=tmp_path,
config_path=config,
message_ids=["om_direct"],
execute=True,
)
inbox = tmp_path / ".loopx" / "inbox" / "lark" / "material-review"
processed = inbox / "processed.json"
processed.unlink()
processed.mkdir()

with pytest.raises(IsADirectoryError):
inspect_lark_event_inbox(project=tmp_path, config_path=config)
with pytest.raises(IsADirectoryError):
project_lark_event_inbox_urgency(project=tmp_path, config_path=config)
with pytest.raises(IsADirectoryError):
acknowledge_lark_event_inbox(
project=tmp_path,
config_path=config,
message_ids=["om_direct"],
)


def test_material_review_receipt_path_damage_is_not_an_empty_ledger(
tmp_path: Path,
) -> None:
config = _config(tmp_path)
_ingest(tmp_path, config)
receipts = (
tmp_path
/ ".loopx"
/ "inbox"
/ "lark"
/ "material-review"
/ "material-review"
/ "receipts.json"
)
receipts.mkdir(parents=True)

with pytest.raises(IsADirectoryError):
settle_lark_event_inbox_material_review(
project=tmp_path,
config_path=config,
message_id="om_material_text",
no_follow_up_reason="Reviewed before ledger damage.",
)
Loading