PROP-046 — the adoption-facts registry (vibefacts/)
1. Mandate — whose status is it
01The 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.
02The 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.
2. The model
03Home 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.
04The 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).
05Status 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.
3. The five laws
06L1 — 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).
07L2 — 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.
08L3 — 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.
09L4 — 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.
10L5 — 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.
4. The CLI surface
11vibe 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).
5. Build order
12Waves, 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).
6. Open questions
13The 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).
14Does 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.