<!-- docs/ADR/2026-07-4.0-packaging.md -->

# ADR: Unified BLE 4.0 packaging, exports, and artifacts

**Status:** Accepted design baseline
**Date:** 2026-07-25
**Supersedes:** the deleted host-specific package and optional-native fallback records

## Decision

4.0 publishes one ESM-first package with strict, tested subpath exports. The
root is inert and host-neutral; it does not import or evaluate a mobile
framework, browser global, Electron, Node add-on, D-Bus, WinRT, CoreBluetooth,
or a backend on import. Host installation and construction occur only through
explicit subpaths:

```text
unified-ble-manager
unified-ble-manager/react-native
unified-ble-manager/web
unified-ble-manager/node/bluez
unified-ble-manager/node/corebluetooth
unified-ble-manager/node/winrt
unified-ble-manager/electron/main
unified-ble-manager/electron/renderer
unified-ble-manager/backend-sdk
unified-ble-manager/testing
unified-ble-manager/profiles/*
unified-ble-manager/codecs
```

The Phase 0 temporary aliases and declaration fixture were deleted at G1
promotion. The authoritative production root and backend shapes are
[`src/index.ts`](../../src/index.ts) and
[`backend.ts`](../../src/backend-contract/backend.ts). Their public ownership,
generation, cancellation, stream, and dotted-error rules remain normative
through `UNIFIED_SEMANTICS.md`.

## Export and dependency invariants

- The root exposes the public API decided by the public ADR. Each host subpath
  exposes only its factory and host initialization types; backend authoring and
  deterministic controls live on their own subpaths.
- No deep import outside the export map is supported. Each export has matching
  runtime, declaration, resolver, pack/install, and host-isolation tests.
- Host dependencies are optional peers or lazy dependencies only on their
  relevant subpath. Installing/importing the root, browser, Node, Electron, or
  mobile surface never resolves an unrelated host implementation.
- The backend contract, capability schema, events, errors, paths, and version
  negotiation are shared source authority, not copied package-local variants.
  A package version is not a protocol compatibility answer; all applicable
  protocol axes negotiate before radio work and reject mismatch without downgrade.

## Native and generated artifact rules

Native artifacts are package-controlled, ABI-bound, provenance-recorded outputs.
The package manifest and build gates enumerate allowed native binaries, source
derivation, ABI/runtime metadata, hashes, and target entrypoints. Add-ons load
only from expected package-controlled locations, validate their complete
surface/version/ABI, and fail closed on absence or mismatch. An add-on compile
or binary load is not a runtime capability or live-radio claim.

The React Native data boundary is one owned C++ JSI transport. Codegen sources
may contain supported control/bootstrap records only; they must not become a
parallel byte route. The draft, experiments, benchmarks, examples, labs,
fixtures, secret/local paths, unapproved archives, legacy code, and unintended
native binaries are excluded from publication. Normal bytes never use Base64.

Electron main and Node may use separately built ABI artifacts from one native
source authority, but cannot fork GATT, ownership, event, error, capability, or
TCK semantics. Renderer packages expose only the constrained IPC client.

## Ownership, security, and diagnostics

Packaging cannot create a manager, select an adapter, elevate a permission, or
enable background behavior. It preserves the public/backend ownership,
generation, cancellation, stream, and cleanup semantics. It does not ship a
mock/Noble/static-matrix fallback that reports radio success.

Artifact creation and verification defend against path traversal, symlinks,
unexpected binaries, poisoned environment, lockfile drift, local paths, and
untrusted native loading. Packed artifacts, diagnostics, source-state records,
and evidence redact identifiers, payloads, secrets, and local paths by default.
Trusted publishing uses minimal permissions, provenance, and post-pack
installation/entrypoint verification.

## Rejected alternatives

- Separate public packages for each host before an ADR proves a single package
  cannot meet install/build constraints: rejected because it creates divergent
  contract and release authority.
- Broad root exports, deep imports, eager host imports, or bundled all-host
  dependencies: rejected because they break framework neutrality and consumers.
- Shipping a development mock, legacy backend, or fallback binary as a nominal
  host implementation: rejected because no package shape may fabricate support.

## Consequences and gates

Build and release gates require zero diagnostics; nonzero expected source and
artifact counts; export-map/type/runtime parity; tarball inventory; clean
independent installs for each intended host; resolver tests; artifact digest and
ABI checks; and absence checks for draft/legacy/local/secret material. The
declaration fixture compiles under Bundler, Node16, and NodeNext but never
appears in production output.

The old package assumptions, root overrides, legacy fallbacks, and accidental
deep-import paths are deleted only after the replacement export, host, package,
and consumer gates pass. No compatibility artifact remains in the stable tarball.

## Related decisions

- [Public API ADR](2026-07-4.0-public-api.md)
- [Boundary ADR](2026-07-4.0-boundary.md)
- [Open-source governance ADR](2026-07-4.0-open-source-governance.md)
