@sabaiway/agent-workflow-kit 5.11.2 → 7.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 (38) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/README.md +3 -2
  3. package/SKILL.md +5 -1
  4. package/bridges/antigravity-cli-bridge/bin/agy-review-await-guard.test.mjs +176 -0
  5. package/bridges/antigravity-cli-bridge/bin/agy-review.sh +61 -14
  6. package/bridges/antigravity-cli-bridge/bin/agy-review.test.mjs +606 -467
  7. package/bridges/antigravity-cli-bridge/references/review-prompt.md +42 -4
  8. package/bridges/codex-cli-bridge/SKILL.md +18 -5
  9. package/bridges/codex-cli-bridge/bin/codex-await-guard.test.mjs +161 -0
  10. package/bridges/codex-cli-bridge/bin/codex-exec.sh +22 -17
  11. package/bridges/codex-cli-bridge/bin/codex-exec.test.mjs +356 -363
  12. package/bridges/codex-cli-bridge/bin/codex-review.sh +6 -6
  13. package/bridges/codex-cli-bridge/bin/codex-review.test.mjs +275 -286
  14. package/bridges/codex-cli-bridge/capability.json +1 -1
  15. package/bridges/codex-cli-bridge/references/driving-codex.md +4 -2
  16. package/bridges/codex-cli-bridge/references/sandbox-and-flags.md +3 -2
  17. package/bridges/codex-cli-bridge/setup/README.md +3 -1
  18. package/capability.json +1 -1
  19. package/package.json +1 -1
  20. package/references/hooks/gate-approve.mjs +1 -1
  21. package/references/modes/grounding.md +1 -1
  22. package/references/modes/mcp.md +37 -0
  23. package/references/modes/procedures.md +3 -3
  24. package/references/modes/recommendations.md +1 -0
  25. package/references/modes/uninstall.md +2 -1
  26. package/references/templates/agent_rules.md +4 -5
  27. package/tools/commands.mjs +7 -0
  28. package/tools/direct-run.mjs +3 -0
  29. package/tools/doc-parity.mjs +18 -2
  30. package/tools/grounding.mjs +10 -20
  31. package/tools/inject-methodology.mjs +2 -0
  32. package/tools/mcp-registration.mjs +283 -0
  33. package/tools/mcp-server.mjs +314 -0
  34. package/tools/mcp-stdio.mjs +229 -0
  35. package/tools/mcp.mjs +299 -0
  36. package/tools/procedures.mjs +7 -8
  37. package/tools/recommendations.mjs +90 -1
  38. package/tools/uninstall.mjs +356 -45
