Видимость зависимостей: public, private и друзья
01У пакета, от которого вы зависите, есть собственные зависимости, и они тоже могут дотянуться до вашего проекта. Дотянутся ли, решается там, где объявлена каждая зависимость, построчно: получают все, не получает никто или только проекты, которые назвали объявляющий пакет другом. Эта страница объясняет три метки, дружбу, которая открывает среднюю из них, что в результате оказывается в вашем дереве и как спросить vibe, почему пакет тут есть или почему его нет.
Три метки на требовании
03Каждая строка под [requires.packages] — ребро от вашего пакета к тому, который она называет, и ребро несёт необязательную метку access. Метка — слово объявляющего пакета о его собственной зависимости: как далеко вверх ей можно просачиваться, к проектам, которые зависят от объявляющего пакета.
04 Each[requires.packages]edge gains an optionalaccessproperty with three values — the provider-side seepage mark on the edgeP → Q, declared byPabout its own dependencyQ:
05[requires.packages]
"org.example/style" = "^1.0"
"org.example/wal" = { version = "^2.0", access = "private" }
"org.example/inner" = { version = "^1.0", access = "friends-only" }
06public — умолчание, и писать его не нужно: зависимость достигает каждого потребителя над вами, как бы высоко он ни стоял, и никому не нужно ничего включать. Это правильная метка для всего, на чём строится ваш собственный текст, и для участников коллекции.
07access = "public"— the default.Qseeps throughPto every consumer above, transitively along the whole hierarchy, with no opt-in. For edges whose target is part of the declarant's substance for all consumers — a collection's members, a stack's core, and every ordinary «my text builds on this» edge.
08private останавливает зависимость у вашей двери. По ребру идут, только когда ваш пакет сам корень установки, как в вашем собственном чекауте. Это метка для инструментов и для дисциплин, которые определяют, как вы работаете, а не что вы поставляете.
09access = "private"—Qdoes not seep throughPat all: the edge is traversed only whenPitself is the consumer root (§4.4 — the dev world). An implementation detail in the strictest sense — the explicit mark for WAL-class disciplines and dev tooling.
10friends-only стоит между ними. Зависимость достигает только потребителей, чьё замыкание друзей содержит ваш пакет: тех, кто намеренно назвал вас другом, напрямую или через своего друга.
11access = "friends-only"—Qseeps throughPonly into consumers whose friend closure containsP(§2.4): those who deliberately namedP(directly or transitively) a friend. The curated middle.
12Поскольку присутствие течёт по умолчанию, долг автора — сужать намеренно. Помечайте private каждое ребро, которое не входит в то, что вы предлагаете, и friends-only — то, на чём должен строиться только ваш ближний круг. Каждое публичное ребро стоит вашим потребителям бюджета чтения, и лок-файл делает этот счёт видимым.
13 Authoring norm for narrowing marks (the JPMS community rule, transposed and then inverted by the public default): since presence flows by default, the author's duty is to narrow deliberately — markprivateevery edge that is not part of your consumable substance (WAL-class disciplines, dev tooling, heavy optional companions), andfriends-onlywhat only your inner circle should build on. Aggregator-style «one edge pulls the world» is lawful for collections, whose members are their declared substance (§4.6); everyone else answers for every public edge with its lane cost (§7 measurements make the bill legible). Advisory, policed by the §7 lints, enforced by nobody — strict-deps culture with the autofix command in place of ceremony.
Дружба: согласие потребителя
14Дружбу объявляет потребитель, и никогда она не подразумевается. На ребре friend = true говорит: «я вступаю в дружбу с этим пакетом»; в секции [visibility] friends = ["org.example/inner"] говорит то же о пакете, который вы напрямую не требуете. По умолчанию false: обычное ребро берёт пакет, а не его ближний круг. Присутствие щедро, дружба скупа, и два умолчания различаются намеренно.
15 Per-edgefriend = true|false(defaultfalse— owner re-ruled 2026-08-23). Friendship is a raised privilege — receiving a target's gated substance — and raised privileges are not granted where they may never be used: an ordinary edge takes the package, not its inner circle.friend = trueis the explicit opt-in; the original default-truebelonged to the earlier one-field paradigm and is retired with it. With the public presence default (§2.2) the two defaults form the deliberate asymmetry: presence is generous, friendship is stingy — the ordinary world works with zero ceremony, the curated world opens only by explicit word.
16Метка friends-only на вашем собственном ребре подразумевает дружбу с пакетом, который оно называет, так что цепочка друзей работает от одной метки на переход. Напишите рядом friend = false, когда хотите доставить пакет своему кругу, не входя в его круг сами.
17 The implication — owner-ratified (F10, 2026-08-23): afriends-onlymark impliesfriend = trueon its own edge. Vouching for a package as part of your substance while standing in no relation to it is incoherent — and under the strictfriend = falsedefault, afriends-onlychain would otherwise need two marks per hop, where forgetting the second silently kills the chain. With the implication, the owner'sA → B → C → Dchain works from one mark per hop, exactly as originally intended; an explicitfriend = falsebeside afriends-onlymark overrides the implication and yields the lawful no-vouch cell of ##ACCESS-FRIEND-MATRIX (terminal delivery to the circle) — no lint, it is a real intent.
18Друг ничего не объявляет о том, что получает. Когда вы дружите с пакетом, всё, за что он ручается, приходит без строки в вашем манифесте, в версиях, которые выбрал поручитель, и ребра friends-only этих пакетов открываются в свою очередь. В тот момент, когда ваш собственный текст начинает строиться на одном из них, объявите собственное прямое ребро: транзит говорит «здесь, потому что на этом стоит друг», а не «моё».
19 A friend declares nothing about the vouched content — by design. WhenRbefriendsP(one explicit line:friend = trueon itsPedge, or afriendsentry), everythingPvouches for arrives with no mention inR's manifest:P's friends-only edge admitsQ(rule (3)), thegrowrule putsQintoC(R), andQ's own friends-only edges then open too — recursively, the owner's original transitivity requirement. Version choice forQstays with the voucher (P's constraint on its own edge) — the vouched set is a bundlePtested, not a menuRassembles. This is the standard re-export semantics of JPMSrequires transitive(implied readability chains), Bazeldeps+exports*, and Gradleapi— a dependency onPis a dependency on «Pwith everythingPstands on». Control never leaves the payer: the grant is explicit (thefalsedefault),unfriend/per-edgeexcludeprune point-wise, and the lock-diff +vibe whymake every transit arrival legible. The hygiene norm transposes from Bazel strict-deps: transit covers «Qis here becausePstands on it»; the momentR's own text starts building onQ,Rdeclares its own direct edge.
20Две метки независимы и указывают в противоположные стороны, поэтому сочетаются. Публичное ребро с friend = true доставляет пакет всем над вами и открывает его двери friends-only только для вас; эта дружба дальше не едет. Передавать дружбу дальше — ровно то, что делает метка friends-only.
21accessandfriendcompose independently on one edge (owner-confirmed 2026-08-23: «access и friend разные понятия»). The two marks point in opposite directions, so their product is well-defined. The default shape —publicpresence, no friendship — is plain delivery: «Q reaches everyone above me; I take none of its gated substance». Addingfriend = trueto a public edge reads: «and for myself I enter friendship with Q, so Q's friends-only doors are open in my own perspective». That friendship does not travel onward through a public edge — re-exporting friendship is exactly what thefriends-onlymark does (##PUBLIC-GIVES-PRESENCE-NOT-FRIENDSHIP) — so in another root's closure a public edge contributes presence and nothing more. The remaining corner — «deliver publicly and vouch friendship onward» in a single edge — is deliberately inexpressible: a consumer that wants Q's gated substance opts in with its ownfriendsline, which is the payer-decides invariant.
Секция visibility, unfriend и exclude
22Всё, что касается пакета в целом, а не одного ребра, живёт в одной секции, [visibility], одинаковой для проекта и для пакета: friends, unfriend, allow-friends и ignore-concept-warnings. Рядом с ней стоит таблица верхнего уровня [override], описанная ниже.
23 The node-level vocabulary lives in one role-blind[visibility]section —friends,unfriend,allow-friends(§2.8),ignore-concept-warnings(##CONCEPTS-GATE-SOFTENED) — plus the sibling top-level[override]table (§2.9). One section serves both manifest roles by construction (PROP-024 equipotence): no field is duplicated between[project]and[package].
24[visibility]
friends = ["org.example/inner"]
unfriend = ["org.example/noisy"]
allow-friends = ["org.example/partner"]
25unfriend убирает названные пакеты из дружб, которые вы передаёте дальше. Они по-прежнему приходят на ваш уровень, когда их допускает ребро, но никто не видит их вашими друзьями через вас. Другой пакет в том же дереве всё ещё может с ними дружить; обрезка только ваша.
26unfriend = ["group/name", …](node-level) removes the named packages from the declaring node'sgrants(…)— and therefore from every friend closure as seen through that node. The unfriended package «притянется, но будет явно исключён из цепочки транзитивности внутренних друзей»: still usable at the declaring level (its edge, if any, still traversable by its own access), just never re-exported as a friend through the declarant. Node-scoped by owner law: «они выбрасываются из замыкания ТОЛЬКО с точки зрения той ноды, которая объявила их unfriend — а какой-нибудь другой пакет в иерархии может нормально включить их в замыкание» — another node's friends-only chain delivers the same package untouched. The nameunfriendis owner-ratified (F2, 2026-08-23); the property list'senemyis retired.
27exclude, записанный на ребре, — другая обрезка: названные пакеты вырезаются из каждой цепочки, проходящей через это ребро, как бы глубоко они ни были, даже там, где они публичны. Другой путь всё ещё может их доставить, и тогда они присутствуют через него. Исключение может только сузить то, что предоставили объявляющие пакеты, и никогда не расширить, поэтому объявить его может любой пакет.
28exclude = ["group/name", …](per-edge) kills the named packages in every chain passing through the declaring edge — «исключены из цепочки транзитивных подключений вообще, даже если внутри они объявлены как public». Maven-exclusions semantics, owner-ratified (F4, 2026-08-23: «exclude per-ребро Maven-style. Можно per-ребро делать глубокие эксклюды по иерархии — это не глобальный deny-list, это сужение в рамках поддерева»): the pruning reaches arbitrarily deep, but only within this edge's subtree; a different path still delivers the package, and then it simply exists inE(R)via that path — classic diamond behaviour, no global veto. Exclusion is pure subtraction — it can only narrow what providers granted, never widen — which is why any node may declare it as part of shaping its own delivery; the expansive counterpart is F9's root-onlyoverride, which may also re-house this syntax (the semantics here stand either way).
29Пакет может и сказать, кому разрешено с ним дружить. Отсутствующий allow-friends значит «кому угодно»; пустой список запечатывает пакет, так что его закрытое содержимое существует только в его собственном чекауте; список называет точный круг. Проверка ложится на того, кто объявляет дружбу: объявление, которого пакет не разрешает, — предупреждение, а не ошибка, и замыкание там не растёт.
30 Owner-ordered design (F8, 2026-08-23: «спроектируй механизм allow-friends… похоже на возможность построить exhaustive замыкание sealed classes»). A providerGmay declare, node-level, who is permitted to enter friendship with it — the Java-sealed/permitsshape transplanted onto the friendship relation (design ratified — «годится»):
31
Three states: field absent — friendship is open, anyone's grant works (the default, today's semantics); empty list — sealed: nobody enters, the gated substance exists only in G's own dev world; a list — exactly the named circle.
32 The check sits on the grant giver: a grantN → G(an explicitfriend = trueedge, afriendsentry, or an F10-implied one) participates in any closure only ifallow-friends(G)is absent or coversN. A friends-only re-export hopF → GinC(R)likewise requiresFpermitted byG. A rejected grant is a warning, never an error (the §5 unknown-target precedent) — the closure simply does not grow there.
33Печать закрывает дружбу, а не доставку. Запечатанный пакет всё равно приходит туда, куда его доставляет ребро friends-only; закрытым остаётся его собственный ближний круг, а отвергнутое объявление попадает в отчёт. vibe friends org.example/partner печатает полную картину по одному пакету: открыт, запечатан или назван круг, кто с ним дружит, какие объявления он отвергает и входит ли он в ваше замыкание.
34 The seal gates friendship, never delivery (pinned at the W6 landing). A sealedGstill arrives wherever a declarant'sfriends-onlyedge delivers it — the declarant owns its own edge and could as well have marked itpublic; what the seal closes is entry intoG's circle: a rejected grant keepsGout of every closure, soG's own friends-only inner content stays shut and the grant surfaces as aRejectedGrantwarning. The observable difference between sealed and unsealed is always the inner content and the diagnostics, never the presence ofGitself. Proven end-to-end bycli_visibility_power.rs(unseal and exact-circle scenarios).
Починить ребро, которым вы не владеете
35Иногда нужная вам метка стоит на ребре, которым вы не владеете: участник коллекции пометил зависимость как private, а она нужна вашим потребителям, или пакет запечатался от вас. Таблица [override] переписывает чужие ребра, и она законна в любом манифесте, в корне или ниже. Агрегатор пользуется ею, чтобы перекроить ребро участника для всех своих потребителей, так же как курирует свою доставку через exclude.
36 Owner-ruled (2026-08-23):overrideis lawful in any manifest, not only the root («разрешён не только в корневом манифесте, а где угодно»). Any nodeNmay carry an[override]table whose entries rewrite foreign edges — theiraccess,friend, presence (exclude = true), or a target'sallow-friends— and the rewrite acts whereverNstands on the chain: an aggregator repairs or reshapes a member's edge for all of its own consumers, exactly as it curates its delivery withexclude. The threat model follows the owner's earlier ruling: a deliberate break-in is not an attack (the developer can edit any file on disk anyway); this is the official verb that replaces reflection-style hacks — and it stays quiet (pull-based provenance only).
37[override]
"org.example/member -> org.example/wal" = { access = "public" }
"org.example/partner" = { allow-friends = "*" }
38Ключ-ребро называет оба конца ребра стрелкой; ключ-пакет называет один пакет и переписывает его allow-friends. Переопределения применяются вдоль цепочек, которые проходят через объявивший их манифест. То, что ближе к корню, применяется позже и побеждает, так что за корнем всегда последнее слово, а посредник побеждает только на цепочках, в которых участвует.
39 Path-stack semantics. An override applies to chains that pass through its declarant: walking a chainR → … → N → … → P → Q, the effective attributes of the edgeP → Qare its declared attributes masked by the[override]tables of the chain's nodes in order, nearer-to-root applied later and winning — the root can re-override any intermediary, the payer always has the final word; between intermediaries, the outer (closer toR) wins on the chains it participates in. Effective attributes are therefore per-chain;E(R)and thegrowrule quantify existentially over chains (a package is present / a hop extends the closure if some chain admits it), which is the diamond behaviourexcludealready has. Determinism is preserved — masks are static declarations, the graph is acyclic, and the implementation dedups identical mask-states while walking the DAG (override tables are rare, so the practical state count stays small).
40Тот же ключ, override, обслуживает и старую форму-массив [[override]], которая закрепляет замену источника для одной координаты. Две формы различаются по виду, каждая по отдельности законна, а манифест с обеими сразу — громкая ошибка, а не слияние.
41 Syntax note (W1 landing): the manifest already carried[[override]]— the array-of-tables registry-pin form (OverrideSection). The visibility table lives under the sameoverridekey as an ordinary table; the wire layer distinguishes the two shapes structurally (array vs table), either form alone is lawful, and one manifest carrying both is a loud validation/serialisation error rather than a silent merge. A future wave may retire or rename the legacy form; until then the coexistence is deliberate.
Что приходит в ваше дерево
42Пакеты, которые метки допускают от вашего корня, образуют действующее множество, и это единственное множество, с которым работает vibe. Разрешение версий идёт только по нему: приватное ребро пакета, который не ваш корень, не вносит ограничения, ничего не скачивает и не может конфликтовать. Лок-файл записывает действующее множество, так что ваш лок никогда не несёт чужих инструментов.
43 Version resolution operates onE(R)only: private edges of non-root packages contribute no constraints, fetch nothing, and cannot conflict.vibe.lockrecordsE(R)— the lock of a consumer no longer contains other packages' dev-world entries. Version unification (one node per(group, name), PROP-003/017) is unchanged within the effective set. A welcome simplification vs code ecosystems: the Cargo-RFC-1977 problem («may private deps duplicate at different versions?») does not arise — an invisible package has no copies at all.
44Дерево зависимостей под vibedeps/ держит ровно это множество. Исключённый или невидимый пакет не оставляет ни папки, ни записи в кэше для вашего мира, ни текста в стартовой полосе.
45vibedeps/holds exactlyE(R): an excluded or invisible package leaves no slot, no cache entry for the root's world, no lane text. This is the structural fix for the WAL specimen: a wal flow declaredprivate(orfriends-only) by whatever requires it simply never arrives in a consumer's tree — no snippet, novibevm/vibespecs/WAL.xmlscaffold, no INDEX row.
46По вашим собственным ребрам идут всегда, включая приватные. Так один [requires] служит и вашим набором для разработки, и вашим контрактом, разделённым по ребрам, а не по секциям: в вашем чекауте приватные инструменты материализуются; когда вас потребляют как зависимость, материализуются только просачивающиеся ребра.
47 Rule (1) of §2.5 — the root's own edges always traverse — combined with explicitprivatemarks resolves the open tail of the equipotence wave (PROP-024): a package's[requires]is simultaneously its dev-set and its contract, split per-edge byaccessrather than by section. When the package is the consumer root (a dev checkout —[project]or[package], equipotently), all its edges traverse and its private tooling materialises; when it is consumed as a dependency, only its seeping edges do. No separate dev-dependencies section needed.
48Поскольку метка в середине графа может расширить то, что до вас доходит, vibe update печатает изменение действующего множества: какие пакеты входят или выходят и сколько бюджета чтения они добавляют или снимают. Расширение — событие для ревью, а не тихое просачивание.
49 Closure-drift visibility. The lock carriesE(R)with each member's lane cost (bytes/tokens of its contribution);vibe updateprints the closure diff — packages entering/leaving and the lane-cost delta — so a mid-graph re-export widening (##CLOSURE-DRIFT-CONTROL) is a reviewed event, not a silent seep.
Спросить почему
50vibe why отвечает на вопрос, который система видимости иначе превращает в фольклор. Для присутствующего пакета она печатает цепочку, которая его допускает, каждый переход с его правилом, меткой доступа и источником дружбы. Для отсутствующего — ближайшие перекрытые цепочки и что перекрыло каждую: приватное ребро, отсутствие дружбы, unfriend или exclude. vibe tree несёт те же пометки на каждом узле.
51 Observability:vibe why <group>/<name>. For any package, print the chains that admit it intoE(R)— each hop annotated with its rule ((1)/(2)/(3)), access mark, and friendship provenance — and for an absent package, the nearest blocked chains and what blocked them (private edge / missing friendship / unfriend / exclude). The debugging surface without which a visibility system rots into folklore;vibe treegains the same annotations.
Особые случаи и правила
52Пакет, достижимый двумя путями, присутствует, как только один путь его допускает; exclude на другом пути ничего не меняет. Исключение — вычитание, и объявлять его можно где угодно; расширение — переопределение, и корень всегда может переопределить посредника заново. Дружба, unfriend и печать меняют, какие цепочки открыты, но никогда не меняют, какие версии выбраны: версия пакета, за который поручились, остаётся за поручителем.