release: 0.5.0 — release-prep audit, generated Copilot instructions, hermetic-auth hint - #130
Merged
Merged
Conversation
The release audit found `.github/copilot-instructions.md` 105 lines behind the file it claims to mirror: it still advertised 12 MCP tools (there are 13), and knew nothing of `citadel refresh`, `citadel capture`, `ingest --jobs`, `serve --http`, the ContextVar wiki accessors, or the HERMETIC/HTTP/JOBS knobs — i.e. the reviewer bot in the ship path was reviewing against a stale contract. A prose "keep the two in sync" promise had not held across four feature PRs, so make it mechanical instead: the Copilot file is now CLAUDE.md with a swapped header and re-rooted relative links, regenerated with CITADEL_WRITE_COPILOT_DOC=1 and pinned by a drift guard in tests/test_packaging.py (the same shape as the env-knob and README-link guards next to it). Also from the audit: README never mentioned `ingest --jobs` (a headline feature of the next release), the audit doc's § 1.4 coverage table was split in half by a resolution note inserted between its rows (so two rows rendered as literal text), its § 1.1 still called `refresh` unreleased, and its accepted-trade-off list still claimed no offline PDF reader after backlog #10 shipped one. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B2VCXZmg8g4nFucS5oZiz9
Bump `__version__` and date the CHANGELOG's [Unreleased] section as `## [0.5.0] - 2026-07-25`, with a fresh empty [Unreleased] above it and the compare-link row added. The release carries the whole 2026-07 audit backlog (#1-#15, all shipped): ranked BM25 search, per-session cost accounting, audio/PDF text pre-passes, MCP prompts + resources, the capture bridge, hermetic sessions, resumable chunked ingest, the serve-side page cache, bounded parallel ingest (`--jobs N`), the opt-in Streamable HTTP transport, and `citadel refresh` as the third lifecycle. The section had accumulated two separate `### Added` blocks around one `### Changed` (an artifact of prepending each feature); merged into one Added + one Changed so the released section reads as Keep a Changelog promises. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B2VCXZmg8g4nFucS5oZiz9
There was a problem hiding this comment.
Pull request overview
Prepares the 0.5.0 release by bumping the package version, finalizing the changelog/audit documentation, and making Copilot instructions mechanically derived from CLAUDE.md to prevent drift between the two agent-guidance documents.
Changes:
- Bump
cite-citadelto 0.5.0 and finalize the 0.5.0 changelog section + compare links. - Add a drift guard test to ensure
.github/copilot-instructions.mdis a deterministic derivation ofCLAUDE.md(with an opt-in regeneration path). - Update docs to reflect shipped features (
ingest --jobs,refresh, offline PDF text extraction note) and fix audit table rendering.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
tests/test_packaging.py |
Adds deterministic generator + test drift guard for Copilot instructions; supports opt-in regeneration via env var. |
README.md |
Documents citadel ingest --jobs N and its cross-linking tradeoff, linking to recipes. |
docs/audit-2026-07.md |
Marks refresh as released, fixes the “offline PDF reader” note, and repairs the coverage table formatting. |
CLAUDE.md |
Documents that .github/copilot-instructions.md is generated and how to regenerate it. |
citadel/__init__.py |
Bumps __version__ from 0.4.0 to 0.5.0. |
CHANGELOG.md |
Finalizes 0.5.0 section (date + structure) and updates compare links. |
.github/copilot-instructions.md |
Updates to the regenerated, current CLAUDE-derived guidance content and header. |
Hermetic sessions append claude's --bare, which skips the user's personal agent
configuration on purpose. On machines where that configuration is ALSO where the CLI keeps
its credentials — a managed container, a devcontainer, an apiKeyHelper in
~/.claude/settings.json — every session then fails on authentication while the same CLI
works perfectly by hand, and the backend blames the network for it ("Authentication error ·
This may be a temporary network issue, please try again"). Nothing in the error, the run
report, or the failures catalog mentioned the flag that caused it.
Auth-shaped failures now carry a one-line hint naming CITADEL_HERMETIC=0. The trigger is
read off the argv rather than the config, so the probe-gated case (an older binary that was
handed no flag) reads exactly as before, and a non-auth failure on a hermetic run — quota,
timeout, a broken page — is untouched. docs/troubleshooting.md gains the symptom with its
fix; the configuration.md row points at it.
Found while live-testing `ingest --jobs` for this release: all four sources failed
identically inside a container whose credentials live in user settings, and the message
pointed at the wrong layer entirely.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B2VCXZmg8g4nFucS5oZiz9
The ledger has stood empty since it was created on 2026-07-24. This release's live testing of `ingest --jobs` on haiku is its first real run, so record it per the file's own protocol: one run block (mode, model, rules_version, verdict) plus four VCB rows. The verdict is that parallel ingest passes on structure and provenance — a purpose-built overlap corpus forced 3 of 4 sources to race one page and the merge kept every source's facts, and an accidental mid-run kill left the wiki valid with 7 sources recorded and 7 back to pending. What the run actually surfaced is MODEL quality: haiku cites whole sections where sonnet cites lines, and 5 of those locators name headings the source does not contain (VCB-001, the sharpest gap and a rules-lane fix); one sentence went uncited past the agent's own check (VCB-002); 4 pages reach for the [^llmN] lane on a corpus fully covered by its sources (VCB-003). The cross-linking gap (VCB-004) stays deliberately unattributed — haiku and --jobs are both plausible causes and only a serial-haiku baseline separates them, so the row names that experiment instead of guessing. Recorded as a scoped parallelism+model test, not a full answer-key grade; the row says so, and a beverages ground-truth walk is still owed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B2VCXZmg8g4nFucS5oZiz9
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.
What
Release prep for 0.5.0 plus the pre-release audit of everything that landed since 0.4.0 (docs, skills, rules, the 15-item 2026-07 audit backlog), and one fix the audit's own live testing turned up.
The release itself
__version__0.4.0 → 0.5.0 (minor — features and a diagnostics fix, no breaking change).## [Unreleased]re-dated to## [0.5.0] - 2026-07-25, a fresh empty[Unreleased]above it, compare-link row added. The section had accumulated two### Addedblocks around one### Changed(an artifact of prepending each feature) — merged into one Added + one Changed, plus a new### Fixed. No wording changed. There was no flip-gate blockquote to carry.ingest --jobs N,serve --http, andcitadel refresh.What the audit found and this PR fixes
.github/copilot-instructions.mdwas 105 lines behind the file it claims to mirror: it still advertised 12 MCP tools (there are 13) and knew nothing ofcitadel refresh,citadel capture,ingest --jobs,serve --http, the ContextVar wiki accessors, or theHERMETIC/HTTP_*/JOBSknobs. The reviewer bot in this repo's own ship path was reviewing against a stale contract. A prose "keep the two in sync" promise had not survived four feature PRs — so it is now mechanical: the Copilot file is CLAUDE.md with a swapped header and re-rooted relative links, regenerated withCITADEL_WRITE_COPILOT_DOC=1and pinned by a drift guard next to the existing env-knob and README-link guards intests/test_packaging.py. This is the one refactor the audit judged worth doing now: it removes a 370-line hand-maintained duplicate rather than adding anything.ingest --jobs— a headline feature of this release. Added, with the default-1 cross-linking trade-off stated.docs/audit-2026-07.md: the § 1.4 coverage table was split by a resolution note inserted between its rows, so two rows rendered as literal text; § 1.1 still calledrefreshunreleased; the accepted-trade-off list still claimed "no offline PDF reader" after backlog Make abbreviations first-class: type, generated glossary, lint nudge #10 shipped one.llm.py: an auth-shaped session failure on a hermetic run now names the knob that caused it. Found by live-testingingest --jobsfor this release: inside a container whose claude credentials live in user-level settings, all four sources failed identically with "Authentication error · This may be a temporary network issue, please try again" — because hermetic mode's--bareskips exactly those settings. The message points at the network; nothing mentionedCITADEL_HERMETIC. Auth-shaped failures raised from a run that really passed an isolation flag (read off the argv, so the probe-gated "nothing was passed" case is untouched, and quota/timeout failures are unaffected) now carry a one-line hint namingCITADEL_HERMETIC=0, with a symptom→fix entry indocs/troubleshooting.mdand a pointer from theconfiguration.mdrow.Deliberately not in this PR (reported, not silently done): a
citadel/ingest.pysplit (3036 lines vs. 1075 for the next-largest module) — a wide mechanical move through the most load-bearing module days before a tag; it belongs in its own post-release PR routed through verify-corpus.Testing
Offline gates:
uv run pytest -q— 1221 passed, 1 skipped (1218 before; +1 drift guard, +2 hermetic-hint tests)uv run ruff check .—All checks passed!uv run ruff format --check .—93 files already formattedCITADEL_WORKSPACE=corpora/beverages uv run python -m citadel lint—OK; all nine committed showcase wikis lintOKuv run python -m citadel --version→citadel 0.5.0Live runs (real
claudesessions on haiku, in throwaway sandboxes — thellm.pydiff means the routing table asks for a corpus run, and parallel ingest wanted proving under real contention):--jobs 4. Forced the collision path hard: 3 of 4 sources raced and were re-run serially (4 parallel + 3 serial = 7 sessions, 277 s, $0.93). Result:checkandlintboth exit 0 with every counter zero, one merged page citing all four sources, and every source's unique facts intact across three consecutive re-runs (1.85 kg + 340 m tether; 210 m + 46 messages; 40-flight-hour interval + 70 kg retirement; 1874 design + 1881 handover). Nothing was overwritten by a later promote — "re-run serially, then merge into the winner's page" holds under a 3-way collision, not just a 2-way one. Cross-linking still produced separate linkedpersons/,organizations/, andmisc/pages with zero orphans.--jobs 4— all 14 first-pass sessions completed with zero races (the realistic case: topically related sources still mostly land on different pages), then the harness reaped the runner mid-re-run. That accident tested the all-or-nothing guarantee for free: the live wiki came outcheckexit 0, 0 broken links, 0 orphans, 0 fabricated sources, 7 sources durably recorded in the manifest and 7 cleanly back topending— nothing half-ingested, with the stale lock and staging dir left exactly as the next run's reclaim logic expects. A resumed run over only the un-promoted sources is what confirms the recovery path.One thing a human should weigh before tagging (needs live agent sessions): all nine showcase wikis are stamped
rules_version=ac0a1175f9dawhile the current rulebook isc99fc290864f— the audio + PDF work changedformats/pdf.md, addedformats/transcripts.md, and touchedcore.md/schema.mdafter the last corpus rebuild. CI flags this as advisory only.gazetteis the corpus whose ingest path materially changed (agent-native PDF read → pypdf text pre-pass), so averify-corpus gazetterun is the highest-value pre-tag check.🤖 Generated with Claude Code
https://claude.ai/code/session_01B2VCXZmg8g4nFucS5oZiz9