claude-dev-env 2.12.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 (193) hide show
  1. package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
  2. package/_shared/pr-loop/scripts/test_code_rules_gate.py +0 -26
  3. package/agents/CLAUDE.md +1 -2
  4. package/agents/pr-description-writer.md +11 -81
  5. package/bin/install.mjs +48 -9
  6. package/bin/install.prune.test.mjs +26 -0
  7. package/bin/install.settings-defaults.test.mjs +60 -33
  8. package/bin/install.test.mjs +2 -4
  9. package/commands/sr-loop.md +9 -5
  10. package/docs/CLAUDE.md +0 -1
  11. package/docs/references/CLAUDE.md +0 -1
  12. package/hooks/CLAUDE.md +1 -1
  13. package/hooks/blocking/CLAUDE.md +2 -27
  14. package/hooks/blocking/config/CLAUDE.md +1 -13
  15. package/hooks/blocking/conventional_pr_title_gate.py +1 -2
  16. package/hooks/blocking/send_user_file_open_locally_blocker.py +1 -1
  17. package/hooks/blocking/stop_dispatcher.py +4 -4
  18. package/hooks/blocking/test_bash_pre_tool_use_dispatcher.py +0 -3
  19. package/hooks/blocking/test_pre_tool_use_dispatcher.py +6 -7
  20. package/hooks/blocking/test_send_user_file_open_locally_blocker.py +4 -3
  21. package/hooks/blocking/test_shared_stdin_adoption.py +0 -2
  22. package/hooks/diagnostic/CLAUDE.md +3 -3
  23. package/hooks/diagnostic/hook_log_extractor.py +2 -36
  24. package/hooks/diagnostic/hook_log_stop_wrapper.py +6 -155
  25. package/hooks/diagnostic/test_hook_log_extractor.py +8 -21
  26. package/hooks/diagnostic/test_hook_log_stop_wrapper.py +3 -331
  27. package/hooks/git-hooks/git_hooks_constants/__init__.py +0 -6
  28. package/hooks/git-hooks/pre_push.py +3 -85
  29. package/hooks/git-hooks/test_pre_push.py +0 -130
  30. package/hooks/hooks.json +0 -35
  31. package/hooks/hooks_constants/CLAUDE.md +0 -9
  32. package/hooks/hooks_constants/bash_pre_tool_use_dispatcher_constants.py +0 -11
  33. package/hooks/hooks_constants/convergence_branch_constants.py +0 -1
  34. package/hooks/hooks_constants/fable_spawn_gate_constants.py +3 -4
  35. package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -8
  36. package/hooks/hooks_constants/send_user_file_open_locally_blocker_constants.py +1 -6
  37. package/hooks/hooks_constants/stop_dispatcher_constants.py +0 -1
  38. package/hooks/hooks_constants/test_bash_pre_tool_use_dispatcher_constants.py +1 -26
  39. package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +0 -5
  40. package/hooks/hooks_constants/test_stop_dispatcher_constants.py +0 -1
  41. package/hooks/lifecycle/CLAUDE.md +1 -3
  42. package/package.json +1 -1
  43. package/rules/CLAUDE.md +0 -2
  44. package/rules/gh-cli-conventions.md +0 -1
  45. package/rules/git-workflow.md +1 -3
  46. package/scripts/CLAUDE.md +0 -1
  47. package/scripts/_code_review_test_support.py +0 -95
  48. package/scripts/dev_env_scripts_constants/code_review_constants.py +9 -98
  49. package/scripts/dev_env_scripts_constants/test_code_review_constants.py +1 -44
  50. package/scripts/invoke_code_review.py +8 -451
  51. package/scripts/test_invoke_code_review.py +5 -61
  52. package/scripts/test_invoke_code_review_cli.py +1 -45
  53. package/scripts/test_invoke_code_review_contract.py +1 -118
  54. package/settings.json +1 -10
  55. package/skills/CLAUDE.md +8 -0
  56. package/skills/autoconverge/reference/CLAUDE.md +1 -1
  57. package/skills/autoconverge/reference/convergence.md +7 -7
  58. package/skills/autoconverge/reference/gotchas.md +3 -10
  59. package/skills/autoconverge/workflow/converge.contract.test.mjs +53 -1388
  60. package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +4 -3
  61. package/skills/autoconverge/workflow/converge.fix-recovery.test.mjs +33 -493
  62. package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +15 -97
  63. package/skills/autoconverge/workflow/converge.mjs +201 -238
  64. package/skills/autoconverge/workflow/converge.precatch.test.mjs +1 -1
  65. package/skills/bugteam/CONSTRAINTS.md +1 -1
  66. package/skills/bugteam/SKILL.md +9 -0
  67. package/skills/bugteam/reference/audit-and-teammates.md +10 -6
  68. package/skills/bugteam/test_skill_additions.py +9 -0
  69. package/skills/comments/SKILL.md +65 -0
  70. package/skills/descriptions/SKILL.md +51 -0
  71. package/skills/emergencies/SKILL.md +42 -0
  72. package/skills/imagegen/SKILL.md +21 -0
  73. package/skills/imagegen/scripts/config/__init__.py +1 -0
  74. package/skills/imagegen/scripts/config/constants.py +28 -0
  75. package/skills/imagegen/scripts/imagegen.py +45 -0
  76. package/skills/imagegen/scripts/imagegen_core.py +528 -0
  77. package/skills/imagegen/scripts/test_imagegen.py +204 -0
  78. package/skills/orchestrator/SKILL.md +2 -4
  79. package/skills/plan-to-pr/SKILL.md +6 -7
  80. package/skills/plan-to-pr/reference/final-validation-tasks.md +1 -1
  81. package/skills/plan-to-pr/reference/process-inventory.md +2 -2
  82. package/skills/plan-to-pr/reference/review-loop.md +2 -2
  83. package/skills/plan-to-pr/reference/run-record.schema.json +1 -1
  84. package/skills/plan-to-pr/reference/task-seeds.md +1 -1
  85. package/skills/plan-to-pr/reference/task-ticket.md +3 -4
  86. package/skills/plan-to-pr/scripts/config/constants.py +1 -2
  87. package/skills/plan-to-pr/scripts/test_validate_protocol.py +1 -1
  88. package/skills/plan-to-pr/test_skill_contract.py +2 -2
  89. package/skills/pr-converge/SKILL.md +13 -16
  90. package/skills/pr-converge/reference/progress-checklist.md +6 -5
  91. package/skills/pr-converge/reference/state-schema.md +1 -27
  92. package/skills/pr-loop-cloud-transport/SKILL.md +1 -1
  93. package/skills/pr-loop-cloud-transport/reference/identity-and-hooks.md +3 -3
  94. package/skills/pr-loop-lifecycle/SKILL.md +1 -1
  95. package/skills/pr-loop-lifecycle/reference/teardown-publish-permissions.md +1 -1
  96. package/skills/prototype/SKILL.md +4 -4
  97. package/skills/prototype/reference/honest-limitations.md +4 -4
  98. package/skills/prototype/reference/promotion-tasks.md +2 -2
  99. package/skills/prototype/workflows/promotion.md +4 -6
  100. package/skills/reviews/SKILL.md +85 -0
  101. package/skills/small-cl/SKILL.md +52 -0
  102. package/agents/code-verifier.md +0 -74
  103. package/docs/PR_DESCRIPTION_GUIDE.md +0 -157
  104. package/docs/references/code-review-enforcement.md +0 -132
  105. package/hooks/blocking/code_review_enforcement_config_bootstrap.py +0 -53
  106. package/hooks/blocking/code_review_gate_deny.py +0 -74
  107. package/hooks/blocking/code_review_pr_create_gate.py +0 -198
  108. package/hooks/blocking/code_review_push_gate.py +0 -145
  109. package/hooks/blocking/code_review_stamp_directory_write_blocker.py +0 -348
  110. package/hooks/blocking/code_review_stamp_store.py +0 -233
  111. package/hooks/blocking/code_review_stamp_write_blocker_parts/__init__.py +0 -7
  112. package/hooks/blocking/code_review_stamp_write_blocker_parts/conftest.py +0 -15
  113. package/hooks/blocking/code_review_stamp_write_blocker_parts/obfuscated_stamp_path_reference.py +0 -212
  114. package/hooks/blocking/code_review_stamp_write_blocker_parts/split_directory_change_into_stamp.py +0 -138
  115. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_obfuscated_stamp_path_reference.py +0 -49
  116. package/hooks/blocking/code_review_stamp_write_blocker_parts/test_split_directory_change_into_stamp.py +0 -38
  117. package/hooks/blocking/code_verifier_spawn_preflight_gate.py +0 -483
  118. package/hooks/blocking/config/code_review_enforcement_constants.py +0 -143
  119. package/hooks/blocking/config/test_code_review_enforcement_constants.py +0 -166
  120. package/hooks/blocking/config/verified_commit_constants.py +0 -160
  121. package/hooks/blocking/config/verified_commit_context_constants.py +0 -21
  122. package/hooks/blocking/config/verified_commit_gate_output_constants.py +0 -14
  123. package/hooks/blocking/conftest.py +0 -123
  124. package/hooks/blocking/convergence_gate_blocker.py +0 -310
  125. package/hooks/blocking/pr_converge_bugteam_enforcer.py +0 -170
  126. package/hooks/blocking/pr_description_body_audit.py +0 -148
  127. package/hooks/blocking/pr_description_command_parser.py +0 -237
  128. package/hooks/blocking/pr_description_enforcer.py +0 -227
  129. package/hooks/blocking/pr_description_pr_number.py +0 -155
  130. package/hooks/blocking/pr_description_proof_of_work.py +0 -385
  131. package/hooks/blocking/pr_description_readability.py +0 -364
  132. package/hooks/blocking/reviewer_spawn_gate.py +0 -182
  133. package/hooks/blocking/test_code_review_enforcement_config_bootstrap.py +0 -62
  134. package/hooks/blocking/test_code_review_gate_deny.py +0 -54
  135. package/hooks/blocking/test_code_review_pr_create_gate.py +0 -199
  136. package/hooks/blocking/test_code_review_push_gate.py +0 -205
  137. package/hooks/blocking/test_code_review_stamp_directory_write_blocker.py +0 -199
  138. package/hooks/blocking/test_code_review_stamp_store.py +0 -205
  139. package/hooks/blocking/test_code_verifier_spawn_preflight_gate.py +0 -710
  140. package/hooks/blocking/test_code_verifier_tools_contract.py +0 -28
  141. package/hooks/blocking/test_convergence_gate_blocker.py +0 -428
  142. package/hooks/blocking/test_pr_converge_bugteam_enforcer.py +0 -311
  143. package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +0 -184
  144. package/hooks/blocking/test_pr_description_enforcer.py +0 -221
  145. package/hooks/blocking/test_pr_description_enforcer_body_audit.py +0 -247
  146. package/hooks/blocking/test_pr_description_enforcer_body_rules.py +0 -493
  147. package/hooks/blocking/test_pr_description_enforcer_command_parser.py +0 -366
  148. package/hooks/blocking/test_pr_description_enforcer_pr_number.py +0 -159
  149. package/hooks/blocking/test_pr_description_enforcer_proof_gate.py +0 -175
  150. package/hooks/blocking/test_pr_description_enforcer_readability.py +0 -443
  151. package/hooks/blocking/test_pr_description_proof_of_work.py +0 -313
  152. package/hooks/blocking/test_reviewer_spawn_gate.py +0 -230
  153. package/hooks/blocking/test_verdict_directory_write_blocker.py +0 -804
  154. package/hooks/blocking/test_verification_verdict_store.py +0 -974
  155. package/hooks/blocking/test_verified_commit_config_bootstrap.py +0 -67
  156. package/hooks/blocking/test_verified_commit_docs_delta.py +0 -176
  157. package/hooks/blocking/test_verified_commit_gate.py +0 -581
  158. package/hooks/blocking/test_verified_commit_gate_additional_context.py +0 -134
  159. package/hooks/blocking/test_verified_commit_message_accuracy_blocker.py +0 -131
  160. package/hooks/blocking/test_verifier_verdict_minter.py +0 -299
  161. package/hooks/blocking/tests/test_verified_commit_gate.py +0 -41
  162. package/hooks/blocking/verdict_directory_write_blocker.py +0 -687
  163. package/hooks/blocking/verification_verdict_store.py +0 -1039
  164. package/hooks/blocking/verified_commit_config_bootstrap.py +0 -63
  165. package/hooks/blocking/verified_commit_gate.py +0 -113
  166. package/hooks/blocking/verified_commit_gate_parts/CLAUDE.md +0 -28
  167. package/hooks/blocking/verified_commit_gate_parts/__init__.py +0 -1
  168. package/hooks/blocking/verified_commit_gate_parts/command_tokenization.py +0 -174
  169. package/hooks/blocking/verified_commit_gate_parts/deny_payload.py +0 -53
  170. package/hooks/blocking/verified_commit_gate_parts/deny_reason.py +0 -80
  171. package/hooks/blocking/verified_commit_gate_parts/directory_resolution.py +0 -170
  172. package/hooks/blocking/verified_commit_gate_parts/gated_invocations.py +0 -217
  173. package/hooks/blocking/verified_commit_gate_parts/tests/conftest.py +0 -10
  174. package/hooks/blocking/verified_commit_gate_parts/tests/test_command_tokenization.py +0 -94
  175. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_payload.py +0 -17
  176. package/hooks/blocking/verified_commit_gate_parts/tests/test_deny_reason.py +0 -38
  177. package/hooks/blocking/verified_commit_gate_parts/tests/test_directory_resolution.py +0 -71
  178. package/hooks/blocking/verified_commit_gate_parts/tests/test_gated_invocations.py +0 -96
  179. package/hooks/blocking/verified_commit_message_accuracy_blocker.py +0 -167
  180. package/hooks/blocking/verifier_verdict_minter.py +0 -280
  181. package/hooks/hooks_constants/code_verifier_spawn_preflight_gate_constants.py +0 -64
  182. package/hooks/hooks_constants/convergence_gate_blocker_constants.py +0 -53
  183. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_constants.py +0 -55
  184. package/hooks/hooks_constants/pr_converge_bugteam_enforcer_state.py +0 -67
  185. package/hooks/hooks_constants/pr_description_enforcer_constants.py +0 -167
  186. package/hooks/hooks_constants/pr_description_proof_of_work_constants.py +0 -107
  187. package/hooks/hooks_constants/reviewer_spawn_gate_constants.py +0 -41
  188. package/hooks/hooks_constants/test_pr_description_enforcer_constants.py +0 -292
  189. package/hooks/lifecycle/pr_converge_bugteam_skill_tracker.py +0 -198
  190. package/hooks/lifecycle/test_pr_converge_bugteam_skill_tracker.py +0 -283
  191. package/rules/proof-of-work-pr-comments.md +0 -22
  192. package/rules/verified-commit-gate-skip.md +0 -4
  193. 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))