# PROP-023 — Bridge packages {#root}

@status:impl/done

[p01] @fact:status-line **Status: IMPLEMENTED** (specified 2026-06-24 in an owner-requested design
session; verified against the tree 2026-07-25 by the spec-actualization
campaign). The umbrella's own addition — `[package].bridge`, defaulting to
false — parses in `vibe-core` with a doctest, and the mechanisms it
composes all ship: [PROP-020](../vibe-workspace/PROP-020-install-hooks.xml)
install hooks, [PROP-021](PROP-021-submodule-sources.xml) submodule sources,
[PROP-022](../vibe-workspace/PROP-022-materialization-modes.xml) materialization
modes, plus [PROP-021 §2.2](PROP-021-submodule-sources.xml#source-abstraction)
dependency-declared embedded sources — so every bridge class recorded here has
live machinery. The umbrella adds a flag and packaging/provenance conventions;
each underlying mechanism remains independently usable. @status:impl/done

[p02] @fact:related **Related:** [PROP-002](PROP-002-decentralized-registry.xml) (a bridge is an
ordinary package + identity), [PROP-008](PROP-008-qualified-naming.xml) (the
consumer-facing qualified product coordinate),
[PROP-015 §2.6](../vibe-mcp/PROP-015-mcp-integration.xml#skill) +
[PROP-015 #skill-include](../vibe-mcp/PROP-015-mcp-integration.xml#skill-include)
(projecting a skill out of the bridged subtree, selectively),
[PROP-000 §16](../../common/PROP-000.xml) (the installable kinds a bridge
still belongs to). @status:spec/done

## 1. Motivation {#motivation}

### 1.1 The problem — good work that nobody packaged {#problem}

- [p03] @fact:unpackaged-work People publish skills and projects to GitHub / GitVerse without ever making a
  vibevm package — out of disinterest, or because their repo's layout has nothing
  to do with vibevm conventions. @status:spec/done
- @fact:unreachable That work is then unreachable through
  `vibe install`, and the original author has no incentive to change. @status:spec/done

- [p04] @fact:BRIDGE-DEF A **bridge package** closes the gap without the author's involvement: a
  *maintainer* volunteers to steward someone else's repository and publishes an
  ordinary vibevm package that **wraps** it. @status:impl/done
- @fact:BRIDGE-MEANS The bridge makes the foreign repo
  consumable by vendoring it, carrying a git-native submodule, or declaring an
  immutable external embedded source. The reference-backed form keeps upstream
  bytes out of the bridge repository and fetches them directly from the author. @status:impl/done

### 1.2 What this is — a thin convention over three mechanisms {#what}

[p05] @fact:thin-convention A bridge is not a new kind of package or a new subsystem. It is: @status:impl/done

- [p06] @fact:conv-ordinary-package an ordinary package (still one of `flow` / `feat` / `stack` / `tool`), @status:impl/done
- @fact:conv-embedded-content carrying the foreign repo as **vendored** content, a git-native **submodule**,
  or a manifest-declared immutable **external embedded source**
  ([PROP-021](PROP-021-submodule-sources.xml)), @status:impl/done
- @fact:conv-hooks optionally **prepared** by install hooks ([PROP-020](../vibe-workspace/PROP-020-install-hooks.xml)), @status:impl/done
- @fact:conv-in-place optionally **materialised** as `in-place` when the upstream is a giant
  ([PROP-022](../vibe-workspace/PROP-022-materialization-modes.xml)), @status:impl/done
- @fact:conv-skill-projection with any skill projected selectively from the bridged subtree
  ([PROP-015 #skill-include](../vibe-mcp/PROP-015-mcp-integration.xml#skill-include)). @status:impl/done

[p07] @fact:plus-flag …plus one flag that says "this is a bridge." @status:impl/done

## 2. Decisions {#decisions}

### 2.1 A bridge is marked by a flag, not a kind {#flag}

[p08] @fact:req-flag `req r1` @status:impl/done

- [p09] @fact:BRIDGE-FLAG `[package].bridge = true` marks a package as a bridge. @status:impl/done
- @fact:FLAG-NOT-KIND It does **not** change
  the package's `kind` (a bridged skill is still a `feat`/`tool` as appropriate)
  or its identity. @status:impl/done
- @fact:FLAG-JOBS The flag is metadata with two jobs: it documents that the
  package's substantive content is *foreign* (stewarded, not authored, by the
  maintainer), and it is the hook the registry/UI uses to surface provenance
  (§2.4). @status:impl/done
- @fact:FLAG-DEFAULT Default `false`; the overwhelming majority of packages are not bridges. @status:impl/done

### 2.2 Three classes — vendored, submodule-backed, and reference-backed {#classes}

[p10] @fact:req-classes `req r1` @status:impl/done

[p11] @fact:TWO-CLASSES A bridge obtains the upstream repo in one of three ways: @status:impl/done

- [p12] @fact:CLASS-VENDORED **Vendored ("git in git")** — the maintainer copied the upstream tree into
  the package and committed it. This needs **none** of PROP-021/022 machinery:
  it is plain files in a `snapshot` package. A vendored bridge is therefore the
  cheapest case — the flag (§2.1) plus, if the layout needs shaping, hooks. @status:impl/done
- @fact:CLASS-SUBMODULE **Submodule-backed authoring** — the maintainer workspace references upstream
  through a Git submodule ([PROP-021](PROP-021-submodule-sources.xml)). A direct
  Git-source install preserves that live Git composition. Registry publication
  deliberately flattens a clean populated gitlink into ordinary package files,
  removes `.gitmodules`, and reports `submodule <path> vendored at <sha>`;
  the published package is therefore a truthful vendored snapshot, not a
  dangling promise that consumers will recurse into the maintainer's repo. @status:impl/done
- @fact:CLASS-REFERENCE **Reference-backed** — the package repository contains only maintainer-authored
  metadata and adapters. `[[embedded_source]]` declares the original public Git
  URL, an immutable full commit, an independently verified source-tree hash and
  upstream licence provenance. Installation fetches that exact source directly
  into the consumer's machine cache; selected `[[skill]]` content or
  `[[skill.resource]]` files are projected from the authenticated cached tree.
  The upstream is not copied into the bridge repository, is not resolved as a
  vibevm dependency node, and its own `vibe.toml`, hooks and submodules are never
  executed implicitly. The installed bridge itself remains a normal physical
  `vibedeps` slot; source-aware loaders combine that owner root with named,
  separately authenticated external roots without pretending the latter are
  UPL package payload. @status:impl/done

[p13] @fact:CLASS-TRADEOFF The maintainer chooses per trade-off: vendored is self-contained; submodule-backed
uses Git's native composition; reference-backed preserves repository and licence
boundaries but needs the pinned upstream once per machine-cache miss. @status:spec/done

### 2.3 Composition — every mechanism is optional {#composition}

[p14] @fact:req-composition `req r1` @status:impl/done

[p15] @fact:COMPOSE-OPTIONAL A bridge is the point where the three orthogonal mechanisms compose, but it
mandates none of them: @status:impl/done

- [p16] @fact:OPT-NO-HOOKS a bridge **without hooks** is valid (the upstream layout already fits); @status:impl/done
- @fact:OPT-NO-SUBMODULE a bridge **without a submodule** is valid (vendored); @status:impl/done
- @fact:OPT-NO-IN-PLACE a bridge **without `in-place`** is the norm (`in-place` is only for giant
  upstreams). @status:impl/done

- [p17] @fact:CANONICAL-FULL-CASE The canonical full case — submodule-backed + `pre-install` hook to shape the
  tree + selective skill projection — is the union of the four specs, but each
  piece is independently usable outside any bridge. @status:impl/done
- @fact:FOUR-SPECS-WHY This is why they are four
  specs and four test sets, not one (the owner's orthogonality requirement). @status:spec/done

### 2.4 The maintainer model {#maintainer-model}

[p18] @fact:req-maintainer `req r1` @status:impl/done

- [p19] @fact:MAINTAINER-VS-AUTHOR The bridge's **maintainer** is distinct from the upstream **author**. @status:impl/done
- @fact:AUTHORSHIP-SEPARATION `[package].authors` names only the people or organisations that authored
  the package's own metadata, adapters and other maintainer-owned payload. It
  never absorbs the authors of referenced bytes. Every `[[embedded_source]]`
  records those separately as a non-empty `upstream_authors` list. Listings,
  locks and indexes preserve both lists under their own labels; they never
  concatenate them into one ambiguous authorship claim. @status:impl/done
- @fact:GROUP-PROVENANCE The package's qualified coordinate
  ([PROP-008](PROP-008-qualified-naming.xml)) names the **product a consumer
  installs**, not the codebase in which its packaging machinery happened to be
  written. Packaging Spec Kit does not make it `org.vibevm/*`; its product
  coordinate may be `org.speckit/speckit`. Packaging Matt Pocock's collection
  may be `com.mattpocock/skills`. The distinct steward remains visible through
  `[package].authors`, the package distribution `source_url`, `bridge = true`,
  and the upstream repository/PURL recorded by `[[embedded_source]]` and
  `describes`. @status:impl/done
- @fact:PROVENANCE-VISIBLE A consumer can therefore see three separate answers: *what product is this*
  (coordinate), *who maintains this packaging* (authors + distribution source),
  and *what original bytes does it wrap* (upstream provenance). @status:impl/done
- @fact:INDEPENDENT-REPOSITORY-DEFAULT **Not everything we package belongs to VibeVM.** A third-party product's
  package source defaults to its own Git repository outside the VibeVM
  monorepository and outside `org.vibevm/*`. Before scaffolding a bridge, the
  maintainer must explicitly ask the owner whether the package belongs in an
  existing product workspace or should be an independent repository. Silence
  never authorises absorbing a foreign product into the VibeVM namespace.
  Locally, independent package repositories may live under a neutral collection
  such as `~/git/v/packages/`; publication still maps one package repository to
  one registry repository. @status:spec/done
- @fact:LICENSE-BOUNDARY A bridge records two distinct legal surfaces: `[package].license` describes
  only the maintainer-authored bridge package, while every external embedded
  source records its upstream licence and an immutable licence URL. A bridge
  never presents upstream bytes as relicensed by the bridge maintainer. @status:impl/done
- @fact:SKILL-VIA-INCLUDE A skill that
  lives inside the bridged subtree is projected through the normal skill
  machinery, using the `include` selector
  ([PROP-015 #skill-include](../vibe-mcp/PROP-015-mcp-integration.xml#skill-include))
  to pick the relevant files out of an upstream tree full of unrelated content. @status:impl/done

## 3. Rejected alternatives {#rejected}

- [p20] @fact:REJ-BRIDGE-KIND **A `bridge` package kind** (a kind of its own beside the §4.1 register) —
  rejected: the kinds describe *what the package is for*; "bridge"
  describes *where its content came from*. They are orthogonal axes, so bridge
  is a flag, not a kind. (The reasoning survives the register later growing
  `mcp` — that kind, too, says what a package is FOR.) @status:spec/done
- @fact:REJ-AUTO-IMPORT **Auto-importing a foreign repo with no maintainer** — rejected: someone must
  take responsibility for shaping, updating, and vouching for the wrapped code;
  an unowned auto-bridge has no one to fix it when upstream moves or breaks. @status:spec/done

## 4. Out of scope {#out-of-scope}

- [p21] @fact:OOS-AUTO-CONVERSION **Automatic conversion of foreign layouts** into vibevm conventions — bridges
  shape upstream with explicit, maintainer-written hooks, not inferred magic. @status:spec/done
- @fact:OOS-SECURITY-SCAN **Security scanning of wrapped third-party code** — the LLM "antivirus" is
  the same far-backlog item as for hooks
  ([PROP-020 §4](../vibe-workspace/PROP-020-install-hooks.xml#out-of-scope)); a
  bridge's trust posture is dependency selection plus the installed extension
  observability defined by PROP-054, an explicitly accepted risk for now. @status:spec/done
- @fact:OOS-LICENSE-TRACKING **Automated legal interpretation or compatibility adjudication** remains out
  of scope. Mechanical provenance is in scope: the bridge licence and each
  external source's upstream licence/immutable licence URL are recorded and
  displayed as separate facts. @status:spec/done

## 5. Acceptance {#acceptance}

- [p22] @fact:ACC-FLAG-PARSES `[package].bridge` parses as a boolean, defaults `false`, and does not alter
  `kind` or identity. @status:impl/done
- @fact:ACC-VENDORED-PLAIN A vendored bridge installs as a plain `snapshot` package (flag + optional
  hooks), with no submodule/materialization machinery engaged. @status:impl/done
- @fact:ACC-SUBMODULE-FULL A Git-source submodule-backed bridge fetches upstream recursively
  ([PROP-021](PROP-021-submodule-sources.xml)). Registry publication instead
  verifies and flattens each clean populated gitlink, omits Git metadata,
  reports the exact vendored commit, then ordinary install/hooks/skill
  projection consume that plain snapshot. @status:impl/done
- @fact:ACC-REFERENCE A reference-backed bridge repository contains no upstream files or gitlinks;
  install authenticates the declared immutable source into a machine-local
  cache, records the pin in `vibe.lock`, and projects only the declared skill
  paths/resources without executing upstream package machinery. Its normal
  bridge payload is still materialised in `vibedeps`, and an operator can ask
  for either the package root or an authenticated named source root. @status:impl/done
- @fact:ACC-PROVENANCE Product identity, packaging stewardship and upstream URL/PURL are separately
  recoverable for a bridge package; none is inferred from another. @status:impl/done
- @fact:ACC-AUTHORSHIP-SEPARATE Package authors and upstream authors are required, stored and displayed as
  separate provenance fields. @status:impl/done
- @fact:ACC-INDEPENDENT-SOURCE A third-party package is not placed below `org.vibevm` or in VibeVM's
  monorepository without an explicit owner decision; the scaffolding workflow
  asks this boundary question before choosing its coordinate or repository. @status:spec/done
- @fact:ACC-FLOOR-GREEN Full `self-check.sh` green; conform 0/0/0; specmap clean. @status:spec/done

