--- 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: a schema `default:` that the compiler applied whenever nobody disagreed, silently turning the least specific value into the collection point for everything unclear. ## Contents - [When to run](#when-to-run) - [Steps](#steps) - [Decision points](#decision-points) - [Scope](#scope) ## 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 _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 _type=` 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 eventually removed again unused. A smaller count is only ever an operator's explicit, named exception (`tracker` survived that same cleanup at two pages, 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/.schema.yaml`) and its `layout:` entry (`types/.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 "" --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`.