Findings from first-fetch (2026-06-03)
Real surprises and validations from fetching the first two sources. Every
finding triggers either a sources.yaml update or a planning-doc edit.
✓ Plan validated
- Philly school count: 302 (212 district + 81 charter + 6 CTC + 2 cyber + 1 special-ed by
OrganizationTypeCode). The plan estimated ~300 with ~210 district + ~85 charter. On the money. - Future Ready PA Index is a single bundle covering every PA school with school-level rows. The plan assumed this.
- Subgroup breakdowns are present for every metric × 12 subgroups: All / AmericanIndianAlaskaNative / Asian / HawaiianPacificIslander / Black / Hispanic / White / 2orMoreRaces / EconomicallyDisadvantaged / EnglishLearner / StudentsWithDisabilities / CombinedEthnicity. Richer than NYC, which doesn't carry HawaiianPacificIslander or AmericanIndianAlaskaNative subgroups separately.
⚠ Plan surprises (substantive)
F.1 Join key is composite (AUN + Schl), not single 9-digit code
The plan said:
PDE 9-digit Public School Code as primary key (covers both sectors)
The reality: every PA school file uses AUN (9-digit LEA Administrative
Unit Number) + Schl (4-digit within-LEA school number). The 9-digit
"school code" doesn't exist as a single column — what we want is the
composite key (AUN, Schl).
Implication for 02_gap_analysis.md §B.1 and the future schema:
schoolsPK should be a derivedschool_code=concat(AUN, '-', Schl)(13 chars). Carry bothAUNandSchlas columns alongside.- LEA-level joins (district info, SDP-only filtering) use
AUN. - SDP's AUN is
126515001. All SDP schools have AUN starting1265….
F.2 Future Ready is richer than the spec assumed
The Performance file has three sheets totalling 514 columns of measures — not just the indicators we estimated. Specifically:
| Sheet | Cols | Contains |
|---|---|---|
| State Assessment Measures | 228 | PSSA + Keystone (Algebra/Lit/Bio) × subgroup × {%Proficient+Advanced, %Advanced, PVAAS growth, ESSA goal, ESSA-progress flag} |
| School On Track Measures | 93 | English-language-proficiency growth, chronic absenteeism, suspensions, regular attendance |
| College Career Measures | 193 | Grade 5/8/11 career standards, AP, dual enrollment, industry credentials, post-secondary enrollment |
Two consequences:
- PVAAS growth metric is available alongside proficiency. This is a value-added indicator that NYC doesn't have. It enables a real distinction between status-of-students and growth-of-students that NYC couldn't make. (Settles `00_overview.md §3 — O.1 in the methodology spec, not the repo decision: status vs. growth controls.)
outcomes.yamlshould be expanded to include the College/Career indicators as future-v2 outcomes. Not in scope for v1 but worth tagging as available.
F.3 ESSA designations are a substantive analytical dimension
Every Philly school has an ESSASchoolDesignation:
| Designation | Meaning | Philly count |
|---|---|---|
| DFLT | Default — meeting goals | 120 |
| TSI | Targeted Support and Improvement | 85 |
| CSI | Comprehensive Support and Improvement | 53 |
| ATSI | Additional Targeted Support and Improvement | 35 |
| ACSI | Additional Comprehensive Support and Improvement | 9 |
53 Philly schools are CSI (the federally-designated bottom 5%), and
9 are ACSI (chronically lowest). This is a live analogue to NYC's Renewal
Schools — except it's still in effect, federally-mandated, and updated
annually. This belongs in the schools table as a first-class column,
not as a story tag. The "CSI/ACSI school retrospective" deserves its own
story stub.
F.4 Cyber-charter footprint in Philly is smaller than I framed
The plan called cyber-charters "out of scope for v1 because they serve students statewide; attribution to Philly is conceptually fuzzy." That framing still stands for the statewide cyber-charter universe, but the Future Ready file shows only 2 cyber-charter schools with a Philadelphia physical address — both very small footprints. The big cyber-charter question isn't "which Philly cyber-charters are good?" but "how many Philly students attend cyber-charters that are physically located elsewhere?" — which requires student-enrollment-origin data not in this file.
Net: keep v1 scope. The statewide-cyber-charter story is its own larger project; the 2 Philly-located cyber-charters can be tagged but won't move analytical narrative weight.
F.5 The regular / charter / compctc / cyber / specialed
OrganizationTypeCode enum, not what the plan wrote
The plan proposed a 6-value school_type enum:
SDP_DISTRICT, RENAISSANCE_CHARTER, CHARTER, CYBER_CHARTER, ALTERNATIVE_ED, SPECIAL_ADMISSION
The Future Ready actual: 5 values (regular, charter, compctc, cyber,
specialed). Renaissance charter is not distinguished from regular
charter in PDE's classification. We'd have to derive Renaissance status
from a different source (SDP's charter office list).
Net change to 02_gap_analysis.md §B.2:
- Adopt PDE's 5-value enum as the source-of-truth.
- Carry a derived
is_renaissanceboolean (from SDP's charter office list, not yet ingested). - Drop
SPECIAL_ADMISSIONfromschool_type(it's an admission category, not a school type — fits cleanly underadmission_category).
What this changes downstream
| Plan doc | Section | Edit needed |
|---|---|---|
02_gap_analysis.md | B.1 (PK) | composite AUN + Schl, not single 9-digit code |
02_gap_analysis.md | B.2 (school_type enum) | adopt PDE's 5-value enum + is_renaissance derived column |
02_gap_analysis.md | B.4 (subgroup enum) | add AMER_INDIAN, HAWAIIAN_PACIFIC_ISL, COMBINED_ETHNICITY, drop TEMP_HOUSING (PA doesn't carry it) |
03_integration_plan.md | P0 | add a column-name normalization step to handle Future Ready's <MeasureName>_<Subgroup> wide format (NYC ingested long-format files; Future Ready is wide) |
05_validation_spec.md | Per-metric table | replace TBD source columns with the now-known AUN, Schl, <MeasureName>_<Subgroup> |
docs/cities/philly/00_overview.md | §3 (open decisions) | add O.7: "Add PVAAS growth as a first-class indicator alongside proficiency, or defer to v2?" |
sources.yaml | All futurereadypa_* entries | column names now known |
What's still TBD
pde_pssaandpde_keystoneURLs — first-fetch needed to confirm sheet shape (or use Future Ready as the substitute and skip these for v1).- All
pses_*(5 PSES respondent files) — URL pattern not known until fetch. sdp_spree— SPREE files atphilasd.org/performance/programsservices/spree/need URL discovery.sdp_school_profilesSocrata resource ID — discovery via OpenDataPhilly catalog.opendataphilly_pssaSocrata resource ID — same.
Each becomes its own first-fetch run.
Bottom line
The Future Ready bundle alone is enough to populate v1 for the 4 core outcomes (PSSA ELA, PSSA Math, Keystone, grad rate) plus chronic absenteeism. The PDE PSSA/Keystone bulk files can be skipped for v1; we add them in v2 if we need grade-by-grade breakdowns Future Ready doesn't expose at the school × grade × subgroup grain. The OpenDataPhilly + SDP files are still important for school directory enrichment (SDP Learning Network, City Council district, school-profile demographics) and for the PSES survey ingest.
Batch 2 findings (2026-06-03) — SDP CDN discovery
Updated after fetching 5 more sources: SDP PSSA+Keystone Zip, PSES Zip, SDP graduation CSV, SDP 90%-attendance CSV, SDP suspensions CSV.
✓ Plan validated
- PSES is one Zip per year, not five (as the plan originally enumerated). One source_id per year is sufficient; the Zip contains a codebook plus xlsx per respondent group.
- Stable CDN URLs at
cdn.philasd.org, going back to 2009-10 for PSSA, 2014-15 for PSES, 2013-14 for graduation. Discovery problem solved for SDP data. - PSSA + Keystone subgroup richness exceeds NYC's: each school × subject × grade × subgroup row has
bel/bas/prof/adv/profadvcounts AND percentages (5 levels × 2 measures). NYC publishes only "% Level 3+4." Philly gives full distribution.
⚠ Batch 2 surprises
F.6 SDP publishes two population cuts per metric, side by side
The SDP PSSA Zip contains two flavors of the same data:
| Variant | File suffix | Population |
|---|---|---|
| Actual | *_Schools_Actual_Suppressed.csv | All tested students at the school |
| Acct | *_Schools_Acct_Suppressed.csv | Accountability cohort (Oct-31-register attribution; SDP's official figure) |
This is exactly the population-cut distinction KX warned about in
verify/METHODOLOGY.md for NYC — where the NYC InfoHub file is only
the "Actual" cut and the accountability cohort had to be reverse-engineered.
Philly gives us both. Implication:
- Canonical for SDP-district schools:
Acct(matches SDP's own published reports + SPREE composite). - (ii) base case validates against the file we ingested ("Acct" specifically).
- (iii) cross-cohort sanity check: load both, compare per-school differences, surface where Acct vs Actual diverges meaningfully — gives us a NYC-style population-cut report at ingest time, not at validation time.
F.7 Three distinct school-ID schemes need a crosswalk
The plan said composite PK = AUN + Schl. Reality:
| ID scheme | Used in | Sample |
|---|---|---|
id_eos (SDP "End of School") | SDP PSSA + Keystone CSV (id_eos column) | 1130 (William T. Tilden MS) |
ULCS Code (SDP Universal Location Code) | SDP attendance + suspensions + graduation CSVs | (need crosswalk) |
(AUN, Schl) (PDE composite) | Future Ready + statewide PA files | (126515001, 1010) |
Plan implication: the schools table needs three identifier columns plus a derived school_code (the canonical join key for site URLs / queries). A separate school_identifiers mapping table or a one-time discovery loader builds the crosswalk from SDP's directory + PDE's directory. This is a real infrastructure task that the plan didn't anticipate.
Operationally: most SDP CSVs use ULCS Code (the schoolid_ulcs field in graduation), while their PSSA CSV uses id_eos. These two SDP codes may or may not be 1:1 — the SDP school directory will clarify (next fetch).
F.8 Some SDP sources cover only district schools, not charters
The graduation CSV's sector column has two values: District and Alternative. No charter rows. Same pattern likely for attendance, suspensions, PSSA Zip (district + alternative only).
This is the inverse of Future Ready, which covers everyone but at a coarser grain. So the architecture is:
| Universe | Source for outcomes |
|---|---|
| SDP district + alternative schools | SDP CSVs (granular: grade × subgroup × full distribution) |
| SDP charters + Renaissance charters + cyber + CTC | Future Ready (school-wide subgroup, less granular) |
Loader does a per-school-type source routing rather than a single source per metric. Add this to the integration plan as a clarification to P1.
F.9 Subgroup taxonomy is richer than the plan said
The graduation CSV's subgroup distinct values include Non-Binary as a gender — NYC doesn't carry it. Also Multi Racial/Other, Native Hawaiian/Pacific Islander, Does Not Have IEP, Non-EL, etc. Subgroup enum needs to be wider than NYC's. Edit B.4 again.
F.10 SDP suspensions data is a distribution, not a rate
The OSS CSV doesn't publish a single "suspension rate" — it publishes the share of students with 0 / 1 / 2 / 3 / 4+ suspensions per year. That's richer than NYC's stub-only suspension data, and gives us a "high-tail suspensions" analytical dimension that NYC doesn't have. v1 should compute and store both:
oss_pct_zero(% with 0 suspensions)oss_pct_multiple(% with 2 or more suspensions — the heavy-user share)oss_pct_chronic(% with 4+ suspensions)
These are derived metrics that ship cleanly via metric_definitions rather than as raw counts.
What this changes downstream
| Plan doc | Section | Edit needed |
|---|---|---|
02_gap_analysis.md | B.1 (PK) | Three ID schemes (id_eos / ULCS / AUN+Schl); add school_identifiers crosswalk table to schema |
02_gap_analysis.md | B.4 (subgroup enum) | Add NON_BINARY, NOT_IEP, NOT_ELL |
03_integration_plan.md | P1 | Add a per-school-type source routing step; both SDP "Actual" and "Acct" variants ingested; ID crosswalk loader prerequisite |
05_validation_spec.md | New section | "Actual vs Acct" per-school diff report at ingest time as an additional check, distinct from (i)-(iv) |
sources.yaml | sdp_pssa_keystone_2024-25 | Document both Schools_Actual and Schools_Acct files inside the zip |
00_overview.md | §3 | Add O.8: "Actual vs Acct as canonical for SDP-district PSSA data?" — recommend Acct (matches SDP's public reports) |
What's still TBD
sdp_charter_office_listURL — needed to flagis_renaissance.
Batch 3 findings (2026-06-03) — SDP Master School List + Enrollment/Demographics
Fetched + empirically tested the school-identifier crosswalk. The "three ID schemes" of F.7 collapse to two, and the SDP Master School List contains the full crosswalk in one file.
✓ Major plan simplification: the ID space is smaller than F.7 said
Verified relationships (333-school Master List + PSSA 2024-25 sample + Future Ready Philly subset):
| Relationship | Match rate | What this means |
|---|---|---|
ULCS Code ≡ SRC School ID × 10 | 239/239 (100%) | SDP's two internal codes are the same identifier at different scales. Drop "three IDs"; carry one canonical SDP code. |
id_eos (PSSA file) ≡ ULCS Code | 33/33 (100%) | The SDP PSSA file's id_eos is just the ULCS code under a different column name. No separate ID family. |
(AUN, PA Code) from PDE ≡ (AUN Code, PA Code) in Master List | 299/302 (99%) | Future Ready's PDE-side composite key joins cleanly to the Master List on AUN Code + PA Code. The 3 unmatched are likely cyber-charters in PDE but not in SDP's directory. |
Updated schema (replaces F.7):
schools (primary key: school_code = "<ulcs_code>"):
ulcs_code (SDP canonical; ≡ id_eos in PSSA; SRC*10)
src_school_id (= ulcs_code / 10; carried for convenience)
pa_code (PDE Schl)
aun_code (PDE LEA)
nces_code (federal cross-state)
...
For SDP-only metrics, query by ulcs_code. For PDE-side joins, use
(aun_code, pa_code). The Master School List is the single crosswalk
source — no separate school_identifiers table needed.
✓ Master School List is a rich directory beyond crosswalk
56 columns. Carries everything we need to seed the schools table:
| Field | Use |
|---|---|
Learning Network | Geography taxonomy |
City Council District | Geography taxonomy |
GPS Location (lat,lng pair) | Geocoding |
Admission Type | Admission category enum (Catchment / lottery / Special Admission / etc.) |
Governance | District / Charter / Contracted |
Federal Accountability Designation | ESSA status (cross-check with Future Ready's ESSA designation) |
School Reporting Category | Carries Renaissance distinction directly (SDP K12 Schools vs Renaissance Charter etc.) |
Major Intervention, Major Intervention Year | Direct analogue of NYC's "Renewal Schools" designation — at the school level, with year |
Community School Cohort | Direct analogue of NYC's Community Schools designation — by cohort |
Title I Designation | Federal Title I flag |
CTE Status | Career & Technical Education flag |
School Leader Name | Current principal (the principal-tenure data NYC couldn't get easily) |
Year Opened | School age — useful for "post-2002 small schools" analogues |
Grade K..12 flags (0/1 per grade) | Grades served — derive grade band |
F.11 — Renaissance Charter distinction is published right here. The plan
said "carry is_renaissance as a derived boolean from the SDP charter office
list." Reality: School Reporting Category column already distinguishes
Renaissance Charter from Charter and from district categories. No
separate charter-office-list fetch required.
F.12 — Major Intervention column = NYC Renewal analogue. SDP records
schools that were placed on an Academic Improvement Plan or similar, with
the year. Bartram HS has Major Intervention: Academic Improvement Plan
since 2017-2018. This is an ANALYTICAL DIMENSION the plan didn't
anticipate — it lets us tell the Philly version of the "schools the
district designated for turnaround" story directly from the directory.
F.13 — Community School Cohort column = NYC Community Schools analogue. Same pattern. SDP tracks which schools have joined the Community Schools program and in which cohort. Direct first-class data.
✓ Demographics file uses ULCS — clean join
The school-level enrollment+demographics CSV (35 cols, 2,629 rows for
2024-25) is grade × school grain (not just school). One row per
(schoolyear, sector, ulcscode, gradelevel). For school-level
demographic shares (the v1 use case), aggregate across grades on ULCS.
subsector column likely carries Renaissance distinction within
charters (TBD, verify on next batch).
What this closes
Combined with the earlier finds, the v1 data layer is discovered:
- All v1 outcome metrics are in fetched files
- All universe-defining metadata (sector, type, admission, geography) is in the Master School List
- The two ID-space families (SDP's ULCS, PDE's AUN+PA Code) have a 99%+ join
- Renaissance Charter status is published, not derived
- ESSA designation is cross-published in two sources for validation
What's still genuinely TBD
- PDE PSSA + Keystone bulk files — only needed if we want grade-by-grade breakdowns for charter schools. Future Ready gives charters school-wide only. Decision: defer to v2 unless a story specifically needs it.
- The 3 Future Ready Philly schools missing from Master List — cyber-charters with Philly mailing addresses but not in SDP's directory. Tag as "out-of-network" and proceed.
Major Interventionhistorical detail — Master List has current designation. For the "Philly Renewal-style retrospective" story, we'd need historical Master Lists (which the longitudinal Zip provides).