ADR-0006: Domain-module split of core.py¶
Context¶
wiki_toolkit/core.py grew to 810 lines mixing six unrelated domain concepts (source lifecycle, wiki catalog, the write gate, the audit log, doctor). The same “walk a directory of frontmatter files” pattern was reimplemented six times (scan_sources, build_catalog, lint_wiki, lint_sources, source_coverage, suggest_dedupe); four of those six crashed on malformed frontmatter instead of reporting a lint violation, and only one had test coverage for that failure mode. The “a source file is canonical” rule (first-seen source id, excluding duplicate: true) was copy-pasted verbatim between scan_sources and source_coverage.
Decision¶
- Split
core.pyinto modules named afterCONTEXT.md’s domain vocabulary:sources.py(the largest — owns the one domain concept,source, and all its state transitions),wiki.py,write_gate.py,log.py,doctor.py, plus a shared internal_io.py(JSONL read/write). - One shared private directory-walk helper (
_iter_markdown) and one shared canonical-source predicate (_is_canonical_source) replace all six/two duplicated implementations. cli.py’s command surface (names, options, output strings, exit codes) is completely unchanged — only its imports move.- Malformed-frontmatter handling becomes uniform across all six former call sites as a side effect of the walk unification — a bug fix bundled into the refactor, not new scope.
- Tests move with the code they cover, split along the same module boundaries (
test_sources.py,test_wiki.py,test_write_gate.py,test_log.py,test_doctor.py,test_io.py).
Consequences¶
- Splitting
sources.pyfurther by individual command (source_scan.py/source_coverage.py/…) was explicitly rejected — it would recreate the original duplication one level up, since all of these share the manifest and the canonical-source rule. core.pyis deleted once every symbol has a new home; nothing imports fromwiki_toolkit.coreafterward.