@m-kopa/launchpad-cli 0.64.0 → 0.65.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -6,7 +6,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
6
6
  This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html);
7
7
  pre-1.0 minor bumps may carry breaking changes per ADR 0005.
8
8
 
9
- ## Unreleased
9
+ ## 0.65.0 - 2026-10-03
10
+
11
+ ### Added
12
+
13
+ - `validate` and `plan` print a non-blocking warning for any container manifest: the platform's runtime switch may still be `off` for the app, in which case `launchpad deploy` returns HTTP 503 `container_runtime_off` (AIO-786).
14
+ A `runtime: aca` manifest gets a second warning — that value is reserved and currently ignored. Exit codes are unchanged; see `docs/concepts/containers.md` for the runtime gate, the `lite`-only container size, and the cold-start behaviour after idle.
15
+
16
+ ### Changed
17
+
18
+ - Raise the production minimum CLI version to 0.64.0 through the governed policy workflow.
19
+ - The `launchpad-deploy`, `launchpad-status` and `launchpad-deploy-status` skills keep a refused dispatch build on dispatch (AIO-756).
20
+ They name the `hidden build output is not supported` error and its fix, and they say that the Cloudflare account allows only 100 Pages projects.
21
+
22
+ ### Fixed
23
+
24
+ - A first deploy with several hundred files no longer times out at `content_seeded` (AIO-738).
25
+ The platform now sends text files inside the git tree call, in chunks of at most 500 files or 8 MB, and checks every file's git SHA after the commit tree is built; if any SHA differs, it falls back to one blob per file.
26
+ Binary files still get their own blobs, created 8 at a time. A rate-limited blob or tree call retries, and the commit step has 10 minutes.
27
+ - `launchpad destroy` now tears down an app whose provisioning failed at stage `pending` when main has no per-app Terraform.
28
+ It refuses with `tf_pr_open` while the app's Terraform PR is open, and with `dispatch_delivery_present` when a pre-Terraform app already has a dispatch delivery.
29
+ - `launchpad deploy`'s apply-status polling (the automatic reconcile path for an access or origin change, e.g. switching `origin: dispatch` → `pages`) no longer goes quiet after a real `terraform apply` failure. Previously, once the bot recorded that failure and queued an automatic retry, every poll *before* the retry's PR opened fell through to a stale lookup and 404'd `no_apply_found` — which the CLI treats as "still settling" and retries. If the retry's regenerated Terraform happened to be byte-identical to what the failed attempt already merged (because the failed apply's file content lands on `main` even though the real infrastructure was never created), its PR carried an empty diff and never re-ran `terraform apply` at all, so the original failure could go unreported for as long as it took that empty retry to resolve (AIO-756). The bot now reports the already-known `failed` verdict on every poll in that window, so `launchpad deploy` exits non-zero with the failing stage named, instead of appearing to hang or (in the worst case) eventually reading as if nothing was wrong. Run `launchpad status <slug>` to confirm the current state after a reported failure.
10
30
 
11
31
  ## 0.64.0 - 2026-09-25
12
32
 
package/dist/cli.js CHANGED
@@ -19,7 +19,7 @@ var __toESM = (mod, isNodeMode, target) => {
19
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
20
20
 
21
21
  // src/version.ts
22
- var CLI_VERSION = "0.64.0";
22
+ var CLI_VERSION = "0.65.0";
23
23
 
24
24
  // src/config.ts
25
25
  import { readFileSync } from "node:fs";
@@ -12402,6 +12402,20 @@ function describe25(e) {
12402
12402
 
12403
12403
  // src/commands/plan.ts
12404
12404
  import { resolve as resolve12 } from "node:path";
12405
+
12406
+ // src/manifest/container-runtime-warnings.ts
12407
+ var CONTAINER_RUNTIME_GATE_WARNING = "Container apps are gated by the platform runtime switch; `launchpad deploy` may return " + "503 container_runtime_off until the platform team enables your app. See docs/concepts/containers.md.";
12408
+ var RUNTIME_ACA_RESERVED_WARNING = "deployment.runtime: aca is reserved and currently ignored — the app runs on cloudflare-containers " + "regardless. See docs/concepts/containers.md.";
12409
+ function containerRuntimeWarnings(view) {
12410
+ if (view.deploymentType !== "container")
12411
+ return [];
12412
+ const warnings = [CONTAINER_RUNTIME_GATE_WARNING];
12413
+ if (view.runtime === "aca")
12414
+ warnings.push(RUNTIME_ACA_RESERVED_WARNING);
12415
+ return warnings;
12416
+ }
12417
+
12418
+ // src/commands/plan.ts
12405
12419
  var planCommand = {
12406
12420
  name: "plan",
12407
12421
  summary: "summarise what the manifest would deploy (offline)",
@@ -12433,7 +12447,15 @@ function renderJson(result, io) {
12433
12447
  switch (result.kind) {
12434
12448
  case "ok": {
12435
12449
  const plan = derivePlan(result.manifest);
12436
- io.out(JSON.stringify({ ok: true, ...plan }));
12450
+ const warnings = containerRuntimeWarnings({
12451
+ deploymentType: plan.desired.app.deploymentType,
12452
+ runtime: plan.desired.app.runtime
12453
+ });
12454
+ io.out(JSON.stringify({
12455
+ ok: true,
12456
+ ...plan,
12457
+ ...warnings.length > 0 ? { warnings } : {}
12458
+ }));
12437
12459
  return 0;
12438
12460
  }
12439
12461
  case "file-not-found":
@@ -12543,6 +12565,12 @@ function renderPlan(m, io) {
12543
12565
  }
12544
12566
  io.out("");
12545
12567
  }
12568
+ for (const w of containerRuntimeWarnings({
12569
+ deploymentType: desired.app.deploymentType,
12570
+ runtime: desired.app.runtime
12571
+ })) {
12572
+ io.out(`⚠ ${w}`);
12573
+ }
12546
12574
  const totalBindings = desired.secrets.envFile.length + desired.secrets.platformManaged.length;
12547
12575
  io.out(`Secrets (${totalBindings})`);
12548
12576
  if (totalBindings === 0) {
@@ -16106,6 +16134,9 @@ function renderHumanOk(result, group, boundary, io) {
16106
16134
  if (!boundary.declared) {
16107
16135
  io.out(`⚠ ${DECLARE_APP_HINT}`);
16108
16136
  }
16137
+ for (const w of containerRuntimeWarnings(view)) {
16138
+ io.out(`⚠ ${w}`);
16139
+ }
16109
16140
  switch (group.kind) {
16110
16141
  case "skipped":
16111
16142
  return 0;
@@ -16172,6 +16203,7 @@ function renderHumanError(result, io) {
16172
16203
  }
16173
16204
  function renderJsonOk(result, group, boundary, io) {
16174
16205
  const view = manifestView(result.manifest);
16206
+ const runtimeWarnings = containerRuntimeWarnings(view);
16175
16207
  const base = {
16176
16208
  manifest_valid: true,
16177
16209
  runtime_supported: true,
@@ -16181,7 +16213,8 @@ function renderJsonOk(result, group, boundary, io) {
16181
16213
  declared: boundary.declared,
16182
16214
  errors: boundary.errors,
16183
16215
  warnings: boundary.warnings
16184
- }
16216
+ },
16217
+ ...runtimeWarnings.length > 0 ? { warnings: runtimeWarnings } : {}
16185
16218
  };
16186
16219
  if (boundary.errors.length > 0) {
16187
16220
  io.out(JSON.stringify({ ok: false, ...base }));
@@ -16270,7 +16303,8 @@ function manifestView(manifest) {
16270
16303
  access: manifest.spec.access,
16271
16304
  buildCommand: manifest.spec.build?.command,
16272
16305
  destinationDir: manifest.spec.build?.destination_dir,
16273
- appDeclared: manifest.spec.app !== undefined
16306
+ appDeclared: manifest.spec.app !== undefined,
16307
+ runtime: undefined
16274
16308
  };
16275
16309
  }
16276
16310
  return {
@@ -16279,7 +16313,8 @@ function manifestView(manifest) {
16279
16313
  access: manifest.access,
16280
16314
  buildCommand: manifest.build?.command,
16281
16315
  destinationDir: manifest.build?.destination_dir,
16282
- appDeclared: manifest.app !== undefined
16316
+ appDeclared: manifest.app !== undefined,
16317
+ runtime: manifest.deployment.runtime
16283
16318
  };
16284
16319
  }
16285
16320
  function parseArgs13(args) {
@@ -1 +1 @@
1
- {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAKjE,eAAO,MAAM,WAAW,EAAE,OAIzB,CAAC"}
1
+ {"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../src/commands/plan.ts"],"names":[],"mappings":"AAqCA,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAMjE,eAAO,MAAM,WAAW,EAAE,OAIzB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAwDA,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AAQjE,eAAO,MAAM,eAAe,EAAE,OAI7B,CAAC"}
1
+ {"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../../src/commands/validate.ts"],"names":[],"mappings":"AAwDA,OAAO,KAAK,EAAS,OAAO,EAAY,MAAM,kBAAkB,CAAC;AASjE,eAAO,MAAM,eAAe,EAAE,OAI7B,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const CONTAINER_RUNTIME_GATE_WARNING: string;
2
+ export declare const RUNTIME_ACA_RESERVED_WARNING: string;
3
+ /**
4
+ * Non-blocking advisories for a manifest, derived from its deployment
5
+ * type and (v1alpha1-only) runtime field. Empty for any non-container
6
+ * manifest, so callers can spread/append it without a type-specific
7
+ * branch.
8
+ */
9
+ export declare function containerRuntimeWarnings(view: {
10
+ readonly deploymentType: string;
11
+ readonly runtime: string | undefined;
12
+ }): readonly string[];
13
+ //# sourceMappingURL=container-runtime-warnings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"container-runtime-warnings.d.ts","sourceRoot":"","sources":["../../src/manifest/container-runtime-warnings.ts"],"names":[],"mappings":"AAcA,eAAO,MAAM,8BAA8B,QAE6D,CAAC;AAOzG,eAAO,MAAM,4BAA4B,QAEO,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;CACtC,GAAG,SAAS,MAAM,EAAE,CAKpB"}
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare const CLI_VERSION = "0.64.0";
1
+ export declare const CLI_VERSION = "0.65.0";
2
2
  //# sourceMappingURL=version.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@m-kopa/launchpad-cli",
3
- "version": "0.64.0",
3
+ "version": "0.65.0",
4
4
  "description": "Launchpad CLI — clone / deploy / review / merge against Launchpad-managed apps. Talks to the portal-bot endpoints (SCOPE-M-760 / T4).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -37,8 +37,8 @@
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "22.18.10",
40
- "@typescript-eslint/eslint-plugin": "8.46.4",
41
- "@typescript-eslint/parser": "8.46.4",
40
+ "@typescript-eslint/eslint-plugin": "8.71.0",
41
+ "@typescript-eslint/parser": "8.71.0",
42
42
  "@vitest/coverage-istanbul": "4.1.5",
43
43
  "eslint": "9.39.4",
44
44
  "typescript": "5.9.3",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-content-pr
3
3
  description: Push a content change to a Launchpad app via `launchpad deploy` and verify it shipped via `launchpad status`. Covers the post-first-deploy iteration loop (edit → deploy → verify) — subsequent deploys commit directly to the app repo's main and the Pages build runs asynchronously, so verification is its own step. Use when someone says "push a content change", "ship an update", "/launchpad-content-pr", "verify my deploy", or after `/launchpad-deploy` reports `done` and they want to follow up with an edit.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-deploy
3
3
  description: Walk a Launchpad user through deploying an app from their local working directory (Model A — `launchpad init` + `launchpad deploy`). Wraps the CLI verbs end-to-end: detects the app shape, scaffolds `launchpad.yaml`, resolves the allowed Entra group via `launchpad groups`, bundles the CWD via `launchpad deploy`, and watches the rollout via `launchpad status`. Use when someone says "deploy a new app", "ship my app to Launchpad", "/launchpad-deploy", "I have an app locally — get it on Launchpad", or any variant. Resume/abandon for legacy in-flight provisioning is at the bottom.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -165,6 +165,14 @@ For a React API app, `functions/` converts as-is and the dispatch build compiles
165
165
  When deploy says secrets carried over, tell the author to run `launchpad secrets push` once `launchpad status` shows the first delivery; pushing earlier fails with `dispatch_script_not_delivered`.
166
166
  Still refused on dispatch, with the reason printed: wrangler settings beyond name, compatibility date/flags, `pages_build_output_dir` and one D1 (so KV, R2, services, vars, Durable Objects, queues), a root `functions/_middleware` or root-level catch-all that would claim `/*`, a hand-written `_worker.js`, and functions that compile to more than one module.
167
167
  A function that calls `next()` to fall through to a static asset does not work on dispatch, because the Worker has no asset fallback.
168
+ A dispatch build also refuses hidden files or directories inside the build output (`destination_dir`) with `hidden build output is not supported: <path>`.
169
+ `launchpad status <slug>` shows this as `v2 delivery: state refused` followed by the reason.
170
+ The usual cause in a React API app is three files copied from the starter template: `static/.github/workflows/launchpad-review.yml`, `static/.github/scripts/parse-html-validate.mjs` and `static/public/.gitkeep`.
171
+ Fix it on dispatch: show the author the paths the error names, delete them with the author's agreement, then run `launchpad deploy` again.
172
+ Delete only the paths the error names, and never the repository-root `.github/` directory.
173
+ If `launchpad status` shows the same refusal after that deploy, the paths are outside the manifest's `app.include`, so the deploy cannot remove them; offer `/launchpad-report` instead of editing `app.include` to force it.
174
+ A refused dispatch build is not a reason to switch to Pages: do not rewrite the manifest as v1, set `origin: pages`, or pass `--origin pages` to get past it.
175
+ The Cloudflare account has a fixed limit of 100 Pages projects, so a new Pages project can fail at Terraform apply even after the CLI accepts the deploy.
168
176
  Changing `appType` to `react` would not convert the API; do not suggest it.
169
177
  Do not use this command to migrate a registered app or delete its Pages resources.
170
178
  If the pool is full or inventory cannot be read, report the capacity error; do not fall back to Pages or change authentication to bypass it.
@@ -437,6 +445,17 @@ Flags — there is nothing useful to pass on the Model A path:
437
445
  symlinks, secret-pattern hits, build-command violations) are
438
446
  rejected at upload and self-describing in the CLI's stderr; surface
439
447
  them verbatim and let the user fix and re-`launchpad deploy`.
448
+ - **Dispatch delivery refused** (`launchpad status` shows `v2 delivery:
449
+ state refused`) → surface the `failed at` reason verbatim. For
450
+ `hidden build output is not supported`, follow the fix in §A.2 and
451
+ re-`launchpad deploy` on dispatch. Do not move the app to Pages.
452
+ - **`tf_apply_failed` naming the Pages project limit** (`You have
453
+ reached the limit of projects`, Cloudflare code 8000027) → this is
454
+ a platform capacity fault, not the author's. Do not retry on Pages,
455
+ and do not create a new slug to get round it. A registered app that
456
+ was moved from dispatch to Pages needs the platform team to return
457
+ it to dispatch (`launchpad migrate` refuses registered apps), so
458
+ offer `/launchpad-report` with the slug and the error.
440
459
  - **Terminal-failed but the app is actually serving** → `launchpad
441
460
  recover <slug>`. The bot re-derives reality from live Cloudflare
442
461
  state and repairs the record to `live` only when the app is
@@ -612,6 +631,10 @@ the in-flight case.
612
631
  TF emits them as `secret_text`; trust it.
613
632
  - Do **not** re-enable `confine_origin` on a gateway `react+api` app — it
614
633
  strips the user JWT and 401s every user (see "Gateway auth").
634
+ - Do **not** switch a dispatch app to Pages (`origin: pages`, a v1
635
+ manifest or `--origin pages`) to get past a refused dispatch build.
636
+ Fix the reason the build names. New Pages projects are limited by
637
+ the account's Pages project limit; dispatch apps are not.
615
638
  - Do **not** `wrangler secret put` for a two-tier app — `launchpad secrets
616
639
  push` with `targets: all` seeds both tiers via the bot.
617
640
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-deploy-status
3
3
  description: Show the current provisioning stage + failure reason for a Launchpad app via `launchpad status` (Model A drift + deployment_verified) and `launchpad apps` (lifecycle bucket), or watch provisioning live with `launchpad watch`. Renders the M-892 stage trace for in-flight provisioning, and is the canonical home for `launchpad recover` (repair a terminal-failed app record that is actually live). Use when someone says "what's the status of demo-X", "/launchpad-deploy-status", "is my deploy stuck", "watch my deploy go live", "watch provisioning", "my app says failed but it's serving", or after `/launchpad-deploy` reports a non-`done` terminal stage.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -382,3 +382,6 @@ A missing Pages project is expected for `origin: dispatch`; do not treat it as a
382
382
  Use `launchpad status <slug> --json` to compare `deployedSha`, `headSha` and `deployment.liveDeployment.buildStatus`.
383
383
  A `live_drift_unknown` state can include a successful dispatch deployment while infrastructure drift is unavailable.
384
384
  If the CLI's first-deploy wait expires, keep checking the recorded submission instead of creating another app or claiming it is live.
385
+ A `v2 delivery: state refused` line means the dispatch build refused the commit; surface its `failed at` reason verbatim.
386
+ For `hidden build output is not supported: <path>`, the author deletes the named paths inside the build output (usually `static/.github/` and `static/public/.gitkeep` copied from the starter) and runs `launchpad deploy` again.
387
+ Do not move a refused dispatch app to Pages: the Cloudflare account has a fixed limit of 100 Pages projects, and a `tf_apply_failed` reason that names the project limit is a platform fault to report with `/launchpad-report`.
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-destroy
3
3
  description: Tear down a Launchpad app end-to-end via `launchpad destroy` — Cloudflare Pages project, edge-auth wiring (gateway KV/audience entries, or the Access app for `auth: access` apps), custom hostname, platform-repo TF, and the app repo (archive-renamed). Owner-only verb with a two-step destructive confirmation. Use when someone says "destroy this app", "/launchpad-destroy", "tear down `<slug>`", "delete the app", or asks to clean up a smoke-test / orphan / retired app.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-identity
3
3
  description: Teach an app author how to use the signed-in user's identity inside a Launchpad app — read the gateway-forwarded X-Launchpad-User-Assertion in a Pages Function, VERIFY it with @m-kopa/platform-auth (fail-closed), and show who's logged in (sub/email/name). Use when someone says "who is logged in", "show the current user", "get the user's email in my app", "auth in my launchpad app", "read the user identity", "/launchpad-identity", or is wiring up an /api/me for a gateway-fronted app.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-onboard
3
3
  description: One-time setup for the Launchpad CLI + Claude Code skill bundle. Verifies the `launchpad` CLI is installed and current, runs `launchpad whoami` to confirm the session is fresh, and checks the bundled skills are installed and in lock-step with the CLI. Idempotent — safe to re-run any time. Use when someone says "set me up for Launchpad", "I just got a new machine and want to use Launchpad", "/launchpad-onboard", or any of the other launchpad-* skills fails on a prereq check.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-report
3
3
  description: File a bug report or feature request to the Launchpad team's tracker from the CLI. Use when someone reports something broken, hits an error in a launchpad command, or wishes a feature existed — e.g. "this is broken", "report a bug", "can you file that", "I wish launchpad could…", "/launchpad-bug", "/launchpad-feature". Always confirm and show exactly what you'll send before filing; never file silently.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: launchpad-status
3
3
  description: Safely refresh a Launchpad clone, show manifest drift, or read the deployed manifest. Wraps bare `launchpad pull` for source sync, `launchpad pull --manifest` for deployed YAML, and `launchpad status` for drift. Use for "sync my clone", "what's deployed", "show drift", "/launchpad-status", "/launchpad-pull", or post-deploy verification.
4
- version: 0.64.0
4
+ version: 0.65.0
5
5
  ---
6
6
 
7
7
  <!-- BEGIN shell-contract (managed by scripts/sync-skill-contract.sh — edit skills/_partials/shell-contract.md) -->
@@ -388,3 +388,6 @@ A missing Pages project is expected for `origin: dispatch`; do not treat it as a
388
388
  Use `launchpad status <slug> --json` to compare `deployedSha`, `headSha` and `deployment.liveDeployment.buildStatus`.
389
389
  A `live_drift_unknown` state can include a successful dispatch deployment while infrastructure drift is unavailable.
390
390
  If the CLI's first-deploy wait expires, keep checking the recorded submission instead of creating another app or claiming it is live.
391
+ A `v2 delivery: state refused` line means the dispatch build refused the commit; surface its `failed at` reason verbatim.
392
+ For `hidden build output is not supported: <path>`, the author deletes the named paths inside the build output (usually `static/.github/` and `static/public/.gitkeep` copied from the starter) and runs `launchpad deploy` again.
393
+ Do not move a refused dispatch app to Pages: the Cloudflare account has a fixed limit of 100 Pages projects, and a `tf_apply_failed` reason that names the project limit is a platform fault to report with `/launchpad-report`.