feat(reporting): NCANDS row mapping (cases DTO -> NcandsChildRow #1-34)

Summary

Phase D / D4 (epic &55 (closed)). The mapping layer: cases-export DTO → NcandsChildRow (#1 (closed)–34).

Uses the #644 (closed) craig-reference literal-code functions (reporter_type_to_rptsrc, disposition_to_rptdisp, abuse_type_to_chmal, gender_to_chsex, ethnicity_to_ncands, race_to_ncands). Implements: RptDisp (#11 (closed)) derivation = most-severe of the report's allegation dispositions (Substantiated > Unsubstantiated > Inconclusive→None); age derivation child_age_code(dob, report_date) (lives here, not craig-reference — needs the report date, avoids a chrono dep on the leaf crate); materialize #1 (closed)/#2 (closed) (submission year + state snapshot) so re-export is stable. De-dup maltreatment types (codebook: each once across ChMal1–4). Typed thiserror errors.

Acceptance criteria

  • Mapping fn + table-driven/proptest tests (incl. RptDisp most-severe + age boundaries + None/unpopulated arms).
  • Semantic-gap NULLs are NOT validation errors; a missing required-and-capturable field (no child_id / no maltreatment) sets row_valid=false.

References

Epic &55 (closed); plan Phase D / D4 + the verified-codes table; ADR-040.

🤖 Generated with Claude Code