claude-dev-env 8.7.0 → 8.9.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 (51) hide show
  1. package/.agents/skills/privacy-hygiene/SKILL.md +1 -0
  2. package/.agents/skills/pull-request/scripts/pull_request.py +1 -0
  3. package/.agents/skills/pull-request/scripts/test_pull_request.py +2 -0
  4. package/.agents/skills/team-advisor/SKILL.md +2 -2
  5. package/_shared/advisor/reference/identity.md +1 -1
  6. package/_shared/advisor/reference/spawn-walk-log.md +1 -1
  7. package/_shared/advisor/reference/warm-up.md +3 -1
  8. package/_shared/advisor/scripts/config/advisor_scripts_constants/model_tier_run_validator_constants.py +15 -0
  9. package/_shared/advisor/scripts/model_tier_run_validator.py +20 -3
  10. package/_shared/advisor/scripts/tests/test_model_tier_run_validator.py +50 -6
  11. package/docs/references/advisor-tool.md +3 -2
  12. package/docs/second-claude-account.md +57 -0
  13. package/hooks/blocking/test_pii_scanner.py +8 -0
  14. package/hooks/hooks.json +25 -0
  15. package/hooks/hooks_constants/pii_prevention_constants.py +1 -0
  16. package/hooks/hooks_constants/skill_loaded_reminder_constants.py +58 -0
  17. package/hooks/session/skill_loaded_reminder.py +199 -0
  18. package/hooks/session/test_skill_loaded_reminder.py +206 -0
  19. package/package.json +1 -1
  20. package/rules/durable-post-artifacts.md +26 -1
  21. package/rules/long-horizon-autonomy.md +11 -0
  22. package/rules/prefer-existing-tools.md +35 -0
  23. package/rules/review-closure-is-a-check.md +1 -1
  24. package/scripts/claude_account_choice.py +291 -0
  25. package/scripts/claude_account_profile.py +371 -0
  26. package/scripts/claude_chain_usage.py +49 -5
  27. package/scripts/dev_env_scripts_constants/claude_account_constants.py +181 -0
  28. package/scripts/dev_env_scripts_constants/gh_artifact_upload_constants.py +27 -0
  29. package/scripts/dev_env_scripts_constants/private_term_constants.py +60 -0
  30. package/scripts/dev_env_scripts_constants/test_claude_account_constants.py +46 -0
  31. package/scripts/dev_env_scripts_constants/test_private_term_constants.py +29 -0
  32. package/scripts/durable_post_lint.py +47 -1
  33. package/scripts/durable_post_lint_config/config/constants.py +1 -0
  34. package/scripts/gh_artifact_upload.py +56 -3
  35. package/scripts/private_term_scan.py +135 -0
  36. package/scripts/private_terms.py +87 -0
  37. package/scripts/repository_checks/config/constants.py +3 -0
  38. package/scripts/repository_checks/runner.py +7 -0
  39. package/scripts/repository_checks/tracked_private_terms.py +57 -0
  40. package/scripts/test_claude_account_choice.py +245 -0
  41. package/scripts/test_claude_account_profile.py +220 -0
  42. package/scripts/test_claude_chain_usage.py +40 -0
  43. package/scripts/test_durable_post_lint.py +90 -0
  44. package/scripts/tests/test_gh_artifact_upload.py +75 -0
  45. package/scripts/tests/test_match_exemptions.py +2 -2
  46. package/scripts/tests/test_private_term_scan.py +176 -0
  47. package/scripts/tests/test_private_terms.py +59 -0
  48. package/scripts/tests/test_repository_policy.py +2 -0
  49. package/scripts/tests/test_runner.py +20 -0
  50. package/scripts/tests/test_tracked_private_terms.py +71 -0
  51. package/scripts/tests/test_tracked_secrets.py +1 -1
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ import shutil
6
7
  import subprocess
7
8
  import sys
8
9
  import types
@@ -26,6 +27,15 @@ _DEFAULT_READBACK_ASSET = {
26
27
  }
27
28
 
28
29
 
