Validate section completion in backlog, then either release or identify gaps.
All arguments are optional with smart defaults.
Argument Parsing
Parse $ARGUMENTS into:
| Variable | Source | Required | Default |
|---|---|---|---|
BACKLOG_PATH |
1st arg | No | Auto-detect: BACKLOG.md, specs/backlog.md, or skip |
SECTION |
2nd arg | No | Latest incomplete section, or skip if no backlog |
BASE_BRANCH |
3rd arg | No | origin/main (fetches first) |
VERSION |
4th arg (if not quoted) | No | Auto-bump from VERSION file or latest git tag |
VALIDATION_PROMPT |
Last quoted string "..." |
No | Auto-derive from checklist |
SKIP_TAG |
--skip-tag flag |
No | false (tags created by default) |
AUTO_MODE |
--auto flag |
No | false (confirmation gates enabled) |
WITH_SQUASHED_COMMITS |
--with-squashed-commits flag |
No | false (squashed commits list excluded by default) |
Parsing Rules:
- If an argument starts and ends with
", treat asVALIDATION_PROMPT - Version is optional; if 4th arg is quoted, there's no version
- If
--skip-tagflag is present anywhere in arguments, setSKIP_TAG=true - If
--with-squashed-commitsflag is present anywhere in arguments, setWITH_SQUASHED_COMMITS=true - If
--autoflag is present anywhere in arguments, setAUTO_MODE=true(skips confirmation gates) - Empty
$ARGUMENTStriggers full auto-detect mode
Phase 0: Smart Defaults Resolution
When NO arguments provided:
0.1 Fetch Remote
git fetch origin main || git fetch origin master
0.2 Determine BASE_BRANCH
# Try origin/main first, fallback to origin/master
git show-ref --verify --quiet refs/remotes/origin/main && echo "origin/main" || echo "origin/master"
0.3 Auto-Detect BACKLOG_PATH
Search in order:
BACKLOG.md(root)specs/backlog.mddocs/backlog.mdbacklog.md
If none found → SKIP backlog validation (proceed without backlog)
0.4 Auto-Detect SECTION (if backlog exists)
Find first incomplete section (has - [ ] items). If all complete → use most recent section.
0.5 Auto-Detect VERSION
If SKIP_TAG=false:
Priority order:
VERSIONfile → parse, increment patch:X.Y.Z→X.Y.(Z+1), prefix withv- Latest git tag matching
v*.*.*→ increment patch - Default:
v0.1.0
If SKIP_TAG=true:
Set VERSION="" (no version/tag will be created)
0.6 CHANGELOG Consistency Check (Critical for No-Args Mode)
# Check if CHANGELOG.md exists and has [Unreleased] content
if [ -f CHANGELOG.md ]; then
grep -A 100 "\\[Unreleased\\]" CHANGELOG.md | grep -E "^- |^### " | head -20
fi
If CHANGELOG [Unreleased] is EMPTY but commits exist since BASE_BRANCH:
CHANGELOG SYNC REQUIRED
Commits since {BASE_BRANCH}:
{commit list}
But CHANGELOG.md [Unreleased] section is empty.
Please update CHANGELOG.md with these changes before proceeding.
-> STOP and wait for user to update changelog.
If CHANGELOG [Unreleased] has content -> proceed.
0.7 Project-Specific Enforcement
Check if AGENTS.md contains project-specific rules:
test -f AGENTS.md && echo "exists" || echo "none"
If exists:
- Read
AGENTS.mdcontent - Parse and store rules for validation in Phase 4.5
- Common rules to detect:
no emojis/DO NOT use emojis-> flag emoji restrictionsproject-agnostic/anonymous-> flag content anonymity requirementsrelative symlinks-> flag symlink requirements
Store parsed rules as PROJECT_RULES for later validation.
Phase 1: Pre-Flight Checks
- Git state: Must be clean (no uncommitted changes)
- Commits exist: Must have commits since
BASE_BRANCH - CHANGELOG exists:
CHANGELOG.mdmust exist with[Unreleased]section - Backlog exists (if path provided): File at
BACKLOG_PATHmust exist - Section exists (if backlog provided): Section
SECTIONmust be found in backlog
If any fail → STOP with specific error message.
Phase 2: Extract Checklist (if backlog provided)
- Read backlog file at
BACKLOG_PATH - Find section matching
SECTION(patterns:#### 1.2,### Phase 1.2,## 1.2) - Extract ALL checklist items until next section:
- [ ]= unchecked- [x]= checked
- Report:
Found X items (Y checked, Z unchecked)
If no backlog: Skip to Phase 4 with auto-approval path.
Phase 3: Validate Implementation (if backlog provided)
If VALIDATION_PROMPT provided:
Use it as explicit criteria. Spawn validation agent with prompt:
VALIDATE: {VALIDATION_PROMPT}
For each criterion, search codebase and report:
- Status: FOUND | MISSING
- Evidence: file:line references
- Notes: any issues
If NO VALIDATION_PROMPT:
For each UNCHECKED item (- [ ]):
Parse item to identify expected:
- Files/components (look for nouns)
- Interfaces/functions (look for code terms)
- Tests (if mentioned)
Search codebase:
- Grep for key terms
- Glob for expected file patterns
- Check test directories
Classify:
- IMPLEMENTED: Found despite unchecked
- MISSING: No evidence found
Phase 4: Decision Gate
Path A: No Backlog (Changelog-Only Validation)
Display:
✅ CHANGELOG Validated
Changes since {BASE_BRANCH}:
- {N} commits
- Key changes: {summary}
CHANGELOG [Unreleased] entries:
{entries}
Release Configuration:
- Base: {BASE_BRANCH}
- Version: {VERSION or "(no tag)" if SKIP_TAG=true}
- Commits to squash: N
Proceed with squash + tag? (yes/no)
If AUTO_MODE=true: Skip confirmation, proceed directly to squash/tag.
→ On "yes" (or auto): proceed to squash/tag.
Path B: Backlog - ALL COMPLETE (all items implemented or checked)
Display:
✅ Section {SECTION} COMPLETE
Validated Items:
- [x] Item 1 - evidence: file:line
- [x] Item 2 - evidence: file:line
...
Release Configuration:
- Base: {BASE_BRANCH}
- Version: {VERSION or "(no tag)" if SKIP_TAG=true}
- Commits to squash: N
- CHANGELOG: [Unreleased] → [{VERSION or section name if SKIP_TAG=true}]
Proceed with squash? (yes/no)
If AUTO_MODE=true: Skip confirmation, proceed directly.
On "yes" (or auto):
- Update backlog: mark all items
[x], add checkmark to section header - Update CHANGELOG.md:
- Move all entries from
[Unreleased]to new[{VERSION}] - {YYYY-MM-DD}section - Keep empty
[Unreleased]section at top - If no VERSION provided or
SKIP_TAG=true, use section name as header
- Move all entries from
- Commit:
docs: mark {SECTION} as completed - Create backup:
{branch}-backup/{YYYY}/{MM}/{DD}/001 - Soft reset to
BASE_BRANCH - Generate Conventional Commit message (see Phase 4B)
- Create squashed commit with generated message
- If
VERSIONandSKIP_TAG=false: create annotated tag
→ Proceed to Phase 5: Push Confirmation
Phase 4B: Conventional Commit Message Generation
Generate a standardized commit message following Conventional Commits extended format.
4B.1 Analyze Changes
# Get full diff for analysis
git diff {BASE_BRANCH}..HEAD --stat
git diff {BASE_BRANCH}..HEAD --name-status
4B.2 Determine Commit Type
Parse changed files and categorize by primary change type:
| Type | When to Use |
|---|---|
feat |
New feature or capability added |
fix |
Bug fix |
docs |
Documentation only changes |
chore |
Maintenance, deps, configs (no production code) |
refactor |
Code restructuring without behavior change |
test |
Adding or modifying tests |
style |
Formatting, whitespace, linting |
perf |
Performance improvements |
build |
Build system or external dependencies |
ci |
CI/CD configuration changes |
Selection Priority:
- If ANY
featchanges exist -> type =feat - Else if ANY
fixchanges exist -> type =fix - Else use dominant change type
4B.3 Determine Scope
Analyze changed file paths to identify scope:
# Get unique top-level directories/components
git diff --name-only {BASE_BRANCH}..HEAD | cut -d'/' -f1-2 | sort -u
Scope Rules:
- Single component modified -> use component name (e.g.,
commands,skills) - Multiple related components -> use parent (e.g.,
core) - Unrelated changes -> omit scope or use
release - Version/release changes -> scope =
releaseor version number
4B.4 Generate Commit Title
Format: <type>(<scope>): <description>
Description Rules:
- Imperative mood ("add" not "added")
- Lowercase first letter
- No period at end
- Max 72 characters total
- Summarize the main purpose
Examples:
feat(commands): add milestone validation workflowfix(parser): handle empty input gracefullydocs(readme): update installation instructionschore(deps): bump typescript to v5.3
4B.5 Generate Commit Body
Structure the body with these sections (include only non-empty):
## Added
- New feature 1
- New feature 2
## Changed
- Modified behavior 1
- Updated component 2
## Fixed
- Bug fix 1
- Issue resolution 2
## Removed
- Deprecated item 1
Content Generation:
- Parse
git diff {BASE_BRANCH}..HEADfile by file - Categorize each file's changes:
- New files -> Added
- Modified files -> Changed (or Fixed if bug-related)
- Deleted files -> Removed
- Summarize meaningful changes (not every line)
- Reference file paths where helpful
4B.6 Include Original Commits (Optional)
Only if WITH_SQUASHED_COMMITS=true, append squashed commit references:
# Get original commit messages
git log --oneline {BASE_BRANCH}..HEAD
Format as:
Squashed commits:
- {sha7} {message}
- {sha7} {message}
- {sha7} {message}
If WITH_SQUASHED_COMMITS=false (default): Omit this section entirely from the commit message.
4B.7 Complete Message Template
<type>(<scope>): <description>
## Added
- {additions}
## Changed
- {changes}
## Fixed
- {fixes}
## Removed
- {removals}
<!-- Only if WITH_SQUASHED_COMMITS=true -->
Squashed commits:
- {sha} {message}
- {sha} {message}
<!-- End conditional -->
4B.8 Commit Message Example
feat(milestone): add validation workflow with smart defaults
## Added
- Smart defaults resolution for all arguments
- CHANGELOG consistency check
- AGENTS.md validation
- Push confirmation gate
## Changed
- Refactored argument parsing to support quoted strings
- Updated backup branch naming format
## Fixed
- Backlog detection now searches multiple locations
Path C: Backlog - INCOMPLETE (missing items)
Display:
❌ Section {SECTION} INCOMPLETE
Missing:
1. {item} - {what's missing}
2. {item} - {what's missing}
Implemented but unchecked:
1. {item} - {evidence}
Options:
(A) Create /spec for missing items
(B) Abort - review manually
On "A": Output spec creation commands:
Missing items require specs:
/spec CREATE {item-1-title}
/spec CREATE {item-2-title}
On "B": STOP cleanly.
Phase 4.5: AGENTS.md Validation (Pre-Push Gate)
If PROJECT_RULES were parsed in Phase 0.7, validate all changes against AGENTS.md rules:
4.5.1 Get Changed Files
git diff --name-only {BASE_BRANCH}..HEAD
4.5.2 Validate Against PROJECT_RULES
For each rule detected, run validation:
Emoji Check (if emoji restriction detected):
# Check all changed .md files for emoji characters
git diff {BASE_BRANCH}..HEAD -- '*.md' | grep -P '[\x{1F300}-\x{1F9FF}]' || echo "clean"
Project-Agnostic/Anonymous Check (if anonymity rule detected):
# Check for personal identifiers, hardcoded usernames, local paths
git diff {BASE_BRANCH}..HEAD | grep -iE '(/Users/[a-z]+|/home/[a-z]+|@[a-z]+\.com)' || echo "clean"
Symlink Check (if symlink rule detected):
# Check for absolute symlinks in changed files
for f in $(git diff --name-only {BASE_BRANCH}..HEAD); do
if [ -L "$f" ]; then
target=$(readlink "$f")
[[ "$target" = /* ]] && echo "ABSOLUTE: $f -> $target"
fi
done
4.5.3 Violation Handling
If ANY violations found:
AGENTS.md VIOLATIONS DETECTED
The following changes violate project-specific rules:
Rule: {rule description}
Violations:
- {file}: {violation details}
- {file}: {violation details}
Rule: {rule description}
Violations:
- {file}: {violation details}
Fix these violations before proceeding with release.
-> STOP - Do not proceed to Phase 5.
If NO violations -> proceed to Phase 5.
Phase 5: Push Confirmation
After successful squash/tag, display:
## Release Prepared Locally
Commit: {sha} {message}
Branch: {branch}
Tag: {VERSION} (if created, otherwise "(none)")
Backup: {backup-branch}
Push to origin?
Commands to execute:
git push --force-with-lease origin {branch}
git push origin {VERSION} # if tag created (SKIP_TAG=false)
Proceed with push? (yes/no)
If AUTO_MODE=true: Skip confirmation, proceed directly with push.
On "yes" (or auto):
- Execute:
git push --force-with-lease origin {branch} - If
VERSIONandSKIP_TAG=false: Execute:git push origin {VERSION} - Report success with remote URLs
On "no" (only when AUTO_MODE=false):
Display manual commands and exit:
Skipped push. Run manually when ready:
git push --force-with-lease origin {branch}
git push origin {VERSION} # if tag was created
Abort Conditions
| Condition | Action |
|---|---|
| Backlog not found (when path specified) | STOP: "File not found: {path}" |
| Section not found (when specified) | STOP: "Section {SECTION} not found. Available: [list]" |
| No commits to squash | STOP: "No commits between {BASE_BRANCH} and HEAD" |
| Git dirty | STOP: "Uncommitted changes. Commit or stash first." |
| CHANGELOG missing | STOP: "CHANGELOG.md not found or missing [Unreleased] section" |
| CHANGELOG empty + commits exist | STOP: "Update CHANGELOG [Unreleased] before proceeding" |
| AGENTS.md violations | STOP: List violations, require fixes before release |
| User declines | STOP cleanly, no changes |
| Push fails | STOP: show error, suggest manual resolution |
Usage Examples
# NO ARGUMENTS - full auto mode
# Auto-detects: backlog, section, base branch, version, validates changelog
/milestone
# With backlog and section (base branch auto-detected)
/milestone specs/backlog.md 1.2
# Full release with all args
/milestone specs/backlog.md 1.2 main v0.1.1-alpha
# With validation prompt
/milestone specs/backlog.md 1.2 main v0.1.1-alpha "NoteMeta extended, parser exists, 20 tests pass"
# Without custom prompt (auto-detect from checklist)
/milestone docs/roadmap.md 2.1 main v2.1.0
# Validate only, no version tag
/milestone CHANGELOG.md phase-3 develop
# Quoted prompt without version
/milestone specs/features.md 4.0 main "API endpoints implemented, auth working"
# Skip tag creation with --skip-tag flag
/milestone specs/backlog.md 1.2 main --skip-tag
# Skip tag with validation prompt
/milestone specs/backlog.md 1.2 main --skip-tag "All tests passing"
# Auto-detect mode without tags
/milestone --skip-tag
# Autonomous mode (skip all confirmation gates)
/milestone --auto
# Autonomous mode with skip-tag (used by orchestrators)
/milestone --skip-tag --auto
# Include squashed commits in message (opt-in)
/milestone --with-squashed-commits