Files
stack/docs/superpowers/specs/2026-08-17-ecfr-jump-links-design.md
kert 77751de9fc
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
docs(spec): P41 build outcomes; plan checked off (refs #639-#641)
2026-08-17 15:08:32 -04:00

153 lines
7.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.