# Лок-файл: vibe.lock {#root}

@status:doc/work @audience:user

[p01] `vibe.lock` — файл, который vibe пишет, а вы коммитите: точные версии, которые получил проект. Эта страница объясняет каждое поле, чтобы вы могли прочитать его диф в пул-реквесте и понять, что изменилось.

## Где живёт и кто пишет {#where}

[p02] На рабочее пространство один `vibe.lock`, в самом верхнем корне, рядом с корневым [манифестом](../glossary/index.xml#manifest); у участников своих нет никогда. `vibe install` и `vibe update` его пишут; `vibe clean` его сохраняет; руками его никто не правит. Это записанное решение; дерево зависимостей на диске лишь его следствие, и его всегда можно пересобрать из лока.

> [p03] Nesting is **hierarchical grouping**, not independent resolution domains. The lockfile and unified resolution always live at the *absolute root* of the workspace tree. A nested `[workspace]` provides (a) the `[workspace.versions]` matryoshka (§2.6) and (b) logical grouping of members — never its own lockfile, never its own resolution pass.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-007#NESTING-PRINCIPLE>

> [p04] **Never touched — `vibe.lock`.** The lock is the recorded resolution, not derived state: keeping it is what makes `vibe clean install --offline` reproduce the exact world from the machine cache with zero network — the mvn analogy is `target/` vs the dependency resolution, and the lock sits on the resolution side.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-053#CLEAN-KEEPS-THE-LOCK>

## [meta] {#meta}

[p05]
| Поле | Смысл |
| --- | --- |
| `generated_by`, `generated_at` | версия vibe и время последней записи; справочно |
| `schema_version` | схема лока; более старый vibe отказывается от более новой схемы, а не читает её неверно |
| `solver` | резолвер, который произвёл граф |
| `root_dependencies` | координаты, которые манифесты запросили напрямую; база для проверки свежести |
| `language_chain` | разрешённое предпочтение языка и его запасные варианты, чтобы переустановка на другой машине материализовала те же файлы |

> [p06] The freshness check itself adds no `vibe.lock` field: the lockfile *is* the baseline, and the check is a `cargo`-style **satisfiability test** of the locked versions against the current `[requires]` — see §5.1.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-011#NO-NEW-FIELD>

> [p07] `[meta].language_chain` (§2.7.5) — shipped as **one** ordered field merging the preference and its fallback, not the `language` + `language_fallback` pair this section drafted.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-resolver/PROP-003#LF-META-LANGUAGE>

[p08] `root_dependencies` копирует `[requires.packages]` манифеста, так что [лок-файл](../glossary/index.xml#lock-file) — самодостаточный снимок, для чтения которого манифест не нужен. Удаление корня через `vibe uninstall` убирает его из обоих файлов; удаление пакета, который только транзитивная зависимость, отвергается с объяснением.

> [p09] **Decision.** `vibe.lock` gains `schema_version = 2` and the following record shape per package:
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#LOCKFILE-V2>

> [p10] `root_dependencies` is a **mirror of `vibe.toml` `[requires].packages`** — the lockfile keeps the user's declared roots inline so it remains a self-contained snapshot of the solve state (nothing inside `vibe.lock` requires reading `vibe.toml` to interpret). The source of truth for *what the user asked for* is the manifest's `[requires]` section; the lockfile carries a copy plus the resolved transitive closure. `vibe uninstall` of a root drops the entry from both files; `vibe uninstall` of a pure transitive is rejected with an explanation.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#LF-ROOT-DEPENDENCIES>

## [[package]] {#package-entries}

[p11]
| Поле | Смысл |
| --- | --- |
| `group`, `name`, `version` | координата, которая была разрешена |
| `kind` | вид пакета, для размещения и фильтров |
| `content_hash` | отпечаток поставляемого дерева пакета; идентичность; проверяется при каждом скачивании |
| `registry` | имя реестра, который ответил, из списка манифеста |
| `source_url`, `source_ref`, `resolved_commit` | откуда и на каком git-рефе байты были скачаны в этот раз; справочно, всегда канонический адрес, даже когда отдало зеркало |
| `source_kind` | `registry`, `git`, `override` или `path`: какой путь разрешения произвёл запись; для `path` поле URL держит папку участника относительно корня |
| `dependencies` | разрешённые зависимости этого пакета, точными координатами |
| `overridden` | true, когда пакет поставил `[[override]]` |
| `features`, `subskills` | активные фичи и поднавыки, записанные для пакета |
| `files_written` | файлы проекта, которые установка этого пакета записала вне его собственной папки, чтобы удаление убрало ровно их |
| `via_redirect` | адрес заглушки реестра, которая делегировала пакет в другое место, когда по ней шли; иначе отсутствует |

> [p12] **Decision.** A package's identity is the tuple `(kind, name, version, content_hash)`. The `content_hash` is a digest over the deterministically-ordered concatenation of `(rel_path_bytes || 0x00 || file_bytes || 0x00)` for every file in the package directory, and **the value names the recipe that produced it** ([PROP-044 §4.7](../../common/PROP-044-change-native-formats.xml#machinery)): `sha256-tree/1:<hex>` is recipe 1, whose exclusion list, path normalisation and traversal order are carried as data in `formats/hash_recipes/1.toml`; the bare `sha256:<hex>` is recipe 0, the pre-recipe form, frozen verbatim in code — not configurable, because a frozen recipe that can be edited is not frozen — so that values written before recipes were named stay readable. Two hashes are comparable only **at the same recipe**; comparing across recipes answers a question nobody asked, and is never done silently. [PROP-024 §2.2](../../common/PROP-024-code-bearing-packages.xml#shippable-tree) re-scopes this to the package's **shippable tree** — its source, minus build output (`.git/`, `.vibe/`, `target/`, `node_modules/`, `.vibeignore` globs) — so a code-bearing package's identity is its source, not its build state; that exclusion lands with the code that implements it. The URL used to fetch the content is **informational** — recorded in the lockfile for debuggability, not for identity.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#IDENTITY-TUPLE>

> [p13] `source_kind` is `"registry"` for the M1.13 default, `"git"` for git-source declarations, `"override"` for `[[override]]`-resolved (existing `overridden = true` is preserved as redundant marker for back-compat). Lockfile schema bumps to v3; v2 lockfiles read transparently and migrate to v3 on next install (everything that was `overridden = true` becomes `source_kind = "override"`; everything else `"registry"`).
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#SOURCE-KIND-VALUES>

> [p14] The canonical URL is always recorded as the `source_url` in the lockfile when the fetch produces a new pin. Mirror URLs do not appear there.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#MIR-CANONICAL-IN-LOCKFILE>

## Как читать диф {#reading-a-diff}

[p15] Изменившийся `version` с изменившимся `content_hash` — обновление. Изменившийся `content_hash` при той же версии в здоровом мире невозможен: vibe отказывает в нём при скачивании, так что если вы видите такое в дифе, кто-то правил файл. Изменившийся только `source_url` — [зеркало](../glossary/index.xml#mirror) или переезд хоста, и для проекта он ничего не значит. Новая запись с `source_kind = "override"` — патч, который кто-то применил намеренно, и в сообщении коммита должна быть причина.

> [p16] mirror-switching, host-migration, and vendoring never change the lockfile;
>
> <spec://org.vibevm.core/vibevm/modules/vibe-registry/PROP-002#EFF-LOCKFILE-STABLE>

## Особые случаи и правила {#edge-cases}

[p17] Неизменённый манифест против неизменённого [лок-файла](../glossary/index.xml#lock-file) заставляет `vibe install` вовсе пропустить резолвер: лок и есть ответ.

> [p18] With the freshness check, **`vibe install` becomes lockfile-respecting**: unchanged `[requires]` ⇒ the locked versions are honoured verbatim.
>
> <spec://org.vibevm.core/vibevm/modules/vibe-workspace/PROP-011#LOCKFILE-RESPECTING>

[p19] Лок-файл не записывает ни [индекс](../glossary/index.xml#index-registry) [реестра](../glossary/index.xml#registry), ни зеркало: чтобы воспроизвести его, нужны только [координаты](../glossary/index.xml#coordinate), [отпечатки](../glossary/index.xml#fingerprint) и источник, который может их отдать.

[p20] `vibe why <coordinate>` объясняет по лок-файлу и манифестам, почему пакет в проекте или что его заблокировало.

[p21] Файл читается строго: поле, которого vibe не знает, — ошибка, а не предупреждение, так что ручная правка или поле из более нового vibe ловится сразу.

