Skip to content

create-devstack-app: rebuild around a minimal, survives-into-production template#27

Open
hayes-mysten wants to merge 6 commits into
mainfrom
mh/create-devstack-app-rebuild
Open

create-devstack-app: rebuild around a minimal, survives-into-production template#27
hayes-mysten wants to merge 6 commits into
mainfrom
mh/create-devstack-app-rebuild

Conversation

@hayes-mysten

@hayes-mysten hayes-mysten commented Jun 12, 2026

Copy link
Copy Markdown
Collaborator

Summary

pnpm create @mysten-incubation/devstack-app currently scaffolds 44 files / ~2,000 lines, most of which a real app deletes on day one: four demo panels, per-service helper libs, a demo vault Move package, Playwright specs, and an app-local plugin framework (src/devstack/contribution.ts + two generated barrels) that exists only so the scaffolder can compose by deleting files. This PR rebuilds the tool around one rule: every generated file survives into a real app — it gets edited, never deleted.

  • Two verbatim templatestemplates/app (React dapp: vite + dapp-kit + dev wallet, ~19 files) and templates/ts (headless, ~10 files). One counter Move package, one screen/module driving it through the generated bindings, one vitest spec that runs against the live stack. Demos live in examples/.
  • One rendered filedevstack.config.ts is generated from the service selection as straight-line defineDevstack({ members }), exactly like the documented API. No barrels, no contribution framework.
  • Services are config lines, not file sets — walrus/seal selection adds a factory line + the SDK dep, nothing else. DeepBook is no longer offered: devstack stopped auto-synthesizing local DeepBook (it needs vendored Move packages + explicit pool config), so the generated README points at examples/deepbook-trader instead.
  • No token surgery — generated apps carry no DEVSTACK_APP= script tokens and no pinned stackName (both inferred from package.json#name); the only package.json mutations are name, service-dep pruning, and SDK version injection.
  • CLI rebuilt on @clack/prompts (Ink/React dropped): --template app|ts, --services walrus,seal, --all, --minimal, --yes (default flipped to no services), --target-dir, --no-install, --no-git, plus a non-fatal Docker preflight.
  • The compose-down machinery (PLUGIN_MANIFEST, compose.ts, dangling-import scanner, Ink picker) is deleted. Net −2,641 lines.

devstack collateral fix

The end-to-end smoke surfaced a real substrate bug: the CLI resolves the stack name as flag > env > package.json name > 'main', but the build-integrations discovery ladder skipped the package-inference rung — so pnpm test in a bare app looked for stacks/main/ while the supervisor wrote stacks/<name>/ (VitestManifestNotFoundError). The shared ladder now package-infers (opt-in cwd rung, threaded through discover/vitest/playwright/vite call sites), and a latent two-module import cycle (TDZ hazard) between resolve-discovery-env and inference-network is broken. Separate changeset, patch bump.

Two more standalone-consumer bugs fixed in the templates: @mysten/signers added as a dep (dev-wallet's /adapters entry eagerly requires its optional peer; inside the monorepo it resolves transitively, outside it the dev-wallet virtual module dies at eval and the wallet silently never registers), and the test liveness probe treats router 404/5xx as "stack down" (the long-lived router answers routed hostnames even when the stack is dead).

CI

New create-devstack-app.yml scaffolds app×{minimal,all} + ts×minimal, installs against packed workspace tarballs (same mechanism as smoke:pack-consumer), and runs each app's pnpm typecheck (= devstack apply + tsc). devstack-e2e.yml gains a boot smoke: scaffold → devstack up → assert bindings → run the app's vitest spec live → teardown. The old template was workspace-excluded and never CI-verified.

Verification (local, Docker)

Both templates, full path: scaffold → install (tarball overrides) → pnpm typecheck (boots chain, publishes counter, generates src/generated/, tsc green) → pnpm devbrowser walkthrough on the routed origin: dev wallet auto-connects, counter created and incremented 0→1 on-chainpnpm test green against the live stack → stack-down run shows the friendly "start the stack with pnpm dev" error. Repo-wide turbo lint/build/test + oxlint green.

Notes for review

  • The pending unreleased changeset describing the demo-panels/picker rebuild was replaced (those panels now never ship; the new changeset describes the net result).
  • Open design questions flagged, not decided here: stack-name inference from package.json yields dev.<name>.<name>.localhost doubled hostnames; cold-start conventional URLs still assume stack 'main'; sui-rpc/rpc endpoint-name aliasing gap; devstackVitestTestConfig({testSetup}) is a silent no-op.

🤖 Generated with Claude Code


AI Assistance Notice

Please disclose the usage of AI. This is primarily to help inform reviewers of how careful they need to review PRs, and to keep track of AI usage across our team. Please fill this out accurately, and do not modify the content or heading for this section!

  • This PR was primarily written by AI.
  • I used AI for docs / tests, but manually wrote the source code.
  • I used AI to understand the problem space / repository.
  • I did not use AI for this PR.

…on template

Replace the 44-file demo-panel gallery (and the app-local plugin framework +
compose-down machinery that existed to serve it) with two small verbatim
templates — `app` (React dapp) and `ts` (headless) — plus ONE rendered file,
devstack.config.ts, generated from the service selection. Every scaffolded
file is meant to be edited and kept, never deleted; rich demos live in
examples/ instead.

Scaffolder: @clack/prompts CLI (Ink/React dropped), --template app|ts,
walrus/seal multiselect (one factory line + SDK dep each; DeepBook dropped —
devstack no longer auto-synthesizes it, README points at
examples/deepbook-trader), --yes default flips to no services. Generated apps
carry no DEVSTACK_APP tokens and no pinned stackName (both inferred from
package.json#name). Templates carry @mysten/signers: dev-wallet's /adapters
entry eagerly requires its optional peer, which only resolves transitively
inside the monorepo — without it the vite dev-wallet virtual module dies at
eval and the wallet silently never registers.

devstack: align the build-integrations discovery ladder with the CLI — the
default stack name now package-infers from the nearest package.json (explicit
> $DEVSTACK_STACK > package name > 'main'), so `pnpm test` finds a bare app's
live stack instead of failing with VitestManifestNotFoundError for 'main'.
Threaded through discover/vitest/playwright/vite call sites; also breaks a
latent two-module import cycle (TDZ) between resolve-discovery-env and
inference-network.

CI: new workflow scaffolds app×{minimal,all} + ts×minimal, installs against
packed workspace tarballs, and runs each app's typecheck (devstack apply +
tsc); devstack-e2e gains a boot smoke that runs `devstack up` on a scaffolded
app and executes its vitest spec against the live stack. The old template was
workspace-excluded and never CI-verified.

Verified locally end-to-end for both templates: scaffold → install →
typecheck → pnpm dev → browser counter create/increment via the dev wallet →
pnpm test green against the live stack, plus the stack-down friendly-error
path (the long-lived router answers dead routes with 404, so the probe now
treats router error statuses as "stack down").

Supersedes the unreleased demo-panels/picker changeset (folded into the new
one).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jun 12, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sui-dev-wallet Ready Ready Preview, Comment Jun 16, 2026 11:50pm
sui-ts-sdks-incubation Ready Ready Preview, Comment Jun 16, 2026 11:50pm

Request Review

@pkg-pr-new

pkg-pr-new Bot commented Jun 12, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@mysten-incubation/create-devstack-app@27
npm i https://pkg.pr.new/@mysten-incubation/dev-wallet@27
npm i https://pkg.pr.new/@mysten-incubation/devstack@27
npm i https://pkg.pr.new/@mysten-incubation/tsconfig@27

commit: 1cf94bf

Published 2026-06-11, it fails `pnpm audit --prod --audit-level high` on every
branch including main. The vulnerable path (fumadocs-mdx > esbuild 0.27.4) only
matters under Deno's loader; here esbuild runs under Node. The real fix is
double-blocked: fumadocs-mdx pins esbuild ^0.27.2 and the patched 0.28.1 is
younger than the repo's 2-day minimumReleaseAge gate. Dated revisit note in
the comment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The boot wait polled .devstack/stacks/main/manifest.json, but a bare app's
stack is named after the package (stacks/smoke-boot/) via package.json
inference — the stack booted in ~3.5min and the step then timed out for 16.
Wait on the `codegen.emitted` log line instead: name-blind, and it lands
after the manifest flush, guaranteeing src/generated/ is fresh for the
assert + pnpm test steps.

Also stop uploading the whole .devstack/ tree on failure — package cache
keys contain `::`, which upload-artifact rejects; keep just the manifests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@hayes-mysten

Copy link
Copy Markdown
Collaborator Author

@clud-bot review

…-tag composition

New stack option passed through to @mysten/codegen's
generateFromPackageSummary. Off (default), structs whose type parameters
are all phantom render as plain consts with the phantoms erased from the
BCS name — unusable as concrete type tags. On, they render as factories
whose phantom parameters are REQUIRED BcsType arguments, and the returned
class's .name is the fully-qualified, type-level-tracked tag — so tags
compose by nesting (`Receipt(Vault(USDC)).name`) instead of being spelled
by hand.

Threaded as a static per-stack option: substrate options →
resolveProductionCodegenOptions → layerMystenMoveCodegen(options) factory →
generateFromPackageSummary. Default unchanged.

Docs: new "Phantom type parameters and type tags" section in
devstack/features/codegen.mdx covering the setting and showing type
arguments in both forms — hand-written strings (nested tags included) and
composed generated BCS classes via .name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

@clud-bot clud-bot Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: APPROVE ✅

Reviewed via deep multi-lens analysis (correctness, concurrency, security, docs) with the repo on disk. Installed deps, built @mysten-incubation/devstack, and ran the touched suites.

Tests: create-devstack-app 30/30 pass. PR-touched devstack build-integration suites (discover, vitest/env, vitest/setup, vitest/stack-context, playwright/stack-context) 85/85 pass. The only failures were environmental (missing dist before build, missing sui/docker toolchain) — no PR regressions.

Key claims verified:

  • Import-cycle break (TDZ): inference-network.ts imports nothing back from resolve-discovery-env.ts — one-way diamond, no TDZ under any import order.
  • Stack-name ladder: the cwd rung is threaded into resolveDiscoveryEnv at every manifest-locating call site (discover.ts, vite/index.ts, vitest/stack-context.ts, playwright/stack-context.ts), so discovery agrees with devstack up on the package-inferred stack. cold-start-url.ts deliberately omits the package rung and global-setup.ts omits cwd — both intentional and tested.
  • render-config.ts: all 8 {app,ts} × {∅,walrus,seal,walrus+seal} combinations emit declaration-ordered symbols with exactly-matching imports — no dead/missing imports, no undeclared references.
  • Security: NAME_RE gates the name before any spawn; all spawn/spawnSync use arg arrays with no shell:true; only closed enums reach the rendered config — no injection path.

Findings (minor only)

  1. examples/README.md (~L37-42): the "Adding An Example" section still points to the deleted packages/create-devstack-app/template/ dir and describes removed features (core/walrus/seal/deepbook panels, the e2e spec, DEVSTACK_APP token rewriting). Not in the diff and nothing breaks, but it now misleads anyone following it — worth updating to the new two-template / inferred-identity design.
  2. src/bin.ts — flag-value parsing can swallow a following flag token; pre-existing, not a regression (inline note left).

No critical or major issues. The substrate inference-ladder fix, the cycle break, the CLI rewrite, and the rendered-config logic are all correct and well-covered.

}
} else if (arg !== undefined && arg.startsWith('--plugins=')) {
pluginsFlag = arg.slice('--plugins='.length);
return argv[i];

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor (not a regression): valueOf lets a value-taking flag swallow a following flag token — --target-dir --yes my-app sets targetDir = '--yes'. This is standard getopt behavior and identical to the pre-PR implementation, so nothing breaks for normal invocations. A argv[i]?.startsWith('--') guard would harden it if you care.

…ct + scaffolder

Split the conflated `chain` concept into three precise ones across devstack +
dev-wallet: `network` (the network name), `chainId` (the genesis-transaction
digest, unique per spun-up network), and the wallet-standard `sui:<network>`
chain name (derived only at the dev-wallet boundary — `sui:` never appears in
devstack internals). Deletes NETWORK_ALIASES, drops the `local` shorthand,
canonicalizes the codegen active-network key (`local` → `localnet`), and fixes
a latent deepbook DEEP-funding gate that compared a genesis digest against the
`'sui:testnet'` literal. BREAKING: invalidates on-disk local state keyed by the
old `sui:local` brand — run `devstack wipe`.

- devstack vite plugin: `optimizeDeps.include` + Lit `resolve.dedupe` fixes the
  injected dev wallet's `Illegal constructor` / unusable-reconnect failure.
- dev-wallet: injected wallet bundles a WebCryptoSignerAdapter so users can
  create their own (IndexedDB-persisted) accounts; the inject + DevWalletConfig
  API take `network` instead of `chain`; exposes a single localnet network.
- dashboard: `print-schema` now formats via prettier (schema.graphql is
  byte-stable, no more generated-vs-formatted flutter); GraphQL surfaces
  `chainId` (sui) / `network` (deepbook).
- create-devstack-app: templates approve native build scripts via both
  `pnpm.onlyBuiltDependencies` (pnpm 10) and `allowBuilds` (pnpm 11) — fixes
  stock pnpm-11 `ERR_PNPM_IGNORED_BUILDS` on `pnpm install` / `pnpm dev`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bumps the dapp-kit / vite / typescript toolchain (catalog -> dapp-kit-core
1.6, dapp-kit-react 2.x, vite 8) and resolves the fallout:

- Wallet connect: dapp-kit 1.6 rewrote auto-connect into a two-phase
  "reconnecting" state machine, which broke the dev wallet's
  localStorage-seeding hack. The dev wallet no longer pre-connects or seeds
  dApp Kit storage; a page loads disconnected and autoConnect only restores a
  genuine prior session. A new DEV-only devstack `/dapp-kit` entry
  (registerDAppKitForTesting) publishes the `connectAs` slot, which drives a
  real connection through dApp Kit's public connectWallet/switchAccount
  (resolving accounts by label). The dev wallet auto-approves
  standard:connect only under autoApprove (the headless-e2e signal).

- deepbook-trader: read networkEndpoints.chainId (codegen emits chainId, not
  chain) so the app renders instead of crashing on shortId(undefined).

- dev-wallet demo: add src/vite-env.d.ts (fixes TS2882 on the CSS
  side-effect import under the upgraded TypeScript).

- Docs: document the new /dapp-kit export (README + CHANGELOG) + changeset.

Green: lint, typecheck, 1915 unit tests, devstack package e2e (19/19, incl.
the snapshot-restore matrix and per-app cold-boot suites), and all four
example Playwright e2e suites.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant