From f3ea1a3088efecbb8fe8cbea539709358932c38d Mon Sep 17 00:00:00 2001 From: nieao Date: Sat, 9 May 2026 15:38:13 +0800 Subject: [PATCH] fix(understand-knowledge): Windows path + zod schema null compatibility Four single-line fixes that make `/understand-knowledge` work end-to-end on Windows. ## Root causes 1. Path separator mismatch (3 occurrences in parse-knowledge-base.py) - `str(rel.with_suffix(""))` returns backslash-separated stems on Windows (e.g. `entities\foo`), while wikilinks always use forward slashes (`[[entities/foo]]`). - Result: name_map keys and article_ids hold `entities\foo`, while `resolve_wikilink()` looks up `entities/foo` -> 100% miss. - Tested on Windows 11 + Python 3.14: 151/151 wikilinks unresolved, 0 edges built from wikilinks. Fix: use `rel.with_suffix("").as_posix()` in all three places (lines 235, 316, 330 on main). 2. Null vs missing field (1 occurrence) - `"category": category or None` writes `null` when category is empty. - `KnowledgeMetaSchema.category` in packages/core/src/schema.ts is `z.string().optional()`, which accepts `undefined`/missing but rejects `null`. - Result: every article node fails GraphNodeSchema validation in the dashboard (`Invalid input: expected string, received null`), all nodes get dropped, dashboard renders empty. Fix: omit the field when empty using dict spread. ## After Tested with a 27-article Karpathy wiki on Windows: - before: 151 unresolved wikilinks, 0 edges, 0 nodes rendered in dashboard - after: 0 unresolved, 110 wikilink edges + 23 LLM-implicit edges, all 53 nodes (article/topic/entity/claim) render correctly No behavior change on macOS/Linux: `as_posix()` is a no-op when the OS already uses `/`, and dict spread produces the same key as the previous truthy branch. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../skills/understand-knowledge/parse-knowledge-base.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/understand-anything-plugin/skills/understand-knowledge/parse-knowledge-base.py b/understand-anything-plugin/skills/understand-knowledge/parse-knowledge-base.py index 45d95e4..d607051 100644 --- a/understand-anything-plugin/skills/understand-knowledge/parse-knowledge-base.py +++ b/understand-anything-plugin/skills/understand-knowledge/parse-knowledge-base.py @@ -232,7 +232,7 @@ def build_name_to_stem_map(wiki_root: Path) -> dict[str, str]: basename_counts: dict[str, int] = {} for md_file in wiki_root.rglob("*.md"): rel = md_file.relative_to(wiki_root) - stem = str(rel.with_suffix("")) # e.g., "decisions/decision-foo" + stem = rel.with_suffix("").as_posix() # e.g., "decisions/decision-foo" basename = md_file.stem # e.g., "decision-foo" # Full relative path always maps uniquely name_map[stem.lower()] = stem @@ -313,7 +313,7 @@ def parse_wiki(root: Path) -> dict: article_ids: set[str] = set() for md_file in sorted(wiki_root.rglob("*.md")): rel = md_file.relative_to(wiki_root) - stem = str(rel.with_suffix("")) + stem = rel.with_suffix("").as_posix() # Only filter infra files at root level (no parent directory) if rel.parent == Path(".") and rel.name.lower() in INFRA_FILES: continue @@ -327,7 +327,7 @@ def parse_wiki(root: Path) -> dict: for md_file in sorted(wiki_root.rglob("*.md")): rel = md_file.relative_to(wiki_root) - stem = str(rel.with_suffix("")) + stem = rel.with_suffix("").as_posix() basename = md_file.stem # Skip infrastructure files only at wiki root level @@ -381,7 +381,7 @@ def parse_wiki(root: Path) -> dict: "complexity": complexity, "knowledgeMeta": { "wikilinks": [wl["target"] for wl in wikilinks], - "category": category or None, + **({"category": category} if category else {}), "content": text[:3000], # First 3000 chars for LLM analysis }, })