# PROP-046 — the adoption-facts registry (`vibefacts/`) {#root}

@status:spec/work

## 1. Mandate — whose status is it {#mandate}

[p01] @fact:REGISTRY-MANDATE **The owner's design (2026-08-22, chat, near-verbatim):** «сделать в корне отдельную директорию vibefacts в структурном и grep-совместимом формате (toml), в которой собираем данные о том, как реализован или не реализован факт. Внутри реестра — признак: факт из пакета или из spec; каждый факт хранится вместе с адресом своего пакета-источника. Утилита `vibe facts` даёт CRUD, search, переводы статусов. При полной ре-материализации статусы мерджатся в vibedeps в момент ре-материализации. Для спецификаций внутри spec статус всегда синхронизирован. Когда статус пакетного факта меняется в ходе кодирования — точечно меняем его в vibedeps, а STATIC не трогаем: новое значение накатывается динамическим лоадером. Статус опционален: нет статуса — неопределённое состояние, типовое для внешних пакетов. При импорте пакета авторские статусы игнорируются. При удалении пакета статусы не исчезают; `vibe uninstall` спрашивает, чистить или хранить; `vibe facts clean` удаляет ничейные факты исчезнувших пакетов.» Landed across W1–W3 (commits 700a91f1, 7548144f + the W3 landing): the registry, the full `vibe facts` surface, derivation merge with point re-derivation, and the lifecycle all live; the cache-stable-STATIC strong form remains the follow-up wave. @status:impl/done

[p02] @fact:WHY-THE-AXIS-IS-MISSING **The problem the registry closes.** A status marker in a package SOURCE is the author's claim about the author's own scope. For packages that ship their implementation (engines travelling inside `core-ai-native`) the claim stays true for every consumer. For practice packages (`wal`, `git-practices`, redbook adoption facts) «impl/done» means «implemented at the author's reference project» — it says NOTHING about the importing project, and the markup has no per-consumer axis (stage/state, `action`, `audience` all describe the claim, not the adopter). The genre boundary runs per FACT, not per package — one package holds both kinds. The registry is the consumer-owned overlay that carries the missing axis without ever rewriting the package source. @status:spec/done

## 2. The model {#model}

[p03] @fact:REGISTRY-HOME **Home and format.** `vibefacts/` at the project root, tracked in git (it is project state a teammate must see), one TOML file per source: `vibefacts/spec.toml` for the host's own `spec/` tree, `vibefacts/<group>.<name>.toml` per installed package (the vibedeps slot-naming convention). Grep-friendly, small diffs, per-package lifecycle: removing a package's overlay is removing one file. Landed in W1. @status:impl/done

[p04] @fact:REGISTRY-KEY **The key is the fully-qualified anchor.** Every entry is keyed by the fact's full `spec://` address (PROP-029 grammar); beside it the entry records the origin kind (`package` | `spec`) and the source package address — the owner's «каждый факт хранится вместе с адресом своего пакета-источника». Landed in W1; addresses are minted by the router's forward law (`canonical_doc_path`). @status:impl/done

[p05] @fact:TRI-STATE **Status is optional; absence is the indeterminate state.** The dialect already models status as optional on a fact — no grammar change. What changes is the reading law, per corpus: in the HOST's judged corpus «every fact carries a marker» stays binding (the court's unmarked-fact debt is unchanged); in the registry's view of PACKAGE facts, absence of an adoption status is the NORMAL initial state — «типовое состояние для спецификаций во внешних пакетах». Landed in W1. @status:impl/done

## 3. The five laws {#laws}

[p06] @fact:LAW-SOVEREIGNTY **L1 — consumer sovereignty: imported statuses are ignored.** On package import the authored statuses in the package source are NOT copied into the registry — the package may use them for its own internal purposes, and the consumer's adoption state starts indeterminate. Acceptance of authored statuses is a deliberate act, never a default: `vibe facts adopt --package <X> [--from-source] [filter]` bulk-copies the author's statuses into the overlay in one auditable gesture (the escape hatch for implementation-shipping packages whose facts are done-by-construction). Landed: import never touches the registry by construction (W1); `adopt` fills absent entries only and reports added/kept (W2). @status:impl/done

[p07] @fact:LAW-SYNC **L2 — the two-home sync law, with an asymmetric tie-break.** For HOST facts (origin `spec`) the spec file is authoritative and the registry mirrors it; for PACKAGE facts the registry is authoritative and the materialised copies mirror it. The sync is a MACHINE gate, not an instruction: a `facts_sync` check (panel + `vibe check`) fails when `vibefacts/spec.toml` and the `spec/` markers disagree; `vibe facts sync` reconciles mechanically in the direction the tie-break dictates. The boot lane carries the convention line (change one home → sync the other), but the gate is what makes it true. Landed in W1: the facts_sync cell sits in the roster with its red/green proven live; the sync reads only the documents the registry cites, so an unrelated spec file's defect cannot fail the gate. @status:impl/done

