All skills
asyrafhussin avatar

/code-slop

@346603c

Detect AI-generated code patterns ("slop") in PHP/Laravel and TypeScript/React source — comment narration, generic naming, premature interfaces, defensive overdose, mock-everything tests, and the absence of human "scars". Use when reviewing AI-assisted PRs, auditing code for taste/quality (not metrics — that's technical-debt), or hardening a code-review checklist. Triggers on "review for AI slop", "find AI patterns", "check code feels human", "audit code-quality taste".

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/code-slop

This session only. Nothing lands on disk.

rulesdefensive-missing-real.md

≈1.6k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Defensive in the Wrong Places — Missing the Real Defenses

Impact: CRITICAL (Code looks safe but the actual failure modes — network, queues, races — are unprotected)

OX Security 2025: 76% of AI-assisted PRs miss timeouts on external calls. The same PRs are full of impossible null checks and generic try/catch. The pattern is: defensive in places that don't need it; not defensive in places that do.

What AI often misses (the failure modes that actually take down production):

  • No timeouts on outbound HTTP — a slow third-party API hangs your request indefinitely
  • No retry policy + jitter — a transient blip cascades into a customer-visible failure
  • No idempotency keys on payment/order creation — a network retry creates duplicate charges
  • No rate-limit checks before hitting an external API — banned by Stripe / Shopify / etc.
  • No circuit breaker — keeps hammering a known-down dependency, queue backs up
  • No locking / unique constraint — race condition double-spends inventory
  • No backpressure on workers — queue grows unbounded; OOM

These are the defenses that matter. A robust system has these; the dramatic-looking try/catch and null checks don't replace them.

Incorrect

// ❌ Defensive theatre: try/catch wraps the wrong thing; real risks unprotected

public function syncOrder(string $orderId): void
{
    try {
        // No timeout — could hang for minutes
        $response = Http::get("https://api.shopify.com/orders/{$orderId}");
        $data = $response->json();

        if ($data === null) {            // impossible — Http returns array or throws
            Log::error('Sync failed');
            return;
        }

        // No idempotency — if this retries, we create duplicates
        Order::create($data);
    } catch (Exception $e) {
        Log::error('Sync failed: ' . $e->getMessage());
    }
}
// ❌ Same pattern in TS
async function syncOrder(orderId: string): Promise<void> {
  try {
    // No timeout; no retry; no backoff
    const res = await fetch(`https://api.shopify.com/orders/${orderId}`);
    const data = await res.json();

    if (!data) {       // impossible if res.json() resolved
      console.error('Sync failed');
      return;
    }

    await db.orders.create({ data });
  } catch (e) {
    console.error(e);    // swallow
  }
}

Why it's slop:

  • The if ($data === null) is dead defence; the actual failure mode is "Shopify takes 90s to respond and our request timeout hits us first"
  • The try/catch swallows real failures but doesn't add the things that prevent them
  • Retried calls create duplicate orders (idempotency missing)
  • Looks responsible; isn't

Correct

// ✅ The real defences — timeouts, idempotency, retry with backoff, circuit-break

public function syncOrder(string $orderId): void
{
    $response = Http::timeout(10)                                  // hard timeout
        ->retry(times: 3, sleepMilliseconds: 200, when: fn ($e) =>
            $e instanceof ConnectionException || $e->getCode() === 429)
        ->get("https://api.shopify.com/orders/{$orderId}");

    $response->throw();   // throw on 4xx/5xx — let it propagate

    Order::updateOrCreate(
        ['shopify_id' => $orderId],                                // idempotency via unique key
        $response->json()
    );
}
// ✅ TS with AbortController for timeout + retry policy
async function syncOrder(orderId: string): Promise<void> {
  const data = await fetchWithRetry(
    `https://api.shopify.com/orders/${orderId}`,
    { timeoutMs: 10_000, retries: 3, retryOnStatus: [429, 502, 503, 504] },
  );

  // Idempotent upsert by external id (unique index on shopify_id)
  await db.orders.upsert({
    where: { shopify_id: orderId },
    create: data,
    update: data,
  });
}

Why it reads human:

  • The actual failure modes (slow third party, transient errors, duplicate retries) are each addressed
  • Errors NOT covered by retry propagate — observability/alerts catch them
  • The idempotency key (shopify_id) prevents duplicate orders even under retry storms
  • The "try/catch" is gone — it added no value here

The real-defences checklist

For any code that talks to the outside world, ask:

  • Timeout on every outbound call (HTTP, DB, queue, cache)?
  • Retry policy: how many, with what backoff, on which error types?
  • Idempotency: if the call is retried, will it create duplicate side effects?
  • Rate limit awareness: am I tracking my own request rate or relying on the upstream's error?
  • Circuit breaker for known-down dependencies (or at least a backoff cap)?
  • Locking / unique constraints on writes that could race?
  • Backpressure on workers reading from a queue?
  • Real errors propagated to observability instead of swallowed?

A try/catch that doesn't add any of these isn't a defense. It's theatre.

Detection

# HTTP calls without explicit timeout (Laravel Http facade)
grep -rEn 'Http::get\(|Http::post\(|Http::put\(|Http::delete\(' --include='*.php' app/ \
  | grep -v 'timeout('

# fetch() calls without AbortController / signal
grep -rEn 'await fetch\(' --include='*.ts' --include='*.tsx' --include='*.js' src/ \
  | grep -vE 'signal:|AbortController'

# create() / insert() without idempotency check (rough — manual review)
grep -rEn '(Order|Charge|Payment)::create\(' --include='*.php' app/

A repo can pass every other rule in this skill and still ship the wrong defenses. This rule is the one to take seriously on payment, checkout, and integration code paths.

Reference: Stripe — Idempotent Requests · Hystrix / Resilience4j circuit breaker patterns · OX Security study · Internal: defensive-generic-catch

Source: SKILL.md on GitHub

No alerts16d3 checks · Risk SAFE
  • Gen Agent Trust Hub16d

    The skill provides heuristics for auditing code quality in PHP and TypeScript projects and is generally safe. It contains a low-severity risk of indirect prompt injection because it processes untrusted source code and PR diffs using shell-based analysis tools without defining boundary markers or sanitization procedures.

  • Socket16d

    No alerts

  • Snyk16d

    Risk: LOW · No issues

Signed by skilld at 346603c. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 5 months ago
metadata
{
  "author": "agent-skills",
  "version": "1.0.0"
}

README badge

README badge for asyrafhussin/agent-skills/code-slop