claude-dev-env 1.93.1 → 1.95.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 (262) hide show
  1. package/_shared/advisor/CLAUDE.md +2 -2
  2. package/_shared/advisor/advisor-protocol.md +35 -27
  3. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +3 -2
  4. package/_shared/advisor/scripts/model_tier_run_validator.py +23 -15
  5. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +81 -17
  6. package/_shared/pr-loop/CLAUDE.md +1 -0
  7. package/_shared/pr-loop/audit-contract.md +1 -1
  8. package/_shared/pr-loop/gh-payloads.md +3 -3
  9. package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
  10. package/_shared/pr-loop/scripts/README.md +2 -2
  11. package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
  12. package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -69
  13. package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
  14. package/_shared/pr-loop/state-schema.md +3 -22
  15. package/bin/CLAUDE.md +11 -2
  16. package/bin/ever-shipped-skills.mjs +70 -0
  17. package/bin/install.mjs +138 -13
  18. package/bin/install.prune.test.mjs +457 -0
  19. package/bin/install.test.mjs +10 -4
  20. package/docs/CODE_RULES.md +1 -1
  21. package/hooks/blocking/CLAUDE.md +2 -0
  22. package/hooks/blocking/code_rules_enforcer.py +4 -0
  23. package/hooks/blocking/code_rules_shared.py +82 -0
  24. package/hooks/blocking/code_rules_test_layout.py +9 -3
  25. package/hooks/blocking/pii_commit_command.py +398 -0
  26. package/hooks/blocking/pii_payload_scan.py +133 -0
  27. package/hooks/blocking/pii_prevention_blocker.py +151 -449
  28. package/hooks/blocking/plain_language_blocker.py +138 -4
  29. package/hooks/blocking/sensitive_file_protector.py +114 -48
  30. package/hooks/blocking/tdd_enforcer.py +9 -2
  31. package/hooks/blocking/test_code_rules_enforcer_scratchpad.py +105 -0
  32. package/hooks/blocking/test_code_rules_shared.py +181 -0
  33. package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
  34. package/hooks/blocking/test_plain_language_blocker_allowlist.py +184 -0
  35. package/hooks/blocking/test_sensitive_file_protector.py +185 -0
  36. package/hooks/blocking/test_tdd_enforcer_scratchpad.py +105 -0
  37. package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
  38. package/hooks/hooks_constants/CLAUDE.md +4 -2
  39. package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
  40. package/hooks/hooks_constants/harness_scratchpad_constants.py +17 -0
  41. package/hooks/hooks_constants/local_identity.py +65 -15
  42. package/hooks/hooks_constants/pii_prevention_constants.py +16 -0
  43. package/hooks/hooks_constants/plain_language_blocker_constants.py +5 -0
  44. package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
  45. package/hooks/hooks_constants/sensitive_file_protector_constants.py +42 -0
  46. package/hooks/hooks_constants/test_local_identity.py +68 -0
  47. package/hooks/pyproject.toml +75 -4
  48. package/hooks/validators/CLAUDE.md +1 -1
  49. package/hooks/validators/README.md +2 -0
  50. package/hooks/validators/python_style_checks.py +114 -136
  51. package/hooks/validators/python_style_helpers.py +95 -0
  52. package/hooks/validators/test_python_style_checks.py +0 -164
  53. package/hooks/validators/test_python_style_checks_decorator_gap.py +119 -0
  54. package/hooks/validators/test_python_style_fixes.py +251 -0
  55. package/hooks/validators/test_python_style_helpers.py +125 -0
  56. package/package.json +1 -1
  57. package/rules/CLAUDE.md +1 -0
  58. package/rules/anti-corollary-tests.md +69 -0
  59. package/rules/bdd.md +1 -3
  60. package/rules/code-reviews.md +1 -1
  61. package/rules/gh-paginate.md +1 -1
  62. package/rules/plain-language.md +2 -0
  63. package/scripts/CLAUDE.md +10 -1
  64. package/scripts/Get-SessionAccount.ps1 +210 -0
  65. package/scripts/tests/CLAUDE.md +15 -1
  66. package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
  67. package/skills/CLAUDE.md +6 -27
  68. package/skills/anthropic-plan/SKILL.md +1 -13
  69. package/skills/autoconverge/CLAUDE.md +5 -2
  70. package/skills/autoconverge/SKILL.md +112 -387
  71. package/skills/autoconverge/reference/CLAUDE.md +4 -1
  72. package/skills/autoconverge/reference/closing-report.md +9 -2
  73. package/skills/autoconverge/reference/convergence.md +26 -22
  74. package/skills/autoconverge/reference/headless-safety.md +44 -0
  75. package/skills/autoconverge/reference/multi-pr.md +88 -0
  76. package/skills/autoconverge/reference/self-closing-loop.md +84 -0
  77. package/skills/autoconverge/reference/stop-conditions.md +21 -13
  78. package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
  79. package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
  80. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
  81. package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
  82. package/skills/autoconverge/workflow/converge.mjs +286 -96
  83. package/skills/bugteam/CLAUDE.md +1 -1
  84. package/skills/bugteam/CONSTRAINTS.md +3 -3
  85. package/skills/bugteam/PROMPTS.md +8 -19
  86. package/skills/bugteam/SKILL.md +13 -14
  87. package/skills/bugteam/reference/CLAUDE.md +1 -1
  88. package/skills/bugteam/reference/README.md +2 -3
  89. package/skills/bugteam/reference/audit-and-teammates.md +2 -2
  90. package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
  91. package/skills/bugteam/reference/github-pr-reviews.md +56 -38
  92. package/skills/bugteam/reference/team-setup.md +11 -11
  93. package/skills/bugteam/scripts/CLAUDE.md +6 -6
  94. package/skills/bugteam/scripts/README.md +20 -8
  95. package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
  96. package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
  97. package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
  98. package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
  99. package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
  100. package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
  101. package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
  102. package/skills/closeout/SKILL.md +153 -0
  103. package/skills/closeout/reference/handoff-prompt-template.md +72 -0
  104. package/skills/closeout/reference/issue-body-templates.md +108 -0
  105. package/skills/closeout/reference/pii-redaction-checklist.md +36 -0
  106. package/skills/copilot-finding-triage/SKILL.md +80 -28
  107. package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
  108. package/skills/copilot-finding-triage/templates/notification.md +8 -3
  109. package/skills/everything-search/SKILL.md +1 -2
  110. package/skills/grokify/SKILL.md +58 -0
  111. package/skills/grokify/templates/handoff-template.md +92 -0
  112. package/skills/orchestrator/SKILL.md +35 -25
  113. package/skills/orchestrator-refresh/SKILL.md +15 -11
  114. package/skills/pr-converge/CLAUDE.md +1 -1
  115. package/skills/pr-converge/SKILL.md +132 -233
  116. package/skills/pr-converge/reference/convergence-gates.md +102 -66
  117. package/skills/pr-converge/reference/examples.md +37 -25
  118. package/skills/pr-converge/reference/fix-protocol.md +7 -7
  119. package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
  120. package/skills/pr-converge/reference/per-tick.md +64 -41
  121. package/skills/pr-converge/reference/state-schema.md +27 -2
  122. package/skills/pr-converge/scripts/CLAUDE.md +8 -0
  123. package/skills/pr-converge/scripts/README.md +4 -3
  124. package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
  125. package/skills/pr-converge/scripts/check_convergence.py +189 -645
  126. package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
  127. package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
  128. package/skills/pr-converge/scripts/conftest.py +8 -0
  129. package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
  130. package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
  131. package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
  132. package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
  133. package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
  134. package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
  135. package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
  136. package/skills/pr-fix-protocol/SKILL.md +20 -20
  137. package/skills/privacy-hygiene/SKILL.md +4 -3
  138. package/skills/reviewer-gates/SKILL.md +26 -8
  139. package/skills/session-log/SKILL.md +11 -12
  140. package/skills/session-tidy/SKILL.md +40 -30
  141. package/skills/skill-builder/CLAUDE.md +13 -10
  142. package/skills/skill-builder/SKILL.md +31 -31
  143. package/skills/skill-builder/references/CLAUDE.md +10 -6
  144. package/skills/skill-builder/references/delegation-map.md +13 -4
  145. package/skills/skill-builder/references/description-field.md +113 -0
  146. package/skills/skill-builder/references/self-audit-checklist.md +21 -4
  147. package/skills/skill-builder/references/skill-modularity.md +113 -0
  148. package/skills/skill-builder/references/skill-types.md +4 -0
  149. package/skills/skill-builder/templates/CLAUDE.md +4 -4
  150. package/skills/skill-builder/templates/gap-analysis.md +25 -1
  151. package/skills/skill-builder/workflows/CLAUDE.md +6 -4
  152. package/skills/skill-builder/workflows/improve-skill.md +17 -16
  153. package/skills/skill-builder/workflows/new-skill.md +34 -10
  154. package/skills/skill-builder/workflows/polish-skill.md +59 -43
  155. package/skills/team-advisor/SKILL.md +15 -11
  156. package/skills/usage-pause/SKILL.md +10 -14
  157. package/system-prompts/software-engineer.xml +7 -6
  158. package/hooks/validators/test_verify_paths.py +0 -32
  159. package/hooks/validators/verify_paths.py +0 -57
  160. package/skills/bdd-protocol/CLAUDE.md +0 -26
  161. package/skills/bdd-protocol/SKILL.md +0 -30
  162. package/skills/bdd-protocol/references/CLAUDE.md +0 -10
  163. package/skills/bdd-protocol/references/anti-patterns.md +0 -26
  164. package/skills/bdd-protocol/references/example-mapping.md +0 -23
  165. package/skills/bg-agent/CLAUDE.md +0 -17
  166. package/skills/bg-agent/SKILL.md +0 -69
  167. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
  168. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
  169. package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
  170. package/skills/caveman/CLAUDE.md +0 -15
  171. package/skills/caveman/SKILL.md +0 -38
  172. package/skills/code/CLAUDE.md +0 -17
  173. package/skills/code/SKILL.md +0 -178
  174. package/skills/deep-research/CLAUDE.md +0 -17
  175. package/skills/deep-research/SKILL.md +0 -80
  176. package/skills/findbugs/CLAUDE.md +0 -20
  177. package/skills/findbugs/SKILL.md +0 -241
  178. package/skills/fixbugs/CLAUDE.md +0 -19
  179. package/skills/fixbugs/SKILL.md +0 -142
  180. package/skills/gh-paginate/CLAUDE.md +0 -18
  181. package/skills/gh-paginate/SKILL.md +0 -84
  182. package/skills/gotcha/CLAUDE.md +0 -33
  183. package/skills/gotcha/SKILL.md +0 -73
  184. package/skills/implement/CLAUDE.md +0 -27
  185. package/skills/implement/SKILL.md +0 -66
  186. package/skills/implement/scripts/CLAUDE.md +0 -22
  187. package/skills/implement/scripts/append_note.py +0 -133
  188. package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
  189. package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
  190. package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
  191. package/skills/implement/scripts/test_append_note.py +0 -191
  192. package/skills/log-audit/CLAUDE.md +0 -20
  193. package/skills/log-audit/SKILL.md +0 -66
  194. package/skills/log-audit/reference/CLAUDE.md +0 -9
  195. package/skills/log-audit/reference/charter.md +0 -52
  196. package/skills/log-audit/scripts/CLAUDE.md +0 -27
  197. package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
  198. package/skills/log-audit/scripts/collect_log_window.py +0 -199
  199. package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
  200. package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
  201. package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
  202. package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
  203. package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
  204. package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
  205. package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
  206. package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
  207. package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
  208. package/skills/logifix/CLAUDE.md +0 -36
  209. package/skills/logifix/SKILL.md +0 -69
  210. package/skills/logifix/scripts/CLAUDE.md +0 -16
  211. package/skills/logifix/scripts/logifix.ps1 +0 -205
  212. package/skills/monitor-open-prs/CLAUDE.md +0 -34
  213. package/skills/monitor-open-prs/SKILL.md +0 -88
  214. package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
  215. package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
  216. package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
  217. package/skills/monitor-open-prs/test_skill_contract.py +0 -27
  218. package/skills/post-audit-findings/SKILL.md +0 -80
  219. package/skills/pr-consistency-audit/CLAUDE.md +0 -34
  220. package/skills/pr-consistency-audit/SKILL.md +0 -116
  221. package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
  222. package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
  223. package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
  224. package/skills/pr-review-responder/CLAUDE.md +0 -35
  225. package/skills/pr-review-responder/EXAMPLES.md +0 -590
  226. package/skills/pr-review-responder/PRINCIPLES.md +0 -533
  227. package/skills/pr-review-responder/README.md +0 -168
  228. package/skills/pr-review-responder/SKILL.md +0 -203
  229. package/skills/pr-review-responder/TESTING.md +0 -350
  230. package/skills/pr-scope-resolve/SKILL.md +0 -45
  231. package/skills/pre-compact/CLAUDE.md +0 -24
  232. package/skills/pre-compact/SKILL.md +0 -134
  233. package/skills/qbug/CLAUDE.md +0 -40
  234. package/skills/qbug/SKILL.md +0 -387
  235. package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
  236. package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
  237. package/skills/refine/CLAUDE.md +0 -44
  238. package/skills/refine/SKILL.md +0 -262
  239. package/skills/refine/templates/CLAUDE.md +0 -17
  240. package/skills/refine/templates/implementation-notes-template.html +0 -56
  241. package/skills/refine/templates/plan-template.md +0 -60
  242. package/skills/research-mode/CLAUDE.md +0 -35
  243. package/skills/research-mode/SKILL.md +0 -53
  244. package/skills/structure-prompt/CLAUDE.md +0 -42
  245. package/skills/structure-prompt/SKILL.md +0 -44
  246. package/skills/structure-prompt/reference/CLAUDE.md +0 -28
  247. package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
  248. package/skills/structure-prompt/reference/block-classification.md +0 -27
  249. package/skills/structure-prompt/reference/canonical-case.md +0 -48
  250. package/skills/structure-prompt/reference/citation-depth.md +0 -70
  251. package/skills/structure-prompt/reference/cleanup.md +0 -33
  252. package/skills/structure-prompt/reference/constraints.md +0 -33
  253. package/skills/structure-prompt/reference/directives.md +0 -37
  254. package/skills/structure-prompt/reference/examples.md +0 -72
  255. package/skills/structure-prompt/reference/instantiation.md +0 -51
  256. package/skills/structure-prompt/reference/output-contract.md +0 -72
  257. package/skills/structure-prompt/reference/per-category.md +0 -23
  258. package/skills/structure-prompt/reference/persona.md +0 -38
  259. package/skills/structure-prompt/reference/research.md +0 -33
  260. package/skills/structure-prompt/reference/structure.md +0 -28
  261. package/skills/verified-build/CLAUDE.md +0 -33
  262. package/skills/verified-build/SKILL.md +0 -33
