Files
chemenu/instructions/evolve-subtypes.md
T
torben 6b300aa782
CI / verify (push) Successful in 52s
Release / release (push) Successful in 34s
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
2026-09-09 07:50:18 +02:00

6.1 KiB

type, name, description, manual
type name description manual
types/instruction.md evolve-subtypes 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. 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 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 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), not this loop. Not for the one-time choice of an instance's starting vocabulary - that is setup-instance.md's KB-language step, seeded from kb-profiles.md.

Steps

  1. See what has actually collected in the catch-all, before touching anything:

    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:

      tools/wikitool touch --page "<Title>" --set <x>_type=<new-value>
      tools/wikitool move --reconcile
      
    5. Rebuild and check:

      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.