mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
Foundation for making all hazard formatting+gating source-agnostic. ZERO behavior change — the formatter/decider registries are empty (get_formatter/ get_decider return None → existing precomposed/Mode-B path preserved), and the shadow comparator is off unless MESHAI_SHADOW_CATEGORIES is set. - notifications/formatters/ (registry+dispatch with family fallback), gating/ (GateResult + deferred-commit contract), both empty registries. - notifications/clock.py determinism seam; route wfigs/quake/nws gating time reads through it (identical values) so goldens can freeze time. - formatters/_budget.py = copy of central/budget.py; central/budget.py is now a re-export shim (import-smoke test guards it). - compose_mesh_message consults the registry first (verbatim, no Mode-B re-cap), falls back to legacy; _resolve_budget injects per-category budget. - notifications/shadow.py + two DRY-RUN hooks (consumer._normalize, dispatcher render): compute the new result and diff-log SHADOW_MISMATCH JSONL, but NEVER commit/emit/write tables and always broadcast the OLD result. Inert by default. - tests/harness (pinned_time/pinned_tz, byte-golden, gate-sequence) + scripts/capture_fixtures.py (ephemeral read-only NATS capture); tzdata pinned. Tests: +60 (18 scaffold + 42 harness/shadow); 0 new failures (34 baseline). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
46 lines
1.6 KiB
Python
46 lines
1.6 KiB
Python
"""Phase-0 purity guard: formatter modules must not call time directly.
|
|
|
|
Every file under meshai/notifications/formatters/ must route all time reads
|
|
through meshai.notifications.clock (the determinism seam). Direct calls to
|
|
datetime.now or time.time( inside a formatter break the monkeypatch contract.
|
|
|
|
This test is intentionally static (source-text check) so it catches new
|
|
formatters that forget the contract even before any test calls them.
|
|
"""
|
|
|
|
import glob
|
|
import os
|
|
|
|
|
|
def _formatter_sources():
|
|
"""Return (path, source) pairs for all .py files in the formatters package."""
|
|
pattern = os.path.join(
|
|
os.path.dirname(__file__),
|
|
"..", "meshai", "notifications", "formatters", "*.py",
|
|
)
|
|
paths = sorted(glob.glob(pattern))
|
|
return [(p, open(p).read()) for p in paths]
|
|
|
|
|
|
def test_no_datetime_now_in_formatters():
|
|
"""No formatter file may call datetime.now directly."""
|
|
violations = []
|
|
for path, src in _formatter_sources():
|
|
if "datetime.now" in src:
|
|
violations.append(os.path.basename(path))
|
|
assert not violations, (
|
|
"The following formatter files call datetime.now directly "
|
|
"(use clock.now_dt() instead):\n" + "\n".join(violations)
|
|
)
|
|
|
|
|
|
def test_no_time_time_in_formatters():
|
|
"""No formatter file may call time.time( directly."""
|
|
violations = []
|
|
for path, src in _formatter_sources():
|
|
if "time.time(" in src:
|
|
violations.append(os.path.basename(path))
|
|
assert not violations, (
|
|
"The following formatter files call time.time( directly "
|
|
"(use clock.now() instead):\n" + "\n".join(violations)
|
|
)
|