page:docs-specs-platform-gaps-research-packaging-state
Packaging state: fabric today vs three target forms reference
Research dossier for the platform-gaps spec effort (intent: docs/specs/platform-gaps/00-intent.md, decision D3).
Packaging state: fabric today vs three target forms
Research dossier for the platform-gaps spec effort (intent: docs/specs/platform-gaps/00-intent.md, decision D3). Date: 2026-09-27. Author: research agent (packaging dossier).
Scope per D3 (docs/specs/platform-gaps/00-intent.md:22): fabric must ship in **all three** packaging forms — (A) single-node installer UX (curl|sh / winget / npx, service, self-update), (B) cluster/Helm distribution (GKE path), (C) npm SDK surface — with priorities proposed by the spec (not by this dossier).
Evidence conventions: fabric paths are relative to C:/work/fabric; the t3code reference snapshot is cited as t3code/<path> and lives at C:/Users/tmusk/AppData/Local/Temp/claude/C--work-company/368e0399-fd11-4960-8b4d-b4dcf1d79c43/scratchpad/t3code. Every claim below is CONFIRMED (read directly from the cited file) unless listed in the "Inferred findings" section, which is the exhaustive list of inferences.
---
Part 1 — Fabric's packaging reality today
1.1 Monorepo and npm publish setup
workspace entries including the genty client apps and mobile/TV/watch shells (package.json:2-50). A pnpm-workspace.yaml also exists at the root listing only the packages/* and adapters-hooks globs (pnpm-workspace.yaml:1-15), but CI installs with npm ci (.github/workflows/publish.yml:1312, deploy_kradle step at publish.yml:896).
scripts/lib/publishable-packages.cjs:1-36 (every workspace member with private !== true and publishConfig.access === "public"). Running it today yields **43 public packages**, all at version 6.0.0 in-tree, spanning @a5c-ai/adapters*, @a5c-ai/hooks-adapter-* (12 harness plugins), @a5c-ai/genty*, @a5c-ai/babysitter(-sdk), @a5c-ai/kradle, @a5c-ai/kradle-installer, @a5c-ai/atlas, @a5c-ai/trust-core, etc. (verified by executing the script; count printed 43).
fixed group pinning the five adapters packages), and one pending changeset markdown sits in the directory (.changeset/observer-dashboard-replacement.md), but **no workflow references changesets at all** (grep of .github/workflows for changeset returns nothing). Versioning is instead owned by a bespoke, incident-hardened release-version resolver.
develop → develop (scripts/lib/release-version.cjs:44-46). Staging/develop releases are **commit-pinned prereleases** of the form <base>-<channel>.<short-sha> (scripts/lib/release-version.cjs:146-151), i.e. identity, not order — the resolver has special comparison rules because sha-suffixed prereleases do not sort (scripts/lib/release-version.cjs:162-170).
(docs/release-incident-2026-08-13.md, referenced from scripts/lib/release-version.cjs:5): - scripts/release-version.cjs:1-43 — single resolver; subcommands resolve/verify/from-tag/ verify-manifests/ensure-tag/tag-source/assert-channel-tags; never mutates the registry. - .github/workflows/release-tags.yml:1-14 — immutable, idempotent annotated release tags (babysitter/<branch>/v<version>), created only from the version the publish run resolved. - .github/workflows/publish-packages-from-tag.yml:1-27 — recovery-only tag publication: derives version from the tag, refuses double publication, gates on the strict 43-package packed-artifact matrix, publishes in dependency-ordered waves under a candidate dist-tag, validates from npm via a published-consumer workflow, and only then moves the channel tag. - .github/workflows/release-artifact-verifier.yml:1-16 — "FIX-011" full packed-artifact gate: build → exact tarball → surface/bin/shebang checks → fresh install without workspace links → root and subpath imports → consumer typecheck → bin smoke tests, over the full 43-package matrix. - .github/workflows/fix-npm-tags.yml:1-5 — remediation workflow that retags latest when it points at a broken publish. - scripts/release-matrix.cjs:1-27 — dependency-ordered publication "waves" derived from the inventory so workflows never carry hand-maintained package lists.
(a5c-ai/fabric) GKE deploy pipeline**; "npm publishing, release tags and the docs site stay in the public a5c-ai/babysitter repository and are deliberately absent here" (.github/workflows/publish.yml:3-6). The tag/recovery/verifier workflows for npm publication nevertheless live in this same tree.
- The repo is an **npm-workspaces** monorepo named
babysitter, root version **6.0.3**, with ~44 - The **authoritative publishable inventory** is derived from the root
workspacesglobs by - **Changesets are configured but dead machinery**:
.changeset/config.json:1-20exists (with a - **Release channels are branch-mapped npm dist-tags**:
main → latest,staging → staging, - The release integrity machinery is extensive and traceable to a real incident
- Split-repo publication topology: the checked-out
publish.ymlis the **private mirror
1.2 Cluster distribution: the kradle Helm chart
**version: 0.1.0, appVersion: 0.1.0** — static, and out of step with the 6.0.x product version (packages/kradle/charts/Chart.yaml:1-7 vs package.json:3).
application surface, external-secrets, network policies, org namespaces, assistant stack — packages/kradle/charts/templates/), **8 CRD files** under crds/ (packages/kradle/charts/crds/), a 617-line values.yaml, and three **vendored subchart tarballs** (jitsi-meet-1.5.2.tgz, kyverno-3.9.1.tgz, vela-core-1.10.9.tgz in packages/kradle/charts/charts/). Declared dependencies: vela-core, kyverno, jitsi-meet, each behind an install condition; Envoy AI Gateway is deliberately deployed via Kustomize, not as a subchart (packages/kradle/charts/Chart.yaml:17-35).
Kubernetes-native contract", "production-shaped but demo-safe", with a documented local flow (npm run setup:minikube -- --apply, requiring minikube, kubectl, helm) and a documented operator flow helm upgrade --install kradle charts/kradle -n kradle-system --create-namespace (packages/kradle/charts/README.md:1-9,30-56).
**nested** workflow file packages/kradle/core/.github/workflows/publish.yml:94 (helm push dist/charts/*.tgz oci://ghcr.io/${{ github.repository }}/charts) — a workflow file living inside a package directory, where GitHub Actions never executes it (workflows only run from the repository root .github/workflows). The root deploy pipeline consumes the chart **from the source path**: helm dependency update packages/kradle/charts (.github/workflows/publish.yml:952), CRDs applied out-of-band with kubectl apply -f packages/kradle/charts/crds/ --server-side --force-conflicts (publish.yml:955), then helm upgrade --install kradle packages/kradle/charts ... (publish.yml:1080).
piped through envsubst (.github/workflows/publish.yml:740-742), outside the kradle chart.
- One platform chart exists at
packages/kradle/charts:apiVersion: v2, namekradle, - Chart contents: 23 templates (deployments, ingress, RBAC, NATS, Gitea + token job, Argo CD
- The chart README positions it as "the installable package surface for the Kradle
- **The chart is not published anywhere by the active CI.** The only
helm pushin the tree is in a - The "platform" is **not one bundle**: the atlas web UI deploys via a separate Kustomize overlay
1.3 The GKE deploy pipeline
branch → environment mapping main→kradle (kradle.a5c.ai), staging→kradle-staging, develop→kradle-develop, each with its own Secret Manager prefix, runtime GSA suffix, org namespace prefix, and same-environment atlas namespace (publish.yml:852-877).
publish.yml:871-884 region), gcloud + gke-gcloud-auth-plugin, docker builds pushed to Artifact Registry us-central1-docker.pkg.dev in GCP project boot-464019 (publish.yml:3-6,880-893), images tagged with the checked-out commit sha (publish.yml:812-816), then the helm-from-source-path upgrade of §1.2. Post-deploy jobs: e2e_smoke_kradle (publish.yml:1131) and a gated live Jitsi E2E g0_rt_jitsi_e2e (publish.yml:1230-1234).
(publish.yml:1285-1357) builds @a5c-ai/kradle-installer and runs npm run deploy:cloud:staging|prod (root scripts at package.json:88-89), i.e. node ./packages/kradle/installer/dist/cli.js install --env <env> — driven entirely by A5C_CLOUD_* repository variables and gated behind A5C_CLOUD_*_DEPLOY_ENABLED flags.
deploy_kradle(.github/workflows/publish.yml:792-1130) is a branch-driven CD job:- Mechanics: Workload Identity Federation auth (
google-github-actions/auth@v2, - A second, config-driven deploy path exists:
deploy_staging_cloud
1.4 The kradle installer package (`@a5c-ai/kradle-installer`)
(packages/kradle/installer/package.json:2-33). Self-description: "Kradle Kubernetes deployment installer — Helm, Terraform, and environment management."
upgrade, auth bootstrap, provider setup and status; Terraform rendering/apply for cluster infra; Kubernetes/Helm-style rendering for repo services; CI/CD entrypoints for staging and production (packages/kradle/installer/SPEC.md:1-33).
(packages/kradle/installer/src/types.ts:3), with GKE config validation (projectId, region, clusterName — packages/kradle/installer/src/sdk/config.ts:234-243) and a complete GKE Standard Terraform module renderer (packages/kradle/installer/src/terraform/root.ts:115-193). It exports renderTerraform and buildKradleHelmPlan/renderHelmValuesYaml (packages/kradle/installer/src/index.ts:4-6).
end-user installer and does not touch services, self-update, or OS package managers.
- Published npm package (in the 43), version 6.0.0, bin
kradle-install - Its spec frames it as the monorepo's **deployment control plane**: SDK + CLI for install,
- Target model:
TargetType = "minikube" | "existing" | "eks" | "aks" | "gke" - This is a **cluster** installer for operators of the repo's own services; it is not a single-node
1.5 Single-node install story
Install" is npm install -g @a5c-ai/babysitter@latest @a5c-ai/adapters-cli (docs/user-guide/getting-started/installation.md:126-131), with **two install tracks** (host-side adapters CLI vs in-session per-harness plugin, installation.md:13-16). Node.js **>=20** is a prerequisite, and the Adapters CLI needs **>=22.13.0** because it loads node:sqlite (installation.md:45-49,199-202).
four genty runtime packages, pinned 6.0.0 (packages/babysitter/package.json:2-27).
claude plugin marketplace add a5c-ai/babysitter-claude@staging + scope-user install — pinned by a live-stack test (packages/adapters/cli/tests/live-stack/documented-install.test.ts:41-50); non-Claude harnesses use npx --yes @a5c-ai/babysitter-codex install --workspace ... (README.md:390).
self-update|selfUpdate|systemd|launchd|winget; the only hits are documentation/knowledge-graph data files under packages/atlas/graph/** and prose in packages/adapters/gateway/README.md, none of them shipping code): - no install.sh / install.ps1, no curl | sh path, no standalone binary or release archive; - no checksum-verified download, no per-version installed layout, no channel selection at install; - no background service (service install/status/restart/uninstall) on any OS; - no self-update (update command, launcher, rollback) — updates mean re-running npm install -g (installation.md "Keeping Updated" section is npm-based); - no OS package-manager presence (winget/Homebrew/apt/AUR) and no GitHub-Releases binary flow — the root .github/workflows/ contains no desktop/binary release workflow (directory listing); - no uninstaller.
private: true — never published, and no workflow builds or releases it (packages/genty/desktop-app/package.json:4,10-26). Per intent D2 the genty shells are slated to be retired in favor of adapted t3code apps (00-intent.md:21).
- The documented install is **npm-global only** and Node-gated: the getting-started guide's "Quick
@a5c-ai/babysitteris a **metapackage**: binbabysitter, depending on babysitter-sdk and the- In-harness plugin install is via harness marketplaces, e.g.
- **What does not exist anywhere in fabric** (searched
packages/andscripts/for - A desktop shell exists (
@a5c-ai/genty-desktop-app, electron + electron-builder) but it is
1.6 npm SDK surface
(babysitter-sdk, babysitter, babysitter-mcp-server, adapters-hooks, adapters) and **no exports map at all** (packages/babysitter-sdk/package.json:7-14; programmatic check: exports keys = 0). Every module under dist/ is therefore deep-importable — there is no declared public API boundary.
(programmatic check on packages/adapters/sdk/package.json: exports keys = 1).
@a5c-ai/babysitter-sdk declare a babysitter bin (packages/babysitter/package.json:7-9, packages/babysitter-sdk/package.json:9-14).
exact release version and verified (release-version.cjs verify-manifests, scripts/release-version.cjs:24-27); semver therefore encodes the release train, not per-package API compatibility. Intent D10 says "break freely, major bump" (00-intent.md:30), which this lockstep scheme can express, but nothing today distinguishes breaking from non-breaking releases per package (changesets, the tool that would, is unwired — §1.1).
subpath imports, consumer typecheck and bin smoke tests per package (.github/workflows/release-artifact-verifier.yml:5-9), and docs QA is wired (package.json:116-123).
@a5c-ai/babysitter-sdkshipsmain: dist/index.js,types, **five bins**@a5c-ai/adapters(the adapters SDK) declares anexportsmap with a **single "." entry**- Note a bin-name collision by design or accident: both
@a5c-ai/babysitter(metapackage) and - Versioning is **lockstep 6.0.x across all 43 packages** with internal dependencies pinned to the
- Consumer-quality gates do exist and are strong: the packed-artifact verifier checks root and
---
Part 2 — The t3code reference machinery
t3code is the packaging reference the intent designates (D3 cites "t3-style install.sh/ps1, service, self-update"; process brief at C:/work/company/.a5c/processes/platform-gaps-spec.mjs:377). Server/CLI version at snapshot: 0.0.42 (t3code/apps/server/package.json:3).
2.1 POSIX/PowerShell installers
sha256sum or shasum, and curl or wget; no Node, npm, or compiler" (t3code/scripts/install.sh:1-4), invoked as curl -fsSL https://t3.codes/install.sh | sh (t3code/docs/user/install.md:13-15).
stable), T3CODE_VERSION pins exactly, T3CODE_RELEASE_BASE_URL allows mirrors (install.sh:8-16). Channel resolution greps the GitHub Releases index with a **per-train tag pattern**, so a stable install can never accidentally pick a nightly (install.sh:145-160); un-requested preview builds are refused (install.sh:163-174).
the checksum, extracts to a staging dir, **smoke-runs the binary** (t3 --version), writes an .install-complete marker, then atomically moves into place (install.sh:194-215). A 404 on the checksum asset produces a targeted message pointing at the npm fallback for pre-binary releases (install.sh:196).
$T3CODE_HOME/runtime/versions/<version> — "the same layout t3 service install uses, so the service reuses this download" (install.sh:17-19,177); the user-facing entry point is a symlink ~/.local/bin/t3 → versions/<version>/t3 (install.sh:220-221), with a PATH hint printed when needed (install.sh:223-226). UX polish: ANSI branding and a live progress bar on stderr so piping to sh still shows progress (install.sh:33-52,88-118).
and channel rules (install.ps1:7-22,130-150); entry is irm https://t3.codes/install.ps1 | iex (t3code/docs/user/install.md:19-21). The installers themselves are covered by a test (t3code/scripts/install.test.ts).
t3code/scripts/install.sh(227 lines) is a dependency-free POSIX installer: "Needs only sh, tar,- Channel/version model at install time:
T3CODE_CHANNEL=stable|nightly|preview(default - Artifact handling: downloads
SHA256SUMSplust3-<version>-<platform>-<arch>.tar.gz, verifies - Versioned layout shared with the service: archives unpack into
t3code/scripts/install.ps1(224 lines) mirrors this on Windows with the same env vars, layout
2.2 Background service and update UX
(t3code/docs/user/install.md:28-35, t3code/docs/user/background-service.md:11-17).
(com.t3tools.t3code.service.plist) on macOS; **Windows background services are explicitly not supported** (background-service.md:45-54,90-94). Status diagnoses concrete problem codes (linger-disabled, user-manager-unavailable, service-stopped, restart-pending) with repair guidance (background-service.md:83-88).
the CLI's own version, refusing silent downgrades unless --allow-downgrade (t3code/apps/server/src/cli/service.ts:38-63). The service downloads release archives itself when needed (service.ts:22-25).
before restarting live work, supports exact-version pinning, --channel nightly train switching and --allow-downgrade (background-service.md:23-36). t3 uninstall removes service, launcher and downloaded versions but keeps user data (background-service.md:38-41).
- Command surface:
t3 service install|status|restart|uninstall,t3 update,t3 uninstall - Platform backends: **systemd user services with lingering** on Linux, **launchd LaunchAgent**
t3 service installis a **reconciler**:reconcileServiceinstalls, updates or repairs usingt3 updatesemantics: moves symlink + service to the newest release on the channel, prompts
2.3 Self-update architecture (server)
systemd/launchd and is the only writer of durable service state; server children request updates over inherited IPC and never rewrite their own service definition (t3code/docs/internals/server-updates.md:3-8).
**trial** which must finish migrations, bind HTTP and park at an activation gate before reporting prepared; only after a durable commit does the child go live; failed/timed-out trials roll back (server-updates.md:17-31).
trial migrations are reversible without down-migrations; a durable restore marker survives interruptions (server-updates.md:33-43).
(t3code/apps/server/src/cloud/selfUpdate.ts:40-47); exact-version installs keep restarts independent of npm cache eviction or moving tags (server-updates.md:10-14). Desktop app updates use a separate token-based two-phase handoff, with merged update manifests for mac/win generated at release time (server-updates.md:52-57, t3code/scripts/merge-update-manifests.ts:19-24).
- A **stable launcher** (
t3code/apps/server/src/serviceLauncher.ts) owns the runtime selected by - Two-phase commit: the launcher durably records the pending update, starts the target as a
- **Database rollback**: launcher snapshots SQLite main/WAL/shm after stopping the old child, so
- Capability negotiation distinguishes
desktop-managedvsboot-serviceself-update
2.4 Release pipeline and version channels
Triggers: stable tag push v* (nightly tags excluded), a **cron every 30 minutes** (8,38 * * * *) that decides whether a nightly is due, and manual dispatch with a channel input (release.yml:4-27). Concurrency: separate nightly and stable queue groups so a nightly can never block a stable release (release.yml:29-37).
published nightly, so stable only ever ships a build that nightly users have already run" (release.yml:44-47,88-93). Three trains — stable, nightly, preview — map to npm dist-tags latest/nightly/preview, preview being a maintainers-only pipeline-exercise train that nothing resolves by default (release.yml:148-204).
test_server → build_bundle → six desktop matrix jobs (mac arm64/x64, linux x64/arm64, win x64/arm64) → publish_cli → release (GitHub Release) → publish_aur → deploy_web → deploy_marketing → finalize → announce_discord (release.yml:48,98,230,264,300,447,539-688, 689,753,938,948,1067,1121,1200`).
the web client, the Rust resource monitor, and a production install of the runtime-external native packages — "nothing in it may require Node, npm, or a compiler on the machine that unpacks it" (t3code/scripts/build-cli-archive.ts:1-16). Release assets carry SHA256SUMS (consumed by §2.1). Supporting scripts: resolve-nightly-release.ts (nightly version/tag derivation from date + run number + sha, t3code/scripts/resolve-nightly-release.ts:14-30), resolve-previous-release-tag.ts, release-smoke.ts, notify-discord-release.ts, sign-macos.ts (all under t3code/scripts/).
- One
Releaseworkflow (t3code/.github/workflows/release.yml, 1258 lines) drives everything. - **Stable promotes a tested nightly**: "Manual stable releases build the commit of the latest
- Job graph: `resolve_commit → preflight (release identity, previous tag) → quality/test/
- The unit of distribution is a **self-contained per-platform archive**: the
t3single-executable,
2.5 npm distribution built from the same bytes
release into the npm story behind npx t3 / npm i -g t3: one @t3code/t3-<platformKey> package per archive **holding the archive's contents verbatim**, plus a thin t3 launcher package listing them as optionalDependencies and exec-ing the one npm installed (build-npm-platform-packages.ts:184). Publishing uses tarballs, not directories, so npm cannot strip the flattened node_modules (build-npm-platform-packages.ts:16-21). The npm bytes are therefore identical to the GitHub Release bytes, and npx t3@latest is the documented zero-install trial path (t3code/docs/user/install.md:38-39).
t3code/scripts/build-npm-platform-packages.ts:2-21converts the per-platform archives of one
2.6 OS package managers and desktop channels
(macOS), sudo apt install ./T3-Code-*.deb (Debian/Ubuntu, self-updating), yay -S t3code-bin and t3code-nightly-bin (Arch) (t3code/docs/user/install.md:61-67).
repackaging the .deb, t3code/packaging/aur/t3code-bin/PKGBUILD:3-9) and a release script; publish-aur.yml is workflow_called from the release pipeline with the release tag and pushes via AUR_SSH_PRIVATE_KEY (t3code/.github/workflows/publish-aur.yml:5-17,36-39, release.yml:938).
only docs; release-desktop.yml contains neither) — those manifests are maintained outside the repo. Desktop auto-update manifests for mac/win are merged per release (t3code/scripts/merge-update-manifests.ts:19-24).
- Documented matrix:
winget install T3Tools.T3Code(Windows),brew install --cask t3-code - AUR publication is automated in-repo:
packaging/aur/holds two PKGBUILDs (stable + nightly, - No winget or Homebrew publication automation exists in the t3code repo (grep for
wingethits
---
Part 3 — Gap map: fabric vs the three D3 target forms
Form A — single-node installer UX (t3-style)
| Capability (t3code reference) | Fabric today | Gap |
|---|---|---|
| `curl\ | sh / irm\ | iex installers, no Node required (t3code/scripts/install.sh:1-4, install.ps1:7-22`) |
Self-contained per-platform archive + SHA256SUMS on GitHub Releases (t3code/scripts/build-cli-archive.ts:1-16) | 43 npm tarballs only; no binary build, no release-asset workflow in .github/workflows/ | **Total** — needs a single-executable/bundling story for the server+CLI |
Versioned runtime layout ~/.t3/runtime/versions/<v> shared by installer and service (install.sh:17-19,177) | none | **Total** |
Background service: systemd user + linger, launchd; reconciling service install (t3code/apps/server/src/cli/service.ts:38-63, docs/user/background-service.md:45-54) | none (no systemd/launchd/service code in any shipping package) | **Total** |
Self-update with launcher-owned two-phase commit and SQLite snapshot rollback (t3code/docs/internals/server-updates.md:17-43) | none; update = npm install -g again | **Total** |
Channels at install/update time: stable/nightly/preview with per-train tag patterns (install.sh:145-160) | npm dist-tags latest/staging/develop exist (scripts/lib/release-version.cjs:44-46) but are npm-only and named after git branches, not user-facing trains | **Partial** — channel *concept* exists; installer-facing train selection, nightly cadence and preview safety rails do not |
npx zero-install trial via launcher + platform packages (t3code/scripts/build-npm-platform-packages.ts:2-21) | npx used only for plugin installers (README.md:390); metapackage @a5c-ai/babysitter is Node-dependent deps, not platform binaries (packages/babysitter/package.json:20-27) | **Large** — no platform-package pattern; npm and (future) binary bytes are unrelated |
winget / brew / apt / AUR presence, AUR automated (t3code/docs/user/install.md:61-67, t3code/.github/workflows/publish-aur.yml) | none | **Total** |
Uninstall story (t3 uninstall, keeps user data) | none | **Total** |
Form B — cluster/Helm bundle (versioned platform chart)
| Capability | Fabric today | Gap |
|---|---|---|
| A platform chart exists | Yes: packages/kradle/charts — 23 templates, 8 CRDs, 617-line values, vela/kyverno/jitsi subcharts (Chart.yaml:17-35) | Chart exists and is production-shaped |
| Chart versioned with the product | version: 0.1.0 static while the platform ships 6.0.x (Chart.yaml:5-6 vs package.json:3); no pipeline step bumps chart version/appVersion | **Chart-release process missing** |
| Chart published to a registry | No helm push in any executing workflow; the only one is in the inert nested packages/kradle/core/.github/workflows/publish.yml:94 | **Total** — chart consumable only from a source checkout |
| One installable bundle for the whole platform | Kradle chart + separate atlas-webui Kustomize overlay + Envoy AI Gateway via external Kustomize (publish.yml:740-742, Chart.yaml:31-34); CRDs applied out-of-band (publish.yml:955) | **Partial** — the "platform" is three mechanisms, CI-orchestrated, not a bundle a customer can helm install |
| Cluster installer for non-CI operators | @a5c-ai/kradle-installer: published CLI/SDK, minikube/existing/EKS/AKS/GKE targets, Terraform + Helm-plan rendering (src/types.ts:3, SPEC.md:16-24) | Exists; needs alignment with a *published* chart instead of repo paths, and its deploy hooks are CI-flag-gated (publish.yml:1316-1321) |
| GKE path proven | Yes — branch-driven WIF + Artifact Registry + helm upgrade --install to prod/staging/develop environments (publish.yml:792-1080) | Works, but is the *company's* CD, coupled to project boot-464019 vars/secrets — not a distributable artifact |
Form C — npm SDK surface (stable publics, semver, docs)
| Capability | Fabric today | Gap |
|---|---|---|
| Publish pipeline integrity | Strong: single version resolver, immutable tags, waves, packed-artifact verifier over all 43 (scripts/release-version.cjs:1-43, release-artifact-verifier.yml:1-16) | Keep; this exceeds the t3code reference for multi-package publishing |
| Declared public API boundary | @a5c-ai/babysitter-sdk has **no exports map** (5 bins, main only — packages/babysitter-sdk/package.json:7-14); @a5c-ai/adapters exports only "." | **Large** — no enforced publics; every dist/ file is API. D6's ACP-canonical re-platforming will land on an unbounded surface unless exports maps come first |
| Semantic versioning | Lockstep 6.0.x + commit-pinned prereleases (release-version.cjs:44-46,146-151); changesets configured but unwired (§1.1) | **Medium** — no per-package semver signal; D10 (break freely, major bump) needs at least a deliberate major-bump mechanism |
| Package count / consumer ergonomics | 43 packages incl. 12 hooks-adapter plugins; duplicate babysitter bin in two packages (§1.6) | **Medium** — surface consolidation and bin-collision resolution are spec material |
| Docs of the SDK surface | docs-site + docs QA gates exist (package.json:112-127); getting-started documents install | Docs exist; publics-reference docs depend on first defining publics |
---
Inferred findings (everything not directly read from a file)
1. INFERRED: the nested packages/kradle/core/.github/workflows/publish.yml never executes in this repository's CI, because GitHub Actions only runs workflows from the repo-root .github/workflows directory. (The file's existence and its helm push line are CONFIRMED.) 2. INFERRED: pnpm-workspace.yaml is vestigial/secondary — every observed install step in CI uses npm (npm ci/npm install), and the release inventory is derived from the root package.json workspaces globs by design (scripts/lib/publishable-packages.cjs:26-33). 3. INFERRED: winget/Homebrew manifests for t3code are maintained outside the t3code repo, since the docs advertise the IDs (t3code/docs/user/install.md:63-64) but no repo workflow publishes them. 4. INFERRED: fabric npm publication jobs run in the public a5c-ai/babysitter repository's copy of the publish workflow — stated by the private-mirror header (publish.yml:3-6), but the public repo's workflow file was not inspected here. 5. INFERRED: the @a5c-ai/genty-desktop-app electron shell has never shipped to users (private, no release workflow); consistent with D2 retiring genty shells.
Spec inputs
Concrete facts the 40-packaging.md spec author must use:
1. **Three forms bind (D3)**: single-node installer UX, cluster/Helm bundle, npm SDK surface; the spec proposes priority order (docs/specs/platform-gaps/00-intent.md:22); per D11 packaging follows the t3code migration and coverage work in the roadmap (00-intent.md:30). 2. **Publishable inventory**: exactly **43 public packages**, derived at run time from root workspaces globs via scripts/lib/publishable-packages.cjs; any packaging work must derive lists from this script, never hand-maintain them (FIX-005, publishable-packages.cjs:1-10). 3. **Version/channel scheme to build on**: main→latest, staging→staging, develop→develop; commit-pinned prereleases <base>-<channel>.<short-sha>; immutable tags babysitter/<branch>/v<version>; single resolver scripts/release-version.cjs (never bypass it). Changesets (.changeset/config.json) are present but unwired — the spec must either wire or remove them. 4. **Release gates that already exist and must gate new artifacts too**: packed-artifact verifier (43-package matrix, .github/workflows/release-artifact-verifier.yml), dependency-wave publication (scripts/release-matrix.cjs --format waves), published-consumer validation and candidate dist-tags (publish-packages-from-tag.yml:20-27). 5. **Single-node target (Form A) starts from zero**: no installer scripts, no binary archive, no GitHub-Releases asset flow, no service, no self-update, no OS package managers, no uninstall in fabric. Node >=22.13 is currently a hard runtime floor because of node:sqlite in adapters-gateway (installation.md:199-202) — a self-contained executable must bundle that runtime. 6. **Reference blueprint for Form A** (all in the t3code snapshot): scripts/install.sh + install.ps1 (channel-aware, checksum-verified, $HOME/.t3/runtime/versions/<v> layout, symlinked bin, exit-44 asset probing); t3 service install/status/restart/uninstall on systemd-user+linger and launchd, Windows service explicitly out of scope; t3 update with downgrade guard and restart consent; launcher-owned two-phase self-update with SQLite main/WAL/shm snapshot rollback (docs/internals/server-updates.md); self-contained archives from scripts/build-cli-archive.ts; npm launcher + @t3code/t3-<platform> optionalDependencies packages from scripts/build-npm-platform-packages.ts so npm bytes equal release bytes; AUR automation packaging/aur + publish-aur.yml; three trains stable/nightly/preview where stable promotes the tested nightly commit (release.yml:44-47) on a 30-minute nightly cron. 7. **Cluster target (Form B) assets in hand**: chart packages/kradle/charts (Chart version: 0.1.0 must be re-versioned with the platform); 8 CRD files applied today out-of-band (publish.yml:955); vendored subcharts vela-core 1.10.9 / kyverno 3.9.1 / jitsi-meet 1.5.2; Envoy AI Gateway deliberately Kustomize-based (Chart.yaml:31-34); atlas-webui deploys via a separate Kustomize overlay (publish.yml:740-742) and must be folded into or federated with the bundle; no chart registry publication exists (only inert helm push at packages/kradle/core/.github/workflows/publish.yml:94 — ghcr OCI is the precedent to adopt or reject); @a5c-ai/kradle-installer (bin kradle-install, targets minikube/existing/eks/aks/gke, Terraform GKE module renderer) is the operator-facing entry point to evolve; the working GKE reference deployment is publish.yml:792-1130 (WIF, Artifact Registry us-central1-docker.pkg.dev, project boot-464019, three branch environments). 8. **npm SDK target (Form C) blockers**: @a5c-ai/babysitter-sdk has **no exports map** and five bins including a babysitter bin that collides with the metapackage's; @a5c-ai/adapters exports only "."; defining and enforcing exports maps across the 43 packages is prerequisite to any "stable publics" claim, and interacts directly with D6 (ACP-canonical adapters) and D10 (break freely, major bump — the major bump should land together with the exports-map cutover). 9. **Split-repo constraint**: npm publishing lives in public a5c-ai/babysitter, GKE CD in private a5c-ai/fabric (publish.yml:3-6); any new artifact channel (GitHub Release binaries, OCI charts) must pick a home repo explicitly and keep the single-resolver version discipline. 10. **License/attribution (D15)**: t3code is MIT (T3 Tools Inc., 2026) — installer/service/update code adopted from t3code/scripts/* and t3code/apps/server/src/cloud/* requires third-party attribution work items in the packaging spec (00-intent.md:34).