Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions .claude/commands/deploynope-deploy.md
Original file line number Diff line number Diff line change
Expand Up @@ -320,21 +320,29 @@ the option to poll until staging is released rather than abandoning the workflow

If the user accepts:

1. **Set up a recurring poll** using `CronCreate` with a `*/1 * * * *` schedule that runs:
1. **Set up a background polling loop** using the Bash tool with `run_in_background: true`:

```shell
git fetch origin && git tag -l "staging/active"
while true; do
git fetch origin 2>/dev/null
if ! git tag -l "staging/active" | grep -q "staging/active"; then
UNRELEASED=$(git log origin/<production-branch>..origin/<staging-branch> --oneline 2>/dev/null)
if [ -z "$UNRELEASED" ]; then
echo "STAGING_CLEAR"
exit 0
fi
fi
echo "Still waiting — staging claimed."
sleep 60
done
```

- If `staging/active` still exists → report "Still waiting — staging claimed by `<name>`."
- If `staging/active` is gone **and** `git log origin/<production-branch>..origin/<staging-branch> --oneline`
shows no unreleased commits → report **"Staging is now clear!"** and proceed.
- The loop checks every 60 seconds whether `staging/active` has been removed.
- When the tag is gone **and** there are no unreleased commits on staging, it prints
`STAGING_CLEAR` and exits.
- You will be notified when the background task completes — do not poll or sleep-wait for it.

2. **Clean up immediately** — as soon as staging is detected as clear (or the user cancels
the wait), delete the cron job using `CronDelete` with the job ID returned by `CronCreate`.
Never leave a polling job running after it has served its purpose.

3. **Resume the deployment flow** — once staging is clear and the cron job is cleaned up,
2. **Resume the deployment flow** — once the background poll reports staging is clear,
continue from the "Claiming staging" step below without requiring the user to re-invoke
the deployment command.

Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ reset, release, etc.), the confirmation prompt **must** include a visible
Examples:
> "Ready to commit all of this? **`🤓 DeployNOPE 2.10.0 · Feature`**"
> "Shall I push this to origin? **`🤓 DeployNOPE 2.10.0 · Feature`**"
> "Ready to reset `master` to match `staging`? **`⚠️ DeployNOPE 2.10.0 · Production`**"
> "Ready to reset `main` to match `staging`? **`⚠️ DeployNOPE 2.10.0 · Production`**"

The absence of the tag on a deployment-related confirmation is itself a red flag that
the framework was not loaded.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ All commands are prefixed with `deploynope-` so they stay distinct in your slash
| `/deploynope-rollback` | Guides you through rolling back production to a previous release. Supports standard (through staging) and emergency (skip staging) modes. Handles frontend cache-busting automatically. |
| `/deploynope-stale-check` | Identifies stale branches, aging PRs, and pipeline bottlenecks. Helps keep the repo tidy and surfaces work that may have been forgotten. |
| `/deploynope-verify-rules` | A read-only self-check that confirms the deployment ruleset is loaded and Claude understands all 10 critical safety rules. Good for sanity-checking before a big release. |
| `/deploynope-console` | **DEPRECATED** (removed in 2.10.0). The sidecar console log feature has been replaced by inline chat tags (`<emoji> DeployNOPE <context> · <Stage>`). |

---

Expand Down Expand Up @@ -221,7 +222,7 @@ If you previously had these commands inside a project's `.claude/commands/`, you

## Tests

DeployNOPE includes a bash test suite covering all 9 hooks with 116 assertions. Tests create disposable git repos, simulate hook JSON input, and verify deny/ask/passthrough decisions.
DeployNOPE includes a bash test suite covering all 9 hooks with 124 assertions. Tests create disposable git repos, simulate hook JSON input, and verify deny/ask/passthrough decisions.

