Backlog Triage Disposition Board
Backlog Triage Disposition Board
Section titled “Backlog Triage Disposition Board”Generated 2026-06-26 — 15 backlog records verified against current code + 1 security/quality (CodeQL/knip) sweep.
Headline: 2 to close outright, 4 trivial quick-wins landable this session, 1 to ride the ID-45 zero-row window; the rest are correctly parked/blocked. Zero security or correctness impact in the CodeQL sweep — ~26 of ~37 findings are idiomatic false-positives.
Disposition board
Section titled “Disposition board”Sorted with actionable items at the top (close / quick-win / ride-window first, then parked, then bigger).
| bl-id | title | verdict | disposition | effort | rationale |
|---|---|---|---|---|---|
| 308 | PullMD-parity for manually-added references (ID-110) | superseded-stale | CLOSE NOW | trivial | PullMD fully retired (id-129 decommission migration, zero code refs); id-112 Trafilatura already converges both URL paths on one TRAFILATURA_CONFIG. No TS PullMD client warranted. Record’s own notes concur. |
| 165 | Persist Pydantic exception sub-class to pipeline_runs.result | already-done | CLOSE NOW | trivial | Both halves shipped: Option-D redaction (flow.py:471-497) + Option-B fine pydantic_class persisted to pipeline_runs.result.error_detail as ID-61.4 (flow.py:434-468/3875 + record/route.ts:221). Nothing left to decide. |
| 314 | Clarify/drop dedup_status:'clean' in /api/ingest/url | still-valid | QUICK WIN | trivial | Hardcoded constant (route.ts:62,320); no dedup in reference path; RPC’s already_existed ignored in new-write branch. Drop the field (or surface already_existed). ID-110 close-gate. |
| 371 | Re-add pullmd-free Playwright/Chromium image-baking CI guard | still-valid | QUICK WIN | trivial | id-129 commit 75d9146e deleted the Playwright/Chromium image-bake assertion as collateral with the (correct) pullmd grep removal. No replacement. Trivial defense-in-depth grep restore on onprem-deploy.yml. |
| 369 | Add qa_dedup_proposer_failed to pipeline error-class vocab | still-valid | QUICK WIN | trivial | Classifier (flow.py:369) can return a value missing from the Python tuple (flow.py:236-243) AND the TS Zod enum (error-classes.ts:27-34). Safe today (proposer failure swallowed) but a footgun before flow-fatal promotion. 4-point fix: two arrays + Inv-25 comment + test length 6→7. |
| 46 | run_quality_scan inserts flag_type=‘classification_low’ not in CHECK enum | still-valid | QUICK WIN | small | Latent SQLSTATE 23514: function body inserts 'classification_low' (squash:4871,4901) which the CHECK (squash:6836) rejects. No callers yet. Add value to CHECK enum (or remap to low_quality) + test. |
| 325 | Durable stale-citation marker on citations table | still-valid | RIDE ID-45 | small | citations table has no stale column; UC4 re-anchor (route.ts:689-700) explicitly notes “no stale column to set”, emits only a transient warning. One-column ALTER + write-site update, best timed with the ID-45 re-ingest zero-row window per the record. |
| 316 | Revisit summary_data disposition at content_items→reference cutover | still-valid | KEEP PARKED | small | Live TS writer (generateSummary) + many live readers; cocoindex does NOT populate it; no ontology spec subsumes it. Unblocks when: an ontology lane explicitly replaces structured summaries. |
| 310 | Server-side progress surface for sync /api/upload | still-valid | KEEP PARKED | medium | Sync path still uses cosmetic STEP_ADVANCE_INTERVAL timer; route is single synchronous response. pipeline_runs writes exist server-side but no sync-upload client polls them. Unblocks when: prioritised as UX work (convert route to async+poll or stream). |
| 169 | Runbook: absent error_class, expected Vercel 400, Cloud Run monitoring | still-valid | KEEP PARKED | trivial | Code behaviour is live + accurate (flow.py:3794-3808); ~15min doc-only item. Before writing: re-point “Cloud Run monitoring” → Coolify/IONOS (Cloud Run torn down S298). Promote when the ops/observability runbook is next edited (ID-61/T13). |
| 176 | Resolve cocoindex operation-value vocabulary case mismatch | still-valid | KEEP PARKED | trivial | Dormant _emit_upsert_log uses uppercase INSERT/UPDATE vs cocoindex’s lowercase upsert; helper unwired. Unblocks when: wired under ID-61/T13 (cocoindex still exposes no per-UPSERT callback at 1.0.7). Clean stale Cloud Run docstring at that time. |
| 162 | Evaluate cocoindex num_reprocesses vs KH _FlowRetryCounter | still-valid | KEEP PARKED | small | _FlowRetryCounter live (flow.py:577/3343); S299 blocker holds — num_reprocesses reachable only outside app_main, webhook fires inside. Unblocks when: engine API exposes the counter at emission time (not met at 1.0.7). |
| 374 | Verify reference_items FK-tolerance on clean platform standup | needs-more-info | KEEP PARKED | small | Cited FK + {75.17} converge path are live/current. Premise is an unconfirmed S415 cross-DB test-state contamination artefact, NOT a reproducible clean-standup defect. Unblocks when: re-run on next clean ID-127 / ID-45 standup walk (cannot reproduce statically). |
| 326 | Block/page citation re-indexing after content edits | still-valid | CONVERT TO TASK | large | UC4 deliberately skips block/page re-anchor (route.ts:703-705) pending the Claude block-splitter (out of scope). Real but large; no observed consumer regression. Convert to a task scoped under the block-splitter work when a drift consumer-break is seen. |
| 304 | Mount QARevisionHistory on a q_a_pairs detail surface | still-valid | MERGE | small | Component built+tested with all backing infra live (table/fetcher/key/route); gap is a missing q_a_pairs detail host (admin only has dedup-proposals). Merge into whatever task creates a q_a_pairs detail page/drawer. |
Recommended waves
Section titled “Recommended waves”CLOSE NOW — done or stale, no work needed
Section titled “CLOSE NOW — done or stale, no work needed”- bl-308 — superseded: PullMD retired (id-129), Trafilatura already converges both URL paths (id-112). Prime close candidate.
- bl-165 — already shipped as ID-61.4 (Option-D redaction + Option-B
error_detailpersistence both landed).
QUICK WINS — ready, trivial/small, no ID-45 dependency (in-session candidates)
Section titled “QUICK WINS — ready, trivial/small, no ID-45 dependency (in-session candidates)”- bl-314 (trivial) — drop/replace hardcoded
dedup_status:'clean'inapp/api/ingest/url/route.ts; ID-110 close-gate. - bl-371 (trivial) — restore the pullmd-free + Playwright/Chromium grep guard in
.github/workflows/onprem-deploy.yml. - bl-369 (trivial) — add
qa_dedup_proposer_failedto both vocab arrays (flow.py:236-243,error-classes.ts:27-34) + bump test length 6→7 + Inv-25 comment. - bl-46 (small) — migration: add
classification_lowtoingestion_quality_log_flag_type_check(or remap tolow_quality) + regression test forrun_quality_scan.
Suggested batch: bl-314 + bl-371 + bl-369 are pure trivial edits; bl-46 is a one migration + test. All four are independent and land cleanly in one hygiene-adjacent session.
RIDE THE ID-45 ZERO-ROW WINDOW — schema items timed to the cutover
Section titled “RIDE THE ID-45 ZERO-ROW WINDOW — schema items timed to the cutover”- bl-325 (small) — add a durable stale/
is_stalecolumn tocitations+ set it in the UC4 re-anchor lost-text branch (app/api/items/[id]/route.ts:689-700). Cheap while tables are sparse; the record explicitly asks to ride this window.
KEEP PARKED — genuinely blocked/deferred (with unblock trigger)
Section titled “KEEP PARKED — genuinely blocked/deferred (with unblock trigger)”- bl-316 — unblocks when an ontology lane explicitly replaces structured summaries.
- bl-310 — unblocks when sync-upload UX is prioritised (async+poll
pipeline_runs, or stream). - bl-169 — doc-only; promote when the Coolify/IONOS ops+observability runbook is next edited (re-point Cloud Run framing first).
- bl-176 — unblocks when
_emit_upsert_logis wired under ID-61/T13 (cocoindex still has no per-UPSERT callback at 1.0.7). - bl-162 — unblocks if/when cocoindex exposes
num_reprocessesat webhook-emission point. - bl-374 — see Disagreements below; ride the next clean ID-127/ID-45 standup walk.
CONVERT TO TASK / MERGE — bigger or host-dependent
Section titled “CONVERT TO TASK / MERGE — bigger or host-dependent”- bl-326 (large) → CONVERT to a task under the Claude block-splitter work; the block/page re-index gap is by-design until that lands. No consumer regression observed yet, so not urgent.
- bl-304 (small) → MERGE into the task that introduces a q_a_pairs detail page/drawer; the component and all infra are ready, only a host surface is missing.
SECURITY / QUALITY CLEANUP — CodeQL buckets + knip baseline plan
Section titled “SECURITY / QUALITY CLEANUP — CodeQL buckets + knip baseline plan”Overall severity: LOW / cosmetic. Zero security, zero correctness impact. ~26 of ~37 findings are false-positives or intentional-and-documented.
FALSE POSITIVES — do NOT touch (idiomatic / load-bearing):
- 13× Protocol
...stub bodies (flow_context.py124/125/177/178/272-274; test stub classes) — Ellipsis is the typed-stub convention. CodeQL “statement has no effect” is a known FP. Rewriting topasswould NOT silence it and is semantically identical. Suppress at source if anything. - 2× BaseException handlers (
test_platform_manifest_gate.py:191,test_cocoindex_flow_live_ingest.py:221) — already carry# noqa: BLE001+ comment; deliberate binder-thread error surfacing. Accept. - 2× server.py rate-limit globals (
server.py:172/173) — CONFIRMED FP; both are mutated viaglobalin_rate_limit_allows()/reset_rate_limit_state(). The limiter works. Do NOT “fix”. - 1× PDF byte-literal wrap (
test_pdf_zero_archetype.py:76) — deliberate line-wrapping of one PDF dict, not a missing comma. - 1× ast-dataflow fixture import (
tools/ast-dataflow/__tests__/fixtures/05-importers/caller-unused.ts:2) — the unused import IS the fixture’s purpose (input for theimportersquery). MUST preserve; add the fixtures dir to knip/eslint/CodeQL ignores. - 1× unreachable retry-return (
extraction.py:916_anthropic_retry) — tenacityreraise=Truemakes the implicitNoneunreachable. Optional: appendraise AssertionError('unreachable').
GENUINE but trivial (one-line, no behaviour change) — batch into ONE low-priority hygiene PR:
- 12×
except: passneeding a# best-effort: …comment (10 in.claude/skills/+ tooling, onlydocx.pytmpfile cleanup +extract_tender_questions.py:344weight-parse in pipeline path; both intentional). - Delete duplicate
import json(test_cocoindex_flow_write_path.py:3290). - Delete leftover
_OQ_PARENT_SH(test_crash_injection.py:37) — copy-paste cruft (siblings actually use it). - Drop 2 dead stores (
extract_conflict_context.py:153,173). - KEEP
_PIPELINE_ERROR_CLASSES(flow.py:236) — test-synced spec mirror oflib/pipeline/error-classes.ts; add# noqa/keep-alive, do NOT delete. (Note: bl-369 above adds the 7th class to this same mirror.)
knip baseline plan (TS/JS only — does NOT clear in-file Python/TS unused imports):
- Triage 86 findings by language first;
bun run knipshows only the TS file/export/dep layer. - TS unused imports/locals →
bun lint --fix(ESLintno-unused-vars), review diffs. - Python unused imports/locals → no ruff/autoflake config exists; add ruff (F401/F841) OR
autoflake --remove-all-unused-importsonscripts/**.py, review diffs. - Prefer DELETE for ordinary dead imports; reserve baseline-acknowledge for load-bearing exceptions.
- Load-bearing — IGNORE, never auto-delete:
tools/ast-dataflow/__tests__/fixtures/**(add toknip.config.tsignore + eslint/CodeQL path filter — currently NOT excluded); re-export/barrel shims (e.g.lib/taxonomy/taxonomy.ts);_PIPELINE_ERROR_CLASSES. - Sequence: extend ignores → ESLint
--fix→ ruff/autoflake → re-runbun run knip+bun run test+pytest scripts/tests/→ land as one hygiene PR.
Disagreements / low-confidence
Section titled “Disagreements / low-confidence”-
bl-374 (needs-more-info) — The only non-binary verdict. The cited FK (
reference_items_source_document_id_fkey) and the {75.17} converge-by-walk path are confirmed live/current, so it is NOT stale. BUT the original S415 failure looks like a cross-DB test-state contamination artefact (a STAGINGsource_document_ida196e2d8…landed in a PRODreference_itemsrow during manual walk-proof edits), not a reproducible clean-standup defect. By design,source_document_idis a deterministicuuid5('sd:'+url)co-declared with therirow in the same component, so the source_document always exists on a clean walk — hence no FK tolerance is needed.- What to check: run a clean ID-127 platform standup pipeline walk (same clean-DB window as ID-45 / ID-127.11) and confirm
_ingest_url_bodycompletes with noreference_items_source_document_id_fkeyviolation. Cannot be settled statically — keep parked to ride that window. If it reproduces on a genuinely clean DB, escalate to a real bug + add targeted FK tolerance; if not, close as test-state artefact.
- What to check: run a clean ID-127 platform standup pipeline walk (same clean-DB window as ID-45 / ID-127.11) and confirm
-
No other verifier dissent: the remaining 14 verdicts (1 already-done, 1 superseded-stale, 12 still-valid) are all backed by concrete file:line evidence against current code.