# Presidential Economic Pressure Index (PEPI)
## Methodology Whitepaper

*Aka TACO Index*

**A Quantitative Behavioral Index for Measuring the Gap Between Presidential Pressure, Follow-Through, and Economic Results**

— By Kurt Sather  
**API Automations**  
Version 1.2.13 | June 2026
Classification: Public

> Combining behavioral analysis with economic impact measurement using the Cavallo, Llamas & Vazquez (2026) price impact methodology.

This document describes the methodology, data sources, scoring framework, and API architecture of the PEPI Index. It is intended for traders, policy researchers, journalists, and analysts who require transparency into how the index is constructed.

---

## Changelog

**v1.2.13 (June 2026)** — Realistic outcomes: follow-through detection, indeterminate state, de-duplication. An audit found the index was recording a ~97% chicken-out rate with **zero** follow-throughs — an artifact of four structural gaps, not behavior: (a) foreign-policy/domestic threats had no outcome resolver, so every lapsed one was auto-slipped to `abandoned`; (b) claims of completed action and de-escalations were minted as brand-new pending threats; and (c) near-duplicate reposts were each counted as separate TACOs. Four fixes (migration v25) correct this:

- **Follow-through / de-escalation detection (classification).** The classifier (`src/classify/threat_classifier.py`, still Claude) now tags each statement with a `statement_kind`: `new_threat`, `action_claim`, `deescalation`, or `reiteration`. Only `new_threat` mints a fresh trackable event. An `action_claim` (a claim of completed/ongoing action) is evidence of follow-through, a `deescalation` is a pause/ceasefire/call-off/deal, and a `reiteration` restates a prior threat (feeding the repetition dimension) — none of these creates a standalone pending TACO, so reposts and self-reported wins no longer inflate the pending count.
- **Foreign-policy/domestic outcome resolver (Gemini, grounded).** A new step (`src/ingest/fp_outcomes.py`, `resolve_fp_outcomes()`, run before finalization) reconciles overdue, still-pending non-trade events that the Federal Register / court / Congress resolvers cannot reach. It uses the **Gemini API with Google Search grounding** (env `GEMINI_API_KEY`, model via `GEMINI_MODEL`, default `gemini-2.5-flash`) to determine whether the threatened action was actually carried out (`full`/`partial_implementation`), de-escalated/reversed, or genuinely abandoned — weighting **independent reporting above the president's own posts** — and records `outcome_evidence_source` as `gemini_websearch` (independently corroborated) vs. `self_reported`. Confidence below 0.70 leaves the event monitoring. This **adds** Gemini for cost-efficient grounded reconciliation; **Claude remains the classifier** (unchanged). The step no-ops gracefully when `GEMINI_API_KEY` is unset.
- **Domain-aware finalization + the `indeterminate` state.** When an event's immediate window closes with no outcome, **trade** events still slip to `abandoned` (the statutory↔applied gap is observable evidence of a chicken-out), but **non-trade** events (foreign policy / domestic / rhetorical) now finalize to the new `outcome_type='indeterminate'` (evidence `none`) rather than a fabricated `abandoned`. An `indeterminate` lapse is **not scored** (`composite_score` stays `NULL`, so it never enters `index_daily`) and is **excluded from the chicken-out rate** — both numerator and denominator (in `db/export_snapshot.py` and `src/api/main.py`); the snapshot adds an `Indeterminate` outcome display label.
- **Near-duplicate collapse.** A new pipeline step (`collapse_duplicate_events()`, `db/pipeline.py`) groups unresolved, non-locked events by same domain + overlapping target entities + threat dates within ±2 days + a `threat_summary` token-Jaccard ≥ 0.8, keeps the earliest, sets the rest `status='rejected'` with `duplicate_of` → the keeper, and increments the keeper's `repetition_count`. This is the near-duplicate pass the methodology had described but which did not previously exist (ingest dedup was exact SHA-256 hash only).

Migration v25 adds `indeterminate` to the `outcome_type` enum and three `taco_events` columns (`statement_kind`, `outcome_evidence_source`, `duplicate_of`, with indexes on the latter two), and is registered in `.github/workflows/pipeline.yml`; the FP resolve step adds `google-genai` to requirements and runs before `finalize_slipped_events()`. New pipeline CLI flags: `--skip-dedup`, `--skip-fp-resolve`, `--fp-resolve-limit`. See §1.3 of the Data Dictionary for the new columns and enum value.

**v1.2.13 (June 2026)** — Live pressure for deadline-less threats + source attribution. (1) **Effective deadline.** `score_pending_pressure()` previously built live pressure only for events with an explicit `stated_deadline`, so a past-dated threat that named **no** deadline — the common case — contributed nothing to `index.live_value` until it auto-finalized 270 days later (the live index read as inert). Pressure is now measured against an **effective deadline**: the `stated_deadline`, or `threat_date + 30 days` (a grace window) when none is stated, so a deadline-less threat builds pressure 30 days after it is made. The live window remains the exact complement of `finalize_slipped_events()` — deadline events to `stated_deadline + 90 days`, deadline-less to `threat_date + 270 days` — so there is still no limbo gap or double-count. Only `index.live_value` is affected; the settled `index.value` is unchanged. (2) **Source attribution.** The CourtListener and Congress.gov resolvers now tag resolved outcomes with `outcome_evidence_source = 'court'/'congress'` (previously unwritten), so the public "Source Freshness" panel reflects their real last-resolution date instead of showing "—" permanently. See §5.4.

**v1.2.12 (June 2026)** — Deadline-pressure / finalization window reconciliation. `score_pending_pressure()` scoped live pressure to "current threats" by anchoring on `threat_date + 90 days`, while `finalize_slipped_events()` closes the immediate window on `stated_deadline + 90 days`. For any event whose deadline falls **after** its threat date, those anchors diverge: once the threat aged past 90 days the event lost its `live_pressure_score`, yet it was not finalized until `deadline + 90` — so for the `(deadline − threat_date)`-day interval in between it dropped silently out of **both** `index.live_value` (pressure cleared) and `index.value` (still unresolved). The live-pressure clear/select clauses are now anchored on `stated_deadline` (the documented immediate window), making them the exact complement of finalization: pressure runs from the deadline up to `deadline + 90`, then the event slips to `abandoned` — no limbo gap, no double-count. Ancient backfill is still excluded (its deadline is long past, so it is finalized rather than pressured), so `index.live_value` stays honest. Behavior is unchanged for events whose deadline ≈ threat date (the common short-fuse case); only deadline-after-threat events are affected. The current published snapshot is unaffected (every overdue event today has both anchors inside 90 days). A point-in-time reconciliation catalogue of all past-deadline threats lives in [`docs/DEADLINE_RECONCILIATION.md`](DEADLINE_RECONCILIATION.md). See §5.4.

**v1.2.11 (June 2026)** — Event lifecycle finalization + calibration-catalogue integrity. (1) **Every event now terminates** as a TACO or a facilitation rather than being tracked indefinitely: promotion to `monitoring` is keyed on `threat_date` (current threats only), and `finalize_slipped_events()` resolves any event whose immediate window has closed — `stated_deadline + 90 days`, or `threat_date + 270 days` when there is no deadline — to `outcome_type='abandoned'` (a slippage-scored TACO), after the FR/court/Congress resolvers have first opportunity to detect a facilitation/reversal. The slowly-classified historical backfill (already past-window) finalizes immediately instead of parking in monitoring. Live deadline pressure remains scoped to current threats so finalized history does not distort `index.live_value`; the settled composite is unaffected (historical events contribute via resolution at their own dates). (2) **Calibration catalogue integrity:** the published snapshot now always includes the hand-curated calibration/seed events regardless of the 200-row event cap (they carry the oldest threat dates and were being trimmed from the dashboard), and **migration v24** makes `calibration_locked` consistent across both seed loaders by re-locking all resolved calibration seeds. No event data was lost — this restored the seeds to the published index and froze the catalogue correctly.

