Skip to content

Commit 3306918

Browse files
authored
cli/sync: Detect errored syncs, track unmatched patterns (#512)
Detect and report errored git syncs via libvcs SyncResult. Track unmatched patterns separately in sync summary. Exit non-zero on unmatched patterns when --exit-on-error is set. why: git fetch failures (remote unreachable, branch mismatch) were silently reported as successful syncs. Unmatched patterns (typos, stale config) were invisible in the summary and did not trigger --exit-on-error. what: - Add SyncFailedError; check SyncResult.ok in update_repo() - Only pass set_remotes=True for git (not SVN/HG) - Track unmatched_count; add "unmatched" key to summary dict - Extract _emit_summary() helper with conditional display of previewed/unmatched counts and colored total - Fix path-pattern "None" bug in unmatched message - Downgrade duplicate log.info to log.debug - Suppress unmatched lines in --summary-only mode - Exit non-zero when --exit-on-error and unmatched_count > 0 - Bump libvcs 0.38.6 -> 0.39.0 - Add 19 new test cases: errored git/SVN/HG repos, cross-VCS mixed failure, rev/branch mismatch, ambiguous branch/dir, unmatched pattern counting, exit-on-error for unmatched, path pattern None fix, duplicate log regression, redacted repo paths
2 parents b73788b + 382213c commit 3306918

9 files changed

Lines changed: 2089 additions & 134 deletions

File tree

.claude/commands/changelog.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
---
2+
description: Generate CHANGES entries from branch commits and PR context
3+
argument-hint: "[optional additional context about the changes]"
4+
allowed-tools: Bash(git log:*), Bash(git branch:*), Bash(git symbolic-ref:*), Bash(gh pr view:*), Bash(gh pr list:*), Read, Grep, Glob, Edit
5+
---
6+
7+
# Changelog Entry Generator
8+
9+
Generate well-formatted CHANGES entries from the current branch's commits and PR context. This command analyzes commits, categorizes them, and inserts entries into the CHANGES file after user review.
10+
11+
Additional context from user: $ARGUMENTS
12+
13+
---
14+
15+
## Phase 1: Gather Context
16+
17+
**Goal**: Collect all information needed to generate changelog entries.
18+
19+
**Actions**:
20+
21+
1. **Detect project name** from `pyproject.toml`:
22+
- Read `pyproject.toml` and extract the `name` field under `[project]`
23+
- This is used for matching the CHANGES heading format (`## <project> vX.Y.Z`)
24+
25+
2. **Detect trunk branch**:
26+
```
27+
git symbolic-ref refs/remotes/origin/HEAD
28+
```
29+
- Strip `refs/remotes/origin/` prefix to get branch name
30+
- Fall back to `master` if the above fails
31+
32+
3. **Verify not on trunk**:
33+
- Check current branch: `git branch --show-current`
34+
- If on trunk, report "Already on trunk branch — nothing to generate" and stop
35+
36+
4. **Read CHANGES file**:
37+
- Find the CHANGES file (usually `CHANGES` with no extension at project root)
38+
- Identify the unreleased section heading (e.g., `## vcspull v1.51.x (unreleased)`)
39+
- Locate the `<!-- END PLACEHOLDER` marker line — this is where new entries are inserted
40+
- Note any existing entries between `<!-- END PLACEHOLDER -->` and the next `## ` release heading
41+
- Record which section headings (e.g., `### Bug fixes`, `### Features`) already exist in the unreleased block
42+
43+
5. **Check for PR**:
44+
```
45+
gh pr view --json number,title,body,labels 2>/dev/null
46+
```
47+
- If a PR exists, extract the number, title, body, and labels
48+
- If no PR exists, note that `(#???)` placeholders will be used
49+
50+
6. **Get commits**:
51+
```
52+
git log <trunk>..HEAD --oneline
53+
```
54+
- Also get full commit details for body parsing:
55+
```
56+
git log <trunk>..HEAD --format='%H %s%n%b---'
57+
```
58+
- If no commits ahead of trunk, report "No commits ahead of trunk" and stop
59+
60+
---
61+
62+
## Phase 2: Categorize Commits
63+
64+
**Goal**: Parse commits into changelog categories and group related ones.
65+
66+
### Commit type mapping
67+
68+
Parse the commit type from the `Component(type[sub])` convention in commit subjects:
69+
70+
| Commit type | CHANGES section | Notes |
71+
|---|---|---|
72+
| `feat` | Features / New features | New functionality |
73+
| `fix` | Bug fixes | Bug fixes |
74+
| `docs` | Documentation | Doc changes |
75+
| `test` | Tests | Test additions/changes |
76+
| `refactor` | (only if user-visible) | Skip internal-only refactors |
77+
| `chore`, `deps` | Development | Maintenance, dependency bumps |
78+
| `style` | (skip) | Formatting-only changes |
79+
80+
### Grouping rules
81+
82+
- **TDD workflow sequences**: An xfail commit + a fix commit + an xfail-removal commit should collapse into a **single** bug fix entry. The fix commit's message is the primary source.
83+
- **Dependency bumps**: A `pyproject` deps commit + a CHANGES doc commit = 1 entry under "Breaking changes" (if it's a minimum version bump) or "Development"
84+
- **Multi-commit features**: Sequential `feat` commits on the same component collapse into one entry
85+
- **Skip entirely**: merge commits, `commands(feat[...])` commits (adding claude commands), lock-only changes, internal-only refactors
86+
87+
### Output of this phase
88+
89+
A structured list of entries grouped by section, each with:
90+
- Section name (e.g., "Bug fixes")
91+
- Entry text (formatted markdown)
92+
- Source commit(s) for traceability
93+
94+
---
95+
96+
## Phase 3: Generate Entries
97+
98+
**Goal**: Write the exact markdown to be inserted into CHANGES.
99+
100+
### Format rules (derived from existing CHANGES files)
101+
102+
1. **Section headings**: Use `### Section Name` (e.g., `### Bug fixes`, `### Features`)
103+
104+
2. **Section order** (only include sections that have entries):
105+
- Breaking changes
106+
- Features / New features
107+
- Bug fixes
108+
- Documentation
109+
- Tests
110+
- Development
111+
112+
3. **Simple entries** — single bullet:
113+
```markdown
114+
- Brief description of the change (#123)
115+
```
116+
117+
4. **Detailed entries** — sub-heading with description:
118+
```markdown
119+
#### Component: Brief description (#123)
120+
121+
Explanatory paragraph about what changed and why.
122+
123+
- Bullet point with specific detail
124+
- Another detail
125+
```
126+
127+
5. **PR references**:
128+
- If PR number is known: `(#512)`
129+
- If no PR exists: `(#???)`
130+
131+
6. **Match existing style**:
132+
- Check whether the project uses "Bug fixes" or "Bug Fixes" (match existing capitalization)
133+
- Check whether "Features" or "New features" is used
134+
- Preserve the project's conventions
135+
136+
### Entry writing guidelines
137+
138+
- Write from the user's perspective — what changed for them, not internal implementation details
139+
- Lead with the *what*, not the *why* (the description paragraph handles *why*)
140+
- Use present tense for the entry title ("Add support for..." not "Added support for...")
141+
- Don't repeat the section heading in the entry text
142+
- Keep bullet entries to 1-2 lines; use the sub-heading format for anything needing more explanation
143+
144+
---
145+
146+
## Phase 4: Present for Review
147+
148+
**CRITICAL**: This is a mandatory confirmation gate. Never skip to Phase 5 without explicit user approval.
149+
150+
**Present to the user**:
151+
152+
1. **Summary line**:
153+
```
154+
Branch: <branch-name> | Commits: <count> | PR: #<number> (or "none")
155+
```
156+
157+
2. **Proposed entries** in a fenced code block showing the exact markdown:
158+
````
159+
```markdown
160+
### Bug fixes
161+
162+
- Fix phantom "None" message when syncing path-based patterns (#512)
163+
164+
#### cli/sync: Report errored git syncs in summary (#512)
165+
166+
`update_repo()` now detects and reports git sync failures instead of
167+
silently succeeding. The sync summary shows errored repositories
168+
alongside successful and failed counts.
169+
```
170+
````
171+
172+
3. **Insertion point**: Describe where these entries will go:
173+
```
174+
Insert after: <!-- END PLACEHOLDER - ADD NEW CHANGELOG ENTRIES BELOW THIS LINE -->
175+
Before: (next release heading or existing unreleased entries)
176+
```
177+
178+
4. **Ask the user**: "Insert these entries into CHANGES? You can also ask me to modify them first."
179+
180+
**Wait for user response.** Do not proceed until they confirm.
181+
182+
---
183+
184+
## Phase 5: Insert into CHANGES
185+
186+
**Goal**: Insert the approved entries into the CHANGES file.
187+
188+
**Only execute after explicit user approval in Phase 4.**
189+
190+
### Insertion logic
191+
192+
1. **Find the insertion point**: Locate the `<!-- END PLACEHOLDER` line in the CHANGES file
193+
194+
2. **Check for existing unreleased section headings**:
195+
- If the CHANGES file already has a `### Bug fixes` section in the unreleased block, and the new entries also have bug fixes, **append** to the existing section rather than creating a duplicate heading
196+
- If the section doesn't exist yet, insert the full section with heading
197+
198+
3. **Insert the entries**:
199+
- Use the Edit tool to insert after the `<!-- END PLACEHOLDER -->` line
200+
- Ensure exactly one blank line between the placeholder comment and the first section heading
201+
- Ensure exactly one blank line between sections
202+
203+
4. **Show the result**:
204+
- After editing, read the modified region of the CHANGES file and display it so the user can verify
205+
- Note: this command does NOT commit — the user decides when to stage and commit the CHANGES update
206+
207+
### Edge case: merging with existing entries
208+
209+
If there are already entries below the placeholder in the unreleased section:
210+
211+
- New entries for **existing sections** are appended at the end of that section (before the next `###` heading or the next `## ` release heading)
212+
- New entries for **new sections** follow the section order defined in Phase 3 — insert the new section in the correct position relative to existing sections
213+
- Never duplicate a `###` section heading
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
description: Process external code review feedback — validate each point, apply valid fixes as separate commits
3+
argument-hint: "Paste the external code review feedback here"
4+
allowed-tools: Bash(git diff:*), Bash(git log:*), Bash(git branch:*), Bash(git status:*), Bash(git add:*), Bash(git commit:*), Bash(uv run ruff:*), Bash(uv run mypy:*), Bash(uv run py.test:*), Bash(uv run pytest:*), Read, Grep, Glob, Edit, Write
5+
---
6+
7+
# External Code Review Feedback Processor
8+
9+
Process external code review feedback against the current branch. Validate each feedback point independently, apply valid fixes as separate atomic commits, and ensure all quality gates pass after each commit.
10+
11+
Review feedback to process: $ARGUMENTS
12+
13+
---
14+
15+
## Phase 1: Gather Context
16+
17+
**Goal**: Understand what changed on this branch and parse the review feedback.
18+
19+
**Actions**:
20+
21+
1. **Get the diff** against the trunk branch:
22+
```
23+
git diff origin/master --stat
24+
git diff origin/master
25+
```
26+
27+
2. **Get commit history** for this branch:
28+
```
29+
git log origin/master..HEAD --oneline
30+
```
31+
32+
3. **Read modified files** identified in the diff to understand the full context of each change
33+
34+
4. **Parse the review feedback** into a numbered list of discrete, actionable points. Each point should capture:
35+
- **What**: The specific issue or suggestion
36+
- **Where**: File and approximate location
37+
- **Category**: bug, style, logic, naming, performance, test gap, documentation, etc.
38+
39+
5. **Create a todo list** tracking each feedback point with its validation status
40+
41+
---
42+
43+
## Phase 2: Validate Each Feedback Point
44+
45+
**Goal**: Independently assess whether each feedback point is valid and actionable.
46+
47+
For EACH feedback point:
48+
49+
1. **Read the relevant code** — the exact lines the reviewer is referring to
50+
51+
2. **Assess validity** using these criteria:
52+
- **Valid**: The feedback identifies a real issue or improvement that aligns with project conventions (CLAUDE.md)
53+
- **Already addressed**: The issue was already fixed in a later commit on the branch
54+
- **Incorrect**: The reviewer misread the code or the suggestion would introduce a bug
55+
- **Out of scope**: Valid concern but belongs in a separate PR/issue
56+
- **Subjective/style**: Preference-based with no clear project convention favoring it
57+
58+
3. **Document the verdict** for each point:
59+
- If valid: note exactly what change is needed and in which file(s)
60+
- If invalid: note the specific reason (cite code, tests, or CLAUDE.md conventions)
61+
62+
4. **Present the validation results** to the user before making any changes:
63+
- List each feedback point with its verdict (valid / invalid / out of scope)
64+
- For invalid points, explain why concisely
65+
- For valid points, describe the planned fix
66+
- **Wait for user confirmation** before proceeding to Phase 3
67+
68+
---
69+
70+
## Phase 3: Apply Valid Fixes (One Commit Per Point)
71+
72+
**Goal**: Apply each valid feedback point as a separate, atomic commit.
73+
74+
**CRITICAL**: Process one feedback point at a time. Complete the full cycle for each before moving to the next.
75+
76+
For EACH valid feedback point:
77+
78+
### Step 1: Apply the Fix
79+
80+
- Make the minimal change that addresses the feedback
81+
- Do not bundle unrelated changes
82+
- Follow project conventions from CLAUDE.md:
83+
- `from __future__ import annotations` at top of files
84+
- `import typing as t` namespace style
85+
- NumPy docstring style
86+
- Functional tests only (no test classes)
87+
88+
### Step 2: Run Quality Gates
89+
90+
Run ALL quality gates and ensure they pass:
91+
92+
```bash
93+
uv run ruff check . --fix --show-fixes
94+
uv run ruff format .
95+
uv run mypy
96+
uv run py.test --reruns 0 -vvv
97+
```
98+
99+
- If any gate fails, fix the issue before proceeding
100+
- If a test fails due to the change, either:
101+
- Adjust the fix to be correct, OR
102+
- Update the test if the reviewer's feedback changes expected behavior
103+
- ALL FOUR gates must pass before committing
104+
105+
### Step 3: Commit
106+
107+
Stage only the files changed for this specific feedback point:
108+
109+
```bash
110+
git add <specific-files>
111+
```
112+
113+
Use the project commit message format with HEREDOC:
114+
115+
```bash
116+
git commit -m "$(cat <<'EOF'
117+
Component(fix[subcomponent]) Brief description of the review feedback fix
118+
119+
why: Address code review feedback — <what the reviewer pointed out>
120+
what:
121+
- <specific change 1>
122+
- <specific change 2>
123+
EOF
124+
)"
125+
```
126+
127+
**Commit type guidance**:
128+
- `fix` for bug fixes or correctness issues
129+
- `refactor` for code clarity or structure improvements
130+
- `style` for naming or formatting changes
131+
- `docs` for documentation or docstring fixes
132+
- `test` for test improvements
133+
134+
### Step 4: Verify Clean State
135+
136+
After committing, confirm:
137+
```bash
138+
git status
139+
git diff
140+
```
141+
142+
No uncommitted changes should remain before moving to the next feedback point.
143+
144+
---
145+
146+
## Phase 4: Summary
147+
148+
After processing all valid points, present a summary:
149+
150+
1. **Applied fixes**: List each committed fix with its commit hash
151+
2. **Skipped points**: List each invalid/out-of-scope point with the reason
152+
3. **Final verification**: Run the full quality gate one last time:
153+
```bash
154+
uv run ruff check . --fix --show-fixes
155+
uv run ruff format .
156+
uv run mypy
157+
uv run py.test --reruns 0 -vvv
158+
```
159+
4. Report the final pass/fail status
160+
161+
---
162+
163+
## Recovery: Quality Gate Failure
164+
165+
If quality gates fail after applying a fix:
166+
167+
1. **Identify** which gate failed and why
168+
2. **Fix** the issue (adjust the change, not bypass the gate)
169+
3. **Re-run** all four gates
170+
4. If the fix cannot be made to pass all gates after 2 attempts:
171+
- Revert the change: `git checkout -- <files>`
172+
- Mark the feedback point as "valid but could not apply cleanly"
173+
- Move to the next point
174+
- Report the issue in the Phase 4 summary
175+
176+
---
177+
178+
## Rules
179+
180+
- Never skip quality gates
181+
- Never bundle multiple feedback points into one commit
182+
- Never modify code that isn't related to the feedback being addressed
183+
- Always wait for user confirmation after Phase 2 validation
184+
- Always use project commit message conventions
185+
- If a feedback point requires changes in multiple files, that is still ONE commit (one logical change)

0 commit comments

Comments
 (0)