claude-dev-env 1.82.0 → 1.84.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 (217) hide show
  1. package/CLAUDE.md +16 -13
  2. package/_shared/pr-loop/audit-contract.md +24 -12
  3. package/_shared/pr-loop/scripts/CLAUDE.md +1 -0
  4. package/_shared/pr-loop/scripts/README.md +1 -0
  5. package/_shared/pr-loop/scripts/_claude_permissions_common.py +16 -5
  6. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  7. package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -0
  8. package/_shared/pr-loop/scripts/pr_loop_shared_constants/reviewer_availability_constants.py +12 -0
  9. package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +0 -2
  10. package/_shared/pr-loop/scripts/reviewer_availability.py +182 -0
  11. package/_shared/pr-loop/scripts/reviews_disabled.py +2 -0
  12. package/_shared/pr-loop/scripts/terminology_sweep.py +9 -33
  13. package/_shared/pr-loop/scripts/tests/CLAUDE.md +2 -0
  14. package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +212 -0
  15. package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +18 -0
  16. package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +18 -0
  17. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +37 -0
  18. package/_shared/pr-loop/scripts/tests/test_reviewer_availability.py +159 -0
  19. package/_shared/pr-loop/scripts/tests/test_reviewer_availability_constants.py +36 -0
  20. package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +14 -0
  21. package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +14 -4
  22. package/agents/clean-coder.md +3 -0
  23. package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
  24. package/bin/install.mjs +16 -8
  25. package/bin/install.test.mjs +17 -6
  26. package/commands/CLAUDE.md +0 -1
  27. package/docs/CODE_RULES.md +1 -1
  28. package/hooks/blocking/CLAUDE.md +2 -4
  29. package/hooks/blocking/code_rules_constants_config.py +164 -1
  30. package/hooks/blocking/code_rules_docstrings.py +428 -15
  31. package/hooks/blocking/code_rules_enforcer.py +33 -0
  32. package/hooks/blocking/code_rules_imports_logging.py +867 -1
  33. package/hooks/blocking/code_rules_magic_values.py +5 -0
  34. package/hooks/blocking/code_rules_naming_collection.py +152 -6
  35. package/hooks/blocking/code_rules_shared.py +34 -0
  36. package/hooks/blocking/code_rules_string_magic.py +68 -0
  37. package/hooks/blocking/duplicate_rmtree_helper_blocker.py +4 -4
  38. package/hooks/blocking/pre_tool_use_dispatcher.py +3 -3
  39. package/hooks/blocking/reviewer_spawn_gate.py +182 -0
  40. package/hooks/blocking/stale_comment_reference_blocker.py +267 -0
  41. package/hooks/blocking/state_description_blocker.py +96 -5
  42. package/hooks/blocking/test_code_rules_config_duplicate_path_anchor.py +132 -0
  43. package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +2 -0
  44. package/hooks/blocking/test_code_rules_enforcer_docstring_delegation_summary.py +385 -0
  45. package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +197 -0
  46. package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +27 -0
  47. package/hooks/blocking/test_code_rules_enforcer_join_separator_magic.py +67 -0
  48. package/hooks/blocking/test_code_rules_enforcer_module_docstring_roster.py +40 -0
  49. package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +213 -0
  50. package/hooks/blocking/test_code_rules_enforcer_referenced_underscore_loop.py +169 -0
  51. package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +33 -0
  52. package/hooks/blocking/test_code_rules_enforcer_split_shared.py +23 -0
  53. package/hooks/blocking/test_code_rules_js_bare_flag_return_directive.py +266 -0
  54. package/hooks/blocking/test_code_rules_js_sibling_return_object_key_drift.py +490 -0
  55. package/hooks/blocking/test_code_rules_logging_adjacent_literals.py +171 -0
  56. package/hooks/blocking/test_code_rules_magic_values.py +54 -0
  57. package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +0 -6
  58. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +184 -0
  59. package/hooks/blocking/test_pre_tool_use_dispatcher.py +9 -3
  60. package/hooks/blocking/test_reviewer_spawn_gate.py +230 -0
  61. package/hooks/blocking/test_shared_stdin_adoption.py +5 -30
  62. package/hooks/blocking/test_stale_comment_reference_blocker.py +236 -0
  63. package/hooks/blocking/test_state_description_blocker.py +135 -0
  64. package/hooks/hooks.json +5 -0
  65. package/hooks/hooks_constants/CLAUDE.md +3 -4
  66. package/hooks/hooks_constants/blocking_check_limits.py +46 -0
  67. package/hooks/hooks_constants/code_rules_enforcer_constants.py +41 -0
  68. package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
  69. package/hooks/hooks_constants/duplicate_rmtree_helper_blocker_constants.py +0 -1
  70. package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -5
  71. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +4 -0
  72. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +41 -0
  73. package/hooks/hooks_constants/stale_comment_reference_blocker_constants.py +76 -0
  74. package/hooks/hooks_constants/state_description_blocker_constants.py +8 -0
  75. package/hooks/validation/post_tool_use_dispatcher.py +1 -1
  76. package/hooks/validation/test_post_tool_use_dispatcher.py +51 -35
  77. package/hooks/workflow/CLAUDE.md +2 -8
  78. package/package.json +1 -1
  79. package/rules/CLAUDE.md +5 -2
  80. package/rules/claude-md-orphan-file.md +5 -0
  81. package/rules/docstring-prose-matches-implementation.md +10 -1
  82. package/rules/env-var-table-code-drift.md +5 -0
  83. package/rules/es-exe-file-search.md +17 -0
  84. package/rules/no-historical-clutter.md +12 -1
  85. package/rules/orphan-css-class.md +5 -0
  86. package/rules/package-inventory-stale-entry.md +10 -0
  87. package/rules/paired-test-coverage.md +5 -0
  88. package/rules/plain-illustrative-docstrings.md +40 -1
  89. package/rules/verify-before-asking.md +7 -0
  90. package/rules/verify-runtime-state.md +40 -0
  91. package/rules/windows-filesystem-safe.md +8 -0
  92. package/rules/workers-done-before-complete.md +33 -0
  93. package/rules/workflow-substitution-slots.md +5 -0
  94. package/skills/CLAUDE.md +7 -2
  95. package/skills/autoconverge/SKILL.md +48 -61
  96. package/skills/autoconverge/reference/closing-report.md +6 -6
  97. package/skills/autoconverge/reference/convergence.md +17 -15
  98. package/skills/autoconverge/reference/gotchas.md +6 -3
  99. package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +2 -9
  100. package/skills/autoconverge/workflow/converge.contract.test.mjs +153 -10
  101. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +55 -19
  102. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +16 -9
  103. package/skills/autoconverge/workflow/converge.mjs +167 -106
  104. package/skills/autoconverge/workflow/render_report.py +7 -11
  105. package/skills/bugteam/CLAUDE.md +1 -1
  106. package/skills/bugteam/PROMPTS.md +7 -6
  107. package/skills/bugteam/SKILL.md +25 -72
  108. package/skills/bugteam/reference/CLAUDE.md +1 -3
  109. package/skills/bugteam/reference/README.md +1 -1
  110. package/skills/bugteam/reference/audit-and-teammates.md +1 -1
  111. package/skills/bugteam/reference/obstacles/fix-publish-summary.md +1 -1
  112. package/skills/bugteam/reference/team-setup.md +8 -7
  113. package/skills/bugteam/scripts/CLAUDE.md +0 -6
  114. package/skills/bugteam/scripts/README.md +0 -4
  115. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +0 -1
  116. package/skills/code/SKILL.md +2 -0
  117. package/skills/copilot-review/CLAUDE.md +1 -1
  118. package/skills/copilot-review/SKILL.md +25 -23
  119. package/skills/everything-search/SKILL.md +5 -0
  120. package/skills/findbugs/CLAUDE.md +2 -2
  121. package/skills/findbugs/SKILL.md +22 -83
  122. package/skills/fixbugs/SKILL.md +2 -4
  123. package/skills/log-audit/CLAUDE.md +20 -0
  124. package/skills/log-audit/SKILL.md +68 -0
  125. package/skills/log-audit/reference/CLAUDE.md +9 -0
  126. package/skills/log-audit/reference/charter.md +52 -0
  127. package/skills/log-audit/scripts/CLAUDE.md +27 -0
  128. package/skills/log-audit/scripts/cluster_recurrences.py +261 -0
  129. package/skills/log-audit/scripts/collect_log_window.py +199 -0
  130. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +12 -0
  131. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +23 -0
  132. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +24 -0
  133. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +49 -0
  134. package/skills/log-audit/scripts/mine_copilot_findings.py +302 -0
  135. package/skills/log-audit/scripts/test_cluster_recurrences.py +160 -0
  136. package/skills/log-audit/scripts/test_collect_log_window.py +111 -0
  137. package/skills/log-audit/scripts/test_mine_copilot_findings.py +126 -0
  138. package/skills/monitor-open-prs/SKILL.md +2 -2
  139. package/skills/post-audit-findings/SKILL.md +84 -0
  140. package/skills/pr-converge/CLAUDE.md +2 -0
  141. package/skills/pr-converge/SKILL.md +72 -59
  142. package/skills/pr-converge/reference/CLAUDE.md +1 -1
  143. package/skills/pr-converge/reference/convergence-gates.md +16 -19
  144. package/skills/pr-converge/reference/examples.md +5 -5
  145. package/skills/pr-converge/reference/fix-protocol.md +16 -43
  146. package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +1 -1
  147. package/skills/pr-converge/reference/per-tick.md +24 -45
  148. package/skills/pr-converge/reference/state-schema.md +15 -0
  149. package/skills/pr-converge/scripts/README.md +3 -5
  150. package/skills/pr-fix-protocol/SKILL.md +70 -0
  151. package/skills/pr-loop-lifecycle/SKILL.md +73 -0
  152. package/skills/{bugteam → pr-loop-lifecycle}/reference/teardown-publish-permissions.md +22 -24
  153. package/skills/pr-scope-resolve/SKILL.md +48 -0
  154. package/skills/qbug/CLAUDE.md +4 -4
  155. package/skills/qbug/SKILL.md +46 -144
  156. package/skills/qbug/test_qbug_skill_audit_schema.py +2 -2
  157. package/skills/qbug/test_qbug_skill_post_fix_audit.py +1 -1
  158. package/skills/reviewer-gates/SKILL.md +96 -0
  159. package/skills/session-log/CLAUDE.md +7 -7
  160. package/skills/session-log/SKILL.md +27 -44
  161. package/skills/test_markdown_link_integrity.py +103 -0
  162. package/commands/doc-gist.md +0 -16
  163. package/hooks/blocking/_md_to_html_blocker_test_support.py +0 -65
  164. package/hooks/blocking/conftest.py +0 -30
  165. package/hooks/blocking/md_path_exemptions.py +0 -224
  166. package/hooks/blocking/md_to_html_blocker.py +0 -155
  167. package/hooks/blocking/test_md_to_html_blocker_exemptions.py +0 -434
  168. package/hooks/blocking/test_md_to_html_blocker_extensions.py +0 -157
  169. package/hooks/blocking/test_md_to_html_blocker_path_resolution.py +0 -336
  170. package/hooks/hooks_constants/doc_gist_auto_publish_constants.py +0 -18
  171. package/hooks/hooks_constants/html_companion_constants.py +0 -20
  172. package/hooks/hooks_constants/md_to_html_blocker_constants.py +0 -76
  173. package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +0 -125
  174. package/hooks/workflow/doc_gist_auto_publish.py +0 -144
  175. package/hooks/workflow/md_to_html_companion.py +0 -358
  176. package/hooks/workflow/test_doc_gist_auto_publish.py +0 -117
  177. package/hooks/workflow/test_md_to_html_companion.py +0 -613
  178. package/skills/bugteam/reference/audit-contract.md +0 -163
  179. package/skills/bugteam/scripts/_bugteam_permissions_common.py +0 -455
  180. package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +0 -69
  181. package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -280
  182. package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -266
  183. package/skills/bugteam/scripts/test__bugteam_permissions_common.py +0 -160
  184. package/skills/bugteam/scripts/test_agent_config_carveout.py +0 -356
  185. package/skills/bugteam/scripts/test_bugteam_permissions_common.py +0 -140
  186. package/skills/doc-gist/CLAUDE.md +0 -25
  187. package/skills/doc-gist/SKILL.md +0 -97
  188. package/skills/doc-gist/references/CLAUDE.md +0 -9
  189. package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +0 -453
  190. package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +0 -515
  191. package/skills/doc-gist/references/examples/03-code-review-pr.html +0 -638
  192. package/skills/doc-gist/references/examples/04-code-understanding.html +0 -491
  193. package/skills/doc-gist/references/examples/05-design-system.html +0 -629
  194. package/skills/doc-gist/references/examples/06-component-variants.html +0 -605
  195. package/skills/doc-gist/references/examples/07-prototype-animation.html +0 -455
  196. package/skills/doc-gist/references/examples/08-prototype-interaction.html +0 -396
  197. package/skills/doc-gist/references/examples/09-slide-deck.html +0 -592
  198. package/skills/doc-gist/references/examples/10-svg-illustrations.html +0 -492
  199. package/skills/doc-gist/references/examples/11-status-report.html +0 -528
  200. package/skills/doc-gist/references/examples/12-incident-report.html +0 -596
  201. package/skills/doc-gist/references/examples/13-flowchart-diagram.html +0 -395
  202. package/skills/doc-gist/references/examples/14-research-feature-explainer.html +0 -381
  203. package/skills/doc-gist/references/examples/15-research-concept-explainer.html +0 -368
  204. package/skills/doc-gist/references/examples/16-implementation-plan.html +0 -702
  205. package/skills/doc-gist/references/examples/17-pr-writeup.html +0 -595
  206. package/skills/doc-gist/references/examples/18-editor-triage-board.html +0 -573
  207. package/skills/doc-gist/references/examples/19-editor-feature-flags.html +0 -663
  208. package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +0 -722
  209. package/skills/doc-gist/references/examples/21-decision-signoff.html +0 -546
  210. package/skills/doc-gist/references/examples/CLAUDE.md +0 -25
  211. package/skills/doc-gist/references/examples/README.md +0 -5
  212. package/skills/doc-gist/scripts/CLAUDE.md +0 -27
  213. package/skills/doc-gist/scripts/doc_gist_scripts_constants/CLAUDE.md +0 -10
  214. package/skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py +0 -16
  215. package/skills/doc-gist/scripts/gist_upload.py +0 -177
  216. package/skills/doc-gist/scripts/test_gist_upload.py +0 -51
  217. /package/skills/{doc-gist/scripts/doc_gist_scripts_constants → log-audit/scripts/log_audit_constants}/__init__.py +0 -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
