PROP-008: Qualified package naming — groups, short aliases, collision detection
01Milestone: M1.18 + M1.19 (ROADMAP.md) — shipped, implementation-locked. (The line read "design proposal … not implementation-locked" until 2026-07-25; it had never been reconciled with the IMPLEMENTED status one line below.)
02Status: IMPLEMENTED — Phases 1–4 + 7 landed 2026-05-22 (M1.18, see §7); Phases 5–6 + 8 landed with M1.19 (index-backed short-name resolution at the CLI boundary — vibe-cli::commands::short_name; collision detection with exit code 7 — InstallError::AmbiguousPackage; the live-registry fqdn migration). Decision units typed at REQ grain 2026-06-12 (the depth program).
03Related: PROP-002 §2.1 / §3.4 (content-addressed identity; the rejection of per-registry identity — and why group does not violate it); PROP-005 (per-org index — required for short-name resolution); PROP-007 (workspace — companion document, same design session); VIBEVM-SPEC.md §4.1 / §7.1 (the installable kinds; current name-uniqueness rule).
04Design rationale: vibevm/vibespecs/design/workspace-and-qualified-naming.xml — the why and the lore behind this PROP: the owner's mental model, the fork-by-fork decision record, the Cargo-vs-Maven precedents. Non-normative; this PROP is the contract.
05Discipline: the general namespace-scaling laws — why a flat namespace fails, the mandatory group, the identity tuple, why a rename is a new identity, why short names live only at the human boundary, and why a collision and a conflict are different failures — are the qualified-naming flow: spec://org.vibevm.world/qualified-naming/flows/qualified-naming/QUALIFIED-NAMING-PROTOCOL#root. This PROP is vibevm's IMPLEMENTED instance of them — the org.vibevm.* groups, the CLI short-name resolution (vibe-cli::commands::short_name), and collision detection (InstallError::AmbiguousPackage).
06Owner sanction: the owner granted (2026-05-20) explicit sanction to edit any specification, including VIBEVM-SPEC.md §7.1. PROP-008 is the requirements record; the VIBEVM-SPEC.md edit lands at implementation time.
1. Motivation
- 07Before this PROP, vibevm's package namespace was flat: a pkgref was
<kind>:<name>,name"globally unique within its kind". That did not scale — two unrelated authors will both wantflow:wal— and this PROP is what ended it: §7.1 now carries the group-qualified tuple and the pkgref grammar[kind:][group/]name[@version]. - Maven solved exactly this with
groupId(reverse-FQDN) for global uniqueness; npm with@scope/.
- 08The owner's request (design session 2026-05-20): introduce reverse-FQDN qualification at the top level (
org.vibevm), while keeping short names usable — a user typesvibe install walin the CLI, but the package is canonicallyorg.vibevm.world/wal. - On a name collision, show alternatives; on a dependency conflict, fail without applying the plan; under full-auto, fail rather than guess.
- 09Why this does not violate PROP-002 §3.4. PROP-002 §3.4 rejected per-registry identity —
vibespecs/flow:walmust not be a different identity fromcorporate/flow:wal, because that would make mirror-switching impossible. groupis not the registry.groupis an attribute of the package (exactly as Maven'sgroupIdis an attribute of the artifact, not of the repository serving it).- The registry remains a runtime resolution detail. Adding
groupto the identity tuple is orthogonal to §3.4 and does not reopen it.
10PROP-008 covers the naming axis. The companion PROP-007 covers workspaces; the two were specified together.
2. Decisions
2.1 The group field
11req r1
12Decision. [package] gains a mandatory group field:
13[package]
kind = "flow"
name = "wal"
group = "org.vibevm"
version = "0.3.0"
- 14Reverse-FQDN is the recommended convention; the core does not enforce it. Whether
grouplooks like a reversed domain is a matter of style, left to humans and linters. (Maven likewise does not enforce groupId shape.) - Grammar (owner ruling 2026-08-13 — «настоящие домены»): dot-separated segments, each an LDH hostname label —
[a-z0-9-]+, ASCII lowercase, hyphen never at a label edge;_is forbidden (it is not legal in a domain). Interior doubled hyphens stay legal, as DNS itself allows (xn--…punycode). A group is therefore grammatically a valid reversed FQDN even though semantically it is a claim, not a credential (§2.10). Enforced byGroup::parse. Considered and rejected: keeping_(groups would not even be formally domains, and the flat<group>.<name>carrier §2.5 would lose its unambiguous split); recording "FQDN-like, not FQDN-valid" as a deliberate looseness (the ruling chose real domain rules). Revisit: a real-world group needing_appears — it cannot, if groups track domains. - The core grammar requires ≥ 1 segment —
acmeparses. Requiring ≥ 2 segments, reverse-FQDN shape, or any other domain-likeness is registry policy, enforced at registration time by the registry's moderation or web surface (owner ruling 2026-08-13), never by the core. Short single-segment groups are the norm for local registries (§2.10). groupis mandatory as of this PROP. The three then-current canonical packages migrated togroup = "org.vibevm"(§3) — the owner's reverse-FQDN, recorded at the time as the one canonical group. Superseded on the group's NAME by PROP-029 (2026-07-12): first-party packages now live underorg.vibevm.worldandorg.vibevm.ai-native(andorg.vibevm.fractality); the mandatory-group law itself is unchanged and live in every manifest.
2.2 Identity tuple — (group, name, version, content_hash)
15req r1
16Decision. Package identity becomes (group, name, version, content_hash). kind leaves the identity tuple.
- 17
namebecomes unique within agroup(was: within akind,VIBEVM-SPEC.md§7.1).(group, name)is therefore unique on its own —kindis no longer needed to disambiguate. content_hashis unchanged — computed over package file bytes per PROP-002 §2.1.grouplives invibe.toml, so it influences the hash only as ordinary file content; the tuple lists it explicitly so that changinggroupyields a different package.- Changing a package's
groupis a new package, not a rename — same discipline as changingname.
2.3 kind becomes pure metadata
18req r1
19Decision. kind (flow / feat / stack / tool) stays a mandatory [package] field but is now a pure attribute — it identifies nothing and names nothing.
20It is still needed for:
- 21content placement —
spec/flows/vsspec/feats/vsspec/stacks/; - the
--kindfilter onvibe list/vibe search; - the UX signal in a kind-prefixed pkgref (§2.4).
22The kind taxonomy (VIBEVM-SPEC.md §4.1 — four kinds when this PROP was written; six today, mcp and lang joined by PROP-025/027 and the 2026-08-06 ruling) is unchanged in importance — it simply stops being part of identity and repository naming, whatever its member count.
2.4 pkgref grammar
23req r1
24Decision. The pkgref grammar gains an optional group segment and makes the kind prefix optional:
25pkgref := [ <kind> ":" ] [ <group> "/" ] <name> [ "@" <version> ]
26The group↔name separator is / (: is taken by kind, @ by version).
| Form | Context | Behaviour |
|---|---|---|
org.vibevm.world/wal |
qualified — the form written into manifests (see §2.6, PROP-002) | resolved exactly |
flow:org.vibevm.world/wal |
qualified + kind | resolved exactly; kind validated against the manifest |
wal |
short — CLI sugar | resolved via the index (§2.6) |
flow:wal |
short + kind | resolved via the index; kind validated |
- 28kind validation. If the
kindprefix is present, after resolution the resolver assertsresolved.kind == prefix; mismatch is aKindMismatcherror. A kind prefix is validation + a UX signal — it does not disambiguate, because by §2.2nameis unique within agroup, soflow:org.vibevm.world/walandfeat:org.vibevm.world/walcannot co-exist. A short-name collision is always a group collision (§2.7), resolved by group-qualification, never by kind. - The short form is CLI-only sugar. It is never written to a manifest (§2.6).
2.5 Repository naming — naming = "fqdn"
29req r1
30Decision. kind leaves the repository name. A new [[registry]] naming convention value:
31[[registry]]
name = "vibespecs"
url = "https://github.com/vibespecs"
naming = "fqdn" # repo name = "<group>.<name>" → org.vibevm.world.wal
- 32
naming = "fqdn"maps a pkgref to the repository name<group>.<name>(org.vibevm.world/wal→org.vibevm.world.wal) — the composite is itself a valid reversed FQDN, and that is the point of the ruling. Owner ruling 2026-08-13 («убери подчёркивания везде, чтобы получились настоящие FQDN с доменными правилами»), superseding the 2026-05_-joiner decision recorded in this unit's earlier text. The split stays deterministic without a charset-excluded joiner: the name is a single dot-free LDH label (validate_package_name), so the last dot is always the boundary — parse back by taking the last label asname, the rest asgroup. The old rationale's premises are both gone:_was then legal inside groups (no longer — §2.1 LDH), and the composite was not required to be a domain (now it is). Considered and rejected: keeping_(the composite is not even formally a domain; and a_-joined name contradicts the LDH ruling the halves now obey). Migration: live_-joined repositories invibespecs(M0/M1 scale) rename to the dot form as a follow-up of the 2026-08-13 landing — pre-public and cheap, and GitHub redirects renamed repositories; the §3 history below records the_-era as it happened and is not rewritten. Revisit: a hosting provider that forbids.in repository names appears in the registry set — then that provider's adapter gets its own naming value, never a silent re-join. - Because
(group, name)is unique (§2.2),<group>.<name>is a collision-free repo name without needingkind. The existingkind-name/name/kind/nameconventions (PROP-002 §2.2) remain for registries that have not adoptedgroup. - This realises the owner's "short name in the CLI, fat name in the repository" goal: the repository is the pure reverse-FQDN; the CLI keeps the short alias.
2.6 Short-name resolution
33req r1
34Decision. A short name (wal, flow:wal) is resolved only at the CLI input boundary — through the index, or from vibe.lock alone where the verb acts on installed state (##INSTALLED-STATE-RESOLVES-LOCALLY). Manifests always store the qualified form.
- 35
vibe install walresolves the collision once, at the top level, and writesorg.vibevm.world/walinto[requires]. Manifests are therefore always qualified — exactly the cargo/npm pattern (cargo add serdeon the CLI,serde = "1"inCargo.toml). - Consequence — no transitive collisions. Every package's
[requires]is qualified (its author published through the same flow). The dependency graph is built from qualified names; short-name resolution never recurses into the graph. It happens once, for a human-typed CLI argument. - Index dependency. Resolving a short name requires enumerating candidates
(*, name)across registries. The host cannot list an org cheaply (PROP-005 §1 — GitVerse exposes no org listing, GitHub is rate-limited). Therefore short-name resolution requires PROP-005: one HTTP GET ofby-name/<name>.jsonper registry yields the candidate set. Without an index, a registry's short names are unavailable and the qualified form is required. - Lockfile is authoritative. If
vibe.lockalready pinsorg.vibevm.world/wal, a latervibe install walresolves to the locked entry — the short name prefers what is already locked. - A verb that acts on an already-installed package resolves a short name from
vibe.lockalone — no index, no network.vibe uninstall walandvibe update waloperate over installed state, and the lockfile is that state's record, so the answer sits in the file beside them and##INDEX-DEPENDENCYdoes not bind: a name the lockfile does not carry is not installed — a local failure with a local remedy, never a lookup that could not be performed. Requiring the qualified form there would be a restriction with no cause behind it. The counter-case is the registry-side redirect verbs (vibe registry redirect,redirect-sync,redirect-update), which create and maintain a stub for a package that need not be installed at all: there is no lockfile to answer from, so##INDEX-DEPENDENCYbinds as written and the qualified form stays required — an honest requirement rather than an unfinished one.
2.7 Collision vs conflict
36req r1
37Decision. Two distinct failure classes, with distinct handling. This terminology is fixed by this PROP.
- 38Collision (a naming ambiguity). Two different packages match one short name (
wal) with differentgroup. Detected during short-name resolution (§2.6). - Conflict (a dependency conflict). The depsolver cannot satisfy version constraints — incompatible constraints, declared
[conflicts], an unsatisfiable diamond. Already handled (PROP-002 §2.9 — resolvo/libsolv conflict-explanation chain).
39Collision handling (new):
- 40The resolver collects all candidates of a short name — it does not stop at the first registry. (PROP-002 §2.2's first-match-wins remains correct for the same package mirrored across registries — identical identity. It is wrong for different packages sharing a short name; the two are distinguishable only once
groupexists.) - One candidate → resolve. Multiple candidates with different identity → collision:
- interactive TTY — print the alternatives and fail with a hint pointing at the qualified form (no interactive pick: the choice must be recorded deliberately, not clicked);
--unattended/ full-auto — fail-fast; the resolver never guesses.- A new exit code
7("ambiguous package") is assigned, distinct from3("package conflict",VIBEVM-SPEC.md§9.4).
41flow:wal is ambiguous — 2 packages match:
1. org.vibevm.world/wal (registry vibespecs)
2. com.acme/wal (registry acme-internal)
Re-run with the qualified form, e.g. `vibe install org.vibevm.world/wal`.
42Conflict handling is unchanged: the install pipeline is already atomic (resolve → plan → confirm → apply); a failed resolve never reaches apply — "fail without applying the plan", as the owner specified.
2.8 Index extension
43req r1
44Decision. PROP-005's entry schema (§2.6) gains two fields: group (mandatory, §2.1) and workspace_origin (optional — set when the package was published from a workspace, PROP-007 §2.8 [origin]).
- 45The
by-name/layer indexes bynameand returns the candidate set with each candidate'sgroup, so §2.6 short-name resolution is one GET per registry. - PROP-005 was a draft when these edits were written; it has since shipped (implemented, folded into the workspace 2026-05-22), and the §2.8 edits below live in that implemented contract.
2.9 Registry explorer
46design r1
47Decision (forward-looking, out of implementation scope). The index makes a Maven-Central-style browsable visualisation possible — and richer. A vibevm registry explorer is recorded here as a long-term direction (a ROADMAP.md M3+ entry):
- 48a reverse-FQDN group tree with drill-down (
org→org.vibevm→ packages → versions), as Maven Central does; - beyond Maven Central: filter by
kind; a capability graph ([provides]/[requires]);describes/PURL links to upstream libraries; redirect-stub delegation; the full dependency DAG; and workspace provenance ("Y is a sub-package of X", fromworkspace_origin).
- 49The explorer is a separate, optional layer over the index — not part of PROP-008's implementation. PROP-005 §2.10 already reserves the hook (
vibe-index serve, CORS-open read endpoints). - The only obligation on this refactor is that the index carry
groupandworkspace_origin(§2.8) so the explorer is not a retrofit later.
2.10 Trust: the group is a claim, not a credential
50req r2
51Decision (owner ruling, 2026-08-13). A group is a claim, not a credential. Nothing verifies that the author of com.google/x owns google.com — and nothing ever will: vibevm is decentralised and has no central verifier to delegate to (Maven's central domain verification is the model we deliberately do not inherit). The claim's grammar is domain-shaped (§2.1); its semantics carry no domain-ownership assertion.
- 52Domain existence is never checked, in any layer, ever. No resolver, registry client, publisher, or linter of the core performs a DNS lookup or any other existence/ownership test on a group. A future contributor "improving" this would be reverting an owner ruling.
- Within one registry, the organisation vouches for the group. Legitimacy of a name is established at registration time by the registry's own admission mechanism — for the default registries, a moderator admitting the package (the OpenVSX model): creating the package repository is the act of vouching. Naming policy (≥ 2 segments, reverse-FQDN shape, squatting rules) is that registry's business, enforced by its moderation or registration surface — never by the core (§2.1).
- Across registries, truth is held by the content hash, not the name. Two registries may serve different packages under one
(group, name)— a local registry shadowing a well-known package with a patched build is a legal, normal configuration (the Maven-local-override pattern), not an attack by definition. The consumer's protections are structural: the priority-ordered[[registry]]list (PROP-002 §2.2) says who is asked first, and the lockfile'scontent_hashpin says what may be reproduced. - The default trust set is exactly two roots —
https://github.com/vibespecsandhttps://gitverse.ru/vibespecs— trusted by default as the registriesvibe initwrites. Every other registry is trusted only by the user's own act of adding it to their configuration (owner ruling, 2026-08-13). - No blessed convention for authors without domains. A group like
oleg.examples— a domain that exists nowhere — is fully legal at the core and normal in a local registry. Whether a public registry admits it (or requires anio.github.<user>-class convention) is that registry's naming policy. The core blesses nothing (owner ruling, 2026-08-13, superseding the earlier recommendation to bless one convention).
53Package removal, name re-registration, and the visible-lineage obligation that trust depends on are registry semantics and live in PROP-002 §2.13; the two-tier publish flow that operationalises moderation is PROP-002 §2.10.
3. Migration
54design r1
55The breaking-change window is open: vibevm has no public release, no external users (PROP-003 — "schema churn before v0.1.0 is free").
- 56Canonical packages.
flow-wal,flow-sync-from-code,flow-atomic-commitsmigrate togroup = "org.vibevm". Repositories rename to thenaming = "fqdn"shape (org.vibevm_wal, …). The owner authorised migrating the test fixtures and these three without further questions. Superseded in part by PROP-029: the redbook trio's local group later moved toorg.vibevm.world, so their real repos renderorg.vibevm.world_wal; the still-publishedorg.vibevmrepos and the group-org.vibevmtest fixtures trail that move and rename on their next publish. The 2026-08-13 dot-join ruling (§2.5) supersedes the rename target: the pending renames land directly on the dot form. - Test orgs.
vibespecstest1/2/3fixtures re-laid-out to the new naming. - Manifests.
vibe-package.toml→vibe.toml(PROP-007 §2.2); add thegroupfield. - Lockfile. Schema bumps to v5 — PROP-007 had already taken v4 for
source_kind = "path"; adds thegroupfield per[[package]]. VIBEVM-SPEC.md§7.1 is edited (under the owner sanction) — thename-uniqueness rule changes from "within a kind" to "within a group", and the identity tuple and pkgref grammar are updated.
4. Rejected alternatives
- 57Per-registry identity. Already rejected in PROP-002 §3.4.
groupis a package attribute, not a registry — §1 explains why it does not reopen that decision. kindin the repository name. Rejected (this PROP, §2.5). With(group, name)unique,kindin the repo name is redundant noise;naming = "fqdn"drops it.- Short names inside manifests. Rejected (§2.6). Manifests store the qualified form; short names are CLI-only sugar. This eliminates transitive collisions by construction.
- kind prefix as a disambiguator. Rejected (§2.4). With
nameunique withingroup, the kind prefix can only validate, never disambiguate; a real ambiguity is a group collision.
5. Open questions
- 58Exit code
7— the assignment is finalised:VIBEVM-SPEC.md§9.4 carries «7 — ambiguous package» citing this PROP's §2.7,AMBIGUOUS_PACKAGE: u8 = 7lives inexit_code.rs, and codes 0–7 are taken without collision. - Registry explorer scope (§2.9) — when (if) it becomes a funded milestone, it gets its own PROP.
- Whether
naming = "fqdn"should also offer akind-bearing variant for registries that want it, or stay strictly<group>.<name>.
6. Phase plan
59PROP-008 depends on PROP-005 being implemented (short-name resolution, §2.6) and is best sequenced after PROP-007. Suggested order: PROP-007 (workspace) → PROP-005 implementation (index) → PROP-008 (qualified naming) → collision-detection slice (§2.7). The group field, identity-tuple change, pkgref grammar, and naming = "fqdn" can land before short-name resolution; short-name resolution and collision detection land once the index is real.
7. Version history
- 602026-05-20 — draft 1. Initial proposal. Requirements locked in an owner design session (decisions on
group, identity tuple,kind-as-metadata, pkgref grammar,fqdnrepo naming, index-backed short-name resolution, collision detection, exit code 7, registry explorer as a long-term direction). Open for review. - 2026-05-22 — Phases 1–4 + 7 implemented (under MFBT). The identity core landed on
main: theGroupnewtype and the mandatory[package].group(Phase 1); the(group, name, version, content_hash)identity refactor withkinddemoted to metadata (Phase 2); the lockfilegroupfield at schema v5 (Phase 3); the group-native registry withNamingConvention::Fqdnas the default (Phase 4). Phase 7 (§2.8) then made the package index group-native — the PROP-005 entry schema gainedgroup+workspace_origin, theby-name/layer became the candidate-set fileby-name/<name>.json, and thevibe-registryindex client +vibe-publishpost-publish hook were realigned. Remaining: Phase 5 (index-backed short-name resolution at the CLI boundary, §2.6), Phase 6 (collision detection + exit code7, §2.7), Phase 8 (canonical-package migration + theVIBEVM-SPEC.md §7.1edit + docs, §3). - 2026-05-23 — Phases 5 + 6 + 8 shipped with M1.19. Short-name resolution at the CLI input boundary (
vibe-cli::commands::short_name— index-backed candidate sets, lockfile-prefers-locked); collision detection with the dedicated exit code7(InstallError::AmbiguousPackage); the live-registry migration tofqdnnaming and thevibe initdefault fix (cc32d7e— the M1.19 defect AUDIT 2026-05-23-02 records). This entry back-fills the record: the work shipped with M1.19 but the history was not updated at the time. - 2026-06-12 — unit typing (the depth program). §2.1–2.8 typed
req r1; §2.9 and §3 typeddesign r1; the Status line updated from the stale DRAFT to the shipped reality.