Review the git diff origin/main output for the issues listed below. Be specific — cite file:line and suggest fixes. Skip anything that's fine. Only flag real problems.
Two-pass review:
All findings get action via Fix-First Review: obvious mechanical fixes are applied automatically, genuinely ambiguous issues are batched into a single user question.
Output format:
Pre-Landing Review: N issues (X critical, Y informational)
**AUTO-FIXED:**
- [file:line] Problem → fix applied
**NEEDS INPUT:**
- [file:line] Problem description
Recommended fix: suggested fix
If no issues found: Pre-Landing Review: No issues found.
Be terse. For each issue: one line describing the problem, one line with the fix. No preamble, no summaries, no "looks good overall."
.to_i/.to_f — use sanitize_sql_array or Arel)WHERE + update_allupdate_column/update_columns bypassing validations on fields that have or should have constraints.includes() missing for associations used in loops/views (especially avatar, attachments)rescue RecordNotUnique; retry (e.g., where(hash:).first then save! without handling concurrent insert)find_or_create_by on columns without unique DB index — concurrent calls can create duplicatesWHERE old_status = ? UPDATE SET new_status — concurrent updates can skip or double-apply transitionshtml_safe on user-controlled data (XSS) — check any .html_safe, raw(), or string interpolation into html_safe outputEMAIL_REGEXP, URI.parse, .strip) before persisting.When the diff introduces a new enum value, status string, tier name, or type constant:
%w[] lists containing sibling values (e.g., if adding "revise" to tiers, find every %w[quick lfg mega] and verify "revise" is included where needed).case/if-elsif chains. If existing code branches on the enum, does the new value fall through to a wrong default?
To do this: use Grep to find all references to the sibling values (e.g., grep for "lfg" or "mega" to find all tier consumers). Read each match. This step requires reading code OUTSIDE the diff.tool_classes/tools array.expects(:something).never missing when a code path should explicitly NOT call an external servicerand() / Random.rand for security-sensitive values — use SecureRandom instead==) on secrets or tokens — vulnerable to timing attacks.to_s or equivalent before serialization — { cores: 8 } vs { cores: "8" } produce different hashes<style> blocks in partials (re-parsed every render)Array#find in a loop instead of index_by hash).select{} filtering on DB results that could be a WHERE clause (unless intentionally avoiding leading-wildcard LIKE)CRITICAL (highest severity): INFORMATIONAL (lower severity):
├─ SQL & Data Safety ├─ Conditional Side Effects
├─ Race Conditions & Concurrency ├─ Magic Numbers & String Coupling
├─ LLM Output Trust Boundary ├─ Dead Code & Consistency
└─ Enum & Value Completeness ├─ LLM Prompt Issues
├─ Test Gaps
├─ Completeness Gaps
├─ Crypto & Entropy
├─ Time Window Safety
├─ Type Coercion at Boundaries
└─ View/Frontend
All findings are actioned via Fix-First Review. Severity determines
presentation order and classification of AUTO-FIX vs ASK — critical
findings lean toward ASK (they're riskier), informational findings
lean toward AUTO-FIX (they're more mechanical).
This heuristic is referenced by both /review and /ship. It determines whether
the agent auto-fixes a finding or asks the user.
AUTO-FIX (agent fixes without asking): ASK (needs human judgment):
├─ Dead code / unused variables ├─ Security (auth, XSS, injection)
├─ N+1 queries (missing .includes()) ├─ Race conditions
├─ Stale comments contradicting code ├─ Design decisions
├─ Magic numbers → named constants ├─ Large fixes (>20 lines)
├─ Missing LLM output validation ├─ Enum completeness
├─ Version/path mismatches ├─ Removing functionality
├─ Variables assigned but never read └─ Anything changing user-visible
└─ Inline styles, O(n*m) view lookups behavior
Rule of thumb: If the fix is mechanical and a senior engineer would apply it without discussion, it's AUTO-FIX. If reasonable engineers could disagree about the fix, it's ASK.
Critical findings default toward ASK (they're inherently riskier). Informational findings default toward AUTO-FIX (they're more mechanical).
present? redundant with length > 20).reject on an element that's never in the array)