Skip to content
Open
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
11 changes: 7 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -112,11 +112,10 @@ jobs:
- name: Test OpenTimestamps anchoring
run: uv run pytest -q --durations=30 tests/test_ots_anchor.py

- name: Build source input database
- name: Smoke-test the source-artifact database CLI
run: |
uv run chronicle --db /tmp/chronicle-targets-ci.db init
uv run chronicle --db /tmp/chronicle-targets-ci.db load all
uv run chronicle --db /tmp/chronicle-targets-ci.db stats
uv run chronicle --db /tmp/chronicle-source-ci.db init
uv run chronicle --db /tmp/chronicle-source-ci.db source-stats

- name: Build wheel
run: |
Expand All @@ -134,13 +133,17 @@ jobs:
import policyengine_chronicle.normalization
import policyengine_chronicle.sources
import policyengine_chronicle.targets
from policyengine_chronicle.targets import hard_target_package_aliases
from policyengine_chronicle.schema import (
CONSUMER_FACT_SCHEMA_SHA256,
CONSUMER_FACT_SCHEMA_SHA256_BY_VERSION,
consumer_fact_schema,
)

assert policyengine_chronicle.__name__ == "policyengine_chronicle"
# Target source coverage ships; the legacy target tables do not.
assert "soi-table-1-1" in hard_target_package_aliases()
assert not hasattr(policyengine_chronicle.targets, "query_targets")
# Every consumer-fact contract must ship inside the wheel: frozen v1,
# the v2 labels contract, the v3 geography-name contract, and the v4
# contract rows are emitted under.
Expand Down
59 changes: 23 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,12 @@ chronicle/
│ ├── sources/ # Source lineage helpers
│ ├── facts/ # Source-backed facts
│ ├── normalization/ # Low-assumption representation helpers
│ ├── targets/ # Target input schema, client, loaders
│ ├── targets/ # Target source coverage (US poverty/nonfiler)
│ ├── jurisdictions/ # Temporary in-repo jurisdiction source prototypes
├── db/ # SQLModel persistence and source loaders
│ ├── schema.py # SQLModel: Target, Stratum, StratumConstraint
├── db/ # Source-artifact store and packaged source files
│ ├── schema.py # SQLModel: SourceArtifact, SourceTable, SourceRow
│ ├── supabase_client.py # Supabase client helpers
│ └── etl_*.py # Source-specific ETL pipelines
│ └── data/ # Publisher files that source packages read
├── data/ # Cached data files
└── docs/ # Architecture and source documentation
```
Expand All @@ -127,23 +127,16 @@ Solver execution and calibrated dataset construction belong in Microcosm.

### 1. Install

Chronicle is not published on PyPI. Clone this repository into a
Chronicle-named directory and install it with its development extras:

```bash
pip install policyengine-chronicle
# Or for development, clone this repository into a Chronicle-named directory:
git clone <current repository URL> chronicle
cd chronicle
pip install -e ".[dev]"
```

### 2. Initialize and Load Legacy Target Inputs

```bash
chronicle init
chronicle load soi --years 2021
chronicle stats
uv sync --all-extras
```

### 3. Validate Fixture Facts
### 2. Validate Fixture Facts

The standalone Chronicle fact harness validates JSONL aggregate facts and emits a
JSON report with fact counts, QA counts, warnings, and validation errors:
Expand Down Expand Up @@ -516,7 +509,7 @@ same `--axiom-cli` and `--axiom-root` flags, plus
`--require-axiom-validation` when skipped concept checks should fail agent
acceptance.

### 4. Run Chronicle Explorer
### 3. Run Chronicle Explorer

Chronicle Explorer is a Next/Tailwind app that reads the fixture fact JSONL and
source-cell JSONL, then shows aggregate facts, source-cell lineage, and
Expand All @@ -538,25 +531,19 @@ By default, the workbench reads the current local suite outputs at
LEDGER_EXPLORER_DATA_DIRS=/tmp/chronicle-build-a,/tmp/chronicle-build-b npm run dev -- --port 3090
```

### 5. Query Target Inputs in Python

```python
from policyengine_chronicle.targets import DataSource, Target, TargetType, query_targets

target_rows = query_targets(jurisdiction="us", year=2024)
```

## Target Input Schema

Target inputs use a three-table schema:

- **strata**: Population subgroups, such as California filers with AGI between
$50k and $75k.
- **stratum_constraints**: Rules defining each stratum.
- **targets**: Source-published aggregate values linked to strata.
## Retired legacy target tables

These are source-backed inputs. Microcosm owns the contracts that select them,
the active support-aware subset, and calibrated solver execution.
Chronicle used to load hand-written ETLs into a local SQLite `targets` /
`strata` / `stratum_constraints` schema (`chronicle load`, `chronicle stats`,
`chronicle query`) and to read a Supabase copy through
`policyengine_chronicle.targets.query_targets`. No live consumer read those
tables, and most of their values did not trace to publisher bytes, so they were
retired.
Source-backed facts reach consumers through source packages, the bundle and
the consumer-fact export. Microcosm owns the contracts that select them, the
active support-aware subset, and calibrated solver execution. See
[`docs/us-legacy-etl-cleanup.md`](docs/us-legacy-etl-cleanup.md) for what each
retired loader held and which package publishes it now.

## Identifier Epochs

Expand Down Expand Up @@ -623,7 +610,7 @@ normalized_fact = convert_units(fact, 1000, "count")

## Current Coverage

### Aggregate Facts And Target Inputs
### Aggregate facts

