Skip to content

Commit 7a87dfe

Browse files
committed
Add official observation facts for Thesis resolutions
1 parent 5e851c5 commit 7a87dfe

3 files changed

Lines changed: 97 additions & 0 deletions

File tree

ledger/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# PolicyEngine Ledger
2+
3+
PolicyEngine Ledger records public source facts. It does not record predictions,
4+
forecast distributions, agent traces, or forecast scores.
5+
6+
`official_observations.jsonl` contains source-backed `AggregateFact` rows for
7+
official observations that downstream systems can use as resolution facts. Each
8+
row should keep `source_record_id` stable and source-specific, because downstream
9+
prediction systems resolve against that ID.

ledger/official_observations.jsonl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{"value":172,"period":{"type":"month","value":"2026-05"},"geography":{"level":"country","id":"0100000US","vintage":"current","name":"United States"},"entity":{"name":"person","role":"nonfarm_payroll_employee"},"measure":{"concept":"bls.ces.total_nonfarm_payroll_change","unit":"thousands","source_concept":"bls.ces.total_nonfarm_payroll_change","concept_relation":"source_label","concept_authority":"bls","concept_evidence_url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","concept_evidence_notes":"BLS first reported total nonfarm payroll employment increased by 172,000 in May 2026."},"aggregation":{"method":"sum"},"source":{"source_name":"bls","source_table":"Employment Situation, May 2026","source_file":"empsit_06052026.htm","url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","vintage":"may_2026_first_print","extracted_at":"2026-06-08","extraction_method":"Official release value recorded from publisher news release","method_notes":"First-print May 2026 Employment Situation release."},"filters":{"seasonal_adjustment":"seasonally_adjusted"},"domain":"total_nonfarm_payroll_employment","label":"United States May 2026 sum BLS CES total nonfarm payroll change for persons [BLS Employment Situation, May 2026 first print]","source_record_id":"bls.ces.total_nonfarm_payroll_change.may_2026.first_print","source_cell_keys":["official_release:bls:empsit_06052026:payroll_change"],"source_row_keys":["official_release:bls:empsit_06052026:summary"]}
2+
{"value":4.3,"period":{"type":"month","value":"2026-05"},"geography":{"level":"country","id":"0100000US","vintage":"current","name":"United States"},"entity":{"name":"person","role":"civilian_labor_force"},"measure":{"concept":"bls.cps.unemployment_rate","unit":"percent","source_concept":"bls.cps.unemployment_rate","concept_relation":"source_label","concept_authority":"bls","concept_evidence_url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","concept_evidence_notes":"BLS first reported the May 2026 unemployment rate was unchanged at 4.3 percent."},"aggregation":{"method":"rate","denominator":"civilian_labor_force"},"source":{"source_name":"bls","source_table":"Employment Situation, May 2026","source_file":"empsit_06052026.htm","url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","vintage":"may_2026_first_print","extracted_at":"2026-06-08","extraction_method":"Official release value recorded from publisher news release","method_notes":"First-print May 2026 Employment Situation release."},"filters":{"seasonal_adjustment":"seasonally_adjusted","age":"16_plus"},"domain":"civilian_labor_force","label":"United States May 2026 rate BLS CPS unemployment rate for persons [BLS Employment Situation, May 2026 first print]","source_record_id":"bls.cps.unemployment_rate.may_2026.first_print","source_cell_keys":["official_release:bls:empsit_06052026:unemployment_rate"],"source_row_keys":["official_release:bls:empsit_06052026:summary"]}
3+
{"value":0.3,"period":{"type":"month","value":"2026-05"},"geography":{"level":"country","id":"0100000US","vintage":"current","name":"United States"},"entity":{"name":"person","role":"private_nonfarm_payroll_employee"},"measure":{"concept":"bls.ces.average_hourly_earnings_private_monthly_change","unit":"percent_growth","source_concept":"bls.ces.average_hourly_earnings_private","concept_relation":"source_label","concept_authority":"bls","concept_evidence_url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","concept_evidence_notes":"BLS first reported average hourly earnings for all employees on private nonfarm payrolls rose 0.3 percent in May 2026."},"aggregation":{"method":"mean"},"source":{"source_name":"bls","source_table":"Employment Situation, May 2026","source_file":"empsit_06052026.htm","url":"https://www.bls.gov/news.release/archives/empsit_06052026.htm","vintage":"may_2026_first_print","extracted_at":"2026-06-08","extraction_method":"Official release value recorded from publisher news release","method_notes":"First-print May 2026 Employment Situation release."},"filters":{"seasonal_adjustment":"seasonally_adjusted","employee_group":"all_employees_private_nonfarm"},"domain":"private_nonfarm_payroll_employees","label":"United States May 2026 mean BLS CES average hourly earnings private monthly change for persons [BLS Employment Situation, May 2026 first print]","source_record_id":"bls.ces.average_hourly_earnings_private.may_2026.first_print","source_cell_keys":["official_release:bls:empsit_06052026:average_hourly_earnings_mom"],"source_row_keys":["official_release:bls:empsit_06052026:summary"]}
4+
{"value":6.6,"period":{"type":"month","value":"2026-05"},"geography":{"level":"country","id":"CA","vintage":"current","name":"Canada"},"entity":{"name":"person","role":"labour_force"},"measure":{"concept":"statcan.lfs.unemployment_rate","unit":"percent","source_concept":"statcan.lfs.unemployment_rate","concept_relation":"source_label","concept_authority":"statcan","concept_evidence_url":"https://www150.statcan.gc.ca/n1/daily-quotidien/260605/dq260605a-eng.htm","concept_evidence_notes":"Statistics Canada first reported the unemployment rate fell 0.3 percentage points to 6.6% in May 2026."},"aggregation":{"method":"rate","denominator":"labour_force"},"source":{"source_name":"statcan","source_table":"Labour Force Survey, May 2026","source_file":"dq260605a-eng.htm","url":"https://www150.statcan.gc.ca/n1/daily-quotidien/260605/dq260605a-eng.htm","vintage":"may_2026_first_print","extracted_at":"2026-06-08","extraction_method":"Official release value recorded from publisher Daily release","method_notes":"First-print May 2026 Labour Force Survey Daily release."},"filters":{"seasonal_adjustment":"seasonally_adjusted","age":"15_plus"},"domain":"labour_force","label":"Canada May 2026 rate Statistics Canada LFS unemployment rate for persons [Statistics Canada Labour Force Survey, May 2026 first print]","source_record_id":"statcan.lfs.unemployment_rate.canada.may_2026.first_print","source_cell_keys":["official_release:statcan:dq260605a:unemployment_rate"],"source_row_keys":["official_release:statcan:dq260605a:highlights"]}
5+
{"value":88,"period":{"type":"month","value":"2026-05"},"geography":{"level":"country","id":"CA","vintage":"current","name":"Canada"},"entity":{"name":"person","role":"employed"},"measure":{"concept":"statcan.lfs.employment_change","unit":"thousands","source_concept":"statcan.lfs.employment_change","concept_relation":"source_label","concept_authority":"statcan","concept_evidence_url":"https://www150.statcan.gc.ca/n1/daily-quotidien/260605/dq260605a-eng.htm","concept_evidence_notes":"Statistics Canada first reported employment increased by 88,000 in May 2026."},"aggregation":{"method":"sum"},"source":{"source_name":"statcan","source_table":"Labour Force Survey, May 2026","source_file":"dq260605a-eng.htm","url":"https://www150.statcan.gc.ca/n1/daily-quotidien/260605/dq260605a-eng.htm","vintage":"may_2026_first_print","extracted_at":"2026-06-08","extraction_method":"Official release value recorded from publisher Daily release","method_notes":"First-print May 2026 Labour Force Survey Daily release."},"filters":{"seasonal_adjustment":"seasonally_adjusted","age":"15_plus"},"domain":"employment","label":"Canada May 2026 sum Statistics Canada LFS employment change for persons [Statistics Canada Labour Force Survey, May 2026 first print]","source_record_id":"statcan.lfs.employment_change.canada.may_2026.first_print","source_cell_keys":["official_release:statcan:dq260605a:employment_change"],"source_row_keys":["official_release:statcan:dq260605a:highlights"]}

