pr-storyteller
May 16, 2026 · View on GitHub
Each scenario shows: a starting state, the exact command, the helper's structured output, and what Claude produces as the final PR description.
Scenario 1 — refresh-token rotation feature
Setup: A feature branch with two commits beyond main:
$ git log --oneline main..HEAD
def5678 test(auth): cover stale-token replay
abc1234 feat(auth): rotate refresh tokens
Command:
python3 scripts/story.py --base main
Helper output (JSON, faithful to current behaviour):
{
"base": "main",
"head": "HEAD",
"commits": [
{"hash": "def5678", "subject": "test(auth): cover stale-token replay"},
{"hash": "abc1234", "subject": "feat(auth): rotate refresh tokens"}
],
"files_changed": [
{"path": "src/auth/refresh.py", "additions": 84, "deletions": 12},
{"path": "tests/test_refresh.py", "additions": 41, "deletions": 0}
],
"suggested_title": "test(auth): cover stale-token replay"
}
Note:
suggested_titlecomes from the latest commit subject. Claude is expected to override this — the latest commit was a test, but the feature is rotation. See below.
What Claude does next (running /pr-storyteller):
### feat(auth): rotate refresh tokens on every refresh
#### Summary
- Rotate the refresh-token secret on every successful refresh
- Reject any refresh token that has already been redeemed
- Invalidate the full token family on detected replay
#### Changes
- `src/auth/refresh.py` (+84 / −12) — token rotation + replay detection
- `tests/test_refresh.py` (+41 / −0) — coverage for the replay path
#### Test plan
- [ ] Successful login → both access and refresh tokens issued
- [ ] Reuse a refresh token → 401 + token family invalidated
- [ ] Clock-skew tolerance still passes (existing test)
- [ ] Sentry breadcrumbs include the rotation event ID
#### Risk
Medium. Affects every authenticated session. Roll out behind
`AUTH_ROTATE_REFRESH` flag and watch the auth-failure dashboard
for 30 minutes before flipping for the wider population.
Scenario 2 — feature branch against develop instead of main
python3 scripts/story.py --base develop
Works the same way; --base controls both the git log range and the git diff --stat range. If the named branch doesn't exist locally, the helper falls back to master.
Scenario 3 — empty diff is safe
When HEAD is at the same commit as the base (or you're not in a git repo), the helper emits a structurally valid empty payload and exits zero:
{
"base": "main",
"head": "HEAD",
"commits": [],
"files_changed": [],
"suggested_title": null
}
The slash command can detect this and prompt the user to either make commits or pick a different base.