Tag scope is declared where membership claims are made
Type: ../types/design-proposal.md · Tags: kb-maintenance, document-system
Tags today have no declared scope, and the system's consuming surfaces disagree about what scope they assume: the validator enforces the tag-README marks over one collection, one navigation recipe sweeps three collections, another sweeps one, and the reader-facing wording of the complete mark ("every note carrying the tag") names no scope at all. The committed tag README contains only authored curation. ProperDocs composes a wider published view at build time: a rendered note's tag link routes across collection boundaries to the nearest declared landing page, while the generated augmentation enumerates only that landing's own collection. A reader can therefore follow a tag from a note to a published tag page whose authored content plus generated listing omits the note they came from. The mismatch is also live: an unqualified kb/ sweep finds four artifacts outside kb/notes/ that falsify a declared mark, the proposed participating set contains two of them, and the current documented three-collection sweep exposes only one. None of those scopes is named by the mark.
The reframing this proposal rests on: a bare tag string needs no scope. An rg sweep is always explicit about the paths it searches, so tagging a note commits to nothing scope-shaped. Scope becomes load-bearing only where a surface claims exhaustiveness or licenses omission — a complete or covered_by mark, a per-KB membership recipe, a skip rule that lets a consumer trust a mark instead of resolving exact membership, or a build-time view that presents itself as the tag's full listing. A cross-KB membership query may explicitly union multiple per-KB sets without changing any mark's scope. None of those claims establishes that task-level search is complete. The build is where the stakes are highest for human readers: ADR 025 makes the rendered page's authored-content-plus-generated-listing union the completeness surface ("completeness is the build's job"), so whatever scope that union enumerates is the tag's de facto extent for the published site. The committed README itself remains curated-only. So the design question is not "what scope do tags have" but "what scope do membership claims declare and enforce, and how do readers learn it."
The first version (2026-07-24) held three options open. This revision converges on one design: one namespace per KB — a tag string has one sense everywhere within its KB; one membership scope — every membership claim ranges over the declaring KB's participating library collections; retention of useful navigation facets — machine consumption does not disqualify a topical tag, while redundant provenance values disappear; and one home and identity for per-tag heads — a new kb/tags/ collection keeps the heads' introduction role, with <tag>-README.md as the canonical path and an ordinary collection README.md replacing the current special hub. The scope question and the adjacent "should there be two kinds of tag" question resolve together: the corpus supports topical tags that may also feed derived views, plus deletion of metadata already recoverable elsewhere — not global tags versus local tags or tags versus fields.
Adoption produces a new ADR for this combined decision rather than rewriting ADR 026 in place. The new record preserves ADR 026's tag-readme type, weight gates, and complete/covered_by mark shapes; defines explicit participation, the shared resolver, and projection-relative membership; preserves trace-learning as a navigational and machine-consumed tag while removing redundant source-family tags; replaces frontmatter-declared head identity with canonical filename identity; and supersedes point 7's tags-README.md hub and tag-indexes binding. It must also reconcile the sibling semantic contract for tags and tag heads, which owns what membership asserts and separates exact membership recovery, contextual head traversal, and task-level search. ADR 026 receives a short forward annotation only after the new design is implemented, preserving it as the historical record of the system it introduced.
Current state (as of 2026-08-20)
- Enforcement is collection-scoped. The validator's
complete/covered_bychecks build membership from a single-collection scan (collect_collection_tag_index(collection_dir)insrc/commonplace/lib/index_generated.py, consumed by thetag-readmetype rule invalidation.py). Impact expansion likewise constructs a same-collection README path, so editing a tagged artifact never re-validates a head in another collection. - The current collector already applies artifact-eligibility rules inside that collection. It excludes tag pages,
types/and.collection/infrastructure, and replaced archives. Widening the scan must preserve those exclusions explicitly rather than equating every Markdown file underkb/with a library member. - There is no committed head/tail document. A
<tag>-README.mdfile contains only authored, curated content. The repository's "curated head" term names that whole source artifact, not a stored first half. The configured ProperDocson_page_markdownhook calls_append_generated_tailto append## Other tagged notesin memory when uncurated members remain; the generated section exists only in the published page and is omitted when the authored README already links every member. - The published page's two tag surfaces use different scopes. The build-time tag-page augmentation enumerates only the landing page's own collection, but the rendered Tags: footer link resolves by proximity (
_find_tag_indexinsrc/commonplace/docs/properdocs_hooks.pywalks up to four directory levels), so akb/reference/note taggedkb-maintenancelinks across the boundary tokb/notes/kb-maintenance-README.md. Routing is effectively repo-scoped while the published listing is collection-scoped. - Head identity is encoded twice without an invariant between the copies. The type convention names a head
<tag>-README.md, while frontmatter separately declaresindex_key: <tag>; the schema requires a nonempty key but neither it nor the validator requires the key to equal the filename-derived tag. The router consequently scans candidates and trustsindex_key, so differently named files can claim the same tag and a filename can disagree with the membership key its marks check. No current use needs that aliasing. - Reader-facing surfaces disagree. The
tag-readmetype spec andnavigation.mdstate the per-tagcompleteclaim without a scope qualifier;AGENTS.md's by-tag recipe sweepskb/notes/ kb/reference/ kb/instructions/;navigation.md's canonical recipe sweepskb/notes/only. The legacy hubtags-README.mdis a second use of the type:index_source: tag-indexespluscomplete: trueranges over tag pages rather than tagged artifacts, and its description is the one surface that qualifies that distinct claim ("complete over the tag pages in this collection"). - The namespace is already shared in practice. A raw all-
kb/frontmatter sweep finds 20 tag strings in more than one top-level subtree. Nineteen are topical strings that keep one sense across those subtrees; the twentieth is the non-topicalx-articleresidue described below, shared by sources and one work snapshot. The topical collection difference is usually intended contribution (theory inkb/notes/, system description inkb/reference/), not meaning.kb/reference/carries 16 distinct tags, 14 shared withkb/notes/.kb-maintenanceappears in 21kb/notes/files and 18kb/reference/files (mostly proposals) — the reference side invisible to every curated surface. 10 of the 20 per-tag heads already link outsidekb/notes/editorially while their enforced claims stay single-collection. - Per-tag heads are introductions by recorded intent, and the strongest are authored ones. ADR 026 defines a head as "narrative orientation plus selective editorial picks", keeping the "introduction to a tag" role when complete enumeration left the committed artifact for the published build view. In the corpus, 5 of the 20 heads (
self-improving-systems,tool-loop,learning-theory,computational-model,links) are substantial authored introductions whose framing prose — reading instructions, entailment denials, area syntheses, a prior-work positioning — currently has no other home in the graph; 13 more open with a real orientation paragraph; 2 are plain routers. The separatetags-README.mdhub is a directory-scale navigation artifact, not an introduction to one tag. - Membership in the intended participating collections is notes-dominant for most tags. 17 of the 20 tags with heads have at least 80% of their members in
kb/notes/; onlykb-maintenance(nearly half its eligible members inkb/reference/) andtool-loop(about a third outsidekb/notes/) are strongly mixed, withobservabilitythe remaining sub-80% case.kb/instructions/currently contributes no members to any tag. Widening membership across participating collections changes the subject most heads introduce only slightly. - The notes collection's claim machinery does not engage heads. Heads escape
kb/notes/claim titles through the generic indexes exception; the reach gate has run on no head, and no review pair carries thetag-readmetype spec as criterion (general prose and structure gates have run on one head). The concrete guidance heads draw from the notes contract is its outbound-link grammar, not the claim machinery. - Machine consumption and navigation currently coincide for
trace-learning; source-family tags are different.trace-learning(103 members, only inkb/agent-memory-systems/) names a substantive cross-system class with its own comparative survey. It is also a machine-read Boolean: the review type andwrite-agent-memory-system-reviewskill tell authors to emit the tag, andsystems_matrix.pyderives a matrix column from"trace-learning" in tags. ADR 004's navigation rule supports retaining it — a large, useful facet is exactly what generated tag listings are for — but notrace-learninghead exists today, so ProperDocs renders the footer value as plain text instead of a link. By contrast, the 18 currentx-article/x-thread/x-postvalues inkb/sources/, plus one frozen work evidence snapshot carryingx-article, are redundant platform/container residue actively emitted bycommonplace-x-snapshot: its JSON sidecar already records the same classification asfamily, whilecapture, the source URL, and platform metadata also recover it and the snapshot contract tells authors not to put it intags.commonplace-github-snapshothas the same producer-contract defect: it writesgithub-issueorgithub-printo every Markdown snapshot even though the source and API URLs plus the raw API JSON preserve that distinction. No such GitHub snapshot is currently present, so this is a future-write defect rather than a corpus migration. - Violation count depends on the unnamed scope. An unqualified
kb/sweep finds four artifacts that falsify a mark while validating clean at collection scope. Two source ingest reports carrylearning-theorywithout any declaredcovered_bychild, but sources are non-participating under this proposal, so those apparent violations dissolve. The proposed participating set retains two violations:kb/agent-memory-systems/trace-learning-techniques-in-related-systems.mdcarrieslearning-theorywith none of its six declared children;kb/reference/proposals/revise-behavioral-authority-decomposition.mdcarriesartifact-analysis, whose README declarescompleteand does not link it. The currentAGENTS.mdthree-collection sweep sees only the reference proposal. - Free-form decay is observable. ADR 004 made tags freeform and predicted a known-tags registry; none was built. Eight
kb/work/artifacts still carryllm-interpretation-errorsafter that tag's rename tollm-reliability, and onekb/work/artifact carries a literal template placeholder in its tag slot. Nothing detects either. - A negative two-subtree boundary is not the library boundary.
kb/reports/is a non-library output collection andkb/tasks/is an operational surface; the proposal archive is frozen and link-isolated, yet five archived proposals still carry tags. The current collector admits those files: its archive guard recognizes.replaced.*.mdsnapshots, notproposals/archive/. A widened scan defined as “all ofkb/except work and sources” would therefore admit report output and keep live published tag listings linked into the archive unless the positive participation definition excludes both. - Marks currently declared:
completeon the legacy hub (over tag pages),discovery-README.md, andartifact-analysis-README.md;covered_byonlearning-theory-README.md. ADR 026 made the per-tag marks enforced-or-omitted precisely because a claim that tells exhaustive consumers to stop looking must never be silently false — the property the scope mismatch violates at repo scope. Its separate hub binding is retired by commitment 4 rather than widened into another membership claim. tagsis a schema field onnote-base.schema.yaml, and collections never own frontmatter semantics: a collection may not relativize what a committed field value means. That boundary's sanctioned moves permit extending a value set (each value meaning one thing everywhere) and pushing a collection-local distinction into a typed field on a collection-local type; a per-collection tag namespace — same string, two senses — is the move it refuses.- Shipped systems already implement mint-your-own vocabulary.
commonplace-initseeds user collections with zero tags and no heads; the machinery ships shared —kb/types/tag-readme.mdplus schema, the marks validator, and the promoted skills, which degrade cleanly at zero tags — andAGENTS.md.templateinstructs consuming projects to create heads "as they emerge" at 5+ notes per tag. This repo's vocabulary travels only as read-only library content: the current full install vendorskb/notes|reference|instructionsunderkb/commonplace/, heads and marks included, and the template's by-tag recipe sweeps host and vendored trees together. - The installed library is a reduced artifact projection, not the source corpus copied whole. The scaffold manifest and current install docs omit
kb/agent-memory-systems/,kb/agentic-systems/, andkb/articles/, even though the source proposal's intended membership includes them; this also differs from ADR 021's original inclusion of agent-memory reviews. Four per-tag heads (agent-memory,evaluation,computational-model,tool-loop) currently carry local links into the omitted collections.document-system-README.mdcarries two more local selections into sharedkb/types/: those links resolve after the source move tokb/tags/, but the same relative paths would resolve under absentkb/commonplace/types/after installation. Initialization performs no link rewrite. Moving and shipping the heads without six explicit projection-link dispositions would preserve dangling local links inkb/commonplace/tags/. - Rerunning initialization cannot perform a scaffold migration.
commonplace-initis intentionally additive: it creates absent files, preserves every existing file whether identical or different, and never prunes retired paths. After this adoption, rerunning it in an older project would add the new tag collection and upgrade instruction while retaining the old per-tag heads and hub underkb/commonplace/notes/, the old shared tag schema and type, old vendored collection contracts, the oldAGENTS.md.template, and old promoted skill copies. Existing installs therefore need an explicit replace/merge/prune procedure; adding participation declarations alone is insufficient. - Install isolation of marks is accidental. Because today's checks are collection-scoped, host heads and vendored
kb/commonplace/heads validate against disjoint corpora; no surface declares that boundary. kb/types/has no stable per-KB tag owner after installation. In this checkout the global type surface sits inside Commonplace'skb/, but ADR 021 installs it at host-levelkb/types/, shared and extendable by both the host and the read-only library while the library heads live underkb/commonplace/. Assigning the shared directory to the host would make shipped type specs affect host marks; assigning it to the library would make user extensions affect library marks; assigning it to both would overlap the supposedly isolated membership sets. No global type spec currently declares frontmatter tags, so excluding the surface loses no current tag navigation.cp-skill-connectis coherent under collection scope today, and only the notes destination benefits. Its curated-heads step prospects per destination collection and reads the destination's own<tag>-README.md, skipping the by-tagrgonly for that destination when the mark is present. Heads exist only inkb/notes/, so the step finds one only when the destination is notes; every other destination silently gets no curated tag signal, because the skill treats a missing head as "this tag has no head", not as an error.
Evidence update (2026-08-27)
Recent grounded search literature sharpens the navigation boundary without changing the proposed namespace or membership scope. Link-following and search separates contextual local steps from long-range selection, while pointer design separates retained, query-conditioned, and authored cues. The underlying human studies motivate an agent comparison but do not establish LLM behavior.
The resulting constraint is narrower than a new retrieval architecture. The
resolver owns exact tag membership. A head provides stable definition and
contextual routing. Task-level search may find relevant artifacts outside one
tag or rank members for the current query. A complete or covered_by mark may
license omitting only the exact membership operation it replaces; it never
certifies that broader discovery is finished.
Proposed design
Scope attaches to the membership claim, not the string. The design fixes the claims' scope once and derives the rest.
1. One namespace per KB: a tag string has one sense KB-wide. Everywhere within the KB, including the workshop and capture layers. This is the corpus's de facto state for the shared strings, and it is what the frontmatter-semantics boundary requires of a schema field. Intended contribution stays encoded by placement: the tag names the topic, while the collection names what kind of contribution belongs there; fragmenting the namespace by collection role would duplicate what the path already says. Within a KB, same-string-different-sense becomes an error rather than an idiom. The root AGENTS.md states that rule as a binding authoring instruction, the tags property guidance in note-base.schema.yaml mirrors it, and collection contracts may constrain use but never redefine a tag. The schema and validator enforce the tag-token grammar below, not sameness of sense; semantic review remains the detector until the registry rider adds a write-time screen. The index is the KB root, not the repository: where two KBs share a workspace — a consuming project's own kb/ with the vendored library under kb/commonplace/, or a reader install's vendored checkout (commonplace/) inside the project root — the same string in each is two namespaces, not a collision, and neither side is forced off ordinary words the other uses.
The tag-token grammar is ASCII lowercase alphanumeric segments separated by single hyphens: ^[a-z0-9]+(?:-[a-z0-9]+)*$, with a length of 1–63 characters. The 63-character ceiling reserves the fixed seven-character -README suffix within the existing 70-character authored filename-stem limit. The shared schema applies the pattern and length to every tags item; the tag-readme schema applies them to every covered_by item; and canonical-head validation applies the same grammar to the filename-derived tag. Empty values, uppercase, underscores, whitespace, non-ASCII characters, and leading, trailing, or repeated hyphens are invalid.
A surface shared across those roots belongs to neither tag namespace unless its artifacts carry an explicit owner. ADR 021's top-level kb/types/ has no such provenance: it mixes shipped global contracts with user extensions by design. This proposal therefore leaves that surface outside tag use altogether. A later design may add ownership and tag routing if a real need appears; membership exclusion alone would not solve the namespace ambiguity.
2. One membership scope: the declaring KB's participating library collections. Every collection present in a KB projection declares its tag-participation state in COLLECTION.md. A root-owned collection declares participating or non-participating; the shared kb/types/ collection declares tags prohibited because it has no single namespace owner. Missing state is a validation error, not an implicit non-participating default. Embedded foreign KB roots are pruned before their collections are discovered. A new root-owned collection therefore cannot silently change or silently evade the membership universe: it must declare a state, and only an explicit participating declaration widens membership. This creates a deliberate hard upgrade boundary: new scaffolds carry the declarations, while an existing initialized project must migrate every scaffold-controlled surface and add the declaration to each user-owned collection contract before the new validator passes. The missing-state diagnostic names the collection and accepted declarations; it does not infer compatibility state from path.
The adopting change creates kb/instructions/upgrade-from-previous-scaffold.md and links it from INSTALL.md and the commonplace-init command documentation. The instruction states that rerunning init acquires only absent files and is not synchronization. It tells the operator to upgrade the package, rerun init to acquire newly added files including the instruction, create a clean scaffold from the same installed release as the comparison source, and inspect local drift before replacement. It distinguishes framework-owned vendored files from user-owned host content: replace current package-owned files and promoted skill projections; merge the new clauses into the active root instructions and user collection contracts; preserve user extensions in shared directories; and prune only the adoption's enumerated obsolete heads, hub, and branches after their replacements exist. It ends by validating the host and vendored KB roots, their marks, canonical routing, and local-link closure. The instruction contains a named tag-scope migration section with the exact changed and retired paths; it does not ask an operator to infer the migration from a fresh tree.
One pure library resolver discovers those declarations for a KB root and returns the membership-eligible artifacts. It applies contract-declared subtree exclusions and the existing artifact-visibility rules: tag heads and generated, infrastructure, or retired artifacts do not become topical members merely because they are Markdown files. The validator and ProperDocs import the resolver. A command exposes the same result to skills, operators, and frontloaded recipes, including a by-tag projection, so AGENTS.md and navigation.md do not duplicate declaration-derived path lists in rg. Cross-KB navigation calls the resolver once per root and unions the results explicitly; no consumer independently reconstructs membership.
Every input that can change resolver membership is also an all-head invalidation input: participation state, declared exclusions or visibility, collection discovery, and KB-root or ownership boundaries. Impact expansion from a surviving declaration re-validates every marked head in that KB. Creation, deletion, or relocation can remove the old declaration that would have supplied that impact edge, so the root authoring contract and the scaffold-upgrade instruction require whole-tag-collection validation after those operations. Tests cover both declaration edits and the explicit whole-collection path; state changes are not treated as the only membership-changing event.
The source checkout's initial participating set is kb/notes/, kb/reference/ except proposals/archive/, kb/instructions/, kb/agent-memory-systems/, kb/agentic-systems/, and kb/articles/. kb/tags/ is explicitly non-participating: it holds 21 per-tag heads over that set — the 20 existing heads plus a new trace-learning head — an ordinary README.md collection landing, and a generated dir-index.md complete file listing; neither landing nor heads are topical members. kb/work/, its nested collections, kb/sources/, and the non-library kb/reports/ collection declare non-participating. kb/types/ declares tag use prohibited. kb/tasks/ remains an operational non-collection surface outside discovery.
Membership is evaluated over the declared collections actually present in a concrete artifact projection. The source checkout therefore ranges over the six collections above, while the currently installed vendored Commonplace KB ranges over its shipped notes, reference, and instructions collections; an omitted collection is absent, not an undeclared collection. Both projections retain the same tag meanings, but their membership sets differ because one contains fewer artifacts. Marks are recomputed and validated in each projection. If distribution later adds a participating collection, its copied declaration makes it enter the installed set without changing the resolver.
A reduced projection must still ship usable heads. In an initialized-project fixture, every local link from a shipped tag head must resolve inside that projection, every tag footer must resolve within its own KB root, and every retained mark must validate over the installed membership set. Six current selections require explicit adoption dispositions: four point into omitted external-system collections and may ship their target, use a stable external link, or be omitted; two point from document-system to shared type files and must use a projection-invariant target or be omitted. The new trace-learning head is source-only under the current bundle because every member and its primary survey route live in the omitted kb/agent-memory-systems/ collection; it enters an installed projection only when that collection does. A dangling local link or a head with no projected subject is not allowed. This link-closure rule does not add omitted artifacts to membership.
Tags on root-owned, non-participating artifacts keep their KB-wide sense and remain reachable by explicitly-pathed rg; they neither satisfy nor violate a mark. Workshop artifacts are rewritten or deleted, so counting them would make every mark churn with drafting; capture artifacts are tagged at ingest time without taxonomy discipline; archived proposals are reached only through the archive's deliberate entrance. The shared kb/types/ surface is different: because path does not identify one owning root, its artifacts do not carry tags under this design. The foreign-KB exclusion is different again: an embedded KB is another corpus — its strings belong to its own namespace and its marks range over its own participating collections, so neither side's artifacts enter the other's claims.
"Every membership claim" is literal: the per-tag complete and covered_by checks, the ProperDocs tag-page augmentation, each per-KB membership recipe, and every exact-membership skip rule consume the resolver's same assembled set. A cross-KB recipe is not a third membership scope: it explicitly returns the union of independently computed sets, such as membership(host) ∪ membership(vendored Commonplace). That union supports navigation but cannot license either KB's marks or skip rules across the root boundary. No mark licenses stopping task-level search. The retired hub's completeness-over-tag-pages claim is not generalized into this model; ordinary collection navigation replaces it.
3. Machine consumption does not disqualify a navigational tag; redundant provenance tags disappear. trace-learning remains the authored source of truth for positive classification. It names a subject readers use to browse the casebook, while systems_matrix.py legitimately derives its trace_learning Boolean from the same tag. The agent-memory-system review type, template, and write-agent-memory-system-review skill own the stronger type-local evidence obligation: the tag and the ### Trace-learning subsection must appear together. Schema or imperative validation and fixtures enforce both directions, correcting the current conditional's stale trace-derived tag spelling. This constrains when the globally stable tag applies without redefining its meaning by collection.
Adoption creates kb/tags/trace-learning-README.md, using the existing cross-system survey as its primary curated route, so ProperDocs turns all qualifying systems' footer values into links to the complete generated listing. All 103 current classifications remain in tags:; systems_matrix.py, systems.csv, systems-table.md, the collection README, and the historical design record continue to treat the tag as the source. A future requirement for explicit negative/unknown states or structured subtypes may justify a field and a field-backed website facet, but this adoption adds neither duplicate authored state nor a second faceting mechanism.
The x-*, github-issue, and github-pr values are removed without replacement because their platform/container distinctions are already recoverable from capture metadata and source URLs, as the snapshot type states. commonplace-x-snapshot and commonplace-github-snapshot stop writing those values into Markdown tags, and both test suites change with the output contract. All 19 current Markdown occurrences lose the values: 18 source snapshots and the frozen work evidence copy at kb/work/natural-language-theory-human-agent-contribution/evidence/defazio-source.snapshot.md. Editing the frozen copy is a one-time system migration of frontmatter only, not a source refresh; its captured body and provenance remain unchanged. GitHub needs no corpus migration because no current snapshot carries either value. X's existing JSON family property remains capture metadata, while GitHub's source and API URLs plus raw API JSON preserve its distinction; neither producer gains a replacement field. After migration, tags: carries topical navigation vocabulary whether or not a derived view also consumes a tag; redundant provenance data is absent.
4. Per-tag heads move to canonical paths in kb/tags/, a global collection that keeps their introduction role. The reason a library-wide head does not belong in kb/notes/ is contract fit, not that heads are mere routers: the head's recorded identity is an introduction to its tag, while the notes contract's claim machinery does not engage heads and its outbound table gives their cross-collection link posture no type-specific license. The new collection is global within one KB but, unlike shared kb/types/, has exactly one root and one owner; global does not mean contract-free. Its COLLECTION.md declares the tag-readme type (the row leaves kb/notes/COLLECTION.md) and a text contract that carries the introduction function:
- a quality goal of accurate subject introduction, discriminating context phrases, and routing economy — the pointer account explains why a stable cue is useful without treating it as a query-relative relevance oracle; connecting is what a good head does by introducing well, not a replacement goal;
- an outbound-link grammar spanning every library collection, authorizing stable external links when an installed projection omits a local target, and restating the
kb/work/andkb/sources/local-link exclusions heads currently inherit from notes' contract; - topical
<tag>titles; - authoring-time orientation guidance, which today lives only on maintenance-path surfaces (the type spec and the audit instruction), never in a collection contract — and the collection contract is what authors load.
Each per-tag head is a direct child at kb/tags/<tag>-README.md. Its tag identity is the filename stem with -README removed; the tag-readme type and canonical location make index_source: tag redundant, while filename identity makes index_key redundant. The type schema prohibits both fields, and they leave every head's frontmatter. Tag strings use commitment 1's exact token grammar, making the mapping exact and portable rather than a lossy slugification. A KB root can therefore have at most one head for a tag by filesystem construction, and resolution is direct path construction rather than a scan or metadata lookup. The contract accepts the 20 existing heads' authored framing prose unchanged and preserves their editorial selections except where the installed-projection link-closure rule deliberately chooses omission; relocation mechanically rewrites relative links and removes the redundant identity fields. The new twenty-first head introduces trace-learning, routes first to the cross-system survey, and lets the generated augmentation supply complete membership without duplicating the survey's analysis.
A tag does not require a head. If the canonical file is absent, ProperDocs renders the footer tag as plain text, preserving the current fallback; it neither scans for an alternate landing nor synthesizes a page. Head creation remains the authoring choice described by the 5+ note guidance, while an existing head has exactly one identity.
The collection gets an ordinary README.md, governed as a directory landing rather than typed as a tag-readme. It carries concise orientation and whichever editorial groupings remain useful from the current hub; the normal build-time dir-index.md supplies complete enumeration of the collection. The legacy tags-README.md is therefore retired, not relocated as a twenty-second head. Its index_source: tag-indexes binding and the corresponding schema, validator, and build branches disappear. Per-tag heads are then the only tag-readme artifacts, so the type itself is sufficient to select the build behavior and complete and covered_by retain one membership-shaped meaning. The generated-index type also drops its legacy tag and tag-indexes compatibility branches; it keeps index_source: directory only, for virtual directory listings.
Only the tag-readme type contract follows kb/types/ onto the shared, user-extendable surface; the tag collection itself does not. Heads are knowledge artifacts under ADR 021's isolation split: commonplace-init scaffolds host kb/tags/ with its COLLECTION.md and an empty-vocabulary README.md, but no tag heads, so it never seeds this repo's vocabulary into user space — consuming projects mint their own tags on the shipped machinery, as AGENTS.md.template already instructs. This repo's landing and projection-eligible heads ship read-only with the rest of the library (kb/commonplace/tags/ after adoption); the source-only trace-learning head is omitted while its owning subject collection is omitted. Canonical resolution starts at the declaring KB root, so host tag x resolves to kb/tags/x-README.md and vendored tag x resolves independently to kb/commonplace/tags/x-README.md. Shared kb/types/ participates in neither root's membership set and admits no tag declarations.
The move is the obligatory final migration step inside one atomic adopting change — not a free choice or a later release. Membership assembly, validator behavior, build behavior, recipes, mark wording, projection-link dispositions, and the new collection contract can be prepared while paths are stable; then the 20 existing per-tag heads relocate via commonplace-relocate-note, which rewrites consumers and records published redirects, and the new trace-learning head is created at its canonical path. Canonical-path resolution and all 21 heads land together in the source checkout: there is no released interval in which the resolver points at kb/tags/ while heads remain in kb/notes/, and no legacy index_key or proximity fallback. The installed projection applies the source-only-head rule above. Useful orientation and grouping from tags-README.md move into the new collection landing before the legacy artifact is retired; its inbound links route to kb/tags/README.md and its published URL redirects there. The automatic consumer rewrite covers links, not conventions: cp-skill-connect's curated-heads step currently resolves heads per destination, so it must be rewritten to construct the declaring root's canonical path in the same change, and kb/instructions/maintain-curated-indexes.md is checked for the same pattern. Adoption is not complete while any per-tag head remains under a collection contract that does not fit it, the legacy hub remains live, a consumer still discovers heads by metadata scan or proximity, or the initialized-project tag fixture is not link-clean.
Rider — known-tags registry. Registered tag strings (at least: every tag with a README) become reserved terms via write-time vocabulary collision controls, screening new senses and catching rename stragglers and placeholder leaks at write time. Reservation is per KB root — nothing is reserved across a KB boundary. Adoptable separately; nothing above depends on it.
What flips
Under this design, the validator's membership query widens from one collection to the shared resolver's participating set, and impact expansion follows a changed eligible artifact's tags to their heads across collection boundaries. Adding or editing a COLLECTION.md without a participation state fails. Editing any declaration that can change resolver output — participation, exclusions or visibility, root identity, or ownership — re-validates every marked head in that KB root; creating, deleting, or relocating a collection requires an explicit whole-tag-collection validation because the old declaration may no longer exist to trigger impact expansion. Tag declarations on shared artifacts with no owning KB, initially the global specs under kb/types/, also fail. The root authoring contract states one-string-one-sense, while the schema and validator enforce commitment 1's tag-token grammar.
The committed <tag>-README.md remains curated-only. At publication time, ProperDocs augments its rendered page with eligible members not already linked in that authored content, so the rendered union covers the resolver's set. The per-KB recipes in AGENTS.md and navigation.md call the resolver command; the scaffold's host-plus-vendored recipe calls it once per root and labels the union as navigation, not evidence about either root's marks. Every surface that states a mark's meaning — the tag-readme type spec, navigation.md, and skip-rule wording — gains the scope qualifier ("every membership-eligible artifact in this KB projection carrying the tag"). A declared mark licenses skipping exact by-tag membership resolution for every participating destination in that projection. It does not license stopping task-level search. Curated-head signal reaches the non-notes destinations that today get none.
Head resolution changes from metadata/proximity search to canonical path construction: derive the tag exactly from a validated <tag>-README.md filename for mark checks and rendered augmentation, and resolve a page's tag t to the declaring root's tags/t-README.md when that file exists. A headless tag renders as plain footer text. The relocation and this resolver ship atomically. A complete head must link every eligible member in its authored content, cross-collection ones included; sectioning is editorial, the weight gates are unchanged, and a tag whose membership outgrows the gates takes ADR 026's existing exits (drop to selective, split). The old tag-indexes path is removed; kb/tags/README.md and the ordinary generated dir-index.md provide collection navigation. Routing stays wider than membership: a root-owned, non-participating artifact's footer tag link may still route to the head, whose published page owes it nothing — by declaration rather than by accident. A foreign-KB page's footer resolves within its own KB root: routing, like membership, does not cross a root.
Operativity
The change has these operative channels:
- The membership resolver and its command are binding system-definition code. They own KB-root discovery, explicit participation states, projection-aware artifact eligibility, foreign-root pruning, and by-tag results. All other channels consume them rather than reimplementing their rules.
- The root and scaffolded authoring contracts are binding for agents:
AGENTS.md,AGENTS.md.template, and thetagsproperty guidance innote-base.schema.yamlstate one-string-one-sense and the exact tag-token grammar. They also require whole-tag-collection validation after creating, deleting, or relocating a collection. Collection contracts may route a tag but cannot redefine it. Semantic review detects sense collisions until the optional registry supplies a write-time oracle. - The validator is binding: it rejects missing participation states, invalid tag strings, shared-surface tag declarations, noncanonical
tag-readmelocations, and obsolete tag-head identity fields; checks marks over resolver membership; re-validates affected heads after eligible tag edits; re-validates every marked head after any surviving declaration that can change membership; and removes the retired hub branches. - The build is the human completeness channel: tag-page augmentation is selected by the
tag-readmetype and keyed from the filename, membership comes from the resolver, footer routing uses the canonical path when present and plain text when absent, and ordinary collection-index generation replaces the hub-specific listing. - The frontloaded recipes and
cp-skill-connectare advisory and licensing consumers: per-KB recipes call the resolver command, cross-KB recipes name their union, and the skill's exact-membership skip rule and curated-heads step use the widened within-root mark license and canonical head path. No mark suppresses task-level search. A missing head still means "no head", so an unedited skill would degrade silently rather than fail. - The trace-learning classification contract and source-family producers are binding: the review schema/type/template and
write-agent-memory-system-reviewkeep the tag and### Trace-learningsubsection in parity,systems_matrix.pycontinues to derive the matrix Boolean from the tag, and fixtures/tests guard both uses. The new canonical head makes the same classification navigable on the site. Separately,commonplace-x-snapshotandcommonplace-github-snapshotstop emitting source-family Markdown tags, with both snapshot test suites guarding the change and existing non-tag metadata retaining the distinctions. - The scaffold and install-projection fixture are binding distribution channels:
commonplace-initships the source and host tag-collection split, explicit collection states, root-aware recipes, and projection-eligible read-only Commonplace heads; the fixture validates both KB roots independently, retained marks, canonical routing, source-only-head omission, and shipped-head link closure. - The upgrade-from-previous-scaffold instruction and fixture are the binding migration channel for existing installs. The instruction distinguishes init's additive acquisition from synchronization, separates replaceable framework files from merge-only user files, enumerates the tag-scope migration's retired paths, and requires final validation. A fixture starts from a checked-in pre-adoption scaffold, follows those same replacement, merge, and prune steps, preserves sentinel user additions, refreshes promoted skill copies, and reaches the same validated shape as a fresh scaffold without stale heads or hub files.
The kb/tags/ COLLECTION.md joins the system-definition surface as the heads' binding authoring contract; its normal README.md is a knowledge artifact rather than another typed head. Review-sweep targeting is path-scoped, so kb/tags/ must enter sweep scope in the adopting change or heads drop out of the gate-review population with no surface reporting it. No new semantic oracle is claimed: the per-tag marks keep ADR 026's enforcement machinery and warrant; membership range changes, while identity and participation become structural invariants.
Alternatives considered
- Formalized collection scope — a tag is
(collection, string); the same string in two collections is two tags; the footer router stops at the collection boundary. Rejected: it entrenches the fragmentation (kb-maintenance's reference side stays invisible to every curated surface, and no mark can ever license the union sweep); it hardens a state no consumer fully inhabits, since heads, recipes, and the build's routing already behave cross-collection; and formalizing per-collection senses for a schema field is the move the frontmatter-semantics boundary refuses. - Unqualified repo scope — marks checked over all of
kb/. Rejected: it drags drafts, captures, operational artifacts, and frozen archive residue into membership — and in consuming projects it swallows the vendoredkb/commonplace/corpus into every host mark. A positive eligibility rule is needed regardless; unqualified repo scope is this design without a defensible library boundary. - Declared per-README dual scope — a
scope: kbopt-in on the tag-README, defaulting to collection scope. Rejected: readers must track which kind of promise each mark makes, and every consumer must read the field or it decays into decoration. Once redundant source-family values leavetags, no current tag needs a per-README exception, so the mechanism would ship without a user. - Center-of-mass placement — each head lives in the collection holding its tag's dominant membership. Rejected: it forces every collection to declare the
tag-readmetype and fragments the heads' authoring contract, while 17 of the 20 tags are at least 80% notes-dominant, so the scattering changes nothing for most heads and hands the mixed ones (kb-maintenance,tool-loop) an arbitrary host. - Frontmatter-declared head identity — retain
index_key, enforce that it matches the filename, and reject duplicate keys. Rejected: once all heads are direct children of one dedicated collection, the filename already supplies an exact unique identity; retaining a checked copy adds schema and invalidation machinery without enabling aliases the design wants.index_source: tagis redundant for the same reason: the dedicatedtag-readmetype selects that behavior, while the retired hub was its only alternative source. - Implicit non-participation — treat a collection with no participation declaration as outside membership. Rejected: a newly added library collection could then remain invisible to complete heads, the build, and agent recipes through omission rather than decision. Explicit
non-participatingpreserves positive opt-in without making absence silently meaningful. - Force the installed library to equal the source corpus — ship every source-participating collection so both membership sets are identical. Rejected for this design: it would reverse the package's deliberate omission of research collections merely to simplify tag scope. Membership over the concrete projection preserves one semantics without making tags decide distribution policy; the install fixture and head-link closure address the actual correctness obligations.
Free choices
- Declaration syntax. Participation state lives in
COLLECTION.md; the adopting ADR may choose a frontmatter field or a standardized body clause, and whether a participating collection names excluded subtrees in the same declaration or through the existing artifact-visibility mechanism. The three semantic states are fixed — root-owned participating, root-owned non-participating, and shared tag-prohibited — as is failure on missing state. The foreign-KB boundary may use the knownkb/commonplace/path first or generalize ADR 021's read-only marker. Whatever syntax is chosen, every membership-affecting declaration is an all-head invalidation input, and the root contract covers deletion and relocation with explicit whole-tag-collection validation. - Resolver command interface. Command name and whether its default output is paths, path-plus-description, or structured data. The pure resolver semantics and the requirement that recipes call it are not free. The query-specific pointer evidence is reason to preserve an optional presentation layer, not evidence that an LLM-facing summarizer should ship.
- Projection-sensitive head links. Four current selections point into collections omitted from the installed projection; each target may be shipped, replaced by a stable external link, or omitted. Two
document-systemselections point into sharedkb/types/; each needs a projection-invariant target or omission because one unchanged relative path cannot address that directory from both source and installed tag roots. The package policy may choose among those six outcomes; shipping a dangling local link is not an option. - Excluded-layer footer links. Whether a
kb/work/orkb/sources/page whose tag has an existing head routes to it (navigation without membership) or renders plain text. A tag with no head renders as plain text by commitment 4. - Sources topic tags. Whether non-genre tags remaining on ingests after eviction stay as search-only convenience or are dropped; revisit membership if a sources-side consumer ever wants tag routing rather than an explicit sweep.
- Head-only syntheses. Whether framing claims that currently live only in heads (the
tool-loopresolution section, theself-improving-systemsprofile argument) should later be distilled into notes — where the claim machinery can engage them — with the head keeping the summary; a maintenance question under the new contract, not an adoption gate. - Registry rider timing. With adoption or separately.
Adoption criteria
- One pure membership resolver assembles the eligible artifacts from declarations and exposes a by-tag operator command. The validator, ProperDocs augmentation,
cp-skill-connect, per-KB recipes, and every skip rule consume it; no consumer maintains a separate path list or eligibility implementation. The footer router resolvestdirectly to the declaring root's canonicaltags/t-README.mdwhen present and otherwise renders plain text, with headed and headless cases covered by build tests. Routing remains wider than membership by declaration, and every surface that states a mark's meaning names the projection boundary. - Every discovered collection in the source checkout and initialized-project fixture declares a participation state; missing state fails validation. The source checkout declares
kb/notes/,kb/reference/except its proposal archive,kb/instructions/,kb/agent-memory-systems/,kb/agentic-systems/, andkb/articles/participating; tag, work (including nested collections), sources, and reports collections non-participating; and sharedkb/types/tag-prohibited. Tasks remain an operational non-collection surface. Changing participation, exclusions or visibility, root identity, ownership, or collection discovery re-validates every marked head in that KB through impact expansion or the documented whole-tag-collection path; tests exercise at least a participation edit, an exclusion edit, and collection creation/removal. A shared surface cannot participate without first gaining artifact ownership semantics. New scaffolds carry the declarations, and each missing-state finding names the affected collection and accepted declarations rather than inferring a path-based default. - Root
AGENTS.md,AGENTS.md.template, and thetagsproperty guidance innote-base.schema.yamlstate one-string-one-sense and the exact grammar^[a-z0-9]+(?:-[a-z0-9]+)*$with a 1–63-character limit. The shared schema enforces it fortags, thetag-readmeschema enforces it forcovered_by, and canonical-head validation enforces it for filename-derived identity. Collection contracts cannot redefine values; semantic review remains the sense-collision detector until the optional registry ships. - The build is coherent for membership-eligible artifacts: a tag link from an eligible artifact lands on the declaring root's canonical tag page, whose authored README content or build-time augmentation includes it; the committed README remains curated-only.
- The two violations within the proposed participating set resolve on the new terms: the
kb/agent-memory-systems/note gains one oflearning-theory's declared children or the children list is revised; theartifact-analysishead links its proposals member or that member drops the tag. The two source-ingest apparent violations remain outside membership by declared non-participation and therefore enter no mark's membership claim. trace-learningremains intags:as the single authored source for navigation and matrix classification. The review schema, type instructions, template, and writing skill require the tag exactly when the### Trace-learningsubsection applies; tests cover both directions and correct the conditional's staletrace-derivedtag spelling.systems_matrix.pycontinues to derivetrace_learningfrom the tag, and its fixtures protect that contract. A new canonicaltrace-learninghead routes first to the comparative survey and its rendered augmentation lists every eligible tagged review. Separately, all 19 current Markdownx-*markers — 18 source snapshots and one frozen work evidence snapshot — are removed without replacement;commonplace-x-snapshot,commonplace-github-snapshot, and both test suites stop writing source-family values to Markdown tags; X's existing JSONfamilyand GitHub's existing source/API metadata remain unchanged. GitHub has no current corpus instances to migrate.- Marks remain enforced-or-omitted at the declared scope: no reader is licensed to skip an exact membership query wider than what is checked, and no mark is presented as evidence that task-level discovery is complete.
- In a consuming project no membership claim crosses a KB root: host marks are unaffected by
kb/commonplace/artifacts and vice versa; the installed Commonplace projection ranges only over participating collections actually shipped; sharedkb/types/participates in neither set and rejects tags.commonplace-initscaffoldskb/tags/with its non-participating contract, an empty-vocabularyREADME.md, explicit host collection states, and no heads; it ships this repo's landing plus projection-eligible heads read-only underkb/commonplace/tags/, omittingtrace-learningwhilekb/agent-memory-systems/is absent. An initialized-project fixture validates each root's marks and canonical routing independently and rejects every dangling local link or subjectless head in the installed projection; all six current projection-sensitive selections have an explicit disposition. Cross-KB sweeps are documented as resolver unions for navigation, not membership or skipping. - The adopting change creates
kb/instructions/upgrade-from-previous-scaffold.md, links it fromINSTALL.mdandkb/reference/commands.md, and makes its tag-scope migration section exact about framework-owned replacements, user-owned merges, promoted-skill refresh, and retired paths. It warns thatcommonplace-initonly acquires absent files. A checked-in pre-adoption fixture follows the documented migration, preserves sentinel user content and shared-type extensions, removes the old vendored heads and hub, installs the new schemas, contracts, recipes, skills, landing, and heads, and then passes both-root validation, mark checks, canonical routing, and shipped-link closure. A fresh scaffold and the upgraded fixture agree on every framework-owned path. - Last, completing adoption inside the same atomic change: all 21 per-tag heads — 20 relocated and
trace-learningnewly created — are direct children ofkb/tags/, whoseCOLLECTION.mddeclares the type and the introduction-function text contract of commitment 4; existing head prose remains unchanged, relative links are mechanically rewritten, and editorial selections change only through an explicit projection-link disposition; tag strings obey commitment 1's exact grammar and length limit; a head's tag is derived by removing-README.mdfrom its filename; thetag-readmeschema prohibitsindex_sourceandindex_key, and both fields are absent from every head; the generated-index type retains onlyindex_source: directory; the type row has leftkb/notes/COLLECTION.md;kb/tags/README.mdis the ordinary collection landing; useful hub prose has moved there;tags-README.md,index_source: tag,index_source: tag-indexes, and their schema, validator, and build branches are gone; consumers that resolved heads by metadata, proximity, or destination collection (cp-skill-connect's curated-heads step, andkb/instructions/maintain-curated-indexes.mdif it shares the pattern) construct the declaring root's canonical path;kb/tags/is in review-sweep scope; entry points route to the new paths; published URLs redirect. The resolver and all canonical heads land together in the source checkout, with no legacy fallback; the installed projection then selects only eligible heads. No per-tag head remains under an ill-fitting collection contract or nested below the tag collection root. - The adopting change lands the new ADR with the relationship to ADR 026 stated above; only after implementation does ADR 026 gain its forward annotation. The proposal then archives through the normal adoption lifecycle.
Risks
- Membership-definition changes have KB-wide blast radius. A new collection must declare a state; declaring it non-participating leaves membership unchanged, while participation, exclusions, visibility, discovery, or root/ownership changes can alter every mark's universe, so
completemay become harder to hold at once. Surviving declaration edits trigger all-head impact expansion; creation, deletion, and relocation require the documented whole-tag-collection validation. ADR 026's existing exits and weight gates handle heads that no longer fit. - Sharpened collision stakes. With local senses outlawed, a same-string-different-sense pair within a KB is an error rather than an idiom. The authoring contract makes the violation explicit and semantic review can report it, but only the optional registry can prevent it at write time.
- Projection-specific counts can surprise. Source and installed Commonplace heads carry the same mark semantics over different concrete artifact sets. A reader or test that compares raw counts without naming the projection can mistake a valid subset for drift. Resolver-based recipes and per-projection validation make the distinction explicit; link closure prevents omitted artifacts from leaving unusable curated routes.
- Structural correctness can be mistaken for navigation quality. Resolver parity and valid marks establish membership relations, not whether a head's fixed cues or a query-conditioned result lead an agent to the right task material at acceptable cost. The adopting ADR must avoid a performance claim; a later bounded agent trial can compare presentations without weakening the structural acceptance criteria.
- Partial adoption reproduces the disease. Flipping prose without the shared resolver, or the validator without the published-page augmentation, recreates marks that read one scope and check another. Consumers extend the risk from falsehood to silence: relocating heads without
cp-skill-connectfails silently, activating canonical resolution before relocation makes every head disappear, retaining metadata/proximity fallback preserves two identities, and adding thetrace-learninghead without the review-contract parity guard lets classification drift while unchanged X/GitHub producers recreate redundant source-family tags. Leavingtags-README.md, thetag-indexesbranches, or additive-init leftovers live likewise preserves two landing models. The adopting change must move resolver, enforcement, build, authoring contracts, producers, derived outputs, path consumers, identity-field removal, relocation, upgrade instruction and fixture, projection fixture, and hub retirement together. - Existing projects meet a hard upgrade boundary. A project initialized before adoption has old package-owned schemas, contracts, heads, recipes, and skill projections as well as user-owned collection contracts without participation declarations. The new validator fails until those surfaces migrate. This is intentional under the no-backwards-compatibility policy, but it must be an explained migration rather than a surprise:
kb/instructions/upgrade-from-previous-scaffold.md, itsINSTALL.mdand command-reference links, the upgrade fixture, and actionable missing-state diagnostics provide the path. Rerunning init alone is explicitly insufficient, and no inferred compatibility state weakens the explicitness invariant. - Adoption can break installs. A resolver that traverses
kb/instead of identifying the declaring root hands every host mark the vendored corpus and every vendored mark the host's — silent falsification in both directions. A resolver that assumes source collections exist in the installed projection instead reports or links absent artifacts. The scaffold and initialized-project fixture must ship and test root boundaries, present declarations, reduced membership, canonical routing, and head-link closure together. - The boundary can become tribal knowledge. An agent that ignores the participation declarations may sweep repo-wide, find a workshop draft or archived proposal carrying a covered tag, or assign shared type specs to the wrong KB. The declaration-derived recipes and the shared-surface prohibition must be visible operator rules, not merely implementation details.
Relevant Notes:
- Indexes lower recall when they suppress retrieval that would find more — rests-on: a mark whose enforced scope is narrower than its stated scope can suppress the more complete sweep the marks exist to preserve
- A derived copy of recomputable truth must be checked or absent — rests-on: scope is a parameter of the derived claim, so it must be part of what the check enforces or the claim must not be made
- Load-bearing vocabulary collisions should be prevented or visibly scoped at write time — rests-on: one-string-one-sense is this invariant applied to tag strings within a KB root; the authoring contract makes violations visible errors now, while the registry rider can later prevent them at write time
- An enforced tag-README is a MOC with a machine-checked contract — rests-on: the head's identity is the MOC's orientation-plus-picks role with one checkable claim fastened on; relocation must carry that identity rather than replace it with routing
- Link-following and search impose different metadata requirements — rests-on: a canonical head and long-range search expose different context at the next-read decision
- Pointer design tradeoffs in progressive disclosure — rests-on: fixed, query-time, and authored pointers have different cost, availability, specificity, and accuracy profiles
- Knowledge-access architecture must be evaluated end to end — rests-on: exact membership recovery is one access checkpoint and cannot proxy for task success