wiki-knowledge

Custom kind-folders are opt-in and self-describing

wiki/’s four kind-folders (concepts/, entities/, sources/, synthesis/) are the plugin-fixed set, chosen because kind is the one axis both decidable from a page’s content and domain-independent (ADR-0008, wiki-conventions “Vault structure”). But real vaults grow domain vocabulary the four don’t name — a vault of design work wants wiki/decisions/, a CRM-ish vault wants wiki/people/. The script layer already tolerates these: Vault.discoveredKinds() picks up any pre-existing wiki/<folder>/ that isn’t one of the four, and enchiridion ingest places into it via an extraKindFolders map (#125). The gap #161 raised is one-sided: the wiki-ingest agent’s SKILL.md hard-codes source | synthesis | entity | concept as the only legal kind values, so the agent never emits a custom kind even when the folder exists. The four decidable kinds still cover the common case; a custom folder is a deliberate domain extension the agent should honour, not invent.

Custom kind-folders are opt-in, must pre-exist, and place as peers. The plugin never auto-creates a kind-folder — a folder appearing under wiki/ is a human decision, so a typo’d kind can’t silently mint a new folder. When a custom folder does exist, it enters the placement algorithm as a peer of the canonical four rather than a last-resort fallthrough: the agent weighs all available kind-folders together (wiki-conventions “Placement algorithm”). enchiridion ingest still refuses an unknown kind loudly — the safety valve that made auto-discovery safe in the first place.

A custom kind’s value is declared in wiki/<kind>/KIND.md, not guessed from the folder name. Deriving the kind value by stripping a trailing s (decisions → decision) works for regular plurals but mangles irregular ones (people → people, never person). KIND.md — a small YAML frontmatter carrying kind: and a one-line summary:, plus an optional freeform body for humans — makes the value explicit and gives a definition for tooling to surface. It is the single source of truth for a custom kind’s value, symmetric with how place.KindFolders is authoritative for the canonical four. When KIND.md is absent the folder is still a usable kind (discoveredKinds() and ingest accept a bare folder unchanged); the value then falls back to the strip-s heuristic (folderToKind), and the declared kind: exists precisely to override that guess when it would be wrong.

The declared value binds on both the placement path and the read path. A page written into wiki/people/ as kind person must read back as person, not people — whatever reader turns a folder name into a kind value (pagerecord, FolderKinds) consults KIND.md and honours its declaration, falling back to folderToKind only when the file is absent. A write path and read path that disagreed about a page’s kind would be the same class of divergence pagepredicate.ts was created to eliminate (#310), so symmetry here is not optional.

Discovery is a read-only CLI verb, not agent filesystem-poking. enchiridion vault kinds emits a JSON array of {kind, folder, canonical, definition} covering every kind — the canonical four flagged canonical: true, each discovered custom folder with its KIND.md-declared value and summary (or definition: null when the file is absent). The agent gets its complete placement vocabulary in one call, rather than merging a hard-coded four with a separately-queried delta, and reads the kind’s definition without a second file read. The verb also folds in the definition so the agent never needs a direct read of the kind-folder’s KIND.md; this is distinct from raw/<folder>/INGESTION.md, which stays a direct agent read scoped to the source document’s folder (reference/ingestion-hints.md) and is untouched by this change.

Creating a missing KIND.md follows the subagent-can’t-confirm rule. A wiki-ingest subagent can’t pause for the yes/no that gates a write — the same constraint that made wiki-researcher propose a save-candidate block for the invoking session to act on. So when the agent wants to place into a custom folder that has no KIND.md, it proposes one (suggested kind + summary) and the /wiki-ingest orchestration layer asks the user and writes it on explicit yes. The agent never writes KIND.md unprompted.

Consequences