Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

risk-governance.md 20KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. # Risk Governance and Gatekeeping
  2. ## Principle
  3. Risk governance transforms subjective "should we ship?" debates into objective, data-driven decisions. By scoring risk (probability × impact), classifying by category (TECH, SEC, PERF, etc.), and tracking mitigation ownership, teams create transparent quality gates that balance speed with safety.
  4. ## Rationale
  5. **The Problem**: Without formal risk governance, releases become political—loud voices win, quiet risks hide, and teams discover critical issues in production. "We thought it was fine" isn't a release strategy.
  6. **The Solution**: Risk scoring (1-3 scale for probability and impact, total 1-9) creates shared language. Scores ≥6 demand documented mitigation. Scores = 9 mandate gate failure. Every acceptance criterion maps to a test, and gaps require explicit waivers with owners and expiry dates.
  7. **Why This Matters**:
  8. - Removes ambiguity from release decisions (objective scores vs subjective opinions)
  9. - Creates audit trail for compliance (FDA, SOC2, ISO require documented risk management)
  10. - Identifies true blockers early (prevents last-minute production fires)
  11. - Distributes responsibility (owners, mitigation plans, deadlines for every risk >4)
  12. ## Pattern Examples
  13. ### Example 1: Risk Scoring Matrix with Automated Classification (TypeScript)
  14. **Context**: Calculate risk scores automatically from test results and categorize by risk type
  15. **Implementation**:
  16. ```typescript
  17. // risk-scoring.ts - Risk classification and scoring system
  18. export const RISK_CATEGORIES = {
  19. TECH: 'TECH', // Technical debt, architecture fragility
  20. SEC: 'SEC', // Security vulnerabilities
  21. PERF: 'PERF', // Performance degradation
  22. DATA: 'DATA', // Data integrity, corruption
  23. BUS: 'BUS', // Business logic errors
  24. OPS: 'OPS', // Operational issues (deployment, monitoring)
  25. } as const;
  26. export type RiskCategory = keyof typeof RISK_CATEGORIES;
  27. export type RiskScore = {
  28. id: string;
  29. category: RiskCategory;
  30. title: string;
  31. description: string;
  32. probability: 1 | 2 | 3; // 1=Low, 2=Medium, 3=High
  33. impact: 1 | 2 | 3; // 1=Low, 2=Medium, 3=High
  34. score: number; // probability × impact (1-9)
  35. owner: string;
  36. mitigationPlan?: string;
  37. deadline?: Date;
  38. status: 'OPEN' | 'MITIGATED' | 'WAIVED' | 'ACCEPTED';
  39. waiverReason?: string;
  40. waiverApprover?: string;
  41. waiverExpiry?: Date;
  42. };
  43. // Risk scoring rules
  44. export function calculateRiskScore(probability: 1 | 2 | 3, impact: 1 | 2 | 3): number {
  45. return probability * impact;
  46. }
  47. export function requiresMitigation(score: number): boolean {
  48. return score >= 6; // Scores 6-9 demand action
  49. }
  50. export function isCriticalBlocker(score: number): boolean {
  51. return score === 9; // Probability=3 AND Impact=3 → FAIL gate
  52. }
  53. export function classifyRiskLevel(score: number): 'LOW' | 'MEDIUM' | 'HIGH' | 'CRITICAL' {
  54. if (score === 9) return 'CRITICAL';
  55. if (score >= 6) return 'HIGH';
  56. if (score >= 4) return 'MEDIUM';
  57. return 'LOW';
  58. }
  59. // Example: Risk assessment from test failures
  60. export function assessTestFailureRisk(failure: {
  61. test: string;
  62. category: RiskCategory;
  63. affectedUsers: number;
  64. revenueImpact: number;
  65. securityVulnerability: boolean;
  66. }): RiskScore {
  67. // Probability based on test failure frequency (simplified)
  68. const probability: 1 | 2 | 3 = 3; // Test failed = High probability
  69. // Impact based on business context
  70. let impact: 1 | 2 | 3 = 1;
  71. if (failure.securityVulnerability) impact = 3;
  72. else if (failure.revenueImpact > 10000) impact = 3;
  73. else if (failure.affectedUsers > 1000) impact = 2;
  74. else impact = 1;
  75. const score = calculateRiskScore(probability, impact);
  76. return {
  77. id: `risk-${Date.now()}`,
  78. category: failure.category,
  79. title: `Test failure: ${failure.test}`,
  80. description: `Affects ${failure.affectedUsers} users, $${failure.revenueImpact} revenue`,
  81. probability,
  82. impact,
  83. score,
  84. owner: 'unassigned',
  85. status: score === 9 ? 'OPEN' : 'OPEN',
  86. };
  87. }
  88. ```
  89. **Key Points**:
  90. - **Objective scoring**: Probability (1-3) × Impact (1-3) = Score (1-9)
  91. - **Clear thresholds**: Score ≥6 requires mitigation, score = 9 blocks release
  92. - **Business context**: Revenue, users, security drive impact calculation
  93. - **Status tracking**: OPEN → MITIGATED → WAIVED → ACCEPTED lifecycle
  94. ---
  95. ### Example 2: Gate Decision Engine with Traceability Validation
  96. **Context**: Automated gate decision based on risk scores and test coverage
  97. **Implementation**:
  98. ```typescript
  99. // gate-decision-engine.ts
  100. export type GateDecision = 'PASS' | 'CONCERNS' | 'FAIL' | 'WAIVED';
  101. export type CoverageGap = {
  102. acceptanceCriteria: string;
  103. testMissing: string;
  104. reason: string;
  105. };
  106. export type GateResult = {
  107. decision: GateDecision;
  108. timestamp: Date;
  109. criticalRisks: RiskScore[];
  110. highRisks: RiskScore[];
  111. coverageGaps: CoverageGap[];
  112. summary: string;
  113. recommendations: string[];
  114. };
  115. export function evaluateGate(params: { risks: RiskScore[]; coverageGaps: CoverageGap[]; waiverApprover?: string }): GateResult {
  116. const { risks, coverageGaps, waiverApprover } = params;
  117. // Categorize risks
  118. const criticalRisks = risks.filter((r) => r.score === 9 && r.status === 'OPEN');
  119. const highRisks = risks.filter((r) => r.score >= 6 && r.score < 9 && r.status === 'OPEN');
  120. const unresolvedGaps = coverageGaps.filter((g) => !g.reason);
  121. // Decision logic
  122. let decision: GateDecision;
  123. // FAIL: Critical blockers (score=9) or missing coverage
  124. if (criticalRisks.length > 0 || unresolvedGaps.length > 0) {
  125. decision = 'FAIL';
  126. }
  127. // WAIVED: All risks waived by authorized approver
  128. else if (risks.every((r) => r.status === 'WAIVED') && waiverApprover) {
  129. decision = 'WAIVED';
  130. }
  131. // CONCERNS: High risks (score 6-8) with mitigation plans
  132. else if (highRisks.length > 0 && highRisks.every((r) => r.mitigationPlan && r.owner !== 'unassigned')) {
  133. decision = 'CONCERNS';
  134. }
  135. // PASS: No critical issues, all risks mitigated or low
  136. else {
  137. decision = 'PASS';
  138. }
  139. // Generate recommendations
  140. const recommendations: string[] = [];
  141. if (criticalRisks.length > 0) {
  142. recommendations.push(`🚨 ${criticalRisks.length} CRITICAL risk(s) must be mitigated before release`);
  143. }
  144. if (unresolvedGaps.length > 0) {
  145. recommendations.push(`📋 ${unresolvedGaps.length} acceptance criteria lack test coverage`);
  146. }
  147. if (highRisks.some((r) => !r.mitigationPlan)) {
  148. recommendations.push(`⚠️ High risks without mitigation plans: assign owners and deadlines`);
  149. }
  150. if (decision === 'PASS') {
  151. recommendations.push(`✅ All risks mitigated or acceptable. Ready for release.`);
  152. }
  153. return {
  154. decision,
  155. timestamp: new Date(),
  156. criticalRisks,
  157. highRisks,
  158. coverageGaps: unresolvedGaps,
  159. summary: generateSummary(decision, risks, unresolvedGaps),
  160. recommendations,
  161. };
  162. }
  163. function generateSummary(decision: GateDecision, risks: RiskScore[], gaps: CoverageGap[]): string {
  164. const total = risks.length;
  165. const critical = risks.filter((r) => r.score === 9).length;
  166. const high = risks.filter((r) => r.score >= 6 && r.score < 9).length;
  167. return `Gate Decision: ${decision}. Total Risks: ${total} (${critical} critical, ${high} high). Coverage Gaps: ${gaps.length}.`;
  168. }
  169. ```
  170. **Usage Example**:
  171. ```typescript
  172. // Example: Running gate check before deployment
  173. import { assessTestFailureRisk, evaluateGate } from './gate-decision-engine';
  174. // Collect risks from test results
  175. const risks: RiskScore[] = [
  176. assessTestFailureRisk({
  177. test: 'Payment processing with expired card',
  178. category: 'BUS',
  179. affectedUsers: 5000,
  180. revenueImpact: 50000,
  181. securityVulnerability: false,
  182. }),
  183. assessTestFailureRisk({
  184. test: 'SQL injection in search endpoint',
  185. category: 'SEC',
  186. affectedUsers: 10000,
  187. revenueImpact: 0,
  188. securityVulnerability: true,
  189. }),
  190. ];
  191. // Identify coverage gaps
  192. const coverageGaps: CoverageGap[] = [
  193. {
  194. acceptanceCriteria: 'User can reset password via email',
  195. testMissing: 'e2e/auth/password-reset.spec.ts',
  196. reason: '', // Empty = unresolved
  197. },
  198. ];
  199. // Evaluate gate
  200. const gateResult = evaluateGate({ risks, coverageGaps });
  201. console.log(gateResult.decision); // 'FAIL'
  202. console.log(gateResult.summary);
  203. // "Gate Decision: FAIL. Total Risks: 2 (1 critical, 1 high). Coverage Gaps: 1."
  204. console.log(gateResult.recommendations);
  205. // [
  206. // "🚨 1 CRITICAL risk(s) must be mitigated before release",
  207. // "📋 1 acceptance criteria lack test coverage"
  208. // ]
  209. ```
  210. **Key Points**:
  211. - **Automated decision**: No human interpretation required
  212. - **Clear criteria**: FAIL = critical risks or gaps, CONCERNS = high risks with plans, PASS = low risks
  213. - **Actionable output**: Recommendations drive next steps
  214. - **Audit trail**: Timestamp, decision, and context for compliance
  215. ---
  216. ### Example 3: Risk Mitigation Workflow with Owner Tracking
  217. **Context**: Track risk mitigation from identification to resolution
  218. **Implementation**:
  219. ```typescript
  220. // risk-mitigation.ts
  221. export type MitigationAction = {
  222. riskId: string;
  223. action: string;
  224. owner: string;
  225. deadline: Date;
  226. status: 'PENDING' | 'IN_PROGRESS' | 'COMPLETED' | 'BLOCKED';
  227. completedAt?: Date;
  228. blockedReason?: string;
  229. };
  230. export class RiskMitigationTracker {
  231. private risks: Map<string, RiskScore> = new Map();
  232. private actions: Map<string, MitigationAction[]> = new Map();
  233. private history: Array<{ riskId: string; event: string; timestamp: Date }> = [];
  234. // Register a new risk
  235. addRisk(risk: RiskScore): void {
  236. this.risks.set(risk.id, risk);
  237. this.logHistory(risk.id, `Risk registered: ${risk.title} (Score: ${risk.score})`);
  238. // Auto-assign mitigation requirements for score ≥6
  239. if (requiresMitigation(risk.score) && !risk.mitigationPlan) {
  240. this.logHistory(risk.id, `⚠️ Mitigation required (score ${risk.score}). Assign owner and plan.`);
  241. }
  242. }
  243. // Add mitigation action
  244. addMitigationAction(action: MitigationAction): void {
  245. const risk = this.risks.get(action.riskId);
  246. if (!risk) throw new Error(`Risk ${action.riskId} not found`);
  247. const existingActions = this.actions.get(action.riskId) || [];
  248. existingActions.push(action);
  249. this.actions.set(action.riskId, existingActions);
  250. this.logHistory(action.riskId, `Mitigation action added: ${action.action} (Owner: ${action.owner})`);
  251. }
  252. // Complete mitigation action
  253. completeMitigation(riskId: string, actionIndex: number): void {
  254. const actions = this.actions.get(riskId);
  255. if (!actions || !actions[actionIndex]) throw new Error('Action not found');
  256. actions[actionIndex].status = 'COMPLETED';
  257. actions[actionIndex].completedAt = new Date();
  258. this.logHistory(riskId, `Mitigation completed: ${actions[actionIndex].action}`);
  259. // If all actions completed, mark risk as MITIGATED
  260. if (actions.every((a) => a.status === 'COMPLETED')) {
  261. const risk = this.risks.get(riskId)!;
  262. risk.status = 'MITIGATED';
  263. this.logHistory(riskId, `✅ Risk mitigated. All actions complete.`);
  264. }
  265. }
  266. // Request waiver for a risk
  267. requestWaiver(riskId: string, reason: string, approver: string, expiryDays: number): void {
  268. const risk = this.risks.get(riskId);
  269. if (!risk) throw new Error(`Risk ${riskId} not found`);
  270. risk.status = 'WAIVED';
  271. risk.waiverReason = reason;
  272. risk.waiverApprover = approver;
  273. risk.waiverExpiry = new Date(Date.now() + expiryDays * 24 * 60 * 60 * 1000);
  274. this.logHistory(riskId, `⚠️ Waiver granted by ${approver}. Expires: ${risk.waiverExpiry}`);
  275. }
  276. // Generate risk report
  277. generateReport(): string {
  278. const allRisks = Array.from(this.risks.values());
  279. const critical = allRisks.filter((r) => r.score === 9 && r.status === 'OPEN');
  280. const high = allRisks.filter((r) => r.score >= 6 && r.score < 9 && r.status === 'OPEN');
  281. const mitigated = allRisks.filter((r) => r.status === 'MITIGATED');
  282. const waived = allRisks.filter((r) => r.status === 'WAIVED');
  283. let report = `# Risk Mitigation Report\n\n`;
  284. report += `**Generated**: ${new Date().toISOString()}\n\n`;
  285. report += `## Summary\n`;
  286. report += `- Total Risks: ${allRisks.length}\n`;
  287. report += `- Critical (Score=9, OPEN): ${critical.length}\n`;
  288. report += `- High (Score 6-8, OPEN): ${high.length}\n`;
  289. report += `- Mitigated: ${mitigated.length}\n`;
  290. report += `- Waived: ${waived.length}\n\n`;
  291. if (critical.length > 0) {
  292. report += `## 🚨 Critical Risks (BLOCKERS)\n\n`;
  293. critical.forEach((r) => {
  294. report += `- **${r.title}** (${r.category})\n`;
  295. report += ` - Score: ${r.score} (Probability: ${r.probability}, Impact: ${r.impact})\n`;
  296. report += ` - Owner: ${r.owner}\n`;
  297. report += ` - Mitigation: ${r.mitigationPlan || 'NOT ASSIGNED'}\n\n`;
  298. });
  299. }
  300. if (high.length > 0) {
  301. report += `## ⚠️ High Risks\n\n`;
  302. high.forEach((r) => {
  303. report += `- **${r.title}** (${r.category})\n`;
  304. report += ` - Score: ${r.score}\n`;
  305. report += ` - Owner: ${r.owner}\n`;
  306. report += ` - Deadline: ${r.deadline?.toISOString().split('T')[0] || 'NOT SET'}\n\n`;
  307. });
  308. }
  309. return report;
  310. }
  311. private logHistory(riskId: string, event: string): void {
  312. this.history.push({ riskId, event, timestamp: new Date() });
  313. }
  314. getHistory(riskId: string): Array<{ event: string; timestamp: Date }> {
  315. return this.history.filter((h) => h.riskId === riskId).map((h) => ({ event: h.event, timestamp: h.timestamp }));
  316. }
  317. }
  318. ```
  319. **Usage Example**:
  320. ```typescript
  321. const tracker = new RiskMitigationTracker();
  322. // Register critical security risk
  323. tracker.addRisk({
  324. id: 'risk-001',
  325. category: 'SEC',
  326. title: 'SQL injection vulnerability in user search',
  327. description: 'Unsanitized input allows arbitrary SQL execution',
  328. probability: 3,
  329. impact: 3,
  330. score: 9,
  331. owner: 'security-team',
  332. status: 'OPEN',
  333. });
  334. // Add mitigation actions
  335. tracker.addMitigationAction({
  336. riskId: 'risk-001',
  337. action: 'Add parameterized queries to user-search endpoint',
  338. owner: 'alice@example.com',
  339. deadline: new Date('2025-10-20'),
  340. status: 'IN_PROGRESS',
  341. });
  342. tracker.addMitigationAction({
  343. riskId: 'risk-001',
  344. action: 'Add WAF rule to block SQL injection patterns',
  345. owner: 'bob@example.com',
  346. deadline: new Date('2025-10-22'),
  347. status: 'PENDING',
  348. });
  349. // Complete first action
  350. tracker.completeMitigation('risk-001', 0);
  351. // Generate report
  352. console.log(tracker.generateReport());
  353. // Markdown report with critical risks, owners, deadlines
  354. // View history
  355. console.log(tracker.getHistory('risk-001'));
  356. // [
  357. // { event: 'Risk registered: SQL injection...', timestamp: ... },
  358. // { event: 'Mitigation action added: Add parameterized queries...', timestamp: ... },
  359. // { event: 'Mitigation completed: Add parameterized queries...', timestamp: ... }
  360. // ]
  361. ```
  362. **Key Points**:
  363. - **Ownership enforcement**: Every risk >4 requires owner assignment
  364. - **Deadline tracking**: Mitigation actions have explicit deadlines
  365. - **Audit trail**: Complete history of risk lifecycle (registered → mitigated)
  366. - **Automated reports**: Markdown output for Confluence/GitHub wikis
  367. ---
  368. ### Example 4: Coverage Traceability Matrix (Test-to-Requirement Mapping)
  369. **Context**: Validate that every acceptance criterion maps to at least one test
  370. **Implementation**:
  371. ```typescript
  372. // coverage-traceability.ts
  373. export type AcceptanceCriterion = {
  374. id: string;
  375. story: string;
  376. criterion: string;
  377. priority: 'P0' | 'P1' | 'P2' | 'P3';
  378. };
  379. export type TestCase = {
  380. file: string;
  381. name: string;
  382. criteriaIds: string[]; // Links to acceptance criteria
  383. };
  384. export type CoverageMatrix = {
  385. criterion: AcceptanceCriterion;
  386. tests: TestCase[];
  387. covered: boolean;
  388. waiverReason?: string;
  389. };
  390. export function buildCoverageMatrix(criteria: AcceptanceCriterion[], tests: TestCase[]): CoverageMatrix[] {
  391. return criteria.map((criterion) => {
  392. const matchingTests = tests.filter((t) => t.criteriaIds.includes(criterion.id));
  393. return {
  394. criterion,
  395. tests: matchingTests,
  396. covered: matchingTests.length > 0,
  397. };
  398. });
  399. }
  400. export function validateCoverage(matrix: CoverageMatrix[]): {
  401. gaps: CoverageMatrix[];
  402. passRate: number;
  403. } {
  404. const gaps = matrix.filter((m) => !m.covered && !m.waiverReason);
  405. const passRate = ((matrix.length - gaps.length) / matrix.length) * 100;
  406. return { gaps, passRate };
  407. }
  408. // Example: Extract criteria IDs from test names
  409. export function extractCriteriaFromTests(testFiles: string[]): TestCase[] {
  410. // Simplified: In real implementation, parse test files with AST
  411. // Here we simulate extraction from test names
  412. return [
  413. {
  414. file: 'tests/e2e/auth/login.spec.ts',
  415. name: 'should allow user to login with valid credentials',
  416. criteriaIds: ['AC-001', 'AC-002'], // Linked to acceptance criteria
  417. },
  418. {
  419. file: 'tests/e2e/auth/password-reset.spec.ts',
  420. name: 'should send password reset email',
  421. criteriaIds: ['AC-003'],
  422. },
  423. ];
  424. }
  425. // Generate Markdown traceability report
  426. export function generateTraceabilityReport(matrix: CoverageMatrix[]): string {
  427. let report = `# Requirements-to-Tests Traceability Matrix\n\n`;
  428. report += `**Generated**: ${new Date().toISOString()}\n\n`;
  429. const { gaps, passRate } = validateCoverage(matrix);
  430. report += `## Summary\n`;
  431. report += `- Total Criteria: ${matrix.length}\n`;
  432. report += `- Covered: ${matrix.filter((m) => m.covered).length}\n`;
  433. report += `- Gaps: ${gaps.length}\n`;
  434. report += `- Waived: ${matrix.filter((m) => m.waiverReason).length}\n`;
  435. report += `- Coverage Rate: ${passRate.toFixed(1)}%\n\n`;
  436. if (gaps.length > 0) {
  437. report += `## ❌ Coverage Gaps (MUST RESOLVE)\n\n`;
  438. report += `| Story | Criterion | Priority | Tests |\n`;
  439. report += `|-------|-----------|----------|-------|\n`;
  440. gaps.forEach((m) => {
  441. report += `| ${m.criterion.story} | ${m.criterion.criterion} | ${m.criterion.priority} | None |\n`;
  442. });
  443. report += `\n`;
  444. }
  445. report += `## ✅ Covered Criteria\n\n`;
  446. report += `| Story | Criterion | Tests |\n`;
  447. report += `|-------|-----------|-------|\n`;
  448. matrix
  449. .filter((m) => m.covered)
  450. .forEach((m) => {
  451. const testList = m.tests.map((t) => `\`${t.file}\``).join(', ');
  452. report += `| ${m.criterion.story} | ${m.criterion.criterion} | ${testList} |\n`;
  453. });
  454. return report;
  455. }
  456. ```
  457. **Usage Example**:
  458. ```typescript
  459. // Define acceptance criteria
  460. const criteria: AcceptanceCriterion[] = [
  461. { id: 'AC-001', story: 'US-123', criterion: 'User can login with email', priority: 'P0' },
  462. { id: 'AC-002', story: 'US-123', criterion: 'User sees error on invalid password', priority: 'P0' },
  463. { id: 'AC-003', story: 'US-124', criterion: 'User receives password reset email', priority: 'P1' },
  464. { id: 'AC-004', story: 'US-125', criterion: 'User can update profile', priority: 'P2' }, // NO TEST
  465. ];
  466. // Extract tests
  467. const tests: TestCase[] = extractCriteriaFromTests(['tests/e2e/auth/login.spec.ts', 'tests/e2e/auth/password-reset.spec.ts']);
  468. // Build matrix
  469. const matrix = buildCoverageMatrix(criteria, tests);
  470. // Validate
  471. const { gaps, passRate } = validateCoverage(matrix);
  472. console.log(`Coverage: ${passRate.toFixed(1)}%`); // "Coverage: 75.0%"
  473. console.log(`Gaps: ${gaps.length}`); // "Gaps: 1" (AC-004 has no test)
  474. // Generate report
  475. const report = generateTraceabilityReport(matrix);
  476. console.log(report);
  477. // Markdown table showing coverage gaps
  478. ```
  479. **Key Points**:
  480. - **Bidirectional traceability**: Criteria → Tests and Tests → Criteria
  481. - **Gap detection**: Automatically identifies missing coverage
  482. - **Priority awareness**: P0 gaps are critical blockers
  483. - **Waiver support**: Allow explicit waivers for low-priority gaps
  484. ---
  485. ## Risk Governance Checklist
  486. Before deploying to production, ensure:
  487. - [ ] **Risk scoring complete**: All identified risks scored (Probability × Impact)
  488. - [ ] **Ownership assigned**: Every risk >4 has owner, mitigation plan, deadline
  489. - [ ] **Coverage validated**: Every acceptance criterion maps to at least one test
  490. - [ ] **Gate decision documented**: PASS/CONCERNS/FAIL/WAIVED with rationale
  491. - [ ] **Waivers approved**: All waivers have approver, reason, expiry date
  492. - [ ] **Audit trail captured**: Risk history log available for compliance review
  493. - [ ] **Traceability matrix**: Requirements-to-tests mapping up to date
  494. - [ ] **Critical risks resolved**: No score=9 risks in OPEN status
  495. ## Integration Points
  496. - **Used in workflows**: `*trace` (Phase 2: gate decision), `*nfr-assess` (risk scoring), `*test-design` (risk identification)
  497. - **Related fragments**: `probability-impact.md` (scoring definitions), `test-priorities-matrix.md` (P0-P3 classification), `nfr-criteria.md` (non-functional risks)
  498. - **Tools**: Risk tracking dashboards (Jira, Linear), gate automation (CI/CD), traceability reports (Markdown, Confluence)
  499. _Source: Murat risk governance notes, gate schema guidance, enterprise production gate workflows, ISO 31000 risk management standards_