30
+ _REAL_WHICH = shutil.which
31
+ _REAL_RUN = subprocess.run
32
+
33
+
34
+ @pytest.fixture(autouse=True)
35
+ def oxipng_on_path(monkeypatch: pytest.MonkeyPatch) -> None:
36
+ monkeypatch.setattr(shutil, "which", lambda _name: "oxipng")
37
+
38
+
29
39
  def _make_gh_stub(
30
40
  recorded_calls: list[list[str]],
31
41
  view_return_code: int,
@@ -203,3 +213,68 @@ def test_upload_artifact_missing_file_raises(monkeypatch: pytest.MonkeyPatch) ->
203
213
  monkeypatch.setattr(subprocess, "run", _make_gh_stub([], view_return_code=0))
204
214
  with pytest.raises(mod.ArtifactUploadError):
205
215
  mod.upload_artifact("does_not_exist_9f3a.png", "owner/repo")
216
+
217
+
218
+ def test_upload_artifact_sends_the_oxipng_shrunk_png(
219
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
220
+ ) -> None:
221
+ image_module = pytest.importorskip("PIL.Image")
222
+ if _REAL_WHICH("oxipng") is None:
223
+ pytest.skip("oxipng is not on PATH")
224
+ monkeypatch.setattr(shutil, "which", _REAL_WHICH)
225
+ source_file = tmp_path / "contact_sheet.png"
226
+ image_module.new("RGB", (64, 64), (200, 40, 40)).save(
227
+ source_file, format="PNG", compress_level=0
228
+ )
229
+ source_bytes = source_file.read_bytes()
230
+ all_uploaded_sizes: list[int] = []
231
+ gh_stub = _make_gh_stub([], view_return_code=0)
232
+
233
+ def gh_stub_beside_live_oxipng(
234
+ all_command_arguments: list[str], **keyword_arguments: object
235
+ ) -> object:
236
+ if all_command_arguments[0] != "gh":
237
+ return _REAL_RUN(all_command_arguments, **keyword_arguments)
238
+ if "upload" in all_command_arguments:
239
+ staged_path = Path(all_command_arguments[4])
240
+ all_uploaded_sizes.append(staged_path.stat().st_size)
241
+ with image_module.open(staged_path) as uploaded:
242
+ assert uploaded.convert("RGB").tobytes() == bytes((200, 40, 40)) * 4096
243
+ return gh_stub(all_command_arguments, **keyword_arguments)
244
+
245
+ monkeypatch.setattr(subprocess, "run", gh_stub_beside_live_oxipng)
246
+
247
+ mod.upload_artifact(str(source_file), "owner/repo")
248
+
249
+ assert all_uploaded_sizes and all_uploaded_sizes[0] < len(source_bytes)
250
+ assert source_file.read_bytes() == source_bytes
251
+
252
+
253
+ def test_upload_artifact_refuses_a_png_when_oxipng_is_missing(
254
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
255
+ ) -> None:
256
+ source_file = tmp_path / "out.png"
257
+ source_file.write_bytes(b"binary")
258
+ recorded_calls: list[list[str]] = []
259
+ monkeypatch.setattr(shutil, "which", lambda _name: None)
260
+ monkeypatch.setattr(subprocess, "run", _make_gh_stub(recorded_calls, view_return_code=0))
261
+
262
+ with pytest.raises(mod.ArtifactUploadError, match="oxipng"):
263
+ mod.upload_artifact(str(source_file), "owner/repo")
264
+
265
+ assert not any("upload" in call for call in recorded_calls)
266
+
267
+
268
+ def test_optimize_png_losslessly_leaves_other_formats_alone(
269
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
270
+ ) -> None:
271
+ source_file = tmp_path / "trace.zip"
272
+ source_file.write_bytes(b"PK archive")
273
+ recorded_calls: list[list[str]] = []
274
+ monkeypatch.setattr(shutil, "which", lambda _name: None)
275
+ monkeypatch.setattr(subprocess, "run", _make_gh_stub(recorded_calls, view_return_code=0))
276
+
277
+ mod.optimize_png_losslessly(source_file)
278
+
279
+ assert source_file.read_bytes() == b"PK archive"
280
+ assert recorded_calls == []
@@ -21,8 +21,8 @@ from repository_checks.tracked_secrets import collect_tracked_secret_findings
21
21
  CONTACT = "fixture-contact@company.io"
22
22
  CHANGED_CONTACT = "changed-contact@company.io"
23
23
  DIGEST = hashlib.sha256(CONTACT.encode()).hexdigest()
24
- RUNNER_ADDRESS = "10.83.21.14"
25
- CHANGED_RUNNER_ADDRESS = "10.83.21.15"
24
+ RUNNER_ADDRESS = "10.0.0.14"
25
+ CHANGED_RUNNER_ADDRESS = "10.0.0.15"
26
26
  RUNNER_ADDRESS_DIGEST = hashlib.sha256(RUNNER_ADDRESS.encode()).hexdigest()
27
27
 
28
28
 
@@ -0,0 +1,176 @@
1
+ """Behavior tests for the GitHub event private-term scan."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import subprocess
8
+ import sys
9
+ from pathlib import Path
10
+
11
+ import pytest
12
+ from dev_env_scripts_constants.private_term_constants import PrivateTermDigest
13
+
14
+ _SCRIPTS_DIRECTORY = Path(__file__).resolve().parents[1]
15
+ if str(_SCRIPTS_DIRECTORY) not in sys.path:
16
+ sys.path.insert(0, str(_SCRIPTS_DIRECTORY))
17
+
18
+ import private_term_scan
19
+
20
+ _FIXTURE_TERM = "acmewidget"
21
+ _CLEAN_IDENTITY = ("Claude", "noreply@example.com")
22
+
23
+
24
+ @pytest.fixture(autouse=True)
25
+ def private_fixture_terms(monkeypatch: pytest.MonkeyPatch) -> None:
26
+ monkeypatch.setattr(
27
+ private_term_scan,
28
+ "ALL_PRIVATE_TERM_DIGESTS",
29
+ frozenset(
30
+ {
31
+ PrivateTermDigest(
32
+ length=len(_FIXTURE_TERM),
33
+ sha256=hashlib.sha256(_FIXTURE_TERM.encode("utf-8")).hexdigest(),
34
+ )
35
+ }
36
+ ),
37
+ )
38
+
39
+
40
+ def _write_event(tmp_path: Path, event: dict[str, object]) -> Path:
41
+ event_path = tmp_path / "event.json"
42
+ event_path.write_text(json.dumps(event), encoding="utf-8")
43
+ return event_path
44
+
45
+
46
+ def _git(repository_root: Path, *all_arguments: str, name: str, email: str) -> str:
47
+ completed = subprocess.run(
48
+ ["git", "-C", str(repository_root), *all_arguments],
49
+ check=True,
50
+ capture_output=True,
51
+ text=True,
52
+ env={
53
+ "GIT_AUTHOR_NAME": name,
54
+ "GIT_AUTHOR_EMAIL": email,
55
+ "GIT_COMMITTER_NAME": name,
56
+ "GIT_COMMITTER_EMAIL": email,
57
+ "GIT_CONFIG_GLOBAL": "/dev/null",
58
+ "GIT_CONFIG_NOSYSTEM": "1",
59
+ "HOME": str(repository_root),
60
+ },
61
+ )
62
+ return completed.stdout.strip()
63
+
64
+
65
+ def _repository_with_commits(
66
+ tmp_path: Path, all_commits: list[tuple[str, str, str]]
67
+ ) -> tuple[Path, str, str]:
68
+ repository_root = tmp_path / "repo"
69
+ repository_root.mkdir()
70
+ _git(
71
+ repository_root, "init", "-q", name=_CLEAN_IDENTITY[0], email=_CLEAN_IDENTITY[1]
72
+ )
73
+ _git(
74
+ repository_root,
75
+ "commit",
76
+ "-q",
77
+ "--allow-empty",
78
+ "-m",
79
+ "base",
80
+ name=_CLEAN_IDENTITY[0],
81
+ email=_CLEAN_IDENTITY[1],
82
+ )
83
+ base = _git(repository_root, "rev-parse", "HEAD", name="x", email="x@example.com")
84
+ for each_message, each_name, each_email in all_commits:
85
+ _git(
86
+ repository_root,
87
+ "commit",
88
+ "-q",
89
+ "--allow-empty",
90
+ "-m",
91
+ each_message,
92
+ name=each_name,
93
+ email=each_email,
94
+ )
95
+ head = _git(repository_root, "rev-parse", "HEAD", name="x", email="x@example.com")
96
+ return repository_root, base, head
97
+
98
+
99
+ def test_should_flag_a_pull_request_body_line(tmp_path: Path) -> None:
100
+ event_path = _write_event(
101
+ tmp_path,
102
+ {
103
+ "pull_request": {
104
+ "title": "fix: one",
105
+ "body": "Why\nSee Acme-Widgets-Inc/x#1\n",
106
+ }
107
+ },
108
+ )
109
+ all_findings = private_term_scan.scan_event(event_path)
110
+ assert all_findings == [
111
+ "pull request body: Line 2 names a private organization. "
112
+ "Describe it in general terms and drop any link to it."
113
+ ]
114
+
115
+
116
+ def test_should_flag_a_pull_request_title(tmp_path: Path) -> None:
117
+ event_path = _write_event(
118
+ tmp_path, {"pull_request": {"title": "fix: acme widget upload", "body": None}}
119
+ )
120
+ assert private_term_scan.scan_event(event_path)[0].startswith("pull request title:")
121
+
122
+
123
+ @pytest.mark.parametrize(
124
+ ("event", "label"),
125
+ [
126
+ ({"comment": {"body": "by acme widgets"}}, "comment body"),
127
+ ({"review": {"body": "by acme widgets"}}, "review body"),
128
+ ({"issue": {"title": "clean", "body": "by acme widgets"}}, "issue body"),
129
+ ({"release": {"name": "v1", "body": "by acme widgets"}}, "release body"),
130
+ ],
131
+ )
132
+ def test_should_flag_each_event_text(
133
+ tmp_path: Path, event: dict[str, object], label: str
134
+ ) -> None:
135
+ all_findings = private_term_scan.scan_event(_write_event(tmp_path, event))
136
+ assert len(all_findings) == 1
137
+ assert all_findings[0].startswith(f"{label}: Line 1")
138
+
139
+
140
+ def test_should_pass_a_clean_event(tmp_path: Path) -> None:
141
+ event_path = _write_event(
142
+ tmp_path,
143
+ {"comment": {"body": "Looks good."}, "issue": {"title": "t", "body": None}},
144
+ )
145
+ assert private_term_scan.scan_event(event_path) == []
146
+
147
+
148
+ def test_should_flag_a_commit_message_and_a_commit_identity(tmp_path: Path) -> None:
149
+ repository_root, base, head = _repository_with_commits(
150
+ tmp_path,
151
+ [
152
+ ("fix: clean change", "Pat", "pat@acmewidgets.example.com"),
153
+ ("docs: note\n\nWritten for Acme Widgets.", *_CLEAN_IDENTITY),
154
+ ("chore: clean", *_CLEAN_IDENTITY),
155
+ ],
156
+ )
157
+ all_findings = private_term_scan.scan_commits(repository_root, f"{base}..{head}")
158
+ assert len(all_findings) == 2
159
+ assert all_findings[0].startswith("commit ")
160
+ assert " identity:" in all_findings[0]
161
+ assert " message: Line 3 " in all_findings[1]
162
+ assert all("acme" not in each_finding.lower() for each_finding in all_findings)
163
+
164
+
165
+ def test_should_print_findings_and_fail_from_the_command_line(
166
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
167
+ ) -> None:
168
+ event_path = _write_event(tmp_path, {"comment": {"body": "by acme widgets"}})
169
+ exit_code = private_term_scan.main(["--event-path", str(event_path)])
170
+ assert exit_code == 1
171
+ assert "comment body: Line 1" in capsys.readouterr().out
172
+
173
+
174
+ def test_should_pass_from_the_command_line_on_a_clean_event(tmp_path: Path) -> None:
175
+ event_path = _write_event(tmp_path, {"comment": {"body": "fine"}})
176
+ assert private_term_scan.main(["--event-path", str(event_path)]) == 0
@@ -0,0 +1,59 @@
1
+ """Behavior tests for the hashed private-term matcher."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ _SCRIPTS_DIRECTORY = Path(__file__).resolve().parents[1]
10
+ if str(_SCRIPTS_DIRECTORY) not in sys.path:
11
+ sys.path.insert(0, str(_SCRIPTS_DIRECTORY))
12
+
13
+ from dev_env_scripts_constants.private_term_constants import PrivateTermDigest
14
+ from private_terms import names_private_term, private_term_line_numbers
15
+
16
+ _FIXTURE_TERM = "acmewidget"
17
+ _ALL_FIXTURE_DIGESTS = frozenset(
18
+ {
19
+ PrivateTermDigest(
20
+ length=len(_FIXTURE_TERM),
21
+ sha256=hashlib.sha256(_FIXTURE_TERM.encode("utf-8")).hexdigest(),
22
+ )
23
+ }
24
+ )
25
+
26
+
27
+ def test_should_report_the_line_of_a_spaced_spelling() -> None:
28
+ text = "first line\nBuilt for Acme Widgets, Inc. last year\nthird line\n"
29
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == (2,)
30
+
31
+
32
+ def test_should_report_a_hyphenated_owner_in_a_link() -> None:
33
+ text = "See https://github.com/Acme-Widgets-Inc/tools/pull/12 for detail."
34
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == (1,)
35
+
36
+
37
+ def test_should_report_an_email_domain() -> None:
38
+ text = "Co-authored-by: Pat <pat@ACMEWIDGETS.EXAMPLE.COM>"
39
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == (1,)
40
+
41
+
42
+ def test_should_report_a_term_split_across_a_line_break_at_its_first_line() -> None:
43
+ text = "one\nsold by Acme\nWidgets today\n"
44
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == (2,)
45
+
46
+
47
+ def test_should_report_each_line_once() -> None:
48
+ text = "acme widget and acme-widget\nclean\nAcmeWidget\n"
49
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == (1, 3)
50
+
51
+
52
+ def test_should_report_nothing_for_clean_text() -> None:
53
+ text = "An acme of widgets.\nWidget acme.\n"
54
+ assert private_term_line_numbers(text, _ALL_FIXTURE_DIGESTS) == ()
55
+ assert not names_private_term(text, _ALL_FIXTURE_DIGESTS)
56
+
57
+
58
+ def test_should_answer_whether_text_names_a_term() -> None:
59
+ assert names_private_term("Acme-Widgets-Inc", _ALL_FIXTURE_DIGESTS)
@@ -20,6 +20,7 @@ from repository_checks.config.constants import (
20
20
  CHECK_ID_PACKAGE_INVENTORY,
21
21
  CHECK_ID_PYTEST_TESTPATHS,
22
22
  CHECK_ID_TRACKED_PERSONAL_DATA,
23
+ CHECK_ID_TRACKED_PRIVATE_TERMS,
23
24
  FOLLOWUP_LEDGER_MODULE_NAME,
24
25
  SUCCESS_EXIT_CODE,
25
26
  )
@@ -52,6 +53,7 @@ def test_should_keep_stable_check_identifiers() -> None:
52
53
  CHECK_ID_PACKAGE_INVENTORY,
53
54
  CHECK_ID_PYTEST_TESTPATHS,
54
55
  CHECK_ID_TRACKED_PERSONAL_DATA,
56
+ CHECK_ID_TRACKED_PRIVATE_TERMS,
55
57
  )
