@njeybe/nativ 2.0.0

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 (140) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +21 -0
  3. package/README.md +334 -0
  4. package/bin/cli.js +31 -0
  5. package/dist/commands/bench.d.ts +8 -0
  6. package/dist/commands/bench.js +52 -0
  7. package/dist/commands/db.d.ts +22 -0
  8. package/dist/commands/db.js +415 -0
  9. package/dist/commands/doctor.d.ts +38 -0
  10. package/dist/commands/doctor.js +169 -0
  11. package/dist/commands/hook.d.ts +31 -0
  12. package/dist/commands/hook.js +105 -0
  13. package/dist/commands/init.d.ts +3 -0
  14. package/dist/commands/init.js +144 -0
  15. package/dist/commands/mcp.d.ts +5 -0
  16. package/dist/commands/mcp.js +16 -0
  17. package/dist/commands/setup.d.ts +17 -0
  18. package/dist/commands/setup.js +51 -0
  19. package/dist/commands/status.d.ts +5 -0
  20. package/dist/commands/status.js +75 -0
  21. package/dist/commands/task.d.ts +83 -0
  22. package/dist/commands/task.js +807 -0
  23. package/dist/commands/test-gen.d.ts +14 -0
  24. package/dist/commands/test-gen.js +52 -0
  25. package/dist/commands/triage.d.ts +70 -0
  26. package/dist/commands/triage.js +343 -0
  27. package/dist/commands/update.d.ts +14 -0
  28. package/dist/commands/update.js +112 -0
  29. package/dist/commands/validate.d.ts +1 -0
  30. package/dist/commands/validate.js +89 -0
  31. package/dist/commands/verify.d.ts +8 -0
  32. package/dist/commands/verify.js +65 -0
  33. package/dist/commands/worktree.d.ts +47 -0
  34. package/dist/commands/worktree.js +396 -0
  35. package/dist/core/benchmark.d.ts +57 -0
  36. package/dist/core/benchmark.js +772 -0
  37. package/dist/core/enforcement.d.ts +80 -0
  38. package/dist/core/enforcement.js +271 -0
  39. package/dist/core/legacy-templates.d.ts +24 -0
  40. package/dist/core/legacy-templates.js +60 -0
  41. package/dist/core/lock-manager.d.ts +54 -0
  42. package/dist/core/lock-manager.js +260 -0
  43. package/dist/core/root-resolver.d.ts +32 -0
  44. package/dist/core/root-resolver.js +149 -0
  45. package/dist/core/setup-assets.d.ts +123 -0
  46. package/dist/core/setup-assets.js +438 -0
  47. package/dist/core/telemetry.d.ts +156 -0
  48. package/dist/core/telemetry.js +416 -0
  49. package/dist/core/test-generator-types.d.ts +227 -0
  50. package/dist/core/test-generator-types.js +179 -0
  51. package/dist/core/test-generator.d.ts +34 -0
  52. package/dist/core/test-generator.js +1690 -0
  53. package/dist/core/tier1-liaison.d.ts +139 -0
  54. package/dist/core/tier1-liaison.js +320 -0
  55. package/dist/core/verifier.d.ts +52 -0
  56. package/dist/core/verifier.js +249 -0
  57. package/dist/core/version.d.ts +5 -0
  58. package/dist/core/version.js +14 -0
  59. package/dist/core/worktree-merge.d.ts +30 -0
  60. package/dist/core/worktree-merge.js +98 -0
  61. package/dist/db/data-engine.d.ts +46 -0
  62. package/dist/db/data-engine.js +802 -0
  63. package/dist/db/diff.d.ts +39 -0
  64. package/dist/db/diff.js +420 -0
  65. package/dist/db/env-parser.d.ts +86 -0
  66. package/dist/db/env-parser.js +549 -0
  67. package/dist/db/introspector.d.ts +21 -0
  68. package/dist/db/introspector.js +835 -0
  69. package/dist/db/types.d.ts +217 -0
  70. package/dist/db/types.js +9 -0
  71. package/dist/governor/circuit-breaker.d.ts +100 -0
  72. package/dist/governor/circuit-breaker.js +231 -0
  73. package/dist/governor/evaluator.d.ts +67 -0
  74. package/dist/governor/evaluator.js +851 -0
  75. package/dist/governor/index.d.ts +6 -0
  76. package/dist/governor/index.js +6 -0
  77. package/dist/governor/rules/api-rules.d.ts +2 -0
  78. package/dist/governor/rules/api-rules.js +140 -0
  79. package/dist/governor/rules/db-rules.d.ts +2 -0
  80. package/dist/governor/rules/db-rules.js +180 -0
  81. package/dist/governor/store.d.ts +33 -0
  82. package/dist/governor/store.js +84 -0
  83. package/dist/governor/types.d.ts +64 -0
  84. package/dist/governor/types.js +1 -0
  85. package/dist/index.d.ts +3 -0
  86. package/dist/index.js +358 -0
  87. package/dist/mcp/server.d.ts +3 -0
  88. package/dist/mcp/server.js +309 -0
  89. package/dist/providers/claude-api.d.ts +5 -0
  90. package/dist/providers/claude-api.js +75 -0
  91. package/dist/providers/claude-cli.d.ts +13 -0
  92. package/dist/providers/claude-cli.js +134 -0
  93. package/dist/providers/gemini.d.ts +8 -0
  94. package/dist/providers/gemini.js +78 -0
  95. package/dist/providers/index.d.ts +5 -0
  96. package/dist/providers/index.js +5 -0
  97. package/dist/providers/registry.d.ts +38 -0
  98. package/dist/providers/registry.js +121 -0
  99. package/dist/providers/types.d.ts +74 -0
  100. package/dist/providers/types.js +25 -0
  101. package/dist/runner/agent-supervisor.d.ts +363 -0
  102. package/dist/runner/agent-supervisor.js +1672 -0
  103. package/dist/scanner/context-builder.d.ts +2 -0
  104. package/dist/scanner/context-builder.js +29 -0
  105. package/dist/scanner/detector.d.ts +2 -0
  106. package/dist/scanner/detector.js +760 -0
  107. package/dist/scanner/types.d.ts +80 -0
  108. package/dist/scanner/types.js +50 -0
  109. package/dist/server/studio-server.d.ts +39 -0
  110. package/dist/server/studio-server.js +1931 -0
  111. package/dist/server/studio-ui.d.ts +15 -0
  112. package/dist/server/studio-ui.js +5321 -0
  113. package/package.json +70 -0
  114. package/plugin/.claude-plugin/plugin.json +18 -0
  115. package/plugin/.mcp.json +10 -0
  116. package/plugin/agents/architect.md +28 -0
  117. package/plugin/agents/verifier.md +28 -0
  118. package/plugin/agents/worker.md +26 -0
  119. package/plugin/hooks/hooks.json +28 -0
  120. package/plugin/skills/nativ/SKILL.md +75 -0
  121. package/templates/.claudeignore +45 -0
  122. package/templates/AGENTS.md +67 -0
  123. package/templates/CLAUDE.md +32 -0
  124. package/templates/GEMINI.md +66 -0
  125. package/templates/claude-agents/architect.md +28 -0
  126. package/templates/claude-agents/verifier.md +28 -0
  127. package/templates/claude-agents/worker.md +26 -0
  128. package/templates/dot-ai/api_contracts.json +66 -0
  129. package/templates/dot-ai/context.md +38 -0
  130. package/templates/dot-ai/db_schema.json +39 -0
  131. package/templates/dot-ai/master_plan.json +28 -0
  132. package/templates/dot-ai/subagents/backend.md +11 -0
  133. package/templates/dot-ai/subagents/database.md +10 -0
  134. package/templates/dot-ai/subagents/db-migration.md +11 -0
  135. package/templates/dot-ai/subagents/devops-agent.md +11 -0
  136. package/templates/dot-ai/subagents/flutter-developer.md +11 -0
  137. package/templates/dot-ai/subagents/frontend.md +126 -0
  138. package/templates/dot-ai/subagents/qa-tester.md +13 -0
  139. package/templates/dot-ai/subagents/security-auditor.md +11 -0
  140. package/templates/dot-ai/ui_specs.md +78 -0
