Agentic AI Atlasby a5c.ai
OverviewWikiGraphFor AgentsEdgesSearchWorkspace
/
GitHubDocsDiscord
iiRecord
Agentic AI Atlas · Spec 40 — Packaging (WP-PKG-*)
page:docs-specs-platform-gaps-40-packaginga5c.ai
Search record views/
Record · tabs

Available views

II.Record viewspp. 1 - 1
overviewarticlejsongraph
III.Related pagespp. 1 - 1
II.
Page reference

page:docs-specs-platform-gaps-40-packaging

Reading · 47 min

Spec 40 — Packaging (WP-PKG-*) reference

Date: 2026-09-27 · Status: Gate-B draft · Binding decisions: D3 (all three packaging forms, spec proposes priorities — docs/specs/platform-gaps/00-intent.md:23), executed PRIVATE-first under D17/D19/D21 (00-intent.md:42-46), constrained by D10 (break freely, major bump), D11 (packaging follows migration + coverage in the roadmap), D13 (mobile deferred), D15 (attribution).

Pagewiki/docs/specs/platform-gaps/40-packaging.mdOutgoing · 0Incoming · 1

Spec 40 — Packaging (WP-PKG-*)

Date: 2026-09-27 · Status: Gate-B draft · Binding decisions: **D3** (all three packaging forms, spec proposes priorities — docs/specs/platform-gaps/00-intent.md:23), executed **PRIVATE-first under D17/D19/D21** (00-intent.md:42-46), constrained by D10 (break freely, major bump), D11 (packaging follows migration + coverage in the roadmap), D13 (mobile deferred), D15 (attribution).

Inputs consumed in full: R3 research/packaging-state.md (the packaging dossier; cited R3 §n with underlying path:line), Gate-A synthesis research/synthesis-adoption-proposal.md (§1.5 packaging-form priority, §4.4 planned 13-WP inventory), spec 15 15-private-isolation-rename.md (its §2 scope, §3 name matrix, §4 registry/guards, and §5 interaction rules **govern all distribution work in this spec**), spec 10 10-t3code-migration.md (cross-refs only: unit row 12 at 10-t3code-migration.md:46 hands install/service/self-update landing to this spec; WP-T3-19/26/28/31/32/33 are consumed as dependencies; WP-T3-31's relay ops story is productized by WP-PKG-14), spec 20 20-functionality-coverage.md (cross-refs only: WP-FN-02 AC4 / WP-FN-03 AC2 hand default key provisioning to this spec's WP-PKG-04/05; WP-FN-16 FR-7 hands relay rollout to WP-PKG-14).

Path conventions: fabric paths relative to C:/work/fabric; the t3code MIT snapshot is cited t3:<path> (snapshot location per 00-intent.md:51). Spec 30 (30-testing.md) is not yet authored; its work packages are referenced as WP-TEST-<nn> under the fixed mapping Q-nn → WP-TEST-nn from the Gate-A inventory (research/synthesis-adoption-proposal.md §4.3) — e.g. WP-TEST-04 is Q-04, the whole-platform packaged-install e2e.

**Naming discipline**: this spec introduces **no** package, binary, env prefix, directory, or chart name outside the spec 15 §3 locked matrix or its stated derivation rules. Two derived-name additions are **proposed for the Gate-B name lock** (§3.6 launcher alias, §2.4 npm platform packages) and are explicitly flagged as matrix amendments, not minted here.

---

1. Scope, private-first doctrine, and priority order

1.1 The three forms (D3) and what exists today

FormD3 wordingFabric todayGap class
**Form 1 — single-node installer UX**curl\sh / winget / npx, service, self-updatenpm-global only, Node-gated (docs/user-guide/getting-started/installation.md:126-131); **no** installer scripts, binary archives, service, self-update, or uninstaller anywhere in fabric (R3 §1.5, negative search recorded at research/packaging-state.md:150-161)
**Form 2 — cluster/Helm bundle**versioned platform chart, GKE pathChart exists, production-shaped, but frozen at version: 0.1.0 vs product 6.0.x (packages/kradle/charts/Chart.yaml:1-7 vs package.json:3), never published (only inert helm push in the non-executing nested workflow packages/kradle/core/.github/workflows/publish.yml:94, R3 inferred finding 1), consumed from source path by CI (.github/workflows/publish.yml:952,1080); the platform deploys via **three mechanisms** (chart + atlas Kustomize overlay publish.yml:740-742 + Envoy Kustomize Chart.yaml:31-34)Productization — versioning, publication, bundling
**Form 3 — npm SDK surface**stable publics, semver, docsStrong publish integrity (single resolver, waves, packed-artifact verifier — R3 §1.1) but **no declared publics**: @a5c-ai/babysitter-sdk has zero exports entries and five bins (packages/babysitter-sdk/package.json:7-14), @a5c-ai/adapters exports only "." (R3 §1.6); lockstep 6.0.x with no per-package semver signal; changesets configured but dead (.changeset/config.json:1-20, no workflow references — R3 §1.1)Boundary definition + versioning posture

1.2 Private-first doctrine (D17/D19 — binding on every WP below)

The Gate-A synthesis pre-dates amendment 2026-09-27b; this spec applies the amendment throughout:

