Files
Claude-Code-Workflow/.codex/skills/ship/phases/04-changelog-commit.md
catlog22 67ff3fe339 feat: add investigate, security-audit, ship skills (Claude + Codex)
- Add 3 new Claude skills: investigate (Iron Law debugging), security-audit
  (OWASP Top 10 + STRIDE), ship (gated release pipeline)
- Port all 3 skills to Codex v4 format under .codex/skills/ using
  Deep Interaction pattern (spawn_agent + assign_task phase transitions)
- Update README/README_CN acknowledgments: credit gstack
  (https://github.com/garrytan/gstack) as inspiration source

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-30 10:31:13 +08:00

7.5 KiB

Phase 4: Changelog & Commit

COMPACT PROTECTION: This is a core execution phase. If context compression has occurred and this file is only a summary, MUST Read this file again before executing any Step. Do not execute from memory.

Generate changelog entry from git history, update CHANGELOG.md, create release commit, and push to remote.

Objective

  • Parse git log since last tag into grouped changelog entry
  • Update or create CHANGELOG.md
  • Create a release commit with version in the message
  • Push the branch to remote

Input

Source Required Description
Phase 3 output Yes new_version, version_file, bump_type
Git history Yes Commits since last tag
CHANGELOG.md No Updated in-place if it exists; created if not

Execution Steps

Step 1: Gather Commits Since Last Tag

Retrieve commits to include in the changelog.

Decision Table:

Condition Action
Last tag exists git log "$last_tag"..HEAD --pretty=format:"%h %s" --no-merges
No previous tag found Use last 50 commits: git log --pretty=format:"%h %s" --no-merges -50
last_tag=$(git describe --tags --abbrev=0 2>/dev/null || echo "")

if [ -n "$last_tag" ]; then
  echo "Generating changelog since tag: $last_tag"
  git log "$last_tag"..HEAD --pretty=format:"%h %s" --no-merges
else
  echo "No previous tag found — using last 50 commits"
  git log --pretty=format:"%h %s" --no-merges -50
fi

Step 2: Group Commits by Conventional Commit Type

Parse commit messages and group into changelog sections.

Conventional Commit Grouping Table:

Prefix Category Changelog Section
feat: / feat(*): Features Features
fix: / fix(*): Bug Fixes Bug Fixes
perf: Performance Performance
docs: Documentation Documentation
refactor: Refactoring Refactoring
chore: Maintenance Maintenance
test: Testing (omitted from changelog)
Other Miscellaneous Other Changes
# Example grouping logic (executed by the agent, not a literal script):
# 1. Read all commits since last tag
# 2. Parse prefix from each commit message
# 3. Group into categories
# 4. Format as markdown sections
# 5. Omit empty categories

Step 3: Format Changelog Entry

Generate a markdown changelog entry using ISO 8601 date format.

Decision Table:

Condition Action
Category has commits Include section with all entries
Category is empty Omit section entirely
test: commits present Omit from changelog output

Changelog entry format:

## [X.Y.Z] - YYYY-MM-DD

### Features
- feat: description (sha)
- feat(scope): description (sha)

### Bug Fixes
- fix: description (sha)

### Performance
- perf: description (sha)

### Other Changes
- chore: description (sha)

Rules:

  • Date format: YYYY-MM-DD (ISO 8601)
  • Each entry includes the short SHA for traceability
  • Empty categories are omitted
  • Entries are listed in chronological order within each category

Step 4: Update CHANGELOG.md

Write the new entry into CHANGELOG.md.

Decision Table:

Condition Action
CHANGELOG.md exists Insert new entry after the first heading line (# Changelog), before previous version entry
CHANGELOG.md does not exist Create new file with # Changelog heading followed by new entry
if [ -f "CHANGELOG.md" ]; then
  # Insert new entry after the first heading line (# Changelog)
  # The new entry goes between the main heading and the previous version entry
  # Use Write tool to insert the new section at the correct position
  echo "Updating existing CHANGELOG.md"
else
  # Create new CHANGELOG.md with header
  echo "Creating new CHANGELOG.md"
fi

CHANGELOG.md structure:

# Changelog

## [X.Y.Z] - YYYY-MM-DD
(new entry here)

## [X.Y.Z-1] - YYYY-MM-DD
(previous entry)

Step 5: Create Release Commit

Stage changed files and create conventionally-formatted release commit.

Decision Table:

Condition Action
Version file is package.json Stage package.json and package-lock.json (if present)
Version file is pyproject.toml Stage pyproject.toml
Version file is VERSION Stage VERSION
CHANGELOG.md was updated/created Stage CHANGELOG.md
git commit succeeds Proceed to push step
git commit fails BLOCKED — report error
# Stage version file and changelog
git add package.json package-lock.json pyproject.toml VERSION CHANGELOG.md 2>/dev/null

# Only stage files that actually exist and are modified
git add -u

# Create release commit
git commit -m "$(cat <<'EOF'
chore: bump version to X.Y.Z

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
EOF
)"

Commit message format: chore: bump version to <new_version>

  • Follows conventional commit format
  • Includes Co-Authored-By trailer

Step 6: Push to Remote

Push the branch to the remote origin.

Decision Table:

Condition Action
Remote tracking branch exists git push origin "<current_branch>"
No remote tracking branch git push -u origin "<current_branch>"
Push succeeds (exit 0) PASS — gate satisfied
Push rejected (non-fast-forward) BLOCKED — report error, suggest git pull --rebase
Permission denied BLOCKED — report error, advise check remote access
No remote configured BLOCKED — report error, suggest git remote add
current_branch=$(git branch --show-current)

# Check if remote tracking branch exists
if git rev-parse --verify "origin/$current_branch" &>/dev/null; then
  git push origin "$current_branch"
else
  git push -u origin "$current_branch"
fi

Output

Artifact Format Description
Commit and push record JSON changelog_entry, commit_sha, commit_message, pushed_to
CHANGELOG.md Markdown file Updated with new version entry
{
  "phase": "changelog-commit",
  "changelog_entry": "## [X.Y.Z] - YYYY-MM-DD ...",
  "commit_sha": "abc1234",
  "commit_message": "chore: bump version to X.Y.Z",
  "pushed_to": "origin/branch-name",
  "overall": "pass|fail"
}

Success Criteria

Criterion Validation Method
Commits gathered since last tag Commit list non-empty or warn if empty
Changelog entry formatted Markdown entry with correct sections
CHANGELOG.md updated or created File exists with new entry at top
Release commit created git log -1 --oneline shows commit
Branch pushed to remote Push command exits 0
overall = "pass" All steps completed without error

Error Handling

Scenario Resolution
No commits since last tag WARN — create minimal changelog entry, continue
CHANGELOG.md write error BLOCKED — report file system error
git commit fails (nothing staged) Verify version file and CHANGELOG.md were modified, re-stage
Push rejected (non-fast-forward) BLOCKED — suggest git pull --rebase, halt
Push permission denied BLOCKED — advise check SSH keys or access token
No remote configured BLOCKED — suggest git remote add origin <url>

Next Phase

-> Phase 5: PR Creation

If commit and push succeed (overall: "pass"), proceed to Phase 5. If push fails, report BLOCKED status with error details. Do not proceed.