**v1.2.10 (June 2026)** — Live deadline pressure for pending events. Previously a pre-announced `stated_deadline` was stored and displayed but exerted no force on the index: only `resolved` events were scored, so a `monitoring`/`confirmed` threat sitting past its deadline never moved. The pipeline now runs `score_pending_pressure()` every cycle (`db/pipeline.py`), recomputing a provisional `live_pressure_score` (0–100) for overdue, unresolved events from the live timeline-slippage curve (`20·ln(1+days_past)`, severity-weighted) — so an unmet deadline visibly builds pressure over time, between resolving documents. The settled index value (`index.value`, resolved-event EWMA) is **unchanged**; a new `index.live_value` folds overdue pressures in as additional samples so the headline can move in real time, and a top-level `live_pressure` snapshot block summarizes overdue events. Migration v23 adds `live_pressure_score`, `days_past_deadline`, `live_pressure_updated_at` to `taco_events`. Dashboard surfaces an "N days past deadline" badge, a per-event live-pressure readout, and an Overview Live Pressure strip. See §5.4.

**v1.2.9 (June 2026)** — USTR Section 301 notice-window backfill fix. The Federal Register `--notices` ingest now derives its incremental `--since-db` window from the most recent **NOTICE** publication date rather than the global maximum across all document types. Because NOTICE is a doc class first ingested in v1.2.7, anchoring to the global max (typically a more recent executive order or proclamation) would have skipped every earlier USTR notice below that floor; the notice window now backfills from the 2025-01-20 cold-start on first run and stays incremental thereafter. This makes the pipeline reliably catch, FR-link, and score the June 2026 USTR Section 301 actions — the proposed 25% tariff on Brazil (digital trade / illegal deforestation; comment 2026-07-01, hearing 2026-07-06) and the proposed 10%/12.5% tariffs on ~60 economies following a forced-labor investigation (hearing 2026-07-07) — as auto-detected `ustr_notice` events with full provenance, rather than hand-seeding them.

**v1.2.8 (June 2026)** — Calibration-event catalogue. The hand-curated calibration benchmarks (TACO-2018-0001/0002, TACO-2019-0002, TACO-2025-0001, TACO-2026-9001) are pre-scored, source-of-truth records, yet every 6-hour cycle the pipeline re-touched them — `market_data` re-fetched yfinance closes, `usitc_gap_analysis` overwrote their hand-coded `applied_rate_final` with live USITC data, the scoring engine re-computed `composite_score`, `price_impact_updater` overwrote the Cavallo hand-coded impacts, and FR matching / `auto_resolve_from_fr_docs` spent Claude calls on them. The per-run re-seed then re-asserted the hand-coded values the same run had just overwritten. Migration v20 adds `taco_events.calibration_locked`; `db/seed_data_v2.py` sets it `TRUE` for resolved seed events (a still-`monitoring` benchmark such as TACO-2026-9002 stays unlocked until it resolves, then locks); eight `AND NOT calibration_locked` guards (market data ×2, USITC gap, scoring, economic-impact, price-impact, FR-match, auto-resolve) make those steps skip locked events. The locked rows are an immutable catalogue: their calibration values can no longer drift, and the redundant per-cycle recompute + ~1,460 wasted Claude calls/yr are eliminated. Live (non-calibration) events are unaffected — they continue to receive live market/USITC/BLS updates every cycle.

**v1.2.7 (June 2026)** — Section 301 USTR ingestion gap closed + ingestion-efficiency hardening for commercial operation.

- **Section 301 coverage.** v1.2.5 generalized classification to presidential *proclamations*, but Section 301 actions are not issued as proclamations — the USTR initiates Section 301 investigations and publishes proposed-tariff actions as **agency `NOTICE` documents in the Federal Register**, not as presidential documents. They were therefore fetched by neither the executive-order nor the proclamation query and were structurally invisible to the index (e.g. the June 2026 proposed 25% Brazil tariff and the forced-labor-probe proposed tariffs on most trading partners). The Federal Register ingest now supports `--notices`, pulling USTR `NOTICE` documents (USTR is low-volume/high-signal, so all are ingested and the classifier decides relevance); `classify_presidential_documents()` classifies them with `threat_source='ustr_notice'`. Migration v19 adds `ustr_notice` to the `source_type` enum. The classifier prompt now recognizes formal agency trade actions (Section 301 investigations / proposed tariffs) as trackable events.
- **Redundant-pull reduction.** The BLS ingest re-fetched its full 2017→current window (~1,500 rows) on every 6-hour cycle. A same-day staleness guard now skips the default-window fetch if BLS rows were already stored today (`--force` and explicit `--years` ranges override). No breadth is lost — BLS data is monthly, so the maximum freshness penalty is sub-day. This mirrors the existing USITC DataWeb staleness check.
- **Duplicate-event hardening.** `event_sources` had no uniqueness guard; migration v19 deduplicates existing rows and adds `UNIQUE (event_id, statement_id, role)` so repeated classification cycles cannot stack duplicate event-source link rows.

**v1.2.6 (June 2026)** — Consumer dashboard now reads live data from the API. The Vite SPA fetches `GET /api/v1/snapshot` (DB-backed, identical payload to `data.json` via the shared `export()`) on load, falling back to the static `data.json` then seed constants. This decouples published-data freshness from Vercel deployments — new events and scores appear without a redeploy, and a stalled deploy no longer freezes the public index. The committed `data.json` remains the offline/static fallback.

**v1.2.5 (June 2026)** — Categorical Federal Register ingestion fix for presidential **proclamations**. Section 232/301 and reciprocal tariff actions are issued as proclamations, not executive orders; prior to v1.2.5 they were fetched into `fr_documents` but never classified into events (`classify_executive_orders()` queried `presidential_document_type = 'executive_order'` only). The classifier (`db/pipeline.py`) now handles executive orders, proclamations, memoranda, and determinations and sets `threat_source` per document type; migration v17 adds `proclamation`/`memorandum`/`determination` to the `source_type` enum. §7 calibration table expanded with two 2026 Section 232 metals events: TACO-2026-9001 (Proclamation 11021 — 50% steel/aluminum/copper, partial reversal, score 24; FR 2026-06960 / 91 FR 18201) and TACO-2026-9002 (June 2026 amending proclamation, monitoring). Copper (HS chapter 74) added to the `hs10_coicop_map` crosswalk so copper tariffs are measured in `event_sector_impacts` alongside steel (72) and aluminum (76).

**v1.2.4 (May 2026)** — Removed Harvard Dataverse replication dataset references (academic-only license, not for commercial use). Two BEA PCE FRED series updated to current IDs: `DGDSRG3M086SBEA` (PCE Goods, SA) and `DDURRG3M086SBEA` (PCE Durable Goods, SA), replacing retired NSA series. Scoring moved to post-data-ingest position in pipeline (after FRED/BLS/price-impact) so events are scored with the freshest economic data and FR auto-resolved events are scored in the same run.

**v1.2.3 (May 2026)** — PACER PCL REST API integrated as automated court fallback. `fetch_pacer_pcl_cases()` added to `src/ingest/court_decisions.py`; triggers when CourtListener raises exceptions on all courts (not just returns empty) or via `--pacer-fallback` flag. Auth: `POST pacer.login.uscourts.gov/services/cso-auth` (XML response, not JSON) → `nextGenCSO` token; case search via PCL `courtId=["citdc","cafc"]`. `pyotp` added to requirements for TOTP-enrolled accounts. `PACER_USERNAME`/`PACER_PASSWORD` wired into pipeline workflow secrets. §3.2 automated ingest table updated. `src/ingest/pacer.py` standalone script available for manual spot-checks.

