All checks were successful
CI / lint (push) Successful in 32s
CI / notebooks-smoke (push) Successful in 1m29s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Deploy / api (push) Has been skipped
Deploy / llm (push) Has been skipped
Deploy / mc (push) Has been skipped
Infra CI / notebooks (push) Successful in 56s
Infra CI / zotero (push) Successful in 13s
Infra CI / docs (push) Successful in 1m26s
Infra CI / api (push) Successful in 1m1s
Infra CI / llm (push) Successful in 52s
Infra CI / mc (push) Successful in 22s
Deploy / report (push) Successful in 13s
CI / test (push) Successful in 13m16s
Notebooks Integration / notebooks-integration (push) Successful in 7m55s
Zotero Sync / zotero-sync (push) Successful in 1m21s
Package Supply Chain / pkg-supply-chain (push) Successful in 58s
153 lines
7.0 KiB
Markdown
153 lines
7.0 KiB
Markdown
# 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.
|
||
|
||
## P41 build outcomes (2026-08-17)
|
||
|
||
Issues #639–#641, milestone P41 (5 commits, b872d7d..HEAD, direct on
|
||
main; TDD throughout).
|
||
|
||
- **#639 (b872d7d)**: `bib/cfrlink.py` pure transmuter — 23 tests incl.
|
||
both round-trip laws; live smoke: the generated
|
||
`…/section-425.400#p-425.400(c)(1)(iv)` URL returns 200 with the
|
||
fragment's anchor present in the body.
|
||
- **#640**: `item_for` (exact-section beats part over the 699
|
||
regulation items), `place()` reusing `fr_links` (page=0) and the
|
||
untouched `_sync_fr_links` path, `cfr-jump` CLI with `--url` reverse
|
||
mode. Live: all three rollout cites matched exact **section-level**
|
||
regulation items (74YXT4TC / HFY6IREA / 95PQGJRP).
|
||
- **#641**: `Pincite.jump_url` cfr branch (store-free); notebook
|
||
`cfr_md()` helper + live eCFR links for `42 CFR 414.1450(b)(1)`,
|
||
`42 CFR 414.1425(c)(5)(ii)`, `42 CFR 425.400(c)` (gate `pass=1`,
|
||
in-container resolution proven); placed links synced via
|
||
`--tag source:ecfr` (699 items, all URL-dedupe skips, `fr_links: 3`)
|
||
onto the **pre-existing** Zotero regulation items (GEZXDVQ9 /
|
||
AEHYQWRZ / 47GK4BZE — Zotero held the regs under pre-bib keys; the
|
||
sync's URL dedupe attached children to them exactly as designed).
|
||
- Note: `cite_of` on a date-pinned URL returns the undated canonical
|
||
cite (the date is a view parameter, not part of the citation) — by
|
||
design, documented here.
|