feat: Prosa ist kein Identifier - Link-Taxonomie als Enum, generierte Regionen mit Markern (4.0.0)
Files changed: - .gitea/workflows/ci.yml - AGENTS.md - CHANGES.md - VERSION - instructions/CONTRACT.md - instructions/link-taxonomy.md - instructions/migrations/4.0.0-link-taxonomy.md - instructions/setup-instance.md - kb/CONTRACT.md - kb/CONVENTIONS.md - kb/CONVENTIONS.md.template - kb/comparisons/COLLECTION.md - kb/concepts/COLLECTION.md - kb/entities/COLLECTION.md - kb/sources/COLLECTION.md - tools/CONTRACT.md - tools/README.md - tools/chemenu/blocks.py - tools/chemenu/cli.py - tools/chemenu/commands/cite_cmd.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/docs_verify.py - tools/chemenu/commands/doctor.py - tools/chemenu/commands/links_cmd.py - tools/chemenu/commands/migrate_cmd.py - tools/chemenu/commands/new_page.py - tools/chemenu/commands/page_ops.py - tools/chemenu/commands/run_budget.py - tools/chemenu/commands/xref.py - tools/chemenu/conventions.py - tools/chemenu/corpus_diff.py - tools/chemenu/frontmatter_io.py - tools/chemenu/kb_collections.py - tools/chemenu/kb_state.py - tools/chemenu/links.py - tools/chemenu/lint_core.py - tools/chemenu/provenance.py - tools/chemenu/sections.py - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_blocks.py - tools/chemenu/tests/test_cite_cmd.py - tools/chemenu/tests/test_conventions.py - tools/chemenu/tests/test_dist_cmd.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_migrate_cmd.py - tools/chemenu/tests/test_new_page.py - tools/chemenu/tests/test_pipeline_l0.py - tools/chemenu/tests/test_types_cmd.py - tools/chemenu/tests/test_xref.py - types/concept.schema.yaml - types/entity.md - types/entity.schema.yaml - types/instruction.schema.yaml - types/type-spec.md - work/link-taxonomy-migration/README.md - work/link-taxonomy-migration/plan.md
This commit is contained in:
@@ -0,0 +1,146 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: 4.0.0-link-taxonomy
|
||||
description: Move every relationship from free-text prose in a body bullet to a labelled edge in related:, and every tool-owned body region from heading-matching to a marker pair.
|
||||
manual: true
|
||||
migrates_to: 4.0.0
|
||||
migration_kind: assisted
|
||||
obligation: required
|
||||
---
|
||||
# Move relationships into the data, and generated regions behind markers (4.0.0)
|
||||
|
||||
Until 4.0.0 the stack used **prose as an identifier** in three places, and each one cost
|
||||
something measurable:
|
||||
|
||||
| Was the identifier | Cost |
|
||||
|---|---|
|
||||
| A section's heading text (`## Beziehungen`) | The KB language was a compiler constant, and the region's *end* was a guess. Content sitting after it was silently deleted on eight pages |
|
||||
| A relationship label in a body bullet (`- **hängt ab von:**`) | Nothing could check the vocabulary, so it drifted to **152 distinct labels** across 337 bullets against thirteen that were documented |
|
||||
| The reciprocal half of every edge | `xref add` mirrored every link, which made per-collection label authorisation impossible and filled `## Siehe auch` with 555 unlabelled bullets, 353 of them provably redundant |
|
||||
|
||||
4.0.0 replaces all three. A region is delimited by a marker pair and rendered from frontmatter;
|
||||
a label is a machine value in `related:`, drawn from a catalogue and authorised per destination
|
||||
by the source collection; an edge is authored in one direction and the inbound view is computed.
|
||||
|
||||
**This one touches pages.** Unlike 3.0.0 it is not a contract reshuffle: every `related:` entry
|
||||
and every tool-owned body region changes. It is `assisted` because there is no mapping table -
|
||||
mapping free-text German onto a 35-label catalogue is a judgment call per edge, and a large
|
||||
minority of the old labels are reverse directions that under the new model are not stored at all.
|
||||
|
||||
## When to run
|
||||
|
||||
After installing 4.0.0 over an instance on 3.x. `tools/wikitool migrate status` names it, and
|
||||
`lint` reports `unlabelled_edges` for every unconverted edge - that count reaching zero is how
|
||||
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.
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Rewrite `kb/CONVENTIONS.md`'s `sections:` block.** Three slots become two, because the
|
||||
See Also region is gone:
|
||||
|
||||
```yaml
|
||||
sections:
|
||||
links: <your heading for declared relationships>
|
||||
footnotes: <your heading for citation definitions>
|
||||
```
|
||||
|
||||
Delete `section_aliases:` if you have one - nothing matches on heading text any more, so
|
||||
there is nothing to alias. The heading is now a *rendering* value: changing it re-renders
|
||||
the words above each region on the next write and can no longer split a page.
|
||||
|
||||
2. **Add an `outbound:` block to every `kb/<name>/COLLECTION.md`.** Which labels a page may use,
|
||||
per destination collection, with `any` as a wildcard:
|
||||
|
||||
```yaml
|
||||
outbound:
|
||||
entities: [depends-on, runs-on, uses, see-also]
|
||||
concepts: [implements, see-also]
|
||||
```
|
||||
|
||||
The catalogue to draw from is [link-taxonomy.md](../link-taxonomy.md); the four contracts in
|
||||
the origin repo are worked examples. **The source collection decides** - that is what makes a
|
||||
35-label palette usable, and it is why the reverse edge can no longer be written
|
||||
automatically. A destination you do not list authorises nothing, which is a real answer.
|
||||
|
||||
3. **Fix your page type-spec templates.** If you adopted the 3.0.0 templates, they contain
|
||||
`## {section.relationships}` and `## {section.see_also}`. Those variables no longer exist and
|
||||
would be written into new pages literally. **Delete both sections from the `## Template`
|
||||
block** - a template must not scaffold a tool-owned region at all: it is generated between
|
||||
markers on the first `xref add` / `cite add` and re-rendered on every write.
|
||||
|
||||
4. **Convert the corpus**, following [migrate-corpus.md](../migrate-corpus.md). Cut it into
|
||||
units sized against the iteration budget; the origin repo used four, ~45 pages each. Per page:
|
||||
|
||||
- For each labelled bullet under the old relationships heading: say the sentence
|
||||
`[this page] <label> [target]` and pick the catalogue label that makes it true. If it only
|
||||
reads true **backwards**, the edge belongs on the other page - move it there rather than
|
||||
inventing an inverse label the catalogue does not have.
|
||||
- For each bare `- [[X]]` bullet under the old See Also heading: drop it if a labelled edge
|
||||
already connects the pair. Otherwise decide - a real label, or dropped with the reason
|
||||
recorded. **Do not convert them to `see-also` in bulk.** That is the one shortcut this
|
||||
migration explicitly refuses: it would start the new taxonomy with most of its edges on its
|
||||
weakest label, which is the sediment the change exists to remove.
|
||||
- Write edges with `tools/wikitool xref add --a "<A>" --b "<B>" --rel <label>`, never by
|
||||
hand. The body region is rendered from `related:`; editing inside a marker pair is
|
||||
overwritten without warning.
|
||||
- `cite sync` converts a page's old footnote block into a marked region in passing.
|
||||
|
||||
5. **Check each unit mechanically before anything else:**
|
||||
|
||||
```bash
|
||||
tools/wikitool migrate verify --from <pre-migration rev> --path kb/<area> --fail-on-error
|
||||
```
|
||||
|
||||
It compares wikilink and citation **counts**, footnote definitions, H1, structural
|
||||
frontmatter, and - new in 4.0.0 - the **count of marker pairs per region**. A dropped marker
|
||||
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:**
|
||||
|
||||
```bash
|
||||
tools/wikitool lint # unlabelled_edges and unauthorised_labels must be 0
|
||||
tools/wikitool migrate done 4.0.0 --pages <N>
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
## How to tell a migrated page from an unmigrated one
|
||||
|
||||
Its `related:` entries are `- <label>: <title>` rather than bare titles, and its relationship
|
||||
and footnote sections sit between `<!-- wikitool:links -->` / `<!-- wikitool:footnotes -->`
|
||||
marker pairs. `tools/wikitool links show --page "<Title>"` prints `unlabelled` for every edge
|
||||
still waiting, and `lint`'s `unlabelled_edges` count is the corpus-wide version of the same
|
||||
question.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **A label you want is not in the catalogue?** Add it - a row in `link-taxonomy.md` and an
|
||||
entry in the authorising `COLLECTION.md`. No code change is involved, and the registers are
|
||||
advisory groupings rather than a schema. Do not stretch a label whose sentence reads false: a
|
||||
wrong edge is worse than a weak one, because it is machine-readable and will be believed.
|
||||
- **`related:` holds an entry with no body bullet to derive a label from?** Expected - the
|
||||
origin repo found 480 edges against 337 bullets, because frontmatter and body had already
|
||||
drifted apart while the label lived only in prose. Read the page and decide; that drift is
|
||||
itself part of what this migration repairs.
|
||||
- **A page loses its last inbound edge?** The orphan check will now report it, and that is the
|
||||
check working: directional edges mean a page nothing points at is genuinely unreachable, where
|
||||
the old mirrored model always manufactured a back-link. Either something should point at it,
|
||||
or it is reached through the catalog and that is fine.
|
||||
- **Tempted to keep writing reverse edges for navigation?** Do not. `links show` computes the
|
||||
inbound view, and the rendered bullet on the asserting page is an ordinary `[[wikilink]]`, so
|
||||
a backlink panel in an editor already shows it.
|
||||
|
||||
## Scope
|
||||
|
||||
The corpus under `kb/`, plus the three instance-owned declarations in steps 1-3. It does not
|
||||
touch `raw/`, and it learns nothing new: the same knowledge is restated in a form that can be
|
||||
checked. Installing the 4.0.0 machinery itself is `INSTALL.md`'s and must have happened first.
|
||||
Reference in New Issue
Block a user