# Proposal — Migrate @suaveplan/electrobun to genesis - **Design**: ./design.md - **Spec delta**: ./specs/electrobun/spec.md ## Why Upstream `@suaveplan/electrobun` (`/mnt/s/web/suaveplan/packages/universal/electrobun/`) is a thin scaffolder: it prompts the user for an app name and a feature list, runs an `iconz`-backed icon pipeline, and emits a fixed-template Electrobun project to disk. The genesis `enterprise/electrobun.md` audit highlights five structural gaps: 1. every failure path is either an `Error` literal or a `console.log`, so consumers cannot discriminate between "icon source missing" and "rollup chain blew up"; 2. no OpenTelemetry surface for what is the entry point new contributors hit first; 3. the generator is hard-wired to `node:fs` with no seam for tests or for embedding in CI; 4. only the `create` step is covered — operators have nothing for `dev` (watch + restart), `build` (cross-platform packaging), `package` (release-bundle assembly), `sign` (notarization + entitlements), or the auto-update pipeline; and 5. the `renderXxxFeature()` template functions are inlined-string blobs with no validation of the render input, so a stale `ProjectOptions` shape silently produces a broken project. Genesis cleans up the contract: every failure raises a typed `ElectrobunError` carrying a stable `ELECTROBUN.*` code; every command and every render-helper takes an injectable seam (`FsAdapter`, `CommandRunner`, `IconRendererAdapter`, `Prompter`); every public option is Zod-validated; the CLI grows from one command (`create`) to five (`create`, `dev`, `build`, `package`, `sign`); and an OTel surface (5 counters + 2 histograms) makes the package observable in CI. ## What Land `@suaveplan/electrobun@0.1.0` at `packages/devtools/tooling/electrobun/` with the following feature modules (one folder per feature, sibling `.md` per implementation file; no `electrobun-` prefix inside the package): - `error-codes/` — `ELECTROBUN_CODES = defineCodes("ELECTROBUN", [...])` and `ElectrobunError`. - `telemetry/` — OTel counters + histograms (`projects_generated_total`, `icons_generated_total`, `runner_invocations_total`, `runner_failures_total`, `prompts_total`, `generate_duration_ms`, `runner_duration_ms`). - `types/` — shared interfaces (`ProjectOptions`, `ElectrobunConfig`, `GeneratedFile`, `FsAdapter`, `CommandRunner`, `CommandInput`, `CommandResult`, `IconRendererAdapter`, `Prompter`, every `*Report` / `*Options`). - `schemas/` — Zod schemas for every public type; both schema and inferred type exported. - `fs/` — `nodeFsAdapter` and seam-driven `ensureDirectory`, `directoryExists`, `directoryIsEmpty`, `writeFiles`, `findConfigFile`. - `runner/` — `CommandRunner` contract + `nodeCommandRunner` default (spawn-based). - `config/` — `loadConfig(cliArgs, opts?)` consuming the `@suaveplan/config` 5-layer pipeline (defaults / file / CLI) with the `FsAdapter` injected for file discovery. - `templates/` — render helpers for every project file (`electrobun-config`, `main-process`, `package-json`, `tsconfig`, `readme`, `mainview-html`, `mainview-css`, `mainview-entry`, `shared-types`) plus `templates/features/` for every feature renderer (`i18n`, `network`, `network-full`, `api`, `api-schemas`, `storage`, `auth`, `form-builder`, `r3f`). - `generator/` — `collectFiles(options)`, `generate(options, opts?)` — orchestrates the renderer table, writes via `FsAdapter`, returns a `GenerateReport`. - `icons/` — `generateIcons(opts, deps?)` — wraps `iconz` via the `IconRendererAdapter` seam. - `prompts/` — `runPrompts(config, outputDir, prompter?)`. - `cli/` — five pure functions (`createCommand`, `devCommand`, `buildCommand`, `packageCommand`, `signCommand`) and `runCli(argv, opts?)`. Each returns a `*Report`; none call `process.exit`. - `bin/` — `suaveplan-electrobun` binary wiring `runCli` through `process.argv.slice(2)`. ## Parity Checklist | Capability | Upstream | Migrated | Notes | |---|---|---|---| | `ProjectOptions` / `ElectrobunConfig` types | upstream | yes | Preserved; now Zod-validated. | | `FEATURE_LABELS`, `R3F_MODULE_LABELS` | upstream | yes | Preserved. | | `FeatureModule`, `NetworkingComplexity`, `R3fModule` unions | upstream | yes | Preserved verbatim. | | `loadConfig(cliArgs)` layered loader | upstream | yes | Now consumes `@suaveplan/config`'s 5-layer pipeline; `findConfigFile` is `FsAdapter`-driven. | | `directoryExists` / `directoryIsEmpty` / `ensureDirectory` / `writeFiles` | upstream | yes | Preserved; injectable `FsAdapter`. | | `collectFiles(options)` core file list | upstream | yes | Preserved with deterministic ordering. | | `generate(options)` orchestrator | upstream | enhanced | Returns `GenerateReport`; emits OTel histogram. | | `generateIcons(input, outputDir)` | upstream | enhanced | Takes an `IconRendererAdapter` seam; default = real `iconz`. Returns `IconReport`. | | Project file renderers (9) | upstream | yes | Same outputs, deterministic. | | Feature renderers (8) | upstream | yes | Same outputs. | | Interactive `create` command | upstream | yes | Preserved; Zod-validated; `--skip-prompts` honoured. | | `dev` / `build` / `package` / `sign` commands | none | added | Orchestrate the upstream `electrobun` binary via `CommandRunner`; report shape + error classification. | | Auto-update bundling | none | added | Surfaced via `buildOptions.update` → `electrobun build --update` argv. | | Code-signing options | none | added | `signCommand` exposes identity / entitlements / notarization; the wrapper shapes args + classifies errors. | | Error-code registry | none | added | `ELECTROBUN.*` namespace, eleven codes. | | OTel self-telemetry | none | added | 5 counters + 2 histograms. | | Zod-validated options | none | added | Every public option is Zod-validated. | | Subpath exports per feature | none | added | One subpath export per feature module. | ## Impact ### Affected dependents Run `bunx turbo ls --filter=...@suaveplan/electrobun` to enumerate. Net-new package on genesis — no current dependents. - No dependents (verified by `bunx turbo ls --filter=...@suaveplan/electrobun`). ## Risks - **`iconz` ESM resolution.** Upstream loads `iconz/presets` dynamically. The genesis port keeps the dynamic-import path behind the `IconRendererAdapter` seam so tests can swap in a fake renderer without paying the `iconz` cost. - **`electrobun` binary availability.** `dev`/`build`/`package`/`sign` shell out to the user's local `electrobun` binary (installed in the generated project's `node_modules`). The wrapper probes for it via the `CommandRunner` seam and raises `ELECTROBUN.BINARY_UNAVAILABLE` with a remediation hint when missing. - **Signing requires real certs.** The `sign` command's test surface asserts only the argument-shape and error-classification contract; the real signing pipeline runs against the user's keychain / Apple-developer-id / Windows-PFX and is exercised by an opt-in integration scenario that probes the runner. - **Cross-platform packaging.** `electrobun build` produces platform-specific outputs; the wrapper records the requested target triple (`mac` / `linux` / `windows`) in the `PackageReport` so CI can branch on it.