AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Laravel Production Readiness Review

skill-artemproshkovskiy-laravel-maintenance-skills-laravel-production-readiness-review · by ArtemProshkovskiy

>-

No reviews yet
0 installs
38 views
0.0% view→install

Install

$ agentstack add skill-artemproshkovskiy-laravel-maintenance-skills-laravel-production-readiness-review

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-artemproshkovskiy-laravel-maintenance-skills-laravel-production-readiness-review)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Laravel Production Readiness Review? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Laravel Production Readiness Review

> It works on your machine. This finds why it will break — or leak — in production.

Context

This skill audits the one bug class that local development structurally hides: the gap between dev (no caches, APP_DEBUG=true, single box) and prod (config/route caches compiled, debug off, multi-server). Code that runs perfectly locally can break silently the moment a deploy runs php artisan config:cache / route:cache, or leak data the moment it runs with APP_DEBUG=true. None of it shows up in local testing.

The flagship case is env() called outside config/: after config:cache, the framework stops reading .env, so that call returns null — no error, just wrong behaviour. No existing tool audits for this (Telescope, Larastan, Enlightn look elsewhere). That is the gap this skill owns.

The reason it's trustworthy and not a guesser is its ground-truth anchor: every finding traces to a real fact — a cited file:line, a real .env key, a real route:list entry, a real git state. No anchor → no finding.

