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
@@ -0,0 +1,266 @@
1
+ """Tests for check_js_bare_flag_return_directive.
2
+
3
+ The check catches a JS/.mjs self-contradiction. A converge workflow states a
4
+ full result-object contract that rules out a bare status flag ("the full down
5
+ result {sha, clean:false, down:true, findings:[]}, never a bare down flag"),
6
+ then, in its agent-prompt prose, tells the agent to "return down: true" — the
7
+ same bare flag the workflow just ruled out. A StructuredOutput run whose
8
+ schema needs every field would reject a lone {down:true}, so the shorthand
9
+ drifts from the contract stated a couple of lines above.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ _HOOK_DIRECTORY = Path(__file__).parent
18
+ if str(_HOOK_DIRECTORY) not in sys.path:
19
+ sys.path.insert(0, str(_HOOK_DIRECTORY))
20
+
21
+ from code_rules_imports_logging import ( # noqa: E402
22
+ check_js_bare_flag_return_directive,
23
+ )
24
+
25
+ _MJS_PATH = "skills/autoconverge/workflow/converge.mjs"
26
+
27
+ _DOWN_CONTRACT = (
28
+ "the full down result {sha, clean:false, down:true, findings:[]}, "
29
+ "never a bare down flag"
30
+ )
31
+
32
+
33
+ def _contract_with_bare_directive_source() -> str:
34
+ return (
35
+ "const PREAMBLE =\n"
36
+ f" 'When the run carries a result schema, {_DOWN_CONTRACT}.'\n\n"
37
+ "function gate(head) {\n"
38
+ " return spawn(\n"
39
+ " `Poll for the review; once the budget runs out with no review on "
40
+ "HEAD, return down: true.`,\n"
41
+ " { label: 'gate' },\n"
42
+ " )\n"
43
+ "}\n"
44
+ )
45
+
46
+
47
+ def _contract_with_full_result_source() -> str:
48
+ return _contract_with_bare_directive_source().replace(
49
+ "return down: true",
50
+ "return {sha:`${head}`, clean:false, down:true, findings:[]}",
51
+ )
52
+
53
+
54
+ def _bare_directive_without_contract_source() -> str:
55
+ return (
56
+ "function gate(head) {\n"
57
+ " return spawn(\n"
58
+ " `Poll for a review; once the budget runs out, return down: true.`,\n"
59
+ " { label: 'gate' },\n"
60
+ " )\n"
61
+ "}\n"
62
+ )
63
+
64
+
65
+ def _return_the_result_phrase_source() -> str:
66
+ return (
67
+ "const PREAMBLE = 'the full down result {...}, never a bare down flag'\n\n"
68
+ "function gate(head) {\n"
69
+ " return spawn(\n"
70
+ " `Out-of-usage notice on HEAD -> return the down result above and stop.`,\n"
71
+ " { label: 'gate' },\n"
72
+ " )\n"
73
+ "}\n"
74
+ )
75
+
76
+
77
+ def _contract_with_bare_false_directive_source() -> str:
78
+ return (
79
+ "const PREAMBLE =\n"
80
+ f" 'When the run carries a result schema, {_DOWN_CONTRACT}.'\n\n"
81
+ "function gate(head) {\n"
82
+ " return spawn(\n"
83
+ " `Poll for the review; once HEAD stays clean with no findings, "
84
+ "return down: false.`,\n"
85
+ " { label: 'gate' },\n"
86
+ " )\n"
87
+ "}\n"
88
+ )
89
+
90
+
91
+ def _other_flag_name_source() -> str:
92
+ return (
93
+ "const PREAMBLE = 'the full result {clean, stale}, never a bare stale flag'\n\n"
94
+ "function gate(head) {\n"
95
+ " return spawn(\n"
96
+ " `If nothing lands in the window, return stale: true.`,\n"
97
+ " { label: 'gate' },\n"
98
+ " )\n"
99
+ "}\n"
100
+ )
101
+
102
+
103
+ def _capitalized_contract_with_bare_directive_source() -> str:
104
+ return (
105
+ "const PREAMBLE =\n"
106
+ " 'When the run carries a result schema, the full down result "
107
+ "{sha, clean:false, down:true, findings:[]}, Never a bare Down flag.'\n\n"
108
+ "function gate(head) {\n"
109
+ " return spawn(\n"
110
+ " `Poll for the review; once the budget runs out with no review on "
111
+ "HEAD, return down: true.`,\n"
112
+ " { label: 'gate' },\n"
113
+ " )\n"
114
+ "}\n"
115
+ )
116
+
117
+
118
+ def _contract_with_capitalized_directive_source() -> str:
119
+ return (
120
+ "const PREAMBLE =\n"
121
+ f" 'When the run carries a result schema, {_DOWN_CONTRACT}.'\n\n"
122
+ "function gate(head) {\n"
123
+ " return spawn(\n"
124
+ " `Poll for the review; once the budget runs out with no review on "
125
+ "HEAD, Return down: true.`,\n"
126
+ " { label: 'gate' },\n"
127
+ " )\n"
128
+ "}\n"
129
+ )
130
+
131
+
132
+ def test_flags_bare_down_directive_when_the_workflow_states_the_contract() -> None:
133
+ issues = check_js_bare_flag_return_directive(
134
+ _contract_with_bare_directive_source(), _MJS_PATH
135
+ )
136
+ assert len(issues) == 1
137
+ assert "return down" in issues[0]
138
+ assert "never a bare down flag" in issues[0]
139
+
140
+
141
+ def test_flags_bare_down_false_directive_when_the_workflow_states_the_contract() -> None:
142
+ issues = check_js_bare_flag_return_directive(
143
+ _contract_with_bare_false_directive_source(), _MJS_PATH
144
+ )
145
+ assert len(issues) == 1
146
+ assert "return down" in issues[0]
147
+ assert "never a bare down flag" in issues[0]
148
+
149
+
150
+ def test_generalizes_to_any_flag_name_the_contract_rules_out() -> None:
151
+ issues = check_js_bare_flag_return_directive(_other_flag_name_source(), _MJS_PATH)
152
+ assert len(issues) == 1
153
+ assert "return stale" in issues[0]
154
+
155
+
156
+ def test_accepts_a_full_result_object_return() -> None:
157
+ issues = check_js_bare_flag_return_directive(
158
+ _contract_with_full_result_source(), _MJS_PATH
159
+ )
160
+ assert issues == []
161
+
162
+
163
+ def test_accepts_a_bare_directive_when_the_workflow_states_no_contract() -> None:
164
+ issues = check_js_bare_flag_return_directive(
165
+ _bare_directive_without_contract_source(), _MJS_PATH
166
+ )
167
+ assert issues == []
168
+
169
+
170
+ def test_accepts_return_the_result_prose_that_names_no_bare_flag() -> None:
171
+ issues = check_js_bare_flag_return_directive(
172
+ _return_the_result_phrase_source(), _MJS_PATH
173
+ )
174
+ assert issues == []
175
+
176
+
177
+ def test_flags_bare_directive_when_contract_uses_capitalized_never() -> None:
178
+ source = _capitalized_contract_with_bare_directive_source()
179
+ issues = check_js_bare_flag_return_directive(source, _MJS_PATH)
180
+ assert len(issues) == 1
181
+ assert "return down" in issues[0]
182
+ quoted_contract_phrase = "Never a bare Down flag"
183
+ assert quoted_contract_phrase in issues[0]
184
+ assert quoted_contract_phrase in source
185
+
186
+
187
+ def test_flags_capitalized_return_directive_against_lowercase_contract() -> None:
188
+ issues = check_js_bare_flag_return_directive(
189
+ _contract_with_capitalized_directive_source(), _MJS_PATH
190
+ )
191
+ assert len(issues) == 1
192
+ assert "never a bare down flag" in issues[0]
193
+
194
+
195
+ def test_skips_python_files() -> None:
196
+ issues = check_js_bare_flag_return_directive(
197
+ _contract_with_bare_directive_source(), "workflow/converge.py"
198
+ )
199
+ assert issues == []
200
+
201
+
202
+ def _contract_with_six_bare_directives_source() -> str:
203
+ directive_lines = "\n".join(
204
+ f" gate{each_index}(head) {{ return spawn(`return down: true.`) }}"
205
+ for each_index in range(1, 7)
206
+ )
207
+ return (
208
+ "const PREAMBLE =\n"
209
+ f" 'When the run carries a result schema, {_DOWN_CONTRACT}.'\n\n"
210
+ f"{directive_lines}\n"
211
+ )
212
+
213
+
214
+ def test_flags_directive_when_only_directive_line_changed_but_contract_sits_elsewhere() -> None:
215
+ directive_line_number = 6
216
+ issues = check_js_bare_flag_return_directive(
217
+ _contract_with_bare_directive_source(),
218
+ _MJS_PATH,
219
+ all_changed_lines={directive_line_number},
220
+ )
221
+ assert len(issues) == 1
222
+ assert "return down" in issues[0]
223
+
224
+
225
+ def test_flags_when_only_the_contract_line_changed_and_the_directive_line_did_not() -> None:
226
+ contract_line_number = 2
227
+ issues = check_js_bare_flag_return_directive(
228
+ _contract_with_bare_directive_source(),
229
+ _MJS_PATH,
230
+ all_changed_lines={contract_line_number},
231
+ )
232
+ assert len(issues) == 1
233
+ assert "return down" in issues[0]
234
+
235
+
236
+ def test_accepts_when_neither_the_contract_nor_the_directive_line_changed() -> None:
237
+ unrelated_line_number = 3
238
+ issues = check_js_bare_flag_return_directive(
239
+ _contract_with_bare_directive_source(),
240
+ _MJS_PATH,
241
+ all_changed_lines={unrelated_line_number},
242
+ )
243
+ assert issues == []
244
+
245
+
246
+ def test_flags_a_changed_line_violation_past_the_cap_of_unchanged_ones() -> None:
247
+ sixth_directive_line_number = 9
248
+ issues = check_js_bare_flag_return_directive(
249
+ _contract_with_six_bare_directives_source(),
250
+ _MJS_PATH,
251
+ all_changed_lines={sixth_directive_line_number},
252
+ )
253
+ assert len(issues) == 1
254
+ assert f"Line {sixth_directive_line_number}:" in issues[0]
255
+
256
+
257
+ def test_defer_scope_to_caller_returns_the_finding_regardless_of_changed_lines() -> None:
258
+ unrelated_line_number = 1
259
+ issues = check_js_bare_flag_return_directive(
260
+ _contract_with_bare_directive_source(),
261
+ _MJS_PATH,
262
+ all_changed_lines={unrelated_line_number},
263
+ defer_scope_to_caller=True,
264
+ )
265
+ assert len(issues) == 1
266
+ assert "return down" in issues[0]