- 'return { classifyCopilotOutcome, resolveCopilotDown };',
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 {
@@ -72,7 +73,7 @@ test('Copilot findings route to a fix when Copilot is reachable and not down', (
72
73
 
73
74
  test('COPILOT_SCHEMA carries a required down field', () => {
74
75
  const schemaStart = convergeSource.indexOf('const COPILOT_SCHEMA =');
75
- const schemaEnd = convergeSource.indexOf('const HEAD_SCHEMA =');
76
+ const schemaEnd = convergeSource.indexOf('const REVIEWER_AVAILABILITY_SCHEMA =');
76
77
  assert.notEqual(schemaStart, -1, 'expected COPILOT_SCHEMA to exist');
77
78
  const schemaSource = convergeSource.slice(schemaStart, schemaEnd);
78
79
  assert.match(schemaSource, /down:\s*\{\s*type:\s*'boolean'/);
@@ -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 input.copilotDisabled before spawning the gate agent', () => {
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 quota pre-check bypass to guard the COPILOT phase before any gate agent spawns',
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('if (input.copilotDisabled)', copilotPhaseStart);
312
- assert.notEqual(bypassStart, -1, 'expected an input.copilotDisabled bypass in the COPILOT phase');
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(
@@ -511,8 +525,8 @@ test('a second standards-only round never re-opens a hardening PR after the firs
511
525
  };
512
526
  const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged);
513
527
 
514
- const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round');
515
- 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 });
516
530
 
517
531
  const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
518
532
  assert.equal(
@@ -545,8 +559,8 @@ test('a hardening-commit that opens a PR but returns an unparseable URL still la
545
559
  const unparseableUrlHardeningCommitResult = { hardeningPrUrl: 'draft-hardening-pr-opened', summary: 'opened' };
546
560
  const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged, unparseableUrlHardeningCommitResult);
547
561
 
548
- const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round');
549
- const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot');
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 });
550
564
 
551
565
  const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
552
566
  assert.equal(
@@ -575,8 +589,8 @@ test('a hardening-commit that opens no PR (empty hardeningPrUrl) leaves the run-
575
589
  const noPrHardeningCommitResult = { hardeningPrUrl: '', summary: 'no PR opened' };
576
590
  const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFailedHardeningStaged, noPrHardeningCommitResult);
577
591
 
578
- const firstRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'converge-round');
579
- const secondRoundHardeningPr = await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1 }], 'copilot');
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 });
580
594
 
