claude-dev-env 1.93.0 → 1.94.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/_shared/pr-loop/CLAUDE.md +1 -0
- package/_shared/pr-loop/audit-contract.md +1 -1
- package/_shared/pr-loop/gh-payloads.md +3 -3
- package/_shared/pr-loop/post-audit-thread-contract.md +51 -0
- package/_shared/pr-loop/scripts/README.md +2 -2
- package/_shared/pr-loop/scripts/code_rules_gate.py +127 -13
- package/_shared/pr-loop/scripts/fix_hookspath.py +47 -12
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/code_rules_gate_constants.py +4 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +146 -69
- package/_shared/pr-loop/scripts/tests/test_fix_hookspath.py +497 -374
- package/_shared/pr-loop/state-schema.md +3 -22
- package/bin/CLAUDE.md +1 -1
- package/bin/install.mjs +2 -7
- package/bin/install.test.mjs +10 -4
- package/hooks/blocking/CLAUDE.md +2 -0
- package/hooks/blocking/pii_commit_command.py +398 -0
- package/hooks/blocking/pii_payload_scan.py +133 -0
- package/hooks/blocking/pii_prevention_blocker.py +151 -449
- package/hooks/blocking/pii_scanner.py +26 -1
- package/hooks/blocking/test_code_rules_enforcer_dead_config_field.py +2 -2
- package/hooks/blocking/test_pii_prevention_blocker.py +299 -0
- package/hooks/blocking/test_pii_scanner.py +27 -2
- package/hooks/blocking/test_verdict_directory_write_blocker.py +42 -7
- package/hooks/hooks_constants/CLAUDE.md +2 -2
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +0 -2
- package/hooks/hooks_constants/local_identity.py +65 -15
- package/hooks/hooks_constants/pii_prevention_constants.py +20 -8
- package/hooks/hooks_constants/pr_description_enforcer_constants.py +1 -1
- package/hooks/hooks_constants/test_local_identity.py +68 -0
- package/package.json +1 -1
- package/scripts/CLAUDE.md +10 -1
- package/scripts/Get-SessionAccount.ps1 +210 -0
- package/scripts/tests/CLAUDE.md +15 -1
- package/scripts/tests/Get-SessionAccount.Tests.ps1 +350 -0
- package/skills/CLAUDE.md +3 -25
- package/skills/anthropic-plan/SKILL.md +1 -13
- package/skills/autoconverge/CLAUDE.md +5 -2
- package/skills/autoconverge/SKILL.md +112 -387
- package/skills/autoconverge/reference/CLAUDE.md +4 -1
- package/skills/autoconverge/reference/closing-report.md +9 -2
- package/skills/autoconverge/reference/convergence.md +26 -22
- package/skills/autoconverge/reference/headless-safety.md +44 -0
- package/skills/autoconverge/reference/multi-pr.md +88 -0
- package/skills/autoconverge/reference/self-closing-loop.md +84 -0
- package/skills/autoconverge/reference/stop-conditions.md +21 -13
- package/skills/autoconverge/workflow/converge.clean-audit.test.mjs +87 -27
- package/skills/autoconverge/workflow/converge.contract.test.mjs +40 -10
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +179 -47
- package/skills/autoconverge/workflow/converge.fix-progress.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.mjs +284 -94
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/CONSTRAINTS.md +3 -3
- package/skills/bugteam/PROMPTS.md +8 -19
- package/skills/bugteam/SKILL.md +13 -14
- package/skills/bugteam/reference/CLAUDE.md +1 -1
- package/skills/bugteam/reference/audit-and-teammates.md +2 -2
- package/skills/bugteam/reference/copilot-gap-analysis.md +2 -0
- package/skills/bugteam/reference/github-pr-reviews.md +56 -38
- package/skills/bugteam/reference/team-setup.md +11 -11
- package/skills/bugteam/scripts/CLAUDE.md +6 -6
- package/skills/bugteam/scripts/README.md +20 -8
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +13 -1463
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +23 -358
- package/skills/bugteam/scripts/bugteam_preflight.py +13 -315
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +2 -3
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +34 -1053
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +30 -347
- package/skills/bugteam/scripts/test_bugteam_preflight.py +30 -305
- package/skills/copilot-finding-triage/SKILL.md +80 -28
- package/skills/copilot-finding-triage/reference/tier-rubric.md +70 -11
- package/skills/copilot-finding-triage/templates/notification.md +8 -3
- package/skills/everything-search/SKILL.md +1 -2
- package/skills/grokify/SKILL.md +58 -0
- package/skills/grokify/templates/handoff-template.md +92 -0
- package/skills/orchestrator/SKILL.md +12 -8
- package/skills/orchestrator-refresh/SKILL.md +3 -3
- package/skills/pr-converge/SKILL.md +132 -233
- package/skills/pr-converge/reference/convergence-gates.md +102 -66
- package/skills/pr-converge/reference/examples.md +37 -25
- package/skills/pr-converge/reference/fix-protocol.md +7 -7
- package/skills/pr-converge/reference/multi-pr-orchestration.md +10 -4
- package/skills/pr-converge/reference/per-tick.md +64 -41
- package/skills/pr-converge/reference/state-schema.md +27 -2
- package/skills/pr-converge/scripts/CLAUDE.md +8 -0
- package/skills/pr-converge/scripts/README.md +4 -3
- package/skills/pr-converge/scripts/_pr_converge_path_setup.py +23 -0
- package/skills/pr-converge/scripts/check_convergence.py +189 -645
- package/skills/pr-converge/scripts/check_convergence_gates.py +235 -0
- package/skills/pr-converge/scripts/check_convergence_thread_gates.py +219 -0
- package/skills/pr-converge/scripts/conftest.py +8 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/CLAUDE.md +1 -0
- package/skills/pr-converge/scripts/pr_converge_scripts_constants/convergence_gate_constants.py +36 -0
- package/skills/pr-converge/scripts/test__pr_converge_path_setup.py +26 -0
- package/skills/pr-converge/scripts/test_check_convergence.py +73 -228
- package/skills/pr-converge/scripts/test_check_convergence_contract.py +321 -0
- package/skills/pr-converge/scripts/test_check_convergence_gates.py +68 -0
- package/skills/pr-converge/scripts/test_check_convergence_thread_gates.py +75 -0
- package/skills/privacy-hygiene/SKILL.md +8 -7
- package/skills/reviewer-gates/SKILL.md +26 -8
- package/skills/session-log/SKILL.md +11 -12
- package/skills/session-tidy/SKILL.md +40 -30
- package/skills/skill-builder/CLAUDE.md +13 -10
- package/skills/skill-builder/SKILL.md +31 -31
- package/skills/skill-builder/references/CLAUDE.md +10 -6
- package/skills/skill-builder/references/delegation-map.md +13 -4
- package/skills/skill-builder/references/description-field.md +113 -0
- package/skills/skill-builder/references/self-audit-checklist.md +21 -4
- package/skills/skill-builder/references/skill-modularity.md +113 -0
- package/skills/skill-builder/references/skill-types.md +4 -0
- package/skills/skill-builder/templates/CLAUDE.md +4 -4
- package/skills/skill-builder/templates/gap-analysis.md +25 -1
- package/skills/skill-builder/workflows/CLAUDE.md +6 -4
- package/skills/skill-builder/workflows/improve-skill.md +17 -16
- package/skills/skill-builder/workflows/new-skill.md +34 -10
- package/skills/skill-builder/workflows/polish-skill.md +59 -43
- package/skills/usage-pause/SKILL.md +10 -14
- package/skills/bdd-protocol/CLAUDE.md +0 -26
- package/skills/bdd-protocol/SKILL.md +0 -30
- package/skills/bdd-protocol/references/CLAUDE.md +0 -10
- package/skills/bdd-protocol/references/anti-patterns.md +0 -26
- package/skills/bdd-protocol/references/example-mapping.md +0 -23
- package/skills/bg-agent/CLAUDE.md +0 -17
- package/skills/bg-agent/SKILL.md +0 -69
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_code_rules_gate_constants.py +0 -55
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_fix_hookspath_constants.py +0 -26
- package/skills/bugteam/scripts/bugteam_scripts_constants/bugteam_preflight_constants.py +0 -35
- package/skills/caveman/CLAUDE.md +0 -15
- package/skills/caveman/SKILL.md +0 -38
- package/skills/code/CLAUDE.md +0 -17
- package/skills/code/SKILL.md +0 -178
- package/skills/deep-research/CLAUDE.md +0 -17
- package/skills/deep-research/SKILL.md +0 -80
- package/skills/findbugs/CLAUDE.md +0 -20
- package/skills/findbugs/SKILL.md +0 -241
- package/skills/fixbugs/CLAUDE.md +0 -19
- package/skills/fixbugs/SKILL.md +0 -142
- package/skills/gh-paginate/CLAUDE.md +0 -18
- package/skills/gh-paginate/SKILL.md +0 -84
- package/skills/gotcha/CLAUDE.md +0 -33
- package/skills/gotcha/SKILL.md +0 -73
- package/skills/implement/CLAUDE.md +0 -27
- package/skills/implement/SKILL.md +0 -66
- package/skills/implement/scripts/CLAUDE.md +0 -22
- package/skills/implement/scripts/append_note.py +0 -133
- package/skills/implement/scripts/implement_scripts_constants/CLAUDE.md +0 -22
- package/skills/implement/scripts/implement_scripts_constants/__init__.py +0 -0
- package/skills/implement/scripts/implement_scripts_constants/notes_constants.py +0 -12
- package/skills/implement/scripts/test_append_note.py +0 -191
- package/skills/log-audit/CLAUDE.md +0 -20
- package/skills/log-audit/SKILL.md +0 -66
- package/skills/log-audit/reference/CLAUDE.md +0 -9
- package/skills/log-audit/reference/charter.md +0 -52
- package/skills/log-audit/scripts/CLAUDE.md +0 -27
- package/skills/log-audit/scripts/cluster_recurrences.py +0 -261
- package/skills/log-audit/scripts/collect_log_window.py +0 -199
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +0 -12
- package/skills/log-audit/scripts/log_audit_constants/__init__.py +0 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +0 -23
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +0 -24
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +0 -49
- package/skills/log-audit/scripts/mine_copilot_findings.py +0 -302
- package/skills/log-audit/scripts/test_cluster_recurrences.py +0 -160
- package/skills/log-audit/scripts/test_collect_log_window.py +0 -111
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +0 -126
- package/skills/logifix/CLAUDE.md +0 -36
- package/skills/logifix/SKILL.md +0 -69
- package/skills/logifix/scripts/CLAUDE.md +0 -16
- package/skills/logifix/scripts/logifix.ps1 +0 -205
- package/skills/monitor-open-prs/CLAUDE.md +0 -34
- package/skills/monitor-open-prs/SKILL.md +0 -88
- package/skills/monitor-open-prs/scripts/CLAUDE.md +0 -17
- package/skills/monitor-open-prs/scripts/discover_open_prs.py +0 -69
- package/skills/monitor-open-prs/scripts/test_discover_open_prs.py +0 -149
- package/skills/monitor-open-prs/test_skill_contract.py +0 -27
- package/skills/post-audit-findings/SKILL.md +0 -80
- package/skills/pr-consistency-audit/CLAUDE.md +0 -34
- package/skills/pr-consistency-audit/SKILL.md +0 -116
- package/skills/pr-consistency-audit/reference/CLAUDE.md +0 -16
- package/skills/pr-consistency-audit/reference/detection-rules.md +0 -96
- package/skills/pr-consistency-audit/reference/illustrations.md +0 -78
- package/skills/pr-fix-protocol/SKILL.md +0 -65
- package/skills/pr-review-responder/CLAUDE.md +0 -35
- package/skills/pr-review-responder/EXAMPLES.md +0 -590
- package/skills/pr-review-responder/PRINCIPLES.md +0 -533
- package/skills/pr-review-responder/README.md +0 -168
- package/skills/pr-review-responder/SKILL.md +0 -203
- package/skills/pr-review-responder/TESTING.md +0 -350
- package/skills/pr-scope-resolve/SKILL.md +0 -45
- package/skills/pre-compact/CLAUDE.md +0 -24
- package/skills/pre-compact/SKILL.md +0 -134
- package/skills/qbug/CLAUDE.md +0 -40
- package/skills/qbug/SKILL.md +0 -387
- package/skills/qbug/test_qbug_skill_audit_schema.py +0 -152
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +0 -103
- package/skills/refine/CLAUDE.md +0 -44
- package/skills/refine/SKILL.md +0 -262
- package/skills/refine/templates/CLAUDE.md +0 -17
- package/skills/refine/templates/implementation-notes-template.html +0 -56
- package/skills/refine/templates/plan-template.md +0 -60
- package/skills/research-mode/CLAUDE.md +0 -35
- package/skills/research-mode/SKILL.md +0 -53
- package/skills/structure-prompt/CLAUDE.md +0 -42
- package/skills/structure-prompt/SKILL.md +0 -44
- package/skills/structure-prompt/reference/CLAUDE.md +0 -28
- package/skills/structure-prompt/reference/adversarial-tuning.md +0 -62
- package/skills/structure-prompt/reference/block-classification.md +0 -27
- package/skills/structure-prompt/reference/canonical-case.md +0 -48
- package/skills/structure-prompt/reference/citation-depth.md +0 -70
- package/skills/structure-prompt/reference/cleanup.md +0 -33
- package/skills/structure-prompt/reference/constraints.md +0 -33
- package/skills/structure-prompt/reference/directives.md +0 -37
- package/skills/structure-prompt/reference/examples.md +0 -72
- package/skills/structure-prompt/reference/instantiation.md +0 -51
- package/skills/structure-prompt/reference/output-contract.md +0 -72
- package/skills/structure-prompt/reference/per-category.md +0 -23
- package/skills/structure-prompt/reference/persona.md +0 -38
- package/skills/structure-prompt/reference/research.md +0 -33
- package/skills/structure-prompt/reference/structure.md +0 -28
- package/skills/verified-build/CLAUDE.md +0 -33
- package/skills/verified-build/SKILL.md +0 -33
|
@@ -1,316 +1,41 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Smoke tests for the skill-path preflight wrap.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
-
|
|
5
|
-
- core.hooksPath pointing to the correct claude hooks dir: exits zero
|
|
6
|
-
- core.hooksPath pointing elsewhere (husky override): exits non-zero
|
|
7
|
-
- core.hooksPath with trailing slash: must still pass after normalization
|
|
3
|
+
Behavioral coverage lives at
|
|
4
|
+
``_shared/pr-loop/scripts/tests/test_preflight.py``.
|
|
8
5
|
"""
|
|
9
6
|
|
|
10
7
|
from __future__ import annotations
|
|
11
8
|
|
|
12
9
|
import importlib.util
|
|
13
|
-
import subprocess
|
|
14
|
-
import sys
|
|
15
10
|
from pathlib import Path
|
|
16
11
|
from types import ModuleType
|
|
17
|
-
from unittest.mock import MagicMock, patch
|
|
18
12
|
|
|
19
|
-
import pytest
|
|
20
13
|
|
|
21
|
-
|
|
22
|
-
def _load_preflight_module() -> ModuleType:
|
|
14
|
+
def _load_wrap_module() -> ModuleType:
|
|
23
15
|
module_path = Path(__file__).parent / "bugteam_preflight.py"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
def
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
assert "core.hooksPath" in captured.err
|
|
51
|
-
assert "npx claude-dev-env" in captured.err or "git config" in captured.err
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
def test_should_exit_zero_when_core_hooks_path_points_to_claude_hooks(tmp_path: Path) -> None:
|
|
55
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
56
|
-
claude_hooks_path.mkdir(parents=True)
|
|
57
|
-
with patch("subprocess.run") as mock_run:
|
|
58
|
-
mock_run.return_value = _make_completed_process(
|
|
59
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
60
|
-
)
|
|
61
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
62
|
-
assert exit_code == 0
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
def test_should_exit_nonzero_when_core_hooks_path_points_elsewhere(capsys: pytest.CaptureFixture[str]) -> None:
|
|
66
|
-
with patch("subprocess.run") as mock_run:
|
|
67
|
-
mock_run.return_value = _make_completed_process(
|
|
68
|
-
"/some/other/path/.husky\n", returncode=0
|
|
69
|
-
)
|
|
70
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
71
|
-
assert exit_code != 0
|
|
72
|
-
captured = capsys.readouterr()
|
|
73
|
-
assert "core.hooksPath" in captured.err
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def test_should_include_correction_commands_in_error_message(capsys: pytest.CaptureFixture[str]) -> None:
|
|
77
|
-
with patch("subprocess.run") as mock_run:
|
|
78
|
-
mock_run.return_value = _make_completed_process("", returncode=1)
|
|
79
|
-
bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
80
|
-
captured = capsys.readouterr()
|
|
81
|
-
assert (
|
|
82
|
-
"npx claude-dev-env" in captured.err
|
|
83
|
-
or "git config --global core.hooksPath" in captured.err
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def test_main_should_exit_nonzero_when_hooks_path_unset() -> None:
|
|
88
|
-
with patch("subprocess.run") as mock_run:
|
|
89
|
-
mock_run.return_value = _make_completed_process("", returncode=1)
|
|
90
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
91
|
-
assert exit_code != 0
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_main_should_continue_when_hooks_path_valid(tmp_path: Path) -> None:
|
|
95
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
96
|
-
claude_hooks_path.mkdir(parents=True)
|
|
97
|
-
with patch("subprocess.run") as mock_run:
|
|
98
|
-
mock_run.return_value = _make_completed_process(
|
|
99
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
100
|
-
)
|
|
101
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
102
|
-
assert exit_code == 0
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def test_should_accept_hooks_path_with_trailing_slash() -> None:
|
|
106
|
-
with patch("subprocess.run") as mock_run:
|
|
107
|
-
mock_run.return_value = _make_completed_process(
|
|
108
|
-
"/home/user/.claude/hooks/git-hooks/\n", returncode=0
|
|
109
|
-
)
|
|
110
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
111
|
-
assert exit_code == 0, (
|
|
112
|
-
"hooksPath with trailing slash must pass verification after normalization"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
def test_should_exit_zero_when_hooks_path_set_at_repo_scope(tmp_path: Path) -> None:
|
|
117
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
118
|
-
claude_hooks_path.mkdir(parents=True)
|
|
119
|
-
repo_root = tmp_path / "my-repo"
|
|
120
|
-
repo_root.mkdir()
|
|
121
|
-
with patch("subprocess.run") as mock_run:
|
|
122
|
-
mock_run.return_value = _make_completed_process(
|
|
123
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
124
|
-
)
|
|
125
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(repo_root)
|
|
126
|
-
assert exit_code == 0, (
|
|
127
|
-
"verify_git_hooks_path must accept a valid path returned by effective "
|
|
128
|
-
"config query (not restricted to --global scope)"
|
|
129
|
-
)
|
|
130
|
-
called_command = mock_run.call_args[0][0]
|
|
131
|
-
assert "--global" not in called_command, (
|
|
132
|
-
"verify_git_hooks_path must query effective config, not --global only"
|
|
133
|
-
)
|
|
134
|
-
assert "-C" in called_command, (
|
|
135
|
-
"verify_git_hooks_path must use git -C <repo_root> for repo-effective config"
|
|
136
|
-
)
|
|
137
|
-
dash_c_index = called_command.index("-C")
|
|
138
|
-
assert called_command[dash_c_index + 1] == str(repo_root), (
|
|
139
|
-
"git -C must receive the resolved repository root path"
|
|
140
|
-
)
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
def test_verify_git_hooks_path_accepts_none_repository_root(tmp_path: Path) -> None:
|
|
144
|
-
"""When repository_root is None, the call must use git's cwd-effective config.
|
|
145
|
-
|
|
146
|
-
Binds the documented optional contract: passing None must not raise and must
|
|
147
|
-
omit the `-C <root>` arguments so git falls back to the working directory.
|
|
148
|
-
"""
|
|
149
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
150
|
-
claude_hooks_path.mkdir(parents=True)
|
|
151
|
-
with patch("subprocess.run") as mock_run:
|
|
152
|
-
mock_run.return_value = _make_completed_process(
|
|
153
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
154
|
-
)
|
|
155
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(None)
|
|
156
|
-
assert exit_code == 0
|
|
157
|
-
called_command = mock_run.call_args[0][0]
|
|
158
|
-
assert "-C" not in called_command, (
|
|
159
|
-
"verify_git_hooks_path(None) must omit -C so git uses cwd-effective config"
|
|
160
|
-
)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
def test_should_accept_hooks_path_with_backslash_and_trailing_slash() -> None:
|
|
164
|
-
with patch("subprocess.run") as mock_run:
|
|
165
|
-
mock_run.return_value = _make_completed_process(
|
|
166
|
-
"C:\\Users\\user\\.claude\\hooks\\git-hooks\\\n", returncode=0
|
|
167
|
-
)
|
|
168
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
169
|
-
assert exit_code == 0, (
|
|
170
|
-
"Windows hooksPath with trailing backslash must pass after normalization"
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
def test_should_exit_nonzero_when_git_executable_not_found(
|
|
175
|
-
capsys: pytest.CaptureFixture[str],
|
|
176
|
-
) -> None:
|
|
177
|
-
"""Preflight must not crash with a traceback when git is missing from PATH."""
|
|
178
|
-
with patch("subprocess.run", side_effect=FileNotFoundError()):
|
|
179
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
180
|
-
assert exit_code != 0, (
|
|
181
|
-
"FileNotFoundError from subprocess.run must produce a non-zero exit, "
|
|
182
|
-
"not a propagated traceback"
|
|
183
|
-
)
|
|
184
|
-
captured = capsys.readouterr()
|
|
185
|
-
assert "git" in captured.err.lower(), (
|
|
186
|
-
"Error message must mention git so the user knows what is missing"
|
|
187
|
-
)
|
|
188
|
-
assert (
|
|
189
|
-
"npx claude-dev-env" in captured.err
|
|
190
|
-
or "git config --global core.hooksPath" in captured.err
|
|
191
|
-
), "Error message must include the enforcement-absent remediation hints"
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
def test_should_exit_nonzero_when_subprocess_run_raises_os_error(
|
|
195
|
-
capsys: pytest.CaptureFixture[str],
|
|
196
|
-
) -> None:
|
|
197
|
-
"""Preflight must surface a clean error for other OS-level git launch failures."""
|
|
198
|
-
with patch("subprocess.run", side_effect=OSError("permission denied")):
|
|
199
|
-
exit_code = bugteam_preflight.verify_git_hooks_path(Path("."))
|
|
200
|
-
assert exit_code != 0, (
|
|
201
|
-
"OSError from subprocess.run must produce a non-zero exit, "
|
|
202
|
-
"not a propagated traceback"
|
|
203
|
-
)
|
|
204
|
-
captured = capsys.readouterr()
|
|
205
|
-
assert "bugteam_preflight" in captured.err, (
|
|
206
|
-
"Error message must be prefixed with the preflight tool name for context"
|
|
207
|
-
)
|
|
208
|
-
assert "permission denied" in captured.err, (
|
|
209
|
-
"Error message must include the underlying OSError detail for diagnosis"
|
|
210
|
-
)
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
def test_has_pytest_configuration_finds_pytest_ini(tmp_path: Path) -> None:
|
|
214
|
-
"""has_pytest_configuration must detect pytest.ini at the repo root.
|
|
215
|
-
|
|
216
|
-
Regression for loop1-17/loop1-18: the literals "pytest.ini",
|
|
217
|
-
"pyproject.toml", and "[tool.pytest" were inlined in production function
|
|
218
|
-
bodies; centralizing them in bugteam_scripts_constants and importing here
|
|
219
|
-
pins the contract.
|
|
220
|
-
"""
|
|
221
|
-
repository_root = tmp_path / "repo"
|
|
222
|
-
repository_root.mkdir()
|
|
223
|
-
(repository_root / "pytest.ini").write_text("[pytest]\n", encoding="utf-8")
|
|
224
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is True
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
def test_has_pytest_configuration_finds_pyproject_pytest_section(
|
|
228
|
-
tmp_path: Path,
|
|
229
|
-
) -> None:
|
|
230
|
-
repository_root = tmp_path / "repo"
|
|
231
|
-
repository_root.mkdir()
|
|
232
|
-
(repository_root / "pyproject.toml").write_text(
|
|
233
|
-
"[tool.pytest.ini_options]\nminversion = '6.0'\n", encoding="utf-8"
|
|
234
|
-
)
|
|
235
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is True
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
def test_has_pytest_configuration_returns_false_without_either_file(
|
|
239
|
-
tmp_path: Path,
|
|
240
|
-
) -> None:
|
|
241
|
-
repository_root = tmp_path / "repo"
|
|
242
|
-
repository_root.mkdir()
|
|
243
|
-
assert bugteam_preflight.has_pytest_configuration(repository_root) is False
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
def test_main_should_halt_when_env_var_lists_bugteam(
|
|
247
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
248
|
-
capsys: pytest.CaptureFixture[str],
|
|
249
|
-
) -> None:
|
|
250
|
-
"""CLAUDE_REVIEWS_DISABLED=bugteam must halt preflight with the dedicated exit code."""
|
|
251
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "bugteam")
|
|
252
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
253
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
254
|
-
assert exit_code == bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
255
|
-
captured = capsys.readouterr()
|
|
256
|
-
assert "CLAUDE_REVIEWS_DISABLED" in captured.err
|
|
257
|
-
assert "bugteam" in captured.err
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
def test_main_should_continue_when_env_var_omits_bugteam(
|
|
261
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
262
|
-
tmp_path: Path,
|
|
263
|
-
) -> None:
|
|
264
|
-
"""CLAUDE_REVIEWS_DISABLED without the bugteam token must not halt preflight."""
|
|
265
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", "copilot,bugbot")
|
|
266
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
267
|
-
claude_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
268
|
-
claude_hooks_path.mkdir(parents=True)
|
|
269
|
-
with patch("subprocess.run") as mock_run:
|
|
270
|
-
mock_run.return_value = _make_completed_process(
|
|
271
|
-
str(claude_hooks_path) + "\n", returncode=0
|
|
272
|
-
)
|
|
273
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
274
|
-
assert exit_code != bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
def test_main_should_halt_when_env_var_contains_uppercase_or_whitespace_bugteam_token(
|
|
278
|
-
monkeypatch: pytest.MonkeyPatch,
|
|
279
|
-
) -> None:
|
|
280
|
-
"""Token matching must be case-insensitive and whitespace-tolerant."""
|
|
281
|
-
monkeypatch.setenv("CLAUDE_REVIEWS_DISABLED", " BugTeam , copilot ")
|
|
282
|
-
monkeypatch.delenv("BUGTEAM_PREFLIGHT_SKIP", raising=False)
|
|
283
|
-
exit_code = bugteam_preflight.main(["--no-pytest"])
|
|
284
|
-
assert exit_code == bugteam_preflight.EXIT_CODE_BUGTEAM_DISABLED_VIA_ENV
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
def _was_called_with_argument(
|
|
288
|
-
mock_subprocess_run: MagicMock, argument_token: str
|
|
289
|
-
) -> bool:
|
|
290
|
-
return any(
|
|
291
|
-
argument_token in each_call_args[0][0]
|
|
292
|
-
for each_call_args in mock_subprocess_run.call_args_list
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
def test_verify_git_hooks_path_invokes_self_heal_before_effective_query(
|
|
297
|
-
tmp_path: Path,
|
|
298
|
-
) -> None:
|
|
299
|
-
"""verify_git_hooks_path must delegate to the shared self-heal helper before --get."""
|
|
300
|
-
canonical_hooks_path = tmp_path / ".claude" / "hooks" / "git-hooks"
|
|
301
|
-
canonical_hooks_path.mkdir(parents=True)
|
|
302
|
-
with patch("subprocess.run") as mock_run:
|
|
303
|
-
mock_run.return_value = _make_completed_process(
|
|
304
|
-
str(canonical_hooks_path) + "\n", returncode=0
|
|
305
|
-
)
|
|
306
|
-
bugteam_preflight.verify_git_hooks_path(tmp_path)
|
|
307
|
-
assert _was_called_with_argument(mock_run, "--get-all"), (
|
|
308
|
-
"verify_git_hooks_path must run the --local --get-all read for self-heal"
|
|
309
|
-
)
|
|
310
|
-
assert _was_called_with_argument(mock_run, "--get"), (
|
|
311
|
-
"verify_git_hooks_path must still run the effective --get verification"
|
|
312
|
-
)
|
|
313
|
-
first_called_command = mock_run.call_args_list[0][0][0]
|
|
314
|
-
assert "--get-all" in first_called_command, (
|
|
315
|
-
"Self-heal must run BEFORE the effective config query, not after"
|
|
316
|
-
)
|
|
16
|
+
specification = importlib.util.spec_from_file_location(
|
|
17
|
+
"bugteam_preflight",
|
|
18
|
+
module_path,
|
|
19
|
+
)
|
|
20
|
+
assert specification is not None
|
|
21
|
+
assert specification.loader is not None
|
|
22
|
+
wrap_module = importlib.util.module_from_spec(specification)
|
|
23
|
+
specification.loader.exec_module(wrap_module)
|
|
24
|
+
return wrap_module
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_wrap_main_delegates_to_shared_module() -> None:
|
|
28
|
+
wrap_module = _load_wrap_module()
|
|
29
|
+
assert wrap_module.main.__module__ == "preflight"
|
|
30
|
+
shared_filename = Path(wrap_module.main.__code__.co_filename).name
|
|
31
|
+
assert shared_filename == "preflight.py"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_wrap_main_help_exits_cleanly() -> None:
|
|
35
|
+
wrap_module = _load_wrap_module()
|
|
36
|
+
try:
|
|
37
|
+
wrap_module.main(["--help"])
|
|
38
|
+
except SystemExit as exit_signal:
|
|
39
|
+
assert exit_signal.code in (0, None)
|
|
40
|
+
return
|
|
41
|
+
raise AssertionError("main(['--help']) must raise SystemExit")
|
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: copilot-finding-triage
|
|
3
3
|
description: >-
|
|
4
|
-
Sorts each Copilot gate finding from a converge run into one of two tiers
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
Sorts each Copilot gate finding from a converge run into one of two tiers, then
|
|
5
|
+
verifies each code concern with an executed check before routing it: a
|
|
6
|
+
self-healing finding is auto-fixed, pushed, and counted toward convergence with
|
|
7
|
+
no user page; a confirmed or refuted code concern is handled by the run with its
|
|
8
|
+
check evidence; only a code concern the check leaves inconclusive pages the user
|
|
9
|
+
over ntfy and holds the run behind a 45-minute gate. Triggers: 'copilot finding
|
|
10
|
+
triage', 'user review gate', 'tier copilot findings', 'triage the copilot gate'.
|
|
9
11
|
---
|
|
10
12
|
|
|
11
13
|
# Copilot Finding Triage
|
|
12
14
|
|
|
13
|
-
After a converge run's Copilot gate returns its findings, this skill
|
|
14
|
-
one
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
After a converge run's Copilot gate returns its findings, this skill tiers each
|
|
16
|
+
one, verifies every code concern with an executed check, then acts on it.
|
|
17
|
+
Findings scoped to style, type hints, or tests heal themselves inside the run. A
|
|
18
|
+
logic, security, or behavior finding goes to a verifier that runs a check against
|
|
19
|
+
HEAD: a confirmed defect joins the fix round carrying its repro, a refuted claim
|
|
20
|
+
is answered on the thread with the check evidence, and only a finding the check
|
|
21
|
+
leaves inconclusive pages the user and holds the run until they answer or a
|
|
22
|
+
45-minute deadline passes.
|
|
17
23
|
|
|
18
24
|
## When this applies
|
|
19
25
|
|
|
@@ -22,7 +28,6 @@ inside one of these callers:
|
|
|
22
28
|
|
|
23
29
|
- `autoconverge` — the single-run converge workflow, at its Copilot wait-gate.
|
|
24
30
|
- `pr-converge` — the looping converge workflow, at each Copilot tick.
|
|
25
|
-
- `copilot-review` — the standalone Copilot babysitter.
|
|
26
31
|
|
|
27
32
|
Each caller already fetches the review and carries a fix-and-push flow. This
|
|
28
33
|
skill decides, per finding, whether that flow runs on its own or waits behind
|
|
@@ -40,12 +45,48 @@ Read the complete rubric in `reference/tier-rubric.md`. The short form:
|
|
|
40
45
|
product decision. Logic or correctness defects, security, data handling,
|
|
41
46
|
error-handling semantics, and concurrency.
|
|
42
47
|
|
|
43
|
-
A finding that straddles both tiers sorts to CODE CONCERN. Any doubt
|
|
44
|
-
the
|
|
48
|
+
A finding that straddles both tiers sorts to CODE CONCERN. Any doubt about the
|
|
49
|
+
tier sorts the finding to CODE CONCERN.
|
|
50
|
+
|
|
51
|
+
## The verification stage
|
|
52
|
+
|
|
53
|
+
Tiering names who might act; the verification stage between tiering and the user
|
|
54
|
+
gate settles it for every code concern. Each code-concern finding goes to its own
|
|
55
|
+
verifier agent, all in parallel, inside the run. The verifier runs a check
|
|
56
|
+
against the flagged HEAD and returns one of three verdicts.
|
|
57
|
+
|
|
58
|
+
**The governing rule: a verdict is conclusive only if an actual check was
|
|
59
|
+
executed.** Reading the source and reasoning about it, however sound, never
|
|
60
|
+
produces a conclusive verdict. A check is a concrete command the verifier runs
|
|
61
|
+
against the flagged HEAD — executing the code path with crafted inputs, forcing
|
|
62
|
+
the claimed error condition, or running a purpose-built test — whose captured
|
|
63
|
+
output demonstrates the behavior in question. Each verdict carries
|
|
64
|
+
`{verdict, checkCommand, checkOutput, evidence}`; a conclusive verdict whose
|
|
65
|
+
`checkCommand` or `checkOutput` is empty carries no executed check, so the run
|
|
66
|
+
downgrades it to inconclusive.
|
|
67
|
+
|
|
68
|
+
- **CONFIRMED** — the check reproduces the defect. The finding becomes
|
|
69
|
+
self-healing: it joins the fix round carrying its repro. The fix re-runs the
|
|
70
|
+
same repro check and shows it passes, adds the repro to the test suite as a
|
|
71
|
+
regression test where the suite covers that surface, lands in one commit and
|
|
72
|
+
pushes, replies to the thread with the fix SHA plus the before/after check
|
|
73
|
+
output, and resolves the thread. No page.
|
|
74
|
+
- **REFUTED** — the check shows the code already behaves correctly in the exact
|
|
75
|
+
scenario the finding claims is broken. The run replies to the thread with the
|
|
76
|
+
command(s) and captured output, resolves the thread, and counts the finding
|
|
77
|
+
clean. No page.
|
|
78
|
+
- **INCONCLUSIVE** — everything else: no runnable check exists, the check is
|
|
79
|
+
infeasible in this environment, the results are ambiguous, or the fix needs a
|
|
80
|
+
product decision between defensible behaviors. The verifier defaults to
|
|
81
|
+
inconclusive, and any doubt sorts here. These findings, and only these, flow
|
|
82
|
+
into the user gate.
|
|
83
|
+
|
|
84
|
+
A run with zero inconclusive findings never reaches the user gate.
|
|
45
85
|
|
|
46
86
|
## Self-healing flow
|
|
47
87
|
|
|
48
|
-
A self-healing finding never pages the user.
|
|
88
|
+
A self-healing finding never pages the user. This covers a finding tiered
|
|
89
|
+
self-healing and a code concern the verifier confirmed.
|
|
49
90
|
|
|
50
91
|
1. Fix the finding through the caller's existing fix flow.
|
|
51
92
|
2. Verify the fix.
|
|
@@ -54,16 +95,18 @@ A self-healing finding never pages the user.
|
|
|
54
95
|
|
|
55
96
|
## User gate protocol
|
|
56
97
|
|
|
57
|
-
Run this protocol when one or more
|
|
98
|
+
Run this protocol when one or more code-concern findings stayed inconclusive
|
|
99
|
+
after verification on the round's HEAD.
|
|
58
100
|
|
|
59
101
|
### Step 1 — Page the user
|
|
60
102
|
|
|
61
103
|
Run `scripts/notify_ntfy.py` with:
|
|
62
104
|
|
|
63
105
|
- `--title` naming the PR.
|
|
64
|
-
- `--message` summarizing each
|
|
65
|
-
`file:line — severity — one sentence
|
|
66
|
-
|
|
106
|
+
- `--message` summarizing each inconclusive finding, one line each, as
|
|
107
|
+
`file:line — severity — one sentence`, followed by the verifier's one-line
|
|
108
|
+
evidence note stating what check was attempted and why it was not decisive.
|
|
109
|
+
Build the body from `templates/notification.md`.
|
|
67
110
|
- `--click-url` set to the Copilot review URL, so tapping the page opens the
|
|
68
111
|
review.
|
|
69
112
|
|
|
@@ -82,17 +125,22 @@ moment the script exits zero.
|
|
|
82
125
|
- If the deadline passes with no answer, run the caller's normal teardown and
|
|
83
126
|
report the un-reviewed findings in the final report.
|
|
84
127
|
|
|
85
|
-
### Step 3 — Self-healing findings run in parallel
|
|
128
|
+
### Step 3 — Self-healing and confirmed findings run in parallel
|
|
86
129
|
|
|
87
|
-
Self-healing findings on the same HEAD do not wait
|
|
88
|
-
commit, and push them through the caller's fix flow,
|
|
89
|
-
convergence.
|
|
130
|
+
Self-healing findings and confirmed code concerns on the same HEAD do not wait
|
|
131
|
+
for the gate. Fix, verify, commit, and push them through the caller's fix flow,
|
|
132
|
+
and count the round toward convergence. A confirmed finding carries its repro, so
|
|
133
|
+
its fix re-runs that same check and posts the before/after output on the thread.
|
|
90
134
|
|
|
91
135
|
## Gate checklist
|
|
92
136
|
|
|
93
137
|
- [ ] Every finding on HEAD carries a tier.
|
|
94
138
|
- [ ] Each self-healing finding is fixed, verified, and pushed.
|
|
95
|
-
- [ ] Each code-concern finding
|
|
139
|
+
- [ ] Each code-concern finding carries a verifier verdict from an executed check.
|
|
140
|
+
- [ ] Each confirmed finding is fixed with its repro re-run and pushed.
|
|
141
|
+
- [ ] Each refuted finding's thread carries the check evidence and is resolved.
|
|
142
|
+
- [ ] Each inconclusive finding appears as one line in the ntfy body with its
|
|
143
|
+
evidence note.
|
|
96
144
|
- [ ] The page carries the PR name, the per-finding summary, and the review URL.
|
|
97
145
|
- [ ] `scripts/notify_ntfy.py` exited zero before the 45-minute clock started.
|
|
98
146
|
- [ ] The wakeup is armed for 45 minutes from a delivered page.
|
|
@@ -102,9 +150,9 @@ convergence.
|
|
|
102
150
|
|
|
103
151
|
| Path | Role |
|
|
104
152
|
|------|------|
|
|
105
|
-
| `SKILL.md` | This hub: when it applies, the tier split, the gate protocol. |
|
|
106
|
-
| `reference/tier-rubric.md` | The complete tier rubric and the
|
|
107
|
-
| `templates/notification.md` | The ntfy message body for
|
|
153
|
+
| `SKILL.md` | This hub: when it applies, the tier split, the verification stage, the gate protocol. |
|
|
154
|
+
| `reference/tier-rubric.md` | The complete tier rubric, the three verdicts, and the executed-check standard. |
|
|
155
|
+
| `templates/notification.md` | The ntfy message body for an inconclusive-finding page. |
|
|
108
156
|
| `scripts/notify_ntfy.py` | The ntfy publish CLI. |
|
|
109
157
|
| `scripts/test_notify_ntfy.py` | Tests for the publish CLI. |
|
|
110
158
|
|
|
@@ -116,9 +164,13 @@ convergence.
|
|
|
116
164
|
- **A failed page is not consent.** A failed ntfy POST holds the gate open. It
|
|
117
165
|
does not auto-approve the round. Read the script's non-zero exit as a page that
|
|
118
166
|
never reached the user, and keep the run held.
|
|
119
|
-
- **
|
|
120
|
-
|
|
121
|
-
|
|
167
|
+
- **A conclusive verdict needs an executed check.** Source reading, however
|
|
168
|
+
sound, never confirms or refutes a finding. A confirmed or refuted verdict
|
|
169
|
+
whose `checkCommand` or `checkOutput` is empty downgrades to inconclusive and
|
|
170
|
+
pages the user.
|
|
171
|
+
- **Doubt sorts to INCONCLUSIVE.** When an executed check does not pin down the
|
|
172
|
+
behavior, the verifier defaults to inconclusive and the finding pages the user.
|
|
173
|
+
The safe default never auto-fixes a finding whose behavior a check did not show.
|
|
122
174
|
- **The 45-minute clock starts at page success.** The timer starts when the page
|
|
123
175
|
reaches the user, which is the moment `scripts/notify_ntfy.py` exits zero, not
|
|
124
176
|
the moment the finding is classified.
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
# Tier Rubric
|
|
2
2
|
|
|
3
|
-
Every Copilot finding on the round's HEAD
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Every Copilot finding on the round's HEAD passes through two stages: tiering,
|
|
4
|
+
then — for a code concern — an executed-check verification stage that decides who
|
|
5
|
+
acts. Tiering sorts a finding into one of two tiers. The run fixes a self-healing
|
|
6
|
+
finding on its own; a code concern goes to a verifier agent, and only a finding
|
|
7
|
+
the verifier leaves inconclusive pages the user.
|
|
6
8
|
|
|
7
9
|
## The two tiers
|
|
8
10
|
|
|
9
|
-
| Tier |
|
|
10
|
-
|
|
11
|
+
| Tier | Next step | Page the user | Counts toward convergence |
|
|
12
|
+
|------|-----------|---------------|---------------------------|
|
|
11
13
|
| SELF-HEALING | The run auto-fixes | No | Yes |
|
|
12
|
-
| CODE CONCERN |
|
|
14
|
+
| CODE CONCERN | An executed-check verifier decides | Only when the verifier's verdict is inconclusive | Confirmed and refuted count; inconclusive holds until the user answers or the deadline passes |
|
|
13
15
|
|
|
14
|
-
## The test that splits
|
|
16
|
+
## The test that splits the tiers
|
|
15
17
|
|
|
16
18
|
A finding is SELF-HEALING when its fix cannot change what a production caller
|
|
17
19
|
observes at runtime. A finding is a CODE CONCERN when its fix changes runtime
|
|
@@ -42,8 +44,65 @@ weigh a tradeoff.
|
|
|
42
44
|
- Concurrency.
|
|
43
45
|
- Anything whose fix changes observable production behavior.
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
A finding that touches both tiers sorts to CODE CONCERN. Any doubt about the tier
|
|
48
|
+
sorts the finding to CODE CONCERN.
|
|
46
49
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
## The verification stage
|
|
51
|
+
|
|
52
|
+
Every code-concern finding goes to its own verifier agent, all in parallel,
|
|
53
|
+
inside the run. The verifier decides one of three verdicts by executing a check
|
|
54
|
+
against the flagged HEAD.
|
|
55
|
+
|
|
56
|
+
**The governing rule: a verdict is conclusive only if an actual check was
|
|
57
|
+
executed.** Reading the source and reasoning about it, however sound, never
|
|
58
|
+
produces a conclusive verdict. A check is a concrete command the verifier runs
|
|
59
|
+
against the flagged HEAD — executing the flagged code path with crafted inputs,
|
|
60
|
+
forcing the claimed error condition, or running a purpose-built test — whose
|
|
61
|
+
captured output demonstrates the behavior in question. Source inspection points
|
|
62
|
+
the check at the right place; it is never itself grounds for a conclusive verdict.
|
|
63
|
+
|
|
64
|
+
Each verdict carries `{verdict, checkCommand, checkOutput, evidence}`. A
|
|
65
|
+
conclusive verdict whose `checkCommand` or `checkOutput` is empty carries no
|
|
66
|
+
executed check, so the run downgrades it to inconclusive.
|
|
67
|
+
|
|
68
|
+
| Verdict | What the executed check shows | Who acts | Page the user |
|
|
69
|
+
|---------|-------------------------------|----------|---------------|
|
|
70
|
+
| CONFIRMED | The check tangibly reproduces the defect | The run auto-fixes | No |
|
|
71
|
+
| REFUTED | The check tangibly shows the code already behaves correctly in the exact scenario the finding claims is broken | The run replies and resolves | No |
|
|
72
|
+
| INCONCLUSIVE | Everything else | The user decides | Yes |
|
|
73
|
+
|
|
74
|
+
### CONFIRMED
|
|
75
|
+
|
|
76
|
+
The executed check reproduces the defect; the evidence carries the exact
|
|
77
|
+
command(s) and the captured output. Only a confirmed verdict makes a
|
|
78
|
+
code-concern finding self-healing. The finding joins the round's fix list
|
|
79
|
+
carrying its repro, and the fix:
|
|
80
|
+
|
|
81
|
+
1. Re-runs the same repro check and shows it passes.
|
|
82
|
+
2. Adds the repro to the repo's test suite as a regression test where the suite
|
|
83
|
+
covers that surface.
|
|
84
|
+
3. Lands in one commit and pushes.
|
|
85
|
+
4. Replies to the thread with the fix SHA plus the before/after check output,
|
|
86
|
+
then resolves the thread.
|
|
87
|
+
|
|
88
|
+
The loop resumes on its own. No page.
|
|
89
|
+
|
|
90
|
+
### REFUTED
|
|
91
|
+
|
|
92
|
+
The executed check demonstrates the code already behaves correctly in the exact
|
|
93
|
+
scenario the finding claims is broken. The run replies to the thread with the
|
|
94
|
+
command(s) and captured output, resolves the thread, and counts the finding
|
|
95
|
+
clean. No page.
|
|
96
|
+
|
|
97
|
+
### INCONCLUSIVE
|
|
98
|
+
|
|
99
|
+
Everything else: no runnable check exists, the check is infeasible in this
|
|
100
|
+
environment, the results are ambiguous, or the fix needs a product decision
|
|
101
|
+
between defensible behaviors. A finding that is not tangibly reproducible is not
|
|
102
|
+
self-healing. These findings, and only these, flow into the user gate.
|
|
103
|
+
|
|
104
|
+
## The default is inconclusive
|
|
105
|
+
|
|
106
|
+
The verifier defaults to inconclusive. Any doubt sorts to inconclusive, and an
|
|
107
|
+
inconclusive finding pages the user. The safe default pages a person; it never
|
|
108
|
+
auto-fixes a finding whose behavior an executed check did not pin down.
|
|
@@ -13,10 +13,12 @@ Copilot code concern on PR {pr_number}: {short_pr_title}
|
|
|
13
13
|
## Body
|
|
14
14
|
|
|
15
15
|
```
|
|
16
|
-
{finding_count} code concern(s) need your call on PR {pr_number} ({head_sha}):
|
|
16
|
+
{finding_count} code concern(s) survived verification as inconclusive and need your call on PR {pr_number} ({head_sha}):
|
|
17
17
|
|
|
18
18
|
- {file_1}:{line_1} — {severity_1} — {one_sentence_concern_1}
|
|
19
|
+
Check: {evidence_note_1}
|
|
19
20
|
- {file_2}:{line_2} — {severity_2} — {one_sentence_concern_2}
|
|
21
|
+
Check: {evidence_note_2}
|
|
20
22
|
|
|
21
23
|
Review: {review_url}
|
|
22
24
|
Reply within 45 minutes or the run tears down and reports these findings unreviewed.
|
|
@@ -24,10 +26,13 @@ Reply within 45 minutes or the run tears down and reports these findings unrevie
|
|
|
24
26
|
|
|
25
27
|
## Field notes
|
|
26
28
|
|
|
27
|
-
- `{finding_count}` counts the
|
|
28
|
-
self-healing
|
|
29
|
+
- `{finding_count}` counts the inconclusive findings on this HEAD — the code
|
|
30
|
+
concerns an executed check left undecided — not the self-healing, confirmed, or
|
|
31
|
+
refuted ones.
|
|
29
32
|
- Each bullet holds one finding: its `file:line`, its severity, and one sentence
|
|
30
33
|
naming the concern.
|
|
34
|
+
- `{evidence_note_N}` is the verifier's one-line note stating what check was
|
|
35
|
+
attempted and why it was not decisive.
|
|
31
36
|
- `{review_url}` is the same URL passed as `--click-url`, so the reader sees it
|
|
32
37
|
in the body and reaches it by tapping the message.
|
|
33
38
|
- The 45-minute clock starts when the page reaches the user, which is the moment
|
|
@@ -13,8 +13,7 @@ Search files instantly on Windows using the Everything command-line interface (e
|
|
|
13
13
|
|
|
14
14
|
## Hard limits
|
|
15
15
|
|
|
16
|
-
|
|
17
|
-
- **Fall back on failure.** When `es.exe` fails or returns nothing, try to start it. if that doesn't work, prompt the user to decide next steps: grep, debug further, etc.
|
|
16
|
+
Scope and fallback policy: `packages/claude-dev-env/rules/es-exe-file-search.md`.
|
|
18
17
|
|
|
19
18
|
## Instructions
|
|
20
19
|
|