Immutable Shipped Stories
A core principle of MOMENTUM: once a story ships to production, it becomes immutable. It's a historical record of intent and implementation. If behavior needs to change later, you create a new story.
This guide explains why, how to enforce it, and how to handle behavior changes elegantly.
Why Immutable?
1. Auditability
When a story ships, it documents:
- What we built
- Why we built it (acceptance criteria)
- When it shipped
- What behavior it realized
Rewriting that story later obscures what actually happened. Git's history should reflect reality.
2. Traceability
If a bug is discovered in shipped code, you want to know:
- What story introduced it?
- What were the acceptance criteria?
- What assumptions did we make?
Rewriting the story after the fact makes this impossible.
3. Learning
By keeping old stories intact, you can:
- Look back and see what assumptions were wrong
- Understand how a feature evolved
- Train new team members on "here's what we shipped and why"
4. Coherence
Stories are part of a larger narrative (epics → strategy). Changing a shipped story breaks the narrative. Better to create a new story that explicitly supersedes it.
Shipped Story Lifecycle
Draft → Ready → In Development
Draft
├─ Writing acceptance criteria
├─ Designer working on UX
└─ Ready for engineering
In Development
├─ Engineering in progress
├─ Code review
└─ Merged to main
Ready for Deployment / In Staging
Deployed to Production
Status: Done (immutable)
├─ Link in related docs
├─ Update metrics
└─ Refer to in new stories if behavior changes
Once a story is marked status: done, do not edit its acceptance criteria, description, or scope.
Metadata for Shipped Stories
Use front-matter to mark shipped status clearly:
---
id: ST-EP003-001
type: story
title: "Redesign checkout form layout"
status: done # immutable
epic: EP-003
shipped_date: 2025-01-20
owner: bob@example.com
merged_pr: #456
deployed_to_prod: 2025-01-21
related_stories:
- ST-EP003-003 # Continuation or related work
metrics:
- form_completion_time
- form_field_clarity
---
Key fields:
status: done– Signals immutabilityshipped_date– When it went to staging/proddeployed_to_prod– When users experienced itmerged_pr– Link to the change that shipped itrelated_stories– If new stories supersede or extend this one
The Rule: Never Rewrite Shipped Stories
❌ Don't Do This
# Story EP003-001: Redesign checkout form layout
[...original story content...]
## 1. User Story
As a customer, I want an intuitive checkout form, so I can complete purchase quickly.
## 2. Acceptance Criteria
- [ ] Form has fields in order: email, address, card
- [x] SHIPPED ✓ 2025-01-21
🔴 LATER UPDATE (Feb 8):
Actually, we realized we should also validate ZIP codes in real-time...
Let me add that acceptance criterion...
❌ Don't "add" or "update" shipped acceptance criteria.
✅ Do This Instead
Original story (immutable):
---
id: ST-EP003-001
status: done
shipped_date: 2025-01-20
---
# Story EP003-001: Redesign checkout form layout
## 1. User Story
As a customer, I want an intuitive checkout form, so I can complete purchase quickly.
## 2. Acceptance Criteria
- [ ] Form has fields in order: email, address, card
- [ ] Real-time field validation: email, card number
- [ ] Graceful error states for invalid input
New story (tracks the change):
---
id: ST-EP003-007
type: story
title: "Add address ZIP validation to checkout"
status: draft
epic: EP-003
depends_on:
- ST-EP003-001 # This story's work is based on EP003-001
related_stories:
- ST-EP003-001
sources:
- discovery/insights.md#INS-018 # Insight that drove this
---
# Story EP003-007: Add address ZIP validation to checkout
## 1. User Story
As a customer, I want ZIP code validation in checkout, so I don't enter invalid addresses.
## 2. Acceptance Criteria
- [ ] ZIP code field validates on blur (supports US + international formats)
- [ ] Show error if ZIP doesn't match address state
- [ ] Allow retry or use alternate address
## 3. Context
ST-EP003-001 shipped basic address validation but didn't validate ZIP codes. After launch, we learned that ~8% of users enter mismatched ZIP/address combinations, leading to delivery issues (INS-018).
✅ Create a new story that explicitly references the original.
How to Handle Behavior Changes
Scenario 1: Bug Fix (Not a Scope Change)
A shipped story has a bug. Do you create a new story or fix it?
Rule: If the bug violates an acceptance criterion, fix it in the code. Don't change the story – the acceptance criteria remain the truth of what was shipped.
Example:
- Story EP003-001 says: "ZIP field validates on blur"
- Bug: ZIP validation broken for international addresses
- Action: Fix the code (bug fix)
- Story: Leave unchanged – the AC was the intent; the implementation was buggy
Scenario 2: Scope Change (Post-Ship)
A shipped story's scope needs to change based on user feedback or new requirements.
How to Handle Behavior Changes
Example:
- Original Story EP003-001: "Form has basic validation"
- New insight: "Users enter ZIP codes incorrectly" (INS-018)
- Create: New Story EP003-007 (ZIP validation)
- Link: ST-EP003-007 references ST-EP003-001
Scenario 3: Deprecation
A shipped story's functionality is being replaced.
Rule: Mark the original story as status: deprecated. Link to the new feature. Leave the original untouched.
Example:
---
id: ST-EP003-005
type: story
status: deprecated # New field
title: "In-app password reset flow"
deprecated_date: 2025-02-08
superseded_by: [ST-XX-YYY] # Link to replacement
reason: "User interviews show 85% prefer email reset. Deprecated Feb 8 per design review."
---
# Story EP003-005: In-app password reset flow
[Original story content - unchanged]
Scenario 4: Epic Scope Changed, Affecting Stories
If an epic changes scope and some of its stories are no longer relevant:
- Mark those stories
status: deprecated - Note why (
reason: "Out of scope per PR #123") - Create new stories for new scope items
- Link: epics point to current stories; deprecated stories link to their replacements
Example:
In the Feb 8 meeting, you decided to deprecate in-app password reset from the checkout epic.
---
id: ST-EP003-005
status: deprecated
deprecated_date: 2025-02-08
reason: "Deprecation decision from design review (meetings/2025-02-08-design-review.md). In-app reset removed from EP-003 scope."
---
Implications for Epic
When stories are tied to an epic, and you deprecate some:
Epic file /delivery/epic-003-checkout/epic-003-checkout.md:
# ... (Epic header, problem, goals, scope)
## 6. Linked Stories
- **Active Stories:**
- ST-EP003-001 ✓ Shipped
- ST-EP003-002 ✓ Shipped
- ST-EP003-003 In Development
- ST-EP003-004 Ready
- **Deprecated (out of scope):**
- ~~ST-EP003-005~~ (In-app password reset; deprecated 2025-02-08)
## 7. Related Epics
- [See below: EP-004 for password reset flow]
And create/update a related epic for the new work:
---
id: EP-004
type: epic
title: "Authentication Improvements"
status: draft
related:
- EP-003 # Checkout epic (password reset moved out)
---
Story Status Enumeration
Use consistent status values across all stories:
| Status | Meaning |
|---|---|
draft |
Being written; not ready for review |
ready |
Ready for engineering; acceptance criteria finalized |
in-progress |
Engineering is working on it |
done |
Shipped to production (immutable) |
deprecated |
Was shipped but functionality is no longer in use |
Important: Don't use status values like "on-hold" or "cancelled." Instead:
Story Status Enumeration
Add a pre-commit hook or CI check:
#!/bin/bash
# Check for modifications to shipped stories
for file in delivery/**/*.md; do
if grep -q "^status: done" "$file"; then
# This story is shipped
if [ $(git diff --stat "$file" | wc -l) -gt 1 ]; then
echo "ERROR: Story $file is marked 'status: done' but has uncommitted changes."
echo "Shipped stories are immutable. Create a new story instead."
exit 1
fi
fi
done
Or, in your LLM instructions (.llm/base.md):
## Immutable Shipped Stories
- Never edit the acceptance criteria or scope of a story marked `status: done`.
- If a shipped story's behavior needs to change, create a new story instead.
- Mark old stories that are superseded as `status: deprecated`.
- **Exception:** Bug fixes in code don't require story updates; the original AC was the intent.
Benefits of Immutability
✓ Auditability – You can trace what was shipped and why
✓ Learning – Understand assumptions and decisions over time
✓ Traceability – Link bugs or metrics back to the original story
✓ Coherence – Product narrative remains intact
✓ Team clarity – New hires understand the full product history
Example: Checkout Epic Story Timeline
# EP-003: Checkout Redesign – Story Timeline
## Shipped Wave 1 (Jan 2025)
- ST-EP003-001 ✓ Form Layout (Jan 20)
- ST-EP003-002 ✓ Field Validation (Jan 25)
- ST-EP003-003 ✓ Error States (Jan 28)
## Shipped Wave 2 (Feb 2025)
- ST-EP003-006 ✓ 3-D Secure Flow (Feb 10)
## Deprecated (out of scope)
- ~~ST-EP003-005~~ In-app Password Reset (deprecated Feb 8, design review)
## Follow-up Stories (from user feedback)
- ST-EP003-007 (draft) ZIP validation (based on INS-018)
- ST-EP003-008 (draft) Mobile checkout UX refinement
Each shipped story is a historical record. Future stories build on lessons learned.