581
595
  const hardeningCommitCalls = recordedCalls.filter((call) => call.task === 'hardening-commit').length;
582
596
  assert.equal(
@@ -603,8 +617,8 @@ test('a later standards-only round resolves its own review threads after the fol
603
617
  };
604
618
  const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFiledNoHardening);
605
619
 
606
- await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round');
607
- 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 });
608
622
 
609
623
  const standardsEditCalls = recordedCalls.filter((call) => call.task === 'standards-edit');
610
624
  assert.equal(standardsEditCalls.length, 1, 'expected the follow-up issue to be filed exactly once across the run');
@@ -633,8 +647,8 @@ test('a reuse-path standards round carrying no review threads spawns no thread-r
633
647
  };
634
648
  const runtime = loadStandardsFollowUpRuntime(recordedCalls, issueFiledNoHardening);
635
649
 
636
- await runtime.openStandardsFollowUpOnce('sha1', [{ file: 'a.py', line: 1, replyToCommentId: null }], 'converge-round');
637
- 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 });
638
652
 
639
653
  const resolveCalls = recordedCalls.filter((call) => call.task === 'standards-resolve-threads');
640
654
  assert.equal(
@@ -644,3 +658,25 @@ test('a reuse-path standards round carrying no review threads spawns no thread-r
644
658
  );
645
659
  });
