@rigour-labs/core 2.17.2 → 2.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/context.test.js +15 -1
- package/dist/discovery.js +14 -1
- package/dist/discovery.test.js +23 -0
- package/dist/gates/agent-team.d.ts +50 -0
- package/dist/gates/agent-team.js +159 -0
- package/dist/gates/agent-team.test.d.ts +1 -0
- package/dist/gates/agent-team.test.js +113 -0
- package/dist/gates/checkpoint.d.ts +72 -0
- package/dist/gates/checkpoint.js +231 -0
- package/dist/gates/checkpoint.test.d.ts +1 -0
- package/dist/gates/checkpoint.test.js +102 -0
- package/dist/gates/context.d.ts +35 -0
- package/dist/gates/context.js +151 -2
- package/dist/gates/runner.js +15 -0
- package/dist/gates/security-patterns.d.ts +48 -0
- package/dist/gates/security-patterns.js +236 -0
- package/dist/gates/security-patterns.test.d.ts +1 -0
- package/dist/gates/security-patterns.test.js +133 -0
- package/dist/services/adaptive-thresholds.d.ts +63 -0
- package/dist/services/adaptive-thresholds.js +204 -0
- package/dist/services/adaptive-thresholds.test.d.ts +1 -0
- package/dist/services/adaptive-thresholds.test.js +129 -0
- package/dist/templates/index.d.ts +1 -0
- package/dist/templates/index.js +81 -0
- package/dist/types/fix-packet.d.ts +4 -4
- package/dist/types/index.d.ts +404 -0
- package/dist/types/index.js +36 -0
- package/package.json +1 -1
- package/src/context.test.ts +15 -1
- package/src/discovery.test.ts +27 -0
- package/src/discovery.ts +15 -2
- package/src/gates/agent-team.test.ts +134 -0
- package/src/gates/agent-team.ts +210 -0
- package/src/gates/checkpoint.test.ts +135 -0
- package/src/gates/checkpoint.ts +311 -0
- package/src/gates/context.ts +200 -2
- package/src/gates/runner.ts +18 -0
- package/src/gates/security-patterns.test.ts +162 -0
- package/src/gates/security-patterns.ts +303 -0
- package/src/services/adaptive-thresholds.test.ts +189 -0
- package/src/services/adaptive-thresholds.ts +275 -0
- package/src/templates/index.ts +82 -0
- package/src/types/index.ts +36 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Thresholds Service
|
|
3
|
+
*
|
|
4
|
+
* Dynamically adjusts quality gate thresholds based on:
|
|
5
|
+
* - Project maturity (age, commit count, file count)
|
|
6
|
+
* - Historical failure rates
|
|
7
|
+
* - Complexity tier (hobby/startup/enterprise)
|
|
8
|
+
* - Recent trends (improving/degrading)
|
|
9
|
+
*
|
|
10
|
+
* This enables Rigour to be "strict but fair" - new projects get
|
|
11
|
+
* more lenient thresholds while mature codebases are held to higher standards.
|
|
12
|
+
*
|
|
13
|
+
* @since v2.14.0
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as fs from 'fs';
|
|
17
|
+
import * as path from 'path';
|
|
18
|
+
import { Logger } from '../utils/logger.js';
|
|
19
|
+
|
|
20
|
+
export type ComplexityTier = 'hobby' | 'startup' | 'enterprise';
|
|
21
|
+
export type QualityTrend = 'improving' | 'stable' | 'degrading';
|
|
22
|
+
|
|
23
|
+
export interface ProjectMetrics {
|
|
24
|
+
fileCount: number;
|
|
25
|
+
commitCount?: number;
|
|
26
|
+
ageInDays?: number;
|
|
27
|
+
testCoverage?: number;
|
|
28
|
+
recentFailureRate?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface AdaptiveConfig {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
base_coverage_threshold?: number;
|
|
34
|
+
base_quality_threshold?: number;
|
|
35
|
+
auto_detect_tier?: boolean;
|
|
36
|
+
forced_tier?: ComplexityTier;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ThresholdAdjustments {
|
|
40
|
+
tier: ComplexityTier;
|
|
41
|
+
trend: QualityTrend;
|
|
42
|
+
coverageThreshold: number;
|
|
43
|
+
qualityThreshold: number;
|
|
44
|
+
securityBlockLevel: 'critical' | 'high' | 'medium' | 'low';
|
|
45
|
+
leniencyFactor: number; // 0.0 = strict, 1.0 = lenient
|
|
46
|
+
reasoning: string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Historical failure data (persisted to .rigour/adaptive-history.json)
|
|
50
|
+
interface FailureHistory {
|
|
51
|
+
runs: {
|
|
52
|
+
timestamp: string;
|
|
53
|
+
passedGates: number;
|
|
54
|
+
failedGates: number;
|
|
55
|
+
totalFailures: number;
|
|
56
|
+
}[];
|
|
57
|
+
lastUpdated: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let cachedHistory: FailureHistory | null = null;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Load failure history from disk
|
|
64
|
+
*/
|
|
65
|
+
function loadHistory(cwd: string): FailureHistory {
|
|
66
|
+
if (cachedHistory) return cachedHistory;
|
|
67
|
+
|
|
68
|
+
const historyPath = path.join(cwd, '.rigour', 'adaptive-history.json');
|
|
69
|
+
try {
|
|
70
|
+
if (fs.existsSync(historyPath)) {
|
|
71
|
+
cachedHistory = JSON.parse(fs.readFileSync(historyPath, 'utf-8'));
|
|
72
|
+
return cachedHistory!;
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
Logger.debug('Failed to load adaptive history, starting fresh');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
cachedHistory = { runs: [], lastUpdated: new Date().toISOString() };
|
|
79
|
+
return cachedHistory;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Save failure history to disk
|
|
84
|
+
*/
|
|
85
|
+
function saveHistory(cwd: string, history: FailureHistory): void {
|
|
86
|
+
const rigourDir = path.join(cwd, '.rigour');
|
|
87
|
+
if (!fs.existsSync(rigourDir)) {
|
|
88
|
+
fs.mkdirSync(rigourDir, { recursive: true });
|
|
89
|
+
}
|
|
90
|
+
const historyPath = path.join(rigourDir, 'adaptive-history.json');
|
|
91
|
+
fs.writeFileSync(historyPath, JSON.stringify(history, null, 2));
|
|
92
|
+
cachedHistory = history;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Record a gate run for historical tracking
|
|
97
|
+
*/
|
|
98
|
+
export function recordGateRun(
|
|
99
|
+
cwd: string,
|
|
100
|
+
passedGates: number,
|
|
101
|
+
failedGates: number,
|
|
102
|
+
totalFailures: number
|
|
103
|
+
): void {
|
|
104
|
+
const history = loadHistory(cwd);
|
|
105
|
+
history.runs.push({
|
|
106
|
+
timestamp: new Date().toISOString(),
|
|
107
|
+
passedGates,
|
|
108
|
+
failedGates,
|
|
109
|
+
totalFailures,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// Keep last 100 runs
|
|
113
|
+
if (history.runs.length > 100) {
|
|
114
|
+
history.runs = history.runs.slice(-100);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
history.lastUpdated = new Date().toISOString();
|
|
118
|
+
saveHistory(cwd, history);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Get quality trend from historical data
|
|
123
|
+
*/
|
|
124
|
+
export function getQualityTrend(cwd: string): QualityTrend {
|
|
125
|
+
const history = loadHistory(cwd);
|
|
126
|
+
if (history.runs.length < 5) return 'stable';
|
|
127
|
+
|
|
128
|
+
const recent = history.runs.slice(-10);
|
|
129
|
+
const older = history.runs.slice(-20, -10);
|
|
130
|
+
|
|
131
|
+
if (older.length === 0) return 'stable';
|
|
132
|
+
|
|
133
|
+
const recentFailRate = recent.reduce((sum, r) => sum + r.totalFailures, 0) / recent.length;
|
|
134
|
+
const olderFailRate = older.reduce((sum, r) => sum + r.totalFailures, 0) / older.length;
|
|
135
|
+
|
|
136
|
+
const delta = recentFailRate - olderFailRate;
|
|
137
|
+
|
|
138
|
+
if (delta < -2) return 'improving';
|
|
139
|
+
if (delta > 2) return 'degrading';
|
|
140
|
+
return 'stable';
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Detect project complexity tier based on metrics
|
|
145
|
+
*/
|
|
146
|
+
export function detectComplexityTier(metrics: ProjectMetrics): ComplexityTier {
|
|
147
|
+
// Enterprise: Large teams, many files, mature codebase
|
|
148
|
+
if (metrics.fileCount > 500 || (metrics.commitCount && metrics.commitCount > 1000)) {
|
|
149
|
+
return 'enterprise';
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Startup: Growing codebase, active development
|
|
153
|
+
if (metrics.fileCount > 50 || (metrics.commitCount && metrics.commitCount > 100)) {
|
|
154
|
+
return 'startup';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Hobby: Small projects, early stage
|
|
158
|
+
return 'hobby';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Calculate adaptive thresholds based on project state
|
|
163
|
+
*/
|
|
164
|
+
export function calculateAdaptiveThresholds(
|
|
165
|
+
cwd: string,
|
|
166
|
+
metrics: ProjectMetrics,
|
|
167
|
+
config: AdaptiveConfig = {}
|
|
168
|
+
): ThresholdAdjustments {
|
|
169
|
+
const reasoning: string[] = [];
|
|
170
|
+
|
|
171
|
+
// Determine tier
|
|
172
|
+
const tier = config.forced_tier ??
|
|
173
|
+
(config.auto_detect_tier !== false ? detectComplexityTier(metrics) : 'startup');
|
|
174
|
+
reasoning.push(`Complexity tier: ${tier} (files: ${metrics.fileCount})`);
|
|
175
|
+
|
|
176
|
+
// Get trend
|
|
177
|
+
const trend = getQualityTrend(cwd);
|
|
178
|
+
reasoning.push(`Quality trend: ${trend}`);
|
|
179
|
+
|
|
180
|
+
// Base thresholds
|
|
181
|
+
let coverageThreshold = config.base_coverage_threshold ?? 80;
|
|
182
|
+
let qualityThreshold = config.base_quality_threshold ?? 80;
|
|
183
|
+
let securityBlockLevel: 'critical' | 'high' | 'medium' | 'low' = 'high';
|
|
184
|
+
let leniencyFactor = 0.5;
|
|
185
|
+
|
|
186
|
+
// Adjust by tier
|
|
187
|
+
switch (tier) {
|
|
188
|
+
case 'hobby':
|
|
189
|
+
// Lenient for small/new projects
|
|
190
|
+
coverageThreshold = Math.max(50, coverageThreshold - 30);
|
|
191
|
+
qualityThreshold = Math.max(60, qualityThreshold - 20);
|
|
192
|
+
securityBlockLevel = 'critical'; // Only block on critical
|
|
193
|
+
leniencyFactor = 0.8;
|
|
194
|
+
reasoning.push('Hobby tier: relaxed thresholds, only critical security blocks');
|
|
195
|
+
break;
|
|
196
|
+
|
|
197
|
+
case 'startup':
|
|
198
|
+
// Moderate strictness
|
|
199
|
+
coverageThreshold = Math.max(60, coverageThreshold - 15);
|
|
200
|
+
qualityThreshold = Math.max(70, qualityThreshold - 10);
|
|
201
|
+
securityBlockLevel = 'high';
|
|
202
|
+
leniencyFactor = 0.5;
|
|
203
|
+
reasoning.push('Startup tier: moderate thresholds, high+ security blocks');
|
|
204
|
+
break;
|
|
205
|
+
|
|
206
|
+
case 'enterprise':
|
|
207
|
+
// Strict standards
|
|
208
|
+
coverageThreshold = coverageThreshold;
|
|
209
|
+
qualityThreshold = qualityThreshold;
|
|
210
|
+
securityBlockLevel = 'medium';
|
|
211
|
+
leniencyFactor = 0.2;
|
|
212
|
+
reasoning.push('Enterprise tier: strict thresholds, medium+ security blocks');
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Adjust by trend
|
|
217
|
+
if (trend === 'improving') {
|
|
218
|
+
// Reward improvement with slightly relaxed thresholds
|
|
219
|
+
coverageThreshold = Math.max(50, coverageThreshold - 5);
|
|
220
|
+
qualityThreshold = Math.max(60, qualityThreshold - 5);
|
|
221
|
+
leniencyFactor = Math.min(1, leniencyFactor + 0.1);
|
|
222
|
+
reasoning.push('Improving trend: bonus threshold relaxation (+5%)');
|
|
223
|
+
} else if (trend === 'degrading') {
|
|
224
|
+
// Tighten thresholds to encourage recovery
|
|
225
|
+
coverageThreshold = Math.min(95, coverageThreshold + 5);
|
|
226
|
+
qualityThreshold = Math.min(95, qualityThreshold + 5);
|
|
227
|
+
leniencyFactor = Math.max(0, leniencyFactor - 0.1);
|
|
228
|
+
reasoning.push('Degrading trend: tightened thresholds (-5%)');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Recent failure rate adjustment
|
|
232
|
+
if (metrics.recentFailureRate !== undefined) {
|
|
233
|
+
if (metrics.recentFailureRate > 50) {
|
|
234
|
+
// High failure rate - be more lenient to avoid discouragement
|
|
235
|
+
leniencyFactor = Math.min(1, leniencyFactor + 0.2);
|
|
236
|
+
reasoning.push(`High failure rate (${metrics.recentFailureRate.toFixed(0)}%): increased leniency`);
|
|
237
|
+
} else if (metrics.recentFailureRate < 10) {
|
|
238
|
+
// Low failure rate - team is mature, can handle stricter gates
|
|
239
|
+
leniencyFactor = Math.max(0, leniencyFactor - 0.1);
|
|
240
|
+
reasoning.push(`Low failure rate (${metrics.recentFailureRate.toFixed(0)}%): stricter enforcement`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return {
|
|
245
|
+
tier,
|
|
246
|
+
trend,
|
|
247
|
+
coverageThreshold: Math.round(coverageThreshold),
|
|
248
|
+
qualityThreshold: Math.round(qualityThreshold),
|
|
249
|
+
securityBlockLevel,
|
|
250
|
+
leniencyFactor: Math.round(leniencyFactor * 100) / 100,
|
|
251
|
+
reasoning,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Clear adaptive history (for testing)
|
|
257
|
+
*/
|
|
258
|
+
export function clearAdaptiveHistory(cwd: string): void {
|
|
259
|
+
cachedHistory = null;
|
|
260
|
+
const historyPath = path.join(cwd, '.rigour', 'adaptive-history.json');
|
|
261
|
+
if (fs.existsSync(historyPath)) {
|
|
262
|
+
fs.unlinkSync(historyPath);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Get summary of adaptive thresholds for logging
|
|
268
|
+
*/
|
|
269
|
+
export function getAdaptiveSummary(adjustments: ThresholdAdjustments): string {
|
|
270
|
+
return `[${adjustments.tier.toUpperCase()}] ` +
|
|
271
|
+
`Coverage: ${adjustments.coverageThreshold}%, ` +
|
|
272
|
+
`Quality: ${adjustments.qualityThreshold}%, ` +
|
|
273
|
+
`Security: ${adjustments.securityBlockLevel}+, ` +
|
|
274
|
+
`Trend: ${adjustments.trend}`;
|
|
275
|
+
}
|
package/src/templates/index.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface Template {
|
|
|
9
9
|
commands?: Partial<Commands>;
|
|
10
10
|
gates?: Partial<Gates>;
|
|
11
11
|
planned?: string[];
|
|
12
|
+
ignore?: string[];
|
|
12
13
|
};
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -28,6 +29,17 @@ export const TEMPLATES: Template[] = [
|
|
|
28
29
|
],
|
|
29
30
|
config: {
|
|
30
31
|
preset: 'ui',
|
|
32
|
+
ignore: [
|
|
33
|
+
'.git/**',
|
|
34
|
+
'node_modules/**',
|
|
35
|
+
'dist/**',
|
|
36
|
+
'build/**',
|
|
37
|
+
'.next/**',
|
|
38
|
+
'.nuxt/**',
|
|
39
|
+
'.svelte-kit/**',
|
|
40
|
+
'coverage/**',
|
|
41
|
+
'.turbo/**',
|
|
42
|
+
],
|
|
31
43
|
gates: {
|
|
32
44
|
max_file_lines: 300,
|
|
33
45
|
required_files: ['docs/SPEC.md', 'docs/ARCH.md', 'README.md'],
|
|
@@ -53,6 +65,23 @@ export const TEMPLATES: Template[] = [
|
|
|
53
65
|
],
|
|
54
66
|
config: {
|
|
55
67
|
preset: 'api',
|
|
68
|
+
ignore: [
|
|
69
|
+
'.git/**',
|
|
70
|
+
// Node.js
|
|
71
|
+
'node_modules/**',
|
|
72
|
+
'dist/**',
|
|
73
|
+
// Python
|
|
74
|
+
'venv/**',
|
|
75
|
+
'.venv/**',
|
|
76
|
+
'__pycache__/**',
|
|
77
|
+
'*.pyc',
|
|
78
|
+
'.tox/**',
|
|
79
|
+
'.pytest_cache/**',
|
|
80
|
+
'.mypy_cache/**',
|
|
81
|
+
'*.egg-info/**',
|
|
82
|
+
// Go
|
|
83
|
+
'vendor/**',
|
|
84
|
+
],
|
|
56
85
|
gates: {
|
|
57
86
|
max_file_lines: 400,
|
|
58
87
|
required_files: ['docs/SPEC.md', 'docs/ARCH.md', 'README.md'],
|
|
@@ -75,6 +104,14 @@ export const TEMPLATES: Template[] = [
|
|
|
75
104
|
],
|
|
76
105
|
config: {
|
|
77
106
|
preset: 'infra',
|
|
107
|
+
ignore: [
|
|
108
|
+
'.git/**',
|
|
109
|
+
'.terraform/**',
|
|
110
|
+
'*.tfstate',
|
|
111
|
+
'*.tfstate.backup',
|
|
112
|
+
'.terragrunt-cache/**',
|
|
113
|
+
'charts/**/*.tgz',
|
|
114
|
+
],
|
|
78
115
|
gates: {
|
|
79
116
|
max_file_lines: 300,
|
|
80
117
|
required_files: ['docs/RUNBOOK.md', 'docs/ARCH.md', 'README.md'],
|
|
@@ -92,6 +129,17 @@ export const TEMPLATES: Template[] = [
|
|
|
92
129
|
],
|
|
93
130
|
config: {
|
|
94
131
|
preset: 'data',
|
|
132
|
+
ignore: [
|
|
133
|
+
'.git/**',
|
|
134
|
+
'.ipynb_checkpoints/**',
|
|
135
|
+
'__pycache__/**',
|
|
136
|
+
'*.pyc',
|
|
137
|
+
'dbt_packages/**',
|
|
138
|
+
'target/**',
|
|
139
|
+
'logs/**',
|
|
140
|
+
'*.parquet',
|
|
141
|
+
'*.csv',
|
|
142
|
+
],
|
|
95
143
|
gates: {
|
|
96
144
|
max_file_lines: 500,
|
|
97
145
|
required_files: ['docs/DATA_DICTIONARY.md', 'docs/PIPELINE.md', 'README.md'],
|
|
@@ -195,6 +243,10 @@ export const UNIVERSAL_CONFIG: Config = {
|
|
|
195
243
|
sensitivity: 0.8,
|
|
196
244
|
mining_depth: 100,
|
|
197
245
|
ignored_patterns: [],
|
|
246
|
+
cross_file_patterns: true,
|
|
247
|
+
naming_consistency: true,
|
|
248
|
+
import_relationships: true,
|
|
249
|
+
max_cross_file_depth: 50,
|
|
198
250
|
},
|
|
199
251
|
environment: {
|
|
200
252
|
enabled: true,
|
|
@@ -208,6 +260,36 @@ export const UNIVERSAL_CONFIG: Config = {
|
|
|
208
260
|
auto_classify: true,
|
|
209
261
|
doc_sources: {},
|
|
210
262
|
},
|
|
263
|
+
agent_team: {
|
|
264
|
+
enabled: false,
|
|
265
|
+
max_concurrent_agents: 3,
|
|
266
|
+
cross_agent_pattern_check: true,
|
|
267
|
+
handoff_verification: true,
|
|
268
|
+
task_ownership: 'strict',
|
|
269
|
+
},
|
|
270
|
+
checkpoint: {
|
|
271
|
+
enabled: false,
|
|
272
|
+
interval_minutes: 15,
|
|
273
|
+
quality_threshold: 80,
|
|
274
|
+
drift_detection: true,
|
|
275
|
+
auto_save_on_failure: true,
|
|
276
|
+
},
|
|
277
|
+
security: {
|
|
278
|
+
enabled: false,
|
|
279
|
+
sql_injection: true,
|
|
280
|
+
xss: true,
|
|
281
|
+
path_traversal: true,
|
|
282
|
+
hardcoded_secrets: true,
|
|
283
|
+
insecure_randomness: true,
|
|
284
|
+
command_injection: true,
|
|
285
|
+
block_on_severity: 'high',
|
|
286
|
+
},
|
|
287
|
+
adaptive: {
|
|
288
|
+
enabled: false,
|
|
289
|
+
base_coverage_threshold: 80,
|
|
290
|
+
base_quality_threshold: 80,
|
|
291
|
+
auto_detect_tier: true,
|
|
292
|
+
},
|
|
211
293
|
staleness: {
|
|
212
294
|
enabled: false,
|
|
213
295
|
rules: {
|
package/src/types/index.ts
CHANGED
|
@@ -54,6 +54,11 @@ export const GatesSchema = z.object({
|
|
|
54
54
|
sensitivity: z.number().min(0).max(1).optional().default(0.8), // 0.8 correlation threshold
|
|
55
55
|
mining_depth: z.number().optional().default(100), // Number of files to sample
|
|
56
56
|
ignored_patterns: z.array(z.string()).optional().default([]),
|
|
57
|
+
// v2.14+ Extended Context for frontier models
|
|
58
|
+
cross_file_patterns: z.boolean().optional().default(true),
|
|
59
|
+
naming_consistency: z.boolean().optional().default(true),
|
|
60
|
+
import_relationships: z.boolean().optional().default(true),
|
|
61
|
+
max_cross_file_depth: z.number().optional().default(50),
|
|
57
62
|
}).optional().default({}),
|
|
58
63
|
environment: z.object({
|
|
59
64
|
enabled: z.boolean().optional().default(true),
|
|
@@ -67,6 +72,37 @@ export const GatesSchema = z.object({
|
|
|
67
72
|
auto_classify: z.boolean().optional().default(true), // Auto-detect failure category from error message
|
|
68
73
|
doc_sources: z.record(z.string()).optional().default({}), // Custom doc URLs per category
|
|
69
74
|
}).optional().default({}),
|
|
75
|
+
agent_team: z.object({
|
|
76
|
+
enabled: z.boolean().optional().default(false),
|
|
77
|
+
max_concurrent_agents: z.number().optional().default(3),
|
|
78
|
+
cross_agent_pattern_check: z.boolean().optional().default(true),
|
|
79
|
+
handoff_verification: z.boolean().optional().default(true),
|
|
80
|
+
task_ownership: z.enum(['strict', 'collaborative']).optional().default('strict'),
|
|
81
|
+
}).optional().default({}),
|
|
82
|
+
checkpoint: z.object({
|
|
83
|
+
enabled: z.boolean().optional().default(false),
|
|
84
|
+
interval_minutes: z.number().optional().default(15),
|
|
85
|
+
quality_threshold: z.number().optional().default(80),
|
|
86
|
+
drift_detection: z.boolean().optional().default(true),
|
|
87
|
+
auto_save_on_failure: z.boolean().optional().default(true),
|
|
88
|
+
}).optional().default({}),
|
|
89
|
+
security: z.object({
|
|
90
|
+
enabled: z.boolean().optional().default(false),
|
|
91
|
+
sql_injection: z.boolean().optional().default(true),
|
|
92
|
+
xss: z.boolean().optional().default(true),
|
|
93
|
+
path_traversal: z.boolean().optional().default(true),
|
|
94
|
+
hardcoded_secrets: z.boolean().optional().default(true),
|
|
95
|
+
insecure_randomness: z.boolean().optional().default(true),
|
|
96
|
+
command_injection: z.boolean().optional().default(true),
|
|
97
|
+
block_on_severity: z.enum(['critical', 'high', 'medium', 'low']).optional().default('high'),
|
|
98
|
+
}).optional().default({}),
|
|
99
|
+
adaptive: z.object({
|
|
100
|
+
enabled: z.boolean().optional().default(false),
|
|
101
|
+
base_coverage_threshold: z.number().optional().default(80),
|
|
102
|
+
base_quality_threshold: z.number().optional().default(80),
|
|
103
|
+
auto_detect_tier: z.boolean().optional().default(true),
|
|
104
|
+
forced_tier: z.enum(['hobby', 'startup', 'enterprise']).optional(),
|
|
105
|
+
}).optional().default({}),
|
|
70
106
|
});
|
|
71
107
|
|
|
72
108
|
export const CommandsSchema = z.object({
|