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,270 +1,303 @@
1
- #!/usr/bin/env node
2
- /**
3
- * architecture_verifier.js — Tribunal Zero-Trust Architecture Verifier
4
- * ═════════════════════════════════════════════════════════════════════════════
5
- * Evaluates architectural models against ground-truth source code bytes.
6
- *
7
- * Capabilities:
8
- * - Verifies all source citations (file exists, line valid, no path escapes)
9
- * - Detects Stale Evidence via SHA-256 drift verification
10
- * - Verifies AST Symbol and Relationship Call Sites
11
- * - Assigns Epistemic Confidence Levels (L1 to L5)
12
- * - Surfaces Architectural Contradictions as first-class disputable records
13
- * - Prevents unverified agent claims from masquerading as verified facts
14
- *
15
- * Emits: .agent/history/architecture-verification.json
16
- * Zero external dependencies.
17
- */
18
-
19
- 'use strict';
20
-
21
- const fs = require('fs');
22
- const path = require('path');
23
- const crypto = require('crypto');
24
- const { GREEN, YELLOW, CYAN, RED, DIM, RESET, BOX, banner, timer, formatMs } = require('./_colors');
25
-
26
- function computeSha256(str) {
27
- return crypto.createHash('sha256').update(str || '').digest('hex');
28
- }
29
-
30
- class ArchitectureVerifier {
31
- constructor(repoRoot = process.cwd(), options = {}) {
32
- this.repoRoot = path.resolve(repoRoot);
33
- this.options = { strict: false, ...options };
34
- this.results = {
35
- verified: true,
36
- timestamp: new Date().toISOString(),
37
- stats: {
38
- totalEntities: 0,
39
- verifiedEntities: 0,
40
- staleEntities: 0,
41
- fabricatedEntities: 0,
42
- totalRelations: 0,
43
- verifiedRelations: 0,
44
- contradictedRelations: 0,
45
- },
46
- confidenceDistribution: { L1: 0, L2: 0, L3: 0, L4: 0, L5: 0 },
47
- contradictions: [],
48
- diagnostics: [],
49
- };
50
- }
51
-
52
- verifyModel(model) {
53
- if (!model || !Array.isArray(model.entities)) {
54
- throw new Error('Invalid model: expected an object with entities array.');
55
- }
56
-
57
- const { entities, relationships = [], contradictions = [] } = model;
58
- this.results.stats.totalEntities = entities.length;
59
- this.results.stats.totalRelations = relationships.length;
60
-
61
- // Entity verification map for fast lookup
62
- const entityIndex = new Map();
63
-
64
- // 1. Verify Entity Sources & Ground Truth
65
- for (const entity of entities) {
66
- entityIndex.set(entity.id, entity);
67
- const entityVerification = this.verifyEntity(entity);
68
- if (entityVerification.status === 'verified') {
69
- this.results.stats.verifiedEntities++;
70
- this.results.confidenceDistribution[entity.confidence || 'L1']++;
71
- } else if (entityVerification.status === 'stale') {
72
- this.results.stats.staleEntities++;
73
- this.results.confidenceDistribution['L3']++;
74
- this.results.diagnostics.push({
75
- severity: 'warning',
76
- code: 'ARCH_EVIDENCE_STALE',
77
- entityId: entity.id,
78
- message: `Evidence has drifted for entity "${entity.name}" (${entity.id}). Content hash does not match.`,
79
- details: entityVerification.details,
80
- });
81
- } else {
82
- this.results.stats.fabricatedEntities++;
83
- this.results.confidenceDistribution['L5']++;
84
- this.results.verified = false;
85
- this.results.diagnostics.push({
86
- severity: 'error',
87
- code: 'ARCH_EVIDENCE_FABRICATED',
88
- entityId: entity.id,
89
- message: `Evidence does not exist in repository for entity "${entity.name}" (${entity.id}).`,
90
- details: entityVerification.details,
91
- });
92
- }
93
- }
94
-
95
- // 2. Verify Relationships & Call Sites
96
- for (const rel of relationships) {
97
- const relVerification = this.verifyRelationship(rel, entityIndex);
98
- if (relVerification.status === 'verified') {
99
- this.results.stats.verifiedRelations++;
100
- } else if (relVerification.status === 'contradicted') {
101
- this.results.stats.contradictedRelations++;
102
- this.results.verified = false;
103
- this.results.contradictions.push({
104
- id: `conflict.${rel.id}`,
105
- relationshipId: rel.id,
106
- sourceId: rel.sourceId,
107
- targetId: rel.targetId,
108
- conflictType: relVerification.conflictType || 'unreachable_call_path',
109
- claimedRelation: rel.relationType,
110
- reason: relVerification.reason,
111
- confidence: 'L5',
112
- });
113
- this.results.diagnostics.push({
114
- severity: 'error',
115
- code: 'ARCH_RELATION_CONTRADICTED',
116
- relationId: rel.id,
117
- message: `Contradiction detected in relationship ${rel.sourceId} -> ${rel.targetId}: ${relVerification.reason}`,
118
- });
119
- } else {
120
- this.results.diagnostics.push({
121
- severity: 'warning',
122
- code: 'ARCH_RELATION_UNVERIFIED',
123
- relationId: rel.id,
124
- message: `Relationship ${rel.sourceId} -> ${rel.targetId} lacks direct call-site evidence.`,
125
- });
126
- }
127
- }
128
-
129
- // 3. Reconcile explicit contradictions passed in model
130
- for (const c of contradictions) {
131
- this.results.contradictions.push(c);
132
- }
133
-
134
- return this.results;
135
- }
136
-
137
- verifyEntity(entity) {
138
- if (!entity.sources || !entity.sources.length) {
139
- return { status: 'unverified', details: { reason: 'No sources declared' } };
140
- }
141
-
142
- let anyStale = false;
143
- let anyMissing = false;
144
-
145
- for (const src of entity.sources) {
146
- const check = this.verifySourceFile(src.file, src.startLine, src.snippet, src.contentHash);
147
- if (!check.exists) anyMissing = true;
148
- if (check.stale) anyStale = true;
149
- }
150
-
151
- if (anyMissing) return { status: 'fabricated', details: { reason: 'Cited file or line does not exist' } };
152
- if (anyStale) return { status: 'stale', details: { reason: 'File content hash has drifted' } };
153
- return { status: 'verified', details: {} };
154
- }
155
-
156
- verifyRelationship(rel, entityIndex) {
157
- const source = entityIndex.get(rel.sourceId);
158
- const target = entityIndex.get(rel.targetId);
159
-
160
- if (!source) {
161
- return { status: 'contradicted', conflictType: 'missing_source_entity', reason: `Source entity ${rel.sourceId} not found.` };
162
- }
163
- if (!target) {
164
- return { status: 'contradicted', conflictType: 'missing_target_entity', reason: `Target entity ${rel.targetId} not found.` };
165
- }
166
-
167
- // Check evidence call site if present
168
- if (rel.evidence && rel.evidence.length > 0) {
169
- for (const ev of rel.evidence) {
170
- const check = this.verifySourceFile(ev.file, ev.startLine, ev.snippet, ev.contentHash);
171
- if (!check.exists) {
172
- return {
173
- status: 'contradicted',
174
- conflictType: 'fabricated_call_site',
175
- reason: `Evidence file ${ev.file} does not exist for relation.`,
176
- };
177
- }
178
- }
179
- return { status: 'verified' };
180
- }
181
-
182
- // If relation is an import or route, check if source imports target
183
- return { status: 'unverified', reason: 'No call-site evidence' };
184
- }
185
-
186
- verifySourceFile(relPath, lineNum, expectedSnippet, expectedHash) {
187
- const fullPath = path.resolve(this.repoRoot, relPath);
188
-
189
- // Path escape boundary defense
190
- const normalizedRepo = path.resolve(this.repoRoot);
191
- if (!fullPath.startsWith(normalizedRepo)) {
192
- return { exists: false, reason: 'Path traversal escape detected' };
193
- }
194
-
195
- if (!fs.existsSync(fullPath)) {
196
- return { exists: false, reason: 'File does not exist' };
197
- }
198
-
199
- let content;
200
- try {
201
- content = fs.readFileSync(fullPath, 'utf8');
202
- } catch (_e) {
203
- return { exists: false, reason: 'File could not be read' };
204
- }
205
-
206
- const lines = content.split('\n');
207
- if (lineNum !== undefined && lineNum !== null) {
208
- if (lineNum < 1 || lineNum > lines.length) {
209
- return { exists: false, reason: `Line ${lineNum} out of range (file has ${lines.length} lines)` };
210
- }
211
-
212
- if (expectedHash) {
213
- const actualLine = lines[lineNum - 1] || '';
214
- const actualHash = computeSha256(actualLine).substring(0, 16);
215
- const actualFileHash = computeSha256(content).substring(0, 16);
216
- if (actualHash !== expectedHash && actualFileHash !== expectedHash) {
217
- return { exists: true, stale: true, reason: 'Content hash mismatch' };
218
- }
219
- }
220
- }
221
-
222
- return { exists: true, stale: false };
223
- }
224
-
225
- saveReport(outputPath) {
226
- const defaultPath = path.join(this.repoRoot, '.agent', 'history', 'architecture-verification.json');
227
- const dest = outputPath || defaultPath;
228
- fs.mkdirSync(path.dirname(dest), { recursive: true });
229
- fs.writeFileSync(dest, JSON.stringify(this.results, null, 2), 'utf8');
230
- return dest;
231
- }
232
- }
233
-
234
- // ── Standalone CLI ────────────────────────────────────────────────────────────
235
- if (require.main === module) {
236
- const t = timer();
237
- const repoRoot = process.argv[2] || process.cwd();
238
- const modelFile = path.join(repoRoot, '.agent', 'history', 'architecture-model.json');
239
-
240
- console.log(banner('Tribunal Architecture Zero-Trust Verifier'));
241
-
242
- if (!fs.existsSync(modelFile)) {
243
- console.error(` ${RED}✖ architecture-model.json not found.${RESET} Run architecture_extractor.js first.`);
244
- process.exit(1);
245
- }
246
-
247
- const model = JSON.parse(fs.readFileSync(modelFile, 'utf8'));
248
- const verifier = new ArchitectureVerifier(repoRoot);
249
- const results = verifier.verifyModel(model);
250
- const reportPath = verifier.saveReport();
251
-
252
- const isOk = results.verified && results.diagnostics.filter((d) => d.severity === 'error').length === 0;
253
- const statusColor = isOk ? GREEN : RED;
254
- const statusIcon = isOk ? BOX.check : BOX.cross_mark;
255
-
256
- console.log(` ${statusColor}${statusIcon} Verification ${isOk ? 'PASSED' : 'FLAGGED ISSUES'}${RESET} in ${formatMs(t())}`);
257
- console.log(` ${DIM}Entities Verified:${RESET} ${GREEN}${results.stats.verifiedEntities}${RESET} / ${results.stats.totalEntities}`);
258
- console.log(` ${DIM}Stale Evidence:${RESET} ${results.stats.staleEntities > 0 ? YELLOW : GREEN}${results.stats.staleEntities}${RESET}`);
259
- console.log(` ${DIM}Contradictions Surfaced:${RESET} ${results.contradictions.length > 0 ? RED : GREEN}${results.contradictions.length}${RESET}`);
260
- console.log(` ${DIM}Confidence (L1 Facts):${RESET} ${CYAN}${results.confidenceDistribution.L1}${RESET}`);
261
- console.log(` ${DIM}Saved Report:${RESET} ${CYAN}${reportPath}${RESET}\n`);
262
-
263
- if (!isOk && process.argv.includes('--strict')) {
264
- process.exit(1);
265
- }
266
- }
267
-
268
- module.exports = {
269
- ArchitectureVerifier,
270
- };
1
+ #!/usr/bin/env node
2
+ /**
3
+ * architecture_verifier.js — Tribunal Zero-Trust Architecture Verifier
4
+ * ═════════════════════════════════════════════════════════════════════════════
5
+ * Evaluates architectural models against ground-truth source code bytes.
6
+ *
7
+ * Capabilities:
8
+ * - Verifies all source citations (file exists, line valid, no path escapes)
9
+ * - Detects Stale Evidence via SHA-256 drift verification
10
+ * - Verifies AST Symbol and Relationship Call Sites
11
+ * - Assigns Epistemic Confidence Levels (L1 to L5)
12
+ * - Surfaces Architectural Contradictions as first-class disputable records
13
+ * - Prevents unverified agent claims from masquerading as verified facts
14
+ *
15
+ * Emits: .agent/history/architecture-verification.json
16
+ * Zero external dependencies.
17
+ */
18
+
19
+ 'use strict';
20
+
21
+ const fs = require('fs');
22
+ const path = require('path');
23
+ const crypto = require('crypto');
24
+ const { GREEN, YELLOW, CYAN, RED, DIM, RESET, BOX, banner, timer, formatMs } = require('./_colors');
25
+
26
+ function computeSha256(str) {
27
+ return crypto
28
+ .createHash('sha256')
29
+ .update(str || '')
30
+ .digest('hex');
31
+ }
32
+
33
+ class ArchitectureVerifier {
34
+ constructor(repoRoot = process.cwd(), options = {}) {
35
+ this.repoRoot = path.resolve(repoRoot);
36
+ this.options = { strict: false, ...options };
37
+ this.results = {
38
+ verified: true,
39
+ timestamp: new Date().toISOString(),
40
+ stats: {
41
+ totalEntities: 0,
42
+ verifiedEntities: 0,
43
+ staleEntities: 0,
44
+ fabricatedEntities: 0,
45
+ totalRelations: 0,
46
+ verifiedRelations: 0,
47
+ contradictedRelations: 0,
48
+ },
49
+ confidenceDistribution: { L1: 0, L2: 0, L3: 0, L4: 0, L5: 0 },
50
+ contradictions: [],
51
+ diagnostics: [],
52
+ };
53
+ }
54
+
55
+ verifyModel(model) {
56
+ if (!model || !Array.isArray(model.entities)) {
57
+ throw new Error('Invalid model: expected an object with entities array.');
58
+ }
59
+
60
+ const { entities, relationships = [], contradictions = [] } = model;
61
+ this.results.stats.totalEntities = entities.length;
62
+ this.results.stats.totalRelations = relationships.length;
63
+
64
+ // Entity verification map for fast lookup
65
+ const entityIndex = new Map();
66
+
67
+ // 1. Verify Entity Sources & Ground Truth
68
+ for (const entity of entities) {
69
+ entityIndex.set(entity.id, entity);
70
+ const entityVerification = this.verifyEntity(entity);
71
+ if (entityVerification.status === 'verified') {
72
+ this.results.stats.verifiedEntities++;
73
+ this.results.confidenceDistribution[entity.confidence || 'L1']++;
74
+ } else if (entityVerification.status === 'stale') {
75
+ this.results.stats.staleEntities++;
76
+ this.results.confidenceDistribution['L3']++;
77
+ this.results.diagnostics.push({
78
+ severity: 'warning',
79
+ code: 'ARCH_EVIDENCE_STALE',
80
+ entityId: entity.id,
81
+ message: `Evidence has drifted for entity "${entity.name}" (${entity.id}). Content hash does not match.`,
82
+ details: entityVerification.details,
83
+ });
84
+ } else {
85
+ this.results.stats.fabricatedEntities++;
86
+ this.results.confidenceDistribution['L5']++;
87
+ this.results.verified = false;
88
+ this.results.diagnostics.push({
89
+ severity: 'error',
90
+ code: 'ARCH_EVIDENCE_FABRICATED',
91
+ entityId: entity.id,
92
+ message: `Evidence does not exist in repository for entity "${entity.name}" (${entity.id}).`,
93
+ details: entityVerification.details,
94
+ });
95
+ }
96
+ }
97
+
98
+ // 2. Verify Relationships & Call Sites
99
+ for (const rel of relationships) {
100
+ const relVerification = this.verifyRelationship(rel, entityIndex);
101
+ if (relVerification.status === 'verified') {
102
+ this.results.stats.verifiedRelations++;
103
+ } else if (relVerification.status === 'contradicted') {
104
+ this.results.stats.contradictedRelations++;
105
+ this.results.verified = false;
106
+ this.results.contradictions.push({
107
+ id: `conflict.${rel.id}`,
108
+ relationshipId: rel.id,
109
+ sourceId: rel.sourceId,
110
+ targetId: rel.targetId,
111
+ conflictType: relVerification.conflictType || 'unreachable_call_path',
112
+ claimedRelation: rel.relationType,
113
+ reason: relVerification.reason,
114
+ confidence: 'L5',
115
+ });
116
+ this.results.diagnostics.push({
117
+ severity: 'error',
118
+ code: 'ARCH_RELATION_CONTRADICTED',
119
+ relationId: rel.id,
120
+ message: `Contradiction detected in relationship ${rel.sourceId} -> ${rel.targetId}: ${relVerification.reason}`,
121
+ });
122
+ } else {
123
+ this.results.diagnostics.push({
124
+ severity: 'warning',
125
+ code: 'ARCH_RELATION_UNVERIFIED',
126
+ relationId: rel.id,
127
+ message: `Relationship ${rel.sourceId} -> ${rel.targetId} lacks direct call-site evidence.`,
128
+ });
129
+ }
130
+ }
131
+
132
+ // 3. Reconcile explicit contradictions passed in model
133
+ for (const c of contradictions) {
134
+ this.results.contradictions.push(c);
135
+ }
136
+
137
+ return this.results;
138
+ }
139
+
140
+ verifyEntity(entity) {
141
+ if (!entity.sources || !entity.sources.length) {
142
+ return { status: 'unverified', details: { reason: 'No sources declared' } };
143
+ }
144
+
145
+ let anyStale = false;
146
+ let anyMissing = false;
147
+
148
+ for (const src of entity.sources) {
149
+ const check = this.verifySourceFile(src.file, src.startLine, src.snippet, src.contentHash);
150
+ if (!check.exists) anyMissing = true;
151
+ if (check.stale) anyStale = true;
152
+ }
153
+
154
+ if (anyMissing)
155
+ return { status: 'fabricated', details: { reason: 'Cited file or line does not exist' } };
156
+ if (anyStale) return { status: 'stale', details: { reason: 'File content hash has drifted' } };
157
+ return { status: 'verified', details: {} };
158
+ }
159
+
160
+ verifyRelationship(rel, entityIndex) {
161
+ const source = entityIndex.get(rel.sourceId);
162
+ const target = entityIndex.get(rel.targetId);
163
+
164
+ if (!source) {
165
+ return {
166
+ status: 'contradicted',
167
+ conflictType: 'missing_source_entity',
168
+ reason: `Source entity ${rel.sourceId} not found.`,
169
+ };
170
+ }
171
+ if (!target) {
172
+ return {
173
+ status: 'contradicted',
174
+ conflictType: 'missing_target_entity',
175
+ reason: `Target entity ${rel.targetId} not found.`,
176
+ };
177
+ }
178
+
179
+ // Check evidence call site if present
180
+ if (rel.evidence && rel.evidence.length > 0) {
181
+ for (const ev of rel.evidence) {
182
+ const check = this.verifySourceFile(ev.file, ev.startLine, ev.snippet, ev.contentHash);
183
+ if (!check.exists) {
184
+ return {
185
+ status: 'contradicted',
186
+ conflictType: 'fabricated_call_site',
187
+ reason: `Evidence file ${ev.file} does not exist for relation.`,
188
+ };
189
+ }
190
+ }
191
+ return { status: 'verified' };
192
+ }
193
+
194
+ // If relation is an import or route, check if source imports target
195
+ return { status: 'unverified', reason: 'No call-site evidence' };
196
+ }
197
+
198
+ verifySourceFile(relPath, lineNum, expectedSnippet, expectedHash) {
199
+ const fullPath = path.resolve(this.repoRoot, relPath);
200
+
201
+ // Path escape boundary defense
202
+ const normalizedRepo = path.resolve(this.repoRoot);
203
+ if (!fullPath.startsWith(normalizedRepo)) {
204
+ return { exists: false, reason: 'Path traversal escape detected' };
205
+ }
206
+
207
+ if (!fs.existsSync(fullPath)) {
208
+ return { exists: false, reason: 'File does not exist' };
209
+ }
210
+
211
+ let content;
212
+ try {
213
+ content = fs.readFileSync(fullPath, 'utf8');
214
+ } catch (_e) {
215
+ return { exists: false, reason: 'File could not be read' };
216
+ }
217
+
218
+ const lines = content.split('\n');
219
+ if (lineNum !== undefined && lineNum !== null) {
220
+ if (lineNum < 1 || lineNum > lines.length) {
221
+ return {
222
+ exists: false,
223
+ reason: `Line ${lineNum} out of range (file has ${lines.length} lines)`,
224
+ };
225
+ }
226
+
227
+ if (expectedHash) {
228
+ const actualLine = lines[lineNum - 1] || '';
229
+ const actualHash = computeSha256(actualLine).substring(0, 16);
230
+ const actualFileHash = computeSha256(content).substring(0, 16);
231
+ if (actualHash !== expectedHash && actualFileHash !== expectedHash) {
232
+ return { exists: true, stale: true, reason: 'Content hash mismatch' };
233
+ }
234
+ }
235
+ }
236
+
237
+ return { exists: true, stale: false };
238
+ }
239
+
240
+ saveReport(outputPath) {
241
+ const defaultPath = path.join(
242
+ this.repoRoot,
243
+ '.agent',
244
+ 'history',
245
+ 'architecture-verification.json',
246
+ );
247
+ const dest = outputPath || defaultPath;
248
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
249
+ fs.writeFileSync(dest, JSON.stringify(this.results, null, 2), 'utf8');
250
+ return dest;
251
+ }
252
+ }
253
+
254
+ // ── Standalone CLI ────────────────────────────────────────────────────────────
255
+ if (require.main === module) {
256
+ const t = timer();
257
+ const repoRoot = process.argv[2] || process.cwd();
258
+ const modelFile = path.join(repoRoot, '.agent', 'history', 'architecture-model.json');
259
+
260
+ console.log(banner('Tribunal Architecture Zero-Trust Verifier'));
261
+
262
+ if (!fs.existsSync(modelFile)) {
263
+ console.error(
264
+ ` ${RED}✖ architecture-model.json not found.${RESET} Run architecture_extractor.js first.`,
265
+ );
266
+ process.exit(1);
267
+ }
268
+
269
+ const model = JSON.parse(fs.readFileSync(modelFile, 'utf8'));
270
+ const verifier = new ArchitectureVerifier(repoRoot);
271
+ const results = verifier.verifyModel(model);
272
+ const reportPath = verifier.saveReport();
273
+
274
+ const isOk =
275
+ results.verified && results.diagnostics.filter(d => d.severity === 'error').length === 0;
276
+ const statusColor = isOk ? GREEN : RED;
277
+ const statusIcon = isOk ? BOX.check : BOX.cross_mark;
278
+
279
+ console.log(
280
+ ` ${statusColor}${statusIcon} Verification ${isOk ? 'PASSED' : 'FLAGGED ISSUES'}${RESET} in ${formatMs(t())}`,
281
+ );
282
+ console.log(
283
+ ` ${DIM}Entities Verified:${RESET} ${GREEN}${results.stats.verifiedEntities}${RESET} / ${results.stats.totalEntities}`,
284
+ );
285
+ console.log(
286
+ ` ${DIM}Stale Evidence:${RESET} ${results.stats.staleEntities > 0 ? YELLOW : GREEN}${results.stats.staleEntities}${RESET}`,
287
+ );
288
+ console.log(
289
+ ` ${DIM}Contradictions Surfaced:${RESET} ${results.contradictions.length > 0 ? RED : GREEN}${results.contradictions.length}${RESET}`,
290
+ );
291
+ console.log(
292
+ ` ${DIM}Confidence (L1 Facts):${RESET} ${CYAN}${results.confidenceDistribution.L1}${RESET}`,
293
+ );
294
+ console.log(` ${DIM}Saved Report:${RESET} ${CYAN}${reportPath}${RESET}\n`);
295
+
296
+ if (!isOk && process.argv.includes('--strict')) {
297
+ process.exit(1);
298
+ }
299
+ }
300
+
301
+ module.exports = {
302
+ ArchitectureVerifier,
303
+ };