fix(project-brain): resolve bare wikilink targets to their real path - #206
Merged
Conversation
convert-to-okf.ps1 assumed a bare-filename [[wikilink]] target (no directory segment — Obsidian-style same-vault resolution) was already bundle-root-relative, emitting /basename.md regardless of where the file actually lives. Found while implementing #199 (batch B): its source used bare targets like [[T9a-ado-prerequisites]], which resolved to the wrong root-level path instead of /tickets/T9a-ado-prerequisites.md. Fixed by indexing every .md file's basename under the repo root once per invocation and resolving a bare target against it (same-directory preferred on an ambiguous match, else the lexicographically-first match with a warning); a target already containing a path segment, or with no on-disk match at all, keeps the prior literal behavior unchanged. 2 new tests; all 44 existing tests still pass.
This was referenced Aug 24, 2026
… targets too The prior fix on this branch only handled bare-filename (no '/') wikilink targets. Batch D+E (#201) hit the same root-cause bug for path-containing targets: a source [[adr/0001-x]] link is Obsidian-vault-relative to the initiative directory, not the whole bundle root, so treating it as bundle-root-relative literally produced a dead link whenever that literal path didn't exist. Now: trust the literal bundle-root-relative path only when it actually resolves on disk; otherwise fall back to the same basename lookup used for bare targets, keyed on the target's last path segment. 2 more tests (wrong-base path resolves correctly; already-correct literal path is left as-is). 46/46 tests pass; re-scanned the whole Hollard brain repo afterward — 0 new broken links, the 22 remaining are pre-existing (external local-only wiki + archived sibling initiatives), unchanged from before this fix.
Closed
6 tasks
…ink resolution
Where-Object unwraps a single-element result to a bare string instead of a
1-element array; indexing that string with [0] returned its first character
('i' from a path starting with 'initiatives/'), not the intended array
element, silently producing a broken one-character link target whenever
exactly one same-directory match existed among several same-basename
candidates. Wrap the pipeline result in @() and check .Count instead of
truthiness.
Closed
6 tasks
…king file's own directory tree Fable + Codex review-fix-loop (2 independent reviewers, both confirmed): 1. HIGH (Codex) / MEDIUM (Fable) — the path-containing fallback discarded a wikilink target's own directory segment before the basename lookup, so a target like [[adr/0001-x]] could silently resolve to a same-named file in an unrelated initiative when duplicate basenames exist across the brain repo. Fixed: walk up from the linking file's own directory (Obsidian same-vault semantics — relative to the initiative root, not the bundle root) trying '/.md' at each ancestor before ever widening to a basename-only search; the basename search itself now prefers a candidate whose path still preserves the target's own segment. 3 new tests (cross-initiative duplicate resolves correctly; bare-target ambiguity resolves via same-directory preference and via sorted-fallback — closing a real test gap in the prior commit's ambiguity branch too). 2. MEDIUM (both) — Get-WikilinkIndex silently swallowed enumeration errors (-ErrorAction SilentlyContinue), which could cache an incomplete index and degrade every subsequent lookup to a wrong or dead link with no signal why. Removed; a real traversal failure now terminates per the script's existing $ErrorActionPreference = 'Stop' convention instead of masquerading as 'no such file'. 49/49 tests pass, PSScriptAnalyzer clean. Already-migrated brain-repo content is unaffected (idempotent — the fix only changes behavior for literal [[wikilink]] syntax, and none remains in already-converted files).
…match Fable cycle-2 review caught: the wildcard-injection risk in Resolve-PathWikilinkTarget's suffix filter -- a raw wikilink target containing an unclosed '[' throws WildcardPatternException under -like (verified), aborting the whole conversion run; '*'/'?' in a target also carry unintended wildcard meaning. Ordinal EndsWith has neither problem. 49/49 tests still pass.
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.
Found while implementing #199 (Hollard OKF migration batch B):
convert-to-okf.ps1's wikilink rewrite assumed a bare-filename[[target]](no directory segment) was already bundle-root-relative, emitting a wrong root-level link instead of the target's real path. Confirmed the same defect in the already-merged batch A (#198) too — both fixed by a direct follow-up commit in the Hollard brain repo (out of scope for this repo).Fix: index every
.mdfile's basename under the repo root once per script invocation, resolve a bare wikilink target against it (same-directory preferred on ambiguity, else deterministic + warned). A target that already has a path segment, or has no on-disk match, keeps the prior behavior.2 new Pester tests (bare-target resolves to real location; falls back to old behavior when nothing matches). All 44 tests in
tests/convert-to-okf.Tests.ps1pass; PSScriptAnalyzer clean.