# NJDOE IDEA Section 618 Classification Rates

```toml ergo
[dataset]
ergo = "0.4"
slug = "idea-classification-rates"
title = "NJDOE IDEA Section 618 Classification Rates"
publisher = "NJ Department of Education, Office of Special Education"
subject = "https://www.nj.gov/education/specialed/monitor/ideapublicdata/"
source_url = "https://www.nj.gov/education/specialed/monitor/ideapublicdata/docs/2025_618data/2025IDEA618PublicReporting_ClassificationRates.xlsx"
pitfall = "NJDOE counted public and non-public students through October 2018 and public-school students only from October 2019 — a 1.13 to 1.21 percentage-point step statewide — so no figure may be joined, averaged or compared across that break."
status = "live"
confidence = "A"
updated = "2026-08-03"
implementation = "https://github.com/lavallee/njschooldata"

[dataset.coverage]
period = "Eleven October reference dates, 2014-10-15 to 2024-10-15 — but on two counted populations, so not eleven comparable points"
grain = "district overall classification row x coverage universe x metric; statewide category row x coverage universe x metric"
entities = "8,495 district rows across eleven editions and two coverage universes, 13 edition-universe registers, 84 statewide category rows, and 99 Section 618 report-family coverage cells; zero school rows in any edition; 558 of 587 rendered budget districts match by exact code"

[dataset.access]
keys = ["edition_id", "coverage_universe", "reference_date", "district_code", "category_id", "metric_id"]
```

## Consumer question and boundary

The budget product already asks how much of the UFB planning denominator is
special education. AQ-061 adds one earlier, bounded question, asked on the
district budget report and — since 2026-07-30 — on the district profile itself:

> What enrollment, students-with-IEPs count, and classification rate did
> NJDOE report for this district on October 15, 2024, before a reader
> interprets later UFB special-education planning rows?

Aquifer owns official-file custody, source literals and cell locators, mixed
native rate scales, value states, reconciliations, immutable releases, and
offline snapshots. This repo owns the budget question, five-table family,
exact-code join, period comparison, wording, placement, static UAT, and
recovery through retained pins.

