claude-dev-env 2.11.0 → 2.13.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 (203) hide show
  1. package/_shared/advisor/reference/sol-rung.md +5 -2
  2. package/_shared/advisor/reference/warm-up.md +1 -0
  3. package/_shared/advisor/scripts/codex_sol_advisor.py +96 -31
  4. package/_shared/advisor/scripts/config/advisor_scripts_constants/sol_advisor_constants.py +5 -0
  5. package/_shared/advisor/scripts/tests/test_codex_sol_advisor.py +173 -11
  6. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  7. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  8. package/agents/CLAUDE.md +1 -2
  9. package/agents/pr-description-writer.md +11 -81
  10. package/bin/install.mjs +48 -9
  11. package/bin/install.prune.test.mjs +26 -0
  12. package/bin/install.settings-defaults.test.mjs +60 -33
  13. package/bin/install.test.mjs +2 -4
  14. package/commands/CLAUDE.md +1 -0
  15. package/commands/sr-loop.md +52 -0
  16. package/docs/CLAUDE.md +0 -1
  17. package/docs/references/CLAUDE.md +0 -1
  18. package/hooks/CLAUDE.md +1 -1
  19. package/hooks/blocking/CLAUDE.md +2 -27
  20. package/hooks/blocking/config/CLAUDE.md +1 -13
  21. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  22. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  23. package/hooks/blocking/stop_dispatcher.py +4 -4
  24. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  25. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  26. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  27. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  28. package/hooks/diagnostic/CLAUDE.md +3 -3
  29. package/hooks/diagnostic/hook_log_extractor.py +2 -36
  30. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  31. package/hooks/diagnostic/test_hook_log_extractor.py +8 -21
  32. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  33. package/hooks/git-hooks/git_hooks_constants/__init__.py +0 -6
  34. package/hooks/git-hooks/pre_push.py +3 -85
  35. package/hooks/git-hooks/test_pre_push.py +0 -130
  36. package/hooks/hooks.json +0 -35
  37. package/hooks/hooks_constants/CLAUDE.md +0 -9
  38. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  39. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  40. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  41. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  42. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  43. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  44. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  45. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  46. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  47. package/hooks/lifecycle/CLAUDE.md +1 -3
  48. package/package.json +1 -1
  49. package/rules/CLAUDE.md +0 -2
  50. package/rules/gh-cli-conventions.md +0 -1
  51. package/rules/git-workflow.md +1 -3
  52. package/scripts/CLAUDE.md +0 -1
  53. package/scripts/_code_review_test_support.py +0 -95
  54. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  55. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  56. package/scripts/invoke_code_review.py +8 -451
  57. package/scripts/test_invoke_code_review.py +5 -61
  58. package/scripts/test_invoke_code_review_cli.py +1 -45
  59. package/scripts/test_invoke_code_review_contract.py +1 -118
  60. package/settings.json +1 -10
  61. package/skills/CLAUDE.md +8 -0
  62. package/skills/autoconverge/reference/CLAUDE.md +1 -1
  63. package/skills/autoconverge/reference/convergence.md +7 -7
  64. package/skills/autoconverge/reference/gotchas.md +3 -10
  65. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  66. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  67. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  68. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  69. package/skills/autoconverge/workflow/converge.mjs +201 -238
  70. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  71. package/skills/bugteam/CONSTRAINTS.md +1 -1
  72. package/skills/bugteam/SKILL.md +9 -0
  73. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  74. package/skills/bugteam/test_skill_additions.py +9 -0
  75. package/skills/comments/SKILL.md +65 -0
  76. package/skills/descriptions/SKILL.md +51 -0
  77. package/skills/emergencies/SKILL.md +42 -0
  78. package/skills/imagegen/SKILL.md +21 -0
  79. package/skills/imagegen/scripts/config/__init__.py +1 -0
  80. package/skills/imagegen/scripts/config/constants.py +28 -0
  81. package/skills/imagegen/scripts/imagegen.py +45 -0
  82. package/skills/imagegen/scripts/imagegen_core.py +528 -0
  83. package/skills/imagegen/scripts/test_imagegen.py +204 -0
  84. package/skills/orchestrator/SKILL.md +2 -4
  85. package/skills/plan-to-pr/SKILL.md +6 -7
  86. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  87. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  88. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  89. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  90. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  91. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  92. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  93. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  94. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  95. package/skills/pr-converge/SKILL.md +13 -16
  96. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  97. package/skills/pr-converge/reference/state-schema.md +1 -27
  98. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  99. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  100. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  101. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  102. package/skills/prototype/SKILL.md +4 -4
  103. package/skills/prototype/reference/honest-limitations.md +4 -4
  104. package/skills/prototype/reference/promotion-tasks.md +2 -2
  105. package/skills/prototype/workflows/promotion.md +4 -6
  106. package/skills/reviews/SKILL.md +85 -0
  107. package/skills/small-cl/SKILL.md +52 -0
  108. package/skills/usage-pause/SKILL.md +1 -1
  109. package/skills/usage-pause/scripts/resolve_usage_window.py +32 -4
  110. package/skills/usage-pause/scripts/test_resolve_usage_window.py +26 -0
  111. package/skills/usage-pause/scripts/usage_pause_constants/resolve_usage_window_constants.py +3 -1
  112. package/agents/code-verifier.md +0 -74
  113. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  114. package/docs/references/code-review-enforcement.md +0 -132
  115. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  116. package/hooks/blocking/code_review_gate_deny.py +0 -74
  117. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  118. package/hooks/blocking/code_review_push_gate.py +0 -145
  119. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  120. package/hooks/blocking/code_review_stamp_store.py +0 -233
  121. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  122. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  123. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  124. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  125. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  126. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  127. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  128. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  129. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  130. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  131. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  132. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  133. package/hooks/blocking/conftest.py +0 -123
  134. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  135. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  136. package/hooks/blocking/pr_description_body_audit.py +0 -148
  137. package/hooks/blocking/pr_description_command_parser.py +0 -237
  138. package/hooks/blocking/pr_description_enforcer.py +0 -227
  139. package/hooks/blocking/pr_description_pr_number.py +0 -155
  140. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  141. package/hooks/blocking/pr_description_readability.py +0 -364
  142. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  143. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  144. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  145. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  146. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  147. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  148. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  149. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  150. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  151. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  152. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  153. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  154. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  155. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  156. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  157. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  158. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  159. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  160. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  161. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  162. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  163. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  164. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  165. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  166. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  167. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  168. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  169. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  170. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  171. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  172. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  173. package/hooks/blocking/verification_verdict_store.py +0 -1039
  174. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  175. package/hooks/blocking/verified_commit_gate.py +0 -113
  176. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  177. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  178. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  179. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  180. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  181. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  182. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  183. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  184. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  185. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  186. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  187. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  188. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  189. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  190. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  191. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  192. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  193. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  194. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  195. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  196. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  197. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  198. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  199. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  200. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  201. package/rules/proof-of-work-pr-comments.md +0 -22
  202. package/rules/verified-commit-gate-skip.md +0 -4
  203. package/scripts/Show-Asset.ps1 +0 -106
