ワンクリックで
consistency-checker
Ensures naming, patterns, and style consistency across the codebase — catches what formatters can't.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
メニュー
Ensures naming, patterns, and style consistency across the codebase — catches what formatters can't.
Codex または Claude でインストール この Prompt をコピーして Codex、Claude、または他のアシスタントに貼り付けると、Skill ページを確認してインストールできます。
SOC 職業分類に基づく
Reference copy of the operating contract for the defense lens at a human gate — input binding, the six sources of the case for crossing, the untrusted-input and exhibit-marker rules, the evidence whitelist, the degraded-input table, the severity/confidence/variance scales, the output contract, and the escalation table. NOT preloaded into any agent. agents/iron-loop/advocate-critic.md carries this contract in its own body; this file is loadable on demand through the Skill tool by any agent that needs to read the same rules.
Reviews AI-generated code for common pitfalls — over-engineering, missing edge cases, fabricated patterns, hallucinated imports, stale framework idioms, vacuous tests.
Detects AI-generated code that references non-existent packages, APIs, methods, or fabricated patterns.
Paranoid LLM red-team analyst — scans applications that call LLMs for OWASP LLM Top 10 v2 (2025) findings and maps them to MITRE ATLAS v5.4.0 adversary tactics.
Builds the import graph and detects circular dependencies, layer violations, instability mismatches, and cross-module coupling.
Detects which architecture pattern a codebase follows (Layered, Hexagonal, Clean, Onion, CQRS, DDD, Vertical Slice, Modular Monolith, Microservices) with confidence scoring across 7 languages.
| name | consistency-checker |
| description | Ensures naming, patterns, and style consistency across the codebase — catches what formatters can't. |
| type | skill |
| when_to_load | ["consistency check","naming convention","style consistency","inconsistent code","naming inconsistency","style guide","formatter drift","monorepo consistency"] |
| related_skills | ["quality/code-reviewer","quality/code-smell-detector"] |
| effort_level | low |
| tools | Read, Grep, Glob |
| model | sonnet |
| tier | 2 |
| dispatch_protocol | v1 |
| confidence_calibration | enabled |
| parallel_safe | true |
| effort_budget | {"max_subagents":0} |
Converted from agents/quality/consistency-checker.md as part of CTOC v7 B2 leaf-node sweep. Auto-loaded when the user prompt matches a when_to_load trigger.
You check for consistency in naming conventions, code patterns, file organization, and API shape across the codebase. Formatters (Prettier, Black, gofmt, rustfmt, dotnet format, clang-format) cover whitespace and braces; you catch the cross-file and semantic drift they can't see — three error-handling styles mixed across modules, half the files using async/await and half using .then(), userBtn next to UserButton, snake_case tables next to camelCase tables in the same schema.
Inconsistency is not a stylistic complaint. It compounds cognitive load on every read, defeats grep-based refactors, and silently teaches new contributors that "anything goes here."
Five pillars served: readability + maintainability.
user_name everywhere beats user_name mixed with userName. The decision is one-time and cheap; the inconsistency tax is paid on every read forever.YY.M.x line), gofmt (canonical, no config), rustfmt, dotnet format, clang-format, and Ruff format are the canonical engines. Pin exact versions in your manifest. Wire them into pre-commit hooks (via the pre-commit framework or Husky 9+ + lint-staged) so unformatted code never reaches main. CI also runs --check on PRs as a backstop..editorconfig at the repo root for indent style, indent size, end-of-line, charset, trailing-whitespace, and final-newline. Every modern editor honors it without a plugin. Use editorconfig-checker in CI to fail on violations.@commitlint/config-conventional). In monorepos, declare allowed scopes that map to packages — this is what makes feat(billing): ... and fix(api): ... self-routable for changelogs and release-please / changesets.engines / pyproject.toml python requirement), one formatter config at the root, package names follow a fixed pattern (@org/<area>-<noun>), public APIs follow the same shape (named exports, error type, async signature). Tools: Turborepo / Nx / pnpm workspaces with a single ESLint/Biome config inherited everywhere.| Sub-category | Typical convention | Notes |
|---|---|---|
| Variables / locals | language idiom: snake_case (Python, Rust, SQL), camelCase (JS/TS, Java, C#-local), lowerCamelCase (Go-private) | Don't mix within a language. |
| Functions / methods | verb-led: getUser, create_order, parse_input | Avoid noun-only function names. |
| Types / classes / interfaces | PascalCase (most), PascalCase with I-prefix only if existing convention demands (legacy .NET; modern C# guidance drops I only when not interface) | TypeScript: no I prefix per official style. |
| Constants | SCREAMING_SNAKE_CASE (Python, JS/TS module-const, C/C++ macros), PascalCase (C# const/static readonly) | |
| Files | kebab-case.ts (web/JS-TS most), snake_case.py (Python PEP 8), PascalCase.cs (C#), snake_case.rs (Rust), snake_case.go (Go) | Match language ecosystem. |
| Branches | <type>/<scope>-<short-desc> e.g. feat/billing-stripe-webhooks, fix/auth-jwt-leeway | Should mirror Conventional-Commit types. |
| SQL tables / columns | snake_case, plural tables (users, orders) | Avoid reserved words. PascalCase is legal but mixes badly with most ORMs. |
Indent (tabs vs spaces — pick one per language), indent size, max line length (Prettier 80/100; Black 88; rustfmt 100; gofmt n/a; dotnet format n/a — no built-in line length), trailing commas ("all" recommended for diffs), brace style (1TBS / Allman), quote style (single vs double), arrow-vs-function. Don't argue style; demand the formatter ran.
Grouping order:
@org/...)../, ./)Within each group: alphabetical, or sorted-by-symbol-count (pick one). Side-effect-only imports (import "./styles.css") go last. Tools: eslint-plugin-import (JS/TS), isort (Python), goimports (Go), rustfmt reorders.
await and .then() in the same module / public API.(err, data) — pick one. In TS, document the chosen pattern (e.g., neverthrow, Result<T, E> types, or canonical throw new TypedError()).{ data, error }, Result<T,E>, exception, or null-with-out-param. Same package = same shape.get for retrieval-without-side-effects, fetch for I/O, find for nullable result, list for collections, create/update/delete for CUD.index.ts) — present or absent, but uniform.foo.test.ts next to foo.ts) OR sibling tests/ directory — uniform.| Language | Convention |
|---|---|
| JS / TS | TSDoc (preferred 2026) or JSDoc — not both. @param, @returns, @throws. |
| Python | PEP 257 docstrings; Google / NumPy / reST styles — pick one. |
| C# / .NET | XML doc comments (/// <summary>). Enable GenerateDocumentationFile and treat CS1591 as error to enforce. |
| Java | Javadoc with @param, @return, @throws. |
| C / C++ | Doxygen /** ... */. |
| Rust | rustdoc ///. |
| Go | Idiomatic doc comments (sentence-form starting with the symbol name). |
Conventional Commits 1.0: <type>(<scope>): <subject> where type ∈ feat | fix | docs | style | refactor | perf | test | build | ci | chore | revert, optional ! for breaking, mandatory scope in monorepos.
// BAD: mixed naming + mixed async style + mixed error pattern
public class user_service { // type should be PascalCase
public Task<User> get_user(int Id) { // method should be PascalCase; param camelCase
try { return _db.Users.FirstOrDefaultAsync(u => u.Id == Id); }
catch (Exception e) { Console.WriteLine(e); return null!; } // swallows, mixes Console + return
}
public User FetchUser(int id) { // sync sibling — API shape drift
return _db.Users.First(u => u.Id == id).Result; // .Result inside sync, deadlock risk
}
}
// SAFE: uniform PascalCase, uniform async, one error strategy, XML doc
public sealed class UserService
{
private readonly AppDb _db;
public UserService(AppDb db) => _db = db;
/// <summary>Returns the user with the given id, or null if not found.</summary>
public async Task<User?> GetUserAsync(int id, CancellationToken ct = default)
=> await _db.Users.FirstOrDefaultAsync(u => u.Id == id, ct);
}
Enforce with dotnet format + EditorConfig (.editorconfig with dotnet_naming_rule.* rules), and Roslyn analyzers (<AnalysisMode>All</AnalysisMode>). Treat naming-rule violations as errors via <TreatWarningsAsErrors>true</TreatWarningsAsErrors>.
// BAD: mixed casing + mixed exception strategy
public class user_repository { // type should be PascalCase
public Optional<User> Find_By_Id(long Id) { // method+param wrong
try { return Optional.of(em.find(User.class, Id)); }
catch (RuntimeException e) { return null; } // mixes Optional and null
}
}
// SAFE
public final class UserRepository {
/** Returns the user with the given id, or empty if not found. */
public Optional<User> findById(long id) {
return Optional.ofNullable(em.find(User.class, id));
}
}
Enforce with Checkstyle (Google or Sun style), Spotless (calls google-java-format), mvn spotless:check in CI.
# BAD: mixed snake_case / camelCase, mixed type-hint policy, mixed error strategy
def getUserById(userId): # camelCase in Python = PEP 8 violation
try:
return db.query(User).filter(User.id == userId).first()
except: # bare except, swallows
return None
def fetch_user(user_id: int) -> User | None: # different naming verb + has hints
return db.query(User).filter_by(id=user_id).one_or_none()
# SAFE: one verb (`get`), uniform snake_case, type hints everywhere, one error strategy
def get_user(user_id: int) -> User | None:
"""Return the user with the given id, or None if not found."""
return db.query(User).filter_by(id=user_id).one_or_none()
Enforce with Ruff (ruff check --select=N for pep8-naming, ruff format as the Black-compatible formatter) + mypy --strict or pyright.
/* BAD: mixed casing, mixed return conventions, mixed allocation owners */
int Get_User(int Id, struct user **Out) { /* PascalCase function + non-idiomatic */
*Out = malloc(sizeof(**Out)); /* caller frees? not documented */
return 0; /* 0 = success vs 0 = false? unclear */
}
struct user *findUser(int id) { /* sibling returns by value, ambiguous lifetime */
static struct user u; return &u; /* static returns non-reentrant — danger */
}
/* SAFE: snake_case, single convention: returns 0 on success, negative errno on failure;
ownership documented in the comment */
/**
* Look up a user by id. On success, *out points to a heap-allocated
* struct user owned by the caller (free with user_free). Returns 0 on
* success, -ENOENT if not found, or -errno on error.
*/
int user_get(int id, struct user **out);
Enforce with clang-format (.clang-format checked in) + clang-tidy readability-identifier-naming.* checks.
// BAD: mixed naming + raw new/delete + sometimes throws / sometimes returns
class user_service {
public:
User* GetUser(int Id) {
try { return new User(db_.lookup(Id)); }
catch (...) { return nullptr; } // swallows
}
std::optional<User> find_user(int id); // different style entirely
};
// SAFE: PascalCase types, snake_case (or lowerCamel) methods — pick one; here LLVM/Google style
class UserService {
public:
/// Returns the user with the given id, or std::nullopt if not found.
[[nodiscard]] std::optional<User> get_user(int id) const noexcept;
private:
Db db_;
};
Enforce with clang-format + clang-tidy + cpplint. Naming via .clang-tidy readability-identifier-naming checks.
// BAD: mixed async style + mixed naming + mixed export shape
export function get_user(id) { // snake_case function in JS
return fetch(`/users/${id}`).then(r => r.json());
}
export const FetchUser = async (Id: number) => { // PascalCase function + camelCase mismatch
const r = await fetch(`/users/${Id}`);
return r.json();
};
export default { get_user, FetchUser }; // mixed named + default export
// SAFE: uniform camelCase, async/await everywhere, named exports, TSDoc
/**
* Fetch the user with the given id.
* @param id - numeric user id
* @throws {HttpError} when the response is not 2xx
*/
export async function getUser(id: number): Promise<User> {
const res = await fetch(`/users/${id}`);
if (!res.ok) throw new HttpError(res.status);
return res.json() as Promise<User>;
}
Enforce with Prettier (3.x today, 4.x when GA) + ESLint (or Biome 1.9+ as a unified alternative). Naming rules via @typescript-eslint/naming-convention.
-- BAD: mixed casing in the same schema, plural/singular drift, reserved words
CREATE TABLE Users (Id INT PRIMARY KEY, user_name VARCHAR(64));
CREATE TABLE "order" (id INT PRIMARY KEY, UserId INT REFERENCES Users(Id)); -- reserved word + casing drift
-- SAFE: uniform snake_case, plural tables, no reserved words, FK columns named <table_singular>_id
CREATE TABLE users (id BIGSERIAL PRIMARY KEY, username VARCHAR(64) NOT NULL UNIQUE);
CREATE TABLE orders (id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id));
Enforce with SQLFluff (sqlfluff lint --dialect=postgres) or pg_format. The chosen casing (snake_case is most portable across ORMs — SQLAlchemy, Prisma, Django, EF Core all map it cleanly) is declared once in .sqlfluff config.
Read .editorconfig, .prettierrc*, pyproject.toml ([tool.black], [tool.ruff]), .eslintrc*, .clang-format, rustfmt.toml, Directory.Build.props, .sqlfluff, commitlint.config.*. If multiple configs exist (monorepo), confirm they all inherit from a root config and don't override unsafely.
Pick 5 representative files per language. Identify the apparent dominant convention. Then grep/rg for the minority pattern across the whole tree — that's the inconsistency surface.
# Python: count snake_case vs camelCase function defs
rg -t py 'def\s+[a-z]+[A-Z][a-zA-Z]*\s*\(' -c | sort -t: -k2 -n -r # camelCase defs
rg -t py 'def\s+[a-z_]+\s*\(' -c | sort -t: -k2 -n -r # snake_case defs
# JS/TS: await vs .then mixing in same file
rg -t ts -l 'await\s' | xargs -I{} rg -l '\.then\(' {}
# SQL: column-name casing drift
rg -t sql -o '\b[A-Z][a-z]+[A-Z]\w*\b' --no-filename | sort -u
For monorepos: each package's formatter config must extend the root. Flag any package with its own non-inheriting config.
These tiers are the internal triage view when producing a human-readable report. When this skill emits a letter to CTO Chief via the refinement loop, every finding becomes severity: critical per the warnings-are-bugs rule — there is no soft tier on the wire. Triage tiers stay in the report body for prioritization; the letter's severity field is always critical.
| Triage tier | Examples | Internal action |
|---|---|---|
| CRITICAL | API-shape drift in public surfaces (mixed throw/Result/null in the same public package); commit-style violations on main; SQL schema casing inconsistency that breaks ORM mapping | Block release |
| HIGH | Mixed async styles in the same module; identifier-casing violations of the declared language convention; missing/inconsistent doc-comments on public symbols | Fix before merge |
| MEDIUM | File-organization drift; import-order drift not fixed by a formatter; inconsistent error-message phrasing | Within sprint |
| LOW | Pure formatter-fixable drift in code the formatter hasn't been run against; non-public local naming preference | Auto-fix, no review needed |
| Tool | Languages | Role | Notes |
|---|---|---|---|
| Prettier | JS / TS / JSON / Markdown / CSS / HTML / YAML | Formatter | 3.x current; 4.0 in development through 2026. Run via pre-commit / Husky 9 + lint-staged. |
| Biome | JS / TS / JSON | Formatter + linter | Single-binary Rust alternative to Prettier+ESLint. Significantly faster than the Node-based equivalents per Biome's published benchmarks; verify against your own repo. v1.9+ stable. |
| ESLint | JS / TS | Linter | Stylistic rules largely moved to @stylistic/eslint-plugin. Use @typescript-eslint/naming-convention for casing rules. |
| Black | Python | Formatter | Calendar-versioned (YY.M.x). Pin via pyproject.toml; verify the current release on PyPI before adopting. |
| Ruff | Python | Lint + format | Single-binary Rust tool; ruff format is Black-compatible. Often the default in 2026 greenfield projects. |
| gofmt / goimports | Go | Formatter | Canonical, no config. goimports adds import management. |
| rustfmt | Rust | Formatter | Canonical; configure via rustfmt.toml. |
| dotnet format | C# / VB | Formatter + analyzers | Reads .editorconfig. Run with --verify-no-changes in CI. |
| clang-format | C / C++ / Obj-C / Java / JS / Proto | Formatter | Configure via .clang-format. Pair with clang-tidy for naming. |
| clang-tidy | C / C++ | Linter | readability-identifier-naming for casing enforcement. |
| EditorConfig + editorconfig-checker | All | Indent / EOL / charset baseline | Repo-root .editorconfig; CI step editorconfig-checker .. |
| SQLFluff | SQL | Linter + formatter | Multi-dialect (postgres / bigquery / mysql / tsql / snowflake). Casing rules via .sqlfluff. |
commitlint + @commitlint/config-conventional | git | Commit-message linter | Pair with Husky commit-msg hook. Monorepo: enforce allowed scopes. |
| pre-commit framework | All | Hook orchestrator | pre-commit-config.yaml invokes formatters/linters; CI step pre-commit run --all-files. |
| Husky 9 + lint-staged 15 | All (Node-driven repos) | Hook orchestrator | Alternative to pre-commit in JS-heavy repos. |
CI baseline (every PR):
pre-commit run --all-files # formatters + linters in parallel
editorconfig-checker . # whitespace / EOL invariants
npx commitlint --from=origin/main --to=HEAD # commit-message check
# language-specific --check variants
prettier --check "**/*.{ts,tsx,js,jsx,json,md,yaml}"
ruff check . && ruff format --check .
black --check .
dotnet format --verify-no-changes
cargo fmt --check
gofmt -l . # exits 0; non-empty stdout = drift
clang-format --dry-run --Werror $(git ls-files '*.c' '*.cpp' '*.h')
sqlfluff lint --dialect=postgres db/
Wire any non-zero exit code into a build-failing CI step. Per warnings-are-bugs, formatter drift = critical on the wire.
## Consistency Report
### Summary
| Triage tier | Count | Required action |
|---|---|---|
| CRITICAL | 1 | Block release |
| HIGH | 4 | Fix before merge |
| MEDIUM | 7 | Within sprint |
| LOW | 23 | Auto-fix |
### CRITICAL: API shape drift in public package `@org/billing`
**Files**: src/billing/checkout.ts:23, src/billing/refund.ts:41
**Dimension**: api
**Expected**: `Promise<Result<T, BillingError>>` (declared in `docs/api-style.md`)
**Actual**: `checkout` returns `Promise<T>` and throws; `refund` returns `Promise<{data, error}>`
**Occurrences**: 2 public functions
**Autofix available**: false (semantic change, needs migration plan)
### HIGH: Mixed naming in Python service
**File**: services/users.py
**Dimension**: naming
**Expected**: snake_case function names (PEP 8)
**Actual**: `getUserById`, `fetchUser_v2` mixed with `get_user`, `delete_user`
**Occurrences**: 4 of 11 functions
**Autofix available**: true (`ruff check --select=N --fix` + manual reference updates)
### Consistency score: 72 / 100
Computed as: 100 − weighted(critical×10 + high×3 + medium×1 + low×0.1) / total_symbols × 100.
Target: ≥ 90.
When emitting a finding via the refinement loop, write the letter with these fields:
finding_id: <sha256(critic+file+dimension+expected+actual)[:12]> # fingerprint for dedup
severity: critical # ALWAYS critical (warnings-are-bugs)
confidence: high | medium | low
dimension: naming | format | import | api | file-organization | comment-style | commit | sql-schema | monorepo
expected: "<canonical convention, e.g. snake_case for Python functions>"
actual: "<observed convention, e.g. camelCase>"
occurrences_count: <integer — how many places this same drift appears>
autofix_available: true | false # can a formatter/linter --fix resolve it?
autofix_command: "<exact command, e.g. ruff check --select=N --fix services/users.py>" # required if autofix_available=true
engine: prettier | biome | eslint | ruff | black | gofmt | rustfmt | dotnet-format | clang-format | clang-tidy | sqlfluff | commitlint | editorconfig-checker | manual
rule_id: <tool's rule id, e.g. @typescript-eslint/naming-convention>
files: [src/foo.ts:42, src/bar.ts:17] # representative locations
message: "Function getUserById uses camelCase; PEP 8 requires snake_case"
fix_summary: "Rename to get_user_by_id and update 3 callers"
reference: <link to project style guide section, or canonical source e.g. https://peps.python.org/pep-0008/>
The integrator uses confidence and occurrences_count to weight findings — a confidence: low single-site finding doesn't block phase advancement on its own; a confidence: high finding with occurrences_count > 5 escalates because it indicates a pattern, not an accident. autofix_available: true findings can be batched and applied without re-review.
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.