Skip to content

fix(project-brain): resolve bare wikilink targets to their real path - #206

Merged
jinyeow merged 5 commits into
mainfrom
fix/convert-to-okf-bare-wikilink-resolution
Aug 24, 2026
Merged

fix(project-brain): resolve bare wikilink targets to their real path#206
jinyeow merged 5 commits into
mainfrom
fix/convert-to-okf-bare-wikilink-resolution

Conversation

@jinyeow

@jinyeow jinyeow commented Aug 24, 2026

Copy link
Copy Markdown
Owner

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 .md file'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.ps1 pass; PSScriptAnalyzer clean.

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.
… 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.
…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.
…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.
@jinyeow
jinyeow merged commit 0dd4544 into main Aug 24, 2026
5 checks passed
@jinyeow
jinyeow deleted the fix/convert-to-okf-bare-wikilink-resolution branch August 24, 2026 20:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant