#794 Guide kb-add note placement with the store's declared taxonomy - #1223
Merged
Conversation
`@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.
Dependency auditProduction dependency audit passed. |
williamthorsen
marked this pull request as ready for review
August 7, 2026 01:33
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)
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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.yamlgave a base somewhere tostate 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-addhelper--surveyreports the destination's shape: the resolved knowledge base, its.kb/taxonomy.yamlpath,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: truesurveys successfully;only writes are refused.
the declared structure stays connected and a nested capture mints no
taxonomy.orphanwarning.--domain-descriptionsupplies a new domain's one-line description; without it the domain is recordedbare.
--autoroutes the domain toprovisional:, as does any capture that supplied no description.placementin the result names the domain the note sits in and every domain the capture declared, eachcarrying the block it landed in. A note at the assertions root reports as sitting under no domain.
went unrecorded; the folder surfaces as
taxonomy.undeclaredon the nextkb check.its first capture.
.kb/config.yamlor.kb/taxonomy.yamlreturns the newinvalid-configerror code with theoffending file named, where a survey previously exited non-zero with a bare stderr line.
kb-addskill## Placementsection states the policy: the declared taxonomy is a strong prior with the long tailcarried 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.
description, and the taxonomy file they came from, so the user can see what the placement was measured
against.
--folder, and the two redundant restatements of theassertions/-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./checkgainsenumerateNotePaths, which returns the note setenumerateNotesselects asstore-root-relative paths without opening a single note.
./taxonomygainsresolveDomainandresolveParent, so a consumer classifying notes against ataxonomy 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
enumerateNotesandenumerateNotePathsshare one walk, scope matcher, and pruning pass viacollectNoteLocations.kb-curate's module-private readonly-tolerant resolver wrapper moves onto arequireWritableoption onthe shared knowledge-base resolver, which the survey path also uses. The wrapper's reconstruction of the
sourcelabel from the explicit-KB input goes away with it.🧪 Tests
both reachable declaration-failure causes, a store with no taxonomy and one with an empty taxonomy, and a
note at the assertions root.
readFileto prove no note is opened, andby a stdin stream that never ends, so a regression that consumed stdin hangs rather than passing quietly.
enumerateNotePathsis tested for set equality againstenumerateNotesunder the same targets andexcludes, which is the invariant the export exists to preserve.
Closes #794