Skip to content

#1196 Add the .kb/taxonomy.yaml format with drift reporting and back-fill - #1210

Merged
williamthorsen merged 12 commits into
mainfrom
1196
Aug 5, 2026
Merged

#1196 Add the .kb/taxonomy.yaml format with drift reporting and back-fill#1210
williamthorsen merged 12 commits into
mainfrom
1196

Conversation

@williamthorsen

@williamthorsen williamthorsen commented Aug 5, 2026

Copy link
Copy Markdown
Owner

What

Introduces .kb/taxonomy.yaml, in which a knowledge base declares the structure of its assertions. kb check now reports three kinds of drift between that declaration and the folders on disk: a folder that holds notes nothing declares, a declared area that holds no notes, and a declared area whose parent is undeclared. A knowledge base that already holds notes can adopt a declaration in one pass with the new kb taxonomy init, and --merge adds only what an existing declaration omits.

Why

A knowledge base had no way to state where its notes belong, so nothing could say whether a folder was deliberate or accidental, and nothing reported when the tree had drifted from intent. The coding vault shows the result at scale: 111 folders for 353 notes, with lifecycle markers interleaved among topics and no check able to distinguish the two.

An earlier attempt at this, .kb/schema.yaml, decayed because it described what the record parsers already enforced, so nothing broke when it drifted. This change makes the declaration load-bearing instead: it is read at runtime, and disagreements with the tree are reported.

Details

🎉 Features

  • .kb/taxonomy.yaml declares intended structure as two disjoint maps: domains for reviewed declarations and provisional for those declared but not yet reviewed, keyed by paths relative to the assertions root at any depth. Parents are not implied, and a path declared in both maps fails the load.
  • kb check reports taxonomy drift as warnings. Its findings can now describe the knowledge base rather than a single note, so they survive a run narrowed to selected paths, a --vs run, and a run that matches no notes at all.
  • kb taxonomy init derives a starting declaration from the notes a store already holds, covering every folder that holds notes along with its ancestors. Without --merge, a store that already declares a taxonomy is left untouched. A store the registry marks readonly is refused, matching kb-curate --apply.
  • @williamthorsen/kb/taxonomy exposes the loader and a comment-preserving writer, so a declaration can be extended without losing hand-written comments, key order, or formatting.
  • kb-curate surfaces the same drift, with its rule-code table and follow-up list covering it.

♻️ Refactoring

  • Store resolution and CLI flag-value parsing are shared across kb subcommands rather than restated per command.

🧪 Tests

  • Coverage for the file format, the three drift rules, the writer's comment and ordering behavior, and the new command, including refusals for a declaration in both maps, a malformed key, a block holding something other than a mapping, and a readonly store.

📚 Documentation

  • The package README documents the file format, the promotion workflow from provisional to domains, the drift rules, and the new command.

Closes #1196

A knowledge base can now declare its intended assertion structure in `.kb/taxonomy.yaml`: domain paths with one-line descriptions, split between a reviewed `domains` block and a `provisional` block for domains not yet reviewed. Keys are relative to the assertions root and nest to any depth.

The new `@williamthorsen/kb/taxonomy` entry point exposes `loadTaxonomy` and `writeTaxonomy`. Declaring domains preserves the file's existing comments, key order, and formatting, and leaves a path either block already declares as it stands.

A taxonomy that is absent or declares nothing loads as empty rather than failing. A malformed key fails the load and names the file: a restated `content/assertions/` prefix, a leading or trailing slash, an empty segment, or a `.` or `..` segment.
A `kb check` finding can now describe the store itself. Such a finding is reported under every run, including one narrowed to selected paths, one narrowed by `--vs`, and one that matched no notes at all, where it follows the line explaining why nothing was checked. Findings about a note keep being reported only when the run covers that note.

The `--json` report carries a `scope` field on every finding, absent for a finding about a note.
`kb check` now reports where a store's assertion folders and its declared taxonomy disagree. `taxonomy.undeclared` names a folder that holds notes but that no domain declares, `taxonomy.unused` a declared domain with no note at or beneath it, and `taxonomy.orphan` a declared domain whose parent is undeclared.

All three are warnings, so drift is reported without failing the run, and all three describe the store rather than any one note, so a run narrowed to selected paths or to `--vs` reports them too.

A store whose taxonomy is absent or declares nothing reports none of them, so the rules apply only to a store that has adopted a taxonomy.
Store resolution and flag-value parsing each move out of the `check` command into a module of their own, so a second command consumes them rather than restating them.

The mapping from a note's path to its domain moves beside the taxonomy loader. The drift rules ask which domains a note set observes and back-fill asks which domains it implies; deriving both from one mapping is what keeps a back-filled taxonomy from reporting drift against the notes it came from.
`kb taxonomy init` derives a starting taxonomy from the notes a knowledge base already holds, so a taxonomy can be introduced to a populated store without every folder reporting as undeclared. Every folder holding notes is declared, along with each of its ancestors, under `provisional:` with no description, and a store back-filled this way reports no taxonomy drift.

