Link-Katalog: authored/alternative-to/addresses, entity→entity-Lineage, Lint-Befund redundant_see_also (4.7.0, #43 #49)
Files changed: - CHANGES.md - VERSION - instructions/link-taxonomy.md - kb/concepts/COLLECTION.md - kb/entities/COLLECTION.md - kb/entities/people/Andrej Karpathy.md - kb/entities/people/Vannevar Bush.md - tools/chemenu/evals/scorecard.py - tools/chemenu/links.py - tools/chemenu/lint_core.py - tools/chemenu/tests/test_lint.py
This commit is contained in:
@@ -30,6 +30,7 @@ ADVISORY_KEYS = (
|
||||
"unmarked_provenance",
|
||||
"missing_from_index",
|
||||
"title_mismatches",
|
||||
"redundant_see_also",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -33,6 +33,17 @@ from typing import Any, Iterable, Optional
|
||||
# so `lint` cannot be satisfied by declaring the placeholder legal.
|
||||
UNLABELLED = None
|
||||
|
||||
# The one catalogue label this tool knows by name. Everything else about the
|
||||
# vocabulary lives in `instructions/link-taxonomy.md` and each collection's
|
||||
# `outbound:` block, on purpose - an instance may authorise any label it likes
|
||||
# and the tool never has an opinion about which. `see-also` is the exception
|
||||
# because it is the catalogue's declared last resort: it asserts only that
|
||||
# nothing better fit, which is what lets `lint` judge it as *weaker than*
|
||||
# another edge on the same pair rather than merely different. No behaviour
|
||||
# depends on the string beyond that comparison, and an instance that dropped
|
||||
# `see-also` from every contract would simply never see the finding.
|
||||
SEE_ALSO = "see-also"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Edge:
|
||||
|
||||
@@ -181,6 +181,26 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
malformed_edges: list[dict] = []
|
||||
unlabelled_edges: list[dict] = []
|
||||
unauthorised_labels: list[dict] = []
|
||||
redundant_see_also: list[dict] = []
|
||||
|
||||
# Every specific thing any page asserts about any pair, collected before the
|
||||
# loop below because the reverse direction of an edge is not known while
|
||||
# standing on the page that carries it.
|
||||
#
|
||||
# What it is for: `see-also` is the catalogue's declared last resort - it
|
||||
# asserts that nothing better fit. When the *other* page already says
|
||||
# something specific about the same pair (`Wine GE depends-on Wine` opposite
|
||||
# `Wine see-also Wine GE`), the weak edge adds nothing a reader did not
|
||||
# have: direction is authored but the inbound view is rendered, so the
|
||||
# labelled edge already shows on both pages. Measured once on this corpus,
|
||||
# that was 57 of 180 `see-also` edges - the largest single class, and none
|
||||
# of it a vocabulary gap.
|
||||
typed_edges: dict[tuple[str, str], str] = {}
|
||||
for source_title, source_page in pages.items():
|
||||
for edge in links.edges(source_page.frontmatter, "related"):
|
||||
if edge.is_labelled and edge.label != links.SEE_ALSO:
|
||||
typed_edges[(source_title, edge.target)] = edge.label
|
||||
|
||||
for title, page in sorted(pages.items()):
|
||||
type_path = page.frontmatter.get("type")
|
||||
if not type_path:
|
||||
@@ -211,6 +231,16 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
if not edge.is_labelled:
|
||||
unlabelled_edges.append({"page": title, "target": edge.target})
|
||||
continue
|
||||
if edge.label == links.SEE_ALSO:
|
||||
reverse_label = typed_edges.get((edge.target, title))
|
||||
if reverse_label is not None:
|
||||
redundant_see_also.append(
|
||||
{
|
||||
"page": title,
|
||||
"target": edge.target,
|
||||
"reverse_label": reverse_label,
|
||||
}
|
||||
)
|
||||
target_page = pages.get(edge.target)
|
||||
if source_collection is None or target_page is None:
|
||||
continue
|
||||
@@ -301,6 +331,7 @@ def run_lint(kb_dir: Path) -> dict:
|
||||
"malformed_edges": malformed_edges,
|
||||
"unlabelled_edges": unlabelled_edges,
|
||||
"unauthorised_labels": unauthorised_labels,
|
||||
"redundant_see_also": redundant_see_also,
|
||||
"unbalanced_markers": unbalanced_marker_findings,
|
||||
"quote_limit_violations": quote_limit_violations,
|
||||
"invalid_type_paths": invalid_type_paths,
|
||||
@@ -403,6 +434,11 @@ def render_markdown(report: dict) -> str:
|
||||
report.get("unauthorised_labels", []),
|
||||
lambda i: f"[[{i['page']}]] `{i['label']}` -> kb/{i['destination']}/ ([[{i['target']}]])",
|
||||
)
|
||||
_section(
|
||||
lines, "Redundant see-also (the other page already says something specific)",
|
||||
report.get("redundant_see_also", []),
|
||||
lambda i: f"[[{i['page']}]] `see-also` -> [[{i['target']}]], but [[{i['target']}]] already asserts `{i['reverse_label']}` back - drop the weaker edge, the inbound view renders the other one here",
|
||||
)
|
||||
_section(
|
||||
lines, "Dangling Frontmatter References", report["dangling_frontmatter_refs"],
|
||||
lambda i: f"[[{i['page']}]] `{i['field']}:` names `{i['target']}`, which is not a page",
|
||||
@@ -485,7 +521,13 @@ def default_report_path(report: dict) -> Path:
|
||||
# Findings that make a tree structurally wrong rather than merely untidy.
|
||||
# `orphan_pages` is deliberately absent: many pages are validly reachable
|
||||
# through the index or navigation only. `quote_limit_violations` is advisory
|
||||
# too - it flags a habit, not a broken tree.
|
||||
# too - it flags a habit, not a broken tree. `redundant_see_also` joins them for
|
||||
# both of those reasons at once: a weak edge beside a specific one is redundant
|
||||
# rather than wrong, and the check arrived long after the corpora it judges, so
|
||||
# promoting it would turn every existing instance red on the upgrade that
|
||||
# shipped it. Unlike `unlabelled_edges` it is not migration-gated either - there
|
||||
# is no version at which the redundancy becomes an error, only a sweep someone
|
||||
# does or does not get to.
|
||||
#
|
||||
# `malformed_edges` and `unbalanced_markers` are hard from the start: neither
|
||||
# describes an unconverted page, only a broken one.
|
||||
|
||||
@@ -579,3 +579,93 @@ def test_a_tree_that_never_declared_a_kb_version_keeps_every_key(kb_dir):
|
||||
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
|
||||
|
||||
|
||||
# --- `redundant_see_also` --------------------------------------------------
|
||||
#
|
||||
# `see-also` is the catalogue's declared last resort. The finding is about the
|
||||
# case where the *other* page already said something specific about the same
|
||||
# pair, so the weak edge carries nothing the inbound view did not already
|
||||
# render. Measured once on this repo's corpus, that was 57 of 180 see-also
|
||||
# edges - which is why it is worth a check rather than a habit.
|
||||
|
||||
|
||||
def _pair(kb_dir, forward, backward):
|
||||
"""Two tool pages asserting `forward` and `backward` about each other."""
|
||||
for name, edge in (("nearside", forward), ("farside", backward)):
|
||||
other = "farside" if name == "nearside" else "nearside"
|
||||
write_page(
|
||||
kb_dir / f"entities/tools/{name}.md",
|
||||
{"type": "types/entity.md", "entity_type": "tool", "tags": [],
|
||||
"created": "2026-09-04", "modified": "2026-09-04",
|
||||
"related": [] if edge is None else [{edge: other}],
|
||||
"sources": [], "confidence": 0.8, "provenance": "general",
|
||||
"summary": f"One half of a pair, asserting {edge} about the other."},
|
||||
f"\n# {name}\n\nHalf a pair.\n",
|
||||
)
|
||||
|
||||
|
||||
def test_see_also_is_redundant_when_the_other_page_asserts_something_specific(kb_dir):
|
||||
"""`nearside see-also farside` beside `farside depends-on nearside`: the
|
||||
labelled edge already shows on both pages, so the weak one says nothing."""
|
||||
_pair(kb_dir, "see-also", "depends-on")
|
||||
report = run_lint(kb_dir)
|
||||
assert {
|
||||
"page": "nearside", "target": "farside", "reverse_label": "depends-on"
|
||||
} in report["redundant_see_also"]
|
||||
|
||||
|
||||
def test_a_see_also_with_no_reverse_edge_at_all_is_not_redundant(kb_dir):
|
||||
"""The ordinary case the label exists for - nothing more specific fits, and
|
||||
the other page says nothing back."""
|
||||
_pair(kb_dir, "see-also", None)
|
||||
assert [
|
||||
i for i in run_lint(kb_dir)["redundant_see_also"] if i["page"] == "nearside"
|
||||
] == []
|
||||
|
||||
|
||||
def test_a_mutual_see_also_pair_is_not_reported_here(kb_dir):
|
||||
"""Two weak edges about one pair is a different finding - a mirror, which
|
||||
the catalogue's 'direction is authored, never mirrored' rule covers and a
|
||||
corpus sweep resolves. This check must not claim it: it is about a weak
|
||||
edge standing beside a *specific* one, and reporting the mutual case here
|
||||
would tell an author to drop an edge without saying which."""
|
||||
_pair(kb_dir, "see-also", "see-also")
|
||||
assert [
|
||||
i for i in run_lint(kb_dir)["redundant_see_also"]
|
||||
if i["page"] in ("nearside", "farside")
|
||||
] == []
|
||||
|
||||
|
||||
def test_the_specific_edge_is_never_the_one_reported(kb_dir):
|
||||
"""Only the `see-also` side is a finding. Reporting the labelled edge too
|
||||
would make the pair unfixable - dropping both loses the assertion."""
|
||||
_pair(kb_dir, "see-also", "depends-on")
|
||||
assert [
|
||||
i for i in run_lint(kb_dir)["redundant_see_also"] if i["page"] == "farside"
|
||||
] == []
|
||||
|
||||
|
||||
def test_redundant_see_also_is_advisory_at_every_kb_version(kb_dir):
|
||||
"""Redundant, not wrong - and the check arrived long after the corpora it
|
||||
judges, so promoting it would turn every existing instance red on the
|
||||
upgrade that shipped it. Unlike `unlabelled_edges` there is no version at
|
||||
which it becomes an error, so it is not migration-gated either."""
|
||||
_pair(kb_dir, "see-also", "depends-on")
|
||||
report = run_lint(kb_dir)
|
||||
assert report["redundant_see_also"] != []
|
||||
for version in (Version(3, 0, 0), Version(4, 0, 0), Version(5, 0, 0)):
|
||||
kb_state.write_kb_state(version, [])
|
||||
assert "redundant_see_also" not in hard_error_keys()
|
||||
assert has_hard_errors({"redundant_see_also": report["redundant_see_also"]}) is False
|
||||
|
||||
|
||||
def test_redundant_see_also_reaches_the_rendered_report_and_the_summary(kb_dir):
|
||||
"""A finding nobody prints is a finding nobody acts on. `render_summary`
|
||||
drops every empty section, so this also proves the section is not empty."""
|
||||
_pair(kb_dir, "see-also", "depends-on")
|
||||
report = run_lint(kb_dir)
|
||||
assert "Redundant see-also" in render_markdown(report)
|
||||
summary = render_summary(report)
|
||||
assert "Redundant see-also" in summary
|
||||
assert "[[nearside]]" in summary and "depends-on" in summary
|
||||
|
||||
Reference in New Issue
Block a user