tests/test_policyengine_ledger.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
"""Tests for checked-in PolicyEngine Ledger observation facts."""
2+
3+
from __future__ import annotations
4+
5+
import json
6+
from pathlib import Path
7+
8+
from arch.core import (
9+
AggregateFact,
10+
Aggregation,
11+
EntityDimension,
12+
GeographyDimension,
13+
Measure,
14+
PeriodDimension,
15+
SourceProvenance,
16+
validate_facts,
17+
)
18+
19+
20+
ROOT = Path(__file__).resolve().parents[1]
21+
LEDGER_PATH = ROOT / "ledger" / "official_observations.jsonl"
22+
23+
EXPECTED_SOURCE_RECORD_IDS = {
24+
"bls.ces.total_nonfarm_payroll_change.may_2026.first_print",
25+
"bls.cps.unemployment_rate.may_2026.first_print",
26+
"bls.ces.average_hourly_earnings_private.may_2026.first_print",
27+
"statcan.lfs.unemployment_rate.canada.may_2026.first_print",
28+
"statcan.lfs.employment_change.canada.may_2026.first_print",
29+
}
30+
31+
32+
def _read_ledger_facts() -> list[dict]:
33+
return [
34+
json.loads(line)
35+
for line in LEDGER_PATH.read_text(encoding="utf-8").splitlines()
36+
if line.strip()
37+
]
38+
39+
40+
def _to_aggregate_fact(row: dict) -> AggregateFact:
41+
return AggregateFact(
42+
value=row["value"],
43+
period=PeriodDimension(**row["period"]),
44+
geography=GeographyDimension(**row["geography"]),
45+
entity=EntityDimension(**row["entity"]),
46+
measure=Measure(**row["measure"]),
47+
aggregation=Aggregation(**row["aggregation"]),
48+
source=SourceProvenance(**row["source"]),
49+
filters=row.get("filters", {}),
50+
domain=row.get("domain", "all"),
51+
label=row.get("label"),
52+
source_record_id=row.get("source_record_id"),
53+
source_cell_keys=tuple(row.get("source_cell_keys", ())),
54+
source_row_keys=tuple(row.get("source_row_keys", ())),
55+
)
56+
57+
58+
def test_official_observation_ledger_has_expected_rows():
59+
rows = _read_ledger_facts()
60+
61+
assert {row["source_record_id"] for row in rows} == EXPECTED_SOURCE_RECORD_IDS
62+
assert len(rows) == len(EXPECTED_SOURCE_RECORD_IDS)
63+
64+
65+
def test_official_observation_ledger_contains_facts_not_predictions():
66+
rows = _read_ledger_facts()
67+
68+
for row in rows:
69+
assert "prediction" not in json.dumps(row).lower()
70+
assert "forecast" not in json.dumps(row).lower()
71+
assert row["source_record_id"]
72+
assert row["source"]["url"].startswith("https://")
73+
assert row["source"]["vintage"] == "may_2026_first_print"
74+
75+
76+
def test_official_observations_validate_as_aggregate_facts():
77+
facts = [_to_aggregate_fact(row) for row in _read_ledger_facts()]
78+
79+
report = validate_facts(facts)
80+
81+
assert report.valid, report.to_dict()
82+
assert report.counts["by_source"] == {"bls": 3, "statcan": 2}
83+
assert report.counts["missing_lineage"]["count"] == 0

0 commit comments

Comments
 (0)