claude-dev-env 1.38.1 → 1.40.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/CLAUDE.md +10 -36
- package/_shared/pr-loop/audit-reply-template.md +147 -0
- package/_shared/pr-loop/fix-protocol.md +25 -4
- package/_shared/pr-loop/gh-payloads.md +37 -50
- package/_shared/pr-loop/scripts/code_rules_gate.py +0 -60
- package/_shared/pr-loop/scripts/config/post_audit_thread_constants.py +199 -0
- package/_shared/pr-loop/scripts/config/reviews_disabled_constants.py +8 -0
- package/_shared/pr-loop/scripts/post_audit_thread.py +1242 -0
- package/_shared/pr-loop/scripts/preflight.py +129 -2
- package/_shared/pr-loop/scripts/reviews_disabled.py +59 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +0 -19
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread.py +1116 -0
- package/_shared/pr-loop/scripts/tests/test_post_audit_thread_constants.py +127 -0
- package/_shared/pr-loop/scripts/tests/test_preflight.py +41 -0
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +36 -0
- package/_shared/pr-loop/state-schema.md +1 -1
- package/agents/clean-coder.md +2 -2
- package/agents/pr-description-writer.md +150 -52
- package/bin/install.mjs +6 -7
- package/bin/install.test.mjs +8 -0
- package/commands/doc-gist.md +16 -0
- package/commands/plan.md +0 -2
- package/commands/review-plan.md +1 -1
- package/docs/CODE_RULES.md +122 -2
- package/docs/PR_DESCRIPTION_GUIDE.md +127 -64
- package/hooks/blocking/bot_mention_comment_blocker.py +75 -0
- package/hooks/blocking/code_rules_enforcer.py +1143 -129
- package/hooks/blocking/convergence_gate_blocker.py +130 -0
- package/hooks/blocking/destructive_command_blocker.py +74 -0
- package/hooks/blocking/gh_body_arg_blocker.py +30 -0
- package/hooks/blocking/md_to_html_blocker.py +119 -0
- package/hooks/blocking/pr_description_enforcer.py +57 -22
- package/hooks/blocking/test_bot_mention_comment_blocker.py +131 -0
- package/hooks/blocking/test_code_rules_enforcer.py +21 -0
- package/hooks/blocking/test_code_rules_enforcer_any_exempt_files.py +70 -0
- package/hooks/blocking/test_code_rules_enforcer_any_imports_and_cast.py +92 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_import_alias.py +143 -0
- package/hooks/blocking/test_code_rules_enforcer_banned_prefixes.py +152 -0
- package/hooks/blocking/test_code_rules_enforcer_bare_except.py +120 -0
- package/hooks/blocking/test_code_rules_enforcer_boundary_types.py +175 -0
- package/hooks/blocking/test_code_rules_enforcer_cap_meta.py +0 -1
- package/hooks/blocking/test_code_rules_enforcer_collection_prefix.py +50 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_format.py +255 -0
- package/hooks/blocking/test_code_rules_enforcer_inline_tuple_string_magic.py +130 -0
- package/hooks/blocking/test_code_rules_enforcer_stub_implementations.py +141 -0
- package/hooks/blocking/test_code_rules_enforcer_test_branching.py +143 -0
- package/hooks/blocking/test_code_rules_enforcer_thin_wrapper_files.py +169 -0
- package/hooks/blocking/test_code_rules_enforcer_todo_markers.py +99 -0
- package/hooks/blocking/test_code_rules_enforcer_typed_dict_pairs.py +141 -0
- package/hooks/blocking/test_convergence_gate_blocker.py +63 -0
- package/hooks/blocking/test_destructive_command_blocker.py +146 -0
- package/hooks/blocking/test_destructive_command_blocker_no_verify.py +102 -0
- package/hooks/blocking/test_gh_body_arg_blocker.py +45 -0
- package/hooks/blocking/test_md_to_html_blocker.py +317 -0
- package/hooks/blocking/test_pr_description_enforcer.py +69 -8
- package/hooks/config/any_type_config.py +7 -0
- package/hooks/config/banned_identifiers_constants.py +11 -0
- package/hooks/config/blocking_check_limits.py +38 -0
- package/hooks/config/bot_mention_comment_blocker_constants.py +20 -0
- package/hooks/config/code_rules_enforcer_constants.py +53 -0
- package/hooks/config/convergence_branch_constants.py +9 -0
- package/hooks/config/doc_gist_auto_publish_constants.py +18 -0
- package/hooks/config/html_companion_constants.py +20 -0
- package/hooks/config/inline_tuple_string_magic_constants.py +22 -0
- package/hooks/config/pr_description_enforcer_constants.py +14 -0
- package/hooks/config/test_banned_identifiers_constants.py +17 -0
- package/hooks/hooks.json +28 -20
- package/hooks/pyproject.toml +69 -0
- package/hooks/validators/mypy_integration.py +47 -1
- package/hooks/validators/run_all_validators.py +3 -3
- package/hooks/validators/test_mypy_integration.py +50 -1
- package/hooks/workflow/doc_gist_auto_publish.py +144 -0
- package/hooks/workflow/md_to_html_companion.py +365 -0
- package/hooks/workflow/test_doc_gist_auto_publish.py +117 -0
- package/hooks/workflow/test_md_to_html_companion.py +452 -0
- package/package.json +1 -1
- package/rules/gh-body-file.md +2 -0
- package/scripts/Install-SweepEmptyDirs.ps1 +111 -0
- package/scripts/check.ps1 +106 -0
- package/scripts/config/timing.py +11 -0
- package/scripts/sweep_empty_dirs.py +138 -0
- package/scripts/sync_to_cursor/rules.py +1 -1
- package/scripts/test_sweep_empty_dirs.py +183 -0
- package/skills/_shared/pr-loop/prompts/pr-consistency-audit.xml +323 -0
- package/skills/_shared/pr-loop/scripts/_cli_utils.py +22 -0
- package/skills/_shared/pr-loop/scripts/_path_resolver.py +165 -0
- package/skills/_shared/pr-loop/scripts/_xml_utils.py +20 -0
- package/skills/_shared/pr-loop/scripts/build_audit_prompt.py +182 -0
- package/skills/_shared/pr-loop/scripts/build_fix_prompt.py +185 -0
- package/skills/_shared/pr-loop/scripts/config/__init__.py +0 -0
- package/skills/_shared/pr-loop/scripts/config/path_resolver_constants.py +78 -0
- package/skills/_shared/pr-loop/scripts/init_loop_state.py +135 -0
- package/skills/_shared/pr-loop/scripts/teardown_worktrees.py +175 -0
- package/skills/_shared/pr-loop/scripts/write_audit_outcomes.py +182 -0
- package/skills/_shared/pr-loop/scripts/write_fix_outcomes.py +206 -0
- package/skills/bugteam/CONSTRAINTS.md +21 -22
- package/skills/bugteam/EXAMPLES.md +3 -3
- package/skills/bugteam/PROMPTS.md +227 -67
- package/skills/bugteam/SKILL.md +132 -455
- package/skills/bugteam/reference/README.md +1 -1
- package/skills/bugteam/reference/audit-and-teammates.md +112 -39
- package/skills/bugteam/reference/audit-contract.md +4 -22
- package/skills/bugteam/reference/copilot-gap-analysis.md +8 -5
- package/skills/bugteam/reference/design-rationale.md +2 -2
- package/skills/bugteam/reference/github-pr-reviews.md +50 -57
- package/skills/bugteam/reference/obstacles/audit-assign-ids.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-capture-excerpts.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-walk-categories.md +13 -0
- package/skills/bugteam/reference/obstacles/audit-write-xml.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-append-summary.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-apply-fixes.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-git-add-commit.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-git-push.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-post-reply.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-publish-summary.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-py-compile.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-read-files.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-resolve-thread.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-test-suite.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-violation-count.md +13 -0
- package/skills/bugteam/reference/obstacles/fix-write-xml.md +13 -0
- package/skills/bugteam/reference/team-setup.md +111 -9
- package/skills/bugteam/reference/teardown-publish-permissions.md +39 -8
- package/skills/bugteam/scripts/README.md +60 -0
- package/skills/bugteam/scripts/_claude_permissions_common.py +358 -0
- package/skills/bugteam/scripts/bugteam_code_rules_gate.py +976 -0
- package/skills/bugteam/scripts/bugteam_fix_hookspath.py +375 -0
- package/skills/bugteam/scripts/bugteam_preflight.py +328 -0
- package/skills/bugteam/scripts/config/bugteam_code_rules_gate_constants.py +25 -0
- package/skills/bugteam/scripts/config/bugteam_fix_hookspath_constants.py +26 -0
- package/skills/bugteam/scripts/config/bugteam_preflight_constants.py +35 -0
- package/skills/bugteam/scripts/config/claude_permissions_common_constants.py +20 -0
- package/skills/bugteam/scripts/config/probe_code_rules_enforcer_check_constants.py +12 -0
- package/skills/bugteam/scripts/config/windows_safe_rmtree_constants.py +7 -0
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +175 -0
- package/skills/bugteam/scripts/probe_code_rules_enforcer_check.py +107 -0
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +220 -0
- package/skills/bugteam/scripts/test__claude_permissions_common.py +112 -0
- package/skills/bugteam/scripts/test_bugteam_code_rules_gate.py +400 -0
- package/skills/bugteam/scripts/test_bugteam_fix_hookspath.py +384 -0
- package/skills/bugteam/scripts/test_bugteam_preflight.py +309 -0
- package/skills/bugteam/scripts/test_claude_permissions_common.py +195 -0
- package/skills/bugteam/scripts/test_grant_project_claude_permissions.py +55 -0
- package/skills/bugteam/scripts/test_probe_code_rules_enforcer_check.py +76 -0
- package/skills/bugteam/scripts/test_revoke_project_claude_permissions.py +55 -0
- package/skills/bugteam/scripts/test_windows_safe_rmtree.py +108 -0
- package/skills/bugteam/scripts/windows_safe_rmtree.py +100 -0
- package/skills/bugteam/test_skill_additions.py +1 -11
- package/skills/code/SKILL.md +176 -0
- package/skills/copilot-review/SKILL.md +16 -0
- package/skills/doc-gist/SKILL.md +99 -0
- package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +453 -0
- package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +515 -0
- package/skills/doc-gist/references/examples/03-code-review-pr.html +638 -0
- package/skills/doc-gist/references/examples/04-code-understanding.html +491 -0
- package/skills/doc-gist/references/examples/05-design-system.html +629 -0
- package/skills/doc-gist/references/examples/06-component-variants.html +605 -0
- package/skills/doc-gist/references/examples/07-prototype-animation.html +455 -0
- package/skills/doc-gist/references/examples/08-prototype-interaction.html +396 -0
- package/skills/doc-gist/references/examples/09-slide-deck.html +592 -0
- package/skills/doc-gist/references/examples/10-svg-illustrations.html +492 -0
- package/skills/doc-gist/references/examples/11-status-report.html +528 -0
- package/skills/doc-gist/references/examples/12-incident-report.html +596 -0
- package/skills/doc-gist/references/examples/13-flowchart-diagram.html +395 -0
- package/skills/doc-gist/references/examples/14-research-feature-explainer.html +381 -0
- package/skills/doc-gist/references/examples/15-research-concept-explainer.html +368 -0
- package/skills/doc-gist/references/examples/16-implementation-plan.html +702 -0
- package/skills/doc-gist/references/examples/17-pr-writeup.html +595 -0
- package/skills/doc-gist/references/examples/18-editor-triage-board.html +573 -0
- package/skills/doc-gist/references/examples/19-editor-feature-flags.html +663 -0
- package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +722 -0
- package/skills/doc-gist/references/examples/README.md +5 -0
- package/skills/doc-gist/scripts/config/__init__.py +0 -0
- package/skills/doc-gist/scripts/config/gist_upload_constants.py +16 -0
- package/skills/doc-gist/scripts/gist_upload.py +177 -0
- package/skills/doc-gist/scripts/test_gist_upload.py +51 -0
- package/skills/findbugs/SKILL.md +96 -2
- package/skills/monitor-open-prs/SKILL.md +14 -32
- package/skills/monitor-open-prs/test_skill_contract.py +0 -11
- package/skills/pr-consistency-audit/SKILL.md +112 -0
- package/skills/pr-consistency-audit/reference/detection-rules.md +96 -0
- package/skills/pr-consistency-audit/reference/illustrations.md +78 -0
- package/skills/pr-converge/SKILL.md +229 -23
- package/skills/pr-converge/config/__init__.py +0 -0
- package/skills/pr-converge/config/constants.py +63 -0
- package/skills/pr-converge/reference/convergence-gates.md +138 -44
- package/skills/pr-converge/reference/examples.md +43 -11
- package/skills/pr-converge/reference/fix-protocol.md +6 -5
- package/skills/pr-converge/reference/ground-rules.md +5 -3
- package/skills/pr-converge/reference/multi-pr-orchestration.md +44 -19
- package/skills/pr-converge/reference/obstacles/fix-post-replies.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-push.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-read-filelines.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-reset-state.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-resolve-threads.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-spawn-clean-coder.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-stage-commit.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-trigger-bugbot.md +13 -0
- package/skills/pr-converge/reference/obstacles/fix-write-test.md +13 -0
- package/skills/pr-converge/reference/per-tick.md +107 -31
- package/skills/pr-converge/reference/state-schema.md +22 -1
- package/skills/pr-converge/reference/stop-conditions.md +9 -7
- package/skills/pr-converge/scripts/README.md +34 -46
- package/skills/pr-converge/scripts/check_bugbot_ci.py +279 -0
- package/skills/pr-converge/scripts/check_convergence.py +497 -0
- package/skills/pr-converge/scripts/check_pending_reviews.py +154 -0
- package/skills/pr-converge/scripts/config/pr_converge_constants.py +118 -0
- package/skills/pr-converge/scripts/fetch_copilot_reviews.py +134 -0
- package/skills/pr-converge/scripts/post_fix_reply.py +168 -0
- package/skills/pr-converge/scripts/test_check_bugbot_ci.py +312 -0
- package/skills/pr-converge/workflows/schedule-wakeup-loop.md +5 -12
- package/skills/qbug/SKILL.md +157 -27
- package/skills/session-log/SKILL.md +216 -114
- package/skills/session-tidy/SKILL.md +1 -1
- package/skills/skill-builder/SKILL.md +138 -56
- package/skills/skill-builder/references/delegation-map.md +72 -113
- package/skills/skill-builder/references/progressive-disclosure.md +122 -0
- package/skills/skill-builder/references/self-audit-checklist.md +92 -0
- package/skills/skill-builder/references/skill-types.md +228 -0
- package/skills/skill-builder/references/thariq-x-post-skills.json +33 -0
- package/skills/skill-builder/templates/gap-analysis.md +15 -8
- package/skills/skill-builder/workflows/improve-skill.md +86 -57
- package/skills/skill-builder/workflows/new-skill.md +80 -168
- package/skills/skill-builder/workflows/polish-skill.md +78 -54
- package/skills/structure-prompt/SKILL.md +50 -0
- package/skills/structure-prompt/reference/adversarial-tuning.md +62 -0
- package/skills/structure-prompt/reference/block-classification.md +27 -0
- package/skills/structure-prompt/reference/canonical-case.md +48 -0
- package/skills/structure-prompt/reference/citation-depth.md +70 -0
- package/skills/structure-prompt/reference/cleanup.md +33 -0
- package/skills/structure-prompt/reference/constraints.md +33 -0
- package/skills/structure-prompt/reference/directives.md +37 -0
- package/skills/structure-prompt/reference/examples.md +72 -0
- package/skills/structure-prompt/reference/instantiation.md +51 -0
- package/skills/structure-prompt/reference/output-contract.md +72 -0
- package/skills/structure-prompt/reference/per-category.md +23 -0
- package/skills/structure-prompt/reference/persona.md +38 -0
- package/skills/structure-prompt/reference/research.md +33 -0
- package/skills/structure-prompt/reference/structure.md +28 -0
- package/agents/code-standards-agent.md +0 -93
- package/agents/groq-coder.md +0 -113
- package/agents/plan-executor.md +0 -226
- package/agents/project-docs-analyzer.md +0 -53
- package/agents/project-structure-organizer-agent.md +0 -72
- package/agents/skill-to-agent-converter.md +0 -370
- package/agents/skill-writer-agent.md +0 -470
- package/agents/user-docs-writer.md +0 -67
- package/agents/workflow-visual-documenter.md +0 -82
- package/commands/readability-review.md +0 -20
- package/hooks/mypy.ini +0 -2
- package/hooks/notification/attention_needed_notify.py +0 -71
- package/hooks/notification/claude_notification_handler.py +0 -67
- package/hooks/notification/notification_utils.py +0 -267
- package/hooks/notification/subagent_complete_notify.py +0 -381
- package/hooks/notification/test_attention_needed_notify.py +0 -47
- package/hooks/notification/test_claude_notification_handler.py +0 -54
- package/hooks/notification/test_notification_utils.py +0 -91
- package/hooks/notification/test_subagent_complete_notify.py +0 -79
- package/scripts/config/groq_bugteam_config.py +0 -230
- package/scripts/config/test_groq_bugteam_config.py +0 -83
- package/scripts/config/test_spec_implementer_prompt.py +0 -32
- package/scripts/groq_bugteam.README.md +0 -131
- package/scripts/groq_bugteam.py +0 -647
- package/scripts/groq_bugteam_dotenv.py +0 -40
- package/scripts/groq_bugteam_spec.py +0 -226
- package/scripts/test_groq_bugteam.py +0 -529
- package/scripts/test_groq_bugteam_apply_fix_from_spec.py +0 -426
- package/scripts/test_groq_bugteam_dotenv.py +0 -66
- package/scripts/test_groq_bugteam_spec.py +0 -338
- package/skills/bugteam/SKILL_EVALS.md +0 -309
- package/skills/dream/SKILL.md +0 -118
- package/skills/ingest/SKILL.md +0 -40
- package/skills/npm-creator/SKILL.md +0 -187
- package/skills/readability-review/SKILL.md +0 -127
- package/skills/resume-review/SKILL.md +0 -261
- package/skills/rule-audit/SKILL.md +0 -307
- package/skills/rule-creator/SKILL.md +0 -150
- package/skills/searching-obsidian-vault/SKILL.md +0 -131
- package/skills/skill-writer/REFERENCE.md +0 -284
- package/skills/skill-writer/SKILL.md +0 -222
- package/skills/tdd-team/SKILL.md +0 -128
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
"""Tests for check_bugbot_ci silent-pass detection.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- is_bugbot_run_clean returns True for completed success / completed neutral
|
|
5
|
+
- is_bugbot_run_clean returns False for completed failure, in_progress, missing
|
|
6
|
+
- is_bugbot_run_clean returns None when the gh CLI fails
|
|
7
|
+
- main(--check-clean) returns 0 on clean, 1 on not-clean, and
|
|
8
|
+
EXIT_CODE_GH_ERROR on gh CLI failure (with stderr diagnostics)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import importlib.util
|
|
14
|
+
import json
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from collections.abc import Iterator
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from types import ModuleType
|
|
20
|
+
from unittest.mock import MagicMock, patch
|
|
21
|
+
|
|
22
|
+
import pytest
|
|
23
|
+
|
|
24
|
+
_SCRIPTS_DIRECTORY = Path(__file__).resolve().parent
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.fixture(scope="session")
|
|
28
|
+
def check_bugbot_ci_module() -> Iterator[ModuleType]:
|
|
29
|
+
"""Load check_bugbot_ci with full sys.path and sys.modules isolation.
|
|
30
|
+
|
|
31
|
+
Snapshots sys.path and sys.modules at session start; restores both
|
|
32
|
+
unconditionally at session teardown so sibling tests inherit a clean
|
|
33
|
+
loading environment. The production script performs its own
|
|
34
|
+
membership-guarded sys.path.insert during exec_module so its config
|
|
35
|
+
dependency resolves; that insert and any config.* modules it loads
|
|
36
|
+
are reverted on teardown.
|
|
37
|
+
"""
|
|
38
|
+
module_path = _SCRIPTS_DIRECTORY / "check_bugbot_ci.py"
|
|
39
|
+
spec = importlib.util.spec_from_file_location("check_bugbot_ci", module_path)
|
|
40
|
+
assert spec is not None
|
|
41
|
+
assert spec.loader is not None
|
|
42
|
+
module = importlib.util.module_from_spec(spec)
|
|
43
|
+
sys_path_snapshot = list(sys.path)
|
|
44
|
+
sys_modules_snapshot = dict(sys.modules)
|
|
45
|
+
evicted_config_modules = {
|
|
46
|
+
each_module_name: sys.modules.pop(each_module_name)
|
|
47
|
+
for each_module_name in list(sys.modules)
|
|
48
|
+
if each_module_name == "config" or each_module_name.startswith("config.")
|
|
49
|
+
}
|
|
50
|
+
sys_modules_snapshot.update(evicted_config_modules)
|
|
51
|
+
spec.loader.exec_module(module)
|
|
52
|
+
try:
|
|
53
|
+
yield module
|
|
54
|
+
finally:
|
|
55
|
+
sys.path[:] = sys_path_snapshot
|
|
56
|
+
for each_new_module_name in list(sys.modules):
|
|
57
|
+
if each_new_module_name not in sys_modules_snapshot:
|
|
58
|
+
del sys.modules[each_new_module_name]
|
|
59
|
+
for each_module_name, each_module_value in sys_modules_snapshot.items():
|
|
60
|
+
sys.modules[each_module_name] = each_module_value
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _make_completed_process(
|
|
64
|
+
stdout: str, returncode: int = 0
|
|
65
|
+
) -> subprocess.CompletedProcess[str]:
|
|
66
|
+
process = MagicMock(spec=subprocess.CompletedProcess)
|
|
67
|
+
process.stdout = stdout
|
|
68
|
+
process.stderr = ""
|
|
69
|
+
process.returncode = returncode
|
|
70
|
+
return process
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _build_stdout(*all_check_entries: dict[str, object]) -> str:
|
|
74
|
+
return "\n".join(json.dumps(each_entry) for each_entry in all_check_entries) + "\n"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_should_return_true_when_bugbot_completed_with_success_conclusion(
|
|
78
|
+
check_bugbot_ci_module: ModuleType,
|
|
79
|
+
) -> None:
|
|
80
|
+
stdout = _build_stdout(
|
|
81
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "success"}
|
|
82
|
+
)
|
|
83
|
+
with patch.object(
|
|
84
|
+
check_bugbot_ci_module,
|
|
85
|
+
"_run_check_runs_api",
|
|
86
|
+
return_value=_make_completed_process(stdout),
|
|
87
|
+
):
|
|
88
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
89
|
+
owner="acme", repo="repo", sha="abc"
|
|
90
|
+
)
|
|
91
|
+
assert is_clean is True
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def test_should_return_true_when_bugbot_completed_with_neutral_conclusion(
|
|
95
|
+
check_bugbot_ci_module: ModuleType,
|
|
96
|
+
) -> None:
|
|
97
|
+
stdout = _build_stdout(
|
|
98
|
+
{"name": "bugbot", "status": "completed", "conclusion": "neutral"}
|
|
99
|
+
)
|
|
100
|
+
with patch.object(
|
|
101
|
+
check_bugbot_ci_module,
|
|
102
|
+
"_run_check_runs_api",
|
|
103
|
+
return_value=_make_completed_process(stdout),
|
|
104
|
+
):
|
|
105
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
106
|
+
owner="acme", repo="repo", sha="abc"
|
|
107
|
+
)
|
|
108
|
+
assert is_clean is True
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def test_should_return_false_when_bugbot_completed_with_failure_conclusion(
|
|
112
|
+
check_bugbot_ci_module: ModuleType,
|
|
113
|
+
) -> None:
|
|
114
|
+
stdout = _build_stdout(
|
|
115
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "failure"}
|
|
116
|
+
)
|
|
117
|
+
with patch.object(
|
|
118
|
+
check_bugbot_ci_module,
|
|
119
|
+
"_run_check_runs_api",
|
|
120
|
+
return_value=_make_completed_process(stdout),
|
|
121
|
+
):
|
|
122
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
123
|
+
owner="acme", repo="repo", sha="abc"
|
|
124
|
+
)
|
|
125
|
+
assert is_clean is False
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def test_should_return_false_when_bugbot_still_in_progress(
|
|
129
|
+
check_bugbot_ci_module: ModuleType,
|
|
130
|
+
) -> None:
|
|
131
|
+
stdout = _build_stdout(
|
|
132
|
+
{"name": "Cursor Bugbot", "status": "in_progress", "conclusion": None}
|
|
133
|
+
)
|
|
134
|
+
with patch.object(
|
|
135
|
+
check_bugbot_ci_module,
|
|
136
|
+
"_run_check_runs_api",
|
|
137
|
+
return_value=_make_completed_process(stdout),
|
|
138
|
+
):
|
|
139
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
140
|
+
owner="acme", repo="repo", sha="abc"
|
|
141
|
+
)
|
|
142
|
+
assert is_clean is False
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_should_return_false_when_no_bugbot_check_run_present(
|
|
146
|
+
check_bugbot_ci_module: ModuleType,
|
|
147
|
+
) -> None:
|
|
148
|
+
stdout = _build_stdout(
|
|
149
|
+
{"name": "ci-other", "status": "completed", "conclusion": "success"}
|
|
150
|
+
)
|
|
151
|
+
with patch.object(
|
|
152
|
+
check_bugbot_ci_module,
|
|
153
|
+
"_run_check_runs_api",
|
|
154
|
+
return_value=_make_completed_process(stdout),
|
|
155
|
+
):
|
|
156
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
157
|
+
owner="acme", repo="repo", sha="abc"
|
|
158
|
+
)
|
|
159
|
+
assert is_clean is False
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_should_return_false_when_first_bugbot_run_is_in_progress_even_if_later_one_clean(
|
|
163
|
+
check_bugbot_ci_module: ModuleType,
|
|
164
|
+
) -> None:
|
|
165
|
+
stdout = _build_stdout(
|
|
166
|
+
{"name": "Cursor Bugbot", "status": "in_progress", "conclusion": None},
|
|
167
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "success"},
|
|
168
|
+
)
|
|
169
|
+
with patch.object(
|
|
170
|
+
check_bugbot_ci_module,
|
|
171
|
+
"_run_check_runs_api",
|
|
172
|
+
return_value=_make_completed_process(stdout),
|
|
173
|
+
):
|
|
174
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
175
|
+
owner="acme", repo="repo", sha="abc"
|
|
176
|
+
)
|
|
177
|
+
assert is_clean is False
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def test_should_return_false_when_first_bugbot_run_failed_even_if_later_one_clean(
|
|
181
|
+
check_bugbot_ci_module: ModuleType,
|
|
182
|
+
) -> None:
|
|
183
|
+
stdout = _build_stdout(
|
|
184
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "failure"},
|
|
185
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "success"},
|
|
186
|
+
)
|
|
187
|
+
with patch.object(
|
|
188
|
+
check_bugbot_ci_module,
|
|
189
|
+
"_run_check_runs_api",
|
|
190
|
+
return_value=_make_completed_process(stdout),
|
|
191
|
+
):
|
|
192
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
193
|
+
owner="acme", repo="repo", sha="abc"
|
|
194
|
+
)
|
|
195
|
+
assert is_clean is False
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_should_return_none_when_gh_cli_fails(
|
|
199
|
+
check_bugbot_ci_module: ModuleType,
|
|
200
|
+
) -> None:
|
|
201
|
+
failing_process = MagicMock(spec=subprocess.CompletedProcess)
|
|
202
|
+
failing_process.stdout = ""
|
|
203
|
+
failing_process.stderr = "boom"
|
|
204
|
+
failing_process.returncode = 1
|
|
205
|
+
with patch.object(
|
|
206
|
+
check_bugbot_ci_module,
|
|
207
|
+
"_run_check_runs_api",
|
|
208
|
+
return_value=failing_process,
|
|
209
|
+
):
|
|
210
|
+
is_clean = check_bugbot_ci_module.is_bugbot_run_clean(
|
|
211
|
+
owner="acme", repo="repo", sha="abc"
|
|
212
|
+
)
|
|
213
|
+
assert is_clean is None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def test_main_check_clean_should_return_gh_error_code_when_gh_cli_fails(
|
|
217
|
+
check_bugbot_ci_module: ModuleType,
|
|
218
|
+
capsys: pytest.CaptureFixture[str],
|
|
219
|
+
) -> None:
|
|
220
|
+
failing_process = MagicMock(spec=subprocess.CompletedProcess)
|
|
221
|
+
failing_process.stdout = ""
|
|
222
|
+
failing_process.stderr = "boom"
|
|
223
|
+
failing_process.returncode = 1
|
|
224
|
+
with patch.object(
|
|
225
|
+
check_bugbot_ci_module,
|
|
226
|
+
"_run_check_runs_api",
|
|
227
|
+
return_value=failing_process,
|
|
228
|
+
):
|
|
229
|
+
exit_code = check_bugbot_ci_module.main(
|
|
230
|
+
["--check-clean", "--owner", "acme", "--repo", "repo", "--sha", "abc"]
|
|
231
|
+
)
|
|
232
|
+
assert exit_code == check_bugbot_ci_module.EXIT_CODE_GH_ERROR
|
|
233
|
+
captured = capsys.readouterr()
|
|
234
|
+
assert "gh api error: boom" in captured.err
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def test_main_check_clean_should_return_zero_when_bugbot_clean(
|
|
238
|
+
check_bugbot_ci_module: ModuleType,
|
|
239
|
+
capsys: pytest.CaptureFixture[str],
|
|
240
|
+
) -> None:
|
|
241
|
+
stdout = _build_stdout(
|
|
242
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "success"}
|
|
243
|
+
)
|
|
244
|
+
with patch.object(
|
|
245
|
+
check_bugbot_ci_module,
|
|
246
|
+
"_run_check_runs_api",
|
|
247
|
+
return_value=_make_completed_process(stdout),
|
|
248
|
+
):
|
|
249
|
+
exit_code = check_bugbot_ci_module.main(
|
|
250
|
+
["--check-clean", "--owner", "acme", "--repo", "repo", "--sha", "abc"]
|
|
251
|
+
)
|
|
252
|
+
assert exit_code == 0
|
|
253
|
+
captured = capsys.readouterr()
|
|
254
|
+
assert "not clean" not in captured.out
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_main_check_clean_should_return_one_when_bugbot_not_clean(
|
|
258
|
+
check_bugbot_ci_module: ModuleType,
|
|
259
|
+
capsys: pytest.CaptureFixture[str],
|
|
260
|
+
) -> None:
|
|
261
|
+
stdout = _build_stdout(
|
|
262
|
+
{"name": "Cursor Bugbot", "status": "completed", "conclusion": "failure"}
|
|
263
|
+
)
|
|
264
|
+
with patch.object(
|
|
265
|
+
check_bugbot_ci_module,
|
|
266
|
+
"_run_check_runs_api",
|
|
267
|
+
return_value=_make_completed_process(stdout),
|
|
268
|
+
):
|
|
269
|
+
exit_code = check_bugbot_ci_module.main(
|
|
270
|
+
["--check-clean", "--owner", "acme", "--repo", "repo", "--sha", "abc"]
|
|
271
|
+
)
|
|
272
|
+
assert exit_code == 1
|
|
273
|
+
captured = capsys.readouterr()
|
|
274
|
+
assert "not clean" in captured.out
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def test_main_check_active_should_return_zero_when_bugbot_in_progress(
|
|
278
|
+
check_bugbot_ci_module: ModuleType,
|
|
279
|
+
) -> None:
|
|
280
|
+
stdout = _build_stdout(
|
|
281
|
+
{"name": "Cursor Bugbot", "status": "in_progress", "conclusion": None}
|
|
282
|
+
)
|
|
283
|
+
with patch.object(
|
|
284
|
+
check_bugbot_ci_module,
|
|
285
|
+
"_run_check_runs_api",
|
|
286
|
+
return_value=_make_completed_process(stdout),
|
|
287
|
+
):
|
|
288
|
+
exit_code = check_bugbot_ci_module.main(
|
|
289
|
+
["--check-active", "--owner", "acme", "--repo", "repo", "--sha", "abc"]
|
|
290
|
+
)
|
|
291
|
+
assert exit_code == 0
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def test_main_should_reject_check_clean_and_check_active_together(
|
|
295
|
+
check_bugbot_ci_module: ModuleType,
|
|
296
|
+
capsys: pytest.CaptureFixture[str],
|
|
297
|
+
) -> None:
|
|
298
|
+
with pytest.raises(SystemExit):
|
|
299
|
+
check_bugbot_ci_module.main(
|
|
300
|
+
[
|
|
301
|
+
"--check-clean",
|
|
302
|
+
"--check-active",
|
|
303
|
+
"--owner",
|
|
304
|
+
"acme",
|
|
305
|
+
"--repo",
|
|
306
|
+
"repo",
|
|
307
|
+
"--sha",
|
|
308
|
+
"abc",
|
|
309
|
+
]
|
|
310
|
+
)
|
|
311
|
+
captured = capsys.readouterr()
|
|
312
|
+
assert "not allowed with" in captured.err or "mutually exclusive" in captured.err
|
|
@@ -4,21 +4,14 @@ Load this document for converge **loop pacing**. The pre-flight in `SKILL.md`
|
|
|
4
4
|
guarantees `ScheduleWakeup` is available before any tick runs. Shared bugbot
|
|
5
5
|
/ bugteam / Fix protocol steps stay in the main `SKILL.md`.
|
|
6
6
|
|
|
7
|
-
## Session behavior
|
|
8
|
-
|
|
9
|
-
Call `ScheduleWakeup` from this same session so the next tick fires back into **this** transcript with the prior tick's state line and PR context still addressable.
|
|
10
|
-
|
|
11
7
|
## Calling ScheduleWakeup
|
|
12
8
|
|
|
13
|
-
At end of tick
|
|
14
|
-
|
|
9
|
+
At end of every tick — across all phases (BUGBOT, BUGTEAM, COPILOT_WAIT)
|
|
10
|
+
without distinction — call `ScheduleWakeup` unless convergence or another
|
|
11
|
+
stop condition already omitted pacing:
|
|
15
12
|
|
|
16
|
-
- `delaySeconds:
|
|
17
|
-
|
|
18
|
-
mandatory re-trigger, or by BUGTEAM's same-tick re-trigger). Bugbot
|
|
19
|
-
finishes a review in 1–4 minutes, so 270s stays under the 5-minute
|
|
20
|
-
prompt-cache TTL with margin past bugbot's typical upper bound. The
|
|
21
|
-
exception is the BUGBOT inline-lag branch (see below).
|
|
13
|
+
- `delaySeconds: 360` — default wakeup interval. Keeps the loop advancing
|
|
14
|
+
through all phases. Exception: BUGBOT inline-lag branch (see below).
|
|
22
15
|
- `reason`: one short sentence on what is being awaited, including the
|
|
23
16
|
current `phase` and `bugbot_clean_at` SHA when set.
|
|
24
17
|
- `prompt: "/pr-converge"` — re-enters this skill on the next firing.
|
package/skills/qbug/SKILL.md
CHANGED
|
@@ -23,7 +23,7 @@ Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE
|
|
|
23
23
|
- Code-rules gate script: `${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/code_rules_gate.py`
|
|
24
24
|
- Bug category rubric A–J: [`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
|
-
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#
|
|
26
|
+
- PR comment lifecycle shape: [`bugteam/SKILL.md`](../bugteam/SKILL.md#audit-posting)
|
|
27
27
|
|
|
28
28
|
## When this skill applies
|
|
29
29
|
|
|
@@ -31,6 +31,12 @@ Shared artifacts with /bugteam are referenced below by path, using the `${CLAUDE
|
|
|
31
31
|
|
|
32
32
|
Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
33
33
|
|
|
34
|
+
- **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` contains the
|
|
35
|
+
token `bugteam` (comma-separated, case-insensitive, whitespace-tolerant):
|
|
36
|
+
`/qbug is disabled via CLAUDE_REVIEWS_DISABLED.` `/qbug` is the bugteam
|
|
37
|
+
baseline review and shares the `bugteam` token with `/bugteam`; the shared
|
|
38
|
+
pre-flight script also exits 7 in this case so any caller invoking it
|
|
39
|
+
directly halts on the same signal.
|
|
34
40
|
- **No PR or upstream diff.** `No PR or upstream diff. /qbug needs a target.`
|
|
35
41
|
- **Dirty tree.** `Uncommitted changes detected. Stash, commit, or revert before /qbug.`
|
|
36
42
|
- **Missing subagent.** Before Step 2, confirm `clean-coder` exists. Else: `Required subagent type clean-coder not installed. /qbug needs clean-coder available.`
|
|
@@ -198,17 +204,97 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
198
204
|
<qbug_temp_dir>/loop-<loop_count>-audit.json per the contract's
|
|
199
205
|
persistence schema.
|
|
200
206
|
|
|
201
|
-
Post ONE review per loop
|
|
202
|
-
|
|
203
|
-
the
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
`
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
Post ONE review per loop via `post_audit_thread.py`. Before
|
|
208
|
+
serializing, partition the merged findings into anchored (line
|
|
209
|
+
appears in the captured diff) and unanchored (line is not in the
|
|
210
|
+
diff) buckets. Only anchored findings are written to
|
|
211
|
+
`<qbug_temp_dir>/loop-<loop_count>-findings.json` — the GitHub
|
|
212
|
+
reviews API rejects the entire POST if any inline comment in
|
|
213
|
+
`comments[]` targets a line not in the diff at `--commit`, so a
|
|
214
|
+
single unanchored entry breaks the whole review. Unanchored
|
|
215
|
+
findings surface in the loop's user-facing summary rather than
|
|
216
|
+
as inline anchored comments. The JSON root is a list of objects
|
|
217
|
+
shaped `{path, line, side, severity, description, fix_summary}`.
|
|
218
|
+
For each anchored finding, map `file` → `path`; split the
|
|
219
|
+
finding's `failure_mode` at the literal `Fix:` heading so the
|
|
220
|
+
failure narrative becomes `description` and the suffix beginning
|
|
221
|
+
at `Fix:` (including the trailing `Validation:` clause) becomes
|
|
222
|
+
`fix_summary` (the `failure_mode` field carries the full
|
|
223
|
+
audit-to-fix handoff per
|
|
224
|
+
[`agents/code-quality-agent.md`](../../agents/code-quality-agent.md)).
|
|
225
|
+
When a finding's `failure_mode` omits the `Fix:` heading, write
|
|
226
|
+
the full text to BOTH `description` and `fix_summary`. Set
|
|
227
|
+
`side="RIGHT"` for every entry. Zero anchored findings → write
|
|
228
|
+
`[]` and pass `--state CLEAN`; one or more anchored findings →
|
|
229
|
+
pass `--state DIRTY` with the full list.
|
|
230
|
+
|
|
231
|
+
**Self-PR auto-toggle.** GitHub rejects both `APPROVE` and
|
|
232
|
+
`REQUEST_CHANGES` reviews with HTTP 422 when the authenticated
|
|
233
|
+
identity matches the PR author ("Cannot approve/request changes
|
|
234
|
+
on your own pull request"). `post_audit_thread.py` detects this
|
|
235
|
+
case via `gh api user` + `gh api repos/<o>/<r>/pulls/<n>` and
|
|
236
|
+
auto-resolves an alternate gh account's token for the reviews
|
|
237
|
+
POST — the active `gh auth` account is not mutated; only the
|
|
238
|
+
bearer token sent on the request changes. After the POST the
|
|
239
|
+
active account is still whoever it was before, so no "swap back"
|
|
240
|
+
step is needed.
|
|
241
|
+
|
|
242
|
+
Configuration:
|
|
243
|
+
|
|
244
|
+
- `GH_TOKEN` / `GITHUB_TOKEN` env vars take precedence over the
|
|
245
|
+
toggle. Set them when you need to pin a specific reviewer
|
|
246
|
+
identity by token rather than by account login.
|
|
247
|
+
- `BUGTEAM_REVIEWER_ACCOUNT` env var names which authenticated
|
|
248
|
+
alternate to prefer when a toggle is needed (for example,
|
|
249
|
+
`BUGTEAM_REVIEWER_ACCOUNT=jl-cmd`). The env var name is shared
|
|
250
|
+
across every skill that invokes `post_audit_thread.py`. When
|
|
251
|
+
unset, the script falls back to the first alternate account
|
|
252
|
+
`gh auth status` reports.
|
|
253
|
+
- The named alternate must be logged in (`gh auth login -h
|
|
254
|
+
github.com -u <login>`) before the audit skill runs. The
|
|
255
|
+
script exits 1 with a pointing-at-`gh auth login` message
|
|
256
|
+
when self-PR is detected and no usable alternate is
|
|
257
|
+
authenticated.
|
|
258
|
+
|
|
259
|
+
```
|
|
260
|
+
python "${CLAUDE_SKILL_DIR}/../../_shared/pr-loop/scripts/post_audit_thread.py" \
|
|
261
|
+
--skill qbug \
|
|
262
|
+
--owner <owner> \
|
|
263
|
+
--repo <repo> \
|
|
264
|
+
--pr-number <pr_number> \
|
|
265
|
+
--commit <head_sha> \
|
|
266
|
+
--state <CLEAN|DIRTY> \
|
|
267
|
+
--findings-json <qbug_temp_dir>/loop-<loop_count>-findings.json
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
The script POSTs a single review with `event=APPROVE` on CLEAN
|
|
271
|
+
(the request event; GitHub stores it as `state=APPROVED`; empty
|
|
272
|
+
`comments[]`, body documents "no findings") or
|
|
273
|
+
`event=REQUEST_CHANGES` on DIRTY (one inline anchored comment per
|
|
274
|
+
finding; each becomes its own resolvable thread on the PR). It
|
|
275
|
+
handles retries internally (1s / 4s / 16s backoff across four
|
|
276
|
+
attempts). Exit 0 emits the new review's `html_url` to stdout;
|
|
277
|
+
extract the numeric review id from the URL's
|
|
278
|
+
`#pullrequestreview-<id>` suffix (the trailing URL fragment of
|
|
279
|
+
`html_url`, the part after `#`). Then harvest child-comment URLs **and PR review
|
|
280
|
+
thread node ids** via
|
|
281
|
+
`pull_request_read(method="get_review_comments",
|
|
282
|
+
owner=<owner>, repo=<repo>, pullNumber=<pr_number>)` filtered to
|
|
283
|
+
that review id. The same response carries each
|
|
284
|
+
comment's PR review thread node id (e.g. `PRRT_kwDOxxx`) — match
|
|
285
|
+
children to findings in the order they appear in the findings
|
|
286
|
+
JSON. Each `loop_comment_index[finding_id]` entry must carry
|
|
287
|
+
`finding_comment_id` (numeric, used by
|
|
288
|
+
`add_reply_to_pull_request_comment`), `finding_comment_url`, and
|
|
289
|
+
`thread_node_id` (`PRRT_kwDOxxx`, used by `resolve_thread`) so
|
|
290
|
+
the FIX step can reply against the comment and resolve the
|
|
291
|
+
thread.
|
|
292
|
+
|
|
293
|
+
Exit 2 means retry exhaustion — exit
|
|
294
|
+
`error: post_audit_thread retry exhausted` without retrying and
|
|
295
|
+
without falling back to a flat issue comment. A hard blocker on
|
|
296
|
+
the audit-posting path is a halt condition, not a fallback
|
|
297
|
+
opportunity.
|
|
212
298
|
|
|
213
299
|
Update state: last_action="audited", last_findings=counts.
|
|
214
300
|
Append `<N> audit: <P0>P0 / <P1>P1 / <P2>P2` to audit_log.
|
|
@@ -245,12 +331,54 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
245
331
|
Write <qbug_temp_dir>/loop-<loop_count>-diagnostics.json per the
|
|
246
332
|
contract's diagnostics schema (all eight keys required).
|
|
247
333
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
334
|
+
For each finding, atomically (a) post the fix reply and
|
|
335
|
+
(b) call `resolve_thread`. The two calls form one logical action
|
|
336
|
+
per thread — do not yield to the lead between them, and do not
|
|
337
|
+
batch all replies before any resolves.
|
|
338
|
+
|
|
339
|
+
(a) Reply via
|
|
340
|
+
`add_reply_to_pull_request_comment(commentId=<loop_comment_index[finding_id].finding_comment_id>,
|
|
341
|
+
body=<reply_body>, owner=<owner>, repo=<repo>,
|
|
342
|
+
pullNumber=<pr_number>)`. The reply body uses the unified template
|
|
343
|
+
at [`../../_shared/pr-loop/audit-reply-template.md`](../../_shared/pr-loop/audit-reply-template.md).
|
|
344
|
+
Skeleton (identical across all paths):
|
|
345
|
+
|
|
346
|
+
```
|
|
347
|
+
**Claude finished @<reviewer>'s task** —— <status_line>
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
### <action_heading> ✅
|
|
351
|
+
|
|
352
|
+
<1–2 paragraph plain-language explanation>
|
|
353
|
+
|
|
354
|
+
**`<file>:<line>`:**
|
|
355
|
+
- <bullet describing change or rationale>
|
|
356
|
+
- <bullet describing change or rationale>
|
|
357
|
+
|
|
358
|
+
<closing paragraph>
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Per-path `<status_line>` / `<action_heading>`:
|
|
362
|
+
- `status=fixed`: `Fixed in <short_sha>` (first 7 chars) /
|
|
363
|
+
finding-specific action verb (e.g.,
|
|
364
|
+
`Replaced Any with concrete type`).
|
|
365
|
+
- `status=could_not_address`: `Could not address this loop` /
|
|
366
|
+
one-line reason text.
|
|
367
|
+
- `status=hook_blocked`: `Hook blocked the fix commit` /
|
|
368
|
+
one-line hook summary.
|
|
369
|
+
|
|
370
|
+
Body text is passed directly as string parameters — no temp
|
|
371
|
+
files, no jq, no shell pipes.
|
|
372
|
+
|
|
373
|
+
(b) Immediately call
|
|
374
|
+
`pull_request_review_write(method="resolve_thread",
|
|
375
|
+
threadId=<loop_comment_index[finding_id].thread_node_id>,
|
|
376
|
+
owner=<owner>, repo=<repo>, pullNumber=<pr_number>)` for the
|
|
377
|
+
same thread (this is the PR review thread node ID —
|
|
378
|
+
`PRRT_kwDOxxx` — distinct from the numeric comment ID; the
|
|
379
|
+
AUDIT step captures it at audit time when calling
|
|
380
|
+
`get_review_comments` and stores it on each
|
|
381
|
+
`loop_comment_index` entry alongside `finding_comment_id`).
|
|
254
382
|
|
|
255
383
|
Update state: last_action="fixed". Append
|
|
256
384
|
`<N> fix: <short_sha> — <fixed>/<could_not_address>/<hook_blocked>`
|
|
@@ -260,14 +388,16 @@ The subagent receives this prompt and loops internally — the lead does not re-
|
|
|
260
388
|
</cycle>
|
|
261
389
|
|
|
262
390
|
<example_finding_body>
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
391
|
+
Populate these two fields on each finding entry of the JSON payload
|
|
392
|
+
consumed by `post_audit_thread.py` (the script renders the inline
|
|
393
|
+
comment body via `INLINE_COMMENT_BODY_TEMPLATE`):
|
|
394
|
+
|
|
395
|
+
```json
|
|
396
|
+
{
|
|
397
|
+
"description": "Two writers can both pass the existence check at line 88 before either commits the write at line 91 — whichever writes second overwrites the first under contention.",
|
|
398
|
+
"fix_summary": "Hold the cache lock across the check and the write, or use a compare-and-swap primitive. Validation: pytest -k cache_concurrent with the threaded-writer fixture."
|
|
399
|
+
}
|
|
400
|
+
```
|
|
271
401
|
</example_finding_body>
|
|
272
402
|
|
|
273
403
|
<constraints>
|
|
@@ -315,7 +445,7 @@ Delete the resolved `<qbug_temp_dir>` tree and any `.qbug-*.md` temp files in th
|
|
|
315
445
|
- **Code rules gate before every AUDIT.** Same `validate_content` logic as /bugteam.
|
|
316
446
|
- **One commit per FIX action.** Linear branch, fast-forward push only.
|
|
317
447
|
- **Categories A–J.** Same rubric as [`bugteam/PROMPTS.md`](../bugteam/PROMPTS.md).
|
|
318
|
-
- **One review per loop.** Anchored findings as `comments[]`; unanchored
|
|
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.
|
|
319
449
|
- **PR description rewrite on every exit**, same as /bugteam Step 4.5.
|
|
320
450
|
- **Temp file cleanup on every exit path.**
|
|
321
451
|
- **No per-loop clean-room.** The single subagent's context accumulates across loops — that is the explicit trade vs /bugteam. For convergence-critical audits where bias isolation matters, use /bugteam.
|