@@ -0,0 +1,772 @@
1
+ import fs from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import pc from 'picocolors';
5
+ import { withPlanLock, loadPlan } from './lock-manager.js';
6
+ import { ContractGovernor } from '../governor/index.js';
7
+ import { executeVerification } from './verifier.js';
8
+ import { loadTelemetry, recordTaskStart, recordTaskComplete, recordCircuitBreakerTrip, estimateTokens, } from './telemetry.js';
9
+ import { runTaskStart, runTaskComplete, runTaskAdd } from '../commands/task.js';
10
+ /**
11
+ * Creates an isolated scratch workspace for benchmarking with valid .ai/ contracts.
12
+ */
13
+ export function createBenchmarkSandbox() {
14
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'nativ-bench-'));
15
+ const aiDir = path.join(dir, '.ai');
16
+ fs.mkdirSync(aiDir, { recursive: true });
17
+ fs.writeFileSync(path.join(aiDir, 'context.md'), '# Benchmark Project\nArchitecture: Node.js / TypeScript microservices with SQLite / PostgreSQL.\n', 'utf8');
18
+ const initialPlan = {
19
+ version: '1.0.0',
20
+ projectName: 'benchmark-sandbox',
21
+ overallStatus: 'in_progress',
22
+ activeMilestoneId: 'm1',
23
+ lastUpdated: new Date().toISOString(),
24
+ milestones: [
25
+ {
26
+ id: 'm1',
27
+ name: 'Core Infrastructure',
28
+ status: 'in_progress',
29
+ tasks: [
30
+ {
31
+ id: 'task-01',
32
+ title: 'Setup Database Connection',
33
+ description: 'Database setup',
34
+ assignedSubagent: 'database',
35
+ dependencies: [],
36
+ targetFiles: ['src/db.ts'],
37
+ status: 'pending',
38
+ verificationCommand: 'node -e "process.exit(0)"',
39
+ },
40
+ {
41
+ id: 'task-02',
42
+ title: 'Implement Auth Routes',
43
+ description: 'Authentication routes',
44
+ assignedSubagent: 'backend',
45
+ dependencies: ['task-01'],
46
+ targetFiles: ['src/auth.ts'],
47
+ status: 'pending',
48
+ verificationCommand: 'node -e "process.exit(0)"',
49
+ },
50
+ {
51
+ id: 'task-03',
52
+ title: 'Design Login Screen',
53
+ description: 'Login UI layout',
54
+ assignedSubagent: 'frontend',
55
+ dependencies: ['task-02'],
56
+ targetFiles: ['src/views/login.html'],
57
+ status: 'pending',
58
+ verificationCommand: 'none',
59
+ },
60
+ ],
61
+ },
62
+ {
63
+ id: 'm2',
64
+ name: 'API & Gateway',
65
+ status: 'pending',
66
+ tasks: [
67
+ {
68
+ id: 'task-04',
69
+ title: 'Build GraphQL Gateway',
70
+ description: 'Gateway service',
71
+ assignedSubagent: 'backend',
72
+ dependencies: ['task-02'],
73
+ targetFiles: ['src/gateway.ts'],
74
+ status: 'pending',
75
+ verificationCommand: 'node -e "process.exit(0)"',
76
+ },
77
+ ],
78
+ },
79
+ ],
80
+ };
81
+ fs.writeFileSync(path.join(aiDir, 'master_plan.json'), JSON.stringify(initialPlan, null, 2), 'utf8');
82
+ const dbSchema = {
83
+ version: '1.0.0',
84
+ tables: [
85
+ {
86
+ name: 'users',
87
+ columns: [
88
+ { name: 'id', type: 'VARCHAR(36)', nullable: false, primaryKey: true },
89
+ { name: 'email', type: 'VARCHAR(255)', nullable: false, unique: true },
90
+ { name: 'password_hash', type: 'VARCHAR(255)', nullable: false },
91
+ ],
92
+ indexes: [{ name: 'idx_users_email', columns: ['email'], unique: true }],
93
+ },
94
+ ],
95
+ };
96
+ fs.writeFileSync(path.join(aiDir, 'db_schema.json'), JSON.stringify(dbSchema, null, 2), 'utf8');
97
+ const apiContracts = {
98
+ version: '1.0.0',
99
+ endpoints: [
100
+ {
101
+ id: 'post-auth-login',
102
+ path: '/api/auth/login',
103
+ method: 'POST',
104
+ summary: 'User login',
105
+ requestBody: {
106
+ contentType: 'application/json',
107
+ schema: { email: 'string', password: 'string' },
108
+ },
109
+ responses: {
110
+ '200': { description: 'Authenticated token', schema: { token: 'string' } },
111
+ '401': { description: 'Invalid credentials' },
112
+ },
113
+ },
114
+ ],
115
+ };
116
+ fs.writeFileSync(path.join(aiDir, 'api_contracts.json'), JSON.stringify(apiContracts, null, 2), 'utf8');
117
+ return dir;
118
+ }
119
+ /**
120
+ * Resilient sandbox cleanup handling Windows NTFS delayed locks.
121
+ */
122
+ export function cleanupSandbox(dir) {
123
+ for (let attempt = 0; attempt < 10; attempt++) {
124
+ try {
125
+ if (fs.existsSync(dir)) {
126
+ fs.rmSync(dir, { recursive: true, force: true });
127
+ }
128
+ return;
129
+ }
130
+ catch {
131
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 50);
132
+ }
133
+ }
134
+ }
135
+ /**
136
+ * Executes a function quietly, keeping benchmark stdout clean for reports and JSON.
137
+ */
138
+ async function silent(fn) {
139
+ const origLog = console.log;
140
+ const origErr = console.error;
141
+ const origInfo = console.info;
142
+ const origWarn = console.warn;
143
+ console.log = () => { };
144
+ console.error = () => { };
145
+ console.info = () => { };
146
+ console.warn = () => { };
147
+ try {
148
+ return await fn();
149
+ }
150
+ finally {
151
+ console.log = origLog;
152
+ console.error = origErr;
153
+ console.info = origInfo;
154
+ console.warn = origWarn;
155
+ }
156
+ }
157
+ /**
158
+ * Scenario 1: Multi-Agent Concurrency & Lock Contention
159
+ */
160
+ async function runConcurrencyScenario(concurrency = 6) {
161
+ const sandbox = createBenchmarkSandbox();
162
+ const planPath = path.join(sandbox, '.ai', 'master_plan.json');
163
+ const errors = [];
164
+ let assertionsPassed = 0;
165
+ let assertionsFailed = 0;
166
+ const startTime = Date.now();
167
+ const lockWaitTimes = [];
168
+ try {
169
+ // Populate 20 tasks into milestone 1
170
+ await withPlanLock(planPath, (plan) => {
171
+ plan.milestones[0].tasks = [];
172
+ for (let i = 1; i <= 20; i++) {
173
+ const id = `bench-task-${String(i).padStart(2, '0')}`;
174
+ plan.milestones[0].tasks.push({
175
+ id,
176
+ title: `Synthetic Task ${i}`,
177
+ description: `Synthetic task ${i} description`,
178
+ assignedSubagent: i % 2 === 0 ? 'backend' : 'frontend',
179
+ dependencies: [],
180
+ targetFiles: [`file-${i}.ts`],
181
+ status: 'pending',
182
+ verificationCommand: 'node -e "process.exit(0)"',
183
+ });
184
+ }
185
+ });
186
+ const totalOps = 20 + 20 + 8; // 20 starts, 20 completes, 8 concurrent adds
187
+ // 1. Parallel Task Starts
188
+ const startPromises = Array.from({ length: 20 }, async (_, i) => {
189
+ const taskId = `bench-task-${String(i + 1).padStart(2, '0')}`;
190
+ const t0 = Date.now();
191
+ await runTaskStart(taskId, sandbox);
192
+ lockWaitTimes.push(Date.now() - t0);
193
+ });
194
+ await Promise.all(startPromises);
195
+ // Verify all 20 in_progress
196
+ const p1 = loadPlan(planPath);
197
+ if (p1 && p1.milestones[0].tasks.every((t) => t.status === 'in_progress')) {
198
+ assertionsPassed++;
199
+ }
200
+ else {
201
+ assertionsFailed++;
202
+ errors.push('Not all tasks were atomically transitioned to in_progress.');
203
+ }
204
+ // 2. Parallel Task Completes
205
+ const completePromises = Array.from({ length: 20 }, async (_, i) => {
206
+ const taskId = `bench-task-${String(i + 1).padStart(2, '0')}`;
207
+ const t0 = Date.now();
208
+ await runTaskComplete(taskId, sandbox, { skipVerify: true });
209
+ lockWaitTimes.push(Date.now() - t0);
210
+ });
211
+ await Promise.all(completePromises);
212
+ // Verify all 20 completed
213
+ const p2 = loadPlan(planPath);
214
+ if (p2 && p2.milestones[0].tasks.every((t) => t.status === 'completed')) {
215
+ assertionsPassed++;
216
+ }
217
+ else {
218
+ assertionsFailed++;
219
+ errors.push('Not all tasks were atomically transitioned to completed.');
220
+ }
221
+ // 3. Parallel Task Adds
222
+ const addPromises = Array.from({ length: 8 }, async (_, i) => {
223
+ const t0 = Date.now();
224
+ await runTaskAdd(`Dynamic Worker Task ${i + 1}`, sandbox);
225
+ lockWaitTimes.push(Date.now() - t0);
226
+ });
227
+ await Promise.all(addPromises);
228
+ const p3 = loadPlan(planPath);
229
+ const totalTasks = p3 ? p3.milestones.reduce((acc, m) => acc + m.tasks.length, 0) : 0;
230
+ if (totalTasks === 29) {
231
+ assertionsPassed++;
232
+ }
233
+ else {
234
+ assertionsFailed++;
235
+ errors.push(`Expected 29 tasks across milestones after concurrent adds, found ${totalTasks}`);
236
+ }
237
+ const durationMs = Date.now() - startTime;
238
+ const meanLockWait = lockWaitTimes.length > 0
239
+ ? Math.round((lockWaitTimes.reduce((a, b) => a + b, 0) / lockWaitTimes.length) * 100) / 100
240
+ : 0;
241
+ const maxLockWait = lockWaitTimes.length > 0 ? Math.max(...lockWaitTimes) : 0;
242
+ const throughput = Math.round((totalOps / (durationMs / 1000)) * 10) / 10;
243
+ return {
244
+ id: 'concurrency',
245
+ name: 'Multi-Agent Lock Contention & Throughput',
246
+ description: 'Stresses atomic OS advisory locks and state transitions across concurrent agent workers.',
247
+ status: assertionsFailed === 0 ? 'passed' : 'failed',
248
+ durationMs,
249
+ operations: totalOps,
250
+ throughputOpsPerSec: throughput,
251
+ assertionsPassed,
252
+ assertionsFailed,
253
+ details: {
254
+ concurrentWorkers: concurrency,
255
+ totalAtomicMutations: totalOps,
256
+ meanLockWaitMs: meanLockWait,
257
+ maxLockWaitMs: maxLockWait,
258
+ },
259
+ errors: errors.length > 0 ? errors : undefined,
260
+ };
261
+ }
262
+ finally {
263
+ cleanupSandbox(sandbox);
264
+ }
265
+ }
266
+ /**
267
+ * Scenario 2: Contract Governor Invariant Engine & Circuit Breaker
268
+ */
269
+ async function runGovernorScenario() {
270
+ const sandbox = createBenchmarkSandbox();
271
+ const errors = [];
272
+ let assertionsPassed = 0;
273
+ let assertionsFailed = 0;
274
+ const startTime = Date.now();
275
+ let operations = 0;
276
+ try {
277
+ // 1. Additive DB change (Valid -> should approve)
278
+ operations++;
279
+ const addColPatch = {
280
+ taskId: 'bench-task-gov',
281
+ target: 'db_schema',
282
+ operation: 'ADD',
283
+ path: 'users.columns.bio',
284
+ value: { name: 'bio', type: 'TEXT', nullable: true },
285
+ reason: 'Add user bio field',
286
+ };
287
+ const res1 = ContractGovernor.evaluate(sandbox, addColPatch);
288
+ if (res1.approved && res1.patchApplied) {
289
+ assertionsPassed++;
290
+ }
291
+ else {
292
+ assertionsFailed++;
293
+ errors.push(`Additive DB change was rejected: ${res1.message}`);
294
+ }
295
+ // 2. Destructive DB change (Drop column -> must reject)
296
+ operations++;
297
+ const dropColPatch = {
298
+ taskId: 'bench-task-gov',
299
+ target: 'db_schema',
300
+ operation: 'DROP',
301
+ path: 'users.columns.email',
302
+ reason: 'Drop email column',
303
+ };
304
+ const res2 = ContractGovernor.evaluate(sandbox, dropColPatch);
305
+ if (!res2.approved && (res2.ruleId === 'DB_DROP_FORBIDDEN' || res2.blastRadius === 'HIGH_DESTRUCTIVE')) {
306
+ assertionsPassed++;
307
+ }
308
+ else {
309
+ assertionsFailed++;
310
+ errors.push('Destructive DB drop column was unexpectedly approved.');
311
+ }
312
+ // 3. Additive API change (Valid -> should approve)
313
+ operations++;
314
+ const addEndpointPatch = {
315
+ taskId: 'bench-task-gov',
316
+ target: 'api_contracts',
317
+ operation: 'ADD',
318
+ path: 'endpoints./api/users/profile',
319
+ value: {
320
+ id: 'get-users-profile',
321
+ path: '/api/users/profile',
322
+ method: 'GET',
323
+ summary: 'Get user profile',
324
+ },
325
+ reason: 'Expose profile query',
326
+ };
327
+ const res3 = ContractGovernor.evaluate(sandbox, addEndpointPatch);
328
+ if (res3.approved && res3.patchApplied) {
329
+ assertionsPassed++;
330
+ }
331
+ else {
332
+ assertionsFailed++;
333
+ errors.push(`Additive API change was rejected: ${res3.message}`);
334
+ }
335
+ // 4. Destructive API change (Drop endpoint -> must reject)
336
+ operations++;
337
+ const dropEndpointPatch = {
338
+ taskId: 'bench-task-gov',
339
+ target: 'api_contracts',
340
+ operation: 'DROP',
341
+ path: 'endpoints.post-auth-login',
342
+ reason: 'Drop login route',
343
+ };
344
+ const res4 = ContractGovernor.evaluate(sandbox, dropEndpointPatch);
345
+ if (!res4.approved && (res4.ruleId === 'API_DROP_FORBIDDEN' || res4.blastRadius === 'HIGH_DESTRUCTIVE')) {
346
+ assertionsPassed++;
347
+ }
348
+ else {
349
+ assertionsFailed++;
350
+ errors.push('Destructive endpoint deletion was unexpectedly approved.');
351
+ }
352
+ // 5. 3-Strike Circuit Breaker Tripping
353
+ operations += 3;
354
+ let tripped = false;
355
+ for (let i = 0; i < 3; i++) {
356
+ const dropAttempt = {
357
+ taskId: 'bench-task-runaway',
358
+ target: 'db_schema',
359
+ operation: 'DROP',
360
+ path: 'users.columns.id',
361
+ reason: `Repeated destructive drop attempt ${i + 1}`,
362
+ };
363
+ const r = ContractGovernor.evaluate(sandbox, dropAttempt);
364
+ if (r.circuitBreaker?.tripped) {
365
+ tripped = true;
366
+ }
367
+ }
368
+ if (tripped) {
369
+ assertionsPassed++;
370
+ }
371
+ else {
372
+ assertionsFailed++;
373
+ errors.push('Governor circuit breaker did not trip after 3 consecutive destructive violations.');
374
+ }
375
+ const escalationPath = path.join(sandbox, '.ai', 'escalation.json');
376
+ if (fs.existsSync(escalationPath)) {
377
+ const esc = JSON.parse(fs.readFileSync(escalationPath, 'utf8'));
378
+ if (esc.escalations.length > 0) {
379
+ assertionsPassed++;
380
+ }
381
+ else {
382
+ assertionsFailed++;
383
+ errors.push('Escalation record was not logged by circuit breaker flight recorder.');
384
+ }
385
+ }
386
+ else {
387
+ assertionsFailed++;
388
+ errors.push('No .ai/escalation.json was created upon circuit breaker trip.');
389
+ }
390
+ const durationMs = Date.now() - startTime;
391
+ const throughput = Math.round((operations / (durationMs / 1000)) * 10) / 10;
392
+ return {
393
+ id: 'governor',
394
+ name: 'Contract Invariant & Circuit Breaker Engine',
395
+ description: 'Tests invariant evaluation, additive auto-approvals, destructive rejections, and 3-strike escalation.',
396
+ status: assertionsFailed === 0 ? 'passed' : 'failed',
397
+ durationMs,
398
+ operations,
399
+ throughputOpsPerSec: throughput,
400
+ assertionsPassed,
401
+ assertionsFailed,
402
+ details: {
403
+ invariantsChecked: ['NO_DESTRUCTIVE_SCHEMA', 'NO_ENDPOINT_DELETION', 'ADDITIVE_SCHEMA_APPROVAL'],
404
+ circuitBreakerTripped: tripped,
405
+ },
406
+ errors: errors.length > 0 ? errors : undefined,
407
+ };
408
+ }
409
+ finally {
410
+ cleanupSandbox(sandbox);
411
+ }
412
+ }
413
+ /**
414
+ * Scenario 3: Verification Gatekeeper Performance & Isolation
415
+ */
416
+ async function runVerificationScenario() {
417
+ const sandbox = createBenchmarkSandbox();
418
+ const errors = [];
419
+ let assertionsPassed = 0;
420
+ let assertionsFailed = 0;
421
+ const startTime = Date.now();
422
+ let operations = 0;
423
+ try {
424
+ // 1. Passing verification
425
+ operations++;
426
+ const passRes = await executeVerification('node -e "process.exit(0)"', sandbox);
427
+ if (passRes.success && passRes.exitCode === 0) {
428
+ assertionsPassed++;
429
+ }
430
+ else {
431
+ assertionsFailed++;
432
+ errors.push(`Passing verification failed with exit code ${passRes.exitCode}`);
433
+ }
434
+ // 2. Failing verification with custom exit code
435
+ operations++;
436
+ const failRes = await executeVerification('node -e "console.error(\\"synthetic error\\"); process.exit(42)"', sandbox);
437
+ if (!failRes.success && failRes.exitCode === 42 && failRes.stderr.includes('synthetic error')) {
438
+ assertionsPassed++;
439
+ }
440
+ else {
441
+ assertionsFailed++;
442
+ errors.push('Failing verification did not capture exit code 42 or stderr correctly.');
443
+ }
444
+ // 3. Skipped verification command
445
+ operations++;
446
+ const skipRes = await executeVerification('none', sandbox);
447
+ if (skipRes.success && skipRes.skipped) {
448
+ assertionsPassed++;
449
+ }
450
+ else {
451
+ assertionsFailed++;
452
+ errors.push('Verification command "none" was not recognized as skipped.');
453
+ }
454
+ // 4. Timeout handling (150ms timeout on a command sleeping 500ms)
455
+ operations++;
456
+ const timeoutRes = await executeVerification('node -e "setTimeout(() => {}, 500)"', sandbox, 150);
457
+ if (!timeoutRes.success && (timeoutRes.exitCode === 124 || timeoutRes.exitCode === 1 || timeoutRes.durationMs >= 140)) {
458
+ assertionsPassed++;
459
+ }
460
+ else {
461
+ assertionsFailed++;
462
+ errors.push(`Timeout command did not fail as expected (exitCode ${timeoutRes.exitCode}, success: ${timeoutRes.success})`);
463
+ }
464
+ const durationMs = Date.now() - startTime;
465
+ const throughput = Math.round((operations / (durationMs / 1000)) * 10) / 10;
466
+ return {
467
+ id: 'verification',
468
+ name: 'Verification Gatekeeper & Regression Guard',
469
+ description: 'Tests test command execution, error diagnostics capture, timeout isolation, and skip handling.',
470
+ status: assertionsFailed === 0 ? 'passed' : 'failed',
471
+ durationMs,
472
+ operations,
473
+ throughputOpsPerSec: throughput,
474
+ assertionsPassed,
475
+ assertionsFailed,
476
+ details: {
477
+ testsExecuted: operations,
478
+ },
479
+ errors: errors.length > 0 ? errors : undefined,
480
+ };
481
+ }
482
+ finally {
483
+ cleanupSandbox(sandbox);
484
+ }
485
+ }
486
+ /**
487
+ * Scenario 4: Telemetry & Cost Accounting Integrity
488
+ */
489
+ async function runTelemetryScenario() {
490
+ const sandbox = createBenchmarkSandbox();
491
+ const errors = [];
492
+ let assertionsPassed = 0;
493
+ let assertionsFailed = 0;
494
+ const startTime = Date.now();
495
+ let operations = 0;
496
+ try {
497
+ const telemetryPath = path.join(sandbox, '.ai', 'telemetry.json');
498
+ // Create synthetic target files for token weight calculation
499
+ fs.writeFileSync(path.join(sandbox, 'service.ts'), 'export function execute() { return true; }\n'.repeat(40), 'utf8');
500
+ const syntheticTask = {
501
+ id: 'telemetry-task-01',
502
+ title: 'Implement Core Service',
503
+ description: 'Core service implementation',
504
+ assignedSubagent: 'backend',
505
+ dependencies: [],
506
+ targetFiles: ['service.ts'],
507
+ status: 'pending',
508
+ verificationCommand: 'npm test',
509
+ };
510
+ // 1. Estimate tokens
511
+ operations++;
512
+ const tokens = estimateTokens(sandbox, syntheticTask);
513
+ if (tokens.inputEstimated > 3500 && tokens.costUsdEstimated > 0) {
514
+ assertionsPassed++;
515
+ }
516
+ else {
517
+ assertionsFailed++;
518
+ errors.push('Token estimation heuristics failed to compute input tokens or cost.');
519
+ }
520
+ // 2. Record task start
521
+ operations++;
522
+ await recordTaskStart(sandbox, syntheticTask);
523
+ let tel = loadTelemetry(telemetryPath, 'bench-proj');
524
+ if (tel.tasks.length === 1 && tel.tasks[0].status === 'in_progress') {
525
+ assertionsPassed++;
526
+ }
527
+ else {
528
+ assertionsFailed++;
529
+ errors.push('Telemetry recordTaskStart did not register in_progress record.');
530
+ }
531
+ // 3. Record task complete with verification
532
+ operations++;
533
+ await recordTaskComplete(sandbox, syntheticTask, {
534
+ command: 'npm test',
535
+ durationMs: 45,
536
+ exitCode: 0,
537
+ stdout: 'All tests passed',
538
+ stderr: '',
539
+ skipped: false,
540
+ success: true,
541
+ });
542
+ tel = loadTelemetry(telemetryPath, 'bench-proj');
543
+ if (tel.summary.totalTasksCompleted === 1 &&
544
+ tel.summary.totalVerificationsRun === 1 &&
545
+ tel.summary.verificationPassRate === 1.0 &&
546
+ tel.summary.estimatedTotalCostUsd > 0) {
547
+ assertionsPassed++;
548
+ }
549
+ else {
550
+ assertionsFailed++;
551
+ errors.push('Telemetry recordTaskComplete failed to compute summary metrics correctly.');
552
+ }
553
+ // 4. Circuit breaker trip recording
554
+ operations++;
555
+ await recordCircuitBreakerTrip(sandbox, 'telemetry-task-01');
556
+ tel = loadTelemetry(telemetryPath, 'bench-proj');
557
+ if (tel.summary.circuitBreakerTrips === 1) {
558
+ assertionsPassed++;
559
+ }
560
+ else {
561
+ assertionsFailed++;
562
+ errors.push('Telemetry recordCircuitBreakerTrip did not increment trip counter.');
563
+ }
564
+ const durationMs = Date.now() - startTime;
565
+ const throughput = Math.round((operations / (durationMs / 1000)) * 10) / 10;
566
+ return {
567
+ id: 'telemetry',
568
+ name: 'Telemetry & Cost Accounting Integrity',
569
+ description: 'Tests live recording of durations, token heuristics, cost estimates, and pass rates.',
570
+ status: assertionsFailed === 0 ? 'passed' : 'failed',
571
+ durationMs,
572
+ operations,
573
+ throughputOpsPerSec: throughput,
574
+ assertionsPassed,
575
+ assertionsFailed,
576
+ details: {
577
+ totalCostUsd: tel.summary.estimatedTotalCostUsd,
578
+ totalTokens: tel.summary.estimatedTotalTokens,
579
+ circuitBreakersRecorded: tel.summary.circuitBreakerTrips,
580
+ },
581
+ errors: errors.length > 0 ? errors : undefined,
582
+ };
583
+ }
584
+ finally {
585
+ cleanupSandbox(sandbox);
586
+ }
587
+ }
588
+ /**
589
+ * Scenario 5: End-to-End Autonomous Multi-Agent Pipeline
590
+ */
591
+ async function runE2EPipelineScenario() {
592
+ const sandbox = createBenchmarkSandbox();
593
+ const errors = [];
594
+ let assertionsPassed = 0;
595
+ let assertionsFailed = 0;
596
+ const startTime = Date.now();
597
+ let operations = 0;
598
+ try {
599
+ const planPath = path.join(sandbox, '.ai', 'master_plan.json');
600
+ // 1. Task 1 lifecycle
601
+ operations++;
602
+ await runTaskStart('task-01', sandbox);
603
+ await runTaskComplete('task-01', sandbox);
604
+ // 2. Task 2 lifecycle
605
+ operations++;
606
+ await runTaskStart('task-02', sandbox);
607
+ await runTaskComplete('task-02', sandbox);
608
+ // 3. Task 3 lifecycle (completes Milestone 1)
609
+ operations++;
610
+ await runTaskStart('task-03', sandbox);
611
+ await runTaskComplete('task-03', sandbox);
612
+ // Check if Milestone 1 completed and active milestone advanced to m2
613
+ const planAfterM1 = loadPlan(planPath);
614
+ if (planAfterM1 &&
615
+ planAfterM1.milestones[0].status === 'completed' &&
616
+ planAfterM1.activeMilestoneId === 'm2') {
617
+ assertionsPassed++;
618
+ }
619
+ else {
620
+ assertionsFailed++;
621
+ errors.push(`Active milestone failed to advance to m2. Current: ${planAfterM1?.activeMilestoneId}`);
622
+ }
623
+ // 4. Task 4 lifecycle (completes Milestone 2 & overall project)
624
+ operations++;
625
+ await runTaskStart('task-04', sandbox);
626
+ await runTaskComplete('task-04', sandbox);
627
+ const finalPlan = loadPlan(planPath);
628
+ if (finalPlan &&
629
+ finalPlan.milestones[1].status === 'completed' &&
630
+ finalPlan.overallStatus === 'completed') {
631
+ assertionsPassed++;
632
+ }
633
+ else {
634
+ assertionsFailed++;
635
+ errors.push(`Overall project status failed to mark completed. Status: ${finalPlan?.overallStatus}`);
636
+ }
637
+ const durationMs = Date.now() - startTime;
638
+ const throughput = Math.round((operations / (durationMs / 1000)) * 10) / 10;
639
+ return {
640
+ id: 'e2e_pipeline',
641
+ name: 'Autonomous Multi-Agent Lifecycle & Milestone Graph',
642
+ description: 'Simulates complete multi-agent lifecycle across dependency graph and milestone advancement.',
643
+ status: assertionsFailed === 0 ? 'passed' : 'failed',
644
+ durationMs,
645
+ operations,
646
+ throughputOpsPerSec: throughput,
647
+ assertionsPassed,
648
+ assertionsFailed,
649
+ details: {
650
+ milestonesResolved: 2,
651
+ tasksCompleted: 4,
652
+ projectStatus: finalPlan?.overallStatus || 'unknown',
653
+ },
654
+ errors: errors.length > 0 ? errors : undefined,
655
+ };
656
+ }
657
+ finally {
658
+ cleanupSandbox(sandbox);
659
+ }
660
+ }
661
+ /**
662
+ * Main benchmark execution coordinator.
663
+ */
664
+ export async function runBenchmarks(options = {}) {
665
+ const scenarioFilter = options.scenario ? options.scenario.toLowerCase().trim() : 'all';
666
+ const concurrency = options.concurrency || 6;
667
+ const scenariosToRun = [];
668
+ if (scenarioFilter === 'all' || scenarioFilter === 'concurrency') {
669
+ scenariosToRun.push(() => runConcurrencyScenario(concurrency));
670
+ }
671
+ if (scenarioFilter === 'all' || scenarioFilter === 'governor') {
672
+ scenariosToRun.push(() => runGovernorScenario());
673
+ }
674
+ if (scenarioFilter === 'all' || scenarioFilter === 'verification') {
675
+ scenariosToRun.push(() => runVerificationScenario());
676
+ }
677
+ if (scenarioFilter === 'all' || scenarioFilter === 'telemetry') {
678
+ scenariosToRun.push(() => runTelemetryScenario());
679
+ }
680
+ if (scenarioFilter === 'all' || scenarioFilter === 'e2e' || scenarioFilter === 'e2e_pipeline') {
681
+ scenariosToRun.push(() => runE2EPipelineScenario());
682
+ }
683
+ const results = [];
684
+ const overallStart = Date.now();
685
+ for (const runner of scenariosToRun) {
686
+ const res = await silent(() => runner());
687
+ results.push(res);
688
+ }
689
+ const totalDurationMs = Date.now() - overallStart;
690
+ const passedCount = results.filter((r) => r.status === 'passed').length;
691
+ const totalOps = results.reduce((acc, r) => acc + r.operations, 0);
692
+ const avgThroughput = results.length > 0
693
+ ? Math.round((results.reduce((acc, r) => acc + r.throughputOpsPerSec, 0) / results.length) * 10) / 10
694
+ : 0;
695
+ const score = results.length > 0 ? Math.round((passedCount / results.length) * 100) : 0;
696
+ const report = {
697
+ version: '1.0.0',
698
+ timestamp: new Date().toISOString(),
699
+ environment: {
700
+ platform: process.platform,
701
+ nodeVersion: process.version,
702
+ cpuCount: os.cpus().length,
703
+ arch: process.arch,
704
+ },
705
+ summary: {
706
+ totalScenarios: results.length,
707
+ passedScenarios: passedCount,
708
+ failedScenarios: results.length - passedCount,
709
+ score,
710
+ totalDurationMs,
711
+ totalOperations: totalOps,
712
+ averageThroughputOpsPerSec: avgThroughput,
713
+ },
714
+ scenarios: results,
715
+ };
716
+ return report;
717
+ }
718
+ /**
719
+ * Formats the benchmark report for high-readability CLI output.
720
+ */
721
+ export function formatBenchmarkReport(report) {
722
+ const lines = [];
723
+ lines.push(pc.bold(pc.cyan('\n============================================================')));
724
+ lines.push(pc.bold(pc.cyan(' NATIV LOCAL SYNTHETIC BENCHMARK SUITE ')));
725
+ lines.push(pc.bold(pc.cyan('============================================================')));
726
+ lines.push(pc.dim('Environment: ') +
727
+ pc.white(`${report.environment.platform} (${report.environment.arch})`) +
728
+ pc.dim(' | Node: ') +
729
+ pc.white(report.environment.nodeVersion) +
730
+ pc.dim(' | CPUs: ') +
731
+ pc.white(`${report.environment.cpuCount}`));
732
+ lines.push(pc.dim('Timestamp: ') + pc.white(report.timestamp));
733
+ lines.push('');
734
+ lines.push(pc.bold('Benchmark Scenarios:'));
735
+ for (const s of report.scenarios) {
736
+ const badge = s.status === 'passed' ? pc.green('[PASS]') : pc.red('[FAIL]');
737
+ const dur = (s.durationMs / 1000).toFixed(2);
738
+ lines.push(` ${badge} ${pc.bold(s.name)}`);
739
+ lines.push(pc.dim(` Throughput: `) +
740
+ pc.yellow(`${s.throughputOpsPerSec} ops/sec`) +
741
+ pc.dim(` (${s.operations} ops in ${dur}s)`));
742
+ lines.push(pc.dim(` Assertions: `) +
743
+ pc.green(`${s.assertionsPassed} passed`) +
744
+ (s.assertionsFailed > 0 ? pc.red(`, ${s.assertionsFailed} failed`) : ''));
745
+ if (s.details.meanLockWaitMs !== undefined) {
746
+ lines.push(pc.dim(` Lock Wait: `) +
747
+ pc.white(`mean ${s.details.meanLockWaitMs}ms | max ${s.details.maxLockWaitMs}ms`));
748
+ }
749
+ if (s.details.totalCostUsd !== undefined) {
750
+ lines.push(pc.dim(` Telemetry: `) +
751
+ pc.white(`~$${s.details.totalCostUsd} USD (${s.details.totalTokens} tokens)`));
752
+ }
753
+ if (s.errors && s.errors.length > 0) {
754
+ for (const err of s.errors) {
755
+ lines.push(pc.red(` ✖ Error: ${err}`));
756
+ }
757
+ }
758
+ lines.push('');
759
+ }
760
+ lines.push(pc.bold(pc.cyan('------------------------------------------------------------')));
761
+ const scoreColor = report.summary.score === 100 ? pc.green : report.summary.score >= 80 ? pc.yellow : pc.red;
762
+ lines.push(pc.bold('Benchmark Score: ') +
763
+ scoreColor(pc.bold(`${report.summary.score}%`)) +
764
+ pc.dim(` (${report.summary.passedScenarios}/${report.summary.totalScenarios} scenarios passed)`));
765
+ lines.push(pc.bold('Total Operations: ') +
766
+ pc.white(`${report.summary.totalOperations}`) +
767
+ pc.dim(` (${report.summary.averageThroughputOpsPerSec} avg ops/sec)`));
768
+ lines.push(pc.bold('Total Elapsed Time: ') +
769
+ pc.white(`${(report.summary.totalDurationMs / 1000).toFixed(2)}s (${report.summary.totalDurationMs}ms)`));
770
+ lines.push(pc.bold(pc.cyan('============================================================\n')));
771
+ return lines.join('\n');
772
+ }