aios-core
Version:
Synkra AIOS: AI-Orchestrated System for Full Stack Development - Core Framework
861 lines (658 loc) • 23.8 kB
Markdown
# pre-push-quality-gate.md
**Task**: Pre-Push Quality Gate Validation (Repository-Agnostic)
**Purpose**: Execute comprehensive quality checks before pushing code to remote repository, ensuring code quality, tests, and security standards are met.
**When to use**: Before pushing code to GitHub, always via `@github-devops *pre-push` command.
## Execution Modes
**Choose your execution mode:**
### 1. YOLO Mode - Fast, Autonomous (0-1 prompts)
- Autonomous decision making with logging
- Minimal user interaction
- **Best for:** Simple, deterministic tasks
### 2. Interactive Mode - Balanced, Educational (5-10 prompts) **[DEFAULT]**
- Explicit decision checkpoints
- Educational explanations
- **Best for:** Learning, complex decisions
### 3. Pre-Flight Planning - Comprehensive Upfront Planning
- Task analysis phase (identify all ambiguities)
- Zero ambiguity execution
- **Best for:** Ambiguous requirements, critical work
**Parameter:** `mode` (optional, default: `interactive`)
## Task Definition (AIOS Task Format V1.0)
```yaml
task: githubDevopsPrePushQualityGate()
responsável: Gage (Automator)
responsavel_type: Agente
atomic_layer: Organism
**Entrada:**
- campo: task
tipo: string
origem: User Input
obrigatório: true
validação: Must be registered task
- campo: parameters
tipo: object
origem: User Input
obrigatório: false
validação: Valid task parameters
- campo: mode
tipo: string
origem: User Input
obrigatório: false
validação: yolo|interactive|pre-flight
**Saída:**
- campo: execution_result
tipo: object
destino: Memory
persistido: false
- campo: logs
tipo: array
destino: File (.ai/logs/*)
persistido: true
- campo: state
tipo: object
destino: State management
persistido: true
```
## Constitutional Gate: Quality First
> **Reference:** Constitution Article V - Quality First (MUST)
> **Severity:** BLOCK
> **Enforcement:** Mandatory checks before any push
```yaml
constitutional_gate:
article: V
name: Quality First
severity: BLOCK
validation:
required_checks:
- name: lint
command: npm run lint
must_pass: true
- name: typecheck
command: npm run typecheck
must_pass: true
- name: test
command: npm test
must_pass: true
- name: build
command: npm run build
must_pass: true
- name: coderabbit
check: No CRITICAL issues
must_pass: true
- name: story_status
check: Story status is "Done" or "Ready for Review"
must_pass: true
on_violation:
action: BLOCK
message: |
CONSTITUTIONAL VIOLATION: Article V - Quality First
Push blocked due to failed quality checks.
Failed checks:
{list_failed_checks}
Resolution: Fix all failing checks before pushing.
Run: npm run lint && npm run typecheck && npm test && npm run build
bypass:
allowed: false
reason: "Quality First is NON-NEGOTIABLE per Constitution"
```
## Pre-Conditions
**Purpose:** Validate prerequisites BEFORE task execution (blocking)
**Checklist:**
```yaml
pre-conditions:
- [ ] Constitutional gate passed (Article V: Quality First)
tipo: constitutional-gate
blocker: true
validação: |
All quality checks must pass: lint, typecheck, test, build
error_message: "Constitutional violation - Quality First checks failed"
- [ ] Task is registered; required parameters provided; dependencies met
tipo: pre-condition
blocker: true
validação: |
Check task is registered; required parameters provided; dependencies met
error_message: "Pre-condition failed: Task is registered; required parameters provided; dependencies met"
```
## Post-Conditions
**Purpose:** Validate execution success AFTER task completes
**Checklist:**
```yaml
post-conditions:
- [ ] Task completed; exit code 0; expected outputs created
tipo: post-condition
blocker: true
validação: |
Verify task completed; exit code 0; expected outputs created
error_message: "Post-condition failed: Task completed; exit code 0; expected outputs created"
```
## Acceptance Criteria
**Purpose:** Definitive pass/fail criteria for task completion
**Checklist:**
```yaml
acceptance-criteria:
- [ ] Task completed as expected; side effects documented
tipo: acceptance-criterion
blocker: true
validação: |
Assert task completed as expected; side effects documented
error_message: "Acceptance criterion not met: Task completed as expected; side effects documented"
```
## Tools
**External/shared resources used by this task:**
- **Tool:** git
- **Purpose:** Version control operations
- **Source:** System CLI
- **Tool:** npm
- **Purpose:** Run quality scripts (lint, test, typecheck, build)
- **Source:** System CLI
- **Tool:** gh (GitHub CLI)
- **Purpose:** GitHub PR operations
- **Source:** System CLI
## Error Handling
**Strategy:** retry
**Common Errors:**
1. **Error:** Task Not Found
- **Cause:** Specified task not registered in system
- **Resolution:** Verify task name and registration
- **Recovery:** List available tasks, suggest similar
2. **Error:** Invalid Parameters
- **Cause:** Task parameters do not match expected schema
- **Resolution:** Validate parameters against task definition
- **Recovery:** Provide parameter template, reject execution
3. **Error:** Execution Timeout
- **Cause:** Task exceeds maximum execution time
- **Resolution:** Optimize task or increase timeout
- **Recovery:** Kill task, cleanup resources, log state
## Performance
**Expected Metrics:**
```yaml
duration_expected: 5-15 min (estimated)
cost_estimated: $0.003-0.010
token_usage: ~3,000-10,000 tokens
```
**Optimization Notes:**
- Break into smaller workflows; implement checkpointing; use async processing where possible
## Metadata
```yaml
story: N/A
version: 1.0.0
dependencies:
- N/A
tags:
- automation
- workflow
updated_at: 2025-11-17
```
## Prerequisites
- Git repository with changes to push
- package.json with npm scripts (gracefully handles missing scripts)
- Repository context detected (run `aios init` if needed)
## Quality Gate Checks
### 1. Repository Context Detection
```javascript
const { detectRepositoryContext } = require('./../scripts/repository-detector');
const context = detectRepositoryContext();
if (!context) {
console.error('❌ Unable to detect repository context');
console.error('Run "aios init" to configure installation mode');
process.exit(1);
}
console.log(`\n🚀 Pre-Push Quality Gate`);
console.log(`Repository: ${context.repositoryUrl}`);
console.log(`Mode: ${context.mode}`);
console.log(`Package: ${context.packageName} v${context.packageVersion}\n`);
```
### 2. Check for Uncommitted Changes
```bash
git status --porcelain
```
If output is not empty, fail with message:
```
❌ Uncommitted changes detected!
Please commit or stash changes before pushing:
git add .
git commit -m "your message"
```
### 3. Check for Merge Conflicts
```bash
git diff --check
```
If conflicts detected, fail with message:
```
❌ Merge conflicts detected!
Resolve conflicts before pushing.
```
### 4. Run npm run lint (if script exists)
```javascript
function runNpmScript(scriptName, projectRoot) {
const packageJsonPath = path.join(projectRoot, 'package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
if (!packageJson.scripts || !packageJson.scripts[scriptName]) {
console.log(`⚠️ Script "${scriptName}" not found - skipping`);
return { skipped: true };
}
try {
execSync(`npm run ${scriptName}`, {
cwd: projectRoot,
stdio: 'inherit'
});
console.log(`✓ ${scriptName} PASSED`);
return { passed: true };
} catch (error) {
console.error(`❌ ${scriptName} FAILED`);
return { passed: false, error };
}
}
```
### 5. Run npm test (if script exists)
Same logic as lint, but for `npm test`.
### 6. Run npm run typecheck (if script exists)
Same logic as lint, but for `npm run typecheck`.
### 7. Run npm run build (if script exists)
Same logic as lint, but for `npm run build`.
### 8. Run CodeRabbit CLI Review (TR-3.14.12)
```javascript
const { execSync } = require('child_process');
function runCodeRabbitReview(projectRoot) {
console.log('\n🐰 Running CodeRabbit CLI Review...');
console.log('⏱️ This may take 7-30 minutes. Please wait...\n');
try {
// Construct WSL command with proper paths
const wslProjectPath = projectRoot
.replace(/\\/g, '/')
.replace(/^([A-Z]):/, (match, drive) => `/mnt/${drive.toLowerCase()}`);
const coderabbitCommand = `wsl bash -c 'cd ${wslProjectPath} && ~/.local/bin/coderabbit --prompt-only -t uncommitted'`;
console.log(`Executing: ${coderabbitCommand}\n`);
// Execute with 15-minute timeout
const output = execSync(coderabbitCommand, {
cwd: projectRoot,
encoding: 'utf8',
timeout: 900000, // 15 minutes
stdio: 'pipe',
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
});
// Parse CodeRabbit output
const results = parseCodeRabbitOutput(output);
console.log(`\n✅ CodeRabbit Review Complete:`);
console.log(` - CRITICAL: ${results.critical}`);
console.log(` - HIGH: ${results.high}`);
console.log(` - MEDIUM: ${results.medium}`);
console.log(` - LOW: ${results.low}`);
// Determine gate impact
const gateImpact = determineCodeRabbitGate(results);
return { gateImpact, results, rawOutput: output };
} catch (error) {
// Handle timeout
if (error.killed && error.signal === 'SIGTERM') {
console.error('❌ CodeRabbit review timed out after 15 minutes');
console.error(' Review may still be processing. Check manually.');
return { gateImpact: 'FAIL', error: 'Timeout', timeout: true };
}
// Handle authentication errors
if (error.stderr && error.stderr.includes('not authenticated')) {
console.error('❌ CodeRabbit not authenticated');
console.error(' Run: wsl bash -c "~/.local/bin/coderabbit auth status"');
return { gateImpact: 'FAIL', error: 'Not authenticated' };
}
// Handle command not found
if (error.stderr && error.stderr.includes('command not found')) {
console.error('❌ CodeRabbit CLI not found in WSL');
console.error(' Expected location: ~/.local/bin/coderabbit');
console.error(' Verify: wsl bash -c "~/.local/bin/coderabbit --version"');
return { gateImpact: 'FAIL', error: 'Not installed' };
}
// Generic error with output for debugging
console.error('❌ CodeRabbit review failed:', error.message);
if (error.stdout) {
console.log('Output:', error.stdout.toString().substring(0, 500));
}
return { gateImpact: 'CONCERNS', error: error.message };
}
}
function parseCodeRabbitOutput(output) {
// CodeRabbit outputs issues with type markers
const lines = output.split('\n');
let critical = 0;
let high = 0;
let medium = 0;
let low = 0;
for (const line of lines) {
// Check for issue type markers
if (line.includes('Type: critical') || line.match(/\bCRITICAL\b/i)) {
critical++;
} else if (line.includes('Type: high') || line.match(/\bHIGH\b/i)) {
high++;
} else if (line.includes('Type: potential_issue') || line.match(/\bMEDIUM\b/i)) {
medium++;
} else if (line.includes('Type: refactor_suggestion') || line.match(/\bLOW\b/i)) {
low++;
}
}
return { critical, high, medium, low };
}
function determineCodeRabbitGate(results) {
// CRITICAL issues = auto-fail (block push)
if (results.critical > 0) {
console.log(`\n❌ FAIL: ${results.critical} CRITICAL issue(s) found - MUST FIX`);
return 'FAIL';
}
// HIGH issues = concerns (warn but allow push)
if (results.high > 0) {
console.log(`\n⚠️ CONCERNS: ${results.high} HIGH issue(s) found - recommend fix`);
return 'CONCERNS';
}
// Only MEDIUM or LOW = pass with notes
if (results.medium > 0 || results.low > 0) {
console.log(`\n✅ PASS: Only ${results.medium} MEDIUM and ${results.low} LOW issues`);
} else {
console.log(`\n✅ PASS: No issues found`);
}
return 'PASS';
}
```
**Usage in pre-push flow:**
```javascript
const coderabbitResult = runCodeRabbitReview(process.cwd());
if (coderabbitResult.gateImpact === 'FAIL') {
console.error('\n❌ CodeRabbit quality gate FAILED - cannot push');
process.exit(1);
}
if (coderabbitResult.gateImpact === 'CONCERNS') {
// Ask user for confirmation
const { confirm } = await inquirer.prompt([{
type: 'confirm',
name: 'confirm',
message: 'CodeRabbit found HIGH issues. Continue anyway?',
default: false
}]);
if (!confirm) {
console.log('Push cancelled - please address HIGH issues');
process.exit(2);
}
}
```
### 9. Run Security Scan (TR-3.14.11)
```javascript
const { execSync } = require('child_process');
const path = require('path');
function runSecurityScan(storyId, storyPath, projectRoot) {
console.log('\n🔒 Running Security Scan (SAST)...\n');
try {
// Execute security-scan.md task
const securityScanPath = path.join(__dirname, 'security-scan.md');
// For now, run security checks directly
const results = {
audit: runNpmAudit(projectRoot),
eslint: runESLintSecurity(projectRoot),
secrets: runSecretDetection(projectRoot)
};
// Determine gate impact
const gateImpact = determineSecurityGate(results);
console.log(`\nSecurity Scan Complete: ${gateImpact}`);
return { gateImpact, results };
} catch (error) {
console.error('❌ Security scan failed:', error.message);
return { gateImpact: 'FAIL', error };
}
}
function runNpmAudit(projectRoot) {
try {
const output = execSync('npm audit --audit-level=moderate --json', {
cwd: projectRoot
}).toString();
const results = JSON.parse(output);
const vulns = results.metadata?.vulnerabilities || {};
return {
critical: vulns.critical || 0,
high: vulns.high || 0,
moderate: vulns.moderate || 0,
low: vulns.low || 0,
gate: vulns.critical > 0 ? 'FAIL' : (vulns.high > 0 ? 'CONCERNS' : 'PASS')
};
} catch (error) {
// npm audit exits with 1 if vulnerabilities found
if (error.stdout) {
const results = JSON.parse(error.stdout.toString());
const vulns = results.metadata?.vulnerabilities || {};
return {
critical: vulns.critical || 0,
high: vulns.high || 0,
moderate: vulns.moderate || 0,
low: vulns.low || 0,
gate: vulns.critical > 0 ? 'FAIL' : (vulns.high > 0 ? 'CONCERNS' : 'PASS')
};
}
console.warn('⚠️ npm audit failed - skipping dependency check');
return { gate: 'PASS', skipped: true };
}
}
function runESLintSecurity(projectRoot) {
// Check if ESLint security config exists
const eslintConfigPath = path.join(projectRoot, '.eslintrc.security.json');
if (!fs.existsSync(eslintConfigPath)) {
console.log('⚠️ .eslintrc.security.json not found - skipping ESLint security');
return { gate: 'PASS', skipped: true };
}
try {
execSync('npx eslint . --ext .js,.ts --config .eslintrc.security.json', {
cwd: projectRoot,
stdio: 'pipe'
});
return { gate: 'PASS', issues: 0 };
} catch (error) {
// ESLint exits with 1 if issues found
const output = error.stdout?.toString() || '';
const errorCount = (output.match(/error/g) || []).length;
const warningCount = (output.match(/warning/g) || []).length;
return {
gate: errorCount > 0 ? 'FAIL' : (warningCount > 0 ? 'CONCERNS' : 'PASS'),
errors: errorCount,
warnings: warningCount
};
}
}
function runSecretDetection(projectRoot) {
try {
execSync('npx secretlint "**/*"', {
cwd: projectRoot,
stdio: 'pipe'
});
return { gate: 'PASS', secretsFound: 0 };
} catch (error) {
// secretlint exits with 1 if secrets found
return { gate: 'FAIL', secretsFound: 1 };
}
}
function determineSecurityGate(results) {
// Secrets are auto-fail
if (results.secrets.gate === 'FAIL') return 'FAIL';
// Any FAIL → overall FAIL
if (results.audit.gate === 'FAIL' || results.eslint.gate === 'FAIL') return 'FAIL';
// Any CONCERNS → overall CONCERNS
if (results.audit.gate === 'CONCERNS' || results.eslint.gate === 'CONCERNS') return 'CONCERNS';
// All PASS → overall PASS
return 'PASS';
}
```
### 9.1 Impact Analysis (Code Intelligence — Advisory Only)
> **Added by:** Story NOG-7 (DevOps Pre-Push Impact Analysis)
> **Behavior:** Advisory only — NEVER blocks push. Auto-skips if code intelligence unavailable.
```javascript
const { assessPrePushImpact, classifyRiskLevel } = require('.aios-core/core/code-intel/helpers/devops-helper');
async function runImpactAnalysis(changedFiles) {
// Auto-skip if code intelligence unavailable
const { isCodeIntelAvailable } = require('.aios-core/core/code-intel');
if (!isCodeIntelAvailable()) {
console.log('ℹ️ Code intelligence not available — skipping impact analysis');
return { skipped: true };
}
console.log('\n📊 Running Impact Analysis...\n');
const result = await assessPrePushImpact(changedFiles);
if (!result) {
console.log('ℹ️ Impact analysis returned no data — skipping');
return { skipped: true };
}
// Display formatted report
console.log(result.report);
// HIGH risk: add extra warning (advisory, does not block)
if (result.riskLevel === 'HIGH') {
console.log('\n⚠️ HIGH RISK detected. Additional confirmation recommended before push.');
}
return {
skipped: false,
riskLevel: result.riskLevel,
blastRadius: result.impact ? result.impact.blastRadius : 0,
report: result.report,
};
}
```
**Integration with Summary Report:**
Add impact analysis results to the summary report section:
```
Impact Analysis:
📊 Blast Radius: {N} files affected
📊 Risk Level: {LOW|MEDIUM|HIGH}
{if HIGH: ⚠️ HIGH RISK: {N} files affected. Confirm push?}
{if skipped: ℹ️ Skipped (code intelligence not available)}
```
**Important:** This step is purely advisory. A HIGH risk level does NOT change the overall gate status from PASS to FAIL. It only adds an informational warning and may prompt additional user confirmation.
### 10. Verify Story Status (Optional - if using story-driven workflow)
```javascript
function checkStoryStatus(storyPath) {
if (!storyPath || !fs.existsSync(storyPath)) {
console.log('⚠️ No story file specified - skipping story status check');
return { skipped: true };
}
const storyContent = fs.readFileSync(storyPath, 'utf8');
// Look for status: "Done" or status: "Ready for Review"
const statusMatch = storyContent.match(/status:\s*["']?(Done|Ready for Review|InProgress)["']?/i);
if (!statusMatch) {
console.log('⚠️ Unable to determine story status - skipping');
return { skipped: true };
}
const status = statusMatch[1];
if (status === 'Done' || status === 'Ready for Review') {
console.log(`✓ Story status: ${status}`);
return { passed: true, status };
} else {
console.log(`⚠️ Story status: ${status} (expected Done or Ready for Review)`);
return { passed: false, status };
}
}
```
## Summary Report
After all checks complete, present summary:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🚀 Pre-Push Quality Gate Summary
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Repository: {repositoryUrl}
Package: {packageName} v{version}
Mode: {framework-development | project-development}
Quality Checks:
✓ No uncommitted changes
✓ No merge conflicts
✓ npm run lint PASSED
✓ npm test PASSED
✓ npm run typecheck PASSED
✓ npm run build PASSED
✓ Security scan PASSED
⚠️ Story status SKIPPED (no story file)
Impact Analysis (Advisory):
📊 Blast Radius: {N} files affected
📊 Risk Level: LOW | MEDIUM | HIGH
ℹ️ Advisory only — does not affect gate status
Security Scan Results:
✓ Dependencies: 0 critical, 0 high, 2 moderate, 5 low
✓ Code patterns: No security issues
✓ Secrets: No secrets detected
Overall Status: ✅ READY TO PUSH
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Proceed with push to remote? (Y/n)
```
### If FAIL status:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
❌ Pre-Push Quality Gate FAILED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Quality Checks:
❌ npm test FAILED
❌ Security scan FAILED (CRITICAL vulnerabilities)
Security Issues:
❌ Dependencies: 2 CRITICAL, 5 HIGH vulnerabilities
❌ Secrets: 1 API key detected in config/db.js
Overall Status: ❌ BLOCKED - Cannot push to remote
Action Required:
1. Fix failing tests
2. Run: npm audit fix --force
3. Remove secrets from codebase
4. Re-run quality gate
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
```
### If CONCERNS status:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
⚠️ Pre-Push Quality Gate: CONCERNS
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Quality Checks:
✓ All tests passed
⚠️ Security scan CONCERNS (HIGH vulnerabilities)
Security Issues:
⚠️ Dependencies: 0 CRITICAL, 3 HIGH, 10 MODERATE vulnerabilities
⚠️ Code patterns: 2 medium-severity issues
Overall Status: ⚠️ CONCERNS - Review recommended
Recommendations:
- Address HIGH vulnerabilities before production
- Review medium-severity code patterns
- Consider running: npm audit fix
Proceed with push anyway? (y/N)
```
## User Approval
```javascript
async function requestPushApproval(gateStatus) {
if (gateStatus === 'FAIL') {
console.log('\n❌ Quality gate FAILED. Cannot proceed with push.');
process.exit(1);
}
const { confirm } = await inquirer.prompt([
{
type: 'confirm',
name: 'confirm',
message: gateStatus === 'PASS'
? 'Proceed with push to remote?'
: 'Quality gate has CONCERNS. Proceed anyway?',
default: gateStatus === 'PASS'
}
]);
return confirm;
}
```
## Integration with @github-devops Agent
Called via `@github-devops *pre-push` command.
## Exit Codes
- `0` - All checks passed, user approved
- `1` - Quality gate failed (blocking)
- `2` - User declined to push
## Notes
- Works with ANY repository (framework or project)
- Gracefully handles missing npm scripts
- Security scan is mandatory (TR-3.14.11)
- User always has final approval
- Detailed logging for troubleshooting
## Handoff
next_agent: @devops
next_command:
condition: All quality checks PASS
alternatives:
- agent: @dev, command: *run-tests, condition: Quality checks FAIL, needs fixes