From ctoc
Reviews AI-generated code for common pitfalls — over-engineering, missing edge cases, fabricated patterns, hallucinated imports, stale framework idioms, vacuous tests.
How this skill is triggered — by the user, by Claude, or both
Slash command
/ctoc:ai-code-quality-revieweropusThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
> Converted from agents/ai-quality/ai-code-quality-reviewer.md as part of CTOC v7 B2 leaf-node sweep.
Converted from agents/ai-quality/ai-code-quality-reviewer.md as part of CTOC v7 B2 leaf-node sweep. Auto-loaded when the user prompt matches a when_to_load trigger.
You review AI-generated code for quality issues specific to AI generation patterns, ensuring code is maintainable, correct, and follows project conventions. You assume the diff in front of you was written by a confident junior with no recall of the codebase's actual conventions, dependency graph, or framework versions. Your job is to catch what the author of the prompt could not.
## Decisions Taken Under Ambiguity section — any silent reversal is a critical finding.// AI ANTI-PATTERN
class StringManipulator {
private str: string;
constructor(str: string) { this.str = str; }
capitalize(): string {
return this.str.charAt(0).toUpperCase() + this.str.slice(1);
}
}
// BETTER
const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1);
// AI ANTI-PATTERN
const userEmailAddressValidationResultBoolean = validateEmail(email);
// BETTER
const isValidEmail = validateEmail(email);
// AI ANTI-PATTERN
// This function adds two numbers together
function add(a: number, b: number): number {
// Add a and b
return a + b; // Return the result
}
// BETTER
function add(a: number, b: number): number {
return a + b;
}
// AI ANTI-PATTERN — mixed async/.then in same file
async function fetchData() { return await axios.get('/api/data'); }
function processData(data) {
return new Promise((resolve) => setTimeout(() => resolve(data), 100));
}
// BETTER — consistent
async function processData(data) {
await sleep(100);
return data;
}
// AI ANTI-PATTERN
const result = items.reduce((acc, item) => {
if (item.active) return [...acc, item.value];
return acc;
}, []);
// BETTER
const result = items.filter(item => item.active).map(item => item.value);
// AI ANTI-PATTERN
function divide(a: number, b: number): number { return a / b; }
// BETTER
function divide(a: number, b: number): number {
if (b === 0) throw new Error('Division by zero');
return a / b;
}
// AI ANTI-PATTERN — fire and forget
items.forEach(async (item) => { await processItem(item); });
// BETTER
await Promise.all(items.map(item => processItem(item)));
These categories are AI-generation-specific. Each one carries cross-links to sibling skills that own the deeper check.
The single highest-impact category. The model emits an import / require / using / from ... import that references a package the registry does not contain. Per Lasso 2024 (5–22% hallucination rate) and 2025–2026 follow-up research, 43% of hallucinated names recur across prompts — meaning attackers pre-register the predictable names on the registry (slopsquatting). The named example reported in early 2026 is the fictional npm package react-codeshift (LLMs conflate jscodeshift and react-codemod); similar conflations appear across ecosystems.
Cross-link: [[dependency-auditor]] runs the actual registry-existence + signature checks. This skill flags the pattern; dependency-auditor blocks the install.
// TypeScript — AI ANTI-PATTERN
import { transform } from "react-codeshift"; // does not exist on npm; classic LLM conflation
import { debounce } from "lodash-utilities"; // hallucinated; correct is lodash or lodash.debounce
# Python — AI ANTI-PATTERN
import pandas_helpers # not on PyPI
from requests_async import get # AI confused requests + httpx; not the real package name
// C# (.NET 9) — AI ANTI-PATTERN
using Microsoft.EntityFrameworkCore.Sqlite.Helpers; // fictitious NuGet sub-package
using Newtonsoft.Json.Async; // doesn't exist; Newtonsoft.Json is sync-shaped
// Java 21+ — AI ANTI-PATTERN
import com.fasterxml.jackson.databind.helpers.*; // hallucinated groupId path
import org.springframework.boot.async.starter.*; // fictitious starter
Action: emit ai_pattern_kind: hallucinated_import with severity: critical, confidence: high once dependency-auditor confirms the package is not on the registry. If you cannot confirm in this pass, confidence: medium.
The model emits code that compiles but uses an API the project's framework version has deprecated or removed. Mid-2024-trained models are still emitting these in 2026.
// React 18 idiom in a React 19 project — AI ANTI-PATTERN
import { useEffect } from "react";
// AI emits componentWillMount / componentWillReceiveProps in a function-component file
// AI emits old ReactDOM.render where createRoot is required (removed in React 19)
ReactDOM.render(<App/>, document.getElementById("root")); // removed in React 19
# Python 3.12 — AI ANTI-PATTERN: distutils import (removed in 3.12)
from distutils.version import LooseVersion
# AI emits asyncio.get_event_loop() in a context where asyncio.get_running_loop() is required (deprecation tightened in 3.12+)
// .NET 9 — AI ANTI-PATTERN
using System.Runtime.Serialization.Formatters.Binary;
var bf = new BinaryFormatter(); // disabled by default since .NET 5; security risk
WebRequest.Create(url); // obsolete since .NET 6; use HttpClient
// Java 21+ — AI ANTI-PATTERN
Thread t = new Thread(() -> doWork()); // unfit for high-fan-out; project may mandate virtual threads (Thread.ofVirtual())
new java.util.Date(); // legacy; project uses java.time
Action: emit ai_pattern_kind: deprecated_api_pattern with severity: critical (warnings-are-bugs: deprecations cross the wire as critical).
The AI writes a test that asserts only that something exists, that the function ran, or that the output equals the implementation's current return value — not the specification. Coverage rises, defect detection does not.
// AI ANTI-PATTERN — pass-through assertion
it("computes total", () => {
const result = computeTotal(items);
expect(result).toBeDefined(); // passes for any non-undefined value
expect(typeof result).toBe("number"); // passes for NaN, 0, Infinity, -1
});
// AI ANTI-PATTERN — tautology mirroring the implementation
it("returns user", () => {
const u = getUser(1);
expect(u).toEqual(getUser(1)); // always true; tests nothing
});
// BETTER — assert the specification
it("computes total with tax for active items only", () => {
const result = computeTotal([{ price: 10, active: true }, { price: 5, active: false }]);
expect(result).toBe(10 * 1.21); // exact spec'd value
});
# Python — AI ANTI-PATTERN
def test_parse():
assert parse("x=1") is not None # passes for any object
assert isinstance(parse("x=1"), dict) # passes for {}
Action: ai_pattern_kind: vacuous_test_assertion, severity: critical. The principle: a green test that asserts nothing is worse than a red test, because it lies about coverage.
The model emits concurrent code that compiles but races: forEach(async ...) without await on the array, shared mutable state in goroutines, missing locks, TOCTOU on filesystem.
// AI ANTI-PATTERN — fire-and-forget; concurrent writes to shared state
items.forEach(async (item) => { total += await price(item); });
console.log(total); // logs 0 — none of the awaits have resolved
// Java 21+ — AI ANTI-PATTERN: HashMap shared across virtual threads without sync
Map<String, Integer> counts = new HashMap<>();
items.parallelStream().forEach(i -> counts.merge(i.key(), 1, Integer::sum)); // not thread-safe
// BETTER: ConcurrentHashMap or Collectors.toConcurrentMap
Cross-link: [[concurrency-checker]] owns the deep check. This skill flags the obvious patterns inline; concurrency-checker traces shared state.
Action: ai_pattern_kind: race_condition, severity: critical.
The model emits string-concatenated or template-literal SQL, exec, eval, or innerHTML on untrusted input.
# AI ANTI-PATTERN
db.execute(f"SELECT * FROM users WHERE id = {user_id}")
-- AI ANTI-PATTERN: LLM-generated raw query with no parameter placeholders
-- AI agents frequently emit raw SQL strings in migration files / repo scripts that interpolate input
EXEC('SELECT * FROM users WHERE name = ''' + @name + '''');
Cross-link: [[sast-scanner]] owns the security pass with full taint analysis. This skill emits an immediate flag and routes.
Action: ai_pattern_kind: ai_sql_injection, severity: critical, confidence: high. Set corroborated_by: [sast-scanner] once sast-scanner has run.
Distinct from category B (deprecated API) — this one is contract mismatch between the AI suggestion's assumed version and the project's actual version. Symptoms include ReactCurrentDispatcher undefined errors when React 19 RC and react-dom 18.x are mixed; Thread.ofVirtual() absent when project still targets Java 17; record patterns rejected on Java 16; using declarations rejected on C# 7.
// React 19 project — AI emits React 18-only API
import { useFormState } from "react-dom"; // React 19 renamed/replaced this; verify project version
// React 18 project — AI emits React 19-only hook
import { useFormStatus } from "react-dom"; // React 19+ only — fails on React 18
// AI ANTI-PATTERN: model assumes .NET 9 features in a .NET 7 csproj
TimeProvider.System.GetUtcNow(); // .NET 8+; not on .NET 7
Action: emit ai_pattern_kind: framework_version_mismatch, severity: critical. The integrator routes to the framework-version-aware specialist.
console.log / debug print left in codefunction handlePayment(amount: number) {
console.log("DEBUG payment", amount); // AI added during a debug suggestion; never removed
return charge(amount);
}
print(f"DEBUG: token={token}") # AI debug print — leaks secret
Action: ai_pattern_kind: debug_print_left, severity: critical (warnings-are-bugs: any production code path printing to stdout/stderr without structured logging is a critical finding, especially if it touches tokens / PII).
The model emits a CRUD endpoint, a form, a serializer — but skips the project's business rules: tenant scoping, role check, tax calculation, audit log, idempotency key. The diff looks complete; the requirements are not met.
// AI ANTI-PATTERN — generated controller missing tenant scoping
[HttpGet("/orders/{id}")] public IActionResult Get(int id)
=> Ok(db.Orders.Find(id)); // no check that order.TenantId == current.TenantId
# AI ANTI-PATTERN — generated serializer doesn't enforce the "amount > 0" business rule
class OrderSerializer(serializers.ModelSerializer):
class Meta: model = Order; fields = "__all__"
Action: ai_pattern_kind: missing_business_rule, severity: critical. Compare against the plan's functional + business stages — any rule present there and missing in the diff is a hard kickback to Step 10.
except:)## Decisions Taken Under Ambiguityconsole.log / print / debug noise left behindfiles: declaration## AI Code Quality Review
### Summary
| Category | Issues | Severity (triage) |
|----------|--------|----------|
| Hallucinated import | 1 | Critical |
| Framework-version mismatch | 2 | Critical |
| Vacuous test assertion | 4 | High |
| Deprecated API pattern | 3 | High |
| Missing business rule | 1 | Critical |
| Over-engineering | 3 | Medium |
| Inconsistent style | 5 | Low |
### Critical Issues
**1. Hallucinated import — `react-codeshift`**
- File: `src/transforms/codemod.ts:3`
- Code: `import { transform } from "react-codeshift";`
- Evidence: not present on npm registry; likely conflation of jscodeshift + react-codemod
- Fix: replace with `jscodeshift` (verify intended API) or remove
- Route: [[dependency-auditor]] for blocking install
**2. Framework-version mismatch — React 19 hook in React 18 project**
- File: `src/forms/Submit.tsx:8`
- Code: `import { useFormStatus } from "react-dom";`
- Evidence: project package.json pins `react: ^18.3.0`; useFormStatus is React 19+
- Fix: upgrade React project-wide OR use a React 18 form pattern
### High Severity
- **Vacuous test assertion** — `src/services/__tests__/billing.test.ts:42` (assertion is `expect(result).toBeDefined()` only)
- **Deprecated API pattern** — `src/legacy/serializer.cs:14` (BinaryFormatter on .NET 9)
### Recommendations
1. Critical: replace hallucinated `react-codeshift` import; route to dependency-auditor.
2. Critical: resolve React version mismatch before merge.
3. Re-author 4 vacuous tests with spec-based assertions.
4. Remove 3 deprecated API calls.
5. Add tenant-scoping check to generated controller.
The 2026 review stack assumes layered AI-detection across IDE → PR → CI → runtime.
| Tool | Role | When |
|---|---|---|
| GitHub Copilot review filters | Per-PR AI-author flag, auto-tag of AI-generated diffs, configurable rules block patterns (hallucinated imports, debug prints, vacuous tests) before they hit reviewer | Every PR |
| Claude Code self-critique | The integrator in Iron Loop calls this skill as a critic. Self-critique runs before the human gate; findings become letters | Step 11 (REVIEW), Step 16 (FINAL-REVIEW) |
Cursor rules (.cursorrules) | Pre-suggest filters: "never propose a package not in lockfile," "always verify framework version before suggesting hook." Best practice in 2026 is a rules file checked into every repo | Editor time |
| Aikido AI Code Reviewer | Third-party AI code-review service. Pairs with sast-scanner for security + quality on AI diffs | PR + nightly |
| Veracode AI Audit | Compliance + security on AI-generated code. Reuses the Veracode 40%-flaw study findings | Scheduled |
| Custom git hooks for AI-detection | Pre-commit / pre-push: detect AI-author signatures (Copilot, Cursor, Claude Code), require a [ai-reviewed] trailer on commit message, reject commits with no human reviewer if commit-trailer says Generated-by: ai | Local + CI |
Recommended minimum: .cursorrules + Copilot review filters + this skill as a critic in Iron Loop + dependency-auditor + sast-scanner. The combination catches the high-impact AI-specific failure modes (hallucination, framework mismatch, vacuous test, security smell) before merge.
These tiers are the internal triage view for the human-readable report. When this skill emits a letter to CTO Chief via the refinement loop, every finding crosses the wire as severity: critical per the warnings-are-bugs rule. The internal tiers below stay in the report body for the user's prioritization; the letter's severity is always critical.
| Triage tier | Examples | Internal action |
|---|---|---|
| CRITICAL | Hallucinated import that resolves on registry (slopsquatting), framework-version mismatch causing build/runtime break, AI-suggested SQL injection, prompt drift reversing a documented Decision | BLOCK |
| HIGH | Vacuous test assertions hiding regressions, deprecated API pattern that compiles but is removed in next release, missing business rule, race condition | BLOCK |
| MEDIUM | Over-engineering, inconsistent style, excessive comments, verbose naming, unrelated edits inside scope | Fix soon |
| LOW | Stylistic comment noise, minor naming preference, formatting drift | Backlog |
Severity reconciliation rule: if a single finding qualifies for two categories (e.g., a hallucinated import that is also a deprecated API name), report the higher tier and note the dual classification in the letter's message field.
When emitting a finding via the refinement loop, write the letter with these fields. The schema aligns with sast-scanner's letter contract so the integrator can dedup across critics.
finding_id: <sha256(critic+file+line+ai_pattern_kind)[:12]>
severity: critical # ALWAYS critical (warnings-are-bugs)
confidence: high | medium | low # high = registry-confirmed / second engine agrees; low = single-source heuristic
engine: ai-code-quality-reviewer # this skill
kind: ai_quality # category bucket — distinct from sast's "security"
ai_pattern_kind: hallucinated_import | deprecated_api_pattern | vacuous_test_assertion |
race_condition | ai_sql_injection | framework_version_mismatch |
debug_print_left | missing_business_rule | prompt_drift | unrelated_edit
corroborated_by: [<other engines that also flagged this — dependency-auditor, sast-scanner, concurrency-checker>]
target_file: src/transforms/codemod.ts
target_line: 3
source_hint: "import statement" # what generation step likely produced this
message: "Hallucinated import: 'react-codeshift' not on npm; likely jscodeshift+react-codemod conflation"
suggested_fix: "Replace with jscodeshift; verify intended API surface."
reachable: true | false | unknown # is the offending line on a real call path?
delta_to_baseline: new | unchanged | regressed # vs prior review baseline if any
reference: https://owasp.org/www-community/attacks/ # or specific Lasso / Veracode / framework changelog URL
The integrator uses confidence and corroborated_by to weight findings — a confidence: low heuristic-only finding doesn't block phase advancement alone, but two engines agreeing escalates it. For hallucinated-import findings, set confidence: high only after [[dependency-auditor]] has confirmed the package is absent from the registry.
print(...) left in production paths.## Decisions Taken Under Ambiguity entry without a documented update.When invoked as a critic by the Iron Loop integrator (see docs/REFINEMENT_LOOP.md), apply the warnings-are-critical rule:
severity: critical in the letter you write to CTO Chief.warn — there is no soft tier.## Decisions Taken Under Ambiguity section.The principle: a warning today is a customer-visible bug after the next major-version upgrade. Code that ships green-with-warnings ships with known latent failures.
npx claudepluginhub robotijn/ctoc --plugin ctocProvides a checklist for code reviews covering functionality, security, performance, maintainability, tests, and quality. Use for pull requests, audits, team standards, and developer training.