claude-dev-env 1.83.0 → 1.85.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_shared/pr-loop/audit-contract.md +24 -12
- package/_shared/pr-loop/scripts/_claude_permissions_common.py +16 -5
- package/_shared/pr-loop/scripts/code_rules_gate.py +2 -3
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/CLAUDE.md +1 -1
- package/_shared/pr-loop/scripts/pr_loop_shared_constants/terminology_sweep_constants.py +8 -0
- package/_shared/pr-loop/scripts/reviews_disabled.py +2 -0
- package/_shared/pr-loop/scripts/terminology_sweep.py +85 -39
- package/_shared/pr-loop/scripts/tests/test__claude_permissions_common.py +212 -0
- package/_shared/pr-loop/scripts/tests/test_agent_config_carveout.py +18 -0
- package/_shared/pr-loop/scripts/tests/test_claude_permissions_common.py +18 -0
- package/_shared/pr-loop/scripts/tests/test_code_rules_gate.py +37 -0
- package/_shared/pr-loop/scripts/tests/test_reviews_disabled.py +14 -0
- package/_shared/pr-loop/scripts/tests/test_terminology_sweep.py +94 -4
- package/agents/clean-coder.md +3 -0
- package/audit-rubrics/category_rubrics/category-o-docstring-vs-impl-drift.md +1 -1
- package/bin/install.mjs +16 -8
- package/bin/install.test.mjs +17 -6
- package/commands/CLAUDE.md +0 -1
- package/docs/CODE_RULES.md +1 -1
- package/hooks/blocking/CLAUDE.md +0 -4
- package/hooks/blocking/claude_md_orphan_file_blocker.py +1 -1
- package/hooks/blocking/code_rules_constants_config.py +5 -0
- package/hooks/blocking/code_rules_docstrings.py +133 -19
- package/hooks/blocking/code_rules_enforcer.py +4 -0
- package/hooks/blocking/code_rules_imports_logging.py +10 -6
- package/hooks/blocking/code_rules_magic_values.py +5 -0
- package/hooks/blocking/code_rules_naming_collection.py +15 -8
- package/hooks/blocking/code_rules_paired_test.py +3 -2
- package/hooks/blocking/code_rules_shared.py +34 -0
- package/hooks/blocking/code_rules_string_magic.py +1 -1
- package/hooks/blocking/code_rules_unused_imports.py +2 -2
- package/hooks/blocking/duplicate_rmtree_helper_blocker.py +4 -4
- package/hooks/blocking/test_code_rules_enforcer_docstring_args_span_scope.py +29 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_prose_wall_illustration.py +197 -0
- package/hooks/blocking/test_code_rules_enforcer_docstring_runon_sentence.py +27 -0
- package/hooks/blocking/test_code_rules_enforcer_naive_datetime.py +24 -0
- package/hooks/blocking/test_code_rules_enforcer_split_constants_config.py +33 -0
- package/hooks/blocking/test_code_rules_enforcer_split_shared.py +23 -0
- package/hooks/blocking/test_code_rules_magic_values.py +54 -0
- package/hooks/blocking/test_duplicate_rmtree_helper_blocker.py +0 -6
- package/hooks/blocking/test_pr_converge_bugteam_enforcer_state_tolerance.py +184 -0
- package/hooks/blocking/test_shared_stdin_adoption.py +5 -30
- package/hooks/hooks_constants/CLAUDE.md +0 -3
- package/hooks/hooks_constants/blocking_check_limits.py +4 -0
- package/hooks/hooks_constants/code_rules_path_utils_constants.py +1 -1
- package/hooks/hooks_constants/duplicate_rmtree_helper_blocker_constants.py +0 -1
- package/hooks/hooks_constants/post_tool_use_dispatcher_constants.py +0 -5
- package/hooks/validation/post_tool_use_dispatcher.py +1 -1
- package/hooks/validation/test_post_tool_use_dispatcher.py +51 -35
- package/hooks/workflow/CLAUDE.md +2 -8
- package/package.json +1 -1
- package/rules/CLAUDE.md +1 -1
- package/rules/docstring-prose-matches-implementation.md +1 -1
- package/rules/env-var-table-code-drift.md +1 -1
- package/rules/package-inventory-stale-entry.md +1 -1
- package/rules/paired-test-coverage.md +1 -1
- package/rules/plain-illustrative-docstrings.md +35 -1
- package/skills/CLAUDE.md +6 -1
- package/skills/_shared/pr-loop/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/CLAUDE.md +1 -0
- package/skills/_shared/pr-loop/scripts/skills_pr_loop_constants/handoff_constants.py +45 -0
- package/skills/_shared/pr-loop/scripts/test_write_handoff.py +201 -0
- package/skills/_shared/pr-loop/scripts/write_handoff.py +309 -0
- package/skills/autoconverge/SKILL.md +82 -58
- package/skills/autoconverge/reference/closing-report.md +6 -6
- package/skills/autoconverge/reference/convergence.md +17 -15
- package/skills/autoconverge/reference/gotchas.md +6 -3
- package/skills/autoconverge/workflow/autoconverge_report_constants/render_report_constants.py +2 -9
- package/skills/autoconverge/workflow/converge.contract.test.mjs +87 -13
- package/skills/autoconverge/workflow/converge.copilot-gate.test.mjs +1 -1
- package/skills/autoconverge/workflow/converge.merge-conflict.test.mjs +16 -9
- package/skills/autoconverge/workflow/converge.mjs +71 -74
- package/skills/autoconverge/workflow/render_report.py +7 -11
- package/skills/bugteam/CLAUDE.md +1 -1
- package/skills/bugteam/PROMPTS.md +7 -6
- package/skills/bugteam/SKILL.md +25 -72
- package/skills/bugteam/reference/CLAUDE.md +1 -3
- package/skills/bugteam/reference/README.md +1 -1
- package/skills/bugteam/reference/audit-and-teammates.md +1 -1
- package/skills/bugteam/reference/obstacles/fix-publish-summary.md +1 -1
- package/skills/bugteam/reference/team-setup.md +8 -7
- package/skills/bugteam/scripts/CLAUDE.md +0 -6
- package/skills/bugteam/scripts/README.md +0 -4
- package/skills/bugteam/scripts/bugteam_scripts_constants/CLAUDE.md +0 -1
- package/skills/code/SKILL.md +2 -0
- package/skills/copilot-review/CLAUDE.md +1 -1
- package/skills/copilot-review/SKILL.md +25 -23
- package/skills/findbugs/CLAUDE.md +2 -2
- package/skills/findbugs/SKILL.md +22 -83
- package/skills/fixbugs/SKILL.md +2 -4
- package/skills/log-audit/CLAUDE.md +20 -0
- package/skills/log-audit/SKILL.md +68 -0
- package/skills/log-audit/reference/CLAUDE.md +9 -0
- package/skills/log-audit/reference/charter.md +52 -0
- package/skills/log-audit/scripts/CLAUDE.md +27 -0
- package/skills/log-audit/scripts/cluster_recurrences.py +261 -0
- package/skills/log-audit/scripts/collect_log_window.py +199 -0
- package/skills/log-audit/scripts/log_audit_constants/CLAUDE.md +12 -0
- package/skills/log-audit/scripts/log_audit_constants/cluster_recurrences_constants.py +23 -0
- package/skills/log-audit/scripts/log_audit_constants/collect_log_window_constants.py +24 -0
- package/skills/log-audit/scripts/log_audit_constants/mine_copilot_findings_constants.py +49 -0
- package/skills/log-audit/scripts/mine_copilot_findings.py +302 -0
- package/skills/log-audit/scripts/test_cluster_recurrences.py +160 -0
- package/skills/log-audit/scripts/test_collect_log_window.py +111 -0
- package/skills/log-audit/scripts/test_mine_copilot_findings.py +126 -0
- package/skills/monitor-open-prs/SKILL.md +2 -2
- package/skills/post-audit-findings/SKILL.md +84 -0
- package/skills/pr-converge/CLAUDE.md +2 -0
- package/skills/pr-converge/SKILL.md +99 -60
- package/skills/pr-converge/reference/CLAUDE.md +1 -1
- package/skills/pr-converge/reference/convergence-gates.md +16 -19
- package/skills/pr-converge/reference/examples.md +5 -5
- package/skills/pr-converge/reference/fix-protocol.md +16 -43
- package/skills/pr-converge/reference/obstacles/fix-publish-summary.md +1 -1
- package/skills/pr-converge/reference/per-tick.md +24 -45
- package/skills/pr-converge/reference/state-schema.md +27 -0
- package/skills/pr-converge/scripts/README.md +3 -5
- package/skills/pr-fix-protocol/SKILL.md +70 -0
- package/skills/pr-loop-lifecycle/SKILL.md +73 -0
- package/skills/{bugteam → pr-loop-lifecycle}/reference/teardown-publish-permissions.md +22 -24
- package/skills/pr-scope-resolve/SKILL.md +48 -0
- package/skills/qbug/CLAUDE.md +4 -4
- package/skills/qbug/SKILL.md +46 -144
- package/skills/qbug/test_qbug_skill_audit_schema.py +2 -2
- package/skills/qbug/test_qbug_skill_post_fix_audit.py +1 -1
- package/skills/reviewer-gates/SKILL.md +96 -0
- package/skills/session-log/CLAUDE.md +7 -7
- package/skills/session-log/SKILL.md +27 -44
- package/skills/test_markdown_link_integrity.py +103 -0
- package/commands/doc-gist.md +0 -16
- package/hooks/blocking/_md_to_html_blocker_test_support.py +0 -65
- package/hooks/blocking/conftest.py +0 -30
- package/hooks/blocking/md_path_exemptions.py +0 -224
- package/hooks/blocking/md_to_html_blocker.py +0 -155
- package/hooks/blocking/test_md_to_html_blocker_exemptions.py +0 -434
- package/hooks/blocking/test_md_to_html_blocker_extensions.py +0 -157
- package/hooks/blocking/test_md_to_html_blocker_path_resolution.py +0 -336
- package/hooks/hooks_constants/doc_gist_auto_publish_constants.py +0 -18
- package/hooks/hooks_constants/html_companion_constants.py +0 -20
- package/hooks/hooks_constants/md_to_html_blocker_constants.py +0 -76
- package/hooks/hooks_constants/test_md_to_html_blocker_constants.py +0 -125
- package/hooks/workflow/doc_gist_auto_publish.py +0 -144
- package/hooks/workflow/md_to_html_companion.py +0 -358
- package/hooks/workflow/test_doc_gist_auto_publish.py +0 -117
- package/hooks/workflow/test_md_to_html_companion.py +0 -613
- package/skills/bugteam/reference/audit-contract.md +0 -163
- package/skills/bugteam/scripts/_bugteam_permissions_common.py +0 -455
- package/skills/bugteam/scripts/bugteam_scripts_constants/claude_permissions_common_constants.py +0 -69
- package/skills/bugteam/scripts/grant_project_claude_permissions.py +0 -280
- package/skills/bugteam/scripts/revoke_project_claude_permissions.py +0 -266
- package/skills/bugteam/scripts/test__bugteam_permissions_common.py +0 -160
- package/skills/bugteam/scripts/test_agent_config_carveout.py +0 -356
- package/skills/bugteam/scripts/test_bugteam_permissions_common.py +0 -140
- package/skills/doc-gist/CLAUDE.md +0 -25
- package/skills/doc-gist/SKILL.md +0 -97
- package/skills/doc-gist/references/CLAUDE.md +0 -9
- package/skills/doc-gist/references/examples/01-exploration-code-approaches.html +0 -453
- package/skills/doc-gist/references/examples/02-exploration-visual-designs.html +0 -515
- package/skills/doc-gist/references/examples/03-code-review-pr.html +0 -638
- package/skills/doc-gist/references/examples/04-code-understanding.html +0 -491
- package/skills/doc-gist/references/examples/05-design-system.html +0 -629
- package/skills/doc-gist/references/examples/06-component-variants.html +0 -605
- package/skills/doc-gist/references/examples/07-prototype-animation.html +0 -455
- package/skills/doc-gist/references/examples/08-prototype-interaction.html +0 -396
- package/skills/doc-gist/references/examples/09-slide-deck.html +0 -592
- package/skills/doc-gist/references/examples/10-svg-illustrations.html +0 -492
- package/skills/doc-gist/references/examples/11-status-report.html +0 -528
- package/skills/doc-gist/references/examples/12-incident-report.html +0 -596
- package/skills/doc-gist/references/examples/13-flowchart-diagram.html +0 -395
- package/skills/doc-gist/references/examples/14-research-feature-explainer.html +0 -381
- package/skills/doc-gist/references/examples/15-research-concept-explainer.html +0 -368
- package/skills/doc-gist/references/examples/16-implementation-plan.html +0 -702
- package/skills/doc-gist/references/examples/17-pr-writeup.html +0 -595
- package/skills/doc-gist/references/examples/18-editor-triage-board.html +0 -573
- package/skills/doc-gist/references/examples/19-editor-feature-flags.html +0 -663
- package/skills/doc-gist/references/examples/20-editor-prompt-tuner.html +0 -722
- package/skills/doc-gist/references/examples/21-decision-signoff.html +0 -546
- package/skills/doc-gist/references/examples/CLAUDE.md +0 -25
- package/skills/doc-gist/references/examples/README.md +0 -5
- package/skills/doc-gist/scripts/CLAUDE.md +0 -27
- package/skills/doc-gist/scripts/doc_gist_scripts_constants/CLAUDE.md +0 -10
- package/skills/doc-gist/scripts/doc_gist_scripts_constants/gist_upload_constants.py +0 -16
- package/skills/doc-gist/scripts/gist_upload.py +0 -177
- package/skills/doc-gist/scripts/test_gist_upload.py +0 -51
- /package/skills/{doc-gist/scripts/doc_gist_scripts_constants → log-audit/scripts/log_audit_constants}/__init__.py +0 -0
|
@@ -27,7 +27,7 @@ description: >-
|
|
|
27
27
|
|
|
28
28
|
Refusals — first match wins; respond with the quoted line exactly and stop:
|
|
29
29
|
|
|
30
|
-
- **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` (comma-separated, case-insensitive, whitespace-tolerant)
|
|
30
|
+
- **Disabled via environment.** When `CLAUDE_REVIEWS_DISABLED` (comma-separated, case-insensitive, whitespace-tolerant) has the token `bugteam`: `/monitor-open-prs is a /bugteam dispatcher and /bugteam is disabled via CLAUDE_REVIEWS_DISABLED.` Gate semantics live in the `reviewer-gates` skill ([../reviewer-gates/SKILL.md](../reviewer-gates/SKILL.md)).
|
|
31
31
|
- **GitHub API not accessible.** `get_me failed. /monitor-open-prs needs active GitHub MCP credentials.`
|
|
32
32
|
- **Dirty tree on the caller's repo.** `Uncommitted changes detected. Stash, commit, or revert before /monitor-open-prs.`
|
|
33
33
|
- **Required subagents missing.** Confirm `code-quality-agent` and `clean-coder` exist. Else: `Required subagent type <name> not installed.`
|
|
@@ -41,7 +41,7 @@ Call `scripts/discover_open_prs.discover_open_prs(all_owners=["jl-cmd", "JonEcho
|
|
|
41
41
|
For each discovered PR:
|
|
42
42
|
|
|
43
43
|
1. Resolve the PR's repo checkout (existing worktree or fresh `git clone`).
|
|
44
|
-
2. From that checkout, invoke `/bugteam --bugbot-retrigger <pr_number>`. When `
|
|
44
|
+
2. From that checkout, invoke `/bugteam --bugbot-retrigger <pr_number>`. When `python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer bugbot` exits 0 (bugbot disabled), omit `--bugbot-retrigger` from the dispatched command so the bugbot leg sits out the run.
|
|
45
45
|
3. The `--bugbot-retrigger` flag tells bugteam to post `bugbot run` as an issue comment after every successful FIX push so Cursor's bugbot re-evaluates the new commit.
|
|
46
46
|
4. Bugteam runs its own 20-loop audit/fix cycle per PR; this skill waits for each bugteam invocation to return before dispatching the next (or fanning out — see below).
|
|
47
47
|
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: post-audit-findings
|
|
3
|
+
description: >-
|
|
4
|
+
Publishes an audit pass as one GitHub PR review via the shared
|
|
5
|
+
post_audit_thread.py: maps audit findings to the {path, line, side,
|
|
6
|
+
severity, description, fix_summary} JSON shape (splitting failure_mode at
|
|
7
|
+
the literal `Fix:` heading), partitions anchored vs unanchored findings so
|
|
8
|
+
one bad anchor cannot reject the whole POST, handles the self-PR reviewer
|
|
9
|
+
toggle via BUGTEAM_REVIEWER_ACCOUNT, and harvests finding comment ids and
|
|
10
|
+
PRRT thread node ids for the fix loop. Invoked by audit skills (bugteam,
|
|
11
|
+
qbug, findbugs) after each audit pass, CLEAN or DIRTY; not for ad-hoc PR
|
|
12
|
+
comments or review replies.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Post Audit Findings
|
|
16
|
+
|
|
17
|
+
**Core principle:** every audit pass — CLEAN or DIRTY — lands on the PR as exactly one review, so the unresolved-thread gate sees the pass and each finding becomes its own resolvable thread.
|
|
18
|
+
|
|
19
|
+
## How callers invoke this
|
|
20
|
+
|
|
21
|
+
- **Skill-capable contexts** (a lead session with the `Skill` tool): `Skill({skill: "post-audit-findings", args: "--skill <caller> --owner <O> --repo <R> --pr-number <N> --commit <head_sha> --state <CLEAN|DIRTY> --findings-json <path>"})`.
|
|
22
|
+
- **Fallback** (a subagent or teammate without the `Skill` tool): the caller's spawn prompt says "Read `~/.claude/skills/post-audit-findings/SKILL.md` and apply it with the parameters below."
|
|
23
|
+
|
|
24
|
+
`--skill <caller>` is required — the posted review names the audit skill that produced it. Capture `<head_sha>` once, at the start of the posting step, via `git rev-parse HEAD` in the worktree the diff was scoped against.
|
|
25
|
+
|
|
26
|
+
## Findings-JSON mapping
|
|
27
|
+
|
|
28
|
+
The findings file's root is a list of objects shaped `{path, line, side, severity, description, fix_summary}`. Map each audit finding:
|
|
29
|
+
|
|
30
|
+
- Finding `file` → `path`.
|
|
31
|
+
- The `failure_mode` field carries both the failure narrative AND the `Fix:` / `Validation:` text (per [`agents/code-quality-agent.md`](../../agents/code-quality-agent.md), "The `failure_mode` field is the audit-to-fix handoff"). Split it at the literal `Fix:` heading: the prefix becomes `description`; the suffix starting at `Fix:` (including the trailing `Validation:` clause) becomes `fix_summary`. When a finding omits the `Fix:` heading, write the full `failure_mode` text to BOTH fields so the script's `INLINE_COMMENT_BODY_TEMPLATE` still renders coherently.
|
|
32
|
+
- Set `side="RIGHT"` for every entry.
|
|
33
|
+
- On CLEAN, the file holds an empty array (`[]`).
|
|
34
|
+
|
|
35
|
+
## Anchored vs unanchored partition
|
|
36
|
+
|
|
37
|
+
Before serializing, partition findings into **anchored** (the line appears in the diff at `--commit`) and **unanchored** (it does not). Only anchored findings go into the JSON — the GitHub reviews API rejects the ENTIRE POST when any inline comment targets a line outside the diff, so a single unanchored entry breaks the whole review. Surface unanchored findings in the caller's user-facing report instead.
|
|
38
|
+
|
|
39
|
+
Zero anchored findings → `--state CLEAN`; one or more → `--state DIRTY`.
|
|
40
|
+
|
|
41
|
+
## Posting
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
python "$HOME/.claude/_shared/pr-loop/scripts/post_audit_thread.py" \
|
|
45
|
+
--skill <caller> \
|
|
46
|
+
--owner <owner> \
|
|
47
|
+
--repo <repo> \
|
|
48
|
+
--pr-number <N> \
|
|
49
|
+
--commit <head_sha> \
|
|
50
|
+
--state <CLEAN|DIRTY> \
|
|
51
|
+
--findings-json <path>
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The script POSTs one review to `/repos/{owner}/{repo}/pulls/{N}/reviews`: `event=APPROVE` on CLEAN (GitHub stores it as `state=APPROVED`; empty `comments[]`; body documents "no findings"), `event=REQUEST_CHANGES` on DIRTY (one inline anchored comment per finding; each becomes its own resolvable thread).
|
|
55
|
+
|
|
56
|
+
**Self-PR auto-toggle.** GitHub rejects both `APPROVE` and `REQUEST_CHANGES` with HTTP 422 when the authenticated identity matches the PR author. `post_audit_thread.py` detects this via `gh api user` + `gh api repos/<o>/<r>/pulls/<n>` and auto-resolves an alternate gh account's token for the reviews POST — the active `gh auth` account is not mutated; only the bearer token on the request changes, so no swap-back step exists.
|
|
57
|
+
|
|
58
|
+
Configuration:
|
|
59
|
+
|
|
60
|
+
- `GH_TOKEN` / `GITHUB_TOKEN` env vars take precedence over the toggle — set one to pin a reviewer identity by token.
|
|
61
|
+
- `BUGTEAM_REVIEWER_ACCOUNT` names which authenticated alternate to prefer (for example `BUGTEAM_REVIEWER_ACCOUNT=jl-cmd`). The env var name is shared across every skill that invokes `post_audit_thread.py`. When unset, the script falls back to the first alternate account `gh auth status` reports.
|
|
62
|
+
- The named alternate must be logged in (`gh auth login -h github.com -u <login>`) before the audit runs; on self-PR with no usable alternate the script exits 1 with a message pointing at `gh auth login`.
|
|
63
|
+
|
|
64
|
+
**Exit codes:** `0` — success; the new review's `html_url` prints to stdout (surface it in the caller's report). `1` — user input error. `2` — retry exhaustion (1s / 4s / 16s backoff across four attempts): a hard blocker. On exit 2 the caller halts the loop (`error: post_audit_thread retry exhausted`) or tells the user the review post failed and the unresolved-thread gate will not see this pass — never a silent retry.
|
|
65
|
+
|
|
66
|
+
## Harvest ids for the fix loop
|
|
67
|
+
|
|
68
|
+
After a DIRTY post, fetch the new review's threads once and record, per finding: the finding comment's URL, its numeric comment id (the reply anchor), and its thread node id (`PRRT_…`, the `resolve_thread` handle):
|
|
69
|
+
|
|
70
|
+
```
|
|
71
|
+
pull_request_read(method="get_review_comments", ...) → match comments to the new review id
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Hand the id triple to the fix step — `pr-fix-protocol` consumes it for the reply-and-resolve unit. Harvesting at post time saves the fix executor a second fetch and pins the mapping before any thread state changes.
|
|
75
|
+
|
|
76
|
+
## Gotchas
|
|
77
|
+
|
|
78
|
+
- **One unanchored comment voids the whole review.** The partition is not an optimization; skipping it makes every DIRTY post a coin flip.
|
|
79
|
+
- **CLEAN still posts.** A pass that finds nothing and posts nothing is invisible to the unresolved-thread gate and to anyone auditing the loop's history.
|
|
80
|
+
- **Exit 2 is a stop, not a retry.** The script already retried with backoff; a caller looping on top of it hammers the API to no effect.
|
|
81
|
+
|
|
82
|
+
## Folder map
|
|
83
|
+
|
|
84
|
+
- `SKILL.md` — this file. The posting script and its body template constants live in `_shared/pr-loop/scripts/` (`post_audit_thread.py`, `pr_loop_shared_constants/post_audit_thread_constants.py`); the review-payload reference is `_shared/pr-loop/gh-payloads.md`.
|
|
@@ -27,3 +27,5 @@ Drives a draft PR to convergence by looping Cursor Bugbot, a code-review pass, a
|
|
|
27
27
|
- The pre-flight gate requires `EnterWorktree` before any file read or API call, and confirms `ScheduleWakeup` is in the tool list.
|
|
28
28
|
- All findings and PR reports state verified facts only — no hedging language.
|
|
29
29
|
- The GitHub MCP (`pull_request_read`, `pull_request_review_write`) is the primary path for PR inspection; `gh api` is the fallback.
|
|
30
|
+
- Three step-scoped agents (`fix_executor`, `thread_sweep`, `copilot_watch`) persist across ticks via the `persistent_agents` map in loop state; each tick resumes them with `SendMessage` and spawns a fresh named agent when a stored id is dead.
|
|
31
|
+
- The tick delegates shared mechanics to five sibling sub-skills: `reviewer-gates` (opt-out, Copilot quota, Bugbot trigger), `pr-scope-resolve` (target resolution), `pr-fix-protocol` (fix sequence + unresolved-thread sweep), `post-audit-findings` (audit posting), and `pr-loop-lifecycle` (run open/close).
|
|
@@ -43,25 +43,26 @@ working directory routes into the PR's repo for local work and returns to
|
|
|
43
43
|
the session worktree before teardown. See
|
|
44
44
|
[`reference/per-tick.md` § Step 1.5](reference/per-tick.md).
|
|
45
45
|
|
|
46
|
+
## Resume from a prior run
|
|
47
|
+
|
|
48
|
+
Before Step 0, check
|
|
49
|
+
`~/.claude/runtime/pr-loop/bugteam-pr-<PR number>/handoff.json`. When
|
|
50
|
+
`$CLAUDE_JOB_DIR/pr-converge-state.json` is absent but that handoff exists, seed
|
|
51
|
+
`phase`, `tick_count`, and the clean-at SHAs from the run's `state-copy.json`, so
|
|
52
|
+
a fresh session continues where the last one stopped rather than restarting at
|
|
53
|
+
BUGBOT.
|
|
54
|
+
|
|
46
55
|
## Copilot quota pre-check (start of run)
|
|
47
56
|
|
|
48
|
-
On the first tick,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
the exact `.env` path and key to set.
|
|
58
|
-
|
|
59
|
-
Run the pre-check once, not per tick. Every tick reads `copilot_down` from
|
|
60
|
-
state. When `copilot_down` is true, skip the Copilot gate outright — no fetch,
|
|
61
|
-
no request, no poll, no agent — and export `CLAUDE_REVIEWS_DISABLED="copilot"`
|
|
62
|
-
in that tick's shell before the convergence check, so `check_convergence.py`
|
|
63
|
-
bypasses the Copilot review gate and the pending-requested-reviews gate and the
|
|
64
|
-
run still marks ready on the remaining signals.
|
|
57
|
+
On the first tick, apply the `reviewer-gates` skill's Copilot quota gate
|
|
58
|
+
(`../reviewer-gates/SKILL.md` § Gate 2) — once per run, never per tick. The
|
|
59
|
+
flag lives in `pr-converge-state.json` as `copilot_down`, and every tick reads
|
|
60
|
+
it. While `copilot_down` is true, the tick skips all Copilot work — no fetch,
|
|
61
|
+
no request, no poll, no agent — and exports
|
|
62
|
+
`CLAUDE_REVIEWS_DISABLED="copilot"` in that tick's shell before
|
|
63
|
+
`check_convergence.py`, so the check bypasses the Copilot review gate and the
|
|
64
|
+
pending-requested-reviews gate and the run still marks ready on the remaining
|
|
65
|
+
signals.
|
|
65
66
|
|
|
66
67
|
## Budget-aware tick boundaries
|
|
67
68
|
|
|
@@ -69,7 +70,8 @@ Before starting any tick, estimate whether the remaining session/usage
|
|
|
69
70
|
budget covers one full clean tick (worst case: a BUGBOT fetch + a
|
|
70
71
|
full-diff CODE_REVIEW + a fix commit + replies). If it does not, do not
|
|
71
72
|
start the tick. Stop at the current tick boundary: write updated state to
|
|
72
|
-
`$CLAUDE_JOB_DIR/pr-converge-state.json`,
|
|
73
|
+
`$CLAUDE_JOB_DIR/pr-converge-state.json`, write the durable handoff (see
|
|
74
|
+
[State persistence](#state-persistence)), then report the exact resume
|
|
73
75
|
command (`/pr-converge <PR URL>`) and the persisted `phase`/`tick_count`.
|
|
74
76
|
A tick cut off mid-flight poisons the resume state — clean SHAs recorded
|
|
75
77
|
against work that never landed — so an unstarted tick is always cheaper
|
|
@@ -91,10 +93,58 @@ On tick entry, read this file if it exists to restore phase, tick_count, and
|
|
|
91
93
|
clean-at SHAs. On tick exit, write updated state before calling ScheduleWakeup
|
|
92
94
|
so the next tick resumes with accurate state.
|
|
93
95
|
|
|
96
|
+
After the state write and before ScheduleWakeup, write the durable handoff so a
|
|
97
|
+
fresh session in a new job can resume this run:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
python "$HOME/.claude/skills/_shared/pr-loop/scripts/write_handoff.py" \
|
|
101
|
+
--pr-number <N> --head-ref <branch> --phase <phase> \
|
|
102
|
+
--resume-command "/pr-converge <PR URL>" \
|
|
103
|
+
--state-file "$CLAUDE_JOB_DIR/pr-converge-state.json" \
|
|
104
|
+
--completed-steps "<clean phases this run>"
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
It writes `handoff.json`, `HANDOFF.md`, and `state-copy.json` under
|
|
108
|
+
`~/.claude/runtime/pr-loop/<run-name>/`. The job-dir state stays the source of
|
|
109
|
+
truth for a resumed tick; the handoff copy is the pointer a fresh session reads
|
|
110
|
+
when `$CLAUDE_JOB_DIR` is gone.
|
|
111
|
+
|
|
94
112
|
Fields: `phase`, `tick_count`, `bugbot_clean_at`, `code_review_clean_at`,
|
|
95
113
|
`bugteam_clean_at`, `copilot_clean_at`, `current_head`,
|
|
96
114
|
`bugbot_acknowledged_at`, `bugbot_down`, `copilot_down`,
|
|
97
|
-
`bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick
|
|
115
|
+
`bugteam_skill_invoked_at_head`, `bugteam_skill_invoked_at_tick`,
|
|
116
|
+
`agents_session_id`, `persistent_agents`.
|
|
117
|
+
|
|
118
|
+
## Persistent per-step agents
|
|
119
|
+
|
|
120
|
+
Three step-scoped agents persist across ticks so their context carries
|
|
121
|
+
forward: `fix_executor`, `thread_sweep`, and `copilot_watch`, recorded in
|
|
122
|
+
the `persistent_agents` map
|
|
123
|
+
([`reference/state-schema.md`](reference/state-schema.md)).
|
|
124
|
+
|
|
125
|
+
- **Resume:** read `persistent_agents.<key>`. When an entry exists,
|
|
126
|
+
`SendMessage` to the stored `agent_id` with a tick payload that restates
|
|
127
|
+
the PR scope, `current_head`, the PR worktree path, this tick's findings
|
|
128
|
+
or threads, and the report-back contract. Bump `last_used_tick`, then
|
|
129
|
+
await completion.
|
|
130
|
+
- **First spawn:** `Agent(subagent_type: "clean-coder", name:
|
|
131
|
+
"prc-fix-<PR#>")` — the `name` makes the agent a persistent teammate
|
|
132
|
+
that idles awaiting messages. Record `{agent_id, created_tick,
|
|
133
|
+
last_used_tick}` under the step key. Keep the spawn prompt fix-shaped,
|
|
134
|
+
never audit-shaped: the `pr_converge_bugteam_enforcer` hook blocks
|
|
135
|
+
audit-shaped clean-coder spawns during the BUGTEAM phase.
|
|
136
|
+
- **Stale or dead id:** on a `SendMessage` failure, or no acknowledgment
|
|
137
|
+
within one bounded wait, drop the map entry, spawn a fresh named agent,
|
|
138
|
+
record it, and continue the tick. Never abort a tick on a stale id;
|
|
139
|
+
never retry the same dead id.
|
|
140
|
+
- **Fresh every round (never persisted):** the Step 5 `/code-review --fix`
|
|
141
|
+
pass and the Step 6 bugteam audit (unbiased eyes each round; the
|
|
142
|
+
enforcer needs the formal Skill call), and every `code-verifier` — a
|
|
143
|
+
named code-verifier never fires `SubagentStop`, so no verdict mints (see
|
|
144
|
+
the named-`code-verifier` entry in the Gotchas list below).
|
|
145
|
+
- **Shutdown:** at loop end (convergence or a stop condition), send each
|
|
146
|
+
persistent agent a shutdown request and clear `persistent_agents` before
|
|
147
|
+
the `pr-loop-lifecycle` Close.
|
|
98
148
|
|
|
99
149
|
## Gotchas
|
|
100
150
|
|
|
@@ -159,13 +209,9 @@ script invocations, decision criteria. Every "return to Step N" means the next
|
|
|
159
209
|
tick starts fresh from that step.
|
|
160
210
|
|
|
161
211
|
**Hard gate: do not advance from any step while ANY unresolved review
|
|
162
|
-
thread exists on the PR.** The thread
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
resolve the thread via `pull_request_review_write(method="resolve_thread")`.
|
|
166
|
-
For each unresolved thread, verify the concern against current HEAD;
|
|
167
|
-
either fix-and-resolve, or reply-with-note-and-resolve when the concern
|
|
168
|
-
no longer applies.
|
|
212
|
+
thread exists on the PR.** The sweep semantics and per-thread handling live
|
|
213
|
+
in the `pr-fix-protocol` skill's unresolved-thread sweep
|
|
214
|
+
(`../pr-fix-protocol/SKILL.md`).
|
|
169
215
|
|
|
170
216
|
**Full-PR-diff rule: every CODE-REVIEW round (Step 5) and every BUGTEAM
|
|
171
217
|
round (Step 6) covers the FULL `origin/main...HEAD` diff — every file
|
|
@@ -176,11 +222,13 @@ against a partial diff is not a valid clean. Re-run the round against the
|
|
|
176
222
|
full diff before recording `code_review_clean_at` or treating the bugteam
|
|
177
223
|
round as converged. This rule holds every tick, every loop, every PR.
|
|
178
224
|
|
|
179
|
-
- [ ] **Step 0:
|
|
180
|
-
`
|
|
225
|
+
- [ ] **Step 0: Open the run**
|
|
226
|
+
Apply the `pr-loop-lifecycle` skill's Open section
|
|
227
|
+
(`../pr-loop-lifecycle/SKILL.md`): permission grant + worktree preflight.
|
|
181
228
|
|
|
182
229
|
- [ ] **Step 1: Resolve PR scope + PR worktree**
|
|
183
|
-
|
|
230
|
+
Apply the `pr-scope-resolve` skill (`../pr-scope-resolve/SKILL.md`)
|
|
231
|
+
with caller `pr-converge`. Resolve the **PR
|
|
184
232
|
worktree** — the local checkout every local step this tick targets:
|
|
185
233
|
the `EnterWorktree` checkout when the PR shares the session's repo,
|
|
186
234
|
else a checkout of the PR's repo that the working directory routes
|
|
@@ -205,6 +253,8 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
205
253
|
See: [`reference/per-tick.md` § Step 2 BUGBOT + Step 3](reference/per-tick.md)
|
|
206
254
|
|
|
207
255
|
- [ ] **Opt-out gate (runs first, every BUGBOT entry).**
|
|
256
|
+
Gate semantics live in the `reviewer-gates` skill
|
|
257
|
+
(`../reviewer-gates/SKILL.md` § Gate 1).
|
|
208
258
|
`python "$HOME/.claude/_shared/pr-loop/scripts/reviews_disabled.py" --reviewer bugbot`
|
|
209
259
|
- [ ] Exit 0 (`CLAUDE_REVIEWS_DISABLED` lists `bugbot`) → set `bugbot_down = true`, `phase = CODE_REVIEW`, advance to Step 5 (bypass). Cursor Bugbot is skipped for the entire run.
|
|
210
260
|
- [ ] Exit 1 → continue below.
|
|
@@ -212,9 +262,7 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
212
262
|
Fetch bugbot reviews + inline comments on `current_head`.
|
|
213
263
|
|
|
214
264
|
- [ ] **dirty** (findings on `current_head`) →
|
|
215
|
-
- [ ]
|
|
216
|
-
- [ ] Reply to each finding comment via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py`
|
|
217
|
-
- [ ] Resolve each addressed thread via `pull_request_review_write(method="resolve_thread")`
|
|
265
|
+
- [ ] Apply the `pr-fix-protocol` skill (`../pr-fix-protocol/SKILL.md`) to this tick's findings
|
|
218
266
|
- [ ] Push → return to Step 4
|
|
219
267
|
- [ ] **clean** (no findings on `current_head`) →
|
|
220
268
|
- [ ] Count ALL unresolved threads on PR (`is_resolved == false`) → zero? advance; >0? fix + resolve first
|
|
@@ -222,14 +270,12 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
222
270
|
- [ ] `phase = CODE_REVIEW`
|
|
223
271
|
- [ ] Advance to Step 5
|
|
224
272
|
- [ ] **no review yet / commit_id mismatch** →
|
|
225
|
-
|
|
226
|
-
-
|
|
227
|
-
|
|
228
|
-
- [ ]
|
|
229
|
-
- [ ]
|
|
230
|
-
- [ ]
|
|
231
|
-
- [ ] Exit non-zero → `bugbot_down = true` → `phase = CODE_REVIEW` → advance to Step 5 (bypass)
|
|
232
|
-
- [ ] Exit 0 → record `bugbot_acknowledged_at`, schedule 360s wakeup → return to Step 4
|
|
273
|
+
Apply the `reviewer-gates` skill's Bugbot flow
|
|
274
|
+
(`../reviewer-gates/SKILL.md` § Gate 3) against `current_head`,
|
|
275
|
+
then map its outcomes:
|
|
276
|
+
- [ ] Silent pass → `bugbot_clean_at = current_head` → `phase = CODE_REVIEW` → advance to Step 5
|
|
277
|
+
- [ ] Already queued, or trigger acknowledged → schedule 360s wakeup → return to Step 4 next tick
|
|
278
|
+
- [ ] Bugbot down → `bugbot_down = true` → `phase = CODE_REVIEW` → advance to Step 5 (bypass)
|
|
233
279
|
- [ ] **Step 5: CODE-REVIEW — run, fix, reset, advance**
|
|
234
280
|
See: [`reference/per-tick.md` § Step 2 CODE_REVIEW](reference/per-tick.md)
|
|
235
281
|
|
|
@@ -264,13 +310,13 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
264
310
|
- [ ] Re-trigger bugbot (Step 4 "no review yet" checklist)
|
|
265
311
|
- [ ] `phase = BUGBOT` → schedule 360s wakeup → return to Step 4
|
|
266
312
|
- [ ] **clean** (no changes applied) →
|
|
267
|
-
- [ ]
|
|
313
|
+
- [ ] Zero unresolved threads per the `pr-fix-protocol` sweep (`../pr-fix-protocol/SKILL.md`) → advance; else fix + resolve first (same skill)
|
|
268
314
|
- [ ] `code_review_clean_at = current_head`
|
|
269
315
|
- [ ] `phase = BUGTEAM` → advance to Step 6
|
|
270
316
|
|
|
271
317
|
- [ ] **Step 6: BUGTEAM — run, decide, fix, reply, resolve**
|
|
272
318
|
See: [`reference/per-tick.md` § Step 2 BUGTEAM](reference/per-tick.md);
|
|
273
|
-
[
|
|
319
|
+
[`../bugteam/SKILL.md`](../bugteam/SKILL.md)
|
|
274
320
|
|
|
275
321
|
Pre-condition: `code_review_clean_at == current_head`.
|
|
276
322
|
|
|
@@ -300,9 +346,7 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
300
346
|
- [ ] **converged + `bugbot_clean_at ≠ current_head`** →
|
|
301
347
|
`phase = BUGBOT` → schedule 360s wakeup → return to Step 4
|
|
302
348
|
- [ ] **findings without committed fixes** →
|
|
303
|
-
- [ ]
|
|
304
|
-
- [ ] Reply to each finding comment via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py`
|
|
305
|
-
- [ ] Resolve each addressed thread via `pull_request_review_write(method="resolve_thread")`
|
|
349
|
+
- [ ] Apply the `pr-fix-protocol` skill (`../pr-fix-protocol/SKILL.md`) to the findings
|
|
306
350
|
- [ ] Push → `phase = BUGBOT` → return to Step 4
|
|
307
351
|
|
|
308
352
|
- [ ] **Step 7: Convergence gates**
|
|
@@ -317,15 +361,13 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
317
361
|
pull_request_read(method="get_review_comments")
|
|
318
362
|
→ filter threads where is_resolved == false
|
|
319
363
|
```
|
|
320
|
-
- [ ] Any unresolved? →
|
|
321
|
-
if still applies → Fix (spawn `clean-coder`) → reply → resolve;
|
|
322
|
-
if no longer applies → reply-with-note → resolve. Push if any code changed → return to Step 4
|
|
364
|
+
- [ ] Any unresolved? → apply the `pr-fix-protocol` skill's unresolved-thread sweep (`../pr-fix-protocol/SKILL.md`). Push if any code changed → return to Step 4
|
|
323
365
|
- [ ] When `copilot_down == true` (start-of-run quota pre-check), skip the Copilot fetch below — no request, no poll, no agent — and continue to gate (b); the Copilot gate is bypassed for the whole run.
|
|
324
366
|
- [ ] Fetch Copilot review on `current_head` (top-level review state — uses get_reviews, identifies by reviewer):
|
|
325
367
|
```
|
|
326
368
|
python ~/.claude/skills/pr-converge/scripts/fetch_copilot_reviews.py --owner <O> --repo <R> --pr-number <N>
|
|
327
369
|
```
|
|
328
|
-
- [ ] dirty →
|
|
370
|
+
- [ ] dirty → apply the `pr-fix-protocol` skill (`../pr-fix-protocol/SKILL.md`) → push → return to Step 4
|
|
329
371
|
- [ ] clean (no findings) → `copilot_clean_at = current_head` → gate (b)
|
|
330
372
|
- [ ] no review yet → gate (b)
|
|
331
373
|
|
|
@@ -357,10 +399,7 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
357
399
|
→ count threads where is_resolved == false
|
|
358
400
|
```
|
|
359
401
|
- [ ] zero unresolved → gate (e)
|
|
360
|
-
- [ ] unresolved →
|
|
361
|
-
still applies → Fix (spawn `clean-coder`) → reply → resolve;
|
|
362
|
-
no longer applies → reply-with-note → resolve.
|
|
363
|
-
Push if code changed → return to Step 4
|
|
402
|
+
- [ ] unresolved → apply the `pr-fix-protocol` skill's unresolved-thread sweep (`../pr-fix-protocol/SKILL.md`). Push if code changed → return to Step 4
|
|
364
403
|
|
|
365
404
|
**(e) Mark ready**
|
|
366
405
|
- [ ] When `copilot_down == true`, export `CLAUDE_REVIEWS_DISABLED="copilot"` in this tick's shell before the check below, so it bypasses the Copilot review gate and the pending-requested-reviews gate.
|
|
@@ -384,22 +423,22 @@ round as converged. This rule holds every tick, every loop, every PR.
|
|
|
384
423
|
- [ ] **clean (no findings)** →
|
|
385
424
|
`copilot_clean_at = current_head` → return to Step 7 (re-validate gates b, d, e)
|
|
386
425
|
- [ ] **dirty (findings present)** →
|
|
387
|
-
- [ ]
|
|
388
|
-
- [ ] Reply to each finding comment via `python ~/.claude/skills/pr-converge/scripts/post_fix_reply.py`
|
|
389
|
-
- [ ] Resolve each addressed thread via `pull_request_review_write(method="resolve_thread")`
|
|
426
|
+
- [ ] Apply the `pr-fix-protocol` skill (`../pr-fix-protocol/SKILL.md`) to the findings
|
|
390
427
|
- [ ] Push → `phase = BUGBOT` → return to Step 4
|
|
391
428
|
- [ ] **no review yet** →
|
|
392
429
|
increment `copilot_wait_count` → ≥ 3 = hard blocker → stop
|
|
393
430
|
schedule 360s wakeup → return to Step 7a next tick
|
|
394
431
|
|
|
395
432
|
- [ ] **Step 8: Clean working tree**
|
|
396
|
-
|
|
433
|
+
`pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md`).
|
|
434
|
+
See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Clean working tree](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
397
435
|
|
|
398
436
|
- [ ] **Step 9: Rewrite PR description**
|
|
399
|
-
|
|
437
|
+
`pr-loop-lifecycle` Close.
|
|
438
|
+
See: [`pr-loop-lifecycle/reference/teardown-publish-permissions.md` § Publish the final PR description](../pr-loop-lifecycle/reference/teardown-publish-permissions.md)
|
|
400
439
|
|
|
401
|
-
- [ ] **Step 10: Revoke project permissions**
|
|
402
|
-
`
|
|
440
|
+
- [ ] **Step 10: Revoke project permissions (always)**
|
|
441
|
+
`pr-loop-lifecycle` Close (`../pr-loop-lifecycle/SKILL.md` § Close).
|
|
403
442
|
|
|
404
443
|
- [ ] **Step 11: Print final report**
|
|
405
444
|
Print this block verbatim — no paraphrase, no extra commentary:
|
|
@@ -8,7 +8,7 @@ Reference documents for the `pr-converge` skill. These files define the per-tick
|
|
|
8
8
|
|---|---|
|
|
9
9
|
| `per-tick.md` | Step-by-step procedure for one tick (resolve HEAD, run BUGBOT, CODE_REVIEW, BUGTEAM, COPILOT_WAIT phases, schedule next wakeup) |
|
|
10
10
|
| `convergence-gates.md` | Six gates that must all pass before the PR is marked ready for review |
|
|
11
|
-
| `fix-protocol.md` |
|
|
11
|
+
| `fix-protocol.md` | pr-converge deltas on the `pr-fix-protocol` skill: multi-PR teammate obligations and the same-tick bugbot re-trigger rule |
|
|
12
12
|
| `ground-rules.md` | Non-negotiable constraints for the convergence loop |
|
|
13
13
|
| `stop-conditions.md` | Conditions that end the loop without convergence (cap reached, stuck, user stop) |
|
|
14
14
|
| `state-schema.md` | Fields in `pr-converge-state.json` and their meanings |
|
|
@@ -35,22 +35,21 @@ Decide (four branches; match first whose predicate holds):
|
|
|
35
35
|
|
|
36
36
|
- **`classification == "dirty"` with non-empty inline comments matching
|
|
37
37
|
`pull_request_review_id`:** Fix protocol input (same shape as bugbot
|
|
38
|
-
dirty).
|
|
39
|
-
|
|
40
|
-
[Single-PR fix workflow](fix-protocol.md#single-pr-fix-workflow) for
|
|
41
|
-
full contract).
|
|
38
|
+
dirty). Apply the `pr-fix-protocol` skill
|
|
39
|
+
(`../../pr-fix-protocol/SKILL.md`) → Step 3 in same tick.
|
|
42
40
|
Reset `bugbot_clean_at = null` AND `copilot_clean_at = null`, `phase =
|
|
43
41
|
BUGBOT`, schedule next wakeup, return. Full back-to-back-clean cycle
|
|
44
42
|
plus all six gates must hold again on new HEAD.
|
|
45
43
|
- **`classification == "dirty"` with empty inline comments matching
|
|
46
44
|
`pull_request_review_id`:** Copilot posted findings only in review body
|
|
47
45
|
(`CHANGES_REQUESTED` or `COMMENTED` with non-empty body, no inline
|
|
48
|
-
threads). Parse body for actionable findings.
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
threads). Parse body for actionable findings. Apply the
|
|
47
|
+
`pr-fix-protocol` skill (`../../pr-fix-protocol/SKILL.md`), whose reply
|
|
48
|
+
step for body-only findings posts a top-level review reply citing the
|
|
49
|
+
new HEAD SHA → Step 3 in same tick. Reset
|
|
51
50
|
`bugbot_clean_at = null` AND
|
|
52
51
|
`copilot_clean_at = null`, `phase = BUGBOT`, Step 3 on new HEAD,
|
|
53
|
-
schedule next wakeup, return. Convergence
|
|
52
|
+
schedule next wakeup, return. Convergence needs full
|
|
54
53
|
back-to-back-clean on new HEAD.
|
|
55
54
|
- **`classification == "clean"` (state `APPROVED`):** Set
|
|
56
55
|
`copilot_clean_at = current_head`. Record evidence: "Copilot APPROVED at <SHA>".
|
|
@@ -77,14 +76,15 @@ Decide (four branches; match first whose predicate holds):
|
|
|
77
76
|
|
|
78
77
|
- **`classification == "dirty"` with non-empty inline comments matching
|
|
79
78
|
`pull_request_review_id`:** Treat identically to gate (a) dirty+inline
|
|
80
|
-
path —
|
|
79
|
+
path — apply the `pr-fix-protocol` skill
|
|
80
|
+
(`../../pr-fix-protocol/SKILL.md`). Reset
|
|
81
81
|
`bugbot_clean_at = null` AND `copilot_clean_at = null`, `phase = BUGBOT`,
|
|
82
82
|
schedule next wakeup, return.
|
|
83
83
|
- **`classification == "dirty"` with empty inline comments matching
|
|
84
84
|
`pull_request_review_id`:** Claude posted findings only in review body
|
|
85
85
|
(`CHANGES_REQUESTED` or `COMMENTED` with non-empty body, no inline
|
|
86
|
-
threads). Treat identically to gate (a) dirty+body path —
|
|
87
|
-
|
|
86
|
+
threads). Treat identically to gate (a) dirty+body path — apply the
|
|
87
|
+
`pr-fix-protocol` skill (`../../pr-fix-protocol/SKILL.md`). Reset
|
|
88
88
|
`bugbot_clean_at = null` AND `copilot_clean_at = null`, `phase = BUGBOT`,
|
|
89
89
|
schedule next wakeup, return.
|
|
90
90
|
- **`classification == "clean"` (state `APPROVED`):** Record evidence:
|
|
@@ -176,19 +176,16 @@ pull_request_read(owner=OWNER, repo=REPO, pullNumber=NUMBER, method="get_review_
|
|
|
176
176
|
→ count
|
|
177
177
|
```
|
|
178
178
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
filter on those fields.
|
|
179
|
+
This count is the `pr-fix-protocol` unresolved-thread sweep
|
|
180
|
+
(`../../pr-fix-protocol/SKILL.md`).
|
|
182
181
|
|
|
183
182
|
Decide:
|
|
184
183
|
|
|
185
184
|
- **Zero unresolved threads:** Record evidence:
|
|
186
185
|
"0 unresolved threads across PR at <SHA>". Continue to gate (f).
|
|
187
|
-
- **One or more unresolved threads:** Do **not** mark ready.
|
|
188
|
-
unresolved
|
|
189
|
-
|
|
190
|
-
no longer applies (e.g. code already changed), reply-with-note
|
|
191
|
-
explaining why and resolve. Push if any code changed → reset
|
|
186
|
+
- **One or more unresolved threads:** Do **not** mark ready. Apply the
|
|
187
|
+
`pr-fix-protocol` skill's unresolved-thread sweep
|
|
188
|
+
(`../../pr-fix-protocol/SKILL.md`). Push if any code changed → reset
|
|
192
189
|
`bugbot_clean_at = null` AND `copilot_clean_at = null`,
|
|
193
190
|
`phase = BUGBOT`, schedule next wakeup, return. If only resolutions
|
|
194
191
|
(no code changes), re-check this gate without resetting.
|
|
@@ -54,9 +54,8 @@ sufficient. Runs `convergence-gates.md` gates in order:
|
|
|
54
54
|
Next tick: re-run gate (a) fetch → Copilot `APPROVED` at `current_head`
|
|
55
55
|
→ set `copilot_clean_at = current_head`, record evidence: "Copilot
|
|
56
56
|
APPROVED at <SHA>", set `phase = BUGTEAM`, re-validate gates (b) and (c).
|
|
57
|
-
Gate (e): `
|
|
58
|
-
|
|
59
|
-
filter) → zero across PR → record evidence.
|
|
57
|
+
Gate (e): the `pr-fix-protocol` unresolved-thread sweep
|
|
58
|
+
(`../../pr-fix-protocol/SKILL.md`) → zero across PR → record evidence.
|
|
60
59
|
Gate (f): all six gates pass → `update_pull_request(pullNumber=NUMBER,
|
|
61
60
|
owner=OWNER, repo=REPO, draft=false)`.
|
|
62
61
|
Reports "PR #N converged: bugbot CLEAN at <SHA>, bugteam CLEAN at <SHA>,
|
|
@@ -100,8 +99,9 @@ Copilot review `state: APPROVED` at `current_head`. Claude: [re-runs
|
|
|
100
99
|
gate (a) fetch → APPROVED → sets `copilot_clean_at = current_head`,
|
|
101
100
|
records evidence: "Copilot APPROVED at <SHA>", sets `phase = BUGTEAM`;
|
|
102
101
|
re-validates gates (b) Claude absent and (c) mergeability clean,
|
|
103
|
-
records evidence for both; gate (e)
|
|
104
|
-
trivially, record evidence:
|
|
102
|
+
records evidence for both; gate (e) — the `pr-fix-protocol`
|
|
103
|
+
unresolved-thread sweep — passes trivially, record evidence:
|
|
104
|
+
"0 unresolved threads across PR at <SHA>";
|
|
105
105
|
runs `update_pull_request(pullNumber=NUMBER,
|
|
106
106
|
owner=OWNER, repo=REPO, draft=false)`; reports "PR #N converged: bugbot CLEAN
|
|
107
107
|
at <SHA>, bugteam CLEAN at <SHA>, mergeable_state clean, copilot CLEAN at
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
# Fix protocol
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
The full fix protocol — executor choice, the shared 13-step sequence,
|
|
4
|
+
reply transport, thread resolution, the unresolved-thread sweep, and
|
|
5
|
+
post-push resets — lives in the `pr-fix-protocol` skill
|
|
6
|
+
(`../../pr-fix-protocol/SKILL.md`), which follows the shared step
|
|
7
|
+
sequence in `../../../_shared/pr-loop/fix-protocol.md`. Hook handling
|
|
6
8
|
per [ground-rules.md](ground-rules.md).
|
|
7
9
|
|
|
10
|
+
This file holds only the pr-converge deltas: the multi-PR teammate
|
|
11
|
+
obligations and the same-tick re-trigger rule.
|
|
12
|
+
|
|
8
13
|
**Multi-PR (`state.json`) teammate obligations** (plus TDD, commit, push):
|
|
9
14
|
|
|
10
15
|
- Replies inline on each addressed finding via
|
|
@@ -21,44 +26,12 @@ per [ground-rules.md](ground-rules.md).
|
|
|
21
26
|
Orchestrator does not reply inline, trigger bugbot, or read repo source
|
|
22
27
|
files during fix phase in multi-PR mode.
|
|
23
28
|
|
|
24
|
-
###
|
|
25
|
-
|
|
26
|
-
**Single-PR (no `state.json`) — same gates, main session executor:**
|
|
27
|
-
|
|
28
|
-
Run every command below in the PR worktree (the working directory routed in
|
|
29
|
-
[per-tick.md § Step 1.5](per-tick.md)). The `git add`, `git commit`, and
|
|
30
|
-
`git push` act on the repo of the current working directory, so a cross-repo
|
|
31
|
-
PR's fix lands in the PR's repo only when the cwd is its worktree. A spawned
|
|
32
|
-
`clean-coder` does not inherit the lead's working directory — name the PR
|
|
33
|
-
worktree path in its prompt and direct it to edit, stage, and commit there,
|
|
34
|
-
matching the worktree-path handoff bugteam embeds in its fix worker's spawn
|
|
35
|
-
prompt.
|
|
36
|
-
|
|
37
|
-
- Read each referenced file:line.
|
|
38
|
-
- Write failing test first when finding has behavior to test. Pure doc /
|
|
39
|
-
comment / naming nits with no behavior → straight to fix.
|
|
40
|
-
- **Implement** via `Agent` (`subagent_type: "clean-coder"`).
|
|
41
|
-
Full-stop if `Agent` is unavailable.
|
|
42
|
-
- Stage affected files and create one new commit on existing branch:
|
|
43
|
-
```bash
|
|
44
|
-
git add <files> && git commit -m "fix(review): <brief summary>"
|
|
45
|
-
```
|
|
46
|
-
**Pre-commit gate:** honor hooks; full-stop on bypass.
|
|
47
|
-
- Push the new commit:
|
|
48
|
-
```bash
|
|
49
|
-
git push origin <BRANCH>
|
|
50
|
-
```
|
|
51
|
-
**Pre-push gate:** honor hooks; full-stop on bypass. Capture new HEAD
|
|
52
|
-
only after both gates pass; set `current_head`, `bugbot_clean_at = null`.
|
|
53
|
-
- Reply inline on each addressed comment thread using `python scripts/post_fix_reply.py`:
|
|
29
|
+
### Same-tick re-trigger rule
|
|
54
30
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
`current_head`, then bugteam CLEAN on same `HEAD` with no
|
|
63
|
-
intervening push. Re-triggering in same tick saves a wakeup cycle vs
|
|
64
|
-
deferring Step 3.
|
|
31
|
+
**After pushing a fix, always run Step 3 (`bugbot run`) in the same
|
|
32
|
+
tick** regardless of phase. A new commit **resets the full convergence
|
|
33
|
+
cycle**: a bugbot clean and a bugteam clean on an older SHA do **not**
|
|
34
|
+
count toward convergence on the new `HEAD`. Re-obtain bugbot CLEAN on
|
|
35
|
+
`current_head`, then bugteam CLEAN on the same `HEAD` with no
|
|
36
|
+
intervening push. Re-triggering in the same tick saves a wakeup cycle
|
|
37
|
+
vs deferring Step 3.
|