tribunal-kit 9.2.6 → 9.2.7

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.
Files changed (63) hide show
  1. package/.agent/ARCHITECTURE.md +2 -2
  2. package/.agent/history/architecture-intelligence.html +1487 -401
  3. package/.agent/history/architecture-model.json +9365 -5697
  4. package/.agent/history/architecture-verification.json +37 -9
  5. package/.agent/routing_index.json +2069 -3340
  6. package/.agent/rules/GEMINI.md +1 -1
  7. package/.agent/scripts/adaptive_execution.js +511 -0
  8. package/.agent/scripts/architecture_diff.js +303 -260
  9. package/.agent/scripts/architecture_extractor.js +792 -753
  10. package/.agent/scripts/architecture_health.js +293 -256
  11. package/.agent/scripts/architecture_intelligence.js +3100 -0
  12. package/.agent/scripts/architecture_verifier.js +303 -270
  13. package/.agent/scripts/architecture_visualizer.js +880 -873
  14. package/.agent/scripts/blast_radius_engine.js +274 -234
  15. package/.agent/scripts/capability_evolution.js +453 -0
  16. package/.agent/scripts/concept_extractor.js +240 -0
  17. package/.agent/scripts/consideration_engine.js +284 -0
  18. package/.agent/scripts/coverage_engine.js +102 -0
  19. package/.agent/scripts/cross_domain_engine.js +1883 -0
  20. package/.agent/scripts/engineering_memory.js +1313 -0
  21. package/.agent/scripts/integrity_manifest.js +1 -5
  22. package/.agent/scripts/judgment_engine.js +790 -0
  23. package/.agent/scripts/materiality_engine.js +251 -0
  24. package/.agent/scripts/outcome_engine.js +624 -0
  25. package/.agent/scripts/outcome_optimization_engine.js +1632 -0
  26. package/.agent/scripts/simulation_engine.js +1478 -0
  27. package/.agent/scripts/skill_intelligence.js +1371 -0
  28. package/.agent/scripts/validation_engine.js +236 -236
  29. package/.agent/scripts/verification_auditor.js +266 -0
  30. package/.agent/skill-intelligence/taxonomy/engineering-concepts.json +555 -0
  31. package/.agent/skill_topic_map.json +301 -341
  32. package/.agent/skills_inventory.json +4272 -6922
  33. package/.agent/workflows/arch.md +96 -80
  34. package/.agent/workflows/skill-intelligence.md +216 -0
  35. package/CONTRIBUTING.md +2 -2
  36. package/README.md +24 -24
  37. package/bin/adapter-install.js +1 -1
  38. package/package.json +8 -9
  39. package/plugins/core-plugin.json +1 -1
  40. package/plugins/gemini/extension.json +1 -1
  41. package/src/browser/index.js +2 -1
  42. package/src/browser/launcher.js +41 -1
  43. package/src/cli.js +3 -1
  44. package/src/commands/arch.js +261 -172
  45. package/src/commands/context.js +27 -8
  46. package/src/commands/learn.js +21 -6
  47. package/src/commands/memory.js +10 -1
  48. package/src/commands/native.js +12 -3
  49. package/src/commands/skill-intelligence.js +1165 -0
  50. package/src/commands/system1.js +105 -4
  51. package/src/context/ranker.js +337 -339
  52. package/src/evolution/engine.js +296 -295
  53. package/src/synthesis/claim_check_separator.js +294 -271
  54. package/src/system1/browser_intelligence.js +300 -0
  55. package/src/system1/constants.js +49 -0
  56. package/src/system1/decision_engine.js +498 -0
  57. package/src/system1/evidence_engine.js +602 -0
  58. package/src/system1/evolution_pipeline.js +581 -0
  59. package/src/system1/index.d.ts +114 -0
  60. package/src/system1/index.js +102 -0
  61. package/src/system1/reviewer_orchestrator.js +477 -0
  62. package/src/tui/banner.js +1 -1
  63. package/src/utils/fs.js +27 -0
