Skip to content

Quality Gates

Sequant enforces quality checks at every phase. Understanding these gates helps you write code that passes on the first try.

Quality gates are automated checks that run during the /qa phase. They verify:

  1. AC Adherence — Does the code satisfy acceptance criteria?
  2. Type Safety — Are types properly defined?
  3. Security — Are there vulnerabilities?
  4. Scope — Are changes within the issue scope?
  5. CI Status — Are GitHub CI checks passing?
  6. Build Verification — Is build failure a regression or pre-existing?

The most important gate: does the code do what the issue asked for?

  1. Reads acceptance criteria from the issue
  2. Maps each AC item to code changes
  3. Verifies each criterion is satisfied

Issue AC:

- [ ] AC-1: Add login button to header
- [ ] AC-2: Button redirects to /login on click
- [ ] AC-3: Button shows "Sign In" text

QA Check:

✅ AC-1: LoginButton component added to Header.tsx
✅ AC-2: onClick handler calls router.push('/login')
✅ AC-3: Button text is "Sign In"
  • Write clear, testable acceptance criteria
  • Each AC should map to a specific code change
  • Use verifiable language (“Button shows X” not “Button looks good”)

Catches type-related issues that can cause runtime errors.

IssueExampleWhy It’s Bad
any typeconst data: any = ...Defeats TypeScript’s purpose
as any cast(data as any).fieldBypasses type checking
Missing typesfunction foo(x) {...}Implicit any
Non-null assertionsdata!.fieldAssumes value exists
// ❌ Flagged
const response: any = await fetch('/api/data');
const name = (response as any).user.name;
// ✅ Good
interface ApiResponse {
user: { name: string };
}
const response: ApiResponse = await fetch('/api/data').then(r => r.json());
const name = response.user.name;
  • Define interfaces for API responses
  • Use generics instead of any
  • Enable strict mode in tsconfig.json

Identifies common security vulnerabilities.

VulnerabilityExample
SQL InjectionRaw SQL with user input
XSSUnescaped HTML output
Command Injectionexec() with user input
Path TraversalFile paths from user input
Hardcoded SecretsAPI keys in code
// ❌ Flagged: Command injection
const output = exec(`git log --author="${userInput}"`);
// ✅ Good: Sanitized input
const sanitized = userInput.replace(/[^a-zA-Z0-9]/g, '');
const output = exec(`git log --author="${sanitized}"`);
  • Never trust user input
  • Use parameterized queries
  • Escape output before rendering
  • Store secrets in environment variables

Semgrep provides AST-aware static analysis that catches issues regex patterns miss.

  1. Detects your project stack (Next.js, Python, Go, etc.)
  2. Applies stack-specific rulesets (e.g., p/typescript, p/react, p/security-audit)
  3. Loads custom rules from .sequant/semgrep-rules.yaml if present
  4. Reports findings by severity (critical, warning, info)
CategoryExamples
SecuritySQL injection, XSS, command injection, hardcoded secrets
Code QualityUnused variables, unreachable code, deprecated APIs
Best PracticesMissing error handling, unsafe type assertions
SeverityVerdict Impact
ERROR / CriticalBlocks mergeAC_NOT_MET
WARNINGNon-blocking — noted for review
INFONon-blocking — suggestions only

Add project-specific rules in .sequant/semgrep-rules.yaml:

rules:
- id: no-console-log
pattern: console.log(...)
message: "Remove console.log before merging"
severity: WARNING
languages: [typescript, javascript]
paths:
exclude:
- "**/*.test.*"

See docs/examples/semgrep-rules.example.yaml for more examples.

If Semgrep is not installed, /qa skips the scan with a message:

⚠️ Semgrep not installed (optional)
Install with: pip install semgrep

This ensures Semgrep is opt-in and doesn’t block workflows.

QA checks GitHub CI status before finalizing verdicts. This prevents premature READY_FOR_MERGE when CI is still running.

  1. Detects if a PR exists for the current branch
  2. Runs gh pr checks to get CI status
  3. Maps CI status to AC status for CI-related criteria
  4. Factors CI status into the final verdict
CI StateCI ConclusionAC StatusVerdict Impact
completedsuccessMETNo impact
completedfailureNOT_METBlocks merge
in_progress-PENDINGNEEDS_VERIFICATION
queued-PENDINGNEEDS_VERIFICATION
(no checks)-N/ANo CI configured