[p08] @fact:LAW-DERIVATION **L3 — the registry is a declared derivation input; point edits are point re-derivations.** Statuses merge into materialised vibedeps AT derivation time: the derived output becomes a pure function of (package source, converter recipe, that package's registry file), and the per-package registry file's hash joins the derived manifest's inputs — so a registry change makes exactly the affected slot stale and «точечная ре-материализация» is driven by ordinary slot-staleness. Editing slot bytes in place is NOT a path: it would break the hash law (PROP-045 ##HASH-LAW) and slot verification; `vibe facts set …` writes the registry and re-derives the slot. Landed in W2: overlay_hash in the derived manifest (old wire reads), replace-if-present with authored extras preserved, targeted same-format files enter the pivot, freshness and the slot verifier both compare the live registry file, set/rm point-re-derive the one slot — proven live on the wal slot both ways. @status:impl/done

[p09] @fact:LAW-STATIC-INVARIANT **L4 — status mutation never touches STATIC.** The owner's ruling (2026-08-22): STATIC is a globally static prefix, loaded once per agent/subagent and cache-stable in the LLM-prompt-cache sense; it is invalidated only by large structural causes (the dependency list changed), never by status flips. The strong form adopted: **statuses are not compiled into STATIC at all** — the static lane is a pure function of structure (dependency list, versions, snippet structure) and NOT a function of the mutable (registry, statuses, dates). A fact's current status is a point query against the registry / the dynamic lane, not prefix knowledge. This law is the bridge to the follow-up cache-stable-prefix wave and is testable as byte-identity of STATIC across rebuilds with unchanged structural inputs. The prefix economy's canon is PROP-048 §3 (##STATIC-ROLE and its neighbours); this fact is that canon's registry-side half. @status:spec/work

[p10] @fact:LAW-LIFECYCLE **L5 — lifecycle: removal keeps, cleaning reports.** Removing a package does not silently erase its overlay; `vibe uninstall` (the CLI verb; built out if found unimplemented) asks whether to clean or keep the package's facts file. `vibe facts clean` is the revision pass that removes orphaned overlays of vanished packages; on dependency UPGRADE, `vibe facts sync` reports anchors that disappeared or moved (orphaned entries with candidates) rather than dropping them — the tombstone discipline, applied to overlays. Landed in W3: lockfile-driven `clean` with dry-run and named removals, the attended-only uninstall dialog (automation flags never imply consent to delete adoption data), spec.toml never an orphan. @status:impl/done

## 4. The CLI surface {#cli}

[p11] @fact:CLI-FACTS **`vibe facts` — the explicit lever.** CRUD over the registry, search by attributes (package, status, stage, indeterminate-only), status transitions (`vibe facts set <address> <status>`), `adopt` (L1), `sync` (L2), `clean` (L5), and the adoption report (`vibe facts report [--package X]` — «12/40 adopted»). An agent flips a fact through the tool — an auditable command — never by editing derived files. Landed across W1–W3: list/get/set/rm/sync (W1), adopt with point re-derivation (W2), clean and the per-package report with `?` for unavailable denominators (W3). @status:impl/done

## 5. Build order {#build-order}

[p12] @fact:WAVES **Waves, owner-ordered (2026-08-22).** W1: registry format + `vibe facts` core (CRUD/search/set) + the `facts_sync` gate. W2: L3 — registry joins derivation inputs, point re-derivation, `adopt`. W3: L5 lifecycle (`clean`, the uninstall dialog) + the adoption report. Then, as the owner's next act after this PROP's waves: the **STATIC materialisation-format wave** (STATIC follows the project's materialisation format — `STATIC.xml` under `spec_format="xml"`; the extension-stable-`STATIC.md` residue law of PROP-045's parity landing is owner-revised); after it, the **cache-stable-prefix wave** (L4 hardened into measured byte-stability and prefix-sharing across agents). @status:spec/work

## 6. Open questions {#open}

[p13] @fact:OPEN-MIXED-STATIC **The mixed-format STATIC extension.** Under `mixed` materialisation the slots are copy-through per file, but STATIC is a generated compilate with no source format of its own — the STATIC-format wave decides its extension law (likely the markdown default, as the single canonical compilate). @status:spec/work

[p14] @fact:OPEN-JUDGED-CORPUS **Does `vibefacts/` join the judged corpus?** Adoption claims are claims — they may deserve verdicts and evidence like any fact. Deferred until W1 lands and the first real overlay exists. @status:spec/work

