All notable changes to this project are documented in this file.
Format: Keep a Changelog + Semantic Versioning.
mcp/browse.py: simplifiedexcept (OSError, PermissionError)→except OSErrorinkb_read_pageandkb_list_sources(PermissionErroris a subclass ofOSError)compile/compiler.pyload_manifest: returns{}onjson.JSONDecodeErrororUnicodeDecodeErrorinstead of propagating — corrupt.data/hashes.jsonno longer crashes compile, detect-drift, or find-changed-sourcesingest/pipeline.py_update_existing_page: entity context insertion usesre.search(r"^## References", …, re.MULTILINE)and positional splice instead ofstr.replace— prevents double-injection when LLM-extracted context itself contains## Referenceslint/runner.pyrun_all_checks: filter key corrected"dead_links"→"dead_link"to match actual issue dict key —--fixnow properly removes resolved dead-link issues from the reportlint/verdicts.pyload_verdicts: addedisinstance(data, list)guard after JSON parse — a{}verdict file no longer causesAttributeErrorinadd_verdictlint/semantic.pybuild_fidelity_context/build_completeness_context: source content now truncated atQUERY_CONTEXT_MAX_CHARS(80K) — large books and arXiv PDFs no longer overflow the LLM context windowlint/semantic.py_group_by_wikilinks: changedseen.update(group)→seen.add(node)+ added frozenset dedup pass — pages in link chains (A→B→C) were consumed by A's group and skipped; B and C now form their own consistency groups; dedup pass prevents the same group being emitted by multiple nodes in the same componentlint/semantic.py: split long import line and renamedlloop variable toline— ruff E501/E741 cleanlint/semantic.py_group_by_term_overlap/build_consistency_context: wrappedread_text()calls intry/except (OSError, UnicodeDecodeError)— unreadable pages no longer abort the full consistency checkmcp/core.pykb_query: API branch wrapsquery_wiki()intry/except—LLMError/timeout no longer escapes raw to MCP clientmcp/core.pykb_ingest_content: extraction JSON validated before writing the raw file — validation failure no longer leaves an orphaned file on diskmcp/core.pykb_save_source: addedoverwriteparameter (defaultfalse) with file-existence guard;write_textwrapped intry/except OSError— silently overwriting existing sources and unhandled OS errors both fixedmcp/quality.pykb_refine_page/kb_affected_pages/kb_save_lint_verdict/kb_lint_consistency: added_validate_page_id()guards — these write-capable tools were the only MCP tools missing traversal protectionmcp/quality.pykb_create_page:source_refsvalidated against path traversal before being written to frontmatter —../../etc/passwdas a source ref is now rejectedreview/context.pypair_page_with_sources: addedpage_path.resolve().relative_to(wiki_dir.resolve())guard andtry/except (OSError, UnicodeDecodeError)around source reads — path traversal and unreadable binary sources both handledreview/refiner.pyrefine_page: added path traversal guard; normalized CRLF→LF before frontmatter parsing (Windows fix); swapped write order so audit history is persisted before page file — a crash between the two now leaves the refinement detectable and retryableutils/llm.pycall_llm_json: validatesblock.name == tool_namebefore returning — wrong-tool responses from the API no longer silently corrupt callerstests/test_v098_fixes.pyTestCallLlmJson/TestMakeApiCall: addedtool_block.name = "extract"(or"my_tool") to 5 mocktool_useblocks — tests were failing becausecall_llm_jsonnow validatesblock.namebut mocks lacked the attributetests/test_v098_fixes.pyTestKbIngestPathTraversal::test_allows_valid_raw_path: fixed weakor-based assertion toassert "project directory" not in result.lower()— previous logic could silently pass on non-traversal errorsutils/markdown.pyextract_raw_refs: filters out matches containing..— consistent path-traversal protection matchingextract_citations()utils/wiki_log.pyappend_wiki_log: replacedexists()+write_text()withopen("x")+FileExistsErrorguard — concurrent MCP calls can no longer race on initial log creationevolve/analyzer.pyfind_connection_opportunities: strips YAML frontmatter before tokenizing — structural keys (title,type,source) were producing false-positive link suggestionsfeedback/store.pyadd_feedback_entry:page_scoresdict now capped atMAX_FEEDBACK_ENTRIES(10k) — previously onlyentrieslist was capped; high-churn wikis could growpage_scoreswithout boundutils/llm.py: retry loop usedrange(MAX_RETRIES)(total attempts) instead ofrange(MAX_RETRIES + 1)(retries);last_errorwasNonewhenMAX_RETRIES=0, causingAttributeErroron exhaustionquery/engine.py:query_wikinever forwardedmax_resultstosearch_pages;_build_query_contextreturned empty string when all pages exceeded 80K char limit;search_pagesmax_resultsnot clamped inside the functioningest/pipeline.py: summary page always overwritten on re-ingest, losing originalcreated:date;ingest_sourcehad no library-level path traversal guard;_update_sources_mappingand_update_index_batchsilently no-op on fresh installcompile/linker.py:inject_wikilinkscompared rawtarget_page_id(and self-skip pid) against lowercased existing links — mixed-case page IDs caused duplicate wikilinkscompile/compiler.py:find_changed_sourceswrote template hashes to manifest as side-effect even when called read-only fromkb_detect_drift; addedsave_hashes=Falseparameterlint/checks.py:check_stalenesssilently skipped pages where YAML-parsedupdatedwas a quoted string; orphan/isolated detection did not exemptcomparisons/andsynthesis/;check_source_coveragesuffix match false-positived on same-named files in different subdirslint/verdicts.py:load_verdictssilently discarded all verdict history onJSONDecodeErrorwith no warninggraph/export.py:_sanitize_labeldid not strip newlines or backticks from Mermaid node labelsevolve/analyzer.py: bareexcept Exception: passon feedback lookup swallowed real bugsconfig.py: env override model IDs accepted empty strings;MAX_FEEDBACK_ENTRIESandMAX_VERDICTSmoved from module constants tokb.configfeedback/reliability.py: docstring said "below threshold" but code used<=(at or below)cli.py:mcpcommand had notry/except;--typechoices missingcomparisonandsynthesismcp/browse.py:kb_searchandkb_list_pagesmissing outertry/exceptmcp/app.py:_format_ingest_resultdead legacy dict-branch foraffected_pagesremoved
graph/builder.pygraph_stats:betweenness_centralityusesk=min(500, n_nodes)sampling approximation for graphs > 500 nodes — prevents O(V·E) stall inkb_evolveon large wikisutils/pages.py: inlined_page_idhelper to break circular import dependency onkb.graph.builder(which pullsnetworkxinto every page-load operation)
scripts/hook_review.py: deleted — standalone Anthropic-API commit-gate script removed; theclaude -pskill gate in hooks covers this use casedocs/superpowers/specs/2026-04-06-phase2-multi-loop-quality-design.md: deleted obsolete Phase 2 design spec (fully implemented as of v0.6.0)docs/superpowers/plans/2026-04-06-phase2-multi-loop-quality.md: deleted obsolete Phase 2 implementation plan (fully shipped)docs/superpowers/plans/2026-04-07-v092-audit-fixes.md: deleted obsolete v0.9.2 audit-fixes plan (all tasks completed, status was COMPLETE)docs/superpowers/plans/2026-04-07-v093-remaining-fixes.md: deleted obsolete v0.9.3 remaining-fixes plan (all tasks completed, status was COMPLETE)
9-item backlog hardening. All Phase 3.92 known issues resolved. Ruff clean.
config.py:MAX_REVIEW_HISTORY_ENTRIES = 10_000andVERDICT_TREND_THRESHOLD = 0.1constants
compile/linker.py:inject_wikilinksuses smart lookahead/lookbehind for titles starting/ending with non-word chars (C++,.NET,GPT-4o)compile/compiler.py:compile_wikinow propagatespages_skipped,wikilinks_injected,affected_pages,duplicatesfrom ingest result;kb_compileMCP output shows these fieldslint/checks.py:check_stalenessnarrowsexcept Exceptionto specific types;check_source_coveragemerged into single-pass loop (reads each file once viafrontmatter.loads())lint/trends.py: hardcoded0.1trend threshold replaced withVERDICT_TREND_THRESHOLDconfig constantutils/wiki_log.py:stat()result cached — called once instead of twiceREADME.md,others/architecture-diagram.html: corrected "26 tools" to "25 tools"
review/refiner.py: review history now capped atMAX_REVIEW_HISTORY_ENTRIES(same pattern as feedback/verdict stores)mcp/browse.py:kb_read_pageandkb_list_sourceswrap I/O intry/except OSError— raw exceptions no longer escape to MCP clientlint/checks.py:fix_dead_linksonly appends audit trail entry whenre.subactually changed content (eliminates phantom entries)evolve/analyzer.py: added module-level logger;find_connection_opportunitiesandsuggest_new_pagesguardread_text()withtry/except (OSError, UnicodeDecodeError)
- 583 tests (+9), 25 MCP tools, 12 modules
5-agent parallel code review fix list.
ingest/pipeline.py:inject_wikilinksfrontmatter split uses regex (_FRONTMATTER_RE)compile/linker.py:resolve_wikilinks/build_backlinkswrapread_text()intry/except (OSError, UnicodeDecodeError)ingest/extractors.py:KNOWN_LIST_FIELDSextended withkey_arguments,quotes,themes,open_questionsgraph/export.py: Mermaid_safe_node_idtracks seen IDs with suffix deduplicationlint/runner.py:run_all_checkswithfix=Trueremoves fixed issues from reportmcp/core.py:kb_ingestwrapped intry/except;kb_create_pageuses_validate_page_id(check_exists=False)mcp/core.py: URL inkb_ingest_content/kb_save_sourcewrapped inyaml_escape()config.py:VERDICTS_PATHand LLM retry constants moved from inline definitionsevolve/analyzer.py:detect_source_driftbareexceptnarrowedingest/extractors.py:extract_raw_refsextended to.csv/.png/.jpg/.jpeg/.svg/.gif
compile/compiler.py:save_manifestnow usesatomic_json_write
- 574 tests, 25 MCP tools, 12 modules
Infrastructure for content growth and AI leverage.
config.py: environment-configurable model tiers (CLAUDE_SCAN_MODEL,CLAUDE_WRITE_MODEL,CLAUDE_ORCHESTRATE_MODELenv vars)search.py: PageRank-blended search ranking (final_score = bm25 * (1 + PAGERANK_SEARCH_WEIGHT * pagerank))ingest/pipeline.py: hash-based duplicate detection (checks compile manifest for existing sources with identical content hash)kb.lint.trends: new module —kb_verdict_trendsMCP tool (weekly pass/fail/warning rates, quality trend direction)kb.graph.export: new module —kb_graph_vizMCP tool (Mermaid flowchart with auto-pruning, subgraph grouping)compile/linker.py:inject_wikilinks()for retroactive inbound wikilink injectioningest/pipeline.py: content-length-aware tiering (SMALL_SOURCE_THRESHOLD=1000)ingest/pipeline.py: cascade update detection (affected_pagesreturn key)
- (post-review round 1)
inject_wikilinksintegrated intoingest_source()with lazy import;_format_ingest_resultshows duplicate detection - (post-review round 2)
_update_existing_pageacceptsverbparameter — concept pages write "Discussed in" correctly;_process_item_batchderivessubdirfrom_SUBDIR_MAP - (post-review round 3)
_format_ingest_result:affected_pagesflat list handling;wikilinks_injectedkey now read by formatter
- 574 tests (+56), 25 MCP tools (+2), 12 modules (+2)
Deep audit fixes and structured outputs.
utils/llm.py:call_llm_json()— structured output via Claude tool_use (forced tool choice guarantees valid JSON)ingest/extractors.py:build_extraction_schema()+_parse_field_spec()for template-to-JSON-Schema conversionutils/llm.py:_make_api_call()shared retry helper (extracted fromcall_llm, used by both text and JSON calls)utils/io.py:atomic_json_write()utility (consolidated 3 identical atomic write implementations)utils/llm.py:_resolve_model()helper (deduplicated tier validation)
ingest/extractors.py:load_template()is now LRU-cached; precompiled regex in_parse_field_spec()utils/llm.py: removed dead 429 fromAPIStatusErrorretry codes (handled byRateLimitErrorcatch)
mcp/core.py:kb_ingestpath traversal protection (validates resolved path withinPROJECT_ROOT)feedback.py:cited_pagesdeduplicated before trust scoring (prevents inflated trust)review/refiner.py: atomic writes for review history (tempfile+rename)
- 518 tests (+29), 23 MCP tools, 10 modules
Tier-3 fixes and observability.
query.py: search logs debug when falling back to raw terms (all stopwords filtered)mcp/health.py:kb_affected_pagesusesdebuginstead ofwarningfor expected shared-sources failureutils/llm.py:LLMErrormessages distinguish error types (timeout, rate limit, connection, server error with status code)
- 490 tests (+7), 23 MCP tools, 10 modules
Tier-2 audit hardening.
query.py: context skips whole pages instead of truncating mid-page (preserves markdown structure)feedback.py/lint/verdicts.py: atomic writes (temp file + rename)ingest/pipeline.py: entity/concept count limits (MAX_ENTITIES_PER_INGEST=50,MAX_CONCEPTS_PER_INGEST=50)
search.py: empty query validation inkb_searchfeedback.py: citation path traversal validation (rejects..and leading/)mcp/quality.py: bare except logging inkb_refine_pageevolve/analyzer.py: surfaces low-trust pages from feedback (flagged_pagesin report)
- 483 tests (+18), 23 MCP tools, 10 modules
Tier-1 audit hardening.
mcp_server.py: MCP instructions string updated with 3 missing tools (kb_compile,kb_detect_drift,kb_save_source)evolve/analyzer.py: stub check logs on failure instead of silentpasslint/checks.py:fix_dead_links()writes audit trail towiki/log.md
ingest/pipeline.py: extraction data type validation (isinstanceguard forentities_mentioned/concepts_mentioned)graph/analysis.py:UnicodeDecodeErrorhandling inbuild_graph()(skips unreadable pages)mcp/core.py: empty title validation inkb_create_page
- 465 tests (+13), 23 MCP tools, 10 modules
Tier 1-3 improvements.
lint/checks.py:check_stub_pages()— flags pages with <100 chars body, integrated intorun_all_checks()and evolveevolve/analyzer.py:detect_source_drift()— finds wiki pages stale due to raw source changesmcp/health.py:kb_detect_driftMCP tool (23rd tool)
graph/analysis.py:build_backlinks()now filters broken links (consistent withbuild_graph())evolve/analyzer.py:analyze_coverage()usesparent.nameinstead of fragile string containmentingest/pipeline.py: redundant.removesuffix(".md")removed from evolveingest/extractors.py: JSON fence stripping handles whitespace
- 452 tests (+21), 23 MCP tools (+1), 10 modules
Feature completion.
mcp/core.py:kb_compileMCP tool (22nd tool, callscompile_wiki())lint/runner.py/cli.py:kb lint --fix(auto-fixes dead links by replacing broken[[wikilinks]]with plain text)config.py:MAX_SEARCH_RESULTSconstant (replaces hardcoded 100)
- 431 tests (+17), 22 MCP tools (+1), 10 modules
Audit fixes — 15 bug fixes across ingest, lint, query, and validation.
ingest/pipeline.py: replaced flawed regex in_update_existing_page()withfinditerlast-match approach; added logging to silent exception handlerlint/semantic.py: removed domain terms fromcommon_wordsstoplist; fixed consistency group truncation (chunks instead of silent discard)query.py: added context truncation logging; BM25 avgdl guard loggingmcp/browse.py: case-insensitive page lookup validates resolved path stays in WIKI_DIRmcp/health.py:logger.exceptionreplaced withlogger.error
feedback.py: length limits enforced (question/notes 2000 chars, page ID 200 chars, max 50 cited pages, path traversal rejection)lint/verdicts.py: severity validation (error/warning/info)review/refiner.py: rejects content starting with---ingest/pipeline.py:pages_skippedsurfaced in CLI and MCP output
- 413 tests (+31), 21 MCP tools, 10 modules
Comprehensive audit and hardening.
- 93 new tests across 6 new test files:
test_llm.py,test_lint_verdicts.py,test_paths.py,test_mcp_browse_health.py,test_mcp_core.py,test_mcp_quality_new.py
utils/llm.py: thread-safe LLM client singleton (double-check locking);ValueErroron invalid tierutils/wiki_log.py: O(1) append (replaces O(n) read-modify-write)mcp/: consistent_validate_page_id()usage across all tools; confidence level validation inkb_create_pageutils/text.py:yaml_escapehandles\rand\0feedback.py/lint/verdicts.py: 10k entry retention limits
search.py: BM25 division-by-zero fix (avgdl=0 guard)ingest/pipeline.py: source path traversal protection inpair_page_with_sources()ingest/pipeline.py: frontmatter-aware source collision detection in_update_existing_page()lint/semantic.py: duplicate "could" removed
- 382 tests (+93), 21 MCP tools, 10 modules. MCP tool test coverage 41% to 95%.
Hardening release.
mcp/: all tools wrap external calls in try/except;max_resultsbounds [1, 100] inkb_query/kb_search; MCP instructions updated with Phase 2 toolsutils/llm.py: Anthropic SDKmax_retries=0(double-retry fix)compile/linker.py/graph/analysis.py: redundant.removesuffix(".md")removed
mcp/:_validate_page_id()rejects..and absolute paths, verifies resolved path within WIKI_DIR — applied tokb_read_page,kb_create_pagemodels.py: citation regex fix (underscore support in page IDs)ingest/pipeline.py: slug collision tracking (pages_skippedin ingest result)ingest/extractors.py: JSON fence hardening (handles single-line```json{...}```)
- 289 tests (+37), 21 MCP tools, 10 modules
BM25 search engine.
search.py: BM25 ranking algorithm replacing naive bag-of-words (term frequency saturation, inverse document frequency, document length normalization)search.py: custom tokenizer with stopword filtering and hyphen preservationconfig.py: configurableBM25_K1/BM25_Bparameters; title boosting viaSEARCH_TITLE_WEIGHTtoken repetition
- 252 tests, 21 MCP tools, 10 modules
S+++ upgrade.
kb.mcppackage: MCP server split into 5 modules from 810-line monolith (app,core,browse,health,quality)graph/analysis.py: PageRank and betweenness centralityingest/pipeline.py: entity/concept enrichment on multi-source ingestionlint/verdicts.py: persistent lint verdict storage with audit trailmcp/core.py:kb_create_pageandkb_save_lint_verdicttoolstemplates/: comparison and synthesis extraction templates
compile/linker.py: case-insensitive wikilink resolutionfeedback.py: trust threshold boundary fix (<to<=)compile/compiler.py: template hash change detection triggers recompile
- 234 tests, 21 MCP tools, 10 modules
DRY refactor and code quality.
kb.utils.text: sharedslugify()(2x to 1x)kb.utils.wiki_log: shared log appending (4x to 1x)kb.utils.pages: sharedload_all_pages()/page_id(3x to 1x)ingest/pipeline.py: source type whitelist validation;normalize_sources()for consistent list format
mcp_server.py:_apply_extraction(80 lines) replaced byingest_source(extraction=...)utils/text.py:yaml_escapeextended for newlines/tabsutils/wiki_log.py: auto-createwiki/log.mdon first write- Test fixtures consolidated (
create_wiki_page,create_raw_source)
- 180 tests (+33 parametrized edge case tests), 19 MCP tools, 10 modules
Quality and robustness fixes.
feedback.py: weighted Bayesian trust scoring (wrong penalized 2x)utils/text.py: canonical path utilities (make_source_ref,_canonical_rel_path)config.py: tuning constants (STALENESS_MAX_DAYS,SEARCH_TITLE_WEIGHT, etc.)
ingest/extractors.py: YAML injection protection; extraction JSON validationmodels.py: regex-based frontmatter parsinggraph/analysis.py: edge invariant enforcement; empty slug guards
- 147 tests, 19 MCP tools, 10 modules
Multi-loop supervision system.
kb.feedback: query feedback store with weighted Bayesian trust scoringkb.review: page-source pairing, review context/checklist builder, page refinerkb.lint.semantic: fidelity, consistency, completeness context builders for LLM evaluationkb.lint.verdicts: persistent lint/review verdict storage.claude/agents/wiki-reviewer.md: Actor-Critic reviewer agent- 7 new MCP tools:
kb_review_page,kb_refine_page,kb_lint_deep,kb_lint_consistency,kb_query_feedback,kb_reliability_map,kb_affected_pages
- 114 tests, 19 MCP tools (+7), 10 modules (+3)
Initial release. Core system with 5 operations + graph + CLI.
- Content-hash incremental compile with three index files
- Model tiering (scan/write/orchestrate)
- Structured lint output
- 5 operations: ingest, compile, query, lint, evolve
- Knowledge graph with wikilink extraction
- CLI with 6 commands
- 12 MCP tools
- 81 tests, 12 MCP tools, 7 modules