feat: auto-detect semver bump in /release skill#496
Conversation
…in backlog These two items deliver the highest immediate impact on agent experience and graph accuracy without requiring Rust porting or TypeScript migration. They should be implemented before any Phase 4+ roadmap work. - #83: hook-optimized `codegraph brief` enriches passively-injected context - #71: basic type inference closes the biggest resolution gap for TS/Java
Impact: 14 functions changed, 0 affected
Add new Phase 4 covering the port of JS-only build phases to Rust: - 4.1-4.3: AST nodes, CFG, dataflow visitor ports (~587ms savings) - 4.4: Batch SQLite inserts (~143ms) - 4.5: Role classification & structure (~42ms) - 4.6: Complete complexity pre-computation - 4.7: Fix incremental rebuild data loss on native engine - 4.8: Incremental rebuild performance (target sub-100ms) Bump old Phases 4-10 to 5-11 with all cross-references updated. Benchmark evidence shows ~50% of native build time is spent in JS visitors that run identically on both engines.
Take main's corrected #57 section anchors; keep HEAD's v2.7.0 version reference. Impact: 10 functions changed, 11 affected
…ative-acceleration Impact: 25 functions changed, 46 affected
- Add COMMITS=0 guard in publish.yml to return clean version when HEAD is exactly at a tag (mirrors bench-version.js early return) - Change bench-version.js to use PATCH+1-dev.COMMITS format instead of PATCH+COMMITS-dev.SHA (mirrors publish.yml's new scheme) - Fix fallback in bench-version.js to use dev.1 matching publish.yml's no-tags COMMITS=1 default Impact: 1 functions changed, 0 affected
The release skill now scans commit history using conventional commit rules to determine major/minor/patch automatically. Explicit version argument still works as before.
Greptile SummaryThis PR adds optional auto-detection of the semver bump to the Key changes:
Notable concern: Confidence Score: 3/5
Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["/release invoked"] --> B{"\$ARGUMENTS provided?"}
B -- Yes --> C["VERSION = \$ARGUMENTS"]
B -- No --> D["Step 1a: git log, read package.json, find last tag"]
D --> E["Step 1b: scan commit messages"]
E --> F{Commit type?}
F -- "BREAKING CHANGE / feat! / fix!" --> G["bump = major → (M+1).0.0"]
F -- "feat: / feat(scope):" --> H["bump = minor → M.(N+1).0"]
F -- "fix: / chore: / etc." --> I["bump = patch → M.N.(P+1)"]
G & H & I --> C
C --> J["Step 2: npm version in package.json + lock"]
J --> K["Step 3–7: CHANGELOG, ROADMAP, BACKLOG, README"]
K --> L["Step 8: branch / commit / push / PR"]
subgraph "publish.yml compute-version (CI)"
M["git describe last tag"] --> N{COMMITS since tag}
N -- "= 0" --> O["VERSION = M.N.P-dev.0"]
N -- "> 0" --> P["VERSION = M.N.(P+1)-dev.COMMITS"]
end
subgraph "bench-version.js"
Q["git describe"] --> R{COMMITS?}
R -- "= 0" --> S["M.N.P-dev.0"]
R -- "> 0" --> T["M.N.(P+1)-dev.COMMITS"]
R -- "no tags" --> U["git rev-parse --short HEAD"]
U -- success --> V["M.N.(P+1)-dev.SHA"]
U -- fail --> W["M.N.(P+1)-dev"]
end
Last reviewed commit: 7185f9b |
scripts/bench-version.js
Outdated
| // Fallback: no git or no tags — match publish.yml's no-tags behavior (COMMITS=1) | ||
| const parts = pkgVersion.split('.'); | ||
| if (parts.length === 3) { | ||
| const [major, minor, patch] = parts; | ||
| try { | ||
| const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim(); | ||
| return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`; | ||
| } catch { | ||
| return `${major}.${minor}.${Number(patch) + 1}-dev`; | ||
| } | ||
| return `${major}.${minor}.${Number(patch) + 1}-dev.1`; | ||
| } |
There was a problem hiding this comment.
Hardcoded
dev.1 breaks benchmark deduplication uniqueness
The file comment explicitly states the version string is used to deduplicate benchmark reports. The previous fallback tried to fetch the short SHA to keep builds unique; if that failed it returned a bare -dev suffix. The new fallback always returns dev.1, so every run in a no-tags environment (CI machines that haven't fetched tags, fresh checkouts, etc.) will produce the same version string, causing later benchmark runs to silently overwrite the data of earlier ones.
| // Fallback: no git or no tags — match publish.yml's no-tags behavior (COMMITS=1) | |
| const parts = pkgVersion.split('.'); | |
| if (parts.length === 3) { | |
| const [major, minor, patch] = parts; | |
| try { | |
| const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim(); | |
| return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`; | |
| } catch { | |
| return `${major}.${minor}.${Number(patch) + 1}-dev`; | |
| } | |
| return `${major}.${minor}.${Number(patch) + 1}-dev.1`; | |
| } | |
| const parts = pkgVersion.split('.'); | |
| if (parts.length === 3) { | |
| const [major, minor, patch] = parts; | |
| // Try to get a unique SHA so repeated runs don't collide in benchmark reports | |
| try { | |
| const hash = execFileSync('git', ['rev-parse', '--short', 'HEAD'], { cwd, ...GIT_OPTS }).trim(); | |
| return `${major}.${minor}.${Number(patch) + 1}-dev.${hash}`; | |
| } catch { | |
| return `${major}.${minor}.${Number(patch) + 1}-dev`; | |
| } | |
| } |
There was a problem hiding this comment.
Fixed in fc7497d — restored SHA-based uniqueness in the no-tags fallback. The fallback now tries git rev-parse --short HEAD for a unique identifier, falling back to a bare -dev suffix only if git is completely unavailable.
| if [ "$COMMITS" -eq 0 ]; then | ||
| VERSION="${MAJOR}.${MINOR}.${PATCH}" | ||
| else | ||
| VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))-dev.${COMMITS}" | ||
| fi |
There was a problem hiding this comment.
COMMITS=0 emits a clean semver dev build, no -dev suffix
When COMMITS is 0 (HEAD is exactly at a release tag), the new branch produces VERSION="${MAJOR}.${MINOR}.${PATCH}" with NPM_TAG="dev". The old code always appended -dev.${SHORT_SHA}, so dev builds could never be confused with a stable release version.
While the preflight condition filters commits whose message starts with chore: release v, this protection is fragile — a manually-placed tag on any non-release commit (or a squash-merge with a custom message) can still set COMMITS=0 without triggering the skip. If the publish-dev job then fires, the pre-release on GitHub is tagged dev-v${MAJOR}.${MINOR}.${PATCH}, which visually looks identical to a stable v${MAJOR}.${MINOR}.${PATCH} release and could mislead users picking up tarballs.
| if [ "$COMMITS" -eq 0 ]; then | |
| VERSION="${MAJOR}.${MINOR}.${PATCH}" | |
| else | |
| VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))-dev.${COMMITS}" | |
| fi | |
| if [ "$COMMITS" -eq 0 ]; then | |
| VERSION="${MAJOR}.${MINOR}.${PATCH}-dev.0" | |
| else | |
| VERSION="${MAJOR}.${MINOR}.$((PATCH + 1))-dev.${COMMITS}" | |
| fi |
scripts/bench-version.js
Outdated
| // Dev build: MAJOR.MINOR.(PATCH+1)-dev.COMMITS | ||
| return `${major}.${minor}.${Number(patch) + 1}-dev.${commits}`; |
There was a problem hiding this comment.
Commit count is not unique across cherry-picks or rebases
Replacing the short SHA with the commit count removes per-commit uniqueness. Two different commits with the same count since the last tag (possible after a revert-and-redo, interactive rebase, or cherry-pick) will produce the same version string, causing the benchmark report to silently overwrite earlier data. The SHA guaranteed uniqueness; the count only guarantees monotonicity on a linear history.
This isn't critical for normal CI runs on main, but it is worth noting as a trade-off compared to the previous behavior — especially since the whole purpose of this identifier (as stated in the file's header comment) is to prevent overwriting benchmark data.
There was a problem hiding this comment.
Impact: 71 functions changed, 255 affected
…dev.0 suffix The no-tags fallback hardcoded dev.1, causing repeated benchmark runs to silently overwrite each other. Restore short SHA for uniqueness. Also add -dev.0 suffix when COMMITS=0 so dev builds at an exact tag are never confused with stable releases. Impact: 1 functions changed, 0 affected
When HEAD is exactly at a release tag, the dev build emitted a clean semver string identical to the stable release. Add -dev.0 suffix to prevent confusion between dev pre-releases and stable releases.
Summary
/releasenow works without a version argument — it scans commits since the last tag and applies conventional commit rules (breaking → major, feat → minor, else → patch)/release 3.2.0) still works as beforeVERSIONvariableTest plan
/releasewith no argument — verify it detects correct bump from commit history/release 3.2.0with explicit version — verify it uses the provided version