--- id: PX-PLAN-_BUERPCN type: plan title: Release Build and Verified Publication spec: PX-SPEC-DZLVGRW4 status: draft depends_on: - PX-PLAN-RMJ8BIOX --- ## Outcome A release build produces the same package boundary as the development build, is verified as immutable bytes, and only those bytes are published to npm and Git for explicitly selected extensions. Consumers install without source or toolchain. Verified current state: `//:publish` is Git-only; release commits README, CHANGELOG, `package.json`, lockfile; version lives in root `package.json`; extension packages are `private: true` without versions. ## Behaviors ```mermaid flowchart TB r1["1. Release build of ultra behaves identically to dev build"] --> r2["2. Snapshot digest gates publication"] r2 --> r3["3. Selected extensions publish to npm"] r2 --> r4["4. Git distribution branch carries built packages"] r3 --> r5["5. Consumer install smoke without checkout"] r4 --> r5 ``` 1. `build-ext --release` emits into the same `dist/` with minification and no sourcemaps; chunk boundaries, externals, manifest, and resources are asserted equal to a dev build by a structural diff test. Reload of a release package after a dev package shows no behavior change in the contract suite. 2. `//:release-snapshot` copies `dist/` into an immutable snapshot directory keyed by content digest, runs `verify-packages` on the snapshot, and records digests in a release manifest file. `//:publish` refuses when the current `dist` digest differs from the manifest. 3. Publication eligibility is a per-extension declaration (`publishConfig` or `pi-ext.publish: true` in the source `package.json`); private stays default. npm publish packs the snapshot directory; generated `package.json` gets the root version, the public name, and no scripts. 4. Git channel: a `release/` tag or `dist` branch containing only built packages so `pi install git:…/dist/` works. 5. Consumer smoke: temp directory, independently installed Pi, `pi install` from the npm tarball and from Git, load, run one tool per extension. ## Module structure ```mermaid flowchart LR build["scripts/build driver --release"] --> dist["dist/name"] dist --> snap["scripts/release/snapshot: digest + manifest"] snap --> gate{"digest match?"} gate -->|yes| npm["npm publish snapshot"] gate -->|yes| git["Git dist tag"] gate -->|no| reject["reject"] select["per-extension publish declaration"] --> snap ``` Touched: `scripts/build/*` (release mode), `scripts/release/*`, `scripts/extensions.mjs` (`publish`, `release-prepare`), `mise.toml` (`release-snapshot`, `publish`), `extensions//package.json` (publish declaration, public name), `.ci/*.kdl` later plan. ## Constraints - Root `package.json` version remains the single version source; generated manifests copy it at build time. - Compatibility metadata (`peerDependencies` on Pi) is authored once in the extension source manifest. - Release verification uses the snapshot, never a rebuilt `dist`. ## Verification - [ ] Structural diff between dev and release builds shows only sourcemap and minification differences. - [ ] Publish with a modified `dist` after snapshot is rejected with a digest mismatch. - [ ] npm pack contents equal the snapshot byte-for-byte. - [ ] Only extensions declared publishable appear in the release plan. - [ ] npm and Git consumer smoke passes in a temp directory without repository source.