**v1.2.2 (May 2026)** — New economic verification sources and BEA PCE pass-through series. Two BEA PCE series added to FRED ingest for consumer price pass-through cross-validation against Yale Budget Lab Tariff Tracker. Budget Lab (Yale) and PACER added to §3.2 verification sources. Migration v15 registers both in `research_sources` table. TACO-2025-0006 (IEEPA) noted: SCOTUS *Learning Resources, Inc. v. Trump* (Feb 20, 2026) vacated all IEEPA tariffs; ~$165B in duties subject to refund — pipeline auto-resolution via `auto_resolve_from_fr_docs()` expected to have updated outcome.

**v1.2.1 (May 2026)** — Live §6.2–§6.6 measurement pipeline. BLS CPI component series (`CUSR0000SAA`, `CUSR0000SAH3`, `CUSR0000SAF11`, `CUSR0000SAM1`, `CUSR0000SAS`, `CUSR0000SAC`) added as four-group price-impact proxies; FRED pass-through/incidence series (`PPIACO`, `PPIFCG`, `DTWEXBGS`, `DGS10`, `DGS2`); sector ETFs (XLP, XLI, XLY, EEM) now populated in `market_impact`. New pipeline module `src/ingest/price_impact_updater.py` writes live trend-deviation estimates to `price_impact` every cycle. BLS default lookback extended to **2017** so first-term calibration events (TACO-2018-*) receive five months of pre-threat CPI for trend fitting. Dashboard UX: economy-wide FRED/BLS/USITC on **Live Data** tab; per-threat measurement (`price_impact[]`, statutory/applied/gap) on **Events** tab when expanding a trade event. §3.2–§3.4 and §6.2–§6.6 updated to document production data sources vs. Cavallo retail microdata gold standard. Sector-level impact tables added: `hs10_coicop_map` (HS-2 → COICOP-3 crosswalk seeded from Cavallo 2019) and `event_sector_impacts` exposed via `GET /api/v1/events/{id}/sector-impact` (Standard tier). Event auto-resolution from FR docs (`auto_resolve_from_fr_docs()`; confidence ≥ 0.70). Public API key self-service: `POST /api/v1/keys/request` and `api_key_requests` table (migration v13); admin review via `GET /api/v1/admin/key-requests`. Admin endpoints secured via `require_admin()` dependency. Confidence calibration CLI (`db/calibration_audit.py`) added for threshold precision/recall monitoring.

**v1.2.0 (May 2026)** — Added `statutory_authority` classification field (IEEPA · Section_301 · Section_232 · Section_122 · Executive_Order · National_Emergencies_Act · HEA · USMCA · None). Expanded §3.2 verification layer with Atlantic Council Tariff Tracker and PIIE Trade War Monitor. Expanded §7 calibration table with first-term trade tariff events (TACO-2018-0001 Section 232 Steel/Aluminum, TACO-2018-0002 Section 301 / Phase One, TACO-2019-0002 Canada/Mexico USMCA Steel Removal) and the 2025 IEEPA 10% Baseline + Section 122 reference case. Added "Authority" column to the calibration table. Reversal-magnitude methodology (§5.1) updated to document the live-USITC-gap hybrid for trade events. Executive orders documented as a primary classification source alongside Truth Social (§3.1). §8 API specification expanded to cover all production endpoints, including the free `/health` and `/api/v1/snapshot` endpoints.

**v1.1.0 (May 2026)** — Integrated Cavallo, Llamas & Vazquez (2026) economic impact methodology (§6). Formalised six-dimension behavioral scoring framework (§5.1) with weights 30/20/15/15/10/10. Added rolling 30-day exponentially-weighted index calculation with a ~23-day half-life (§5.3). Added REST API specification (§8).

**v1.0.0 (May 2026)** — Initial public release. Four-criterion event definition (specificity, actionability, measurability, materiality); four-domain taxonomy (Trade, Foreign Policy, Domestic, Rhetorical); five-stage event lifecycle; AI-assisted detection with human-in-the-loop review.

---

## Table of Contents

