@polderlabs/bizar 10.18.0 → 10.19.1
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.
- package/cli/bin.mjs +75 -2
- package/cli/commands/bench.mjs +360 -0
- package/cli/commands/explain-run.mjs +278 -0
- package/cli/commands/objective-scheduler.mjs +609 -0
- package/cli/commands/release-provenance.mjs +261 -0
- package/cli/commands/spec-list.mjs +225 -0
- package/cli/commands/verify-release.mjs +104 -0
- package/cli/cost-gate.mjs +323 -0
- package/config/claude/hooks/permission-request.mjs +176 -11
- package/package.json +1 -1
- package/packages/sdk/dist/autonomy/evidence-bundle.d.ts +10 -1
- package/packages/sdk/dist/autonomy/evidence-bundle.js +8 -0
- package/packages/sdk/dist/autonomy/index.d.ts +3 -3
- package/packages/sdk/dist/autonomy/index.js +3 -3
- package/packages/sdk/dist/autonomy/objective-run.d.ts +17 -0
- package/packages/sdk/dist/autonomy/objective-run.js +28 -0
- package/packages/sdk/dist/autonomy/outcome-record.d.ts +9 -0
- package/packages/sdk/dist/autonomy/outcome-record.js +8 -0
- package/packages/sdk/dist/bench/auto-reduction.d.ts +91 -0
- package/packages/sdk/dist/bench/auto-reduction.js +130 -0
- package/packages/sdk/dist/bench/efficiency.d.ts +174 -0
- package/packages/sdk/dist/bench/efficiency.js +202 -0
- package/packages/sdk/dist/bench/index.d.ts +15 -0
- package/packages/sdk/dist/bench/index.js +13 -0
- package/packages/sdk/dist/index.d.ts +2 -0
- package/packages/sdk/dist/index.js +4 -0
- package/packages/sdk/dist/release/index.d.ts +20 -0
- package/packages/sdk/dist/release/index.js +20 -0
- package/packages/sdk/dist/release/known-good-releases.d.ts +101 -0
- package/packages/sdk/dist/release/known-good-releases.js +200 -0
- package/packages/sdk/dist/release/provenance.d.ts +82 -0
- package/packages/sdk/dist/release/provenance.js +62 -0
- package/packages/sdk/dist/release/sbom.d.ts +77 -0
- package/packages/sdk/dist/release/sbom.js +114 -0
- package/packages/sdk/dist/release/signature.d.ts +84 -0
- package/packages/sdk/dist/release/signature.js +139 -0
- package/packages/sdk/dist/version.d.ts +1 -1
- package/packages/sdk/dist/version.js +1 -1
- 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)
|
|
@@ -188,6 +192,9 @@ async function main() {
|
|
|
188
192
|
// Pass --help to the command. Commands dispatched through util.mjs
|
|
189
193
|
// (audit, init, export, doctor, backup, restore, etc.) don't have
|
|
190
194
|
// their own cli/commands/<name>.mjs — they all live in util.mjs.
|
|
195
|
+
// Direct command modules (bench, release-provenance, verify-release,
|
|
196
|
+
// spec-list, …) have their own --help handling and accept
|
|
197
|
+
// run(cmdArgs); they fall through to the switch below.
|
|
191
198
|
const UTIL_COMMANDS = new Set([
|
|
192
199
|
'audit', 'init', 'export', 'test-gate',
|
|
193
200
|
'doctor', 'repair', 'heads-up', 'backup', 'restore',
|
|
@@ -204,8 +211,6 @@ async function main() {
|
|
|
204
211
|
const { runMigrate } = await import('./migrate.mjs');
|
|
205
212
|
await runMigrate(['--help']);
|
|
206
213
|
return;
|
|
207
|
-
} else {
|
|
208
|
-
mod = await importCommand(cmd);
|
|
209
214
|
}
|
|
210
215
|
if (mod && typeof mod.run === 'function') {
|
|
211
216
|
await mod.run(cmd, cmdArgs, true);
|
|
@@ -447,6 +452,58 @@ async function main() {
|
|
|
447
452
|
break;
|
|
448
453
|
}
|
|
449
454
|
|
|
455
|
+
case 'release-provenance': {
|
|
456
|
+
const mod = await importCommand('release-provenance');
|
|
457
|
+
if (!mod) {
|
|
458
|
+
console.error(chalk.red(` ✗ Could not load release-provenance command module`));
|
|
459
|
+
process.exit(EXIT_ERROR);
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
dbg('loaded command module:', 'release-provenance');
|
|
463
|
+
const code = await mod.run(cmdArgs);
|
|
464
|
+
if (typeof code === 'number') process.exit(code);
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
case 'verify-release': {
|
|
469
|
+
const mod = await importCommand('verify-release');
|
|
470
|
+
if (!mod) {
|
|
471
|
+
console.error(chalk.red(` ✗ Could not load verify-release command module`));
|
|
472
|
+
process.exit(EXIT_ERROR);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
dbg('loaded command module:', 'verify-release');
|
|
476
|
+
const code = await mod.run(cmdArgs);
|
|
477
|
+
if (typeof code === 'number') process.exit(code);
|
|
478
|
+
break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
case 'spec-list': {
|
|
482
|
+
const mod = await importCommand('spec-list');
|
|
483
|
+
if (!mod) {
|
|
484
|
+
console.error(chalk.red(` ✗ Could not load spec-list command module`));
|
|
485
|
+
process.exit(EXIT_ERROR);
|
|
486
|
+
return;
|
|
487
|
+
}
|
|
488
|
+
dbg('loaded command module:', 'spec-list');
|
|
489
|
+
const code = await mod.run(cmdArgs);
|
|
490
|
+
if (typeof code === 'number') process.exit(code);
|
|
491
|
+
break;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
case 'bench': {
|
|
495
|
+
const mod = await importCommand('bench');
|
|
496
|
+
if (!mod) {
|
|
497
|
+
console.error(chalk.red(` ✗ Could not load bench command module`));
|
|
498
|
+
process.exit(EXIT_ERROR);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
dbg('loaded command module:', 'bench');
|
|
502
|
+
const code = await mod.run(cmdArgs);
|
|
503
|
+
if (typeof code === 'number') process.exit(code);
|
|
504
|
+
break;
|
|
505
|
+
}
|
|
506
|
+
|
|
450
507
|
case 'improve': {
|
|
451
508
|
const mod = await importCommand('improve');
|
|
452
509
|
if (!mod) {
|
|
@@ -521,6 +578,22 @@ async function main() {
|
|
|
521
578
|
return;
|
|
522
579
|
}
|
|
523
580
|
|
|
581
|
+
case 'explain-run': {
|
|
582
|
+
const mod = await importCommand('explain-run');
|
|
583
|
+
if (!mod) {
|
|
584
|
+
console.error(chalk.red(` ✗ Could not load explain-run command module`));
|
|
585
|
+
process.exit(EXIT_ERROR);
|
|
586
|
+
return;
|
|
587
|
+
}
|
|
588
|
+
dbg('loaded command module:', 'explain-run');
|
|
589
|
+
const found = await mod.run(cmd, cmdArgs, isHelpRequest);
|
|
590
|
+
if (found === false) {
|
|
591
|
+
console.error(chalk.red(` ✗ Usage: bizar explain-run <id> — run 'bizar explain-run --help'`));
|
|
592
|
+
process.exit(EXIT_USAGE);
|
|
593
|
+
}
|
|
594
|
+
break;
|
|
595
|
+
}
|
|
596
|
+
|
|
524
597
|
default: {
|
|
525
598
|
console.error(chalk.red(` ✗ Unknown command: ${cmd}`));
|
|
526
599
|
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
|
+
}
|