Instruction file imported from fxstein/ascii-guard (
.cursor/rules/ascii-guard-releases.mdc). Copyright stays with the author.
ascii-guard Release Process
CRITICAL: Two-Phase Process with Human Gate
The release process has TWO separate phases with a mandatory human review between them:
Phase 1: PREPARE (AI does this)
When user says "prepare release":
- Check CI/CD status
- Write AI summary
- Run
./release/release.sh --prepare - STOP and wait for human
Phase 2: EXECUTE (AI does this ONLY when told)
When user says "execute release":
- Run
./release/release.sh --execute - Report success
⚠️ NEVER skip the human review gate between prepare and execute. ⚠️ NEVER automatically execute after prepare - ALWAYS wait for explicit "execute" command.
When the user says "Release ascii-guard" or similar:
CRITICAL: Python Environment Management
THIS PROJECT USES uv FOR PACKAGE MANAGEMENT:
Rules for AI Agents:
-
ALWAYS use
uvfor package operations- ❌ NEVER run:
pip install <package> - ✅ PREFER:
uv sync --frozen --extra devfor installing project dependencies (enforces lockfile) - ✅ ALTERNATIVE:
uv pip install <package>for installing individual tools/packages - ❌ NEVER run:
python -m pytest(uses system Python) - ✅ ALWAYS run:
uv run pytest(uses project venv automatically) - ❌ NEVER run:
python3 -m venv .venv - ✅ ALWAYS run:
uv venvto create virtual environment
Dependency Installation Priority:
- For project dependencies (from pyproject.toml): Use
uv sync --frozen --extra dev(strict lockfile enforcement) - For CI/CD workflows: ALWAYS use
uv sync --frozen --extra dev(neveruv pip install -e ".[dev]") - For installing tools (bandit, safety, etc.): Use
uv pip install <tool>(acceptable for one-off tools)
- ❌ NEVER run:
-
Check uv is available before operations
if ! command -v uv &> /dev/null; then echo "ERROR: uv not found!" echo "Install: curl -LsSf https://astral.sh/uv/install.sh | sh" exit 1 fi -
Check venv exists before operations
if [[ ! -d .venv ]]; then echo "ERROR: Virtual environment not found!" echo "Run: ./setup.sh" exit 1 fi -
Pre-commit hooks work with uv venv
.pre-commit-config.yamluses.venv/bin/pythonpaths- No manual venv activation needed for commits
- If pre-commit fails with import errors, venv is broken - rebuild with
./setup.sh
-
If you need to install a package for testing:
# For project dependencies (from pyproject.toml/uv.lock): uv sync --frozen --extra dev # Preferred - enforces lockfile # For one-off tools or packages not in lockfile: uv pip install <package> # Acceptable for tools like bandit, safety # NEVER: pip install <package> # Wrong! Bypasses uv -
Use
uv runfor all commands- ✅
uv run pytest- runs pytest in project venv - ✅
uv run ruff check .- runs ruff in project venv - ✅
uv run mypy src/- runs mypy in project venv - ✅
uv run python -m build- runs build in project venv - No need to activate venv manually when using
uv run
- ✅
Why this matters:
- uv provides faster, more reliable package management
- uv run automatically uses the project's venv
- Eliminates manual venv activation steps
- Ensures consistent environment across all operations
Step 0: MANDATORY CI/CD Check (ALWAYS DO THIS FIRST)
BEFORE STARTING ANY RELEASE - CHECK CI/CD STATUS:
./scripts/wait-for-ci.sh
This script will:
- Check all active workflows
- Wait for completion (up to 10 minutes)
- Report success or failure with clean output
- Exit with appropriate status code
If the script fails (exit code 1):
- STOP IMMEDIATELY - Do NOT start release preparation
- Politely inform user: "CI/CD is currently failing. Cannot proceed with release until all checks pass."
- Ask user to check:
gh run list --limit 5 - Ask user to fix CI/CD errors first
- Wait for user confirmation that CI/CD is fixed before proceeding
Only proceed if:
./scripts/wait-for-ci.shexits with code 0 (success)
During release preparation (after any commits):
- Run
./scripts/wait-for-ci.shto wait for workflows - If it fails: STOP and report
- Never proceed while CI/CD is failing
NEVER:
- Bypass CI/CD failures with manual workarounds
- Proceed with release if CI/CD is failing
- Use
--no-verifyon release-related commits - Ignore or work around GitHub Actions errors
Why this matters:
- CI/CD failures indicate code quality issues
- Releasing broken code to PyPI is irreversible
- Users depend on package quality
- Automated tests are the last line of defense
CRITICAL: Error Handling During Releases
ZERO TOLERANCE FOR ERRORS DURING RELEASE PROCESS
Rule: STOP on ANY Error
Once a release process has started (from Step 1 onwards), EVERY ERROR MUST STOP THE PROCESS IMMEDIATELY.
When an error occurs:
- STOP - Do NOT continue with workarounds
- REPORT - Clearly communicate:
- What command failed
- What the error message says
- What step of the release process you were in
- DISCUSS - Ask user: "An error occurred during release. How should we proceed?"
- WAIT - Do NOT make assumptions or try fixes without approval
What Counts as an Error
Examples of errors that MUST stop the process:
pyenv: version 'X.Y' is not installedNo module named 'build'pre-commit hook failedgh: command not foundgit pushfailed- Package build failed
- Any Python exception or traceback
- Any non-zero exit code from release commands
- CI/CD workflow failures after commits
What NOT to Do
NEVER:
- Work around errors with manual commands
- Skip steps to "fix it later"
- Use alternative methods without discussion
- Modify files manually to bypass issues
- Continue hoping "it will work out"
- Create manual GitHub releases
- Upload to PyPI manually
- Force push without approval
What TO Do
ALWAYS:
- Report the error immediately and clearly
- Stop all release activities
- Explain what the error means
- Propose systematic fixes (not workarounds)
- Wait for user approval before implementing fixes
- Document the error and fix in task notes
- Verify fix works before retrying release
Example Error Response
Bad Response:
Error: pyenv version not found. Let me work around this...
[proceeds to manually build package]
Good Response:
❌ ERROR: Release process stopped
Command failed: python3 -m build
Error: pyenv: version '3.12' is not installed
This error occurred during Step 4 (Execute Release).
Root cause: System doesn't have Python 3.12 installed.
We need to fix this before continuing the release. Options:
A) Install Python 3.12: pyenv install 3.12
B) Cancel release and create task to fix environment
How would you like to proceed?
Recovery Process
If release is stopped due to errors:
- Document: Create task for fixing the error properly
- Rollback: If needed, rollback any partial release state
- Fix: Address root cause systematically
- Test: Verify fix with dry-run
- Retry: Start fresh release attempt
Remember: A delayed release is better than a broken release.
Step 1: Generate AI Release Summary
- Review commit messages to understand changes since last release
- Write 2-3 paragraph summary highlighting key improvements
- Focus on user-facing benefits and why this release matters
- Keep it human-readable (no commit lists, no technical jargon)
- CRITICAL: DO NOT include any headers (no
#markdown headers) - The release script will automatically generate the title header
- Save to
release/AI_RELEASE_SUMMARY.md
Example AI Summary (NO HEADERS)
This release introduces comprehensive ASCII art box detection and auto-fixing
capabilities for documentation files. The linter now accurately identifies
misaligned corners, borders, and box-drawing characters across multiple
Unicode styles, ensuring consistent visual formatting.
Key improvements include a powerful CLI interface with both lint and fix
modes, zero-dependency architecture using only Python standard library, and
extensive test coverage. The tool integrates seamlessly with CI/CD pipelines
via pre-commit hooks.
Users can now maintain clean, professional documentation with automatically
aligned ASCII art boxes, eliminating manual character-by-character adjustments
and improving overall documentation quality.
Note: The script will prepend # ascii-guard vX.Y.Z as the H1 header automatically.
Step 2: Prepare Release
./release/release.sh --prepare
What it does:
- Analyzes commits since last release
- Determines version bump (major/minor/patch) automatically
- Reads AI summary from
release/AI_RELEASE_SUMMARY.md - Generates complete release notes in
release/RELEASE_NOTES.md(summary + categorized commits) - Displays preview and saves prepare state
Output:
release/RELEASE_NOTES.md(complete release notes - THIS IS WHAT GETS REVIEWED)release/.prepare_state(metadata for execute step)
⚠️ CRITICAL: STOP HERE AND WAIT FOR HUMAN
After this step:
- STOP IMMEDIATELY - Do NOT proceed to execute
- Show the user what version was detected
- Show the user the release notes location
- WAIT for explicit human instruction to:
- Execute the release (
--execute) - Change the version (
--set-version X.Y.Z) - Cancel/edit manually
- Execute the release (
NEVER automatically execute after prepare.
Step 3: Review & Edit Release Notes (IMPORTANT)
CRITICAL: Between prepare and execute, the human can/should:
- Edit the generated release notes
- Override the version number if desired
Where Are the Notes?
TWO files are used in the release process:
-
release/AI_RELEASE_SUMMARY.md- AI-written summary (Step 1)- Written by AI in Step 1
- Just the 2-3 paragraph summary, no commits
- Can be edited before running prepare
- Input to the prepare process
-
release/RELEASE_NOTES.md- Complete release notes (Step 2)- Generated by prepare script
- Contains: AI summary + categorized commits
- THIS IS THE FILE TO REVIEW/EDIT before execute
How to Edit
Option 1: Edit AI Summary Before Prepare
# Edit the AI-written summary before running prepare
vim release/AI_RELEASE_SUMMARY.md
./release/release.sh --prepare
Option 2: Edit Release Notes After Prepare (RECOMMENDED)
# Edit the complete release notes after prepare
vim release/RELEASE_NOTES.md
./release/release.sh --execute
Option 3: Override Version Number
If the human wants a specific version (e.g., "This should be 1.0.0"):
./release/release.sh --set-version 1.0.0
# Then execute
./release/release.sh --execute
Natural Language Triggers:
- "change version to 1.0.0"
- "make this version 2.0.0"
- "set version to 1.5.0"
- "this should be version 1.0.0"
What it does:
- Validates format (X.Y.Z)
- Validates new version > current version
- Updates prepare state
- Updates release/RELEASE_NOTES.md header
- Logs the override
Common Edits
- Remove commits that shouldn't be in release notes
- Regroup commits into different categories
- Rewrite commit messages for clarity
- Add context or explanations
- Improve AI summary wording
Step 4: Execute Release
./release/release.sh --execute
What it does:
- Updates version in
pyproject.tomlandsrc/ascii_guard/__init__.py - Builds Python package (
uv run python -m build) - Commits version changes and release notes
- Creates git tag
vX.Y.Z - Pushes tag and main branch to GitHub
- GitHub Actions takes over:
- Runs full test suite
- Publishes to PyPI (trusted publishing)
- Creates GitHub release with wheel/sdist artifacts
- Cleans up working files
- Commits release log
Result: Release is initiated, GitHub Actions completes publishing
Complete Workflow Example
# 1. AI writes summary
# (AI creates release/AI_RELEASE_SUMMARY.md with 2-3 paragraphs)
# 2. AI commits the summary immediately (NO VERSION NUMBER YET!)
git add release/AI_RELEASE_SUMMARY.md
git commit -m "chore: add AI release summary"
git push
# 3. AI runs prepare (within 60 seconds of commit)
# This determines the version number automatically
./release/release.sh --prepare
# (Reads AI summary, adds commits, writes release/RELEASE_NOTES.md)
# 4. Human reviews and optionally edits release notes
vim release/RELEASE_NOTES.md # THIS is the file to edit!
# Optional: Override version
./release/release.sh --set-version 1.0.0
# 5. Human instructs AI to execute
./release/release.sh --execute
# (Updates version, builds, tags, pushes - GitHub Actions publishes)
Key GitHub Actions Integration
CRITICAL DIFFERENCE from ocroot:
- ascii-guard does NOT publish to PyPI directly from release.sh
- Instead, pushing the tag triggers GitHub Actions workflow (
.github/workflows/release.yml) - GitHub Actions uses trusted publishing (no PyPI tokens needed)
- This ensures:
- Tests run before publishing
- Secure publishing without local credentials
- Consistent CI/CD process
Workflow after execute:
release.sh --executepushes tagvX.Y.Z- GitHub Actions
release.ymlis triggered by tag push - GitHub Actions builds package
- GitHub Actions runs tests
- GitHub Actions publishes to PyPI (trusted publishing via OIDC)
- GitHub Actions creates GitHub release with wheel and sdist
Monitor release:
- GitHub Actions: https://github.com/fxstein/ascii-guard/actions
- PyPI: https://pypi.org/project/ascii-guard/
- GitHub Releases: https://github.com/fxstein/ascii-guard/releases
Safeguards
❌ NEVER DO THIS:
- Do NOT modify
release/release.shlogic to filter commits - Do NOT add skip conditions to the release script
- Do NOT change the release process code
- Do NOT attempt to publish to PyPI manually (GitHub Actions does this)
- Do NOT push tags manually (use the script)
✅ ALWAYS DO THIS:
- ALWAYS create AI summary in
release/AI_RELEASE_SUMMARY.mdfirst - ALWAYS commit the AI summary immediately after creating it
- ALWAYS run prepare within 60 seconds of committing the AI summary
- ALWAYS run prepare before execute
- ALWAYS wait for human review before execute
- ALWAYS let the human edit
release/RELEASE_NOTES.mdif they want - ALWAYS verify GitHub CLI is authenticated (
gh auth status) - ALWAYS check build dependencies are installed (
uv run python -m buildworks, or runuv sync --extra devto install all dependencies)
Editing release notes is MANUAL TEXT EDITING, not code changes.
Error Handling
Common Issues
Error: "Release not prepared"
- Solution: Run
./release/release.sh --preparefirst
Error: "Uncommitted changes detected"
- Solution: Commit or stash all changes before releasing
- Exception: Release working files are automatically excluded
Error: "Invalid version format"
- Solution: Use X.Y.Z format (e.g., 1.0.0, 0.2.5)
Error: "New version must be greater than current"
- Solution: Ensure new version > current version
Error: "Package build failed"
- Solution: Ensure dependencies are installed:
uv sync --extra dev(preferred) oruv pip install build - Or run:
./setup.shto install all dev dependencies - Check pyproject.toml is valid
Error: "gh not authenticated"
- Solution: Run
gh auth login
Version Numbering
ascii-guard uses Semantic Versioning:
Automatic Detection
The script analyzes commit messages:
# MAJOR bump triggers (X.0.0)
feat!: ... # Breaking feature
fix!: ... # Breaking fix
BREAKING CHANGE: # Explicit breaking change
# MINOR bump triggers (0.X.0)
feat: ... # New feature
feature: ... # New feature (alt)
# PATCH bump triggers (0.0.X)
fix: ... # Bug fix
chore: ... # Maintenance
docs: ... # Documentation
refactor: ... # Code refactoring
Manual Override
If automatic detection doesn't match your intent:
./release/release.sh --set-version 1.0.0
Files and Tracking
What Gets Committed
| File | When | Why |
|---|---|---|
release/AI_RELEASE_SUMMARY.md |
During execute | Audit trail: what AI wrote |
release/RELEASE_NOTES.md |
During execute | Audit trail: what was reviewed |
release/RELEASE_SUMMARY.md |
During execute | Historical record |
release/RELEASE_LOG.log |
After execute | Operational audit log |
pyproject.toml |
During execute | Version updated |
src/ascii_guard/__init__.py |
During execute | Version updated |
What Gets Deleted
| File | When | Why |
|---|---|---|
release/AI_RELEASE_SUMMARY.md |
After execute | Working file, already committed |
release/RELEASE_NOTES.md |
After execute | Working file, already committed |
release/.prepare_state |
After execute | Temporary metadata |
dist/ |
After execute | Build artifacts |
build/ |
After execute | Build cache |
What's Gitignored
dist/build/*.egg-info/release/.prepare_state
AI Agent Workflow
When User Says "prepare release" or "prepare ascii-guard release"
-
Check CI/CD Status FIRST:
./scripts/wait-for-ci.sh- If this fails, STOP and tell user to fix CI/CD first
- Only proceed if exit code is 0
-
Generate AI Summary:
I'll help you prepare the release. First, let me create a release summary. [Write 2-3 paragraph summary about what changed and why it matters] [NO HEADERS - just plain paragraphs] Saved to: release/AI_RELEASE_SUMMARY.md -
Commit AI Summary Immediately:
git add release/AI_RELEASE_SUMMARY.md git commit -m "chore: add AI release summary" git push- This keeps the AI summary in version history for easy diffs
- Must be done BEFORE running --prepare (within 60 seconds)
- DO NOT include version number - it's not determined yet!
-
Run Prepare:
./release/release.sh --prepare -
STOP AND SHOW PREVIEW:
✅ Release prepared successfully! - Current version: 1.2.0 - Proposed version: 1.2.1 (patch) - Release notes: release/RELEASE_NOTES.md What would you like to do? - Review the release notes in release/RELEASE_NOTES.md - Tell me "execute release" when ready - Tell me "set version to X.Y.Z" if you want to change it -
⚠️ STOP HERE - WAIT FOR USER TO SAY:
- "execute release" → Then run
./release/release.sh --execute - "set version to X.Y.Z" → Then run
./release/release.sh --set-version X.Y.Z - Any other instruction
- "execute release" → Then run
NEVER proceed to execute automatically - ALWAYS wait for explicit instruction.
When User Says "execute release" (ONLY after prepare)
-
Run Execute:
./release/release.sh --execute -
Report Success:
✅ Release v1.2.1 initiated successfully! GitHub Actions is now: - Building and testing the package - Publishing to PyPI (trusted publishing) - Creating GitHub release Monitor progress: - GitHub Actions: https://github.com/fxstein/ascii-guard/actions - PyPI (when complete): https://pypi.org/project/ascii-guard/ - GitHub Release: https://github.com/fxstein/ascii-guard/releases
Remember: Release notes editing = text editing, NOT code changes. GitHub Actions handles PyPI publishing automatically via trusted publishing.