56
58
 
57
59
 
@@ -12,6 +12,7 @@ if str(_TESTS_DIRECTORY) not in sys.path:
12
12
  sys.path.insert(0, str(_TESTS_DIRECTORY))
13
13
 
14
14
  from repository_checks.config.constants import (
15
+ CHECK_ID_TRACKED_PRIVATE_TERMS,
15
16
  FAILED_CHECK_EXIT_CODE,
16
17
  FINDINGS_EXIT_CODE,
17
18
  )
@@ -57,3 +58,22 @@ def test_should_emit_stable_sorted_findings(tmp_path: Path) -> None:
57
58
  assert first_exit_code == FINDINGS_EXIT_CODE
58
59
  assert first_stdout == second_stdout
59
60
  assert first_stdout.index("alpha/CLAUDE.md") < first_stdout.index("zeta/CLAUDE.md")
61
+
62
+
63
+ def test_should_fail_closed_when_the_private_term_check_cannot_read_a_file(
64
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
65
+ ) -> None:
66
+ repository_root = seed_clean_repository(tmp_path / "repo")
67
+
68
+ def fail_private_term_scan(
69
+ _repository_root: Path, _all_tracked_paths: object
70
+ ) -> list[object]:
71
+ raise OSError("tracked file unreadable")
72
+
73
+ monkeypatch.setattr(
74
+ "repository_checks.runner.collect_tracked_private_term_findings",
75
+ fail_private_term_scan,
76
+ )
77
+ exit_code, stdout_text, _stderr_text = run_policy(repository_root)
78
+ assert exit_code == FAILED_CHECK_EXIT_CODE
79
+ assert f"error: rule failed: {CHECK_ID_TRACKED_PRIVATE_TERMS}" in stdout_text
@@ -0,0 +1,71 @@
1
+ """Behavior tests for the tracked private-term check."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import sys
7
+ from pathlib import Path
8
+
9
+ import pytest
10
+ from dev_env_scripts_constants.private_term_constants import PrivateTermDigest
11
+ from repository_checks import tracked_private_terms
12
+ from repository_checks.config.constants import (
13
+ CHECK_ID_TRACKED_PRIVATE_TERMS,
14
+ FINDINGS_EXIT_CODE,
15
+ SUCCESS_EXIT_CODE,
16
+ )
17
+
18
+ _TESTS_DIRECTORY = Path(__file__).resolve().parent
19
+ if str(_TESTS_DIRECTORY) not in sys.path:
20
+ sys.path.insert(0, str(_TESTS_DIRECTORY))
21
+
22
+ from repository_policy_test_support import (
23
+ commit_tracked_files,
24
+ initialize_repository,
25
+ run_policy,
26
+ write_text,
27
+ )
28
+
29
+ _FIXTURE_TERM = "acmewidget"
30
+
31
+
32
+ @pytest.fixture(autouse=True)
33
+ def private_fixture_terms(monkeypatch: pytest.MonkeyPatch) -> None:
34
+ monkeypatch.setattr(
35
+ tracked_private_terms,
36
+ "ALL_PRIVATE_TERM_DIGESTS",
37
+ frozenset(
38
+ {
39
+ PrivateTermDigest(
40
+ length=len(_FIXTURE_TERM),
41
+ sha256=hashlib.sha256(_FIXTURE_TERM.encode("utf-8")).hexdigest(),
42
+ )
43
+ }
44
+ ),
45
+ )
46
+
47
+
48
+ def test_should_flag_a_tracked_file_naming_a_private_organization(
49
+ tmp_path: Path,
50
+ ) -> None:
51
+ repository_root = tmp_path / "repo"
52
+ initialize_repository(repository_root)
53
+ write_text(repository_root / "docs" / "notes.md", "intro\nBuilt by Acme Widgets.\n")
54
+ commit_tracked_files(repository_root)
55
+ exit_code, stdout_text, _stderr_text = run_policy(repository_root)
56
+ assert exit_code == FINDINGS_EXIT_CODE
57
+ assert f"{CHECK_ID_TRACKED_PRIVATE_TERMS}: docs/notes.md:" in stdout_text
58
+ assert "Line 2" in stdout_text
59
+ assert "Acme" not in stdout_text
60
+
61
+
62
+ def test_should_pass_a_tree_that_names_no_private_organization(
63
+ tmp_path: Path,
64
+ ) -> None:
65
+ repository_root = tmp_path / "repo"
66
+ initialize_repository(repository_root)
67
+ write_text(repository_root / "docs" / "notes.md", "An acme of widget design.\n")
68
+ commit_tracked_files(repository_root)
69
+ exit_code, stdout_text, _stderr_text = run_policy(repository_root)
70
+ assert exit_code == SUCCESS_EXIT_CODE
71
+ assert stdout_text == ""
@@ -46,7 +46,7 @@ _PII_PREVENTION_CONSTANTS = load_hooks_module(
46
46
  _PII_CATEGORY_EMAIL = _PII_PREVENTION_CONSTANTS.CATEGORY_EMAIL
47
47
  _PII_CATEGORY_HOME_PATH = _PII_PREVENTION_CONSTANTS.CATEGORY_HOME_PATH
48
48
  _PII_CATEGORY_PRIVATE_IP = _PII_PREVENTION_CONSTANTS.CATEGORY_PRIVATE_IP
49
- _OWNED_ADDRESS = "10.83.21.14"
49
+ _OWNED_ADDRESS = "10.0.0.14"
50
50
  _OWNED_ADDRESS_RELATIVE_PATH = "src/runner.py"
51
51
 
52
52