feat: Link-Taxonomie u5 - Restkanten gelabelt, migrate done 4.0.0, Workshop geschlossen
CI / verify (push) Successful in 51s

Files changed:
- .wikitool-kb.json
- kb/entities/technologies/Arch Linux.md
- kb/entities/technologies/Disk Encryption.md
- kb/entities/technologies/Wine-Staging.md
- kb/entities/tools/AUR.md
- kb/entities/tools/GPG.md
- kb/entities/tools/Lutris.md
- kb/entities/tools/NotebookLM.md
- kb/entities/tools/Obsidian.md
- kb/entities/tools/OpenAI Codex.md
- kb/entities/tools/OpenCode.md
- kb/entities/tools/Pi.md
- kb/entities/tools/Proton.md
- kb/entities/tools/Steam.md
- kb/entities/tools/Wine.md
- kb/entities/tools/gdeploy.md
- kb/entities/tools/makepkg.md
- kb/entities/tools/pascalandy schema.md
- kb/log.md
- work/link-taxonomy-migration/README.md
- work/link-taxonomy-migration/glossary.md
- work/link-taxonomy-migration/plan.md
This commit is contained in:
2026-09-02 23:06:08 +02:00
parent 807094deae
commit 3f99d6715f
22 changed files with 92 additions and 340 deletions
-106
View File
@@ -1,106 +0,0 @@
# Workshop: link-taxonomy-migration
- **Run key:** `link-taxonomy-migration` (this directory's name - there is no other identifier)
- **Input:** none - this run is not an ingest
- **Started:** 2026-09-02
- **Session id form:** `WIKITOOL_SESSION_ID="link-taxonomy-migration/u<N>"`, one per unit
- **Issue:** Gitea #40, sections *Label werden Enum* and *Toolgeführte Blöcke*
## Goal
Move every relationship in `kb/` from free-text German prose in a body bullet to a machine
value in `related:`, and every tool-owned body region from heading-matching to a marker pair.
Afterwards the compiler contains no heading text and no relationship label, and `lint` can
enforce the vocabulary because there is one.
## Why this is `assisted` and not `mechanical`
Measured on 2026-09-02, against the corpus rather than against the documentation:
| | |
|---|---|
| Pages | 180 |
| Distinct relationship labels in body bullets | **152** |
| Labelled bullets | 337 |
| Labels occurring exactly once | 102 |
| Top 20 labels cover | 167 of 337 |
| Bare `- [[X]]` bullets under `## Siehe auch` | 555 |
| ...of those, provably redundant (a labelled edge already exists) | 353 |
| ...of those, the only connection between the two pages | **202**, across 63 target pages |
`kb/CONVENTIONS.md` documents thirteen labels. Nothing ever checked that, and the corpus does
not follow it - so there is no mapping table to apply, and roughly 539 edges need a judgment
call each. A large minority are reverse directions (`Verwendet von` 20x, `implementiert durch`,
`Ersetzt durch`), which under authored directional edges are exactly the edges that stop being
stored and start being rendered.
Rejected alternative, recorded so it is not re-proposed: map the top 20 mechanically and set
everything else to `see-also`. That would start the new taxonomy with ~370 of ~539 edges on its
weakest label - the `verwandt mit` sediment this whole change exists to end, re-created as the
documented initial state.
## Closes when
Every unit in `plan.md` is published, and:
- `wikitool lint` reports zero unlabelled edges and zero labels outside the authorising
collection's `outbound:` block
- `wikitool migrate verify --from <pre-migration rev>` reports no wikilink or citation count
change, and no unbalanced marker
- `wikitool migrate done 4.0.0 --pages <N>` has run
## Checklist
- [x] u0 mechanism - taxonomy, `links.py`, `blocks.py`, `xref` rewrite, lint checks,
`links show` for the inbound view, deletion of the matching layer. No page touched.
- [x] u1 `kb/entities/` (systems, tools, technologies) - 57 pages, 199 edges, run as five
sub-sessions (`u1`, `u1-technologies`, `u1-technologies-2`, `u1-tools-1`, `u1-tools-2`,
`u1-close`) against one 60-call-per-session ceiling; published together as commit
`b137359`. Deferred cross-collection edges recorded in `glossary.md` for u2/u3.
- [x] u2 `kb/entities/` (projects, people) + `kb/comparisons/` - 16 pages, ~45 edges, single
session (`link-taxonomy-migration/u2`, closed as `u2-close`). Discovered comparisons pages
carry no `related:` field at all (only `entities:`, unlabelled by the type spec); labelled
the prose by hand instead. Confirmed taxonomy gap for person-authored/extended
relationships, resolved as `see-also` throughout - see `glossary.md`.
- [x] u3 `kb/concepts/` - 80 pages, ~250 edges, run as seven sub-sessions (`u3`, `u3-b2`..`u3-b7`,
close `u3-close`) against the 60-call ceiling, including one gate-forced pause mid-batch
resumed with explicit user approval. Two self-caught defects: 14 pages left unlabelled
after a wrong "already covered elsewhere" call (fixed via `lint --full`'s unlabelled-edges
report), and one real content loss - a cited, substantive `Verwandte Concepts` line deleted
alongside a bare wikilink during prose cleanup, caught by `migrate verify`'s `cite-refs`
diff and restored. Published as commit `a35c94e`. See `glossary.md` for the
add-is-safe/remove-is-risky mechanism note this unit established.
- [x] u4 `kb/sources/` - zero page edits needed. `types/source.md` declares no `related:` field
(only `entities:`/`concepts:`, same structural gap as comparisons), and all 27 source pages
already had those fields correctly populated via `xref link-source` at ingest time - there
was never an unlabelled edge here to fix. `migrate verify --from 177c7e9 --path kb/sources`:
0 findings. No commit, no session id spent beyond the check itself. See `glossary.md`.
- [ ] u5 close-out - `migrate done`, version bump, `CHANGES.md`, workshop close
## Measured after u0
`lint` against the corpus, with the machinery in place and no page touched:
| Finding | Count |
|---|---|
| `unlabelled_edges` | **480** - every `related:` entry, since none carries a label yet |
| `unauthorised_labels` | 0 - nothing declares a label at all, so nothing can be unauthorised |
| `malformed_edges` | 0 |
| `unbalanced_markers` | 0 |
| `orphan_pages` | 1 (`GRUB`, pre-existing) |
| `broken_links`, `dangling_frontmatter_refs`, `schema_validation_errors` | 0 |
480 is the number u1-u4 have to bring to zero. It is larger than the 337 labelled body bullets
because `related:` also holds entries whose bullet was lost or never written - which is itself a
finding: the frontmatter and the body had already drifted apart under the old model, and nothing
could see it while the label lived only in the prose.
## Open decisions
- **Settled 2026-09-02:** edges are directional; the reverse edge is authored only when it is a
primary statement on its own page. The inbound view is rendered, not stored.
- **Settled 2026-09-02:** the 353 provably-redundant `## Siehe auch` edges are dropped
mechanically. The 202 that are the only connection get a real label each, or are dropped with
a reason - never converted to `see-also` in bulk.
- **Settled 2026-09-02:** labels are not localized. `- **depends-on:** [[Hermes]]` is what a
German page carries.
-114
View File
@@ -1,114 +0,0 @@
# Glossary: link-taxonomy-migration
Vocabulary decided while processing one page, carried forward so u3 (concepts) and u2
(projects/people/comparisons) do not re-decide it. Add to this file before dispatching the next
unit or sub-session.
## Mechanism correction, discovered in u3 (concepts): `xref add` is always safe
`xref add --a A --b B --rel X` **only ever touches page A** - it upserts (idempotent by target),
so calling it on a pair that already has an unlabelled or even a differently-labelled entry just
relabels it in place. `xref remove` is the only bidirectional, destructive call (strips the pair
from **both** pages' `related:` and matching body lines). u1 already learned to sequence removes
before adds per pair; u3 goes further and mostly **stops calling `xref remove` at all** for
drift-only entries. Standing policy for the rest of this migration: when a frontmatter `related:`
entry does not deserve its most literal label, relabel it with the best authorised fit - `see-also`
if truly nothing fits - via `xref add`, rather than removing it. Only remove when the entry is a
misplaced mention with nothing worth keeping on either side, and only after confirming (grep) that
the target page carries no reciprocal reference that a bidirectional strip would destroy. This
avoided several near-misses in u3 (`CPPC`/`amd-pstate` already carried `amd-pstate implements
CPPC` from u1 - removing would have destroyed it; relabelled as `CPPC mechanism amd-pstate`
instead).
## Unit sizing correction (2026-09-02)
u1 as written in `plan.md` (`kb/entities/systems, tools, technologies`, estimated ~45 pages) is
actually **57 pages carrying 199 `related:` entries** (systems 6/19, technologies 20/69,
tools 31/111). At one `wikitool` call per edge (no bulk mode exists for `xref add`/`xref
remove`), that is far past the 60-call session ceiling. Decided with the user: run u1 as planned
in scope (all three areas, one eventual publish), but as **per-area sub-sessions**, each under
its own `WIKITOOL_SESSION_ID`, holding `index rebuild`/`lint`/`log append`/`publish` until every
area is done:
- `link-taxonomy-migration/u1` - systems (done, see below)
- `link-taxonomy-migration/u1-technologies` - technologies
- `link-taxonomy-migration/u1-tools` - tools
## Recurring pattern: reverse-reading edge, correct statement already elsewhere
Several old bullets read true only backwards, and the *correct* forward statement already exists
(as still-unmigrated prose) on the other page - sometimes in a collection this unit does not
touch. Rule applied: drop the backward one from the current page's `related:` (via `xref
remove`, so it stops showing as an unlabelled edge) and note the correct edge here for whichever
unit owns the other page. Never invert the label to fake the direction.
Deferred edges for later units, discovered while processing systems:
- **u3 (concepts):** `Denylist over Allowlist` invokes-or-operates-on `AGENTS.md` (its own body
already says "beruft sich auf: AGENTS.md" - pick the label when concepts' outbound set is in
front of you).
- **u3 (concepts):** `LLM Wiki Pattern` derived-from-or-abstracted-from `Memex` (Memex's old
"Geistiger Vorgänger von" bullet reads backwards from Memex's side).
- **u2 (people):** `Vannevar Bush` owns `Memex` (Memex's "Erfunden von" bullet is `owns`'s
unlabelled inverse; `owns` has no catalogue inverse, so it must be authored from Bush's page).
- **u2 (people):** `E3DC GmbH` owns `E3DC` (same reasoning: `owns` has no inverse to assert from
the owned entity's side).
- **u2 (projects):** no action needed on `ha-core`/`hacs-e3dc` - E3DC's page independently
asserts `required-by` (a real pair with a genuine inverse), which already covers the
relationship without needing anything written on the project pages.
## Label choices worth reusing
- A file that is normative *because* a tool enforces it (`AGENTS.md` vs. `wikitool`) is
`depends-on`, not `implements`/`mechanism` - those realization labels are entities-to-concepts
only, and these pages are both entities.
- An optional dependency that the source tolerates the absence of (`CLAUDE.md`'s `@ENVIRONMENT.md`
import) is `uses`, not `depends-on` - the catalogue's own test ("does removing the target break
this thing?") answers no.
- A root file that another root file's own prose defines the rules for (`AGENTS.md` defines what
`ENVIRONMENT.md` is and is allowed to contain) is `owns` from the defining page, not asserted
back from the defined one (no inverse exists).
- A hardware/software pairing where the dependent side is a specific integration package, not the
general platform (E3DC vs. `ha-core`/`hacs-e3dc` vs. Home Assistant broadly): assert
`required-by` from the depended-upon entity's page, and do not manufacture an edge to the broad
platform if no specific claim holds there.
- A device supporting a communication standard: `implements` reads fine even outside pure
software (E3DC implements Modbus).
- Two entities in the same wiki cited as parallel real-world examples of the same concept, with
no sharper label fitting: `see-also` is correct and not a cop-out (Memex/Tolkien Gateway).
- **Taxonomy gap, confirmed while processing u2 (people):** there is no "created by" / "authored"
/ "extends the work of" label for a person-entity pointing at another entity or at a concept.
`owns` covers a person being *accountable for* an entity going forward (used for `Vannevar
Bush owns Memex`, `Rohit Gupta owns Agent Memory`, `E3DC GmbH owns E3DC`), but a one-time
historical act of creating/originating a *concept* (Karpathy authoring the LLM Wiki Pattern,
Bush originating Memex-the-idea) or of one person's work building on another's (Gupta extending
Karpathy) has nothing in Realization or Lineage that is authorised for a person: those
registers are concept-to-concept or concept-to-entity, never entity-to-entity or
entity-to-concept for authorship. Resolved as `see-also` in every such case on Karpathy's,
Gupta's and Bush's own pages, checked against every register first - not a stretch, a genuine
gap. Worth proposing a label (`originates`/`authored-by`?) if this recurs in u3/u4.
- **`kb/sources/` pages carry no `related:` field either, same as comparisons.**
`types/source.md` declares only `page_ref_fields: [entities, concepts]` - `xref add` refuses
there with the identical "type does not declare a related: field" error. Unlike comparisons,
though, there is nothing left to migrate by hand: every one of the 27 source pages already had
`entities:`/`concepts:` correctly populated via `xref link-source` at ingest time, and their
prose "Verwandte Entities"/"Verwandte Concepts" sections already just mirror those fields -
there was never a labelled-vs-unlabelled distinction to fix here. `migrate verify --from
177c7e9 --path kb/sources` returns 0 findings against baseline. u4 closes with zero page edits.
- **comparisons pages carry no `related:` field at all.** `types/comparison.md` declares only
`entities` (`page_ref_fields: [entities]`) - unlabelled by the schema, like `sources:`. `xref
add`/`xref remove` refuse on this type ("type does not declare a related: field"). The
`compares-with` label from `kb/comparisons/COLLECTION.md`'s `outbound:` block is documentational
only for this type: it is written into the `## Beziehungen` prose by hand (there is no marker
region either), never into machine data. Confirmed on `amd-pstate vs acpi-cpufreq.md`.
- A dependent-side project whose own page only names the platform vaguely ("Kernintegrationsbibliothek
für E3DC-Systeme") while a specific downstream package (a HACS component, an automation
blueprint) explicitly wraps it: assert `required-by` from the depended-upon project's page for
each specific downstream package, mirroring `depends-on` asserted on each dependent's own page -
the same genuine two-sided pair as `E3DC`/`ha-core` from u1, one level up the stack
(`ha-core`/`hacs-e3dc`, `ha-core`/`hacs-integration-blueprint`).
- An external, out-of-wiki reference project cited only as "look how someone else solved this" (a
GitHub repo mentioned as a structural analogue, no functional dependency): `see-also` asserted
from the external project's own page toward the project it inspired, not the reverse - matches
"reads correctly from the source page" even though nothing here is a stretch, just weak by
nature (`wiki-skills`, `wiki-skills-vanillaflava`, `llm-wiki-skills` -> `Chemenu`).
-48
View File
@@ -1,48 +0,0 @@
# Plan: link-taxonomy-migration
One unit is one session id and one `publish`, sized against the 60-call iteration budget.
Per-page cost here is roughly `1 touch` + the edges on it; the corpus-wide commands
(`index rebuild`, `sources rebuild-index`, `log append`, `publish` twice for the gate) are
per unit, not per page. That puts the ceiling near 45 pages and the target at 40.
| # | Unit | Pages | Job | Done when |
|---|------|------:|-----|-----------|
| u0 | mechanism | 0 | Taxonomy catalogue, `links.py`, `blocks.py`, `outbound:` in every `COLLECTION.md`, `xref` rewritten to one directional edge, lint checks, `links show` for the inbound view, `migrate verify` marker invariant, deletion of `sections.py` and the matching layer | Suite green; `lint` reports the corpus's unlabelled edges as findings rather than crashing |
| u1 | `kb/entities/systems`, `tools`, `technologies` | ~45 | Label every edge, drop redundant see-also, wrap markers | `migrate verify --path kb/entities --fail-on-error` clean |
| u2 | `kb/entities/projects`, `people`, `kb/comparisons` | ~40 | as u1 | as u1 |
| u3 | `kb/concepts` | ~45 | as u1 | `migrate verify --path kb/concepts --fail-on-error` clean |
| u4 | `kb/sources` | ~50 | as u1, plus `entities:`/`concepts:` on source pages | `migrate verify --path kb/sources --fail-on-error` clean |
| u5 | close-out | 0 | `migrate done 4.0.0`, `version bump --major`, `CHANGES.md` body, promote nothing, close workshop | `docs verify` + `lint --fail-on-error` green, workshop deleted |
Unit boundaries are written down here *before* the run so that publishing several units
together stays a planned batch rather than a way around a Mass-Update Gate refusal - see
`instructions/gates.md`.
## Per-page procedure
1. Read the page's `## Beziehungen` and `## Siehe auch` blocks.
2. For each labelled bullet: say the sentence `[this page] <label> [target]`. Pick the catalogue
label that makes it true. If it only reads true backwards, the edge belongs on the other
page - move it, do not invert the label into something the catalogue does not have.
3. For each bare `## Siehe auch` bullet: drop it if a labelled edge already connects the pair
(the tooling lists these). Otherwise decide - a real label, or dropped with the reason
recorded in the unit's notes.
4. Write the edges with `xref add --rel`, never by hand.
5. The body blocks are then *generated*: no hand-editing inside a marker pair.
## Vocabulary carried between units
`glossary.md` in this directory. A mapping decided in u1 and re-decided in u3 is the failure the
file exists to prevent - add to it **before** dispatching the next unit.
## Deliberately excluded from this run
- **Commonplace's articulation test and the `connect` report workflow.** They change how ingest
proposes links, not how links are stored. Separate question, separate issue.
- **Promoting the lint checks to hard errors.** During this run an unlabelled edge is a finding,
because that is precisely the migration window `.wikitool-kb.json` exists to represent. The
promotion is a later version's change, once the corpus can pass it.
- **`sources:` and `[^cite-id]`.** The provenance path is unlabelled by construction and is not
part of the link taxonomy.
- **Any change to page prose.** This run restates relationships in a new form; it learns
nothing new, and a body edit outside a marker pair is out of scope.