Skip to content

#794 Guide kb-add note placement with the store's declared taxonomy - #1223

Merged
williamthorsen merged 5 commits into
mainfrom
794
Aug 7, 2026
Merged

#794 Guide kb-add note placement with the store's declared taxonomy#1223
williamthorsen merged 5 commits into
mainfrom
794

Conversation

@williamthorsen

@williamthorsen williamthorsen commented Aug 7, 2026

Copy link
Copy Markdown
Owner

What

Improves classification of captured knowledge-base notes by aligning with the domains declared by the KB's taxonomy rather than looking to the directory structure. If a note is filed in a folder not covered by a domain, that folder is now added to the base's taxonomy. A domain added without confirmation is recorded as awaiting review.

Why

Notes drifted into inconsistent folders as a knowledge base grew. With nothing declaring what a base's
shelves were, each capture inferred a folder from whatever a directory listing happened to show, and an
early guess became the precedent the next capture imitated. .kb/taxonomy.yaml gave a base somewhere to
state its structure, but nothing consulted it at the moment a note was placed, so the declared structure
and the structure on disk had no way to converge.

Details

🎉 Features

kb-add helper

  • --survey reports the destination's shape: the resolved knowledge base, its .kb/taxonomy.yaml path,
    each declared domain with its description, review state, and note count, and the folders holding notes
    that no domain declares. It writes nothing, opens no note, and returns without consuming stdin, so it is
    safe to run before composing a note. A store the registry marks readonly: true surveys successfully;
    only writes are refused.
  • A capture into a folder no domain declares records that folder, plus every undeclared folder above it, so
    the declared structure stays connected and a nested capture mints no taxonomy.orphan warning.
  • --domain-description supplies a new domain's one-line description; without it the domain is recorded
    bare. --auto routes the domain to provisional:, as does any capture that supplied no description.
  • placement in the result names the domain the note sits in and every domain the capture declared, each
    carrying the block it landed in. A note at the assertions root reports as sitting under no domain.
  • A capture whose declaration fails still succeeds, returning the written path plus a warning naming what
    went unrecorded; the folder surfaces as taxonomy.undeclared on the next kb check.
  • Adoption is the taxonomy file's presence, so a base whose taxonomy is empty grows its first domain from
    its first capture.
  • A malformed .kb/config.yaml or .kb/taxonomy.yaml returns the new invalid-config error code with the
    offending file named, where a survey previously exited non-zero with a bare stderr line.

kb-add skill

  • A ## Placement section states the policy: the declared taxonomy is a strong prior with the long tail
    carried by tags, a new domain is warranted by intent rather than note count and is named to match the
    domains already declared, auto mode does not mint a top-level domain, a folder name restating a tag is
    expected, and a reviewed domain beats an unreviewed one where both fit.
  • The process runs the survey before classifying, and the proposal names the matched domain, that domain's
    description, and the taxonomy file they came from, so the user can see what the placement was measured
    against.
  • The worked invocation example carries a concrete --folder, and the two redundant restatements of the
    assertions/-segment rule are cut so it is stated once, in the argument table. Closes kb-add silently nests a note when --folder restates the content/assertions/ prefix #825.

@williamthorsen/kb

  • ./check gains enumerateNotePaths, which returns the note set enumerateNotes selects as
    store-root-relative paths without opening a single note.
  • ./taxonomy gains resolveDomain and resolveParent, so a consumer classifying notes against a
    taxonomy derives its answers from the same mapping the drift rules and the back-fill use, and the three
    cannot disagree about which folder a note observes.

♻️ Refactoring

  • enumerateNotes and enumerateNotePaths share one walk, scope matcher, and pruning pass via
    collectNoteLocations.
  • kb-curate's module-private readonly-tolerant resolver wrapper moves onto a requireWritable option on
    the shared knowledge-base resolver, which the survey path also uses. The wrapper's reconstruction of the
    source label from the explicit-KB input goes away with it.

🧪 Tests

  • New unit suites cover the survey and the declaration step: the mixed leaf/ancestor review-state case,
    both reachable declaration-failure causes, a store with no taxonomy and one with an empty taxonomy, and a
    note at the assertions root.
  • The survey's read-only contracts are pinned by instrumenting readFile to prove no note is opened, and
    by a stdin stream that never ends, so a regression that consumed stdin hangs rather than passing quietly.
  • enumerateNotePaths is tested for set equality against enumerateNotes under the same targets and
    excludes, which is the invariant the export exists to preserve.

