ANALYSIS_DEFICIT.md
Analysis-phase deficit — Northern Illinois Recovery onboarding
Recorded 2026-07-06. This documents a known, intentional shortcut so it can be repaid later.
What happened
The NIR "analysis phase" (the work that turns raw source data into the 15 content briefs)
was produced manually / ad hoc: a one-off script (build_analysis.js) tuned by judgment
to NIR's full-continuum Illinois positioning, plus hand-authored briefs. There is no
reusable, parameterized system — provider onboardings before this one carried the same
debt, and it is documented in the parent addiction.md repo. This run accommodated an urgent
need; the intended long-term approach is a programmatic, replicable "semantic basis."
New in this onboarding (worth folding into the eventual system):
- Net-new harvest lane: NIR's WordPress REST API is deliberately disabled, so a sitemap
slow-lane harvester (
sitemap_harvest.js, parent repo root) was built: Yoast sitemap → polite HTML fetch → chrome-strip → WP-REST-shapedwp_content/<domain>/<type>.json→ the existingwp_to_markdown.js convert. Currently hardcoded to NIR's domain — the systematized version should take{domain}as a parameter and become the standard fallback whenever a site's REST API is unusable. - A third strategic-filter variant now exists across the provider analyses (outpatient-only,
residential-only, and NIR's full-continuum "zero nothing"). Three data points make the
declarative config shape obvious:
{ownedThemes[], zeroedThemes[], exemptThemes[]}.
Which steps were manual / ad hoc
| Step | Artifact(s) | How it was done | Fragility |
|---|---|---|---|
| Cohort state-join | il_wp_*.txt | build_analysis.js joins harvested domains → CARF/JC/SAMHSA/NAATP by registrable domain; full-state-name→2-letter | Join keyed on URL/domain match; no NPI/entity key |
| Blog inventory | il_local_blog_inventory.tsv | Aggregated from the parent repo's corpus, editorial-class items | Depends on a heuristic class field |
| Taxonomy tagging | topic_matrix.json, topic_coverage_matrix.tsv | Keyword/regex title classifier over 46 themes | No canonical taxonomy file; single-best-theme cascade over-assigns to the "Understanding/What Is" catch-all (local_items inflated there) |
| Benchmark | bench_all.json | Percentiles vs IL + NAATP cohorts | Fine, but formula lives only in the script |
| Gap filter | nir_topic_gaps.*, nir_aligned_opportunities.tsv, nir_continuum_opportunities.tsv | Full-continuum filter: nothing zeroed, six owned-core themes exempt from the coverage penalty | Filter is a judgment call encoded as constants in one script |
| Briefs | NIR_tierA_backlog.*, NIR_tierA_content_briefs.md | Backlog from NAATP demand + extracted service map; the 15 briefs hand-authored | Human authorship; not reproducible |
Gap formulas (so they're not lost):
gap_ratio = (na_items / local_items) × (Σlocal_items / Σna_items),local_itemsfloored at 1.gap_score = round(gap_ratio × na_items / 8).- topic-gaps opportunity
= round(na_items × max(0, 1 − 0.25 × own_posts)); owned-core themes exempt.
External data dependencies (not in this repo)
build_analysis.js reads from the parent addiction.md repo (it will not run from a bare
NIRC checkout): the source directories (carf_all.json, jc_snapshots/, samhsa_facilities.json,
naatp_deep/, provider_urls.json), the harvested corpus (corpus/*.jsonl), and the shared
NAATP baseline artifacts (naatp_blog_inventory.tsv, the national topic_matrix.json). The
generated outputs are committed here, so the site builds standalone.
Target: a programmatic, replicable "semantic basis"
The scalable version onboards provider N as a config, not a manual pass:
- Canonical taxonomy as data — a versioned
taxonomy.json(46 themes + signals), ideally a semantic/embedding classifier rather than a keyword cascade. - Parameterized cohort builder —
buildCohort({state, providerDomain})over a shared domain→state resolution layer. - Parameterized gap engine —
gapAnalysis({cohort, ownPosts, serviceFilter})with a declarativeserviceFilterrather than hand-edited constants. - Service-map extractor — generalize the harvested-page → service-URL extraction.
- Brief scaffolding — generate brief skeletons from the backlog + service map; keep human review for voice/clinical nuance.
- Provider-parameterized pipeline — the content pipeline (
lib/) is duplicated per provider with 3 hand-edited spots (SERP location, prompt identity, briefs path). Make it a single engine taking a provider profile. - Harvest-lane selection — a single
harvest({domain})that probes REST first and falls back to the sitemap lane automatically.
Until that exists, each new provider repeats this manual pass.