@polderlabs/bizar 10.18.0 → 10.19.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 (39) hide show
  1. package/cli/bin.mjs +72 -0
  2. package/cli/commands/bench.mjs +360 -0
  3. package/cli/commands/explain-run.mjs +278 -0
  4. package/cli/commands/objective-scheduler.mjs +609 -0
  5. package/cli/commands/release-provenance.mjs +261 -0
  6. package/cli/commands/spec-list.mjs +225 -0
  7. package/cli/commands/verify-release.mjs +104 -0
  8. package/cli/cost-gate.mjs +323 -0
  9. package/config/claude/hooks/permission-request.mjs +176 -11
  10. package/package.json +1 -1
  11. package/packages/sdk/dist/autonomy/evidence-bundle.d.ts +10 -1
  12. package/packages/sdk/dist/autonomy/evidence-bundle.js +8 -0
  13. package/packages/sdk/dist/autonomy/index.d.ts +3 -3
  14. package/packages/sdk/dist/autonomy/index.js +3 -3
  15. package/packages/sdk/dist/autonomy/objective-run.d.ts +17 -0
  16. package/packages/sdk/dist/autonomy/objective-run.js +28 -0
  17. package/packages/sdk/dist/autonomy/outcome-record.d.ts +9 -0
  18. package/packages/sdk/dist/autonomy/outcome-record.js +8 -0
  19. package/packages/sdk/dist/bench/auto-reduction.d.ts +91 -0
  20. package/packages/sdk/dist/bench/auto-reduction.js +130 -0
  21. package/packages/sdk/dist/bench/efficiency.d.ts +174 -0
  22. package/packages/sdk/dist/bench/efficiency.js +202 -0
  23. package/packages/sdk/dist/bench/index.d.ts +15 -0
  24. package/packages/sdk/dist/bench/index.js +13 -0
  25. package/packages/sdk/dist/index.d.ts +2 -0
  26. package/packages/sdk/dist/index.js +4 -0
  27. package/packages/sdk/dist/release/index.d.ts +20 -0
  28. package/packages/sdk/dist/release/index.js +20 -0
  29. package/packages/sdk/dist/release/known-good-releases.d.ts +101 -0
  30. package/packages/sdk/dist/release/known-good-releases.js +200 -0
  31. package/packages/sdk/dist/release/provenance.d.ts +82 -0
  32. package/packages/sdk/dist/release/provenance.js +62 -0
  33. package/packages/sdk/dist/release/sbom.d.ts +77 -0
  34. package/packages/sdk/dist/release/sbom.js +114 -0
  35. package/packages/sdk/dist/release/signature.d.ts +84 -0
  36. package/packages/sdk/dist/release/signature.js +139 -0
  37. package/packages/sdk/dist/version.d.ts +1 -1
  38. package/packages/sdk/dist/version.js +1 -1
  39. package/packages/sdk/package.json +3 -2
