claude-dev-env 1.50.1 → 1.50.3
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/audit-contract.md +3 -3
- package/audit-rubrics/category_rubrics/category-e-dead-code.md +3 -2
- package/audit-rubrics/prompts/category-a-api-contracts.md +1 -1
- package/audit-rubrics/prompts/category-b-selector-engine-compat.md +2 -2
- package/audit-rubrics/prompts/category-c-resource-cleanup.md +2 -2
- package/audit-rubrics/prompts/category-d-scoping-and-ordering.md +2 -2
- package/audit-rubrics/prompts/category-e-dead-code.md +5 -4
- package/audit-rubrics/prompts/category-f-silent-failures.md +2 -2
- package/audit-rubrics/prompts/category-g-bounds-and-overflow.md +2 -2
- package/audit-rubrics/prompts/category-h-security-boundaries.md +2 -2
- package/audit-rubrics/prompts/category-i-concurrency.md +2 -2
- package/audit-rubrics/prompts/category-j-code-rules-compliance.md +2 -2
- package/audit-rubrics/prompts/category-k-codebase-conflicts.md +2 -2
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/code_rules_annotations_length.py +167 -0
- package/hooks/blocking/code_rules_banned_identifiers.py +385 -0
- package/hooks/blocking/code_rules_boolean_mustcheck.py +350 -0
- package/hooks/blocking/code_rules_comments.py +337 -0
- package/hooks/blocking/code_rules_constants_config.py +252 -0
- package/hooks/blocking/code_rules_docstrings.py +308 -0
- package/hooks/blocking/code_rules_enforcer.py +98 -5807
- package/hooks/blocking/code_rules_imports_logging.py +276 -0
- package/hooks/blocking/code_rules_magic_values.py +180 -0
- package/hooks/blocking/code_rules_mock_completeness.py +295 -0
- package/hooks/blocking/code_rules_naming_collection.py +264 -0
- package/hooks/blocking/code_rules_optional_params.py +288 -0
- package/hooks/blocking/code_rules_paths_syspath.py +186 -0
- package/hooks/blocking/code_rules_probe_chains.py +305 -0
- package/hooks/blocking/code_rules_probe_detection.py +257 -0
- package/hooks/blocking/code_rules_probe_recording.py +225 -0
- package/hooks/blocking/code_rules_scope_binding.py +151 -0
- package/hooks/blocking/code_rules_shared.py +301 -0
- package/hooks/blocking/code_rules_string_magic.py +207 -0
- package/hooks/blocking/code_rules_test_assertions.py +226 -0
- package/hooks/blocking/code_rules_test_branching_except.py +181 -0
- package/hooks/blocking/code_rules_test_isolation.py +341 -0
- package/hooks/blocking/code_rules_type_escape.py +341 -0
- package/hooks/blocking/code_rules_typeddict_stub.py +305 -0
- package/hooks/blocking/code_rules_unused_imports.py +256 -0
- package/hooks/blocking/tdd_enforcer.py +31 -0
- package/hooks/blocking/test_code_rules_constants_config.py +26 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_noun_word.py +5 -2
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -5
- package/hooks/blocking/test_code_rules_enforcer_comment_string_awareness.py +21 -15
- package/hooks/blocking/test_code_rules_enforcer_config_path.py +20 -16
- package/hooks/blocking/test_code_rules_enforcer_exempt_marker_chained.py +4 -2
- package/hooks/blocking/test_code_rules_enforcer_function_length.py +18 -13
- package/hooks/blocking/test_code_rules_enforcer_hardcoded_user_path.py +1 -2
- package/hooks/blocking/test_code_rules_enforcer_ignored_must_check_return.py +22 -12
- package/hooks/blocking/test_code_rules_enforcer_split_annotations_length.py +55 -0
- package/hooks/blocking/test_code_rules_enforcer_split_banned.py +170 -0
- package/hooks/blocking/test_code_rules_enforcer_split_comments.py +60 -0
- package/hooks/blocking/test_code_rules_enforcer_split_config_path.py +52 -0
- package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +236 -0
- package/hooks/blocking/test_code_rules_enforcer_split_entry_1.py +296 -0
- package/hooks/blocking/test_code_rules_enforcer_split_entry_2.py +238 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_1.py +271 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_2.py +283 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_3.py +268 -0
- package/hooks/blocking/test_code_rules_enforcer_split_isolation_4.py +85 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_1.py +303 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mocks_2.py +111 -0
- package/hooks/blocking/test_code_rules_enforcer_split_mustcheck.py +87 -0
- package/hooks/blocking/test_code_rules_enforcer_split_naming.py +107 -0
- package/hooks/blocking/test_code_rules_enforcer_split_optional_params.py +325 -0
- package/hooks/blocking/test_code_rules_enforcer_split_paths_syspath.py +110 -0
- package/hooks/blocking/test_code_rules_enforcer_split_shared.py +44 -0
- package/hooks/blocking/test_code_rules_enforcer_split_string_magic.py +55 -0
- package/hooks/blocking/test_code_rules_enforcer_split_test_assertions.py +56 -0
- package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +21 -15
- package/hooks/blocking/test_code_rules_paths_syspath.py +26 -0
- package/hooks/blocking/test_tdd_enforcer.py +116 -0
- package/hooks/hooks_constants/blocking_check_limits.py +3 -0
- package/hooks/hooks_constants/code_rules_enforcer_constants.py +8 -0
- package/hooks/hooks_constants/sys_path_insert_constants.py +1 -0
- package/package.json +1 -1
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +13 -7
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/path_resolver_constants.py +21 -11
- package/skills/_shared/pr-loop/scripts/test_build_audit_prompt.py +92 -0
- package/skills/bugteam/CONSTRAINTS.md +1 -1
- package/skills/bugteam/PROMPTS.md +20 -48
- package/skills/bugteam/SKILL.md +5 -5
- package/skills/bugteam/reference/audit-and-teammates.md +1 -1
- package/skills/bugteam/reference/audit-contract.md +4 -4
- package/skills/bugteam/reference/design-rationale.md +1 -1
- package/skills/findbugs/SKILL.md +21 -12
- package/skills/fixbugs/SKILL.md +1 -1
- package/skills/qbug/SKILL.md +5 -5
- package/skills/qbug/test_qbug_skill_audit_schema.py +13 -23
- package/skills/refine/SKILL.md +1 -1
- package/hooks/blocking/test_code_rules_enforcer.py +0 -2669
|
@@ -574,3 +574,119 @@ def test_is_inside_dotclaude_segment_helper_matches_only_exact_segments() -> Non
|
|
|
574
574
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("C:\\Users\\dev\\.claude\\agent.py") is True
|
|
575
575
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("/src/my.claude.helpers.py") is False
|
|
576
576
|
assert _PRODUCTION_MODULE._is_inside_dotclaude_segment("/src/app/service.py") is False
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def test_should_offer_split_family_test_files_as_candidates_for_code_rules_module(
|
|
580
|
+
tmp_path: Path,
|
|
581
|
+
) -> None:
|
|
582
|
+
sandbox = _sandbox(tmp_path)
|
|
583
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
584
|
+
string_magic_family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
585
|
+
string_magic_family_test.write_text("def test_string_magic(): pass\n")
|
|
586
|
+
banned_family_test = sandbox / "test_code_rules_enforcer_split_banned.py"
|
|
587
|
+
banned_family_test.write_text("def test_banned(): pass\n")
|
|
588
|
+
|
|
589
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
590
|
+
|
|
591
|
+
assert string_magic_family_test in all_candidates
|
|
592
|
+
assert banned_family_test in all_candidates
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
def test_should_keep_plain_stem_candidates_first_for_code_rules_module(
|
|
596
|
+
tmp_path: Path,
|
|
597
|
+
) -> None:
|
|
598
|
+
sandbox = _sandbox(tmp_path)
|
|
599
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
600
|
+
family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
601
|
+
family_test.write_text("def test_string_magic(): pass\n")
|
|
602
|
+
|
|
603
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
604
|
+
|
|
605
|
+
assert all_candidates[0] == sandbox / "test_code_rules_magic_values.py"
|
|
606
|
+
assert all_candidates[1] == sandbox / "code_rules_magic_values_test.py"
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def test_should_not_offer_split_family_candidates_for_non_code_rules_module(
|
|
610
|
+
tmp_path: Path,
|
|
611
|
+
) -> None:
|
|
612
|
+
sandbox = _sandbox(tmp_path)
|
|
613
|
+
production_module = sandbox / "orders.py"
|
|
614
|
+
family_test = sandbox / "test_code_rules_enforcer_split_string_magic.py"
|
|
615
|
+
family_test.write_text("def test_string_magic(): pass\n")
|
|
616
|
+
|
|
617
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
618
|
+
|
|
619
|
+
assert family_test not in all_candidates
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
def test_should_add_no_split_family_candidates_when_directory_has_none(
|
|
623
|
+
tmp_path: Path,
|
|
624
|
+
) -> None:
|
|
625
|
+
sandbox = _sandbox(tmp_path)
|
|
626
|
+
production_module = sandbox / "code_rules_magic_values.py"
|
|
627
|
+
|
|
628
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
629
|
+
|
|
630
|
+
expected_stem_candidates = [
|
|
631
|
+
sandbox / "test_code_rules_magic_values.py",
|
|
632
|
+
sandbox / "code_rules_magic_values_test.py",
|
|
633
|
+
]
|
|
634
|
+
assert all_candidates == expected_stem_candidates
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def test_should_not_offer_family_candidates_for_code_ruleset_stem(
|
|
638
|
+
tmp_path: Path,
|
|
639
|
+
) -> None:
|
|
640
|
+
sandbox = _sandbox(tmp_path)
|
|
641
|
+
production_module = sandbox / "code_ruleset.py"
|
|
642
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example.py"
|
|
643
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
644
|
+
|
|
645
|
+
all_candidates = _PRODUCTION_MODULE.candidate_test_paths_for(production_module)
|
|
646
|
+
|
|
647
|
+
expected_stem_candidates = [
|
|
648
|
+
sandbox / "test_code_ruleset.py",
|
|
649
|
+
sandbox / "code_ruleset_test.py",
|
|
650
|
+
]
|
|
651
|
+
assert all_candidates == expected_stem_candidates
|
|
652
|
+
assert family_test not in all_candidates
|
|
653
|
+
|
|
654
|
+
|
|
655
|
+
def test_should_allow_code_rules_edit_when_fresh_split_family_sibling_exists(
|
|
656
|
+
tmp_path: Path,
|
|
657
|
+
) -> None:
|
|
658
|
+
sandbox = _sandbox(tmp_path)
|
|
659
|
+
production_module = sandbox / "code_rules_example.py"
|
|
660
|
+
production_module.write_text("def detect() -> None:\n return None\n")
|
|
661
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example_concern.py"
|
|
662
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
663
|
+
|
|
664
|
+
payload = _make_edit_payload(
|
|
665
|
+
production_module,
|
|
666
|
+
old_string="return None",
|
|
667
|
+
new_string="return None # adjusted",
|
|
668
|
+
)
|
|
669
|
+
completed = _run_hook_with_payload(payload)
|
|
670
|
+
|
|
671
|
+
assert _decision_from(completed) == "allow"
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def test_should_deny_code_rules_edit_when_split_family_sibling_is_stale(
|
|
675
|
+
tmp_path: Path,
|
|
676
|
+
) -> None:
|
|
677
|
+
sandbox = _sandbox(tmp_path)
|
|
678
|
+
production_module = sandbox / "code_rules_example.py"
|
|
679
|
+
production_module.write_text("def detect() -> None:\n return None\n")
|
|
680
|
+
family_test = sandbox / "test_code_rules_enforcer_split_example_concern.py"
|
|
681
|
+
family_test.write_text("def test_detects_example(): pass\n")
|
|
682
|
+
stale_timestamp = time.time() - STALE_MTIME_OFFSET_SECONDS
|
|
683
|
+
os.utime(family_test, (stale_timestamp, stale_timestamp))
|
|
684
|
+
|
|
685
|
+
payload = _make_edit_payload(
|
|
686
|
+
production_module,
|
|
687
|
+
old_string="return None",
|
|
688
|
+
new_string="return None # adjusted",
|
|
689
|
+
)
|
|
690
|
+
completed = _run_hook_with_payload(payload)
|
|
691
|
+
|
|
692
|
+
assert _decision_from(completed) == "deny"
|
|
@@ -20,6 +20,9 @@ MAX_DOCSTRING_ARGS_SIGNATURE_ISSUES: int = 5
|
|
|
20
20
|
MAX_IGNORED_MUST_CHECK_RETURN_ISSUES: int = 5
|
|
21
21
|
MAX_TYPE_ESCAPE_HATCH_ISSUES: int = 5
|
|
22
22
|
MAX_THIN_WRAPPER_ISSUES: int = 1
|
|
23
|
+
MAX_LOGGING_FSTRING_ISSUES: int = 3
|
|
24
|
+
MAX_WINDOWS_API_NONE_ISSUES: int = 3
|
|
25
|
+
MAX_E2E_TEST_NAMING_ISSUES: int = 3
|
|
23
26
|
DOCSTRING_TRIVIAL_FUNCTION_BODY_LINE_LIMIT: int = 3
|
|
24
27
|
|
|
25
28
|
ALL_BARE_EXCEPT_BANNED_HANDLER_NAMES: frozenset[str] = frozenset({"Exception", "BaseException"})
|
|
@@ -79,6 +79,14 @@ NOT_INSIDE_TYPE_CHECKING_BLOCK = -1
|
|
|
79
79
|
TRIPLE_QUOTE_PARITY_DIVISOR = 2
|
|
80
80
|
TRIPLE_DOUBLE_QUOTE_DELIMITER = '"""'
|
|
81
81
|
TRIPLE_SINGLE_QUOTE_DELIMITER = "'''"
|
|
82
|
+
MAX_MAGIC_VALUE_ISSUES = 3
|
|
83
|
+
STRING_LITERAL_QUOTE_PAIR_LENGTH = 2
|
|
84
|
+
MINIMUM_FSTRING_LITERAL_LENGTH = 2
|
|
85
|
+
MAX_FSTRING_STRUCTURAL_LITERAL_ISSUES = 100
|
|
86
|
+
ALL_ALLOWED_MAGIC_NUMBER_LITERALS: frozenset[str] = frozenset({"0", "1", "-1", "0.0", "1.0"})
|
|
87
|
+
ALL_NON_MAGIC_FSTRING_STRIPPED_VALUES: frozenset[str] = frozenset({"", "True", "False"})
|
|
88
|
+
DUPLICATED_FORMAT_MINIMUM_REPETITION_COUNT = 3
|
|
89
|
+
DUPLICATED_FORMAT_MINIMUM_LITERAL_CHARACTER_COUNT = 5
|
|
82
90
|
FILE_GLOBAL_UPPER_SNAKE_PATTERN = re.compile(r"^_?[A-Z][A-Z0-9_]*$")
|
|
83
91
|
|
|
84
92
|
ALL_COLLECTION_TYPE_NAMES: frozenset[str] = frozenset({
|
package/package.json
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"""Emit the complete AUDIT spawn prompt XML to stdout.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
<
|
|
3
|
+
Builds <context> and <scope> from CLI args; <bug_categories>,
|
|
4
|
+
<rubric_reference>, and <constraints> come from the shared constants in
|
|
5
|
+
skills_pr_loop_constants; <comment_posting> and <output_format> are built
|
|
6
|
+
inline.
|
|
5
7
|
|
|
6
8
|
Usage:
|
|
7
9
|
python scripts/build_audit_prompt.py --owner jl-cmd --repo claude-code-config --pr-number 422 --loop 1 --head-ref feat/branch --base-ref main --worktree-path <PATH> --run-temp-dir <PATH>
|
|
@@ -22,6 +24,7 @@ from _xml_utils import emit_pretty_xml
|
|
|
22
24
|
from skills_pr_loop_constants.path_resolver_constants import (
|
|
23
25
|
ALL_AUDIT_CATEGORY_ENTRIES,
|
|
24
26
|
ALL_AUDIT_CONSTRAINT_TEXTS,
|
|
27
|
+
AUDIT_RUBRIC_REFERENCE_TEXT,
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
|
|
@@ -74,6 +77,9 @@ def build_audit_prompt_xml(
|
|
|
74
77
|
cat_elem = SubElement(bug_categories, "category", {"id": each_category_id})
|
|
75
78
|
cat_elem.text = each_category_label
|
|
76
79
|
|
|
80
|
+
rubric_reference = SubElement(root, "rubric_reference")
|
|
81
|
+
rubric_reference.text = AUDIT_RUBRIC_REFERENCE_TEXT
|
|
82
|
+
|
|
77
83
|
constraints = SubElement(root, "constraints")
|
|
78
84
|
for each_constraint in ALL_AUDIT_CONSTRAINT_TEXTS:
|
|
79
85
|
SubElement(constraints, "constraint").text = each_constraint
|
|
@@ -167,12 +173,12 @@ def main(all_arguments: list[str]) -> int:
|
|
|
167
173
|
xml_output = emit_audit_prompt(
|
|
168
174
|
owner=arguments.owner,
|
|
169
175
|
repo=arguments.repo,
|
|
170
|
-
pr_number=
|
|
176
|
+
pr_number=arguments.pr_number,
|
|
171
177
|
loop=arguments.loop,
|
|
172
|
-
head_ref=
|
|
173
|
-
base_ref=
|
|
174
|
-
worktree_path=
|
|
175
|
-
run_temp_dir=
|
|
178
|
+
head_ref=arguments.head_ref,
|
|
179
|
+
base_ref=arguments.base_ref,
|
|
180
|
+
worktree_path=arguments.worktree_path,
|
|
181
|
+
run_temp_dir=arguments.run_temp_dir,
|
|
176
182
|
)
|
|
177
183
|
sys.stdout.write(xml_output)
|
|
178
184
|
return 0
|
|
@@ -29,19 +29,29 @@ ALL_AUDIT_CONSTRAINT_TEXTS = [
|
|
|
29
29
|
]
|
|
30
30
|
|
|
31
31
|
ALL_AUDIT_CATEGORY_ENTRIES = [
|
|
32
|
-
("A", "
|
|
33
|
-
("B", "
|
|
34
|
-
("C", "
|
|
35
|
-
("D", "
|
|
36
|
-
("E", "
|
|
37
|
-
("F", "
|
|
38
|
-
("G", "
|
|
39
|
-
("H", "
|
|
40
|
-
("I", "
|
|
41
|
-
("J", "
|
|
42
|
-
("K", "Codebase conflicts
|
|
32
|
+
("A", "API contract verification"),
|
|
33
|
+
("B", "Selector / query / engine compatibility"),
|
|
34
|
+
("C", "Resource cleanup and lifecycle"),
|
|
35
|
+
("D", "Variable scoping, ordering, and unbound references"),
|
|
36
|
+
("E", "Dead code and unused imports"),
|
|
37
|
+
("F", "Silent failures"),
|
|
38
|
+
("G", "Off-by-one, bounds, integer overflow"),
|
|
39
|
+
("H", "Security boundaries"),
|
|
40
|
+
("I", "Concurrency hazards"),
|
|
41
|
+
("J", "CODE_RULES.md compliance"),
|
|
42
|
+
("K", "Codebase conflicts (incomplete propagation)"),
|
|
43
|
+
("L", "Behavior-equivalence for refactors"),
|
|
44
|
+
("M", "Producer/consumer cardinality vs collection-type contract"),
|
|
45
|
+
("N", "Test-name scenario verifier"),
|
|
43
46
|
]
|
|
44
47
|
|
|
48
|
+
AUDIT_RUBRIC_REFERENCE_TEXT = (
|
|
49
|
+
"The category list above is a summary. The binding definition of each "
|
|
50
|
+
"category is its rubric file under $HOME/.claude/audit-rubrics/category_rubrics/ "
|
|
51
|
+
"(ready-to-send prompt variants under $HOME/.claude/audit-rubrics/prompts/). "
|
|
52
|
+
"Read the rubric files before auditing."
|
|
53
|
+
)
|
|
54
|
+
|
|
45
55
|
ALL_FIX_EXECUTION_STEPS = [
|
|
46
56
|
"Read the finding and verify it against the current file at file:line.",
|
|
47
57
|
"Write a failing test that reproduces the bug.",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"""Tests pinning build_audit_prompt's emitted A-N category taxonomy."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import re
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from types import ModuleType
|
|
10
|
+
from xml.etree.ElementTree import Element
|
|
11
|
+
|
|
12
|
+
_SCRIPTS_DIR = Path(__file__).resolve().parent
|
|
13
|
+
if str(_SCRIPTS_DIR) not in sys.path:
|
|
14
|
+
sys.path.insert(0, str(_SCRIPTS_DIR))
|
|
15
|
+
|
|
16
|
+
from skills_pr_loop_constants.path_resolver_constants import (
|
|
17
|
+
ALL_AUDIT_CATEGORY_ENTRIES,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
_CATEGORY_RUBRICS_DIR = _SCRIPTS_DIR.parents[3] / "audit-rubrics" / "category_rubrics"
|
|
21
|
+
_HEADING_PATTERN = re.compile(r"^# Category ([A-N]) — (.+)$")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _load_build_audit_prompt() -> ModuleType:
|
|
25
|
+
module_path = _SCRIPTS_DIR / "build_audit_prompt.py"
|
|
26
|
+
spec = importlib.util.spec_from_file_location("build_audit_prompt", module_path)
|
|
27
|
+
assert spec is not None
|
|
28
|
+
assert spec.loader is not None
|
|
29
|
+
module = importlib.util.module_from_spec(spec)
|
|
30
|
+
sys.modules["build_audit_prompt"] = module
|
|
31
|
+
spec.loader.exec_module(module)
|
|
32
|
+
return module
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
build_audit_prompt = _load_build_audit_prompt()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _rubric_label_by_letter() -> dict[str, str]:
|
|
39
|
+
assert _CATEGORY_RUBRICS_DIR.is_dir(), f"Missing rubric directory: {_CATEGORY_RUBRICS_DIR}"
|
|
40
|
+
all_labels: dict[str, str] = {}
|
|
41
|
+
for each_rubric_file in sorted(_CATEGORY_RUBRICS_DIR.glob("category-*.md")):
|
|
42
|
+
all_rubric_lines = each_rubric_file.read_text(encoding="utf-8").splitlines()
|
|
43
|
+
assert all_rubric_lines, f"Empty rubric file: {each_rubric_file}"
|
|
44
|
+
each_match = _HEADING_PATTERN.match(all_rubric_lines[0])
|
|
45
|
+
assert each_match is not None, f"Heading pattern not matched in {each_rubric_file}"
|
|
46
|
+
all_labels[each_match.group(1)] = each_match.group(2)
|
|
47
|
+
return all_labels
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _build_audit_root() -> Element:
|
|
51
|
+
return build_audit_prompt.build_audit_prompt_xml(
|
|
52
|
+
owner="jl-cmd",
|
|
53
|
+
repo="claude-code-config",
|
|
54
|
+
pr_number=422,
|
|
55
|
+
loop=1,
|
|
56
|
+
head_ref="feat/branch",
|
|
57
|
+
base_ref="main",
|
|
58
|
+
worktree_path=Path("/tmp/bugteam-pr-422/worktree"),
|
|
59
|
+
run_temp_dir=Path("/tmp/bugteam-pr-422"),
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_bug_categories_carry_ids_a_through_n_in_order() -> None:
|
|
64
|
+
root = _build_audit_root()
|
|
65
|
+
bug_categories = root.find("bug_categories")
|
|
66
|
+
assert bug_categories is not None
|
|
67
|
+
all_emitted_ids = [each_category.get("id") for each_category in bug_categories]
|
|
68
|
+
all_expected_ids = list("ABCDEFGHIJKLMN")
|
|
69
|
+
assert all_emitted_ids == all_expected_ids
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_emitted_category_labels_match_constant_entries() -> None:
|
|
73
|
+
root = _build_audit_root()
|
|
74
|
+
bug_categories = root.find("bug_categories")
|
|
75
|
+
assert bug_categories is not None
|
|
76
|
+
label_by_id = {
|
|
77
|
+
each_category.get("id"): each_category.text for each_category in bug_categories
|
|
78
|
+
}
|
|
79
|
+
for each_category_id, each_category_label in ALL_AUDIT_CATEGORY_ENTRIES:
|
|
80
|
+
assert label_by_id[each_category_id] == each_category_label
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_category_labels_match_rubric_file_headings() -> None:
|
|
84
|
+
assert dict(ALL_AUDIT_CATEGORY_ENTRIES) == _rubric_label_by_letter()
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def test_rubric_reference_element_names_category_rubrics_directory() -> None:
|
|
88
|
+
root = _build_audit_root()
|
|
89
|
+
rubric_reference = root.find("rubric_reference")
|
|
90
|
+
assert rubric_reference is not None
|
|
91
|
+
assert rubric_reference.text is not None
|
|
92
|
+
assert "audit-rubrics/category_rubrics" in rubric_reference.text
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Constraints
|
|
4
4
|
|
|
5
|
-
- **Full A–
|
|
5
|
+
- **Full A–N audit every loop, no exceptions.** PR size, "focused audit," "team overhead," "CODE_RULES already passed" — not valid reasons. Empty `<findings/>` for any category is a valid result. The audit agent walks all A–N rubrics each loop.
|
|
6
6
|
- **One run per invocation, multi-PR supported.** All PRs in a single /bugteam invocation share one `run_temp_dir`. Per-PR identity lives in the subagent name prefix (`bugfind-pr<N>-loop<L>` / `bugfix-pr<N>-loop<L>`) and the `<run_temp_dir>/pr-<N>/` subfolder containing that PR's git worktree, diff patches, and outcome XML files.
|
|
7
7
|
- **Grant before any spawn, revoke before any return.** Step 0 grants project `.claude/**` permissions; Step 5 revokes. Both are mandatory. Revoke runs on every exit path including error, cap-reached, and stuck.
|
|
8
8
|
- **Fresh subagent per loop.** Both bugfind and bugfix are spawned new each loop. Reusing a subagent across loops accumulates context inside that subagent's window — defeats clean-room.
|
|
@@ -44,58 +44,30 @@ cd into `<worktree_path>` before any git or file operation.
|
|
|
44
44
|
a `---` separator and a worked example against an authentic PR below —
|
|
45
45
|
are in `$HOME/.claude/audit-rubrics/prompts/`):
|
|
46
46
|
|
|
47
|
-
A. API contract verification
|
|
47
|
+
A. API contract verification
|
|
48
48
|
B. Selector / query / engine compatibility
|
|
49
|
-
C. Resource cleanup and lifecycle
|
|
49
|
+
C. Resource cleanup and lifecycle
|
|
50
50
|
D. Variable scoping, ordering, and unbound references
|
|
51
|
-
E. Dead code
|
|
52
|
-
F. Silent failures
|
|
53
|
-
G. Off-by-one, bounds,
|
|
54
|
-
H. Security boundaries
|
|
55
|
-
I. Concurrency hazards
|
|
56
|
-
J.
|
|
57
|
-
K. Codebase conflicts
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
L. Behavior-equivalence for refactors. When the PR rewrites an existing
|
|
62
|
-
function (especially an enforcement check, parser, or path classifier),
|
|
63
|
-
compare the rewrite's edge-case handling against the sibling implementation
|
|
64
|
-
at the same git commit base. Pin the historically-valid inputs in a
|
|
65
|
-
`KNOWN_GOOD_INPUTS` table and assert each still passes. Cited in audits:
|
|
66
|
-
ccc#479 F1 (`#noqa` no-space variant dropped after a tokenize-based
|
|
67
|
-
refactor); ccc#479 F4 (bare `#` lookalike misclassified after refactor);
|
|
68
|
-
ccc#479 F5 (inline `#!` lookalike misclassified); ccc#479 F6 (early-exit
|
|
69
|
-
invariant dropped); ccc#472 F44 (`startswith('## Problem')` too loose vs
|
|
70
|
-
the sibling regex shape).
|
|
71
|
-
M. Producer/consumer cardinality vs collection-type contract. For each new
|
|
72
|
-
function returning `list[X]`, `Sequence[X]`, or `Iterable[X]`, ask
|
|
73
|
-
whether the return can contain duplicates and whether any downstream
|
|
74
|
-
consumer treats the value as a set. Subprocess-stdout parsers must return
|
|
75
|
-
`frozenset[Path]` or `dict.fromkeys`-deduplicated `list[Path]`.
|
|
76
|
-
Functions whose consumer is itself an `extend(...)` into a list pass;
|
|
77
|
-
functions with explicit "duplicates preserved" docstring text pass.
|
|
78
|
-
Cited in audits: pa#143 F10 (`_extract_paths_from_everything_cli_stdout`
|
|
79
|
-
duplicates → `RuntimeError` — the only High-severity crash bug in the
|
|
80
|
-
audit set); pa#136 F30 / F32 (duplicate content_id rows submit twice;
|
|
81
|
-
writeback ignores content_id key).
|
|
82
|
-
N. Test-name claims a scenario the body does not enter. Tests named
|
|
83
|
-
`test_*_at_*`, `test_*_under_*`, `test_*_when_*`, and `test_*_with_*`
|
|
84
|
-
must, via monkeypatch / fixture inspection, demonstrate the named
|
|
85
|
-
condition is in effect when the system under test runs. Path-decision
|
|
86
|
-
functions (registered in `*_path_exemptions.py` / `is_*_path` /
|
|
87
|
-
`_resolve_*_path` modules) must ship with a parametric matrix of
|
|
88
|
-
canonical edge cases (empty string, single filename, tilde, UNC,
|
|
89
|
-
drive-letter, symlinked, `..`-containing, trailing-slash). Tests with
|
|
90
|
-
neutral names (`test_returns_empty_list_on_x`) are unaffected. Cited
|
|
91
|
-
in audits: ccc#476 F5 / F21 / F23 / F26 / F27 (cross-platform
|
|
92
|
-
scenarios never exercised under the claimed conditions); pa#135 F11 /
|
|
93
|
-
F15 (string-shape and integration tests that exercise only the no-op
|
|
94
|
-
branch); pa#136 F50 (`<substring> not in executed_sql` assertion that
|
|
95
|
-
cannot fail because the substring shape never matches the real
|
|
96
|
-
fragment).
|
|
51
|
+
E. Dead code and unused imports
|
|
52
|
+
F. Silent failures
|
|
53
|
+
G. Off-by-one, bounds, integer overflow
|
|
54
|
+
H. Security boundaries
|
|
55
|
+
I. Concurrency hazards
|
|
56
|
+
J. CODE_RULES.md compliance
|
|
57
|
+
K. Codebase conflicts (incomplete propagation)
|
|
58
|
+
L. Behavior-equivalence for refactors
|
|
59
|
+
M. Producer/consumer cardinality vs collection-type contract
|
|
60
|
+
N. Test-name scenario verifier
|
|
97
61
|
</bug_categories>
|
|
98
62
|
|
|
63
|
+
<rubric_reference>
|
|
64
|
+
The category list above is a summary. The binding definition of each
|
|
65
|
+
category is its rubric file under
|
|
66
|
+
`$HOME/.claude/audit-rubrics/category_rubrics/` (ready-to-send prompt
|
|
67
|
+
variants under `$HOME/.claude/audit-rubrics/prompts/`). Read the rubric
|
|
68
|
+
files before auditing.
|
|
69
|
+
</rubric_reference>
|
|
70
|
+
|
|
99
71
|
<constraints>
|
|
100
72
|
- Read-only on source code: the audit does not modify any source file.
|
|
101
73
|
- Cite file:line for every finding.
|
package/skills/bugteam/SKILL.md
CHANGED
|
@@ -11,10 +11,10 @@ description: >-
|
|
|
11
11
|
# Bugteam
|
|
12
12
|
|
|
13
13
|
Audit–fix until convergence. Bugfind: `code-quality-agent`, fresh context each
|
|
14
|
-
loop, auditing all A–
|
|
14
|
+
loop, auditing all A–N categories. Bugfix: `clean-coder`. Hard cap: 20 audit
|
|
15
15
|
loops. Grant `.claude/**` at start, revoke always at end.
|
|
16
16
|
|
|
17
|
-
The audit agent loads the A–
|
|
17
|
+
The audit agent loads the A–N category rubrics from
|
|
18
18
|
`$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` alongside
|
|
19
19
|
[`PROMPTS.md`](PROMPTS.md) and produces a single outcome XML per loop.
|
|
20
20
|
|
|
@@ -146,7 +146,7 @@ end-to-end mental model before starting Step 0.
|
|
|
146
146
|
| Posting the end-of-pass audit review via `post_audit_thread.py` (APPROVE on CLEAN — the request event; GitHub stores it as `state=APPROVED` — REQUEST_CHANGES with inline anchored comments on DIRTY) | [§ Audit posting](#audit-posting) |
|
|
147
147
|
| Posting per-finding fix replies via GitHub MCP `add_reply_to_pull_request_comment` (rendered with the unified template at [`_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md)) | [reference/github-pr-reviews.md](reference/github-pr-reviews.md) |
|
|
148
148
|
| Teardown, PR description rewrite via `pr-description-writer`, permission revoke, final report | [reference/teardown-publish-permissions.md](reference/teardown-publish-permissions.md) |
|
|
149
|
-
| Spawn-prompt XML, A–
|
|
149
|
+
| Spawn-prompt XML, A–N category bindings, outcome XML schemas | [PROMPTS.md](PROMPTS.md) |
|
|
150
150
|
| Per-category audit content (sub-buckets, decision criteria, ready-to-send Variant C templates) | `$HOME/.claude/audit-rubrics/{category_rubrics,prompts}/` |
|
|
151
151
|
| Invariants and design rationale | [CONSTRAINTS.md](CONSTRAINTS.md), [reference/design-rationale.md](reference/design-rationale.md) |
|
|
152
152
|
| Audit-contract finding shape (Shape A / B), Haiku secondary, post-fix self-audit | [reference/audit-contract.md](reference/audit-contract.md) |
|
|
@@ -159,11 +159,11 @@ end-to-end mental model before starting Step 0.
|
|
|
159
159
|
- `SKILL.md` — this hub.
|
|
160
160
|
- `reference/` — workflow detail per situation.
|
|
161
161
|
- `scripts/` — utility scripts executed, not loaded as primary context.
|
|
162
|
-
- `PROMPTS.md` — spawn XML, A–
|
|
162
|
+
- `PROMPTS.md` — spawn XML, A–N category bindings, outcome schemas.
|
|
163
163
|
- `CONSTRAINTS.md` — invariants.
|
|
164
164
|
- `EXAMPLES.md` — exit scenarios.
|
|
165
165
|
- `sources.md` — doc URLs and verbatim quotes.
|
|
166
166
|
- `~/.claude/audit-rubrics/` — installed by `npx claude-dev-env` from
|
|
167
|
-
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–
|
|
167
|
+
`packages/claude-dev-env/audit-rubrics/`; the audit agent reads all A–N
|
|
168
168
|
rubrics under `category_rubrics/` and prompts under `prompts/`. Required
|
|
169
169
|
at audit time alongside `PROMPTS.md`.
|
|
@@ -21,7 +21,7 @@ Each finding an audit produces MUST be one of exactly two shapes.
|
|
|
21
21
|
"id": "loop<L>-<K>",
|
|
22
22
|
"file": "path/relative/to/repo/root.py",
|
|
23
23
|
"line": 123,
|
|
24
|
-
"category": "A | B | C | D | E | F | G | H | I | J | K",
|
|
24
|
+
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
|
|
25
25
|
"severity": "P0 | P1 | P2",
|
|
26
26
|
"excerpt": "verbatim code snippet from the offending line(s)",
|
|
27
27
|
"failure_mode": "one sentence describing what goes wrong and when",
|
|
@@ -37,7 +37,7 @@ Used when an audit investigates a category and does NOT find a bug. Bare "verifi
|
|
|
37
37
|
|
|
38
38
|
```json
|
|
39
39
|
{
|
|
40
|
-
"category": "A | B | C | D | E | F | G | H | I | J | K",
|
|
40
|
+
"category": "A | B | C | D | E | F | G | H | I | J | K | L | M | N",
|
|
41
41
|
"files_opened": ["file1.py", "file2.py"],
|
|
42
42
|
"lines_quoted": [
|
|
43
43
|
{"file": "file1.py", "line": 88, "text": "verbatim line content"}
|
|
@@ -89,7 +89,7 @@ After the primary finding list is complete, every audit runs a second pass again
|
|
|
89
89
|
|
|
90
90
|
The audit must either produce new Shape A findings citing new file:line references not present in the first pass, or cite explicit Shape B adversarial-probe entries for each category it re-examined. An adversarial pass that returns "nothing new, confident first pass was complete" is REJECTED — produce evidence or findings, not confidence.
|
|
91
91
|
|
|
92
|
-
For `/bugteam`, the single audit agent provides per-category coverage by walking all A–
|
|
92
|
+
For `/bugteam`, the single audit agent provides per-category coverage by walking all A–N rubrics in one invocation.
|
|
93
93
|
|
|
94
94
|
## Merge rules
|
|
95
95
|
|
|
@@ -113,7 +113,7 @@ Sequence:
|
|
|
113
113
|
3. Run `py_compile` (or language-equivalent) on each modified file.
|
|
114
114
|
4. Compute `fix_diff` against pre-fix contents for the modified set.
|
|
115
115
|
5. Run `bugteam_code_rules_gate.py` with explicit paths for every modified file.
|
|
116
|
-
6. Spawn a scoped audit of `fix_diff` with full A–
|
|
116
|
+
6. Spawn a scoped audit of `fix_diff` with full A–N rigor, Shape A/B contract, adversarial pass, AND Haiku secondary in parallel (paranoid mode on post-fix).
|
|
117
117
|
7. Any new findings become same-loop fix-targets. Internal iteration count increments by one.
|
|
118
118
|
8. After 3 internal iterations with fresh findings each time, exit `stuck: post-fix audit not converging`.
|
|
119
119
|
9. Only when `gate_findings` empty AND `post_fix_findings` empty: `git add`, commit, push.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Core principle (expanded)
|
|
4
4
|
|
|
5
|
-
One audit agent (`code-quality-agent`, opus) walks all A–
|
|
5
|
+
One audit agent (`code-quality-agent`, opus) walks all A–N categories per loop. One fix agent (`clean-coder`, opus) addresses the audit's findings.
|
|
6
6
|
|
|
7
7
|
Fresh-spawn clean-room isolation: each `Agent` call creates a new subagent with its own context window and no access to prior conversation. After the subagent writes its outcome XML and self-terminates, the lead reads the file. Results never accumulate in the lead’s context beyond the XML artifact. Verbatim Anthropic quotes and URLs: [`../sources.md`](../sources.md).
|
|
8
8
|
|
package/skills/findbugs/SKILL.md
CHANGED
|
@@ -88,16 +88,25 @@ The XML prompt skeleton:
|
|
|
88
88
|
|
|
89
89
|
<bug_categories>
|
|
90
90
|
Investigate each explicitly:
|
|
91
|
-
A. API contract verification
|
|
91
|
+
A. API contract verification
|
|
92
92
|
B. Selector / query / engine compatibility
|
|
93
|
-
C. Resource cleanup and lifecycle
|
|
93
|
+
C. Resource cleanup and lifecycle
|
|
94
94
|
D. Variable scoping, ordering, and unbound references
|
|
95
95
|
E. Dead code and unused imports
|
|
96
|
-
F. Silent failures
|
|
97
|
-
G. Off-by-one, bounds,
|
|
98
|
-
H. Security boundaries
|
|
99
|
-
I. Concurrency hazards
|
|
100
|
-
J.
|
|
96
|
+
F. Silent failures
|
|
97
|
+
G. Off-by-one, bounds, integer overflow
|
|
98
|
+
H. Security boundaries
|
|
99
|
+
I. Concurrency hazards
|
|
100
|
+
J. CODE_RULES.md compliance
|
|
101
|
+
K. Codebase conflicts (incomplete propagation)
|
|
102
|
+
L. Behavior-equivalence for refactors
|
|
103
|
+
M. Producer/consumer cardinality vs collection-type contract
|
|
104
|
+
N. Test-name scenario verifier
|
|
105
|
+
|
|
106
|
+
The category list above is a summary. The binding definition of each
|
|
107
|
+
category is its rubric file under $HOME/.claude/audit-rubrics/category_rubrics/
|
|
108
|
+
(ready-to-send prompt variants under $HOME/.claude/audit-rubrics/prompts/).
|
|
109
|
+
Read the rubric files before auditing.
|
|
101
110
|
</bug_categories>
|
|
102
111
|
|
|
103
112
|
<constraints>
|
|
@@ -256,18 +265,18 @@ Want me to run /fixbugs for the P0/P1 findings?
|
|
|
256
265
|
User: `/findbugs`
|
|
257
266
|
Claude: [resolves PR #42 from current branch, fetches full diff, spawns code-quality-agent foreground with self-contained prompt, returns]
|
|
258
267
|
|
|
259
|
-
`1 P0 / 2 P1 / 0 P2 —
|
|
268
|
+
`1 P0 / 2 P1 / 0 P2 — 11 categories cleared`
|
|
260
269
|
|
|
261
270
|
`P0 — race condition on shared cache write`
|
|
262
|
-
` src/cache.py:88 — concurrent writers can both pass the existence check before either writes (category:
|
|
271
|
+
` src/cache.py:88 — concurrent writers can both pass the existence check before either writes (category: I — Concurrency hazards)`
|
|
263
272
|
|
|
264
273
|
`P1 — silent paste failure`
|
|
265
|
-
` utils/clipboard.py:33 — validated_paste returns success without verifying the post-paste state (category:
|
|
274
|
+
` utils/clipboard.py:33 — validated_paste returns success without verifying the post-paste state (category: F — Silent failures)`
|
|
266
275
|
|
|
267
276
|
`P1 — unbound variable on early-exception path`
|
|
268
|
-
` src/processor.py:283 — scheduling_log referenced after try/finally where it may be unbound (category: scoping)`
|
|
277
|
+
` src/processor.py:283 — scheduling_log referenced after try/finally where it may be unbound (category: D — Variable scoping, ordering, and unbound references)`
|
|
269
278
|
|
|
270
|
-
`Verified clean: API contract, selector compatibility, resource cleanup, dead code, off-by-one, security boundaries,
|
|
279
|
+
`Verified clean: API contract, selector compatibility, resource cleanup, dead code, off-by-one, security boundaries, CODE_RULES.md compliance, codebase conflicts, behavior-equivalence, producer/consumer cardinality, Test-name scenario verifier`
|
|
271
280
|
|
|
272
281
|
`Open questions: none`
|
|
273
282
|
|
package/skills/fixbugs/SKILL.md
CHANGED
|
@@ -33,7 +33,7 @@ Locate the most recent `/findbugs` output in the current conversation. For each
|
|
|
33
33
|
|
|
34
34
|
- Severity (`P0` / `P1` / `P2`)
|
|
35
35
|
- `file:line`
|
|
36
|
-
- Category (the A–
|
|
36
|
+
- Category (the A–N letter or category name `/findbugs` reported)
|
|
37
37
|
- One-sentence description as `/findbugs` wrote it
|
|
38
38
|
|
|
39
39
|
Apply the severity filter from `$ARGUMENTS` if present:
|
package/skills/qbug/SKILL.md
CHANGED
|
@@ -3,7 +3,7 @@ name: qbug
|
|
|
3
3
|
description: >-
|
|
4
4
|
Required baseline review for every new PR. Runs the /bugteam audit → fix →
|
|
5
5
|
commit → push cycle via one clean-coder subagent (not a full team), looping
|
|
6
|
-
until convergence or stuck. Uses the same CODE_RULES gate, A–
|
|
6
|
+
until convergence or stuck. Uses the same CODE_RULES gate, A–N category
|
|
7
7
|
rubric, and per-loop PR review shape as /bugteam — without TeamCreate,
|
|
8
8
|
teammates, per-loop clean-room, or a loop cap. Invoke /bugteam instead for
|
|
9
9
|
larger PRs that need per-loop bias isolation or a hard loop cap. Triggers:
|
|
@@ -21,7 +21,7 @@ Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE
|
|
|
21
21
|
|
|
22
22
|
- Pre-flight script: `${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/preflight.py`
|
|
23
23
|
- Code-rules gate script: `${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py`
|
|
24
|
-
- Bug category rubric A–
|
|
24
|
+
- Bug category rubric A–N: [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md#audit-spawn-prompt-xml-bugfind-teammate)
|
|
25
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)
|
|
26
26
|
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#audit-posting)
|
|
27
27
|
|
|
@@ -117,7 +117,7 @@ Agent(
|
|
|
117
117
|
subagent_type="code-quality-agent",
|
|
118
118
|
model="haiku",
|
|
119
119
|
description="qbug Haiku secondary audit for PR <number>",
|
|
120
|
-
prompt="<audit-only prompt: read the PR diff, apply A-
|
|
120
|
+
prompt="<audit-only prompt: read the PR diff, apply A-N categories from <categories_file>, return structured findings. No FIX, no git add, no git commit, no git push.>",
|
|
121
121
|
run_in_background=False
|
|
122
122
|
)
|
|
123
123
|
```
|
|
@@ -188,7 +188,7 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
188
188
|
|
|
189
189
|
- Read the patch file.
|
|
190
190
|
- Audit only added/modified lines. Read <categories_file> for the
|
|
191
|
-
A–
|
|
191
|
+
A–N category definitions; investigate each category explicitly.
|
|
192
192
|
- Follow the shared audit contract at
|
|
193
193
|
bugteam/reference/audit-contract.md. Per category: produce
|
|
194
194
|
either a Shape A structured finding or a Shape B structured
|
|
@@ -444,7 +444,7 @@ Delete the resolved `<qbug_temp_dir>` tree and any `.qbug-*.md` temp files in th
|
|
|
444
444
|
- **No loop cap.** Cycle runs until `converged`, `stuck`, or `error`. User can interrupt.
|
|
445
445
|
- **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
|
|
446
446
|
- **One commit per FIX action.** Linear branch, fast-forward push only.
|
|
447
|
-
- **Categories A–
|
|
447
|
+
- **Categories A–N.** Same rubric as [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md).
|
|
448
448
|
- **One review per loop.** Anchored findings as `comments[]`; unanchored findings surface in the calling skill's user-facing output (chat reply to the user) rather than in the PR review body.
|
|
449
449
|
- **PR description rewrite on every exit**, same as /bugteam Step 4.5.
|
|
450
450
|
- **Temp file cleanup on every exit path.**
|