Surface failed + zero-page sources and add citadel ingest --retry - #136
Merged
Conversation
A brand-new source whose session succeeds with zero page changes used to be marked done and never revisited, indistinguishable from a healthy ingest. Now the run report carries a WARNING section (IngestReport.no_pages) plus a log.md line per such source, the live progress renders the "no changes" verdict in yellow, and `citadel status` marks every ingested source no wiki page cites with NO PAGES (also "uncited" in --json), closing with a retry hint whenever anything is stuck. Reconciles and delete cleanups are deliberately not flagged (an unchanged verdict is legitimate there). `citadel ingest --retry` re-runs the whole stuck set without naming paths: every failed source still on disk (duplicate skips and curate records excluded) plus every ingested-but-uncited source as a forced reconcile, printing the computed set first, refusing --force/explicit paths, and exiting 0 cleanly when nothing is stuck. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cy9pETjieaS5z48Ku1tArB
There was a problem hiding this comment.
Pull request overview
This PR improves ingest observability and recovery by surfacing (1) failed sources and (2) “successful” ingests that produced zero wiki entries, and by adding a one-command retry flow (citadel ingest --retry) to re-run the bounded “stuck” set.
Changes:
- Add
IngestReport.no_pages+ reporting/logging so fresh ingests that produce zero wiki changes are visible and actionable. - Add
citadel ingest --retry(CLI + ingest support) to retry failed sources on disk and force-reconcile ingested-but-uncited sources. - Extend
citadel statusto mark ingested sources with no citing pages asNO PAGES(and expose"uncited"in--json), plus add a retry hint.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
citadel/ingest.py |
Tracks and renders no_pages, persists log entries for zero-page sources, and adds retry_candidates() backing --retry. |
citadel/cli.py |
Adds --retry flag to ingest command, including usage constraints and computed retry-set execution. |
citadel/status.py |
Adds uncited marker to ingested rows via store.citing_pages_map and prints a one-line retry hint when relevant. |
citadel/progress.py |
Changes “no changes” verdict styling to yellow for higher visibility. |
tests/test_retry.py |
Adds offline tests covering no_pages, retry_candidates(), --retry CLI behavior, and status uncited markers. |
docs/troubleshooting.md |
Documents NO PAGES in citadel status and introduces citadel ingest --retry. |
docs/maintenance.md |
Documents the new NO PAGES/no-pages surfaces and the retry workflow. |
CLAUDE.md |
Updates contributor docs to include --retry and the enhanced status behavior. |
CHANGELOG.md |
Records the new visibility and retry features in the changelog. |
.github/copilot-instructions.md |
Regenerated docs reflecting the --retry and status updates. |
Comments suppressed due to low confidence (1)
citadel/ingest.py:3240
- The log hint for zero-page sources suggests
citadel ingest --forcewithout indicating it requires a path. Since--forceis explicitly refused without paths, this log entry is misleading for users following it later.
store.append_log(
f"ingested {key} but the session produced no wiki changes (0 pages); "
"retry with `citadel ingest --retry` or `citadel ingest --force`"
)
…le wiki, precise wording - retry_candidates() now treats the citing-pages traversal as best-effort (mirroring status): a wiki that cannot be parsed degrades to an empty uncited list instead of crashing the recovery command, so failed sources are still retried. - The zero-page warning/log wording now names the actual condition (no page created, updated, or deleted) and the log hint shows --force with the path it requires. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Cy9pETjieaS5z48Ku1tArB
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (2)
citadel/status.py:304
- The new
NO PAGES/uncitedmarker currently applies to every manifest entry, including sources that are also present in the failures catalog (notablyUNREADABLEsources, which ingest records in both places for stat caching). That can produce misleadingNO PAGESlabels for sources that were never ingested successfully; it also makes the status output inconsistent with the intent thatuncitedmeans “paid session succeeded but contributed zero entries”. Consider suppressing the marker for any key that has a non-duplicate, non-curate failures record.
except Exception: # noqa: BLE001 - status is read-only and must degrade, never raise
refs = None
if refs is not None:
for row in report.ingested:
row.uncited = not refs.get(row.key)
citadel/ingest.py:2582
retry_candidates()can classify a source as bothfailedanduncited(e.g., UNREADABLE sources are recorded in the failures catalog and are marked done in the manifest). That breaks the docstring claim that the lists are disjoint, and it can lead to duplicated paths incitadel ingest --retry(same key appearing in both buckets). Exclude keys already selected into thefailedbucket from theuncitedbucket.
uncited = [
key for key in sorted(manifest_dict) if not refs.get(key) and config.source_path_for_key(key).exists()
]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Two ways a source can silently contribute nothing to the wiki were too easy to miss:
citadel statusto notice — and retrying meant collecting paths for--forceby hand;status.What changed
Visibility
IngestReport.no_pages: a fresh source (plain ingest of a new file/repo — not a reconcile, not a delete cleanup, where an unchanged verdict is legitimate) that changes nothing now lands in aWARNING — ingested but produced NO wiki changessection of the run report, plus a permanentlog.mdline. The Errors section now also points at the retry lane.no changesverdict in yellow instead of dim.citadel statusmarks every ingested source that no wiki page cites withNO PAGES (nothing cites this source)(computed via the existingstore.citing_pages_map— the same verdict assources/index.md's "Referenced by" column, best-effort so a broken wiki degrades to no markers instead of a failed status).--jsoncarries it as"uncited". The table closes with a retry hint whenever anything is failed or uncited.Easy retry
citadel ingest --retry: computes its own bounded set — every failed source still on disk (deliberate same-basenameduplicateskips and page-keyedcuraterecords excluded) plus every ingested-but-uncited source as a forced reconcile — prints it, then runs it. Refuses explicit paths and--force(exit 2, keeping--force's no-accidental-corpus-wide-run discipline intact: the set is bounded and explicit either way), and exits 0 withNothing to retryon a healthy corpus.Tests
tests/test_retry.py(14 new tests, all offline on the sharedtmp_citadel/fake_agent/seed_pagefixtures): the no_pages flag on fresh ingests and its absence on reconciles, the report/log/render surfaces,retry_candidates()bucketing (vanished/duplicate/curate/cited exclusions), the CLI refusals, the spy-verified forced run over the computed set, an end-to-end retry that clears the stuck source, and the status markers/hints in table + JSON.Gates:
pytest(1301 passed, 1 skipped),ruff check,ruff format --check, and the beverages-workspacecitadel lintall green;citadel statuson the committed beverages showcase shows no falseNO PAGESmarkers. No rules-tree or prompt changes (the ingest diff is report bookkeeping only), so no verify-corpus run was needed. Docs updated (CLAUDE.md + regenerated copilot twin, CHANGELOG, maintenance + troubleshooting).🤖 Generated with Claude Code
https://claude.ai/code/session_01Cy9pETjieaS5z48Ku1tArB
Generated by Claude Code