package/cli/bin.mjs CHANGED
@@ -109,6 +109,10 @@ function showHelp() {
109
109
  restore Restore BizarHarness from a backup
110
110
  validate Validate the Bizar install
111
111
  setup-provider Configure a provider in ~/.claude/settings.json (since v6.2.2 installer doesn't touch providers)
112
+ release-provenance Generate SBOM + provenance + minisig for a release (audit #83)
113
+ verify-release Verify a release artifact set against the pinned allowlist
114
+ spec-list List SDK schemas, policy docs, and mirror sync status (audit #84)
115
+ bench Efficiency benchmarks + auto-fan-out rule (audit #85)
112
116
  team Run the office-manager orchestration agent
113
117
  subagent Run a named agent in read-only plan mode
114
118
  run Run Claude Code once (optionally --bg)
@@ -447,6 +451,58 @@ async function main() {
447
451
  break;
448
452
  }
449
453
 
454
+ case 'release-provenance': {
455
+ const mod = await importCommand('release-provenance');
456
+ if (!mod) {
457
+ console.error(chalk.red(` ✗ Could not load release-provenance command module`));
458
+ process.exit(EXIT_ERROR);
459
+ return;
460
+ }
461
+ dbg('loaded command module:', 'release-provenance');
462
+ const code = await mod.run(cmdArgs);
463
+ if (typeof code === 'number') process.exit(code);
464
+ break;
465
+ }
466
+
467
+ case 'verify-release': {
468
+ const mod = await importCommand('verify-release');
469
+ if (!mod) {
470
+ console.error(chalk.red(` ✗ Could not load verify-release command module`));
471
+ process.exit(EXIT_ERROR);
472
+ return;
473
+ }
474
+ dbg('loaded command module:', 'verify-release');
475
+ const code = await mod.run(cmdArgs);
476
+ if (typeof code === 'number') process.exit(code);
477
+ break;
478
+ }
479
+
480
+ case 'spec-list': {
481
+ const mod = await importCommand('spec-list');
482
+ if (!mod) {
483
+ console.error(chalk.red(` ✗ Could not load spec-list command module`));
484
+ process.exit(EXIT_ERROR);
485
+ return;
486
+ }
487
+ dbg('loaded command module:', 'spec-list');
488
+ const code = await mod.run(cmdArgs);
489
+ if (typeof code === 'number') process.exit(code);
490
+ break;
491
+ }
492
+
493
+ case 'bench': {
494
+ const mod = await importCommand('bench');
495
+ if (!mod) {
496
+ console.error(chalk.red(` ✗ Could not load bench command module`));
497
+ process.exit(EXIT_ERROR);
498
+ return;
499
+ }
500
+ dbg('loaded command module:', 'bench');
501
+ const code = await mod.run(cmdArgs);
502
+ if (typeof code === 'number') process.exit(code);
503
+ break;
504
+ }
505
+
450
506
  case 'improve': {
451
507
  const mod = await importCommand('improve');
452
508
  if (!mod) {
@@ -521,6 +577,22 @@ async function main() {
521
577
  return;
522
578
  }
523
579
 
580
+ case 'explain-run': {
581
+ const mod = await importCommand('explain-run');
582
+ if (!mod) {
583
+ console.error(chalk.red(` ✗ Could not load explain-run command module`));
584
+ process.exit(EXIT_ERROR);
585
+ return;
586
+ }
587
+ dbg('loaded command module:', 'explain-run');
588
+ const found = await mod.run(cmd, cmdArgs, isHelpRequest);
589
+ if (found === false) {
590
+ console.error(chalk.red(` ✗ Usage: bizar explain-run <id> — run 'bizar explain-run --help'`));
591
+ process.exit(EXIT_USAGE);
592
+ }
593
+ break;
594
+ }
595
+
524
596
  default: {
525
597
  console.error(chalk.red(` ✗ Unknown command: ${cmd}`));
526
598
  showHelp();
@@ -0,0 +1,360 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * cli/commands/bench.mjs
4
+ *
5
+ * `bizar bench` — audit #85 (P2 efficiency benchmarks) operator surface.
6
+ *
7
+ * The SDK module at `packages/sdk/src/bench/efficiency.ts` is pure
8
+ * (no I/O, no env reads); this CLI is the thin operator wrapper that
9
+ * supplies the standard task profiles + auto-fan-out report and emits
10
+ * the JSON / human-readable output.
11
+ *
12
+ * Subcommands:
13
+ *
14
+ * bizar bench default: run all benchmarks
15
+ * bizar bench single-vs-multi compare fanOut=1 vs fanOut=4
16
+ * bizar bench sequential-vs-parallel compare serial vs parallel DAG
17
+ * bizar bench reviewers compare 0/1/2 reviewer gates
18
+ * bizar bench worktree compare worktree overhead vs saved conflict time
19
+ * bizar bench recommend-fan-out apply auto-fan-out rule to a context
20
+ * bizar bench --format=json|human output format (default: human)
21
+ * bizar bench --seed=N deterministic seed (default: 42)
22
+ *
23
+ * Why this exists: the audit calls out six required comparisons. We
24
+ * ship five as built-in subcommands; the sixth (research depth) is
25
+ * operator-configurable via `--tasks=<file.json>` and is intentionally
26
+ * left to workflow owners to specialize. The auto-fan-out report is
27
+ * the audit's "automatically reduce fan-out when coordination
28
+ * overhead exceeds expected benefit" rule, surfaced as a runnable
29
+ * command rather than buried in a router.
30
+ */
31
+
32
+ import { readFileSync } from 'node:fs';
33
+ import {
34
+ EFFICIENCY_BENCH_SCHEMA_VERSION,
35
+ AUTO_REDUCTION_SCHEMA_VERSION,
36
+ runBench,
37
+ compareConfigurations,
38
+ recommendFanOut,
39
+ } from '../../packages/sdk/dist/bench/index.js';
40
+
41
+ const REPO_ROOT = process.cwd();
42
+
43
+ /**
44
+ * Default synthetic workload: a 4-task chain where each task is
45
+ * roughly independent. Matches a typical "implement a feature" pass
46
+ * (research → design → implement → verify).
47
+ */
48
+ function defaultTasks() {
49
+ return [
50
+ { id: 'research', workMs: 4_000, costUsd: 12_000, successProb: 0.95, quality: 0.80 },
51
+ { id: 'design', workMs: 3_000, costUsd: 10_000, successProb: 0.90, quality: 0.85 },
52
+ { id: 'implement',workMs: 8_000, costUsd: 24_000, successProb: 0.85, quality: 0.75 },
53
+ { id: 'verify', workMs: 3_000, costUsd: 8_000, successProb: 0.92, quality: 0.88 },
54
+ ];
55
+ }
56
+
57
+ function loadTasks(args) {
58
+ const tasksFlag = args.find((a) => a.startsWith('--tasks='));
59
+ if (tasksFlag) {
60
+ const path = tasksFlag.slice('--tasks='.length);
61
+ const abs = path.startsWith('/') ? path : `${REPO_ROOT}/${path}`;
62
+ const raw = JSON.parse(readFileSync(abs, 'utf8'));
63
+ if (!Array.isArray(raw)) {
64
+ throw new TypeError('--tasks=<file.json> must contain a JSON array');
65
+ }
66
+ return raw;
67
+ }
68
+ return defaultTasks();
69
+ }
70
+
71
+ function seedOf(args) {
72
+ const sFlag = args.find((a) => a.startsWith('--seed='));
73
+ return sFlag ? Number(sFlag.slice('--seed='.length)) : 42;
74
+ }
75
+
76
+ function formatOf(args) {
77
+ const fFlag = args.find((a) => a.startsWith('--format='));
78
+ return fFlag ? fFlag.slice('--format='.length) : 'human';
79
+ }
80
+
81
+ /** Configurations for the single-vs-multi comparison. */
82
+ function configsSingleVsMulti() {
83
+ return [
84
+ { label: 'single-agent', fanOut: 1, reviewerCount: 1, parallel: false, coordinationOverheadRatio: 0.0 },
85
+ { label: 'multi-agent-par', fanOut: 4, reviewerCount: 1, parallel: true, coordinationOverheadRatio: 0.15 },
86
+ ];
87
+ }
88
+
89
+ function configsSequentialVsParallel() {
90
+ return [
91
+ { label: 'sequential', fanOut: 1, reviewerCount: 1, parallel: false, coordinationOverheadRatio: 0.0 },
92
+ { label: 'parallel-dag', fanOut: 4, reviewerCount: 1, parallel: true, coordinationOverheadRatio: 0.10 },
93
+ ];
94
+ }
95
+
96
+ function configsReviewers() {
97
+ return [
98
+ { label: 'no-reviewers', fanOut: 1, reviewerCount: 0, parallel: false, coordinationOverheadRatio: 0.0 },
99
+ { label: '1-reviewer', fanOut: 1, reviewerCount: 1, parallel: false, coordinationOverheadRatio: 0.0 },
100
+ { label: '2-reviewers', fanOut: 1, reviewerCount: 2, parallel: false, coordinationOverheadRatio: 0.0 },
101
+ ];
102
+ }
103
+
104
+ function configsWorktree() {
105
+ // 4 workers with and without worktree overhead.
106
+ return [
107
+ { label: 'no-worktree', fanOut: 4, reviewerCount: 1, parallel: true, coordinationOverheadRatio: 0.10 },
108
+ { label: 'with-worktree', fanOut: 4, reviewerCount: 1, parallel: true, coordinationOverheadRatio: 0.35 },
109
+ ];
110
+ }
111
+
112
+ function runComparison(tasks, configs, seed) {
113
+ const [a, b] = configs;
114
+ return compareConfigurations(tasks, a, b, seed);
115
+ }
116
+
117
+ function benchSingleVsMulti(tasks, seed) {
118
+ return runComparison(tasks, configsSingleVsMulti(), seed);
119
+ }
120
+
121
+ function benchSequentialVsParallel(tasks, seed) {
122
+ return runComparison(tasks, configsSequentialVsParallel(), seed);
123
+ }
124
+
125
+ function benchReviewers(tasks, seed) {
126
+ const configs = configsReviewers();
127
+ return configs.map((c) => runBench(tasks, c, seed));
128
+ }
129
+
130
+ function benchWorktree(tasks, seed) {
131
+ return runComparison(tasks, configsWorktree(), seed);
132
+ }
133
+
134
+ /**
135
+ * Render a single BenchResult as a fixed-width row.
136
+ */
137
+ function formatRow(r) {
138
+ const cost = Number.isFinite(r.costPerVerifiedUsd)
139
+ ? (r.costPerVerifiedUsd / 1_000_000).toFixed(4) + ' USD'
140
+ : '∞';
141
+ const wall = Number.isFinite(r.wallClockPerVerifiedMs)
142
+ ? r.wallClockPerVerifiedMs.toFixed(0) + ' ms'
143
+ : '∞';
144
+ return [
145
+ r.configLabel.padEnd(22),
146
+ `fanOut=${String(r.fanOut).padEnd(2)}`,
147
+ `reviewers=${r.reviewerCount}`,
148
+ `verified=${String(r.verifiedOutcomes).padEnd(2)}/${r.totalAttempts}`,
149
+ `cost/v=${cost.padStart(10)}`,
150
+ `wall/v=${wall.padStart(10)}`,
151
+ ].join(' ');
152
+ }
153
+
154
+ function renderHumanSingleVsMulti(comparison) {
155
+ const out = [];
156
+ out.push(' single-agent vs multi-agent (audit #85)');
157
+ out.push('');
158
+ out.push(' ' + formatRow(comparison.a));
159
+ out.push(' ' + formatRow(comparison.b));
160
+ out.push('');
161
+ out.push(
162
+ ` cost ratio (a/b): ${comparison.costRatioAOverB.toFixed(2)} wall ratio (a/b): ${comparison.wallClockRatioAOverB.toFixed(2)}`,
163
+ );
164
+ out.push(
165
+ ` winner by cost: ${comparison.costWinner} winner by wall-clock: ${comparison.wallClockWinner}`,
166
+ );
167
+ return out.join('\n');
168
+ }
169
+
170
+ function renderHumanSequentialVsParallel(comparison) {
171
+ const out = [];
172
+ out.push(' sequential vs parallel DAG (audit #85)');
173
+ out.push('');
174
+ out.push(' ' + formatRow(comparison.a));
175
+ out.push(' ' + formatRow(comparison.b));
176
+ out.push('');
177
+ out.push(
178
+ ` wall-clock speedup (a/b): ${comparison.wallClockRatioAOverB.toFixed(2)} (lower = sequential is faster)`,
179
+ );
180
+ return out.join('\n');
181
+ }
182
+
183
+ function renderHumanReviewers(results) {
184
+ const out = [];
185
+ out.push(' reviewer count vs verified outcomes (audit #85)');
186
+ out.push('');
187
+ for (const r of results) {
188
+ out.push(' ' + formatRow(r));
189
+ }
190
+ out.push('');
191
+ out.push(' expectation: 0 reviewers = cheapest, highest defect rate;');
192
+ out.push(' 2 reviewers = lowest defect rate, ~2× reviewer cost.');
193
+ return out.join('\n');
194
+ }
195
+
196
+ function renderHumanWorktree(comparison) {
197
+ const out = [];
198
+ out.push(' worktree overhead vs saved conflict time (audit #85)');
199
+ out.push('');
200
+ out.push(' ' + formatRow(comparison.a));
201
+ out.push(' ' + formatRow(comparison.b));
202
+ out.push('');
203
+ out.push(
204
+ ` observed overhead ratios: a=${comparison.a.observedOverheadRatio.toFixed(2)} b=${comparison.b.observedOverheadRatio.toFixed(2)}`,
205
+ );
206
+ out.push(
207
+ ` verdict: ${comparison.a.totalWallClockMs <= comparison.b.totalWallClockMs ? 'no-worktree' : 'with-worktree'} wins on wall-clock`,
208
+ );
209
+ return out.join('\n');
210
+ }
211
+
212
+ function renderHumanAutoReduction(decision, ctx) {
213
+ const out = [];
214
+ out.push(' recommend-fan-out (auto-reduction rule, audit #85)');
215
+ out.push('');
216
+ out.push(` context: fanOut=${ctx.currentFanOut} overheadRatio=${ctx.historicalCoordinationOverheadRatio} expectedBenefitRatio=${ctx.expectedBenefitRatio} defectEscapeRate=${ctx.historicalDefectEscapeRate}`);
217
+ out.push(` recommended fan-out: ${decision.recommendedFanOut}`);
218
+ out.push(` reason: ${decision.reason}`);
219
+ out.push(` overhead-vs-benefit: ${decision.overheadVsBenefitGap.toFixed(3)}`);
220
+ out.push(` reduced: ${decision.reduced}`);
221
+ out.push('');
222
+ out.push(' rule:');
223
+ out.push(' defectEscapeRate > 0.20 → verifier-cannot-certify (fan-out unchanged; widen reviewers)');
224
+ out.push(' overheadRatio >= benefit → reduce by one step (single-worker-fan-out at floor)');
225
+ out.push(' otherwise → keep-current');
226
+ return out.join('\n');
227
+ }
228
+
229
+ function parseFlags(args) {
230
+ const flags = {};
231
+ for (let i = 0; i < args.length; i++) {
232
+ const arg = args[i];
233
+ if (arg.startsWith('--')) {
234
+ const key = arg.slice(2);
235
+ const eq = key.indexOf('=');
236
+ if (eq >= 0) {
237
+ flags[key.slice(0, eq)] = key.slice(eq + 1);
238
+ } else {
239
+ const next = args[i + 1];
240
+ if (!next || next.startsWith('--')) flags[key] = true;
241
+ else { flags[key] = next; i++; }
242
+ }
243
+ }
244
+ }
245
+ return flags;
246
+ }
247
+
248
+ function parseAutoReductionFlags(args) {
249
+ // bizar bench recommend-fan-out --fan-out=N --overhead=R --benefit=R --escape=R
250
+ const flags = parseFlags(args);
251
+ const fanOut = Number(flags['fan-out'] ?? flags.fanOut ?? 4);
252
+ const overhead = Number(flags.overhead ?? 0.5);
253
+ const benefit = Number(flags.benefit ?? 0.6);
254
+ const escape = Number(flags.escape ?? 0.05);
255
+ return {
256
+ currentFanOut: fanOut,
257
+ historicalCoordinationOverheadRatio: overhead,
258
+ expectedBenefitRatio: benefit,
259
+ historicalDefectEscapeRate: escape,
260
+ };
261
+ }
262
+
263
+ export const USAGE = `
264
+ bizar bench — audit #85 efficiency benchmarks + auto-fan-out rule
265
+
266
+ Usage:
267
+ bizar bench [single-vs-multi | sequential-vs-parallel | reviewers | worktree]
268
+ [--format=json|human] [--seed=N] [--tasks=<file.json>]
269
+ bizar bench recommend-fan-out --fan-out=N --overhead=R --benefit=R --escape=R
270
+ [--format=json|human]
271
+
272
+ Subcommands (default: run all four):
273
+ single-vs-multi fanOut=1 sequential vs fanOut=4 parallel
274
+ sequential-vs-parallel serial 4-task chain vs parallel 4-task DAG
275
+ reviewers 0/1/2 reviewer gates on the same task set
276
+ worktree coordination overhead 0.10 vs 0.35
277
+
278
+ recommend-fan-out:
279
+ bizar bench recommend-fan-out --fan-out=4 --overhead=0.5 --benefit=0.6 --escape=0.05
280
+ Applies the audit's "automatically reduce fan-out when coordination
281
+ overhead exceeds expected benefit" rule.
282
+
283
+ Schema versions:
284
+ bench/efficiency.ts ${EFFICIENCY_BENCH_SCHEMA_VERSION}
285
+ bench/auto-reduction.ts ${AUTO_REDUCTION_SCHEMA_VERSION}
286
+ `;
287
+
288
+ export async function run(subargs) {
289
+ if (subargs.includes('--help') || subargs.includes('-h')) {
290
+ console.log(USAGE);
291
+ return 0;
292
+ }
293
+
294
+ const subcommand = subargs.find((a) => !a.startsWith('--'));
295
+
296
+ if (subcommand === 'recommend-fan-out') {
297
+ const ctx = parseAutoReductionFlags(subargs);
298
+ const decision = recommendFanOut(ctx);
299
+ if (formatOf(subargs) === 'json') {
300
+ console.log(JSON.stringify({ context: ctx, decision, schemaVersion: AUTO_REDUCTION_SCHEMA_VERSION }, null, 2));
301
+ } else {
302
+ console.log(renderHumanAutoReduction(decision, ctx));
303
+ }
304
+ return 0;
305
+ }
306
+
307
+ const tasks = loadTasks(subargs);
308
+ const seed = seedOf(subargs);
309
+ const format = formatOf(subargs);
310
+ const runOne = subcommand === undefined;
311
+ const want = (name) => runOne || subcommand === name;
312
+
313
+ const out = { schemaVersion: EFFICIENCY_BENCH_SCHEMA_VERSION, generatedAt: new Date().toISOString() };
314
+
315
+ if (want('single-vs-multi')) {
316
+ out.singleVsMulti = benchSingleVsMulti(tasks, seed);
317
+ }
318
+ if (want('sequential-vs-parallel')) {
319
+ out.sequentialVsParallel = benchSequentialVsParallel(tasks, seed);
320
+ }
321
+ if (want('reviewers')) {
322
+ out.reviewers = benchReviewers(tasks, seed);
323
+ }
324
+ if (want('worktree')) {
325
+ out.worktree = benchWorktree(tasks, seed);
326
+ }
327
+
328
+ if (Object.keys(out).length === 2) {
329
+ console.log(USAGE);
330
+ return 1;
331
+ }
332
+
333
+ if (format === 'json') {
334
+ console.log(JSON.stringify(out, null, 2));
335
+ } else {
336
+ console.log(`bizar bench — audit #85 (generated ${out.generatedAt}, seed=${seed})`);
337
+ console.log('');
338
+ if (out.singleVsMulti) {
339
+ console.log(renderHumanSingleVsMulti(out.singleVsMulti));
340
+ console.log('');
341
+ }
342
+ if (out.sequentialVsParallel) {
343
+ console.log(renderHumanSequentialVsParallel(out.sequentialVsParallel));
344
+ console.log('');
345
+ }
346
+ if (out.reviewers) {
347
+ console.log(renderHumanReviewers(out.reviewers));
348
+ console.log('');
349
+ }
350
+ if (out.worktree) {
351
+ console.log(renderHumanWorktree(out.worktree));
352
+ console.log('');
353
+ }
354
+ }
355
+ return 0;
356
+ }
357
+
358
+ if (import.meta.url === `file://${process.argv[1]}`) {
359
+ run(process.argv.slice(2)).then((code) => process.exit(code));
360
+ }