docs(spec): eCFR jump links — bidirectional CFR cite/URL (P41)
This commit is contained in:
126
docs/superpowers/specs/2026-08-17-ecfr-jump-links-design.md
Normal file
126
docs/superpowers/specs/2026-08-17-ecfr-jump-links-design.md
Normal file
@@ -0,0 +1,126 @@
|
||||
# eCFR Jump Links — Bidirectional CFR Cite ↔ URL (P41)
|
||||
|
||||
Date: 2026-08-17. Milestone: P41. Requested as: "the same [as P40's FR
|
||||
jump links] for eCFR — so a CFR cite goes back and forth."
|
||||
|
||||
## Verified ground truth (2026-08-17)
|
||||
|
||||
- `https://www.ecfr.gov/current/title-42/section-425.400` (short form)
|
||||
**302-redirects server-side** to the canonical hierarchy URL
|
||||
(`/current/title-42/chapter-IV/subchapter-B/part-425/subpart-E/section-425.400`)
|
||||
— cite → URL needs no structure lookup; fragments survive redirects
|
||||
client-side.
|
||||
- Paragraph anchors on section pages are **semantic**:
|
||||
`id="p-425.400(c)(1)(iv)"` — the fragment IS the cite's paragraph
|
||||
path (316 such anchors on § 425.400). Both directions are therefore
|
||||
deterministic string transforms.
|
||||
- The eCFR versioner API is open (`/api/versioner/v1/titles.json`
|
||||
responds), but the plain `/cite/` permalink endpoint 406s for
|
||||
non-browser clients — irrelevant, the short form covers it.
|
||||
- bib holds **699 `item_type='regulation'` items with eCFR URLs** —
|
||||
both part-level (`…/title-42/part-414`) and section-level
|
||||
(`…/part-405/section-405.534`) — the Title 42/45 Regulations
|
||||
collections. These are the placement targets and the validation
|
||||
index.
|
||||
|
||||
## Decisions (inherited from P40's user-selected pattern)
|
||||
|
||||
Consumers: all (notebooks, `:pincite:`, Zotero child links, CLI).
|
||||
Addressing: cite (with/without paragraph path), URL (inverse), both
|
||||
directions. Persistence: no anchor grab needed — the transform is
|
||||
deterministic; the bib regulation index provides context/validation.
|
||||
Placement discipline: only `place()`d links reach Zotero.
|
||||
|
||||
## Architecture
|
||||
|
||||
**`src/bib/cfrlink.py`** — parse, transmute, context, place. Hooks:
|
||||
`bib/pincite.py` (`jump_url` covers the existing `cfr` locator type),
|
||||
notebook helper `cfr_md()`, CLI `stack bib cfr-jump`. Zotero placement
|
||||
reuses the P40 `fr_links` table + `_sync_fr_links` path unchanged
|
||||
(rows carry `page=0` and `p_id=NULL` for non-FR links — the sync only
|
||||
reads `label`/`url`; documented here rather than forking a parallel
|
||||
table).
|
||||
|
||||
### Data model
|
||||
|
||||
`CfrCite` frozen dataclass: `title: int, part: str, section: str
|
||||
("425.400", empty for part-level cites), paras: tuple[str, ...]
|
||||
(("c","1","iv")), date: str ("" = current)`.
|
||||
|
||||
### Parse (`parse_cite(ref) -> CfrCite`)
|
||||
|
||||
Accepted spellings (case-insensitive, `C.F.R.` dots optional, `§`
|
||||
optional, whitespace collapsed):
|
||||
- `42 CFR 425.400(c)(1)(iv)` / `42 C.F.R. § 425.400(c)(1)(iv)`
|
||||
- `42 CFR § 414.1425` / `42 CFR 414.1450(b)(1)`
|
||||
- `42 CFR Part 425` / `42 CFR part 425 subpart E` (subpart ignored for
|
||||
URL purposes — the short form resolves placement)
|
||||
Title is required (bare `§ 425.400` raises naming the reason — the
|
||||
title selects the eCFR tree). Paragraph path parsed from consecutive
|
||||
parenthesized tokens.
|
||||
|
||||
### Transmute
|
||||
|
||||
- `url(cite) -> str`:
|
||||
section: `https://www.ecfr.gov/current/title-{t}/section-{s}` +
|
||||
(`#p-{s}({a})({b})…` when paras present);
|
||||
part: `https://www.ecfr.gov/current/title-{t}/part-{p}`;
|
||||
date-pinned: `/on/{date}/` replaces `/current/` when `cite.date`.
|
||||
- `cite_of(url) -> str`: inverse over every eCFR URL form — short,
|
||||
canonical full-hierarchy, `/on/{date}/`, with or without `#p-…`
|
||||
fragment. Emits the canonical spelling `42 CFR 425.400(c)(1)(iv)`
|
||||
(part-level: `42 CFR Part 425`).
|
||||
- **Round-trip law**: `cite_of(url(parse_cite(x)))` == canonical
|
||||
spelling of `x`, and `url(parse_cite(cite_of(u)))` reproduces `u`'s
|
||||
short form — tested explicitly.
|
||||
|
||||
### Bib context + placement
|
||||
|
||||
- `item_for(cite, store) -> str | ""`: match against regulation
|
||||
items' eCFR URLs — exact section item first, then the part item.
|
||||
Powers placement targets and lightweight validation ("is this cited
|
||||
reg in the library?"); a cite with no library item still transmutes
|
||||
(URL construction never requires the store).
|
||||
- `place(store, ref, *, label="") -> JumpLink`: resolve + `item_for`
|
||||
(raises when no regulation item matches — placed links need a
|
||||
parent), upsert into `fr_links` (page=0), synced by the existing
|
||||
`_sync_fr_links` as linkMode=3 children.
|
||||
- `md_link(ref, *, text="")` — store-free (deterministic URL).
|
||||
- CLI: `stack bib cfr-jump REF [--md] [--place [--label L]]` and
|
||||
`stack bib cfr-jump --url URL` for the reverse direction (prints the
|
||||
cite).
|
||||
|
||||
### `:pincite:`
|
||||
|
||||
`Pincite.jump_url` gains the `cfr` branch: locators like
|
||||
`42 CFR 425.400(c)` resolve through `cfrlink` (no store data needed);
|
||||
non-title locators (`§ 3.2`-style, classified `section`) stay
|
||||
URL-less. Degradation contract unchanged (`""`, never raise).
|
||||
|
||||
### Notebook
|
||||
|
||||
`cfr_md()` helper beside `fr_md()` (same graceful fallback shape, but
|
||||
store-free); the CY2027 notebook's `42 CFR 414.1450(b)(1)` (APM
|
||||
revival), `§ 414.1425(c)(5)(ii)` (QP alignment — spelled with title),
|
||||
and the PQM section's `42 CFR § 425.400(c)` cites become live eCFR
|
||||
links. Gate: `nb_integration.py` single-notebook run.
|
||||
|
||||
## Testing
|
||||
|
||||
- Parse: every accepted spelling + reject-without-title + part form.
|
||||
- Transmute: URL forms incl. date pin and fragments; `cite_of` over
|
||||
short/canonical/dated/fragmented URLs; both round-trip laws.
|
||||
- Context: `item_for` section-over-part preference against a fixture
|
||||
store; `place` on a regulation item; sync reuses the P40 linkMode=3
|
||||
tests' path (one new test: CFR-placed row syncs identically).
|
||||
- Live smoke: `url(parse_cite("42 CFR 425.400(c)(1)(iv)"))` fetches
|
||||
200 with the fragment's anchor present in the body.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- eCFR structure/versioner grabs (no data needed for the transform;
|
||||
revisit only if full-hierarchy canonical URLs or historical
|
||||
snapshots become requirements).
|
||||
- Quote → paragraph addressing for CFR text (would need section text
|
||||
ingestion; FR quotes already cover the rulemaking corpus).
|
||||
- Auto-creating regulation items for cites not in the library.
|
||||
Reference in New Issue
Block a user