1. **Every distribution channel in this spec is private.** npm packages publish only to the GCP Artifact Registry npm repo fabric-npm (https://us-central1-npm.pkg.dev/boot-464019/fabric-npm/, spec 15 §4.1). Renamed @fabricstack/* packages are **never** published to public npmjs (D19; guards G1–G6, spec 15 §4.3). 2. **Non-npm artifacts get private homes too** (§1.3): binary archives, desktop installers, and Helm charts publish to Artifact Registry repositories in the same GCP project boot-464019 — not to public GitHub Releases, not to ghcr.io, not to winget/Homebrew/AUR (this drops the public-channel half of the planned P-08; justification in §1.5). 3. **CI/publish isolation from the public babysitter pipelines**: the public a5c-ai/babysitter repo keeps publishing @a5c-ai/* to public npmjs untouched (D17); fabric's packaging pipelines are the private train of spec 15 §4.2 (publish-npm-private.yml, tags fabric/<branch>/v<version>), and no workflow this spec adds may reference the public repo as a push target (WP-RN-02 guard) or registry.npmjs.org/ghcr.io in a publish context (WP-PKG-13 extends G2 to non-npm artifacts). 4. **All packaging operates on the renamed matrix**: package names, bins, env prefixes (FABRIC_* for platform-wide installer variables, per spec 15 §2/§3.4), data dirs (~/.fabric/, spec 15 §3.5), and chart names (control-plane, spec 15 §3.6) come from spec 15 §3.

1.3 Artifact-home decision table (the split-repo constraint, R3 spec input 9)

R3 spec input 9 requires every new artifact channel to "pick a home repo explicitly and keep the single-resolver version discipline". Spec 15 §4.2 already settled npm bytes ("private npm artifacts live in AR under boot-464019; spec 40 inherits that decision"). This spec decides the rest:

Artifact classHome (decided)Why
npm packages (43 renamed + §2.4 platform packages)AR npm repo fabric-npm (spec 15 §4.1)inherited from spec 15 §4.2
Self-contained per-platform archives + SHA256SUMS (Form 1)**New AR generic-format repo fabric-dist**, project boot-464019, region us-central1Same project/region/WIF auth as the existing Docker AR (publish.yml:3-6,880-893, R3 §1.3); AR generic repos serve versioned binary blobs over authenticated HTTPS, which is exactly what a private installer needs. Alternatives rejected: *private GitHub Releases on a5c-ai/fabric* — asset download on private repos requires GitHub API tokens with repo scope, coupling customer machines to GitHub org membership; *GCS bucket* — loses AR's IAM/version/immutability semantics and would be the only artifact class outside AR; *public GitHub Releases* — violates D19's private-distribution posture outright
Desktop installers + update manifests (console-desktop, WP-T3-26)Same fabric-dist repo, desktop/ artifact path familyOne channel, one auth story; t3code's merged mac/win update manifests (t3:scripts/merge-update-manifests.ts:19-24, R3 §2.3) point at the same base URL the archives use
Helm chart control-plane**AR OCI**: oci://us-central1-docker.pkg.dev/boot-464019/fabric-charts (new AR Docker-format repo fabric-charts; Helm OCI charts are OCI artifacts)The precedent in-tree is the inert ghcr push (packages/kradle/core/.github/workflows/publish.yml:94, R3 §1.2) — **rejected**: ghcr is public-facing and belongs to the public repo's org surface. AR OCI reuses WIF auth and keeps the chart beside the container images the chart references
Container imagesExisting AR Docker repo at us-central1-docker.pkg.dev (publish.yml:880-893)Already private and working; WP-PKG-09 adds platform-version tags beside the commit-sha tags (publish.yml:812-816)
Relay deployables (Cloudflare Worker bundle + Postgres migrations + cloudflared config, WP-T3-31)**Deployed, not distributed**: the fabric-controlled Cloudflare account (Workers) and its managed Postgres — no customer-facing artifact exists for this classThe relay is a hosted service fabric operates under the broker-not-proxy trust model (WP-FN-16 FR-2); its "home" is the deployment target itself. Sources live in infra/relay (10-t3code-migration.md:476); deploy provenance is the train tag; pipeline, promotion, and guard coverage are WP-PKG-14. Publishing the Worker to any public marketplace/registry is excluded (PG1 extension, WP-PKG-14 AC5)

All four artifact-registry homes are inside boot-464019, authenticated by the existing WIF step (google-github-actions/auth@v2, R3 §1.3) in CI and by gcloud ADC or short-lived tokens for humans/installers. The relay's deployment target is the one deliberate non-AR home — a deploy destination, not a distribution channel — credentialed separately under the PG5 pattern (WP-PKG-14 AC6).

1.4 Node floor encoding (binding; synthesis risk 3, R3 spec input 5)

FloorValueWhere encoded
Runtime floor, npm-installed surfaces>=22.16 workspace engines (WP-T3-32 stage 1; the strictest already-landed floor), which subsumes the node:sqlite floor >=22.5 (installation.md:199-202; R2 §15 via 10-t3code-migration.md:483)root package.json engines + per-package engines; CI engines check (WP-T3-32 AC5)
Runtime floor, Form 1 archives**None for the consumer** — the SEA executables embed their runtime; "nothing in it may require Node, npm, or a compiler on the machine that unpacks it" (t3:scripts/build-cli-archive.ts:1-16, adopted as the archive contract)WP-PKG-03 AC1
SEA **build hosts**>=25.7 (R3 spec input 5; synthesis risk 3)release-pipeline build-job Node matrix, WP-PKG-03 AC6

1.5 Priority order (D3 "spec proposes priorities") and planned-inventory delta

**Decision proposed: Form 3 (npm SDK publics) → Form 1 (single-node installer/service/self-update) → Form 2 (chart productization), with the private-registry substrate (WP-RN-03/04/07) preceding all three.**

Rationale (inheriting synthesis §1.5, restated under the amendment):

  • **Form 3 first** because its blocker — no exports maps, unbounded publics (R3 §1.6) — is a *prerequisite* for D6's ACP re-platforming (new lane packages need defined publics to land against) and because spec 15 §5 rule 6 binds it to the RN-1 train: "P-01's exports-map program and the D10 major bump land in the same train as RN-1 so publics are defined once, under the new names". Doing it later means defining publics twice.
  • **Form 1 second**: it starts from zero (R3 §1.5) but has a complete, tested t3code blueprint (R3 §2) and its central deliverable — the environment server — only exists after WP-T3-19; the adopted desktop app (WP-T3-26) amplifies it. Under D17 the single-node private product is the commercial surface, which ranks it above Form 2.
  • **Form 2 last**: the chart + GKE pipeline already work internally (R3 §1.2–1.3); what remains is productization (versioning, OCI publication, bundling), and spec 15 already schedules the chart's rename for phase RN-5 — coordinating WP-PKG-09 with WP-RN-12 "so re-versioning and renaming land as one chart-consumer break, not two" (spec 15 WP-RN-12 dependency note) naturally places it last.
  • Consistent with D11: all of this follows the t3code migration and coverage work in the roadmap; nothing here blocks WP-T3-* except where spec 10 itself declared the dependency (WP-T3-28's bootstrap-download activation gates on WP-PKG-03 — 10-t3code-migration.md:451-453).

**Delta against the planned 13-WP inventory** (synthesis §4.4; count becomes **14** — three scope adjustments plus one addition):

1. P-01's bin-collision half moved to spec 15 (WP-RN-06 + guard G5 own it); WP-PKG-01 keeps exports maps/publics and *consumes* the collision-free bin set. 2. P-08's public OS package managers (winget/brew/AUR/deb) are **dropped**: they are public distribution channels and contradict D19's private posture (the synthesis §4.4 row pre-dates amendment 2026-09-27b). WP-PKG-08 keeps the uninstall story and records the no-public-channel decision. If a future decision opens a public channel for the private product, that is a new intent amendment, not a packaging WP. 3. P-13 narrows to non-npm artifact channels + guard extension, because spec 15 §4.2 already decided the npm artifact home. 4. **WP-PKG-14 added** — the hosted relay's production rollout had no owning WP anywhere in the program: WP-T3-31 builds the relay and documents its ops story "(Worker + Postgres + cloudflared) … for spec 40/50 consumption" (10-t3code-migration.md:476), and WP-FN-16 FR-7 requires that "its packaging/rollout is carried by specs 40/50" (20-functionality-coverage.md:111) — but the planned inventory carried no deploy-pipeline package for it. WP-PKG-14 (§7) now owns the relay artifact class (§1.3 last row) and its deploy/promotion/rollback pipeline, closing the hand-off both sibling specs already point at.

1.6 Scope deltas recorded for the Gate-B register (WP-RM-03)

Where this spec narrows the surface of a binding decision, the narrowing is recorded here as a labeled scope delta so the Gate-B compliance matrix (WP-RM-03, spec 50) can carry it by ID — the register cites 40-packaging.md §1.6 SD-PKG-<n> and needs no re-derivation or sibling-spec edit:

  • **Scope delta SD-PKG-1 — D3 narrowing: on Windows, the environment server runs foreground or desktop-app-managed only in v1; no Windows background service ships.** D3's Form 1 names "service" without OS qualification; this spec ships systemd-user (with lingering) and launchd services (§3.4) but adopts the reference implementation's explicit exclusion of Windows services (t3:docs/user/background-service.md:90-94). *Rationale*: there is no t3code blueprint to fork for Windows service hosting of a user-context server (service wrapper, session-0 isolation, credential plumbing would all be greenfield), and the desktop app's DesktopBackendManager (WP-T3-26, 10-t3code-migration.md:434-440) already provides managed background operation on Windows — so v1 spends the effort where a tested reference exists. *Enforced surface*: environment-server service install on Windows exits with the typed unsupported-platform error naming both alternatives (WP-PKG-05 AC5); the stance is recorded in docs/PACKAGING-DECISIONS.md (WP-PKG-08). *Revisit condition*: a customer requirement for headless unattended Windows hosts triggers an intent-level amendment, which would mint a new work package (Windows service wrapper) rather than stretch WP-PKG-05.

---

2. Form 3 — npm SDK surface (priority 1)

2.1 Exports-map program (publics defined once, under the new names)

  • Every one of the 43 publishables (inventory always derived from scripts/lib/publishable-packages.cjs:122-161, never hand-maintained — FIX-005 discipline, R3 spec input 2) gains an exports map that enumerates its public entry points; everything else under dist/ becomes unreachable. Today the SDK has **zero** exports entries and every dist/ module is deep-importable API (packages/babysitter-sdk/package.json:7-14, R3 §1.6) — D6's re-platforming would otherwise land on an unbounded surface (R3 spec input 8).
  • Subpath policy: packages expose "." plus a small named-subpath set (e.g. the orchestrator SDK's storage/journal and breakpoint surfaces that other WPs cite as seams); no "./*" wildcard exports — a wildcard reproduces the unbounded surface with extra steps.
  • The packed-artifact verifier already checks "root and subpath imports, consumer typecheck and bin smoke tests" per package (.github/workflows/release-artifact-verifier.yml:5-9, R3 §1.6); WP-PKG-01 turns it from permissive to **exact**: the verifier asserts that the set of importable subpaths equals the exports map, and that deep imports of non-exported paths fail.
  • Timing is bound by spec 15 §5 rule 6: exports maps + the D10 major bump ride the RN-1 train (WP-RN-05/06/07), so the first @fabricstack/* publication already carries defined publics.

2.2 Semver posture: lockstep train kept, changesets removed (decision)

**Decision proposed: keep lockstep versioning across the 43-package train; remove the dead changesets machinery; encode breaking-ness at train level, not per package.**

  • The lockstep 6.0.x scheme with exact-pinned internal deps and verify-manifests (scripts/release-version.cjs:24-27; R3 §1.6) is incident-hardened and matches the consumer reality of a **private** product: consumers are the platform itself, the Form 1 archives, and enterprise installs that consume whole trains via dist-tags — not a public ecosystem cherry-picking package versions. Per-package semver would decorate 43 manifests with signals nobody resolves against.
  • Changesets (.changeset/config.json:1-20 + one stranded changeset file) are referenced by **no workflow** (R3 §1.1) — R3 spec input 3 requires wire-or-remove; under lockstep-kept, **remove** is the coherent half: delete .changeset/ and the pending markdown, so the repo carries one versioning mechanism (scripts/release-version.cjs, the single resolver that is never bypassed — R3 spec input 3).
  • D10 is expressed at train level: the RN-1/exports cutover publishes as the **7.0.0** train (major bump), and subsequent breaking trains bump the train major. A one-page docs/VERSIONING.md records the posture: "semver encodes the release train; within-train packages are exact-pinned lockstep; majors mark breaking trains" — plus the alternative considered (per-package semver via changesets) and why it was rejected.
  • HYG2 closure rides here: wildcard workspace deps (e.g. "@a5c-ai/atlas": "*" at packages/atlas/webui/package.json:16, spec 15 §3.2) are eliminated; verify-manifests (already extended by G2(d), spec 15 §4.3) additionally fails on any */^-ranged internal dependency.

2.3 Private publication and isolation (inherited, verified here)

Publication mechanics are owned by WP-RN-07 (resolver + tags + waves + verifier retargeted to AR, dist-tags main→latest / staging→staging / develop→develop carried over — spec 15 §4.1/§4.2) and guarded by WP-RN-04's G1–G6 (spec 15 §4.3). Form 3's residual obligations, owned by WP-PKG-01/02:

  • Publics documentation: the docs-site SDK reference is regenerated from the exports maps (docs QA gates exist, package.json:116-123, R3 §1.6); the private-install page documents .npmrc scope routing + google-artifactregistry-auth per spec 15 §4.1.
  • No packaging WP creates a public-npm channel for @fabricstack/* (spec 15 §7 obligation on this spec — restated as binding).

2.4 npm platform packages + launcher (`npx` zero-install, private)

Adopt t3code's pattern: per-platform npm packages holding the release archive's contents **verbatim**, plus a thin launcher listing them as optionalDependencies and exec-ing the one npm installed; publish from tarballs so npm cannot strip the flattened node_modules (t3:scripts/build-npm-platform-packages.ts:2-21,16-21,184, R3 §2.5). npm bytes therefore equal fabric-dist archive bytes.

  • Launcher: @fabricstack/environment (the §3.3 spec 15 unit for apps/environment, bin environment-server) doubles as the launcher package.
  • Platform packages: **proposed Gate-B matrix amendment** — @fabricstack/environment-<platformKey> for platformKey ∈ {darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64, win32-arm64} (the t3code six-target matrix, t3:.github/workflows/release.yml desktop/cli matrix — R3 §2.4), derived by the rule <unit>-<platformKey>. Flagged for the spec 15 §3 name lock; no WP lands them before that amendment is approved.
  • npx @fabricstack/environment against a private registry requires the consumer's .npmrc scope routing + AR auth; the documented trial flow includes that step (unlike t3code's anonymous npx t3@latest, t3:docs/user/install.md:38-39 — the delta is the price of D19 and is documented, not hidden).

---

3. Form 1 — single-node installer UX (priority 2)

The t3code machinery is the reference implementation (D3; R3 §2 and spec input 6); spec 10 row 12 hands its landing to this spec (10-t3code-migration.md:46), having itself landed only cloud/selfUpdate.ts inside the environment-server tree (WP-T3-19). Everything below is adapted hard-fork material (fork posture per synthesis §2.2 row "install/service/self-update machinery": hard fork, MIT attribution via WP-PKG-12).

3.1 What Form 1 installs

The single-node unit is: **the SEA environment-server executable** (apps/environment, WP-T3-19 — it serves contracts RPC and the local console-web client, per WP-T3-25's "server-served local client" deployment form, 10-t3code-migration.md:427), **the SEA orchestrator CLI** (metapackage bin, spec 15 §3.1 row 1), **console-web static assets**, and the native prebuilds the server needs (node-pty; optional resource-monitor sidecar — 10-t3code-migration.md:380). Mobile shells are out (D13). The desktop app (@fabricstack/console-desktop, WP-T3-26) is a sibling distribution riding the same pipeline and fabric-dist channel (§3.7), not part of the headless archive.

3.2 Archives (adapting `build-cli-archive.ts`)

  • One self-contained archive per platform/arch, named environment-<version>-<platform>-<arch>.tar.gz (zip on Windows), containing the two SEA executables, console-web assets, and production-installed runtime-external native packages — the "no Node, npm, or compiler on the unpacking machine" contract (t3:scripts/build-cli-archive.ts:1-16).
  • SHA256SUMS published beside the archives (consumed by the installers exactly as t3:scripts/install.sh:194-215 does). Detached trust-core signatures over SHA256SUMS are **deferred, with rationale**: fabric-dist downloads are already authenticated + TLS (unlike t3code's anonymous GitHub CDN), so checksums cover corruption and signatures add value only for offline mirrors — recorded as a follow-on in the WP-PKG-03 closing note, not built now.
  • Versions come from the single resolver (scripts/release-version.cjs — R3 spec input 3): archive versions are the same train versions npm gets; commit-pinned prereleases (<base>-<channel>.<short-sha>, scripts/lib/release-version.cjs:146-151) name staging/develop archives.

3.3 Installers (adapting `install.sh` / `install.ps1`)

  • POSIX installer adapted from the 227-line dependency-free t3:scripts/install.sh:1-4 ("Needs only sh, tar, sha256sum or shasum, and curl or wget") and t3:scripts/install.ps1:7-22 for Windows, kept dependency-free.
  • Env-var surface uses the platform-wide FABRIC_ prefix (spec 15 §2/§3.4): FABRIC_CHANNEL (= stable|staging|dev, §5), FABRIC_VERSION (exact pin), FABRIC_DIST_BASE_URL (mirror override), FABRIC_DIST_TOKEN (auth), FABRIC_HOME (layout root, default ~/.fabric) — mirroring T3CODE_CHANNEL/T3CODE_VERSION/T3CODE_RELEASE_BASE_URL (t3:scripts/install.sh:8-16). No new prefix is minted.
  • **Private-download auth (the one structural delta from t3code)**: the installer resolves credentials in order — FABRIC_DIST_TOKEN env; else gcloud auth print-access-token when gcloud ADC is present; else fail with a documented pointer to the private-install doc. Downloads hit fabric-dist over authenticated HTTPS. There is no anonymous path — that is D19 working as intended.
  • Channel/version resolution keeps t3code's safety rails: per-train version listing so a stable install can never pick a prerelease (t3:scripts/install.sh:145-160), un-requested trains refused (install.sh:163-174), targeted error on missing checksum asset (install.sh:196).
  • Versioned layout shared with the service: archives unpack to $FABRIC_HOME/runtime/versions/<version> (t3code's layout, install.sh:17-19,177, with ~/.t3 → ~/.fabric per spec 15 §3.5); staging dir + smoke-run (environment-server --version) + .install-complete marker + atomic move (install.sh:194-215); user entry point symlinked ~/.local/bin/environment-server → versions/<v>/environment-server with a PATH hint (install.sh:220-221,223-226). Progress UX on stderr survives piping (install.sh:33-52,88-118).
  • **First-boot key provisioning (the WP-FN-02/WP-FN-03 hand-off, made concrete here)**: the install finishes by invoking the installed binary's idempotent environment-server keys init, which generates — only when absent — the default **run signing key** (WP-FN-02 AC4: "spec 40 P-04 provisions a default run key so the shipped platform signs by default") and the **attestation keys** (proxy attestation key + model-decision signing key) that make WP-FN-03 AC2's default-covered posture real ("with keys present (the packaged default), proxy attestation and in-process model-decision signing are ON without env opt-ins"). Key material lands in $FABRIC_HOME/keys/ (0700 dir, 0600 files) — a data directory per spec 15 §3.5, deliberately **outside** runtime/versions/<v> so keys survive updates (WP-PKG-06) and uninstall (WP-PKG-08 AC1 preserves data directories). Owned by WP-PKG-04 (generation) and WP-PKG-05 (service-start refusal when keys are missing).
  • Installer entry: curl -fsSL <install-url> | sh / irm <install-url> | iex where <install-url> is a fabric-operated HTTPS location serving the scripts (the scripts themselves are not secret; only artifact downloads are authenticated). The t3code precedent is https://t3.codes/install.sh (t3:docs/user/install.md:13-21).
  • Installer tests ride the t3code precedent of a real test file (t3:scripts/install.test.ts, R3 §2.1).

3.4 Background service

  • Adopt t3code's backends: **systemd user services with lingering** on Linux, **launchd LaunchAgent** on macOS (t3:docs/user/background-service.md:45-54); service install is a **reconciler** (install/update/repair from the CLI's own version, downgrade refused without --allow-downgrade — t3:apps/server/src/cli/service.ts:38-63); service status diagnoses concrete problem codes (linger-disabled, user-manager-unavailable, service-stopped, restart-pending) with repair guidance (background-service.md:83-88).
  • **Windows-service stance (decision — recorded as scope delta SD-PKG-1, §1.6)**: Windows background services are **out of scope**, adopting the reference's explicit position (t3:docs/user/background-service.md:90-94). On Windows the installer installs the binaries and the recommended managed path is the desktop app, whose DesktopBackendManager owns spawning the bundled environment server (WP-T3-26, 10-t3code-migration.md:434-440). environment-server service install on Windows exits with a typed unsupported-platform message pointing at both alternatives (foreground run; desktop app). This narrows D3's "service" for one OS; the rationale and revisit condition live in the SD-PKG-1 entry (§1.6), which WP-RM-03's Gate-B register carries, in addition to the WP-PKG-05 doc page.
  • Command surface hangs off the installed binary, mirroring t3's single-binary subcommands (t3 service install|status|restart|uninstall, t3 update, t3 uninstall — t3:docs/user/install.md:28-35): environment-server service …, environment-server update, environment-server uninstall.

3.5 Self-update

  • Adopt the launcher-owned two-phase architecture: a **stable launcher** (from t3:apps/server/src/serviceLauncher.ts) is the runtime systemd/launchd selects and the only writer of durable service state; server children request updates over inherited IPC and never rewrite their own service definition (t3:docs/internals/server-updates.md:3-8).
  • Two-phase commit verbatim: pending update durably recorded → target starts as a **trial** (migrations done, HTTP bound, parked at the activation gate) → prepared → durable commit → live; failed/timed-out trials roll back (server-updates.md:17-31). **SQLite snapshot rollback**: launcher snapshots main/WAL/shm after stopping the old child so trial migrations are reversible without down-migrations, with a durable restore marker (server-updates.md:33-43).
  • Exact-version installs keep restarts independent of cache eviction and moving tags (server-updates.md:10-14); capability negotiation distinguishes desktop-managed vs boot-service self-update (t3:apps/server/src/cloud/selfUpdate.ts:40-47 — this module lands with WP-T3-19 per spec 10 row 12 and is *activated* here).
  • environment-server update semantics per t3:docs/user/background-service.md:23-36: newest release on the channel, prompt before restarting live work, exact-version pinning, --channel train switching, --allow-downgrade guard. Update downloads hit fabric-dist with the §3.3 auth chain.

3.6 Entry-point naming (Gate-B note)

The installed entry point is the environment-server bin — the spec 15 §3.3 name for apps/environment; this spec does not mint a new bin. A short launcher alias (candidate: fabric) would improve CLI ergonomics (environment-server service install is a mouthful next to t3 service install) but is a **new binary name requiring a spec 15 §3 matrix amendment at the Gate-B name lock**; it is proposed there, and every WP below is written to work without it.

3.7 Desktop channel note

@fabricstack/console-desktop (WP-T3-26) ships as electron-builder artifacts published to fabric-dist under desktop/, with merged mac/win auto-update manifests generated at release time per the t3code pattern (t3:scripts/merge-update-manifests.ts:19-24; desktop's token-based two-phase update handoff per t3:docs/internals/server-updates.md:52-57). Desktop artifact production is an acceptance item of WP-PKG-03/06, not a separate WP: the reference implementation builds desktop targets inside the same release workflow (six-target matrix, t3:.github/workflows/release.yml — R3 §2.4), and splitting it would duplicate the pipeline.

---

4. Form 2 — cluster/Helm bundle (priority 3)

4.1 One versioned platform chart

  • The chart un-freezes: version/appVersion move from the static 0.1.0 (packages/kradle/charts/Chart.yaml:1-7) to the **platform train version** resolved by scripts/release-version.cjs, stamped at package time (never committed as a moving value). Chart name and K8s identities follow spec 15 §3.6 (control-plane, CRD group control-plane.fabric.internal, namespace control-plane-system) — and per spec 15 WP-RN-12's dependency note, **re-versioning and renaming land as one chart-consumer break**: WP-PKG-09 and WP-RN-12 ship in the same train.
  • Container image references in values.yaml default to platform-version tags in the private AR Docker repo (today images are only sha-tagged at deploy time, publish.yml:812-816); the sha tag remains as a co-tag for provenance.

4.2 Fold the three deploy mechanisms into the bundle

Today "the platform is not one bundle" (R3 §1.2): kradle chart + atlas-webui Kustomize overlay (publish.yml:740-742) + Envoy AI Gateway Kustomize (Chart.yaml:31-34), with CRDs applied out-of-band (publish.yml:955). Fold decisions:

MechanismDispositionWhy
atlas-webui Kustomize overlay**Folded into the chart** as catalog-webui templates behind an install condition (like the existing vela/kyverno/jitsi conditions, Chart.yaml:17-35)It is fabric's own service; envsubst-piped Kustomize (publish.yml:740-742) exists only because nobody folded it — no upstream constraint
Envoy AI Gateway Kustomize**Stays out of the chart; becomes an operator-managed install phase** (operator install renders/applies it, replacing the CI-only step)Upstream deliberately deploys Envoy via Kustomize, not as a subchart (Chart.yaml:31-34) — the deliberate choice is respected; the *customer-facing* unit becomes one operator command anyway
CRDs (8 files, packages/kradle/charts/crds/)Chart keeps crds/ for first install; the **operator owns upgrades** via server-side apply, productizing what CI does today (kubectl apply -f … --server-side --force-conflicts, publish.yml:955)Helm does not upgrade crds/ contents; today's out-of-band step is correct and just needs an owner that is not the company CD
Vendored subcharts (jitsi-meet 1.5.2, kyverno 3.9.1, vela-core 1.10.9 — packages/kradle/charts/charts/)Remain vendored in the published chartPrivate distribution must not depend on third-party chart repos at install time

4.3 OCI publication and the operator as the entry point

  • Chart published to oci://us-central1-docker.pkg.dev/boot-464019/fabric-charts per train, with channel co-tags mirroring the dist-tag scheme (§5). The publish step lives in fabric's private pipeline (spec 15 §4.2), gated by the same packed/verify discipline as npm (chart render + install dry-run gate in the verifier lane).
  • @fabricstack/operator (the renamed kradle-installer, spec 15 §3.1 row 43; today @a5c-ai/kradle-installer, bin kradle-install, targets minikube|existing|eks|aks|gke, Terraform GKE renderer — packages/kradle/installer/src/types.ts:3, SPEC.md:1-33, src/terraform/root.ts:115-193) becomes the operator entry consuming **published** artifacts: it pulls the chart by OCI ref + version instead of rendering against repo paths, and drops the CI-flag coupling (A5C_CLOUD_*→FABRIC_* repo variables per spec 15 §3.4; deploy hooks currently gated at publish.yml:1316-1321).
  • Dogfooding closes the loop: the company's own deploy_kradle job (publish.yml:792-1130) switches from helm upgrade --install kradle packages/kradle/charts (publish.yml:1080) to installing the **published** OCI chart at the resolved train version — CI becomes the first customer of the bundle, so a broken publication breaks CD before it breaks a customer.

---

5. Channels and trains (cross-form; adapting t3code's discipline to fabric's branch model)

**Decision proposed: keep fabric's three branch-mapped channels, present them installer-facing as stable/staging/dev, and adopt t3code's exact-commit promotion discipline; no cron-driven nightly train is added.**

  • Mapping: stable ↔ branch main ↔ dist-tag latest; staging ↔ staging ↔ staging; dev ↔ develop ↔ develop (scripts/lib/release-version.cjs:44-46). The same three channel names select archives in fabric-dist, desktop manifests, and chart co-tags — one channel vocabulary across all three forms, all resolved by the single resolver (R3 spec input 3; sha-suffixed prerelease comparison rules preserved, release-version.cjs:162-170).
  • **Exact-commit promotion**: t3code's core discipline — "stable only ever ships a build that nightly users have already run" (t3:.github/workflows/release.yml:44-47,88-93) — is adopted as: a stable release must promote the **exact commit** of a previously published staging release; the release job verifies this against the fabric/staging/v* tag set (tags per spec 15 §4.2) and refuses otherwise. Commit-pinned prerelease versions (<base>-staging.<short-sha>) make the check mechanical.
  • **No nightly cron**: t3code's 30-minute nightly cron (release.yml:4-27) exists to give an anonymous public user base a fresh train. Fabric's develop channel already publishes per-merge commit-pinned prereleases — a push-driven "nightly" with better provenance — and the private audience does not need cadence-based freshness. Adding a fourth train would complicate the resolver for zero consumers. t3code's concurrency separation (nightly never blocks stable, release.yml:29-37) is still adopted: dev-channel publication runs in a separate concurrency group from staging/stable.
  • Preview-train safety rail equivalent: un-requested channels are refused at install/update time (§3.3; t3:scripts/install.sh:163-174).

---

6. CI/publish isolation (cross-form)

Spec 15 §4.3's G1–G6 cover npm. This spec extends isolation to every artifact class it creates (owned by WP-PKG-13; WP-PKG-14 AC5 further extends PG1 to the relay's deploy targets):

GuardMechanism
**PG1 — no public artifact endpoints**The WP-RN-04 CI enforcement job gains assertions: no workflow file references ghcr.io, registry.npmjs.org, public GitHub releases upload actions, or non-pkg.dev helm/oci push targets in a publish context. Negative fixtures included (a deliberately-bad workflow snippet must trip it)
**PG2 — inert-workflow removal**The nested non-executing workflow carrying the ghcr helm push (packages/kradle/core/.github/workflows/publish.yml:94, R3 inferred finding 1) is deleted, so it can never be promoted to the root by a refactor
**PG3 — installer URL pinning**install.sh/install.ps1 default FABRIC_DIST_BASE_URL to the fabric-dist AR endpoint; CI asserts the shipped scripts contain no github.com/.../releases or npmjs URL
**PG4 — public-repo non-interference**Restated invariant: no WP in this spec touches a5c-ai/babysitter names, publishing, tags, or workflows (intent acceptance criterion 7; enforced by WP-RN-02's guard). The public repo's npm-global install story for @a5c-ai/* (installation.md:126-131) continues to exist **there**, untouched
**PG5 — credential starvation extended**Fabric repo secrets contain no ghcr/npmjs/public-release credentials; CI asserts no workflow consumes such a secret (extends G3, spec 15 §4.3)

---

7. Work packages

Fourteen work packages: the Gate-A planned inventory (synthesis §4.4) with the three §1.5 scope adjustments, plus WP-PKG-14 (§1.5 delta item 4) owning the relay production rollout, which previously had no owner among the program's work packages.

WP-PKG-01: Exports-map program — publics defined across the renamed 43-package matrix

  • **Objective**: Every publishable package declares its public API via an exports map (root + enumerated subpaths, no wildcards); deep imports of non-exported modules fail; the packed-artifact verifier enforces exactness; publics ship once, under @fabricstack/* names, in the D10 major-bump train (7.0.0) together with RN-1.
  • **Affected paths**: all 43 manifests enumerated by scripts/lib/publishable-packages.cjs:122-161 (exports fields; today packages/babysitter-sdk/package.json:7-14 has zero exports entries and packages/adapters/sdk/package.json exports only "." — R3 §1.6); .github/workflows/release-artifact-verifier.yml (exactness assertions added to the FIX-011 gate, release-artifact-verifier.yml:1-16); source import sites that deep-import now-private paths (workspace-wide sweep); docs-site SDK reference pages regenerated from exports maps (package.json:116-123 docs gates).
  • **Acceptance criteria**: (1) node -e "require('@fabricstack/orchestrator-sdk/dist/internal-anything')" -style non-exported deep imports fail for every package (verifier negative matrix, at least one probe per package); (2) all declared subpaths import and typecheck in the verifier's fresh-install consumer lane (release-artifact-verifier.yml:5-9 extended); (3) zero in-repo imports of non-exported paths (CI grep/resolver check); (4) the exports cutover and the RN-1 rename publish as one train versioned 7.0.0 (tag fabric/<branch>/v7.0.0 exists; no intermediate train carries renamed-but-unbounded packages); (5) docs-site publics reference lists exactly the exported subpaths per package.
  • **Test requirements**: packed-artifact verifier full 43-package matrix green with the exactness assertions; consumer-typecheck lane green; WP-TEST-09 (Q-09) projection/parity suites compile against the declared publics only.
  • **Dependencies**: WP-RN-05, WP-RN-06 (same train; collision-free bin set), WP-RN-07 (train publishes to AR); blocks the public-surface claims of WP-T3-05 consumers.
  • **Size**: L

WP-PKG-02: Versioning discipline — lockstep kept, changesets removed, HYG2 closed

  • **Objective**: Execute the §2.2 decision: one versioning mechanism (the single resolver), changesets deleted, train-level semver posture documented, wildcard internal deps eliminated.
  • **Affected paths**: .changeset/config.json:1-20 + .changeset/observer-dashboard-replacement.md (deleted); new docs/VERSIONING.md (posture + rejected alternative recorded); scripts/release-version.cjs:24-27 verify-manifests (new failure on */range-versioned internal deps); packages/atlas/webui/package.json:16 and every other wildcard internal dep found by the check.
  • **Acceptance criteria**: (1) repo-wide grep for changeset in .github/workflows and tree returns zero live references; (2) verify-manifests fails on a fixture manifest with "@fabricstack/catalog": "*" and passes on the cleaned tree; (3) docs/VERSIONING.md states the lockstep-train posture, the D10 major-bump rule, and the changesets-removal rationale; (4) the 7.0.0 train publishes with all internal deps exact-pinned (existing verify + new check both green).
  • **Test requirements**: verify-manifests negative fixture in CI; release train dry-run (release-version.cjs resolve/verify) green.
  • **Dependencies**: WP-RN-07 (resolver retarget lands first so this extends the retargeted script once).
  • **Size**: M

WP-PKG-03: SEA executables + self-contained per-platform archives + private dist publication

  • **Objective**: Build environment-server and orchestrator as Node SEA executables; produce one self-contained archive per platform/arch (§3.2 contents, six-target matrix) plus SHA256SUMS; publish per train to the fabric-dist AR generic repo; desktop electron-builder artifacts + merged update manifests ride the same pipeline (§3.7).
  • **Affected paths**: new scripts/build-archives/ (adapted hard-fork of t3:scripts/build-cli-archive.ts:1-16 and t3:scripts/merge-update-manifests.ts:19-24; attribution per WP-PKG-12); fabric's private publish workflow (spec 15 §4.2 publish-npm-private.yml gains archive build/publish jobs with the §5 concurrency split); apps/environment + orchestrator CLI SEA entry configs; CI Node matrix (SEA build hosts ≥25.7 — §1.4); fabric-dist repo provisioning in boot-464019 (WIF SA roles/artifactregistry.writer, mirroring spec 15 §4.1's npm setup).
  • **Acceptance criteria**: (1) on a clean VM per platform (no Node, no npm, no compiler), unpacking the archive and running environment-server --version and orchestrator --version succeeds — the build-cli-archive contract holds; (2) SHA256SUMS verifies for every asset; a corrupted archive fails installer verification (negative test); (3) archives exist in fabric-dist for a full staging train at the resolver-derived version, addressable by channel and by exact version; (4) console-web is served by the unpacked environment-server and loads in a browser (smoke); (5) desktop artifacts + merged mac/win update manifests published under desktop/ for the same train; (6) build jobs run on Node ≥25.7 hosts and the workspace engines floor (≥22.16) is untouched (CI matrix asserts both); (7) zero public-endpoint references in the new jobs (PG1 green).
  • **Test requirements**: per-platform boot smoke in the release pipeline (t3code's release-smoke.ts pattern, R3 §2.4); checksum negative test; WP-TEST-04 (Q-04) consumes these archives as its packaged-install input.
  • **Dependencies**: WP-T3-19 (environment server exists), WP-T3-32 stage 2 (toolchain + floors), WP-T3-25 (console-web assets), WP-PKG-13 (fabric-dist guards live), WP-RN-07 (resolver/train); WP-T3-26 for the desktop artifacts. Unblocks WP-T3-28's bootstrap-download activation (10-t3code-migration.md:451-453).
  • **Size**: L

WP-PKG-04: `install.sh` / `install.ps1` — channel-aware, checksum-verified, authenticated, versioned layout

  • **Objective**: Ship the §3.3 installers: dependency-free POSIX + PowerShell scripts installing from fabric-dist into $FABRIC_HOME/runtime/versions/<v> with symlinked entry point, channel/version/mirror/auth env-var surface (FABRIC_CHANNEL|VERSION|DIST_BASE_URL|DIST_TOKEN|HOME), t3code's safety rails and UX — and first-boot provisioning of the default run signing key and attestation keys (§3.3), so the installed platform satisfies WP-FN-02 AC4 and WP-FN-03 AC2 (spec 20) out of the box: signing and attested by default, no opt-in.
  • **Affected paths**: new scripts/install/install.sh + scripts/install/install.ps1 (adapted hard-forks of t3:scripts/install.sh (227 lines) / t3:scripts/install.ps1 (224 lines)); new scripts/install/install.test.ts (per t3:scripts/install.test.ts precedent); apps/environment keys init CLI surface (the idempotent generator the installers invoke, §3.3 — key formats per WP-FN-02's trust-core run-key configuration in packages/babysitter-sdk/src/config/ and WP-FN-03's proxy/model attestation key seams, spec 20); hosting of the scripts at the fabric-operated install URL; docs/user-guide/getting-started/installation.md private-install rewrite (coordinated with WP-RN-13, which owns the docs sweep — spec 15 WP-RN-13 affected paths already name installation.md:126-131).
  • **Acceptance criteria**: (1) on clean Linux/macOS/Windows machines with only a FABRIC_DIST_TOKEN, the one-line install produces a working environment-server --version via the symlink, with layout exactly $FABRIC_HOME/runtime/versions/<v> + .install-complete marker; (2) checksum verification: a tampered archive aborts before the atomic move (negative test); (3) channel rails: FABRIC_CHANNEL=stable can never resolve a -staging./-develop. prerelease; an unrequested channel is refused (fixture tests mirroring install.sh:145-174 behavior); (4) FABRIC_VERSION exact-pin installs that version; FABRIC_DIST_BASE_URL mirror override honored; (5) auth chain order (token → gcloud ADC → documented failure) proven by matrix test; (6) re-running the installer over an existing install is idempotent and atomic (staging-dir + move semantics); (7) PATH hint printed when ~/.local/bin is not on PATH; (8) **default key provisioning (WP-FN-02 AC4 / WP-FN-03 AC2, spec 20)**: a fresh install generates the run signing key and the attestation keys (proxy attestation + model-decision signing) into $FABRIC_HOME/keys/ with 0700/0600 permissions via the idempotent keys init step (§3.3); the first run on a fresh install produces a trust-core-signed journal (WP-FN-02 AC1 envelope present) and journals **neither** the journal-signing-disabled nor the attestation-disabled loud marker (WP-FN-02 AC3 / WP-FN-03 AC1) — proving the shipped default is the covered posture; re-running the installer over an existing $FABRIC_HOME never regenerates or overwrites existing keys (idempotence fixture asserts byte-identical key files).
  • **Test requirements**: installer test suite in CI against a staged fabric-dist fixture (token-authenticated local mock acceptable for PR CI; real AR in the release lane); key-provisioning assertions in that suite (fresh vs pre-populated $FABRIC_HOME, permission bits, signed-first-run smoke); WP-TEST-04 uses this installer as step 1 of the whole-platform e2e and asserts the signed-by-default posture.
  • **Dependencies**: WP-PKG-03 (artifacts exist), WP-PKG-11 (channel vocabulary final); WP-FN-02/WP-FN-03 (spec 20 — they define the key formats, consumers, and posture semantics this provisioning must satisfy; their ACs cross-reference this WP as "spec 40 P-04").
  • **Size**: M

WP-PKG-05: Background service — systemd-user + linger, launchd; reconciling `service install`; Windows out of service scope

  • **Objective**: environment-server service install|status|restart|uninstall per §3.4: reconciler semantics with downgrade guard, diagnostic problem codes with repair guidance, launchd LaunchAgent + systemd user unit with lingering; Windows returns the typed unsupported-platform guidance (desktop app or foreground — scope delta SD-PKG-1, §1.6); service start refuses to run key-less by default, guarding the WP-FN-02/WP-FN-03 covered posture (§3.3 keys).
  • **Affected paths**: apps/environment/src/cli/service.ts + platform backends (adapted from t3:apps/server/src/cli/service.ts:38-63 and the backend split described in t3:docs/user/background-service.md:45-54); service unit/plist templates (fabric identifiers, e.g. LaunchAgent label under a neutral reverse-DNS consistent with spec 15 §3.6's .internal posture); docs/user-guide/ background-service page (incl. the Windows stance record, §3.4).
  • **Acceptance criteria**: (1) on Linux: service install enables lingering, installs the user unit, starts it, and service status reports healthy; disabling linger yields problem code linger-disabled with repair text (fault-injection test); (2) on macOS: LaunchAgent installed/loaded, survives logout-login (the linger-equivalent check); (3) reconciler proven: service install from an older binary refuses (downgrade guard) unless --allow-downgrade; from a newer binary it updates the service in place (service.ts:38-63 semantics); (4) service uninstall removes unit/agent and leaves user data intact; (5) on Windows: service install exits non-zero with the typed unsupported message naming the two alternatives (test); (6) the service runs the versioned layout's launcher path, not a bare copy (layout integration with WP-PKG-04); (7) **key-presence refusal (WP-FN-02 AC4 / WP-FN-03 AC2, spec 20)**: on service start, missing or unreadable $FABRIC_HOME/keys/ material (the WP-PKG-04 AC8 provisioned set) is a **refusal** — the service exits before binding, service status reports the new problem code keys-missing with repair guidance (environment-server keys init, or re-run the installer), and the service never silently starts in the unsigned/unattested posture (fault-injection test: delete the keys dir, assert refusal + problem code). The only escape is the operator's *explicit* unsigned/unattested configuration, in which case start proceeds and the run journals the loud journal-signing-disabled / attestation-disabled markers exactly per WP-FN-02 AC3 / WP-FN-03 AC1 (opt-out path test asserts the markers) — the refusal guards the shipped default; it does not remove the documented explicit posture.
  • **Test requirements**: per-OS service lifecycle suite in the release lane (containerized systemd for Linux; macOS runner for launchd); problem-code fault-injection matrix per background-service.md:83-88 codes plus the fabric-added keys-missing code (AC7); explicit-opt-out marker assertion shared with WP-FN-02/03's posture matrix (spec 20).
  • **Dependencies**: WP-PKG-03, WP-PKG-04 (key provisioning it refuses without); WP-T3-19 (the server being serviced); WP-FN-02/WP-FN-03 (spec 20 — posture semantics and loud-marker contracts).
  • **Size**: M

WP-PKG-06: Self-update — launcher-owned two-phase commit with SQLite snapshot rollback

  • **Objective**: Implement §3.5: stable launcher owning durable service state; environment-server update with channel switching, exact pinning, restart consent, downgrade guard; trial-activate-commit update flow with SQLite main/WAL/shm snapshot rollback; desktop-managed vs boot-service capability negotiation; desktop auto-update manifests consumed by console-desktop.
  • **Affected paths**: new apps/environment/src/launcher/ (adapted hard-fork of t3:apps/server/src/serviceLauncher.ts); update orchestration activating the cloud/selfUpdate.ts module landed by WP-T3-19 (t3:apps/server/src/cloud/selfUpdate.ts:40-47; spec 10 row 12, 10-t3code-migration.md:46); update CLI command; snapshot/restore machinery beside the environment's SQLite store (10-t3code-migration.md:380-381 — the WP-T3-19 event store); console-desktop update consumption of the §3.7 manifests.
  • **Acceptance criteria**: (1) happy path: update on a service install downloads the exact target version from fabric-dist, trial-starts it (migrations complete, HTTP bound, parked at the activation gate), commits durably, goes live; the old version remains on disk in the versioned layout; (2) rollback: a trial that fails migrations or times out rolls back automatically, and the SQLite main/WAL/shm snapshot restores byte-identical pre-trial state (checksummed fixture test per server-updates.md:33-43); a restore marker survives a simulated crash mid-restore; (3) the launcher is the only writer of service definitions: a child attempting to rewrite service state is rejected over IPC (unit test of the server-updates.md:3-8 invariant); (4) restart consent: with a live run in progress, update prompts (or defers under --no-restart) before restarting (background-service.md:23-36 semantics); (5) downgrade refused without --allow-downgrade; channel switch update --channel staging moves the install to the staging train; (6) desktop capability: a desktop-managed environment reports desktop-managed and defers restart authority to the desktop app (capability-negotiation test per selfUpdate.ts:40-47).
  • **Test requirements**: update state-machine unit suite (all trial outcomes); crash-injection restore test; end-to-end update between two real published staging train versions in the release lane.
  • **Dependencies**: WP-PKG-03, WP-PKG-05; WP-T3-19 (selfUpdate module + SQLite store); WP-T3-26 for the desktop-managed path.
  • **Size**: L

WP-PKG-07: npm platform packages + launcher — npm bytes equal archive bytes (private)

  • **Objective**: Per §2.4: @fabricstack/environment becomes the thin launcher with per-platform optionalDependencies packages holding the archive contents verbatim, published from tarballs to AR; npx @fabricstack/environment is the documented zero-install trial for authenticated consumers.
  • **Affected paths**: new scripts/build-npm-platform-packages/ (adapted hard-fork of t3:scripts/build-npm-platform-packages.ts:2-21, tarball publication per :16-21, exec dispatch per :184); apps/environment/package.json (launcher shape: bin environment-server, optionalDependencies on the six platform packages); the private publish workflow (platform packages join the wave publication after the archive build); spec 15 §3 matrix amendment record for the @fabricstack/environment-<platformKey> names (Gate-B item, §2.4 — this WP does not start until the amendment is locked).
  • **Acceptance criteria**: (1) byte-equality: the files inside @fabricstack/environment-linux-x64 match the linux-x64 archive of the same train (checksum comparison in the release lane); (2) on a machine with Node ≥22.16, configured .npmrc scope routing and AR auth, npx @fabricstack/environment --version (or the documented two-step equivalent) runs the platform binary; npm i -g @fabricstack/environment installs the working environment-server bin; (3) publication uses tarballs so the flattened node_modules survives npm (verifier unpacks and asserts); (4) the launcher exits with a targeted error naming supported platforms when no platform package matched; (5) all six platform packages carry G1 publishConfig.registry pinning and pass G6's scope check (spec 15 §4.3); the publishable-inventory selector counts them (selector update per G1's criterion, scripts/lib/publishable-packages.cjs:136-137).
  • **Test requirements**: byte-equality gate in the release lane; published-consumer validation job pattern (publish-packages-from-tag.yml:20-27) extended to the launcher; per-platform npx smoke on the CI matrix.
  • **Dependencies**: WP-PKG-03 (archives are the byte source), WP-RN-03/04/07 (registry, guards, train); Gate-B name-lock amendment for platform-package names.
  • **Size**: M

WP-PKG-08: Uninstall story + no-public-channel decision record

  • **Objective**: environment-server uninstall removes service, launcher symlink, and downloaded versions while keeping user data (~/.fabric run/journal state preserved), mirroring t3 uninstall (t3:docs/user/background-service.md:38-41); the §1.5 decision dropping public OS package managers (winget/brew/AUR/deb — the t3code matrix at t3:docs/user/install.md:61-67 and AUR automation t3:packaging/aur/t3code-bin/PKGBUILD:3-9 + t3:.github/workflows/publish-aur.yml:5-17 are **not** adapted) is recorded as a packaging decision with its D19 rationale.
  • **Affected paths**: apps/environment/src/cli/uninstall.ts; installer docs page ("Uninstalling" section); docs/PACKAGING-DECISIONS.md (new: the no-public-channel record, the §1.3 artifact-home table incl. the relay row, the §3.4 Windows stance recorded verbatim as scope delta SD-PKG-1 (§1.6), and the §3.2 signature deferral — the single place packaging decisions live).
  • **Acceptance criteria**: (1) after uninstall on a machine with a service install: no service unit/agent remains, no symlink, $FABRIC_HOME/runtime/ removed, $FABRIC_HOME's data directories (runs/journal/profiles per spec 15 §3.5, and keys/ per WP-PKG-04 AC8 — a reinstall must sign with the same identity) intact — asserted by fixture comparison; (2) uninstall is safe to run twice (idempotent); (3) the decision record exists, states that no @fabricstack/* artifact ships via winget/brew/AUR/deb/public GitHub Releases/ghcr/npmjs, carries the SD-PKG-1 scope-delta entry (§1.6) with its rationale and revisit condition, and is cross-linked from the Gate-B compliance matrix (WP-R-03 in spec 50's planned inventory); (4) repo-wide grep confirms no AUR/winget/brew publication automation was introduced.
  • **Test requirements**: uninstall lifecycle test in the per-OS service suite (extends WP-PKG-05's lane); docs-quality gate on the decision record.
  • **Dependencies**: WP-PKG-04, WP-PKG-05, WP-PKG-06.
  • **Size**: S

WP-PKG-09: Helm chart productization — version-with-platform, OCI publication, one bundle

  • **Objective**: Execute §4.1–4.2: chart re-versioned per train (unfreezing 0.1.0, packages/kradle/charts/Chart.yaml:1-7), renamed with WP-RN-12 in the same consumer break, atlas-webui overlay folded in as conditional catalog-webui templates, image references platform-version-tagged, chart published per train to oci://us-central1-docker.pkg.dev/boot-464019/fabric-charts with channel co-tags, render/install gates in the verifier lane.
  • **Affected paths**: packages/kradle/charts/ (post-RN-10 home per spec 15 WP-RN-12; Chart.yaml, values.yaml (617 lines), new templates/catalog-webui/* replacing the Kustomize overlay consumed at publish.yml:740-742); the private publish workflow (chart package/push job, version stamped from scripts/release-version.cjs); fabric-charts AR repo provisioning; image tag co-tagging in the build jobs (publish.yml:812-816,880-893); deletion of the inert nested workflow (packages/kradle/core/.github/workflows/publish.yml, PG2).
  • **Acceptance criteria**: (1) a staging train publishes chart control-plane at the train version to AR OCI; helm show chart oci://…/control-plane --version <v> returns matching version/appVersion; (2) helm install of the published chart (no source checkout) on a clean minikube brings up the platform including catalog-webui when enabled — replacing the source-path flow documented at packages/kradle/charts/README.md:30-56; (3) the atlas-webui Kustomize overlay step is deleted from CI, with catalog-webui served from the chart in all three GKE environments; (4) default image references resolve to platform-version tags in the private AR Docker repo (rendered-values golden test); (5) chart render + install dry-run gate wired into the verifier lane and red on a template error (negative fixture); (6) vendored subcharts still install with no external chart-repo fetch (air-gap render test); (7) lands in the same train as WP-RN-12 (one chart-consumer break — tag audit).
  • **Test requirements**: chart render golden tests; clean-minikube install e2e; three-environment GKE deploy green post-cutover (publish.yml:1131 smoke per environment).
  • **Dependencies**: WP-RN-07 (train), WP-RN-12 (same-train rename partner), WP-PKG-13 (fabric-charts + PG guards), WP-PKG-11 (channel co-tags).
  • **Size**: L

WP-PKG-10: Operator alignment — consume published artifacts, decouple from CI flags and repo paths

  • **Objective**: @fabricstack/operator (spec 15 §3.1 row 43) installs/upgrades from the **published** OCI chart by version/channel, owns CRD upgrade applies (productizing publish.yml:955) and the Envoy install phase (§4.2), keeps its Terraform/target surface (minikube|existing|eks|aks|gke, packages/kradle/installer/src/types.ts:3; GKE module src/terraform/root.ts:115-193), and drops CI-variable coupling; the company's own CD becomes its first consumer.
  • **Affected paths**: packages/kradle/installer/ (post-RN home packages/control-plane/operator): chart source resolution switched from repo path (buildKradleHelmPlan/renderHelmValuesYaml, src/index.ts:4-6) to OCI ref + resolver-derived version; new CRD-lifecycle phase (server-side apply of the published chart's crds/); new Envoy phase (renders/applies the gateway Kustomize the chart deliberately excludes, Chart.yaml:31-34); config surface renamed A5C_CLOUD_*→FABRIC_* (spec 15 §3.4; today's gating at publish.yml:1285-1357,1316-1321); deploy_kradle (publish.yml:792-1130) retargeted to run operator install --env <env> against the published chart (dogfooding, §4.3).
  • **Acceptance criteria**: (1) on a clean workstation with only AR reader credentials and the operator installed from AR npm, operator install --target minikube deploys the full bundle — chart + CRDs + Envoy — with zero fabric source checkout; (2) operator upgrade against a cluster running train N installs train N+1 including CRD changes (server-side apply proven by a CRD-schema-change fixture between two staging trains); (3) grep: zero repo-path chart references and zero A5C_CLOUD_* reads remain in the operator; (4) CI's three GKE environments deploy via the operator consuming published artifacts (pipeline diff shows helm upgrade --install … packages/kradle/charts at publish.yml:1080 replaced); (5) operator status reports chart version, app version, and channel for a deployed environment.
  • **Test requirements**: operator install e2e on minikube in CI; upgrade-with-CRD-change fixture test; deployed-stack smoke (publish.yml:1131) green in all three environments after the CD retarget.
  • **Dependencies**: WP-PKG-09; WP-RN-05 (operator's renamed package identity).
  • **Size**: L

WP-PKG-11: Channel/train semantics unified across all artifact classes

  • **Objective**: Implement §5: one channel vocabulary (stable/staging/dev) mapped onto the existing branch/dist-tag scheme across npm dist-tags, fabric-dist archive channels, desktop manifests, and chart OCI co-tags; exact-commit stable promotion enforced; concurrency separation between dev and staging/stable publication.
  • **Affected paths**: scripts/lib/release-version.cjs:44-46 (channel alias layer — branch mapping unchanged, user-facing names added); the private publish workflow (promotion job: stable release verifies its commit equals a published fabric/staging/v* tag's commit and refuses otherwise; concurrency groups per t3:.github/workflows/release.yml:29-37 pattern); fabric-dist channel index objects the installers resolve (per-train listing enabling the install.sh:145-160 rails); chart co-tag push step.
  • **Acceptance criteria**: (1) a stable release attempt from a commit never published to staging fails with a targeted error (negative test using a scratch commit); the passing path records the promoted staging version in the release notes metadata; (2) for one full train, the same version string is resolvable by channel across all four artifact classes (npm dist-tag, archive channel index, desktop manifest, chart co-tag) — cross-class consistency test; (3) FABRIC_CHANNEL=stable installs can never resolve commit-pinned prereleases (rail test shared with WP-PKG-04 AC3); (4) a dev-channel publication running concurrently with a staging release neither blocks nor corrupts it (concurrency-group test); (5) docs/VERSIONING.md (WP-PKG-02) gains the channel table and the no-nightly-cron rationale verbatim from §5.
  • **Test requirements**: promotion-guard negative test in CI; cross-class channel consistency check in the release lane; resolver unit tests over the alias layer (sha-suffix comparison rules untouched, release-version.cjs:162-170).
  • **Dependencies**: WP-RN-07 (retargeted resolver/train is the substrate); feeds WP-PKG-03/04/09.
  • **Size**: M

WP-PKG-12: Third-party license/attribution machinery (D15)

  • **Objective**: Extend WP-T3-01's attribution machinery (spec 10, 10-t3code-migration.md:73,234-240) to the packaging-adopted units: installer scripts, service/launcher/self-update code, archive/npm-platform-package build scripts, and update-manifest tooling adapted from t3code (MIT, T3 Tools Inc., 2026 — 00-intent.md:35; R3 spec input 10) all carry attribution; every shipped artifact class embeds the third-party notices.
  • **Affected paths**: the license config + sync script adopted by WP-T3-01 (extended with the packaging units: scripts/install/*, scripts/build-archives/*, scripts/build-npm-platform-packages/*, apps/environment/src/launcher/*, apps/environment/src/cli/{service,uninstall}.ts); THIRD-PARTY-NOTICES embedding: archives ship it at root, npm platform packages include it, installers print a pointer, the chart ships it in the OCI package.
  • **Acceptance criteria**: (1) the license sync check fails when a file adapted from t3:scripts/* or t3:apps/server/src/{serviceLauncher.ts,cloud/*,cli/service.ts} lacks its attribution entry (negative fixture); (2) every Form 1 archive and every npm platform package of a train contains the notices file (verifier assertion); (3) the notices name t3code's MIT license and T3 Tools Inc. copyright for each adapted unit, enumerated file-by-file; (4) chart OCI package carries the notices (unpack assertion in the chart gate).
  • **Test requirements**: license-sync CI check with negative fixture; packed-artifact and archive verifier assertions.
  • **Dependencies**: WP-T3-01 (the machinery exists); touches WP-PKG-03/04/05/06/07/09 outputs as they land.
  • **Size**: S

WP-PKG-13: Private artifact channels — provisioning + publish-isolation extension (PG1–PG5)

  • **Objective**: Provision the two new AR repos (fabric-dist generic, fabric-charts OCI/Docker) in boot-464019/us-central1 with WIF-writer for CI and reader-by-default for humans (mirroring spec 15 §4.1's npm IAM posture); implement guards PG1–PG5 (§6) so no packaging artifact can ever ship to a public endpoint; delete the inert ghcr-push workflow.
  • **Affected paths**: GCP project boot-464019 (two AR repos + IAM bindings); the WP-RN-04 CI enforcement job (ci.yml guard job extended with PG1/PG3/PG5 assertions + negative fixtures); packages/kradle/core/.github/workflows/publish.yml (deleted — PG2; its only live-relevant line was the never-executing helm push … oci://ghcr.io/… at :94); docs/PACKAGING-DECISIONS.md (artifact-home table §1.3 recorded); repo secret audit note (PG5).
  • **Acceptance criteria**: (1) authenticated upload+download smoke against both new repos from CI via WIF and from a developer machine following only the written doc (mirroring spec 15 WP-RN-03 AC2); (2) PG1: a fixture workflow snippet pushing to ghcr/npmjs/public releases trips the guard (red-until-green demonstrated); (3) PG2: the nested workflow file is gone and the guard fails if any .github/workflows/** file reappears under a package directory with publish verbs; (4) PG3: shipped installer scripts contain only pkg.dev/FABRIC_DIST_BASE_URL-derived endpoints (static assertion in the installer test suite); (5) PG5: secret audit recorded — no ghcr/npmjs/public-release credential exists in fabric repo secrets; a CI assertion fails on any workflow consuming such a secret name; (6) IAM review recorded: CI SA writer on both repos, humans reader-by-default.
  • **Test requirements**: guard job negative-fixture suite (one per PG); provisioning smoke as a workflow-dispatch job; secret-scan over workflow files.
  • **Dependencies**: WP-RN-03 (project/WIF/npm-repo precedent), WP-RN-04 (the guard job this extends). First mover for this spec: WP-PKG-03/07/09/14 depend on it.
  • **Size**: M

WP-PKG-14: Relay artifact class & production rollout — deploy pipeline, channel promotion, runbook execution, guard coverage

  • **Objective**: Own the hosted relay's path to production — the hand-off WP-T3-31 documents "(Worker + Postgres + cloudflared) … for spec 40/50 consumption" (10-t3code-migration.md:476) and WP-FN-16 FR-7 requires carried by specs 40/50 (20-functionality-coverage.md:111): a repeatable, train-versioned deploy pipeline for the Cloudflare Worker + managed Postgres + cloudflared stack, per-channel environment promotion under §5 discipline, executed deploy/rollback runbook procedures, and publish-guard coverage so relay deployment can never leak onto a public distribution surface.
  • **Affected paths**: infra/relay/ deploy tooling (per-environment wrangler configuration; invocation of the @effect/sql-pg/drizzle migration runner — relay sources per WP-T3-31, 10-t3code-migration.md:476); the private publish workflow (spec 15 §4.2 — new relay deploy jobs for dev/staging/prod in their own §5 concurrency group); Cloudflare account + managed-Postgres provisioning records and credential setup (Worker-scoped deploy token; migration-role DB credential; PG5-pattern secret-audit entries); docs/remote-access-runbook.md deployment/rollback sections (runbook ownership stays with WP-FN-16 — this WP writes and executes its deploy/rollback procedures); the WP-RN-04 CI enforcement job (PG1 extension for relay deploy targets); docs/PACKAGING-DECISIONS.md (the §1.3 relay-home row recorded).
  • **Acceptance criteria**: (1) **channel promotion**: relay deploys are train-versioned and channel-mapped per §5 — dev and staging relay environments deploy from their trains, and the production relay only ever deploys a build whose exact commit a staging relay already ran (the §5 exact-commit promotion rule applied to the relay class; negative test: a prod deploy attempt from a commit never deployed to staging is refused with a targeted error); (2) **staging is the proving ground**: the staging relay deployment that WP-T3-31 AC1's broker e2e and WP-FN-16 AC7's runbook execution run against is produced by this pipeline (workflow-dispatch or train publication), not hand-rolled — asserted by deploy-provenance metadata (train tag + commit) on the staging Worker; (3) **migrations gated**: Postgres schema migrations run as an explicit, logged pipeline step; the migration up/down fixture passes in staging before any prod promotion (pipeline ordering asserted); (4) **rollback proven**: following the runbook's rollback procedure restores the previous Worker version + compatible schema — demonstrated in staging by fault injection (deploy a known-bad build, roll back, WP-T3-31's broker e2e green again); (5) **guard coverage (PG1 extension)**: the WP-RN-04 guard job asserts relay deploy jobs target only the fabric-controlled Cloudflare account (account-id allowlist) and that no workflow publishes the Worker or any relay artifact to a public marketplace/registry endpoint — a deliberately-bad fixture snippet trips it (red-until-green demonstrated, same discipline as WP-PKG-13 AC2); (6) **credential posture (PG5 pattern)**: the Cloudflare deploy token is scoped to the relay Worker and the DB credential to the migration role; both are recorded in the PG5 secret audit and a CI assertion fails if any non-relay workflow consumes them; (7) **environment-side channel consistency**: the cloudflared managed-tunnel/relay endpoints that environments consume (WP-T3-31's apps/environment/src/cloud/ activation) resolve per channel from the same §5 vocabulary — the WP-PKG-11 AC2 cross-class consistency check is extended to cover relay endpoints as a fifth class; (8) **steady-state observability**: the production relay emits a minimum production signal set — Worker error rate, bootstrap-grant failure count, cloudflared managed-tunnel liveness, and managed-Postgres disk-utilization + connection-count — each wired to an alert routed to the fabric on-call channel with a documented threshold in the runbook's observability section; a synthetic fault (forced bootstrap-grant failure and a stopped tunnel in staging) fires the corresponding alert (fault-injection test), so the production rollout ships with alerting, not just deploy/rollback tooling.
  • **Test requirements**: post-deploy staging smoke reusing WP-T3-31's broker e2e as the gate; migration up/down fixture; staged rollback fault-injection drill (AC4); PG1-extension negative fixture in the guard suite; the WP-FN-16 AC7 runbook execution consumes this pipeline's staging deployment (cross-spec, asserted there).
  • **Dependencies**: staged per the roadmap's stage split (spec 50 §3 note 5) — **stage 1** (deploy pipeline): WP-T3-31 (relay code + documented ops story exist); **stage 2** (production rollout): WP-PKG-11 (channel vocabulary + exact-commit promotion rule), WP-PKG-13 (guard-job framework + PG discipline). Consumed by WP-FN-16 (FR-7 acceptance runs against this WP's staging/prod deployments).
  • **Size**: M

---

8. Cross-spec obligations honored (D21 checklist)

  • **Spec 15 §7 row for this spec, satisfied**: P-01 (→WP-PKG-01) lands with RN-1 in one major bump; P-07 (→WP-PKG-07) publishes to AR only; P-09/P-13 (→WP-PKG-09/13) inherit §3.6 chart names and the §4.2 artifact-home decision; no packaging WP creates a public-npm — or any public — channel for @fabricstack/* (§6 extends the guard set beyond npm).
  • **Spec 10 hand-offs consumed**: row 12's install/service/self-update landing (WP-PKG-03…08); WP-T3-26's desktop artifacts productized (§3.7); WP-T3-28's bootstrap-download unblocked by WP-PKG-03; WP-T3-32's Node floors encoded (§1.4); WP-T3-31's relay ops story (Worker + Postgres + cloudflared, documented "for spec 40/50 consumption") productized as WP-PKG-14.
  • **Spec 20 hand-offs consumed**: WP-FN-02 AC4 and WP-FN-03 AC2's default key provisioning ("spec 40 P-04") now has its concrete owner — WP-PKG-04 AC8 (first-boot generation into $FABRIC_HOME/keys/) plus WP-PKG-05 AC7 (service-start refusal without keys, explicit opt-out preserving the WP-FN-02 AC3 / WP-FN-03 AC1 loud markers); WP-FN-16 FR-7's relay packaging/rollout is owned by WP-PKG-14, whose staging/prod deployments FR-7's runbook-execution acceptance runs against.
  • **Spec 30 (planned)**: WP-TEST-04's whole-platform e2e ("packaged install → create run → dispatch → agent session → journal → UI assertions", D7(b)) consumes WP-PKG-03/04 outputs and installs from Artifact Registry, never npmjs (spec 15 §7 row for spec 30).
  • **Spec 50 (planned)**: sequencing input — WP-PKG-13 → {03 → 04 → 05 → 06 → 08, 07} and {09 → 10} after WP-RN-12; WP-PKG-14 after WP-T3-31 + WP-PKG-11/13 (relay rollout cannot precede the relay or the guard/channel substrate); WP-PKG-01/02 ride the RN-1 train; all packaging follows migration + coverage per D11. Scope delta SD-PKG-1 (§1.6) is written for direct citation by WP-RM-03's Gate-B register — no sibling-spec edit required to carry it.
  • **Public repo untouched** (intent acceptance criterion 7): no WP above modifies a5c-ai/babysitter names, publishing, tags, or workflows; PG4 restates it as a standing guard.
  • **Gate-B items raised by this spec**: (a) launcher alias bin (§3.6, candidate fabric); (b) @fabricstack/environment-<platformKey> platform-package names (§2.4) — both proposed as spec 15 §3 matrix amendments for the name lock; WP-PKG-07 is gated on (b); (c) scope delta SD-PKG-1 (§1.6, D3 narrowing: no Windows background service in v1) — recorded for the WP-RM-03 Gate-B register.

Work-package count in this spec: **14** (WP-PKG-01 … WP-PKG-14).

Article source

The article body is owned directly by this record.

Related pages

No related wiki pages for this record.

Shortcuts

Open overview
Open JSON
Open graph