Help us improve
Share bugs, ideas, or general feedback.
From rails-audit
Comprehensive Rails project stability audit across 18 dimensions — foundation, domain shape, specs, coverage, deploy/CI, security, authorization, money paths, code health, code smells, performance, reliability, observability, jobs, data integrity, governance, DX, and cost. Use when user asks to "audit this Rails project", "stability assessment", "code health check", "pre-launch review", "Rails security audit", or "is this ready to ship". Produces a severity-ranked markdown report with a recommended fix sequence. Read-only — never edits code.
npx claudepluginhub kurenn/marketplace --plugin rails-auditHow this skill is triggered — by the user, by Claude, or both
Slash command
/rails-audit:rails-auditThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Run a structured stability audit of a Ruby on Rails codebase and produce a single markdown report with a severity-ranked punch list and a recommended fix sequence.
dimensions/background-jobs.mddimensions/code-health.mddimensions/cost-estimation.mddimensions/data-governance.mddimensions/data-integrity.mddimensions/deploy-ci.mddimensions/domain-shape.mddimensions/dx-and-cost.mddimensions/money-and-payments.mddimensions/money-revalidation.mddimensions/observability.mddimensions/performance-reliability.mddimensions/security-and-authz.mddimensions/security-revalidation.mddimensions/self-check.mddimensions/severity-inheritance.mddimensions/spec-and-coverage.mddimensions/trend-tracking.mdexamples/sample-report.jsonexamples/sample-report.mdApplies C++ Core Guidelines to write, review, or refactor C++ code. Enforces modern, safe, and idiomatic practices for C++17/20/23.
Share bugs, ideas, or general feedback.
Run a structured stability audit of a Ruby on Rails codebase and produce a single markdown report with a severity-ranked punch list and a recommended fix sequence.
--quick — static-only checks (bundle audit, brakeman, rubocop, basic greps). Single agent. ~3 min. Suitable for PR-time.--standard (default) — full audit with parallel subagents. ~10–15 min.--deep — standard + boots app, runs subset of specs, mutation testing on critical paths. ~30+ min.If the user invokes /rails-audit with no mode, ask using prompt P1 in prompts.md — then proceed.
By default an audit covers all 18 dimensions. Several args narrow the scope:
--only=<comma-list> — run only these dimensions (or aliases). Example: /rails-audit --only=money,security.--exclude=<comma-list> — run all dimensions except these. Example: /rails-audit --exclude=dx-and-cost,observability.--only-cluster=<list> (added v0.4) — run only these clusters. Accepts cluster letters (A/B/C/D) or English aliases (spec/deploy/health/security). Example: /rails-audit --only-cluster=A,D.--exclude-cluster=<list> (added v0.4) — run all clusters except these.--only=<arg> (or --only-cluster=<arg> if it matches a cluster letter). Example: /rails-audit money, /rails-audit A.Mutually exclusive groups: at most one of --only, --exclude, --only-cluster, --exclude-cluster may be set. Reject combinations with an error.
| Alias | Letter | Dimensions covered |
|---|---|---|
spec | A | spec-stability, test-coverage |
deploy | B | foundation, deploy-and-ci, observability |
health | C | domain-shape, risk-hotspots, code-smells, performance, reliability, background-jobs, data-integrity, developer-experience, cost-and-scaling |
security | D | security-and-authz, authorization, money-and-payments, data-governance |
Note: security resolves to cluster D in --only-cluster, but to the single dimension security-and-authz in --only. Use the cluster flag to mean "the broad security audit slice including money + governance + authorization."
| Alias | Resolves to |
|---|---|
all | every dimension |
money | money-and-payments |
security | security-and-authz |
auth | authorization |
authz | authorization |
deploy | deploy-and-ci |
ci | deploy-and-ci |
specs | spec-stability |
coverage | test-coverage |
code-smells | code-smells |
code | code-smells + risk-hotspots |
perf | performance + reliability |
jobs | background-jobs |
obs | observability |
data | data-integrity + data-governance |
dx | developer-experience |
cost | cost-and-scaling |
Dimension names accepted directly (e.g. money-and-payments) as well.
"Unknown dimension '<name>'. Did you mean '<closest-match>'?" and exit. Use Levenshtein distance ≤2 for the suggestion."Unknown cluster '<name>'. Valid: A, B, C, D, or spec/deploy/health/security."--only= (or --only-cluster=) empty → reject.--only/--exclude/--only-cluster/--exclude-cluster set → reject.When scope is narrower than all:
Step 3 fan-out skips clusters whose dimensions are entirely outside the scope. Cluster ↔ dimension mapping:
spec-stability, test-coveragefoundation, deploy-and-ci, observabilitydomain-shape, risk-hotspots, code-smells, performance, reliability, background-jobs, data-integrity, developer-experience, cost-and-scalingsecurity-and-authz, authorization, money-and-payments, data-governance
A cluster that has at least one in-scope dimension still runs; agents are told to focus on the in-scope subset.audit.scope[] in the report lists the resolved dimension names (not aliases). For full audits: ["all"]. For scoped: ["money-and-payments", "security-and-authz", ...].
Scorecards and findings are filtered to in-scope dimensions only. Findings whose primary_dimension is in scope are included; those whose only in-scope tag is in secondary_dimensions[] are also included (tagging is the point of cross-cuts).
Renumbering. The dimension index in the markdown scorecard table re-numbers from 1 (don't render #3 #5 #7 — render #1 #2 #3).
Money-revalidation (Step 4.5) runs only if money-and-payments is in scope.
Self-check (Step 5.5) runs in scoped mode too — calibration percentages compute over the scoped finding set.
Trend (Step 7) compares only the scoped slice of findings against the prior report's matching slice.
Beyond the initial-audit modes (Quick / Standard / Deep), the skill supports two re-run modes that skip detect + agent fan-out and re-use prior findings.
--continue — load the most recent report-*.json in tmp/rails-audit/ and re-run only Steps 4.4 onward (apply ignores, money-revalidation, security-revalidation, self-check, trend, render). Skip Steps 1–3 entirely. Useful after fixing N findings and wanting fresh self-check + trend data without rerunning the full ~50K-token agent fan-out.
--from-findings=<path> — load findings from a specific JSON file (could be hand-edited or copied from another run). Re-run pipeline from Step 4.4 against that input. Useful for what-if analysis or for surfacing a different prior baseline.
When --continue or --from-findings is set:
audit.stack, audit.commit, audit.branch from the loaded report — re-detected only if --re-detect is also set.(continued from <prior date>) annotation..audit-ignore.yml) — load the file fresh; surface stale ignores.trend.prior_report_path if present so the chain stays linked.report-YYYY-MM-DD.{json,md} files with audit.mode set to "continue".| Scenario | Mode | Cost |
|---|---|---|
| First audit on a project | --standard (default) | ~50K tokens |
| Re-audit after fixing 3 blockers | --continue | ~5K tokens |
| Compare a hand-edited finding set | --from-findings=fixed-baseline.json | ~5K tokens |
| Same as previous run but with re-detected stack | --continue --re-detect | ~6K tokens |
| Full fresh audit | --standard (or --quick) | ~50K / ~13K tokens |
--continue and --from-findings are mutually exclusive.--continue errors out cleanly if no prior report exists in tmp/rails-audit/."Prior report schema_version <N> incompatible with current <M>; run a fresh audit").audit.mode in the new report records "continue" or "from-findings" (NOT the original mode of the loaded report).The audit's value is bounded by which static tools are available. Before fanning out, ensure the project has the tools the skill expects.
Detect what's installed (see tooling.md for the contract).
If any Tier-1 (required) tool is missing, ask per missing tool using prompt P2 in prompts.md. The prompt's three answers (gemfile / global / skip) each have well-defined behavior and a fallback.
For Tier-2 (recommended) tools, ask once using prompt P3 in prompts.md.
Tier-3 (optional / deep-mode) tools are only prompted in --deep mode, and are always opt-in.
Hard floor: if the project declines all of {rubocop, brakeman, simplecov}, abort the audit (P2's hard-floor branch handles this).
Don't auto-add anything without explicit consent. The Gemfile is the user's source of truth — modifying it silently is out of scope.
Cache provisioning state: write tmp/rails-audit/tools-detected.json so subsequent runs can fast-path. Re-detect on --quick only if the cache is older than 7 days.
First-run profile init: if .claude/rails-audit.yml doesn't exist and the skill auto-detected stack values, ask using prompt P4 in prompts.md. Skip this step on subsequent runs.
A reusable Gemfile snippet for the user to paste manually if they prefer is documented in tooling.md under "Recommended Gemfile additions".
After provisioning (Step 0) but before detect/fan-out, compute a token budget estimate using the heuristic in dimensions/cost-estimation.md. Store in cost.estimated_input_tokens and cost.estimated_output_tokens.
If --budget=<N> is set explicitly, store in cost.budget_tokens.
If --budget is not set AND estimated_input_tokens > 30_000, run prompt P5 in prompts.md. Accepted answers:
Y (default) — proceed with no cap.n — abort.budget=N (or a bare number) — set cost.budget_tokens = N and proceed.When a budget is in effect, gate every agent call: before launching, compute remaining = budget - usage_so_far. If the agent's conservative upper-bound cost (use per_dim_in + per_dim_out per dimension in scope) would exceed remaining, skip the call. When the budget is hit, save the partial JSON, set summary.verdict to note the abort, and render a partial markdown report flagging missing dimensions.
Hard floor: budget_tokens < 5000 is rejected (no audit can fit).
Confirm Rails project, capture orientation:
test -f Gemfile && grep -q "gem ['\"]rails['\"]" Gemfile || echo "NOT_RAILS"
test -f config/application.rb || echo "NOT_RAILS"
ruby -e 'puts RUBY_VERSION' 2>/dev/null
grep -E "^ruby|gem ['\"]rails['\"]" Gemfile
ls app/ config/ .github/workflows/ 2>/dev/null
test -f Procfile -o -f Dockerfile -o -f fly.toml -o -f config/deploy.yml && echo "deploy artifacts found"
Read .claude/rails-audit.yml if present (project profile — see "Project profile" below). Otherwise auto-detect:
deploy-to-cloud-run.yml → Cloud Run; kamal* → Kamal; presence of Procfile + Heroku buildpacks → Heroku; fly.toml → Fly).Gemfile greps — sidekiq, cloudtasker, good_job, delayed_job, resque.Gemfile greps — devise, warden, jwt, clearance, doorkeeper..rspec exists → RSpec; test/ exists → Minitest.Before running static tooling, run bin/scan-secrets --json > tmp/rails-audit/secrets.json from the project root. The script enumerates git ls-files, matches against secret-shaped filename patterns (.pem, .p12, .pfx, .keystore, id_rsa, *credentials*.json, credentials.txt, secrets.txt, .env.production, etc.), and emits ready-to-merge finding stubs.
Each match is auto-blocker by default (private keys, GCP service-account JSONs, AWS credentials) — tracked secrets are permanently compromised by definition. Exclusions: .crt/.cer/.cert/.ca/.pub (public certs), files matching *.example, *.sample, *.template, anything under spec/, test/, or fixtures/.
The findings stubs go directly into findings[] during synthesis (Step 4). Their phase is always 1 — secret rotation must precede any other work.
If a finding is a false positive (e.g. an intentionally-tracked dummy .pem for testing), the user can add it to .audit-ignore.yml post-audit. The scanner does not attempt to read file contents — only filename patterns — so it cannot distinguish "dummy test cert" from "production key" by content. The .audit-ignore.yml mechanism is the right escape hatch.
The scanner is also available standalone:
bin/scan-secrets # human-readable table
bin/scan-secrets --strict # exit 1 if any tracked secret-shaped file is found (CI gate)
See tooling.md for the full contract. Capture all output to tmp/rails-audit/:
mkdir -p tmp/rails-audit
Then run available tools in parallel Bash calls. Skip silently if not installed (note absence in the report's tooling section). For Bundler-managed tools, prefix with bundle exec if Gemfile lists them; otherwise try the bare command.
v0.4 — parse tool output via bin/parse-* scripts. After the tools finish, invoke the parsers to convert raw output into ready-to-merge finding stubs:
bin/parse-brakeman tmp/rails-audit/brakeman.json > tmp/rails-audit/brakeman-stubs.json
bin/parse-bundle-audit tmp/rails-audit/bundle-audit.txt > tmp/rails-audit/bundle-audit-stubs.json
bin/parse-rubocop tmp/rails-audit/rubocop.json > tmp/rails-audit/rubocop-aggregate.json
The brakeman + bundle-audit stubs go into findings[] during synthesis (Step 4). The rubocop aggregate goes into appendices.rubocop_offenses. Each parser:
findings[] (or appendices.*).parse-brakeman drops array-form Open3.capture3 — argv, no shell).f-bk- (brakeman), f-ba- (bundle-audit) so the same warning produces the same id across runs (good for trend tracking).Synthesis (Step 4) then merges parser stubs with agent findings, dedupes by fingerprint and by (file, line, finding_type) overlap.
Launch up to 4 Explore agents IN PARALLEL (single message, multiple Agent calls). When audit.scope is narrower than all, skip clusters whose dimensions are entirely outside the scope (see "Scope arguments" → cluster mapping). Clusters that have at least one in-scope dimension still run, but the agent brief instructs them to focus on the in-scope subset only.
Each agent gets:
dimensions/rubric.mdCluster A — Spec & Coverage (dimensions/spec-and-coverage.md)
Audit the test suite for stability and the coverage signals that matter.
Cluster B — Deploy & CI (dimensions/deploy-ci.md, dimensions/observability.md)
Audit deploy pipeline, prod config, secrets, health checks, rollback, and observability.
Cluster C — Code Health (dimensions/domain-shape.md, dimensions/code-health.md, dimensions/performance-reliability.md, dimensions/background-jobs.md)
Audit risk hotspots, smells, antipatterns, performance, jobs, data integrity.
Cluster D — Security & Money (dimensions/security-and-authz.md, dimensions/money-and-payments.md, dimensions/data-governance.md)
Audit AuthN/AuthZ, OWASP-top patterns, payment/idempotency, PII handling.
For each agent, write a self-contained brief: what to investigate, where to look, what to return. Don't say "based on the dimension file"; quote the specific checks the agent should run.
Read the four agent outputs and produce a JSON object conforming to schema/report.schema.json. JSON is the source of truth; the markdown report is rendered from it (Step 5). This step is the one that must get the contract right.
sed -n '<line>p' <file>. Agents hallucinate line numbers. Findings whose cite couldn't be verified get self_check.status: "unverified" and surface in the self-check section (see PR#5).findings[] entry. Pick the most-load-bearing dimension as primary_dimension; tag all other applicable dimensions in secondary_dimensions[]. Each dimension file documents its known cross-cuts (## Cross-cuts section) — read those before tagging. Examples: update_column(:stripe_customer_id) is code-smells primary with money-and-payments + data-integrity secondary; a Stripe webhook missing event-ID dedup is money-and-payments primary with security-and-authz secondary; force_ssl disabled is deploy-and-ci primary with security-and-authz secondary. Per-dimension scorecards count findings where the dimension appears in primary_dimension OR any secondary_dimensions[] — so accurate tagging makes the scorecards trustworthy.id = "f-" + first 16 hex chars of SHA256(primary_dimension + file_path + finding_type + normalize(evidence_snippet)). The normalize function: strip leading/trailing whitespace, remove inline + block comments, collapse internal whitespace runs to single space, preserve case. Store the normalized snippet in evidence.normalized so the fingerprint is reproducible.rubric.md. Don't inflate to "high" to seem useful. Money/auth defects default to blocker unless verified non-exploitable.phase: 1..5) — phase numbers come from your fix-sequence ordering. Phases are deduped + ordered so each unblocks the next.summary, scorecards, tooling, fix_sequence as the schema requires.The output of this step is a single JSON object. Validate it against schema/report.schema.json before proceeding.
.audit-ignore.ymlRead .audit-ignore.yml from the repo root if present. Each entry is a fingerprint-keyed acknowledgement (see examples/.audit-ignore.yml.example). Required fields per entry: id (matches findings[].id) and reason (non-empty). Optional: acknowledged_by, expires_at.
For each entry:
expires_at is present and < today — the ignore is expired. The matching finding stays in the punch list with a _(ignore expired YYYY-MM-DD)_ note appended to its explanation. Do not add to ignored_findings[].id matches no current finding — push a string to audit.ignore_warnings[] describing the stale ignore (so it can be cleaned up). Skip.findings[<id>].ignored = true and add an entry to top-level ignored_findings[] with id, reason, acknowledged_by, expires_at, expired: false.Findings with ignored: true are excluded from:
But they are included in:
If the user wants to add a new ignore entry interactively during an audit, use prompt P6 in prompts.md (free-form reason; default 90-day expiry).
The schema and template already support ignored_findings[] and audit.ignore_warnings[] from PR#1. No schema or template change needed.
Before self-check, run a focused second pass on every finding tagged money-and-payments in primary_dimension OR secondary_dimensions[]. See dimensions/money-revalidation.md for the full checklist.
For each finding in scope:
sed -n '<start>,<end>p' <file>.dimensions/money-and-payments.md: idempotency-key derivation (M-RV-1), transaction-boundary ordering (M-RV-2), webhook event-ID dedup (M-RV-3), money column types (M-RV-4), refund idempotency (M-RV-5), audit trail completeness (M-RV-6).findings[<id>].money_revalidation to one of:
confirmed — finding stands as writtenrefined — same finding, evidence/explanation/fix_sketch improved with money-specific detail (record notes)rejected — false positive on closer look (set ignored: true, record notes)promoted — severity bumped one tier because a money-specific check applies (record notes)app/services/payments/*, app/controllers/webhooks/stripe_*, app/jobs/*payment*, app/jobs/*payout*, and money-shaped columns in db/schema.rb to surface findings that synthesis may have missed. New findings get tool_origin: "money-revalidation".Re-validation never modifies id, primary_dimension, or secondary_dimensions[]. Schema field findings[].money_revalidation is already defined as nullable in schema/report.schema.json from PR#1; the template renders _Re-validated: <status>_ next to fix sketches.
After revalidations but before self-check, apply severity inheritance: when N findings are consequences of one root-cause finding, demote them by one tier and link them with severity_inherited_from.
Detection — a finding is inherited when ALL of:
tool_origin is bundle-audit (CVEs aggregated from gemfile lock).primary_dimension or secondary_dimensions contains foundation.dimensions/severity-inheritance.md for the full root-coverage list).tool_warning_id: "unmaintained_dependency" covering Rails or Ruby (Brakeman's EOL flags).Action:
findings[<id>].severity_inherited_from = <root_id>.severity by one tier: blocker → high, high → medium, medium → low, low → low.findings[<id>].self_check.notes: "Demoted by inheritance from <root_id> (foundation upgrade fixes this)."Why: influapp v0.4 surfaced 17 blocker + 46 high CVEs all inherited from Rails 7.0.4 EOL. Each is technically real but landing one foundation-upgrade PR fixes all 63. Without inheritance, they look like 63 distinct concerns. With inheritance, they're consequences of finding B1 (Ruby/Rails EOL) and the punch list says so.
Render: inherited findings render under the root-cause finding's punch-list entry as a nested table or sub-list, NOT as separate top-level entries. The aggregate count is still surfaced ("63 CVEs inherited from B1") so the work scope is visible.
Trend interaction: if the root-cause finding is fixed in a future audit, all inherited children automatically resolve. Trend reports them collectively under the root, not individually.
See dimensions/severity-inheritance.md for the full coverage list, edge cases, and when NOT to inherit.
After money revalidation (Step 4.5), run a focused second pass on every finding tagged security-and-authz (in primary_dimension OR secondary_dimensions[]). See dimensions/security-revalidation.md for the full checklist.
For each finding in scope, apply the six security re-checks: S-RV-1 token comparison (secure_compare discipline), S-RV-2 IDOR (current_user.<association> scoping), S-RV-3 trusted-header identity (JWT verification), S-RV-4 SQL interpolation (blocker default), S-RV-5 open redirect (host allowlist), S-RV-6 SSRF (private-IP block).
Set findings[<id>].security_revalidation to confirmed / refined / rejected / promoted with notes. Same shape as money_revalidation. Schema field is additive (new in v0.4); v0.2/v0.3 reports validate clean.
Proactive sweep paths: app/controllers/**/*authenticate*.rb, app/controllers/api/**/*.rb, app/controllers/webhooks/**/*.rb, anything with Open3/system(/backtick/eval/instance_eval, app/models/ URL validators.
Before rendering, run the self-check defined in dimensions/self-check.md. Seven checks (C1–C7) operate on the JSON from Step 4:
>40% of findings are high (warn unless C7 overrides)>25% are blocker (warn unless C7 overrides)sed -n '<line>p' <file> confirm a substring of evidence.normalized (always fires; no override)coverage/.resultset.json mtime > 30 days (warn-only)risk_score ≤ 4 AND ≥6 dimensions ≤4v0.4+ behavior — block-with-override. When C1, C2, or C3 fires (and C7 doesn't override C1/C2), the skill stops at Step 5.5 and runs prompt P7 in prompts.md. The user picks:
block (default) — skill aborts, writes partial JSON to tmp/rails-audit/report-YYYY-MM-DD-blocked.json, does NOT render markdown. Re-run after fixing findings.demote — skill auto-demotes (high → medium for C1; blocker → high for C2) until thresholds clear. Demoted findings get self_check.status: "demoted" with notes citing the check + original severity. Skill proceeds.accept — skill prompts for a non-empty reason, records it in audit.calibration_overrides[], proceeds.C3 (unverified blocker) always blocks with no accept option — an unverified blocker is hallucination risk and must be re-cited or removed.
C4, C5, C6 remain warn-only — they're informational, not exploit-shaped.
Self-check meta-findings render in the markdown report as a ## Self-check section above the punch list.
If a prior report-*.json exists in tmp/rails-audit/, compute the per-finding diff per dimensions/trend-tracking.md and populate trend{} in the JSON before rendering.
Algorithm:
tmp/rails-audit/report-*.json and pick the most recent file whose date is older than today.trend = null and skip.schema_version ≠ current, set trend = null and push to audit.ignore_warnings[] ("Prior report at <path> has incompatible schema_version <N>; trend skipped.").fixed_ids / new_ids / persisted_ids by fingerprint set diff. Filter by audit.scope if the current run is scoped.first_seen: for persisted findings, copy from prior; for new findings, set to today.stale for the render layer (no schema field — it's a derived render flag).Apply output-template.md to the JSON from Step 4 (now possibly mutated by Step 5.5). The template is authoritative — placeholder names refer to JSON paths. Filters (date, dimension_label, range_str, percent, join, where, sort_by) are deterministic; same JSON in produces same markdown out.
Pre-write validation gate (added v0.5.2). Before writing the JSON to disk, validate it via bin/validate-report - (stdin). If validation fails, repair the in-memory report and re-validate; do not write a known-invalid report. Common failures the validator surfaces:
audit.commit = "HEAD" — resolve via git rev-parse --short=12 HEAD and use the hex hash.findings[].time_estimate outside the enum (5m, 1h, 1d, 1w, 1mo) — round UP to the next bucket (30m → 1h; 2h → 1d).fix_sequence.finding_ids, summary.top_blocker_ids, severity_inherited_from, self_check.{verified,unverified}_blockers) pointing to non-existent finding ids — synthesis-time copy-paste mistake.The validator exits 0 on a clean report; 2 with a stderr punch list otherwise. See bin/validate-report --help for what gets checked. Lessons-learned §12 has the post-mortem on why this gate exists (pouch's first end-to-end synthesis emitted both "HEAD" and "30m" and would have shipped them without manual catch).
Save the structured JSON + rendered markdown to tmp/rails-audit/. Filename pattern: report-YYYY-MM-DD. Use today's actual date.
Single-file mode (default — when rendered markdown is < 30 KB):
report-YYYY-MM-DD.json — structured source of truthreport-YYYY-MM-DD.md — rendered viewMulti-file mode (added v0.4 — when rendered markdown is ≥ 30 KB):
report-YYYY-MM-DD.json — structured source of truth (unchanged shape, single file)report-YYYY-MM-DD.md — top-level index with anchor links to the three sub-filesreport-YYYY-MM-DD-summary.md — executive summary, scorecards, fix sequence (the part stakeholders read)report-YYYY-MM-DD-findings.md — full punch list with all findings (the part engineers read)report-YYYY-MM-DD-appendix.md — tooling, coverage map, hotspots, ignored, cost (the audit-trail part)Force one mode with --single-file or --multi-file. Without a flag, the renderer measures the assembled markdown and chooses based on the 30 KB threshold.
The top-level report-YYYY-MM-DD.md in multi-file mode includes:
Sub-files inherit the same header so each is self-contained when read alone.
If a prior report-*.json exists in the same directory, populate trend{} in the new JSON before rendering.
≤200 words back to the user: top 3 blockers, recommended next action, links to both the .json and .md files.
brakeman, bundle-audit, rubocop, reek, rails_best_practices, simplecov already do detection well.app/controllers/api/authenticated_controller.rb:33 — not "in the auth controller".Optional .claude/rails-audit.yml at repo root. When missing, auto-detect from Gemfile + workflows + paths.
deploy_target: cloud_run # cloud_run | heroku | kamal | render | ecs | fly | other
job_adapter: cloudtasker # sidekiq | resque | good_job | cloudtasker | delayed_job | other
auth_strategy: warden_jwt # devise | warden | jwt | clearance | doorkeeper | custom
money_columns: # checked for Decimal type
- transactions.amount_cents
- payouts.amount
critical_paths: # paths held to higher coverage + security bar
- app/services/payments/
- app/controllers/webhooks/
- app/services/identity_platform/
ignore_paths: # excluded from smell/coverage checks
- app/admin/
- lib/legacy/
rubric.md — severity definitions and calibration examplestooling.md — tool contract, detection, and invocation patternsprompts.md — every user-facing question (P1–P7) with answers, defaults, and fallbacksschema/report.schema.json — JSON Schema (draft 2020-12) for the report — the contract; everything else is a viewoutput-template.md — markdown render template applied to the JSONdimensions/ — per-dimension check lists, one file per clusterexamples/sample-report.json — example structured outputexamples/sample-report.md — example rendered output (derived from sample-report.json)When working on a specific dimension, read the corresponding dimensions/*.md file. Don't try to keep all 18 dimensions in head at once.