claude-dev-env 8.69.0 → 8.70.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.
- package/.agents/skills/correction/SKILL.md +28 -6
- package/.agents/skills/correction/references/filing.md +33 -0
- package/.agents/skills/correction/scripts/correction_filing.py +327 -0
- package/.agents/skills/correction/scripts/correction_filing_constants/__init__.py +0 -0
- package/.agents/skills/correction/scripts/correction_filing_constants/config/__init__.py +0 -0
- package/.agents/skills/correction/scripts/correction_filing_constants/config/constants.py +56 -0
- package/.agents/skills/correction/scripts/pytest.ini +4 -0
- package/.agents/skills/correction/scripts/test_correction_filing.py +155 -0
- package/.agents/skills/pr-lifecycle/SKILL.md +3 -1
- package/.agents/skills/second-account-workers/SKILL.md +21 -3
- package/docs/rule-guides/features-start-with-an-eval.md +40 -0
- package/hooks/blocking/followup_pr_dedupe.py +55 -5
- package/hooks/blocking/image_read_size_gate.py +167 -0
- package/hooks/blocking/pr_lifecycle_skill_gate.py +62 -11
- package/hooks/blocking/pull_request_proof.py +86 -20
- package/hooks/blocking/test_artifact_look_gate.py +1 -0
- package/hooks/blocking/test_followup_pr_dedupe.py +33 -0
- package/hooks/blocking/test_image_read_size_gate.py +79 -0
- package/hooks/blocking/test_pr_lifecycle_skill_gate.py +117 -3
- package/hooks/blocking/test_pre_tool_use_dispatcher.py +1 -5
- package/hooks/blocking/test_pull_request_proof.py +124 -1
- package/hooks/features/README.md +1 -0
- package/hooks/features/conduct-gates.md +1 -1
- package/hooks/features/read-gates.md +20 -0
- package/hooks/features/write-edit-blocking.md +3 -5
- package/hooks/hooks.json +11 -1
- package/hooks/hooks_constants/artifact_look_gate_constants.py +1 -2
- package/hooks/hooks_constants/followup_pr_dedupe_constants.py +4 -0
- package/hooks/hooks_constants/image_read_size_gate_constants.py +41 -0
- package/hooks/hooks_constants/pr_lifecycle_skill_gate_constants.py +3 -0
- package/hooks/hooks_constants/pre_tool_use_dispatcher_constants.py +0 -5
- package/hooks/hooks_constants/pull_request_proof_constants.py +20 -1
- package/hooks/hooks_constants/test_pre_tool_use_dispatcher_constants.py +4 -11
- package/hooks/hooks_constants/transcript_skill_scan_constants.py +1 -0
- package/hooks/test_transcript_skill_scan.py +75 -1
- package/hooks/transcript_skill_scan.py +87 -15
- package/package.json +1 -1
- package/rules/anti-corollary-tests.md +2 -2
- package/rules/archiving-agent-config.md +2 -2
- package/rules/asd-ste100-language.md +2 -2
- package/rules/bdd.md +1 -1
- package/rules/build-needs-a-user.md +1 -1
- package/rules/cleanup-temp-files.md +3 -3
- package/rules/code-standards.md +1 -1
- package/rules/correction-lens.md +3 -3
- package/rules/destructive-commands.md +3 -3
- package/rules/doc-inventory-integrity.md +1 -1
- package/rules/docstring-prose-matches-implementation.md +1 -1
- package/rules/explore-thoroughly.md +1 -1
- package/rules/failure-blast-radius.md +1 -1
- package/rules/falsify-before-green.md +2 -2
- package/rules/features-start-with-an-eval.md +9 -0
- package/rules/filesystem-search.md +3 -3
- package/rules/flag-non-breaking-findings.md +2 -2
- package/rules/long-horizon-autonomy.md +2 -2
- package/rules/memory-stores-durable-facts.md +2 -2
- package/rules/no-contrast-framing.md +1 -1
- package/rules/no-cross-skill-duplicate-helpers.md +1 -1
- package/rules/orphan-css-class.md +1 -1
- package/rules/paired-test-coverage.md +1 -1
- package/rules/plain-illustrative-docstrings.md +1 -1
- package/rules/prefer-existing-tools.md +2 -2
- package/rules/proof-before-pull-request.md +1 -1
- package/rules/request-scope.md +1 -1
- package/rules/research-mode.md +2 -2
- package/rules/retired-hook-prose.md +1 -1
- package/rules/session-title.md +1 -1
- package/rules/shell-invocation.md +2 -2
- package/rules/testing.md +1 -1
- package/rules/verify-before-asking.md +3 -3
- package/rules/verify-runtime-state.md +2 -2
- package/rules/windows-filesystem-safe.md +1 -1
- package/rules/workers-done-before-complete.md +2 -2
- package/rules/workflow-substitution-slots.md +1 -1
- package/scripts/account_broker.py +10 -3
- package/scripts/account_broker_support.py +7 -3
- package/scripts/agent_image_copy.py +155 -0
- package/scripts/claude_account_worker.py +24 -5
- package/scripts/claude_account_worker_process.py +5 -0
- package/scripts/claude_account_worker_report.py +16 -1
- package/scripts/dev_env_scripts_constants/agent_image_copy_constants.py +11 -0
- package/scripts/dev_env_scripts_constants/claude_account_worker_constants.py +12 -0
- package/scripts/policy_lint/config/constants.py +1 -0
- package/scripts/test_account_broker.py +38 -0
- package/scripts/test_account_broker_support.py +24 -0
- package/scripts/test_agent_image_copy.py +76 -0
- package/scripts/test_claude_account_worker.py +47 -0
- package/scripts/test_claude_account_worker_process.py +20 -0
- package/scripts/test_claude_account_worker_report.py +15 -0
- package/scripts/tests/test_policy_lint_rules_retired_hook_prose.py +4 -4
- package/scripts/tests/test_rule_load_scopes.py +1 -0
- package/hooks/advisory/conftest.py +0 -10
- package/hooks/advisory/refactor_guard.py +0 -385
- package/hooks/advisory/refactor_guard_test_support.py +0 -46
- package/hooks/advisory/test_refactor_guard.py +0 -148
- package/hooks/advisory/test_refactor_guard_advisory.py +0 -242
- package/hooks/advisory/test_refactor_guard_eligibility.py +0 -166
- package/hooks/hooks_constants/refactor_guard_constants.py +0 -77
- package/hooks/hooks_constants/test_refactor_guard_constants.py +0 -21
|
@@ -909,6 +909,26 @@ def test_should_keep_a_spent_mark_without_a_reset_for_one_hour(
|
|
|
909
909
|
assert decision["resets_at"] == (NOW + timedelta(hours=1)).isoformat()
|
|
910
910
|
|
|
911
911
|
|
|
912
|
+
def test_should_mark_a_windows_launcher_path_account_spent(
|
|
913
|
+
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
914
|
+
) -> None:
|
|
915
|
+
launcher_name = "C:\\Users\\someone\\.local\\bin\\claude-first.cmd"
|
|
916
|
+
first = _account(launcher_name)
|
|
917
|
+
second = _account("second")
|
|
918
|
+
monkeypatch.setitem(
|
|
919
|
+
account_broker.all_product_adapters,
|
|
920
|
+
Product.CODEX,
|
|
921
|
+
_adapter((first, second), {launcher_name: _meters(80, 80), "second": _meters(80, 80)}),
|
|
922
|
+
)
|
|
923
|
+
_freeze_clock(monkeypatch, NOW)
|
|
924
|
+
|
|
925
|
+
code = account_broker.main(("choose", "--product", "codex", "--spent", f"{launcher_name}:{(NOW + timedelta(hours=2)).timestamp()}"))
|
|
926
|
+
|
|
927
|
+
decision = json.loads(capsys.readouterr().out)["decision"]
|
|
928
|
+
assert code == 0
|
|
929
|
+
assert decision["account"] == "second"
|
|
930
|
+
|
|
931
|
+
|
|
912
932
|
def test_should_reject_an_invalid_spent_reset(
|
|
913
933
|
monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
914
934
|
) -> None:
|
|
@@ -1194,3 +1214,21 @@ def test_should_keep_the_parent_environment_for_codex_jobs(
|
|
|
1194
1214
|
run_job(Product.CODEX, ("codex", "exec", "task"))
|
|
1195
1215
|
|
|
1196
1216
|
assert all_child_environments[0]["CLAUDE_CODE_SESSION_ID"] == "parent-session"
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
def test_should_hand_the_live_log_to_the_subprocess_runner(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
1221
|
+
live_log = tmp_path / "events.jsonl"
|
|
1222
|
+
adapter = _adapter((_account("only"),), {"only": _meters(80, 80)})
|
|
1223
|
+
captured_options: dict[str, object] = {}
|
|
1224
|
+
|
|
1225
|
+
def runner(command: object, **options: object) -> subprocess.CompletedProcess[str]:
|
|
1226
|
+
captured_options.update(options)
|
|
1227
|
+
return subprocess.CompletedProcess(command, 0, "served", "")
|
|
1228
|
+
|
|
1229
|
+
monkeypatch.setitem(account_broker.all_product_adapters, Product.CODEX, adapter)
|
|
1230
|
+
with account_broker.override_subprocess_runner(runner):
|
|
1231
|
+
outcome, _ = account_broker._execute(Product.CODEX, ("job",), now=NOW, live_log=live_log)
|
|
1232
|
+
|
|
1233
|
+
assert outcome.returncode == 0
|
|
1234
|
+
assert captured_options["live_log"] == live_log
|
|
@@ -419,3 +419,27 @@ def test_should_pass_metacharacters_to_an_executable_unchanged(monkeypatch: pyte
|
|
|
419
419
|
support.subprocess_runner(["claude", "-p", 'say "a & b" | 100%!'])
|
|
420
420
|
|
|
421
421
|
assert all_launched_argv == [[resolved_command, "-p", 'say "a & b" | 100%!']]
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def test_should_write_child_output_into_the_live_log_while_it_runs(tmp_path: Path) -> None:
|
|
425
|
+
live_log = tmp_path / "worker.jsonl"
|
|
426
|
+
child_script = "import sys, pathlib; print('first', flush=True); sys.stdout.write(pathlib.Path(sys.argv[1]).read_text())"
|
|
427
|
+
|
|
428
|
+
completed = support._run_captured_subprocess([sys.executable, "-c", child_script, str(live_log)], live_log=live_log)
|
|
429
|
+
|
|
430
|
+
assert completed.stdout == "first\nfirst\n"
|
|
431
|
+
assert live_log.read_text(encoding="utf-8") == "first\nfirst\n"
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_should_match_order_names_to_an_account_named_by_its_windows_launcher_path() -> None:
|
|
435
|
+
launcher_account = Account(
|
|
436
|
+
Product.CLAUDE,
|
|
437
|
+
"C:\\Users\\someone\\.local\\bin\\claude-editor.cmd",
|
|
438
|
+
Path("/profiles/editor"),
|
|
439
|
+
False,
|
|
440
|
+
"C:\\Users\\someone\\.local\\bin\\claude-editor.cmd",
|
|
441
|
+
)
|
|
442
|
+
|
|
443
|
+
ranked = support._with_priorities((launcher_account,), ("claude-org-jon", "claude-editor"))
|
|
444
|
+
|
|
445
|
+
assert ranked[0].priority == 1
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"""Behavior tests for the agent image copy script."""
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import sys
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import pytest
|
|
8
|
+
from PIL import Image
|
|
9
|
+
|
|
10
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
11
|
+
|
|
12
|
+
import agent_image_copy
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_capped_size_should_keep_the_aspect_ratio() -> None:
|
|
16
|
+
assert agent_image_copy.capped_size(1440, 2560) == (288, 512)
|
|
17
|
+
assert agent_image_copy.capped_size(2000, 1000) == (512, 256)
|
|
18
|
+
assert agent_image_copy.capped_size(300, 200) == (300, 200)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def test_visual_tokens_should_count_28_pixel_patches() -> None:
|
|
22
|
+
assert agent_image_copy.visual_tokens(1440, 2560) == 4784
|
|
23
|
+
assert agent_image_copy.visual_tokens(288, 512) == 209
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def test_parse_crop_box_should_refuse_three_numbers() -> None:
|
|
27
|
+
assert agent_image_copy.parse_crop_box("0,0,10,20") == (0, 0, 10, 20)
|
|
28
|
+
with pytest.raises(argparse.ArgumentTypeError):
|
|
29
|
+
agent_image_copy.parse_crop_box("0,0,10")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_write_agent_copy_should_write_a_capped_newer_file(tmp_path: Path) -> None:
|
|
33
|
+
source_path = tmp_path / "preview.png"
|
|
34
|
+
Image.new("RGB", (1440, 2560), "white").save(source_path)
|
|
35
|
+
copy_path = agent_image_copy.write_agent_copy(source_path, None)
|
|
36
|
+
assert copy_path.name == "preview.agent.png"
|
|
37
|
+
with Image.open(copy_path) as copy_image:
|
|
38
|
+
assert copy_image.size == (288, 512)
|
|
39
|
+
assert copy_path.stat().st_mtime >= source_path.stat().st_mtime
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_write_agent_copy_should_crop_before_scaling(tmp_path: Path) -> None:
|
|
43
|
+
source_path = tmp_path / "preview.png"
|
|
44
|
+
Image.new("RGB", (1440, 2560), "white").save(source_path)
|
|
45
|
+
copy_path = agent_image_copy.write_agent_copy(source_path, (0, 0, 720, 640))
|
|
46
|
+
with Image.open(copy_path) as copy_image:
|
|
47
|
+
assert copy_image.size == (512, 455)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_write_agent_copy_should_write_a_cmyk_jpeg_as_rgb(tmp_path: Path) -> None:
|
|
51
|
+
source_path = tmp_path / "print.jpg"
|
|
52
|
+
Image.new("CMYK", (1000, 800)).save(source_path)
|
|
53
|
+
copy_path = agent_image_copy.write_agent_copy(source_path, None)
|
|
54
|
+
with Image.open(copy_path) as copy_image:
|
|
55
|
+
assert copy_image.mode == "RGB"
|
|
56
|
+
assert copy_image.size == (512, 410)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_write_agent_copy_should_follow_the_exif_orientation(tmp_path: Path) -> None:
|
|
60
|
+
source_path = tmp_path / "phone.jpg"
|
|
61
|
+
source_image = Image.new("RGB", (1000, 600), "white")
|
|
62
|
+
all_exif_tags = source_image.getexif()
|
|
63
|
+
all_exif_tags[0x0112] = 6
|
|
64
|
+
source_image.save(source_path, exif=all_exif_tags)
|
|
65
|
+
copy_path = agent_image_copy.write_agent_copy(source_path, None)
|
|
66
|
+
with Image.open(copy_path) as copy_image:
|
|
67
|
+
assert copy_image.size == (307, 512)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_main_should_print_the_copy_path(
|
|
71
|
+
tmp_path: Path, capsys: pytest.CaptureFixture[str]
|
|
72
|
+
) -> None:
|
|
73
|
+
source_path = tmp_path / "shot.png"
|
|
74
|
+
Image.new("RGB", (1600, 900), "white").save(source_path)
|
|
75
|
+
assert agent_image_copy.main([str(source_path)]) == 0
|
|
76
|
+
assert "shot.agent.png 512x288 about 209 tokens" in capsys.readouterr().out
|
|
@@ -45,6 +45,7 @@ def _run_worker(
|
|
|
45
45
|
*,
|
|
46
46
|
model: str | None = None,
|
|
47
47
|
permission_mode: str = "auto",
|
|
48
|
+
live_log: Path | None = None,
|
|
48
49
|
) -> tuple[int, dict[str, object], dict[str, object]]:
|
|
49
50
|
prompt_file = tmp_path / "brief.md"
|
|
50
51
|
prompt_file.write_text("standalone brief", encoding="utf-8")
|
|
@@ -65,6 +66,7 @@ def _run_worker(
|
|
|
65
66
|
model=model,
|
|
66
67
|
permission_mode=permission_mode,
|
|
67
68
|
timeout_minutes=60,
|
|
69
|
+
live_log=live_log,
|
|
68
70
|
)
|
|
69
71
|
return exit_code, json.loads(report_file.read_text(encoding="utf-8")), captured
|
|
70
72
|
|
|
@@ -88,6 +90,51 @@ def test_should_send_prompt_and_flags_to_broker(
|
|
|
88
90
|
assert captured["timeout_seconds"] == 3600
|
|
89
91
|
|
|
90
92
|
|
|
93
|
+
def test_should_stream_events_into_the_live_log_when_one_is_named(
|
|
94
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
95
|
+
) -> None:
|
|
96
|
+
live_log = tmp_path / "worker.jsonl"
|
|
97
|
+
|
|
98
|
+
exit_code, report, captured = _run_worker(monkeypatch, tmp_path, _outcome(), live_log=live_log)
|
|
99
|
+
|
|
100
|
+
assert exit_code == 0
|
|
101
|
+
assert report["result"] == "ready"
|
|
102
|
+
assert captured["argv"][:5] == ["claude", "-p", "--output-format", "stream-json", "--verbose"]
|
|
103
|
+
assert captured["live_log"] == live_log
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_should_pass_no_live_log_when_none_is_named(
|
|
107
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
108
|
+
) -> None:
|
|
109
|
+
_, _, captured = _run_worker(monkeypatch, tmp_path, _outcome())
|
|
110
|
+
|
|
111
|
+
assert "live_log" not in captured
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_should_report_an_unwritable_live_log_before_the_broker_runs(
|
|
115
|
+
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
116
|
+
) -> None:
|
|
117
|
+
prompt_file = tmp_path / "brief.md"
|
|
118
|
+
prompt_file.write_text("standalone brief", encoding="utf-8")
|
|
119
|
+
report_file = tmp_path / "report.json"
|
|
120
|
+
monkeypatch.setattr(worker, "worker_job_runner", lambda *args, **kwargs: pytest.fail("broker ran"))
|
|
121
|
+
|
|
122
|
+
exit_code = worker.run_worker(
|
|
123
|
+
prompt_file=prompt_file,
|
|
124
|
+
cwd=tmp_path,
|
|
125
|
+
report_file=report_file,
|
|
126
|
+
model=None,
|
|
127
|
+
permission_mode="auto",
|
|
128
|
+
timeout_minutes=60,
|
|
129
|
+
live_log=tmp_path / "missing" / "worker.jsonl",
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
report = json.loads(report_file.read_text(encoding="utf-8"))
|
|
133
|
+
assert exit_code == report["exit_code"] == 1
|
|
134
|
+
assert report["account"] == "none"
|
|
135
|
+
assert report["reason"] == "live log file is unwritable"
|
|
136
|
+
|
|
137
|
+
|
|
91
138
|
def test_should_report_the_broker_wait_reason_and_exit_three(
|
|
92
139
|
monkeypatch: pytest.MonkeyPatch, tmp_path: Path
|
|
93
140
|
) -> None:
|
|
@@ -45,3 +45,23 @@ def test_should_send_job_to_runner_and_return_outcome_with_elapsed_seconds(
|
|
|
45
45
|
},
|
|
46
46
|
)
|
|
47
47
|
]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_should_pass_the_live_log_to_the_runner_when_one_is_named(tmp_path: Path) -> None:
|
|
51
|
+
live_log = tmp_path / "events.jsonl"
|
|
52
|
+
captured_options: dict[str, object] = {}
|
|
53
|
+
|
|
54
|
+
def recording_runner(product: Product, argv: list[str], **options: object) -> JobOutcome:
|
|
55
|
+
captured_options.update(options)
|
|
56
|
+
return JobOutcome(0, "{}", "", "main", (), "served", None, None)
|
|
57
|
+
|
|
58
|
+
invoke_worker(
|
|
59
|
+
all_arguments=["claude", "-p"],
|
|
60
|
+
cwd=tmp_path,
|
|
61
|
+
prompt_text="brief",
|
|
62
|
+
timeout_minutes=1,
|
|
63
|
+
runner=recording_runner,
|
|
64
|
+
live_log=live_log,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
assert captured_options["live_log"] == live_log
|
|
@@ -51,6 +51,21 @@ def test_should_build_report_from_json_result_and_round_duration() -> None:
|
|
|
51
51
|
assert report.wait_reset_at is None
|
|
52
52
|
|
|
53
53
|
|
|
54
|
+
def test_should_take_the_result_event_from_streamed_output() -> None:
|
|
55
|
+
streamed_lines = "\n".join(
|
|
56
|
+
(
|
|
57
|
+
'{"type":"system","subtype":"init","session_id":"s1"}',
|
|
58
|
+
'{"type":"assistant","message":{"content":[]}}',
|
|
59
|
+
'{"type":"result","result":"ready","is_error":false}',
|
|
60
|
+
)
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
report = make_report("extra_2", "served", exit_code=0, duration_seconds=1.0, stdout_text=streamed_lines)
|
|
64
|
+
|
|
65
|
+
assert report.payload == "ready"
|
|
66
|
+
assert report.is_error is False
|
|
67
|
+
|
|
68
|
+
|
|
54
69
|
def test_should_mark_report_as_error_when_json_flags_error() -> None:
|
|
55
70
|
report = make_report(
|
|
56
71
|
"extra_2",
|
|
@@ -25,7 +25,7 @@ _HOOKS_CONFIGURATION = """
|
|
|
25
25
|
"""
|
|
26
26
|
_DISPATCHER_ROSTER_SOURCE = """
|
|
27
27
|
ALL_HOSTED_HOOK_ENTRIES = (
|
|
28
|
-
HostedHookEntry("advisory/
|
|
28
|
+
HostedHookEntry("advisory/migration_safety_advisor.py", is_blocking=False),
|
|
29
29
|
)
|
|
30
30
|
"""
|
|
31
31
|
|
|
@@ -40,7 +40,7 @@ def _build_package(repository_root: Path) -> None:
|
|
|
40
40
|
(package_root / "bin").mkdir(parents=True)
|
|
41
41
|
(package_root / "rules").mkdir(parents=True)
|
|
42
42
|
(hooks_root / "blocking" / "windows_rmtree_blocker.py").write_text("", "utf-8")
|
|
43
|
-
(hooks_root / "advisory" / "
|
|
43
|
+
(hooks_root / "advisory" / "migration_safety_advisor.py").write_text("", "utf-8")
|
|
44
44
|
(hooks_root / "observability" / "session_file_edit_tracker.py").write_text(
|
|
45
45
|
"", "utf-8"
|
|
46
46
|
)
|
|
@@ -96,7 +96,7 @@ def test_a_registered_hook_described_in_the_present_tense_passes(
|
|
|
96
96
|
_build_package(tmp_path)
|
|
97
97
|
all_messages = _diagnostics_for(
|
|
98
98
|
tmp_path,
|
|
99
|
-
"`session_file_edit_tracker` records each Write path, and `
|
|
99
|
+
"`session_file_edit_tracker` records each Write path, and `migration_safety_advisor` reports a risk.\n",
|
|
100
100
|
)
|
|
101
101
|
assert all_messages == ()
|
|
102
102
|
|
|
@@ -156,7 +156,7 @@ def test_a_second_hook_name_bounds_the_first_claim(tmp_path: Path) -> None:
|
|
|
156
156
|
_build_package(tmp_path)
|
|
157
157
|
all_messages = _diagnostics_for(
|
|
158
158
|
tmp_path,
|
|
159
|
-
"`destructive_command_blocker` was retired, and `
|
|
159
|
+
"`destructive_command_blocker` was retired, and `migration_safety_advisor` reports a risk.",
|
|
160
160
|
)
|
|
161
161
|
assert all_messages == ()
|
|
162
162
|
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
"""Pytest registration for shared refactor guard test support."""
|
|
2
|
-
|
|
3
|
-
import sys
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
|
|
6
|
-
advisory_directory = str(Path(__file__).resolve().parent)
|
|
7
|
-
if advisory_directory not in sys.path:
|
|
8
|
-
sys.path.insert(0, advisory_directory)
|
|
9
|
-
|
|
10
|
-
from refactor_guard_test_support import git_repository # noqa: E402, F401
|