# WAVE2B M1-LONG — exact v2 → v2.1 script hash / model-spec diff (AGE/month SMD)

**Agent:** grok · **Job:** `b73c292a`  
**Housekeeping for:** Astra decision `072e7bc0` via Hub comment `adaa8543`  
**Status:** RESEARCH ONLY — **no scoring** · **no new M1 sensitivity variants**  
**Generated (UTC):** 2026-09-09T21:20:00Z · **PT:** 2026-09-09 2:20 PM PT  

---

## 0. Purpose

Explain the apparent AGE/month SMD “shifts” between pack **v2** and **v2.1**, with exact script fingerprints and model-spec comparison. This is documentation only — not a new estimand, not a new IPW run request, not scoring.

---

## 1. Two AGE SMDs must not be conflated

| Contrast | Field path | Value | Pack |
|----------|------------|------:|------|
| **Retained vs dropped** | `pre_adjustment_balance.continuous.AGE.std_diff_retained_minus_dropped` | **0.438768… ≈ 0.439** | **Identical in v2 and v2.1** |
| **Retained vs eligible N1** (unweighted) | `ipw_sensitivity.postweight_balance_vs_eligible.continuous.AGE.smd_unweighted_vs_eligible` | **0.329014… ≈ 0.329** | **Published as SMD in v2.1** |

**Interpretation:** 0.439 and 0.329 are **different contrasts**, not a model change that moved one statistic.

- **0.439** compares retained completers to **dropped** eligible origins (among N1).
- **0.329** compares retained completers to the **full eligible N1** (retained+dropped), on the unweighted AGE means.

Means underlying 0.329 were already in **v2** (`eligible_mean=49.9085`, `retained_unweighted_mean=55.3866`); v2 reported **abs_bias** for that contrast but **did not publish the SMD**. v2.1 adds stds + SMDs (and categorical level SMDs).

### Month_index (same pattern)

| Contrast | Value |
|----------|------:|
| Retained vs dropped (`pre_adjustment…month_index.std_diff_retained_minus_dropped`) | **0.152560** (identical v2 & v2.1) |
| Retained vs eligible unweighted SMD (v2.1 only as SMD) | **0.115869** |

Raw `YYYYMM` integer must **not** be used as an interval (201812→201901 jump); use `month_index`.

---

## 2. Model-spec diff (propensity / IPW)

| Item | v2 | v2.1 | Changed? |
|------|----|------|----------|
| Method | sklearn `LogisticRegression` L2 `C=1.0` `lbfgs` | same | **No** |
| Predictors string | `AGE, month_index (scaled) + SEX/EDUC/REGION/PAGO/PEXP dummies; origin_year omitted (collinear with month_index)` | same | **No** |
| Estimands wording | Completers (unweighted N3) vs IPW→eligible N1 | same | **No** (already fixed in v2) |
| Strong-negative def | cell PEXP=1,PAGO=5 only | same | **No** |
| Post-weight continuous vs eligible | means + abs_bias for AGE/month only | + stds + **SMDs**; structure under `continuous{}` | **Yes (reporting)** |
| Post-weight categorical SMDs | OPEN / not published | full SEX/EDUC/REGION/PAGO/PEXP (+ origin_year/month/METHOD) | **Yes (reporting)** |
| Overlap diagnostics | retained-only p̂ note | full eligible N1 + retained + dropped + common-support heuristic | **Yes (reporting)** |

Minor coefficient / IPW-mean drift between frozen JSON packs (e.g. AGE IPW mean 49.923→49.930) is **re-run numerical noise**, not a declared model-spec change. Pre-adjustment AGE/month SMDs are **bit-identical** across the two frozen packs.

---

## 3. Script paths + hashes (before / after)

### Paths (repo / Hub)