The panel does not subtract the IDEA rate from the later UFB planning share.
It does not interpret either row as evidence of underlying disability
prevalence, unmet need, placement, staffing, spending cause, service quality,
appropriateness, or compliance. The October 15 reference date remains separate
from the UFB budget year, and the workbook's conflicting embedded `School Year
2023-24` title remains a publisher defect rather than an inferred period.

## Exact offline pin and mart

Two committed pins, one family file.

`contracts/aquifer/nj-idea-classification-rate-editions.lock.json` carries the
numbers:

- release: `nj-idea-classification-rate-editions-1992a65abcfd`;
- manifest SHA-256:
  `d6de61e649a5fd63545b25f9dfe8f52933067697b84ab11f83bd38b180d27293`;
- SQLite SHA-256:
  `1992a65abcfd3482730bf495b50a509ecb3407d3379dfe25ac49357811c0bd37`
  (28,295,168 bytes).

`contracts/aquifer/nj-idea-618-report-families.lock.json` carries NJDOE's own
index of what else it publishes under Section 618 — **custody and discovery
only, no measured values**:

- release: `nj-idea-618-report-families-797d688b2b50`;
- manifest SHA-256:
  `88a93c08bfc3a754092759dc5792bd6da8b9e7ef10cc0b07954692170b10665d`;
- SQLite SHA-256:
  `797d688b2b505ddfce1a9ce511a224c64e98c4ecc0e916cb37f4bd40c00cd1c3`
  (1,523,712 bytes).

The single-edition pin `nj-idea-classification-rates`
(`nj-idea-classification-aq061-refresh2-v3`) was **retired on 2026-08-01**. It
was verified a clean subset first: all 1,994 of its cells appear in the editions
release's `2025-data` edition with identical values and identical value states,
and the editions release adds 13 cells it never carried (a denominator for each
statewide disability category). Nothing was lost by dropping it.

The mart is five tables — `idea_classification_edition` (the register: one row
per edition and coverage universe, carrying everything that decides whether two
points may be joined), `idea_classification_district_history`,
`idea_classification_state_history`, `idea_classification_category_history`, and
`idea_618_report_family`.

## Static rendering and omissions

The canonical profile build writes 587 district budget reports. Exactly 558
join the IDEA mart by the same zero-padded NJDOE district code and render the
panel. The remaining 29 omit it instead of falling back to names. They include
the special-services, jointure, and educational-services entities absent from
the source, plus a small set of ordinary districts with no exact source row.
The full path set is locked in
`tests/test_aquifer_idea_classification_adapter.py` and enumerated in the
adoption receipt.

South Orange-Maplewood renders 7,140 total enrollment, 1,340 students with
IEPs, and 18.77%. Holland is the sole rendered not-collected case and displays
two `Not collected` cells, never zero. Atlantic County Special Services is an
intentional exact-code omission. Every rendered panel links the official XLSX
and preserves its source-row locator.

Since 2026-07-30 the same mart also feeds a district-profile module, **How many
students have an IEP** (`mod_special_education`). It renders the district count,
the district rate, the 17.35% statewide comparator, the three states an empty
cell can be in, and the three dimensions this file does not carry. It ships no
chart: a single rate against a single comparator is a stat figure, not a chart
form, and it sits in the enrolled-population block rather than beside the budget
section so that no layout invites the subtraction the boundary above forbids.

## Issues

### A withheld cell, an empty cell and a printed zero are three different things

```toml ergo
[issue]
id = "three-empties-are-not-zero"
title = "Three districts have a suppressed enrollment denominator (`*`), one has not-collected IEP and rate cells (empty), and four have an exact zero NJDOE printed — none of the three may render as the others"
effect = "breaks"
core = true
type = "suppression"
status = "mitigated"
detection = "Value-state populations in idea_classification_district: total_enrollment 652 reported / 3 suppressed; students_with_iep and classification_rate 650 reported / 4 exact-reconciliation-zero / 1 not-collected. The suppressed literal is `*` and the not-collected literal is the empty string. No classification rate is suppressed anywhere in the file — suppression lands only on the denominator."
misuse = "Rendering `*` or the empty literal as 0 or 0%. Holland Township (2220) reports 522 students and no IEP count at all; printed as a zero it becomes a district where no child has an IEP. Conversely, greying the four printed zeros as 'not available' deletes a figure NJDOE published."

[issue.scope]
tables = ["idea_classification_district"]
columns = ["total_enrollment_state", "students_with_iep_state", "classification_rate_state"]
rows = "total_enrollment_state <> 'reported' OR students_with_iep_state <> 'reported'"
```

The five non-clean districts, exactly as published:

| Code | District | County | Enrollment | Students with IEP | Rate |
| --- | --- | --- | --- | --- | --- |
| `2220` | Holland Township | Hunterdon | `522` reported | *empty* not-collected | *empty* not-collected |
| `8505` | Bergen Co JDC | Bergen | `*` suppressed | `0` printed zero | `0` printed zero |
| `8509` | Essex Co JDC | Essex | `5` reported | `0` printed zero | `0` printed zero |
| `8511` | Middlesex Co JDC | Middlesex | `*` suppressed | `0` printed zero | `0` printed zero |
| `8513` | Ocean Co JDC | Ocean | `*` suppressed | `0` printed zero | `0` printed zero |

The profile module renders each state distinctly, from one shared cell
formatter: a withheld cell keeps the publisher's `*` literal beside the word, an
empty cell reads "Not published" in italics, and a printed zero is emphasized as
the figure it is. The three JDC rows are the shape that most easily goes wrong —
a rate published without the denominator it was computed from — and the module
says so in words rather than showing a rate beside a blank.

### The classification rate is an administrative count, not a measure of anything

```toml ergo
[issue]
id = "rate-is-an-administrative-count"
title = "The classification rate is the share of students holding an IEP on one October day; it measures neither need, identification quality, service quality, staffing, spending, placement nor compliance, and it is directionless"
effect = "misleads"
core = true
type = "definitional"
status = "mitigated"
detection = "The published district rates run from 0.0 to 65.57 percentage points against a statewide 17.35. The four highest are state-operated or county-operated programs — Department of Corrections education (65.57), Department of Children and Families education (40.55), the Juvenile Justice Commission (39.6) — placement settings whose enrolled population arrives already classified. Nothing in the file distinguishes a district that identifies students well from one that over-identifies them, and there is no direction in which the number is better."
misuse = "Reading a high rate as unmet need being met, or as over-classification; reading a low rate as under-identification, or as a healthy population. Ranking districts on the rate. Placing the rate beside the UFB special-education planning share as if the difference between them meant something — different population, different date, different file."

