<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">PROP-008: Qualified package naming — groups, short aliases, collision detection</title>
  <status stage="impl" state="done" comment="B0 2026-07-24: IMPLEMENTED, M1.18 + M1.19"/>
  <p p="1"><fact id="milestone-line" status="impl/done">**Milestone:** `M1.18` + `M1.19` ([`ROADMAP.md`](../../../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.)</fact></p>
  <p p="2"><fact id="status-line" status="impl/done">**Status:** 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).</fact></p>
  <p p="3"><fact id="related" status="spec/done">**Related:** [PROP-002 §2.1 / §3.4](PROP-002-decentralized-registry.xml) (content-addressed identity; the rejection of *per-registry* identity — and why `group` does not violate it); [PROP-005](../vibe-index/PROP-005-package-index.xml) (per-org index — **required** for short-name resolution); [PROP-007](../vibe-workspace/PROP-007-workspace.xml) (workspace — companion document, same design session); [`VIBEVM-SPEC.md` §4.1 / §7.1](../../../VIBEVM-SPEC.md) (the installable kinds; current `name`-uniqueness rule).</fact></p>
  <p p="4"><fact id="design-rationale" status="spec/done">**Design rationale:** [`vibevm/vibespecs/design/workspace-and-qualified-naming.xml`](../../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.</fact></p>
  <p p="5"><fact id="discipline-line" status="spec/done">**Discipline:** 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`).</fact></p>
  <p p="6"><fact id="OWNER-SANCTION" status="impl/done">**Owner 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.</fact></p>
  <section id="motivation" title="1. Motivation">
    <list ordered="false" p="7">
      <item><fact id="flat-namespace" status="impl/done">Before this PROP, vibevm's package namespace was flat: a pkgref was `&lt;kind&gt;:&lt;name&gt;`, `name` "globally unique within its kind". That did not scale — two unrelated authors will both want `flow:wal` — and this PROP is what ended it: §7.1 now carries the group-qualified tuple and the pkgref grammar `[kind:][group/]name[@version]`.</fact></item>
      <item><fact id="prior-art" status="impl/done">Maven solved exactly this with `groupId` (reverse-FQDN) for global uniqueness; npm with `@scope/`.</fact></item>
    </list>
    <list ordered="false" p="8">
      <item><fact id="OWNER-REQUEST" status="impl/done">The 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 types `vibe install wal` in the CLI, but the package is canonically `org.vibevm.world/wal`.</fact></item>
      <item><fact id="collision-behavior-request" status="impl/done">On a name collision, show alternatives; on a dependency conflict, fail without applying the plan; under full-auto, fail rather than guess.</fact></item>
    </list>
    <list ordered="false" p="9">
      <item><fact id="why-not-per-registry" status="impl/done">**Why this does not violate [PROP-002 §3.4](PROP-002-decentralized-registry.xml).** PROP-002 §3.4 rejected *per-registry identity* — `vibespecs/flow:wal` must not be a different identity from `corporate/flow:wal`, because that would make mirror-switching impossible.</fact></item>
      <item><fact id="GROUP-NOT-REGISTRY" status="impl/done">`group` is **not** the registry. `group` is an attribute of the *package* (exactly as Maven's `groupId` is an attribute of the artifact, not of the repository serving it).</fact></item>
      <item><fact id="group-orthogonal" status="impl/done">The registry remains a runtime resolution detail. Adding `group` to the identity tuple is orthogonal to §3.4 and does not reopen it.</fact></item>
    </list>
    <p p="10"><fact id="naming-axis" status="impl/done">PROP-008 covers the naming axis. The companion [PROP-007](../vibe-workspace/PROP-007-workspace.xml) covers workspaces; the two were specified together.</fact></p>
  </section>
  <section id="decisions" title="2. Decisions">
    <section id="group" title="2.1 The `group` field">
      <p p="11"><fact id="req-group" status="impl/done">`req r1`</fact></p>
      <p p="12"><fact id="GROUP-MANDATORY" status="impl/done" action="continue" actionstage="doc" audience="user,author">**Decision.** `[package]` gains a **mandatory** `group` field:</fact></p>
      <fence lang="toml" p="13">[package]
kind    = "flow"
name    = "wal"
group   = "org.vibevm"
version = "0.3.0"</fence>
      <list ordered="false" p="14">
        <item><fact id="GROUP-CONVENTION" status="impl/done">Reverse-FQDN is the **recommended convention**; the core does **not** enforce it. Whether `group` looks like a reversed domain is a matter of style, left to humans and linters. (Maven likewise does not enforce groupId shape.)</fact></item>
        <item><fact id="GROUP-GRAMMAR" status="impl/done" action="continue" actionstage="doc" audience="user,author">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 by `Group::parse`. *Considered and rejected:* keeping `_` (groups would not even be formally domains, and the flat `&lt;group&gt;.&lt;name&gt;` 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.</fact></item>
        <item><fact id="SEGMENT-COUNT-IS-REGISTRY-POLICY" status="impl/done">The core grammar requires **≥ 1 segment** — `acme` parses. 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).</fact></item>
        <item><fact id="GROUP-CANONICAL" status="impl/done">`group` is mandatory as of this PROP. The three then-current canonical packages migrated to `group = "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 under `org.vibevm.world` and `org.vibevm.ai-native` (and `org.vibevm.fractality`); the mandatory-group law itself is unchanged and live in every manifest.</fact></item>
      </list>
    </section>
    <section id="identity" title="2.2 Identity tuple — `(group, name, version, content_hash)`">
      <p p="15"><fact id="req-identity" status="impl/done">`req r1`</fact></p>
      <p p="16"><fact id="IDENTITY-TUPLE" status="impl/done" action="continue" actionstage="doc" audience="user,author">**Decision.** Package identity becomes `(group, name, version, content_hash)`. `kind` **leaves the identity tuple**.</fact></p>
      <list ordered="false" p="17">
        <item><fact id="NAME-UNIQUE-IN-GROUP" status="impl/done" action="continue" actionstage="doc" audience="author">`name` becomes unique **within a `group`** (was: within a `kind`, `VIBEVM-SPEC.md` §7.1). `(group, name)` is therefore unique on its own — `kind` is no longer needed to disambiguate.</fact></item>
        <item><fact id="HASH-UNCHANGED" status="impl/done">`content_hash` is unchanged — computed over package file bytes per [PROP-002 §2.1](PROP-002-decentralized-registry.xml#identity). `group` lives in `vibe.toml`, so it influences the hash only as ordinary file content; the tuple lists it explicitly so that changing `group` yields a different package.</fact></item>
        <item><fact id="GROUP-CHANGE-NEW-PACKAGE" status="impl/done" action="continue" actionstage="doc" audience="user,author">Changing a package's `group` is a new package, not a rename — same discipline as changing `name`.</fact></item>
      </list>
    </section>
    <section id="kind" title="2.3 `kind` becomes pure metadata">
      <p p="18"><fact id="req-kind" status="impl/done">`req r1`</fact></p>
      <p p="19"><fact id="KIND-METADATA" status="impl/done" action="continue" actionstage="doc" audience="user,author">**Decision.** `kind` (`flow` / `feat` / `stack` / `tool`) stays a **mandatory `[package]` field** but is now a pure attribute — it identifies nothing and names nothing.</fact></p>
      <p p="20"><fact id="kind-still-needed-lead" status="impl/done">It is still needed for:</fact></p>
      <list ordered="false" p="21">
        <item><fact id="KIND-PLACEMENT" status="impl/done">content placement — `spec/flows/` vs `spec/feats/` vs `spec/stacks/`;</fact></item>
        <item><fact id="KIND-FILTER" status="impl/done">the `--kind` filter on `vibe list` / `vibe search`;</fact></item>
        <item><fact id="KIND-UX" status="impl/done">the UX signal in a kind-prefixed pkgref (§2.4).</fact></item>
      </list>
      <p p="22"><fact id="TAXONOMY-UNCHANGED" status="impl/done">The 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.</fact></p>
    </section>
    <section id="pkgref" title="2.4 pkgref grammar">
      <p p="23"><fact id="req-pkgref" status="impl/done">`req r1`</fact></p>
      <p p="24"><fact id="PKGREF-GRAMMAR" status="impl/done" action="continue" actionstage="doc" audience="user,author">**Decision.** The pkgref grammar gains an optional `group` segment and makes the `kind` prefix optional:</fact></p>
      <fence p="25">pkgref := [ &lt;kind&gt; ":" ] [ &lt;group&gt; "/" ] &lt;name&gt; [ "@" &lt;version&gt; ]</fence>
      <p p="26"><fact id="PKGREF-SEPARATOR" status="impl/done">The `group`↔`name` separator is `/` (`:` is taken by `kind`, `@` by version).</fact></p>
      <table p="27">
        <tr>
          <td>Form</td>
          <td>Context</td>
          <td>Behaviour</td>
        </tr>
        <tr>
          <td><fact id="ROW-QUALIFIED" status="impl/done">`org.vibevm.world/wal`</fact></td>
          <td><fact id="ROW-QUALIFIED-CONTEXT" status="impl/done">qualified — the form written into manifests (see §2.6, [PROP-002](PROP-002-decentralized-registry.xml))</fact></td>
          <td><fact id="ROW-QUALIFIED-BEHAVIOUR" status="impl/done">resolved exactly</fact></td>
        </tr>
        <tr>
          <td><fact id="ROW-QUALIFIED-KIND" status="impl/done">`flow:org.vibevm.world/wal`</fact></td>
          <td><fact id="ROW-QUALIFIED-KIND-CONTEXT" status="impl/done">qualified + kind</fact></td>
          <td><fact id="ROW-QUALIFIED-KIND-BEHAVIOUR" status="impl/done">resolved exactly; **kind validated against the manifest**</fact></td>
        </tr>
        <tr>
          <td><fact id="ROW-SHORT" status="impl/done">`wal`</fact></td>
          <td><fact id="ROW-SHORT-CONTEXT" status="impl/done">short — CLI sugar</fact></td>
          <td><fact id="ROW-SHORT-BEHAVIOUR" status="impl/done">resolved via the index (§2.6)</fact></td>
        </tr>
        <tr>
          <td><fact id="ROW-SHORT-KIND" status="impl/done">`flow:wal`</fact></td>
          <td><fact id="ROW-SHORT-KIND-CONTEXT" status="impl/done">short + kind</fact></td>
          <td><fact id="ROW-SHORT-KIND-BEHAVIOUR" status="impl/done">resolved via the index; kind validated</fact></td>
        </tr>
      </table>
      <list ordered="false" p="28">
        <item><fact id="KIND-VALIDATION" status="impl/done" action="continue" actionstage="doc" audience="user">**kind validation.** If the `kind` prefix is present, after resolution the resolver asserts `resolved.kind == prefix`; mismatch is a `KindMismatch` error. A kind prefix is validation + a UX signal — it does **not** disambiguate, because by §2.2 `name` is unique within a `group`, so `flow:org.vibevm.world/wal` and `feat:org.vibevm.world/wal` cannot co-exist. A short-name collision is always a *group* collision (§2.7), resolved by group-qualification, never by kind.</fact></item>
        <item><fact id="SHORT-CLI-ONLY" status="impl/done" action="continue" actionstage="doc" audience="user,author">**The short form is CLI-only sugar.** It is never written to a manifest (§2.6).</fact></item>
      </list>
    </section>
    <section id="repo-naming" title="2.5 Repository naming — `naming = &quot;fqdn&quot;`">
      <p p="29"><fact id="req-repo-naming" status="impl/done">`req r1`</fact></p>
      <p p="30"><fact id="FQDN-NAMING" status="impl/done">**Decision.** `kind` leaves the repository name. A new `[[registry]]` naming convention value:</fact></p>
      <fence lang="toml" p="31">[[registry]]
name   = "vibespecs"
url    = "https://github.com/vibespecs"
naming = "fqdn"          # repo name = "&lt;group&gt;.&lt;name&gt;"  →  org.vibevm.world.wal</fence>
      <list ordered="false" p="32">
        <item><fact id="JOINER-UNDERSCORE" status="impl/done" action="continue" actionstage="doc" audience="author">`naming = "fqdn"` maps a pkgref to the repository name `&lt;group&gt;.&lt;name&gt;` (`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 as `name`, the rest as `group`. 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 in `vibespecs` (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.</fact></item>
        <item><fact id="COLLISION-FREE-REPO" status="impl/done">Because `(group, name)` is unique (§2.2), `&lt;group&gt;.&lt;name&gt;` is a collision-free repo name without needing `kind`. The existing `kind-name` / `name` / `kind/name` conventions (PROP-002 §2.2) remain for registries that have not adopted `group`.</fact></item>
        <item><fact id="SHORT-CLI-FAT-REPO" status="impl/done">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.</fact></item>
      </list>
    </section>
    <section id="short-name" title="2.6 Short-name resolution">
      <p p="33"><fact id="req-short-name" status="impl/done">`req r1`</fact></p>
      <p p="34"><fact id="SHORT-AT-BOUNDARY" status="impl/done">**Decision.** 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.</fact></p>
      <list ordered="false" p="35">
        <item><fact id="RESOLVE-ONCE-WRITE-QUALIFIED" status="impl/done" action="continue" actionstage="doc" audience="user">`vibe install wal` resolves the collision once, at the top level, and writes `org.vibevm.world/wal` into `[requires]`. Manifests are therefore always qualified — exactly the cargo/npm pattern (`cargo add serde` on the CLI, `serde = "1"` in `Cargo.toml`).</fact></item>
        <item><fact id="NO-TRANSITIVE-COLLISIONS" status="impl/done">**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.</fact></item>
        <item><fact id="INDEX-DEPENDENCY" status="impl/done" action="continue" actionstage="doc" audience="user">**Index dependency.** Resolving a short name requires enumerating candidates `(*, name)` across registries. The host cannot list an org cheaply ([PROP-005 §1](../vibe-index/PROP-005-package-index.xml) — GitVerse exposes no org listing, GitHub is rate-limited). Therefore short-name resolution **requires [PROP-005](../vibe-index/PROP-005-package-index.xml)**: one HTTP GET of `by-name/&lt;name&gt;.json` per registry yields the candidate set. Without an index, a registry's short names are unavailable and the qualified form is required.</fact></item>
        <item><fact id="LOCKFILE-AUTHORITATIVE" status="impl/done" action="continue" actionstage="doc" audience="user">**Lockfile is authoritative.** If `vibe.lock` already pins `org.vibevm.world/wal`, a later `vibe install wal` resolves to the locked entry — the short name prefers what is already locked.</fact></item>
        <item><fact id="INSTALLED-STATE-RESOLVES-LOCALLY" status="impl/done">**A verb that acts on an already-installed package resolves a short name from `vibe.lock` alone — no index, no network.** `vibe uninstall wal` and `vibe update wal` operate over installed state, and the lockfile *is* that state's record, so the answer sits in the file beside them and `##INDEX-DEPENDENCY` does 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-DEPENDENCY` binds as written and the qualified form stays required — an honest requirement rather than an unfinished one.</fact></item>
      </list>
    </section>
    <section id="collision" title="2.7 Collision vs conflict">
      <p p="36"><fact id="req-collision" status="impl/done">`req r1`</fact></p>
      <p p="37"><fact id="TWO-FAILURE-CLASSES" status="impl/done">**Decision.** Two distinct failure classes, with distinct handling. This terminology is fixed by this PROP.</fact></p>
      <list ordered="false" p="38">
        <item><fact id="COLLISION-DEF" status="impl/done">**Collision (a naming ambiguity).** Two *different* packages match one short name (`wal`) with different `group`. Detected during short-name resolution (§2.6).</fact></item>
        <item><fact id="CONFLICT-DEF" status="impl/done">**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).</fact></item>
      </list>
      <p p="39"><fact id="collision-handling-lead" status="impl/done">Collision handling (new):</fact></p>
      <list ordered="false" p="40">
        <item><fact id="COLLECT-ALL-CANDIDATES" status="impl/done">The 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 `group` exists.)</fact></item>
        <item><fact id="COLLISION-BEHAVIOR" status="impl/done" action="continue" actionstage="doc" audience="user">One candidate → resolve. Multiple candidates with different identity → **collision**:</fact></item>
        <item><fact id="collision-interactive" status="impl/done">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);</fact></item>
        <item><fact id="collision-unattended" status="impl/done">`--unattended` / full-auto — fail-fast; the resolver never guesses.</fact></item>
        <item><fact id="EXIT-CODE-7" status="impl/done" action="continue" actionstage="doc" audience="user">A new exit code **`7`** ("ambiguous package") is assigned, distinct from `3` ("package conflict", `VIBEVM-SPEC.md` §9.4).</fact></item>
      </list>
      <fence p="41">flow: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`.</fence>
      <p p="42"><fact id="CONFLICT-UNCHANGED" status="impl/done">Conflict 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.</fact></p>
    </section>
    <section id="index-ext" title="2.8 Index extension">
      <p p="43"><fact id="req-index-ext" status="impl/done">`req r1`</fact></p>
      <p p="44"><fact id="INDEX-FIELDS" status="impl/done">**Decision.** [PROP-005](../vibe-index/PROP-005-package-index.xml)'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](../vibe-workspace/PROP-007-workspace.xml) `[origin]`).</fact></p>
      <list ordered="false" p="45">
        <item><fact id="BY-NAME-CANDIDATES" status="impl/done">The `by-name/` layer indexes by `name` and returns the candidate set with each candidate's `group`, so §2.6 short-name resolution is one GET per registry.</fact></item>
        <item><fact id="draft-edit-note" status="spec/done">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.</fact></item>
      </list>
    </section>
    <section id="explorer" title="2.9 Registry explorer">
      <p p="46"><fact id="design-explorer" status="spec/done">`design r1`</fact></p>
      <p p="47"><fact id="EXPLORER-DIRECTION" status="spec/done">**Decision (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):</fact></p>
      <list ordered="false" p="48">
        <item><fact id="explorer-group-tree" status="spec/done">a reverse-FQDN group tree with drill-down (`org` → `org.vibevm` → packages → versions), as Maven Central does;</fact></item>
        <item><fact id="explorer-beyond-maven" status="spec/done">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", from `workspace_origin`).</fact></item>
      </list>
      <list ordered="false" p="49">
        <item><fact id="EXPLORER-SEPARATE-LAYER" status="spec/done">The 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).</fact></item>
        <item><fact id="EXPLORER-INDEX-OBLIGATION" status="spec/done">The only obligation on this refactor is that the index carry `group` and `workspace_origin` (§2.8) so the explorer is not a retrofit later.</fact></item>
      </list>
    </section>
    <section id="trust" title="2.10 Trust: the group is a claim, not a credential">
      <p p="50"><fact id="req-trust" status="spec/done">`req r2`</fact></p>
      <p p="51"><fact id="GROUP-IS-A-CLAIM" status="spec/done" action="continue" actionstage="doc" audience="user,author">**Decision (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**.</fact></p>
      <list ordered="false" p="52">
        <item><fact id="DOMAIN-NEVER-VERIFIED" status="spec/done">**Domain 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.</fact></item>
        <item><fact id="ORG-VOUCHES-WITHIN-A-REGISTRY" status="spec/done">**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).</fact></item>
        <item><fact id="CROSS-REGISTRY-TRUTH-IS-THE-HASH" status="spec/done">**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](PROP-002-decentralized-registry.xml#registry-model)) says who is asked first, and the lockfile's `content_hash` pin says what may be reproduced.</fact></item>
        <item><fact id="DEFAULT-TRUSTED-REGISTRIES" status="spec/done" action="continue" actionstage="doc" audience="user">**The default trust set is exactly two roots** — `https://github.com/vibespecs` and `https://gitverse.ru/vibespecs` — trusted by default as the registries `vibe init` writes. Every other registry is trusted **only** by the user's own act of adding it to their configuration (owner ruling, 2026-08-13).</fact></item>
        <item><fact id="DOMAINLESS-IS-REGISTRY-POLICY" status="spec/done">**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 an `io.github.&lt;user&gt;`-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).</fact></item>
      </list>
      <p p="53"><fact id="trust-lifecycle-pointer" status="spec/done">Package **removal, name re-registration, and the visible-lineage obligation** that trust depends on are registry semantics and live in [PROP-002 §2.13](PROP-002-decentralized-registry.xml#lifecycle); the two-tier publish flow that operationalises moderation is [PROP-002 §2.10](PROP-002-decentralized-registry.xml#publish).</fact></p>
    </section>
  </section>
  <section id="migration" title="3. Migration">
    <p p="54"><fact id="design-migration" status="impl/done">`design r1`</fact></p>
    <p p="55"><fact id="breaking-window" status="impl/done">The breaking-change window is open: vibevm has no public release, no external users ([PROP-003](../vibe-resolver/PROP-003-dep-evolution.xml) — "schema churn before v0.1.0 is free").</fact></p>
    <list ordered="false" p="56">
      <item><fact id="MIG-CANONICAL" status="impl/done">**Canonical packages.** `flow-wal`, `flow-sync-from-code`, `flow-atomic-commits` migrate to `group = "org.vibevm"`. Repositories rename to the `naming = "fqdn"` shape (`org.vibevm_wal`, …). The owner authorised migrating the test fixtures and these three without further questions. **Superseded in part by [PROP-029](../../common/PROP-029-fully-qualified-addresses.xml):** the redbook trio's *local* group later moved to `org.vibevm.world`, so their real repos render `org.vibevm.world_wal`; the still-published `org.vibevm` repos and the group-`org.vibevm` test 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.</fact></item>
      <item><fact id="MIG-TEST-ORGS" status="impl/done">**Test orgs.** `vibespecstest1/2/3` fixtures re-laid-out to the new naming.</fact></item>
      <item><fact id="MIG-MANIFESTS" status="impl/done">**Manifests.** `vibe-package.toml` → `vibe.toml` ([PROP-007 §2.2](../vibe-workspace/PROP-007-workspace.xml)); add the `group` field.</fact></item>
      <item><fact id="MIG-LOCKFILE-V5" status="impl/done">**Lockfile.** Schema bumps to **v5** — PROP-007 had already taken v4 for `source_kind = "path"`; adds the `group` field per `[[package]]`.</fact></item>
      <item><fact id="MIG-SPEC-EDIT" status="impl/done">**`VIBEVM-SPEC.md` §7.1** is edited (under the owner sanction) — the `name`-uniqueness rule changes from "within a kind" to "within a group", and the identity tuple and pkgref grammar are updated.</fact></item>
    </list>
  </section>
  <section id="rejected" title="4. Rejected alternatives">
    <list ordered="false" p="57">
      <item><fact id="REJ-PER-REGISTRY" status="spec/done">**Per-registry identity.** Already rejected in [PROP-002 §3.4](PROP-002-decentralized-registry.xml). `group` is a package attribute, not a registry — §1 explains why it does not reopen that decision.</fact></item>
      <item><fact id="REJ-KIND-IN-REPO" status="spec/done">**`kind` in the repository name.** Rejected (this PROP, §2.5). With `(group, name)` unique, `kind` in the repo name is redundant noise; `naming = "fqdn"` drops it.</fact></item>
      <item><fact id="REJ-SHORT-IN-MANIFESTS" status="spec/done">**Short names inside manifests.** Rejected (§2.6). Manifests store the qualified form; short names are CLI-only sugar. This eliminates transitive collisions by construction.</fact></item>
      <item><fact id="REJ-KIND-DISAMBIGUATOR" status="spec/done">**kind prefix as a disambiguator.** Rejected (§2.4). With `name` unique within `group`, the kind prefix can only validate, never disambiguate; a real ambiguity is a group collision.</fact></item>
    </list>
  </section>
  <section id="open" title="5. Open questions">
    <list ordered="true" p="58">
      <item><fact id="OPEN-EXIT-CODE-7" status="impl/done">Exit code `7` — the assignment is finalised: `VIBEVM-SPEC.md` §9.4 carries «7 — ambiguous package» citing this PROP's §2.7, `AMBIGUOUS_PACKAGE: u8 = 7` lives in `exit_code.rs`, and codes 0–7 are taken without collision.</fact></item>
      <item><fact id="OPEN-EXPLORER-SCOPE" status="spec/work">Registry explorer scope (§2.9) — when (if) it becomes a funded milestone, it gets its own PROP.</fact></item>
      <item><fact id="OPEN-FQDN-KIND-VARIANT" status="spec/work">Whether `naming = "fqdn"` should also offer a `kind`-bearing variant for registries that want it, or stay strictly `&lt;group&gt;.&lt;name&gt;`.</fact></item>
    </list>
  </section>
  <section id="phases" title="6. Phase plan">
    <p p="59"><fact id="phase-plan" status="impl/done">PROP-008 depends on [PROP-005](../vibe-index/PROP-005-package-index.xml) being implemented (short-name resolution, §2.6) and is best sequenced after [PROP-007](../vibe-workspace/PROP-007-workspace.xml). 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.</fact></p>
  </section>
  <section id="history" title="7. Version history">
    <list ordered="false" p="60">
      <item><fact id="HISTORY-DRAFT-1" status="spec/done">**2026-05-20 — draft 1.** Initial proposal. Requirements locked in an owner design session (decisions on `group`, identity tuple, `kind`-as-metadata, pkgref grammar, `fqdn` repo naming, index-backed short-name resolution, collision detection, exit code 7, registry explorer as a long-term direction). Open for review.</fact></item>
      <item><fact id="HISTORY-PHASES-1-4-7" status="spec/done">**2026-05-22 — Phases 1–4 + 7 implemented (under MFBT).** The identity core landed on `main`: the `Group` newtype and the mandatory `[package].group` (Phase 1); the `(group, name, version, content_hash)` identity refactor with `kind` demoted to metadata (Phase 2); the lockfile `group` field at schema v5 (Phase 3); the group-native registry with `NamingConvention::Fqdn` as the default (Phase 4). Phase 7 (§2.8) then made the package index group-native — the [PROP-005](../vibe-index/PROP-005-package-index.xml) entry schema gained `group` + `workspace_origin`, the `by-name/` layer became the candidate-set file `by-name/&lt;name&gt;.json`, and the `vibe-registry` index client + `vibe-publish` post-publish hook were realigned. **Remaining:** Phase 5 (index-backed short-name resolution at the CLI boundary, §2.6), Phase 6 (collision detection + exit code `7`, §2.7), Phase 8 (canonical-package migration + the `VIBEVM-SPEC.md §7.1` edit + docs, §3).</fact></item>
      <item><fact id="HISTORY-PHASES-5-6-8" status="spec/done">**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 code `7` (`InstallError::AmbiguousPackage`); the live-registry migration to `fqdn` naming and the `vibe init` default 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.</fact></item>
      <item><fact id="HISTORY-UNIT-TYPING" status="spec/done">**2026-06-12 — unit typing (the depth program).** §2.1–2.8 typed `req r1`; §2.9 and §3 typed `design r1`; the Status line updated from the stale DRAFT to the shipped reality.</fact></item>
    </list>
  </section>
</spec>