@@ -1,205 +0,0 @@
1
- """Behavioral tests for the per-work-tree code-review stamp store.
2
-
3
- Each test builds a real git repository with a real origin remote and records or
4
- reads real stamp files under an isolated home, so it exercises the same store
5
- surface the push and PR-create gates call: record a clean stamp, then ask
6
- whether it covers the live branch surface at a given effort.
7
- """
8
-
9
- import importlib.util
10
- import pathlib
11
- import subprocess
12
- import sys
13
-
14
- import pytest
15
-
16
- _HOOK_DIR = pathlib.Path(__file__).parent
17
- if str(_HOOK_DIR) not in sys.path:
18
- sys.path.insert(0, str(_HOOK_DIR))
19
-
20
- _store_spec = importlib.util.spec_from_file_location(
21
- "code_review_stamp_store",
22
- _HOOK_DIR / "code_review_stamp_store.py",
23
- )
24
- assert _store_spec is not None
25
- assert _store_spec.loader is not None
26
- _store_module = importlib.util.module_from_spec(_store_spec)
27
- _store_spec.loader.exec_module(_store_module)
28
-
29
- record_clean_stamp = _store_module.record_clean_stamp
30
- stamp_covers_surface = _store_module.stamp_covers_surface
31
- live_surface_hash = _store_module.live_surface_hash
32
- stamp_path_for_repo = _store_module.stamp_path_for_repo
33
-
34
- _verdict_store_module = sys.modules["verification_verdict_store"]
35
- root_key_for_repo = _verdict_store_module.root_key_for_repo
36
-
37
- _constants_spec = importlib.util.spec_from_file_location(
38
- "code_review_enforcement_constants",
39
- _HOOK_DIR / "config" / "code_review_enforcement_constants.py",
40
- )
41
- assert _constants_spec is not None
42
- assert _constants_spec.loader is not None
43
- _constants_module = importlib.util.module_from_spec(_constants_spec)
44
- _constants_spec.loader.exec_module(_constants_module)
45
- PUSH_REQUIRED_EFFORT = _constants_module.PUSH_REQUIRED_EFFORT
46
- PR_CREATE_REQUIRED_EFFORT = _constants_module.PR_CREATE_REQUIRED_EFFORT
47
- ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER = _constants_module.ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER
48
-
49
- PRODUCTION_SOURCE = "def add(left: int, right: int) -> int:\n return left + right\n"
50
- CHANGED_SOURCE = "def add(left: int, right: int) -> int:\n return left - right\n"
51
- FURTHER_CHANGED_SOURCE = "def add(left: int, right: int) -> int:\n return left * right\n"
52
-
53
-
54
- def _run_git(repo_dir: pathlib.Path, *git_arguments: str) -> None:
55
- subprocess.run(
56
- ["git", "-C", str(repo_dir), *git_arguments],
57
- check=True,
58
- capture_output=True,
59
- text=True,
60
- )
61
-
62
-
63
- def _make_repo_with_origin(tmp_path: pathlib.Path) -> pathlib.Path:
64
- origin_dir = tmp_path / "origin.git"
65
- work_dir = tmp_path / "work"
66
- work_dir.mkdir()
67
- subprocess.run(
68
- ["git", "init", "--bare", "--initial-branch=main", str(origin_dir)],
69
- check=True,
70
- capture_output=True,
71
- text=True,
72
- )
73
- empty_hooks_dir = tmp_path / "nohooks"
74
- empty_hooks_dir.mkdir()
75
- _run_git(work_dir, "init", "--initial-branch=main")
76
- _run_git(work_dir, "config", "user.email", "tests@example.com")
77
- _run_git(work_dir, "config", "user.name", "Stamp Store Tests")
78
- _run_git(work_dir, "config", "core.hooksPath", str(empty_hooks_dir))
79
- (work_dir / "src").mkdir()
80
- (work_dir / "src" / "app.py").write_text(PRODUCTION_SOURCE, encoding="utf-8")
81
- _run_git(work_dir, "add", "-A")
82
- _run_git(work_dir, "commit", "-m", "base")
83
- _run_git(work_dir, "remote", "add", "origin", str(origin_dir))
84
- _run_git(work_dir, "push", "-u", "origin", "main")
85
- return work_dir
86
-
87
-
88
- def _isolate_home(monkeypatch: pytest.MonkeyPatch, fake_home: pathlib.Path) -> None:
89
- home_text = str(fake_home)
90
- monkeypatch.setenv("HOME", home_text)
91
- monkeypatch.setenv("USERPROFILE", home_text)
92
- monkeypatch.delenv("HOMEDRIVE", raising=False)
93
- monkeypatch.delenv("HOMEPATH", raising=False)
94
-
95
-
96
- def _repo_with_change(tmp_path: pathlib.Path, changed_source: str) -> pathlib.Path:
97
- work_dir = _make_repo_with_origin(tmp_path)
98
- (work_dir / "src" / "app.py").write_text(changed_source, encoding="utf-8")
99
- return work_dir
100
-
101
-
102
- def test_recorded_low_stamp_covers_the_push_surface(
103
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
104
- ) -> None:
105
- _isolate_home(monkeypatch, tmp_path / "home")
106
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
107
- surface_hash = live_surface_hash(str(work_dir))
108
- assert surface_hash is not None
109
- record_clean_stamp(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT)
110
- assert stamp_covers_surface(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT) is True
111
-
112
-
113
- def test_low_stamp_does_not_cover_the_pr_create_surface(
114
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
115
- ) -> None:
116
- _isolate_home(monkeypatch, tmp_path / "home")
117
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
118
- surface_hash = live_surface_hash(str(work_dir))
119
- assert surface_hash is not None
120
- record_clean_stamp(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT)
121
- assert stamp_covers_surface(str(work_dir), surface_hash, PR_CREATE_REQUIRED_EFFORT) is False
122
-
123
-
124
- def test_xhigh_stamp_covers_both_push_and_pr_create(
125
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
126
- ) -> None:
127
- _isolate_home(monkeypatch, tmp_path / "home")
128
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
129
- surface_hash = live_surface_hash(str(work_dir))
130
- assert surface_hash is not None
131
- record_clean_stamp(str(work_dir), surface_hash, PR_CREATE_REQUIRED_EFFORT)
132
- assert stamp_covers_surface(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT) is True
133
- assert stamp_covers_surface(str(work_dir), surface_hash, PR_CREATE_REQUIRED_EFFORT) is True
134
-
135
-
136
- def test_stamp_bound_to_one_hash_does_not_cover_a_different_hash(
137
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
138
- ) -> None:
139
- _isolate_home(monkeypatch, tmp_path / "home")
140
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
141
- surface_hash = live_surface_hash(str(work_dir))
142
- assert surface_hash is not None
143
- record_clean_stamp(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT)
144
- other_hash = "f" * len(surface_hash)
145
- assert stamp_covers_surface(str(work_dir), other_hash, PUSH_REQUIRED_EFFORT) is False
146
-
147
-
148
- def test_editing_a_file_after_minting_drops_coverage(
149
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
150
- ) -> None:
151
- _isolate_home(monkeypatch, tmp_path / "home")
152
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
153
- minted_hash = live_surface_hash(str(work_dir))
154
- assert minted_hash is not None
155
- record_clean_stamp(str(work_dir), minted_hash, PUSH_REQUIRED_EFFORT)
156
- (work_dir / "src" / "app.py").write_text(FURTHER_CHANGED_SOURCE, encoding="utf-8")
157
- live_hash_after_edit = live_surface_hash(str(work_dir))
158
- assert live_hash_after_edit is not None
159
- assert live_hash_after_edit != minted_hash
160
- assert stamp_covers_surface(str(work_dir), live_hash_after_edit, PUSH_REQUIRED_EFFORT) is False
161
-
162
-
163
- def test_no_stamp_file_reads_as_not_covered(
164
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
165
- ) -> None:
166
- _isolate_home(monkeypatch, tmp_path / "home")
167
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
168
- surface_hash = live_surface_hash(str(work_dir))
169
- assert surface_hash is not None
170
- assert not stamp_path_for_repo(str(work_dir)).exists()
171
- assert stamp_covers_surface(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT) is False
172
-
173
-
174
- def test_recording_a_second_effort_keeps_the_first(
175
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
176
- ) -> None:
177
- _isolate_home(monkeypatch, tmp_path / "home")
178
- work_dir = _repo_with_change(tmp_path, CHANGED_SOURCE)
179
- surface_hash = live_surface_hash(str(work_dir))
180
- assert surface_hash is not None
181
- record_clean_stamp(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT)
182
- record_clean_stamp(str(work_dir), surface_hash, PR_CREATE_REQUIRED_EFFORT)
183
- assert stamp_covers_surface(str(work_dir), surface_hash, PUSH_REQUIRED_EFFORT) is True
184
- assert stamp_covers_surface(str(work_dir), surface_hash, PR_CREATE_REQUIRED_EFFORT) is True
185
-
186
-
187
- def test_live_surface_hash_is_none_when_nothing_changed(
188
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
189
- ) -> None:
190
- _isolate_home(monkeypatch, tmp_path / "home")
191
- work_dir = _make_repo_with_origin(tmp_path)
192
- assert live_surface_hash(str(work_dir)) is None
193
-
194
-
195
- def test_push_required_effort_is_the_lowest_known_token() -> None:
196
- assert PUSH_REQUIRED_EFFORT == ALL_EFFORT_TOKENS_IN_ASCENDING_ORDER[0]
197
-
198
-
199
- def test_stamp_path_for_repo_stem_matches_the_shared_root_key(
200
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
201
- ) -> None:
202
- _isolate_home(monkeypatch, tmp_path / "home")
203
- work_dir = tmp_path / "work-tree"
204
- work_dir.mkdir()
205
- assert stamp_path_for_repo(str(work_dir)).stem == root_key_for_repo(str(work_dir))