claude-dev-env 1.28.1 → 1.29.1
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/caveman.md +74 -0
- package/hooks/blocking/code_rules_enforcer.py +82 -7
- package/hooks/blocking/code_rules_path_utils.py +31 -0
- package/hooks/blocking/es_exe_path_rewriter.py +159 -0
- package/hooks/blocking/hedging_language_blocker.py +12 -2
- package/hooks/blocking/test_code_rules_enforcer.py +148 -0
- package/hooks/blocking/test_code_rules_enforcer_config_path.py +123 -0
- package/hooks/blocking/test_code_rules_enforcer_magic_allowlist.py +1 -1
- package/hooks/blocking/test_code_rules_path_utils.py +52 -0
- package/hooks/blocking/test_es_exe_path_rewriter.py +369 -0
- package/hooks/blocking/test_hedging_language_blocker.py +7 -6
- package/hooks/config/dynamic_stderr_handler.py +22 -0
- package/hooks/config/path_rewriter_constants.py +13 -0
- package/hooks/config/project_paths_reader.py +78 -0
- package/hooks/config/setup_project_paths_constants.py +41 -0
- package/hooks/config/test_dynamic_stderr_handler.py +48 -0
- package/hooks/config/test_messages.py +5 -1
- package/hooks/config/test_path_rewriter_constants.py +57 -0
- package/hooks/config/test_project_paths_reader.py +149 -0
- package/hooks/config/test_setup_project_paths_constants.py +74 -0
- package/hooks/git-hooks/test_config.py +1 -0
- package/hooks/git-hooks/test_gate_utils.py +1 -0
- package/hooks/git-hooks/test_pre_commit.py +1 -0
- package/hooks/git-hooks/test_pre_push.py +1 -0
- package/hooks/hooks.json +10 -0
- package/hooks/session/test_untracked_repo_detector.py +192 -0
- package/hooks/session/untracked_repo_detector.py +103 -0
- package/hooks/validators/exempt_paths.py +17 -14
- package/hooks/validators/test_exempt_paths.py +65 -0
- package/hooks/validators/test_git_checks.py +17 -17
- package/package.json +1 -1
- package/scripts/config/__init__.py +1 -0
- package/scripts/config/groq_bugteam_config.py +118 -0
- package/scripts/config/test_groq_bugteam_config.py +72 -0
- package/scripts/groq_bugteam.README.md +129 -0
- package/scripts/groq_bugteam.py +586 -0
- package/scripts/setup_project_paths.py +352 -0
- package/scripts/test_groq_bugteam.py +391 -0
- package/scripts/test_setup_project_paths.py +532 -0
- package/scripts/test_setup_project_paths_config.py +6 -0
- package/skills/bugteam/CONSTRAINTS.md +1 -1
- package/skills/bugteam/PROMPTS.md +1 -1
- package/skills/bugteam/SKILL.md +5 -5
- package/skills/bugteam/SKILL_EVALS.md +5 -5
- package/skills/bugteam/reference/audit-and-teammates.md +3 -3
- package/skills/bugteam/reference/audit-contract.md +159 -0
- package/skills/bugteam/reference/team-setup.md +2 -2
- package/skills/bugteam/scripts/bugteam_preflight.py +66 -0
- package/skills/bugteam/scripts/test_bugteam_preflight.py +189 -0
- package/skills/copilot-review/SKILL.md +145 -0
- package/skills/findbugs/SKILL.md +14 -22
- package/skills/qbug/SKILL.md +56 -13
- package/skills/qbug/test_qbug_skill_audit_schema.py +156 -0
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +103 -0
package/skills/qbug/SKILL.md
CHANGED
|
@@ -22,6 +22,7 @@ Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE
|
|
|
22
22
|
- Pre-flight script: `${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_preflight.py`
|
|
23
23
|
- Code-rules gate script: `${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_code_rules_gate.py`
|
|
24
24
|
- Bug category rubric A–J: [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md#audit-spawn-prompt-xml-bugfind-teammate)
|
|
25
|
+
- **Audit contract** (finding schema, proof-of-absence, adversarial pass, Haiku secondary, post-fix self-audit, diagnostics JSON): [`bugteam/reference/audit-contract.md`](../bugteam/reference/audit-contract.md)
|
|
25
26
|
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#step-25-pr-comments-one-review-per-loop)
|
|
26
27
|
|
|
27
28
|
## When this skill applies
|
|
@@ -52,6 +53,17 @@ python "${CLAUDE_SKILL_DIR}/../bugteam/scripts/bugteam_preflight.py"
|
|
|
52
53
|
|
|
53
54
|
`${CLAUDE_SKILL_DIR}` is host-substituted before the shell runs. Non-zero → fix before continuing. `BUGTEAM_PREFLIGHT_SKIP=1` is emergency only. Add `--pre-commit` when `.pre-commit-config.yaml` exists.
|
|
54
55
|
|
|
56
|
+
Pre-flight checks (in order):
|
|
57
|
+
|
|
58
|
+
1. **Git hooks path** — verifies `git -C <repository_root> config --get core.hooksPath` resolves to a path ending in `hooks/git-hooks`. Queries the repository-effective config so repo-level overrides (Husky, lefthook) are detected. If unset or pointing elsewhere, exits non-zero:
|
|
59
|
+
```
|
|
60
|
+
Git-side CODE_RULES enforcement is not active on this host.
|
|
61
|
+
Run: npx claude-dev-env .
|
|
62
|
+
Or: git config --global core.hooksPath ~/.claude/hooks/git-hooks
|
|
63
|
+
```
|
|
64
|
+
2. **pytest** — runs the test suite when `pytest.ini` or `[tool.pytest]` is present.
|
|
65
|
+
3. **pre-commit** — runs when `--pre-commit` flag is passed and `.pre-commit-config.yaml` exists.
|
|
66
|
+
|
|
55
67
|
## Step 1: Resolve PR scope (lead)
|
|
56
68
|
|
|
57
69
|
1. `gh pr view --json number,baseRefName,headRefName,url`
|
|
@@ -76,7 +88,7 @@ qbug_temp_dir: Path = Path(tempfile.gettempdir()) / f"qbug-pr-{pr_number}"
|
|
|
76
88
|
qbug_temp_dir.mkdir(parents=True, exist_ok=True)
|
|
77
89
|
```
|
|
78
90
|
|
|
79
|
-
## Step 2: Spawn the
|
|
91
|
+
## Step 2: Spawn the primary and secondary audit agents
|
|
80
92
|
|
|
81
93
|
Before calling `Agent`, the lead resolves the three absolute paths the subagent needs and substitutes them into the prompt template (the `<gate_script>`, `<categories_file>`, and `<qbug_temp_dir>` placeholders in § Subagent cycle prompt):
|
|
82
94
|
|
|
@@ -89,19 +101,27 @@ gate_script_path = (skill_dir / ".." / "bugteam" / "scripts" / "bugteam_code_rul
|
|
|
89
101
|
categories_file_path = (skill_dir / ".." / "bugteam" / "PROMPTS.md").resolve()
|
|
90
102
|
```
|
|
91
103
|
|
|
92
|
-
Then call `Agent
|
|
104
|
+
Then call `Agent` twice in the same message — the primary clean-coder and the Haiku secondary run in parallel per the audit contract. The Haiku secondary receives an **audit-only** prompt (no FIX step, no git operations) and returns findings to the lead only. The lead merges their findings before the FIX step:
|
|
93
105
|
|
|
94
106
|
```
|
|
95
107
|
Agent(
|
|
96
108
|
subagent_type="clean-coder",
|
|
97
|
-
model="
|
|
98
|
-
description="qbug audit/fix cycle for PR <number>",
|
|
109
|
+
model="opus",
|
|
110
|
+
description="qbug primary audit/fix cycle for PR <number>",
|
|
99
111
|
prompt="<filled cycle XML; see § Subagent cycle prompt>",
|
|
100
112
|
run_in_background=False
|
|
101
113
|
)
|
|
114
|
+
|
|
115
|
+
Agent(
|
|
116
|
+
subagent_type="code-quality-agent",
|
|
117
|
+
model="haiku",
|
|
118
|
+
description="qbug Haiku secondary audit for PR <number>",
|
|
119
|
+
prompt="<audit-only prompt: read the PR diff, apply A-J categories from <categories_file>, return structured findings. No FIX, no git add, no git commit, no git push.>",
|
|
120
|
+
run_in_background=False
|
|
121
|
+
)
|
|
102
122
|
```
|
|
103
123
|
|
|
104
|
-
|
|
124
|
+
The Haiku secondary is a read-only auditor per `audit-contract.md` — it returns findings to the lead and never modifies the working tree. The lead merges primary and Haiku secondary findings per the de-dup rules in the audit contract before proceeding. No `TeamCreate`, no `team_name`, no teammate shutdown protocol. The primary subagent returns when it has exited the cycle (`converged`, `stuck`, or `error`).
|
|
105
125
|
|
|
106
126
|
## Subagent cycle prompt
|
|
107
127
|
|
|
@@ -128,7 +148,8 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
128
148
|
|
|
129
149
|
<exit_conditions>
|
|
130
150
|
The cycle stops when ONE of these is true. Check on every iteration:
|
|
131
|
-
- converged: most recent AUDIT returned zero findings
|
|
151
|
+
- converged: most recent AUDIT returned zero findings AND
|
|
152
|
+
post_fix_audit_clean is true for the committing loop.
|
|
132
153
|
- stuck: most recent FIX left `git rev-parse HEAD` unchanged.
|
|
133
154
|
- error: three consecutive pre-audit gate rounds failed (three is
|
|
134
155
|
chosen because two is within normal clean-coder variance; four
|
|
@@ -166,14 +187,22 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
166
187
|
|
|
167
188
|
- Read the patch file.
|
|
168
189
|
- Audit only added/modified lines. Read <categories_file> for the
|
|
169
|
-
A–J category definitions; investigate each category explicitly
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
-
|
|
190
|
+
A–J category definitions; investigate each category explicitly.
|
|
191
|
+
- Follow the shared audit contract at
|
|
192
|
+
bugteam/reference/audit-contract.md. Per category: produce
|
|
193
|
+
either a Shape A structured finding or a Shape B structured
|
|
194
|
+
proof-of-absence. Bare "verified clean" labels are REJECTED.
|
|
195
|
+
- Run the contract's adversarial second pass after the primary
|
|
196
|
+
finding list.
|
|
197
|
+
- The LEAD spawns the Haiku secondary auditor in parallel with
|
|
198
|
+
this primary audit per the contract's Haiku secondary section.
|
|
199
|
+
- Partition findings into anchored (line appears in the diff) vs
|
|
175
200
|
unanchored (line does not).
|
|
176
201
|
|
|
202
|
+
Persist the merged audit result to
|
|
203
|
+
<qbug_temp_dir>/loop-<loop_count>-audit.json per the contract's
|
|
204
|
+
persistence schema.
|
|
205
|
+
|
|
177
206
|
Post ONE review per loop. Use the payload shape from
|
|
178
207
|
<categories_file>'s sibling SKILL.md § "PR comments" — build
|
|
179
208
|
the JSON with jq `--rawfile` / `-Rs` reading per-finding body
|
|
@@ -192,6 +221,7 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
192
221
|
|
|
193
222
|
4. FIX:
|
|
194
223
|
Capture the pre-FIX sha: `pre_fix_sha = git rev-parse HEAD`.
|
|
224
|
+
Capture pre-fix file contents for every file this FIX will touch.
|
|
195
225
|
|
|
196
226
|
Apply each fix. Read every file before editing. Preserve existing
|
|
197
227
|
comments on lines you do not modify. Add type hints on every
|
|
@@ -200,6 +230,16 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
200
230
|
Validate each modified Python file with `python -m py_compile`
|
|
201
231
|
(or the language-equivalent compile check).
|
|
202
232
|
|
|
233
|
+
Compute fix_diff: the diff between pre-fix and post-fix file contents
|
|
234
|
+
for every modified file.
|
|
235
|
+
|
|
236
|
+
Post-fix self-audit: follow the contract's post-fix self-audit
|
|
237
|
+
sequence at bugteam/reference/audit-contract.md. Paranoid mode
|
|
238
|
+
(Haiku secondary in parallel), internal iteration cap = 3, exit
|
|
239
|
+
"stuck: post-fix audit not converging" after 3 rounds with fresh
|
|
240
|
+
findings. Only when gate_findings empty AND post_fix_findings
|
|
241
|
+
empty: proceed to git add.
|
|
242
|
+
|
|
203
243
|
Stage each modified path by explicit name: `git add <path>`.
|
|
204
244
|
Create one commit summarizing the fixed findings. Let every git
|
|
205
245
|
hook run. If a hook blocks the commit, capture its stderr, mark
|
|
@@ -208,6 +248,9 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
208
248
|
|
|
209
249
|
Push with a plain fast-forward: `git push`.
|
|
210
250
|
|
|
251
|
+
Write <qbug_temp_dir>/loop-<loop_count>-diagnostics.json per the
|
|
252
|
+
contract's diagnostics schema (all eight keys required).
|
|
253
|
+
|
|
211
254
|
Reply to each finding at loop_comment_index[finding_id].finding_comment_id
|
|
212
255
|
using the reply CLI shape (jq `-Rs` → `gh api .../comments/<id>/replies --input -`).
|
|
213
256
|
Reply body is one of:
|
|
@@ -273,7 +316,7 @@ Delete the resolved `<qbug_temp_dir>` tree and any `.qbug-*.md` temp files in th
|
|
|
273
316
|
|
|
274
317
|
## Constraints
|
|
275
318
|
|
|
276
|
-
- **One
|
|
319
|
+
- **One primary + one secondary auditor, not a team.** Lead spawns a `clean-coder` primary (audit + fix cycle) and a `code-quality-agent` Haiku secondary (audit-only, read-only — no FIX, no git). No `TeamCreate`. Neither subagent spawns further subagents.
|
|
277
320
|
- **No loop cap.** Cycle runs until `converged`, `stuck`, or `error`. User can interrupt.
|
|
278
321
|
- **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
|
|
279
322
|
- **One commit per FIX action.** Linear branch, fast-forward push only.
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Tests verifying the shared audit contract and qbug's reference to it.
|
|
2
|
+
|
|
3
|
+
The contract lives in bugteam/reference/audit-contract.md and is the single
|
|
4
|
+
source of truth for finding schema, proof-of-absence shape, adversarial pass,
|
|
5
|
+
Haiku secondary, and de-dup/merge rules. qbug/SKILL.md must reference it.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
SKILL_FILE_PATH = Path(__file__).parent / "SKILL.md"
|
|
14
|
+
PROMPTS_FILE_PATH = Path(__file__).parent.parent / "bugteam" / "PROMPTS.md"
|
|
15
|
+
CONTRACT_FILE_PATH = (
|
|
16
|
+
Path(__file__).parent.parent / "bugteam" / "reference" / "audit-contract.md"
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _load_skill_text() -> str:
|
|
21
|
+
return SKILL_FILE_PATH.read_text(encoding="utf-8")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _load_prompts_text() -> str:
|
|
25
|
+
return PROMPTS_FILE_PATH.read_text(encoding="utf-8")
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _load_contract_text() -> str:
|
|
29
|
+
return CONTRACT_FILE_PATH.read_text(encoding="utf-8")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_skill_should_reference_audit_contract_by_path() -> None:
|
|
33
|
+
skill_text = _load_skill_text()
|
|
34
|
+
assert "audit-contract.md" in skill_text, (
|
|
35
|
+
"qbug/SKILL.md must reference the shared audit contract by path"
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_contract_should_require_structured_finding_schema() -> None:
|
|
40
|
+
contract_text = _load_contract_text()
|
|
41
|
+
assert "evidence_files" in contract_text, (
|
|
42
|
+
"Contract must require structured finding with evidence_files[]"
|
|
43
|
+
)
|
|
44
|
+
assert "proof_of_absence" in contract_text, (
|
|
45
|
+
"Contract must require structured proof-of-absence for clean categories"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_contract_should_reject_bare_verified_clean_labels() -> None:
|
|
50
|
+
contract_text = _load_contract_text()
|
|
51
|
+
assert "lines_quoted" in contract_text, (
|
|
52
|
+
"Proof-of-absence must require lines_quoted[] not bare 'verified clean'"
|
|
53
|
+
)
|
|
54
|
+
assert "adversarial_probes" in contract_text, (
|
|
55
|
+
"Proof-of-absence must require adversarial_probes[]"
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_contract_should_require_adversarial_second_pass() -> None:
|
|
60
|
+
contract_text = _load_contract_text()
|
|
61
|
+
assert "Assume your first pass missed" in contract_text, (
|
|
62
|
+
"Contract must include the adversarial second-pass re-prompt"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_should_require_haiku_secondary_auditor_spawn() -> None:
|
|
67
|
+
skill_text = _load_skill_text()
|
|
68
|
+
assert "haiku" in skill_text.lower(), (
|
|
69
|
+
"SKILL.md must reference Haiku secondary auditor"
|
|
70
|
+
)
|
|
71
|
+
assert "secondary" in skill_text.lower(), (
|
|
72
|
+
"SKILL.md must reference secondary auditor concept"
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_contract_should_require_dedup_merge_by_file_line_category() -> None:
|
|
77
|
+
contract_text = _load_contract_text()
|
|
78
|
+
assert (
|
|
79
|
+
"file, line, category" in contract_text
|
|
80
|
+
or "(file, line, category)" in contract_text
|
|
81
|
+
), "De-dup key must be (file, line, category)"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_contract_should_require_severity_max_wins_on_conflict() -> None:
|
|
85
|
+
contract_text = _load_contract_text()
|
|
86
|
+
assert (
|
|
87
|
+
"max wins" in contract_text.lower() or "severity conflict" in contract_text.lower()
|
|
88
|
+
), "Severity conflict resolution must specify max wins"
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def test_should_require_loop_n_audit_json_persistence() -> None:
|
|
92
|
+
skill_text = _load_skill_text()
|
|
93
|
+
assert "loop-" in skill_text and "audit.json" in skill_text, (
|
|
94
|
+
"SKILL.md must reference loop-N-audit.json persistence path"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_contract_should_require_findings_and_proof_of_absence_keys_in_json() -> None:
|
|
99
|
+
contract_text = _load_contract_text()
|
|
100
|
+
assert '"findings"' in contract_text or "findings[]" in contract_text, (
|
|
101
|
+
"loop-N-audit.json must have findings[] key"
|
|
102
|
+
)
|
|
103
|
+
assert (
|
|
104
|
+
'"proof_of_absence"' in contract_text or "proof_of_absence[]" in contract_text
|
|
105
|
+
), "loop-N-audit.json must have proof_of_absence[] key"
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_contract_should_require_files_opened_in_proof_of_absence() -> None:
|
|
109
|
+
contract_text = _load_contract_text()
|
|
110
|
+
assert "files_opened" in contract_text, (
|
|
111
|
+
"Proof-of-absence struct must include files_opened[]"
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def test_step2_spawn_should_include_model_sonnet_parameter() -> None:
|
|
116
|
+
skill_text = _load_skill_text()
|
|
117
|
+
assert 'model="sonnet"' in skill_text, (
|
|
118
|
+
"Step 2 Agent() spawn template must include model=\"sonnet\" for the primary subagent"
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_step2_spawn_should_reference_clean_coder_and_haiku_secondary() -> None:
|
|
123
|
+
skill_text = _load_skill_text()
|
|
124
|
+
step2_marker = "## Step 2:"
|
|
125
|
+
step3_marker = "## Step 3:"
|
|
126
|
+
step2_start = skill_text.find(step2_marker)
|
|
127
|
+
step3_start = skill_text.find(step3_marker)
|
|
128
|
+
assert step2_start != -1, "SKILL.md must have a Step 2 section"
|
|
129
|
+
assert step3_start != -1, "SKILL.md must have a Step 3 section"
|
|
130
|
+
step2_region = skill_text[step2_start:step3_start]
|
|
131
|
+
assert "clean-coder" in step2_region, (
|
|
132
|
+
"Step 2 must reference the clean-coder primary subagent spawn"
|
|
133
|
+
)
|
|
134
|
+
assert "haiku" in step2_region.lower(), (
|
|
135
|
+
"Step 2 must reference the Haiku secondary auditor spawn"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def test_prompts_md_should_contain_expanded_category_e_dead_code_variants() -> None:
|
|
140
|
+
prompts_text = _load_prompts_text()
|
|
141
|
+
assert (
|
|
142
|
+
"dead parameter" in prompts_text.lower()
|
|
143
|
+
or "dead parameters" in prompts_text.lower()
|
|
144
|
+
), "Category E must cover dead parameters"
|
|
145
|
+
assert (
|
|
146
|
+
"dead local" in prompts_text.lower() or "dead locals" in prompts_text.lower()
|
|
147
|
+
), "Category E must cover dead locals"
|
|
148
|
+
assert (
|
|
149
|
+
"dead import" in prompts_text.lower() or "dead imports" in prompts_text.lower()
|
|
150
|
+
), "Category E must cover dead imports"
|
|
151
|
+
assert (
|
|
152
|
+
"dead branch" in prompts_text.lower() or "dead branches" in prompts_text.lower()
|
|
153
|
+
), "Category E must cover dead branches"
|
|
154
|
+
assert (
|
|
155
|
+
"dead return" in prompts_text.lower() or "dead returns" in prompts_text.lower()
|
|
156
|
+
), "Category E must cover dead returns"
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Tests verifying qbug SKILL.md contains required post-fix self-audit structural elements.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- Post-fix self-audit inserted between py_compile and git add
|
|
5
|
+
- Internal iteration cap of 3
|
|
6
|
+
- loop-N-diagnostics.json with all eight source keys
|
|
7
|
+
- converged condition requires both primary and post-fix audits clean
|
|
8
|
+
- Stuck state when post-fix audit does not converge after 3 iterations
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
SKILL_FILE_PATH = Path(__file__).parent / "SKILL.md"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_skill_text() -> str:
|
|
20
|
+
return SKILL_FILE_PATH.read_text(encoding="utf-8")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def test_should_require_post_fix_gate_before_git_add() -> None:
|
|
24
|
+
skill_text = _load_skill_text()
|
|
25
|
+
assert "bugteam_code_rules_gate" in skill_text, (
|
|
26
|
+
"FIX step must run bugteam_code_rules_gate against modified files"
|
|
27
|
+
)
|
|
28
|
+
assert "post-fix" in skill_text.lower() or "post_fix" in skill_text.lower(), (
|
|
29
|
+
"FIX step must reference a post-fix audit phase"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def test_should_require_post_fix_audit_of_fix_diff() -> None:
|
|
34
|
+
skill_text = _load_skill_text()
|
|
35
|
+
assert "fix_diff" in skill_text, (
|
|
36
|
+
"FIX step must compute fix_diff for the post-fix scoped audit"
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_should_require_paranoid_mode_with_haiku_on_post_fix() -> None:
|
|
41
|
+
skill_text = _load_skill_text()
|
|
42
|
+
assert "paranoid" in skill_text.lower(), (
|
|
43
|
+
"Post-fix audit must be flagged as paranoid mode with Haiku secondary"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_should_require_internal_iteration_cap_of_three() -> None:
|
|
48
|
+
skill_text = _load_skill_text()
|
|
49
|
+
assert "internal iteration cap = 3" in skill_text, (
|
|
50
|
+
"FIX step must specify the exact phrase 'internal iteration cap = 3'"
|
|
51
|
+
)
|
|
52
|
+
assert "stuck: post-fix audit not converging" in skill_text, (
|
|
53
|
+
"Exit message for cap exceeded must be 'stuck: post-fix audit not converging'"
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def test_should_only_git_add_when_post_fix_audit_is_clean() -> None:
|
|
58
|
+
skill_text = _load_skill_text()
|
|
59
|
+
post_fix_audit_block_header = "Post-fix self-audit"
|
|
60
|
+
post_fix_audit_block_index = skill_text.find(post_fix_audit_block_header)
|
|
61
|
+
assert post_fix_audit_block_index != -1, (
|
|
62
|
+
f"SKILL.md must contain the literal block header '{post_fix_audit_block_header}'"
|
|
63
|
+
)
|
|
64
|
+
git_add_index = skill_text.find("git add", post_fix_audit_block_index)
|
|
65
|
+
assert git_add_index > post_fix_audit_block_index, (
|
|
66
|
+
"git add must appear after the Post-fix self-audit block header, not before"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_should_require_loop_n_diagnostics_json() -> None:
|
|
71
|
+
skill_text = _load_skill_text()
|
|
72
|
+
assert "diagnostics.json" in skill_text, (
|
|
73
|
+
"Each loop must write loop-N-diagnostics.json"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_contract_should_require_all_eight_source_keys_in_diagnostics() -> None:
|
|
78
|
+
contract_path = (
|
|
79
|
+
Path(__file__).parent.parent / "bugteam" / "reference" / "audit-contract.md"
|
|
80
|
+
)
|
|
81
|
+
contract_text = contract_path.read_text(encoding="utf-8")
|
|
82
|
+
required_keys = [
|
|
83
|
+
"loop",
|
|
84
|
+
"gate_findings",
|
|
85
|
+
"primary_findings",
|
|
86
|
+
"adversarial_findings",
|
|
87
|
+
"haiku_findings",
|
|
88
|
+
"post_fix_findings",
|
|
89
|
+
"merged",
|
|
90
|
+
"deduped",
|
|
91
|
+
]
|
|
92
|
+
for each_key in required_keys:
|
|
93
|
+
assert each_key in contract_text, (
|
|
94
|
+
f"loop-N-diagnostics.json schema in audit-contract.md must contain key '{each_key}'"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_should_update_exit_conditions_to_require_post_fix_clean() -> None:
|
|
99
|
+
skill_text = _load_skill_text()
|
|
100
|
+
assert (
|
|
101
|
+
"post_fix_audit_clean" in skill_text
|
|
102
|
+
or "post-fix audit clean" in skill_text.lower()
|
|
103
|
+
), "converged exit condition must require post_fix_audit_clean"
|