@@ -17,17 +17,19 @@ function functionBody(functionName) {
17
17
 
18
18
  const productionModule = new Function(
19
19
  `${functionBody('classifyReviewerGateOutcome')}\n` +
20
- `${functionBody('classifyCopilotOutcome')}\n` +
21
- `${functionBody('hasCodeConcernFinding')}\n` +
20
+ `${functionBody('partitionFindingsByTier')}\n` +
21
+ `${functionBody('normalizeVerifierVerdict')}\n` +
22
+ `${functionBody('attachVerifiedRepro')}\n` +
22
23
  `${functionBody('buildUserReview')}\n` +
23
24
  `${functionBody('resolveCopilotDown')}\n` +
24
25
  `${functionBody('resolveReviewerDown')}\n` +
25
- 'return { classifyReviewerGateOutcome, classifyCopilotOutcome, hasCodeConcernFinding, buildUserReview, resolveCopilotDown, resolveReviewerDown };',
26
+ 'return { classifyReviewerGateOutcome, partitionFindingsByTier, normalizeVerifierVerdict, attachVerifiedRepro, buildUserReview, resolveCopilotDown, resolveReviewerDown };',
26
27
  )();
27
28
  const {
28
29
  classifyReviewerGateOutcome,
29
- classifyCopilotOutcome,
30
- hasCodeConcernFinding,
30
+ partitionFindingsByTier,
31
+ normalizeVerifierVerdict,
32
+ attachVerifiedRepro,
31
33
  buildUserReview,
32
34
  resolveCopilotDown,
33
35
  resolveReviewerDown,
@@ -95,59 +97,112 @@ test('Copilot findings route to a fix when Copilot is reachable and not down', (
95
97
  assert.equal(outcome.kind, 'fix');
96
98
  });
97
99
 
98
- test('self-healing-only Copilot findings route to a fix when Copilot is reachable and not down', () => {
99
- const outcome = classifyCopilotOutcome(
100
- copilotResult({
101
- findings: [copilotFinding({ tier: 'self-healing' })],
102
- }),
103
- );
104
- assert.equal(outcome.kind, 'fix');
100
+ function verifierResult(overrides) {
101
+ return {
102
+ verdict: 'confirmed',
103
+ checkCommand: 'pytest -k repro',
104
+ checkOutput: 'AssertionError: boom',
105
+ evidence: 'ran the repro and it failed',
106
+ ...overrides,
107
+ };
108
+ }
109
+
110
+ test('partitionFindingsByTier splits self-healing findings from code-concern findings', () => {
111
+ const { selfHealing, codeConcern } = partitionFindingsByTier([
112
+ copilotFinding({ tier: 'self-healing' }),
113
+ copilotFinding({ tier: 'code-concern', file: 'b.py', line: 2 }),
114
+ ]);
115
+ assert.equal(selfHealing.length, 1);
116
+ assert.equal(codeConcern.length, 1);
117
+ assert.equal(codeConcern[0].file, 'b.py');
105
118
  });
106
119
 
107
- test('a code-concern Copilot finding routes to user-review rather than an auto-fix', () => {
108
- const outcome = classifyCopilotOutcome(
109
- copilotResult({
110
- reviewUrl: 'https://github.com/o/r/pull/1#pullrequestreview-9',
111
- findings: [copilotFinding({ tier: 'code-concern', severity: 'P0' })],
112
- }),
113
- );
114
- assert.equal(outcome.kind, 'user-review');
115
- assert.equal(outcome.findings.length, 1);
120
+ test('partitionFindingsByTier routes a finding with a missing tier into code-concern so an unlabeled finding is never auto-fixed', () => {
121
+ const { selfHealing, codeConcern } = partitionFindingsByTier([{ file: 'a.py', line: 1 }]);
122
+ assert.equal(selfHealing.length, 0);
123
+ assert.equal(codeConcern.length, 1);
116
124
  });
117
125
 
118
- test('a mixed round with one code-concern finding routes the whole round to user-review', () => {
119
- const outcome = classifyCopilotOutcome(
120
- copilotResult({
121
- findings: [
122
- copilotFinding({ tier: 'self-healing' }),
123
- copilotFinding({ tier: 'code-concern', file: 'b.py', line: 2 }),
124
- ],
125
- }),
126
- );
127
- assert.equal(outcome.kind, 'user-review');
126
+ test('normalizeVerifierVerdict downgrades a confirmed verdict backed by an empty checkCommand to inconclusive', () => {
127
+ const normalized = normalizeVerifierVerdict(verifierResult({ checkCommand: '' }));
128
+ assert.equal(normalized.verdict, 'inconclusive');
129
+ assert.match(normalized.evidence, /downgraded to inconclusive/);
130
+ });
131
+
132
+ test('normalizeVerifierVerdict downgrades a refuted verdict backed by an empty checkOutput to inconclusive', () => {
133
+ const normalized = normalizeVerifierVerdict(verifierResult({ verdict: 'refuted', checkOutput: '' }));
134
+ assert.equal(normalized.verdict, 'inconclusive');
135
+ assert.match(normalized.evidence, /downgraded to inconclusive/);
136
+ });
137
+
138
+ test('normalizeVerifierVerdict treats whitespace-only check fields as no executed check and downgrades', () => {
139
+ const normalized = normalizeVerifierVerdict(verifierResult({ checkCommand: ' ', checkOutput: '\t' }));
140
+ assert.equal(normalized.verdict, 'inconclusive');
141
+ });
142
+
143
+ test('normalizeVerifierVerdict returns an inconclusive default when the verifier agent died', () => {
144
+ const normalized = normalizeVerifierVerdict(null);
145
+ assert.equal(normalized.verdict, 'inconclusive');
146
+ assert.equal(normalized.checkCommand, '');
147
+ assert.equal(normalized.checkOutput, '');
148
+ assert.match(normalized.evidence, /verifier agent died/);
149
+ });
150
+
151
+ test('normalizeVerifierVerdict passes a backed conclusive verdict through unchanged', () => {
152
+ const backed = verifierResult({ verdict: 'confirmed' });
153
+ assert.deepEqual(normalizeVerifierVerdict(backed), backed);
128
154
  });
129
155
 
130
- test('hasCodeConcernFinding treats a missing or unexpected tier as a code concern', () => {
131
- assert.equal(hasCodeConcernFinding([copilotFinding({ tier: 'self-healing' })]), false);
132
- assert.equal(hasCodeConcernFinding([copilotFinding({ tier: 'code-concern' })]), true);
133
- assert.equal(hasCodeConcernFinding([{ file: 'a.py', line: 1 }]), true);
156
+ test('normalizeVerifierVerdict passes an inconclusive verdict through unchanged even with empty check fields', () => {
157
+ const inconclusive = verifierResult({ verdict: 'inconclusive', checkCommand: '', checkOutput: '' });
158
+ assert.deepEqual(normalizeVerifierVerdict(inconclusive), inconclusive);
134
159
  });
135
160
 
136
- test('buildUserReview carries the review URL and pares findings to the triage fields', () => {
161
+ test('attachVerifiedRepro folds the repro command and captured output into the finding detail, requires the re-run, and drops the verification field', () => {
162
+ const finding = attachVerifiedRepro(
163
+ copilotFinding({
164
+ tier: 'code-concern',
165
+ detail: 'the original finding detail',
166
+ verification: {
167
+ verdict: 'confirmed',
168
+ checkCommand: 'pytest -k repro',
169
+ checkOutput: 'AssertionError: boom',
170
+ evidence: 'ran the repro and it failed',
171
+ },
172
+ }),
173
+ );
174
+ assert.equal(finding.verification, undefined, 'expected the verification field to be dropped from the fix-flow finding');
175
+ assert.match(finding.detail, /the original finding detail/, 'expected the original detail to be preserved');
176
+ assert.match(finding.detail, /pytest -k repro/, 'expected the repro command to be folded into the detail');
177
+ assert.match(finding.detail, /AssertionError: boom/, 'expected the captured failing output to be folded into the detail');
178
+ assert.match(finding.detail, /re-run the exact repro/, 'expected the detail to require re-running the exact repro after the fix');
179
+ assert.match(finding.detail, /regression test/i, 'expected the detail to ask for the repro as a regression test');
180
+ });
181
+
182
+ test('buildUserReview carries the review URL and pares findings to the triage fields plus the verifier evidence note', () => {
137
183
  const reviewUrl = 'https://github.com/o/r/pull/1#pullrequestreview-9';
138
184
  const userReview = buildUserReview(
139
185
  copilotResult({ reviewUrl }),
140
- [copilotFinding({ tier: 'code-concern', severity: 'P0', title: 'unsafe eval', detail: 'ignored' })],
186
+ [
187
+ copilotFinding({
188
+ tier: 'code-concern',
189
+ severity: 'P0',
190
+ title: 'unsafe eval',
191
+ detail: 'ignored',
192
+ verification: { verdict: 'inconclusive', checkCommand: '', checkOutput: '', evidence: 'no runnable check exists for the claim' },
193
+ }),
194
+ ],
141
195
  );
142
196
  assert.equal(userReview.reviewUrl, reviewUrl);
143
197
  assert.deepEqual(userReview.findings, [
144
- { file: 'a.py', line: 1, severity: 'P0', tier: 'code-concern', title: 'unsafe eval' },
198
+ { file: 'a.py', line: 1, severity: 'P0', tier: 'code-concern', title: 'unsafe eval', evidence: 'no runnable check exists for the claim' },
145
199
  ]);
146
200
  });
147
201
 
148
- test('buildUserReview defaults a missing review URL to an empty string', () => {
202
+ test('buildUserReview defaults a missing review URL and a missing verifier evidence note to empty strings', () => {
149
203
  const userReview = buildUserReview(copilotResult({}), [copilotFinding({ tier: 'code-concern' })]);
150
204
  assert.equal(userReview.reviewUrl, '');
205
+ assert.equal(userReview.findings[0].evidence, '');
151
206
  });
152
207
 
153
208
  test('COPILOT_SCHEMA carries a required down field', () => {
@@ -188,6 +243,29 @@ test('COPILOT_SCHEMA carries a reviewUrl the user-review payload links to', () =
188
243
  assert.match(schemaSource, /reviewUrl:\s*\{\s*type:\s*'string'/, 'expected a reviewUrl string field on COPILOT_SCHEMA');
189
244
  });
190
245
 
246
+ test('COPILOT_VERIFY_VERDICTS names the confirmed, refuted, and inconclusive verdicts', () => {
247
+ const verdictsMatch = convergeSource.match(/const COPILOT_VERIFY_VERDICTS = \[([^\]]*)\]/);
248
+ assert.notEqual(verdictsMatch, null, 'expected a COPILOT_VERIFY_VERDICTS constant');
249
+ assert.match(verdictsMatch[1], /'confirmed'/, 'expected the verdict enum to name confirmed');
250
+ assert.match(verdictsMatch[1], /'refuted'/, 'expected the verdict enum to name refuted');
251
+ assert.match(verdictsMatch[1], /'inconclusive'/, 'expected the verdict enum to name inconclusive');
252
+ });
253
+
254
+ test('COPILOT_VERIFY_SCHEMA requires the verdict, checkCommand, checkOutput, and evidence fields with the verdict enum drawn from the verdict constant', () => {
255
+ const schemaStart = convergeSource.indexOf('const COPILOT_VERIFY_SCHEMA =');
256
+ const schemaEnd = convergeSource.indexOf('const REVIEWER_AVAILABILITY_SCHEMA =');
257
+ assert.notEqual(schemaStart, -1, 'expected COPILOT_VERIFY_SCHEMA to exist');
258
+ const schemaSource = convergeSource.slice(schemaStart, schemaEnd);
259
+ assert.match(schemaSource, /enum:\s*COPILOT_VERIFY_VERDICTS/, 'expected the verdict enum to draw from the COPILOT_VERIFY_VERDICTS constant');
260
+ for (const requiredField of ['verdict', 'checkCommand', 'checkOutput', 'evidence']) {
261
+ assert.match(
262
+ schemaSource,
263
+ new RegExp(`required:\\s*\\[[^\\]]*'${requiredField}'[^\\]]*\\]`),
264
+ `expected ${requiredField} to be a required field on the verifier schema`,
265
+ );
266
+ }
267
+ });
268
+
191
269
  test('the Copilot gate prompt tiers each finding and returns the review URL', () => {
192
270
  const copilotPrompt = functionBody('runCopilotGate');
193
271
  assert.match(copilotPrompt, /tier 'self-healing'/, 'expected the prompt to define the self-healing tier');
@@ -196,18 +274,58 @@ test('the Copilot gate prompt tiers each finding and returns the review URL', ()
196
274
  assert.match(copilotPrompt, /reviewUrl set to the Copilot review html_url/, 'expected the findings branch to return the review URL');
197
275
  });
198
276
 
199
- test('the COPILOT phase returns blocker user-review with the findings carried through on a code-concern outcome', () => {
277
+ test('the COPILOT phase returns blocker user-review only when inconclusive findings remain after verification, carrying the verifier-noted payload with no auto-fix in the branch', () => {
200
278
  const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
201
279
  const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", copilotPhaseStart);
202
280
  const copilotPhase = convergeSource.slice(copilotPhaseStart, finalizePhaseStart);
203
- const userReviewBranchStart = copilotPhase.indexOf("copilotOutcome.kind === 'user-review'");
204
- assert.notEqual(userReviewBranchStart, -1, 'expected the COPILOT phase to handle a user-review outcome');
205
- const userReviewBranch = copilotPhase.slice(userReviewBranchStart, userReviewBranchStart + 500);
206
- assert.match(userReviewBranch, /blocker:\s*'user-review'/, 'expected the branch to return blocker user-review');
281
+ const userReviewGuardStart = copilotPhase.indexOf('inconclusive.length > 0');
282
+ assert.notEqual(userReviewGuardStart, -1, 'expected the user-review return to be guarded by inconclusive.length > 0');
283
+ const userReviewBranch = copilotPhase.slice(userReviewGuardStart, userReviewGuardStart + 600);
284
+ assert.match(userReviewBranch, /blocker:\s*'user-review'/, 'expected the guarded branch to return blocker user-review');
207
285
  assert.match(userReviewBranch, /converged:\s*false/, 'expected the branch to return converged false');
208
- assert.match(userReviewBranch, /userReview:\s*buildUserReview\(copilot, copilotOutcome\.findings\)/, 'expected the branch to carry the user-review payload');
286
+ assert.match(
287
+ userReviewBranch,
288
+ /userReview:\s*buildUserReview\(copilot, inconclusive\)/,
289
+ 'expected the branch to carry the inconclusive-findings user-review payload',
290
+ );
209
291
  const autoFixIndex = userReviewBranch.indexOf('applyFixes');
210
- assert.equal(autoFixIndex, -1, 'expected the user-review branch to not auto-fix the code-concern findings');
292
+ assert.equal(autoFixIndex, -1, 'expected the user-review branch to not auto-fix the inconclusive findings');
293
+ const userReviewReturns = (copilotPhase.match(/blocker:\s*'user-review'/g) || []).length;
294
+ assert.equal(userReviewReturns, 1, 'expected exactly one user-review return in the COPILOT phase');
295
+ });
296
+
297
+ test('the COPILOT phase awaits verifyCodeConcernFindings exactly once, inside the code-concern guard', () => {
298
+ const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
299
+ const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", copilotPhaseStart);
300
+ const copilotPhase = convergeSource.slice(copilotPhaseStart, finalizePhaseStart);
301
+ const guardStart = copilotPhase.indexOf('if (codeConcern.length > 0)');
302
+ assert.notEqual(guardStart, -1, 'expected the verification to sit behind a codeConcern.length > 0 guard');
303
+ const verifyCalls = convergeSource.match(/await verifyCodeConcernFindings\(/g) || [];
304
+ assert.equal(verifyCalls.length, 1, 'expected verifyCodeConcernFindings to be awaited exactly once outside its own definition');
305
+ const verifyCallIndex = copilotPhase.indexOf('await verifyCodeConcernFindings(head, codeConcern)');
306
+ assert.ok(verifyCallIndex > guardStart, 'expected the verifyCodeConcernFindings call to sit inside the code-concern guard');
307
+ });
308
+
309
+ test('the all-refuted / empty-roundFindings COPILOT path advances to FINALIZE with the gate passed', () => {
310
+ const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
311
+ const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", copilotPhaseStart);
312
+ const copilotPhase = convergeSource.slice(copilotPhaseStart, finalizePhaseStart);
313
+ const emptyRoundStart = copilotPhase.indexOf('roundFindings.length === 0');
314
+ assert.notEqual(emptyRoundStart, -1, 'expected the COPILOT phase to handle an empty roundFindings set after verification');
315
+ const emptyRoundBranch = copilotPhase.slice(emptyRoundStart, emptyRoundStart + 300);
316
+ assert.match(emptyRoundBranch, /copilotDown = false/, 'expected the empty-roundFindings path to clear copilotDown');
317
+ assert.match(emptyRoundBranch, /phase = 'FINALIZE'/, 'expected the empty-roundFindings path to advance to FINALIZE');
318
+ });
319
+
320
+ test('the COPILOT phase folds confirmed findings through attachVerifiedRepro into the round fix list', () => {
321
+ const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
322
+ const finalizePhaseStart = convergeSource.indexOf("if (phase === 'FINALIZE') {", copilotPhaseStart);
323
+ const copilotPhase = convergeSource.slice(copilotPhaseStart, finalizePhaseStart);
324
+ assert.match(
325
+ copilotPhase,
326
+ /roundFindings = \[\.\.\.selfHealing, \.\.\.confirmed\.map\(\(each\) => attachVerifiedRepro\(each\)\)\]/,
327
+ 'expected the fix list to be the self-healing findings plus the confirmed findings mapped through attachVerifiedRepro',
328
+ );
211
329
  });
212
330
 
213
331
  test('the Copilot gate prompt detects an out-of-usage notice and returns a down result', () => {
@@ -332,7 +450,7 @@ test('resolveCopilotDown clears the bypass for a retry outcome', () => {
332
450
 
333
451
  test('the standards-only Copilot sub-path resets copilotDown before FINALIZE', () => {
334
452
  const standardsBranchStart = convergeSource.indexOf(
335
- 'isStandardsOnlyRound(copilotOutcome.findings)',
453
+ 'isStandardsOnlyRound(roundFindings)',
336
454
  );
337
455
  assert.notEqual(
338
456
  standardsBranchStart,
@@ -407,6 +525,20 @@ test('the merged FINALIZE check opts the unflagged convergence hook out of Copil
407
525
  );
408
526
  });
409
527
 
528
+ test('the merged FINALIZE check opts the unflagged convergence hook out of Bugbot when bugbotDown before gh pr ready', () => {
529
+ const checkBody = functionBody('runConvergenceCheck');
530
+ assert.match(
531
+ checkBody,
532
+ /context\.bugbotDown/,
533
+ 'expected the merged check to branch on bugbotDown so a bugbot-down run reaches the opt-out token push',
534
+ );
535
+ assert.match(
536
+ checkBody,
537
+ /reviewerOptOutTokens\.push\('bugbot'\)/,
538
+ 'expected the merged check opt-out to push the bugbot token so the unflagged mark-ready hook re-derives the Bugbot bypass',
539
+ );
540
+ });
541
+
410
542
  test('the COPILOT phase short-circuits on the unified reviewer-down gate before spawning the gate agent', () => {
411
543
  const copilotPhaseStart = convergeSource.indexOf("if (phase === 'COPILOT') {");
412
544
  assert.notEqual(copilotPhaseStart, -1, 'expected a COPILOT phase block');
@@ -99,7 +99,7 @@ test('the converge call site sets a fix-stalled blocker when an all-null-thread
99
99
 
100
100
  test('the copilot call site sets a fix-stalled blocker when an all-null-thread resolvedWithoutCommit round cannot progress', () => {
101
101
  const copilotBranch = convergeSource.slice(
102
- convergeSource.indexOf("const fixResult = await applyFixes(head, copilotOutcome.findings, 'copilot')"),
102
+ convergeSource.indexOf("const fixResult = await applyFixes(head, roundFindings, 'copilot')"),
103
103
  convergeSource.indexOf("phase = 'CONVERGE'\n continue\n }\n phase = 'FINALIZE'"),
104
104
  );
105
105
  assert.match(copilotBranch, /collectFindingThreadIds/);