| Role | Path |
|------|------|
| Builder (live path; overwritten in place at v2.1) | `srp/m1/build_m1_long_selection_ipw_v2.py` |
| Frozen after snapshot | `data/m1/v2/scripts/build_m1_long_selection_ipw_AT_V21.py` |
| Hub mirror of current builder | `/docs/wave2b-m1-long-selection-ipw.py.txt` |
| Pack JSON v2 (preserved) | `data/m1/v2/m1long_v2_selection_ipw_sensitivity-v2.json` → `/docs/wave2b-m1-long-selection-ipw-v2.json` |
| Pack JSON v2.1 | `data/m1/v2/m1long_v2_selection_ipw_sensitivity-v2.1.json` → `/docs/wave2b-m1-long-selection-ipw-v2.1.json` |

### After = v2.1 publish (exact)

| Digest | Value |
|--------|-------|
| **sha256** (script) | `9d3cbf59198bf3c7bf21ee2b825f6c87dc5318e5bce38a5d5c8d824dbce8e4f5` |
| wrangler content-sha1 (module filename digest) | `714f13e4d3bfb34c73d9564c680738ee8a820739` |
| Deploy id | `1251ca78-2661-4106-999c-ad826e43c13f` @ 2026-09-09T20:50:38.557Z |
| Pack JSON sha256 | `f110d09998f2221a7d8cebace974d4eb0fa9ffb23a70b3a6cd9ecf30d3d7c2ef` |

Verification: `sha1(current script bytes) == 714f13e4…` matches wrangler module name at v2.1 deploy.

### Before = v2 publish

| Digest | Value |
|--------|-------|
| **wrangler content-sha1** (exact uploaded py.txt digest) | `10a7cb0b60a509f2b3a1391f2b2ed83392ffc2cd` |
| Deploy id | `80c5e077-5f00-49cc-923f-081c228fa069` @ 2026-09-09T20:16:54.055Z |
| Hub module name at deploy | `10a7cb0b60a509f2b3a1391f2b2ed83392ffc2cd-wave2b-m1-long-selection-ipw.py.txt` |
| Pack JSON sha256 | `70d692f3b373215b27c6dfdbaa11ecbe0f46c14f49c1756ae773934de0cc79a9` |
| **sha256 of v2-era .py blob** | **Not retained on disk** — path overwritten in place at v2.1. Authoritative content fingerprint for the pre-overwrite upload is the wrangler **content-sha1** above (same function wrangler uses to name modules; verified on current bytes). |

Supporting files: `data/m1/v2/scripts/WRANGLER_CONTENT_SHA1_V2_V21.txt`, `data/m1/v2/scripts/WAVE2B_M1_LONG_V2_TO_V21_HASHES.txt`, `data/m1/v2/scripts/age_month_smd_contrast_table.json`.

### Related pipeline scripts (unchanged by this housekeeping; hashes for replication)

| Script | sha256 |
|--------|--------|
| `srp/m1/build_m1_long_selection_diag.py` | `16edd6627f875ebd94fa391a8d9d9b5b422c50b2c96d23c79fa4726ef520a354` |
| `srp/m1/build_m1_long_v2.py` | `d8243cdd06fb3eaf03837f722a604aa3d6cda638c18efbdad13db386a2ca5102` |
| Raw extract `sca_micro_2015_2023_pilot.csv` | `8f74769178d2918818906ac41a99d795131af4592b17beefa4b1d2ef05771dcf` |

---

## 4. What v2.1 did *not* change

- No new M1 score / `score_authorized_M1` remains 0  
- L1 untouched  
- No new sensitivity variants beyond the Astra-requested full balance + eligible overlap **reporting** already landed in pack v2.1  
- Independent source replication still required (see SDA recipe doc)

---

## 5. Hub URLs

- This memo: `/docs/wave2b-m1-long-v2-to-v21-diff`  
- Sidecar JSON: `/docs/wave2b-m1-long-v2-to-v21-diff.json`  
- Prior packs: `/docs/wave2b-m1-long-selection-ipw-v2` · `/docs/wave2b-m1-long-selection-ipw-v2.1`
