# WAVE2C — ACLED B1/B2 connector / data preparation

**Date:** 2026-09-09 ~2:55 PM PT  
**Agent (proposer):** grok · **Reviewer:** claude  
**Hub job:** `b73c292a-16e2-4f11-9370-1337d0239791` · **Research queue:** `d5023767-64ee-4177-84e2-e77ddc90505c` (`ACLED-B1-B2`)  
**Mode:** RESEARCH ONLY — **no scoring**, **no thresholds**, **no formula freeze**  
**Gates:** `L1-US-v0.1` frozen/unchanged · `score_authorized(M1)=false` · `score_authorized(E2)=false` · `score_authorized(B1|B2)=false` · `MODEL_CHANGE=NO`  
**Hub mirror:** `/docs/wave2c-acled-data-prep`

---

## 0. Purpose

Prepare the **behavioral** ACLED connector path for constructs **B1 Protest Escalation** and **B2 Political Coercion/Violence**: freeze-aware construct roles, public access path, required US variables/events, staging schema plan, credential blockers, and the next concrete pull once access exists.

This memo does **not** authorize B1/B2 scores, change L1/M1/E2, or compute Phase/Load/PPS/Sync.

---

## 1. Existing observatory stubs (Wave 1)

| Path | What it is |
|------|------------|
| `srp/ingestion/acled.py` | Credentials check (`ACLED_API_KEY`, `ACLED_EMAIL`) + `fetch_stub()` → empty rows; note **ACLED ≠ S1**; **B2 remains UNKNOWN** |
| `srp/ingestion/stubs.py` | `fetch_acled()` returns `requires_ACLED_API_KEY` |
| `srp/constants.py` | B1 = Protest Escalation (FAST); B2 = Political Coercion/Violence (FAST); both `raw_ingest_ok=False`, `score_authorized=False` |
| `docs/DATA_SOURCES.md` / `CREDENTIALS_NEEDED.md` / `SENSOR_CANDIDATES.md` | ACLED listed as optional stub; legacy I11 demoted; **ACLED ≠ S1** standing rule |
| `docs/b1b2/` | **New this wave** (this memo) |

No ACLED event rows exist under `data/` prior to this prep. Access probe artifact: `data/raw/acled/ACCESS_PROBE.json`.

---

## 2. Construct roles (behavioral evidence)

| Construct | Frozen name | Time scale | Intended ACLED role (research proposal) | Standing forbid |
|-----------|-------------|------------|-----------------------------------------|-----------------|
| **B1** | Protest Escalation | FAST | **MEASUREMENT** candidate from demonstration intensity / escalation ladder (Protests→intervention/excessive force; Riots/violent demonstration counts) | Do not mashup into S1; do not score |
| **B2** | Political Coercion/Violence | FAST | **MEASUREMENT** candidate from political violence event counts (esp. Violence against civilians; selected Battles / Explosions where US-relevant; fatalities as severity) | **ACLED ≠ S1**; do not use as elite fragmentation (E2) MEASUREMENT |
| **S1** | Affective Tribalization | — | **Out of scope** for this connector | Standing: ACLED ≠ S1 |
| **E2** | Elite Fragmentation | — | ACLED elite-targeted violence = **rejected** as E2 MEASUREMENT (Wave2C E2 memo) | Keep B-domain |

Working research gloss (not a formula freeze):

- **B1** tracks **mass demonstration escalation** — volume and severity of protest/riot events over short windows (weekly/monthly).
- **B2** tracks **coercive/political violence** — organized or one-sided violence with political character, including civilian targeting, not peaceful protest volume.

Neighbor boundaries: peaceful protest counts alone ≠ B2; ACLED event intensity ≠ affective polarization (S1); violence against civilians ≠ elite cohesion (E2).

---

## 3. Public ACLED access path (current)

ACLED has moved to **myACLED** authentication (cookie or OAuth). Unauthenticated API calls return **HTTP 403** `{"message":"Access denied"}`.

| Step | Detail |
|------|--------|
| Register | https://acleddata.com/user/register (institutional email preferred) |
| Accept ToS | Required at registration |
| Auth (programmatic) | `POST https://acleddata.com/oauth/token` with `grant_type=password`, `client_id=acled`, `scope=authenticated`, username=email, password |
| Auth (browser/tool) | Cookie session via `POST https://acleddata.com/user/login?_format=json` |
| Data endpoint | `GET https://acleddata.com/api/acled/read?_format=csv` (or json) + filters |
| Docs | https://acleddata.com/api-documentation/getting-started · https://acleddata.com/api-documentation/acled-endpoint |
| US methodology | https://acleddata.com/methodology/united-states-scope-and-coverage-acled-data |
| Codebook | https://acleddata.com/methodology/acled-codebook |

**Legacy note (Wave 1 stub):** `ACLED_API_KEY` + `ACLED_EMAIL` query-string auth is deprecated; myACLED FAQs indicate key issuance stopped and OAuth/cookie replaced it. Observatory stub still checks `ACLED_API_KEY`/`ACLED_EMAIL` — update connector to OAuth when credentials land (do not invent keys).

### 3.1 Access probe (this environment — 2026-09-09)

| Probe | Result |
|-------|--------|
| `GET .../api/acled/read?limit=1` | **403** Access denied |
| `GET .../api/acled/read?_format=csv&country=United+States&limit=5` | **403** |
| `ACLED_API_KEY` / `ACLED_EMAIL` / password in env or `.env` | **UNSET** |
| Public sample/demo CSV without login | **Not found** (API gated; Data Export Tool requires myACLED login) |

**Conclusion:** **BLOCKED — James / credential gate.** No US event extract landed. Do not invent or scrape unauthorized mirrors.

---

## 4. Required variables / events for US

### 4.1 Filters (first research pull)

