claude-dev-env 8.10.0 → 8.11.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 (77) hide show
  1. package/_shared/pr-loop/scripts/code_rules_gate_parts/staged_test_regression.py +85 -3
  2. package/_shared/pr-loop/scripts/code_rules_gate_parts/tests/test_staged_test_regression.py +34 -0
  3. package/_shared/pr-loop/scripts/fix_pr_test_proof.py +132 -0
  4. package/_shared/pr-loop/scripts/pr_loop_shared_constants/fix_pr_test_proof_constants.py +43 -0
  5. package/_shared/pr-loop/scripts/test_fix_pr_test_proof.py +126 -0
  6. package/bin/codex-skill-load-block.mjs +45 -0
  7. package/bin/codex-skill-load-block.test.mjs +72 -0
  8. package/bin/install.agents-home.test.mjs +76 -1
  9. package/bin/install.mjs +53 -31
  10. package/bin/install.test.mjs +37 -0
  11. package/bin/install.transaction.test.mjs +29 -7
  12. package/bin/seed-codex-pstack-models.mjs +27 -0
  13. package/bin/seed-codex-pstack-models.test.mjs +72 -0
  14. package/docs/CODE_RULES.md +5 -1
  15. package/hooks/blocking/code_rules_enforcer.py +1 -3
  16. package/hooks/blocking/code_rules_magic_values.py +1 -3
  17. package/hooks/blocking/code_rules_paired_test.py +5 -6
  18. package/hooks/blocking/tdd_enforcer_parts/__init__.py +1 -1
  19. package/hooks/blocking/test_code_rules_enforcer_ephemeral.py +1 -46
  20. package/hooks/blocking/test_pre_tool_use_dispatcher.py +2 -40
  21. package/hooks/followup_ledger.py +8 -1
  22. package/hooks/git-hooks/test_installed_policy_lint.py +4 -2
  23. package/hooks/hooks.json +15 -0
  24. package/hooks/hooks_constants/followup_ledger_constants.py +4 -0
  25. package/hooks/hooks_constants/messages.py +0 -1
  26. package/hooks/hooks_constants/nested_project_hooks_constants.py +37 -0
  27. package/hooks/hooks_constants/test_messages.py +1 -7
  28. package/hooks/lifecycle/nested_project_hooks.py +395 -0
  29. package/hooks/lifecycle/test_nested_project_hooks.py +251 -0
  30. package/hooks/test_followup_ledger.py +18 -0
  31. package/package.json +2 -1
  32. package/pstack-codex-models.md +37 -0
  33. package/rules/bdd.md +1 -1
  34. package/rules/build-needs-a-user.md +53 -0
  35. package/rules/code-standards.md +1 -1
  36. package/rules/flag-non-breaking-findings.md +7 -0
  37. package/rules/paired-test-coverage.md +4 -2
  38. package/rules/testing.md +5 -1
  39. package/scripts/cde_lint.py +47 -1
  40. package/scripts/policy_lint/adapter_callers.py +124 -0
  41. package/scripts/policy_lint/adapter_pairing.py +4 -2
  42. package/scripts/policy_lint/adapter_support.py +4 -3
  43. package/scripts/policy_lint/adapters.py +15 -0
  44. package/scripts/policy_lint/check_catalog.py +24 -0
  45. package/scripts/policy_lint/config/approved_test_pairs.py +3 -2
  46. package/scripts/policy_lint/config/check_catalog_constants.py +13 -0
  47. package/scripts/policy_lint/config/constants.py +18 -0
  48. package/scripts/policy_lint/engine.py +9 -3
  49. package/scripts/policy_lint/model.py +6 -2
  50. package/scripts/policy_lint/registry.py +6 -0
  51. package/scripts/policy_lint/selection.py +2 -0
  52. package/scripts/tests/test_cde_lint.py +50 -0
  53. package/scripts/tests/test_policy_lint_rules_archive.py +16 -0
  54. package/scripts/tests/test_policy_lint_rules_callers.py +126 -0
  55. package/scripts/tests/test_policy_lint_rules_check_catalog.py +25 -1
  56. package/scripts/tests/test_policy_lint_rules_pairing.py +31 -2
  57. package/scripts/tests/test_policy_lint_selection.py +35 -0
  58. package/hooks/blocking/tdd_enforcer.py +0 -195
  59. package/hooks/blocking/tdd_enforcer_parts/candidate_paths.py +0 -142
  60. package/hooks/blocking/tdd_enforcer_parts/config/__init__.py +0 -1
  61. package/hooks/blocking/tdd_enforcer_parts/config/tdd_enforcer_constants.py +0 -49
  62. package/hooks/blocking/tdd_enforcer_parts/content_hash_store.py +0 -298
  63. package/hooks/blocking/tdd_enforcer_parts/decisions.py +0 -96
  64. package/hooks/blocking/tdd_enforcer_parts/freshness.py +0 -95
  65. package/hooks/blocking/tdd_enforcer_parts/git_tracking.py +0 -132
  66. package/hooks/blocking/tdd_enforcer_parts/path_classification.py +0 -119
  67. package/hooks/blocking/tdd_enforcer_parts/tests/test_candidate_paths.py +0 -31
  68. package/hooks/blocking/tdd_enforcer_parts/tests/test_content_hash_store.py +0 -256
  69. package/hooks/blocking/tdd_enforcer_parts/tests/test_decisions.py +0 -46
  70. package/hooks/blocking/tdd_enforcer_parts/tests/test_freshness.py +0 -28
  71. package/hooks/blocking/tdd_enforcer_parts/tests/test_git_tracking.py +0 -111
  72. package/hooks/blocking/tdd_enforcer_parts/tests/test_path_classification.py +0 -36
  73. package/hooks/blocking/test_tdd_enforcer.py +0 -1506
  74. package/hooks/blocking/test_tdd_enforcer_restore.py +0 -108
  75. package/hooks/blocking/test_tdd_enforcer_scratchpad.py +0 -105
  76. package/scripts/codex_compat_watcher.py +0 -502
  77. package/scripts/tests/test_codex_compat_watcher.py +0 -599