```bash
./tests/run-tests.sh # all tests
Expand Down
28 changes: 14 additions & 14 deletions docs/deploy-process-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -418,8 +418,8 @@

<header>
<h1><span class="nerd">&#129299;</span> Deploy<span class="nope">NOPE</span></h1>
<p>Deployment Process &mdash; the core 17-step workflow that all releases follow from staging to master to release</p>
<div class="tag">staging &rarr; master &rarr; release</div>
<p>Deployment Process &mdash; the core 17-step workflow that all releases follow from staging to production to release</p>
<div class="tag">staging &rarr; production &rarr; release</div>
</header>

<!-- Workflow Tabs -->
Expand Down Expand Up @@ -460,12 +460,12 @@ <h2>How It Works</h2>
<div class="how-card">
<div class="how-icon">&#128737;</div>
<h4>Branch Protection</h4>
<p>Direct pushes to <code>staging</code>, <code>master</code>, and <code>development</code> are blocked. All changes flow through release branches with controlled resets.</p>
<p>Direct pushes to <code>staging</code>, the production branch, and <code>development</code> are blocked. All changes flow through release branches with controlled resets.</p>
</div>
<div class="how-card">
<div class="how-icon">&#128721;</div>
<h4>Human Gates</h4>
<p>Three mandatory human checkpoints: release readiness, staging validation, and master reset. No gate can be skipped or automated away.</p>
<p>Three mandatory human checkpoints: release readiness, staging validation, and production reset. No gate can be skipped or automated away.</p>
</div>
<div class="how-card">
<div class="how-icon">&#128274;</div>
Expand All @@ -475,7 +475,7 @@ <h4>Staging Contention</h4>
<div class="how-card">
<div class="how-icon">&#128260;</div>
<h4>Cross-Repo Parity</h4>
<p>Before promoting to master, version numbers are checked across all related repositories to prevent mismatched deployments.</p>
<p>Before promoting to production, version numbers are checked across all related repositories to prevent mismatched deployments.</p>
</div>
<div class="how-card">
<div class="how-icon">&#128203;</div>
Expand All @@ -485,7 +485,7 @@ <h4>Release Manifests</h4>
<div class="how-card">
<div class="how-icon">&#9989;</div>
<h4>Auto Post-Deploy</h4>
<p>After master is updated, branches are synced, staging is cleared, Confluence notes are written, and health checks run automatically.</p>
<p>After the production branch is updated, branches are synced, staging is cleared, Confluence notes are written, and health checks run automatically.</p>
</div>
</div>
</div>
Expand All @@ -498,39 +498,39 @@ <h4>Auto Post-Deploy</h4>
// --- Step definitions ---
const ALL_STEPS = [
{ id: 'merge-features', icon: '&#128256;', title: 'Merge feature branches into release branch', desc: 'All approved feature branches are merged into the release branch via fast-forward merges.', gate: false, tags: ['feature'] },
{ id: 'sync-master', icon: '&#128260;', title: 'Sync release branch with master', desc: 'Rebase or merge master into the release branch to pick up any hotfix commits.', gate: false, tags: ['feature', 'chore'] },
{ id: 'sync-master', icon: '&#128260;', title: 'Sync release branch with production', desc: 'Rebase or merge the production branch into the release branch to pick up any hotfix commits.', gate: false, tags: ['feature', 'chore'] },
{ id: 'update-changelog', icon: '&#128221;', title: 'Update changelog', desc: 'CHANGELOG.md is updated with all changes included in this release.', gate: false, tags: ['feature', 'hotfix'] },
{ id: 'confirm-ready', icon: '&#128227;', title: 'Confirm release branch ready', desc: 'Human reviews the release branch and confirms it is ready to proceed.', gate: true, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'contention-check', icon: '&#128270;', title: 'Staging contention check', desc: 'Three-point check: unreleased commits, staging/active tag, stale branch guard.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'claim-staging', icon: '&#128274;', title: 'Claim staging (tag staging/active)', desc: 'Creates staging/active tag annotated with release name and deployer.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'reset-staging', icon: '&#128259;', title: 'Reset staging to release branch', desc: 'git reset --hard staging to the release branch HEAD, then force push.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'validate-staging', icon: '&#128065;', title: 'Validate on staging', desc: 'Human verifies the release works correctly on the staging environment.', gate: true, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'cross-repo-parity', icon: '&#128260;', title: 'Cross-repo version parity check', desc: 'Verify version numbers match across all related repositories before promotion.', gate: false, tags: ['feature', 'hotfix'] },
{ id: 'reset-master', icon: '&#128721;', title: 'Reset master to match staging', desc: 'Human confirms master reset. Master is force-reset to match staging exactly.', gate: true, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'codepipeline', icon: '&#9881;', title: 'Confirm CodePipeline healthy', desc: 'Wait for CI/CD pipeline to report all green after the master push.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'reset-master', icon: '&#128721;', title: 'Reset production to match staging', desc: 'Human confirms production reset. The production branch is force-reset to match staging exactly.', gate: true, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'codepipeline', icon: '&#9881;', title: 'Confirm CodePipeline healthy', desc: 'Wait for CI/CD pipeline to report all green after the production push.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'github-release', icon: '&#127873;', title: 'Create GitHub Release', desc: 'Tag and publish a GitHub Release with auto-generated release notes.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'write-manifest', icon: '&#128203;', title: 'Write release manifest', desc: 'Immediately write the release manifest to the repository after the GitHub Release.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'sync-branches', icon: '&#128257;', title: 'Sync staging + development with master', desc: 'Reset staging and development branches to match the new master.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'sync-branches', icon: '&#128257;', title: 'Sync staging + development with production', desc: 'Reset staging and development branches to match the new production branch.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'clear-staging', icon: '&#128275;', title: 'Clear staging (remove tag)', desc: 'Delete the staging/active tag to release the staging lock for other deployments.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
{ id: 'confluence', icon: '&#128220;', title: 'Write Confluence release notes', desc: 'Publish structured release notes to the team Confluence space.', gate: false, tags: ['feature', 'hotfix'] },
{ id: 'post-deploy', icon: '&#9989;', title: 'Post-deploy checks', desc: 'Automatic health checks, smoke tests, and deployment verification.', gate: false, tags: ['feature', 'hotfix', 'chore'] },
];

const TERMINAL_COMMANDS = {
'merge-features': 'git checkout release/2.5.0 && git merge --ff-only feature/new-widget',
'sync-master': 'git fetch origin master && git rebase origin/master',
'sync-master': 'git fetch origin main && git rebase origin/main',
'update-changelog': 'echo "## 2.5.0" >> CHANGELOG.md && git add CHANGELOG.md',
'confirm-ready': '# HUMAN GATE: Awaiting confirmation that release branch is ready...',
'contention-check': 'git fetch origin && git tag -l "staging/active"',
'claim-staging': 'git tag -a staging/active -m "Claimed by Tim for 2.5.0" && git push origin staging/active',
'reset-staging': 'git checkout staging && git reset --hard release/2.5.0 && git push --force origin staging',
'validate-staging': '# HUMAN GATE: Validate release on staging environment...',
'cross-repo-parity': 'node scripts/check-parity.js --repos api,frontend,shared --version 2.5.0',
'reset-master': '# HUMAN GATE: git checkout master && git reset --hard staging && git push --force origin master',
'reset-master': '# HUMAN GATE: git checkout main && git reset --hard staging && git push --force origin main',
'codepipeline': 'aws codepipeline get-pipeline-state --name prod-pipeline | jq .stageStates[].latestExecution.status',
'github-release': 'gh release create v2.5.0 --generate-notes --target master',
'github-release': 'gh release create v2.5.0 --generate-notes --target main',
'write-manifest': 'echo \'{"version":"2.5.0","date":"2026-03-14","sha":"abc1234"}\' > manifests/2.5.0.json',
'sync-branches': 'git checkout staging && git reset --hard master && git push --force origin staging development',
'sync-branches': 'git checkout staging && git reset --hard main && git push --force origin staging development',
'clear-staging': 'git tag -d staging/active && git push origin :refs/tags/staging/active',
'confluence': 'node scripts/publish-confluence.js --version 2.5.0 --space DEPLOY',
'post-deploy': 'curl -sf https://api.example.com/health && echo "All checks passed"',
Expand Down
4 changes: 2 additions & 2 deletions docs/hooks-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,7 @@ <h2>Hook Overview
<span class="hook-badge badge-deny">Deny</span>
</div>
<div class="hook-intercepts">Intercepts: <strong>gh pr create</strong></div>
<div class="hook-desc">Hard-blocks PRs targeting master, main, staging, or development. Only release branches are valid PR targets &mdash; no exceptions.</div>
<div class="hook-desc">Hard-blocks PRs targeting the production branch (main/master), staging, or development. Only release branches are valid PR targets &mdash; no exceptions.</div>
</div>

<div class="hook-card">
Expand Down Expand Up @@ -725,7 +725,7 @@ <h4>Deny</h4>
{ text: '', delay: 300 },
{ text: '<span class="hook-label">[hook] check-gh-pr-create.sh</span> intercepting gh pr create...', delay: 600 },
{ text: '<span class="dim">PR target: main</span>', delay: 900 },
{ text: '<span class="dim">Checking protected branch list: master, main, staging, development</span>', delay: 1200 },
{ text: '<span class="dim">Checking protected branch list: main, master, staging, development</span>', delay: 1200 },
{ text: '<span class="warn">Target branch "main" is protected.</span>', delay: 1500 },
{ text: '', delay: 1700 },
{ text: '<span class="error">&#128721; DENIED &mdash; PRs to main are not allowed.</span>', delay: 1900 },
Expand Down
2 changes: 1 addition & 1 deletion docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ <h3>Staging Contention Polling</h3>
<a class="card" href="deploy-process-demo.html">
<div class="icon">&#128640;</div>
<h3>Deployment Process</h3>
<p>The full 17-step deployment pipeline — from feature merge through staging, master reset, release, and post-deploy. Three workflow variants.</p>
<p>The full 17-step deployment pipeline — from feature merge through staging, production reset, release, and post-deploy. Three workflow variants.</p>
<div class="badges">
<span class="badge interactive">interactive</span>
<span class="badge simulation">step-through sim</span>
Expand Down
24 changes: 12 additions & 12 deletions docs/preflight-postdeploy-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ <h2>&#9989; Post-Deploy <span class="panel-badge badge-after">After Deploy</span
<ul class="check-list" id="postdeploy-list">
<li class="check-item pending" data-index="0">
<span class="check-icon"></span>
<span class="check-label">Master reset completed (master = staging)</span>
<span class="check-label">Production reset completed (production = staging)</span>
</li>
<li class="check-item pending" data-index="1">
<span class="check-icon"></span>
Expand Down Expand Up @@ -563,7 +563,7 @@ <h2>&#9989; Post-Deploy <span class="panel-badge badge-after">After Deploy</span
</li>
<li class="check-item pending" data-index="7">
<span class="check-icon"></span>
<span class="check-label">Branch alignment (master = staging = dev)</span>
<span class="check-label">Branch alignment (production = staging = dev)</span>
</li>
<li class="check-item pending" data-index="8">
<span class="check-icon"></span>
Expand Down Expand Up @@ -609,12 +609,12 @@ <h2>Branch Alignment Report</h2>
</thead>
<tbody>
<tr>
<td class="check-name">master = staging</td>
<td class="check-name">production = staging</td>
<td class="status-pass">&#9989;</td>
<td class="detail">abc1234 &mdash; heads match</td>
</tr>
<tr>
<td class="check-name">master = development</td>
<td class="check-name">production = development</td>
<td class="status-pass">&#9989;</td>
<td class="detail">abc1234 &mdash; heads match</td>
</tr>
Expand Down Expand Up @@ -749,14 +749,14 @@ <h2>Branch Alignment Report</h2>

// ---- Post-Deploy ----
const postdeployLabels = [
'Master reset completed (master = staging)',
'Production reset completed (production = staging)',
'GitHub Releases created',
'Release manifest written',
'Release branch merged into development',
'Branch protection re-enabled',
'Staging cleared (staging/active removed)',
'Confluence release notes written',
'Branch alignment (master = staging = dev)',
'Branch alignment (production = staging = dev)',
'Changelog updated',
'Production smoke test confirmed'
];
Expand Down Expand Up @@ -815,12 +815,12 @@ <h2>Branch Alignment Report</h2>
if (alignmentHealthy) {
tbody.innerHTML = `
<tr>
<td class="check-name">master = staging</td>
<td class="check-name">production = staging</td>
<td class="status-pass">&#9989;</td>
<td class="detail">abc1234 &mdash; heads match</td>
</tr>
<tr>
<td class="check-name">master = development</td>
<td class="check-name">production = development</td>
<td class="status-pass">&#9989;</td>
<td class="detail">abc1234 &mdash; heads match</td>
</tr>
Expand Down Expand Up @@ -848,14 +848,14 @@ <h2>Branch Alignment Report</h2>
} else {
tbody.innerHTML = `
<tr>
<td class="check-name">master = staging</td>
<td class="check-name">production = staging</td>
<td class="status-fail">&#10060;</td>
<td class="detail">master: abc1234, staging: def5678 &mdash; 3 commits ahead</td>
<td class="detail">main: abc1234, staging: def5678 &mdash; 3 commits ahead</td>
</tr>
<tr>
<td class="check-name">master = development</td>
<td class="check-name">production = development</td>
<td class="status-fail">&#10060;</td>
<td class="detail">master: abc1234, dev: 9ab0cde &mdash; 5 commits behind</td>
<td class="detail">main: abc1234, dev: 9ab0cde &mdash; 5 commits behind</td>
</tr>
<tr>
<td class="check-name">Version parity</td>
Expand Down
Loading