feat: Link-Taxonomie abgeschlossen - Lint hart ab kb_version 4.0.0, outbound: an Type-Spec gebunden, part-of/composition als Inversenpaar (4.1.0, #40)
CI / verify (push) Successful in 53s
Release / release (push) Successful in 36s

Files changed:
- CHANGES.md
- VERSION
- instructions/link-taxonomy.md
- instructions/migrations/4.0.0-link-taxonomy.md
- kb/comparisons/amd-pstate vs acpi-cpufreq.md
- kb/concepts/Episodic Memory.md
- kb/concepts/Memory Lifecycle.md
- kb/concepts/Mesh Sync.md
- kb/concepts/Procedural Memory.md
- kb/concepts/Reciprocal Rank Fusion.md
- kb/concepts/Semantic Memory.md
- kb/concepts/Shared vs Private.md
- kb/concepts/Split Threshold.md
- kb/concepts/Stub Threshold.md
- kb/concepts/Supersession.md
- kb/concepts/Typed Relationships.md
- kb/concepts/Vector Search.md
- kb/concepts/Work Coordination.md
- kb/concepts/Working Memory.md
- kb/entities/technologies/Wine-Staging.md
- kb/entities/tools/pascalandy schema.md
- kb/index.md
- kb/log.md
- kb/sources/COLLECTION.md
- tools/CONTRACT.md
- tools/chemenu/commands/lint.py
- tools/chemenu/kb_collections.py
- tools/chemenu/lint_core.py
- tools/chemenu/tests/test_conventions.py
- tools/chemenu/tests/test_lint.py
- tools/chemenu/tests/test_type_resolver.py
- types/comparison.md
- types/comparison.schema.yaml
This commit is contained in:
2026-09-03 06:19:56 +02:00
parent 23e34a940c
commit cfe925a76c
33 changed files with 379 additions and 66 deletions
+87 -1
View File
@@ -1,8 +1,12 @@
import json
from datetime import date
from chemenu import config
import pytest
from chemenu import config, kb_state
from chemenu.commands.lint import (
HARD_ERROR_KEYS,
hard_error_keys,
has_hard_errors,
lint_command,
render_markdown,
@@ -11,6 +15,8 @@ from chemenu.commands.lint import (
)
from chemenu.frontmatter_io import write_page
from chemenu.provenance import cite_id, render_cite_block
from chemenu.tests.conftest import use_shipped_type_specs
from chemenu.version import Version
def test_lint_detects_unparsable_frontmatter(kb_dir):
@@ -472,3 +478,83 @@ def test_lint_does_not_count_a_shell_prompt_as_a_quote(kb_dir):
)
report = run_lint(kb_dir)
assert [i for i in report["quote_limit_violations"] if i["page"] == "shelly"] == []
# --- the migration gate on `unlabelled_edges` / `unauthorised_labels` -------
#
# These four repoint `config.ROOT` at the fixture tree, which the `kb_dir`
# fixture alone does not do. Two things need it: `write_kb_state()` writes
# `.wikitool-kb.json` relative to `ROOT`, and `lint`'s collection lookup
# resolves a page against `config.KB_DIR` rather than against the directory it
# was handed - so without the repoint the gate would be read off the real
# repository's state file.
@pytest.fixture
def rooted_kb(kb_dir, tmp_path, monkeypatch):
monkeypatch.setattr(config, "ROOT", tmp_path)
use_shipped_type_specs(monkeypatch)
return kb_dir
def _page_with_an_unlabelled_edge(rooted_kb):
"""A `related:` entry that is a bare title rather than a `label: title`
mapping - the shape every page was in before the 4.0.0 migration."""
write_page(
rooted_kb / "entities/tools/bare-edge.md",
{"type": "types/entity.md", "entity_type": "tool", "tags": [], "created": "2026-09-03",
"modified": "2026-09-03", "related": ["Modbus"], "sources": [], "confidence": 0.8,
"provenance": "general", "summary": "One edge whose label was never declared."},
"\n# bare-edge\n\nAn edge without a label.\n",
)
def test_unlabelled_edge_is_advisory_below_kb_version_4(rooted_kb):
"""The window the migration document describes: the machinery has landed,
the corpus has not been converted yet, and `lint --fail-on-error` must not
refuse the very tree the migration tells the instance to publish unit by
unit."""
_page_with_an_unlabelled_edge(rooted_kb)
kb_state.write_kb_state(Version(3, 0, 0), [])
report = run_lint(rooted_kb)
assert report["unlabelled_edges"] != []
assert "unlabelled_edges" not in hard_error_keys()
# Narrowed to the finding under test: the fixture corpus carries unrelated
# hard errors of its own, so asserting on the whole report would prove
# nothing about the gate.
assert has_hard_errors({"unlabelled_edges": report["unlabelled_edges"]}) is False
def test_unlabelled_edge_is_hard_at_kb_version_4(rooted_kb):
"""Once the migration is recorded, a bare title is no longer a page waiting
its turn - it is an edge whose author did not say what it asserts."""
_page_with_an_unlabelled_edge(rooted_kb)
kb_state.write_kb_state(Version(4, 0, 0), [])
report = run_lint(rooted_kb)
assert report["unlabelled_edges"] != []
assert "unlabelled_edges" in hard_error_keys()
assert has_hard_errors({"unlabelled_edges": report["unlabelled_edges"]}) is True
def test_unauthorised_label_is_hard_at_kb_version_4(rooted_kb):
"""The fixture contracts authorise `depends-on` but not `contradicts`."""
write_page(
rooted_kb / "entities/tools/off-menu.md",
{"type": "types/entity.md", "entity_type": "tool", "tags": [], "created": "2026-09-03",
"modified": "2026-09-03", "related": [{"contradicts": "Modbus"}], "sources": [],
"confidence": 0.8, "provenance": "general", "summary": "A label off this menu."},
"\n# off-menu\n\nA label the source collection never authorised.\n",
)
kb_state.write_kb_state(Version(4, 0, 0), [])
report = run_lint(rooted_kb)
assert report["unauthorised_labels"] != []
assert "unauthorised_labels" in hard_error_keys()
assert has_hard_errors({"unauthorised_labels": report["unauthorised_labels"]}) is True
def test_a_tree_that_never_declared_a_kb_version_keeps_every_key(rooted_kb):
"""No `.wikitool-kb.json` means a fresh instance, which starts at the
current shape rather than migrating into it - so there is no outstanding
migration for a gated finding to be the noise of."""
assert kb_state.read_kb_version() is None
assert hard_error_keys() == HARD_ERROR_KEYS