The daily-update skill's Setup Mode references scripts/daily-update.sh,
the launchd plist, and wiki-notify.sh via $OBSIDIAN_WIKI_REPO/scripts/ —
same class of gap as #143 (the Stop hook), which didn't cover this
directory. force-include scripts/ under obsidian_wiki/_data/scripts/ so
pip/uv installs actually have it, and exclude __pycache__/*.pyc from the
wheel target since force-include copies straight from the working tree
(a local scripts/__pycache__ was otherwise leaking stray .pyc files in).
setup.sh had an interactive git-sync flow (git init, .gitignore, remote,
a generated sync.sh, optional cron) that `obsidian-wiki setup` — the entry
point pip/uv installs actually run — never had, so those installs silently
skipped it and .env.example documented a var nothing ever read.
Move the git plumbing into obsidian_wiki/sync.py (configure_sync/run_sync/
get_remote) and expose it as `obsidian-wiki sync-setup <url>` and
`obsidian-wiki sync`. `obsidian-wiki setup` now offers the same interactive
prompt setup.sh always had (or accepts --remote non-interactively), and
setup.sh's own prompt delegates into the same module via the CLI instead of
re-implementing it. Sync status is read live from the vault's own `git
remote` (via `obsidian-wiki info`) rather than a config var that could drift.
wiki-ingest previously walked repo folders with a hardcoded SKIP_DIRS
list, so custom-ignored paths (anything not literally node_modules/
.venv/dist/...) leaked into the ingest plan. batch-plan now detects a
`.git` folder and enumerates files via `git ls-files`, so a repo's own
.gitignore decides what's skipped. Docs updated to spell out the
"clone locally, add to OBSIDIAN_SOURCES_DIR" workflow for public/private
repos on any host.
Legacy vaults fail-closed on every pre-existing page after the trust-ledger
rollout: missing base_confidence/lifecycle and a missing ledger became
mandatory failures with no opt-out, skipping the staged migration from #28.
Split confidence/lifecycle out of the core required-frontmatter check into a
separate confidence_missing_fields finding, and add a strict_trust switch to
lint_vault (default off): missing trust fields, ledger errors, stale reviews,
and score mismatches now warn by default and only fail once a vault owner
opts in via --strict-trust or OBSIDIAN_TRUST_STRICT=1. Ledger presence alone
no longer silently enables strict mode.
cache-check / cache-update assumed a dict-keyed manifest with bare hex
hashes, but the wiki-ingest skill writes 'sources' as a list of entry
objects with sha256:-prefixed content_hash and vault-relative paths.
Against a skill-built manifest the commands crashed, then (once past the
crash) reported every file as modified/new. Both shapes occur in real
vaults.
- _load/_iter: read both dict- and list-shaped 'sources' transparently
- compare content_hash prefix-insensitively (strip optional algo: prefix)
- match paths across raw / absolute / vault-relative forms
- update_source edits in place, preserving manifest shape, duplicate-path
entries, skill-written fields, and top-level keys (stats/version)
- don't flag URL / pseudo-path source keys as missing-on-disk
- add regression tests covering all four failure modes from the issue
The wiki-setup skill wires a Claude Code Stop hook pointing at
<REPO_PATH>/.claude/hooks/wiki-stop-capture.sh, but /.claude is pruned
from the sdist, so the script was missing from the published wheel and
the installed hook dangled at a nonexistent path.
- force-include the hook into the wheel under _data/hooks/ (and re-add it
to the sdist, which the wheel is built from)
- wiki-setup now resolves the packaged hooks/ path, falls back to the
source-checkout .claude/hooks/ path, then to a GitHub raw fetch
- wiki-setup now creates .manifest.json so 'obsidian-wiki doctor' passes
its required-core-files check (secondary note in the issue)
- add regression tests pinning the packaging config and skill wiring
The readme-sync workflow no longer fails when README.md and
README_TW.md don't change together. Instead, check_readme_sync.py
reports drift: it lists commits that touched README.md after the
last README_TW.md update, plus the pending English diff to translate
and backfill. CI surfaces drift as a warning annotation only.
Claude-Session: https://claude.ai/code/session_01PG9Pz6tcUfgpTHLjq3BT5Q
PR #129 added a "prefer ripgrep" policy to llm-wiki/SKILL.md, but six
other skills still hardcoded literal grep commands in their examples,
bypassing that policy. Swap them to rg with correct flag mapping
(--include="*.md" -> --glob '*.md'; -r dropped since rg recurses by
default).
check_sources only matched a manifest entry by the passed key or its
abspath, and detected missing files with Path(key).exists() against CWD.
When the manifest stored vault-relative keys (e.g. _raw/articles/foo.md),
callers passing an absolute path got 'new' instead of 'unchanged', and
every relative key was reported 'missing' when CWD != vault root — so the
docstring's documented abs-or-rel key support was unusable.
Also try the vault-relative form on lookup, and resolve relative manifest
keys against the vault root when checking existence. Add relative-key
tests to close the CI gap (every prior test used absolute keys).
Fixes#136
The Step 7 example wrote ingested_at/pages_created/pages_updated, but
cache.py reads and writes content_hash/last_ingested/pages_produced. An
agent following the docs wrote fields cache-check never reads, so every
source looked new on the next run and incremental-skip detection died.
Rename the fields to match the code, drop the never-persisted
size_bytes/modified_at/pages_updated, and note that source_type/project
are advisory only.
Fixes#135
Validate updated timestamps before fingerprint comparison, support dash-only relationship list items, and reject ambiguous normalized qualified targets and unsupported relationship fields.
Reject symlinked writes and malformed or ambiguous trust data, enforce complete page coverage and strict timestamps/lifecycle values, and parse typed relationships independent of key order.
wiki-narrate saves derived readouts to _readouts/, but cross-linker,
wiki-export, and wiki-lint scanned every .md in the vault and would
have linked, exported, or lint-flagged them. Exclude the directory in
all three and document it in the AGENTS.md vault structure.
The staleness order still let --since suppress a missing output: a source
older than the cutoff with no output file was skipped, leaving the very bug
this branch fixes (never-extracted older sessions) partly alive and
contradicting needs_extraction's own docstring.
Move the "output missing -> extract" check ahead of the --since gate, so a
missing output is always (re)extracted and --since only narrows files that
already have an output. Add a regression test: missing output + future
--since still extracts.
AI-Agent: Claude Code
AI-Session-IDs: session_01PPCNXmXUvemmyvWxrJ3D5E
cmd_normalize warned on relative keys but still ran them through
canonical() (os.path.abspath), which resolves them against the CWD and
rewrote e.g. "-Users-x-github/abc.jsonl" to "/cwd/-Users-x-github/abc.jsonl",
counted it as re-keyed, and persisted the corruption on a non-dry-run write.
Preserve relative keys untouched (ckey = key), skip the re-key count for
them, and only canonicalize absolute keys. normalize's dedup/canonicalize
purpose applies to absolute keys only; for relative keys it is now a safe
no-op. Collision merge logic is unchanged.
AI-Agent: Claude Code
AI-Session-IDs: session_01PPCNXmXUvemmyvWxrJ3D5E
extract-jsonl.py decided re-extraction from the .extract-manifest.json
extracted_at wallclock. A session whose output was missing or that landed
after the first run was treated as already-done and never re-extracted, so
downstream ingest saw it as missing until a manual rerun (e.g. session
2d83ed0d). --since additionally acted as a hard gate that hid never-extracted
older sessions.
Make needs_extraction idempotent on the output file: (re)extract when the
output is missing or the source is newer than it; --since only narrows the
scan range. The manifest (read by nothing else) is removed. Add
test_extract_staleness.py covering missing/newer/older cases.
AI-Agent: Claude Code
AI-Session-IDs: session_01PPCNXmXUvemmyvWxrJ3D5E
cmd_delta keyed every manifest source through canonical() (os.path.abspath),
which resolves relative keys (e.g. "-Users-x-github/abc.jsonl" under
~/.claude/projects/) against the CWD. Real vaults store most keys relatively,
so every already-ingested file was misreported as NEW (446 vs 384 known on the
live vault).
Add a basename-indexed suffix fallback: when a scanned absolute path has no
direct canonical match, look it up by basename and confirm the relative key is
a path suffix (O(1) avg, no O(n^2) scan). Absolute keys keep their exact prior
behavior. cmd_normalize is unchanged but now warns on relative keys, which it
would otherwise rewrite to CWD-based garbage.
AI-Agent: Claude Code
AI-Session-IDs: session_01PPCNXmXUvemmyvWxrJ3D5E