Summary
lint walks .raw/ and indexes archived captures as link-resolution candidates. Obsidian does not — it ignores every dot-prefixed folder. Because a capture and its derived source page share a basename by design, this makes nearly every [[Source Title]] link in a healthy vault report as ambiguous-target.
On my vault the effect is 307 reported findings where 21 are real. The vault is fine; the signal is buried.
Root cause
claude_obsidian/lint_engine.py:56 skips a fixed set of directory names rather than applying Obsidian's dot-prefix rule:
_IGNORED_WALK_DIRS = {
".git",
".obsidian",
".vault-meta",
".cache",
".pytest_cache",
"__pycache__",
"node_modules",
}
.raw is absent, so _walk_files() descends into it. This is inconsistent with the package's own design intent, which treats the archive as hidden — skills/wiki-ingest/SKILL.md:9 describes "inbox/ as visible staging and .raw/ as the legacy immutable archive", and the dot prefix is what makes it invisible in the app.
Reproduction
Any vault where .raw/<Folder>/<Title>.md was ingested into wiki/sources/<Title>.md — i.e. the normal ingest outcome:
$ python scripts/claude-obsidian.py lint --vault "$VAULT" --format markdown
- Pages scanned: 153
- Links scanned: 2892
- Issues found: 307
## Ambiguous Targets
- `wiki/concepts/Agent Readability.md`:108 -> `Effective Harnesses for Long-Running Agents`;
candidates: `.raw/HarnessEngineering/Effective harnesses for long-running agents.md`,
`wiki/sources/Effective Harnesses for Long-Running Agents.md`
The second candidate is the only one Obsidian can actually resolve to.
Impact
Measured on a 153-page vault, comparing current main behaviour against the same vault with .raw added to the skip set:
| Category |
Reported |
Real |
Inflation |
ambiguous_targets |
253 |
6 |
247 |
stale_index_entries |
28 |
2 |
26 |
orphans |
14 |
1 |
13 |
dead_links |
9 |
9 |
— |
empty_sections |
3 |
3 |
— |
| Total |
307 |
21 |
286 |
The orphan inflation is a second-order effect worth calling out: a wiki/sources/* page is linked from wiki/index.md, but because that inbound link resolves ambiguously it isn't counted as inbound, so a correctly-linked page is reported as an orphan. The three categories fail together.
This also makes --strict unusable in automation, since a healthy vault never reaches zero findings.
Suggested fix
Add .raw to _IGNORED_WALK_DIRS. I ran this against the vault above: findings drop 307 → 21, and every remaining finding is genuine.
Two things I checked before proposing it:
- Provenance validation is unaffected.
_provenance_errors() (lint_engine.py:823, called at :1084) reads the ledger from the vault root directly rather than from _walk_files() output, so skipping the directory does not skip the manifest audit. provenance_errors stayed at 0 after the change.
pages_scanned and links_scanned are unchanged (153 / 2892), confirming .raw/ files were only ever acting as link targets, never counted as pages.
A more general fix would be to skip any dot-prefixed directory, matching Obsidian's actual rule and covering user-created hidden folders too. That is a wider behaviour change, so I've suggested the narrow version — happy to send a PR for whichever you prefer.
Environment
- claude-obsidian 2.1.0, engine 1.1.1, report schema 1
- Python 3.13.3, Windows 11
- Vault: 153 pages,
.raw/ archive of 24 markdown captures + 2 PDFs
Summary
lintwalks.raw/and indexes archived captures as link-resolution candidates. Obsidian does not — it ignores every dot-prefixed folder. Because a capture and its derived source page share a basename by design, this makes nearly every[[Source Title]]link in a healthy vault report asambiguous-target.On my vault the effect is 307 reported findings where 21 are real. The vault is fine; the signal is buried.
Root cause
claude_obsidian/lint_engine.py:56skips a fixed set of directory names rather than applying Obsidian's dot-prefix rule:.rawis absent, so_walk_files()descends into it. This is inconsistent with the package's own design intent, which treats the archive as hidden —skills/wiki-ingest/SKILL.md:9describes "inbox/as visible staging and.raw/as the legacy immutable archive", and the dot prefix is what makes it invisible in the app.Reproduction
Any vault where
.raw/<Folder>/<Title>.mdwas ingested intowiki/sources/<Title>.md— i.e. the normal ingest outcome:The second candidate is the only one Obsidian can actually resolve to.
Impact
Measured on a 153-page vault, comparing current
mainbehaviour against the same vault with.rawadded to the skip set:ambiguous_targetsstale_index_entriesorphansdead_linksempty_sectionsThe orphan inflation is a second-order effect worth calling out: a
wiki/sources/*page is linked fromwiki/index.md, but because that inbound link resolves ambiguously it isn't counted as inbound, so a correctly-linked page is reported as an orphan. The three categories fail together.This also makes
--strictunusable in automation, since a healthy vault never reaches zero findings.Suggested fix
Add
.rawto_IGNORED_WALK_DIRS. I ran this against the vault above: findings drop 307 → 21, and every remaining finding is genuine.Two things I checked before proposing it:
_provenance_errors()(lint_engine.py:823, called at:1084) reads the ledger from the vault root directly rather than from_walk_files()output, so skipping the directory does not skip the manifest audit.provenance_errorsstayed at 0 after the change.pages_scannedandlinks_scannedare unchanged (153 / 2892), confirming.raw/files were only ever acting as link targets, never counted as pages.A more general fix would be to skip any dot-prefixed directory, matching Obsidian's actual rule and covering user-created hidden folders too. That is a wider behaviour change, so I've suggested the narrow version — happy to send a PR for whichever you prefer.
Environment
.raw/archive of 24 markdown captures + 2 PDFs