[issue.scope]
tables = ["idea_classification_district", "idea_classification_state"]
columns = ["classification_rate_value"]
```

The module carries this directly beneath the figure rather than in a footnote,
and states the ambiguity in both directions in the same sentence. It also names
the structural reason the high end exists — some reporting units are state-run
programs, county juvenile detention centers and specialized placements — as a
property of what kind of entity a unit is, never as a judgment about a district.

### This workbook has no district breakdown — but NJDOE publishes one elsewhere

```toml ergo
[issue]
id = "no-district-level-breakdowns"
title = "The 13 disability categories in this workbook exist for New Jersey only and there is no district-level breakdown by disability, race, gender or educational environment in THIS file — but NJDOE publishes exactly those breakdowns at district grain in a sibling Section 618 report family, and a surface must not turn this file's limit into a claim about the publisher"
effect = "context"
type = "coverage"
status = "mitigated"
detection = "In the classification-rate release every district-level cell is `all-iep-students`; the 13 `idea-disability-*` categories appear only in statewide rows, and only in the four editions that ship the category workbook. In the sibling release `nj-idea-618-report-families`, `coverage_cell` shows `student-count`, `education-environment` and `exiting` as `held-tabular` at `district` level in all 11 editions, each with breakdowns `disability`, `gender`, `lep` and `race`. `_validate_projection` fails the build if any of the three stops holding eleven district-grain tabular editions."
misuse = "Telling a reader that a district disability, race or placement breakdown does not exist. It does — NJDOE publishes it, at district grain, in every edition; this repo simply has not loaded the values yet. The unscoped absence claim shipped on district profiles until 2026-08-01 and sent readers away from data the state publishes, which is the same error the accountability band made. Also wrong in the other direction: reading the statewide category shares as this district's."

[issue.scope]
tables = ["idea_classification_district_history", "idea_618_report_family"]
columns = ["category_id", "coverage_state", "levels", "breakdowns"]
entities = "all district reporting units, all eleven editions"
```

The module now names the three sibling families out loud and reads every claim
about them off the captured index rather than from prose — if NJDOE stops
publishing one at district grain the sentence goes with it. It also says whose
gap it is: *"This site does not yet carry those figures. That is a gap in what
we have loaded, not a gap in what the state publishes."* Loading the values is a
separate Aquifer materialization; the discovery release holds no measures.

### The counted population changes midway through the record

```toml ergo
[issue]
id = "coverage-universe-changes-midway"
title = "Through the October 2018 count NJDOE counted public and non-public students together; from October 2019 it counted public-school students only; and in October 2022 and October 2023 it published both — so the eleven editions are two series, not one"
effect = "corrupts"
core = true
type = "universe"
status = "mitigated"
detection = "`coverage_universe` on every source row and measure value. Editions 2015-2019 (reference dates 2014-10-15 to 2018-10-15) carry `public-and-nonpublic` only; editions 2020-2022 (2019-10-15 to 2021-10-15) carry `public-only` only; editions 2023 and 2024 (2022-10-15, 2023-10-15) carry BOTH; edition 2025 (2024-10-15) carries `public-only`. Measured on the two overlap years: statewide the rate differs by 1.13pp (17.63 vs 16.51, 247,313 vs 228,241 students) and 1.21pp (18.35 vs 17.14, 259,375 vs 238,721). At district grain only 455 of 648 and 464 of 653 districts have identical rates; among the rest the median move is 0.92pp and 0.95pp and the maximum is 43.85pp and 44.94pp. `EXPECTED_SEAM_STRENGTH` pins all five statistics per year and the build fails on drift."
misuse = "Joining the two bases with one line, averaging across them, computing a change from a pre-2019 year to a post-2019 one, or ranking a district's oldest rate against its newest. District 2520 reads 78.35% on the combined basis and 33.41% on the public-only basis in the SAME October — a reader shown one line through that has been told a district halved its classification rate when nothing about it changed."

