claude-dev-env 1.81.0 → 1.83.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.
- package/CLAUDE.md +16 -13
- package/_shared/pr-loop/scripts/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/README.md +1 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviewer_availability_constants.py +12 -0
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +0 -2
- package/_shared/pr-loop/scripts/reviewer_availability.py +182 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +9 -33
- package/_shared/pr-loop/scripts/tests/CLAUDE.md +2 -0
- package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +159 -0
- package/_shared/pr-loop/scripts/tests/test_reviewer_availability_constants.py +36 -0
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +14 -4
- package/hooks/blocking/CLAUDE.md +3 -0
- package/hooks/blocking/code_rules_constants_config.py +159 -1
- package/hooks/blocking/code_rules_docstrings.py +312 -9
- package/hooks/blocking/code_rules_enforcer.py +29 -0
- package/hooks/blocking/code_rules_imports_logging.py +867 -1
- package/hooks/blocking/code_rules_naming_collection.py +141 -0
- package/hooks/blocking/code_rules_string_magic.py +68 -0
- package/hooks/blocking/conventional_pr_title_gate.py +444 -0
- package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
- package/hooks/blocking/reviewer_spawn_gate.py +182 -0
- package/hooks/blocking/stale_comment_reference_blocker.py +267 -0
- package/hooks/blocking/state_description_blocker.py +96 -5
- package/hooks/blocking/test_code_rules_config_duplicate_path_anchor.py +132 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +2 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +385 -0
- package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +67 -0
- package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +40 -0
- package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +213 -0
- package/hooks/blocking/test_code_rules_enforcer_referenced_underscore_loop.py +169 -0
- package/hooks/blocking/test_code_rules_js_bare_flag_return_directive.py +266 -0
- package/hooks/blocking/test_code_rules_js_sibling_return_object_key_drift.py +490 -0
- package/hooks/blocking/test_code_rules_logging_adjacent_literals.py +171 -0
- package/hooks/blocking/test_conventional_pr_title_gate.py +640 -0
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +9 -3
- package/hooks/blocking/test_reviewer_spawn_gate.py +230 -0
- package/hooks/blocking/test_stale_comment_reference_blocker.py +236 -0
- package/hooks/blocking/test_state_description_blocker.py +135 -0
- package/hooks/hooks.json +10 -0
- package/hooks/hooks_constants/CLAUDE.md +4 -1
- package/hooks/hooks_constants/blocking_check_limits.py +43 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +41 -0
- package/hooks/hooks_constants/conventional_pr_title_gate_constants.py +58 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
- package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +41 -0
- package/hooks/hooks_constants/stale_comment_reference_blocker_constants.py +76 -0
- package/hooks/hooks_constants/state_description_blocker_constants.py +8 -0
- package/package.json +1 -1
- package/rules/CLAUDE.md +4 -1
- package/rules/claude-md-orphan-file.md +5 -0
- package/rules/docstring-prose-matches-implementation.md +10 -1
- package/rules/env-var-table-code-drift.md +5 -0
- package/rules/es-exe-file-search.md +17 -0
- package/rules/no-historical-clutter.md +12 -1
- package/rules/orphan-css-class.md +5 -0
- package/rules/package-inventory-stale-entry.md +10 -0
- package/rules/paired-test-coverage.md +5 -0
- package/rules/plain-illustrative-docstrings.md +5 -0
- package/rules/verify-before-asking.md +7 -0
- package/rules/verify-runtime-state.md +40 -0
- package/rules/windows-filesystem-safe.md +8 -0
- package/rules/workers-done-before-complete.md +33 -0
- package/rules/workflow-substitution-slots.md +5 -0
- package/skills/CLAUDE.md +1 -1
- package/skills/autoconverge/SKILL.md +96 -5
- package/skills/autoconverge/reference/gotchas.md +5 -5
- package/skills/autoconverge/workflow/converge.contract.test.mjs +228 -27
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +154 -21
- package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +2 -2
- package/skills/autoconverge/workflow/converge.mjs +153 -49
- package/skills/autoconverge/workflow/converge_multi.mjs +6 -2
- package/skills/autoconverge/workflow/converge_multi.run-input.test.mjs +26 -0
- package/skills/everything-search/SKILL.md +5 -0
|
@@ -18,9 +18,10 @@ function functionBody(functionName) {
|
|
|
18
18
|
const productionModule = new Function(
|
|
19
19
|
`${functionBody('classifyCopilotOutcome')}\n` +
|
|
20
20
|
`${functionBody('resolveCopilotDown')}\n` +
|
|
21
|
-
'
|
|
21
|
+
`${functionBody('resolveReviewerDown')}\n` +
|
|
22
|
+
'return { classifyCopilotOutcome, resolveCopilotDown, resolveReviewerDown };',
|
|
22
23
|
)();
|
|
23
|
-
const { classifyCopilotOutcome, resolveCopilotDown } = productionModule;
|
|
24
|
+
const { classifyCopilotOutcome, resolveCopilotDown, resolveReviewerDown } = productionModule;
|
|
24
25
|
|
|
25
26
|
function copilotResult(overrides) {
|
|
26
27
|
return {
|
|
@@ -281,7 +282,7 @@ test('the mark-ready task in runGeneralUtilityTask opts the unflagged convergenc
|
|
|
281
282
|
);
|
|
282
283
|
});
|
|
283
284
|
|
|
284
|
-
test('the COPILOT phase short-circuits on
|
|
285
|
+
test('the COPILOT phase short-circuits on the unified reviewer-down gate before spawning the gate agent', () => {
|
|
285
286
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
286
287
|
assert.notEqual(copilotPhaseStart, -1, 'expected a COPILOT phase block');
|
|
287
288
|
const gateCallIndex = convergeSource.indexOf('await runCopilotGate(head)', copilotPhaseStart);
|
|
@@ -289,8 +290,8 @@ test('the COPILOT phase short-circuits on input.copilotDisabled before spawning
|
|
|
289
290
|
const beforeGate = convergeSource.slice(copilotPhaseStart, gateCallIndex);
|
|
290
291
|
assert.match(
|
|
291
292
|
beforeGate,
|
|
292
|
-
/if \(input\.copilotDisabled\)/,
|
|
293
|
-
'expected the
|
|
293
|
+
/if \(resolveReviewerDown\(reviewerAvailability\?\.copilot, input\.copilotDisabled \|\| false\)\)/,
|
|
294
|
+
'expected the unified resolveReviewerDown gate — fed by the reviewer-availability probe or the input override — to guard the COPILOT phase before any gate agent spawns',
|
|
294
295
|
);
|
|
295
296
|
assert.match(beforeGate, /copilotDown = true/, 'expected the bypass to mark copilotDown');
|
|
296
297
|
assert.match(beforeGate, /copilotNote =/, 'expected the bypass to set a copilotNote');
|
|
@@ -298,6 +299,16 @@ test('the COPILOT phase short-circuits on input.copilotDisabled before spawning
|
|
|
298
299
|
assert.match(beforeGate, /continue/, 'expected the bypass to continue without spawning the gate agent');
|
|
299
300
|
});
|
|
300
301
|
|
|
302
|
+
test('the COPILOT phase pre-spawn gate skips the gate agent via input.copilotDisabled with no probe entry', () => {
|
|
303
|
+
const isDownFromInput = resolveReviewerDown(undefined, true);
|
|
304
|
+
assert.equal(isDownFromInput, true, 'expected the input override alone to report Copilot down with no probe entry');
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test('the COPILOT phase pre-spawn gate skips the gate agent via a probe down entry with no input override', () => {
|
|
308
|
+
const isDownFromProbe = resolveReviewerDown({ down: true, reason: 'copilot-quota: out of premium-interaction quota' }, false);
|
|
309
|
+
assert.equal(isDownFromProbe, true, 'expected a probe entry reporting down to skip the gate agent with no input override set');
|
|
310
|
+
});
|
|
311
|
+
|
|
301
312
|
test('copilotDown initializes from input.copilotDisabled so the pre-check decision carries into the loop', () => {
|
|
302
313
|
assert.match(
|
|
303
314
|
convergeSource,
|
|
@@ -308,8 +319,11 @@ test('copilotDown initializes from input.copilotDisabled so the pre-check decisi
|
|
|
308
319
|
|
|
309
320
|
test('a copilotDisabled run reaches FINALIZE with --copilot-down', () => {
|
|
310
321
|
const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
|
|
311
|
-
const bypassStart = convergeSource.indexOf(
|
|
312
|
-
|
|
322
|
+
const bypassStart = convergeSource.indexOf(
|
|
323
|
+
'if (resolveReviewerDown(reviewerAvailability?.copilot, input.copilotDisabled || false))',
|
|
324
|
+
copilotPhaseStart,
|
|
325
|
+
);
|
|
326
|
+
assert.notEqual(bypassStart, -1, 'expected the unified resolveReviewerDown bypass in the COPILOT phase');
|
|
313
327
|
const bypassBlock = convergeSource.slice(bypassStart, bypassStart + 800);
|
|
314
328
|
assert.match(bypassBlock, /copilotDown = true/, 'expected the bypass to set copilotDown');
|
|
315
329
|
assert.match(bypassBlock, /phase = 'FINALIZE'/, 'expected the bypass to advance to FINALIZE');
|
|
@@ -373,7 +387,7 @@ test('openStandardsFollowUpOnce gates spawnStandardsFollowUp behind the run-once
|
|
|
373
387
|
);
|
|
374
388
|
assert.match(
|
|
375
389
|
onceBody,
|
|
376
|
-
/await spawnStandardsFollowUp\(head, findings, sourceLabel, wasStandardsHardeningPrOpened\)/,
|
|
390
|
+
/await spawnStandardsFollowUp\(head, findings, sourceLabel, wasStandardsHardeningPrOpened, deferredReviewerFlags\)/,
|
|
377
391
|
'expected the helper to pass the remembered hardening state into the spawn so an already-opened PR is never re-opened',
|
|
378
392
|
);
|
|
379
393
|
assert.match(
|
|
@@ -388,8 +402,8 @@ test('openStandardsFollowUpOnce gates spawnStandardsFollowUp behind the run-once
|
|
|
388
402
|
);
|
|
389
403
|
assert.match(
|
|
390
404
|
onceBody,
|
|
391
|
-
/
|
|
392
|
-
'expected the
|
|
405
|
+
/hardeningPrOpened: wasStandardsHardeningPrOpened/,
|
|
406
|
+
'expected the helper to return the cached hardening outcome as hardeningPrOpened',
|
|
393
407
|
);
|
|
394
408
|
});
|
|
395
409
|
|
|
@@ -431,14 +445,18 @@ function extractCallableSource(functionName) {
|
|
|
431
445
|
return convergeSource.slice(declarationStart, index);
|
|
432
446
|
}
|
|
433
447
|
|
|
434
|
-
|
|
448
|
+
const parseableHardeningCommitResult = { hardeningPrUrl: 'https://github.com/owner/repo/pull/7', summary: 'opened' };
|
|
449
|
+
|
|
450
|
+
function loadStandardsFollowUpRuntime(recordedCalls, standardsEditResult, hardeningCommitResult = parseableHardeningCommitResult) {
|
|
435
451
|
const runtimeSource =
|
|
436
452
|
'let hasStandardsFollowUpFiled = false;\n' +
|
|
437
453
|
'let wasStandardsHardeningPrOpened = false;\n' +
|
|
438
454
|
"let standardsFollowUpIssueUrl = '';\n" +
|
|
439
455
|
'async function runCodeEditorTask(taskName, context) {\n' +
|
|
440
456
|
' recordedCalls.push({ task: taskName, context });\n' +
|
|
441
|
-
"
|
|
457
|
+
" if (taskName === 'standards-edit') return standardsEditResult;\n" +
|
|
458
|
+
" if (taskName === 'hardening-commit') return hardeningCommitResult;\n" +
|
|
459
|
+
' return {};\n' +
|
|
442
460
|
'}\n' +
|
|
443
461
|
'async function runVerifierTask() {\n' +
|
|
444
462
|
' return { passed: true };\n' +
|
|
@@ -450,6 +468,7 @@ function loadStandardsFollowUpRuntime(recordedCalls, standardsEditResult) {
|
|
|
450
468
|
`${extractCallableSource('collectFindingThreadIds')}\n` +
|
|
451
469
|
`${extractCallableSource('findingsCarryThreads')}\n` +
|
|
452
470
|
`${extractCallableSource('shouldOpenStandardsFollowUp')}\n` +
|
|
471
|
+
`${extractCallableSource('parseDeferredPr')}\n` +
|
|
453
472
|
`${extractCallableSource('spawnStandardsFollowUp')}\n` +
|
|
454
473
|
`${extractCallableSource('resolveStandardsThreadsForBatch')}\n` +
|
|
455
474
|
`${extractCallableSource('openStandardsFollowUpOnce')}\n` +
|
|
@@ -457,12 +476,45 @@ function loadStandardsFollowUpRuntime(recordedCalls, standardsEditResult) {
|
|
|
457
476
|
' openStandardsFollowUpOnce,\n' +
|
|
458
477
|
' guards: () => ({ hasStandardsFollowUpFiled, wasStandardsHardeningPrOpened, standardsFollowUpIssueUrl }),\n' +
|
|
459
478
|
'};';
|
|
460
|
-
return new Function('recordedCalls', 'standardsEditResult', runtimeSource)(
|
|
479
|
+
return new Function('recordedCalls', 'standardsEditResult', 'hardeningCommitResult', runtimeSource)(
|
|
461
480
|
recordedCalls,
|
|
462
481
|
standardsEditResult,
|
|
482
|
+
hardeningCommitResult,
|
|
463
483
|
);
|
|
464
484
|
}
|
|
465
485
|
|
|
486
|
+
function loadParseDeferredPr() {
|
|
487
|
+
return new Function(`${extractCallableSource('parseDeferredPr')}\nreturn parseDeferredPr;`)();
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
test('parseDeferredPr parses a full canonical hardening PR URL into its coordinates', () => {
|
|
491
|
+
const parseDeferredPr = loadParseDeferredPr();
|
|
492
|
+
assert.deepEqual(
|
|
493
|
+
parseDeferredPr('https://github.com/jl-cmd/claude-code-config/pull/824'),
|
|
494
|
+
{ owner: 'jl-cmd', repo: 'claude-code-config', prNumber: 824 },
|
|
495
|
+
);
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test('parseDeferredPr accepts a trailing slash, query string, or fragment on the PR URL', () => {
|
|
499
|
+
const parseDeferredPr = loadParseDeferredPr();
|
|
500
|
+
assert.equal(parseDeferredPr('https://github.com/owner/repo/pull/7/').prNumber, 7);
|
|
501
|
+
assert.equal(parseDeferredPr('https://github.com/owner/repo/pull/7?w=1').prNumber, 7);
|
|
502
|
+
assert.equal(parseDeferredPr('https://github.com/owner/repo/pull/7#issuecomment-42').prNumber, 7);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
test('parseDeferredPr rejects a PR URL embedded in surrounding log text so it never parses the wrong number', () => {
|
|
506
|
+
const parseDeferredPr = loadParseDeferredPr();
|
|
507
|
+
assert.equal(
|
|
508
|
+
parseDeferredPr('opened https://github.com/owner/repo/pull/7 then https://github.com/owner/repo/pull/9'),
|
|
509
|
+
null,
|
|
510
|
+
);
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
test('parseDeferredPr rejects a deep-linked pull path so a non-canonical URL parses no coordinate', () => {
|
|
514
|
+
const parseDeferredPr = loadParseDeferredPr();
|
|
515
|
+
assert.equal(parseDeferredPr('https://github.com/owner/repo/pull/7/files'), null);
|
|
516
|
+
});
|
|
517
|
+
|
|
466
518
|
test('a second standards-only round never re-opens a hardening PR after the first round opened one but failed to file the issue', async () => {
|
|
467
519
|
const recordedCalls = [];
|
|
468
520
|
const issueFailedHardeningStaged = {
|
|
@@ -473,8 +525,8 @@ test('a second standards-only round never re-opens a hardening PR after the firs
|
|
|
473
525
|
};
|
|
474
526
|
const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged);
|
|
475
527
|
|
|
476
|
-
const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round');
|
|
477
|
-
const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot');
|
|
528
|
+
const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round', { copilotDisabled: false, bugbotDisabled: false });
|
|
529
|
+
const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot', { copilotDisabled: false, bugbotDisabled: false });
|
|
478
530
|
|
|
479
531
|
const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
|
|
480
532
|
assert.equal(
|
|
@@ -482,8 +534,8 @@ test('a second standards-only round never re-opens a hardening PR after the firs
|
|
|
482
534
|
1,
|
|
483
535
|
'expected the hardening PR to be committed exactly once even when the follow-up issue filing must retry on the second round',
|
|
484
536
|
);
|
|
485
|
-
assert.equal(firstRoundHardeningPr, true, 'expected the first round to open the hardening PR');
|
|
486
|
-
assert.equal(secondRoundHardeningPr, true, 'expected the second round to report the hardening PR as opened for this run');
|
|
537
|
+
assert.equal(firstRoundHardeningPr.hardeningPrOpened, true, 'expected the first round to open the hardening PR');
|
|
538
|
+
assert.equal(secondRoundHardeningPr.hardeningPrOpened, true, 'expected the second round to report the hardening PR as opened for this run');
|
|
487
539
|
assert.equal(
|
|
488
540
|
runtime.guards().wasStandardsHardeningPrOpened,
|
|
489
541
|
true,
|
|
@@ -496,6 +548,65 @@ test('a second standards-only round never re-opens a hardening PR after the firs
|
|
|
496
548
|
);
|
|
497
549
|
});
|
|
498
550
|
|
|
551
|
+
test('a hardening-commit that opens a PR but returns an unparseable URL still latches the run-once guard', async () => {
|
|
552
|
+
const recordedCalls = [];
|
|
553
|
+
const issueFailedHardeningStaged = {
|
|
554
|
+
issueUrl: '',
|
|
555
|
+
hardeningEdited: true,
|
|
556
|
+
hardeningRepoPath: '/tmp/hardening',
|
|
557
|
+
hardeningBranch: 'harden-standards',
|
|
558
|
+
};
|
|
559
|
+
const unparseableUrlHardeningCommitResult = { hardeningPrUrl: 'draft-hardening-pr-opened', summary: 'opened' };
|
|
560
|
+
const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged, unparseableUrlHardeningCommitResult);
|
|
561
|
+
|
|
562
|
+
const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round', { copilotDisabled: false, bugbotDisabled: false });
|
|
563
|
+
const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot', { copilotDisabled: false, bugbotDisabled: false });
|
|
564
|
+
|
|
565
|
+
const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
|
|
566
|
+
assert.equal(
|
|
567
|
+
hardeningCommitCalls,
|
|
568
|
+
1,
|
|
569
|
+
'expected the non-empty-URL commit to latch the guard so a second round opens no duplicate hardening PR',
|
|
570
|
+
);
|
|
571
|
+
assert.equal(firstRoundHardeningPr.hardeningPrOpened, true, 'expected a non-empty (though unparseable) URL to report the hardening PR as opened');
|
|
572
|
+
assert.equal(firstRoundHardeningPr.deferredPr, null, 'expected the unparseable URL to contribute no deferred coordinate');
|
|
573
|
+
assert.equal(secondRoundHardeningPr.hardeningPrOpened, true, 'expected the second round to report the hardening PR as opened for this run');
|
|
574
|
+
assert.equal(
|
|
575
|
+
runtime.guards().wasStandardsHardeningPrOpened,
|
|
576
|
+
true,
|
|
577
|
+
'expected the hardening guard to latch even though the returned URL never parsed',
|
|
578
|
+
);
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
test('a hardening-commit that opens no PR (empty hardeningPrUrl) leaves the run-once guard clear so a later round retries the open', async () => {
|
|
582
|
+
const recordedCalls = [];
|
|
583
|
+
const issueFailedHardeningStaged = {
|
|
584
|
+
issueUrl: '',
|
|
585
|
+
hardeningEdited: true,
|
|
586
|
+
hardeningRepoPath: '/tmp/hardening',
|
|
587
|
+
hardeningBranch: 'harden-standards',
|
|
588
|
+
};
|
|
589
|
+
const noPrHardeningCommitResult = { hardeningPrUrl: '', summary: 'no PR opened' };
|
|
590
|
+
const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged, noPrHardeningCommitResult);
|
|
591
|
+
|
|
592
|
+
const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round', { copilotDisabled: false, bugbotDisabled: false });
|
|
593
|
+
const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot', { copilotDisabled: false, bugbotDisabled: false });
|
|
594
|
+
|
|
595
|
+
const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
|
|
596
|
+
assert.equal(
|
|
597
|
+
hardeningCommitCalls,
|
|
598
|
+
2,
|
|
599
|
+
'expected the empty-URL commit (no PR opened) to leave the guard clear so a later round retries the open',
|
|
600
|
+
);
|
|
601
|
+
assert.equal(firstRoundHardeningPr.hardeningPrOpened, false, 'expected an empty URL to report no hardening PR opened');
|
|
602
|
+
assert.equal(secondRoundHardeningPr.hardeningPrOpened, false, 'expected the retry round to still report no hardening PR opened');
|
|
603
|
+
assert.equal(
|
|
604
|
+
runtime.guards().wasStandardsHardeningPrOpened,
|
|
605
|
+
false,
|
|
606
|
+
'expected the hardening guard to stay clear when no PR opened so the open keeps retrying',
|
|
607
|
+
);
|
|
608
|
+
});
|
|
609
|
+
|
|
499
610
|
test('a later standards-only round resolves its own review threads after the follow-up issue was already filed', async () => {
|
|
500
611
|
const recordedCalls = [];
|
|
501
612
|
const issueFiledNoHardening = {
|
|
@@ -506,8 +617,8 @@ test('a later standards-only round resolves its own review threads after the fol
|
|
|
506
617
|
};
|
|
507
618
|
const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFiledNoHardening);
|
|
508
619
|
|
|
509
|
-
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round');
|
|
510
|
-
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'b.py', line: 2, replyToCommentId: 42 }], 'copilot');
|
|
620
|
+
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round', { copilotDisabled: false, bugbotDisabled: false });
|
|
621
|
+
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'b.py', line: 2, replyToCommentId: 42 }], 'copilot', { copilotDisabled: false, bugbotDisabled: false });
|
|
511
622
|
|
|
512
623
|
const standardsEditCalls = recordedCalls.filter((call) => call.task === 'standards-edit');
|
|
513
624
|
assert.equal(standardsEditCalls.length, 1, 'expected the follow-up issue to be filed exactly once across the run');
|
|
@@ -536,8 +647,8 @@ test('a reuse-path standards round carrying no review threads spawns no thread-r
|
|
|
536
647
|
};
|
|
537
648
|
const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFiledNoHardening);
|
|
538
649
|
|
|
539
|
-
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round');
|
|
540
|
-
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'b.py', line: 2, replyToCommentId: null }], 'copilot');
|
|
650
|
+
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round', { copilotDisabled: false, bugbotDisabled: false });
|
|
651
|
+
await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'b.py', line: 2, replyToCommentId: null }], 'copilot', { copilotDisabled: false, bugbotDisabled: false });
|
|
541
652
|
|
|
542
653
|
const resolveCalls = recordedCalls.filter((call) => call.task === 'standards-resolve-threads');
|
|
543
654
|
assert.equal(
|
|
@@ -547,3 +658,25 @@ test('a reuse-path standards round carrying no review threads spawns no thread-r
|
|
|
547
658
|
);
|
|
548
659
|
});
|
|
549
660
|
|
|
661
|
+
test('resolveReviewerDown is the single reviewer-down gate; resolveBugbotDown no longer exists', () => {
|
|
662
|
+
assert.doesNotMatch(
|
|
663
|
+
convergeSource,
|
|
664
|
+
/function resolveBugbotDown\(/,
|
|
665
|
+
'expected resolveBugbotDown to be removed in favor of the shared resolveReviewerDown gate',
|
|
666
|
+
);
|
|
667
|
+
assert.match(convergeSource, /function resolveReviewerDown\(/, 'expected the shared resolveReviewerDown gate to exist');
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
test('resolveReviewerDown reports down when the input override is set, even with an available probe entry', () => {
|
|
671
|
+
assert.equal(resolveReviewerDown({ down: false, reason: 'available' }, true), true);
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
test('resolveReviewerDown reports available (fail-open) when the probe entry is missing and no input override is set', () => {
|
|
675
|
+
assert.equal(resolveReviewerDown(null, false), false);
|
|
676
|
+
assert.equal(resolveReviewerDown(undefined, false), false);
|
|
677
|
+
});
|
|
678
|
+
|
|
679
|
+
test('resolveReviewerDown reports available when the probe entry explicitly reports available', () => {
|
|
680
|
+
assert.equal(resolveReviewerDown({ down: false, reason: 'available' }, false), false);
|
|
681
|
+
});
|
|
682
|
+
|
|
@@ -31,7 +31,7 @@ test('isMergeConflicting reports a conflict only when the check returned conflic
|
|
|
31
31
|
assert.equal(isMergeConflicting({ conflicting: false }), false);
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
test('the git agent handles merge-conflict checks with
|
|
34
|
+
test('the git agent handles merge-conflict checks with a Monitor-based poll', () => {
|
|
35
35
|
const gitBody = functionBody('runGitTask');
|
|
36
36
|
assert.match(gitBody, /mergeable/, 'expected the git agent to read the PR mergeable field');
|
|
37
37
|
assert.match(
|
|
@@ -40,7 +40,7 @@ test('the git agent handles merge-conflict checks with shell-agnostic polling',
|
|
|
40
40
|
'expected the git agent merge check to be read-only',
|
|
41
41
|
);
|
|
42
42
|
assert.match(gitBody, /MERGE_CONFLICT_SCHEMA/, 'expected the git agent to return MERGE_CONFLICT_SCHEMA');
|
|
43
|
-
assert.match(gitBody, /
|
|
43
|
+
assert.match(gitBody, /Monitor tool/, 'expected a Monitor-based poll delay, not a foreground sleep');
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
test('runCodeEditorTask conflict-edit path rebases onto origin/main and makes no push', () => {
|