source_type ist Instanzsache: Profilkatalog, Setup-Frage, evolve-subtypes-Instruction (schliesst #68)
Files changed: - CHANGES.md - VERSION - instructions/evolve-subtypes.md - instructions/kb-profiles.md - instructions/setup-instance.md - kb/sources/COLLECTION.md
This commit is contained in:
@@ -0,0 +1,114 @@
|
||||
---
|
||||
type: types/instruction.md
|
||||
name: evolve-subtypes
|
||||
description: Extend or drain a page type's subtype vocabulary - entity_type, concept_type, or source_type - once real material has outgrown it, and the invariants that keep the resulting values honest.
|
||||
manual: true
|
||||
---
|
||||
# Extend a subtype vocabulary, or drain its catch-all
|
||||
|
||||
A subtype field (`entity_type`, `concept_type`, `source_type`) partitions one page type into
|
||||
areas via that type-spec's `layout:` - `types/type-spec.md` has the anatomy. Every one of these
|
||||
three enums is instance-owned content, not stack vocabulary: `entity.md`, `concept.md` and
|
||||
`source.md` all carry `root: kb` and ship only as `.template`, so an instance's own list of
|
||||
values is exactly as much its own decision as its collection contracts are
|
||||
([kb-profiles.md](kb-profiles.md) is the palette that seeds it). This instruction is the
|
||||
procedure for changing that list once it is running, not for choosing it the first time -
|
||||
[setup-instance.md](setup-instance.md) does that.
|
||||
|
||||
The tooling for this loop already exists end to end; this file only names the sequence and the
|
||||
two rules that keep it from repeating what created `kb/sources/`'s old `notes` catch-all in the
|
||||
first place (Gitea #66): a schema `default:` that the compiler applied whenever nobody
|
||||
disagreed, silently turning the least specific value into the collection point for everything
|
||||
unclear.
|
||||
|
||||
## When to run
|
||||
|
||||
- A `wikitool lint` advisory finding reports pages sitting in a subtype's catch-all value (for
|
||||
`source`, that is `unclassified` - visible in the catalog, carrying no default) and there are
|
||||
now enough of them to warrant a real value.
|
||||
- A real ingest keeps producing pages that do not fit any existing value for a subtype field,
|
||||
and forcing them into the nearest existing one would misclassify them.
|
||||
- The catch-all itself needs draining after a value was added, so it does not become a second,
|
||||
quieter collection point.
|
||||
|
||||
Not for renaming or removing a value that pages already carry under - that moves pages and is a
|
||||
corpus migration ([migrate-corpus.md](migrate-corpus.md)), not this loop. Not for the one-time
|
||||
choice of an instance's starting vocabulary - that is
|
||||
[setup-instance.md](setup-instance.md)'s KB-language step, seeded from
|
||||
[kb-profiles.md](kb-profiles.md).
|
||||
|
||||
## Steps
|
||||
|
||||
1. **See what has actually collected in the catch-all**, before touching anything:
|
||||
|
||||
```bash
|
||||
tools/wikitool lint
|
||||
tools/wikitool search --field source_type=unclassified # or the equivalent <x>_type
|
||||
```
|
||||
|
||||
Read every page the search returns. A count alone does not say whether the pages share one
|
||||
real category or three - that judgment is the reason this step exists rather than being
|
||||
folded into the next one.
|
||||
|
||||
2. **Apply the value-and-sweep rule: a new value is added and populated in the same pass, never
|
||||
one without the other.** A declared value that no page carries yet is a category that invites
|
||||
a guess the next time someone has to pick between it and the catch-all - which is exactly how
|
||||
the old `notes` default absorbed 22 of 29 source pages before anyone noticed. So:
|
||||
|
||||
1. Count real candidates first: `tools/wikitool search --field <x>_type=<candidate-guess>`
|
||||
will find nothing yet, so count by reading the catch-all's pages from step 1 instead.
|
||||
2. **Apply the ≥3-page admission threshold.** A value is admitted *after* it has proven
|
||||
itself against real material, never in expectation of some. `spec` and `image` are the
|
||||
cautionary case: both were added to `source_type` ahead of any matching page, both sat at
|
||||
zero for a year, and both were removed in Gitea #66. A smaller count is only ever an
|
||||
operator's explicit, named exception (`tracker` at two pages in #66, kept because the
|
||||
corpus was expected to grow into it from ongoing issue ingests) - never a reason to lower
|
||||
the threshold itself.
|
||||
3. Add the enum value in the type-spec's schema (`types/<t>.schema.yaml`) and its `layout:`
|
||||
entry (`types/<t>.md`) in the same edit - a value with no `layout:` line has nowhere to be
|
||||
moved to.
|
||||
4. Reclassify every candidate page in the same pass:
|
||||
|
||||
```bash
|
||||
tools/wikitool touch --page "<Title>" --set <x>_type=<new-value>
|
||||
tools/wikitool move --reconcile
|
||||
```
|
||||
|
||||
5. Rebuild and check:
|
||||
|
||||
```bash
|
||||
tools/wikitool index rebuild
|
||||
tools/wikitool migrate verify --from HEAD --fail-on-error
|
||||
tools/wikitool lint --fail-on-error
|
||||
```
|
||||
|
||||
`migrate verify` on a subtype sweep should report moved pages and zero findings - a
|
||||
`<x>_type` change alone touches no wikilink, citation, footnote or H1.
|
||||
|
||||
3. **Draining the catch-all is the same loop, run without step 2.2's threshold** - a page
|
||||
sitting in `unclassified` (or the equivalent) already has an intended home; the only question
|
||||
is which existing value it belongs to, which step 1's read-through already answered. Skip
|
||||
straight to reclassifying it (step 2.4) and rebuilding (step 2.5).
|
||||
|
||||
4. **Record it** with `tools/wikitool log append`, describing what moved and why, the same way
|
||||
any other corpus change is logged.
|
||||
|
||||
## Decision points
|
||||
|
||||
- **The catch-all is empty and lint reports nothing?** Nothing to do - an empty catch-all is the
|
||||
success state, not a problem this instruction exists to fix.
|
||||
- **A candidate page could plausibly fit two existing values?** Resolve it by rereading the
|
||||
collection's `COLLECTION.md` for the distinguishing rule (for `sources`, authorship decides
|
||||
`analysis` vs. `document`) before inventing a third value - a genuine gap in the existing
|
||||
values is rarer than an under-read contract.
|
||||
- **More than one subtype field needs the same treatment?** Run this loop once per field; do not
|
||||
try to batch an `entity_type` change and a `source_type` change into one pass, since their
|
||||
admission thresholds are independent judgments about unrelated material.
|
||||
|
||||
## Scope
|
||||
|
||||
Covers `entity_type`, `concept_type` and `source_type` - the three subtype fields with both
|
||||
`subtype_field:` and `layout:` declared. `comparison` has neither and is out of scope by
|
||||
construction. Does not cover the stack-owned capture fields `fidelity`/`authority` on `source`
|
||||
pages: those are fixed vocabulary the stack defines, not an instance's taxonomy - see
|
||||
`types/source.md`.
|
||||
@@ -152,6 +152,30 @@ against.
|
||||
Not optional in the way the others are. An instance may rewrite its authoring rules and may not
|
||||
rename or drop it.
|
||||
|
||||
**`source_type` is a palette too, and the part most likely to be wrong for another domain -
|
||||
adapt its value list first**, the same way `entities`' area list is called out above. This
|
||||
repo's own list (`transcript, analysis, article, document, notes, tracker, unclassified`)
|
||||
describes *what a private-projects instance ingests*; it says nothing about what a source is in
|
||||
a different domain. Two worked lists, to show how little the values carry over:
|
||||
|
||||
| Instance | Plausible `source_type` values |
|
||||
|---|---|
|
||||
| Handball club and federation | `satzung` (bylaws), `protokoll` (minutes), `korrespondenz`, `spielbericht` (match report), `verbandsmitteilung` |
|
||||
| Tabletop game master | `regelwerk` (rulebook), `abenteuermodul` (module), `sessionlog`, `handout`, `weltenbau` (worldbuilding) |
|
||||
|
||||
Adopting one means copying the value list into `types/source.schema.yaml`'s enum and giving each
|
||||
value a `layout:` line in `types/source.md` - the same "copy the text in, do not point at this
|
||||
page" rule as every other profile here. Keep the visible catch-all value (`unclassified` above)
|
||||
in whatever list is adopted: a subtype field without one silently reintroduces the old
|
||||
`default:`-driven collection point that Gitea #66 removed the moment nobody names an edge case.
|
||||
Growing the list later, or draining the catch-all, is
|
||||
[evolve-subtypes.md](evolve-subtypes.md).
|
||||
|
||||
**Not up for choice: `fidelity` and `authority`.** Unlike `source_type`, these two capture
|
||||
fields are stack vocabulary - `types/source.md`'s `capture_fields:` - because they held the same
|
||||
few values across every domain this catalogue tried, where `source_type` did not. An instance
|
||||
adapts the *value list* above; it does not touch `fidelity`'s or `authority`'s enums.
|
||||
|
||||
### `comparisons`
|
||||
|
||||
Structured head-to-head evaluations of two or more things that already have pages.
|
||||
|
||||
@@ -100,6 +100,24 @@ bereit für den ersten `Ingest`.
|
||||
durch das eigene Vokabular ersetzen - sie ist Material des deutschen Profils, nicht des
|
||||
Stacks.
|
||||
|
||||
5. Den Nutzer nach dem Anwendungsgebiet fragen und daraus einen `source_type`-Vorschlag
|
||||
ableiten. [kb-profiles.md](kb-profiles.md) hält dafür zwei ausformulierte Domänenprofile
|
||||
als Anschauung bereit, neben dem Wert, den dieses Repo selbst nutzt. Der Vorschlag ist ein
|
||||
**Startpunkt, keine Festlegung** - zum Setup-Zeitpunkt hat der Betreiber null Quellen und
|
||||
rät seine Taxonomie, bevor er auch nur eine Datei gesehen hat, und das ist der
|
||||
schlechteste Moment, ein Enum festzuzurren. Vorschlag umgesetzt heißt: das Enum in
|
||||
`types/source.schema.yaml` **und** die passende `layout:`-Zeile je Wert in
|
||||
`types/source.md` in derselben Bearbeitung setzen - eine ohne die andere lässt einen Wert
|
||||
ohne Zielverzeichnis zurück. Der sichtbare Auffangwert (`unclassified`) bleibt in jedem
|
||||
Vorschlag erhalten; er ist kein Sammelbecken, sondern das Fach für eine Quelle, deren
|
||||
Kategorie noch nicht feststeht. Die Liste später erweitern oder das Fach leeren:
|
||||
[evolve-subtypes.md](evolve-subtypes.md) - nicht Teil dieses Schritts, aber der Weg dahin,
|
||||
sobald echtes Material vorliegt.
|
||||
|
||||
**Unverändert lassen:** `fidelity` und `authority` auf `source`-Seiten. Die sind
|
||||
Stack-Vokabular, keine Instanzentscheidung - [kb-profiles.md](kb-profiles.md) sagt das im
|
||||
selben Abschnitt.
|
||||
|
||||
**Vor dem ersten Ingest entscheiden.** Die `sections:`-Namen in `kb/CONVENTIONS.md` sind die
|
||||
Überschriften, die `xref` und `cite` in jede Seite schreiben; sie danach zu ändern ist eine
|
||||
Migration jeder vorhandenen Seite (`section_aliases:` trägt die alten Namen, siehe
|
||||
|
||||
Reference in New Issue
Block a user