[issue.scope]
tables = ["idea_classification_edition", "idea_classification_district_history", "idea_classification_state_history", "idea_classification_category_history"]
columns = ["coverage_universe"]
rows = "all"
```

**No public-only figure exists for any edition before the 2020 one.** That was
settled from the release rather than assumed, and it is the reason the
public-only trend can only begin at October 2019: the combined-basis years are
not convertible. Chart P29 therefore draws the public-only series as the trend
and the combined series as the older record on its own basis, as three separate
paths — the combined basis is itself broken by a three-year hole where NJDOE
published only public-only — with a `.split` rule at the change and nothing
crossing it. The two overlap Octobers plot both bases in the same column, so the
vertical distance between the marks *is* the size of the definition change.

### The denominator header names general education; the figure counts everyone

```toml ergo
[issue]
id = "denominator-header-contradicts-the-query"
title = "Seven of the eleven editions head the enrollment column `Gened` or `General Ed. Enrollment`, but the publisher's own embedded query counts every student aged 3-21 including those with IEPs, and only that wider reading reconciles against the published rate"
effect = "corrupts"
type = "coding"
status = "mitigated"
detection = "`idea_classification_edition.denominator_label` per edition: `Gened` (2015, 2016), `General Ed. Enrollment` (2017, 2018, 2019, 2021, 2022), `Total Enrollment` (2020, 2025), `All Students Count` (2024), and two different misspellings in 2023 — `Genral Education Enrollment count` and `General Education Enrollement Count`, one per coverage universe. `denominator_names_general_education` flags the seven; `EXPECTED_DENOMINATOR_LABELS` and `EXPECTED_MISLABELLED_DENOMINATOR_EDITIONS` pin them and the build fails on drift."
misuse = "'Fixing' the rate by adding the IEP count back onto the denominator, on the theory that a general-education enrollment excludes classified students. It does not — the header is wrong, not the figure — and the correction deflates every rate in the seven affected editions. Recomputing the rate from the two counts at all: the site prints NJDOE's published rate and does not recompute it."

[issue.scope]
tables = ["idea_classification_edition", "idea_classification_district_history"]
columns = ["denominator_label", "denominator_names_general_education", "total_enrollment_value"]
entities = "editions 2015, 2016, 2017, 2018, 2019, 2021 and 2022"
```

### A match on the oldest editions confirms the code, not the district

```toml ergo
[issue]
id = "identity-before-the-spine-has-evidence"
title = "The district-code join runs 97.55% (2015 edition) to 99.54%, but the organization spine holds no evidence before 2020-08-11, so a matched pre-2020 row proves the code is in use today rather than that the entity behind it is the same district"
effect = "context"
type = "identity"
status = "mitigated"
detection = "`idea_classification_edition.match_rate_percent` ranges 97.546 to 99.542 across the thirteen edition-universe registers; `identity_evidence_floor` is 2020-08-11 on every row. `EXPECTED_IDENTITY_MATCH_RANGE` pins the range."
misuse = "Reading a 2014 row and a 2024 row on one district page as two observations of the same district without qualification. New Jersey has merged, split and regionalised districts across this span, and this dataset cannot distinguish a stable district from a code reused after a reorganisation."