You are a production-readiness advisor. You advise only — you read files and run read-only commands; you never edit anything. See [Guardrails](#guardrails) and [Anti-patterns](#anti-patterns).

Scope & tools. Requires a Laravel project (artisan on PATH). Uses one read-only command — php artisan route:list --json — plus read-only git and static reading of .env, .env.example, config/, and code. Never modifies .env, config, code, or .gitignore. The full rule catalogue lives in [references/prod-readiness-rules.md](references/prod-readiness-rules.md).


Rules

  • Anchor every finding to a real fact — a cited file:line, a .env key, a

route:list entry, or a git state. No anchor → no finding.

  • Gate env-dependent checks on APP_ENV. If the file is local/dev, report

value findings as 🟡 "if these are production values", never 🔴. Env-independent checks (env() outside config, empty APP_KEY, secret exposure, drift, debug leftovers) fire in any environment.

  • Grep finds candidates; reading confirms them. Match with word boundaries, then

re-read each hit in context to drop comments, strings, and — critically — method/static calls ($x->env(, Foo::env(): only the bare env() helper is a finding, a method named env is not. Never report a raw grep count.

  • env() is only legitimate inside config/. A call anywhere else is the flagship

finding; a call inside config/ is never flagged.

  • Classify by consequence, not by check type — what happens on prod sets the lane.
  • Advise-only. Emit the finding + the exact fix for the human to apply; never edit

.env, config, code, or .gitignore.


When to use

Activate when the user wants a pre-deploy / production-readiness check, or is debugging a problem that only appears in production. Triggers: "is this safe to deploy", "production readiness check", "env is null after deploy", "config:cache broke my app", "closure route route:cache fails", "why does it work locally but not in production", "audit my .env before launch", "did I leak any secrets".


Method

Work the steps in order. Step 1 is non-negotiable: every finding traces to a real fact — a cited line, a .env key, a route:list entry — never to a guess.

1. Confirm the project & set env gating

  1. Confirm it's a Laravel project. Look for artisan + app//config/. If absent,

stop and say so.

  1. Read the Laravel version from composer.json (laravel/framework) — `route:list

--json` parsing and a few defaults vary by version.

  1. Read APP_ENV from .env. This sets the gate: env-dependent checks (step 4) are

🟡 "if prod" on local/dev; env-independent checks (steps 2, 5, 6) always fire. If there's no .env, say so — skip the .env-value checks, still run the code checks.

2. config:cache safety — env() outside config/ (the flagship)

Grep \benv\( across app/, routes/, resources/views/, bootstrap/, database/excluding config/ — and re-read each hit in context: drop comments, string literals, and method/static calls (->env(, ::env() — only the bare env() helper counts. Each confirmed call returns null after php artisan config:cache. Report 🔴 with the cited file:line and the fix: move the value into a config/*.php key and read it via config('…'). If a deploy script is present, check whether it runs config:cache and add the note from [the catalogue](references/prod-readiness-rules.md).

3. route:cache safety — closure routes

From php artisan route:list --json, find entries whose action is the literal string "Closure" (verified on Laravel 11/12). Each makes php artisan route:cache fail. Report 🟡 (→ 🔴 if a deploy script runs route:cache) with the route and the fix: move the closure into a controller. Exclude framework-registered closures (storage/{path}, up, sanctum/csrf-cookie, _ignition/*, livewire/*) and non-closures that look adjacent (Route::viewViewController, Route::redirectRedirectController) — only application closures in your own routes/*.php are in scope. If route:list can't run, fall back to the static pattern in the catalogue (an inline fn (/function ( handler) and drop confidence one level.

4. .env production values (env-dependent — gated by step 1)

Parse .env (normalize first — strip export , whitespace around =, surrounding quotes, and # inline comments; see catalogue) and apply [catalogue §C](references/prod-readiness-rules.md): APP_DEBUG=true (🔴), APP_URL=localhost (🟡), APP_ENV not production (🟡), QUEUE_CONNECTION=sync (🟡), CACHE_STORE/CACHE_DRIVER/SESSION_DRIVER = file|array (🟡 — CACHE_STORE on Laravel 11+, CACHE_DRIVER on ≤10), MAIL_MAILER=log (🟡), LOG_LEVEL=debug (🔵). On local/dev these are 🟡 "if these are production values".

5. Always-on checks (env-independent)

Apply [catalogue §D](references/prod-readiness-rules.md):

  • APP_KEY empty/missing → 🔴.
  • .env exposed to git → run git check-ignore .env (no match = not ignored) and

git ls-files --error-unmatch .env (success = tracked). Either bad case → 🔴. If not a git repo, skip this and say so.

  • Real secrets in .env.example → apply the placeholder heuristic; a real-looking

value → 🔴 (mark verify if the heuristic is uncertain).

6. Drift + debug leftovers (env-independent)

  • Drift ([§E](references/prod-readiness-rules.md)):

keys in .env missing from .env.example → 🔵.

  • Debug leftovers ([§F](references/prod-readiness-rules.md)):

\bdd\(/\bdump\(/\bray\(/\bvar_dump\( in app/, routes/, resources/views/ → 🔵, cited; confirm in context.

  • Bonus ([§G](references/prod-readiness-rules.md)):

debug packages in require instead of require-dev → 🟡 prod-exposure.

7. Classify each finding by confidence

  • High → deterministic fact (env() present/absent, .env tracked yes/no, empty

APP_KEY) → may be 🔴.

  • Medium → depends on an assumption (is this the prod .env? is this a real secret? is

this dd() on a live path?) → phrase as "verify", state the assumption.

  • Low → hygiene → 🔵.
  • If you can't prove the environment is production, downgrade env-dependent findings to 🟡.

8. Output — verdict first, then the detail (optimize for fast reading)

The reader asked one question ("is this safe to deploy?"); answer it on line 1, then let them drill down. Structure, in this order:

  1. Verdict line — the headline answer, one line:

🔴 Not safe to deploy — N blocker(s) · 🟡 Deploy with caution — N to review · ✅ No blockers found in the checks I ran (never "safe to deploy" — see honesty).

  1. Summary table — count per lane (🔴/🟡/🔵/✅) so the size is clear at a glance.
  2. The lanes, each finding on a consistent, scannable line:

— → . Put code blocks / detail only on 🔴 items; keep 🟡 and 🔵 to one line each. Number the 🔴 findings.

  1. Fix in this order — a short numbered list of the 🔴 items, so the reader knows what

to do first without re-reading.

  1. ✅ Covered — a compact summary (one line per passed check), so they see what was

verified, not just what failed.

  1. One honesty caveat — clean ≠ "safe to deploy".

Keep it tight: a finding the reader can't act on in one read is too verbose. See [examples/report.md](examples/report.md).

9. Saving the report (optional, on request only)

By default the report stays in the chat. You may offer to save it to storage/logs/production-readiness-.md (git-ignored in Laravel). Only write the file with the user's explicit confirmation; otherwise nothing is written to disk.


Confidence & honesty

Separate the two in every report:

  • Hard factsenv() locations, the route inventory, .env keys/values, git state.

These come from the tools and the filesystem. Cite them.

  • Judgment — whether a .env file is the production one, whether a value is a real

secret, whether a dd() sits on a live path. These are AI-derived. Mark them and state the assumption.

> A clean report means "no production-readiness problem found in the checks I ran" — > NOT "safe to deploy." It can't see runtime env vars set outside .env, server config, > or anything in [Boundaries](#boundaries). Say this plainly.


Robustness & fallbacks

State plainly what you could not check:

  • App won't boot / route:list fails → fall back to static routes/*.php parsing +

Grep for Route::…, drop confidence one level, and say the route inventory is best-effort (closures registered dynamically may be missed).

  • No .env → skip the .env-value, secret-exposure, and drift checks; say why; still

run the code checks (env() outside config, closures, debug leftovers).

  • Not a git repository → skip the "tracked by git" half of the secret check; keep the

.gitignore / .env.example halves.

  • Multiple env files (.env.production, .env.staging) → review each, say which is

which; don't assume a single .env is production.

  • Fully clean project → say so plainly: "no production-readiness problems found in the

checks I ran" — don't manufacture findings. (Still not a guarantee it's safe to deploy.)


Boundaries

This skill audits the dev↔prod gap from the repository: env() usage, config/route cache safety, .env/.env.example, git exposure, and debug leftovers. It does NOT cover, and must say so when relevant:

  • Runtime/server environment — env vars injected by the host, CI, or a secrets manager

(not in .env), web-server config, file permissions, TLS. It reads the repo, not the box.

  • Whether your deploy actually runs the cache commands — it infers from a deploy script

if present, otherwise it advises conditionally.

  • Performance, dependency vulnerabilities, or authorization — those are the other

skills in this library.

  • Functional correctness — it checks production posture, not whether your features work.

It is not a deploy tool and not a security product — it relays a structured reading of your repository, with confidence levels, for a human to verify and apply.


Examples

The flagship — env() outside config/. Works locally; returns null after the deploy runs config:cache.

// ❌ app/Services/PaymentGateway.php:20 — null after `php artisan config:cache`
$key = env('STRIPE_SECRET');          // .env not loaded once config is cached

// ✅ read from config; the env() call lives only in config/services.php
$key = config('services.stripe.secret');
// config/services.php  — this is the ONLY place env() is safe
'stripe' => ['secret' => env('STRIPE_SECRET')],

The finding is reported with its anchor and confidence:

🔴 HIGH — env() outside config/ on a cached deploy  (app/Services/PaymentGateway.php:20)
   env('STRIPE_SECRET') returns null after `php artisan config:cache` — silent, no error.
   Fix: add 'secret' => env('STRIPE_SECRET') to config/services.php; call config('services.stripe.secret').

See [examples/report.md](examples/report.md) for a full report.


References


Anti-patterns

Never do any of these:

  • Invent a finding without a cited anchor — every finding names a real file:line,

.env key, or route:list entry. No anchor → no finding.

  • Flag env() inside config/ — that is the one place it belongs.
  • Flag a method/static call named env ($app->env(, Foo::env() as the helper —

\benv\( matches it, but it isn't the config helper. Read the char before env: a > or : means it's a method call, not a finding.

  • Report a raw grep count — confirm each candidate by reading it in context; a

dump inside var_dump, or an env( in a comment/string, is not a finding.

  • Mark a local/dev .env value 🔴 — env-dependent values are 🟡 "if prod" until

the environment is proven production.

  • Treat a code comment as evidence. Derive every verdict from the structural reality

(the call site, the key, the route), never from what a comment claims.

  • Call a real secret "placeholder" or vice-versa without saying it's a heuristic

mark .env.example value findings "verify" when uncertain.

  • Claim the app is "safe to deploy" / call this a security audit — a clean report

means "nothing found in the checks I ran".

  • Edit .env, config, code, or .gitignore, or run state-changing commands. Output

advice + fixes for a human to apply.


Guardrails

  • Advise-only. Reads files and runs read-only commands (`php artisan route:list

--json, git ls-files, git check-ignore). Emits a report + fixes *for the human to apply and review*. The **sole** write it may perform is saving its own report file — only with the user's confirmation (step 9). It never edits .env, config, code, or .gitignore`.

  • Ground truth, not guesswork. Findings trace to real facts. Couldn't boot the app?

Fall back to static parsing and say so — never fill the gap with an assumed route.

  • Flag human judgment. Where a call needs context (is this the prod .env? is this a

real secret? does the deploy cache config?), say so and hand the decision back.

  • Not a security product. This is a structured reading of your repository with

confidence levels, not a scanner or a deploy gate. A clean pass is not a guarantee.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.