Skip to content

Commit 31c3d8e

Browse files
Pigbibicodex
andcommitted
feat: add weekly manifest integration
Co-Authored-By: Codex <noreply@openai.com>
1 parent f9fd69e commit 31c3d8e

2 files changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
"""Pure deterministic manifest integration for ``political_event_weekly.v1``."""
2+
from __future__ import annotations
3+
4+
import json
5+
from collections.abc import Mapping
6+
from pathlib import Path
7+
8+
from .weekly_contract import WeeklyContractError, WeeklySourceContract, parse_weekly_contract, serialize_weekly_contract
9+
10+
MANIFEST_TYPE = "political_event_weekly_manifest"
11+
_MANIFEST_KEYS = frozenset({"manifest_type", "contract"})
12+
13+
14+
def build_weekly_manifest(contract: WeeklySourceContract) -> dict[str, object]:
15+
if not isinstance(contract, WeeklySourceContract):
16+
raise WeeklyContractError("manifest_contract_invalid")
17+
try:
18+
contract_payload = json.loads(serialize_weekly_contract(contract))
19+
except (TypeError, ValueError, UnicodeError):
20+
raise WeeklyContractError("manifest_contract_invalid") from None
21+
return {"manifest_type": MANIFEST_TYPE, "contract": contract_payload}
22+
23+
24+
def parse_weekly_manifest(value: Mapping[str, object]) -> WeeklySourceContract:
25+
if not isinstance(value, Mapping) or set(value) != _MANIFEST_KEYS or value.get("manifest_type") != MANIFEST_TYPE:
26+
raise WeeklyContractError("manifest_shape_invalid")
27+
contract = value.get("contract")
28+
if not isinstance(contract, Mapping):
29+
raise WeeklyContractError("manifest_contract_invalid")
30+
return parse_weekly_contract(contract)
31+
32+
33+
def validate_weekly_manifest(value: Mapping[str, object], expected: WeeklySourceContract) -> WeeklySourceContract:
34+
if not isinstance(expected, WeeklySourceContract):
35+
raise WeeklyContractError("manifest_expected_invalid")
36+
parsed = parse_weekly_manifest(value)
37+
if parsed != expected:
38+
raise WeeklyContractError("manifest_contract_mismatch")
39+
return parsed
40+
41+
42+
def serialize_weekly_manifest(contract: WeeklySourceContract) -> bytes:
43+
payload = build_weekly_manifest(contract)
44+
try:
45+
return json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
46+
except (TypeError, ValueError, UnicodeError):
47+
raise WeeklyContractError("manifest_serialization_invalid") from None
48+
49+
50+
def write_weekly_manifest(contract: WeeklySourceContract, output_path: str | Path) -> Path:
51+
content = serialize_weekly_manifest(contract)
52+
output = Path(output_path)
53+
try:
54+
output.parent.mkdir(parents=True, exist_ok=True)
55+
output.write_bytes(content)
56+
except (OSError, TypeError, ValueError):
57+
raise WeeklyContractError("manifest_write_invalid") from None
58+
return output

tests/test_weekly_manifest.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
from __future__ import annotations
2+
3+
import json
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
from political_event_tracking_research.weekly_contract import WeeklyContractError, parse_weekly_contract
9+
from political_event_tracking_research.weekly_manifest import (
10+
MANIFEST_TYPE,
11+
build_weekly_manifest,
12+
parse_weekly_manifest,
13+
serialize_weekly_manifest,
14+
validate_weekly_manifest,
15+
write_weekly_manifest,
16+
)
17+
18+
19+
def contract_payload(**overrides: object) -> dict[str, object]:
20+
value: dict[str, object] = {
21+
"schema_version": "1", "contract_version": "political_event_weekly.v1", "cadence": "weekly",
22+
"as_of": "2026-07-12", "period_start": "2026-07-06", "period_end_exclusive": "2026-07-13",
23+
"generated_at": "2026-07-13T00:00:00.123456Z", "run_mode": "manual", "producer_ref": "a" * 40,
24+
"source_provenance": "official_political_event_tracking_research_v1",
25+
"source_artifacts": [{"path": "data/live/political_events.csv", "sha256": "b" * 64, "row_count": 11}],
26+
"feed_status": {"feed_count": 12, "successful_feed_count": 12, "failed_feed_count": 0, "stale_feed_count": 0, "missing_feed_count": 0, "complete": True},
27+
}
28+
value.update(overrides)
29+
return value
30+
31+
32+
def test_weekly_manifest_is_deterministic_and_round_trips():
33+
contract = parse_weekly_contract(contract_payload())
34+
manifest = build_weekly_manifest(contract)
35+
assert manifest["manifest_type"] == MANIFEST_TYPE
36+
encoded = serialize_weekly_manifest(contract)
37+
assert encoded == serialize_weekly_manifest(parse_weekly_manifest(json.loads(encoded)))
38+
assert parse_weekly_manifest(manifest) == contract
39+
40+
41+
@pytest.mark.parametrize("field,value", [
42+
("as_of", "2026-07-13"),
43+
("generated_at", "2026-07-12T23:59:59Z"),
44+
("producer_ref", "c" * 40),
45+
("source_provenance", "official_other_v1"),
46+
("source_artifacts", [{"path": "data/live/political_events.csv", "sha256": "c" * 64, "row_count": 12}]),
47+
])
48+
def test_manifest_contract_tamper_is_rejected(field, value):
49+
contract = parse_weekly_contract(contract_payload())
50+
manifest = build_weekly_manifest(contract)
51+
manifest["contract"][field] = value
52+
with pytest.raises(WeeklyContractError):
53+
validate_weekly_manifest(manifest, contract)
54+
55+
56+
def test_manifest_feed_partial_and_shape_tamper_fail_closed():
57+
contract = parse_weekly_contract(contract_payload())
58+
manifest = build_weekly_manifest(contract)
59+
manifest["contract"]["feed_status"]["failed_feed_count"] = 1
60+
with pytest.raises(WeeklyContractError):
61+
parse_weekly_manifest(manifest)
62+
manifest = build_weekly_manifest(contract)
63+
manifest["extra"] = True
64+
with pytest.raises(WeeklyContractError):
65+
parse_weekly_manifest(manifest)
66+
67+
68+
def test_write_validates_before_creating_output(tmp_path: Path):
69+
contract = parse_weekly_contract(contract_payload())
70+
output = tmp_path / "manifest.json"
71+
write_weekly_manifest(contract, output)
72+
assert json.loads(output.read_text(encoding="utf-8"))["manifest_type"] == MANIFEST_TYPE
73+
74+
invalid = tmp_path / "invalid.json"
75+
with pytest.raises(WeeklyContractError):
76+
write_weekly_manifest(object(), invalid) # type: ignore[arg-type]
77+
assert not invalid.exists()

0 commit comments

Comments
 (0)