[issue.scope]
tables = ["idea_classification_edition", "idea_classification_district_history"]
columns = ["identity_match_state", "match_rate_percent"]
rows = "reference_date < '2020-08-11'"
```

Every table row older than the spine's evidence floor carries a dagger and a
note saying exactly this, where the reader meets it — not in a source footer.

### The statewide category breakdown exists in four editions, not eleven

```toml ergo
[issue]
id = "category-breakdown-is-four-editions-of-eleven"
title = "The statewide disability-category breakdown ships as a workbook in only four editions and as a PDF in the other seven, so the category series has four points where the district series has eleven — and three categories were renamed across the span"
effect = "context"
type = "coverage"
status = "mitigated"
detection = "`idea_classification_category_history` holds 84 rows across editions 2015-data, 2023-data, 2024-data and 2025-data only; `EXPECTED_CATEGORY_EDITIONS` pins that set. Renames, detected after normalising case and dash characters: `Hearing impairment` to `Auditory Impairment`; `Emotional disturbance` to `Emotional Regulation Impairment`; `Preschool Disabled` to `Developmental Delay` to `Preschool Child with a Disability`. `EXPECTED_CATEGORY_RENAMES` pins all three. `Deaf-blindness` versus `Deaf–blindness` is an en dash, not a rename, and is deliberately excluded."
misuse = "Drawing an eleven-point line for a disability category, or treating a category's disappearance between 2015 and 2023 as a change in prevalence when it is a change in the file format. Also: reporting the en-dash respelling as a renamed category, which buries the three that genuinely moved."

[issue.scope]
tables = ["idea_classification_category_history"]
columns = ["category_id", "reported_label"]
entities = "editions 2015-data, 2023-data, 2024-data, 2025-data"
```

### Deaf-Blindness publishes an exact zero rate against 18 students

```toml ergo
[issue]
id = "deaf-blindness-rate-rounds-to-zero"
title = "The statewide Deaf-Blindness row publishes a classification rate of exactly 0 against a headcount of 18 students"
effect = "corrupts"
type = "measurement"
status = "open"
detection = "In the pinned release, category `idea-disability-deaf-blindness` carries students-with-IEP literal `18` typed reported, and rate literal `0` typed exact-reconciliation-zero, at `State Rates!8:B` and `State Rates!8:C`. Every other statewide category publishes a non-zero fraction; the three next-smallest (visual impairment, traumatic brain injury, orthopedic impairment) publish `0.0002`."
misuse = "Printing the category's rate as 0 or 0% without its headcount, which says no New Jersey student is deaf-blind. The zero is a rounding artifact of a rate expressed as a fraction to four decimal places, not a count."

[issue.scope]
entities = "statewide Deaf-Blindness disability category"
rows = "category_id = 'idea-disability-deaf-blindness' AND metric_id = 'idea.classification.rate-percent'"
```

Status is `open` rather than `mitigated` because the statewide category rows are
not in the consumer projection, so nothing in this repo renders the value and
nothing here can be said to handle it. If the breakdown is ever projected, the
Deaf-Blindness row cannot be printed as a zero without its 18 students.

### The workbook's embedded title contradicts its own reference date

```toml ergo
[issue]
id = "embedded-title-contradicts-reference-date"
title = "The 2025-edition workbook's embedded title reads `School Year 2023-24` while the reference date NJDOE states for the data is 2024-10-15"
effect = "misleads"
type = "entry"
status = "mitigated"
detection = "Release metadata carries `publisher_edition = 2025`, `reference_date = 2024-10-15` and `embedded_title_defect = School Year 2023-24`. The conflict is inside the published file and is preserved rather than reconciled."
misuse = "Labeling the figures `2023-24` from the embedded title, or `2025` from the edition. Either label alone attaches the count to a school year NJDOE did not say it measures."

