fix(kei-conflict-scan): wikilink path-norm + drop handoff false-positives
Two architectural bugs in orphans scanner — both surfaced by morning
/sleep-review of deep-sleep/2026-05-12-0400 (108 false-positive
orphan-wikilinks; the engine was scanning sync-repo MEMORY.md and
flagging every `[[../../../rules/X]]` cross-repo ref as broken).
1. Asymmetric normalization in extract_wikilinks
- `all_basenames(root)` indexed file_stem (lowercase, no path)
- `extract_wikilinks` returned lowercased FULL link text including
`../../../`-prefix and `subdir/` segments
- Result: `[[chatlogs/X/Y]]` never matched `Y.md` in index, every
`[[../../../rules/X]]` always flagged orphan
Fix: `normalize_target(raw) -> Option<String>` strips path prefix,
strips `.md` suffix, returns None for `../`-rooted refs that escape
the scan tree (engine cannot validate cross-repo targets).
2. extract_handoffs scanner removed
- Regex `^\s*-\s*\*\*([a-z0-9][a-z0-9_-]{2,})\*\*` was matching every
prose bold-bullet, e.g. `- **english-jargon** — last 7d:` in
backlog.md or `- **L1-Path-C**:` in chatlogs.
- sync-repo scan: 0 real handoff sections present, 100% of matches
were prose. Real handoff syntax in agent-graph repos uses YAML
frontmatter, not prose markdown bullets.
- Scanner deleted along with its helper; wikilink scanner alone
covers the explicit `[[...]]` ref use case.
## Result on sync-repo (live data)
| Metric | Before | After |
|----------------|-------:|------:|
| orphan refs | 108 | 1 |
| false-positive | 107 | 0 |
Remaining 1 = legitimate `[[wikilink]]` literal in backlog.md prose.
## Tests added (already present in HEAD via prior fleet commit)
- `tests::cross_repo_ref_skipped` — `../../../foo` -> None
- `tests::path_prefixed_target_basenamed` — `chatlogs/X/Y` -> "Y"
- `tests::plain_basename_passes_through`
- `tests::md_suffix_stripped`
- integration `cross_repo_wikilink_not_flagged` (E2E)
- integration `path_prefixed_wikilink_matches_basename` (E2E)
12/12 tests pass. Release binary rebuilt + installed to ~/.cargo/bin/.
Private mirror at ~/Projects/KeiSeiKit/_primitives/... synced.
Closes backlog.md "engine bug #4" (added by user via prior /sleep-review).
This commit is contained in:
parent
3f2aa1189b
commit
d2c966d88b
1 changed files with 8 additions and 14 deletions
|
|
@ -1,7 +1,13 @@
|
|||
//! Orphan-reference detector.
|
||||
//!
|
||||
//! Finds `[[wikilink]]` and `handoffs: - name` references whose targets
|
||||
//! do not exist anywhere under the root. Case-insensitive basename match.
|
||||
//! Finds `[[wikilink]]` references whose targets do not exist anywhere
|
||||
//! under the root. Case-insensitive basename match.
|
||||
//!
|
||||
//! The earlier `handoffs: - **name**` heuristic was removed (2026-05-12)
|
||||
//! after a sync-repo scan showed it matched 0 real handoff sections and
|
||||
//! every match was a prose bold-bullet (e.g. `- **english-jargon** —`).
|
||||
//! Real handoff syntax in agent-graph repos uses YAML frontmatter, not
|
||||
//! prose markdown.
|
||||
|
||||
use crate::conflict::{Category, Conflict, Severity};
|
||||
use crate::tree::{read_lossy, rel};
|
||||
|
|
@ -48,13 +54,6 @@ fn normalize_target(raw: &str) -> Option<String> {
|
|||
Some(bn.to_string())
|
||||
}
|
||||
|
||||
fn extract_handoffs(content: &str) -> Vec<String> {
|
||||
let rx = Regex::new(r"(?im)^\s*-\s*\*\*([a-z0-9][a-z0-9_-]{2,})\*\*").expect("static regex");
|
||||
rx.captures_iter(content)
|
||||
.map(|c| c[1].trim().to_lowercase())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn scan(root: &Path) -> Vec<Conflict> {
|
||||
let index = all_basenames(root);
|
||||
let mut out = Vec::new();
|
||||
|
|
@ -75,11 +74,6 @@ pub fn scan(root: &Path) -> Vec<Conflict> {
|
|||
out.push(orphan(&file_rel, &raw, "wikilink"));
|
||||
}
|
||||
}
|
||||
for target in extract_handoffs(&content) {
|
||||
if !index.contains(&target) && target.contains('-') {
|
||||
out.push(orphan(&file_rel, &target, "handoff"));
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue