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
@@ -1,295 +1,296 @@
1
- 'use strict';
2
-
3
- /**
4
- * evolution/engine.js — Controlled Self-Evolution Engine for Tribunal Kit
5
- * =======================================================================
6
- * System-1 Capability 4:
7
- * Governs the proposal-driven evolution of reviewer routing, context weights,
8
- * heuristics, skill idioms, and regression fixtures while strictly enforcing:
9
- *
10
- * 1. The Human Gate: No evolution can silently deploy without human approval.
11
- * 2. Trust Boundaries: Protected security boundaries, execution policies, and
12
- * package integrity validation can NEVER automatically evolve.
13
- * 3. Anti-Self-Poisoning: Follows strict evidence hierarchy:
14
- * VERIFIED_TEST_RESULT > DETERMINISTIC_TELEMETRY > REPRODUCIBLE_PATTERN > REVIEWER_CLAIM
15
- * Proposals based solely on unverified LLM claims are rejected.
16
- */
17
-
18
- const fs = require('fs');
19
- const path = require('path');
20
- const crypto = require('crypto');
21
-
22
- const ALLOWED_TARGETS = new Set([
23
- 'reviewer-routing',
24
- 'context-weights',
25
- 'skill-instructions',
26
- 'reviewer-selection',
27
- 'deterministic-heuristics',
28
- 'known-failure-patterns',
29
- 'workflow-guidance',
30
- 'regression-fixtures',
31
- ]);
32
-
33
- const FORBIDDEN_TARGETS = new Set([
34
- 'security-boundaries',
35
- 'shell-execution-policy',
36
- 'package-integrity',
37
- 'dependency-pinning',
38
- 'human-gate',
39
- 'system1-trust-boundary',
40
- 'production-security',
41
- 'arbitrary-executable-code',
42
- ]);
43
-
44
- const EVIDENCE_HIERARCHY = {
45
- VERIFIED_TEST_RESULT: 4,
46
- DETERMINISTIC_TELEMETRY: 3,
47
- REPRODUCIBLE_PATTERN: 2,
48
- REVIEWER_CLAIM: 1,
49
- };
50
-
51
- /**
52
- * Creates and formats a new Evolution Proposal.
53
- */
54
- function createEvolutionProposal({
55
- target,
56
- currentBehavior,
57
- observedEvidence,
58
- proposedChange,
59
- expectedEffect,
60
- risk = 'MEDIUM',
61
- testsRequired = [],
62
- regressionRequirements = [],
63
- evidenceType = 'DETERMINISTIC_TELEMETRY',
64
- proposalId = null,
65
- }) {
66
- const id = proposalId || `EVO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
67
-
68
- const proposal = {
69
- proposal_id: id,
70
- target,
71
- current_behavior: currentBehavior,
72
- observed_evidence: observedEvidence,
73
- proposed_change: proposedChange,
74
- expected_effect: expectedEffect,
75
- risk: ['LOW', 'MEDIUM', 'HIGH'].includes(String(risk).toUpperCase())
76
- ? String(risk).toUpperCase()
77
- : 'MEDIUM',
78
- tests_required: Array.isArray(testsRequired) ? testsRequired : [testsRequired],
79
- regression_requirements: Array.isArray(regressionRequirements)
80
- ? regressionRequirements
81
- : [regressionRequirements],
82
- evidence_type: EVIDENCE_HIERARCHY[evidenceType] ? evidenceType : 'REVIEWER_CLAIM',
83
- evidence_weight: EVIDENCE_HIERARCHY[evidenceType] || 1,
84
- status: 'PROPOSED',
85
- created_at: new Date().toISOString(),
86
- approval: null,
87
- };
88
-
89
- return proposal;
90
- }
91
-
92
- /**
93
- * Validates an evolution proposal against Anti-Self-Poisoning & Security Rules.
94
- */
95
- function validateEvolutionProposal(proposal) {
96
- if (!proposal || typeof proposal !== 'object') {
97
- return { valid: false, reason: 'Proposal must be an object.' };
98
- }
99
-
100
- // 1. Check required fields
101
- const requiredFields = [
102
- 'proposal_id',
103
- 'target',
104
- 'current_behavior',
105
- 'observed_evidence',
106
- 'proposed_change',
107
- 'expected_effect',
108
- 'risk',
109
- 'tests_required',
110
- 'regression_requirements',
111
- ];
112
-
113
- for (const field of requiredFields) {
114
- if (!proposal[field] || (Array.isArray(proposal[field]) && proposal[field].length === 0)) {
115
- return {
116
- valid: false,
117
- reason: `Missing required proposal field: "${field}". Every proposal must be backed by evidence.`,
118
- };
119
- }
120
- }
121
-
122
- // 2. Security boundary enforcement: Forbidden targets cannot evolve automatically
123
- if (FORBIDDEN_TARGETS.has(proposal.target)) {
124
- return {
125
- valid: false,
126
- reason: `Target "${proposal.target}" is a protected security boundary. Automatic evolution of security boundaries, shell policies, and package integrity is strictly forbidden.`,
127
- };
128
- }
129
-
130
- // 3. Allowed target check
131
- if (!ALLOWED_TARGETS.has(proposal.target)) {
132
- return {
133
- valid: false,
134
- reason: `Target "${proposal.target}" is not an allowed evolution target. Allowed: ${Array.from(ALLOWED_TARGETS).join(', ')}`,
135
- };
136
- }
137
-
138
- // 4. Anti-Self-Poisoning Rule: Reject proposals backed solely by unverified claims
139
- const evidenceType = proposal.evidence_type || 'REVIEWER_CLAIM';
140
- const evidenceWeight = EVIDENCE_HIERARCHY[evidenceType] || 0;
141
-
142
- if (evidenceWeight <= EVIDENCE_HIERARCHY.REVIEWER_CLAIM) {
143
- return {
144
- valid: false,
145
- reason: `Anti-Self-Poisoning violation: Proposal relies solely on "${evidenceType}". Proposals must be backed by reproducible execution patterns, deterministic telemetry, or verified test results.`,
146
- };
147
- }
148
-
149
- // 5. Evidence content check
150
- if (
151
- typeof proposal.observed_evidence !== 'string' ||
152
- proposal.observed_evidence.trim().length < 10
153
- ) {
154
- return {
155
- valid: false,
156
- reason: 'Observed evidence is too short or vacuous. Measurable, concrete evidence is required.',
157
- };
158
- }
159
-
160
- return { valid: true, reason: 'Proposal meets all verification and safety criteria.' };
161
- }
162
-
163
- /**
164
- * Storage helpers for proposal persistence.
165
- */
166
- function getProposalsDir(agentDir = path.join(process.cwd(), '.agent')) {
167
- return path.join(agentDir, 'evolution', 'proposals');
168
- }
169
-
170
- function findDuplicateProposal(proposal, agentDir) {
171
- const existingProposals = loadEvolutionProposals(agentDir);
172
- return (
173
- existingProposals.find(
174
- p =>
175
- p.target === proposal.target &&
176
- (p.proposed_change === proposal.proposed_change ||
177
- JSON.stringify(p.proposed_change) === JSON.stringify(proposal.proposed_change)) &&
178
- p.status !== 'REJECTED',
179
- ) || null
180
- );
181
- }
182
-
183
- function saveEvolutionProposal(proposal, agentDir, options = {}) {
184
- const validation = validateEvolutionProposal(proposal);
185
- if (!validation.valid) {
186
- throw new Error(`Cannot save invalid evolution proposal: ${validation.reason}`);
187
- }
188
-
189
- const dir = getProposalsDir(agentDir);
190
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
191
-
192
- // Phase 5 Case D: Duplicate proposal detection
193
- const duplicate = findDuplicateProposal(proposal, agentDir);
194
- if (duplicate) {
195
- if (options.throwOnDuplicate !== false) {
196
- const err = new Error(
197
- `Duplicate evolution proposal detected: matches existing proposal ${duplicate.proposal_id}`,
198
- );
199
- err.code = 'DUPLICATE_PROPOSAL';
200
- err.duplicate = true;
201
- err.existing_proposal_id = duplicate.proposal_id;
202
- throw err;
203
- }
204
- return {
205
- saved: false,
206
- duplicate: true,
207
- existing_proposal_id: duplicate.proposal_id,
208
- filePath: path.join(dir, `${duplicate.proposal_id}.json`),
209
- message: `Duplicate proposal detected: matches existing proposal ${duplicate.proposal_id}`,
210
- };
211
- }
212
-
213
- const filePath = path.join(dir, `${proposal.proposal_id}.json`);
214
- fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
215
- return filePath;
216
- }
217
-
218
- function loadEvolutionProposals(agentDir) {
219
- const dir = getProposalsDir(agentDir);
220
- if (!fs.existsSync(dir)) return [];
221
-
222
- const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
223
- const proposals = [];
224
-
225
- for (const f of files) {
226
- try {
227
- const p = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
228
- proposals.push(p);
229
- } catch (_) {}
230
- }
231
-
232
- return proposals;
233
- }
234
-
235
- /**
236
- * Human Gate: Explicitly approve an evolution proposal.
237
- */
238
- function approveEvolutionProposal(proposalId, approverName, agentDir) {
239
- if (!approverName || typeof approverName !== 'string') {
240
- throw new Error('Human approval requires an explicit approver name/signature.');
241
- }
242
-
243
- const dir = getProposalsDir(agentDir);
244
- const filePath = path.join(dir, `${proposalId}.json`);
245
-
246
- if (!fs.existsSync(filePath)) {
247
- throw new Error(`Evolution proposal not found: ${proposalId}`);
248
- }
249
-
250
- const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
251
- proposal.status = 'APPROVED';
252
- proposal.approval = {
253
- approved_by: approverName,
254
- approved_at: new Date().toISOString(),
255
- human_gate_cleared: true,
256
- };
257
-
258
- fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
259
- return proposal;
260
- }
261
-
262
- /**
263
- * Reject an evolution proposal.
264
- */
265
- function rejectEvolutionProposal(proposalId, reason, agentDir) {
266
- const dir = getProposalsDir(agentDir);
267
- const filePath = path.join(dir, `${proposalId}.json`);
268
-
269
- if (!fs.existsSync(filePath)) {
270
- throw new Error(`Evolution proposal not found: ${proposalId}`);
271
- }
272
-
273
- const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
274
- proposal.status = 'REJECTED';
275
- proposal.rejection = {
276
- reason: reason || 'Rejected by developer',
277
- rejected_at: new Date().toISOString(),
278
- };
279
-
280
- fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
281
- return proposal;
282
- }
283
-
284
- module.exports = {
285
- createEvolutionProposal,
286
- validateEvolutionProposal,
287
- saveEvolutionProposal,
288
- findDuplicateProposal,
289
- loadEvolutionProposals,
290
- approveEvolutionProposal,
291
- rejectEvolutionProposal,
292
- ALLOWED_TARGETS,
293
- FORBIDDEN_TARGETS,
294
- EVIDENCE_HIERARCHY,
295
- };
1
+ 'use strict';
2
+
3
+ /**
4
+ * evolution/engine.js — Controlled Self-Evolution Engine for Tribunal Kit
5
+ * =======================================================================
6
+ * System-1 Capability 4:
7
+ * Governs the proposal-driven evolution of reviewer routing, context weights,
8
+ * heuristics, skill idioms, and regression fixtures while strictly enforcing:
9
+ *
10
+ * 1. The Human Gate: No evolution can silently deploy without human approval.
11
+ * 2. Trust Boundaries: Protected security boundaries, execution policies, and
12
+ * package integrity validation can NEVER automatically evolve.
13
+ * 3. Anti-Self-Poisoning: Follows strict evidence hierarchy:
14
+ * VERIFIED_TEST_RESULT > DETERMINISTIC_TELEMETRY > REPRODUCIBLE_PATTERN > REVIEWER_CLAIM
15
+ * Proposals based solely on unverified LLM claims are rejected.
16
+ */
17
+
18
+ const fs = require('fs');
19
+ const path = require('path');
20
+ const crypto = require('crypto');
21
+
22
+ const ALLOWED_TARGETS = new Set([
23
+ 'reviewer-routing',
24
+ 'context-weights',
25
+ 'skill-instructions',
26
+ 'reviewer-selection',
27
+ 'deterministic-heuristics',
28
+ 'known-failure-patterns',
29
+ 'workflow-guidance',
30
+ 'regression-fixtures',
31
+ ]);
32
+
33
+ const FORBIDDEN_TARGETS = new Set([
34
+ 'security-boundaries',
35
+ 'shell-execution-policy',
36
+ 'package-integrity',
37
+ 'dependency-pinning',
38
+ 'human-gate',
39
+ 'system1-trust-boundary',
40
+ 'production-security',
41
+ 'arbitrary-executable-code',
42
+ ]);
43
+
44
+ const EVIDENCE_HIERARCHY = {
45
+ VERIFIED_TEST_RESULT: 4,
46
+ DETERMINISTIC_TELEMETRY: 3,
47
+ REPRODUCIBLE_PATTERN: 2,
48
+ REVIEWER_CLAIM: 1,
49
+ };
50
+
51
+ /**
52
+ * Creates and formats a new Evolution Proposal.
53
+ */
54
+ function createEvolutionProposal({
55
+ target,
56
+ currentBehavior,
57
+ observedEvidence,
58
+ proposedChange,
59
+ expectedEffect,
60
+ risk = 'MEDIUM',
61
+ testsRequired = [],
62
+ regressionRequirements = [],
63
+ evidenceType = 'DETERMINISTIC_TELEMETRY',
64
+ proposalId = null,
65
+ }) {
66
+ const id = proposalId || `EVO-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
67
+
68
+ const proposal = {
69
+ proposal_id: id,
70
+ target,
71
+ current_behavior: currentBehavior,
72
+ observed_evidence: observedEvidence,
73
+ proposed_change: proposedChange,
74
+ expected_effect: expectedEffect,
75
+ risk: ['LOW', 'MEDIUM', 'HIGH'].includes(String(risk).toUpperCase())
76
+ ? String(risk).toUpperCase()
77
+ : 'MEDIUM',
78
+ tests_required: Array.isArray(testsRequired) ? testsRequired : [testsRequired],
79
+ regression_requirements: Array.isArray(regressionRequirements)
80
+ ? regressionRequirements
81
+ : [regressionRequirements],
82
+ evidence_type: EVIDENCE_HIERARCHY[evidenceType] ? evidenceType : 'REVIEWER_CLAIM',
83
+ evidence_weight: EVIDENCE_HIERARCHY[evidenceType] || 1,
84
+ status: 'PROPOSED',
85
+ created_at: new Date().toISOString(),
86
+ approval: null,
87
+ };
88
+
89
+ return proposal;
90
+ }
91
+
92
+ /**
93
+ * Validates an evolution proposal against Anti-Self-Poisoning & Security Rules.
94
+ */
95
+ function validateEvolutionProposal(proposal) {
96
+ if (!proposal || typeof proposal !== 'object') {
97
+ return { valid: false, reason: 'Proposal must be an object.' };
98
+ }
99
+
100
+ // 1. Check required fields
101
+ const requiredFields = [
102
+ 'proposal_id',
103
+ 'target',
104
+ 'current_behavior',
105
+ 'observed_evidence',
106
+ 'proposed_change',
107
+ 'expected_effect',
108
+ 'risk',
109
+ 'tests_required',
110
+ 'regression_requirements',
111
+ ];
112
+
113
+ for (const field of requiredFields) {
114
+ if (!proposal[field] || (Array.isArray(proposal[field]) && proposal[field].length === 0)) {
115
+ return {
116
+ valid: false,
117
+ reason: `Missing required proposal field: "${field}". Every proposal must be backed by evidence.`,
118
+ };
119
+ }
120
+ }
121
+
122
+ // 2. Security boundary enforcement: Forbidden targets cannot evolve automatically
123
+ if (FORBIDDEN_TARGETS.has(proposal.target)) {
124
+ return {
125
+ valid: false,
126
+ reason: `Target "${proposal.target}" is a protected security boundary. Automatic evolution of security boundaries, shell policies, and package integrity is strictly forbidden.`,
127
+ };
128
+ }
129
+
130
+ // 3. Allowed target check
131
+ if (!ALLOWED_TARGETS.has(proposal.target)) {
132
+ return {
133
+ valid: false,
134
+ reason: `Target "${proposal.target}" is not an allowed evolution target. Allowed: ${Array.from(ALLOWED_TARGETS).join(', ')}`,
135
+ };
136
+ }
137
+
138
+ // 4. Anti-Self-Poisoning Rule: Reject proposals backed solely by unverified claims
139
+ const evidenceType = proposal.evidence_type || 'REVIEWER_CLAIM';
140
+ const evidenceWeight = EVIDENCE_HIERARCHY[evidenceType] || 0;
141
+
142
+ if (evidenceWeight <= EVIDENCE_HIERARCHY.REVIEWER_CLAIM) {
143
+ return {
144
+ valid: false,
145
+ reason: `Anti-Self-Poisoning violation: Proposal relies solely on "${evidenceType}". Proposals must be backed by reproducible execution patterns, deterministic telemetry, or verified test results.`,
146
+ };
147
+ }
148
+
149
+ // 5. Evidence content check
150
+ if (
151
+ typeof proposal.observed_evidence !== 'string' ||
152
+ proposal.observed_evidence.trim().length < 10
153
+ ) {
154
+ return {
155
+ valid: false,
156
+ reason:
157
+ 'Observed evidence is too short or vacuous. Measurable, concrete evidence is required.',
158
+ };
159
+ }
160
+
161
+ return { valid: true, reason: 'Proposal meets all verification and safety criteria.' };
162
+ }
163
+
164
+ /**
165
+ * Storage helpers for proposal persistence.
166
+ */
167
+ function getProposalsDir(agentDir = path.join(process.cwd(), '.agent')) {
168
+ return path.join(agentDir, 'evolution', 'proposals');
169
+ }
170
+
171
+ function findDuplicateProposal(proposal, agentDir) {
172
+ const existingProposals = loadEvolutionProposals(agentDir);
173
+ return (
174
+ existingProposals.find(
175
+ p =>
176
+ p.target === proposal.target &&
177
+ (p.proposed_change === proposal.proposed_change ||
178
+ JSON.stringify(p.proposed_change) === JSON.stringify(proposal.proposed_change)) &&
179
+ p.status !== 'REJECTED',
180
+ ) || null
181
+ );
182
+ }
183
+
184
+ function saveEvolutionProposal(proposal, agentDir, options = {}) {
185
+ const validation = validateEvolutionProposal(proposal);
186
+ if (!validation.valid) {
187
+ throw new Error(`Cannot save invalid evolution proposal: ${validation.reason}`);
188
+ }
189
+
190
+ const dir = getProposalsDir(agentDir);
191
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
192
+
193
+ // Phase 5 Case D: Duplicate proposal detection
194
+ const duplicate = findDuplicateProposal(proposal, agentDir);
195
+ if (duplicate) {
196
+ if (options.throwOnDuplicate !== false) {
197
+ const err = new Error(
198
+ `Duplicate evolution proposal detected: matches existing proposal ${duplicate.proposal_id}`,
199
+ );
200
+ err.code = 'DUPLICATE_PROPOSAL';
201
+ err.duplicate = true;
202
+ err.existing_proposal_id = duplicate.proposal_id;
203
+ throw err;
204
+ }
205
+ return {
206
+ saved: false,
207
+ duplicate: true,
208
+ existing_proposal_id: duplicate.proposal_id,
209
+ filePath: path.join(dir, `${duplicate.proposal_id}.json`),
210
+ message: `Duplicate proposal detected: matches existing proposal ${duplicate.proposal_id}`,
211
+ };
212
+ }
213
+
214
+ const filePath = path.join(dir, `${proposal.proposal_id}.json`);
215
+ fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
216
+ return filePath;
217
+ }
218
+
219
+ function loadEvolutionProposals(agentDir) {
220
+ const dir = getProposalsDir(agentDir);
221
+ if (!fs.existsSync(dir)) return [];
222
+
223
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.json'));
224
+ const proposals = [];
225
+
226
+ for (const f of files) {
227
+ try {
228
+ const p = JSON.parse(fs.readFileSync(path.join(dir, f), 'utf8'));
229
+ proposals.push(p);
230
+ } catch (_) {}
231
+ }
232
+
233
+ return proposals;
234
+ }
235
+
236
+ /**
237
+ * Human Gate: Explicitly approve an evolution proposal.
238
+ */
239
+ function approveEvolutionProposal(proposalId, approverName, agentDir) {
240
+ if (!approverName || typeof approverName !== 'string') {
241
+ throw new Error('Human approval requires an explicit approver name/signature.');
242
+ }
243
+
244
+ const dir = getProposalsDir(agentDir);
245
+ const filePath = path.join(dir, `${proposalId}.json`);
246
+
247
+ if (!fs.existsSync(filePath)) {
248
+ throw new Error(`Evolution proposal not found: ${proposalId}`);
249
+ }
250
+
251
+ const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
252
+ proposal.status = 'APPROVED';
253
+ proposal.approval = {
254
+ approved_by: approverName,
255
+ approved_at: new Date().toISOString(),
256
+ human_gate_cleared: true,
257
+ };
258
+
259
+ fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
260
+ return proposal;
261
+ }
262
+
263
+ /**
264
+ * Reject an evolution proposal.
265
+ */
266
+ function rejectEvolutionProposal(proposalId, reason, agentDir) {
267
+ const dir = getProposalsDir(agentDir);
268
+ const filePath = path.join(dir, `${proposalId}.json`);
269
+
270
+ if (!fs.existsSync(filePath)) {
271
+ throw new Error(`Evolution proposal not found: ${proposalId}`);
272
+ }
273
+
274
+ const proposal = JSON.parse(fs.readFileSync(filePath, 'utf8'));
275
+ proposal.status = 'REJECTED';
276
+ proposal.rejection = {
277
+ reason: reason || 'Rejected by developer',
278
+ rejected_at: new Date().toISOString(),
279
+ };
280
+
281
+ fs.writeFileSync(filePath, JSON.stringify(proposal, null, 2), 'utf8');
282
+ return proposal;
283
+ }
284
+
285
+ module.exports = {
286
+ createEvolutionProposal,
287
+ validateEvolutionProposal,
288
+ saveEvolutionProposal,
289
+ findDuplicateProposal,
290
+ loadEvolutionProposals,
291
+ approveEvolutionProposal,
292
+ rejectEvolutionProposal,
293
+ ALLOWED_TARGETS,
294
+ FORBIDDEN_TARGETS,
295
+ EVIDENCE_HIERARCHY,
296
+ };