Skip to content

feat(curator): escalate unrepairable wikilinks into the Librarian pipeline#12

Merged
nickroci merged 1 commit into
fix/librarian-recency-cap-and-wikilink-repairfrom
feat/escalate-unrepairable-wikilinks
May 28, 2026
Merged

feat(curator): escalate unrepairable wikilinks into the Librarian pipeline#12
nickroci merged 1 commit into
fix/librarian-recency-cap-and-wikilink-repairfrom
feat/escalate-unrepairable-wikilinks

Conversation

@nickroci

Copy link
Copy Markdown
Owner

Summary

Stacked on #11. When the deterministic post-write pass (scholar_prompt.repair_broken_wikilinks, added in #11) cannot resolve a broken wikilink, it now escalates the issue into the existing Librarian→Scholar pipeline instead of silently neutralising it. No new agent or role — the Librarian already restructures articles, so we just feed it the issue and tweak its prompt.

Flow / integration points:

  • repair_queue.py (new) — process-global, thread-safe RepairQueue with the in-flight guard. enqueue / drain_pending / clear, fingerprint = (kind, file, target). Structured so other invariant types (over-cap dirs, bad frontmatter) plug in later; only broken_wikilink is wired.
  • Detectionscholar_prompt.repair_broken_wikilinks gained an on_unresolved callback; scholar._escalate_unresolved_wikilink enqueues the task. An escalated link is left broken on disk (not neutralised) so it stays detectable until actually fixed; the no-callback path still neutralises (backward compat).
  • Routinglibrarian.scan drains pending tasks, renders a highest-priority <repair_tasks> block (librarian_prompt), and attaches the drained fingerprints to every packet it emits (incl. error/empty). The Librarian researches the target with its existing bm25/embedding/Read tools and proposes the right existing action (rewrite link / create target / remove).
  • Clearscholar.review collects incoming fingerprints before any work and clears them in a finally (after the repair re-detection pass). The scheduler also clears fingerprints of packets dropped on backpressure.

The guard (only guard, no max-attempts, no give-up): re-escalate on every detection while unfixed. The in-flight marker prevents two concurrent attempts for one issue: set on escalation, kept across the Librarian drain, released when the carrying review concludes. Within one review that carries fingerprint X and doesn't fix it, the repair pass re-detects X but the re-enqueue is suppressed (still in-flight), then the finally clears X — so no double-escalation in one review, and the next pass re-fires once the marker is gone.

Cross-model design + implementation review by Codex confirmed the marker lifecycle is race-free and identified the leak modes that are now closed (drain-then-error, backpressure drop, all-empty early return, neutralise-erases-signal).

Test plan

  • uv run --frozen ruff check . && uv run --frozen ruff format --check .
  • uv run --frozen pyright — 0 errors (1 pre-existing __main__.py warning, untouched)
  • uv run --frozen pytest — 570 passed, 90.38% coverage (≥90%)
  • duplicate-code pylint gate — 10.00/10, no findings
  • New tests: unresolvable-link trigger, in-flight guard blocks concurrent duplicate, re-escalation after marker clears, all-empty/backpressure marker release, Librarian prompt + drain/attach routing

🤖 Generated with Claude Code

…eline

When the deterministic post-write pass can't resolve a broken wikilink it
now ESCALATES the issue into the existing Librarian->Scholar pipeline
instead of silently neutralising it. A new process-global repair_queue
records the issue (file + broken target) and the next Librarian run drains
it, researches the intended target with its existing search/Read tools, and
proposes the right existing action (rewrite link / create target / remove);
the Scholar reviews and executes it as a normal proposal.

The only guard is an in-flight marker keyed on the issue fingerprint
(kind, file, target): set on escalation, kept across the Librarian drain,
and released when the carrying packet's review concludes (or when the
packet is dropped on backpressure). A still-broken link is left in place
rather than neutralised so it stays detectable and re-escalates on every
pass until fixed — no max-attempts cap, no permanent give-up, and never two
concurrent attempts for the same issue. Structured so other invariant types
can plug into the same queue + guard later; only wikilinks are wired now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@nickroci nickroci merged commit 3a996bc into fix/librarian-recency-cap-and-wikilink-repair May 28, 2026
4 checks passed
@nickroci nickroci deleted the feat/escalate-unrepairable-wikilinks branch May 28, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant