feat(bib): --docket scope for backfill-comments; drop ocr stub from refarm
All checks were successful
CI / lint (push) Successful in 34s
CI / notebooks-smoke (push) Successful in 1m28s
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 3m33s
Infra CI / zotero (push) Successful in 23s
Infra CI / docs (push) Successful in 1m34s
Infra CI / api (push) Successful in 1m5s
Infra CI / llm (push) Successful in 49s
Infra CI / mc (push) Successful in 13s
Deploy / report (push) Successful in 15s
CI / test (push) Successful in 17m10s
Harden / build-scan-report (push) Successful in 18m38s
Notebooks Integration / notebooks-integration (push) Successful in 7m29s
Renovate / renovate (push) Successful in 18s
Zotero Sync / zotero-sync (push) Successful in 1m13s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m0s
All checks were successful
CI / lint (push) Successful in 34s
CI / notebooks-smoke (push) Successful in 1m28s
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 3m33s
Infra CI / zotero (push) Successful in 23s
Infra CI / docs (push) Successful in 1m34s
Infra CI / api (push) Successful in 1m5s
Infra CI / llm (push) Successful in 49s
Infra CI / mc (push) Successful in 13s
Deploy / report (push) Successful in 15s
CI / test (push) Successful in 17m10s
Harden / build-scan-report (push) Successful in 18m38s
Notebooks Integration / notebooks-integration (push) Successful in 7m29s
Renovate / renovate (push) Successful in 18s
Zotero Sync / zotero-sync (push) Successful in 1m13s
Package Supply Chain / pkg-supply-chain (push) Successful in 1m0s
An unscoped 'bib backfill-comments' walks every un-enriched reg.gov stub in the store (~167k) at the 970/hr rate cap — a week-long crawl that buried the freshly farmed CMS-2026-2377 comments (highest item ids, ORDER BY i.id) at the back of the queue and blocked the P37 extract/index chain behind it. backfill_details now takes docket=, filtering on the store's reg-docket:<id> tag, and the CLI forwards --docket. The September refarm script uses it, and no longer calls comments extract-ocr, a phase-2 stub that always exits 2 (#253) and would have logged a spurious failed step every run. refs #615
This commit is contained in:
@@ -17,9 +17,10 @@ echo "=== re-farm run $(date -Is) ==="
|
|||||||
FAILURES=0
|
FAILURES=0
|
||||||
|
|
||||||
uv run stack bib fetch-pfs-comments || { echo "WARN: fetch-pfs-comments rc=$?"; FAILURES=$((FAILURES+1)); }
|
uv run stack bib fetch-pfs-comments || { echo "WARN: fetch-pfs-comments rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
uv run stack bib backfill-comments || { echo "WARN: backfill rc=$?"; FAILURES=$((FAILURES+1)); }
|
uv run stack bib backfill-comments --docket CMS-2026-2377 || { echo "WARN: backfill rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
uv run stack comments extract --docket CMS-2026-2377 || { echo "WARN: extract rc=$?"; FAILURES=$((FAILURES+1)); }
|
uv run stack comments extract --docket CMS-2026-2377 || { echo "WARN: extract rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
uv run stack comments extract-ocr --docket CMS-2026-2377 || { echo "WARN: ocr rc=$?"; FAILURES=$((FAILURES+1)); }
|
# extract-ocr is a phase-2 stub (always exits 2, no --docket) — see #253.
|
||||||
|
# ocr_needed attachments stay flagged in combined.md frontmatter for now.
|
||||||
uv run stack llm index --collection comments --docket CMS-2026-2377 \
|
uv run stack llm index --collection comments --docket CMS-2026-2377 \
|
||||||
|| { echo "WARN: index rc=$?"; FAILURES=$((FAILURES+1)); }
|
|| { echo "WARN: index rc=$?"; FAILURES=$((FAILURES+1)); }
|
||||||
|
|
||||||
|
|||||||
@@ -377,6 +377,7 @@ def backfill_details(
|
|||||||
store: Store,
|
store: Store,
|
||||||
client: Client,
|
client: Client,
|
||||||
*,
|
*,
|
||||||
|
docket: str = "",
|
||||||
limit: int | None = None,
|
limit: int | None = None,
|
||||||
log_path: Path | None = None,
|
log_path: Path | None = None,
|
||||||
commit_every: int = 25,
|
commit_every: int = 25,
|
||||||
@@ -403,6 +404,19 @@ def backfill_details(
|
|||||||
# Using a tag (not just the abstract) lets us handle "see attached"
|
# Using a tag (not just the abstract) lets us handle "see attached"
|
||||||
# comments with near-empty body AND still drives distinct retry
|
# comments with near-empty body AND still drives distinct retry
|
||||||
# states (``enriched:gone`` for 404s, untagged for transient fails).
|
# states (``enriched:gone`` for 404s, untagged for transient fails).
|
||||||
|
docket_clause = ""
|
||||||
|
params: list[str] = []
|
||||||
|
if docket:
|
||||||
|
# Scope to one docket so a fresh farm enriches in hours, not the
|
||||||
|
# multi-day full-store crawl (reg.gov's 1000/hr cap is binding).
|
||||||
|
# ``reg-docket:`` is the store's docket tag (``docket:`` is the
|
||||||
|
# empty-valued wart — see llm/source.py).
|
||||||
|
docket_clause = """
|
||||||
|
AND i.id IN (
|
||||||
|
SELECT item_id FROM item_tags
|
||||||
|
WHERE tag_id IN (SELECT id FROM tags WHERE name = ?)
|
||||||
|
)"""
|
||||||
|
params.append(f"reg-docket:{docket}")
|
||||||
rows = con.execute(
|
rows = con.execute(
|
||||||
"""
|
"""
|
||||||
SELECT i.id, i.key, i.url
|
SELECT i.id, i.key, i.url
|
||||||
@@ -415,9 +429,11 @@ def backfill_details(
|
|||||||
SELECT item_id FROM item_tags
|
SELECT item_id FROM item_tags
|
||||||
WHERE tag_id IN (SELECT id FROM tags WHERE name IN ('enriched:ok','enriched:gone'))
|
WHERE tag_id IN (SELECT id FROM tags WHERE name IN ('enriched:ok','enriched:gone'))
|
||||||
)
|
)
|
||||||
ORDER BY i.id
|
|
||||||
"""
|
"""
|
||||||
+ (f" LIMIT {int(limit)}" if limit else "")
|
+ docket_clause
|
||||||
|
+ " ORDER BY i.id"
|
||||||
|
+ (f" LIMIT {int(limit)}" if limit else ""),
|
||||||
|
params,
|
||||||
).fetchall()
|
).fetchall()
|
||||||
|
|
||||||
stats = {
|
stats = {
|
||||||
|
|||||||
@@ -338,6 +338,13 @@ def ingest_mail(
|
|||||||
|
|
||||||
@app.command(name="backfill-comments")
|
@app.command(name="backfill-comments")
|
||||||
def backfill_comments(
|
def backfill_comments(
|
||||||
|
docket: str = typer.Option(
|
||||||
|
"",
|
||||||
|
"--docket",
|
||||||
|
help="Limit to one docket (e.g. CMS-2026-2377). Without it the "
|
||||||
|
"walk covers every un-enriched stub in the store — a multi-day "
|
||||||
|
"crawl at the reg.gov rate cap.",
|
||||||
|
),
|
||||||
limit: int = typer.Option(
|
limit: int = typer.Option(
|
||||||
0,
|
0,
|
||||||
"--limit",
|
"--limit",
|
||||||
@@ -374,6 +381,7 @@ def backfill_comments(
|
|||||||
stats = backfill_details(
|
stats = backfill_details(
|
||||||
store,
|
store,
|
||||||
api,
|
api,
|
||||||
|
docket=docket,
|
||||||
limit=limit or None,
|
limit=limit or None,
|
||||||
log_path=log_path,
|
log_path=log_path,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -144,6 +144,43 @@ class TestUpsertComment:
|
|||||||
|
|
||||||
|
|
||||||
class TestBackfillDetails:
|
class TestBackfillDetails:
|
||||||
|
def test_docket_scope_limits_candidates(self, tmp_path):
|
||||||
|
"""docket= restricts the walk to that docket's stubs, so a
|
||||||
|
freshly farmed docket enriches without a full-store crawl."""
|
||||||
|
from bib.item import Item
|
||||||
|
from bib.store import Store
|
||||||
|
|
||||||
|
store = Store(str(tmp_path / "bib.sqlite"), storage_dir=tmp_path / "storage")
|
||||||
|
for docket, cid in [
|
||||||
|
("CMS-2026-2377", "CMS-2026-2377-0001"),
|
||||||
|
("CMS-2025-0304", "CMS-2025-0304-0001"),
|
||||||
|
]:
|
||||||
|
store.create(
|
||||||
|
Item(
|
||||||
|
item_type="source",
|
||||||
|
title=f"comment {cid}",
|
||||||
|
url=f"https://www.regulations.gov/comment/{cid}",
|
||||||
|
tags=["source:regulations-gov", f"reg-docket:{docket}"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
api = MagicMock()
|
||||||
|
api.get_comment_detail.return_value = {
|
||||||
|
"data": {"attributes": {"comment": "Body", "organization": "Org"}},
|
||||||
|
"included": [],
|
||||||
|
}
|
||||||
|
stats = backfill_details(
|
||||||
|
store,
|
||||||
|
api,
|
||||||
|
docket="CMS-2026-2377",
|
||||||
|
log_path=tmp_path / "log.txt",
|
||||||
|
scratch_root=tmp_path / "scratch",
|
||||||
|
)
|
||||||
|
store.close()
|
||||||
|
|
||||||
|
assert stats["enriched"] == 1
|
||||||
|
api.get_comment_detail.assert_called_once_with("CMS-2026-2377-0001")
|
||||||
|
|
||||||
def test_basic_enrichment(self, tmp_path):
|
def test_basic_enrichment(self, tmp_path):
|
||||||
store = MagicMock()
|
store = MagicMock()
|
||||||
con = MagicMock()
|
con = MagicMock()
|
||||||
|
|||||||
@@ -343,6 +343,22 @@ class TestBackfillComments:
|
|||||||
assert result.exit_code == 0
|
assert result.exit_code == 0
|
||||||
assert "enriched" in result.output
|
assert "enriched" in result.output
|
||||||
|
|
||||||
|
@patch("bib.connect")
|
||||||
|
@patch("bib.regulations_gov.Client")
|
||||||
|
@patch("bib.regulations_gov.backfill_details", return_value={"enriched": 1})
|
||||||
|
def test_docket_forwarded(self, mc_backfill, mc_client_cls, mc_connect):
|
||||||
|
store = MagicMock()
|
||||||
|
mc_connect.return_value = store
|
||||||
|
|
||||||
|
api = MagicMock()
|
||||||
|
api.__enter__ = MagicMock(return_value=api)
|
||||||
|
api.__exit__ = MagicMock(return_value=False)
|
||||||
|
mc_client_cls.return_value = api
|
||||||
|
|
||||||
|
result = runner.invoke(app, ["backfill-comments", "--docket", "CMS-2026-2377"])
|
||||||
|
assert result.exit_code == 0
|
||||||
|
assert mc_backfill.call_args.kwargs["docket"] == "CMS-2026-2377"
|
||||||
|
|
||||||
|
|
||||||
class TestIngestIom:
|
class TestIngestIom:
|
||||||
@patch("bib.connect")
|
@patch("bib.connect")
|
||||||
|
|||||||
Reference in New Issue
Block a user