@@ -1,108 +0,0 @@
1
- """End-to-end tests for the absent-but-tracked restore exemption.
2
-
3
- Drives the real ``tdd_enforcer.py`` entry hook over a Write payload in a temp
4
- git repository, proving a remove-then-Write rewrite of committed code is allowed
5
- even when the paired test's freshness window has lapsed, while new untracked
6
- code and real edits to a present file stay gated.
7
- """
8
-
9
- import json
10
- import os
11
- import subprocess
12
- import sys
13
- from pathlib import Path
14
-
15
- SCRIPT_PATH = Path(__file__).parent / "tdd_enforcer.py"
16
- STALE_MTIME_OFFSET_SECONDS = 700
17
- DISABLE_EPHEMERAL_ENVIRONMENT = {"CLAUDE_CODE_RULES_DISABLE_EPHEMERAL_EXEMPT": "1"}
18
-
19
-
20
- def _run_hook(payload: dict) -> subprocess.CompletedProcess[str]:
21
- return subprocess.run(
22
- [sys.executable, str(SCRIPT_PATH)],
23
- input=json.dumps(payload),
24
- text=True,
25
- capture_output=True,
26
- check=False,
27
- env={**os.environ, **DISABLE_EPHEMERAL_ENVIRONMENT},
28
- )
29
-
30
-
31
- def _write_payload(file_path: Path, content: str) -> dict:
32
- return {"tool_name": "Write", "tool_input": {"file_path": str(file_path), "content": content}}
33
-
34
-
35
- def _decision_from(completed: subprocess.CompletedProcess[str]) -> str | None:
36
- if not completed.stdout:
37
- return None
38
- hook_output = json.loads(completed.stdout).get("hookSpecificOutput", {})
39
- return hook_output.get("permissionDecision")
40
-
41
-
42
- def _run_git(repository_root: Path, *arguments: str) -> None:
43
- subprocess.run(
44
- ["git", *arguments],
45
- cwd=str(repository_root),
46
- check=True,
47
- capture_output=True,
48
- env={**os.environ, "GIT_CONFIG_GLOBAL": os.devnull, "GIT_CONFIG_SYSTEM": os.devnull},
49
- )
50
-
51
-
52
- def _init_git_repository(repository_root: Path) -> None:
53
- _run_git(repository_root, "init", "-q")
54
- _run_git(repository_root, "config", "user.email", "test@example.com")
55
- _run_git(repository_root, "config", "user.name", "Test")
56
- _run_git(repository_root, "config", "commit.gpgsign", "false")
57
-
58
-
59
- def _commit_tracked_service(repository_root: Path) -> tuple[Path, Path]:
60
- production_file = repository_root / "service.py"
61
- production_file.write_text("def serve(): return 1\n")
62
- sibling_test = repository_root / "test_service.py"
63
- sibling_test.write_text("def test_serve(): pass\n")
64
- _run_git(repository_root, "add", "-A")
65
- _run_git(repository_root, "commit", "-q", "-m", "init")
66
- return production_file, sibling_test
67
-
68
-
69
- def test_should_allow_restore_write_of_absent_but_tracked_file_despite_stale_window(
70
- tmp_path: Path,
71
- ) -> None:
72
- repository_root = tmp_path / "repo"
73
- repository_root.mkdir()
74
- _init_git_repository(repository_root)
75
- production_file, sibling_test = _commit_tracked_service(repository_root)
76
- stale_timestamp = os.stat(sibling_test).st_mtime - STALE_MTIME_OFFSET_SECONDS
77
- os.utime(sibling_test, (stale_timestamp, stale_timestamp))
78
- production_file.unlink()
79
-
80
- completed = _run_hook(_write_payload(production_file, "def serve(): return 2\n"))
81
-
82
- assert _decision_from(completed) == "allow"
83
-
84
-
85
- def test_should_still_deny_write_of_absent_untracked_file_without_fresh_test(
86
- tmp_path: Path,
87
- ) -> None:
88
- repository_root = tmp_path / "repo"
89
- repository_root.mkdir()
90
- _init_git_repository(repository_root)
91
- untracked_file = repository_root / "brand_new.py"
92
-
93
- completed = _run_hook(_write_payload(untracked_file, "def fresh(): return 1\n"))
94
-
95
- assert _decision_from(completed) == "deny"
96
-
97
-
98
- def test_should_still_deny_write_of_present_file_with_stale_test(tmp_path: Path) -> None:
99
- repository_root = tmp_path / "repo"
100
- repository_root.mkdir()
101
- _init_git_repository(repository_root)
102
- production_file, sibling_test = _commit_tracked_service(repository_root)
103
- stale_timestamp = os.stat(sibling_test).st_mtime - STALE_MTIME_OFFSET_SECONDS
104
- os.utime(sibling_test, (stale_timestamp, stale_timestamp))
105
-
106
- completed = _run_hook(_write_payload(production_file, "def serve(): return 2\n"))
107
-
108
- assert _decision_from(completed) == "deny"
@@ -1,105 +0,0 @@
1
- """Tests for the session-scratchpad exemption wired into tdd_enforcer.main."""
2
-
3
- import importlib.util
4
- import io
5
- import json
6
- import os
7
- import sys
8
- import tempfile
9
- from pathlib import Path
10
- from types import ModuleType
11
-
12
- import pytest
13
-
14
- TDD_ENFORCER_PATH = Path(__file__).parent / "tdd_enforcer.py"
15
- FIXED_USER_ID = 5150
16
- WORKING_DIRECTORY = "/home/user/project"
17
- SESSION_ID = "tdd-session-987"
18
- PRODUCTION_CONTENT = "def fulfill_order():\n return True\n"
19
-
20
-
21
- def _load_tdd_enforcer() -> ModuleType:
22
- module_spec = importlib.util.spec_from_file_location(
23
- "tdd_enforcer_scratchpad_under_test", TDD_ENFORCER_PATH
24
- )
25
- assert module_spec is not None and module_spec.loader is not None
26
- loaded_module = importlib.util.module_from_spec(module_spec)
27
- module_spec.loader.exec_module(loaded_module)
28
- return loaded_module
29
-
30
-
31
- _TDD_ENFORCER = _load_tdd_enforcer()
32
-
33
-
34
- def _install_scratchpad_signals(monkeypatch: pytest.MonkeyPatch, temporary_root: Path) -> Path:
35
- monkeypatch.setattr(os, "getuid", lambda: FIXED_USER_ID, raising=False)
36
- monkeypatch.setattr(tempfile, "gettempdir", lambda: str(temporary_root))
37
- mangled_working_directory = WORKING_DIRECTORY.replace("/", "-")
38
- scratchpad_directory = (
39
- temporary_root
40
- / f"claude-{FIXED_USER_ID}"
41
- / mangled_working_directory
42
- / SESSION_ID
43
- / "scratchpad"
44
- )
45
- scratchpad_directory.mkdir(parents=True)
46
- return scratchpad_directory
47
-
48
-
49
- def _write_payload(target: Path) -> dict[str, object]:
50
- return {
51
- "tool_name": "Write",
52
- "cwd": WORKING_DIRECTORY,
53
- "session_id": SESSION_ID,
54
- "tool_input": {"file_path": str(target), "content": PRODUCTION_CONTENT},
55
- }
56
-
57
-
58
- def _run_main(
59
- monkeypatch: pytest.MonkeyPatch, payload: dict[str, object]
60
- ) -> tuple[int | None, str]:
61
- monkeypatch.setattr(sys, "stdin", io.StringIO(json.dumps(payload)))
62
- captured_stdout = io.StringIO()
63
- monkeypatch.setattr(sys, "stdout", captured_stdout)
64
- exit_code: int | None = None
65
- try:
66
- _TDD_ENFORCER.main()
67
- except SystemExit as raised_exit:
68
- raw_code = raised_exit.code
69
- exit_code = raw_code if isinstance(raw_code, int) else None
70
- return exit_code, captured_stdout.getvalue()
71
-
72
-
73
- def _decision_from(stdout_text: str) -> str | None:
74
- if not stdout_text.strip():
75
- return None
76
- parsed = json.loads(stdout_text)
77
- return parsed.get("hookSpecificOutput", {}).get("permissionDecision")
78
-
79
-
80
- def test_scratchpad_production_write_is_exempt_from_tdd_gate(
81
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
82
- ) -> None:
83
- scratchpad_directory = _install_scratchpad_signals(monkeypatch, tmp_path)
84
- monkeypatch.setenv("CLAUDE_CODE_RULES_DISABLE_EPHEMERAL_EXEMPT", "1")
85
- throwaway_script = scratchpad_directory / "one_off_probe.py"
86
-
87
- exit_code, stdout_text = _run_main(monkeypatch, _write_payload(throwaway_script))
88
-
89
- assert exit_code == 0
90
- assert _decision_from(stdout_text) != "deny"
91
-
92
-
93
- def test_identical_non_scratchpad_write_is_still_blocked(
94
- tmp_path: Path, monkeypatch: pytest.MonkeyPatch
95
- ) -> None:
96
- _install_scratchpad_signals(monkeypatch, tmp_path)
97
- monkeypatch.setenv("CLAUDE_CODE_RULES_DISABLE_EPHEMERAL_EXEMPT", "1")
98
- outside_directory = tmp_path / "project" / "orders"
99
- outside_directory.mkdir(parents=True)
100
- production_module = outside_directory / "one_off_probe.py"
101
-
102
- exit_code, stdout_text = _run_main(monkeypatch, _write_payload(production_module))
103
-
104
- assert exit_code == 0
105
- assert _decision_from(stdout_text) == "deny"