Pumice desktop app integration — vendor as a co-tenant, converge to shared
Status: open/proposed
Summary
Section titled “Summary”Pending acceptance on merge. The trajectory is settled (vendor now, converge later); the open items in Open questions are sequencing and mechanism details, not blockers.
- Pumice — an existing standalone Tauri v2 + Nuxt 4 desktop application with
its own Rust crates and code-generation pipeline — is brought into this
monorepo as
apps/pumice, fulfilling D-0012-monorepo-tooling’s polyglot brief with a real, load-bearing second ecosystem rather than a thin validating wrapper. See Context. - The integration is phased. Short term, pumice lands as a self-contained
co-tenant: it shares the monorepo’s tooling substrate (moon graph, CI,
justfile,lefthook) but no application code. Long term, it converges — shared Rust crates promote topackages/rust/, and any substrate consumption goes through the S-0008-apps-consume-substrate-through-published-surfaces adapter seam. See Decision. - The JS/TS ecosystem unifies on Bun, per D-0012-monorepo-tooling. Pumice’s frontend migrates from pnpm to Bun as part of onboarding; the three pnpm lockfiles are retired. See Decision.
- Pumice is a fat app (its own frontend, domain crates,
ontogencodegen, template-sync, release pipeline), which distinguishes it from the thin Tauri wrapper drafted inT-CKC1. Their relationship is resolved in Notes. See Why.
Context
Section titled “Context”D-0012-monorepo-tooling fixed the workspace shape (apps/ +
packages/<ecosystem>/ on moon) and named “a Rust Tauri app” as the polyglot
validating case. T-CKC1 drafts that case as a thin wrapper: a minimal
src-tauri embedding the dashboard SPA as a committed static bundle.
Pumice is a different and larger opportunity. It is an already-built Tauri v2
desktop application maintained in sksizer/pumice, with:
| Layer | Technology | Note |
|---|---|---|
| Desktop shell | Tauri v2 (src-tauri, crate pumice-app) | beforeBuildCommand drives the frontend build |
| Frontend | Nuxt 4 + Vue 3 (src-nuxt) | Own pnpm project plus Storybook |
| Domain logic | Three Rust crates (pumice, pumice-config, pumice-desktop) | Path deps; git dep on rust-ontogen pinned by rev |
| Shared TS | packages/admin-types, packages/nuxt_admin_layer | Consumed via file: deps |
| Codegen | ontogen via src-tauri/build.rs | Emits committed .ts/.rs on every cargo build |
| Toolchain | mise (node/pnpm/rust/python/uv) | Dynamic dev ports via exec() scripts |
| Orchestration | justfile + Tauri config + lefthook + release-it | Four GitHub workflows |
Bringing pumice in tests the workspace against a genuinely polyglot, non-trivial application — the outcome D-0012-monorepo-tooling wanted proof of — instead of a scaffold with nothing behind it.
Decision
Section titled “Decision”This decision settles the trajectory and the package-manager stance. Status
is open/proposed; it flips to open/accepted on merge. The per-task mechanics
are decomposed in M-0014-pumice-desktop-app-integration.
Settled
Section titled “Settled”- Adopt pumice as
apps/pumice. An app stays flat (not partitioned by ecosystem) per D-0012-monorepo-tooling — pumice is polyglot internally (Rust core plus a web frontend), exactly the case that rule anticipates. - Phase the integration: co-tenant first, converge later.
- Phase 1 (co-tenant): vendor pumice’s tree under
apps/pumiceintact; wire a thinapps/pumice/moon.ymlwhose tasks shell to pumice’s existing commands (systemtoolchain), matching theapps/dashboardpattern. Pumice shares tooling only — no application-code coupling to the substrate. - Phase 2 (converge): promote genuinely shared Rust crates to
packages/rust/, introduce a workspace-levelCargo.tomlonce two Rust projects share code, and route any substrate consumption through the S-0008-apps-consume-substrate-through-published-surfaces seam.
- Phase 1 (co-tenant): vendor pumice’s tree under
- Unify pumice’s frontend on Bun. Per D-0012-monorepo-tooling’s
Bun-everywhere standard, migrate
src-nuxtand thepackages/*frontend libraries from pnpm to Bun and retire the three pnpm lockfiles. moon’ssystem-toolchain tasks then drivebun/cargo/tauriuniformly. - Keep pumice’s crates an isolated cargo unit in Phase 1. There is no
workspace-wide
Cargo.tomltoday; pumice’s crates keep their relative path deps underapps/pumiceand build under moon’srusttoolchain. A shared cargo workspace is a Phase-2 concern gated on the first shared crate.
Standards touched
Section titled “Standards touched”- S-0008-apps-consume-substrate-through-published-surfaces does not bind in Phase 1 (pumice consumes nothing from the substrate). It becomes the governing contract the moment Phase 2 introduces any app→substrate dependency.
- A real polyglot load, not a scaffold. D-0012-monorepo-tooling wanted
the Rust toolchain proven under moon against a genuine app. Pumice is that
app — a maintained Tauri product — where
T-CKC1is a thin wrapper with no domain behind it. Pumice validates the workspace harder. - Phasing bounds blast radius. Vendoring intact and shelling to pumice’s own commands lets pumice build under moon in days, deferring the invasive work (Bun migration, crate promotion, adapter seams) to isolated, reviewable tasks. This mirrors D-0012-monorepo-tooling’s own “add trees now, sequence the lift separately” stance.
- Bun-everywhere is a standing standard. D-0012-monorepo-tooling chose one JS package manager and made “no stray lockfiles” a success criterion. Admitting pumice’s pnpm lockfiles permanently would reopen exactly the dual-lockfile mess that decision closed, so the migration is not optional — only its sequencing is.
- Fat app, flat placement. Pumice pairs a Rust core with a web frontend, so
apps/pumice(flat) is the honest placement; splitting it by ecosystem would misrepresent a single deployable.
Options considered
Section titled “Options considered”Integration depth
Section titled “Integration depth”| Option | Shape | Verdict |
|---|---|---|
| Co-tenant then converge (chosen) | Vendor intact; share tooling; promote shared code later | Fast to land, defers risk, reaches full integration |
| Full integration up front | Bun migration + crate promotion + adapter seam in one push | Honest end state but a large, hard-to-review single move |
| Submodule / external reference | Keep pumice its own repo, reference it | No shared graph/CI; fails the “one monorepo” goal |
Co-tenant-then-converge is chosen: it reaches the same end state as full integration while keeping each step independently reviewable.
Frontend package manager
Section titled “Frontend package manager”| Option | Verdict |
|---|---|
| Migrate pnpm → Bun (chosen) | Satisfies D-0012-monorepo-tooling; one PM, no stray lockfiles |
| Keep pnpm as a per-app exception | Mechanically possible under system tasks, but reopens the dual-lockfile mess D-0012 closed |
Code-move mechanism
Section titled “Code-move mechanism”Whether the tree lands as a clean vendored copy (pumice history stays in
sksizer/pumice) or via git subtree (history grafted into this monorepo) is
recorded as an open question — it is a one-way door for this
repo’s history and wants explicit sign-off before the Phase-1 move.
Consequences
Section titled “Consequences”- M-0014-pumice-desktop-app-integration decomposes the implementation; this ADR ships the decision and standards only.
- Pumice’s own
justfile,lefthook, and codegen stay intact underapps/pumice; the rootjustfile/lefthook/moongraph gain pumice entries but are not replaced — consistent with D-0012-monorepo-tooling’s “moon sits alongside” stance. - CI grows a pumice-affected path that installs Tauri’s Linux system dependencies and runs the Rust + codegen-drift gates pumice already defines.
- Two Tauri apps may coexist (
apps/pumiceand the thinapps/desktopfromT-CKC1) until Notes’s convergence question resolves.
Migration / rollout
Section titled “Migration / rollout”- Land this ADR and M-0014-pumice-desktop-app-integration (this arc’s first PR).
- Phase 1 tasks: vendor to
apps/pumice+ thinmoon.yml; migrate frontend to Bun; reconcile the Rust toolchain; model codegen as a moon task; fold CI; repoint template-sync scripts. - Phase 2 tasks: promote shared crates to
packages/rust/; converge withT-CKC1and wire the S-0008-apps-consume-substrate-through-published-surfaces seam if pumice consumes the substrate.
Open questions
Section titled “Open questions”- Code-move mechanism. Clean vendored copy versus git subtree with grafted
history. Recommendation: clean copy (matches how D-0013-dashboard-app
scaffolds apps; keeps this repo’s history legible), tagging a handoff commit
in
sksizer/pumice. Confirm before the Phase-1 move. - Relationship to
T-CKC1. Does the fatapps/pumicesupersede the thinapps/desktopwrapper, or do both persist? Resolved in Phase 2, not here. - Rust version. Pumice pins
1.93.0exact; this workspace tracksstable. Relax pumice tostableor pinapps/pumicelocally — decided inT-P4LZ. - Template-sync ownership. Pumice’s upstream-template sync assumes a
repo-root CWD; under
apps/pumicethe helper scripts need repointing (T-V6JD), or the sync workflow retires.
- This is the “application decision” D-0012-monorepo-tooling defers to: how a given application builds and ships is decided in that application’s own decision. D-0013-dashboard-app is the sibling for the dashboard.
- Pumice is fat,
T-CKC1is thin. This ADR does not delete or supersedeT-CKC1; it introduces a parallel, larger app and flags the convergence question for Phase 2. - Filename follows D-0002-entity-identifier-shape:
D-0016-prefix (incrementing decision id), slugpumice-app-integration. Hand-authored against the decision body template — there is nosdlc decision createverb.