| Source | Coverage | Description |
|--------|----------|-------------|
Expand Down
22 changes: 22 additions & 0 deletions changelog.d/legacy-targets-table-retired.removed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
Retire the legacy SQLite target tables (`targets`, `strata`,
`stratum_constraints`) and everything that wrote or read them:
- the `chronicle load`, `chronicle stats` and `chronicle query` commands
- the last two loaders, `db/etl_soi.py` and `db/etl_snap.py`
- the Supabase `query_targets` / `query_strata` / `insert_targets_batch` helpers
- `scripts/export_to_json.py`
- the `as_target` / `target_kwargs` / `TargetBlueprint` normalization helpers
- the legacy names in `chronicle.targets` and `policyengine_chronicle.targets`

Nothing reads these tables today. The values the last two loaders wrote (IRS
SOI Tables 1.1 and 1.4, and SNAP) are published as source-package facts through
the bundle and consumer-fact export, which are unchanged.

`chronicle init` now creates only the source-artifact tables. The AGI-bracket
tables the SOI harness used move into `chronicle/jurisdictions/us/soi.py`.
`policyengine_chronicle.targets` keeps the US poverty/nonfiler target source
coverage.

Migration: Chronicle was never published on PyPI, and no live repository
imports these names. Archived Cosilico-era repositories once did. Code that called `query_targets` should read consumer facts from a
pinned bundle (`chronicle build-bundle`, `chronicle build-consumer-artifact`)
instead.
2 changes: 1 addition & 1 deletion chronicle/facts/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class DerivationStep:

@dataclass(frozen=True)
class SourceFact:
"""A source-backed fact before target-input materialization."""
"""A source-backed fact with its structural normalization lineage."""

name: str
value: float
Expand Down
14 changes: 5 additions & 9 deletions chronicle/jurisdictions/us/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
"""United States Chronicle target loaders."""
"""United States Chronicle source builders.

from chronicle.targets.loaders import (
load_snap_targets,
load_soi_targets,
)
US measures are source packages. The legacy DB target loaders were retired
(see docs/us-legacy-etl-cleanup.md), so this module exports no loaders.
"""

__all__ = [
"load_snap_targets",
"load_soi_targets",
]
__all__: list[str] = []
46 changes: 45 additions & 1 deletion chronicle/jurisdictions/us/soi.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
SourceRecordSpec,
)
from chronicle.yaml_io import safe_load as safe_load_yaml
from db.etl_soi import AGI_BRACKETS, TABLE_1_1_AGI_LABEL_TO_BRACKET

SOI_TABLE_1_1_SOURCE_NAME = "irs_soi"
SOI_TABLE_1_1_SOURCE_TABLE = "Publication 1304 Table 1.1"
Expand All @@ -48,6 +47,51 @@
"tax-year source record."
)

# AGI bracket definitions (lower, upper) in dollars.
AGI_BRACKETS = {
"under_1": (float("-inf"), 1),
"1_to_5k": (1, 5_000),
"5k_to_10k": (5_000, 10_000),
"10k_to_15k": (10_000, 15_000),
"15k_to_20k": (15_000, 20_000),
"20k_to_25k": (20_000, 25_000),
"25k_to_30k": (25_000, 30_000),
"30k_to_40k": (30_000, 40_000),
"40k_to_50k": (40_000, 50_000),
"50k_to_75k": (50_000, 75_000),
"75k_to_100k": (75_000, 100_000),
"100k_to_200k": (100_000, 200_000),
"200k_to_500k": (200_000, 500_000),
"500k_to_1m": (500_000, 1_000_000),
"1m_to_1_5m": (1_000_000, 1_500_000),
"1_5m_to_2m": (1_500_000, 2_000_000),
"2m_to_5m": (2_000_000, 5_000_000),
"5m_to_10m": (5_000_000, 10_000_000),
"10m_plus": (10_000_000, float("inf")),
}

TABLE_1_1_AGI_LABEL_TO_BRACKET = {
"No adjusted gross income": "under_1",
"$1 under $5,000": "1_to_5k",
"$5,000 under $10,000": "5k_to_10k",
"$10,000 under $15,000": "10k_to_15k",
"$15,000 under $20,000": "15k_to_20k",
"$20,000 under $25,000": "20k_to_25k",
"$25,000 under $30,000": "25k_to_30k",
"$30,000 under $40,000": "30k_to_40k",
"$40,000 under $50,000": "40k_to_50k",
"$50,000 under $75,000": "50k_to_75k",
"$75,000 under $100,000": "75k_to_100k",
"$100,000 under $200,000": "100k_to_200k",
"$200,000 under $500,000": "200k_to_500k",
"$500,000 under $1,000,000": "500k_to_1m",
"$1,000,000 under $1,500,000": "1m_to_1_5m",
"$1,500,000 under $2,000,000": "1_5m_to_2m",
"$2,000,000 under $5,000,000": "2m_to_5m",
"$5,000,000 under $10,000,000": "5m_to_10m",
"$10,000,000 or more": "10m_plus",
}


def build_soi_table_1_1_facts(year: int = 2023) -> list[AggregateFact]:
"""Build source-lineaged Chronicle facts from SOI Table 1.1."""
Expand Down
8 changes: 1 addition & 7 deletions chronicle/normalization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,18 @@
"""

from chronicle.facts import DerivationStep, SourceFact
from .targets import (
TargetBlueprint,
from .transforms import (
apply_share,
as_target,
convert_units,
format_derivation,
scale_value,
target_kwargs,
)

__all__ = [
"DerivationStep",
"SourceFact",
"TargetBlueprint",
"apply_share",
"as_target",
"convert_units",
"format_derivation",
"scale_value",
"target_kwargs",
]
Loading
Loading