QA identifies AC items that depend on CI by matching patterns:

  • “Tests pass in CI”
  • “CI passes”
  • “Build succeeds in CI”
  • “GitHub Actions pass”
  • “Pipeline passes”
AC-1: Add login button → MET
AC-2: Tests pass in CI → PENDING (CI still running)
Verdict: NEEDS_VERIFICATION
Reason: CI checks not yet complete

If the repository has no CI checks, CI-related AC items are marked N/A with no impact on verdict.

When npm run build fails, QA verifies whether the failure is a regression (new) or pre-existing (already on main).

Without verification, QA might dismiss build failures as “unrelated to our changes” when they’re actually regressions introduced by the PR.

  1. Run npm run build on feature branch
  2. If build fails, run build on main branch (via main repo directory)
  3. Compare exit codes and error messages
  4. Classify as regression, pre-existing, or unknown
Feature BuildMain BuildError MatchClassification
❌ Fail✅ PassN/ARegression — failure introduced by PR
❌ Fail❌ FailSame errorPre-existing — not blocking
❌ Fail❌ FailDifferentUnknown — manual review needed
✅ Pass*N/AN/A — no verification needed
ClassificationVerdict Impact
Regression detectedBlocks mergeAC_NOT_MET
Pre-existing failureNon-blocking — documented only
UnknownAC_MET_BUT_NOT_A_PLUS — manual review
Build passesNo impact
### Build Verification
| Check | Status |
|-------|--------|
| Feature branch build | ❌ Failed |
| Main branch build | ❌ Failed |
| Error match | ✅ Same error |
| Regression | **No** (pre-existing) |
**Note:** Build failure is pre-existing on main branch. Not blocking this PR.

After running all checks, QA issues one of four verdicts:

VerdictMeaningAction
READY_FOR_MERGEAll AC met, high code qualityMerge the PR
AC_MET_BUT_NOT_A_PLUSAll AC met, minor improvements suggestedCan merge, consider suggestions
NEEDS_VERIFICATIONAll AC met or pending, awaiting external verificationComplete verification, re-run QA
AC_NOT_METOne or more AC not fully metFix issues before merge

Verdicts are determined by AC status counts:

  1. If any AC is NOT_MET or PARTIALLY_MET:AC_NOT_MET
  2. If any AC is PENDING:NEEDS_VERIFICATION
  3. If improvements are suggested:AC_MET_BUT_NOT_A_PLUS
  4. Otherwise:READY_FOR_MERGE

Important: PARTIALLY_MET is treated as NOT_MET for verdict purposes. Partial implementations block merge.

AC-1: Add login button → MET
AC-2: Button redirects → MET
AC-3: CI passes → PENDING (awaiting CI)
Verdict: NEEDS_VERIFICATION
Reason: AC-3 requires external verification

Detects changes unrelated to the issue being worked on.

  • Files changed that aren’t mentioned in the plan
  • Refactors unrelated to the AC
  • “While I was here” improvements
  • Formatting changes to untouched code

Issue: “Add logout button”

// ❌ Flagged: Unrelated change
import { Button } from './Button';
import { Button } from '@/components/Button'; // Unrelated path change
// ❌ Flagged: Scope creep
// Also refactored the login button while here
const LoginButton = () => ...
// ✅ Good: Only the requested change
const LogoutButton = () => <Button onClick={logout}>Sign Out</Button>;
  • Keep changes focused on the issue
  • Save “while I was here” improvements for separate issues
  • If you find a bug, create a new issue instead of fixing inline

When gates fail, the quality loop automatically fixes issues.

┌──────┐ ┌───────────┐ ┌──────────┐
│ QA │───▶│ Analyze │───▶│ Fix │──┐
└──────┘ │ Failures │ └──────────┘ │
▲ └───────────┘ │
│ │
└───────────────────────────────────────┘
(up to 3 iterations)
Terminal window
# Automatic with run command
npx sequant run 123 --quality-loop
# Manual after QA
/loop 123
  • Type errors (adds proper types)
  • Missing AC items (implements missing features)
  • Security issues (applies safe patterns)
  • Test failures (fixes failing tests)
  • Architectural decisions
  • Ambiguous requirements
  • Breaking changes to public APIs
  • Security issues requiring design changes

Projects can add custom quality gates via constitution:

.claude/memory/constitution.md
## Quality Requirements
1. Test coverage must exceed 80%
2. All public functions need JSDoc comments
3. No console.log in production code

These get checked during /qa along with standard gates.