1. [Executive Summary](#1-executive-summary)
2. [Core Definitions](#2-core-definitions)
3. [Data Sources & Collection](#3-data-sources--collection)
4. [Event Classification](#4-event-classification)
5. [Scoring Framework](#5-scoring-framework)
6. [Economic Impact Measurement](#6-economic-impact-measurement)
7. [Calibration & Validation](#7-calibration--validation)
8. [API Specification](#8-api-specification)
9. [Limitations & Methodological Considerations](#9-limitations--methodological-considerations)
10. [References](#10-references)

---

## 1. Executive Summary

The PEPIndex (Aka TACO Index) is a quantitative behavioral index that tracks the lifecycle of presidential threats, ultimatums, and policy announcements, measuring the gap between initial rhetoric and actual follow-through. Each event is classified, monitored through its lifecycle, and scored across six weighted dimensions to produce a composite score from 0 (full implementation) to 100 (total reversal).

The index serves three audiences: traders seeking to exploit the predictable market pattern of overreaction-to-threats followed by relief-on-reversal; policy researchers studying executive signaling behavior; and journalists seeking a rigorous, transparent framework for reporting on credibility gaps in presidential communication.

The index integrates economic impact measurement based on the methodology of Cavallo, Llamas & Vazquez (2026), *Tracking the Short-Run Price Impact of U.S. Tariffs*. This allows the PEPIndex to quantify not only whether a threat was abandoned, but what the threat and its reversal cost in measurable economic terms: consumer price impact, CPI contribution, tariff incidence, and distributional burden.

| Metric | Value |
|---|---|
| Scoring Range | 0–100 |
| Weighted Dimensions | 6 |
| Tracked Domains | 4 |
| Pipeline Cadence | Every 6 hours |

---

## 2. Core Definitions

### 2.1 What Constitutes a PEPI Event

A PEPI event is a public statement or formal action by the President of the United States that satisfies all four of the following criteria:

1. **Specificity.** The statement identifies a concrete action, target, or consequence (not vague rhetoric or general opinion).
2. **Actionability.** The threatened action falls within presidential authority or influence to execute.
3. **Measurability.** The outcome can be independently verified: either the threatened action was taken or it was not.
4. **Materiality.** The threat has economic, diplomatic, or policy consequences significant enough to warrant tracking.

### 2.2 Event Domains

Each PEPI event is classified into one of four domains:

| Domain | Scope | Examples |
|---|---|---|
| **Trade & Tariffs** | Import tariffs, trade deals, sanctions on goods, export controls | Liberation Day tariffs, China 145%, pharma tariffs, USMCA renegotiation threats |
| **Foreign Policy** | Military force threats, diplomatic ultimatums, territorial claims, alliance threats | North Korea fire and fury, Greenland annexation, Russia/Ukraine deadlines, NATO withdrawal |
| **Domestic Policy** | Executive orders, government shutdowns, personnel actions, regulatory threats | Government shutdown threats, firing threats, legislative ultimatums |
| **Rhetorical** | Lawsuits, boycotts, social media retaliation, interpersonal threats | Defamation lawsuit threats, media boycott calls, retribution threats |

### 2.3 Event Lifecycle

Every PEPI event progresses through a defined lifecycle with five stages. The event may resolve at any stage:

| Stage | Definition | Data Captured | Status |
|---|---|---|---|
| 1. Threat | Initial public statement identifying target, consequence, and (optionally) deadline | Date, source, raw text, severity score, entities, deadline | Detected / Confirmed |
| 2. Escalation | Intensified rhetoric, additional threats, or raised stakes | Timeline entries with severity deltas | Monitoring |
| 3. Deadline | Stated or implicit deadline passes | Deadline date, whether met or extended | Monitoring |
| 4. Action | Implementation attempt, partial action, delay, or inaction | Outcome type, actual consequence, scope changes | Monitoring |
| 5. Outcome | Final resolution: implemented, reversed, abandoned, or reframed | All scoring dimensions populated, composite score calculated | Resolved |

---

## 3. Data Sources & Collection

### 3.1 Primary Sources (Where Threats Originate)

The PEPIndex ingests presidential statements and formal actions from multiple sources to ensure comprehensive coverage of both written rhetoric and codified executive actions. Two channels can independently originate a PEPI event:

**(a) Presidential statements** — public utterances that meet the four event criteria:

| Source | Format | Refresh | Coverage |
|---|---|---|---|
| CNN Truth Social Archive | JSON / CSV / Parquet | 5-min cache | All Truth Social posts with engagement metrics |
| American Presidency Project (UCSB) | HTML (scraped) | Daily | 14,700+ statements, executive orders, press remarks |
| Rev Transcript Library | Searchable text | Near real-time | Press conferences, rallies, gaggles (verbal threats) |
| Factba.se / FiscalNote | Searchable archive | Daily | Speeches, tweets, video transcripts back to 2009 |

**(b) Formal executive and agency actions** — codified Federal Register documents are treated as a second classification channel (introduced in v1.2.0; generalized in v1.2.5 and v1.2.7). The `classify_presidential_documents()` step (historical alias `classify_executive_orders()`) runs after Truth Social classification each cycle. It queries `fr_documents` for executive orders, proclamations, memoranda, determinations, **and tariff-relevant USTR `NOTICE` documents** not yet linked to any event, classifies each against the same four-criterion test, and writes qualifying events with `match_role = initial` and `threat_source` set per document type (`executive_order`, `proclamation`, `memorandum`, `determination`, or `ustr_notice`):

| Source | Format | Refresh | Coverage |
|---|---|---|---|
| Federal Register API | JSON API | Daily (incremental) | Executive orders, proclamations, presidential memoranda, presidential determinations, tariff schedules, **USTR Section 301 notices (investigation initiations + proposed tariff actions)** |

Within the schema, the originating channel is captured in `taco_events.threat_source` (enum values `truth_social`, `executive_order`, `proclamation`, `ustr_notice`, etc.). Section 301 tariff actions matter because they are issued by the USTR as agency notices — not presidential documents — and so require this dedicated `NOTICE` ingest path to be captured at all.

### 3.2 Outcome Verification (What Actually Happened)

Outcome verification combines automated ingest from official government data with manual cross-reference against well-regarded independent trackers:

**(a) Automated ingest sources** — feed the pipeline directly and populate `taco_events`, `tariff_rate_snapshots`, `usitc_applied_rates`, `regulatory_outcomes`, `fr_documents`, and `event_timeline`:

| Source | Use Case |
|---|---|
| USITC Harmonized Tariff Schedule | Statutory tariff rates at HS-10 level, including Chapter 99 temporary modifications |
| USITC DataWeb | Live applied tariff rates (duties collected ÷ customs value) at HS-2 × country granularity, refreshed every pipeline cycle |
| U.S. Census Bureau Import Data | Monthly applied-rate backstop for periods predating DataWeb coverage |
| Federal Register API | Executive orders, proclamations, agency rulemaking — implementation evidence |
| eCFR Versioner API | Point-in-time CFR snapshots; regulatory diffs verify codification of threatened actions |
| CourtListener (CIT, CAFC) | Court rulings affecting tariff validity — injunctions, stays, vacaturs (Court of International Trade and Federal Circuit). Primary automated court ingest. |
| PACER PCL (Case Locator) | Automated fallback: `fetch_pacer_pcl_cases()` queries `pcl.uscourts.gov/pcl-public-api/rest/cases/find` for `citdc`/`cafc` cases when CourtListener fails. Auth via `pacer.login.uscourts.gov/services/cso-auth` (XML). Cost: $0.10/page, waived under $30/quarter. |
| FRED | CPI (`CPIAUCSL`), core CPI (`CPILFESL`), S&P 500 (`SP500`), VIX (`VIXCLS`), goods trade balance (`BOPGSTB`), imports (`IMPGS`); pass-through/incidence: PPI all commodities (`PPIACO`), PPI final demand consumer goods (`PPIFCG`), nominal broad dollar index (`DTWEXBGS`), 10Y/2Y Treasury (`DGS10`, `DGS2`); BEA PCE Goods Price Index (`DGDSRG3M086SBEA`), BEA PCE Durable Goods Price Index (`DDURRG3M086SBEA`) — consumer pass-through signal from tariff-exposed goods; crosscheck vs. Budget Lab Tariff Tracker (same BEA source) |
| BLS Public Data API v2 | Import Price Index (`EIUIR`), PPI final demand (`WPUFD4`), manufacturing/retail employment (`CEU30`, `CEU42`); §6.2 four-group CPI proxies: Apparel (`CUSR0000SAA`), Household Furnishings (`CUSR0000SAH3`), Food at Home (`CUSR0000SAF11`), Medical Commodities (`CUSR0000SAM1`), Services (`CUSR0000SAS`), Core Commodities (`CUSR0000SAC`) |
| Yahoo Finance (yfinance) | S&P 500 (SPY), VIX, sector ETFs (XLP, XLI, XLY, EEM) around event dates — daily §6.2/§6.3/§6.5 market proxies |

**(b) Verification references** — independent trackers with no automated ingest. Used for date cross-checks, statutory-authority confirmation, and calibration audits:

| Source | Use Case |
|---|---|
| Atlantic Council Tariff Tracker | Second-term (2025+) tariff event dates, statutory authorities invoked, court ruling outcomes. Visualization only; no API or downloadable dataset. |
| PIIE Trade War Monitor (Chad Bown et al., Peterson Institute) | First-term (2018–2021) and second-term Section 232/301 dates, Phase One deal terms, country-specific exemption timelines. Source authority for the TACO-2018-* calibration events. |
| ECFR Force Threat Database (Shapiro 2025) | Academic tracking of military/diplomatic threats (22 tracked, 2 followed through). Calibrates Foreign Policy domain. |
| USTR Press Releases | Statutory rate confirmation, tariff exemption announcements. |
| **Yale Budget Lab Tariff Tracker** (April 2026) | Independent monthly cross-check of effective tariff rate, customs revenue, and consumer pass-through. Uses same USITC HTS + Census import-weight sources as our DataWeb ingest — deviation between their ETR and ours flags data quality issues. April 2026 report: import-weighted ETR 11.1%; PCE core goods pass-through 46–86%; PCE durable goods 51–115%. Also documents SCOTUS *Learning Resources, Inc. v. Trump* (Feb 20, 2026) IEEPA vacatur. |
| **PACER** (US Courts) | Fallback for CIT and CAFC trade cases not yet mirrored in CourtListener RECAP. Now also **automated** via PCL REST API (`src/ingest/pacer.py`, wired into `court_decisions.py`). Manual spot-checks: `python -m src.ingest.pacer --search --date-from YYYY-MM-DD`. |

Note on schema: court rulings are not stored as `raw_statements` (the `source_type` enum is reserved for *presidential* statements). Instead, CourtListener-sourced rulings are recorded as `event_timeline` entries with `entry_type = 'reversal'` and a `source_url` citing the court decision, then propagated into the parent `taco_events.outcome_type` field. This keeps the four-domain event taxonomy clean while still preserving full court-ruling provenance.

### 3.3 Economic Impact Data

For trade-domain events, the PEPIndex incorporates price impact measurement following the methodology established in Cavallo, Llamas & Vazquez (2026). Their gold-standard approach uses daily retail microdata from five major U.S. retailers, covering over 80% of the commodities component of the CPI. Products are classified to HS-10 tariff codes using AI-based hierarchical classification, and country of origin is identified through UPC matching and generative AI search.

**Production implementation (v1.2.1).** Because retail scanner microdata is proprietary, PEPI uses a layered public-data approximation refreshed every pipeline cycle:

| Layer | Module | Cadence | Output |
|---|---|---|---|
| Applied tariff gap | `src/ingest/usitc_dataweb.py` + `usitc_gap_analysis.py` | Every 6h | `applied_rate_final`, `statutory_applied_gap` on `taco_events` |
| Four-group price indices | `src/ingest.price_impact_updater` | Every 6h (after BLS ingest) | `price_impact` table — imported/domestic affected & unaffected indices, trend deviations, rolling pass-through, CPI contribution |
| Sector ETF event study | `src/ingest.market_data` | Every 6h | `market_impact` — SPY, VIX, XLP, XLI, XLY, EEM daily closes around threat/outcome dates |
| Macro pass-through/incidence | `src/ingest.fred` + `src/ingest.bls` | Daily / monthly | `economic_indicators` — PPI, dollar index, Treasury yields, CPI component series; BEA PCE Goods + Durable Goods price indexes (`DGDSRG3M086SBEA`, `DDURRG3M086SBEA`) crosscheck vs. Yale Budget Lab |
| Calibration constants | `db.seed_data_v2` | Idempotent seed | Cavallo et al. benchmark values (24% pass-through, 0.76pp CPI, 43% consumer incidence) sourced from published AER: Insights paper |

BLS and FRED indicators are persisted to `economic_indicators`. Live price-impact rows are written to `price_impact` and exposed via `GET /api/v1/events/{id}/price-impact` (Premium) and the `/api/v1/snapshot` / `data.json` export.

**BLS lookback (v1.2.1).** `src/ingest/bls.py` defaults to **2017–current** so events as early as March 2018 (Section 232) have sufficient pre-threat monthly observations for log-linear trend fitting in `price_impact_updater`. Household furnishings use `CUSR0000SAH3` (monthly SA); `CUSR0000SEHP` is quarterly-only in BLS API v2 and is not used.

### 3.4 Consumer Dashboard Presentation

The public dashboard at [pepi.apiautomations.com](https://pepi.apiautomations.com) separates two measurement layers to avoid conflating economy-wide macro data with per-threat attribution:

| Layer | Dashboard tab | Data in `data.json` | Interpretation |
|---|---|---|---|
| **Macro backdrop** | Live Data | `economic_indicators[]`, `tariff_rates[]` | Economy-wide FRED/BLS series and country-level USITC applied rates — not tied to a single threat |
| **Per-threat measurement** | Events (expand a trade event) | `price_impact[]` joined by `event_id`; statutory/applied/gap on `events[]` | Cavallo four-group proxy, pass-through, and CPI contribution for that specific threat |

The Live Data tab shows a collapsed top-8 summary of trade events with live measurement (sorted by imported price deviation) and directs users to the Events tab for threat-specific detail. Monitoring events with no outcome yet display **"Outcome pending — under monitoring"** instead of an empty result field.

---

## 4. Event Classification

### 4.1 AI-Powered Detection

Raw presidential statements and unlinked executive orders are classified using the Anthropic Claude API with a structured classification prompt. Each candidate is evaluated against the four PEPI event criteria (specificity, actionability, measurability, materiality) and assigned a confidence score. Candidates with confidence above 0.7 are flagged for human review.

The classifier extracts structured metadata for each potential event: domain, sub-type, target entities, severity score (0–100), stated deadline, and stated consequence. This follows the same batch API pattern used in large-scale content classification pipelines, enabling cost-efficient processing of historical archives.

**Statutory authority classification (v1.2.0).** Each trade or domestic-policy event is tagged with the `statutory_authority` field capturing the legal authority invoked:

- `IEEPA` — International Emergency Economic Powers Act
- `Section_301` — Trade Act of 1974, §301
- `Section_232` — Trade Expansion Act of 1962, §232 (national security)
- `Section_122` — Trade Act of 1974, §122 (balance-of-payments)
- `Executive_Order` — generic executive order without enumerated authority
- `National_Emergencies_Act` — 50 U.S.C. §§1601–1651
- `HEA` — Higher Education Act (for education-domain enforcement)
- `USMCA` — United States–Mexico–Canada Agreement (for trade-deal renegotiation)
- `None` — for rhetorical and foreign-policy events without enumerated authority

This field is populated from Federal Register document headers, USTR press releases, court dockets, and Atlantic Council Tariff Tracker cross-reference. For seed historical events, the authority field is manually curated; automated extraction from Truth Social posts is not yet implemented.

### 4.2 Human Review

All AI-classified events pass through a human review queue before being confirmed. Reviewers verify the classification, correct any errors, and add analyst notes. This human-in-the-loop step is essential for maintaining methodological rigor and catching edge cases where AI classification may be ambiguous.

Inter-rater reliability is tested periodically: multiple analysts score the same events blind, and Cohen's kappa is calculated to ensure classification consistency exceeds 0.80.

### 4.3 Severity Scoring

Initial threat severity is scored on a 0–100 scale using NLP analysis of language intensity:

| Range | Severity Level | Examples |
|---|---|---|
| 90–100 | Existential threat (military action, total embargo, firing) | Fire and fury, wipe Afghanistan off the earth, 145% tariff |
| 70–89 | Major policy threat (high tariffs, sanctions, executive orders) | 50% tariff on Brazil, extreme consequences on Russia |
| 50–69 | Moderate threat (investigations, regulatory action, deadline) | Government shutdown, regulatory review, 25% tariff deadline |
| 30–49 | Minor threat (social media retaliation, boycott calls) | Boycott calls, social media attacks, vague legal threats |
| 0–29 | Not significant (opinion, complaint, boast) | Does not qualify as a PEPI event |

---

## 5. Scoring Framework

The composite PEPI score measures the degree to which the final outcome deviated from the original threat. A score of 0 indicates full follow-through; a score of 100 indicates total reversal. The score is computed as a weighted sum of six dimensions:

### 5.1 Scoring Dimensions

| Weight | Dimension | Measurement |
|---|---|---|
| 30% | **Reversal Magnitude** | How far the final action deviated from the original threat. See §5.1.1 below. |
| 20% | **Timeline Slippage** | Duration of deadline extensions relative to stated timeline. Scored on a logarithmic scale: 7-day delay ≈ 40, 30-day delay ≈ 70, 90+ days ≈ 90+. No stated deadline receives a 30-point vagueness penalty. |
| 15% | **Rhetorical Dilution** | NLP-measured shift in language intensity between initial threat and subsequent statements about the same topic. Captures the linguistic softening that typically precedes a reversal. |
| 15% | **Scope Reduction** | Percentage of originally targeted entities, countries, or product categories exempted from the final action. Directly informed by the statutory-vs-applied tariff rate gap from Cavallo et al. (2026). |
| 10% | **Framing Shift** | Whether the outcome was reframed as a victory despite being a reversal. Measured as the delta between the administration's characterization and independent analysis. |
| 10% | **Repetition Pattern** | Whether this exact threat has been made and reversed before. Each prior reversal adds 20 points, capped at 100. Captures declining credibility. |

#### 5.1.1 Reversal Magnitude — hybrid trade / categorical formula

For **trade-domain events with live USITC applied-rate data**, Reversal Magnitude is calculated directly from the statutory-vs-applied rate gap:

```
reversal_magnitude_trade = (statutory_rate_announced − applied_rate_final) / statutory_rate_announced × 100
```

Worked example: a 145% announced rate with a 28.0% applied rate yields a 117-percentage-point gap, which is 80.7% of the announced rate, producing a Reversal Magnitude score of **80.7**.

Applied rates come from `usitc_applied_rates` (live USITC DataWeb ingest) via `src/ingest/usitc_gap_analysis.py`, which runs after the Cavallo et al. seed-data step so live rates always override hand-coded estimates. The formula is bounded to [0, 100] (negative gaps — applied > statutory — are floored at 0).

For **non-trade events**, or trade events lacking USITC data, Reversal Magnitude falls back to a 7-point categorical scale mapped to the `outcome_type` field:

| `outcome_type` | Reversal Magnitude anchor |
|---|---|
| `full_implementation` | 0 |
| `partial_implementation` | 25 |
| `delayed` | 45 |
| `partial_reversal` | 65 |
| `reframed` | 80 |
| `full_reversal` | 95 |
| `abandoned` | 100 |

This hybrid keeps trade-domain scoring grounded in measurable economic reality while preserving comparability with non-trade domains where rate data does not apply.

### 5.2 Composite Formula

The composite PEPI score is calculated as:

```
PEPI = 0.30 · Reversal + 0.20 · Slippage + 0.15 · Dilution + 0.15 · Scope + 0.10 · Framing + 0.10 · Repetition
```

### 5.3 Rolling Index Calculation

The rolling PEPIndex is the 30-day exponentially-weighted moving average of all resolved event scores within the window. More recent events are weighted more heavily using an exponential decay with a half-life of approximately 23 days:

```
Index(t) = Σ [ score(i) · exp(−0.03 · days_ago(i)) ] / Σ [ exp(−0.03 · days_ago(i)) ]
```

Domain sub-indices (Trade, Foreign Policy, Domestic, Rhetorical) are calculated identically but filtered to events within each domain.

### 5.4 Live Deadline Pressure (pending events) — v1.2.10

The rolling index in §5.3 is built from **resolved** events: it reacts to *outcomes*, not to *deadlines*. A threat with a pre-announced deadline that passes with no follow-through therefore exerted no force until a resolving document arrived. v1.2.10 closes that gap with a provisional, real-time pressure signal for unresolved events.

For every `monitoring`/`confirmed` event that is overdue against its **effective deadline** and has no outcome, each pipeline cycle recomputes a **live pressure score** from the current date. The effective deadline is the event's `stated_deadline`, or — for a threat that names no explicit deadline — `threat_date + 30 days` (a grace window, **v1.2.13**). Without this grace, a past-dated threat with no stated deadline exerted **no** live force at all until it auto-finalized at `threat_date + 270 days`; the grace lets such threats build pressure 30 days after they are made, which is the common case (most presidential economic threats give no hard date).

```
effective_deadline = stated_deadline,  else  threat_date + 30 days        (v1.2.13)
days_past          = CURRENT_DATE − effective_deadline                     (only when > 0)
slippage           = min(100, 20 · ln(1 + days_past))                      (live timeline-slippage curve, §5.1)
live_pressure      = slippage · (0.6 + 0.4 · severity/100)                 (severity-weighted, clamped 0–100)
```

Because it is driven by `CURRENT_DATE` rather than by new documents, the score rises on a log curve as a deadline goes unmet (≈12 at one day, ≈37 at a week, ≈61 at a month, 80+ past three months for a high-severity threat), and is cleared the moment the event resolves.

Live pressure is bounded to the **immediate window** and aligned exactly with finalization: a deadline-bearing event runs from the deadline up to `stated_deadline + 90 days`, and a deadline-less event from its 30-day grace up to `threat_date + 270 days`, the precise points at which `finalize_slipped_events()` resolves the event to `abandoned` and the provisional pressure is replaced by a settled slippage-scored TACO (§4.x). Because the pressure window and finalization share the same bound, they are exact complements — there is no interval in which an overdue, unfinalized threat is missing from both `live_value` and `value` — and historical backfill is still excluded, its long-past window finalizing rather than exerting live pressure (v1.2.12/v1.2.13).

**Index integration (dual value).** The settled index (`index.value`) is left untouched — it remains the calibrated resolved-event EWMA, so the historical series and its interpretation do not change. A parallel **`index.live_value`** pools the overdue pressures in with the trailing resolved sample mean as additional observations:

```
live_value = (value · N + Σ live_pressure_i) / (N + n_overdue),   N = max(resolved_30d, 1)
```

With no overdue events, `live_value == value`. As more (or more overdue) deadlines accumulate, `live_value` is pulled toward the mean live pressure, so the headline moves in real time while the settled series stays clean. The snapshot exposes a `live_pressure` block (overdue count, average/max pressure, lift, top contributors) and per-event `live_pressure_score` / `days_past_deadline` fields; the dashboard renders an overdue badge, a per-event pressure readout, and an Overview Live Pressure strip. `live_pressure_score` is explicitly **not** a settled TACO score and never enters `index.value` or the calibration table.

---

## 6. Economic Impact Measurement

For trade-domain events, the PEPIndex goes beyond behavioral scoring to measure the real-world economic consequences of threats and reversals. This section describes the methodology, which is adapted from Cavallo, Llamas & Vazquez (2026), *Tracking the Short-Run Price Impact of U.S. Tariffs*, a peer-reviewed study using daily retail microdata from five major U.S. retailers covering 359,104 products.

### 6.1 Statutory vs. Applied Tariff Rate Gap

The single most important economic metric for trade-domain PEPI events is the gap between the statutory (announced) tariff rate and the applied (actually collected) rate. This gap exists precisely because of exemptions, delays, carve-outs, and reversals — it is the PEPI score expressed in dollar terms.

Statutory rates are sourced from the USITC Harmonized Tariff Schedule, including Chapter 99 temporary modifications. Applied rates are calculated from USITC DataWeb (live, every pipeline cycle) and Census Bureau import data (monthly backstop) as duties collected divided by customs value at the HS-10 / country level (Cavallo et al. Equation 4). In their sample, statutory rates averaged 27.7% while applied rates averaged 23.8% — a persistent gap reflecting incomplete implementation.

### 6.2 Price Impact Methodology

Price impact is measured using a four-group comparison framework:

1. **Imported Affected.** Products directly subject to tariffs (treatment group).
2. **Imported Unaffected.** Exempt imports such as pharmaceuticals and select electronics (control group).
3. **Domestic Affected.** Domestic goods competing directly with tariffed imports, capturing strategic pricing complementarities.
4. **Domestic Unaffected.** Domestic goods not competing with tariffed imports (second control group).

Daily price indices are constructed using chained matched-model geometric means (Cavallo 2013). Pre-tariff trends are estimated via log-linear regression over the five months prior to the tariff announcement. Price impact is measured as the deviation from this counterfactual trend in percentage points.

**Production proxy (v1.2.1).** `src/ingest/price_impact_updater.py` maps BLS CPI component series to the four groups:

| Cavallo group | BLS series (production proxy) | Rationale |
|---|---|---|
| Imported Affected | `CUSR0000SAA` (Apparel), `CUSR0000SAH3` (Household Furnishings) | High import share; furnishings is Cavallo's #1 impact sector (9.24pp). `CUSR0000SEHP` is quarterly-only in BLS v2 — SAH3 is the monthly SA proxy |
| Domestic Affected | `CUSR0000SAF11` (Food at Home) | Domestic spillover / strategic complementarities (§3.2) |
| Imported Unaffected | `CUSR0000SAM1` (Medical Care Commodities) | Pharma largely IEEPA/§232 exempt — clean control |
| Domestic Unaffected | `CUSR0000SAS` (Services) | Non-tradable; minimal direct tariff exposure |

Monthly BLS observations are normalized to 1.0 at each event's `threat_date`, trend-fit over the pre-threat window, and linearly interpolated to daily resolution for the `price_impact` table. **Daily market proxies:** XLY (Consumer Discretionary) ≈ imported-affected pressure; XLP (Consumer Staples) ≈ domestic-affected; XLI (Industrials) ≈ firm margin compression; EEM (Emerging Markets) ≈ origin-country supplier incidence — populated in `market_impact` via yfinance.

### 6.3 Pass-Through Estimation

Retail tariff pass-through is estimated using a distributed-lag regression with product-level monthly data and COICOP sector fixed effects (Cavallo et al. Equation 2). The critical methodological choice is to use **applied** tariff rates rather than statutory rates, which accounts for exemptions and delays. Using applied rates, the Cavallo et al. study finds cumulative 7-month pass-through of 24%, compared to only 5.5% when using statutory rates.

This distinction directly informs PEPI scoring: the gap between statutory and applied rates measures the portion of the threat that was not followed through, while the pass-through rate measures how much of the remaining applied tariff reached consumers.

**Production signals (v1.2.1).** Live pass-through is approximated as `imported_trend_deviation_pp / applied_rate_final` in `price_impact_updater`. Macro confirmation uses FRED `PPIFCG` vs. `CPIAUCSL` (producer-to-retail gap) and BLS `CUSR0000SAC` vs. `CUSR0000SAS` (tradable goods vs. services divergence). XLY−XLP spread in `market_impact` provides a daily discretionary-vs-staples pass-through asymmetry signal.

### 6.4 CPI Contribution

The contribution to the all-items Consumer Price Index is estimated by weighting sector-level price deviations by official CPI expenditure shares and scaling by the sample's coverage weight (28.5% of all-items CPI for commodities). Cavallo et al. estimate that the 2025 tariffs contributed approximately 0.76 percentage points to all-items CPI, implying that the measured 3.0% annual CPI inflation rate in September 2025 would have been approximately 2.24% without the tariffs.

**Production estimate (v1.2.1).** `price_impact_updater` computes `cpi_contribution_pp` per observation using Cavallo expenditure weights on BLS sector deviations, scaled by the 28.5% sample coverage factor. Headline CPI from FRED `CPIAUCSL` provides the all-items benchmark for dashboard/API cross-check.

**Headline card aggregate.** The Overview "CPI Impact" card (`stats.cpi_impact_pp`) is the cross-event `max` of each trade event's latest *populated* `cpi_contribution_pp`. Because the underlying BLS CPI series is monthly, `cpi_contribution_pp` is only computed on month-start observation dates — every other (daily) row is null — so the aggregate selects each event's most-recent **non-null** value rather than its single latest row. Selecting the latest row directly masks nearly every event behind a null daily value and collapses the headline to ~0.00pp. When no event has any populated value, the card falls back to the calibrated +0.76pp estimate.

### 6.5 Tariff Incidence

Tariff incidence — who bears the cost — is estimated by combining three inputs: (1) near-complete pass-through at the border per Gopinath and Neiman (2026), meaning import prices rise nearly one-for-one with tariffs; (2) an import cost share of approximately 50% at the retail level per Burstein et al. (2003); and (3) the measured retail pass-through rate. After seven months, Cavallo et al. estimate that U.S. consumers bore approximately **43%** of the tariff cost, with the remaining **57%** absorbed by U.S. firms through margin compression.

**Production signals (v1.2.1).** Consumer vs. firm burden is computed from live `applied_rate_final` and rolling `retail_passthrough_pct` via `estimate_tariff_incidence()` in `src/impact/price_tracker.py`. Macro context: FRED `DTWEXBGS` (stronger dollar offsets import price rises, reducing consumer incidence); `DGS10`/`DGS2` spread (firm financing cost during margin compression); XLI underperformance vs. SPY in `market_impact` (industrial margin pressure); EEM underperformance (origin-country supplier absorption).

### 6.6 Distributional Impact

Lower-priced product varieties exhibit faster and larger price increases than higher-priced ones. Cavallo et al. find that prices in the lowest quartile (within product categories) rose by approximately 1.5 percentage points more than those in the highest quartile. This pattern is consistent with thinner margins on cheaper goods and has important distributional implications: low-income households, which disproportionately purchase lower-priced goods, faced a larger share of tariff-induced inflation.

**Production limitation (v1.2.1).** Within-category price-quartile analysis requires proprietary retail scanner microdata (Cavallo's PricingLab dataset) and cannot be replicated from public BLS aggregates alone. PEPI uses `CUSR0000SAF11` (Food at Home — regressive consumption basket) vs. `CUSR0000SAS` (Services — higher-income skew) as a coarse distributional proxy, and retains Cavallo calibration constants (`low_price_quartile_impact`, `high_price_quartile_impact`) on `taco_events` from `seed_data_v2` until scanner data licensing is available.

---

## 7. Calibration & Validation

The PEPIndex is calibrated against historical events with well-documented outcomes. The following table presents seed events with pre-calculated composite scores. The **Authority** column (introduced in v1.2.0) identifies the legal authority invoked for trade-domain events.

| Event | Domain | Authority | Score | Outcome | Key Gap |
|---|---|---|---|---|---|
| Section 232 Steel/Aluminum Global (2018) | Trade | Section_232 | 34.0 | Partial reversal | 25% global → ally exemptions (~45% scope) |
| Section 301 China Trade War / Phase One (2018–2020) | Trade | Section_301 | 53.0 | Partial reversal | 25% on $267B → 12.5% avg (Phase One deal) |
| Canada/Mexico Steel USMCA Removal (2018–2019) | Trade | Section_232 | 82.0 | Full reversal | Steel tariffs → zero for CA/MX (USMCA ratification) |
| North Korea: Fire and Fury (2017) | Foreign | None | 95.5 | Full reversal | Military threat → summit |
| Afghanistan: Wipe Off the Earth (2019) | Foreign | None | 97.0 | Full reversal | Destruction → peace deal |
| IEEPA 10% Baseline + Sec 122 (2025) | Trade | IEEPA, Section_122 | 91.0 | Full reversal | Universal surcharge → dual court invalidation |
| Greenland Annexation (2025) | Foreign | None | 90.5 | Full reversal | Acquisition → dropped |
| Russia: Extreme Consequences (2025) | Foreign | None | 88.0 | Reframed | Consequences → summit |
| Liberation Day: 145% on China (2025) | Trade | IEEPA | 82.1 | Partial reversal | 145% → 28.3% applied |
| 14-Country Tariff Deadline (2025) | Trade | IEEPA | 60.5 | Delayed | Full tariffs → frameworks |
| Pharma Tariffs 100% (2025) | Trade | IEEPA, Section_232 | 56.3 | Delayed | 100% → Section 232 |
| Section 232 Steel/Aluminum/Copper 50% — Proclamation 11021 (2026) | Trade | Section_232 | 24.0 | Partial reversal | 50% → tiered 10–15% caps for allies/derivatives |
| Section 232 Metals Amendment (2026) | Trade | Section_232 | — | Pending (monitoring) | 50% regime amended: ally caps 15%, derivatives 10% |

External validation benchmarks include the ECFR analysis finding a 9.1% follow-through rate on military force threats (2 of 22 tracked), and the Cavallo et al. 24% retail pass-through rate for the 2025 tariffs, which is used to validate economic impact calculations for trade-domain events. First-term calibration events (TACO-2018-* series) are sourced from the PIIE Trade War Monitor (Bown et al.) for dates, rates, and Phase One deal terms. The 2026 Section 232 metals events (TACO-2026-9001 / TACO-2026-9002) are sourced from Federal Register Proclamation 11021 (FR 2026-06960; 91 FR 18201) and its June 2026 amending proclamation; copper (HS chapter 74) was added to the `hs10_coicop_map` crosswalk so copper tariffs are measured alongside steel (72) and aluminum (76).

---

## 8. API Specification

The PEPIndex is accessible via a RESTful API built on FastAPI (Python) at `api.pepi.apiautomations.com/api/v1/`. All endpoints return JSON and support standard pagination, filtering, and sorting. Authentication uses an `X-API-Key` header for tiered endpoints.

### 8.1 Access tiers

| Tier | Key prefix | Price | Endpoints granted |
|---|---|---|---|
| Free | (none) | $0 | `/health`, `/api/v1/index`, `/api/v1/stats`, `/api/v1/snapshot`, `/api/v1/sources/status`, `POST /api/v1/checkout`, `POST /api/v1/keys/request` |
| Standard | `pepi_std_` | $49 / month | All Free + the §8.2 endpoints |
| Premium | `pepi_pro_` | $199 / month | All Standard + the §8.3 endpoints |
| Admin | (internal) | — | `/api/v1/admin/metrics` |

Paid tiers are purchased self-service via Stripe Checkout (`POST /api/v1/checkout`); on successful payment the key is auto-generated, stored, and emailed to the buyer by the Stripe webhook (`POST /api/v1/stripe/webhook`). The legacy `POST /api/v1/keys/request` form and admin queue remain for manual/enterprise/comp provisioning.

### 8.2 Free and Standard endpoints

| Endpoint | Tier | Description |
|---|---|---|
| `GET /health` | Free | Service liveness check |
| `GET /api/v1/index` | Free | Current composite index value with 30-day / 60-day / 90-day trends |
| `GET /api/v1/stats` | Free | Aggregate statistics by domain |
| `GET /api/v1/snapshot` | Free | Free **teaser** snapshot — headline index, stats, live pressure, event summaries (same key shape as the dashboard's `data.json`; paid arrays/detail omitted) |
| `GET /api/v1/snapshot/full` | Standard | **Full** snapshot in one call — all event detail + price/sector impacts, tariff rates, economic indicators |
| `GET /api/v1/sources/status` | Free | Live source freshness — last ingested date per data source. No key required. |
| `GET /api/v1/index/history` | Standard | Daily index values for charting (date-range params) |
| `GET /api/v1/index/domain/{domain}` | Standard | Domain sub-index (`trade`, `foreign_policy`, `domestic`, `rhetorical`) |
| `GET /api/v1/events` | Standard | Paginated event list with domain, status, score filters |
| `GET /api/v1/events/{id}` | Standard | Full event detail with all scoring dimensions |
| `GET /api/v1/events/{id}/timeline` | Standard | Chronological lifecycle entries |
| `GET /api/v1/events/{id}/federal-register` | Standard | Federal Register documents linked to the event |
| `GET /api/v1/tariff-rates` | Standard | Latest USITC applied rates per country (`?country=` optional) |
| `GET /api/v1/tariff-rates/detail` | Standard | Per-HS-chapter applied rates for one country (`?country=` required) |
| `GET /api/v1/economic-indicators` | Standard | BLS + FRED time series (`?series_id=`, `?source=`, `?start=`) |
| `GET /api/v1/economic-stats` | Standard | Aggregate economic impact across all resolved trade events |
| `GET /api/v1/research-sources` | Standard | Methodology citations and source URLs |
| `GET /api/v1/courts` | Standard | Courts with opinion scrapers (from `court_registry`) |
| `GET /api/v1/events/{id}/sector-impact` | Standard | Per-COICOP-3 sector price impacts for a trade event (HS-2 → COICOP crosswalk) |
| `POST /api/v1/checkout` | Free | Create a Stripe Checkout session for a paid tier (JSON body: `tier`, `name`, `email`) — returns `checkout_url` |
| `POST /api/v1/stripe/webhook` | Stripe sig | Payment webhook — auto-issues + emails a key on `checkout.session.completed` (verified via `STRIPE_WEBHOOK_SECRET`) |
| `POST /api/v1/keys/request` | Free | Manual/enterprise/comp request (legacy) — submits to `api_key_requests` for admin review; paid tiers use `POST /api/v1/checkout` |

### 8.3 Premium endpoints

| Endpoint | Description |
|---|---|
| `GET /api/v1/events/{id}/economic-impact` | Full impact assessment: CPI contribution, incidence, distributional |
| `GET /api/v1/events/{id}/tariff-rates` | Statutory-vs-applied rate history for a single event (the PEPI gap over time) |
| `GET /api/v1/events/{id}/price-impact` | Daily price impact time series (4-group Cavallo methodology; live BLS CPI proxies via `price_impact_updater`) |
| `GET /api/v1/events/{id}/market-impact` | S&P 500, VIX, and sector ETFs (XLP, XLI, XLY, EEM) around threat and outcome dates |
| `GET /api/v1/events/{id}/taco-trade` | PEPI trade return analysis (cumulative S&P 500 return from threat to outcome) |

### 8.4 Admin endpoint

| Endpoint | Description |
|---|---|
| `GET /api/v1/admin/metrics` | Per-table row counts and pipeline freshness — internal monitoring |
| `GET /api/v1/admin/key-requests` | List pending manual/enterprise key requests; admin reviews and provisions via `db/provision_api_keys.py`. Paid Standard/Premium keys auto-provision via the Stripe webhook and do not appear here. |

---

## 9. Limitations & Methodological Considerations

**Subjectivity in classification.** Despite AI-assisted detection and human review, some events involve judgment calls about whether a statement constitutes a genuine threat or rhetorical posturing. The inter-rater reliability testing described in §4.2 mitigates but does not eliminate this concern.

**Outcome timing.** Determining when a PEPI event is "resolved" can be ambiguous, particularly for events that are quietly abandoned rather than explicitly reversed. The index uses a 180-day monitoring window, after which unresolved events are scored based on available evidence.

**Causal attribution.** Price movements following tariff announcements may reflect factors beyond the tariffs themselves, including concurrent policy changes, supply chain disruptions, or seasonal patterns. The Cavallo et al. methodology controls for this using pre-tariff trend estimation and event-study design, but causal identification is inherently imperfect.

**Non-trade domains.** Economic impact measurement is currently limited to trade-domain events where tariff rates and price data provide quantifiable metrics. Foreign policy and domestic policy events are scored behaviorally but lack equivalent economic impact data.

**Political neutrality.** The PEPIndex measures a specific behavioral pattern (threat followed by reversal) and does not make normative judgments about whether threats or reversals constitute good or bad policy. A high PEPI score indicates that a threat was not followed through; it does not assess whether follow-through would have been desirable.

---

## 10. References

The Budget Lab at Yale. (2026, April 1). *Tracking the Economic Effects of Tariffs*. Yale University. Retrieved from https://budgetlab.yale.edu/research/tracking-economic-effects-tariffs — Monthly report tracking effective tariff rate (USITC HTS + Census import weights), customs revenue (US Treasury), PCE goods and durable goods deviation from pre-2025 trend, and employment in tariff-exposed industries. Key finding: import-weighted ETR 11.1% as of April 2026; PCE core goods pass-through 46–86%; PCE durable goods pass-through 51–115%. Documents SCOTUS *Learning Resources, Inc. v. Trump* (Feb 20, 2026) IEEPA vacatur and ~$165B potential duty refund.

Amiti, M., Itskhoki, O., and Konings, J. (2019). International Shocks, Variable Markups, and Domestic Prices. *The Review of Economic Studies*, 86(6), 2356–2402.

Amiti, M., Redding, S.J., and Weinstein, D.E. (2019). The Impact of the 2018 Tariffs on Prices and Welfare. *Journal of Economic Perspectives*, 33(4), 187–210.

Bown, C.P. (2021). *US-China Trade War Tariffs: An Up-to-Date Chart*. Peterson Institute for International Economics. PIIE Trade War Monitor.

Burstein, A.T., Neves, J.C., and Rebelo, S. (2003). Distribution costs and real exchange rate dynamics during exchange-rate-based stabilizations. *Journal of Monetary Economics*, 50(6), 1189–1214.

Busch, S. et al. (2026). *Trump Tariff Tracker*. Atlantic Council GeoEconomics Center.

Cavallo, A. (2013). Online and official price indexes: Measuring Argentina's inflation. *Journal of Monetary Economics*.

Cavallo, A., Gopinath, G., Neiman, B., and Tang, J. (2021). Tariff Passthrough at the Border and at the Store: Evidence from US Trade Policy. *American Economic Review: Insights*, 3(1), 19–34.

Cavallo, A., Llamas, P., and Vazquez, F.M. (2026). *Tracking the Short-Run Price Impact of U.S. Tariffs*. Harvard University / Northwestern / Universidad de San Andrés. Updated figures at [pricinglab.org/tariff-tracker](https://pricinglab.org/tariff-tracker).

Gopinath, G. and Neiman, B. (2026). *The Incidence of Tariffs: Rates and Reality*. NBER Working Paper 34620.

Sangani, K. (2025). *Complete Pass-Through in Levels*. Kilts Center Working Paper No. 1.

Shapiro, J. (2025). *Trump Foreign Policy Force Threats Analysis*. European Council on Foreign Relations.

---

*Document version 1.2.10 | June 2026. This methodology is subject to revision as new data sources, academic research, and scoring refinements are incorporated. All changes are versioned and documented in the Changelog above. For questions or feedback, contact data@apiautomations.com.*

*PEPIndex Methodology Whitepaper v1.2.13 | © API Automations. Written by Kurt Sather, 2026.*