| Filter | Value | Notes |
|--------|-------|-------|
| `country` | `United States` | Exact ACLED country string |
| `event_date` + `_where=BETWEEN` | Predeclare window (propose **2020-01-01\|latest** for B1/B2 FAST pilot; extend back only after schema lock) | US coverage denser post-~2020 |
| `limit` / pagination | ≤5000 per page; page until empty | |
| `_format` | `csv` for staging | |

### 4.2 Core columns to keep

`event_id_cnty`, `event_date`, `year`, `time_precision`, `disorder_type`, `event_type`, `sub_event_type`, `actor1`, `assoc_actor_1`, `inter1`, `actor2`, `assoc_actor_2`, `inter2`, `interaction`, `civilian_targeting`, `iso`, `region`, `country`, `admin1`, `admin2`, `admin3`, `location`, `latitude`, `longitude`, `geo_precision`, `source`, `source_scale`, `notes`, `fatalities`, `tags`, `timestamp`

### 4.3 Event-type mapping (research candidates — not frozen)

| Construct | Primary `event_type` / `sub_event_type` | Role |
|-----------|----------------------------------------|------|
| **B1** | Protests: Peaceful protest; Protest with intervention; Excessive force against protesters | MEASUREMENT ladder |
| **B1** | Riots: Violent demonstration (and Mob violence if present) | Escalation / violence-in-demo |
| **B2** | Violence against civilians: Attack; Abduction/forced disappearance; Sexual violence | MEASUREMENT |
| **B2** | Battles / Explosions/Remote violence (US-rare; keep if any) | MEASUREMENT / severity |
| **B2** | `civilian_targeting` flag across types | Severity / targeting diagnostic |
| Either | Strategic developments | CORROBORATION only (non-systematic) |

US coding caveats (from ACLED US coverage FAQs): police brutality coded as Violence against civilians / Attack only when outside legal parameters; hate crimes only when organized group mass targeting — document in provenance, do not “fix” upstream coding.

---

## 5. Schema plan (staging — research)

Proposed paths (create on first successful pull):

```
data/raw/acled/
  ACCESS_PROBE.json          # landed (blocked)
  PROVENANCE.md              # auth method, pull time, filters, codebook URL
  SHA256SUMS.txt
  us_events_<start>_<end>.csv
data/b1b2/
  us_b1_monthly_counts.csv   # research aggregates only
  us_b2_monthly_counts.csv
  coding_map_draft.json      # event_type → B1/B2 tags; approved=false
```

Staging table sketch (D1 / local parquet later):

| Column | Type | Notes |
|--------|------|-------|
| `event_id_cnty` | TEXT PK | |
| `event_date` | DATE | |
| `event_type` / `sub_event_type` | TEXT | |
| `disorder_type` | TEXT | |
| `admin1` | TEXT | US state |
| `fatalities` | INT | |
| `civilian_targeting` | TEXT/BOOL | |
| `construct_tag` | TEXT | `B1` / `B2` / `BOTH` / `NONE` — research only |
| `ingested_at` | TIMESTAMP | |
| `source_sha256` | TEXT | file digest |

Connector update plan (after key): replace stub in `srp/ingestion/acled.py` with OAuth token fetch + paginated US read; keep `credentials_status()`; never block pipeline if missing; never fabricate rows.

---

## 6. Blockers

| Blocker | Owner | What is needed |
|---------|-------|----------------|
| **myACLED account + OAuth credentials** | **James** | Register at acleddata.com; store email + password (or refresh token) as secrets — e.g. `ACLED_EMAIL` + `ACLED_PASSWORD` (preferred) or update stub if legacy key still works for the account. **Do not commit secrets.** Use box `.env` / `wrangler secret` only. |
| Observatory stub still key-shaped | Grok (after creds) | Implement OAuth path; keep missing=unknown |
| No redistributable public demo CSV | — | Cannot bootstrap without ToS-compliant access |

---

## 7. Next concrete pull (when unblocked)

1. James provisions myACLED access; Grok confirms `credentials_status()["configured"]=true` without echoing secrets.  
2. Pull minimal US extract: `country=United States`, `event_date=2024-01-01|2024-03-31`, `_where=BETWEEN`, `limit=5000`, `_format=csv` — prove pagination + schema.  
3. Write `PROVENANCE.md` + `SHA256SUMS.txt`; land under `data/raw/acled/`.  
4. Draft `coding_map_draft.json` B1/B2 tags from event_type/sub_event_type only — **no scores**.  
5. Optional monthly count tables for visual QA — still research-only.  
6. Handoff `EVIDENCE_READY` with file digests for Claude review.

---

## 8. Explicit non-actions

- No `score_authorized(B1|B2)` flip; no Phase/Load/PPS/Sync; no L1/M1/E2 production changes.  
- No invented API keys; no unauthorized third-party ACLED mirrors.  
- No treating ACLED as S1 or E2 MEASUREMENT.

---

## 9. Evidence ids (this prep)

| ID | Artifact |
|----|----------|
| `acled_access_probe:d6f12b8d08681773afc971329f63b3abfb1dc4303c1203ef98545e5b9851f74f` | `data/raw/acled/ACCESS_PROBE.json` |
| `hub_docs:wave2c-acled-data-prep` | This memo (Hub mirror) |
| `obs_memo:docs/b1b2/WAVE2C_ACLED_DATA_PREP.md` | Observatory path |

---

## 10. Suggested review ask (Claude)

Confirm: (1) B1←Protests/Riots ladder and B2←VAC(+battles/explosions) split is construct-valid enough to proceed once creds land; (2) OAuth secret shape (`ACLED_EMAIL`+`ACLED_PASSWORD`) vs updating stub; (3) 2024-Q1 minimal US smoke pull as first evidence — still no scoring.
