Auto-fix removed extraneous f-string prefixes, unused imports
(re, sys, textwrap, defaultdict), an unused local (qids), and
converted datetime.now(timezone.utc) to datetime.now(UTC) (UP017).
Manual fixes split colon/semicolon one-liners onto separate lines
(E701/E702), renamed unused loop vars (cid, chain_id) with leading
underscores (B007), replaced bare except with except Exception (E722),
and renamed loop var L to level to satisfy N806.
vault-cli/scripts/normalize_chain_positions.py (NEW)
Phase-1 split kept only chain_ids[0] per question when legacy corpus
had multi-chain membership (up to 4 chains/question). Chains whose
members chose a different chain_ids[0] were left with position gaps.
Script walks vault/questions/, groups by chain_id, renumbers each
chain's members to contiguous [1..N] sorted by current position.
Idempotent. Rewrote 87 questions across 977 chains.
validator.py #18 (provenance-meta)
Tightened from 'any non-human provenance requires generation_meta'
to 'only llm-draft / llm-then-human-edited require it'. Imported
content legitimately has no LLM attribution and shouldn't carry
stub meta. Was incorrectly flagging 9,199 imported questions.
Re-ran vault build → new release_hash (input changed, which is
correct): fe69d4c4d3c2884efeab6189a67e929e4e970dc0f4de42ab9493531a4cabeda1.
Republished 0.9.0 release artifact. corpus-equivalence-hash.txt updated.
paper/macros.tex + corpus_stats.json regenerated (same counts:
9199/87/964 chains/31.9% coverage).
State: vault check --strict 100% clean on full 9,657-question corpus;
zero load errors; zero invariant failures. 28/28 pytest green.
vault verify 0.9.0 round-trips from YAML source. Citation property
holds on the new hash.