@@ -223,7 +223,12 @@ const makeSandbox = ({ clean = false } = {}) => {
223
223
  // Capture files are per-INVOCATION: a second run() on the same sandbox must not inherit the first
224
224
  // run's turn counter or per-turn prompt files (the fed lane reads them back by turn index).
225
225
  let runSeq = 0;
226
- const run = (sb, { args, env = {}, cwd, wrapper } = {}) => {
226
+ // ASYNCHRONOUS on purpose. A blocking spawnSync here holds the event loop for the whole dispatch,
227
+ // which pinned this file to ONE core: 208 points in a serial chain, 91.4s solo at 103% CPU while
228
+ // the other seven cores idled. Awaiting the child instead lets a `{ concurrency }` describe
229
+ // overlap its tests. The per-test environment still rides the CHILD's options — `process.env` is
230
+ // never mutated, which is what keeps overlapping tests from reading each other's PATH.
231
+ const run = (sb, { args, env = {}, cwd, wrapper } = {}) => new Promise((settle) => {
227
232
  const { home, bin, repo } = sb;
228
233
  const farm = farmFor(['agy', 'agy-run']);
229
234
  const tag = `cap-${++runSeq}`;
@@ -234,10 +239,11 @@ const run = (sb, { args, env = {}, cwd, wrapper } = {}) => {
234
239
  artifactCopy: join(home, `${tag}-artifact-copy`), turns: join(home, `${tag}-turns`),
235
240
  dispatchCwd: join(home, `${tag}-dispatch-cwd`),
236
241
  };
237
- const r = spawnSync('bash', [wrapper || WRAPPER, ...args], {
242
+ const child = execFile('bash', [wrapper || WRAPPER, ...args], {
238
243
  cwd: cwd || repo,
239
244
  encoding: 'utf8',
240
245
  timeout: 30000,
246
+ maxBuffer: 64 * 1024 * 1024,
241
247
  env: {
242
248
  HOME: home,
243
249
  PATH: `${bin}:${farm}`,
@@ -250,68 +256,42 @@ const run = (sb, { args, env = {}, cwd, wrapper } = {}) => {
250
256
  AGY_FAKE_TURNS: cap.turns, AGY_FAKE_CWD: cap.dispatchCwd,
251
257
  ...env,
252
258
  },
253
- });
254
- const readIf = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : '');
255
- // Per-turn captures are read EAGERLY: callers rmSync the sandbox before asserting.
256
- const turns = existsSync(cap.turns) ? Number(readFileSync(cap.turns, 'utf8')) : 0;
257
- const prompts = [];
258
- const argvs = [];
259
- for (let i = 1; i <= turns; i += 1) {
260
- prompts.push(readIf(`${cap.prompt}.${i}`));
261
- argvs.push(readIf(`${cap.argv}.${i}`));
262
- }
263
- return {
264
- ...r,
265
- invoked: existsSync(cap.sentinel),
266
- argv: readIf(cap.argv), capEnv: readIf(cap.env), prompt: readIf(cap.prompt),
267
- adddir: readIf(cap.adddir).trim(), adddirMode: readIf(cap.adddirMode).trim(),
268
- artifactMode: readIf(cap.artifactMode).trim(), artifactCopy: readIf(cap.artifactCopy),
269
- dispatchCwd: readIf(cap.dispatchCwd).trim(), turns, prompts, argvs,
270
- };
271
- };
272
-
273
- // Async twin of run() for the two sleep-bound timeout tests: spawnSync blocks the event loop
274
- // for the whole deliberate wait, so a concurrent describe could not overlap them. Same spawn
275
- // contract and captures.
276
- const runAsync = (sb, { args, env = {}, cwd } = {}) =>
277
- new Promise((done) => {
278
- const { home, bin, repo } = sb;
279
- const cap = {
280
- argv: join(home, 'cap-argv'), env: join(home, 'cap-env'), prompt: join(home, 'cap-prompt'),
281
- sentinel: join(home, 'cap-sentinel'), adddir: join(home, 'cap-adddir'),
282
- adddirMode: join(home, 'cap-adddir-mode'), artifactMode: join(home, 'cap-artifact-mode'),
283
- artifactCopy: join(home, 'cap-artifact-copy'),
284
- };
285
- const child = execFile('bash', [WRAPPER, ...args], {
286
- cwd: cwd || repo,
287
- encoding: 'utf8',
288
- timeout: 30000,
289
- env: {
290
- HOME: home,
291
- PATH: `${bin}:${farmFor(['agy', 'agy-run'])}`,
292
- TMPDIR: process.env.TMPDIR ?? '/tmp',
293
- AGY_FAKE_ARGV: cap.argv, AGY_FAKE_ENV: cap.env, AGY_FAKE_PROMPT: cap.prompt,
294
- AGY_FAKE_SENTINEL: cap.sentinel, AGY_FAKE_ADDDIR: cap.adddir, AGY_FAKE_ADDDIR_MODE: cap.adddirMode,
295
- AGY_FAKE_ARTIFACT_MODE: cap.artifactMode, AGY_FAKE_ARTIFACT_COPY: cap.artifactCopy,
296
- ...env,
297
- },
298
- }, (error, stdout, stderr) => {
299
- const readIf = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : '');
300
- done({
301
- status: error ? (error.code ?? 1) : 0, stdout, stderr,
302
- invoked: existsSync(cap.sentinel),
303
- argv: readIf(cap.argv), capEnv: readIf(cap.env), prompt: readIf(cap.prompt),
304
- adddir: readIf(cap.adddir).trim(), adddirMode: readIf(cap.adddirMode).trim(),
305
- artifactMode: readIf(cap.artifactMode).trim(), artifactCopy: readIf(cap.artifactCopy),
306
- });
259
+ }, (error, stdout, stderr) => {
260
+ const readIf = (p) => (existsSync(p) ? readFileSync(p, 'utf8') : '');
261
+ // Per-turn captures are read EAGERLY: callers rmSync the sandbox before asserting.
262
+ const turns = existsSync(cap.turns) ? Number(readFileSync(cap.turns, 'utf8')) : 0;
263
+ const prompts = [];
264
+ const argvs = [];
265
+ for (let i = 1; i <= turns; i += 1) {
266
+ prompts.push(readIf(`${cap.prompt}.${i}`));
267
+ argvs.push(readIf(`${cap.argv}.${i}`));
268
+ }
269
+ settle({
270
+ status: error ? (error.code ?? 1) : 0, signal: error?.signal ?? null, stdout, stderr,
271
+ invoked: existsSync(cap.sentinel),
272
+ argv: readIf(cap.argv), capEnv: readIf(cap.env), prompt: readIf(cap.prompt),
273
+ adddir: readIf(cap.adddir).trim(), adddirMode: readIf(cap.adddirMode).trim(),
274
+ artifactMode: readIf(cap.artifactMode).trim(), artifactCopy: readIf(cap.artifactCopy),
275
+ dispatchCwd: readIf(cap.dispatchCwd).trim(), turns, prompts, argvs,
307
276
  });
308
- child.stdin.end();
309
277
  });
278
+ // The wrapper refuses many inputs BEFORE it reads stdin, so the pipe can already be closed here.
279
+ // The blocking spawn swallowed that; an async one throws EPIPE at the test. A closed pipe is the
280
+ // refusal working — but ONLY EPIPE is: any other write failure is a real fault and must reach
281
+ // the test instead of passing as a green.
282
+ child.stdin.on('error', (err) => { if (err.code !== 'EPIPE') throw err; });
283
+ child.stdin.end();
284
+ });
310
285
 
311
- describe('agy-review.sh model policy advisory (1)', () => {
312
- it('warns for a non-frontier model but still runs', () => {
286
+ // runAsync was the async twin kept for the two sleep-bound timeout tests, back when run() blocked.
287
+ // run() IS that twin now, with the fuller capture surface, so the twin is one name pointing at it
288
+ // — two spawn paths could only drift.
289
+ const runAsync = run;
290
+
291
+ describe('agy-review.sh — model policy advisory (1)', { concurrency: 2 }, () => {
292
+ it('warns for a non-frontier model but still runs', async () => {
313
293
  const sb = makeSandbox();
314
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.5 Flash (Low)' } });
294
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.5 Flash (Low)' } });
315
295
  rmSync(sb.home, { recursive: true, force: true });
316
296
  assert.equal(r.status, 0, r.stderr);
317
297
  assert.match(r.stderr, /non-frontier model 'Gemini 3.5 Flash \(Low\)'/);
@@ -319,26 +299,26 @@ describe('agy-review.sh — model policy advisory (1)', () => {
319
299
  assert.match(r.argv, /Gemini 3\.5 Flash \(Low\)/, 'the chosen model reaches agy via --model');
320
300
  });
321
301
 
322
- it('AGY_PROBE=1 silences the advisory', () => {
302
+ it('AGY_PROBE=1 silences the advisory', async () => {
323
303
  const sb = makeSandbox();
324
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.5 Flash (Low)', AGY_PROBE: '1' } });
304
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.5 Flash (Low)', AGY_PROBE: '1' } });
325
305
  rmSync(sb.home, { recursive: true, force: true });
326
306
  assert.equal(r.status, 0, r.stderr);
327
307
  assert.doesNotMatch(r.stderr, /non-frontier model/);
328
308
  });
329
309
 
330
- it('the frontier default (no AGY_MODEL) earns no advisory', () => {
310
+ it('the frontier default (no AGY_MODEL) earns no advisory', async () => {
331
311
  const sb = makeSandbox();
332
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'] });
312
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'] });
333
313
  rmSync(sb.home, { recursive: true, force: true });
334
314
  assert.equal(r.status, 0, r.stderr);
335
315
  assert.doesNotMatch(r.stderr, /non-frontier model/);
336
316
  assert.match(r.argv, /Gemini 3\.7 Flash \(High\)/, 'the frontier default reaches agy');
337
317
  });
338
318
 
339
- it('an explicit Gemini 3.7 Flash (High) is FRONTIER — no advisory (fork (a), maintainer 2026-08-14)', () => {
319
+ it('an explicit Gemini 3.7 Flash (High) is FRONTIER — no advisory (fork (a), maintainer 2026-08-14)', async () => {
340
320
  const sb = makeSandbox();
341
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.7 Flash (High)' } });
321
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: 'Gemini 3.7 Flash (High)' } });
342
322
  rmSync(sb.home, { recursive: true, force: true });
343
323
  assert.equal(r.status, 0, r.stderr);
344
324
  assert.doesNotMatch(r.stderr, /non-frontier model/, 'a FRONTIER_SET member never warns');
@@ -354,11 +334,11 @@ describe('agy-review.sh — model policy advisory (1)', () => {
354
334
  // the invocation sentinel, so a probe can never read as a spent run.
355
335
  const FAKE_OLD_NODE = '#!/usr/bin/env bash\nprintf "v18.20.0\\n"\n';
356
336
 
357
- describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
337
+ describe('agy-review.sh — the pre-spend capability door (Decision 2)', { concurrency: 2 }, () => {
358
338
  for (const flag of ['--output-format', '--disable-slash-commands']) {
359
- it(`a --help that does not advertise ${flag} refuses pre-spend and names it`, () => {
339
+ it(`a --help that does not advertise ${flag} refuses pre-spend and names it`, async () => {
360
340
  const sb = makeSandbox();
361
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_OMIT: flag } });
341
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_OMIT: flag } });
362
342
  const receipts = readReceipts(sb.repo);
363
343
  rmSync(sb.home, { recursive: true, force: true });
364
344
  assert.notEqual(r.status, 0, r.stderr);
@@ -373,9 +353,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
373
353
  // is actually missing: a build lacking --disable-slash-commands answers perfectly readably and
374
354
  // corrupts the delivered BODY instead, which the envelope explanation would send an operator
375
355
  // hunting the wrong bug.
376
- it('the refusal names what the MISSING flag buys, not one blanket envelope explanation', () => {
356
+ it('the refusal names what the MISSING flag buys, not one blanket envelope explanation', async () => {
377
357
  const sb = makeSandbox();
378
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_OMIT: '--disable-slash-commands' } });
358
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_OMIT: '--disable-slash-commands' } });
379
359
  rmSync(sb.home, { recursive: true, force: true });
380
360
  assert.notEqual(r.status, 0, r.stderr);
381
361
  assert.equal(r.invoked, false);
@@ -383,9 +363,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
383
363
  assert.doesNotMatch(r.stderr, /cannot read/, 'the unreadable-answer cost belongs to --output-format alone');
384
364
  });
385
365
 
386
- it('`agy --help` exiting non-zero refuses with a DISTINCT cause — never read as "capability present"', () => {
366
+ it('`agy --help` exiting non-zero refuses with a DISTINCT cause — never read as "capability present"', async () => {
387
367
  const sb = makeSandbox();
388
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_EXIT: '3' } });
368
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_HELP_EXIT: '3' } });
389
369
  rmSync(sb.home, { recursive: true, force: true });
390
370
  assert.notEqual(r.status, 0, r.stderr);
391
371
  assert.equal(r.invoked, false, 'a failed probe never becomes a paid dispatch');
@@ -393,9 +373,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
393
373
  assert.doesNotMatch(r.stderr, /does not advertise the flag/, 'a failed probe is not a missing-flag verdict');
394
374
  });
395
375
 
396
- it('node ABSENT refuses pre-spend, naming Node and the floor', () => {
376
+ it('node ABSENT refuses pre-spend, naming Node and the floor', async () => {
397
377
  const sb = makeSandbox();
398
- const r = run(sb, {
378
+ const r = await run(sb, {
399
379
  args: ['code', '--facts', 'f'],
400
380
  env: { PATH: `${sb.bin}:${farmFor(['agy', 'agy-run', 'node'])}` },
401
381
  });
@@ -406,11 +386,11 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
406
386
  assert.match(r.stderr, /Node >= 22/);
407
387
  });
408
388
 
409
- it('node PRESENT but BELOW the floor refuses pre-spend — the version comparison really runs', () => {
389
+ it('node PRESENT but BELOW the floor refuses pre-spend — the version comparison really runs', async () => {
410
390
  const sb = makeSandbox();
411
391
  // Into $HOME/.local/bin, which the wrapper itself prepends — so the fake wins over the real node.
412
392
  writeFileSync(join(sb.bin, 'node'), FAKE_OLD_NODE, { mode: 0o755 });
413
- const r = run(sb, { args: ['code', '--facts', 'f'] });
393
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
414
394
  rmSync(sb.home, { recursive: true, force: true });
415
395
  assert.notEqual(r.status, 0, r.stderr);
416
396
  assert.equal(r.invoked, false, 'an existence-only check would have dispatched here');
@@ -427,9 +407,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
427
407
  ['the flag named inside ANOTHER option`s description', ' --json-schema enforce structured output; see --output-format'],
428
408
  ['another option whose description BEGINS with the flag name', ' --other --output-format is unsupported on this build'],
429
409
  ]) {
430
- it(`${name} does NOT open the door — an option declaration is required`, () => {
410
+ it(`${name} does NOT open the door — an option declaration is required`, async () => {
431
411
  const sb = makeSandbox();
432
- const r = run(sb, {
412
+ const r = await run(sb, {
433
413
  args: ['code', '--facts', 'f'],
434
414
  env: { AGY_FAKE_HELP_OMIT: '--output-format', AGY_FAKE_HELP_EXTRA: extraHelpLine },
435
415
  });
@@ -449,9 +429,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
449
429
  ['an =<value> rendering', ' --output-format=<fmt> the joined-value rendering'],
450
430
  ['a declaration with no description at all', ' --output-format'],
451
431
  ]) {
452
- it(`${name} READS as declared — a capable build is never falsely refused`, () => {
432
+ it(`${name} READS as declared — a capable build is never falsely refused`, async () => {
453
433
  const sb = makeSandbox();
454
- const r = run(sb, {
434
+ const r = await run(sb, {
455
435
  args: ['code', '--facts', 'f'],
456
436
  env: { AGY_FAKE_HELP_OMIT: '--output-format', AGY_FAKE_HELP_EXTRA: extraHelpLine },
457
437
  });
@@ -461,9 +441,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
461
441
  });
462
442
  }
463
443
 
464
- it('the agy-capability refusal carries the INSTALLED version and the upgrade command', () => {
444
+ it('the agy-capability refusal carries the INSTALLED version and the upgrade command', async () => {
465
445
  const sb = makeSandbox();
466
- const r = run(sb, {
446
+ const r = await run(sb, {
467
447
  args: ['code', '--facts', 'f'],
468
448
  env: { AGY_FAKE_HELP_OMIT: '--output-format', AGY_FAKE_VERSION: '1.0.9' },
469
449
  });
@@ -473,9 +453,9 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
473
453
  assert.match(r.stderr, /agy update/, 'and the command that fixes it');
474
454
  });
475
455
 
476
- it('a capable host passes the door and still dispatches (the door is not a blanket refusal)', () => {
456
+ it('a capable host passes the door and still dispatches (the door is not a blanket refusal)', async () => {
477
457
  const sb = makeSandbox();
478
- const r = run(sb, { args: ['code', '--facts', 'f'] });
458
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
479
459
  rmSync(sb.home, { recursive: true, force: true });
480
460
  assert.equal(r.status, 0, r.stderr);
481
461
  assert.equal(r.invoked, true);
@@ -487,10 +467,10 @@ describe('agy-review.sh — the pre-spend capability door (Decision 2)', () => {
487
467
  // change one byte of this: stdout carries the model's answer and NOTHING else (the posture banner
488
468
  // is stderr), and the receipt records the verdict parsed out of that same answer. Green before the
489
469
  // switch, green after — anything the transport quietly rewrites fails here.
490
- describe('agy-review.sh — the operator-visible answer, byte-for-byte (characterization)', () => {
491
- it('stdout is EXACTLY the model answer, and the receipt records the verdict parsed from it', () => {
470
+ describe('agy-review.sh — the operator-visible answer, byte-for-byte (characterization)', { concurrency: 2 }, () => {
471
+ it('stdout is EXACTLY the model answer, and the receipt records the verdict parsed from it', async () => {
492
472
  const sb = makeSandbox();
493
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
473
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
494
474
  const receipts = readReceipts(sb.repo);
495
475
  rmSync(sb.home, { recursive: true, force: true });
496
476
  assert.equal(r.status, 0, r.stderr);
@@ -500,7 +480,7 @@ describe('agy-review.sh — the operator-visible answer, byte-for-byte (characte
500
480
 
501
481
  // The bytes a transport is most likely to mangle: blank lines, non-ASCII, quotes, a backslash, a
502
482
  // tab. If the answer ever rides a JSON field, this is the test that catches a lossy round-trip.
503
- it('blank lines, multibyte, quotes, backslashes and tabs all survive to stdout unchanged', () => {
483
+ it('blank lines, multibyte, quotes, backslashes and tabs all survive to stdout unchanged', async () => {
504
484
  const answer = [
505
485
  '### Verdict',
506
486
  'SHIP — «clean», no nits.',
@@ -515,7 +495,7 @@ describe('agy-review.sh — the operator-visible answer, byte-for-byte (characte
515
495
  'none',
516
496
  ].join('\n');
517
497
  const sb = makeSandbox();
518
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: answer } });
498
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: answer } });
519
499
  const receipts = readReceipts(sb.repo);
520
500
  rmSync(sb.home, { recursive: true, force: true });
521
501
  assert.equal(r.status, 0, r.stderr);
@@ -528,7 +508,7 @@ describe('agy-review.sh — the operator-visible answer, byte-for-byte (characte
528
508
  // Every lane runs `--output-format json` and the wrapper reads the envelope's `response` instead of
529
509
  // raw stdout. The review CONTRACT does not move: the same prompt, the same mandated shape, the same
530
510
  // D4 arm. What changes is that a fact the wrapper used to guess at now arrives named.
531
- describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', () => {
511
+ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', { concurrency: 2 }, () => {
532
512
  // The transport is BOTH flags: the envelope carries the answer, and --disable-slash-commands keeps
533
513
  // a change-set line that happens to begin with a slash command as BODY rather than an instruction
534
514
  // the CLI expands. The pre-spend door requires both, so the dispatch must pass both — a door that
@@ -539,16 +519,16 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
539
519
  return at !== -1 && tokens[at + 1] === 'json' && tokens.includes('--disable-slash-commands');
540
520
  };
541
521
 
542
- it('all three lanes carry the transport flags (single, fed, resume)', () => {
522
+ it('all three lanes carry the transport flags (single, fed, resume)', async () => {
543
523
  const single = makeSandbox();
544
- const one = run(single, { args: ['code', '--facts', 'f'] });
524
+ const one = await run(single, { args: ['code', '--facts', 'f'] });
545
525
  rmSync(single.home, { recursive: true, force: true });
546
526
  assert.equal(one.status, 0, one.stderr);
547
527
  assert.ok(argvCarriesTransport(one.argv), `single lane argv: ${one.argv}`);
548
528
 
549
529
  const fedSb = makeSandbox();
550
530
  seedFedChangeSet(fedSb);
551
- const fed = fedRun(fedSb);
531
+ const fed = await fedRun(fedSb);
552
532
  rmSync(fedSb.home, { recursive: true, force: true });
553
533
  assert.equal(fed.status, 0, fed.stderr);
554
534
  assert.ok(fed.argvs.length >= 3, 'the fixture really chunks');
@@ -557,16 +537,16 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
557
537
  }
558
538
 
559
539
  const resumeSb = makeSandbox();
560
- const resumed = run(resumeSb, { args: ['--continue'] });
540
+ const resumed = await run(resumeSb, { args: ['--continue'] });
561
541
  rmSync(resumeSb.home, { recursive: true, force: true });
562
542
  assert.equal(resumed.status, 0, resumed.stderr);
563
543
  assert.ok(argvCarriesTransport(resumed.argv), `resume lane argv: ${resumed.argv}`);
564
544
  });
565
545
 
566
- it('operator-visible stdout IS the envelope response — and a feed turn`s response is neither published nor parsed', () => {
546
+ it('operator-visible stdout IS the envelope response — and a feed turn`s response is neither published nor parsed', async () => {
567
547
  const sb = makeSandbox();
568
548
  seedFedChangeSet(sb);
569
- const fed = fedRun(sb);
549
+ const fed = await fedRun(sb);
570
550
  const receipts = readReceipts(sb.repo);
571
551
  rmSync(sb.home, { recursive: true, force: true });
572
552
  assert.equal(fed.status, 0, fed.stderr);
@@ -577,9 +557,9 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
577
557
  assert.equal(receipts[0].verdict, 'SHIP', 'only the FINAL response is parsed');
578
558
  });
579
559
 
580
- it('a non-JSON blob on a ZERO exit is an UNREADABLE review: distinct exit, NO receipt, the cause names the envelope', () => {
560
+ it('a non-JSON blob on a ZERO exit is an UNREADABLE review: distinct exit, NO receipt, the cause names the envelope', async () => {
581
561
  const sb = makeSandbox();
582
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_RAW_STDOUT: 'jetski: not an envelope at all\n' } });
562
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_RAW_STDOUT: 'jetski: not an envelope at all\n' } });
583
563
  const receipts = readReceipts(sb.repo);
584
564
  rmSync(sb.home, { recursive: true, force: true });
585
565
  assert.equal(r.status, 5, `an unreadable envelope has its OWN code, not the D4 4: ${r.stderr}`);
@@ -588,9 +568,9 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
588
568
  assert.match(r.stderr, /not a readable agy JSON envelope/, 'and the wrapper says what that means');
589
569
  });
590
570
 
591
- it('a non-SUCCESS status is unreadable too — its own named cause, NO receipt', () => {
571
+ it('a non-SUCCESS status is unreadable too — its own named cause, NO receipt', async () => {
592
572
  const sb = makeSandbox();
593
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_STATUS: 'ERROR' } });
573
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_STATUS: 'ERROR' } });
594
574
  const receipts = readReceipts(sb.repo);
595
575
  rmSync(sb.home, { recursive: true, force: true });
596
576
  assert.equal(r.status, 5, r.stderr);
@@ -600,9 +580,9 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
600
580
 
601
581
  // Decision 4: the CLI's own failure WINS. The envelope is parsed only on a zero exit, so a
602
582
  // non-zero run keeps its code and its message and never has a parse error layered over it.
603
- it('a non-zero CLI exit keeps its code and message on the SINGLE lane — no envelope error replaces them', () => {
583
+ it('a non-zero CLI exit keeps its code and message on the SINGLE lane — no envelope error replaces them', async () => {
604
584
  const sb = makeSandbox();
605
- const r = run(sb, {
585
+ const r = await run(sb, {
606
586
  args: ['code', '--facts', 'f'],
607
587
  env: { AGY_FAKE_EXIT: '7', AGY_FAKE_RAW_STDOUT: 'partial plain text\n', AGY_FAKE_STDERR: 'AGY_CLI_OWN_FAILURE' },
608
588
  });
@@ -615,10 +595,10 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
615
595
  assert.equal(receipts.length, 0);
616
596
  });
617
597
 
618
- it('a non-zero CLI exit keeps its code and message on the FED lane too', () => {
598
+ it('a non-zero CLI exit keeps its code and message on the FED lane too', async () => {
619
599
  const sb = makeSandbox();
620
600
  seedFedChangeSet(sb);
621
- const fed = fedRun(sb, { AGY_FAKE_FAIL_TURN: '2' });
601
+ const fed = await fedRun(sb, { AGY_FAKE_FAIL_TURN: '2' });
622
602
  const receipts = readReceipts(sb.repo);
623
603
  rmSync(sb.home, { recursive: true, force: true });
624
604
  assert.equal(fed.status, 3, 'the failing turn`s own exit code survives');
@@ -630,9 +610,9 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
630
610
 
631
611
  // The switch must not move WHICH arm fires: a readable envelope carrying a verdict-less answer is
632
612
  // still the D4 failed review, exit 4 — not the transport failure.
633
- it('a READABLE envelope with no verdict still exits 4 through the EXISTING D4 arm', () => {
613
+ it('a READABLE envelope with no verdict still exits 4 through the EXISTING D4 arm', async () => {
634
614
  const sb = makeSandbox();
635
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: 'prose without the mandated section' } });
615
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: 'prose without the mandated section' } });
636
616
  const receipts = readReceipts(sb.repo);
637
617
  rmSync(sb.home, { recursive: true, force: true });
638
618
  assert.equal(r.status, 4, 'the D4 arm owns this, not the envelope arm');
@@ -645,11 +625,11 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
645
625
  // (~/.local/bin/agy-review -> <placed>/bin/agy-review.sh). An unresolved BASH_SOURCE names the
646
626
  // LINK's directory, so every sibling payload — the envelope reader above all — would be looked
647
627
  // for beside the link and EVERY review would refuse pre-spend on a correct install.
648
- it('a review launched through a managed SYMLINK resolves its own sibling payload and still attests', () => {
628
+ it('a review launched through a managed SYMLINK resolves its own sibling payload and still attests', async () => {
649
629
  const sb = makeSandbox();
650
630
  const linked = join(sb.bin, 'agy-review');
651
631
  symlinkSync(WRAPPER, linked);
652
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], wrapper: linked });
632
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], wrapper: linked });
653
633
  const receipts = readReceipts(sb.repo);
654
634
  rmSync(sb.home, { recursive: true, force: true });
655
635
  assert.equal(r.status, 0, `a symlinked launch must behave identically: ${r.stderr}`);
@@ -663,7 +643,7 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
663
643
  // shape is an absurd CHAIN — and it is testable exactly because the kernel tolerates a far longer
664
644
  // chain than this bound. An unbounded walk spins until the harness kills it and prints nothing,
665
645
  // so the assertion is on the MESSAGE: an infinite loop can never produce it.
666
- it('a symlink chain past the hop bound refuses LOUDLY and spends nothing', () => {
646
+ it('a symlink chain past the hop bound refuses LOUDLY and spends nothing', async () => {
667
647
  const sb = makeSandbox();
668
648
  const chainDir = join(sb.home, 'link-chain');
669
649
  mkdirSync(chainDir, { recursive: true });
@@ -672,7 +652,7 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
672
652
  symlinkSync(previous, link);
673
653
  return link;
674
654
  }, WRAPPER);
675
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], wrapper: chainTip });
655
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], wrapper: chainTip });
676
656
  rmSync(sb.home, { recursive: true, force: true });
677
657
  assert.equal(r.status, 127, `the walk must refuse, not spin: ${r.stderr}`);
678
658
  assert.match(r.stderr, /exceeded 16 symlink hops/, 'the refusal names the bound it hit');
@@ -680,10 +660,10 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
680
660
  assert.equal(r.invoked, false, 'nothing is spent');
681
661
  });
682
662
 
683
- it('the resume lanes parse the envelope and mint the continuation receipt exactly as before', () => {
663
+ it('the resume lanes parse the envelope and mint the continuation receipt exactly as before', async () => {
684
664
  for (const args of [['--continue'], ['--conversation', 'conv-xyz']]) {
685
665
  const sb = makeSandbox();
686
- const r = run(sb, { args, env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
666
+ const r = await run(sb, { args, env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
687
667
  const receipts = readReceipts(sb.repo);
688
668
  rmSync(sb.home, { recursive: true, force: true });
689
669
  assert.equal(r.status, 0, `${args[0]}: ${r.stderr}`);
@@ -695,17 +675,17 @@ describe('agy-review.sh — the dispatch rides the JSON envelope (Phase 4.3)', (
695
675
  });
696
676
  });
697
677
 
698
- describe('agy-review.sh — guard + grounding (2, 3)', () => {
699
- it('the model/cutoff GUARD line is in the captured prompt', () => {
678
+ describe('agy-review.sh — guard + grounding (2, 3)', { concurrency: 2 }, () => {
679
+ it('the model/cutoff GUARD line is in the captured prompt', async () => {
700
680
  const sb = makeSandbox();
701
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'] });
681
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'] });
702
682
  rmSync(sb.home, { recursive: true, force: true });
703
683
  assert.match(r.prompt, /Do NOT comment on AI model names\/versions or your own knowledge cutoff/);
704
684
  });
705
685
 
706
- it('--facts / --decided / --focus all reach the prompt', () => {
686
+ it('--facts / --decided / --focus all reach the prompt', async () => {
707
687
  const sb = makeSandbox();
708
- const r = run(sb, { args: [
688
+ const r = await run(sb, { args: [
709
689
  'code', '--facts', 'GROUNDED_FACT_MARKER', '--decided', 'DECIDED_MARKER', '--focus', 'FOCUS_MARKER',
710
690
  ] });
711
691
  rmSync(sb.home, { recursive: true, force: true });
@@ -717,29 +697,29 @@ describe('agy-review.sh — guard + grounding (2, 3)', () => {
717
697
  assert.match(r.prompt, /## Focus\nFOCUS_MARKER/);
718
698
  });
719
699
 
720
- it('--facts @file reads the file; --decided @file too', () => {
700
+ it('--facts @file reads the file; --decided @file too', async () => {
721
701
  const sb = makeSandbox();
722
702
  writeFileSync(join(sb.repo, 'facts.md'), 'FILE_FACT_BODY\n');
723
703
  writeFileSync(join(sb.repo, 'decided.md'), 'FILE_DECIDED_BODY\n');
724
- const r = run(sb, { args: ['code', '--facts', '@facts.md', '--decided', '@decided.md'] });
704
+ const r = await run(sb, { args: ['code', '--facts', '@facts.md', '--decided', '@decided.md'] });
725
705
  rmSync(sb.home, { recursive: true, force: true });
726
706
  assert.equal(r.status, 0, r.stderr);
727
707
  assert.match(r.prompt, /FILE_FACT_BODY/);
728
708
  assert.match(r.prompt, /FILE_DECIDED_BODY/);
729
709
  });
730
710
 
731
- it('merges --focus and trailing focus words into one Focus block, in parse order', () => {
711
+ it('merges --focus and trailing focus words into one Focus block, in parse order', async () => {
732
712
  const sb = makeSandbox();
733
- const r = run(sb, { args: ['code', '--facts', 'f', '--focus', 'first', 'second', 'third'] });
713
+ const r = await run(sb, { args: ['code', '--facts', 'f', '--focus', 'first', 'second', 'third'] });
734
714
  rmSync(sb.home, { recursive: true, force: true });
735
715
  assert.equal(r.status, 0, r.stderr);
736
716
  assert.match(r.prompt, /## Focus\nfirst second third/);
737
717
  });
738
718
 
739
- it('plan mode with no --facts keeps the warning and proceeds (unchanged contract)', () => {
719
+ it('plan mode with no --facts keeps the warning and proceeds (unchanged contract)', async () => {
740
720
  const sb = makeSandbox();
741
721
  writeFileSync(join(sb.repo, 'p.md'), '# plan body\n');
742
- const r = run(sb, { args: ['plan', 'p.md'] });
722
+ const r = await run(sb, { args: ['plan', 'p.md'] });
743
723
  rmSync(sb.home, { recursive: true, force: true });
744
724
  assert.equal(r.status, 0, r.stderr);
745
725
  assert.match(r.stderr, /no --facts supplied/);
@@ -753,10 +733,10 @@ describe('agy-review.sh — guard + grounding (2, 3)', () => {
753
733
  // the run would be paid for and attest nothing. The wrapper refuses BEFORE the spend, keyed on the
754
734
  // resolved CONTENT (an empty --facts payload refuses identically). Escapes: the explicit
755
735
  // --ungrounded flag (throwaway opinion) and AGY_PROBE=1 (a probe receipt never attests anyway).
756
- describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)', () => {
757
- it('code mode with no --facts exits 2 before any agy invocation', () => {
736
+ describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)', { concurrency: 2 }, () => {
737
+ it('code mode with no --facts exits 2 before any agy invocation', async () => {
758
738
  const sb = makeSandbox();
759
- const r = run(sb, { args: ['code'] });
739
+ const r = await run(sb, { args: ['code'] });
760
740
  rmSync(sb.home, { recursive: true, force: true });
761
741
  assert.equal(r.status, 2, r.stderr);
762
742
  assert.equal(r.invoked, false, 'the refusal must fire before any agy invocation — zero runs spent');
@@ -767,19 +747,19 @@ describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)',
767
747
  assert.ok(existsSync(hint[1]), 'the resolved hint path exists on this layout');
768
748
  });
769
749
 
770
- it('code mode with --facts naming an EMPTY payload exits 2 before any agy invocation', () => {
750
+ it('code mode with --facts naming an EMPTY payload exits 2 before any agy invocation', async () => {
771
751
  const sb = makeSandbox();
772
752
  writeFileSync(join(sb.repo, 'empty-facts.md'), '');
773
- const r = run(sb, { args: ['code', '--facts', '@empty-facts.md'] });
753
+ const r = await run(sb, { args: ['code', '--facts', '@empty-facts.md'] });
774
754
  rmSync(sb.home, { recursive: true, force: true });
775
755
  assert.equal(r.status, 2, 'the refusal keys on the CONTENT, not the flag');
776
756
  assert.equal(r.invoked, false, 'an empty payload must not spend a run');
777
757
  assert.match(r.stderr, /agy-review code --facts @/);
778
758
  });
779
759
 
780
- it('code --ungrounded proceeds and the receipt records grounded:false', () => {
760
+ it('code --ungrounded proceeds and the receipt records grounded:false', async () => {
781
761
  const sb = makeSandbox();
782
- const r = run(sb, { args: ['code', '--ungrounded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
762
+ const r = await run(sb, { args: ['code', '--ungrounded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
783
763
  const receipts = readReceipts(sb.repo);
784
764
  rmSync(sb.home, { recursive: true, force: true });
785
765
  assert.equal(r.status, 0, r.stderr);
@@ -790,9 +770,9 @@ describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)',
790
770
  assert.match(r.stderr, /no --facts supplied/, 'the escape path stays loud, never silent');
791
771
  });
792
772
 
793
- it('AGY_PROBE=1 code with no --facts proceeds and the receipt records probe:true', () => {
773
+ it('AGY_PROBE=1 code with no --facts proceeds and the receipt records probe:true', async () => {
794
774
  const sb = makeSandbox();
795
- const r = run(sb, { args: ['code'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
775
+ const r = await run(sb, { args: ['code'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
796
776
  const receipts = readReceipts(sb.repo);
797
777
  rmSync(sb.home, { recursive: true, force: true });
798
778
  assert.equal(r.status, 0, r.stderr);
@@ -801,28 +781,28 @@ describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)',
801
781
  assert.equal(receipts[0].grounded, false);
802
782
  });
803
783
 
804
- it('--ungrounded with --facts is a refusal (contradiction)', () => {
784
+ it('--ungrounded with --facts is a refusal (contradiction)', async () => {
805
785
  const sb = makeSandbox();
806
- const r = run(sb, { args: ['code', '--ungrounded', '--facts', 'f'] });
786
+ const r = await run(sb, { args: ['code', '--ungrounded', '--facts', 'f'] });
807
787
  rmSync(sb.home, { recursive: true, force: true });
808
788
  assert.equal(r.status, 2);
809
789
  assert.equal(r.invoked, false);
810
790
  assert.match(r.stderr, /--ungrounded contradicts --facts/);
811
791
  });
812
792
 
813
- it('--ungrounded outside code mode is a refusal', () => {
793
+ it('--ungrounded outside code mode is a refusal', async () => {
814
794
  const sb = makeSandbox();
815
795
  writeFileSync(join(sb.repo, 'p.md'), '# p\n');
816
- const r = run(sb, { args: ['plan', 'p.md', '--ungrounded'] });
796
+ const r = await run(sb, { args: ['plan', 'p.md', '--ungrounded'] });
817
797
  rmSync(sb.home, { recursive: true, force: true });
818
798
  assert.equal(r.status, 2);
819
799
  assert.equal(r.invoked, false);
820
800
  assert.match(r.stderr, /--ungrounded is only valid in code mode/);
821
801
  });
822
802
 
823
- it('--ungrounded on a continuation is a refusal', () => {
803
+ it('--ungrounded on a continuation is a refusal', async () => {
824
804
  const sb = makeSandbox();
825
- const r = run(sb, { args: ['--continue', '--ungrounded'] });
805
+ const r = await run(sb, { args: ['--continue', '--ungrounded'] });
826
806
  rmSync(sb.home, { recursive: true, force: true });
827
807
  assert.equal(r.status, 2);
828
808
  assert.equal(r.invoked, false);
@@ -830,11 +810,11 @@ describe('agy-review.sh — code mode fails CLOSED without grounded facts (D4)',
830
810
  });
831
811
  });
832
812
 
833
- describe('agy-review.sh — code-mode precomputed diff (4, 5, 8)', () => {
834
- it('assembles repo map + status + untracked CONTENTS', () => {
813
+ describe('agy-review.sh — code-mode precomputed diff (4, 5, 8)', { concurrency: 2 }, () => {
814
+ it('assembles repo map + status + untracked CONTENTS', async () => {
835
815
  const sb = makeSandbox();
836
816
  writeFileSync(join(sb.repo, 'untra.txt'), 'UNIQUE_UNTRACKED_BODY\n');
837
- const r = run(sb, { args: ['code', '--facts', 'f'] });
817
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
838
818
  rmSync(sb.home, { recursive: true, force: true });
839
819
  assert.equal(r.status, 0, r.stderr);
840
820
  for (const sec of [/repo file map/, /git status/, /untracked: untra\.txt/, /UNIQUE_UNTRACKED_BODY/]) {
@@ -842,46 +822,46 @@ describe('agy-review.sh — code-mode precomputed diff (4, 5, 8)', () => {
842
822
  }
843
823
  });
844
824
 
845
- it('skips a binary untracked file (noted; raw bytes not inlined)', () => {
825
+ it('skips a binary untracked file (noted; raw bytes not inlined)', async () => {
846
826
  const sb = makeSandbox();
847
827
  writeFileSync(join(sb.repo, 'blob.bin'), Buffer.from([0x00, 0x01, 0x02, 0x00, 0x42]));
848
- const r = run(sb, { args: ['code', '--facts', 'f'] });
828
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
849
829
  rmSync(sb.home, { recursive: true, force: true });
850
830
  assert.match(r.prompt, /binary, skipped\): blob\.bin/);
851
831
  });
852
832
 
853
- it('does not follow an untracked symlink (no out-of-tree leak)', () => {
833
+ it('does not follow an untracked symlink (no out-of-tree leak)', async () => {
854
834
  const sb = makeSandbox();
855
835
  const secret = join(sb.home, 'outside-secret.txt'); // OUTSIDE the repo
856
836
  writeFileSync(secret, 'TOP_SECRET_LEAK_MARKER\n');
857
837
  symlinkSync(secret, join(sb.repo, 'link-to-outside'));
858
- const r = run(sb, { args: ['code', '--facts', 'f'] });
838
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
859
839
  rmSync(sb.home, { recursive: true, force: true });
860
840
  assert.match(r.prompt, /untracked \(symlink\): link-to-outside -> /);
861
841
  assert.doesNotMatch(r.prompt, /TOP_SECRET_LEAK_MARKER/, 'symlink target content must never leak');
862
842
  });
863
843
 
864
- it('handles untracked paths with spaces (NUL-safe)', () => {
844
+ it('handles untracked paths with spaces (NUL-safe)', async () => {
865
845
  const sb = makeSandbox();
866
846
  writeFileSync(join(sb.repo, 'a b c.txt'), 'SPACED_BODY\n');
867
- const r = run(sb, { args: ['code', '--facts', 'f'] });
847
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
868
848
  rmSync(sb.home, { recursive: true, force: true });
869
849
  assert.match(r.prompt, /untracked: a b c\.txt/);
870
850
  assert.match(r.prompt, /SPACED_BODY/);
871
851
  });
872
852
 
873
- it('no-diff preflight: a clean tree exits 0 without invoking agy', () => {
853
+ it('no-diff preflight: a clean tree exits 0 without invoking agy', async () => {
874
854
  const sb = makeSandbox({ clean: true });
875
- const r = run(sb, { args: ['code', '--facts', 'f'] });
855
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
876
856
  rmSync(sb.home, { recursive: true, force: true });
877
857
  assert.equal(r.status, 0);
878
858
  assert.match(r.stderr, /no uncommitted changes to review/);
879
859
  assert.equal(r.invoked, false, 'agy must NOT be invoked on a clean tree');
880
860
  });
881
861
 
882
- it('the strict output-shape footer is present in a fresh review', () => {
862
+ it('the strict output-shape footer is present in a fresh review', async () => {
883
863
  const sb = makeSandbox();
884
- const r = run(sb, { args: ['code', '--facts', 'f'] });
864
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
885
865
  rmSync(sb.home, { recursive: true, force: true });
886
866
  for (const sec of [/### Verdict/, /### Blocking/, /### Non-blocking/, /### Questions/]) {
887
867
  assert.match(r.prompt, sec);
@@ -914,11 +894,11 @@ const seedOversizedMap = (sb, count = 100) => {
914
894
  const mapSectionOf = (prompt) =>
915
895
  prompt.slice(prompt.indexOf(MAP_HEADER) + MAP_HEADER.length, prompt.indexOf('\n\n=== git status (porcelain) ==='));
916
896
 
917
- describe('agy-review.sh — repo file map budget (Phase 2)', () => {
918
- it('agy-review sets the map budget and degrades an over-budget map', () => {
897
+ describe('agy-review.sh — repo file map budget (Phase 2)', { concurrency: 2 }, () => {
898
+ it('agy-review sets the map budget and degrades an over-budget map', async () => {
919
899
  const sb = makeSandbox();
920
900
  const count = seedOversizedMap(sb);
921
- const r = run(sb, { args: ['code', '--facts', 'f'] });
901
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
922
902
  rmSync(sb.home, { recursive: true, force: true });
923
903
  assert.equal(r.status, 0, r.stderr);
924
904
  const note = r.prompt.match(new RegExp(`=== repo file map TRUNCATED to the changed-path subset: (\\d+) of (\\d+) tracked paths shown, (\\d+) omitted \\(map budget ${AGY_MAP_BUDGET_BYTES} bytes\\) ===`));
@@ -931,10 +911,10 @@ describe('agy-review.sh — repo file map budget (Phase 2)', () => {
931
911
  assert.ok(!r.prompt.includes(untouchedPath(0)), 'an untouched path is dropped, and it appears nowhere else in the payload');
932
912
  });
933
913
 
934
- it('the degraded changed-path subset itself stays inside the wrapper budget', () => {
914
+ it('the degraded changed-path subset itself stays inside the wrapper budget', async () => {
935
915
  const sb = makeSandbox();
936
916
  seedOversizedMap(sb);
937
- const r = run(sb, { args: ['code', '--facts', 'f'] });
917
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
938
918
  rmSync(sb.home, { recursive: true, force: true });
939
919
  assert.equal(r.status, 0, r.stderr);
940
920
  const lines = mapSectionOf(r.prompt).split('\n');
@@ -945,9 +925,9 @@ describe('agy-review.sh — repo file map budget (Phase 2)', () => {
945
925
  assert.ok(pathBytes <= AGY_MAP_BUDGET_BYTES, `the subset (${pathBytes} bytes) must stay inside the ${AGY_MAP_BUDGET_BYTES}-byte budget`);
946
926
  });
947
927
 
948
- it('an ordinary in-budget repo keeps the whole map, unnoted', () => {
928
+ it('an ordinary in-budget repo keeps the whole map, unnoted', async () => {
949
929
  const sb = makeSandbox();
950
- const r = run(sb, { args: ['code', '--facts', 'f'] });
930
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
951
931
  rmSync(sb.home, { recursive: true, force: true });
952
932
  assert.equal(r.status, 0, r.stderr);
953
933
  assert.match(r.prompt, /=== repo file map \(git ls-files\) ===\nbase\.txt\n/);
@@ -965,6 +945,18 @@ describe('agy-review.sh — repo file map budget (Phase 2)', () => {
965
945
  const ARTIFACT_HEADER = '## The change set under review (assembled working-tree diff — repo-complete)';
966
946
  const SHAPE_HEADER = '\n## Output — Markdown, this exact shape, nothing else';
967
947
  const FED_CAP = 6000;
948
+ // The proof asks the model to COUNT to the address with no tool, so the address has to be reachable
949
+ // that way. The walk used to start at each part's middle: measured 847..1024 on a 701464-byte change
950
+ // set, which is where the three live false refusals sat. FED_WIDE_CAP gives the countability
951
+ // regression parts wide enough that a midpoint address is unmistakably out of reach — under the
952
+ // narrow FED_CAP the midpoint lands at 80, close enough to the bar that fixture drift could hide it.
953
+ const MAX_COUNTABLE_PROOF_ADDRESS = 40;
954
+ const FED_WIDE_CAP = 24000;
955
+ // The assembler's OWN section banners — the vocabulary the proof selector refuses, because it emits
956
+ // them for every change set and a model can rebuild the per-path forms from part 1's git-status
957
+ // block. A change set's own `=== … ===` line is NOT in this set and stays admissible.
958
+ const isAssemblerBanner = (line) =>
959
+ /^=== (repo file map|git status|staged diff|unstaged diff|untracked)/.test(line) && line.endsWith(' ===');
968
960
 
969
961
  // A change set big enough to need several parts under FED_CAP.
970
962
  const seedFedChangeSet = (sb, { lines = 400, multibyte = false } = {}) => {
@@ -997,12 +989,12 @@ const requestedOf = (finalPrompt) => requestedBlockOf(finalPrompt).map((item) =>
997
989
  const fedRun = (sb, extraEnv = {}) =>
998
990
  run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), ...extraEnv } });
999
991
 
1000
- describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)', () => {
1001
- it('an over-cap code review feeds every part and the concatenated BODIES reproduce the change set exactly', () => {
992
+ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)', { concurrency: 2 }, () => {
993
+ it('an over-cap code review feeds every part and the concatenated BODIES reproduce the change set exactly', async () => {
1002
994
  const sb = makeSandbox();
1003
995
  seedFedChangeSet(sb);
1004
- const inline = run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
1005
- const fed = fedRun(sb);
996
+ const inline = await run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
997
+ const fed = await fedRun(sb);
1006
998
  rmSync(sb.home, { recursive: true, force: true });
1007
999
  assert.equal(inline.status, 0, inline.stderr);
1008
1000
  assert.equal(fed.status, 0, fed.stderr);
@@ -1012,10 +1004,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1012
1004
  assert.equal(bodies.join(''), inlineArtifactOf(inline.prompt), 'the bodies concatenate to the change set byte-for-byte');
1013
1005
  });
1014
1006
 
1015
- it('no envelope text appears in the reconstructed review artifact', () => {
1007
+ it('no envelope text appears in the reconstructed review artifact', async () => {
1016
1008
  const sb = makeSandbox();
1017
1009
  seedFedChangeSet(sb);
1018
- const fed = fedRun(sb);
1010
+ const fed = await fedRun(sb);
1019
1011
  rmSync(sb.home, { recursive: true, force: true });
1020
1012
  const reconstructed = fed.prompts.slice(0, -1).map(bodyOf).join('');
1021
1013
  for (const envelope of ['--- BEGIN CHANGE-SET PART', '--- END CHANGE-SET PART', 'Chunked delivery', 'Reply with exactly OK', 'Grounded facts', 'Requested:']) {
@@ -1023,10 +1015,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1023
1015
  }
1024
1016
  });
1025
1017
 
1026
- it('every fed turn prompt is under AGY_MAX_PROMPT_BYTES', () => {
1018
+ it('every fed turn prompt is under AGY_MAX_PROMPT_BYTES', async () => {
1027
1019
  const sb = makeSandbox();
1028
1020
  seedFedChangeSet(sb);
1029
- const fed = fedRun(sb);
1021
+ const fed = await fedRun(sb);
1030
1022
  rmSync(sb.home, { recursive: true, force: true });
1031
1023
  assert.equal(fed.status, 0, fed.stderr);
1032
1024
  for (const [i, p] of fed.prompts.entries()) {
@@ -1034,10 +1026,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1034
1026
  }
1035
1027
  });
1036
1028
 
1037
- it('the shape block appears only on the final turn, and every feed turn carries the acknowledge-only instruction', () => {
1029
+ it('the shape block appears only on the final turn, and every feed turn carries the acknowledge-only instruction', async () => {
1038
1030
  const sb = makeSandbox();
1039
1031
  seedFedChangeSet(sb);
1040
- const fed = fedRun(sb);
1032
+ const fed = await fedRun(sb);
1041
1033
  rmSync(sb.home, { recursive: true, force: true });
1042
1034
  const feed = fed.prompts.slice(0, -1);
1043
1035
  const final = fed.prompts[fed.prompts.length - 1];
@@ -1056,10 +1048,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1056
1048
  // lines, headless agy auto-denied it ("no output produced — a tool required the \"command\"
1057
1049
  // permission"), and the whole answer was lost. Every turn must forbid tool use outright — the
1058
1050
  // change set is already IN the conversation, so no tool can add anything.
1059
- it('every turn forbids tool use — a denied tool loses the whole answer on this host', () => {
1051
+ it('every turn forbids tool use — a denied tool loses the whole answer on this host', async () => {
1060
1052
  const sb = makeSandbox();
1061
1053
  seedFedChangeSet(sb);
1062
- const fed = fedRun(sb);
1054
+ const fed = await fedRun(sb);
1063
1055
  rmSync(sb.home, { recursive: true, force: true });
1064
1056
  assert.equal(fed.status, 0, fed.stderr);
1065
1057
  for (const [i, p] of fed.prompts.entries()) {
@@ -1071,10 +1063,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1071
1063
  // The delivery verdict must not blame delivery for a run that produced no answer at all: the parts
1072
1064
  // WERE fed, the model was blocked from replying. Reporting it as "the change set never arrived"
1073
1065
  // sends the reader hunting the wrong bug.
1074
- it('a final turn that produced NO answer reports that cause, never a delivery failure', () => {
1066
+ it('a final turn that produced NO answer reports that cause, never a delivery failure', async () => {
1075
1067
  const sb = makeSandbox();
1076
1068
  seedFedChangeSet(sb);
1077
- const fed = fedRun(sb, { AGY_FAKE_OUTPUT: 'jetski: no output produced — a tool required the "command" permission that headless mode cannot prompt for, so it was auto-denied.' });
1069
+ const fed = await fedRun(sb, { AGY_FAKE_OUTPUT: 'jetski: no output produced — a tool required the "command" permission that headless mode cannot prompt for, so it was auto-denied.' });
1078
1070
  const receipts = readReceipts(sb.repo);
1079
1071
  rmSync(sb.home, { recursive: true, force: true });
1080
1072
  assert.equal(fed.status, 4, fed.stderr);
@@ -1086,31 +1078,31 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1086
1078
  assert.equal(receipts.length, 0, 'still no receipt — an unanswered review attests nothing');
1087
1079
  });
1088
1080
 
1089
- it('an answerless final turn with NO recognizable diagnostic reports the cause as unknown', () => {
1081
+ it('an answerless final turn with NO recognizable diagnostic reports the cause as unknown', async () => {
1090
1082
  const sb = makeSandbox();
1091
1083
  seedFedChangeSet(sb);
1092
- const fed = fedRun(sb, { AGY_FAKE_OUTPUT: 'something the wrapper has never seen before' });
1084
+ const fed = await fedRun(sb, { AGY_FAKE_OUTPUT: 'something the wrapper has never seen before' });
1093
1085
  rmSync(sb.home, { recursive: true, force: true });
1094
1086
  assert.equal(fed.status, 4, fed.stderr);
1095
1087
  assert.match(fed.stderr, /CAUSE: unknown/, 'an unrecognized failure is never dressed up as a known one');
1096
1088
  assert.doesNotMatch(fed.stderr, /named by agy itself/);
1097
1089
  });
1098
1090
 
1099
- it('the grounding rides turn 1 only', () => {
1091
+ it('the grounding rides turn 1 only', async () => {
1100
1092
  const sb = makeSandbox();
1101
1093
  seedFedChangeSet(sb);
1102
- const fed = fedRun(sb, { AGY_MAX_PROMPT_BYTES: String(FED_CAP) });
1094
+ const fed = await fedRun(sb, { AGY_MAX_PROMPT_BYTES: String(FED_CAP) });
1103
1095
  rmSync(sb.home, { recursive: true, force: true });
1104
1096
  assert.match(fed.prompts[0], /## Grounded facts — review AGAINST these/);
1105
1097
  assert.match(fed.prompts[0], /grounded fact/);
1106
1098
  for (const p of fed.prompts.slice(1)) assert.ok(!p.includes('## Grounded facts'), 'the grounding is not re-sent');
1107
1099
  });
1108
1100
 
1109
- it('a multibyte body is cut at LINE boundaries — every part decodes cleanly and concatenation stays byte-exact', () => {
1101
+ it('a multibyte body is cut at LINE boundaries — every part decodes cleanly and concatenation stays byte-exact', async () => {
1110
1102
  const sb = makeSandbox();
1111
1103
  seedFedChangeSet(sb, { multibyte: true, lines: 400 });
1112
- const inline = run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
1113
- const fed = fedRun(sb);
1104
+ const inline = await run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
1105
+ const fed = await fedRun(sb);
1114
1106
  rmSync(sb.home, { recursive: true, force: true });
1115
1107
  assert.equal(fed.status, 0, fed.stderr);
1116
1108
  assert.ok(fed.turns >= 3, 'the multibyte fixture really chunks');
@@ -1125,12 +1117,12 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1125
1117
  // The partitioner's two boundary defects, both caught at review: an invented separator byte on an
1126
1118
  // unterminated last line (an extra part and an extra TURN), and an over-eager refusal for a line
1127
1119
  // that is merely longer than the FIRST part's smaller budget.
1128
- it('an artifact with NO trailing newline yields no extra or empty part, and reassembles byte-exactly', () => {
1120
+ it('an artifact with NO trailing newline yields no extra or empty part, and reassembles byte-exactly', async () => {
1129
1121
  const sb = makeSandbox();
1130
1122
  // An untracked file with no final newline: the assembled change set ends without one too.
1131
1123
  writeFileSync(join(sb.repo, 'oversized.txt'), `${Array.from({ length: 400 }, (_, i) => `unique change-set line ${String(i).padStart(4, '0')} — a distinctive body marker ${'x'.repeat(20)}`).join('\n')}`);
1132
- const inline = run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
1133
- const fed = fedRun(sb);
1124
+ const inline = await run(sb, { args: ['code', '--facts', 'grounded fact'], env: { AGY_MAX_PROMPT_BYTES: '130000' } });
1125
+ const fed = await fedRun(sb);
1134
1126
  rmSync(sb.home, { recursive: true, force: true });
1135
1127
  assert.equal(fed.status, 0, fed.stderr);
1136
1128
  const bodies = fed.prompts.slice(0, -1).map(bodyOf);
@@ -1141,7 +1133,7 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1141
1133
  assert.equal(Number(announced[2]), fed.turns, 'and no phantom turn');
1142
1134
  });
1143
1135
 
1144
- it('a line longer than the FIRST part budget but not the later one is placed, not refused', () => {
1136
+ it('a line longer than the FIRST part budget but not the later one is placed, not refused', async () => {
1145
1137
  const sb = makeSandbox();
1146
1138
  // Turn 1 carries the grounding too, so its body budget is SMALLER by exactly the grounding size.
1147
1139
  // A fat grounding opens a real window between the two budgets; a line inside that window must be
@@ -1152,7 +1144,7 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1152
1144
  const filler = (from, n) => Array.from({ length: n }, (_, i) => `unique change-set line ${String(from + i).padStart(4, '0')} — a distinctive body marker ${'x'.repeat(20)}`).join('\n');
1153
1145
  const longLine = `unique long marker line ${'y'.repeat(4200)}`;
1154
1146
  writeFileSync(join(sb.repo, 'oversized.txt'), `${filler(0, 20)}\n${longLine}\n${filler(20, 60)}\n`);
1155
- const fed = run(sb, { args: ['code', '--facts', facts], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
1147
+ const fed = await run(sb, { args: ['code', '--facts', facts], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
1156
1148
  rmSync(sb.home, { recursive: true, force: true });
1157
1149
  assert.equal(fed.status, 0, `a placeable long line must not refuse the run: ${fed.stderr}`);
1158
1150
  const bodies = fed.prompts.slice(0, -1).map(bodyOf);
@@ -1160,20 +1152,20 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1160
1152
  assert.ok(!bodies[0].includes(longLine), 'and it was moved OFF the smaller first part');
1161
1153
  });
1162
1154
 
1163
- it('a line that fits NO part refuses before any turn is spent', () => {
1155
+ it('a line that fits NO part refuses before any turn is spent', async () => {
1164
1156
  const sb = makeSandbox();
1165
1157
  writeFileSync(join(sb.repo, 'oversized.txt'), `head\n${'z'.repeat(FED_CAP * 2)}\ntail\n`);
1166
- const fed = fedRun(sb);
1158
+ const fed = await fedRun(sb);
1167
1159
  rmSync(sb.home, { recursive: true, force: true });
1168
1160
  assert.equal(fed.status, 2, fed.stderr);
1169
1161
  assert.equal(fed.invoked, false, 'not one turn is spent');
1170
1162
  assert.match(fed.stderr, /does not fit even an EMPTY fed part/);
1171
1163
  });
1172
1164
 
1173
- it('feed-turn output never reaches stdout or the parsed capture (a premature verdict is discarded)', () => {
1165
+ it('feed-turn output never reaches stdout or the parsed capture (a premature verdict is discarded)', async () => {
1174
1166
  const sb = makeSandbox();
1175
1167
  seedFedChangeSet(sb);
1176
- const fed = fedRun(sb);
1168
+ const fed = await fedRun(sb);
1177
1169
  const receipts = readReceipts(sb.repo);
1178
1170
  rmSync(sb.home, { recursive: true, force: true });
1179
1171
  assert.equal(fed.status, 0, fed.stderr);
@@ -1183,10 +1175,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1183
1175
  assert.equal(receipts[0].verdict, 'SHIP', 'only the FINAL turn is parsed into the receipt');
1184
1176
  });
1185
1177
 
1186
- it('a non-zero feed turn stops the run, spends no later turn, and writes NO receipt', () => {
1178
+ it('a non-zero feed turn stops the run, spends no later turn, and writes NO receipt', async () => {
1187
1179
  const sb = makeSandbox();
1188
1180
  seedFedChangeSet(sb);
1189
- const fed = fedRun(sb, { AGY_FAKE_FAIL_TURN: '2' });
1181
+ const fed = await fedRun(sb, { AGY_FAKE_FAIL_TURN: '2' });
1190
1182
  const receipts = readReceipts(sb.repo);
1191
1183
  rmSync(sb.home, { recursive: true, force: true });
1192
1184
  assert.notEqual(fed.status, 0, 'a failed feed turn is a failed review');
@@ -1197,10 +1189,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1197
1189
  // The hard cap is ONE wall-clock budget for the whole review. Handing each of the N+1 calls the
1198
1190
  // full AGY_HARD_TIMEOUT multiplied the stated guarantee by the turn count — a 30m cap could run
1199
1191
  // for hours. Each turn now gets only what is LEFT of a shared deadline.
1200
- it('the hard cap is ONE budget for the whole review, not one per turn', () => {
1192
+ it('the hard cap is ONE budget for the whole review, not one per turn', async () => {
1201
1193
  const sb = makeSandbox();
1202
1194
  seedFedChangeSet(sb, { lines: 150 });
1203
- const fed = run(sb, {
1195
+ const fed = await run(sb, {
1204
1196
  args: ['code', '--facts', 'grounded fact'],
1205
1197
  env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_HARD_TIMEOUT: '600s', AGY_TIMEOUT: '600s', AGY_FAKE_SLEEP: '1' },
1206
1198
  });
@@ -1223,10 +1215,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1223
1215
 
1224
1216
  // council R2-M2: shrinking a turn to 1s does not save the cap — a TERM-ignoring process still runs
1225
1217
  // for the SIGKILL grace on top. Too little budget left is a REFUSAL, never a tiny turn.
1226
- it('a cap smaller than the SIGKILL grace refuses BEFORE spending a single turn', () => {
1218
+ it('a cap smaller than the SIGKILL grace refuses BEFORE spending a single turn', async () => {
1227
1219
  const sb = makeSandbox();
1228
1220
  seedFedChangeSet(sb, { lines: 150 });
1229
- const fed = run(sb, {
1221
+ const fed = await run(sb, {
1230
1222
  args: ['code', '--facts', 'grounded fact'],
1231
1223
  env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_HARD_TIMEOUT: '5s', AGY_TIMEOUT: '5s' },
1232
1224
  });
@@ -1238,10 +1230,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1238
1230
  assert.equal(receipts.length, 0, 'a refused review mints NO receipt');
1239
1231
  });
1240
1232
 
1241
- it('the fed lane announces part and turn counts before the first dispatch (D5 quota honesty)', () => {
1233
+ it('the fed lane announces part and turn counts before the first dispatch (D5 quota honesty)', async () => {
1242
1234
  const sb = makeSandbox();
1243
1235
  seedFedChangeSet(sb);
1244
- const fed = fedRun(sb);
1236
+ const fed = await fedRun(sb);
1245
1237
  rmSync(sb.home, { recursive: true, force: true });
1246
1238
  const announce = fed.stderr.match(/feeding the change set in (\d+) part\(s\) over (\d+) subscription turns/);
1247
1239
  assert.ok(announce, `the cost must be stated before it is spent: ${fed.stderr}`);
@@ -1252,10 +1244,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1252
1244
  // The ceiling is a SPENDING guard, so a value the operator sets and the wrapper cannot honour must
1253
1245
  // never be silently ignored. `008000` used to make bash evaluate an invalid octal constant: both
1254
1246
  // range tests errored to false and the ceiling simply stopped existing.
1255
- it('a leading-zero ceiling is canonicalized, not read as octal — and it still refuses', () => {
1247
+ it('a leading-zero ceiling is canonicalized, not read as octal — and it still refuses', async () => {
1256
1248
  const sb = makeSandbox();
1257
1249
  seedFedChangeSet(sb);
1258
- const fed = fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '008000' });
1250
+ const fed = await fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '008000' });
1259
1251
  rmSync(sb.home, { recursive: true, force: true });
1260
1252
  assert.equal(fed.status, 2, fed.stderr);
1261
1253
  assert.equal(fed.invoked, false, 'the ceiling really bound — not one turn was spent');
@@ -1263,11 +1255,11 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1263
1255
  assert.doesNotMatch(fed.stderr, /value too great for base|invalid arithmetic/, 'no octal diagnostic anywhere');
1264
1256
  });
1265
1257
 
1266
- it('an explicit env ceiling the wrapper cannot honour REFUSES, never silently defaults', () => {
1258
+ it('an explicit env ceiling the wrapper cannot honour REFUSES, never silently defaults', async () => {
1267
1259
  const sb = makeSandbox();
1268
1260
  seedFedChangeSet(sb);
1269
1261
  for (const bad of ['999999999999999999999', '200000000', 'lots']) {
1270
- const fed = fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: bad });
1262
+ const fed = await fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: bad });
1271
1263
  assert.equal(fed.status, 2, `${bad}: ${fed.stderr}`);
1272
1264
  assert.equal(fed.invoked, false, `${bad}: no run is spent under an unhonoured ceiling`);
1273
1265
  assert.match(fed.stderr, /not a valid byte ceiling/, `${bad}: the refusal names the cause`);
@@ -1275,20 +1267,20 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1275
1267
  rmSync(sb.home, { recursive: true, force: true });
1276
1268
  });
1277
1269
 
1278
- it('the DEFAULT ceiling still lets an ordinary fed review through', () => {
1270
+ it('the DEFAULT ceiling still lets an ordinary fed review through', async () => {
1279
1271
  const sb = makeSandbox();
1280
1272
  seedFedChangeSet(sb);
1281
- const fed = fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '240000' });
1273
+ const fed = await fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '240000' });
1282
1274
  const receipts = readReceipts(sb.repo);
1283
1275
  rmSync(sb.home, { recursive: true, force: true });
1284
1276
  assert.equal(fed.status, 0, fed.stderr);
1285
1277
  assert.equal(receipts[0].delivery, 'fed');
1286
1278
  });
1287
1279
 
1288
- it('a change set whose total outgoing prompt bytes exceed AGY_REVIEW_MAX_TOTAL_BYTES refuses before the first turn is spent', () => {
1280
+ it('a change set whose total outgoing prompt bytes exceed AGY_REVIEW_MAX_TOTAL_BYTES refuses before the first turn is spent', async () => {
1289
1281
  const sb = makeSandbox();
1290
1282
  seedFedChangeSet(sb);
1291
- const fed = fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '9000' });
1283
+ const fed = await fedRun(sb, { AGY_REVIEW_MAX_TOTAL_BYTES: '9000' });
1292
1284
  const receipts = readReceipts(sb.repo);
1293
1285
  rmSync(sb.home, { recursive: true, force: true });
1294
1286
  assert.equal(fed.status, 2, fed.stderr);
@@ -1297,10 +1289,10 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1297
1289
  assert.equal(receipts.length, 0);
1298
1290
  });
1299
1291
 
1300
- it('a fixed overhead that cannot fit refuses rather than emitting an empty body', () => {
1292
+ it('a fixed overhead that cannot fit refuses rather than emitting an empty body', async () => {
1301
1293
  const sb = makeSandbox();
1302
1294
  seedFedChangeSet(sb);
1303
- const fed = fedRun(sb, { AGY_MAX_PROMPT_BYTES: '900' });
1295
+ const fed = await fedRun(sb, { AGY_MAX_PROMPT_BYTES: '900' });
1304
1296
  rmSync(sb.home, { recursive: true, force: true });
1305
1297
  assert.equal(fed.status, 2, fed.stderr);
1306
1298
  assert.equal(fed.invoked, false);
@@ -1311,20 +1303,20 @@ describe('agy-review.sh — chunked feed: the change set is DELIVERED (Phase 3)'
1311
1303
  // 4.3: agy's own denial names the permission rule it wants. The kit SURFACES that fact and never
1312
1304
  // applies it — granting read_file would widen a boundary for ALL agy use on the machine to re-arm
1313
1305
  // the one lane whose failure mode is undetectable by construction.
1314
- describe('agy-review.sh — the agy permission fact is surfaced, never applied', () => {
1306
+ describe('agy-review.sh — the agy permission fact is surfaced, never applied', { concurrency: 2 }, () => {
1315
1307
  const BRIDGE_ROOT = resolve(HERE, '..');
1316
1308
 
1317
- it('the over-cap path states why the change set is delivered rather than read', () => {
1309
+ it('the over-cap path states why the change set is delivered rather than read', async () => {
1318
1310
  const sb = makeSandbox();
1319
1311
  seedFedChangeSet(sb);
1320
- const fed = fedRun(sb);
1312
+ const fed = await fedRun(sb);
1321
1313
  rmSync(sb.home, { recursive: true, force: true });
1322
1314
  assert.equal(fed.status, 0, fed.stderr);
1323
1315
  assert.match(fed.stderr, /read_file/, 'the notice names the denied tool');
1324
1316
  assert.match(fed.stderr, /never (grants|writes)/, 'and states that the kit does not grant it');
1325
1317
  });
1326
1318
 
1327
- it('the bridge docs state the never-applied posture (doc contract)', () => {
1319
+ it('the bridge docs state the never-applied posture (doc contract)', async () => {
1328
1320
  const prompt = readFileSync(join(BRIDGE_ROOT, 'references', 'review-prompt.md'), 'utf8');
1329
1321
  assert.match(prompt, /read_file/, 'the denial is named');
1330
1322
  assert.match(prompt, /never writes it|never applied/i, 'the never-applied posture is stated');
@@ -1332,7 +1324,7 @@ describe('agy-review.sh — the agy permission fact is surfaced, never applied',
1332
1324
  assert.doesNotMatch(prompt, /grant (the )?read_file permission to (fix|enable)/i, 'the docs never RECOMMEND granting it');
1333
1325
  });
1334
1326
 
1335
- it('no wrapper or doc surface ever writes an agy permission rule', () => {
1327
+ it('no wrapper or doc surface ever writes an agy permission rule', async () => {
1336
1328
  for (const rel of [join('bin', 'agy-review.sh'), join('bin', 'agy.sh')]) {
1337
1329
  const text = readFileSync(join(BRIDGE_ROOT, rel), 'utf8');
1338
1330
  assert.doesNotMatch(text, /--dangerously-skip-permissions/, `${rel} must never pass the blanket-permission flag`);
@@ -1345,11 +1337,11 @@ describe('agy-review.sh — the agy permission fact is surfaced, never applied',
1345
1337
  // to `--continue` when it changed. It is now a NAMED envelope field the reader validates against the
1346
1338
  // UUID grammar, so the pin that used to rot SILENTLY now fails LOUDLY: there is no `--continue`
1347
1339
  // fallback left in this lane and no later turn is spent when turn 1 cannot name its conversation.
1348
- describe('agy-review.sh — fed lane: turn targeting (D9)', () => {
1349
- it('every turn after the first is ROUTED at the id turn 1`s envelope named', () => {
1340
+ describe('agy-review.sh — fed lane: turn targeting (D9)', { concurrency: 2 }, () => {
1341
+ it('every turn after the first is ROUTED at the id turn 1`s envelope named', async () => {
1350
1342
  const sb = makeSandbox();
1351
1343
  seedFedChangeSet(sb);
1352
- const fed = fedRun(sb, { AGY_FAKE_CONV_ID: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' });
1344
+ const fed = await fedRun(sb, { AGY_FAKE_CONV_ID: 'aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee' });
1353
1345
  rmSync(sb.home, { recursive: true, force: true });
1354
1346
  assert.equal(fed.status, 0, fed.stderr);
1355
1347
  assert.ok(!fed.argvs[0].includes('--conversation'), 'turn 1 is fresh');
@@ -1370,10 +1362,10 @@ describe('agy-review.sh — fed lane: turn targeting (D9)', () => {
1370
1362
  ['not a string', { AGY_FAKE_CONV_SHAPE: 'number' }],
1371
1363
  ['failing the UUID grammar', { AGY_FAKE_CONV_ID: 'not-a-conversation-id' }],
1372
1364
  ]) {
1373
- it(`a turn-1 conversation id ${name} fails LOUDLY before turn 2, with NO receipt`, () => {
1365
+ it(`a turn-1 conversation id ${name} fails LOUDLY before turn 2, with NO receipt`, async () => {
1374
1366
  const sb = makeSandbox();
1375
1367
  seedFedChangeSet(sb);
1376
- const fed = fedRun(sb, env);
1368
+ const fed = await fedRun(sb, env);
1377
1369
  const receipts = readReceipts(sb.repo);
1378
1370
  rmSync(sb.home, { recursive: true, force: true });
1379
1371
  assert.equal(fed.status, 5, `an unusable envelope has the transport code: ${fed.stderr}`);
@@ -1390,10 +1382,10 @@ describe('agy-review.sh — fed lane: turn targeting (D9)', () => {
1390
1382
  ['whose payload is not an envelope', { AGY_FAKE_BAD_TURN: '2', AGY_FAKE_RAW_STDOUT: 'jetski: not an envelope\n' }, /agy-envelope: not-json/],
1391
1383
  ['reporting a non-SUCCESS status', { AGY_FAKE_BAD_TURN: '2', AGY_FAKE_STATUS: 'ERROR' }, /agy-envelope: status/],
1392
1384
  ]) {
1393
- it(`a fed turn 2 ${name} stops the run: NO receipt, no turn 3`, () => {
1385
+ it(`a fed turn 2 ${name} stops the run: NO receipt, no turn 3`, async () => {
1394
1386
  const sb = makeSandbox();
1395
1387
  seedFedChangeSet(sb);
1396
- const fed = fedRun(sb, env);
1388
+ const fed = await fedRun(sb, env);
1397
1389
  const receipts = readReceipts(sb.repo);
1398
1390
  rmSync(sb.home, { recursive: true, force: true });
1399
1391
  assert.equal(fed.status, 5, fed.stderr);
@@ -1406,7 +1398,7 @@ describe('agy-review.sh — fed lane: turn targeting (D9)', () => {
1406
1398
 
1407
1399
  // The doc must not describe BOTH routings at once: a residual recording the retired log scrape
1408
1400
  // beside the envelope statement leaves an operator unable to tell which lane their build runs.
1409
- it('the fed-lane doc states envelope routing and keeps no log-scrape residual (doc contract)', () => {
1401
+ it('the fed-lane doc states envelope routing and keeps no log-scrape residual (doc contract)', async () => {
1410
1402
  const prompt = readFileSync(resolve(HERE, '..', 'references', 'review-prompt.md'), 'utf8');
1411
1403
  assert.match(prompt, /Later turns are routed by a NAMED field/, 'the live routing is stated');
1412
1404
  assert.doesNotMatch(prompt, /conversation id is parsed from/i, 'the retired scrape is no longer described as live');
@@ -1414,11 +1406,11 @@ describe('agy-review.sh — fed lane: turn targeting (D9)', () => {
1414
1406
  });
1415
1407
  });
1416
1408
 
1417
- describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1, D7)', () => {
1418
- it('a fed review reproducing every selected line writes a fresh code receipt at the tree fingerprint', () => {
1409
+ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1, D7)', { concurrency: 2 }, () => {
1410
+ it('a fed review reproducing every selected line writes a fresh code receipt at the tree fingerprint', async () => {
1419
1411
  const sb = makeSandbox();
1420
1412
  seedFedChangeSet(sb);
1421
- const fed = fedRun(sb);
1413
+ const fed = await fedRun(sb);
1422
1414
  const receipts = readReceipts(sb.repo);
1423
1415
  rmSync(sb.home, { recursive: true, force: true });
1424
1416
  assert.equal(fed.status, 0, fed.stderr);
@@ -1429,10 +1421,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1429
1421
  assert.equal(receipts[0].delivery, 'fed', 'the receipt declares HOW delivery was established');
1430
1422
  });
1431
1423
 
1432
- it('a fed review whose output omits a part`s echo exits 4 and writes NO receipt', () => {
1424
+ it('a fed review whose output omits a part`s echo exits 4 and writes NO receipt', async () => {
1433
1425
  const sb = makeSandbox();
1434
1426
  seedFedChangeSet(sb);
1435
- const fed = fedRun(sb, { AGY_FAKE_PROOF_OMIT: '2' });
1427
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_OMIT: '2' });
1436
1428
  const receipts = readReceipts(sb.repo);
1437
1429
  rmSync(sb.home, { recursive: true, force: true });
1438
1430
  assert.equal(fed.status, 4, fed.stderr);
@@ -1441,10 +1433,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1441
1433
  assert.equal(receipts.length, 0);
1442
1434
  });
1443
1435
 
1444
- it('a fed review whose echo differs from the recorded line exits 4 and writes NO receipt', () => {
1436
+ it('a fed review whose echo differs from the recorded line exits 4 and writes NO receipt', async () => {
1445
1437
  const sb = makeSandbox();
1446
1438
  seedFedChangeSet(sb);
1447
- const fed = fedRun(sb, { AGY_FAKE_PROOF_CORRUPT: '2' });
1439
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_CORRUPT: '2' });
1448
1440
  const receipts = readReceipts(sb.repo);
1449
1441
  rmSync(sb.home, { recursive: true, force: true });
1450
1442
  assert.equal(fed.status, 4, fed.stderr);
@@ -1452,10 +1444,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1452
1444
  assert.equal(receipts.length, 0);
1453
1445
  });
1454
1446
 
1455
- it('a fed review echoing one part`s line for two parts exits 4 and writes NO receipt', () => {
1447
+ it('a fed review echoing one part`s line for two parts exits 4 and writes NO receipt', async () => {
1456
1448
  const sb = makeSandbox();
1457
1449
  seedFedChangeSet(sb);
1458
- const fed = fedRun(sb, { AGY_FAKE_PROOF_DUP: '1' });
1450
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_DUP: '1' });
1459
1451
  const receipts = readReceipts(sb.repo);
1460
1452
  rmSync(sb.home, { recursive: true, force: true });
1461
1453
  assert.equal(fed.status, 4, fed.stderr);
@@ -1464,10 +1456,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1464
1456
 
1465
1457
  // The proof GRAMMAR, pinned red→green (Test-as-spec). A substring search over the whole answer
1466
1458
  // accepted every shape below except the bullet — which is the one shape that should pass.
1467
- it('an echo placed OUTSIDE the proof section does not count', () => {
1459
+ it('an echo placed OUTSIDE the proof section does not count', async () => {
1468
1460
  const sb = makeSandbox();
1469
1461
  seedFedChangeSet(sb);
1470
- const fed = fedRun(sb, { AGY_FAKE_PROOF_OUTSIDE: '1' });
1462
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_OUTSIDE: '1' });
1471
1463
  const receipts = readReceipts(sb.repo);
1472
1464
  rmSync(sb.home, { recursive: true, force: true });
1473
1465
  assert.equal(fed.status, 4, fed.stderr);
@@ -1477,20 +1469,20 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1477
1469
  // The proof comes FIRST so output truncation can never silently drop it. A block that arrives
1478
1470
  // after a verdict is not that shape, so it is not searched for — otherwise the "first" in the
1479
1471
  // contract would be decoration.
1480
- it('a proof block placed AFTER the verdict does not count — the proof must be the first section', () => {
1472
+ it('a proof block placed AFTER the verdict does not count — the proof must be the first section', async () => {
1481
1473
  const sb = makeSandbox();
1482
1474
  seedFedChangeSet(sb);
1483
- const fed = fedRun(sb, { AGY_FAKE_PROOF_LATE: '1' });
1475
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_LATE: '1' });
1484
1476
  const receipts = readReceipts(sb.repo);
1485
1477
  rmSync(sb.home, { recursive: true, force: true });
1486
1478
  assert.equal(fed.status, 4, fed.stderr);
1487
1479
  assert.equal(receipts.length, 0);
1488
1480
  });
1489
1481
 
1490
- it('an address echoed TWICE fails — one address, one line', () => {
1482
+ it('an address echoed TWICE fails — one address, one line', async () => {
1491
1483
  const sb = makeSandbox();
1492
1484
  seedFedChangeSet(sb);
1493
- const fed = fedRun(sb, { AGY_FAKE_PROOF_TWICE: '1' });
1485
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_TWICE: '1' });
1494
1486
  const receipts = readReceipts(sb.repo);
1495
1487
  rmSync(sb.home, { recursive: true, force: true });
1496
1488
  assert.equal(fed.status, 4, fed.stderr);
@@ -1498,10 +1490,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1498
1490
  assert.equal(receipts.length, 0);
1499
1491
  });
1500
1492
 
1501
- it('an UNREQUESTED address in the proof section fails', () => {
1493
+ it('an UNREQUESTED address in the proof section fails', async () => {
1502
1494
  const sb = makeSandbox();
1503
1495
  seedFedChangeSet(sb);
1504
- const fed = fedRun(sb, { AGY_FAKE_PROOF_EXTRA: '1' });
1496
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_EXTRA: '1' });
1505
1497
  const receipts = readReceipts(sb.repo);
1506
1498
  rmSync(sb.home, { recursive: true, force: true });
1507
1499
  assert.equal(fed.status, 4, fed.stderr);
@@ -1509,10 +1501,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1509
1501
  assert.equal(receipts.length, 0);
1510
1502
  });
1511
1503
 
1512
- it('a marker BURIED inside a sentence is not an echo (the anchor is real)', () => {
1504
+ it('a marker BURIED inside a sentence is not an echo (the anchor is real)', async () => {
1513
1505
  const sb = makeSandbox();
1514
1506
  seedFedChangeSet(sb);
1515
- const fed = fedRun(sb, { AGY_FAKE_PROOF_NESTED: '2' });
1507
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_NESTED: '2' });
1516
1508
  const receipts = readReceipts(sb.repo);
1517
1509
  rmSync(sb.home, { recursive: true, force: true });
1518
1510
  assert.equal(fed.status, 4, fed.stderr);
@@ -1523,10 +1515,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1523
1515
  // and array indexing as `08`, which bash reads as OCTAL — the wrapper crashed with `value too
1524
1516
  // great for base` instead of the contracted clean refusal, and a safely padded `01` also mismatched
1525
1517
  // the unpadded `1`. Both die at the PARSE boundary now: awk hands bash plain decimals.
1526
- it('a zero-padded proof address is normalized, never an octal crash and never a false refusal', () => {
1518
+ it('a zero-padded proof address is normalized, never an octal crash and never a false refusal', async () => {
1527
1519
  const sb = makeSandbox();
1528
1520
  seedFedChangeSet(sb);
1529
- const fed = fedRun(sb, { AGY_FAKE_PROOF_PAD: '1' });
1521
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_PAD: '1' });
1530
1522
  const receipts = readReceipts(sb.repo);
1531
1523
  rmSync(sb.home, { recursive: true, force: true });
1532
1524
  assert.equal(fed.status, 0, `a padded address must pass, not crash: ${fed.stderr}`);
@@ -1535,20 +1527,20 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1535
1527
  assert.equal(receipts[0].delivery, 'fed');
1536
1528
  });
1537
1529
 
1538
- it('a capitalized heading and anchor still count — and the payload keeps its own case', () => {
1530
+ it('a capitalized heading and anchor still count — and the payload keeps its own case', async () => {
1539
1531
  const sb = makeSandbox();
1540
1532
  seedFedChangeSet(sb);
1541
- const fed = fedRun(sb, { AGY_FAKE_PROOF_CASE: '1' });
1533
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_CASE: '1' });
1542
1534
  const receipts = readReceipts(sb.repo);
1543
1535
  rmSync(sb.home, { recursive: true, force: true });
1544
1536
  assert.equal(fed.status, 0, `a capitalization must not fail a real delivery: ${fed.stderr}`);
1545
1537
  assert.equal(receipts.length, 1, 'the review attests');
1546
1538
  });
1547
1539
 
1548
- it('an absurd proof address never reaches bash arithmetic — no crash, no impersonated address', () => {
1540
+ it('an absurd proof address never reaches bash arithmetic — no crash, no impersonated address', async () => {
1549
1541
  const sb = makeSandbox();
1550
1542
  seedFedChangeSet(sb);
1551
- const fed = fedRun(sb, { AGY_FAKE_PROOF_HUGE: '2' });
1543
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_HUGE: '2' });
1552
1544
  const receipts = readReceipts(sb.repo);
1553
1545
  rmSync(sb.home, { recursive: true, force: true });
1554
1546
  assert.equal(fed.status, 4, fed.stderr);
@@ -1558,10 +1550,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1558
1550
 
1559
1551
  // Dropping an out-of-range address made it INVISIBLE: an answer with every correct echo plus one
1560
1552
  // invented giant address then satisfied a grammar whose whole point is that it is closed.
1561
- it('a VALID proof carrying one extra out-of-range address still fails — an invented address is never invisible', () => {
1553
+ it('a VALID proof carrying one extra out-of-range address still fails — an invented address is never invisible', async () => {
1562
1554
  const sb = makeSandbox();
1563
1555
  seedFedChangeSet(sb);
1564
- const fed = fedRun(sb, { AGY_FAKE_PROOF_HUGE_EXTRA: '1' });
1556
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_HUGE_EXTRA: '1' });
1565
1557
  const receipts = readReceipts(sb.repo);
1566
1558
  rmSync(sb.home, { recursive: true, force: true });
1567
1559
  assert.equal(fed.status, 4, fed.stderr);
@@ -1569,18 +1561,157 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1569
1561
  assert.equal(receipts.length, 0, 'a closed grammar does not mint a receipt beside an invented address');
1570
1562
  });
1571
1563
 
1572
- // The candidate cap was 25, so a change set whose first 25 middle-nearest candidates all fail the
1573
- // fixed-string checks earned a false "no usable candidate" refusal while candidate 26 was fine.
1574
- it('a part whose first 25 candidates are unusable still finds the one after them', () => {
1564
+ // A model told to count lines BY READING cannot reach line 1000, and a proof it cannot answer is a
1565
+ // false refusal on a correctly delivered change set the failure that made this lane unusable for
1566
+ // exactly the payloads it exists for. The address must therefore stay near the part's start. The
1567
+ // banner guard is the other half: without it part 1 resolves to `=== repo file map (git ls-files)
1568
+ // ===`, which the wrapper emits for EVERY change set, so echoing it would prove nothing.
1569
+ it('proof addresses stay countable on a multi-part change set', async () => {
1570
+ const sb = makeSandbox();
1571
+ const body = Array.from({ length: 2400 }, (_, i) => `unique body marker line ${String(i).padStart(4, '0')}`);
1572
+ writeFileSync(join(sb.repo, 'oversized.txt'), `${body.join('\n')}\n`);
1573
+ const fed = await fedRun(sb, { AGY_MAX_PROMPT_BYTES: String(FED_WIDE_CAP) });
1574
+ const receipts = readReceipts(sb.repo);
1575
+ rmSync(sb.home, { recursive: true, force: true });
1576
+ assert.equal(fed.status, 0, fed.stderr);
1577
+ assert.equal(receipts.length, 1, 'a countable proof still mints its receipt');
1578
+ assert.equal(receipts[0].delivery, 'fed');
1579
+ const bodies = fed.prompts.slice(0, -1).map(bodyOf);
1580
+ assert.ok(bodies.length >= 3, `the fixture must really chunk (got ${bodies.length} parts)`);
1581
+ for (const { part, line } of requestedOf(fed.prompts[fed.prompts.length - 1])) {
1582
+ assert.ok(line <= MAX_COUNTABLE_PROOF_ADDRESS,
1583
+ `part ${part} asks for line ${line} — a model counting by reading cannot reach it`);
1584
+ const chosen = bodies[part - 1].split('\n')[line - 1].trim();
1585
+ assert.ok(!isAssemblerBanner(chosen),
1586
+ `part ${part} proves delivery with a line the wrapper emits for every change set: ${chosen}`);
1587
+ }
1588
+ });
1589
+
1590
+ // The reject is the assembler's VOCABULARY, not the `=== … ===` shape. Refusing the whole class
1591
+ // discards a change set's own banner-shaped prose, and in a part where nothing else qualifies that
1592
+ // is the false refusal this lane exists to prevent — so an admissible one must still be CHOSEN.
1593
+ // The per-path `=== untracked: <p> ===` is in the reject set for the opposite reason: it is not a
1594
+ // fixed string, and part 1 carries the git-status block a model would rebuild it from.
1595
+ it('a change set may prove delivery with its own banner-shaped line, never with the assembler\'s', async () => {
1596
+ const sb = makeSandbox();
1597
+ const admissible = '=== Deployment configuration notes ===';
1598
+ const body = [admissible, ...Array.from({ length: 900 }, (_, i) => `unique body marker line ${String(i).padStart(4, '0')}`)];
1599
+ writeFileSync(join(sb.repo, 'oversized.txt'), `${body.join('\n')}\n`);
1600
+ const fed = await fedRun(sb);
1601
+ rmSync(sb.home, { recursive: true, force: true });
1602
+ assert.equal(fed.status, 0, `an admissible banner-shaped line must not earn a refusal: ${fed.stderr}`);
1603
+ const bodies = fed.prompts.slice(0, -1).map(bodyOf);
1604
+ const requested = requestedOf(fed.prompts[fed.prompts.length - 1]);
1605
+ const part1 = requested.find((r) => r.part === 1);
1606
+ assert.equal(bodies[0].split('\n')[part1.line - 1].trim(), admissible,
1607
+ 'the change set\'s own banner-shaped line is the first admissible candidate and must be chosen');
1608
+ for (const { part, line } of requested) {
1609
+ assert.ok(!isAssemblerBanner(bodies[part - 1].split('\n')[line - 1].trim()),
1610
+ `part ${part} proves delivery with an assembler banner`);
1611
+ }
1612
+ });
1613
+
1614
+ // A head-first walk lands where the DERIVABLE lines live. The repo map and the status block travel
1615
+ // in part 1, so a model holding part 1 can reproduce a tracked path, its `diff --git a/P b/P`
1616
+ // header and the `--- a/P` / `+++ b/P` pair for a LATER part it never received — and prove a
1617
+ // delivery that never happened. Enumerating those families as a blacklist was the wrong shape; the
1618
+ // wrapper feeds what it can COMPUTE into $nonbody and lets the existing filter reject it.
1619
+ it('no proof line is one a model could derive from the repo map or status block', async () => {
1620
+ const sb = makeSandbox();
1621
+ // Long tracked paths, so the map/header lines clear the 24-byte candidate floor and really compete.
1622
+ const tracked = Array.from({ length: 12 }, (_, i) => `src/deeply/nested/module-directory-${String(i).padStart(3, '0')}.mjs`);
1623
+ mkdirSync(join(sb.repo, 'src', 'deeply', 'nested'), { recursive: true });
1624
+ for (const rel of tracked) writeFileSync(join(sb.repo, rel), `export const seed = ${JSON.stringify(rel)};\n`);
1625
+ sb.g('add', '-A');
1626
+ sb.g('commit', '-qm', 'tracked seed');
1627
+ for (const [i, rel] of tracked.entries()) {
1628
+ const extra = Array.from({ length: 60 }, (_, j) => `changed body line ${i}-${String(j).padStart(3, '0')} in ${rel}`);
1629
+ writeFileSync(join(sb.repo, rel), `export const seed = ${JSON.stringify(rel)};\n${extra.join('\n')}\n`);
1630
+ }
1631
+ const fed = await fedRun(sb);
1632
+ rmSync(sb.home, { recursive: true, force: true });
1633
+ assert.equal(fed.status, 0, fed.stderr);
1634
+ const bodies = fed.prompts.slice(0, -1).map(bodyOf);
1635
+ assert.ok(bodies.length >= 2, `the fixture must really chunk (got ${bodies.length} parts)`);
1636
+ const derivable = new Set(tracked.flatMap((p) => [p, `diff --git a/${p} b/${p}`, `--- a/${p}`, `+++ b/${p}`, `?? ${p}`, `M ${p}`]));
1637
+ for (const { part, line } of requestedOf(fed.prompts[fed.prompts.length - 1])) {
1638
+ const chosen = bodies[part - 1].split('\n')[line - 1].trim();
1639
+ assert.ok(!derivable.has(chosen),
1640
+ `part ${part} proves delivery with a line part 1 already reveals: ${chosen}`);
1641
+ assert.ok(!isAssemblerBanner(chosen), `part ${part} proves delivery with an assembler banner: ${chosen}`);
1642
+ }
1643
+ });
1644
+
1645
+ // A RENAME is the case no re-derivation could have covered: git writes `diff --git a/old b/new`
1646
+ // plus a `rename from`/`rename to` pair, none of which follows from the new path alone — and the
1647
+ // status block spells the rename out, so all of it is derivable. Slicing the assembled artifact
1648
+ // catches them because it never has to guess what git wrote.
1649
+ it('a renamed path leaks no proof line — the derivable set comes from the artifact, not a guess', async () => {
1650
+ const sb = makeSandbox();
1651
+ const from = 'src/original/long-enough-module-name-before-rename.mjs';
1652
+ const to = 'src/relocated/long-enough-module-name-after-rename.mjs';
1653
+ mkdirSync(join(sb.repo, 'src', 'original'), { recursive: true });
1654
+ mkdirSync(join(sb.repo, 'src', 'relocated'), { recursive: true });
1655
+ const seed = Array.from({ length: 400 }, (_, i) => `stable renamed body line ${String(i).padStart(4, '0')} carrying enough bytes`);
1656
+ writeFileSync(join(sb.repo, from), `${seed.join('\n')}\n`);
1657
+ sb.g('add', '-A');
1658
+ sb.g('commit', '-qm', 'seed for rename');
1659
+ sb.g('mv', from, to);
1660
+ writeFileSync(join(sb.repo, to), `${seed.join('\n')}\nappended line so the rename also carries a diff\n`);
1661
+ sb.g('add', '-A');
1662
+ // Rename detection collapses the diff to a few header lines, so the change set needs real bulk
1663
+ // elsewhere or the review never leaves the inline lane and there is no proof to inspect.
1664
+ seedFedChangeSet(sb);
1665
+ const fed = await fedRun(sb);
1666
+ rmSync(sb.home, { recursive: true, force: true });
1667
+ assert.equal(fed.status, 0, fed.stderr);
1668
+ const bodies = fed.prompts.slice(0, -1).map(bodyOf);
1669
+ const renameLines = new Set([
1670
+ `diff --git a/${from} b/${to}`, `rename from ${from}`, `rename to ${to}`,
1671
+ `--- a/${from}`, `+++ b/${to}`, from, to, `R ${from} -> ${to}`,
1672
+ ]);
1673
+ for (const { part, line } of requestedOf(fed.prompts[fed.prompts.length - 1])) {
1674
+ const chosen = bodies[part - 1].split('\n')[line - 1].trim();
1675
+ assert.ok(!renameLines.has(chosen), `part ${part} proves delivery with a rename header: ${chosen}`);
1676
+ }
1677
+ });
1678
+
1679
+ // The derivable-set scan reads the artifact by section, and an untracked file's own content can
1680
+ // contain anything — including a line that looks exactly like a section banner. If that flipped the
1681
+ // scan back into metadata mode, every line after it would be swept into the set, starving the parts
1682
+ // that follow of candidates and refusing a change set that is perfectly provable.
1683
+ it('a banner-shaped line inside untracked CONTENT does not sweep the rest of the change set', async () => {
1684
+ const sb = makeSandbox();
1685
+ const body = Array.from({ length: 900 }, (_, i) => `unique body marker line ${String(i).padStart(4, '0')}`);
1686
+ body.splice(1, 0, '=== git status (porcelain) ===');
1687
+ writeFileSync(join(sb.repo, 'oversized.txt'), `${body.join('\n')}\n`);
1688
+ const fed = await fedRun(sb);
1689
+ const receipts = readReceipts(sb.repo);
1690
+ rmSync(sb.home, { recursive: true, force: true });
1691
+ assert.equal(fed.status, 0, `content that merely LOOKS like a banner must not starve the walk: ${fed.stderr}`);
1692
+ assert.equal(receipts.length, 1, 'the review still mints its receipt');
1693
+ const bodies = fed.prompts.slice(0, -1).map(bodyOf);
1694
+ for (const { part, line } of requestedOf(fed.prompts[fed.prompts.length - 1])) {
1695
+ const chosen = bodies[part - 1].split('\n')[line - 1].trim();
1696
+ assert.match(chosen, /^unique body marker line \d{4}$/,
1697
+ `part ${part} settled on ${chosen} instead of ordinary untracked content`);
1698
+ }
1699
+ });
1700
+
1701
+ // The candidate cap was 25, so a change set whose first 25 candidates all fail the fixed-string
1702
+ // checks earned a false "no usable candidate" refusal while candidate 26 was fine. The decoys sit
1703
+ // FIRST in the fixture because the walk starts at the part's head — buried mid-file they would
1704
+ // never be reached, and this regression would pass while proving nothing.
1705
+ it('a part whose first 25 candidates are unusable still finds the one after them', async () => {
1575
1706
  const sb = makeSandbox();
1576
1707
  // Each decoy is a unique WHOLE line (so it survives the cheap prefilter) that also occurs as a
1577
1708
  // SUBSTRING of a longer line — exactly the case the exact occurrence check must reject.
1578
1709
  const decoys = Array.from({ length: 30 }, (_, i) => `decoy candidate ${String(i).padStart(3, '0')} — appears twice as a substring`);
1579
1710
  const echoes = decoys.map((d) => `carrier line wrapping ${d} inside a longer line`);
1580
1711
  const filler = (from, n) => Array.from({ length: n }, (_, i) => `unique change-set line ${String(from + i).padStart(4, '0')} — a distinctive body marker ${'x'.repeat(20)}`);
1581
- const body = [...filler(0, 40), ...decoys, ...filler(40, 40), ...echoes, ...filler(80, 60)];
1712
+ const body = [...decoys, ...filler(0, 40), ...echoes, ...filler(40, 100)];
1582
1713
  writeFileSync(join(sb.repo, 'oversized.txt'), `${body.join('\n')}\n`);
1583
- const fed = fedRun(sb);
1714
+ const fed = await fedRun(sb);
1584
1715
  rmSync(sb.home, { recursive: true, force: true });
1585
1716
  assert.equal(fed.status, 0, `a usable candidate past position 25 must be found: ${fed.stderr}`);
1586
1717
  const final = fed.prompts[fed.prompts.length - 1];
@@ -1589,12 +1720,20 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1589
1720
  const chosen = bodies[part - 1].split('\n')[line - 1].trim();
1590
1721
  assert.ok(!decoys.includes(chosen), `a twice-occurring decoy was chosen for part ${part}: ${chosen}`);
1591
1722
  }
1723
+ // Not choosing a decoy is cheap to satisfy by never REACHING one, so the walk must be shown to
1724
+ // have crossed the whole unusable run — that, not the negative, is the no-cap property.
1725
+ const part1 = bodies[0].split('\n');
1726
+ const lastDecoyAt = part1.reduce((at, l, i) => (decoys.includes(l.trim()) ? i + 1 : at), 0);
1727
+ assert.ok(lastDecoyAt > 0, 'the decoy block must land in part 1, or this regression proves nothing');
1728
+ const part1Line = requestedOf(final).find((r) => r.part === 1).line;
1729
+ assert.ok(part1Line > lastDecoyAt,
1730
+ `part 1 settled on line ${part1Line}, before the decoy run ended at ${lastDecoyAt} — never traversed`);
1592
1731
  });
1593
1732
 
1594
- it('a harmless `- ` bullet still counts — the anchor is strict, not brittle', () => {
1733
+ it('a harmless `- ` bullet still counts — the anchor is strict, not brittle', async () => {
1595
1734
  const sb = makeSandbox();
1596
1735
  seedFedChangeSet(sb);
1597
- const fed = fedRun(sb, { AGY_FAKE_PROOF_BULLET: '1' });
1736
+ const fed = await fedRun(sb, { AGY_FAKE_PROOF_BULLET: '1' });
1598
1737
  const receipts = readReceipts(sb.repo);
1599
1738
  rmSync(sb.home, { recursive: true, force: true });
1600
1739
  assert.equal(fed.status, 0, `a bulleted proof must not be a false refusal: ${fed.stderr}`);
@@ -1602,10 +1741,10 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1602
1741
  assert.equal(receipts[0].delivery, 'fed');
1603
1742
  });
1604
1743
 
1605
- it('the selected lines never appear in any envelope the wrapper sends', () => {
1744
+ it('the selected lines never appear in any envelope the wrapper sends', async () => {
1606
1745
  const sb = makeSandbox();
1607
1746
  seedFedChangeSet(sb);
1608
- const fed = fedRun(sb);
1747
+ const fed = await fedRun(sb);
1609
1748
  rmSync(sb.home, { recursive: true, force: true });
1610
1749
  assert.equal(fed.status, 0, fed.stderr);
1611
1750
  const final = fed.prompts[fed.prompts.length - 1];
@@ -1628,7 +1767,7 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1628
1767
  // anywhere but the body it is being asked to prove — so it must occur exactly once across the
1629
1768
  // bodies AND nowhere in what the wrapper itself sends, including the request line that names the
1630
1769
  // addresses (which only exists once every address is chosen).
1631
- it('a change-set line that duplicates the wrapper`s own framing is never chosen as a proof', () => {
1770
+ it('a change-set line that duplicates the wrapper`s own framing is never chosen as a proof', async () => {
1632
1771
  const sb = makeSandbox();
1633
1772
  // The change set contains lines copied verbatim out of the envelope the wrapper will send.
1634
1773
  const framing = [
@@ -1639,7 +1778,7 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1639
1778
  const filler = Array.from({ length: 400 }, (_, i) => `unique change-set line ${String(i).padStart(4, '0')} — a distinctive body marker ${'x'.repeat(20)}`);
1640
1779
  const woven = filler.flatMap((l, i) => (i % 40 === 20 ? [framing[(i / 40) | 0 % framing.length] ?? framing[0], l] : [l]));
1641
1780
  writeFileSync(join(sb.repo, 'oversized.txt'), `${woven.join('\n')}\n`);
1642
- const fed = fedRun(sb);
1781
+ const fed = await fedRun(sb);
1643
1782
  rmSync(sb.home, { recursive: true, force: true });
1644
1783
  assert.equal(fed.status, 0, fed.stderr);
1645
1784
  const final = fed.prompts[fed.prompts.length - 1];
@@ -1658,7 +1797,7 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1658
1797
  }
1659
1798
  });
1660
1799
 
1661
- it('a change-set line whose text IS a request address is never chosen (the request would reveal it)', () => {
1800
+ it('a change-set line whose text IS a request address is never chosen (the request would reveal it)', async () => {
1662
1801
  const sb = makeSandbox();
1663
1802
  // Seed every plausible address form the request line could carry, so a naive selector that
1664
1803
  // filters only against the PRE-request envelope can pick one of them.
@@ -1666,7 +1805,7 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1666
1805
  const filler = Array.from({ length: 400 }, (_, i) => `unique change-set line ${String(i).padStart(4, '0')} — a distinctive body marker ${'x'.repeat(20)}`);
1667
1806
  const woven = filler.flatMap((l, i) => (i % 7 === 3 && addresses[(i / 7) | 0] ? [addresses[(i / 7) | 0], l] : [l]));
1668
1807
  writeFileSync(join(sb.repo, 'oversized.txt'), `${woven.join('\n')}\n`);
1669
- const fed = fedRun(sb);
1808
+ const fed = await fedRun(sb);
1670
1809
  rmSync(sb.home, { recursive: true, force: true });
1671
1810
  assert.equal(fed.status, 0, fed.stderr);
1672
1811
  const final = fed.prompts[fed.prompts.length - 1];
@@ -1681,9 +1820,9 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1681
1820
  }
1682
1821
  });
1683
1822
 
1684
- it('an UNDER-cap single-turn review declares delivery `inline` and still attests', () => {
1823
+ it('an UNDER-cap single-turn review declares delivery `inline` and still attests', async () => {
1685
1824
  const sb = makeSandbox();
1686
- const r = run(sb, { args: ['code', '--facts', 'f'] });
1825
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
1687
1826
  const receipts = readReceipts(sb.repo);
1688
1827
  rmSync(sb.home, { recursive: true, force: true });
1689
1828
  assert.equal(r.status, 0, r.stderr);
@@ -1691,13 +1830,13 @@ describe('agy-review.sh — fed lane: delivery is PROVEN or the review FAILS (D1
1691
1830
  });
1692
1831
  });
1693
1832
 
1694
- describe('agy-review.sh — size ceiling + gated --add-dir escape (6)', () => {
1833
+ describe('agy-review.sh — size ceiling + gated --add-dir escape (6)', { concurrency: 2 }, () => {
1695
1834
  // D2: chunking is CODE-mode only. A plan/diff artifact is an operator-supplied file the operator
1696
1835
  // can split, so those modes keep today's refuse-over-cap behaviour verbatim.
1697
- it('plan mode: an oversized prompt exits 2 with guidance, agy not invoked (chunking is code-only)', () => {
1836
+ it('plan mode: an oversized prompt exits 2 with guidance, agy not invoked (chunking is code-only)', async () => {
1698
1837
  const sb = makeSandbox();
1699
1838
  writeFileSync(join(sb.repo, 'big-plan.md'), `# plan\n${'a plan line that is long enough to matter\n'.repeat(400)}`);
1700
- const r = run(sb, { args: ['plan', 'big-plan.md', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: '4000' } });
1839
+ const r = await run(sb, { args: ['plan', 'big-plan.md', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: '4000' } });
1701
1840
  rmSync(sb.home, { recursive: true, force: true });
1702
1841
  assert.equal(r.status, 2, r.stderr);
1703
1842
  assert.match(r.stderr, /over AGY_MAX_PROMPT_BYTES=4000/);
@@ -1708,10 +1847,10 @@ describe('agy-review.sh — size ceiling + gated --add-dir escape (6)', () => {
1708
1847
 
1709
1848
  // D3: the offload is RETIRED, not removed. The key stays recognized (an existing settings line must
1710
1849
  // never start warning as unknown) but it arms nothing, and setting it says so.
1711
- it('a set AGY_REVIEW_ALLOW_ADDDIR prints the retirement notice and does not pass --add-dir', () => {
1850
+ it('a set AGY_REVIEW_ALLOW_ADDDIR prints the retirement notice and does not pass --add-dir', async () => {
1712
1851
  const sb = makeSandbox();
1713
1852
  seedFedChangeSet(sb);
1714
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '1' } });
1853
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '1' } });
1715
1854
  rmSync(sb.home, { recursive: true, force: true });
1716
1855
  assert.equal(r.status, 0, r.stderr);
1717
1856
  assert.match(r.stderr, /AGY_REVIEW_ALLOW_ADDDIR is set \(env\) but it is RETIRED/, 'the notice names the retirement AND where the dead value came from');
@@ -1721,19 +1860,19 @@ describe('agy-review.sh — size ceiling + gated --add-dir escape (6)', () => {
1721
1860
  assert.match(r.stderr, /feeding the change set in \d+ part\(s\)/, 'the fed lane runs regardless of the retired knob');
1722
1861
  });
1723
1862
 
1724
- it('the settings registry still recognizes the retired key (an existing line never warns as unknown)', () => {
1863
+ it('the settings registry still recognizes the retired key (an existing line never warns as unknown)', async () => {
1725
1864
  const sb = makeSandbox();
1726
1865
  writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=1\n');
1727
- const r = run(sb, { args: ['code', '--facts', 'f'] });
1866
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
1728
1867
  rmSync(sb.home, { recursive: true, force: true });
1729
1868
  assert.equal(r.status, 0, r.stderr);
1730
1869
  assert.doesNotMatch(r.stderr, /unknown key 'AGY_REVIEW_ALLOW_ADDDIR'/);
1731
1870
  });
1732
1871
 
1733
- it('the staging dir is trap-cleaned on exit (no leftover after the run)', () => {
1872
+ it('the staging dir is trap-cleaned on exit (no leftover after the run)', async () => {
1734
1873
  const sb = makeSandbox();
1735
1874
  seedFedChangeSet(sb);
1736
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
1875
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
1737
1876
  // The fed lane dispatches every turn FROM the staging dir, so the fake's own cwd names it.
1738
1877
  const stagingPath = r.dispatchCwd;
1739
1878
  const stillThere = stagingPath ? existsSync(stagingPath) : false;
@@ -1743,11 +1882,11 @@ describe('agy-review.sh — size ceiling + gated --add-dir escape (6)', () => {
1743
1882
  });
1744
1883
  });
1745
1884
 
1746
- describe('agy-review.sh — resume / round-2 delta (7)', () => {
1747
- it('--continue takes NO mode, sends a delta (shape + focus + decided), never re-embeds the artifact', () => {
1885
+ describe('agy-review.sh — resume / round-2 delta (7)', { concurrency: 2 }, () => {
1886
+ it('--continue takes NO mode, sends a delta (shape + focus + decided), never re-embeds the artifact', async () => {
1748
1887
  const sb = makeSandbox();
1749
1888
  writeFileSync(join(sb.repo, 'decided.md'), 'ALREADY_DECIDED_ITEM\n');
1750
- const r = run(sb, { args: ['--continue', '--decided', '@decided.md', '--focus', 'ROUND2_FOCUS'] });
1889
+ const r = await run(sb, { args: ['--continue', '--decided', '@decided.md', '--focus', 'ROUND2_FOCUS'] });
1751
1890
  rmSync(sb.home, { recursive: true, force: true });
1752
1891
  assert.equal(r.status, 0, r.stderr);
1753
1892
  assert.match(r.argv, /(^|\n)--continue(\n|$)/, 'agy is continued');
@@ -1758,10 +1897,10 @@ describe('agy-review.sh — resume / round-2 delta (7)', () => {
1758
1897
  assert.doesNotMatch(r.prompt, /repo file map/, 'a continuation must NOT re-assemble the artifact');
1759
1898
  });
1760
1899
 
1761
- it('--continue rejects a mode token and rejects --facts', () => {
1900
+ it('--continue rejects a mode token and rejects --facts', async () => {
1762
1901
  const sb = makeSandbox();
1763
- const r1 = run(sb, { args: ['--continue', 'code'] });
1764
- const r2 = run(sb, { args: ['--continue', '--facts', 'x'] });
1902
+ const r1 = await run(sb, { args: ['--continue', 'code'] });
1903
+ const r2 = await run(sb, { args: ['--continue', '--facts', 'x'] });
1765
1904
  rmSync(sb.home, { recursive: true, force: true });
1766
1905
  assert.equal(r1.status, 2);
1767
1906
  assert.match(r1.stderr, /takes no positional args/);
@@ -1769,9 +1908,9 @@ describe('agy-review.sh — resume / round-2 delta (7)', () => {
1769
1908
  assert.match(r2.stderr, /--facts is not valid on a continuation/);
1770
1909
  });
1771
1910
 
1772
- it('--conversation <id> threads the id through to agy', () => {
1911
+ it('--conversation <id> threads the id through to agy', async () => {
1773
1912
  const sb = makeSandbox();
1774
- const r = run(sb, { args: ['--conversation', 'conv-xyz', '--focus', 'f'] });
1913
+ const r = await run(sb, { args: ['--conversation', 'conv-xyz', '--focus', 'f'] });
1775
1914
  rmSync(sb.home, { recursive: true, force: true });
1776
1915
  assert.equal(r.status, 0, r.stderr);
1777
1916
  assert.match(r.argv, /(^|\n)--conversation(\n|$)/);
@@ -1779,7 +1918,7 @@ describe('agy-review.sh — resume / round-2 delta (7)', () => {
1779
1918
  });
1780
1919
  });
1781
1920
 
1782
- describe('agy-review.sh — delegated guards inherited via agy-run (9, 10)', { concurrency: true }, () => {
1921
+ describe('agy-review.sh — delegated guards inherited via agy-run (9, 10)', { concurrency: 2 }, () => {
1783
1922
  it('hard timeout: a sleeping stub is killed at AGY_HARD_TIMEOUT', async () => {
1784
1923
  const sb = makeSandbox();
1785
1924
  const started = Date.now();
@@ -1791,9 +1930,9 @@ describe('agy-review.sh — delegated guards inherited via agy-run (9, 10)', { c
1791
1930
  assert.match(r.stderr, /exceeded the hard cap/);
1792
1931
  });
1793
1932
 
1794
- it('subscription invariant: a stray FOO_API_KEY is unset for the agy subprocess', () => {
1933
+ it('subscription invariant: a stray FOO_API_KEY is unset for the agy subprocess', async () => {
1795
1934
  const sb = makeSandbox();
1796
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { FOO_API_KEY: 'bar', ANTIGRAVITY_API_KEY: 'baz' } });
1935
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { FOO_API_KEY: 'bar', ANTIGRAVITY_API_KEY: 'baz' } });
1797
1936
  rmSync(sb.home, { recursive: true, force: true });
1798
1937
  assert.equal(r.status, 0, r.stderr);
1799
1938
  assert.match(r.capEnv, /^FOO_API_KEY=<unset>$/m);
@@ -1801,81 +1940,81 @@ describe('agy-review.sh — delegated guards inherited via agy-run (9, 10)', { c
1801
1940
  });
1802
1941
  });
1803
1942
 
1804
- describe('agy-review.sh — mode / arg validation (11)', () => {
1805
- it('unknown mode → usage + exit 2', () => {
1943
+ describe('agy-review.sh — mode / arg validation (11)', { concurrency: 2 }, () => {
1944
+ it('unknown mode → usage + exit 2', async () => {
1806
1945
  const sb = makeSandbox();
1807
- const r = run(sb, { args: ['bogus'] });
1946
+ const r = await run(sb, { args: ['bogus'] });
1808
1947
  rmSync(sb.home, { recursive: true, force: true });
1809
1948
  assert.equal(r.status, 2);
1810
1949
  assert.match(r.stderr, /usage:/);
1811
1950
  assert.equal(r.invoked, false);
1812
1951
  });
1813
1952
 
1814
- it('plan mode with a missing file → exit 2', () => {
1953
+ it('plan mode with a missing file → exit 2', async () => {
1815
1954
  const sb = makeSandbox();
1816
- const r = run(sb, { args: ['plan', 'nope.md'] });
1955
+ const r = await run(sb, { args: ['plan', 'nope.md'] });
1817
1956
  rmSync(sb.home, { recursive: true, force: true });
1818
1957
  assert.equal(r.status, 2);
1819
1958
  assert.match(r.stderr, /plan file 'nope\.md' not found/);
1820
1959
  });
1821
1960
 
1822
- it('diff mode inlines the supplied file', () => {
1961
+ it('diff mode inlines the supplied file', async () => {
1823
1962
  const sb = makeSandbox();
1824
1963
  writeFileSync(join(sb.repo, 'change.diff'), 'DIFF_FILE_BODY_MARKER\n');
1825
- const r = run(sb, { args: ['diff', 'change.diff', '--facts', 'f'] });
1964
+ const r = await run(sb, { args: ['diff', 'change.diff', '--facts', 'f'] });
1826
1965
  rmSync(sb.home, { recursive: true, force: true });
1827
1966
  assert.equal(r.status, 0, r.stderr);
1828
1967
  assert.match(r.prompt, /The diff under review/);
1829
1968
  assert.match(r.prompt, /DIFF_FILE_BODY_MARKER/);
1830
1969
  });
1831
1970
 
1832
- it('rejects a stray -- passthrough (the wrapper owns the posture)', () => {
1971
+ it('rejects a stray -- passthrough (the wrapper owns the posture)', async () => {
1833
1972
  const sb = makeSandbox();
1834
- const r = run(sb, { args: ['code', '--', '--add-dir', '.'] });
1973
+ const r = await run(sb, { args: ['code', '--', '--add-dir', '.'] });
1835
1974
  rmSync(sb.home, { recursive: true, force: true });
1836
1975
  assert.equal(r.status, 2);
1837
1976
  assert.match(r.stderr, /this wrapper OWNS the review posture/);
1838
1977
  });
1839
1978
 
1840
- it('rejects a value-flag that swallows the NEXT flag as its value (--facts --focus x → exit 2)', () => {
1979
+ it('rejects a value-flag that swallows the NEXT flag as its value (--facts --focus x → exit 2)', async () => {
1841
1980
  const sb = makeSandbox();
1842
- const r = run(sb, { args: ['code', '--facts', '--focus', 'x'] });
1981
+ const r = await run(sb, { args: ['code', '--facts', '--focus', 'x'] });
1843
1982
  rmSync(sb.home, { recursive: true, force: true });
1844
1983
  assert.equal(r.status, 2, r.stderr);
1845
1984
  assert.match(r.stderr, /--facts needs a value/);
1846
1985
  assert.equal(r.invoked, false, 'a misplaced flag must not be spent as bogus grounding');
1847
1986
  });
1848
1987
 
1849
- it('rejects a value-flag with no value at the end of args (--decided → exit 2)', () => {
1988
+ it('rejects a value-flag with no value at the end of args (--decided → exit 2)', async () => {
1850
1989
  const sb = makeSandbox();
1851
- const r = run(sb, { args: ['code', '--facts', 'f', '--decided'] });
1990
+ const r = await run(sb, { args: ['code', '--facts', 'f', '--decided'] });
1852
1991
  rmSync(sb.home, { recursive: true, force: true });
1853
1992
  assert.equal(r.status, 2, r.stderr);
1854
1993
  assert.match(r.stderr, /--decided needs a value/);
1855
1994
  });
1856
1995
  });
1857
1996
 
1858
- describe('agy-review.sh — no-env run (12)', () => {
1859
- it('a code review with NO AGY_* env vars runs cleanly (no unbound-var abort under set -u)', () => {
1997
+ describe('agy-review.sh — no-env run (12)', { concurrency: 2 }, () => {
1998
+ it('a code review with NO AGY_* env vars runs cleanly (no unbound-var abort under set -u)', async () => {
1860
1999
  const sb = makeSandbox();
1861
2000
  // run() sets only HOME/PATH + the AGY_FAKE_* capture vars (not AGY_* config) — so this exercises
1862
2001
  // the all-defaults path.
1863
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2002
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
1864
2003
  rmSync(sb.home, { recursive: true, force: true });
1865
2004
  assert.equal(r.status, 0, r.stderr);
1866
2005
  assert.equal(r.invoked, true);
1867
2006
  });
1868
2007
  });
1869
2008
 
1870
- describe('agy-review.sh — subdir invocation is repo-complete (13)', () => {
1871
- it('from a subdir, assembles a repo-complete change set AND reads a relative --facts path', () => {
2009
+ describe('agy-review.sh — subdir invocation is repo-complete (13)', { concurrency: 2 }, () => {
2010
+ it('from a subdir, assembles a repo-complete change set AND reads a relative --facts path', async () => {
1872
2011
  const sb = makeSandbox();
1873
2012
  // a change to a ROOT file (sibling of the subdir we invoke from)
1874
2013
  writeFileSync(join(sb.repo, 'root-change.txt'), 'ROOT_SIBLING_CHANGE\n');
1875
2014
  const sub = join(sb.repo, 'deep', 'nested');
1876
2015
  mkdirSync(sub, { recursive: true });
1877
2016
  writeFileSync(join(sub, 'local-facts.md'), 'SUBDIR_RELATIVE_FACT\n');
1878
- const r = run(sb, { args: ['code', '--facts', '@local-facts.md'], cwd: sub });
2017
+ const r = await run(sb, { args: ['code', '--facts', '@local-facts.md'], cwd: sub });
1879
2018
  rmSync(sb.home, { recursive: true, force: true });
1880
2019
  assert.equal(r.status, 0, r.stderr);
1881
2020
  assert.match(r.prompt, /ROOT_SIBLING_CHANGE/, 'the root/sibling change must appear (repo-complete via cd to toplevel)');
@@ -1987,8 +2126,8 @@ const consultsEnv = (source, name) =>
1987
2126
  // `<facts-file>` behind a stray character) — the catalog declares the whole token a user types.
1988
2127
  const SLOT_RE = /@?<[^<>]+>|\[[^[\]]*\]/g;
1989
2128
 
1990
- describe('agy-review.sh — --help contract (manifest-pinned)', () => {
1991
- it('--help and -h exit 0 pre-preflight (no agy, no git)', () => {
2129
+ describe('agy-review.sh — --help contract (manifest-pinned)', { concurrency: 2 }, () => {
2130
+ it('--help and -h exit 0 pre-preflight (no agy, no git)', async () => {
1992
2131
  for (const arg of ['--help', '-h']) {
1993
2132
  const r = runHelp(arg);
1994
2133
  assert.equal(r.status, 0, `${arg}: ${r.stderr}`);
@@ -1997,39 +2136,39 @@ describe('agy-review.sh — --help contract (manifest-pinned)', () => {
1997
2136
  }
1998
2137
  });
1999
2138
 
2000
- it('Usage set-EQUALS the manifest invocation descriptors (both directions)', () => {
2139
+ it('Usage set-EQUALS the manifest invocation descriptors (both directions)', async () => {
2001
2140
  const help = runHelp('--help').stdout;
2002
2141
  const got = helpSection(help, 'Usage:').filter((l) => l.startsWith('agy-review')).map(norm);
2003
2142
  assert.ok(REVIEW_CONTRACT.invocations.length > 0, 'manifest invocations must be non-empty');
2004
2143
  setEq(got, REVIEW_CONTRACT.invocations.map(norm), 'help Usage ⟷ manifest invocations');
2005
2144
  });
2006
2145
 
2007
- it('Flags set-EQUALS the manifest flag descriptors (both directions)', () => {
2146
+ it('Flags set-EQUALS the manifest flag descriptors (both directions)', async () => {
2008
2147
  const help = runHelp('--help').stdout;
2009
2148
  const got = helpSection(help, 'Flags:').filter((l) => l.startsWith('--')).map(norm);
2010
2149
  assert.ok(REVIEW_CONTRACT.flags.length > 0, 'manifest flags must be non-empty');
2011
2150
  setEq(got, REVIEW_CONTRACT.flags.map(norm), 'help Flags ⟷ manifest flags');
2012
2151
  });
2013
2152
 
2014
- it('Grounding renders the manifest grounding note verbatim', () => {
2153
+ it('Grounding renders the manifest grounding note verbatim', async () => {
2015
2154
  const help = runHelp('--help').stdout;
2016
2155
  assert.equal(norm(helpSection(help, 'Grounding:').join(' ')), norm(REVIEW_CONTRACT.grounding));
2017
2156
  });
2018
2157
 
2019
- it('Notes renders the manifest contract.notes verbatim (a typed contract key that MUST surface)', () => {
2158
+ it('Notes renders the manifest contract.notes verbatim (a typed contract key that MUST surface)', async () => {
2020
2159
  const help = runHelp('--help').stdout;
2021
2160
  assert.ok(REVIEW_CONTRACT.notes.length > 0, 'manifest notes must be non-empty');
2022
2161
  assert.equal(norm(helpSection(help, 'Notes:').join(' ')), norm(REVIEW_CONTRACT.notes.join(' ')));
2023
2162
  });
2024
2163
 
2025
- it('Round-2 / resume set-EQUALS the manifest continue descriptors', () => {
2164
+ it('Round-2 / resume set-EQUALS the manifest continue descriptors', async () => {
2026
2165
  const help = runHelp('--help').stdout;
2027
2166
  const got = helpSection(help, 'Round-2 / resume:').filter((l) => l.startsWith('agy-review')).map(norm);
2028
2167
  assert.ok(REVIEW_CONTRACT.continue.length > 0, 'manifest continue must be non-empty');
2029
2168
  setEq(got, REVIEW_CONTRACT.continue.map(norm), 'help continue ⟷ manifest continue');
2030
2169
  });
2031
2170
 
2032
- it('Receipt renders the manifest receipt contract verbatim (AD-038 three-way lockstep)', () => {
2171
+ it('Receipt renders the manifest receipt contract verbatim (AD-038 three-way lockstep)', async () => {
2033
2172
  const help = runHelp('--help').stdout;
2034
2173
  assert.equal(norm(helpSection(help, 'Receipt:').join(' ')), norm(REVIEW_CONTRACT.receipt));
2035
2174
  assert.match(REVIEW_CONTRACT.receipt, /sha256 over the canonical uncommitted-state payload/, 'the fingerprint definition lives in the manifest contract');
@@ -2037,10 +2176,10 @@ describe('agy-review.sh — --help contract (manifest-pinned)', () => {
2037
2176
  });
2038
2177
  });
2039
2178
 
2040
- describe('agy-review.sh — source-level reverse guard (parser arms ⟷ manifest)', () => {
2179
+ describe('agy-review.sh — source-level reverse guard (parser arms ⟷ manifest)', { concurrency: 2 }, () => {
2041
2180
  const arms = extractArgCaseArms(readFileSync(WRAPPER, 'utf8'));
2042
2181
 
2043
- it('the real mode arms equal the manifest modes (adding a mode without the manifest fails here)', () => {
2182
+ it('the real mode arms equal the manifest modes (adding a mode without the manifest fails here)', async () => {
2044
2183
  // Deliberately a UNION over every `case "$mode"` in the wrapper (the CLI dispatch AND the
2045
2184
  // emit_artifact renderer): the union can only be conservative — a mode added to EITHER case
2046
2185
  // without the manifest goes red; no renderer-only arm can make a missing manifest entry green.
@@ -2049,14 +2188,14 @@ describe('agy-review.sh — source-level reverse guard (parser arms ⟷ manifest
2049
2188
  setEq(new Set(modes), MANIFEST.roles.review.modes, 'parser mode arms ⟷ manifest modes');
2050
2189
  });
2051
2190
 
2052
- it('the real flag arms equal the manifest flag set (closed grammar; catch-alls excluded)', () => {
2191
+ it('the real flag arms equal the manifest flag set (closed grammar; catch-alls excluded)', async () => {
2053
2192
  const flagArms = splitArms(arms.get('"$1"')).filter((a) => !['--', '--*', '*'].includes(a));
2054
2193
  const declared = REVIEW_CONTRACT.flags.map(leadingFlag);
2055
2194
  assert.ok(declared.length > 0, 'manifest flag set must be non-empty');
2056
2195
  setEq(new Set(flagArms), new Set(declared), 'parser flag arms ⟷ manifest flags');
2057
2196
  });
2058
2197
 
2059
- it('the first-arg entrypoints are exactly --help/-h + the manifest continue flags', () => {
2198
+ it('the first-arg entrypoints are exactly --help/-h + the manifest continue flags', async () => {
2060
2199
  const declared = REVIEW_CONTRACT.continue.map(leadingFlag);
2061
2200
  assert.ok(declared.length > 0, 'manifest continue set must be non-empty');
2062
2201
  setEq(new Set(splitArms(arms.get('"${1:-}"'))), new Set(['--help', '-h', ...declared]));
@@ -2067,20 +2206,20 @@ describe('agy-review.sh — source-level reverse guard (parser arms ⟷ manifest
2067
2206
  // The kit validator owns the catalog's INTERNAL shape; these arms pin the half only this wrapper's
2068
2207
  // source can settle — the cataloged review modes ARE the real parser arms, every declared contract
2069
2208
  // invocation is cataloged, and the env-hook the catalog aims at review is a real env var.
2070
- describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)', () => {
2209
+ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)', { concurrency: 2 }, () => {
2071
2210
  const source = readFileSync(WRAPPER, 'utf8');
2072
2211
  const arms = extractArgCaseArms(source);
2073
2212
  const catalog = MANIFEST.modeCatalog ?? [];
2074
2213
  const reviewEntries = catalog.filter((e) => e.role === 'review');
2075
2214
  const reviewPrimaries = reviewEntries.filter((e) => e.kind === 'primary');
2076
2215
 
2077
- it('the catalog submodes ARE the wrapper\'s real parser mode arms (both directions)', () => {
2216
+ it('the catalog submodes ARE the wrapper\'s real parser mode arms (both directions)', async () => {
2078
2217
  const modes = splitArms(arms.get('"$mode"')).filter((a) => a !== '*');
2079
2218
  assert.ok(reviewPrimaries.length > 0, 'the manifest must catalog its review modes');
2080
2219
  setEq(new Set(reviewPrimaries.map((e) => e.submode)), new Set(modes), 'catalog submodes ⟷ real parser mode arms');
2081
2220
  });
2082
2221
 
2083
- it('every review entry composes BY REFERENCE and every reference resolves', () => {
2222
+ it('every review entry composes BY REFERENCE and every reference resolves', async () => {
2084
2223
  for (const entry of reviewEntries) {
2085
2224
  assert.ok(
2086
2225
  Array.isArray(entry.invocationRefs) && entry.invocationRefs.length > 0,
@@ -2096,7 +2235,7 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2096
2235
  }
2097
2236
  });
2098
2237
 
2099
- it('every review contract invocation is claimed by exactly ONE catalog entry (no uncataloged mode)', () => {
2238
+ it('every review contract invocation is claimed by exactly ONE catalog entry (no uncataloged mode)', async () => {
2100
2239
  const claims = reviewEntries.flatMap((e) => e.invocationRefs.map((r) => `${r.contractField}[${r.index}]`));
2101
2240
  assert.equal(new Set(claims).size, claims.length, 'a contract invocation is claimed at most once');
2102
2241
  const declared = [
@@ -2106,7 +2245,7 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2106
2245
  setEq(new Set(claims), declared, 'catalog claims ⟷ declared contract invocations');
2107
2246
  });
2108
2247
 
2109
- it('every env-hook the catalog aims at a review mode is a real EXECUTABLE guard, not a mention', () => {
2248
+ it('every env-hook the catalog aims at a review mode is a real EXECUTABLE guard, not a mention', async () => {
2110
2249
  const hooks = catalog.filter((e) => e.kind === 'env-hook' && e.parents.some((p) => reviewPrimaries.some((r) => r.key === p)));
2111
2250
  assert.ok(hooks.length > 0, 'AGY_PROBE must be cataloged as an env-hook over the review modes');
2112
2251
  for (const hook of hooks) {
@@ -2117,7 +2256,7 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2117
2256
  }
2118
2257
  });
2119
2258
 
2120
- it('the catalog operand slots set-EQUAL the slots its rendered forms really carry (both directions)', () => {
2259
+ it('the catalog operand slots set-EQUAL the slots its rendered forms really carry (both directions)', async () => {
2121
2260
  for (const entry of reviewEntries) {
2122
2261
  const forms = entry.invocationRefs.map((r) => REVIEW_CONTRACT[r.contractField][r.index]);
2123
2262
  // The DEDUPLICATED UNION over every resolved form: a plural-ref entry legitimately spreads its
@@ -2127,7 +2266,7 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2127
2266
  }
2128
2267
  });
2129
2268
 
2130
- it('an entry rendering a LITERAL descriptor is slot-checked too (env-hooks have no role to filter on)', () => {
2269
+ it('an entry rendering a LITERAL descriptor is slot-checked too (env-hooks have no role to filter on)', async () => {
2131
2270
  // The contract-backed arm above filters by role — and an env-hook HAS no role, so its descriptor
2132
2271
  // was never slot-checked at all. That is exactly how a hardcoded dead path can reach the
2133
2272
  // discovery surface looking ready-to-run. Every literal-descriptor kind is covered here:
@@ -2140,7 +2279,7 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2140
2279
  }
2141
2280
  });
2142
2281
 
2143
- it('AGY_PROBE really silences the advisory on EVERY review parent the catalog claims (behavioural)', () => {
2282
+ it('AGY_PROBE really silences the advisory on EVERY review parent the catalog claims (behavioural)', async () => {
2144
2283
  // The catalog CLAIMS these modes are modified by the hook; prove it per parent rather than
2145
2284
  // trusting a source scan: the off-frontier advisory fires without it, is silent with it.
2146
2285
  const hook = catalog.find((e) => e.key === 'AGY_PROBE');
@@ -2157,14 +2296,14 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2157
2296
  // Both runs must really REACH agy: asserting the diagnostic text alone would let an early
2158
2297
  // failure that never dispatched pass the probe-on branch (its stderr simply lacks the string).
2159
2298
  const noisy = makeSandbox();
2160
- const off = run(noisy, { args: drive[parent](noisy), env: { AGY_MODEL: 'Some Weak Model' } });
2299
+ const off = await run(noisy, { args: drive[parent](noisy), env: { AGY_MODEL: 'Some Weak Model' } });
2161
2300
  rmSync(noisy.home, { recursive: true, force: true });
2162
2301
  assert.equal(off.status, 0, `${parent}: ${off.stderr}`);
2163
2302
  assert.equal(off.invoked, true, `${parent}: the control run must reach agy`);
2164
2303
  assert.match(off.stderr, /non-frontier model/, `${parent}: the advisory must fire without the hook`);
2165
2304
 
2166
2305
  const quiet = makeSandbox();
2167
- const on = run(quiet, { args: drive[parent](quiet), env: { AGY_MODEL: 'Some Weak Model', AGY_PROBE: '1' } });
2306
+ const on = await run(quiet, { args: drive[parent](quiet), env: { AGY_MODEL: 'Some Weak Model', AGY_PROBE: '1' } });
2168
2307
  rmSync(quiet.home, { recursive: true, force: true });
2169
2308
  assert.equal(on.status, 0, `${parent}: ${on.stderr}`);
2170
2309
  assert.equal(on.invoked, true, `${parent}: AGY_PROBE=1 must still reach agy — silence must come from the hook, not from an early exit`);
@@ -2173,8 +2312,8 @@ describe('agy-review.sh — mode catalog ⟷ wrapper reality (manifest-pinned)',
2173
2312
  });
2174
2313
  });
2175
2314
 
2176
- describe('agy-review.sh — declared contract is really accepted (forward guard)', () => {
2177
- it('every manifest mode runs green', () => {
2315
+ describe('agy-review.sh — declared contract is really accepted (forward guard)', { concurrency: 2 }, () => {
2316
+ it('every manifest mode runs green', async () => {
2178
2317
  const drive = {
2179
2318
  code: () => ['code', '--facts', 'f'],
2180
2319
  plan: (sb) => { writeFileSync(join(sb.repo, 'p.md'), '# p\n'); return ['plan', 'p.md', '--facts', 'f']; },
@@ -2183,14 +2322,14 @@ describe('agy-review.sh — declared contract is really accepted (forward guard)
2183
2322
  for (const mode of MANIFEST.roles.review.modes) {
2184
2323
  assert.ok(drive[mode], `no test drive for manifest mode "${mode}" — add one`);
2185
2324
  const sb = makeSandbox();
2186
- const r = run(sb, { args: drive[mode](sb) });
2325
+ const r = await run(sb, { args: drive[mode](sb) });
2187
2326
  rmSync(sb.home, { recursive: true, force: true });
2188
2327
  assert.equal(r.status, 0, `mode ${mode}: ${r.stderr}`);
2189
2328
  assert.equal(r.invoked, true, `mode ${mode} must reach agy`);
2190
2329
  }
2191
2330
  });
2192
2331
 
2193
- it('every manifest flag is accepted in code mode', () => {
2332
+ it('every manifest flag is accepted in code mode', async () => {
2194
2333
  for (const descriptor of REVIEW_CONTRACT.flags) {
2195
2334
  const flag = leadingFlag(descriptor);
2196
2335
  const sb = makeSandbox();
@@ -2199,24 +2338,24 @@ describe('agy-review.sh — declared contract is really accepted (forward guard)
2199
2338
  const args = flag === '--facts' ? ['code', '--facts', 'f']
2200
2339
  : flag === '--ungrounded' ? ['code', '--ungrounded']
2201
2340
  : ['code', '--facts', 'f', flag, 'f'];
2202
- const r = run(sb, { args });
2341
+ const r = await run(sb, { args });
2203
2342
  rmSync(sb.home, { recursive: true, force: true });
2204
2343
  assert.equal(r.status, 0, `${flag}: ${r.stderr}`);
2205
2344
  }
2206
2345
  });
2207
2346
 
2208
- it('an invented flag is rejected (closed grammar negative)', () => {
2347
+ it('an invented flag is rejected (closed grammar negative)', async () => {
2209
2348
  const sb = makeSandbox();
2210
- const r = run(sb, { args: ['code', '--facts', 'f', '--bogus-flag'] });
2349
+ const r = await run(sb, { args: ['code', '--facts', 'f', '--bogus-flag'] });
2211
2350
  rmSync(sb.home, { recursive: true, force: true });
2212
2351
  assert.equal(r.status, 2);
2213
2352
  assert.match(r.stderr, /unknown flag '--bogus-flag'/);
2214
2353
  assert.equal(r.invoked, false, 'an unknown flag must not spend a run');
2215
2354
  });
2216
2355
 
2217
- it('--help NOT in first position is an unknown flag, never an intercepted help', () => {
2356
+ it('--help NOT in first position is an unknown flag, never an intercepted help', async () => {
2218
2357
  const sb = makeSandbox();
2219
- const r = run(sb, { args: ['code', '--facts', 'f', '--help'] });
2358
+ const r = await run(sb, { args: ['code', '--facts', 'f', '--help'] });
2220
2359
  rmSync(sb.home, { recursive: true, force: true });
2221
2360
  assert.equal(r.status, 2, 'help is keyed on the FIRST argument only');
2222
2361
  assert.doesNotMatch(r.stdout, /Usage:/);
@@ -2241,10 +2380,10 @@ const sha256HexOf = async (buf) => {
2241
2380
  };
2242
2381
  const VERDICT_OUTPUT = '### Verdict\nSHIP WITH NITS — solid, two nits.\n### Blocking\nnone\n### Non-blocking\n1. nit\n### Questions\nnone';
2243
2382
 
2244
- describe('agy-review.sh — review receipts (AD-038)', () => {
2383
+ describe('agy-review.sh — review receipts (AD-038)', { concurrency: 2 }, () => {
2245
2384
  it('a fresh grounded code review appends ONE fixture-shaped receipt (verdict verbatim, factsHash real)', async () => {
2246
2385
  const sb = makeSandbox();
2247
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2386
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2248
2387
  const receipts = readReceipts(sb.repo);
2249
2388
  rmSync(sb.home, { recursive: true, force: true });
2250
2389
  assert.equal(r.status, 0, r.stderr);
@@ -2268,9 +2407,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2268
2407
  // the kit's review-state gate rejects it. EVERY receipt carries the marker (true or false): it
2269
2408
  // self-declares, so the gate reads the fact rather than inferring it from a version string that
2270
2409
  // bumps in a different release phase. Silence is not a declaration.
2271
- it('AGY_PROBE=1 stamps probe:true — a throwaway probe can never attest a tree (D3)', () => {
2410
+ it('AGY_PROBE=1 stamps probe:true — a throwaway probe can never attest a tree (D3)', async () => {
2272
2411
  const sb = makeSandbox();
2273
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2412
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2274
2413
  const receipts = readReceipts(sb.repo);
2275
2414
  rmSync(sb.home, { recursive: true, force: true });
2276
2415
  assert.equal(r.status, 0, r.stderr);
@@ -2280,17 +2419,17 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2280
2419
 
2281
2420
  // Every receipt SELF-DECLARES: the kit's gate reads the marker, never the wrapper version — so
2282
2421
  // the marker must not depend on a version bump landing in the same release phase.
2283
- it('a normal review self-declares probe:false — the receipt states the fact, not a version', () => {
2422
+ it('a normal review self-declares probe:false — the receipt states the fact, not a version', async () => {
2284
2423
  const sb = makeSandbox();
2285
- run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2424
+ await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2286
2425
  const receipts = readReceipts(sb.repo);
2287
2426
  rmSync(sb.home, { recursive: true, force: true });
2288
2427
  assert.equal(receipts[0].probe, false, 'silence is not a declaration — the gate rejects an unmarked receipt');
2289
2428
  });
2290
2429
 
2291
- it('a probe CONTINUATION is marked too (it is doubly unable to attest — fresh:false AND probe)', () => {
2430
+ it('a probe CONTINUATION is marked too (it is doubly unable to attest — fresh:false AND probe)', async () => {
2292
2431
  const sb = makeSandbox();
2293
- const r = run(sb, { args: ['--continue'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2432
+ const r = await run(sb, { args: ['--continue'], env: { AGY_PROBE: '1', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2294
2433
  const receipts = readReceipts(sb.repo);
2295
2434
  rmSync(sb.home, { recursive: true, force: true });
2296
2435
  assert.equal(r.status, 0, r.stderr);
@@ -2298,9 +2437,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2298
2437
  assert.equal(receipts[0].probe, true, 'both write paths carry the marker — no unmarked probe lane');
2299
2438
  });
2300
2439
 
2301
- it('an --ungrounded fresh run records grounded:false + factsHash null (the vacuous-grounding hole stays visible)', () => {
2440
+ it('an --ungrounded fresh run records grounded:false + factsHash null (the vacuous-grounding hole stays visible)', async () => {
2302
2441
  const sb = makeSandbox();
2303
- const r = run(sb, { args: ['code', '--ungrounded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2442
+ const r = await run(sb, { args: ['code', '--ungrounded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2304
2443
  const receipts = readReceipts(sb.repo);
2305
2444
  rmSync(sb.home, { recursive: true, force: true });
2306
2445
  assert.equal(r.status, 0, r.stderr);
@@ -2308,10 +2447,10 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2308
2447
  assert.equal(receipts[0].factsHash, null);
2309
2448
  });
2310
2449
 
2311
- it('an EMPTY --facts file in code mode refuses pre-spend — no run, no receipt (D4 fail-closed)', () => {
2450
+ it('an EMPTY --facts file in code mode refuses pre-spend — no run, no receipt (D4 fail-closed)', async () => {
2312
2451
  const sb = makeSandbox();
2313
2452
  writeFileSync(join(sb.home, 'empty-facts.md'), '');
2314
- const r = run(sb, { args: ['code', '--facts', `@${join(sb.home, 'empty-facts.md')}`], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2453
+ const r = await run(sb, { args: ['code', '--facts', `@${join(sb.home, 'empty-facts.md')}`], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2315
2454
  const receipts = readReceipts(sb.repo);
2316
2455
  rmSync(sb.home, { recursive: true, force: true });
2317
2456
  assert.equal(r.status, 2, 'vacuous grounding no longer spends a run');
@@ -2319,13 +2458,13 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2319
2458
  assert.equal(receipts.length, 0, 'no run — no receipt');
2320
2459
  });
2321
2460
 
2322
- it('parses REWORK and plain SHIP verbatim (an absent section is a FAILED run — the D4 describe owns that arm)', () => {
2461
+ it('parses REWORK and plain SHIP verbatim (an absent section is a FAILED run — the D4 describe owns that arm)', async () => {
2323
2462
  for (const [output, want] of [
2324
2463
  ['### Verdict\nREWORK — the contract is violated.', 'REWORK'],
2325
2464
  ['### Verdict\nSHIP — clean.', 'SHIP'],
2326
2465
  ]) {
2327
2466
  const sb = makeSandbox();
2328
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: output } });
2467
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: output } });
2329
2468
  const receipts = readReceipts(sb.repo);
2330
2469
  rmSync(sb.home, { recursive: true, force: true });
2331
2470
  assert.equal(r.status, 0, r.stderr);
@@ -2336,12 +2475,12 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2336
2475
  // The wrapper-minted finding manifest (flow-orchestration Phase 4.2, Decision 2/P5/P24-25):
2337
2476
  // nonce-supplied dispatches mint {schema, backend, nonce, fingerprint, findings} beside the
2338
2477
  // receipt, atomic + no-clobber + ORDERED — a failed mint EXCLUDES the receipt append.
2339
- describe('finding manifest (AW_REVIEW_NONCE)', () => {
2478
+ describe('finding manifest (AW_REVIEW_NONCE)', { concurrency: 2 }, () => {
2340
2479
  const manifestPath = (repo, nonce) => join(repo, '.git', `agent-workflow-finding-manifest-agy-${nonce}.json`);
2341
2480
 
2342
- it('a nonce-supplied grounded code dispatch mints the {backend, nonce}-named manifest carrying the captured findings + the receipt fingerprint', () => {
2481
+ it('a nonce-supplied grounded code dispatch mints the {backend, nonce}-named manifest carrying the captured findings + the receipt fingerprint', async () => {
2343
2482
  const sb = makeSandbox();
2344
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r1-d2' } });
2483
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r1-d2' } });
2345
2484
  const receipts = readReceipts(sb.repo);
2346
2485
  const manifest = JSON.parse(readFileSync(manifestPath(sb.repo, 'r1-d2'), 'utf8'));
2347
2486
  rmSync(sb.home, { recursive: true, force: true });
@@ -2356,9 +2495,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2356
2495
  assert.equal(receipts[0].nonce, 'r1-d2', 'a nonce-supplied receipt carries the dispatch nonce — the flow round-land matcher requires exact equality (dispatch identity end-to-end)');
2357
2496
  });
2358
2497
 
2359
- it('a nonce-less invocation mints NO manifest and adds NO nonce field — the receipt field set is unchanged (Decision 2 both branches)', () => {
2498
+ it('a nonce-less invocation mints NO manifest and adds NO nonce field — the receipt field set is unchanged (Decision 2 both branches)', async () => {
2360
2499
  const sb = makeSandbox();
2361
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2500
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2362
2501
  const receipts = readReceipts(sb.repo);
2363
2502
  const gitEntries = readdirSync(join(sb.repo, '.git')).filter((n) => n.startsWith('agent-workflow-finding-manifest-'));
2364
2503
  rmSync(sb.home, { recursive: true, force: true });
@@ -2367,10 +2506,10 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2367
2506
  assert.deepEqual(Object.keys(receipts[0]), Object.keys(RECEIPT_FIXTURE), 'the receipt line field set is unchanged');
2368
2507
  });
2369
2508
 
2370
- it('DIFFERENT bytes at the derived name refuse loudly AND EXCLUDE the receipt append (ordering, behaviorally)', () => {
2509
+ it('DIFFERENT bytes at the derived name refuse loudly AND EXCLUDE the receipt append (ordering, behaviorally)', async () => {
2371
2510
  const sb = makeSandbox();
2372
2511
  writeFileSync(manifestPath(sb.repo, 'r1-d2'), '{"schema":1,"backend":"agy","nonce":"r1-d2","fingerprint":null,"findings":"other bytes"}\n');
2373
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r1-d2' } });
2512
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r1-d2' } });
2374
2513
  const receipts = readReceipts(sb.repo);
2375
2514
  const manifest = readFileSync(manifestPath(sb.repo, 'r1-d2'), 'utf8');
2376
2515
  rmSync(sb.home, { recursive: true, force: true });
@@ -2381,15 +2520,15 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2381
2520
  assert.match(manifest, /other bytes/, 'the pre-existing manifest is never clobbered');
2382
2521
  });
2383
2522
 
2384
- it('a SYMLINK at the derived manifest path refuses and EXCLUDES the receipt — even when its target is byte-identical', () => {
2523
+ it('a SYMLINK at the derived manifest path refuses and EXCLUDES the receipt — even when its target is byte-identical', async () => {
2385
2524
  const sb = makeSandbox();
2386
- assert.equal(run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'sym2' } }).status, 0);
2525
+ assert.equal((await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'sym2' } })).status, 0);
2387
2526
  const mPath = manifestPath(sb.repo, 'sym2');
2388
2527
  const target = join(sb.repo, '.git', 'manifest-target-copy.json');
2389
2528
  writeFileSync(target, readFileSync(mPath));
2390
2529
  rmSync(mPath);
2391
2530
  symlinkSync(target, mPath);
2392
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'sym2' } });
2531
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'sym2' } });
2393
2532
  const receipts = readReceipts(sb.repo);
2394
2533
  rmSync(sb.home, { recursive: true, force: true });
2395
2534
  assert.equal(r.status, 0, 'the review itself still succeeds (the artifact lane failed loudly)');
@@ -2397,11 +2536,11 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2397
2536
  assert.equal(receipts.length, 1, 'the second receipt is EXCLUDED — a symlinked manifest is never read through as the idempotent no-op');
2398
2537
  });
2399
2538
 
2400
- it('a FIFO at the derived manifest path refuses fast and EXCLUDES the receipt (O_NONBLOCK — no hang; fix characterization)', () => {
2539
+ it('a FIFO at the derived manifest path refuses fast and EXCLUDES the receipt (O_NONBLOCK — no hang; fix characterization)', async () => {
2401
2540
  const sb = makeSandbox();
2402
2541
  const mPath = manifestPath(sb.repo, 'fifo2');
2403
2542
  assert.equal(spawnSync('mkfifo', [mPath], { encoding: 'utf8' }).status, 0, 'mkfifo fixture');
2404
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'fifo2' } });
2543
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'fifo2' } });
2405
2544
  const receipts = readReceipts(sb.repo);
2406
2545
  rmSync(sb.home, { recursive: true, force: true });
2407
2546
  assert.equal(r.status, 0, r.stderr);
@@ -2409,18 +2548,18 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2409
2548
  assert.equal(receipts.length, 0, 'a FIFO manifest is never read (fstat-first) and the receipt is excluded');
2410
2549
  });
2411
2550
 
2412
- it('a BOM-prefixed captured output round-trips VERBATIM into the manifest (U+FEFF preserved)', () => {
2551
+ it('a BOM-prefixed captured output round-trips VERBATIM into the manifest (U+FEFF preserved)', async () => {
2413
2552
  const sb = makeSandbox();
2414
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: `\uFEFFpreamble\n${VERDICT_OUTPUT}`, AW_REVIEW_NONCE: 'b2' } });
2553
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: `\uFEFFpreamble\n${VERDICT_OUTPUT}`, AW_REVIEW_NONCE: 'b2' } });
2415
2554
  const manifest = JSON.parse(readFileSync(manifestPath(sb.repo, 'b2'), 'utf8'));
2416
2555
  rmSync(sb.home, { recursive: true, force: true });
2417
2556
  assert.equal(r.status, 0, r.stderr);
2418
2557
  assert.equal(manifest.findings, `\uFEFFpreamble\n${VERDICT_OUTPUT}\n`, 'the captured output is VERBATIM — a stripped BOM would move the findingDigest');
2419
2558
  });
2420
2559
 
2421
- it('an unsafe nonce refuses PRE-SPEND (exit 2, agy never runs) — a non-ASCII letter refuses under a UTF-8 locale too', () => {
2560
+ it('an unsafe nonce refuses PRE-SPEND (exit 2, agy never runs) — a non-ASCII letter refuses under a UTF-8 locale too', async () => {
2422
2561
  const sb = makeSandbox();
2423
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AW_REVIEW_NONCE: 'a/b' } });
2562
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AW_REVIEW_NONCE: 'a/b' } });
2424
2563
  rmSync(sb.home, { recursive: true, force: true });
2425
2564
  assert.equal(r.status, 2);
2426
2565
  assert.match(r.stderr, /safe nonce grammar/);
@@ -2428,15 +2567,15 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2428
2567
  // The grammar ENUMERATES the ASCII set (no ranges): a locale-collated [A-Za-z] could admit a
2429
2568
  // non-ASCII letter the kit's JS reader then refuses, breaking correlation after a paid run.
2430
2569
  const utf8 = makeSandbox();
2431
- const r2 = run(utf8, { args: ['code', '--facts', 'a tiny fact'], env: { AW_REVIEW_NONCE: 'r\u00e91', LC_ALL: 'en_US.UTF-8', LANG: 'en_US.UTF-8' } });
2570
+ const r2 = await run(utf8, { args: ['code', '--facts', 'a tiny fact'], env: { AW_REVIEW_NONCE: 'r\u00e91', LC_ALL: 'en_US.UTF-8', LANG: 'en_US.UTF-8' } });
2432
2571
  rmSync(utf8.home, { recursive: true, force: true });
2433
2572
  assert.equal(r2.status, 2, 'a non-ASCII nonce letter refuses whatever the locale collation says');
2434
2573
  assert.match(r2.stderr, /safe nonce grammar/);
2435
2574
  });
2436
2575
 
2437
- it('a nonce-supplied CONTINUATION mints its manifest with fingerprint null (the receipt identity is null too)', () => {
2576
+ it('a nonce-supplied CONTINUATION mints its manifest with fingerprint null (the receipt identity is null too)', async () => {
2438
2577
  const sb = makeSandbox();
2439
- const r = run(sb, { args: ['--continue'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r2-c1' } });
2578
+ const r = await run(sb, { args: ['--continue'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'r2-c1' } });
2440
2579
  const receipts = readReceipts(sb.repo);
2441
2580
  const manifest = JSON.parse(readFileSync(manifestPath(sb.repo, 'r2-c1'), 'utf8'));
2442
2581
  rmSync(sb.home, { recursive: true, force: true });
@@ -2448,9 +2587,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2448
2587
 
2449
2588
  // The --nonce flag (FLOW-NONCE-DISPATCH-LANE): the plain-argument lane onto the SAME seam —
2450
2589
  // for hosts whose dispatch policy has no env-prefix form.
2451
- it('--nonce rides the AW_REVIEW_NONCE seam: manifest minted, receipt nonce-stamped (flag form ≡ env form)', () => {
2590
+ it('--nonce rides the AW_REVIEW_NONCE seam: manifest minted, receipt nonce-stamped (flag form ≡ env form)', async () => {
2452
2591
  const sb = makeSandbox();
2453
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'f2-d2'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2592
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'f2-d2'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2454
2593
  const receipts = readReceipts(sb.repo);
2455
2594
  const manifest = JSON.parse(readFileSync(manifestPath(sb.repo, 'f2-d2'), 'utf8'));
2456
2595
  rmSync(sb.home, { recursive: true, force: true });
@@ -2459,45 +2598,45 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2459
2598
  assert.equal(receipts[0].nonce, 'f2-d2', 'the flag stamps the receipt exactly like the env form');
2460
2599
  });
2461
2600
 
2462
- it('an unsafe --nonce value refuses PRE-SPEND (exit 2, agy never runs) — same grammar as the env screen', () => {
2601
+ it('an unsafe --nonce value refuses PRE-SPEND (exit 2, agy never runs) — same grammar as the env screen', async () => {
2463
2602
  const sb = makeSandbox();
2464
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'a/b'] });
2603
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'a/b'] });
2465
2604
  rmSync(sb.home, { recursive: true, force: true });
2466
2605
  assert.equal(r.status, 2);
2467
2606
  assert.match(r.stderr, /safe nonce grammar/);
2468
2607
  assert.equal(r.invoked, false, 'the containment refusal fires before any CLI spend');
2469
2608
  });
2470
2609
 
2471
- it('a missing value, a duplicate flag, and a disagreeing env+flag pair each refuse (exit 2); an agreeing pair proceeds', () => {
2610
+ it('a missing value, a duplicate flag, and a disagreeing env+flag pair each refuse (exit 2); an agreeing pair proceeds', async () => {
2472
2611
  const sb = makeSandbox();
2473
- const missing = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce'] });
2612
+ const missing = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce'] });
2474
2613
  assert.equal(missing.status, 2);
2475
2614
  assert.match(missing.stderr, /--nonce needs a value/);
2476
- const dup = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n1', '--nonce', 'n2'] });
2615
+ const dup = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n1', '--nonce', 'n2'] });
2477
2616
  assert.equal(dup.status, 2);
2478
2617
  assert.match(dup.stderr, /duplicate --nonce/);
2479
- const clash = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n1'], env: { AW_REVIEW_NONCE: 'n2' } });
2618
+ const clash = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n1'], env: { AW_REVIEW_NONCE: 'n2' } });
2480
2619
  assert.equal(clash.status, 2);
2481
2620
  assert.match(clash.stderr, /disagrees with the AW_REVIEW_NONCE environment value/);
2482
- const agree = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n3'], env: { AW_REVIEW_NONCE: 'n3', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2621
+ const agree = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', 'n3'], env: { AW_REVIEW_NONCE: 'n3', AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2483
2622
  const exists = existsSync(manifestPath(sb.repo, 'n3'));
2484
2623
  rmSync(sb.home, { recursive: true, force: true });
2485
2624
  assert.equal(agree.status, 0, agree.stderr);
2486
2625
  assert.equal(exists, true, 'an agreeing pair is ONE seam value — the dispatch proceeds');
2487
2626
  });
2488
2627
 
2489
- it('--nonce is valid on a CONTINUATION too — the flag lane covers every seam-honoring form', () => {
2628
+ it('--nonce is valid on a CONTINUATION too — the flag lane covers every seam-honoring form', async () => {
2490
2629
  const sb = makeSandbox();
2491
- const r = run(sb, { args: ['--continue', '--nonce', 'r2-c2'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2630
+ const r = await run(sb, { args: ['--continue', '--nonce', 'r2-c2'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2492
2631
  const manifest = JSON.parse(readFileSync(manifestPath(sb.repo, 'r2-c2'), 'utf8'));
2493
2632
  rmSync(sb.home, { recursive: true, force: true });
2494
2633
  assert.equal(r.status, 0, r.stderr);
2495
2634
  assert.equal(manifest.fingerprint, null, 'a continuation manifest carries no tree identity');
2496
2635
  });
2497
2636
 
2498
- it('a grammar-valid leading-dash --nonce value is ACCEPTED — the flag lane spans the whole declared grammar', () => {
2637
+ it('a grammar-valid leading-dash --nonce value is ACCEPTED — the flag lane spans the whole declared grammar', async () => {
2499
2638
  const sb = makeSandbox();
2500
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', '--n1'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2639
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', '--n1'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2501
2640
  const receipts = readReceipts(sb.repo);
2502
2641
  const exists = existsSync(manifestPath(sb.repo, '--n1'));
2503
2642
  rmSync(sb.home, { recursive: true, force: true });
@@ -2506,18 +2645,18 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2506
2645
  assert.equal(receipts[0].nonce, '--n1', 'the receipt carries the exact grammar-valid value — flag lane ≡ env lane');
2507
2646
  });
2508
2647
 
2509
- it('an EMPTY --nonce value refuses pre-spend under the grammar screen (presence is tracked separately from the value)', () => {
2648
+ it('an EMPTY --nonce value refuses pre-spend under the grammar screen (presence is tracked separately from the value)', async () => {
2510
2649
  const sb = makeSandbox();
2511
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', ''] });
2650
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', ''] });
2512
2651
  rmSync(sb.home, { recursive: true, force: true });
2513
2652
  assert.equal(r.status, 2);
2514
2653
  assert.match(r.stderr, /safe nonce grammar/);
2515
2654
  assert.equal(r.invoked, false, 'the refusal fires before any CLI spend');
2516
2655
  });
2517
2656
 
2518
- it('a duplicate --nonce after an EMPTY first value still refuses as a duplicate — an empty value never erases presence', () => {
2657
+ it('a duplicate --nonce after an EMPTY first value still refuses as a duplicate — an empty value never erases presence', async () => {
2519
2658
  const sb = makeSandbox();
2520
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', '', '--nonce', 'n2'] });
2659
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact', '--nonce', '', '--nonce', 'n2'] });
2521
2660
  rmSync(sb.home, { recursive: true, force: true });
2522
2661
  assert.equal(r.status, 2);
2523
2662
  assert.match(r.stderr, /duplicate --nonce/);
@@ -2525,9 +2664,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2525
2664
  });
2526
2665
  });
2527
2666
 
2528
- it('a continuation receipt is fresh:false with null identity fields, and the wrapper prints the fresh-run notice', () => {
2667
+ it('a continuation receipt is fresh:false with null identity fields, and the wrapper prints the fresh-run notice', async () => {
2529
2668
  const sb = makeSandbox();
2530
- const r = run(sb, { args: ['--continue', '--decided', 'already folded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2669
+ const r = await run(sb, { args: ['--continue', '--decided', 'already folded'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2531
2670
  const receipts = readReceipts(sb.repo);
2532
2671
  rmSync(sb.home, { recursive: true, force: true });
2533
2672
  assert.equal(r.status, 0, r.stderr);
@@ -2549,7 +2688,7 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2549
2688
  it('plan mode: artifact "plan", fingerprint = the artifact-file sha256', async () => {
2550
2689
  const sb = makeSandbox();
2551
2690
  writeFileSync(join(sb.repo, 'p.md'), '# plan body\n');
2552
- const r = run(sb, { args: ['plan', 'p.md', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2691
+ const r = await run(sb, { args: ['plan', 'p.md', '--facts', 'f'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2553
2692
  const receipts = readReceipts(sb.repo);
2554
2693
  rmSync(sb.home, { recursive: true, force: true });
2555
2694
  assert.equal(r.status, 0, r.stderr);
@@ -2557,18 +2696,18 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2557
2696
  assert.equal(receipts[0].fingerprint, await sha256HexOf('# plan body\n'), 'plan fingerprint = file sha256');
2558
2697
  });
2559
2698
 
2560
- it('plan/diff outside a git work tree: warn + skip the receipt (exit 0) unless AW_REVIEW_RECEIPTS is set', () => {
2699
+ it('plan/diff outside a git work tree: warn + skip the receipt (exit 0) unless AW_REVIEW_RECEIPTS is set', async () => {
2561
2700
  const sb = makeSandbox();
2562
2701
  const outside = join(sb.home, 'no-repo');
2563
2702
  mkdirSync(outside, { recursive: true });
2564
2703
  writeFileSync(join(outside, 'p.md'), '# plan outside git\n');
2565
2704
 
2566
- const skipped = run(sb, { args: ['plan', 'p.md', '--facts', 'f'], cwd: outside, env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2705
+ const skipped = await run(sb, { args: ['plan', 'p.md', '--facts', 'f'], cwd: outside, env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT } });
2567
2706
  assert.equal(skipped.status, 0, skipped.stderr);
2568
2707
  assert.match(skipped.stderr, /not inside a git work tree and AW_REVIEW_RECEIPTS is unset — skipping/);
2569
2708
 
2570
2709
  const override = join(sb.home, 'receipts-override.jsonl');
2571
- const written = run(sb, {
2710
+ const written = await run(sb, {
2572
2711
  args: ['plan', 'p.md', '--facts', 'f'],
2573
2712
  cwd: outside,
2574
2713
  env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_RECEIPTS: override },
@@ -2580,9 +2719,9 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2580
2719
  assert.match(body, /"artifact":"plan"/);
2581
2720
  });
2582
2721
 
2583
- it('a receipt write failure warns loudly but never fails the review (fail-safe direction)', () => {
2722
+ it('a receipt write failure warns loudly but never fails the review (fail-safe direction)', async () => {
2584
2723
  const sb = makeSandbox();
2585
- const r = run(sb, {
2724
+ const r = await run(sb, {
2586
2725
  args: ['code', '--facts', 'f'],
2587
2726
  env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_RECEIPTS: join(sb.home, 'no-such-dir', 'r.jsonl') },
2588
2727
  });
@@ -2592,18 +2731,18 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2592
2731
  assert.match(r.stdout, /SHIP WITH NITS/, 'the findings still reach stdout');
2593
2732
  });
2594
2733
 
2595
- it('a failed agy run writes NO receipt (only a successful review attests)', () => {
2734
+ it('a failed agy run writes NO receipt (only a successful review attests)', async () => {
2596
2735
  const sb = makeSandbox();
2597
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_EXIT: '7' } });
2736
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_FAKE_EXIT: '7' } });
2598
2737
  const receipts = readReceipts(sb.repo);
2599
2738
  rmSync(sb.home, { recursive: true, force: true });
2600
2739
  assert.notEqual(r.status, 0);
2601
2740
  assert.equal(receipts.length, 0);
2602
2741
  });
2603
2742
 
2604
- it('the clean-tree preflight exits before any receipt is written', () => {
2743
+ it('the clean-tree preflight exits before any receipt is written', async () => {
2605
2744
  const sb = makeSandbox({ clean: true });
2606
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2745
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2607
2746
  const receipts = readReceipts(sb.repo);
2608
2747
  rmSync(sb.home, { recursive: true, force: true });
2609
2748
  assert.equal(r.status, 0);
@@ -2614,7 +2753,7 @@ describe('agy-review.sh — review receipts (AD-038)', () => {
2614
2753
  // MANIFEST-TMP-ORPHAN-ON-FAILURE: on a FAILURE exit of the finding-manifest mint whose temp
2615
2754
  // unlink ALSO fails, the error names the orphan path — parameterized over BOTH failure codes
2616
2755
  // (rc 3 no-clobber, rc 1 fs failure); a failure whose temp IS removable stays orphan-silent.
2617
- describe('agy-review.sh — finding-manifest failure branches name the orphan (MANIFEST-TMP-ORPHAN-ON-FAILURE)', () => {
2756
+ describe('agy-review.sh — finding-manifest failure branches name the orphan (MANIFEST-TMP-ORPHAN-ON-FAILURE)', { concurrency: 2 }, () => {
2618
2757
  const manifestPath = (repo, nonce) => join(repo, '.git', `agent-workflow-finding-manifest-agy-${nonce}.json`);
2619
2758
  const UNLINK_FAIL = "const fs = require('node:fs');\nconst real = fs.unlinkSync;\nfs.unlinkSync = (p) => { if (String(p).includes('.tmp')) { const e = new Error('EPERM'); e.code = 'EPERM'; throw e; } return real(p); };\n";
2620
2759
  const LINK_FAIL = "const fsLink = require('node:fs');\nfsLink.linkSync = () => { const e = new Error('EPERM'); e.code = 'EPERM'; throw e; };\n";
@@ -2622,12 +2761,12 @@ describe('agy-review.sh — finding-manifest failure branches name the orphan (M
2622
2761
  { rc: 3, name: 'no-clobber (rc 3)', preload: UNLINK_FAIL, plant: true, errRe: /DIFFERENT bytes or is not a regular file/ },
2623
2762
  { rc: 1, name: 'fs failure (rc 1)', preload: UNLINK_FAIL + LINK_FAIL, plant: false, errRe: /could not compose or write the finding manifest/ },
2624
2763
  ]) {
2625
- it(`a FAILURE exit (${failure.name}) whose temp unlink also fails names the ORPHAN PATH in the error`, () => {
2764
+ it(`a FAILURE exit (${failure.name}) whose temp unlink also fails names the ORPHAN PATH in the error`, async () => {
2626
2765
  const sb = makeSandbox();
2627
2766
  if (failure.plant) writeFileSync(manifestPath(sb.repo, 'orphf1'), 'planted different bytes\n');
2628
2767
  const preload = join(sb.home, 'orphan-fail-preload.cjs');
2629
2768
  writeFileSync(preload, failure.preload);
2630
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'orphf1', NODE_OPTIONS: `--require ${preload}` } });
2769
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'orphf1', NODE_OPTIONS: `--require ${preload}` } });
2631
2770
  const receipts = readReceipts(sb.repo);
2632
2771
  rmSync(sb.home, { recursive: true, force: true });
2633
2772
  assert.equal(r.status, 0, 'the review itself still succeeds (the artifact lane failed loudly)');
@@ -2637,10 +2776,10 @@ describe('agy-review.sh — finding-manifest failure branches name the orphan (M
2637
2776
  });
2638
2777
  }
2639
2778
 
2640
- it('a FAILURE exit whose temp IS removable stays orphan-silent (no leftover, no orphan line)', () => {
2779
+ it('a FAILURE exit whose temp IS removable stays orphan-silent (no leftover, no orphan line)', async () => {
2641
2780
  const sb = makeSandbox();
2642
2781
  writeFileSync(manifestPath(sb.repo, 'orphf2'), 'planted different bytes\n');
2643
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'orphf2' } });
2782
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: VERDICT_OUTPUT, AW_REVIEW_NONCE: 'orphf2' } });
2644
2783
  const leftovers = readdirSync(join(sb.repo, '.git')).filter((n) => n.endsWith('.tmp'));
2645
2784
  rmSync(sb.home, { recursive: true, force: true });
2646
2785
  assert.equal(r.status, 0, 'the review itself still succeeds (the artifact lane failed loudly)');
@@ -2666,12 +2805,12 @@ const writeSettings = (sb, text) => {
2666
2805
  };
2667
2806
  const isRoot = typeof process.getuid === 'function' && process.getuid() === 0;
2668
2807
 
2669
- describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency: true }, () => {
2670
- it('a file-set AGY_REVIEW_ALLOW_ADDDIR=1 arms nothing and states its retirement', () => {
2808
+ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency: 2 }, () => {
2809
+ it('a file-set AGY_REVIEW_ALLOW_ADDDIR=1 arms nothing and states its retirement', async () => {
2671
2810
  const sb = makeSandbox();
2672
2811
  seedFedChangeSet(sb);
2673
2812
  writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=1\n');
2674
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
2813
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
2675
2814
  rmSync(sb.home, { recursive: true, force: true });
2676
2815
  assert.equal(r.status, 0, r.stderr);
2677
2816
  assert.match(r.stderr, /AGY_REVIEW_ALLOW_ADDDIR is set \(file\) but it is RETIRED/, 'a FILE-set value is named as such, not as an env override');
@@ -2681,32 +2820,32 @@ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency
2681
2820
 
2682
2821
  // With the knob DISARMED an over-cap code review is no longer a refusal — it is the fed lane. So
2683
2822
  // "env wins over file" is now proven by which LANE runs, not by which error prints.
2684
- it('env overrides file: AGY_REVIEW_ALLOW_ADDDIR env=0 file=1 → the offload stays disarmed and the fed lane runs', () => {
2823
+ it('env overrides file: AGY_REVIEW_ALLOW_ADDDIR env=0 file=1 → the offload stays disarmed and the fed lane runs', async () => {
2685
2824
  const sb = makeSandbox();
2686
2825
  seedFedChangeSet(sb);
2687
2826
  writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=1\n');
2688
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '0' } });
2827
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '0' } });
2689
2828
  rmSync(sb.home, { recursive: true, force: true });
2690
2829
  assert.equal(r.status, 0, r.stderr);
2691
2830
  assert.ok(!r.argv.includes('--add-dir'), 'the file-set knob is overridden — no offload');
2692
2831
  assert.match(r.stderr, /feeding the change set in \d+ part\(s\)/);
2693
2832
  });
2694
2833
 
2695
- it('an EXPLICITLY EMPTY env (AGY_REVIEW_ALLOW_ADDDIR=) disables the file knob', () => {
2834
+ it('an EXPLICITLY EMPTY env (AGY_REVIEW_ALLOW_ADDDIR=) disables the file knob', async () => {
2696
2835
  const sb = makeSandbox();
2697
2836
  seedFedChangeSet(sb);
2698
2837
  writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=1\n');
2699
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '' } });
2838
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP), AGY_REVIEW_ALLOW_ADDDIR: '' } });
2700
2839
  rmSync(sb.home, { recursive: true, force: true });
2701
2840
  assert.equal(r.status, 0, 'env wins over file — empty means knob off (built-in default 0)');
2702
2841
  assert.ok(!r.argv.includes('--add-dir'));
2703
2842
  });
2704
2843
 
2705
- it('an invalid boolean warns and falls back to the built-in default (the offload stays disarmed)', () => {
2844
+ it('an invalid boolean warns and falls back to the built-in default (the offload stays disarmed)', async () => {
2706
2845
  const sb = makeSandbox();
2707
2846
  seedFedChangeSet(sb);
2708
2847
  writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=yes\n');
2709
- const r = run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
2848
+ const r = await run(sb, { args: ['code', '--facts', 'f'], env: { AGY_MAX_PROMPT_BYTES: String(FED_CAP) } });
2710
2849
  rmSync(sb.home, { recursive: true, force: true });
2711
2850
  assert.equal(r.status, 0, r.stderr);
2712
2851
  assert.match(r.stderr, /invalid value 'yes'/);
@@ -2722,20 +2861,20 @@ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency
2722
2861
  assert.match(r.stderr, /exceeded the hard cap AGY_HARD_TIMEOUT=2s/);
2723
2862
  });
2724
2863
 
2725
- it("another bridge's valid key is skipped silently", () => {
2864
+ it("another bridge's valid key is skipped silently", async () => {
2726
2865
  const sb = makeSandbox();
2727
2866
  writeSettings(sb, 'CODEX_SERVICE_TIER=priority\nCODEX_HARD_TIMEOUT=2\nCODEX_REVIEW_MAX_TOTAL_BYTES=100\n');
2728
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2867
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2729
2868
  rmSync(sb.home, { recursive: true, force: true });
2730
2869
  assert.equal(r.status, 0, r.stderr);
2731
2870
  assert.doesNotMatch(r.stderr, /bridge settings/, 'a recognized non-applied key earns NO warning');
2732
2871
  assert.equal(r.invoked, true);
2733
2872
  });
2734
2873
 
2735
- it('a truly unknown key warns ONCE naming the file; the review is unaffected', () => {
2874
+ it('a truly unknown key warns ONCE naming the file; the review is unaffected', async () => {
2736
2875
  const sb = makeSandbox();
2737
2876
  writeSettings(sb, 'TOTALLY_UNKNOWN=1\nTOTALLY_UNKNOWN=2\n');
2738
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2877
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2739
2878
  rmSync(sb.home, { recursive: true, force: true });
2740
2879
  assert.equal(r.status, 0, r.stderr);
2741
2880
  const warns = r.stderr.match(/unknown key 'TOTALLY_UNKNOWN'/g) ?? [];
@@ -2744,10 +2883,10 @@ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency
2744
2883
  assert.equal(r.invoked, true);
2745
2884
  });
2746
2885
 
2747
- it('malformed lines warn and are ignored; comments and blank lines are silent', () => {
2886
+ it('malformed lines warn and are ignored; comments and blank lines are silent', async () => {
2748
2887
  const sb = makeSandbox();
2749
2888
  writeSettings(sb, '# a comment\n\nNOT A KEY VALUE LINE\n');
2750
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2889
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2751
2890
  rmSync(sb.home, { recursive: true, force: true });
2752
2891
  assert.equal(r.status, 0, r.stderr);
2753
2892
  const malformed = r.stderr.match(/malformed line/g) ?? [];
@@ -2755,22 +2894,22 @@ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency
2755
2894
  assert.equal(r.invoked, true);
2756
2895
  });
2757
2896
 
2758
- it('an existing-but-unreadable file warns loudly and falls back to built-ins', { skip: isRoot }, () => {
2897
+ it('an existing-but-unreadable file warns loudly and falls back to built-ins', { skip: isRoot }, async () => {
2759
2898
  const sb = makeSandbox();
2760
2899
  const file = writeSettings(sb, 'AGY_REVIEW_ALLOW_ADDDIR=1\n');
2761
2900
  chmodSync(file, 0o000);
2762
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2901
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2763
2902
  rmSync(sb.home, { recursive: true, force: true });
2764
2903
  assert.equal(r.status, 0, r.stderr);
2765
2904
  assert.match(r.stderr, /unreadable/);
2766
2905
  assert.equal(r.invoked, true);
2767
2906
  });
2768
2907
 
2769
- it('a settings line can NEVER execute code (command-substitution payload inert)', () => {
2908
+ it('a settings line can NEVER execute code (command-substitution payload inert)', async () => {
2770
2909
  const sb = makeSandbox();
2771
2910
  const pwned = join(sb.home, 'pwned');
2772
2911
  writeSettings(sb, `AGY_HARD_TIMEOUT=$(touch ${pwned})\nEVIL_KEY=\`touch ${pwned}2\`\n`);
2773
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2912
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2774
2913
  const executed = existsSync(pwned) || existsSync(`${pwned}2`);
2775
2914
  rmSync(sb.home, { recursive: true, force: true });
2776
2915
  assert.equal(r.status, 0, r.stderr);
@@ -2778,19 +2917,19 @@ describe('agy-review.sh — bridge settings file (bridges 2.3.0)', { concurrency
2778
2917
  assert.equal(r.invoked, true);
2779
2918
  });
2780
2919
 
2781
- it('no file → byte-identical behaviour to today (no settings chatter)', () => {
2920
+ it('no file → byte-identical behaviour to today (no settings chatter)', async () => {
2782
2921
  const sb = makeSandbox();
2783
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2922
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2784
2923
  rmSync(sb.home, { recursive: true, force: true });
2785
2924
  assert.equal(r.status, 0, r.stderr);
2786
2925
  assert.doesNotMatch(r.stderr, /bridge settings/);
2787
2926
  assert.equal(r.invoked, true);
2788
2927
  });
2789
2928
 
2790
- it('a DIRECTORY at the settings path warns loudly and falls back to built-ins (no crash)', () => {
2929
+ it('a DIRECTORY at the settings path warns loudly and falls back to built-ins (no crash)', async () => {
2791
2930
  const sb = makeSandbox();
2792
2931
  mkdirSync(join(sb.home, '.config', 'agent-workflow', 'bridge-settings.conf'), { recursive: true });
2793
- const r = run(sb, { args: ['code', '--facts', 'f'] });
2932
+ const r = await run(sb, { args: ['code', '--facts', 'f'] });
2794
2933
  rmSync(sb.home, { recursive: true, force: true });
2795
2934
  assert.equal(r.status, 0, `a directory must degrade honestly, not kill the run: ${r.stderr}`);
2796
2935
  assert.match(r.stderr, /unreadable or not a regular file/);
@@ -2805,8 +2944,8 @@ const SIBLING_MANIFEST = JSON.parse(readFileSync(join(HERE, '..', '..', 'codex-c
2805
2944
  const ALL_SETTINGS = [...(MANIFEST.settings ?? []), ...(SIBLING_MANIFEST.settings ?? [])];
2806
2945
  const SETTINGS_CMD = 'agy-review';
2807
2946
 
2808
- describe('agy-review.sh — settings surface ⟷ manifest (D6, manifest-pinned)', () => {
2809
- it('--help Settings section keys set-EQUAL the manifest appliesTo subset', () => {
2947
+ describe('agy-review.sh — settings surface ⟷ manifest (D6, manifest-pinned)', { concurrency: 2 }, () => {
2948
+ it('--help Settings section keys set-EQUAL the manifest appliesTo subset', async () => {
2810
2949
  const help = runHelp('--help').stdout;
2811
2950
  const section = helpSection(help, SETTINGS_HEADER);
2812
2951
  const got = section.filter((l) => /^[A-Z][A-Z0-9_]+ —/.test(l)).map((l) => l.split(' ')[0]);
@@ -2818,14 +2957,14 @@ describe('agy-review.sh — settings surface ⟷ manifest (D6, manifest-pinned)'
2818
2957
 
2819
2958
  const source = readFileSync(WRAPPER, 'utf8');
2820
2959
 
2821
- it('aw_settings_known carries exactly the UNION of both bridges settings keys', () => {
2960
+ it('aw_settings_known carries exactly the UNION of both bridges settings keys', async () => {
2822
2961
  const m = source.match(/aw_settings_known\(\) \{\n case " ([^"]+) " in/);
2823
2962
  assert.ok(m, 'aw_settings_known registry case not found');
2824
2963
  assert.ok(ALL_SETTINGS.length >= 5, 'both manifests must contribute settings');
2825
2964
  setEq(m[1].trim().split(/\s+/), ALL_SETTINGS.map((s) => s.key), 'shell registry ⟷ manifest union');
2826
2965
  });
2827
2966
 
2828
- it('AW_SETTINGS_APPLIED equals the manifest appliesTo subset for this wrapper', () => {
2967
+ it('AW_SETTINGS_APPLIED equals the manifest appliesTo subset for this wrapper', async () => {
2829
2968
  const m = source.match(/^AW_SETTINGS_APPLIED="([^"]*)"$/m);
2830
2969
  assert.ok(m, 'AW_SETTINGS_APPLIED not found');
2831
2970
  const want = ALL_SETTINGS.filter((s) => s.appliesTo.includes(SETTINGS_CMD)).map((s) => s.key);
@@ -2833,7 +2972,7 @@ describe('agy-review.sh — settings surface ⟷ manifest (D6, manifest-pinned)'
2833
2972
  setEq(m[1].trim().split(/\s+/), want, 'applied subset ⟷ manifest appliesTo');
2834
2973
  });
2835
2974
 
2836
- it('aw_settings_valid arms carry the manifest typed constants per key', () => {
2975
+ it('aw_settings_valid arms carry the manifest typed constants per key', async () => {
2837
2976
  const body = source.match(/aw_settings_valid\(\) \{[\s\S]*?\n\}/);
2838
2977
  assert.ok(body, 'aw_settings_valid not found');
2839
2978
  const armKeys = [...body[0].matchAll(/^ ([A-Z][A-Z0-9_]*)\)/gm)].map((x) => x[1]);
@@ -2857,10 +2996,10 @@ describe('agy-review.sh — settings surface ⟷ manifest (D6, manifest-pinned)'
2857
2996
  });
2858
2997
 
2859
2998
  // ── strip-the-kit Phase 4: wrapper honesty (D4) + dispatch-posture labeling (D5) ────────────────
2860
- describe('agy-review.sh — wrapper honesty: a verdict-less run is a FAILED review (D4)', () => {
2861
- it('a VERDICT-LESS review output: non-zero exit, NO receipt, the stated re-run recovery', () => {
2999
+ describe('agy-review.sh — wrapper honesty: a verdict-less run is a FAILED review (D4)', { concurrency: 2 }, () => {
3000
+ it('a VERDICT-LESS review output: non-zero exit, NO receipt, the stated re-run recovery', async () => {
2862
3001
  const sb = makeSandbox();
2863
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: 'prose without the mandated section' } });
3002
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: 'prose without the mandated section' } });
2864
3003
  const receipts = readReceipts(sb.repo);
2865
3004
  rmSync(sb.home, { recursive: true, force: true });
2866
3005
  assert.notEqual(r.status, 0, 'a verdict-less review never exits 0');
@@ -2869,19 +3008,19 @@ describe('agy-review.sh — wrapper honesty: a verdict-less run is a FAILED revi
2869
3008
  assert.match(r.stderr, /re-run/i, 'documented as a failed review — re-run, never fatal');
2870
3009
  });
2871
3010
 
2872
- it('EMPTY review output is the same failed run (non-zero, no receipt)', () => {
3011
+ it('EMPTY review output is the same failed run (non-zero, no receipt)', async () => {
2873
3012
  const sb = makeSandbox();
2874
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: '' } });
3013
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: '' } });
2875
3014
  const receipts = readReceipts(sb.repo);
2876
3015
  rmSync(sb.home, { recursive: true, force: true });
2877
3016
  assert.notEqual(r.status, 0);
2878
3017
  assert.equal(receipts.length, 0);
2879
3018
  });
2880
3019
 
2881
- it('the closed vocabulary still parses (SHIP WITH NITS before SHIP; REWORK) and a recognized run exits 0', () => {
3020
+ it('the closed vocabulary still parses (SHIP WITH NITS before SHIP; REWORK) and a recognized run exits 0', async () => {
2882
3021
  for (const [out, want] of [[VERDICT_OUTPUT, 'SHIP WITH NITS'], ['### Verdict\nREWORK — reasons.\n', 'REWORK']]) {
2883
3022
  const sb = makeSandbox();
2884
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: out } });
3023
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_FAKE_OUTPUT: out } });
2885
3024
  const receipts = readReceipts(sb.repo);
2886
3025
  rmSync(sb.home, { recursive: true, force: true });
2887
3026
  assert.equal(r.status, 0, r.stderr);
@@ -2890,10 +3029,10 @@ describe('agy-review.sh — wrapper honesty: a verdict-less run is a FAILED revi
2890
3029
  });
2891
3030
  });
2892
3031
 
2893
- describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2894
- it('ONE banner line carries the ACTUAL model and the receipt carries the SAME posture (agy has no tier)', () => {
3032
+ describe('agy-review.sh — dispatch-posture labeling (D5)', { concurrency: 2 }, () => {
3033
+ it('ONE banner line carries the ACTUAL model and the receipt carries the SAME posture (agy has no tier)', async () => {
2895
3034
  const sb = makeSandbox();
2896
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'] });
3035
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'] });
2897
3036
  const receipts = readReceipts(sb.repo);
2898
3037
  rmSync(sb.home, { recursive: true, force: true });
2899
3038
  assert.equal(r.status, 0, r.stderr);
@@ -2902,9 +3041,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2902
3041
  assert.deepEqual(Object.keys(receipts[0]), Object.keys(RECEIPT_FIXTURE), 'fixture key set + order');
2903
3042
  });
2904
3043
 
2905
- it('an ATTESTING review with AGY_MODEL explicitly emptied REFUSES pre-spend naming the fix', () => {
3044
+ it('an ATTESTING review with AGY_MODEL explicitly emptied REFUSES pre-spend naming the fix', async () => {
2906
3045
  const sb = makeSandbox();
2907
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: '' } });
3046
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: '' } });
2908
3047
  const receipts = readReceipts(sb.repo);
2909
3048
  rmSync(sb.home, { recursive: true, force: true });
2910
3049
  assert.notEqual(r.status, 0);
@@ -2913,9 +3052,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2913
3052
  assert.match(r.stderr, /AGY_MODEL/, 'the fix is named');
2914
3053
  });
2915
3054
 
2916
- it('AGY_PROBE=1 with AGY_MODEL emptied still runs (probe exempt; posture model null on the probe receipt)', () => {
3055
+ it('AGY_PROBE=1 with AGY_MODEL emptied still runs (probe exempt; posture model null on the probe receipt)', async () => {
2917
3056
  const sb = makeSandbox();
2918
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: '', AGY_PROBE: '1' } });
3057
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: '', AGY_PROBE: '1' } });
2919
3058
  const receipts = readReceipts(sb.repo);
2920
3059
  rmSync(sb.home, { recursive: true, force: true });
2921
3060
  assert.equal(r.status, 0, r.stderr);
@@ -2923,10 +3062,10 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2923
3062
  assert.deepEqual(receipts[0].posture, { model: null }, 'an unknowable model is recorded null, never guessed');
2924
3063
  });
2925
3064
 
2926
- it('a HOSTILE model string (quotes + backslash) rides the receipt strictly JSON-encoded', () => {
3065
+ it('a HOSTILE model string (quotes + backslash) rides the receipt strictly JSON-encoded', async () => {
2927
3066
  const hostile = 'we"ird \\ mo"del';
2928
3067
  const sb = makeSandbox();
2929
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: hostile } });
3068
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: hostile } });
2930
3069
  const receipts = readReceipts(sb.repo); // JSON.parse throwing here IS the encoding failure
2931
3070
  rmSync(sb.home, { recursive: true, force: true });
2932
3071
  assert.equal(r.status, 0, r.stderr);
@@ -2934,9 +3073,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2934
3073
  assert.match(r.stderr, /review posture: /, 'the banner still renders');
2935
3074
  });
2936
3075
 
2937
- it('a model string carrying CONTROL BYTES refuses pre-spend (never a broken banner or receipt)', () => {
3076
+ it('a model string carrying CONTROL BYTES refuses pre-spend (never a broken banner or receipt)', async () => {
2938
3077
  const sb = makeSandbox();
2939
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: `bad${String.fromCharCode(1)}model` } });
3078
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: `bad${String.fromCharCode(1)}model` } });
2940
3079
  const receipts = readReceipts(sb.repo);
2941
3080
  rmSync(sb.home, { recursive: true, force: true });
2942
3081
  assert.notEqual(r.status, 0);
@@ -2945,9 +3084,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2945
3084
  assert.match(r.stderr, /control/i);
2946
3085
  });
2947
3086
 
2948
- it('the banner appends the RESOLVED hard timeout verbatim — banner-only, never in the receipt (AD-061)', () => {
3087
+ it('the banner appends the RESOLVED hard timeout verbatim — banner-only, never in the receipt (AD-061)', async () => {
2949
3088
  const sb = makeSandbox();
2950
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'] });
3089
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'] });
2951
3090
  const receipts = readReceipts(sb.repo);
2952
3091
  rmSync(sb.home, { recursive: true, force: true });
2953
3092
  assert.equal(r.status, 0, r.stderr);
@@ -2955,28 +3094,28 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2955
3094
  assert.deepEqual(Object.keys(receipts[0].posture), ['model'], 'timeout never enters the receipt posture');
2956
3095
  });
2957
3096
 
2958
- it('the banner prints the EFFECTIVE hard cap: an env override and a fractional duration ride verbatim', () => {
3097
+ it('the banner prints the EFFECTIVE hard cap: an env override and a fractional duration ride verbatim', async () => {
2959
3098
  for (const [envValue, want] of [['90s', '90s'], ['1.5m', '1\\.5m']]) {
2960
3099
  const sb = makeSandbox();
2961
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: envValue } });
3100
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: envValue } });
2962
3101
  rmSync(sb.home, { recursive: true, force: true });
2963
3102
  assert.equal(r.status, 0, r.stderr);
2964
3103
  assert.match(r.stderr, new RegExp(`^review posture: .* timeout=${want}$`, 'm'));
2965
3104
  }
2966
3105
  });
2967
3106
 
2968
- it('AGY_TIMEOUT (soft print-timeout) alone never moves the banner — the hard cap governs (precedence pin)', () => {
3107
+ it('AGY_TIMEOUT (soft print-timeout) alone never moves the banner — the hard cap governs (precedence pin)', async () => {
2969
3108
  const sb = makeSandbox();
2970
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_TIMEOUT: '5m' } });
3109
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_TIMEOUT: '5m' } });
2971
3110
  rmSync(sb.home, { recursive: true, force: true });
2972
3111
  assert.equal(r.status, 0, r.stderr);
2973
3112
  assert.match(r.stderr, /^review posture: .* timeout=30m$/m, 'the soft print-timeout is not the banner value');
2974
3113
  });
2975
3114
 
2976
- it('an INVALID / EMPTY / OVERFLOW effective AGY_HARD_TIMEOUT falls back to the built-in default (loud on invalid)', () => {
3115
+ it('an INVALID / EMPTY / OVERFLOW effective AGY_HARD_TIMEOUT falls back to the built-in default (loud on invalid)', async () => {
2977
3116
  for (const [bad, wantWarn] of [['10x', true], ['', false], ['99999999m', true]]) {
2978
3117
  const sb = makeSandbox();
2979
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: bad } });
3118
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: bad } });
2980
3119
  rmSync(sb.home, { recursive: true, force: true });
2981
3120
  assert.equal(r.status, 0, r.stderr);
2982
3121
  assert.match(r.stderr, /^review posture: .* timeout=30m$/m, `default must stand for ${JSON.stringify(bad)}`);
@@ -2984,9 +3123,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
2984
3123
  }
2985
3124
  });
2986
3125
 
2987
- it('a timeout value carrying CONTROL BYTES refuses pre-spend (the banner-field screen)', () => {
3126
+ it('a timeout value carrying CONTROL BYTES refuses pre-spend (the banner-field screen)', async () => {
2988
3127
  const sb = makeSandbox();
2989
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: `30m${String.fromCharCode(1)}` } });
3128
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_HARD_TIMEOUT: `30m${String.fromCharCode(1)}` } });
2990
3129
  const receipts = readReceipts(sb.repo);
2991
3130
  rmSync(sb.home, { recursive: true, force: true });
2992
3131
  assert.notEqual(r.status, 0);
@@ -3000,12 +3139,12 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3000
3139
  // child that HONORS the seam. A stale installed agy-run that never reads it could run uncapped
3001
3140
  // past the parent preflight, so the parent verifies the resolved child carries the seam token
3002
3141
  // and refuses loudly naming the refresh recovery otherwise.
3003
- it('a STALE agy-run child that does not honor the timeout seam refuses fail-closed (never a silently uncapped dispatch)', () => {
3142
+ it('a STALE agy-run child that does not honor the timeout seam refuses fail-closed (never a silently uncapped dispatch)', async () => {
3004
3143
  const sb = makeSandbox();
3005
3144
  const staleDir = join(sb.home, 'stale-bin');
3006
3145
  mkdirSync(staleDir, { recursive: true });
3007
3146
  writeFileSync(join(staleDir, 'agy-run'), '#!/usr/bin/env bash\nprintf "FAKE_AGY_REVIEW_OUTPUT\\n### Verdict\\nSHIP\\n"\n', { mode: 0o755 });
3008
- const r = run(sb, {
3147
+ const r = await run(sb, {
3009
3148
  args: ['code', '--facts', 'a tiny fact'],
3010
3149
  env: { PATH: `${staleDir}:${sb.bin}:${farmFor(['agy-run'])}` },
3011
3150
  });
@@ -3019,9 +3158,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3019
3158
  // Flow-orchestration Phase 4.2 (#26): the uncapped lane is CLOSED — without a capping binary the
3020
3159
  // preflight refuses by name BEFORE any CLI run (the pre-fix wrapper printed timeout=uncapped and
3021
3160
  // ran anyway). The shadow-proof resolver discipline now surfaces as the REFUSAL, not a banner.
3022
- it('fails CLOSED when no timeout/gtimeout is on PATH — refuses by name, agy never runs', () => {
3161
+ it('fails CLOSED when no timeout/gtimeout is on PATH — refuses by name, agy never runs', async () => {
3023
3162
  const sb = makeSandbox();
3024
- const r = run(sb, {
3163
+ const r = await run(sb, {
3025
3164
  args: ['code', '--facts', 'a tiny fact'],
3026
3165
  env: { PATH: `${sb.bin}:${farmFor(['agy', 'agy-run', 'timeout', 'gtimeout'])}` },
3027
3166
  });
@@ -3031,9 +3170,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3031
3170
  assert.equal(r.invoked, false, 'agy must NOT be invoked when the preflight refuses');
3032
3171
  });
3033
3172
 
3034
- it('an EXPORTED shell function shadowing timeout never fools the preflight (type -P discipline)', () => {
3173
+ it('an EXPORTED shell function shadowing timeout never fools the preflight (type -P discipline)', async () => {
3035
3174
  const sb = makeSandbox();
3036
- const r = run(sb, {
3175
+ const r = await run(sb, {
3037
3176
  args: ['code', '--facts', 'a tiny fact'],
3038
3177
  env: {
3039
3178
  PATH: `${sb.bin}:${farmFor(['agy', 'agy-run', 'timeout', 'gtimeout'])}`,
@@ -3045,9 +3184,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3045
3184
  assert.match(r.stderr, /hard-timeout preflight fails CLOSED/);
3046
3185
  });
3047
3186
 
3048
- it('an EXPORTED `type` function faking a path never fools the resolver (builtin type discipline)', () => {
3187
+ it('an EXPORTED `type` function faking a path never fools the resolver (builtin type discipline)', async () => {
3049
3188
  const sb = makeSandbox();
3050
- const r = run(sb, {
3189
+ const r = await run(sb, {
3051
3190
  args: ['code', '--facts', 'a tiny fact'],
3052
3191
  env: {
3053
3192
  PATH: `${sb.bin}:${farmFor(['agy', 'agy-run', 'timeout', 'gtimeout'])}`,
@@ -3059,9 +3198,9 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3059
3198
  assert.match(r.stderr, /hard-timeout preflight fails CLOSED/);
3060
3199
  });
3061
3200
 
3062
- it('a DEL (0x7f) byte in a banner field refuses pre-spend like the C0 range', () => {
3201
+ it('a DEL (0x7f) byte in a banner field refuses pre-spend like the C0 range', async () => {
3063
3202
  const sb = makeSandbox();
3064
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: `bad${String.fromCharCode(127)}model` } });
3203
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_MODEL: `bad${String.fromCharCode(127)}model` } });
3065
3204
  const receipts = readReceipts(sb.repo);
3066
3205
  rmSync(sb.home, { recursive: true, force: true });
3067
3206
  assert.notEqual(r.status, 0);
@@ -3070,10 +3209,10 @@ describe('agy-review.sh — dispatch-posture labeling (D5)', () => {
3070
3209
  assert.match(r.stderr, /control/i);
3071
3210
  });
3072
3211
 
3073
- it('a control byte in AGY_TIMEOUT refuses pre-spawn — agy-review forwards it to the child agy-run', () => {
3212
+ it('a control byte in AGY_TIMEOUT refuses pre-spawn — agy-review forwards it to the child agy-run', async () => {
3074
3213
  for (const c of [1, 127]) {
3075
3214
  const sb = makeSandbox();
3076
- const r = run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_TIMEOUT: `30m${String.fromCharCode(c)}` } });
3215
+ const r = await run(sb, { args: ['code', '--facts', 'a tiny fact'], env: { AGY_TIMEOUT: `30m${String.fromCharCode(c)}` } });
3077
3216
  const receipts = readReceipts(sb.repo);
3078
3217
  rmSync(sb.home, { recursive: true, force: true });
3079
3218
  assert.notEqual(r.status, 0, `must refuse control byte ${c}`);