Closes #794

`@williamthorsen/kb/check` gains `enumerateNotePaths`, which returns the same note set `enumerateNotes` selects as store-root-relative paths without opening a single note — for a consumer that needs the note set's shape rather than its content.

`@williamthorsen/kb/taxonomy` gains `resolveDomain` and `resolveParent`, the mapping from a note path to the domain it sits in and from a domain to its parent. A consumer that classifies notes against a taxonomy now derives its answers from the same pair the drift rules and the back-fill use, so the three cannot disagree about which folder a note observes.
`kb-add --survey` reports a knowledge base's shape so a capture can be placed against what the store declares rather than against a raw directory listing: the resolved KB, the domains its taxonomy declares with each one's description and review state, the notes sitting at or beneath each, and the folders holding notes that no domain declares. It writes nothing, opens no note, and returns without reading stdin, so it is safe to run before composing a note.

A store the registry marks `readonly: true` surveys successfully; only writes are refused. A store that declares no taxonomy surveys too, reporting every folder its notes occupy.

A malformed `.kb/config.yaml` or `.kb/taxonomy.yaml` now returns the new `invalid-config` error code with the offending file named, where it previously exited non-zero with a bare stderr line.
A `kb-add` capture into a folder no domain declares now records that folder in `.kb/taxonomy.yaml`, along with any undeclared folder above it, so a knowledge base's declared structure grows from the captures that happen rather than needing a separate curation pass. The result reports the domain the note landed in and every domain the write added; a note at the assertions root is reported as sitting under none.

`--domain-description` supplies the new domain's one-line description; without it the domain is declared bare. A described domain lands in `domains:` only when the capture was confirmed. `--auto` routes it to `provisional:` instead, as does every description-less capture, so an unreviewed entry stays visible as one.

A store with no `.kb/taxonomy.yaml` is left untouched; a store whose taxonomy is empty grows its first domain. A capture whose declaration fails still succeeds, reporting the written path and a warning naming what went undeclared.
The `kb-add` skill now measures a note's placement against the knowledge base's declared domains rather than against a raw listing of whatever folders exist on disk. It runs the helper's survey before classifying, and the proposal names the domain the folder matched, that domain's description, and the taxonomy file they came from, so the user can see what the placement was measured against. A folder holding notes that no domain declares reaches the user rather than being quietly resolved.

A new `## Placement` section states the policy: the taxonomy is a strong prior with the long tail absorbed by tags, a new domain is warranted by intent rather than note count, auto mode does not mint a top-level domain, a folder name restating a tag is expected, and a reviewed domain beats an unreviewed one where both fit.

The argument table covers `--survey`, `--domain-description`, and `--auto`, whose dual role — skipping the proposal and routing a declared domain to `provisional:` — is now stated. Result handling covers the domain a note landed in, the domains a capture declared, a note left under no domain, and the new `invalid-config` error code.

The worked invocation example carries a concrete `--folder` value, and the two redundant restatements of the `--folder` rule are cut so it is stated once, in the argument table.
… agent

A `kb-add` capture that declares a nested folder now reports which block each new domain landed in, so a confirmed capture no longer presents its unreviewed ancestor domains as reviewed. The ancestors a nested capture declares are always provisional, where the leaf of a confirmed described capture is not; a user told otherwise has no reason to go review the shelf the split exists to flag.

Each entry of the result's `placement.added` carries the domain's path alongside whether it awaits review, in root-to-leaf order, rather than the path alone.

The skill also states how to name a new domain: to match the form of the domains the survey reported, since the name is durable structure that later captures inherit.
@williamthorsen williamthorsen added feature Added or improved external functionality scope:agents labels Aug 7, 2026
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown

Dependency audit

Production dependency audit passed.

@williamthorsen williamthorsen self-assigned this Aug 7, 2026
@williamthorsen
williamthorsen marked this pull request as ready for review August 7, 2026 01:33
@williamthorsen
williamthorsen merged commit 5ac29c3 into main Aug 7, 2026
3 checks passed
@williamthorsen
williamthorsen deleted the 794 branch August 7, 2026 01:42
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:agents scope:kb

Projects

None yet

Development

Successfully merging this pull request may close these issues.

kb-add silently nests a note when --folder restates the content/assertions/ prefix Guide kb-add note placement with a per-KB taxonomy.yaml

1 participant