03 — Integration Plan (P0–P5)
Sequenced capabilities to ship the Philly build, analogous to KX's C0–C5 in
docs/design/03_integration_plan.md. Each P-item is one branch + one focused
PR (see 04_contribution_plan.md). Effort = S (<1 day) / M (1–3 days) /
L (>3 days) / XL (>1 week). Risk = Low / Med / High.
Sequencing principle: the Philly source layer is the only thing genuinely new. Everything else (analysis engine, verification harness, UI components, methodology) lifts. So the work plan front-loads the source layer (P0–P2), then re-uses NYC infrastructure to ride.
P0. Repo setup + scaffolding — Effort S · Risk Low
Decision O.1 (same repo vs new repo) settles first. Two execution paths:
- If same repo (multi-city namespace): add
lib/cities/<city>/module pattern; parameterizelib/queries/schools.tsby acitydiscriminator; addcitiesenum toschoolstable. Substantial refactor of the NYC code. - If new repo (
philly-edu-dataas a copy-then-modify): fork the repo, rename, delete NYC-specific data, keep the architecture. Far less code surface to change at the cost of duplicated front-end maintenance.
This plan is written to apply equally to either path — branch names use
philly/ prefix in same-repo mode and main-direct in fork mode.
Concrete items:
pipeline_philly/Python package (orpipeline/if fork),requirements.txtpinned identically to NYC's scaffold.verify-philly/Python harness directory (orverify/), copying the NYC harness's source-registry pattern.data/cities/philly/for analysis-table outputs, reference files, QA reports.- Adapt
.gitignoreto ignore the newdata/processed/philly/. - Empty
outcomes.yaml/covariates.yaml/comparability.yamlfor Philly.
Gate: make assemble-philly (or make assemble in fork mode) runs end-to-end
with empty configs and emits a zero-row parquet. No site impact.
P1. Source ingest (test scores + grad + absenteeism) — Effort L · Risk Med
The first real loader work. Three connector types to build, all under
scripts/loaders/philly/ (or scripts/loaders/ in fork mode):
- PDE bulk xlsx connector — for the authoritative PSSA + Keystone results files.
- OpenDataPhilly Socrata connector — for SDP-curated copies (faster iteration than PDE).
- Future Ready PA Index xlsx connector — for grad + chronic absenteeism + a few SDP-not-published indicators.
Concrete items:
docs/cities/philly/sources.yamlpopulated for all v1 outcomes (the source registry stub already exists; this fills it in with URLs after first fetch).- One
scripts/loaders/philly/<source>.tsper source (xlsx/Socrata pull → normalize → upsert intoschool_year_metrics). metric-definitions-philly.ts— registers all metric_keys with their direction, unit, source, applicable grade band.- Source-file provenance (sha256, retrieved_at, bytes) recorded into
DataLoadper KX's pattern. - A
philly_schools(orschoolsin fork) directory loader, populated from PDE's school code list + SDP's directory + the charter list.
Outcomes covered in P1: PSSA grades 3–8 ELA + Math (proficiency rate, mean scale score), Keystone Algebra/Lit/Bio (proficiency rate), 4-year cohort graduation rate, chronic absenteeism rate.
Out of scope for P1: surveys (P2), composites (P5).
Gate: DB populated, smoke-test query returns expected row counts; no analysis work yet.
P2. PSES survey ingest — Effort M · Risk Low
PSES Zip files unpack to one CSV per respondent group. Each CSV has the
school × question × response-option grain, which is essentially the same as
NYCSS's "Pos & Neg %" sheet — KX's nyc-school-survey.ts pattern applies
verbatim.
Concrete items:
- PSES connector: download Zip → unzip → upsert per-respondent CSVs into a
survey_responsestable. - Survey metric rollups: a
compute-survey-metrics-philly.tsanalog producingsurvey_teacher_recommend_school,survey_teacher_trust,survey_student_safety,survey_student_respect,survey_parent_satisfaction,survey_principal_*,survey_support_staff_*(PSES has more respondent groups than NYCSS). - Survey comparability regime config — PSES has been stable since 2017-18, so likely one regime.
Gate: survey rollups appear in school_year_metrics with subgroup=ALL and
metric_key matching the NYC naming.
P3. Peer-group derivation + percentile compute — Effort S · Risk Low
Lifted verbatim from the NYC build. The only Philly-specific decision is k — with ~300 schools instead of ~1,800, k=40 may produce overly homogeneous peer groups. Plan: derive at k=20, 30, 40 and flip-diff to pick.
Concrete items:
scripts/derive/peer-groups.tsruns with Philly config (admission categories, grade bands match Philly enums).scripts/derive/percentiles.tscomputes citywide percentile + comparison-group percentile, direction-adjusted.
Gate: the Outliers route renders with Philly data and direction-adjusted percentiles look correct on spot-checked schools (e.g. the most-coveted Special Admission high schools surface at the top decile for academic metrics).
P4. Validation harness — Effort M · Risk Low-Med
Direct adaptation of KX's verify/. The three connectors KX defined
(served_db.py, source_file.py, source_socrata.py) all apply unchanged;
only the registry contents differ.
Concrete items:
verify-philly/sources.yaml(or replace contents ofverify/sources.yamlin fork mode) populated for all P1 sources.verify-philly/snapshot.yamlmapping each metric_key to its SPREE varname (where SPREE publishes a comparable measure). For metrics SPREE doesn't cover (e.g. some Keystone subgroups), the (iii) spot check is skipped and noted inverify-philly/METHODOLOGY.md.verify-philly/METHODOLOGY.md— the Philly analog of KX's per-metric definitions doc; cites SDP's SPREE Educator Guide instead of NYC's SQR Educator Guide.- (ii) base case run for every Philly metric, gating a "validation pass" badge per outcome.
- (iii) spot check against SPREE for the metrics it covers, ±1.5pp tolerance per KX's convention.
Gate: docs/qa_reports/<metric>__validation.md and <metric>__snapshot.md
both written per metric; pass rates documented in METHODOLOGY.md.
P5. Analysis pipeline + stories + site — Effort XL · Risk Med
The big front-end + analytical surface buildout. Most of the work is content (story stubs, methodology recipes) rather than code, since the NYC components all apply.
Concrete items:
- Lift
pipeline/analysis/{engine,outliers,trends,run_outcome,trend_runner}.pyfromdocs/design/reference-scaffold/. Configureoutcomes.yaml/covariates.yaml/comparability.yamlfor Philly outcomes (per O.4, O.5). - Run the analysis pipeline on the pilot outcome (PSSA grade 3 ELA + Math composite) — flip-diff against simple decile method.
- Adapt UI components: Outliers/Movers filters get Philly metric labels (
itemsmap per the dropdown-label fix), Learning Network badges replace Borough badges, Special Admission badges replace Specialized HS badges. - 25 v1 Philly story stubs in
data/cities/philly/stories/(ordata/stories/in fork) — see02_gap_analysis.md §Ffor the planned list. - Per-story recipes following the
data/stories/recipes.tspattern. - Methodology page + about page rewritten for Philly context.
Gate: full site walkthrough — every primary route returns 200, sample stories render, Outliers + Movers filter to PSSA grade 3 ELA + Math, school profile renders for ≥10 Philly schools across sectors.
P-extras (post-v1, deferred)
- Cyber-charter sector — add
CYBER_CHARTERto school_type; tag schools serving Philly students; build a "Philly cyber-charter cohort" view. - Statewide reference cohorts — ingest all PA schools (not just Philly) so we can compute "Philly schools vs. demographically similar PA schools."
- Council district + neighborhood geography — point-in-polygon join from school lat/lng to OpenDataPhilly boundaries.
- Per-pupil funding — same gap as NYC; addable when SDP publishes school-allocation memos in structured form (currently PDF).
- InsideSchools-style narratives — partnership with Great Philly Schools or hand-curated.
Suggested sequencing
P0 → P1 → P2 → P3 → P4 → P5, with v1 launch after P5 ships. Total estimated
effort: 6–10 weeks of focused work for one engineer who already knows the NYC
codebase. Risk concentrates in P1 (new connectors with first-time data sources).
Each P-item is independently shippable behind a ?philly=1 query flag (or, in
fork mode, as a separate site). The NYC build is not modified at any step.