A declined Consolidation is a decision about a same-kind cluster, not a property of any one page, so it is recorded once in CONSOLIDATION_EXCLUSIONS.yaml in that kind-folder rather than duplicated in page frontmatter. Each exact-cluster record carries its members, a human-readable reason, their Git blob object IDs as the validation fast path, and semantic fingerprints as the fallback: fingerprints cover canonical title, summary, typed relationships, supersedes, and body, but ignore tags, source date, volatility, YAML formatting, and field order.
Before a cluster is put to the user at all, it is assessed: wiki-linter reads every member in full from one committed snapshot and returns one disposition — consolidate, relate or conflict — with a short rationale and an optional survivor or typed-edge recommendation. The assessment is read-only and advisory; accepting a consolidate still hands off to the ingest flow, which authors the merged body and pins the plan to the snapshot it assessed. Everything here preserves ADR-0021’s lossless-delete and confirm-first invariants.
wiki-linter makes the read (on the same model tier as the rest of lint), the human still decides, and wiki-ingest still authors the accepted merge.HEAD, and a semantic fingerprint over the decoded title, summary, typed edges and body — deliberately blind to tags, source_date, volatility, unrelated keys, field order, YAML formatting and line endings. A matching blob ID is the fast path; a matching fingerprint is the fallback, and fix consolidation-exclusions rewrites the cached ID so the fast path comes back.page rows — the same HEAD snapshot the cluster was found in — and reparses nothing, which is what keeps a vault full of exclusions cheap to lint. The one thing the index does not carry is conflict edges: check parses committed frontmatter for those only once a cluster has survived suppression, so a clean vault pays nothing.fix consolidation-exclusions --prune offers to drop the rest. A member rejoining, or any other set — a smaller cluster, or a superset once a new near-duplicate joins — receives a fresh assessment, because the user’s “no” was about the pages they saw together. Keeping overlapping records independent means a user can decline {a,b}, later see {b,c}, and answer that one differently.contradicts or supersedes between candidate pages already proves they must remain separate and needs no registry record: a Consolidation would merge a live conflict or re-open a settled replacement. That is a fact about the vault rather than a decision a user made, which is exactly what distinguishes it from an exclusion..wiki-knowledge/. A declined cluster is knowledge about the vault’s content, so it belongs in the vault’s history where it can be reviewed, diffed and reverted; the index is a disposable cache (ADR-0010) and .wiki-knowledge/ is gitignored. One file per kind-folder follows from a cluster never mixing kinds (ADR-0027), so a registry is read exactly when its own folder is in scope.exclusion add captures one HEAD snapshot, verifies it has not moved before committing, and writes canonical YAML; fix consolidation-exclusions owns the cache refresh and the confirm-first prune. Humans still inspect and delete records by editing the file, and a malformed file fails open — it suppresses nothing and is reported — because a registry that cannot be read must never hide a proposal.assessed: {head, members: [{page_ref, blob_oid}]}, and ingest refuses it once HEAD or a pinned member has moved: the merged body was authored from those exact revisions, so a plan edited or delayed past a commit must be re-assessed rather than merged stale.--full, or a clone, so a decision the user made has no durable home.supersedes-style record keeping both pages. ADR-0021 already settled that a Consolidation is a lossless delete; an exclusion is not a relationship between pages at all, it is a decision not to act.blob_oid and fingerprint to page; a bump drops and rebuilds from HEAD, the existing migration strategy. PageChange gains oid, so a HEAD-read consumer can pin a blob without a second git walk, and Index.pageFacts(pageRefs) is the one reader of the cached pair — a page the index does not hold reads as “cannot be read at HEAD”.enchiridion assess (the read-only assessment input) and enchiridion exclusion add (the one registry write path). enchiridion fix consolidation-exclusions gains --prune for the confirm-first half, and FIXES entries take a FixOptions — each flag belonging to one fix, refused by every other.IngestPlan feature lands in two places — ingest.ts’s module comment (the schema’s only spec) and wiki-ingest step 4’s agent-facing shape — and assessed is a top-level key on the consolidate variant only.wiki-lint gains a step (assess every cluster before judging) and a mechanical check (consolidation-exclusions), so the catalogue, its reference and the wiki-linter brief all name them. Malformed registries fail open for candidate reporting and produce a high-priority integrity finding; a semantically equivalent blob-ID refresh is an auto-fix, and persisting stale-member pruning remains confirm-first.wiki-linter stays on the cheap tier: the recommendation is advisory and human-confirmed, while the accepted Consolidation’s merged body is still authored on the ingest tier. Escalation waits for a measured eval failure (#457).fix consolidation-exclusions writes without committing, like every other fix. A stale member is not repaired silently — it keeps its stale cache so it stays invalid, and only --prune, after the user agrees, removes it.