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)
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:
+62
@@ -20,6 +20,68 @@ their date-only headings.
|
||||
|
||||
---
|
||||
|
||||
## 4.1.0 - 2026-09-03 - Link-Taxonomie: Lint-Findings hart ab kb_version 4.0.0, outbound: an das Type-Spec gebunden, part-of/composition als Inversenpaar
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
Der Rest von Issue #40, nachdem die Korpus-Migration durch ist: die beiden aufgeschobenen
|
||||
Lint-Findings werden hart, und die drei Befunde aus dem Abschlusskommentar des Migrationslaufs
|
||||
werden aufgelöst.
|
||||
|
||||
**`unlabelled_edges` und `unauthorised_labels` sind harte Fehler — aber an `kb_version`
|
||||
gebunden, nicht an ein Datum.** Der Weg, den `legacy_citation_markers` genommen hat, war ein
|
||||
Umlegen in einer späteren Version: eine Instanz, die die Zitat-Migration noch schuldete, lebte
|
||||
danach mit rotem Lint. Das Ledger kann die Frage inzwischen beantworten, also tut es das.
|
||||
Unterhalb `kb_version` 4.0.0 bleiben beide beratend — genau das Fenster, in dem
|
||||
`instructions/migrations/4.0.0-link-taxonomy.md` der Instanz sagt, sie solle den halb
|
||||
konvertierten Korpus Einheit für Einheit publizieren; ein Check, der dabei fehlschlägt, würde
|
||||
den Korpus verweigern, dessen Fortschritt er misst. Ab 4.0.0 ist eine kahle Titelangabe in
|
||||
`related:` keine Seite mehr, die auf ihre Umstellung wartet, sondern eine Kante, deren Autor
|
||||
nicht gesagt hat, was sie behauptet. `hard_error_keys()` liefert die jeweils geltende Menge,
|
||||
`HARD_ERROR_KEYS` bleibt die vollständige.
|
||||
|
||||
**`outbound:` ist an das Type-Spec gebunden.** `kb/sources/` und `kb/comparisons/`
|
||||
autorisierten Label, die dort strukturell nicht schreibbar waren: keiner der beiden Type-Specs
|
||||
führte ein `related:`. Folgenlos war das nicht — die einzige Comparison-Seite des Korpus trug
|
||||
`- **compares-with:** [[amd-pstate]]` als *handgeschriebene Prosa*, ohne Marker-Region, ohne
|
||||
Frontmatter, für `lint` unsichtbar. Also ein Identifier zurück im Fließtext, gut vier Stunden
|
||||
nachdem 4.0.0 genau das beendet hatte. Eine leere Autorisierung liest sich als Lizenz.
|
||||
|
||||
Aufgelöst nach dem, was die beiden Contracts jeweils selbst sagen: `comparison` bekommt ein
|
||||
`related:` (die `compares-with`-Kante gegen jedes Subjekt ist die eine Aussage, für die die
|
||||
Seite existiert), `kb/sources/` verliert seinen `outbound:`-Block ersatzlos (dessen Contract
|
||||
sagt ausdrücklich, seine Verknüpfungen seien der mechanische Provenance-Pfad und keine
|
||||
Autorenkanten). Neu prüft `docs verify` die Kombination: ein `outbound:`-Block auf einer
|
||||
Collection, in die kein Typ mit `related:` schreibt, ist ein Befund und nennt beide Richtungen
|
||||
der Reparatur.
|
||||
|
||||
**`composition` / `part-of` ist das dritte Inversenpaar**, neben `depends-on` / `required-by`
|
||||
und `runs-on` / `hosts`. Aus der Messung, nicht vom Schreibtisch: der u3-Lauf hatte entschieden,
|
||||
die Gegenseite eines `composition` bekomme `see-also`, weil `part-of` ein Spiegel wäre. Ist es
|
||||
nicht — der Satz des Elternteils zählt seine Teile auf, der des Kindes benennt das Ganze, zu
|
||||
dem es gehört, und ein Leser, der auf dem Kind landet, braucht den zweiten. Übrig blieben 16
|
||||
`see-also`-Kanten für eine Beziehung, für die der Katalog ein Wort hat; sie sind auf `part-of`
|
||||
umgestellt. Ein Inversenpaar macht die Gegenkante weiterhin **nicht** zur Pflicht — Richtung
|
||||
wird verfasst, nicht gespiegelt —, es legt nur fest, welches Label sie trägt, wenn jemand sie
|
||||
schreibt.
|
||||
|
||||
**Stack- und Korpusänderung laufen hier in einem Zug**, entgegen der sonstigen Trennung. Der
|
||||
neue `docs verify`-Check würde eine bestehende 4.0.x-Instanz beim bloßen Kopieren der neuen
|
||||
Maschinerie fehlschlagen lassen, weil deren `kb/sources/COLLECTION.md` den `outbound:`-Block
|
||||
noch trägt — nach [instructions/dev/version-parts.md](instructions/dev/version-parts.md)
|
||||
Schritt 1 ein Grenzübertritt. Statt dafür eine `5.0.0` zu lösen, ist die Ursache mitbeseitigt:
|
||||
die Collection-Contracts dieser Instanz sind angepasst, und `dist export` leitet die
|
||||
`COLLECTION.md.template` daraus ab, also liefert jede neue Distribution die korrigierte Form
|
||||
aus. Für eine bereits bestehende 4.0.x-Instanz bleibt eine Handbewegung übrig, und sie wird
|
||||
hier benannt statt versteckt: die zwei `outbound:`-Zeilen aus `kb/sources/COLLECTION.md`
|
||||
löschen. Das neue `related:` im `comparison`-Type-Spec erreicht sie ohnehin nicht — die vier
|
||||
Page-Type-Specs gehören seit 4.0.0 der Instanz und werden nur als `.template` ausgeliefert.
|
||||
|
||||
Offen aus #40 bleibt nichts mehr; Befund 2 des Migrationslaufs (dem Katalog fehlt ein Register
|
||||
für Urheberschaft) ist als eigenes Issue erfasst.
|
||||
|
||||
---
|
||||
|
||||
## 4.0.1 - 2026-09-02 - Issue-Board: vier Pflicht-Label-Familien und Body-als-Wahrheit
|
||||
|
||||
**Author:** Torben Nehmer
|
||||
|
||||
@@ -40,9 +40,18 @@ edge merely to mirror the first one.** The inbound view is rendered from the gra
|
||||
`index rebuild` and `search`, so a reader landing on the target sees what points at it whether
|
||||
or not anyone wrote a second edge.
|
||||
|
||||
That is why most labels below have no inverse. Only two pairs do, because in each the reverse
|
||||
That is why most labels below have no inverse. Only three pairs do, because in each the reverse
|
||||
direction is a genuine primary statement someone would write on its own: `depends-on` /
|
||||
`required-by` and `runs-on` / `hosts`.
|
||||
`required-by`, `runs-on` / `hosts`, and `composition` / `part-of`.
|
||||
|
||||
The third was added after the 4.0.0 migration, from measurement rather than from the desk. A
|
||||
parent-child structure - a tier list and its tiers, a spectrum and its levels - produces the
|
||||
question on nearly every page: the parent writes `composition`, and the child then reaches for
|
||||
either `part-of` or `see-also`. The migration run answered `see-also`, on the reading that
|
||||
`part-of` would be a mirror, and left sixteen edges saying "these two are related" about a
|
||||
relationship the catalogue already had a word for. It is not a mirror: the parent's sentence
|
||||
lists its parts, the child's names the whole it belongs to, and a reader landing on the child
|
||||
needs the second one.
|
||||
|
||||
## When to run
|
||||
|
||||
@@ -129,6 +138,13 @@ Inference and comparison between ideas.
|
||||
| `composition` | is composed of the target |
|
||||
| `part-of` | is a component of the target |
|
||||
|
||||
`composition` / `part-of` is the third **inverse pair**, alongside `depends-on` / `required-by`
|
||||
and `runs-on` / `hosts` in the operational register. Being a pair does not make the second edge
|
||||
obligatory - direction is still authored - it settles *which label* the second edge takes when
|
||||
someone does write it. The child of a `composition` writes `part-of`, not `see-also`: what it
|
||||
is a component of is a primary statement about the child, and `see-also` says strictly less
|
||||
about the same fact.
|
||||
|
||||
`grounds` / `rests-on` is a genuine pair and both directions are primary statements; they are
|
||||
listed separately rather than as inverses because either page may legitimately carry only its
|
||||
own side.
|
||||
|
||||
@@ -36,8 +36,9 @@ you know the run is finished.
|
||||
**Nothing breaks while it is outstanding.** Unlabelled edges and undelimited regions are read,
|
||||
not rejected: `links.py` treats a bare title as an edge whose label is not declared yet, and
|
||||
`provenance.split_cite_block` falls back to the pre-marker layout. That is deliberate - a corpus
|
||||
has to stay readable while it is being converted - and it is why the two lint findings are
|
||||
advisory until step 6 promotes them.
|
||||
has to stay readable while it is being converted - and it is why the two lint findings stay
|
||||
advisory for as long as `kb_version` is below 4.0.0, which is exactly as long as this document
|
||||
is outstanding.
|
||||
|
||||
## Steps
|
||||
|
||||
@@ -102,7 +103,7 @@ advisory until step 6 promotes them.
|
||||
is otherwise silent: the region becomes ordinary prose and the next write appends a second
|
||||
one beside it.
|
||||
|
||||
6. **Record it, then tighten the checks:**
|
||||
6. **Record it. The checks tighten themselves:**
|
||||
|
||||
```bash
|
||||
tools/wikitool lint # unlabelled_edges and unauthorised_labels must be 0
|
||||
@@ -110,8 +111,14 @@ advisory until step 6 promotes them.
|
||||
```
|
||||
|
||||
Only once `lint` reports zero of both is the run finished. The two findings are advisory
|
||||
during the window and become hard errors afterwards - the same path
|
||||
`legacy_citation_markers` took after the citation migration.
|
||||
while `kb_version` is below 4.0.0 and hard from the moment `migrate done` records it -
|
||||
nothing to flip by hand, and no window in which a half-converted corpus is refused by the
|
||||
check that is measuring its progress.
|
||||
|
||||
Do not record the migration to silence the findings. The promotion is what makes the run
|
||||
stick: after it, a bare title in `related:` is a hard error rather than a page still
|
||||
waiting, so a corpus recorded early fails its next lint instead of quietly keeping the old
|
||||
shape.
|
||||
|
||||
## How to tell a migrated page from an unmigrated one
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ type: types/comparison.md
|
||||
tags: [kernel, power-management, amd, cpu, driver]
|
||||
created: 2026-07-31
|
||||
entities: [amd-pstate, acpi-cpufreq]
|
||||
summary: "Vergleich zweier AMD-CPU-Power-Management-Treiber: CPPC-basiertes amd-pstate gegen\xFC\
|
||||
ber ACPI-basiertem acpi-cpufreq."
|
||||
summary: "Vergleich zweier AMD-CPU-Power-Management-Treiber: CPPC-basiertes amd-pstate gegen\xFCber ACPI-basiertem acpi-cpufreq."
|
||||
related:
|
||||
- compares-with: amd-pstate
|
||||
- compares-with: acpi-cpufreq
|
||||
---
|
||||
# Comparison: amd-pstate vs acpi-cpufreq
|
||||
|
||||
@@ -131,8 +133,9 @@ ls /sys/devices/system/cpu/cpu0/cpufreq/cppc_*
|
||||
|
||||
**amd-pstate** stellt einen bedeutenden Fortschritt in der CPU-Energieverwaltung für AMD-Prozessoren dar und bietet fein-körnige Steuerung, bessere Effizienz und verbessertes Batterielebensdauer. **acpi-cpufreq** bleibt ein zuverlässiger Fallback und dient weiterhin älterer Hardware. Die Wahl zwischen ihnen hängt hauptsächlich von Hardware-Unterstützung und Kernel-Version ab, wobei amd-pstate die klare Präferenz für moderne AMD-Systeme ist.
|
||||
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **compares-with:** [[amd-pstate]]
|
||||
- **compares-with:** [[acpi-cpufreq]]
|
||||
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Consolidation Tiers
|
||||
- part-of: Consolidation Tiers
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -41,5 +41,5 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Consolidation Tiers]]
|
||||
- **part-of:** [[Consolidation Tiers]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: [memory, lifecycle, confidence, knowledge-management]
|
||||
created: 2026-07-26
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: LLM Wiki Pattern
|
||||
- part-of: LLM Wiki Pattern
|
||||
- see-also: Confidence Scoring
|
||||
- composition: Supersession
|
||||
- see-also: Consolidation Tiers
|
||||
@@ -126,7 +126,7 @@ Basierend auf [[Agent Memory]]-Erfahrung:
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[LLM Wiki Pattern]]
|
||||
- **part-of:** [[LLM Wiki Pattern]]
|
||||
- **see-also:** [[Confidence Scoring]]
|
||||
- **composition:** [[Supersession]]
|
||||
- **see-also:** [[Consolidation Tiers]]
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- exemplifies: Implementation Spectrum
|
||||
- see-also: Multi-Agent Collaboration
|
||||
- part-of: Multi-Agent Collaboration
|
||||
- evidenced-by: Source - LLM Wiki v2
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
@@ -46,6 +46,6 @@ TODO
|
||||
## Beziehungen
|
||||
|
||||
- **exemplifies:** [[Implementation Spectrum]]
|
||||
- **see-also:** [[Multi-Agent Collaboration]]
|
||||
- **part-of:** [[Multi-Agent Collaboration]]
|
||||
- **evidenced-by:** [[Source - LLM Wiki v2]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Consolidation Tiers
|
||||
- part-of: Consolidation Tiers
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -43,5 +43,5 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Consolidation Tiers]]
|
||||
- **part-of:** [[Consolidation Tiers]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Hybrid Search
|
||||
- part-of: Hybrid Search
|
||||
- exemplifies: LLM Wiki Pattern
|
||||
- evidenced-by: Source - LLM Wiki v2
|
||||
sources: []
|
||||
@@ -45,7 +45,7 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Hybrid Search]]
|
||||
- **part-of:** [[Hybrid Search]]
|
||||
- **exemplifies:** [[LLM Wiki Pattern]]
|
||||
- **evidenced-by:** [[Source - LLM Wiki v2]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Consolidation Tiers
|
||||
- part-of: Consolidation Tiers
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -43,5 +43,5 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Consolidation Tiers]]
|
||||
- **part-of:** [[Consolidation Tiers]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Multi-Agent Collaboration
|
||||
- part-of: Multi-Agent Collaboration
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -43,5 +43,5 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Multi-Agent Collaboration]]
|
||||
- **part-of:** [[Multi-Agent Collaboration]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: [split, threshold, lines, pages]
|
||||
created: 2026-08-03
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Content Quality Control
|
||||
- part-of: Content Quality Control
|
||||
- see-also: Stub Threshold
|
||||
- see-also: Index Scaling
|
||||
sources: [Source - LLM Improvements Sonnet Analysis]
|
||||
@@ -69,7 +69,7 @@ Split Threshold definiert die maximale Größe, die eine Wiki-Seite erreichen so
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Content Quality Control]]
|
||||
- **part-of:** [[Content Quality Control]]
|
||||
- **see-also:** [[Stub Threshold]]
|
||||
- **see-also:** [[Index Scaling]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: [stub, minimum, quality, lines]
|
||||
created: 2026-08-03
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Content Quality Control
|
||||
- part-of: Content Quality Control
|
||||
- see-also: Split Threshold
|
||||
- see-also: Semantic Lint Automation
|
||||
sources: [Source - LLM Improvements Sonnet Analysis, Source - LLM Wiki v2]
|
||||
@@ -80,7 +80,7 @@ It provides comprehensive information about the topic. It clearly exceeds the st
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Content Quality Control]]
|
||||
- **part-of:** [[Content Quality Control]]
|
||||
- **see-also:** [[Split Threshold]]
|
||||
- **see-also:** [[Semantic Lint Automation]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: [versioning, knowledge, updates, lifecycle]
|
||||
created: 2026-07-26
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Memory Lifecycle
|
||||
- part-of: Memory Lifecycle
|
||||
- rests-on: Confidence Scoring
|
||||
- see-also: Knowledge Graph
|
||||
- exemplifies: LLM Wiki Pattern
|
||||
@@ -138,7 +138,7 @@ Wenn Aussage B Aussage A ersetzt:
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Memory Lifecycle]]
|
||||
- **part-of:** [[Memory Lifecycle]]
|
||||
- **rests-on:** [[Confidence Scoring]]
|
||||
- **see-also:** [[Knowledge Graph]]
|
||||
- **exemplifies:** [[LLM Wiki Pattern]]
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- exemplifies: Implementation Spectrum
|
||||
- see-also: Knowledge Graph
|
||||
- part-of: Knowledge Graph
|
||||
- evidenced-by: Source - LLM Wiki v2
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
@@ -46,6 +46,6 @@ TODO
|
||||
## Beziehungen
|
||||
|
||||
- **exemplifies:** [[Implementation Spectrum]]
|
||||
- **see-also:** [[Knowledge Graph]]
|
||||
- **part-of:** [[Knowledge Graph]]
|
||||
- **evidenced-by:** [[Source - LLM Wiki v2]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Hybrid Search
|
||||
- part-of: Hybrid Search
|
||||
- exemplifies: LLM Wiki Pattern
|
||||
- evidenced-by: Source - LLM Wiki v2
|
||||
sources: []
|
||||
@@ -45,7 +45,7 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Hybrid Search]]
|
||||
- **part-of:** [[Hybrid Search]]
|
||||
- **exemplifies:** [[LLM Wiki Pattern]]
|
||||
- **evidenced-by:** [[Source - LLM Wiki v2]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -6,7 +6,7 @@ created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- exemplifies: Implementation Spectrum
|
||||
- see-also: Multi-Agent Collaboration
|
||||
- part-of: Multi-Agent Collaboration
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -45,5 +45,5 @@ TODO
|
||||
## Beziehungen
|
||||
|
||||
- **exemplifies:** [[Implementation Spectrum]]
|
||||
- **see-also:** [[Multi-Agent Collaboration]]
|
||||
- **part-of:** [[Multi-Agent Collaboration]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: []
|
||||
created: 2026-08-02
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: Consolidation Tiers
|
||||
- part-of: Consolidation Tiers
|
||||
sources: []
|
||||
confidence: 0.50
|
||||
confidence_base: 0.50
|
||||
@@ -43,5 +43,5 @@ TODO
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[Consolidation Tiers]]
|
||||
- **part-of:** [[Consolidation Tiers]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -7,7 +7,7 @@ modified: 2026-08-29
|
||||
related:
|
||||
- depends-on: Wine
|
||||
- see-also: Proton
|
||||
- see-also: Wine GE
|
||||
- part-of: Wine GE
|
||||
- see-also: Arch Linux
|
||||
sources: [Source - Wine]
|
||||
confidence: 0.85
|
||||
@@ -68,6 +68,6 @@ Wine-Staging-Patches enthalten typischerweise:
|
||||
|
||||
- **depends-on:** [[Wine]]
|
||||
- **see-also:** [[Proton]]
|
||||
- **see-also:** [[Wine GE]]
|
||||
- **part-of:** [[Wine GE]]
|
||||
- **see-also:** [[Arch Linux]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
@@ -5,7 +5,7 @@ tags: [schema, taxonomy, external, farzaa-gist]
|
||||
created: 2026-08-03
|
||||
modified: 2026-08-29
|
||||
related:
|
||||
- see-also: farzaa gist
|
||||
- part-of: farzaa gist
|
||||
- see-also: AGENTS.md
|
||||
- evidenced-by: Source - LLM Improvements Sonnet Analysis
|
||||
sources: [Source - LLM Improvements Sonnet Analysis]
|
||||
@@ -79,7 +79,7 @@ Dies sind handlungsfähige Empfehlungen, die in der Sonnet-Analyse als wertvoll
|
||||
<!-- wikitool:links -->
|
||||
## Beziehungen
|
||||
|
||||
- **see-also:** [[farzaa gist]]
|
||||
- **part-of:** [[farzaa gist]]
|
||||
- **see-also:** [[AGENTS.md]]
|
||||
- **evidenced-by:** [[Source - LLM Improvements Sonnet Analysis]]
|
||||
<!-- /wikitool:links -->
|
||||
|
||||
+1
-1
@@ -18,7 +18,7 @@ The page tables live in a generated `INDEX.md` inside each collection, linked be
|
||||
- **Concepts:** 80
|
||||
- **Entities:** 72
|
||||
- **Sources:** 28
|
||||
- **Last Updated:** 2026-09-02
|
||||
- **Last Updated:** 2026-09-03
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -127,3 +127,13 @@ Abschluss der Korpus-Migration auf die Link-Taxonomie (Gitea #40, Abschnitte 2 u
|
||||
Gitea-Issue #41 als raw/notes/Gitea Issue 41 - Issue Management and Label Scheme 2026-09-02.md aufgenommen und als Source-Seite erfasst. kb/concepts/Issue Label Scheme.md auf den Stand vom 2026-09-02 gebracht: vier Pflicht-Achsen (area/kind/prio/size), zwei optionale status/-Flags, Body-als-Wahrheit-Konvention. Das abgeloeste Zweiachsen-Schema steht als Abschnitt Historie mit Diff-Tabelle in der Seite, nicht geloescht. confidence_base 0.70 -> 0.85 (zweite unabhaengige Quelle, Bestaetigung juenger als 30 Tage).
|
||||
|
||||
---
|
||||
|
||||
## [2026-09-03] update | Link-Taxonomie 4.1.0: 16 see-also-Kanten auf part-of, Comparison-Seite auf gelabelte Kanten
|
||||
|
||||
Befund 3 aus #40: composition/part-of ist jetzt das dritte Inversenpaar. Die 16 Gegenkanten eines composition, die im u3-Lauf auf see-also gesetzt wurden, sind per xref add auf part-of relabelt - betroffen sind Consolidation Tiers, Content Quality Control, Hybrid Search, Knowledge Graph, LLM Wiki Pattern, Memory Lifecycle, Multi-Agent Collaboration, Wine GE und farzaa gist samt ihrer Kinder.
|
||||
|
||||
Befund 1 aus #40: die einzige Comparison-Seite (amd-pstate vs acpi-cpufreq) trug ihre compares-with-Bullets als handgeschriebene Prosa ohne Frontmatter-Deckung, weil types/comparison.md kein related: fuehrte. Der Type-Spec hat es jetzt; die beiden Kanten stehen als deklarierte Kanten in einer wikitool:links-Region. kb/sources/COLLECTION.md hat seinen inerten outbound:-Block verloren.
|
||||
|
||||
migrate verify --from HEAD: 181 Seiten, 0 hinzugefuegt, 0 entfernt, 18 Befunde - alle Label-Wechsel im Frontmatter, keine Aenderung an Wikilink- oder Zitatzahlen. lint --fail-on-error gruen.
|
||||
|
||||
---
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
profile: sources
|
||||
outbound:
|
||||
any: [is-evidence-for, defined-in, see-also]
|
||||
required_by_stack: true
|
||||
---
|
||||
|
||||
@@ -40,16 +38,17 @@ The `raw_files:`/`source_url:`/citation rules are shared and live in
|
||||
- `tools/wikitool sources trace --raw <path>` answers "what did we learn from this?";
|
||||
`tools/wikitool sources coverage` lists raw files no source page claims yet.
|
||||
|
||||
## Authorised labels
|
||||
## No authorised labels
|
||||
|
||||
The `outbound:` block above is what `wikitool lint` and `xref add` check: which labels a page in
|
||||
this collection may use, per destination. The catalogue they are drawn from - and what each one
|
||||
asserts - is [instructions/link-taxonomy.md](../../instructions/link-taxonomy.md), which binds
|
||||
nothing on its own.
|
||||
This collection has **no `outbound:` block**, and that is the declaration rather than an
|
||||
omission: the `source` type-spec offers no `related:` field, so a source page has nowhere to
|
||||
put a labelled edge. Everything it would want to assert is already carried by `raw_files:`,
|
||||
`entities:`, `concepts:` and `[^cite-id]` - the mechanical provenance path, not authored edges.
|
||||
|
||||
Deliberately narrow. A source page is evidence *about* a source; almost everything it would want to say is already carried by `raw_files:`, `sources:` and `[^cite-id]`, which are the mechanical provenance path rather than authored edges.
|
||||
|
||||
Adding a label here is a deliberate contract change, not a way around a refusal.
|
||||
An `outbound:` block here would authorise labels that no page in this collection can write.
|
||||
`wikitool docs verify` refuses that combination, so the two cannot drift apart: giving source
|
||||
pages labelled edges means giving the type-spec a `related:` field first, which is a deliberate
|
||||
contract change and not a way around a refusal.
|
||||
|
||||
## Outbound linking
|
||||
|
||||
|
||||
+1
-1
@@ -50,7 +50,7 @@ tools/wikitool <command> --help
|
||||
| `index rebuild [--dry-run]` | Regenerate the catalog from every page's frontmatter: `kb/index.md` becomes a map (statistics, one row per collection and per area, links to the shards) and the page tables are written to a generated `INDEX.md` in each collection. An area past 50 rows gets its own shard. Stale shards from removed collections/areas are deleted in the same pass |
|
||||
| `log append --op ingest\|query\|lint\|create\|update\|delete\|rename --title "..." [--body "..."\|--body-file path]` | Append a formatted entry to `kb/log.md` |
|
||||
| `log status` | Read-only: count `ingest` entries logged since the last `lint` entry - the deterministic trigger behind the Maintenance Schedule's "every 10 sources" full-lint cadence |
|
||||
| `lint [--json] [--markdown out.md] [--full] [--fail-on-error]` | Structural + provenance checks: broken wikilinks, dangling frontmatter references, orphan pages, index drift, schema gaps, duplicate titles, title mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more than one source page, unmarked provenance, citation/frontmatter drift, quote-limit overages (>2 blockquoted lines/page, advisory only). Prints only the sections that found something and always writes the full report to `reports/Lint Report <date>.md` (or `--markdown`), naming the path - `--full` prints everything, `--json` prints the findings and writes nothing |
|
||||
| `lint [--json] [--markdown out.md] [--full] [--fail-on-error]` | Structural + provenance checks: broken wikilinks, dangling frontmatter references, orphan pages, index drift, schema gaps, duplicate titles, title mismatches, uncovered raw files, broken `raw_files:` refs, raw files claimed by more than one source page, unmarked provenance, citation/frontmatter drift, unbalanced generated-region markers, edges whose label is missing or not authorised by the source collection's `outbound:` (both hard once `kb_version` has reached the release that introduced labelled edges - advisory below it, so a corpus mid-migration is not refused by the check measuring it), quote-limit overages (>2 blockquoted lines/page, advisory only). Prints only the sections that found something and always writes the full report to `reports/Lint Report <date>.md` (or `--markdown`), naming the path - `--full` prints everything, `--json` prints the findings and writes nothing |
|
||||
| `search ["<text>"] [--field <predicate> ...] [--kind/--subtype/--collection/--tag <v>] [--regex] [--limit N] [--sort [-]<field>] [--backend <name>] [--matches] [--json]` | Find pages in `kb/` without reading the index. Text search runs through a pluggable backend (`rg` today); `--field` predicates are evaluated on frontmatter - `f=v`, `f~substring`, `'f>=v'`, `'f:*'` (present), `'!f'` (absent), repeatable and ANDed. With no text this is a pure structured query. Results carry kind/summary/confidence so a hit can be judged without opening the page. A page whose frontmatter does not parse can match no positive predicate, so it is **named** rather than dropped: `--json` always carries an `unreadable` list of `{path, reason}` (usually empty), and the table form writes the same lines to stderr. `--regex` is applied by `rg` alone, whose engine is linear; the ranking boosts for title and summary are literal-containment only, so a non-literal pattern is ranked by match count. `rg` is killed after 30 s and reported as a failure. Read-only, and **exempt from the Iteration Budget Gate** |
|
||||
| `confidence decay [--apply]` | Recompute every page's derived `confidence` as `confidence_base * (1 - 0.01/month)`, floored at 0.2; dry-run by default |
|
||||
| `confidence init-base [--apply]` | One-time backfill: set `confidence_base` from the current `confidence` on pages that predate the derived-confidence model |
|
||||
|
||||
@@ -16,10 +16,12 @@ from chemenu import config
|
||||
from chemenu.commands._util import rel_path, success
|
||||
from chemenu.lint_core import (
|
||||
HARD_ERROR_KEYS,
|
||||
MIGRATION_GATED_KEYS,
|
||||
MOST_LINKED_COUNT,
|
||||
QUOTE_LIMIT,
|
||||
count_quote_blocks,
|
||||
default_report_path,
|
||||
hard_error_keys,
|
||||
has_hard_errors,
|
||||
render_markdown,
|
||||
render_summary,
|
||||
@@ -30,10 +32,12 @@ from chemenu.lint_core import (
|
||||
# so does every other name the tests and sibling commands already import.
|
||||
__all__ = [
|
||||
"HARD_ERROR_KEYS",
|
||||
"MIGRATION_GATED_KEYS",
|
||||
"MOST_LINKED_COUNT",
|
||||
"QUOTE_LIMIT",
|
||||
"count_quote_blocks",
|
||||
"default_report_path",
|
||||
"hard_error_keys",
|
||||
"has_hard_errors",
|
||||
"render_markdown",
|
||||
"render_summary",
|
||||
|
||||
@@ -183,6 +183,33 @@ def authorised_labels(source: str, destination: str, kb_dir: Path | None = None)
|
||||
return labels
|
||||
|
||||
|
||||
LABELLED_EDGE_FIELD = "related"
|
||||
|
||||
|
||||
def collections_that_can_carry_labelled_edges() -> set[str]:
|
||||
"""Collection names whose offered page types actually have somewhere to put
|
||||
a labelled edge.
|
||||
|
||||
Derived from `page_ref_fields:`, the same way `stack_required_collections()`
|
||||
is derived from `base_dir:`: a type that does not offer `related:` cannot
|
||||
carry a label, no matter what its collection's contract authorises.
|
||||
"""
|
||||
from chemenu.type_resolver import resolver
|
||||
|
||||
names: set[str] = set()
|
||||
for type_path, _frontmatter in resolver.list_type_specs():
|
||||
try:
|
||||
if resolver.get_root(type_path) != "kb":
|
||||
continue
|
||||
base_dir = resolver.get_base_dir(type_path)
|
||||
fields = resolver.get_page_ref_fields(type_path)
|
||||
except (ValueError, OSError):
|
||||
continue
|
||||
if base_dir and LABELLED_EDGE_FIELD in fields:
|
||||
names.add(str(base_dir).strip("/"))
|
||||
return names
|
||||
|
||||
|
||||
def declaration_issues(kb_dir: Path | None = None) -> list[str]:
|
||||
"""What each `COLLECTION.md` fails to declare about itself.
|
||||
|
||||
@@ -200,6 +227,7 @@ def declaration_issues(kb_dir: Path | None = None) -> list[str]:
|
||||
issues: list[str] = []
|
||||
|
||||
required = stack_required_collections()
|
||||
can_label = collections_that_can_carry_labelled_edges()
|
||||
present = {path.name for path in iter_kb_collections(root)}
|
||||
for name in required:
|
||||
if name not in present:
|
||||
@@ -244,6 +272,20 @@ def declaration_issues(kb_dir: Path | None = None) -> list[str]:
|
||||
)
|
||||
+ f" - it must be {str(expected).lower()}"
|
||||
)
|
||||
|
||||
# An `outbound:` block on a collection whose types offer no `related:`
|
||||
# authorises labels that no page there can write. That is not a harmless
|
||||
# extra: it reads as a licence, so the label gets written into the prose
|
||||
# by hand instead - an identifier back in free text, which is the exact
|
||||
# thing the labelled-edge model exists to end. The two halves have to
|
||||
# move together, so the check names both directions of the fix.
|
||||
if declared.get(OUTBOUND_FIELD) and collection.name not in can_label:
|
||||
issues.append(
|
||||
f"{relative}: `{OUTBOUND_FIELD}:` authorises labels, but no page type writing "
|
||||
f"into kb/{collection.name}/ offers a `{LABELLED_EDGE_FIELD}:` field - so no "
|
||||
f"page here can carry a labelled edge. Either drop the block, or give the "
|
||||
f"type-spec a `{LABELLED_EDGE_FIELD}:` in its `page_ref_fields:` and schema"
|
||||
)
|
||||
return issues
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ from chemenu.provenance import legacy_source_pages as find_legacy_source_pages
|
||||
from chemenu.provenance import orphan_footnote_defs as find_orphan_footnote_defs
|
||||
from chemenu.provenance import uncovered_raw_files as find_uncovered_raw_files
|
||||
from chemenu.provenance import undefined_footnote_refs as find_undefined_footnote_refs
|
||||
from chemenu.version import Version
|
||||
from chemenu.kb_scan import (
|
||||
GENERATED_INDEX,
|
||||
WIKILINK_RE,
|
||||
@@ -478,13 +479,6 @@ def default_report_path(report: dict) -> Path:
|
||||
# through the index or navigation only. `quote_limit_violations` is advisory
|
||||
# too - it flags a habit, not a broken tree.
|
||||
#
|
||||
# `unlabelled_edges` and `unauthorised_labels` are advisory **for now**, and
|
||||
# that is a dated decision rather than a judgment about severity: they describe
|
||||
# exactly the state a corpus is in between the 4.0.0 machinery landing and the
|
||||
# migration reaching each page, which is the window `.wikitool-kb.json` exists
|
||||
# to represent. They become hard errors once the migration is recorded - the
|
||||
# same path `legacy_citation_markers` took.
|
||||
#
|
||||
# `malformed_edges` and `unbalanced_markers` are hard from the start: neither
|
||||
# describes an unconverted page, only a broken one.
|
||||
#
|
||||
@@ -505,11 +499,57 @@ HARD_ERROR_KEYS = (
|
||||
"dangling_frontmatter_refs",
|
||||
"malformed_edges",
|
||||
"unbalanced_markers",
|
||||
"unlabelled_edges",
|
||||
"unauthorised_labels",
|
||||
"invalid_type_paths",
|
||||
"type_resolution_errors",
|
||||
"schema_validation_errors",
|
||||
)
|
||||
|
||||
# Findings that only become hard once the corpus has reached a given shape.
|
||||
#
|
||||
# `unlabelled_edges` and `unauthorised_labels` describe exactly the state a
|
||||
# corpus is in between the 4.0.0 machinery landing and the migration reaching
|
||||
# each page - the window `.wikitool-kb.json` exists to represent. Failing on
|
||||
# them during that window would refuse the very corpus that
|
||||
# `instructions/migrations/4.0.0-link-taxonomy.md` tells an instance to publish
|
||||
# unit by unit. So the promotion is tied to `kb_version` rather than to a
|
||||
# release date: below 4.0.0 they are advisory, at or above it an unlabelled
|
||||
# edge is no longer a page awaiting conversion but an edge whose author did not
|
||||
# say what it asserts.
|
||||
#
|
||||
# Gated rather than simply promoted, which is where this departs from
|
||||
# `legacy_citation_markers`: that one was flipped in a later version and any
|
||||
# instance still owing the citation migration had to live with a red lint. The
|
||||
# ledger can answer the question now, so it does.
|
||||
MIGRATION_GATED_KEYS: dict[str, Version] = {
|
||||
"unlabelled_edges": Version(4, 0, 0),
|
||||
"unauthorised_labels": Version(4, 0, 0),
|
||||
}
|
||||
|
||||
_ALWAYS_HARD = Version(0, 0, 0)
|
||||
|
||||
|
||||
def hard_error_keys(kb_version: Version | None = None) -> tuple[str, ...]:
|
||||
"""`HARD_ERROR_KEYS` minus the findings this corpus has not grown into yet.
|
||||
|
||||
`kb_version` defaults to what `.wikitool-kb.json` records. A tree without
|
||||
one - a fresh instance, which starts at the current shape rather than
|
||||
migrating into it - keeps every key: there is no outstanding migration for
|
||||
a gated finding to be the noise of.
|
||||
"""
|
||||
from chemenu import kb_state
|
||||
|
||||
if kb_version is None:
|
||||
kb_version = kb_state.read_kb_version()
|
||||
if kb_version is None:
|
||||
return HARD_ERROR_KEYS
|
||||
return tuple(
|
||||
key
|
||||
for key in HARD_ERROR_KEYS
|
||||
if kb_version >= MIGRATION_GATED_KEYS.get(key, _ALWAYS_HARD)
|
||||
)
|
||||
|
||||
|
||||
def has_hard_errors(report: dict) -> bool:
|
||||
return any(report.get(key) for key in HARD_ERROR_KEYS)
|
||||
return any(report.get(key) for key in hard_error_keys())
|
||||
|
||||
@@ -192,3 +192,29 @@ def test_an_undeclared_destination_authorises_nothing(kb_root):
|
||||
missing declaration to be filled in with a permissive default."""
|
||||
_authorising(kb_root, "entities", " concepts: [implements]")
|
||||
assert kb_collections.authorised_labels("entities", "sources") == set()
|
||||
|
||||
|
||||
def test_outbound_on_a_collection_that_cannot_carry_labels_is_a_finding(kb_root):
|
||||
"""`kb/sources/` is the live case: the `source` type-spec offers no
|
||||
`related:`, so an `outbound:` block there authorises labels no page can
|
||||
write. Left unchecked it reads as a licence and the label gets written into
|
||||
the prose by hand instead - an identifier back in free text, which is what
|
||||
labelled edges exist to end."""
|
||||
_authorising(kb_root, "sources", " any: [is-evidence-for]", required=True)
|
||||
issues = kb_collections.declaration_issues(kb_root)
|
||||
assert any(
|
||||
"kb/sources/COLLECTION.md" in issue and "no page type writing into kb/sources/" in issue
|
||||
for issue in issues
|
||||
)
|
||||
|
||||
|
||||
def test_outbound_is_fine_on_a_collection_whose_type_offers_related(kb_root):
|
||||
_collection(kb_root, "sources", profile="sources", required=True)
|
||||
_authorising(kb_root, "entities", " any: [uses]")
|
||||
assert kb_collections.declaration_issues(kb_root) == []
|
||||
|
||||
|
||||
def test_a_collection_without_outbound_is_not_a_finding(kb_root):
|
||||
"""Absence is the declaration `kb/sources/` makes: no authored edges here."""
|
||||
_collection(kb_root, "sources", profile="sources", required=True)
|
||||
assert kb_collections.declaration_issues(kb_root) == []
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -45,7 +45,7 @@ def test_get_page_ref_fields_reads_the_type_spec():
|
||||
assert resolver.get_page_ref_fields("types/entity.md") == ["related", "sources"]
|
||||
assert resolver.get_page_ref_fields("types/concept.md") == ["related", "sources"]
|
||||
assert resolver.get_page_ref_fields("types/source.md") == ["entities", "concepts"]
|
||||
assert resolver.get_page_ref_fields("types/comparison.md") == ["entities"]
|
||||
assert resolver.get_page_ref_fields("types/comparison.md") == ["entities", "related"]
|
||||
|
||||
|
||||
def test_page_ref_fields_exist_in_the_type_schema():
|
||||
|
||||
+2
-1
@@ -4,7 +4,7 @@ name: comparison
|
||||
description: Strukturierter Typ für Vergleichsseiten, die mehrere Entities oder Ansätze gegenüberstellen
|
||||
schema: types/comparison.schema.yaml
|
||||
base_dir: comparisons
|
||||
page_ref_fields: [entities]
|
||||
page_ref_fields: [entities, related]
|
||||
---
|
||||
|
||||
# Comparison
|
||||
@@ -32,6 +32,7 @@ page_ref_fields: [entities]
|
||||
| `tags` | Nein | Navigations-Tags zur Kategorisierung |
|
||||
| `created` | Ja | Erstellungsdatum (YYYY-MM-DD) |
|
||||
| `entities` | Ja | Titel der verglichenen Entities |
|
||||
| `related` | Nein | Deklarierte ausgehende Kanten - je Subjekt eine `compares-with`-Kante, geschrieben von `wikitool xref add` |
|
||||
| `summary` | Ja | Einzeiler für `kb/index.md` |
|
||||
|
||||
## Autorenanweisungen
|
||||
|
||||
@@ -21,6 +21,23 @@ properties:
|
||||
type: string
|
||||
description: Entity titles being compared
|
||||
minItems: 2
|
||||
related:
|
||||
type: array
|
||||
items:
|
||||
oneOf:
|
||||
- type: string
|
||||
- type: object
|
||||
minProperties: 1
|
||||
maxProperties: 1
|
||||
additionalProperties:
|
||||
type: string
|
||||
description: >-
|
||||
Declared outbound edges, in the same shape entity and concept pages use.
|
||||
A comparison's own assertion is `compares-with` against each subject: the
|
||||
titles are already in `entities:`, but that field is the untyped
|
||||
provenance-style list, so without this one the edge the page exists to
|
||||
make would live only in hand-written prose - which is the thing
|
||||
instructions/link-taxonomy.md was built to end.
|
||||
summary:
|
||||
type: string
|
||||
description: 1-line summary for index.md
|
||||
|
||||
Reference in New Issue
Block a user