646
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
+
@@ -39,7 +39,7 @@ test('the git agent handles merge-conflict checks with a Monitor-based poll', ()
39
39
  /do not edit, commit, push, or rebase|read only/i,
40
40
  'expected the git agent merge check to be read-only',
41
41
  );
42
- assert.match(gitBody, /MERGE_CONFLICT_SCHEMA/, 'expected the git agent to return MERGE_CONFLICT_SCHEMA');
42
+ assert.match(gitBody, /PREFLIGHT_GIT_SCHEMA/, 'expected the git agent to return PREFLIGHT_GIT_SCHEMA');
43
43
  assert.match(gitBody, /Monitor tool/, 'expected a Monitor-based poll delay, not a foreground sleep');
44
44
  });
45
45
 
@@ -53,31 +53,38 @@ test('runCodeEditorTask conflict-edit path rebases onto origin/main and makes no
53
53
  );
54
54
  });
55
55
 
56
- test('resolveMergeConflicts runs check -> edit -> verify -> commit and gates the push on the verdict', () => {
56
+ test('resolveMergeConflicts runs edit -> verify -> commit and gates the push on the verdict', () => {
57
57
  const body = functionBody('resolveMergeConflicts');
58
- const checkIndex = body.indexOf("runGitTask('check-merge-conflicts'");
59
58
  const editIndex = body.indexOf("runCodeEditorTask('conflict-edit'");
60
59
  const verifyIndex = body.indexOf('runVerifierTask(');
61
60
  const commitIndex = body.indexOf('commitWithRecovery(');
62
- assert.notEqual(checkIndex, -1, 'expected the conflict check to run');
63
61
  assert.notEqual(editIndex, -1, 'expected the edit step to run');
64
62
  assert.notEqual(verifyIndex, -1, 'expected the verify step to run');
65
63
  assert.notEqual(commitIndex, -1, 'expected the commit step to run');
66
64
  assert.ok(
67
- checkIndex < editIndex && editIndex < verifyIndex && verifyIndex < commitIndex,
68
- 'expected the order check -> edit -> verify -> commit',
65
+ editIndex < verifyIndex && verifyIndex < commitIndex,
66
+ 'expected the order edit -> verify -> commit',
69
67
  );
70
68
  assert.match(body, /verdictPassed\(/, 'expected the verifier verdict to gate the force-push');
69
+ assert.doesNotMatch(
70
+ body,
71
+ /runGitTask\(/,
72
+ 'expected the conflict decision to come from the merged preflight-git probe, with no mergeability agent spawned here',
73
+ );
71
74
  });
72
75
 
73
- test('resolveMergeConflicts rebases only when the check reports a conflict', () => {
76
+ test('resolveMergeConflicts rebases only when the caller-supplied preflight decision reports a conflict', () => {
74
77
  const body = functionBody('resolveMergeConflicts');
75
- assert.match(body, /isMergeConflicting\(/, 'expected the orchestrator to branch on the conflict decision');
76
78
  assert.match(
77
79
  body,
78
- /if \(!isMergeConflicting\([^)]*\)\) return head/,
80
+ /if \(!isConflicting\) return head/,
79
81
  'expected a clean PR to return the unchanged HEAD without rebasing',
80
82
  );
83
+ assert.match(
84
+ convergeSource,
85
+ /resolveMergeConflicts\(preflight\.sha, isMergeConflicting\(preflight\)\)/,
86
+ 'expected the call site to derive the conflict decision from the merged preflight-git result',
87
+ );
81
88
  });
82
89
 
83
90
  test('the merge-conflict pre-flight runs once before the round loop, ahead of the parallel bug-check lenses', () => {