pre-deploy
Skill106 starsChanged 7 months agoMITView on GitHub
Check a pracht build for production-only failures before deploying: missing env vars, Node-only APIs in edge bundles, missing ISG manifest or platform config, oversized bundles, and static-host gaps. Use for "pre-deploy check", "ready to ship?", "deployment checklist", "is my build production-safe".
- Reads credentials
5 other projects use a version of this.
Adopted by
Most recently taken up by
- mdigital94/vibe-shield-plugintheir own version, first seen Nov 8, 2026
- robritacca-dotcom/rift-dstheir own version, first seen Nov 1, 2026
- robritacca-dotcom/design-systemtheir own version, first seen Oct 28, 2026
- daichi-0818/claude-code-ops-kittheir own version, first seen Oct 15, 2026
- Valeriy1980/claude-skillstheir own version, first seen Oct 13, 2026
Used with
Projects with it also use security-audit.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
CloudflareSkillsecurity-audit
Security guidance and vulnerability review for codebases, APIs, services, CLI tools, libraries, and daemons. Use for security questions, focused reviews, vulnerability research, security audits, or pen tests. Run the complete workflow only for explicit codebase audit or pen-test requests, full/comprehensive/end-to-end reviews, or requested report artifacts.
Also in 3 projects that use this
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- robritacca-dotcom/rift-ds★ 9same name
- robritacca-dotcom/design-system★ 1same name
- daichi-0818/claude-code-ops-kit★ 0same name
- mdigital94/vibe-shield-plugin★ 0same name
- Valeriy1980/claude-skills★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository33 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-authSkill
- add-capabilitiesSkill
- add-contentSkill
- add-dbSkill
- add-i18nSkill
- add-imagesSkill
- add-observabilitySkill
- add-openapiSkill
- audit-a11ySkill
- audit-agent-surfaceSkill
- audit-authSkill
- audit-bundlesSkill
- audit-csrfSkill
- audit-depsSkill
- audit-headersSkill
- audit-islandsSkill
- audit-loadersSkill
- audit-redirectsSkill
- audit-secretsSkill
- audit-seoSkill
- audit-shellsSkill
- configure-isgSkill
- migrate-nextjsSkill
- pracht-debugSkill
- pracht-deploySkill
- pracht-scaffoldSkill
- pracht-test-apiSkill
- scaffold-e2eSkill
- scaffold-testsSkill
- tune-render-modeSkill
- typed-routesSkill
- upgrade-prachtSkill
The file
We split this skill into the rules it gives an agent
12 rules. 0 are common, 2 are distinctive, and 10 are very specific to this project.
0
Common
Most of the 3 edited versions we read keep these.
2
Distinctive
This author's own choices. Worth a look.
10
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Run before every production deploy.
- Very specific: line 27
Always build first — never lint a stale dist/.
- Very specific: line 54
If pracht inspect agents --json reports any WebMCP exposure, run the live browser boundary too.
- Very specific: line 80
Every env var the app reads (grep process.env.* across src/) is set in the deployment environment.
- Very specific: line 82
If the app mounts createImageHandler() from @pracht/image/node, sharp is installed and localOrigin is the same trusted public origin as …
- Very specific: line 89
If the proxy strips Vite's deploy base, nodeAdapter({ basePathStripped: true }) is set.
- Very specific: line 93
If the app registers background work with args.waitUntil(), the process manager sends SIGTERM (not SIGKILL) on stop and its grace period …
- Very specific: line 111
If the config defines named environments, the deploy step must either build with CLOUDFLARE_ENV=<name> or deploy with --env <name> from a …
- Very specific: line 128
No Node-only APIs in the server bundle: Check compatibility_flags first.
- Very specific: line 166
Every <route>.prerender-config.json sits next to a Serverless <route>.func (.vc-config.json with launcherType: "Nodejs").
- Distinctive: line 198
Security headers are not applied: Every other adapter sets the four default security headers at request time.
- Very specific: line 205
If staticAdapter({ fallback }) is configured, the host needs a rewrite of unmatched URLs to that file that does not shadow real files.
“Common” is measured: kept by at least 6 in 10 of the 3 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Pracht Pre-Deploy
Run before every production deploy. Each adapter has a different runtime contract; enforce the one that matches the build. Never deploy on the user's behalf — end at the verdict.
MCP: if pracht dev-mcp is registered, prefer its inspect_*, doctor, and verify tools to the CLI.
Step 1: Build, then detect the adapter
Always build first — never lint a stale dist/.
pracht build
pracht inspect build --jsonadapterTarget is authoritative; vite.config.ts (nodeAdapter(),
cloudflareAdapter(), vercelAdapter(), staticAdapter()) is the
cross-check. Never assume the adapter.
Step 2: Framework-wide checks
pracht doctor --json
pracht verify --json
pracht typegen --check # only if src/pracht-routes.ts or src/pracht.d.ts existsThese catch app-graph wiring problems independent of the adapter, including
defineApp({ constraints }) violations and a stale .pracht/app-graph.json
snapshot (fix that with pracht plan --write, then re-review the plan output).
Resolve every status: "error" before continuing — if pracht doctor
reports errors, stop here; the remaining checks will be noisy false
positives. Stale generated typed-route files block deployment; if the app does
not use them yet, note that typegen --check is optional.
If pracht inspect agents --json reports any WebMCP exposure, run the live
browser boundary too. Point it at the already-started preview, or let it own the
server lifecycle:
pracht verify webmcp --start "pracht preview" --jsonCI must install a pinned Chrome 150+ build and pass its executable with
--browser; the verifier never silently downloads one. The verifier drives
Chrome through playwright-core, which the app must list as a dev dependency. Treat unsupported API,
startup/registration failure, and graph drift as deployment errors. Do not add
--scenario unless the repository already supplies an explicitly safe WebMCP
eval scenario.
For a PR deploy, pracht report --base origin/main produces a markdown summary
(graph diff + verify + budgets) worth attaching.
Step 3: Adapter checklist
Node (@pracht/adapter-node)
dist/server/server.js,dist/server/.vite/client-manifest.json, and — if any route isrender: "isg"—dist/server/isg-manifest.jsonall exist.pracht preview --skip-build(ornode dist/server/server.js) boots andcurl localhost:3000returns 200.- Every env var the app reads (grep
process.env.*acrosssrc/) is set in the deployment environment. List them for the user. - If the app mounts
createImageHandler()from@pracht/image/node,sharpis installed andlocalOriginis the same trusted public origin asnodeAdapter({ canonicalOrigin }). A relative image endpoint without both is an error in every environment; loopback-looking request origins are intentionally not trusted. - Reverse proxy / TLS termination configured — out of scope here, flag for confirmation.
- If the proxy strips Vite's deploy base,
nodeAdapter({ basePathStripped: true })is set; application code should still observe the public base inrequest.url, and the proxy must own the bare-base redirect (/app→/app/). - If the app registers background work with
args.waitUntil(), the process manager sendsSIGTERM(notSIGKILL) on stop and its grace period exceedsnodeAdapter({ shutdownTimeoutMs })(default 10s); otherwise that work is cut off on every deploy. A custom server entry must callhandler.closeEventStreams()thenhandler.drain()itself. - Background work still reaching for
context.executionContext.waitUntilor a platform context should move to the portableargs.waitUntil()— the platform-specific call is undefined on every other adapter.
Cloudflare Workers (@pracht/adapter-cloudflare)
wrangler.toml/wrangler.jsoncpresent at repo root with an assets block bindingASSETS, and bindings declared for everycontext.env.*access in loaders, middleware, and API routes (grep, then cross-check).pracht buildgeneratesdist/server/wrangler.jsonfrom it withmain: "worker.js",no_bundle, theESModulerule, and the assets directory; confirm it exists after the build and thatwrangler deployprints "Using redirected Wrangler configuration".- If the config defines named environments, the deploy step must either build
with
CLOUDFLARE_ENV=<name>or deploy with--env <name>from a config that sets the keys below. Only then does a config need them:mainpoints atdist/server/worker.js— the deploy entry exporting only the default handler and Cloudflare entrypoint classes. Pointing it atdist/server/server.jsis an error: workerd validates every named export of the deploy entry and rejects the build metadata (buildTarget, manifests,resolvedApp, …) thatserver.jsexports for the prerender pass.no_bundle: trueplus anESModulerule whose globs include"**/*.js", so Wrangler uploads pracht's lazy server chunks as separate modules instead of folding them into the entry, andassets.directory: "dist/client".
compatibility_dateis set and is a date the installed workerd supports. It must not be newer than the runtime, or workerd refuses to start ("This Worker requires compatibility date X, but the newest date supported by this server binary is Y"). Never set it to today's date — that is by construction at or beyond the newest released workerd.- No Node-only APIs in the server bundle. Check
compatibility_flagsfirst. Withoutnodejs_compat,pracht buildalready fails on any Node builtin left in the bundle (nodejs_alsallowsnode:async_hooks), so grep for globals instead:process.cwd,Buffer,__dirname,__filename. Withnodejs_compat(compatibility date 2024-09-23 or later) everynode:import builds, but some modules are stubs that throw when called — flag calls intochild_process,cluster,worker_threadsand writes throughfs. Dev already runs inside workerd via@cloudflare/vite-plugin, so this is the backstop for code paths dev never hits. - An API route importing
@pracht/image/nodeis an error on Workers (its optimizer needssharp). RequirecloudflareLoaderorpassthroughLoader. - ISG uses the per-colo Workers Cache API with no extra config. If
time-revalidated routes should use the edge-tier Workers Caching upgrade,
confirm both sides:
cloudflareAdapter({ cache: true })and"cache": { "enabled": true }in wrangler config. With it enabled, flag ISG routes reachable through unbounded query strings — require a bounded allowlist or canonical redirect, or an uncached gateway with a normalizedcf.cacheKey— and check that markdown-capable routes normalizeAcceptat the gateway when variant fan-out matters. - Bundle size: measure what actually deploys — every
.jsfile underdist/server/(worker.jsalone is a few lines;no_bundleuploads the pre-built module graph).npx wrangler deploy --dry-runprints the total. The Workers limit is ~1 MB compressed on free, ~10 MB on paid. Warn at 80% of the active limit.
Vercel (@pracht/adapter-vercel)
.vercel/output/config.jsonexists withversion: 3, and.vercel/output/static/is populated.- The render function exists at
.vercel/output/functions/<functionName>.func/server.js. The name defaults torenderbut is configurable viavercelAdapter({ functionName })— read it fromvite.config.tsrather than hardcodingrender.func. - Env vars are configured in the Vercel project. This cannot be verified from
the CLI alone: run
vercel env pulland diff againstprocess.env.*uses. - Run the Cloudflare Node-only API check unconditionally. The render
function's
.vc-config.jsonis always written withruntime: "edge". Do not skip it based on a runtime probe — ISG routes run the same bundle on Node, but a Node-only API still breaks the edge function. - Every
<route>.prerender-config.jsonsits next to a Serverless<route>.func(.vc-config.jsonwithlauncherType: "Nodejs"). Vercel rejects a prerender config paired with an edge function:Unexpected function type "EdgeFunction" at path "<route>". vercelAdapter({ regions: "all" })is valid for the Edge render function, but generated Node ISG function configs must omitregionsso the project default applies; Node configs may only contain arrays of concrete region identifiers.- An API route importing
@pracht/image/nodeis an error for the Edge function. RequirevercelLoader(with aligned allowed sizes) orpassthroughLoader.
Static export (@pracht/adapter-static)
There is no server to get wrong, so this checklist is about what the host must do and what the build cannot enforce.
dist/client/exists and is the deploy root.dist/server/is build tooling and must not be uploaded — it contains the prerender bundle.- The build is the gate: it fails closed on
ssr/isgroutes, SPA loaders, non-full SPA hydration, API routes, route/not-found middleware, network-exposed capabilities, and any Vitebasethat is not/or a root-absolute path. Ifpracht buildsucceeded, those contracts already hold — do not re-derive them by hand. Report a failing build verbatim; the message names the routes. - The host serves
index.htmlfor directory URLs. Confirm the actual setting: S3 website endpoints need an index document, nginx needstry_files $uri $uri/index.html; GitHub Pages and Netlify do it by default. - The host maps
404.htmlas its error document, or unknown URLs get the host's generic error page instead of the app'snotFoundroute. Verifydist/client/404.htmlexists — if it does not, the app declares nonotFoundpage:warn. - Security headers are not applied. Every other adapter sets the four
default security headers at request time; a static host has no request
runtime.
dist/server/headers-manifest.jsonrecords what each route would have carried — mirror the ones you need into the host's own header config (_headerson Netlify, CloudFront response header policies, nginxadd_header).errorfor any app handling user input,warnotherwise. HSTS and CSP are host-side decisions either way. - If
staticAdapter({ fallback })is configured, the host needs a rewrite of unmatched URLs to that file that does not shadow real files. It makes unknown URLs answer200(soft 404s), and without it the fallback file is inert — deep links into dynamicrender: "spa"routes will 404. - Smoke test the real output, not the dev server:
pracht preview --skip-buildservesdist/client/the way a dumb host would. Check/, one dynamic SSG path, one deep link into a SPA route, and one unknown URL. - A static host cannot negotiate
Accept, so agents read an SSG page's Markdown at/<path>.md(written by the build for routes with amarkdownexport and no middleware). Check the host serves.mdastext/markdown; charset=utf-8— nginx's stockmime.typeslacks it — and read the build note naming Markdown-capable pages that got no file. - Sub-path deploys (GitHub Pages project site, S3 key prefix) need Vite
baseset to that path (base: "/my-project/"), matching the deploy path exactly — a mismatch 404s every asset. Then confirm no hand-written root-absolute internal links:grep -rn 'href="/' src/and check each hit is external, an asset underpublic/, or a<Link route>. Framework-owned URLs from@pracht/image'sdefaultLoaderand the OpenAPI companion UI/document already carry the base — do not flag their base-free route declarations — but custom image loaders and OpenAPI provider asset URLs still need to match the intended host. CDN bases (https://cdn…) and document-relative bases (""/"./") are build errors, not sub-path deploys.
Step 4: Cross-cutting
/audit-secrets— noprocess.env.*orcontext.env.*value flows into a loader return value./audit-headers—applyDefaultSecurityHeadersis in use on user-facing responses, orheaders()exports cover the same ground. On a static export this moves entirely to the host config; see above.git statusis clean. Deploying uncommitted work is a footgun.
Step 5: Report
A checklist grouped by Framework, Adapter, Cross-cutting. Tag each item
with a primary severity — error (blocks deploy), warn (proceeds but risky),
info — keeping pass/fail as the secondary per-item status. End with one line:
READY / BLOCKED (N errors) / READY WITH WARNINGS (N warnings).
For a static export, never report READY without naming the host settings the
deploy depends on — clean URLs, 404.html, security headers, and the fallback
rewrite if configured. The build cannot verify any of them, so an unqualified
READY is the one way this skill can mislead.
$ARGUMENTS
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

