---
id: T01
parent: S01
milestone: M014
provides: []
requires: []
affects: []
key_files: ["backend/pipeline/schemas.py", "backend/pipeline/citation_utils.py", "backend/pipeline/test_citation_utils.py"]
key_decisions: ["Citations use 0-based indices matching moment list position", "validate_citations returns a dict for flexible downstream use"]
patterns_established: []
drill_down_paths: []
observability_surfaces: []
duration: ""
verification_result: "Ran python -m pytest backend/pipeline/test_citation_utils.py -v — all 15 tests passed (0.09s). Verified schema instantiation confirms body_sections defaults to list and body_sections_format defaults to 'v2'."
completed_at: 2026-04-03T00:50:26.993Z
blocker_discovered: false
---

# T01: Added BodySection/BodySubSection schema models, changed SynthesizedPage.body_sections to list[BodySection] with v2 format tag, and created citation_utils with 15 passing tests

> Added BodySection/BodySubSection schema models, changed SynthesizedPage.body_sections to list[BodySection] with v2 format tag, and created citation_utils with 15 passing tests

## What Happened
---
id: T01
parent: S01
milestone: M014
key_files:
  - backend/pipeline/schemas.py
  - backend/pipeline/citation_utils.py
  - backend/pipeline/test_citation_utils.py
key_decisions:
  - Citations use 0-based indices matching moment list position
  - validate_citations returns a dict for flexible downstream use
duration: ""
verification_result: passed
completed_at: 2026-04-03T00:50:26.994Z
blocker_discovered: false
---

# T01: Added BodySection/BodySubSection schema models, changed SynthesizedPage.body_sections to list[BodySection] with v2 format tag, and created citation_utils with 15 passing tests

**Added BodySection/BodySubSection schema models, changed SynthesizedPage.body_sections to list[BodySection] with v2 format tag, and created citation_utils with 15 passing tests**

## What Happened

Added BodySubSection and BodySection Pydantic models to schemas.py for H2/H3 nested page structure. Changed SynthesizedPage.body_sections from dict to list[BodySection] and added body_sections_format='v2'. Created citation_utils.py with extract_citations (regex-based [N] and [N,M] parsing) and validate_citations (coverage stats, invalid/uncited detection). Wrote 15 unit tests covering all edge cases.

## Verification

Ran python -m pytest backend/pipeline/test_citation_utils.py -v — all 15 tests passed (0.09s). Verified schema instantiation confirms body_sections defaults to list and body_sections_format defaults to 'v2'.

## Verification Evidence

| # | Command | Exit Code | Verdict | Duration |
|---|---------|-----------|---------|----------|
| 1 | `python -m pytest backend/pipeline/test_citation_utils.py -v` | 0 | ✅ pass | 90ms |
| 2 | `python -c 'from pipeline.schemas import BodySection, BodySubSection, SynthesizedPage; ...'` | 0 | ✅ pass | 500ms |


## Deviations

None.

## Known Issues

None.

## Files Created/Modified

- `backend/pipeline/schemas.py`
- `backend/pipeline/citation_utils.py`
- `backend/pipeline/test_citation_utils.py`


## Deviations
None.

## Known Issues
None.
