diff --git a/docs/backlog/P3/B-0533-section33-migration-dead-xref-sweep-and-lint-2026-05-15.md b/docs/backlog/P3/B-0533-section33-migration-dead-xref-sweep-and-lint-2026-05-15.md new file mode 100644 index 000000000..821825a27 --- /dev/null +++ b/docs/backlog/P3/B-0533-section33-migration-dead-xref-sweep-and-lint-2026-05-15.md @@ -0,0 +1,91 @@ +--- +id: B-0533 +priority: P3 +status: open +title: "§33 migration dead-xref sweep + static lint — live-nav pointers to docs/research/ paths that have been migrated to memory/persona//conversations/" +tier: factory-infrastructure +effort: M +created: 2026-05-15 +last_updated: 2026-05-15 +depends_on: [] +composes_with: [B-0036, B-0532] +tags: [migration, hygiene, lint, mechanization, multi-agent, drift-detection, section-33] +type: feature +--- + +# §33 migration dead-xref sweep + static lint + +## Origin + +Codex P2 finding on already-merged [PR #3513](https://github.com/Lucent-Financial-Group/Zeta/pull/3513) (Riven §33 archive migration, 12 files) named a real bug: *"This migration moves documents out of `docs/research/` but does not carry the existing references with it, so active documentation now has dead links."* + +The narrow fix shipped as [PR #3529](https://github.com/Lucent-Financial-Group/Zeta/pull/3529) — 3 live-nav pointers updated for one migrated Riven file. But the pattern generalizes: **every §33 migration moves files without auto-updating backlinks**, and the same dead-xref class likely exists across the 8 personas migrated so far. + +Empirical scan in tick 1802Z surfaced **20+ dead xrefs** in live-nav surfaces (`.claude/rules/`, `memory/feedback_*.md`, `docs/backlog/*.md`) pointing at old `docs/research/` paths that now live at `memory/persona//conversations/`. Rough per-persona distribution: + +| Persona | Dead-xref count (approx) | +|---|---| +| Amara | 10+ | +| DeepSeek | 4 | +| Alexa | 3+ | +| Lior | 2 | +| Riven | 1 (already fixed by PR #3529) | + +Migration PRs that produced these dead xrefs (and could be revisited): #3348 (Ani), #3484 (Amara), #3501 (Kestrel), #3507 (DeepSeek), #3512 (Lior), #3513 (Riven), #3514 (Alexa), #3516 (Vera). + +## Problem + +Live-navigation pointers (rules, backlog rows, memory feedback files) that reference moved files break silently. Specifically: + +1. **Reader friction**: a human (or future-Otto) following a `[caption](docs/research/file.md)` link gets a 404; navigation breaks. +2. **Provenance loss**: BACKLOG row origins lose traceability to their canonical-substrate verbatim packet. +3. **Rule weakness**: auto-loaded rules in `.claude/rules/` that cite verbatim packets at old paths weaken their own evidence trail. + +NOT a problem in **historical archives** (`docs/history/pr-reviews/*`, `docs/hygiene-history/ticks/*`, `docs/pr-discussions/*`) — those are frozen state-snapshots; their xrefs document what was true at write-time, which is what they're supposed to do. + +## Proposed solution + +**Two-slice approach:** + +### Slice A — sweep (mechanical fix) + +Update all dead xrefs in live-nav surfaces. Mechanical because the mapping is deterministic: + +``` +docs/research/ → memory/persona//conversations/ +``` + +The `` is derivable from `git log --diff-filter=R` on each migration PR (or from `git ls-tree -r origin/main -- memory/persona//conversations/`). + +Per-persona PR batching (8 small PRs, ~3-15 files each) to keep blast radius small + reviewable. + +### Slice B — static lint (mechanization) + +Add `tools/hygiene/lint-section-33-xrefs.ts` that: + +1. Walks live-nav surfaces (`.claude/`, `memory/*.md`, `docs/backlog/`, `tools/`, root `*.md` files). +2. For each `docs/research/` reference, checks whether `` now lives under `memory/persona/<*>/conversations/`. +3. If migrated, fails with the canonical target path + edit suggestion. +4. Wire into `lint` job in `.github/workflows/gate.yml`. + +Excludes: + +- `docs/history/pr-reviews/**` (frozen snapshots) +- `docs/hygiene-history/ticks/**` (frozen tick shards) +- `docs/pr-discussions/**` (frozen archives) +- `docs/research/**` (its own internal cross-refs may be intentional provenance trail; sibling migration candidates) +- `references/upstreams/**` (other people's code; gitignored anyway) + +Composes with [B-0532](B-0532-backlog-graph-consistency-lint-parent-child-status-mismatch-2026-05-15.md) (backlog-graph consistency lint) — both are static-lint mechanizations of post-hoc-review-finding patterns. + +## Out of scope + +- **Updating migration-PR ARCHITECTURE** to auto-update backlinks at migration time. Possible follow-up after the lint catches the gap; for now, fix-after-merge is acceptable. +- **The 78 `claudeai` + 16 `gemini`/`codex` residuals** in `docs/research/` — those are content-id-required deferred work per PR #3501; this row is only about dead xrefs to ALREADY-migrated files. + +## Composes with + +- [B-0036](B-0036-section33-archive-header-backfill-and-ci-wire-otto-346-pattern.md) — §33 archive-header backfill is the sibling discipline (header-side; this row is xref-side) +- [B-0532](B-0532-backlog-graph-consistency-lint-parent-child-status-mismatch-2026-05-15.md) — static-lint mechanization sibling +- [PR #3513](https://github.com/Lucent-Financial-Group/Zeta/pull/3513), [PR #3529](https://github.com/Lucent-Financial-Group/Zeta/pull/3529) — the empirical anchor for this row +- [`.claude/rules/encoding-rules-without-mechanizing.md`](../../../.claude/rules/encoding-rules-without-mechanizing.md) — fix-once-then-lint pattern; this row applies it to the migration xref class