@@ -0,0 +1,581 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * evolution_pipeline.js — System-1 Capability 4: Controlled Self-Evolution Pipeline
5
+ * =================================================================================
6
+ * Governs candidate generation, regression validation, adversarial testing,
7
+ * candidate scoring, Human Gate approval, promotion, and clean rollback.
8
+ *
9
+ * CRITICAL RULE:
10
+ * Self-evolution MUST NEVER directly modify production rules.
11
+ * The system generates and evaluates candidate routing / heuristic improvements only.
12
+ *
13
+ * Pipeline Lifecycle:
14
+ * Observed Outcome -> Learning Signal -> Candidate Rule ->
15
+ * Validation Against Regression Corpus -> Adversarial Tests ->
16
+ * Candidate Score -> Human Approval -> Promotion -> (Optional Rollback)
17
+ *
18
+ * Candidate States:
19
+ * - PROPOSED
20
+ * - VALIDATING
21
+ * - REJECTED
22
+ * - APPROVED
23
+ * - PROMOTED
24
+ * - ROLLED_BACK
25
+ */
26
+
27
+ const fs = require('fs');
28
+ const path = require('path');
29
+ const crypto = require('crypto');
30
+ const {
31
+ validateEvolutionProposal,
32
+ ALLOWED_TARGETS,
33
+ FORBIDDEN_TARGETS,
34
+ EVIDENCE_HIERARCHY,
35
+ } = require('../evolution/engine');
36
+
37
+ const VALID_SOURCES = new Set([
38
+ 'SYSTEM1_CLASSIFICATION',
39
+ 'REVIEWER_FINDING',
40
+ 'INNER_LOOP',
41
+ 'HUMAN_GATE',
42
+ 'FP_CORRECTION',
43
+ 'FN_CORRECTION',
44
+ 'TEST_RUNNER',
45
+ ]);
46
+
47
+ const CANDIDATE_STATES = new Set([
48
+ 'PROPOSED',
49
+ 'VALIDATING',
50
+ 'REJECTED',
51
+ 'APPROVED',
52
+ 'PROMOTED',
53
+ 'ROLLED_BACK',
54
+ ]);
55
+
56
+ const FORBIDDEN_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
57
+
58
+ function validateCandidateId(id) {
59
+ if (typeof id !== 'string' || !/^[a-zA-Z0-9_-]+$/.test(id)) {
60
+ throw new Error(
61
+ `Invalid candidate ID "${id}". ID must contain only alphanumeric characters, underscores, and dashes.`,
62
+ );
63
+ }
64
+ }
65
+
66
+ function writeFileSyncAtomic(filePath, data, encoding = 'utf8') {
67
+ const dir = path.dirname(filePath);
68
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
69
+ const tmpPath = path.join(
70
+ dir,
71
+ `.${path.basename(filePath)}.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2, 6)}.tmp`,
72
+ );
73
+ fs.writeFileSync(tmpPath, data, encoding);
74
+ fs.renameSync(tmpPath, filePath);
75
+ }
76
+
77
+ function getEvolutionDir(agentDir = path.join(process.cwd(), '.agent')) {
78
+ return path.join(agentDir, 'evolution');
79
+ }
80
+
81
+ function acquireEvolutionLock(agentDir) {
82
+ const lockPath = path.join(getEvolutionDir(agentDir), 'evolution.lock');
83
+ const dir = path.dirname(lockPath);
84
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
85
+ const maxRetries = 30;
86
+ for (let i = 0; i < maxRetries; i++) {
87
+ try {
88
+ const fd = fs.openSync(lockPath, 'wx');
89
+ fs.closeSync(fd);
90
+ return lockPath;
91
+ } catch (err) {
92
+ if (err.code !== 'EEXIST') throw err;
93
+ const waitBuf = new Int32Array(new SharedArrayBuffer(4));
94
+ Atomics.wait(waitBuf, 0, 0, 30);
95
+ }
96
+ }
97
+ // Check for stale lock (> 15000ms old)
98
+ try {
99
+ const stats = fs.statSync(lockPath);
100
+ if (Date.now() - stats.mtimeMs > 15000) {
101
+ fs.unlinkSync(lockPath);
102
+ const fd = fs.openSync(lockPath, 'wx');
103
+ fs.closeSync(fd);
104
+ return lockPath;
105
+ }
106
+ } catch (_) {}
107
+ throw new Error('Could not acquire concurrency lock for evolution pipeline');
108
+ }
109
+
110
+ function releaseEvolutionLock(lockPath) {
111
+ try {
112
+ if (lockPath && fs.existsSync(lockPath)) {
113
+ fs.unlinkSync(lockPath);
114
+ }
115
+ } catch (_) {}
116
+ }
117
+
118
+ function getCandidatesDir(agentDir) {
119
+ return path.join(getEvolutionDir(agentDir), 'candidates');
120
+ }
121
+
122
+ function getRollbacksDir(agentDir) {
123
+ return path.join(getEvolutionDir(agentDir), 'rollbacks');
124
+ }
125
+
126
+ function getActiveEvolutionsPath(agentDir) {
127
+ return path.join(getEvolutionDir(agentDir), 'active_evolutions.json');
128
+ }
129
+
130
+ /**
131
+ * Ingests a verified outcome and produces a candidate proposal
132
+ * @param {Object} outcome
133
+ * @param {string} outcome.source - Verified source origin
134
+ * @param {string} outcome.target - Allowed evolution target
135
+ * @param {string} outcome.current_behavior
136
+ * @param {string} outcome.proposed_change
137
+ * @param {string} outcome.evidence - Concrete supporting evidence
138
+ * @param {string} [outcome.evidence_type] - Hierarchy level
139
+ * @param {string} [outcome.risk]
140
+ * @param {Object} [outcome.previous_state] - For rollback
141
+ * @param {string} [candidateId] - Optional explicit ID
142
+ * @returns {Object} Candidate object
143
+ */
144
+ function createCandidateFromOutcome(outcome = {}, candidateId = null) {
145
+ if (candidateId) {
146
+ validateCandidateId(candidateId);
147
+ }
148
+
149
+ // 1. Verify Outcome Source (reject raw LLM speculation or arbitrary text)
150
+ if (!outcome.source || !VALID_SOURCES.has(outcome.source)) {
151
+ throw new Error(
152
+ `Invalid learning source "${outcome.source}". Self-evolution strictly rejects unverified speculation. Allowed sources: ${Array.from(VALID_SOURCES).join(', ')}`,
153
+ );
154
+ }
155
+
156
+ // 2. Reject forbidden targets immediately
157
+ if (outcome.target && FORBIDDEN_TARGETS.has(outcome.target)) {
158
+ throw new Error(
159
+ `Target "${outcome.target}" is a protected security boundary. Automatic evolution is strictly prohibited.`,
160
+ );
161
+ }
162
+
163
+ if (!outcome.target || !ALLOWED_TARGETS.has(outcome.target)) {
164
+ throw new Error(
165
+ `Target "${outcome.target}" is not an allowed evolution target. Allowed: ${Array.from(ALLOWED_TARGETS).join(', ')}`,
166
+ );
167
+ }
168
+
169
+ // 3. Evidence quality check (Anti-Self-Poisoning)
170
+ const evidenceType = outcome.evidence_type || 'DETERMINISTIC_TELEMETRY';
171
+ const evidenceWeight = EVIDENCE_HIERARCHY[evidenceType] || 0;
172
+ if (evidenceWeight <= EVIDENCE_HIERARCHY.REVIEWER_CLAIM) {
173
+ throw new Error(
174
+ `Anti-Self-Poisoning violation: Learning signal relies on "${evidenceType}". Only verified outcomes, telemetry, and reproducible patterns can generate candidates.`,
175
+ );
176
+ }
177
+
178
+ const id = candidateId || `EVO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
179
+
180
+ const candidate = {
181
+ candidate_id: id,
182
+ source: outcome.source,
183
+ target: outcome.target,
184
+ observed_behavior: outcome.current_behavior || outcome.observed_behavior || '',
185
+ proposed_change: outcome.proposed_change,
186
+ supporting_evidence: outcome.evidence || outcome.supporting_evidence || '',
187
+ evidence_type: evidenceType,
188
+ evidence_weight: evidenceWeight,
189
+ risk: ['LOW', 'MEDIUM', 'HIGH'].includes(String(outcome.risk).toUpperCase())
190
+ ? String(outcome.risk).toUpperCase()
191
+ : 'MEDIUM',
192
+ tests_passed: [],
193
+ tests_failed: [],
194
+ candidate_score: 0.0,
195
+ confidence: evidenceType === 'VERIFIED_TEST_RESULT' ? 'L1' : 'L2',
196
+ status: 'PROPOSED',
197
+ created_at: new Date().toISOString(),
198
+ approval: null,
199
+ rollback_data: {
200
+ previous_state: outcome.previous_state ?? null,
201
+ target: outcome.target,
202
+ },
203
+ };
204
+
205
+ const validation = validateEvolutionProposal({
206
+ proposal_id: candidate.candidate_id,
207
+ target: candidate.target,
208
+ current_behavior: candidate.observed_behavior,
209
+ observed_evidence: candidate.supporting_evidence,
210
+ proposed_change:
211
+ typeof candidate.proposed_change === 'string'
212
+ ? candidate.proposed_change
213
+ : JSON.stringify(candidate.proposed_change),
214
+ expected_effect: 'Optimized routing / heuristic accuracy backed by verified outcome',
215
+ risk: candidate.risk,
216
+ tests_required: ['regression_suite'],
217
+ regression_requirements: ['no_security_downgrades'],
218
+ evidence_type: candidate.evidence_type,
219
+ });
220
+
221
+ if (!validation.valid) {
222
+ throw new Error(`Candidate proposal failed contract validation: ${validation.reason}`);
223
+ }
224
+
225
+ return candidate;
226
+ }
227
+
228
+ /**
229
+ * Validates candidate against regression corpus and adversarial test fixtures
230
+ * @param {Object} candidate
231
+ * @param {Array<{ name: string, run: Function, isAdversarial?: boolean }>} testSuite
232
+ * @returns {Promise<Object>} Validation results with candidate score
233
+ */
234
+ async function validateCandidateAgainstCorpus(candidate, testSuite = []) {
235
+ if (!candidate || typeof candidate !== 'object') {
236
+ throw new Error('Invalid candidate provided for validation');
237
+ }
238
+
239
+ if (candidate.status === 'PROMOTED' || candidate.status === 'ROLLED_BACK') {
240
+ throw new Error(
241
+ `Cannot validate candidate ${candidate.candidate_id} with status "${candidate.status}". Already promoted or rolled back candidates cannot be re-validated.`,
242
+ );
243
+ }
244
+
245
+ candidate.status = 'VALIDATING';
246
+ const passed = [];
247
+ const failed = [];
248
+
249
+ for (const testCase of testSuite) {
250
+ const testName = testCase.name || 'unnamed-test';
251
+ try {
252
+ const result = await testCase.run(candidate);
253
+ if (result === true || (result && result.passed)) {
254
+ passed.push(testName);
255
+ } else {
256
+ failed.push({
257
+ name: testName,
258
+ reason: result?.reason || 'Test assertion failed',
259
+ isAdversarial: !!testCase.isAdversarial,
260
+ });
261
+ }
262
+ } catch (err) {
263
+ failed.push({
264
+ name: testName,
265
+ reason: err.message,
266
+ isAdversarial: !!testCase.isAdversarial,
267
+ });
268
+ }
269
+ }
270
+
271
+ candidate.tests_passed = passed;
272
+ candidate.tests_failed = failed.map(f => f.name);
273
+
274
+ const total = passed.length + failed.length;
275
+ const passRatio = total > 0 ? passed.length / total : 0;
276
+ candidate.candidate_score = parseFloat(passRatio.toFixed(3));
277
+
278
+ // If any test failed, reject candidate
279
+ if (failed.length > 0) {
280
+ candidate.status = 'REJECTED';
281
+ candidate.rejection = {
282
+ reason: `Regression validation failed on ${failed.length} test(s): ${failed.map(f => f.name).join(', ')}`,
283
+ rejected_at: new Date().toISOString(),
284
+ failures: failed,
285
+ };
286
+ } else {
287
+ // Passed all tests -> ready for Human Gate
288
+ candidate.status = 'VALIDATING'; // Remains in validating until human approves
289
+ }
290
+
291
+ return {
292
+ valid: failed.length === 0,
293
+ candidate_score: candidate.candidate_score,
294
+ passed_count: passed.length,
295
+ failed_count: failed.length,
296
+ candidate,
297
+ };
298
+ }
299
+
300
+ /**
301
+ * Human Gate: Explicitly approves a validated candidate
302
+ * @param {Object} candidate
303
+ * @param {string} approverName
304
+ * @param {string} [agentDir]
305
+ * @returns {Object} Approved candidate
306
+ */
307
+ function approveCandidate(candidate, approverName, agentDir) {
308
+ if (!approverName || typeof approverName !== 'string') {
309
+ throw new Error('Human approval requires an explicit approver name/signature.');
310
+ }
311
+
312
+ if (candidate.status === 'REJECTED') {
313
+ throw new Error(
314
+ `Cannot approve candidate ${candidate.candidate_id}: candidate was rejected during validation.`,
315
+ );
316
+ }
317
+
318
+ if (candidate.status !== 'VALIDATING' && candidate.status !== 'VALIDATED') {
319
+ throw new Error(
320
+ `Cannot approve candidate ${candidate.candidate_id} with status "${candidate.status}". Candidate must undergo regression corpus validation before Human Gate approval.`,
321
+ );
322
+ }
323
+
324
+ if (candidate.tests_failed && candidate.tests_failed.length > 0) {
325
+ throw new Error(
326
+ `Cannot approve candidate ${candidate.candidate_id}: failing regression tests exist.`,
327
+ );
328
+ }
329
+
330
+ if (
331
+ !candidate.target ||
332
+ !ALLOWED_TARGETS.has(candidate.target) ||
333
+ FORBIDDEN_TARGETS.has(candidate.target)
334
+ ) {
335
+ throw new Error(
336
+ `Target "${candidate.target}" is a protected or invalid evolution target. Human Gate approval rejected.`,
337
+ );
338
+ }
339
+
340
+ candidate.status = 'APPROVED';
341
+ candidate.approval = {
342
+ approved_by: approverName,
343
+ approved_at: new Date().toISOString(),
344
+ human_gate_cleared: true,
345
+ };
346
+
347
+ if (agentDir) {
348
+ saveCandidate(candidate, agentDir);
349
+ }
350
+
351
+ return candidate;
352
+ }
353
+
354
+ /**
355
+ * Promotes an APPROVED candidate into active evolutions
356
+ * @param {Object} candidate
357
+ * @param {string} [agentDir]
358
+ * @returns {Object} Promotion summary
359
+ */
360
+ function promoteCandidate(candidate, agentDir = path.join(process.cwd(), '.agent')) {
361
+ if (candidate.status !== 'APPROVED') {
362
+ throw new Error(
363
+ `Cannot promote candidate ${candidate.candidate_id} with status "${candidate.status}". Only APPROVED candidates that cleared the Human Gate can be promoted.`,
364
+ );
365
+ }
366
+
367
+ validateCandidateId(candidate.candidate_id);
368
+ if (FORBIDDEN_KEYS.has(candidate.target)) {
369
+ throw new Error(`Dangerous target key "${candidate.target}" rejected.`);
370
+ }
371
+ if (
372
+ !candidate.target ||
373
+ !ALLOWED_TARGETS.has(candidate.target) ||
374
+ FORBIDDEN_TARGETS.has(candidate.target)
375
+ ) {
376
+ throw new Error(
377
+ `Target "${candidate.target}" is a protected or invalid evolution target. Promotion rejected.`,
378
+ );
379
+ }
380
+
381
+ const rollbacksDir = getRollbacksDir(agentDir);
382
+ const activePath = getActiveEvolutionsPath(agentDir);
383
+ if (!fs.existsSync(rollbacksDir)) fs.mkdirSync(rollbacksDir, { recursive: true });
384
+
385
+ const lock = acquireEvolutionLock(agentDir);
386
+ try {
387
+ // 1. Read existing active evolutions
388
+ let active = {};
389
+ if (fs.existsSync(activePath)) {
390
+ try {
391
+ active = JSON.parse(fs.readFileSync(activePath, 'utf8'));
392
+ } catch (_) {
393
+ active = {};
394
+ }
395
+ }
396
+
397
+ // 2. Save rollback snapshot
398
+ const rollbackSnapshot = {
399
+ candidate_id: candidate.candidate_id,
400
+ target: candidate.target,
401
+ previous_state: active[candidate.target] ?? candidate.rollback_data?.previous_state ?? null,
402
+ promoted_at: new Date().toISOString(),
403
+ };
404
+ writeFileSyncAtomic(
405
+ path.join(rollbacksDir, `${candidate.candidate_id}.rollback.json`),
406
+ JSON.stringify(rollbackSnapshot, null, 2),
407
+ 'utf8',
408
+ );
409
+
410
+ // 3. Promote candidate
411
+ active[candidate.target] = {
412
+ candidate_id: candidate.candidate_id,
413
+ proposed_change: candidate.proposed_change,
414
+ promoted_at: new Date().toISOString(),
415
+ approved_by: candidate.approval?.approved_by || 'Human Gate',
416
+ };
417
+ writeFileSyncAtomic(activePath, JSON.stringify(active, null, 2), 'utf8');
418
+
419
+ candidate.status = 'PROMOTED';
420
+ candidate.promoted_at = new Date().toISOString();
421
+ saveCandidate(candidate, agentDir);
422
+ } finally {
423
+ releaseEvolutionLock(lock);
424
+ }
425
+
426
+ return {
427
+ promoted: true,
428
+ candidate_id: candidate.candidate_id,
429
+ target: candidate.target,
430
+ rollback_available: true,
431
+ };
432
+ }
433
+
434
+ /**
435
+ * Rolls back a PROMOTED candidate cleanly
436
+ * @param {string} candidateId
437
+ * @param {string} [agentDir]
438
+ * @returns {Object} Rollback summary
439
+ */
440
+ function rollbackCandidate(candidateId, agentDir = path.join(process.cwd(), '.agent')) {
441
+ validateCandidateId(candidateId);
442
+ const rollbacksDir = getRollbacksDir(agentDir);
443
+ const activePath = getActiveEvolutionsPath(agentDir);
444
+ const rollbackPath = path.join(rollbacksDir, `${candidateId}.rollback.json`);
445
+
446
+ if (!fs.existsSync(rollbackPath)) {
447
+ throw new Error(`No rollback snapshot found for candidate "${candidateId}".`);
448
+ }
449
+
450
+ const snapshot = JSON.parse(fs.readFileSync(rollbackPath, 'utf8'));
451
+ if (FORBIDDEN_KEYS.has(snapshot.target)) {
452
+ throw new Error(`Dangerous snapshot target key "${snapshot.target}" rejected.`);
453
+ }
454
+ if (
455
+ !snapshot.target ||
456
+ !ALLOWED_TARGETS.has(snapshot.target) ||
457
+ FORBIDDEN_TARGETS.has(snapshot.target)
458
+ ) {
459
+ throw new Error(`Dangerous snapshot target "${snapshot.target}" rejected.`);
460
+ }
461
+
462
+ const lock = acquireEvolutionLock(agentDir);
463
+ try {
464
+ let active = {};
465
+ if (fs.existsSync(activePath)) {
466
+ try {
467
+ active = JSON.parse(fs.readFileSync(activePath, 'utf8'));
468
+ } catch (_) {
469
+ active = {};
470
+ }
471
+ }
472
+
473
+ // Verify candidate is not already rolled back and is in PROMOTED status
474
+ const candidate = loadCandidate(candidateId, agentDir);
475
+ if (candidate && candidate.status === 'ROLLED_BACK') {
476
+ throw new Error(`Candidate "${candidateId}" has already been rolled back.`);
477
+ }
478
+ if (candidate && candidate.status !== 'PROMOTED') {
479
+ throw new Error(
480
+ `Cannot rollback candidate "${candidateId}" with status "${candidate.status}". Only PROMOTED candidates can be rolled back.`,
481
+ );
482
+ }
483
+
484
+ // Enforce LIFO / Active Candidate check:
485
+ // Only the candidate currently active for the target can be rolled back.
486
+ // Rolling back an inactive or superseded candidate would corrupt newer active evolutions.
487
+ const currentActive = active[snapshot.target];
488
+ if (!currentActive || currentActive.candidate_id !== candidateId) {
489
+ throw new Error(
490
+ `Cannot rollback candidate "${candidateId}" on target "${snapshot.target}": candidate is not currently active (active candidate: "${currentActive?.candidate_id || 'none'}"). Rollback the active candidate first.`,
491
+ );
492
+ }
493
+
494
+ // Restore previous state or remove if none existed
495
+ if (snapshot.previous_state !== null && snapshot.previous_state !== undefined) {
496
+ active[snapshot.target] = snapshot.previous_state;
497
+ } else {
498
+ delete active[snapshot.target];
499
+ }
500
+ writeFileSyncAtomic(activePath, JSON.stringify(active, null, 2), 'utf8');
501
+
502
+ // Update candidate status to ROLLED_BACK
503
+ if (candidate) {
504
+ candidate.status = 'ROLLED_BACK';
505
+ candidate.rolled_back_at = new Date().toISOString();
506
+ saveCandidate(candidate, agentDir);
507
+ }
508
+
509
+ // Remove single-use snapshot to prevent replay rollback
510
+ try {
511
+ if (fs.existsSync(rollbackPath)) {
512
+ fs.unlinkSync(rollbackPath);
513
+ }
514
+ } catch (_) {}
515
+
516
+ return {
517
+ rolled_back: true,
518
+ candidate_id: candidateId,
519
+ target: snapshot.target,
520
+ restored_state: snapshot.previous_state,
521
+ };
522
+ } finally {
523
+ releaseEvolutionLock(lock);
524
+ }
525
+ }
526
+
527
+ /**
528
+ * Persistence helpers
529
+ */
530
+ function saveCandidate(candidate, agentDir = path.join(process.cwd(), '.agent')) {
531
+ validateCandidateId(candidate.candidate_id);
532
+ const dir = getCandidatesDir(agentDir);
533
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
534
+ const filePath = path.join(dir, `${candidate.candidate_id}.json`);
535
+ writeFileSyncAtomic(filePath, JSON.stringify(candidate, null, 2), 'utf8');
536
+ return filePath;
537
+ }
538
+
539
+ function loadCandidate(candidateId, agentDir = path.join(process.cwd(), '.agent')) {
540
+ validateCandidateId(candidateId);
541
+ const filePath = path.join(getCandidatesDir(agentDir), `${candidateId}.json`);
542
+ if (!fs.existsSync(filePath)) return null;
543
+ try {
544
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
545
+ } catch (_) {
546
+ return null;
547
+ }
548
+ }
549
+
550
+ function loadAllCandidates(agentDir = path.join(process.cwd(), '.agent')) {
551
+ const dir = getCandidatesDir(agentDir);
552
+ if (!fs.existsSync(dir)) return [];
553
+ return fs
554
+ .readdirSync(dir)
555
+ .filter(f => f.endsWith('.json'))
556
+ .map(f => {
557
+ try {
558
+ return JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
559
+ } catch (_) {
560
+ return null;
561
+ }
562
+ })
563
+ .filter(Boolean);
564
+ }
565
+
566
+ module.exports = {
567
+ createCandidateFromOutcome,
568
+ validateCandidateAgainstCorpus,
569
+ approveCandidate,
570
+ promoteCandidate,
571
+ rollbackCandidate,
572
+ saveCandidate,
573
+ loadCandidate,
574
+ loadAllCandidates,
575
+ getActiveEvolutionsPath,
576
+ acquireEvolutionLock,
577
+ releaseEvolutionLock,
578
+ writeFileSyncAtomic,
579
+ VALID_SOURCES,
580
+ CANDIDATE_STATES,
581
+ };
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Tribunal Kit System-1 Capability Suite Type Definitions
3
+ */
4
+
5
+ import {
6
+ EvidenceResult,
7
+ EvidenceItem,
8
+ DecisionPayload,
9
+ ReviewerOrchestrationResult,
10
+ BrowserRequirementResult,
11
+ EvolutionCandidate,
12
+ } from '../index';
13
+
14
+ export class System1Provider {
15
+ constructor();
16
+ isAvailable(): boolean;
17
+ classifyImpact(task: string, files: string[]): Promise<number>;
18
+ }
19
+
20
+ export function getLayaDir(): string;
21
+ export function getConfigPath(): string;
22
+
23
+ // Capability 1: Evidence Intelligence
24
+ export interface EvidenceOptions {
25
+ task?: string;
26
+ diff?: string;
27
+ files?: string[] | string;
28
+ repoRoot?: string;
29
+ memory?: any[];
30
+ previousFindings?: any[];
31
+ maxItems?: number;
32
+ maxTokens?: number;
33
+ }
34
+
35
+ export function collectAndRankEvidence(options?: EvidenceOptions): EvidenceResult;
36
+ export function extractChangedSymbols(
37
+ diff?: string,
38
+ content?: string,
39
+ ): Array<{ name: string; type: string; line?: number }>;
40
+ export function extractBoundedSnippet(
41
+ content: string,
42
+ targetLine?: number | null,
43
+ targetSymbol?: string | null,
44
+ maxLines?: number,
45
+ ): string;
46
+ export function redactSecrets(text: string): string;
47
+
48
+ export const TYPE_PRIORITY: Record<string, number>;
49
+ export const SECURITY_BOUNDARIES: RegExp[];
50
+
51
+ // Capability 2: Decision Engine
52
+ export interface DecisionOptions {
53
+ files?: string[];
54
+ lines?: number;
55
+ task?: string;
56
+ diff?: string;
57
+ layaTier?: number | null;
58
+ memory?: any[];
59
+ previousFindings?: any[];
60
+ tierResolver?: (options: any) => number;
61
+ }
62
+
63
+ export function evaluateDecision(options?: DecisionOptions): DecisionPayload;
64
+ export function resolveMonotonicImpactTier(options?: any): number;
65
+ export const HIGH_RISK_PATTERNS: RegExp;
66
+ export const HIGH_RISK_EXTENSIONS: RegExp;
67
+ export const TIER_NAMES: Record<number, string>;
68
+ export const ROUTING_NAMES: Record<number, string>;
69
+
70
+ // Capability 3: Reviewer Orchestration
71
+ export function orchestrateReviewers(
72
+ evidenceResult?: any,
73
+ options?: any,
74
+ ): ReviewerOrchestrationResult;
75
+ export function detectRequiredDomains(evidenceResult: any): string[];
76
+ export const REVIEWER_CATALOG: Record<string, any>;
77
+
78
+ // Capability 4: Self-Evolution
79
+ export function createCandidateFromOutcome(outcome: any, id?: string): EvolutionCandidate;
80
+ export function validateCandidateAgainstCorpus(
81
+ candidate: EvolutionCandidate,
82
+ testSuites: any[],
83
+ ): Promise<{ valid: boolean; results: any[] }>;
84
+ export function approveCandidate(
85
+ candidate: EvolutionCandidate,
86
+ approvedBy: string,
87
+ agentDir?: string,
88
+ ): EvolutionCandidate;
89
+ export function promoteCandidate(
90
+ candidate: EvolutionCandidate,
91
+ agentDir?: string,
92
+ ): { promoted: boolean; candidate_id: string; target: string };
93
+ export function rollbackCandidate(
94
+ candidateId: string,
95
+ agentDir?: string,
96
+ ): { rolled_back: boolean; candidate_id: string; target: string; restored_state: any };
97
+ export function saveCandidate(candidate: EvolutionCandidate, agentDir?: string): string;
98
+ export function loadCandidate(candidateId: string, agentDir?: string): EvolutionCandidate | null;
99
+ export function loadAllCandidates(agentDir?: string): EvolutionCandidate[];
100
+ export function getActiveEvolutionsPath(agentDir?: string): string;
101
+ export function acquireEvolutionLock(agentDir?: string): string;
102
+ export function releaseEvolutionLock(lockPath: string): void;
103
+ export function writeFileSyncAtomic(filePath: string, data: string, encoding?: string): void;
104
+
105
+ export const VALID_SOURCES: string[];
106
+ export const CANDIDATE_STATES: string[];
107
+
108
+ // Capability 5: Browser Intelligence
109
+ export function evaluateBrowserRequirement(
110
+ task?: string,
111
+ changedFiles?: string[],
112
+ diff?: string,
113
+ ): BrowserRequirementResult;
114
+ export function extractAffectedRoutes(changedFiles?: string[], diff?: string): string[];