The command leaves an existing taxonomy untouched and exits 2 unless `--merge` is given, which adds only the domains that taxonomy does not already declare. `--kb <name>` selects a registered store; without it the nearest ancestor `.kb/` directory is used.
Readers can now find `.kb/taxonomy.yaml` documented in the `@williamthorsen/kb` README: its two blocks, the key format and what fails to load, what provisional means, the three drift rules, and `kb taxonomy init`. The `kb-curate` skill names those rules in its rule-code table and points at `kb taxonomy init` as their follow-up.

The README's export table also lists `./layout`, which it had omitted, and states the right number of entries.
A `.kb/taxonomy.yaml` whose `domains:` or `provisional:` header has nothing under it now loads as declaring nothing, matching an absent block. Promoting the last entry out of a block leaves exactly that shape, and it previously failed the load, so `kb check` exited 2 with every other check unrun and `kb-curate` reported `invalid-config`.

`kb taxonomy init --merge` fills such a block in place, keeping it where it sits and preserving any comment attached to it.

A load failure caused by a malformed block now names the key at fault.
`kb taxonomy init` now refuses a knowledge base the `kb.yaml` registry marks `readonly`, exiting 2 rather than writing `.kb/taxonomy.yaml` into it. A user who marks a vault they mirror or do not own now gets the same guard from it that `kb-curate --apply` already gives.
The helper prose now counts six detection categories and names `taxonomy.yaml` among the files whose defects surface as `invalid-config`, matching what the skill already documents.
Declaring a domain into a `.kb/taxonomy.yaml` whose `domains:` or `provisional:` block holds a scalar or a sequence now fails with an error naming the block, and leaves the file as it stands. Such a block previously had its contents replaced by the new declarations and reported as a clean write, so a domain list written as a YAML sequence was lost without a message.

A comment written on a block header stays on that header line when the block is filled, rather than moving below the first declaration written into it.
`kb taxonomy init` now refuses a knowledge base marked `readonly` in `kb.yaml` when it is discovered from the working directory, not only when named with `--kb`. Running it from inside a vault marked readonly is the likelier invocation of the two, and it previously wrote.

A `kb check --json` report of a discovered store now carries that store its registry name, where it previously reported `null` for every discovered store.
`kb check --json` reports a discovered knowledge base's registry name, and no name when the directory is unregistered. Neither case was asserted, so a change collapsing the reported name would have left the suite green.
@williamthorsen williamthorsen added feature Added or improved external functionality scope:kb labels Aug 5, 2026
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

Dependency audit

Production dependency audit passed.

@williamthorsen williamthorsen self-assigned this Aug 5, 2026
@williamthorsen
williamthorsen marked this pull request as ready for review August 5, 2026 17:36
@williamthorsen
williamthorsen merged commit 024ea8d into main Aug 5, 2026
3 checks passed
@williamthorsen
williamthorsen deleted the 1196 branch August 5, 2026 17:41
williamthorsen added a commit that referenced this pull request Aug 7, 2026
…ssembly-mcp-v0.2.4 codeassembly-v0.6.0

codeassembly-v0.6.0
- #794 feat: Guide kb-add note placement with the store's declared taxonomy (#1223)
- #1111 feat: Declare which harnesses sync targets (#1221)
- #1214 feat: Allow an artifact to belong to more than one collection (#1219)
- #1186 drop!: Dissolve `common-mistakes` into its consumers' guidance (#1217)
- deps: Upgrade all deps to latest version
- #1203 fix: Bar acceptance-criteria revision prompts to genuine conflicts (#1212)
- #1196 feat: Add the .kb/taxonomy.yaml format with drift reporting and back-fill (#1210)
- #1165 feat: Make a vetted subset of the library declarable (#1207)
- #1197 feat: Report the retired `harnesses:` frontmatter key from `validate` (#1204)
- #963 feat!: Rename the harness id to rovo and qualify the frontmatter key (#1199)
- #1188 drop!: Retire the bundled Bitbucket inline-comment script (#1198)
- #1187 feat: Gate the boolean prefix on a test and add the tail rule it rests on (#1195)

factory-v0.2.4
- deps: Upgrade all deps to latest version
- deps: Upgrade all deps to latest version
- #1209 refactor: Clear factory's remaining lint violations and retire both deferral lists (#1216)
- #1208 refactor: Clear the visualizations lint violations and retire six rule deferrals (#1213)

kb-v0.4.0
- #794 feat: Guide kb-add note placement with the store's declared taxonomy (#1223)
- #1196 feat: Add the .kb/taxonomy.yaml format with drift reporting and back-fill (#1210)

codeassembly-lifecycle-v0.3.0
- #963 feat!: Rename the harness id to rovo and qualify the frontmatter key (#1199)

codeassembly-mcp-v0.2.4
- #1206 refactor: Retire fleet's lint deferrals (#1211)
- #1200 tooling: Retire mcp's lint deferrals and prune the dead root entries (#1205)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Added or improved external functionality scope:kb

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add the .kb/taxonomy.yaml format with drift reporting and back-fill

1 participant