# Quality Gates Reference (v4)

> **Used by:** step-01-v4-execute.md and step-02-v4-verify.md

## Gate Definitions

| Gate | Command | Blocking | When |
|------|---------|----------|------|
| Backend build | `dotnet build --no-restore` | YES | After each module |
| Backend tests | `dotnet test --no-build` | YES | After build passes |
| Frontend typecheck | `npm run typecheck` | YES | After backend gates |
| Frontend tests | `npm test -- --run` | YES | After typecheck |
| MCP security | `validate_security` | YES | After all tests |
| MCP code review | `review_code` | YES (if perm mismatch) | After security |
| MCP conventions | `validate_conventions` | NO (report) | After code review |
| MCP test conventions | `validate_test_conventions` | NO (report) | After conventions |
| BR coverage | grep implementation (enriched) | YES (100%) | After MCP gates |
| File reconciliation | compare expectedFiles vs disk | YES | After BR coverage |
| Spec fidelity | compare specs vs code | NO (report) | After file reconciliation |
| Stub test detection | grep stub patterns | YES | During test gate |

## Stub Test Patterns (AUTO-REJECT)

```
Assert.True(true)
Assert.Pass()
[Fact] + empty body
// TODO
throw new NotImplementedException
[Test] public void Test1() { }
```

If any pattern found in test files → gate FAILS.

## Gate Failure Recovery (v4)

In v4 mode, gate failures are handled IN THE SAME SESSION:

1. Gate fails → error output captured
2. Error shown to Claude (compaction preserves all prior context)
3. `/apex -d "Fix: {error description}"` — Claude sees what it generated + the error
4. Re-run gate
5. Max 3 fix attempts per gate before marking as failed

**Key difference from v3:** No context loss between retry attempts. Claude knows exactly what code it wrote and what went wrong.

## Gate Execution Order

```
dotnet build
    ↓ (pass)
dotnet test
    ↓ (pass)
npm run typecheck
    ↓ (pass)
npm test
    ↓ (pass)
validate_security
    ↓ (pass)
validate_conventions
    ↓ (report)
review_code (semantic permission check)
    ↓ (pass)
validate_test_conventions
    ↓ (report)
BR coverage check
    ↓ (100%)
File reconciliation
    ↓ (all present)
Spec fidelity (non-blocking report)
    ↓ (report)
MODULE COMPLETE
```

## BR Coverage — Enriched Verification

When `prd.specificationFiles` is present and companion files are available, the BR coverage gate uses the enriched `brToCodeMapping` (with `statement`, `formula`, `example`) for verification.

**Standard check:** grep each `brToCodeMapping[].ruleId` in the codebase to confirm implementation.

**Enriched fix context:** When a BR is NOT covered, provide full context for the fix:

```
MISSING BR IMPLEMENTATION:
  Rule: {ruleId}
  Statement: {statement}
  Example: {example}
  Formula: {formula}
  Entities: {entities[]}
  Category: {category}
  Severity: {severity}
  Expected in: {implementationPoints[].component} ({implementationPoints[].layer})
```

This gives `/apex` or the developer the complete context to implement the missing BR without re-reading the BA artifacts.

## Gate: Specification Fidelity (non-blocking report)

> **When:** After file reconciliation. **Blocking:** NO (report only).
> **Requires:** `prd.specificationFiles` present with companion files.

Compares generated code against companion specification files to measure implementation fidelity.

**Checks:**

```javascript
if (prd.specificationFiles) {
  const specsDir = '.ralph';
  const entitiesSpec = readJSON(`${specsDir}/${prd.specificationFiles.entities}`);
  const rulesSpec = readJSON(`${specsDir}/${prd.specificationFiles.rules}`);
  let totalChecks = 0, passedChecks = 0;

  // 1. Entity property coverage
  for (const entity of (entitiesSpec.entities || [])) {
    const entityFile = findFile(`src/**/Domain/**/${entity.name}.cs`);
    if (entityFile) {
      const content = readFile(entityFile);
      for (const attr of (entity.attributes || [])) {
        totalChecks++;
        if (content.includes(attr.name)) passedChecks++;
        else console.warn(`SPEC DRIFT: ${entity.name}.${attr.name} not found in ${entityFile}`);
      }
    }
  }

  // 2. Validator coverage for BR-VAL-*
  for (const rule of (rulesSpec.rules || [])) {
    if (rule.id?.startsWith('BR-VAL-')) {
      totalChecks++;
      const validatorFiles = findFiles(`src/**/Validators/**/*Validator.cs`);
      const found = validatorFiles.some(f => readFile(f).includes(rule.id));
      if (found) passedChecks++;
      else console.warn(`SPEC DRIFT: ${rule.id} (${rule.statement?.slice(0, 50)}...) has no Validator implementation`);
    }
  }

  // 3. Calculation coverage for BR-CALC-*
  for (const rule of (rulesSpec.rules || [])) {
    if (rule.id?.startsWith('BR-CALC-') && rule.formula) {
      totalChecks++;
      const serviceFiles = findFiles(`src/**/Services/**/*Service.cs`);
      const found = serviceFiles.some(f => readFile(f).includes(rule.id));
      if (found) passedChecks++;
      else console.warn(`SPEC DRIFT: ${rule.id} formula "${rule.formula}" has no Service implementation`);
    }
  }

  const fidelityScore = totalChecks > 0 ? Math.round((passedChecks / totalChecks) * 100) : 100;
  console.log(`SPEC FIDELITY: ${fidelityScore}% (${passedChecks}/${totalChecks} checks passed)`);
}
```

**Output:**
```
SPEC FIDELITY: 92% (23/25 checks passed)
  DRIFT: Employee.TerminationDate not found in Employee.cs
  DRIFT: BR-CALC-002 formula "netSalary = grossSalary - deductions" has no Service implementation
```

This gate does NOT block — it provides visibility into specification coverage gaps.
