<?xml version="1.0" encoding="UTF-8"?>
<spec xmlns="https://vibevm.org/spec/1">
  <title id="root">PROP-024 — Code-bearing packages (a package is a project)</title>
  <status stage="spec" state="done" action="continue" comment="B0 2026-07-24: proposed 2026-06-27, owner-directed; fact grain 2026-07-24"/>
  <p p="1"><fact id="status-line" status="impl/done">**Status: IMPLEMENTED** (proposed 2026-06-27, owner-directed; verified against
the tree 2026-07-25 by the spec-actualization campaign — this very session boots on a
vendored toolchain delivered exactly this way). Makes a vibe package able to
ship runnable code, not only prompt content, so the discipline's verification
tools (the conform checker, the specmap/specmark traceability engine) can live
*inside* the discipline packages instead of being hardcoded in the vibevm
workspace. A consumer who installs `stack:org.vibevm.ai-native/rust-ai-native-lang` then has
the working checkers, not a prose description of them.</fact></p>
  <p p="2"><fact id="related" status="spec/done">**Related:** [PROP-002 §2.1](../modules/vibe-registry/PROP-002-decentralized-registry.xml#identity)
(content-hash identity — re-scoped here to the shippable tree),
[PROP-007](../modules/vibe-workspace/PROP-007-workspace.xml) (workspace +
`vibedeps/`), [PROP-009 §2.1/§2.3](../modules/vibe-workspace/PROP-009-loading-model.xml#two-trees)
(the verbatim materialise step + the boot path it emits),
[PROP-011 §2.6](../modules/vibe-workspace/PROP-011-incremental-install.xml)
(in-workspace `file://` mutability — the dev loop that re-materialises edited
package source), [PROP-020](../modules/vibe-workspace/PROP-020-install-hooks.xml)
(the `post-install` build hook), [PROP-022 §2.2](../modules/vibe-workspace/PROP-022-materialization-modes.xml#snapshot)
(the `copy`-mode tree — re-scoped here), the discipline mechanism specs
[ENGINE-CONFORM](../../vibedeps/org.vibevm.ai-native.core-ai-native/1.0.0/vibevm/vibespecs/mechanisms/ENGINE-CONFORM-v0.1.xml) and
[PROP-014](../../vibedeps/org.vibevm.ai-native.core-ai-native/1.0.0/vibevm/vibespecs/mechanisms/PROP-014-specmap-bidirectional-traceability.xml)
(the tools that relocate; the specs themselves now ship in
`flow:org.vibevm.ai-native/core-ai-native` — `spec://org.vibevm.ai-native/core-ai-native/mechanisms/…`).</fact></p>
  <p p="3"><fact id="SANCTION-GRANTED" status="spec/done">**Owner sanction:** PROP-024 reshapes the owner-frozen `VIBEVM-SPEC.md` (§4.2
layout, §7.2 package contents, §7.3 manifest, §7.4 identity, §12 linter, §13.1
package layout). The `VIBEVM-SPEC.md` edits required explicit owner sanction; it
was **granted 2026-06-27** — the same precedent as
[PROP-009 §5 item 8](../modules/vibe-workspace/PROP-009-loading-model.xml#open).</fact></p>
  <section id="motivation" title="1. Motivation">
    <section id="problem" title="1.1 The problem — a package can describe a tool but not ship one">
      <list ordered="false" p="4">
        <item><fact id="prompt-only-today" status="spec/done">The discipline packages (`flow:org.vibevm.ai-native/core-ai-native`,
  `stack:org.vibevm.ai-native/rust-ai-native-lang`, `…/typescript-ai-native`) carry the
  manifesto, the guides, and the nine pattern cards — **prompt content**.</fact></item>
        <item><fact id="machinery-hardcoded" status="spec/done">But the machinery that makes the discipline *real* — the conform checker
  (Class-F/G rules, the file-length budget, the unwrap ban) and the
  specmap/specmark traceability engine — is **hardcoded as crates inside the
  vibevm workspace** (`crates/conform-core`, `crates/conform-frontend-rust`,
  `crates/specmark`, `crates/specmap-core`, the `cargo xtask conform`/`specmap`
  drivers).</fact></item>
      </list>
      <list ordered="false" p="5">
        <item><fact id="install-gets-description" status="spec/done">Install `stack:org.vibevm.ai-native/rust-ai-native-lang` today and you get a *description* of
  checkers you do not have. To actually run the discipline you would have to
  re-implement the very tools vibevm already wrote.</fact></item>
        <item><fact id="not-distributable" status="spec/done">The discipline is therefore **not distributable**: its strong-author artifacts
  (guide, cards) ship, but its runtime (the checkers) does not.</fact></item>
        <item><fact id="gap-consequence" status="spec/done">This is the gap that, left open, makes spec-conformance "fall apart" for any
  consumer that is not vibevm itself.</fact></item>
      </list>
      <list ordered="false" p="6">
        <item><fact id="format-cause" status="spec/done">The package format is the cause: a package is defined as a bundle of prompt
  files (`VIBEVM-SPEC.md` §7.2 — "vibe.toml, README.md, other content files
  referenced by the manifest"), materialised verbatim.</fact></item>
        <item><fact id="nowhere-for-code" status="spec/done">There is nowhere to put code.</fact></item>
        <item><fact id="identity-fights-code" status="spec/done">The identity/materialisation machinery (`content_hash` over every file,
  full-tree copy) actively fights it — a Rust crate's `target/` would make
  identity non-deterministic and the copy ruinous.</fact></item>
      </list>
    </section>
    <section id="shape" title="1.2 The shape — a package is a project">
      <p p="7"><fact id="project-shape" status="spec/done">A vibe *project* already has the right shape: an authored `spec/` corpus
(`VIBEVM-SPEC.md` §4.2 — "`spec/` is *the* spec directory") plus arbitrary code
at the root (`Cargo.toml`, `crates/`, `src/`) plus one `vibe.toml`. A *package*
should be the same object, made installable:</fact></p>
      <p p="8"><fact id="PACKAGE-SHAPE" status="spec/done">**prompt/spec content under `spec/`, arbitrary code at the root, one `vibe.toml`.**</fact></p>
      <list ordered="false" p="9">
        <item><fact id="shape-consequences" status="spec/done">Then a package can ship its tools; an installed package is immediately usable;
  and authoring a package *is* authoring a project — the same layout, the same
  `vibe check`, the same boot computation.</fact></item>
        <item><fact id="four-layer-fit" status="spec/done">The discipline's own four-layer model lands cleanly: L1/L2/L3 (manifesto,
  guide, cards — prompts) live under `spec/`, and L4 (the implemented checkers —
  code) lives at the root.</fact></item>
      </list>
    </section>
  </section>
  <section id="decisions" title="2. Decisions">
    <section id="package-is-project" title="2.1 A package is a project — `spec/` for prompts, the root for code">
      <p p="10"><fact id="req-package-is-project" status="spec/done">`req r1`</fact></p>
      <p p="11"><fact id="PKG-PROJECT-LAW" status="spec/done" action="continue" actionstage="doc" audience="author">**Decision.** A package has the identical on-disk shape as a consumer project:</fact></p>
      <list ordered="false" p="12">
        <item><fact id="SPEC-SUBTREE" status="spec/done" action="continue" actionstage="doc" audience="author">**Prompt/spec content lives under the package's `spec/` subtree** — boot
  snippets (`vibevm/vibespecs/boot/`), cards, guides, manifesto, appendix — laid out exactly
  as an ordinary project's `spec/` (`VIBEVM-SPEC.md` §4.2). `[boot_snippet].source`
  is a `spec/`-relative path (e.g. `spec/boot/20-stack-rust-ai-native.md`).</fact></item>
        <item><fact id="ROOT-CODE" status="spec/done" action="continue" actionstage="doc" audience="author">**The package root holds arbitrary code** (e.g. `Cargo.toml` + `crates/`) and
  `vibe.toml`, exactly as a project root does. Code is optional — a prompt-only
  package (e.g. `discipline-core`) simply has no code at its root.</fact></item>
      </list>
      <list ordered="false" p="13">
        <item><fact id="CONS-DEV-IS-DEV" status="spec/done">**Consequence.** Developing a package is developing a project.</fact></item>
        <item><fact id="CONS-CHECK-UNCHANGED" status="spec/done">`vibe check` applies unchanged (its §12 Check 7 — `vibevm/vibespecs/boot/` exists and holds
  only markdown — is satisfied by the package's own `vibevm/vibespecs/boot/`).</fact></item>
        <item><fact id="CONS-BOOT-SAME" status="spec/done">The package's own boot sequence is computed the same way as any project's.</fact></item>
        <item><fact id="CONS-NO-CONVENTION" status="spec/done">There is no package-only directory convention to learn.</fact></item>
      </list>
      <p p="14"><fact id="RETIRES-FLAT-LAYOUT" status="spec/done">This **retires the flat package layout** (boot snippets and content at the
package root) that the real packages drifted into; it aligns them with — and
extends — `VIBEVM-SPEC.md` §13.1's own canonical example, which already places a
package's content under `spec/`.</fact></p>
    </section>
    <section id="shippable-tree" title="2.2 The shippable tree excludes build output">
      <p p="15"><fact id="req-shippable-tree" status="spec/done">`req r1`</fact></p>
      <p p="16"><fact id="SHIPPABLE-TREE-DEF" status="spec/done" action="continue" actionstage="doc" audience="author">**Decision.** A package's **shippable tree** is its directory minus a
build-output denylist:</fact></p>
      <fence p="17">.git/        .vibe/        target/        node_modules/</fence>
      <list ordered="false" p="18">
        <item><fact id="VIBEIGNORE-EXTENDS" status="spec/done">plus any glob listed in an optional `.vibeignore` at the package root.</fact></item>
        <item><fact id="SHIPPABLE-CONSUMERS" status="spec/done">The `content_hash` (PROP-002 §2.1), the snapshot copy (PROP-022 §2.2), and the
  verbatim materialised slot (PROP-009 §2.1) all operate over the **shippable
  tree**, never the raw directory.</fact></item>
      </list>
      <list ordered="false" p="19">
        <item><fact id="WHY-SOURCE-IDENTITY" status="spec/done">**Why.** Identity is the *source*, never build artifacts: build output is
  non-deterministic (timestamps, host paths, incremental state) and may be
  gigabytes — hashing or copying it would make identity unstable and
  materialisation ruinous, the exact failure PROP-022 §1.1 names for "big in file
  count".</fact></item>
        <item><fact id="SOURCE-IS-SHIPPED" status="spec/done">A package's source — what its author commits — is precisely what is
  hashed, copied, and vendored.</fact></item>
        <item><fact id="REFERENCE-BRIDGE-SOURCE-BOUNDARY" status="impl/done">For a reference-backed bridge, the package source is only its
  maintainer-authored manifest and adapters. A declared external embedded tree
  is independently authenticated and cached; it is not part of the package
  shippable tree or package `content_hash`, and it is never published as if the
  bridge maintainer authored it.</fact></item>
      </list>
      <list ordered="false" p="20">
        <item><fact id="VERBATIM-PRESERVED" status="spec/done">**"Verbatim" is preserved for the source.** PROP-009 §2.1 / `VIBEVM-SPEC.md`
  §13.1 guarantee no path rewriting and no per-file write list — a human reading
  the package directory sees exactly what materialises.</fact></item>
        <item><fact id="VERBATIM-HOLDS-SHIPPABLE" status="spec/done">That guarantee holds unchanged for the shippable tree: build output was never
  part of the authored tree (it is gitignored in the package's own repository
  too).</fact></item>
        <item><fact id="DENYLIST-NOT-SELECTION" status="spec/done">The denylist formalises "what was never source", it does not introduce
  selection.</fact></item>
        <item><fact id="shippable-rejected" status="spec/done">**Considered and rejected:** **hashing and copying build output too** — rejected: non-deterministic and potentially gigabytes, the file-count/byte-count failure PROP-022 §1.1 exists to avoid (`##REJ-HASH-BUILD-OUTPUT`); **a per-file `[ship]` / `[files]` allow-list in the manifest** — rejected: it resurrects the per-file write list PROP-009 §2.6 retired, and a denylist keeps *"what ships" == "the source"*, preserving the verbatim guarantee (`##REJ-ALLOW-LIST`, `##VERBATIM-PRESERVED`).</fact></item>
        <item><fact id="shippable-revisit" status="spec/done">**Revisit when:** a package ships in a language whose build output the four-name denylist does not cover and `.vibeignore` alone cannot carry — Python (`__pycache__/`, `.venv/`), JVM (`build/`), Go (`vendor/`) are the near candidates. **The fired state is mechanically observable and already asserted:** a package whose `content_hash` differs between a clean and a built checkout, which is exactly what `##ACC-HASH-EXCLUDES` tests. Observation point: that acceptance check, run over the published package set.</fact></item>
      </list>
    </section>
    <section id="build" title="2.3 Code materialises, then builds consumer-side into a gitignored target">
      <p p="21"><fact id="req-build" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="22">
        <item><fact id="MATERIALISE-AS-TODAY" status="spec/done">**Decision.** `vibe install` materialises the shippable tree — including code —
  into the `vibedeps/` slot, as today.</fact></item>
        <item><fact id="BUILD-CONSUMER-SIDE" status="spec/done">**Original hook-placement rule, retained as history.** Turning source into a runnable tool is consumer-side and build output never enters the source hash. Its stronger “never write inside the committed slot” clause is superseded by `##BUILD-MECHANISM-SUCCESSOR`: provider-root `target/` is legal because the shippable tree and slot record own neither it nor its bytes.</fact></item>
      </list>
      <list ordered="false" p="23">
        <item><fact id="HOOK-BUILD" status="spec/done">The original generic path remains available: a code-bearing tool package may build via a **`post-install` hook**
  ([PROP-020](../modules/vibe-workspace/PROP-020-install-hooks.xml)) whose build
  output is directed to a **gitignored** location (e.g.
  `&lt;project-root&gt;/.vibe/&lt;pkg&gt;-target/`), never the slot. To let a hook address
  that location, the hook runner gains a `VIBE_PROJECT_ROOT` environment
  variable (the workspace absolute root) alongside the existing
  `VIBE_PACKAGE_DIR` (the slot) — a small [PROP-020 §2.2](../modules/vibe-workspace/PROP-020-install-hooks.xml#script-selection)
  addition.</fact></item>
        <item><fact id="NATIVE-CONSUMER-SKIP" status="spec/done">A **language-native consumer** (vibevm itself is a Rust consumer) MAY instead
  reference the shipped crates directly through its own toolchain (§2.4) and
  skip the build hook entirely — the hook is the path for a consumer that wants
  a ready binary without driving the language's build system itself.</fact></item>
        <item><fact id="BUILD-MECHANISM-SUCCESSOR" status="impl/done">**Lifecycle successor.** Declared build targets are selected through the shared mechanism registry and recorded artifact DAG. The builtin `build:cargo` provider runs under the exact package/provider root, consumes `cargo metadata` plus `cargo build --message-format=json-render-diagnostics`, selects only the declared contained compiler artifact from Cargo's messages, and persists provider/config/platform/toolchain/output identity in the shared artifact record. Native extensions and compatibility-lowered `[[binary]]` targets may therefore build into the provider's own gitignored `target/`; that output remains outside the shippable tree and `.vibe-slot.toml` footprint. Package-supplied native Build and Package providers now use separate versioned four-operation wires and safe ABI-1 macros, one all-role prepared carriage, engine-owned staging/records and exact provider-fresh rollback; a real installed package with two ABI-isolated cdylibs displaces both Cargo and static-file defaults, writes the build A2 record and consumes it through Package with no builtin fallback. Evidence: `a22da2a3`, `1baac652`, `332f8e28`, `06f8f465`, then M-007 `00cabac8`, `93df9d28`, `e9388559`, `2193e6e7`, `fe0eb9df`, `0ca2b781`, `123ac504`, `68ce9078`, `55cfbf94`. This lifecycle execution does not redefine dependency materialisation: bare `vibe install` still materialises package content and is not a build/package mechanism invocation.</fact></item>
      </list>
      <p p="24"><fact id="HOOK-RESET-UNAFFECTED" status="impl/done">The record-aware reset successor does not own build output: whether a hook writes outside the slot or Cargo writes the provider-root `target/` inside it, the path is outside `.vibe-slot.toml`. Same-slot refresh preserves it and unchanged source mtimes let the build system remain incremental; a version change selects a different current slot. Hook-created unrecorded state retains PROP-020's explicit idempotence boundary.</fact></p>
    </section>
    <section id="consume" title="2.4 Consuming shipped code — external-path-dep, no nested workspace">
      <p p="25"><fact id="req-consume" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="26">
        <item><fact id="OWN-WORKSPACE" status="spec/done" action="continue" actionstage="doc" audience="author">**Decision.** A code-bearing package carries its **own** workspace manifest
  (for Rust, a root `Cargo.toml` with `[workspace]`) — it is a standalone,
  independently-buildable project.</fact></item>
        <item><fact id="PATH-DEP-LAW" status="spec/done" action="continue" actionstage="doc" audience="user">A language-native consumer that needs a shipped
  crate — a proc-macro that compiles *into* the consumer's own code (the
  `specmark` case), or a binary it invokes (the `conform`/`specmap` case) —
  references it **by path into the materialised slot**:</fact></item>
      </list>
      <fence lang="toml" p="27"># consumer's root Cargo.toml — one pinned alias, updated once per package bump
[workspace.dependencies]
specmark = { path = "vibevm/vibedeps/org.vibevm.ai-native.rust-ai-native-lang/1.0.0/crates/vendor/core-ai-native-specmark" }

[workspace]
exclude = ["vibevm/vibedeps", "vibevm/vibepacks"]   # disclaim the package's own workspaces</fence>
      <list ordered="false" p="28">
        <item><fact id="WORKSPACE-EXCLUDE" status="spec/done" action="continue" actionstage="doc" audience="user">The consumer **excludes** `vibevm/vibedeps/` (and, for a self-hosting repo, the
  in-repo `vibevm/vibepacks/` source) from its `[workspace]`, so the slot's crates
  belong to the *package's* workspace, not the consumer's — Cargo forbids a
  directory living in two workspaces, and this is the standard resolution for a
  repo that contains a sub-project with its own workspace.</fact></item>
        <item><fact id="PIN-ONCE" status="spec/done">The slot path is version-qualified; pinning it once in
  `[workspace.dependencies]` means a package version bump touches a single line.</fact></item>
      </list>
      <p p="29"><fact id="BINARY-RUN-FORM" status="spec/done">A binary tool (`conform`, `specmap`) is run from the package's workspace —
`cargo run --manifest-path vibedeps/&lt;slot&gt;/Cargo.toml --bin conform -- …` with
`CARGO_TARGET_DIR` pointed at a gitignored dir (§2.3) — so building it pollutes
neither the slot nor the consumer's own `target/`.</fact></p>
      <list ordered="false" p="30">
        <item><fact id="SPIKE-FIRST" status="spec/done">**Spike before the irreversible move.** Cross-workspace path-deps and the
  `exclude` topology are validated empirically on the target host (Windows, where
  `canonicalize()` adds a `\\?\` prefix and Cargo path handling has sharp edges)
  *before* any crate is physically relocated.</fact></item>
        <item><fact id="SPIKE-FALLBACK" status="spec/done">The fallback, if cross-workspace path-deps prove unworkable on a host, is §4's
  rejected-but-retained alternative (the consumer adds the slot crates as its
  own workspace members) — chosen only on evidence.</fact></item>
        <item><fact id="own-workspace-why" status="spec/done">**Why:** an external constraint, not a preference — *"Cargo forbids a directory living in two workspaces, and this is the standard resolution for a repo that contains a sub-project with its own workspace"* (`##WORKSPACE-EXCLUDE`). Giving the package its own workspace is what makes it *"a standalone, independently-buildable project"* (`##OWN-WORKSPACE`) and keeps a version bump to one pinned line (`##PIN-ONCE`).</fact></item>
        <item><fact id="own-workspace-rejected" status="spec/done">**Considered and rejected:** **the consumer adding the slot crates as its own workspace members** (no package workspace, no cross-workspace path-dep) — *considered*, and rejected as the **primary** model because it denies the package standalone-buildability and couples the consumer's workspace membership to generated `vibedeps/` state (`##REJ-CONSUMER-MEMBERS`). **Retained, not discarded**: it is the §2.4 fallback, *"chosen only on evidence"* (`##SPIKE-FALLBACK`).</fact></item>
        <item><fact id="own-workspace-revisit" status="spec/done">**Revisit when:** cross-workspace path-deps prove unworkable on a supported host — the condition `##SPIKE-FALLBACK` already names, here given its observation point: a clean-checkout `cargo build` failing to resolve `vibedeps/&lt;slot&gt;/crates/&lt;crate&gt;` on any of the three platforms of PROP-000 §11 `##PLATFORMS-TRIO`, Windows first (`##SPIKE-FIRST`). The fired state has a landing place already specified, so reopening is a switch, not a redesign.</fact></item>
      </list>
    </section>
    <section id="bootstrap" title="2.5 Self-hosting bootstrap — the toolchain is vendored">
      <p p="31"><fact id="req-bootstrap" status="spec/done">`req r1`</fact></p>
      <list ordered="false" p="32">
        <item><fact id="SELF-HOST-VENDORED" status="spec/done">**Decision.** vibevm consumes its own discipline toolchain from the **committed**
  `vibedeps/` slot.</fact></item>
        <item><fact id="CLEAN-CLONE-BUILDS" status="spec/done">Because `vibedeps/` is committed (PROP-009 §2.1), a fresh clone
  builds from a clean checkout **with no prior `vibe install`** — the path-dep
  target (`vibevm/vibedeps/org.vibevm.ai-native.rust-ai-native-lang/1.0.0/crates/vendor/core-ai-native-specmark`, …) already exists
  in the tree.</fact></item>
        <item><fact id="NO-CHICKEN-EGG" status="spec/done">There is no chicken-and-egg: the toolchain a build needs is vendored
  beside the code that needs it.</fact></item>
        <item><fact id="bootstrap-why" status="spec/done">**Why:** committing the slot is what makes a fresh clone build *"from a clean checkout with no prior `vibe install`"* — the path-dep target already exists in the tree (`##CLEAN-CLONE-BUILDS`), so there is no chicken-and-egg between the toolchain a build needs and the build that would fetch it (`##NO-CHICKEN-EGG`). Acceptance already asserts it: `##ACC-CLEAN-CLONE`.</fact></item>
        <item><fact id="bootstrap-rejected" status="spec/done">**Considered and rejected:** **`materialization = "in-place"` for the tool packages** — rejected: `in-place` slots are `.gitignore`d and unversioned (PROP-022 §2.4/§2.7), and the toolchain must be vendored and versioned so a clone is buildable offline (`##REJ-IN-PLACE`). **Publishing the tool crates to crates.io and depending on the published versions** — **deferred, not rejected**: *"the installed package is the distribution"*, and crates.io publication is *"an optional later convenience for non-vibe Rust consumers, not a requirement of this model"* (`##REJ-CRATES-IO`).</fact></item>
        <item><fact id="bootstrap-revisit" status="spec/done">**Revisit when:** the committed slot's cost outgrows its guarantee — `git count-objects -vH` and the slot's on-disk size showing a clean clone expensive enough to outweigh the offline-buildability it buys (numeric threshold unset — owner 2026-08-01; event-shaped until set) — **or** the deferred demand arrives: a Rust consumer outside the vibe ecosystem needs `conform` / `specmap` without installing a vibe package, recorded in the `F-NNN` findings ledger so the signal has a place to be observed (`##REJ-CRATES-IO`).</fact></item>
      </list>
      <p p="33"><fact id="DEV-LOOP-MUTABLE" status="spec/done">The development loop stays ergonomic: editing the in-repo package source under
`vibevm/vibepacks/org.vibevm.ai-native/rust-ai-native/…` re-materialises the slot automatically on
the next `vibe install` (PROP-011 §2.6 — in-workspace `file://` sources are
mutable), so the consumed `vibedeps/` copy tracks the edited source without a
manual `rm -rf`.</fact></p>
    </section>
    <section id="placement" title="2.6 Placement follows the layer model; the engine split is a follow-up">
      <p p="34"><fact id="req-placement" status="spec/done">`req r1`</fact></p>
      <p p="35"><fact id="PLACEMENT-LAW" status="spec/done">**Decision.** The discipline's tools are code and obey the four-layer model:
L4 (implemented checkers) ships in the package whose language they check.</fact></p>
      <list ordered="false" p="36">
        <item><fact id="THIS-PASS-WHOLE-TOOLCHAIN" status="spec/done">For **this pass**, the **entire Rust discipline toolchain** — the conform
  engine (`conform-core`), its Rust frontend (`conform-frontend-rust`), the
  Rust traceability macros (`specmark`, `specmark-grammar`), the traceability
  engine (`specmap-core`), and the designated audit crate (`env-audit`) — ships
  in `stack:org.vibevm.ai-native/rust-ai-native-lang`. Its centre of gravity is Rust, and
  shipping the toolchain whole avoids carving language-neutral cores out under
  time pressure.</fact></item>
        <item><fact id="CORE-STAYS-PROMPT-ONLY" status="impl/done">**The condition fired.** `flow:org.vibevm.ai-native/core-ai-native` was to stay
  **prompt-only** (manifesto, card format, scaffold catalog, RAID, appendix) until a
  second language actually needed the shared engine. The TypeScript pilot shipped, so
  core-ai-native now **authors the neutral engines** (conform / specmap / specmark / mcp
  cores), which each `-lang` and `-mcp` package vendors byte-identically.</fact></item>
        <item><fact id="DEFERRED-ENGINE-SPLIT" status="impl/done">**Deferred (documented):** the language-neutral conform engine (`conform-core`)
  is a genuine L1 artifact — a future `conform-frontend-typescript` would reuse
  it unchanged. Extracting `conform-core` up into `discipline-core` is a clean
  follow-up, taken when the first non-Rust pilot needs it (YAGNI until then).
  Likewise the neutral half of `specmap-core` (markdown parse, index, ledger,
  test-gate) versus its Rust `rscan` frontend. The end state is symmetric; the
  ordering is driven by real second-language demand, not built speculatively.
  **Executed** — the TypeScript pilot was that demand, and the neutral halves now live
  in core-ai-native, vendored into each family by `cargo xtask sync-engines`.</fact></item>
        <item><fact id="placement-why" status="spec/done">**Why:** the toolchain's *"centre of gravity is Rust, and shipping the toolchain whole avoids carving language-neutral cores out under time pressure"* (`##THIS-PASS-WHOLE-TOOLCHAIN`); the layer model then decides placement rather than convenience — L4 ships with the language it checks. Recorded the same session the decision was taken (§7 `##HIST-DRAFT-1`, 2026-06-27).</fact></item>
        <item><fact id="placement-rejected" status="spec/done">**Considered and rejected:** **extracting the language-neutral cores (`conform-core`, the neutral half of `specmap-core`) up into core-ai-native in the same pass** — **deferred, not rejected**, with its condition stated: *"taken when the first non-Rust pilot needs it (YAGNI until then)"* (`##DEFERRED-ENGINE-SPLIT`). **The deferral has since been honoured**: the TypeScript pilot was that demand, the condition fired, and the neutral engines now live in core-ai-native, vendored by `cargo xtask sync-engines` (`##CORE-STAYS-PROMPT-ONLY`).</fact></item>
        <item><fact id="placement-revisit" status="spec/done">**Revisit when:** *(a successor trigger — the previous one fired with the TypeScript pilot and is spent)* a **third** language family arrives and the neutral engines do not cover it — observed as `cargo xtask sync-engines` being unable to vendor a core byte-identically into the new `-lang` package, or a third family needing a core the two existing ones do not share. Observation point: the `sync-engines` task and the set of `*-ai-native-lang` / `*-ai-native-mcp` packages.</fact></item>
      </list>
    </section>
  </section>
  <section id="surface" title="3. Manifest / schema surface">
    <list ordered="false" p="37">
      <item><fact id="MANIFEST-ROLES-ARE-EQUIPOTENT" status="impl/done">**`[project]` and `[package]` are
  equipotent consumer nodes (owner law, 2026-08-23, near-verbatim: «и
  project, и package могут быть потребительской единицей — в репозитории,
  где разрабатывается пакет, корневой vibe.toml имеет тип package, и всё
  там обязано работать точно так же; сам маркер — косметическое свойство
  для человека или UI; разошлись по возможностям — дефект, один и тот же
  код обслуживает обе роли»).** One grammar, one file, the role declared
  by its XOR section — and every consumer operation (install, lockfile,
  boot-lane generation, `installed:` resolution, `vibe facts`,
  `spec_format`, tree/show) reads the role-blind
  `Manifest::consumer_node()` view and never matches on the role.
  `[package]` gains `spec_format` so a package-rooted dev checkout pins
  its materialisation like a project. Role-SPECIFIC surfaces stay lawful
  where the role is the subject (publish ships packages; a package's
  `[requires]` is a consumer contract, a project's is its own install
  set). Landed with twin tests (spec_format resolution and the facts
  host coordinate answered identically from either role) after the
  divergence audit: bootgen's self coordinate, the facts host reader,
  spec_format resolution, the tree model and the workspace-origin name
  all went through the one view.</fact></item>
      <item><fact id="SURF-NO-NEW-FIELD" status="spec/done">**No new required manifest field.** `[boot_snippet].source` becomes
  `spec/`-relative (a value change, not a schema change). `[package].materialization`
  stays `snapshot` for a vendored code-bearing package.</fact></item>
      <item><fact id="SURF-VIBEIGNORE" status="spec/done" action="continue" actionstage="doc" audience="author">**Optional `.vibeignore`** at the package root — newline-delimited globs added
  to the §2.2 build-output denylist.</fact></item>
      <item><fact id="SURF-HOOKS" status="spec/done">**`[hooks].post-install`** (PROP-020) is the build lever; **`VIBE_PROJECT_ROOT`**
  is added to the hook environment (§2.3).</fact></item>
      <item><fact id="SURF-LOCK-UNAFFECTED" status="impl/done">**Original code-bearing-package law:** package identity remains `content_hash`
  over the shippable tree (§2.2). Reference-backed bridges extend the lock with
  separately nested embedded-source pins; those pins authenticate projected
  upstream bytes but do not become part of package identity.</fact></item>
    </list>
  </section>
  <section id="rejected" title="4. Rejected / deferred alternatives">
    <list ordered="false" p="38">
      <item><fact id="REJ-HASH-BUILD-OUTPUT" status="spec/done">**Hash and copy build output too** — rejected: non-deterministic and
  potentially gigabytes; it is the file-count/byte-count failure PROP-022 §1.1
  exists to avoid. Identity is source (§2.2).</fact></item>
      <item><fact id="REJ-CONSUMER-MEMBERS" status="spec/done">**Consumer adds the slot crates as its own workspace members** (no package
  workspace, no cross-workspace path-dep) — *considered*; rejected as the
  primary model because it denies the package standalone-buildability (a package
  would not be a project) and couples the consumer's workspace membership to
  generated `vibedeps/` state. **Retained as the §2.4 fallback** if
  cross-workspace path-deps prove unworkable on a host — a decision made on spike
  evidence, not by default.</fact></item>
      <item><fact id="REJ-IN-PLACE" status="spec/done">**`materialization = "in-place"` for tool packages** — rejected: `in-place`
  slots are `.gitignore`d and unversioned (PROP-022 §2.4/§2.7); the discipline
  toolchain must be **vendored and versioned** so a clone is buildable offline
  (§2.5). Snapshot-minus-build-output is the right mode.</fact></item>
      <item><fact id="REJ-CRATES-IO" status="spec/done">**Publish the tool crates to crates.io and depend on the published versions** —
  deferred: the installed package *is* the distribution, so the consumer depends
  on the slot, not a registry crate. crates.io publication is an optional later
  convenience for non-vibe Rust consumers, not a requirement of this model.</fact></item>
      <item><fact id="REJ-ALLOW-LIST" status="spec/done">**A per-file `[ship]`/`[files]` allow-list in the manifest** — rejected: it
  resurrects the per-file write list PROP-009 §2.6 retired. A denylist of build
  output (§2.2) keeps "what ships" == "the source", preserving the verbatim
  guarantee.</fact></item>
    </list>
  </section>
  <section id="out-of-scope" title="5. Out of scope">
    <list ordered="false" p="39">
      <item><fact id="OOS-AUTODETECT" status="spec/done">**Auto-detecting the build system / language** of a code-bearing package —
  the package declares a hook or explicit artifact/mechanism target; vibevm
  does not infer `cargo` vs `npm`. The commissioned builtin is selected only by
  the declared `build:cargo` target.</fact></item>
      <item><fact id="OOS-SANDBOX" status="spec/done">**Sandboxing the build hook** — inherits PROP-020 §4's posture (hooks run with
  the user's privileges; package installation is the consent and execution is
  made observable through exact provider/target evidence).</fact></item>
      <item><fact id="OOS-TS-CHECKER" status="impl/done">**Out of scope when written; since delivered.** TypeScript shipped no
  implemented tool to relocate, so its cards kept `specified` checker statuses until a
  TS pilot existed (§2.6 deferral). The `typescript-ai-native` family shipped with its
  floor, conform and specmap engines — the deferral is closed.</fact></item>
    </list>
  </section>
  <section id="acceptance" title="6. Acceptance">
    <list ordered="false" p="40">
      <item><fact id="ACC-SHAPE" status="spec/done">A package may carry code at its root and prompt content under `spec/`;
  `vibe check` passes on it as a project (`vibevm/vibespecs/boot/` markdown-only).</fact></item>
      <item><fact id="ACC-HASH-EXCLUDES" status="spec/done">`content_hash`, the snapshot copy, and the materialised slot exclude `.git/`,
  `.vibe/`, `target/`, `node_modules/`, and `.vibeignore` globs; identical
  source produces an identical hash regardless of build state.</fact></item>
      <item><fact id="ACC-BOOT-RESOLVES" status="spec/done">`[boot_snippet].source` resolves `spec/`-relative; the generated `INDEX.md`
  names `vibedeps/&lt;slot&gt;/spec/boot/&lt;file&gt;`.</fact></item>
      <item><fact id="ACC-CLEAN-CLONE" status="spec/done">vibevm builds from a clean checkout, consuming its discipline toolchain (incl.
  the `specmark` proc-macro the product crates compile against) from the
  committed `vibedeps/` slot, with no prior `vibe install`.</fact></item>
      <item><fact id="ACC-EXTERNAL-CONSUMER" status="spec/done">An external Rust project can install `stack:org.vibevm.ai-native/rust-ai-native-lang` and run
  `conform` / `specmap` against its own code.</fact></item>
      <item><fact id="ACC-FLOOR-GREEN" status="spec/done">Full `self-check.sh` green; conform 0/0/0; specmap clean.</fact></item>
    </list>
  </section>
  <section id="history" title="7. Version history">
    <list ordered="false" p="41">
      <item><fact id="HIST-DRAFT-1" status="spec/done">**2026-06-27 — draft 1.** Owner-directed: make the discipline self-sufficient
  by letting packages ship runnable code (`spec/` for prompts, the root for
  code), then relocate the Rust toolchain (conform + specmap/specmark) out of the
  vibevm workspace and into `stack:org.vibevm.ai-native/rust-ai-native-lang`. The frozen
  `VIBEVM-SPEC.md` sanction was granted the same session (§0). Decisions taken in
  the owner session: the prompt directory is `spec/` (singular, project-identical,
  not `specs/`); the full traceability stack moves alongside conform; conform is
  productised to run on an arbitrary external project (config-driven, not
  vibevm-hardcoded); `conform-core` ships in the Rust stack now with the L1
  engine-extraction deferred (§2.6).</fact></item>
    </list>
  </section>
</spec>