[issue.scope]
all = true
```

Neither label is printed without the other. The reader-facing module names the
October 15, 2024 reference date, then says the embedded title disagrees and that
the disagreement is the publisher's.

### Special-services districts and commissions have no row at all

```toml ergo
[issue]
id = "special-services-districts-have-no-row"
title = "29 of the 587 rendered district reports have no exact-code row in this file, and 20 of them are the county special-services districts, educational-services commissions and jointure commissions a special-education surface most invites a reader to look for"
effect = "context"
type = "linkage"
status = "mitigated"
detection = "558 of 587 rendered district budget reports match by exact zero-padded NJDOE district code; the 29 that do not are locked in tests/test_aquifer_idea_classification_adapter.py. Twenty carry special-services, educational-services-commission or jointure-commission routes; the other nine are ordinary districts (0130, 0200, 0810, 2120, 2160, 3145, 4700, 4720, 5490) with no source row."
misuse = "Falling back to a name match to fill the gap, which would attach a neighboring district's classification rate to a special-services entity. Reading the absence as a district with no students holding IEPs."

[issue.scope]
tables = ["idea_classification_district"]
columns = ["district_code"]
entities = "29 rendered districts with no exact-code source row"
```

`attach_idea_classification` joins on the exact zero-padded district code only.
A district with no row renders no module — not an empty one — so absence is
visible as absence.

## Changelog

```toml ergo
[change]
date = "2026-08-01"
note = "Moved the pin from the single-edition nj-idea-classification-rates snapshot to nj-idea-classification-rate-editions (eleven editions, 15 Oct 2014 to 15 Oct 2024) and added nj-idea-618-report-families as a second pin. The snapshot was retired only after being verified a clean subset: all 1,994 of its cells appear in the editions release with identical values and identical value states, and the editions release adds 13 the snapshot never carried. The mart went from two tables to five, keyed on edition and coverage universe. Registered four new issues the history exposes — the counted population changing midway (measured at 1.13pp and 1.21pp statewide on the two years NJDOE published both bases, and up to 44.94pp for one district), the denominator header naming general education in seven editions while the figure counts everyone, the identity spine having no evidence before 2020-08-11, and the category breakdown existing in four editions of eleven — and rewrote no-district-level-breakdowns, which was FALSE about the publisher: NJDOE publishes child count, educational environment and exiting at district grain, broken out by race, gender, disability and LEP, in all eleven editions. The module asserted otherwise on every district page until today. Chart P29 replaces P21's no-chart ruling; the values behind the sibling families are not loaded and the module says so."
issues = ["coverage-universe-changes-midway", "denominator-header-contradicts-the-query", "identity-before-the-spine-has-evidence", "category-breakdown-is-four-editions-of-eleven", "no-district-level-breakdowns"]
```

```toml ergo
[change]
date = "2026-07-30"
note = "Shipped the district-profile module (How many students have an IEP) and served the public explainer at /data/idea-classification-rates/, then registered the six issues a reader-facing surface has to get right — the three-way empty-cell distinction, the rate's directionlessness, the three absent dimensions, the Deaf-Blindness exact zero against 18 students, the embedded-title date conflict, and the 29 exact-code nonmatches. No projection change: the statewide disability categories remain outside the consumer mart. Measured while writing them: the release carries 13 disability categories plus all-iep-students, not 14 plus one, and 29 state-level cells rather than 31."
issues = ["three-empties-are-not-zero", "rate-is-an-administrative-count", "no-district-level-breakdowns", "deaf-blindness-rate-rounds-to-zero", "embedded-title-contradicts-reference-date", "special-services-districts-have-no-row"]
```
