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,67 +0,0 @@
1
- """Regression tests for the verified-commit constants bootstrap.
2
-
3
- These prove the shared constants module resolves to the sibling
4
- ``config/verified_commit_constants.py`` file even when a foreign ``config``
5
- package sits ahead of ``blocking`` on ``sys.path``, and that a registration a
6
- caller placed first is left untouched.
7
- """
8
-
9
- import subprocess
10
- from collections.abc import Callable
11
-
12
- RunUnderConfigShadow = Callable[[str], subprocess.CompletedProcess[str]]
13
-
14
-
15
- def test_register_binds_dotted_name_to_sibling_constants_file(
16
- run_under_config_shadow: RunUnderConfigShadow,
17
- ) -> None:
18
- completed_probe = run_under_config_shadow(
19
- "import verified_commit_config_bootstrap as bootstrap\n"
20
- "bootstrap.register_verified_commit_constants()\n"
21
- "import config.verified_commit_constants as loaded\n"
22
- "print(loaded.__file__)\n"
23
- "print(loaded.DETACHED_HEAD_LABEL)\n"
24
- )
25
- assert completed_probe.returncode == 0, completed_probe.stderr
26
- assert "IS_DECOY_CONFIG" not in completed_probe.stdout
27
- resolved_file_line, detached_head_line = completed_probe.stdout.splitlines()[:2]
28
- assert resolved_file_line.replace("\\", "/").endswith(
29
- "blocking/config/verified_commit_constants.py"
30
- )
31
- assert detached_head_line == "HEAD"
32
-
33
-
34
- def test_register_binds_gate_output_constants_module(
35
- run_under_config_shadow: RunUnderConfigShadow,
36
- ) -> None:
37
- completed_probe = run_under_config_shadow(
38
- "import verified_commit_config_bootstrap as bootstrap\n"
39
- "bootstrap.register_verified_commit_constants()\n"
40
- "import config.verified_commit_gate_output_constants as loaded\n"
41
- "print(loaded.__file__)\n"
42
- "print(loaded.DENY_PERMISSION_DECISION)\n"
43
- )
44
- assert completed_probe.returncode == 0, completed_probe.stderr
45
- resolved_file_line, deny_decision_line = completed_probe.stdout.splitlines()[:2]
46
- assert resolved_file_line.replace("\\", "/").endswith(
47
- "blocking/config/verified_commit_gate_output_constants.py"
48
- )
49
- assert deny_decision_line == "deny"
50
-
51
-
52
- def test_register_leaves_an_already_cached_module_untouched(
53
- run_under_config_shadow: RunUnderConfigShadow,
54
- ) -> None:
55
- completed_probe = run_under_config_shadow(
56
- "import sys\n"
57
- "import types\n"
58
- "sentinel = types.ModuleType('config.verified_commit_constants')\n"
59
- "sentinel.ORIGIN = 'caller-seeded'\n"
60
- "sys.modules['config.verified_commit_constants'] = sentinel\n"
61
- "import verified_commit_config_bootstrap as bootstrap\n"
62
- "bootstrap.register_verified_commit_constants()\n"
63
- "import config.verified_commit_constants as loaded\n"
64
- "print(getattr(loaded, 'ORIGIN', 'displaced'))\n"
65
- )
66
- assert completed_probe.returncode == 0, completed_probe.stderr
67
- assert completed_probe.stdout.strip().splitlines()[-1] == "caller-seeded"
@@ -1,176 +0,0 @@
1
- """Gate behavior for docs-only deltas and the cross-work-tree deny message.
2
-
3
- Each test builds a real git repository with a real origin remote and drives
4
- ``deny_reason_for_directory`` — the same decision the verified_commit_gate hook
5
- runs — so the docs-only exemption, the docs-after-verified-code block, and the
6
- denial message's work-tree keying are asserted against live git state.
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
- gate_spec = importlib.util.spec_from_file_location(
21
- "verified_commit_gate",
22
- _HOOK_DIR / "verified_commit_gate.py",
23
- )
24
- assert gate_spec is not None
25
- assert gate_spec.loader is not None
26
- gate_module = importlib.util.module_from_spec(gate_spec)
27
- gate_spec.loader.exec_module(gate_module)
28
- deny_reason_for_directory = gate_module.deny_reason_for_directory
29
-
30
- store_spec = importlib.util.spec_from_file_location(
31
- "verification_verdict_store",
32
- _HOOK_DIR / "verification_verdict_store.py",
33
- )
34
- assert store_spec is not None
35
- assert store_spec.loader is not None
36
- store_module = importlib.util.module_from_spec(store_spec)
37
- store_spec.loader.exec_module(store_module)
38
- resolve_merge_base = store_module.resolve_merge_base
39
- branch_surface_manifest = store_module.branch_surface_manifest
40
- manifest_sha256 = store_module.manifest_sha256
41
- write_verdict = store_module.write_verdict
42
-
43
- PRODUCTION_SOURCE = "def add(left: int, right: int) -> int:\n return left + right\n"
44
- BEHAVIORAL_EDIT_SOURCE = "def add(left: int, right: int) -> int:\n return left - right\n"
45
-
46
-
47
- def _run_git(repo_dir: pathlib.Path, *git_arguments: str) -> None:
48
- subprocess.run(
49
- ["git", "-C", str(repo_dir), *git_arguments],
50
- check=True,
51
- capture_output=True,
52
- text=True,
53
- )
54
-
55
-
56
- def _isolate_home(monkeypatch: pytest.MonkeyPatch, fake_home: pathlib.Path) -> None:
57
- home_text = str(fake_home)
58
- monkeypatch.setenv("HOME", home_text)
59
- monkeypatch.setenv("USERPROFILE", home_text)
60
- monkeypatch.delenv("HOMEDRIVE", raising=False)
61
- monkeypatch.delenv("HOMEPATH", raising=False)
62
-
63
-
64
- def _empty_hooks_dir(tmp_path: pathlib.Path) -> pathlib.Path:
65
- hooks_dir = tmp_path / "nohooks"
66
- hooks_dir.mkdir(exist_ok=True)
67
- return hooks_dir
68
-
69
-
70
- def _empty_transcript(tmp_path: pathlib.Path) -> pathlib.Path:
71
- transcript_path = tmp_path / "projects" / "demo" / "sess1.jsonl"
72
- transcript_path.parent.mkdir(parents=True, exist_ok=True)
73
- transcript_path.write_text("", encoding="utf-8")
74
- return transcript_path
75
-
76
-
77
- def _init_pushed_repo(tmp_path: pathlib.Path) -> pathlib.Path:
78
- origin_dir = tmp_path / "origin.git"
79
- work_dir = tmp_path / "work"
80
- work_dir.mkdir()
81
- subprocess.run(
82
- ["git", "init", "--bare", "--initial-branch=main", str(origin_dir)],
83
- check=True,
84
- capture_output=True,
85
- text=True,
86
- )
87
- _run_git(work_dir, "init", "--initial-branch=main")
88
- _run_git(work_dir, "config", "user.email", "tests@example.com")
89
- _run_git(work_dir, "config", "user.name", "Docs Delta Tests")
90
- _run_git(work_dir, "config", "core.hooksPath", str(_empty_hooks_dir(tmp_path)))
91
- (work_dir / "app.py").write_text(PRODUCTION_SOURCE, encoding="utf-8")
92
- (work_dir / "README.md").write_text("# base\n", encoding="utf-8")
93
- _run_git(work_dir, "add", "-A")
94
- _run_git(work_dir, "commit", "-m", "base")
95
- _run_git(work_dir, "remote", "add", "origin", str(origin_dir))
96
- _run_git(work_dir, "push", "-u", "origin", "main")
97
- return work_dir
98
-
99
-
100
- def _make_docs_only_repo(tmp_path: pathlib.Path) -> pathlib.Path:
101
- work_dir = _init_pushed_repo(tmp_path)
102
- (work_dir / "README.md").write_text("# base\n\nUpdated docs.\n", encoding="utf-8")
103
- return work_dir
104
-
105
-
106
- def _make_behavioral_code_repo(tmp_path: pathlib.Path) -> pathlib.Path:
107
- work_dir = _init_pushed_repo(tmp_path)
108
- (work_dir / "app.py").write_text(BEHAVIORAL_EDIT_SOURCE, encoding="utf-8")
109
- return work_dir
110
-
111
-
112
- def _live_surface_hash(work_dir: pathlib.Path) -> str:
113
- merge_base_sha = resolve_merge_base(str(work_dir))
114
- assert merge_base_sha is not None
115
- surface_manifest_text = branch_surface_manifest(str(work_dir), merge_base_sha)
116
- assert surface_manifest_text is not None
117
- return manifest_sha256(surface_manifest_text)
118
-
119
-
120
- def test_pure_docs_only_branch_is_allowed_without_verdict(
121
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
122
- ) -> None:
123
- fake_home = tmp_path / "home"
124
- fake_home.mkdir()
125
- _isolate_home(monkeypatch, fake_home)
126
- work_dir = _make_docs_only_repo(tmp_path)
127
- transcript_path = _empty_transcript(tmp_path)
128
- assert deny_reason_for_directory(str(work_dir), str(transcript_path)) is None
129
-
130
-
131
- def test_readme_change_after_verified_code_commit_is_denied(
132
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
133
- ) -> None:
134
- fake_home = tmp_path / "home"
135
- fake_home.mkdir()
136
- _isolate_home(monkeypatch, fake_home)
137
- work_dir = _make_behavioral_code_repo(tmp_path)
138
- code_surface_hash = _live_surface_hash(work_dir)
139
- write_verdict(str(work_dir), code_surface_hash, True, [], "agent-x")
140
- transcript_path = _empty_transcript(tmp_path)
141
- assert deny_reason_for_directory(str(work_dir), str(transcript_path)) is None
142
- _run_git(work_dir, "add", "-A")
143
- _run_git(work_dir, "commit", "-m", "verified code change")
144
- (work_dir / "README.md").write_text("# added after verification\n", encoding="utf-8")
145
- deny_reason = deny_reason_for_directory(str(work_dir), str(transcript_path))
146
- assert deny_reason is not None
147
- assert "VERIFIED_COMMIT_GATE" in deny_reason
148
-
149
-
150
- def test_readme_added_beside_unverified_code_is_denied(
151
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
152
- ) -> None:
153
- fake_home = tmp_path / "home"
154
- fake_home.mkdir()
155
- _isolate_home(monkeypatch, fake_home)
156
- work_dir = _make_behavioral_code_repo(tmp_path)
157
- (work_dir / "README.md").write_text("# docs beside code\n", encoding="utf-8")
158
- transcript_path = _empty_transcript(tmp_path)
159
- deny_reason = deny_reason_for_directory(str(work_dir), str(transcript_path))
160
- assert deny_reason is not None
161
- assert "VERIFIED_COMMIT_GATE" in deny_reason
162
-
163
-
164
- def test_deny_reason_for_directory_names_worktree_keying_and_remedy(
165
- monkeypatch: pytest.MonkeyPatch, tmp_path: pathlib.Path
166
- ) -> None:
167
- fake_home = tmp_path / "home"
168
- fake_home.mkdir()
169
- _isolate_home(monkeypatch, fake_home)
170
- work_dir = _make_behavioral_code_repo(tmp_path)
171
- transcript_path = _empty_transcript(tmp_path)
172
- deny_reason = deny_reason_for_directory(str(work_dir), str(transcript_path))
173
- assert deny_reason is not None
174
- lowered_reason = deny_reason.lower()
175
- assert "work tree" in lowered_reason
176
- assert "run this command from the work tree" in lowered_reason