The capstone: all 10 roles, 3 sprints, from architecture to production
โฑ ~120 min ยท 3 Sprints ยท Advanced+
This is the capstone. Every command and role you've learned comes together. Multi-tenant SaaS has the highest complexity: tenant isolation, data security, PDF generation, email delivery, payment tracking. You'll use the full framework โ security threat modeling upfront, story splitting for complex features, performance tuning, architecture audits, and a complete release pipeline.
Projects 01-06 each introduced a few commands at a time. This project uses nearly every command and ALL 10 roles in a single, realistic build. Multi-tenant means every query must be tenant-scoped, every endpoint must enforce isolation, and every security gap could expose one customer's data to another. This is the complexity level where the framework proves its value.
This is the only project where every role makes a meaningful contribution:
| Role | What they do in this project | When |
|---|---|---|
๐ @po | Writes tenant-scoped stories, validates multi-tenant acceptance criteria | Story creation, sprint reviews, acceptance |
๐ @sm | Manages 3 sprints, tracks velocity trend across sprints, facilitates 3 retros | All sprint ceremonies |
๐๏ธ @arch | Designs multi-tenant architecture (shared DB vs separate schemas), records ADRs | Sprint 0, architecture audit |
๐จโ๐ป @lead | Reviews tenant isolation code, ensures no cross-tenant data leaks in PRs | Every code review |
โจ๏ธ @dev | Implements with TDD, handles complex PDF generation and payment logic | All implementation |
๐งช @qa | Tests tenant isolation (can tenant A see tenant B's data?), validates coverage | DoD checks, isolation testing |
๐๏ธ @dba | Designs schema with tenant_id foreign keys, indexes for tenant-scoped queries, migration safety | Schema design, Sprint 2 payments |
๐ก๏ธ @sec | STRIDE threat model upfront, OWASP audit on payment/invoice code, dependency CVE scan | Sprint 0, Sprint 3 |
๐จ @ux | Invoice creation form, PDF preview, responsive dashboard, WCAG AA accessibility | Sprint 3 UI work |
๐ @devops | CI/CD pipeline, staging + production environments, deployment with rollback plan | CI, releases, deploy |
"I want to build a multi-tenant invoicing SaaS. Tenants are businesses that create invoices for their clients. Each tenant's data must be completely isolated. Features: tenant registration, user management within tenants, invoice CRUD with line items, PDF generation, email delivery, and payment tracking."
/agile-explore-brainstorm/agile-explore-brainstorm Multi-tenant invoicing SaaS. Key concern: tenant data isolation. Evaluate shared DB with tenant_id column vs separate schemas vs separate databases. Must be cost-effective for MVP with path to scale.
๐๏ธ @arch evaluates multi-tenant isolation strategies:
๐๏ธ @arch responds:
## /agile-explore-brainstorm โ Results
### Problem Statement
Multi-tenant data isolation for invoicing SaaS.
Constraints: cost-effective MVP, path to scale, strong isolation.
### Scoring Matrix
| Approach | Feasibility | Isolation | Cost | Scale Path | Total |
|------------------------|------------|-----------|---------|------------|-------|
| Shared DB + tenant_id | 9/10 | 7/10 | 10/10 | 7/10 | 33 |
| Separate schemas | 7/10 | 9/10 | 7/10 | 8/10 | 31 |
| Separate databases | 5/10 | 10/10 | 4/10 | 9/10 | 28 |
### Recommendation
Shared DB with tenant_id โ highest feasibility, lowest cost.
Isolation enforced at application layer via middleware.
### What Would Change This
- If regulated industry (healthcare, finance) โ separate schemas
- If >1000 tenants with heavy data โ separate databases
- If compliance requires physical isolation โ separate databases
"Let's go with shared DB with tenant_id. Simplest for MVP, and we'll enforce isolation in middleware."
Record the decision so the framework remembers it across sprints:
๐ฌ /agile-memory-remember Architecture decision: shared DB with tenant_id column
for multi-tenant isolation. Enforced via middleware, not DB-level.
๐ @sm: Saved to .memory/semantic/architecture.md
/agile-security-threat-modelMulti-tenant systems have a high security bar. Before writing a single line of code, @sec identifies threats that will drive Sprint 3's security stories.
/agile-security-threat-model Analyze the multi-tenant invoicing system for security threats. Key concerns: tenant data isolation, payment data, PII in invoices.
๐ก๏ธ @sec responds with STRIDE analysis:
## /agile-security-threat-model โ STRIDE Analysis
### System: Multi-Tenant Invoicing SaaS
| Threat Category | Threat | Impact | Mitigation |
|--------------------|-------------------------------------------|--------|---------------------------------------|
| Spoofing | Attacker forges tenant context in JWT | ๐ด High | Sign JWTs server-side, validate |
| | | | tenant_id claim on every request |
| Tampering | User modifies invoice_id in URL to access | ๐ด High | Scope all queries by tenant_id, |
| | another tenant's invoice (IDOR) | | never trust client-supplied IDs alone |
| Repudiation | Tenant disputes invoice was sent/paid | ๐ก Med | Audit log for all invoice state |
| | | | changes and email deliveries |
| Info Disclosure | Cross-tenant data leak via API response | ๐ด High | Tenant isolation middleware on ALL |
| | or error messages | | endpoints, sanitize error responses |
| Denial of Service | Large PDF generation blocks server | ๐ก Med | Queue PDF generation, set timeouts, |
| | | | rate limit per tenant |
| Elevation of Priv | Regular user accesses admin endpoints | ๐ด High | RBAC per tenant, separate admin |
| | or another tenant's admin panel | | middleware, validate role + tenant |
### Key Finding
3 of 6 threat categories are ๐ด High โ all related to tenant isolation.
This confirms: tenant isolation middleware is the single most critical component.
The STRIDE analysis identified that tenant isolation failures appear in three separate threat categories. This isn't a nice-to-have โ it's the architectural foundation. Sprint 3 will address these findings with targeted security stories.
๐๏ธ @dba designs the initial schema with tenant isolation baked into the data model:
๐๏ธ @dba: Initial Schema Design
### Tables
tenants (id PK, name, slug, created_at)
users (id PK, tenant_id FK โ tenants, email UNIQUE per tenant, role, password_hash)
invoices (id PK, tenant_id FK โ tenants, client_name, client_email,
status, due_date, created_at, updated_at)
invoice_items (id PK, invoice_id FK โ invoices, tenant_id FK โ tenants,
description, quantity, unit_price DECIMAL(10,2), line_total DECIMAL(10,2))
payments (id PK, invoice_id FK โ invoices, tenant_id FK โ tenants,
amount DECIMAL(10,2), payment_date, method)
### Key Constraints
- Every table has tenant_id (except tenants itself)
- Composite unique index on users(tenant_id, email)
- All FK cascades scoped to tenant
- DECIMAL(10,2) for ALL money fields โ never FLOAT
### Indexes
- idx_invoices_tenant_status ON invoices(tenant_id, status)
- idx_invoice_items_invoice ON invoice_items(invoice_id)
- idx_payments_invoice ON payments(invoice_id)
๐ฌ "Set up the project with Express, PostgreSQL, and Jest. Then initialize."
โจ๏ธ @dev: Scaffolds project structure
/agile-setup-init โ All memory files populated โ
/agile-setup-onboard โ Coding rules generated โ
/agile-setup-health โ All checks green โ
Phase 1 complete. You have a multi-tenant architecture decision, a STRIDE threat model driving future security work, a schema design with tenant isolation at the data level, and a fully initialized project. Zero features built yet โ Sprint 0 is about foundations.
/agile-story-create Tenants can register their business and get a unique workspace with a slug URL
๐ @po responds:
## /agile-story-create โ Story Created
### Story US-001 (3 pts)
**As a** business owner,
**I want** to register my business as a tenant with a unique slug,
**so that** I get an isolated workspace for my invoicing.
### Acceptance Criteria
AC-1: POST /tenants with {name: "Acme Corp"} โ 201, returns tenant with slug "acme-corp"
AC-2: Duplicate slug โ 409 Conflict
AC-3: Missing name โ 400 with error message
/agile-story-create Users within a tenant can register, log in, and only see their tenant's data
๐ @po creates US-002 โ but @dev estimates it at 13 points. Too large for one story.
/agile-story-split/agile-story-split US-002 --strategy business-rules
๐ @po splits by business rules:
## /agile-story-split โ US-002 Split Into 3 Stories
| Story | Title | Points | Rationale |
|---------|--------------------------------|--------|------------------------------|
| US-002a | User registration within tenant | 3 | Signup + tenant_id FK |
| US-002b | Login + JWT with tenant claim | 3 | Auth + tenant in token |
| US-002c | Tenant isolation middleware | 5 | Core security โ scopes ALL |
| | | | queries by tenant_id |
Original 13 pts โ 3 stories totaling 11 pts (reduced uncertainty)
### INVEST Check: All 3 stories pass independently โ
Create the final foundation story:
๐ฌ /agile-story-create Create and manage invoices: add line items,
calculate totals, set due dates
โ ๐ @po creates US-003: Invoice CRUD (5 pts)
/agile-sprint-planning๐ @sm facilitates:
## Sprint 1 Planning
### Sprint Goal
"Deliver tenant registration, user auth with isolation, and invoice CRUD"
### Sprint Backlog
| Story | Title | Points | Priority |
|---------|--------------------------------|--------|----------|
| US-001 | Tenant registration | 3 | P1 |
| US-002a | User registration within tenant | 3 | P1 |
| US-002b | Login + JWT with tenant claim | 3 | P1 |
| US-002c | Tenant isolation middleware | 5 | P1 |
| US-003 | Invoice CRUD | 5 | P1 |
### Capacity
- Committed: 19 points (aggressive for Sprint 1)
- Risk: No historical velocity โ may need to carry US-003 to Sprint 2
/agile-code-branch feature US-001 tenant-registration
/agile-story-plan US-001
/agile-code-tdd US-001
๐ด Test: POST /tenants creates tenant with slug โ โ
๐ข Implement tenant creation + slug generation โ โ
๐ด Test: duplicate slug returns 409 โ โ
๐ข Add unique constraint check โ โ
๐ด Test: missing name returns 400 โ โ
๐ข Add validation โ โ
/agile-code-ci โ โ
All green
/agile-code-commit โ feat(tenants): add tenant registration with slug
/agile-code-pr โ PR created
/agile-code-pr-review โ โ
Approved (8/10)
/agile-code-merge โ Squash merged to develop
/agile-story-dod โ โ
DONE
/agile-story-accept โ โ
ACCEPTED (3 points)
/agile-code-branch feature US-002a user-registration
/agile-story-plan US-002a โ @lead: Create user with tenant_id FK, validate
email unique within tenant
/agile-code-tdd US-002a
๐ด Test: POST /tenants/:tenantId/users creates user โ โ
๐ข Implement with tenant_id FK โ โ
๐ด Test: duplicate email within same tenant โ 409 โ โ
๐ข Add composite unique check โ โ
๐ด Test: same email in different tenant โ 201 (allowed) โ โ
๐ข Scope uniqueness to tenant โ โ
/agile-code-ci โ commit โ PR โ review โ merge โ DoD โ accept โ
(3 pts)
/agile-code-branch feature US-002b tenant-jwt
/agile-code-tdd US-002b
๐ด Test: POST /login returns JWT with tenant_id in payload โ โ
๐ข Add tenant_id claim to JWT โ โ
๐ด Test: invalid credentials โ 401 โ โ
๐ข Add credential validation โ โ
/agile-code-ci โ commit โ PR โ review โ merge โ DoD โ accept โ
(3 pts)
/agile-code-branch feature US-002c tenant-isolation
/agile-story-plan US-002c โ @lead: Middleware extracts tenant_id from JWT,
injects into every DB query. No endpoint should work without valid tenant context.
/agile-code-tdd US-002c
๐ด Test: request without JWT โ 401 โ โ
๐ข Add auth middleware โ โ
๐ด Test: request with valid JWT โ tenant_id injected into req โ โ
๐ข Extract tenant_id from JWT claims โ โ
๐ด Test: GET /invoices only returns current tenant's data โ โ
๐ข Scope all queries with WHERE tenant_id = ? โ โ
๐ด Test: GET /invoices/:id for another tenant's invoice โ 404 (not 403!) โ โ
๐ข Tenant-scoped lookup returns null โ 404 โ โ
/agile-code-ci โ โ
All green
๐๏ธ @dba reviews the migration for the users table:
๐๏ธ @dba: Migration Review
โ
tenant_id FK with ON DELETE CASCADE โ correct
โ
Composite unique index (tenant_id, email) โ correct
โ ๏ธ Missing index on users(tenant_id) for tenant-scoped lookups โ add it
โ @dev adds index, tests still pass โ
๐งช @qa runs the critical isolation test:
๐งช @qa: Tenant Isolation Verification
Test: Can tenant A see tenant B's invoices?
1. Create tenant A, create invoice for tenant A
2. Create tenant B, authenticate as tenant B
3. GET /invoices โ returns [] (not tenant A's invoice) โ
4. GET /invoices/:tenantA-invoiceId โ 404 (not 200) โ
5. Direct DB query confirms data exists but middleware blocks it โ
Verdict: โ
Isolation verified โ no cross-tenant data leakage
/agile-code-commit โ feat(auth): add tenant isolation middleware
/agile-code-pr โ PR created
/agile-code-pr-review
๐จโ๐ป @lead: PR Review โ US-002c (CRITICAL component)
| # | Severity | Finding | Fix |
|---|----------|--------------------------------------------|------------------------------|
| 1 | ๐ก S2 | Middleware doesn't handle expired JWTs | Add expiry check |
| | | differently from missing JWTs | with distinct error message |
| 2 | ๐ต S3 | Magic string "tenant_id" repeated | Extract to constant |
Score: 8/10
Verdict: โ
APPROVED โ isolation logic is solid
/agile-code-merge โ Squash merged to develop
/agile-story-dod โ โ
DONE
/agile-story-accept โ โ
ACCEPTED (5 pts)
/agile-code-branch feature US-003 invoice-crud
/agile-code-tdd US-003
๐ด Test: POST /invoices creates invoice with line items โ โ
๐ข Implement with tenant-scoped insert โ โ
๐ด Test: GET /invoices returns only current tenant's invoices โ โ
๐ข Query scoped by tenant_id from middleware โ โ
๐ด Test: line item totals auto-calculated โ โ
๐ข Calculate line_total = quantity * unit_price, sum for invoice total โ โ
๐ด Test: invoice with zero items โ 400 โ โ
๐ข Validate at least one line item required โ โ
/agile-code-ci โ commit โ PR โ review โ merge โ DoD โ accept โ
(5 pts)
/agile-sprint-review
Sprint Goal Met: โ
Yes
Completed: 19/19 points (100%)
Velocity baseline: 19 points
/agile-sprint-retro
What went well: Story splitting reduced US-002 risk significantly
What went well: Tenant isolation tested thoroughly โ high confidence
To improve: Sprint was aggressive โ 19 pts may not be sustainable
Action items:
| Action | Owner | Due |
|-------------------------------------|-------|----------|
| Target 15-16 pts next sprint | @sm | Sprint 2 |
| Add tenant isolation to test template| @qa | Sprint 2 |
/agile-memory-learn โ Sprint 1 velocity: 19 pts, story splitting effective
/agile-sprint-refine/agile-sprint-refine
๐ @po: Backlog refined
US-004: Generate PDF invoices โ Ready โ
(ACs updated with logo, line items, totals)
US-005: Send invoice by email โ Ready โ
(ACs include retry on failure)
US-006: Track payments โ Ready โ
(ACs include partial payment, overdue status)
๐ฌ /agile-story-create Generate PDF invoices from invoice data.
The PDF should include company logo, line items, totals, payment terms.
โ ๐ @po creates US-004 (5 pts)
๐ฌ /agile-story-create Send invoice by email to the client
with the PDF attached
โ ๐ @po creates US-005 (3 pts)
๐ฌ /agile-story-create Track payments against invoices:
mark as paid, partially paid, or overdue
โ ๐ @po creates US-006 (5 pts)
/agile-sprint-planning
Sprint Goal: "Add PDF generation, email delivery, and payment tracking"
Committed: 13 points (down from 19 โ learning from retro โ
)
/agile-explore-spikePDF generation has unknowns. Before committing to an implementation, @dev runs a time-boxed spike:
/agile-explore-spike Evaluate PDF generation libraries for Node.js. Need: company logo, styled table of line items, totals, payment terms. Time-box: 2 hours.
โจ๏ธ @dev runs a time-boxed spike:
## /agile-explore-spike โ PDF Library Evaluation (2h time-box)
### Libraries Tested
| Library | Logo Support | Table Layout | File Size | Render Speed | Verdict |
|-------------|-------------|--------------|-----------|-------------|------------|
| PDFKit | โ
Images | Manual layout| 180KB | 120ms | โ
Winner |
| Puppeteer | โ
HTML/CSS | Full CSS | 850KB | 2.1s | Too heavy |
### Recommendation
PDFKit โ fast rendering, small output, manual layout is acceptable
for structured invoice format.
### What We Learned
Puppeteer renders HTML to PDF (flexible) but requires a headless browser
(heavy dependency, slow). PDFKit is code-driven (less flexible) but
10x faster and no external dependencies.
### Decision
Use PDFKit for MVP. If layout needs become complex, revisit Puppeteer.
> ๐ฌ /agile-memory-remember PDF generation: PDFKit chosen over Puppeteer.
> Reason: 10x faster, no headless browser dependency.
> Revisit if layout complexity increases.
/agile-code-branch feature US-004 pdf-generation
/agile-story-plan US-004
/agile-code-tdd US-004
๐ด Test: POST /invoices/:id/pdf returns a PDF buffer โ โ
๐ข Implement PDFKit rendering โ โ
๐ด Test: PDF contains company name, line items, totals โ โ
๐ข Add structured layout: header, items table, total โ โ
๐ด Test: PDF for another tenant's invoice โ 404 โ โ
๐ข Tenant-scoped lookup (middleware handles it) โ โ
/agile-code-ci โ commit โ PR โ review โ merge โ DoD โ accept โ
(5 pts)
/agile-code-perfAfter implementation, PDF generation is slow for large invoices. Time to profile:
/agile-code-perf Profile PDF generation. It takes 3 seconds for a 50-line invoice. Target: under 500ms.
โจ๏ธ @dev profiles and optimizes:
## /agile-code-perf โ PDF Generation Profiling
### Bottleneck Analysis
| Operation | Time | % of Total |
|---------------------|---------|------------|
| DB query (invoice) | 45ms | 1.5% |
| DB query (items) | 62ms | 2.1% |
| Font loading | 2,400ms | 80.0% โ ๐ด |
| PDF rendering | 320ms | 10.7% |
| Buffer finalization | 173ms | 5.8% |
### Root Cause
Font files loaded from disk on EVERY PDF generation.
PDFKit re-reads and re-parses .ttf files per request.
### Fix
Cache font objects at module load time โ load once, reuse forever.
### Results
| Metric | Before | After | Improvement |
|----------------|---------|--------|-------------|
| 50-line invoice | 3,000ms | 380ms | 87% faster |
| Memory per PDF | 12MB | 4MB | 67% less |
โ
Target met: 380ms < 500ms
/agile-code-branch feature US-005 email-delivery
/agile-code-tdd US-005
๐ด Test: POST /invoices/:id/send triggers email with PDF attachment โ โ
๐ข Implement email sending via nodemailer โ โ
๐ด Test: email failure returns 502 with retry guidance โ โ
๐ข Add error handling with status tracking โ โ
๐ด Test: resending already-sent invoice updates sent_at โ โ
๐ข Track email delivery attempts โ โ
/agile-code-ci โ commit โ PR
๐จโ๐ป @lead catches an important issue in review:
๐จโ๐ป @lead: PR Review โ US-005
| # | Severity | Finding | Fix |
|---|----------|--------------------------------------------|-----------------------------|
| 1 | ๐ก S2 | Email failure silently swallowed โ caller | Return 502 with error, |
| | | gets 200 even when email fails | log failure, mark invoice |
| | | | send_status as "failed" |
โ @dev fixes, re-runs CI โ
/agile-code-merge โ DoD โ accept โ
(3 pts)
๐๏ธ @dba designs the payment tracking schema before implementation:
๐๏ธ @dba: Payment Schema Design
payments (
id SERIAL PRIMARY KEY,
invoice_id INTEGER REFERENCES invoices(id),
tenant_id INTEGER REFERENCES tenants(id),
amount DECIMAL(10,2) NOT NULL, -- โ ๏ธ DECIMAL, not FLOAT!
payment_date TIMESTAMP DEFAULT NOW(),
method VARCHAR(20) CHECK (method IN ('bank_transfer','card','cash'))
)
### Why DECIMAL, not FLOAT?
FLOAT: 0.1 + 0.2 = 0.30000000000000004 โ rounding errors in financial totals
DECIMAL: 0.1 + 0.2 = 0.3 โ exact arithmetic, required for money
### Invoice Status Logic
- paid: SUM(payments.amount) >= invoice.total
- partially_paid: SUM(payments.amount) > 0 AND < invoice.total
- overdue: due_date < NOW() AND status != 'paid'
/agile-code-branch feature US-006 payment-tracking
/agile-code-tdd US-006
๐ด Test: POST /invoices/:id/payments records payment โ โ
๐ข Implement payment creation with DECIMAL arithmetic โ โ
๐ด Test: payment exceeding remaining balance โ 400 โ โ
๐ข Add overpayment validation โ โ
๐ด Test: partial payment updates status to "partially_paid" โ โ
๐ข Recalculate invoice status after each payment โ โ
๐ด Test: full payment updates status to "paid" โ โ
๐ข SUM check against invoice total โ โ
/agile-code-ci โ commit โ PR โ review โ merge โ DoD โ accept โ
(5 pts)
/agile-sprint-status/agile-sprint-status
๐ @sm: Sprint 2 Status
| Story | Status | Points |
|--------|------------|--------|
| US-004 | โ
Done | 5 |
| US-005 | โ
Done | 3 |
| US-006 | โ
Done | 5 |
Progress: 13/13 points (100%)
Sprint Goal: โ
On track
Velocity trend: Sprint 1 = 19, Sprint 2 = 13 (stabilizing)
/agile-sprint-review
Sprint Goal Met: โ
Yes
Velocity: 13/13 points (100%)
Cumulative: Sprint 1 (19) + Sprint 2 (13) = 32 points
/agile-sprint-retro
What went well: Spike prevented wrong PDF library choice
What went well: @lead caught silent email failure โ would have been a production bug
What went well: DECIMAL for money โ correct from day one
To improve: Should profile performance earlier, not after implementation
Action items:
| Action | Owner | Due |
|--------------------------------------|-------|----------|
| Add perf benchmarks to CI | @dev | Sprint 3 |
| Run OWASP audit before release | @sec | Sprint 3 |
/agile-memory-learn โ Sprint 2 velocity: 13 pts, spike ROI validated
Sprint 3 addresses the STRIDE findings from Sprint 0 and prepares for production release.
/agile-security-audit/agile-security-audit Scan invoice and payment code against OWASP Top 10
๐ก๏ธ @sec responds:
## /agile-security-audit โ OWASP Top 10 Scan
### Findings
| # | Severity | Category | Finding | Location |
|---|----------|-----------------------|---------------------------------------|-----------------------|
| 1 | ๐ด S0 | A01: Broken Access | Tenant isolation bypass via IDOR โ | GET /invoices/:id |
| | | Control | direct ID guessing bypasses middleware | (missing tenant check |
| | | | on direct-ID lookups | in findById query) |
| 2 | ๐ S1 | A03: Injection | Invoice amounts accepted from client | POST /invoices |
| | | | without server-side validation โ | (line item prices) |
| | | | negative prices allowed | |
| 3 | ๐ก S2 | A03: Injection | PDF generation doesn't sanitize HTML | PDF template |
| | | | in client_name field โ XSS in PDF | rendering |
### Required Actions
Fix #1 (S0) and #2 (S1) โ mandatory before release.
Fix #3 (S2) โ recommended before release.
The findById query fetches invoices by ID without scoping to the current tenant. An attacker who guesses an invoice ID from another tenant can access it directly. This is exactly the IDOR threat identified in the STRIDE analysis during Sprint 0.
/agile-code-tdd Fix S0: scope findById queries by tenant_id
๐ด RED:
test('GET /invoices/:id from another tenant returns 404', async () => {
// Create invoice as tenant A
const invoice = await createInvoice(tenantA);
// Request as tenant B
const res = await request(app)
.get(`/invoices/${invoice.id}`)
.set('Authorization', `Bearer ${tenantBToken}`);
expect(res.status).toBe(404); // NOT 200!
});
// โ FAIL โ returns 200 with tenant A's invoice data
๐ข GREEN:
// Fix: Add tenant_id to ALL findById queries
findById(id, tenantId) {
return db.query(
'SELECT * FROM invoices WHERE id = $1 AND tenant_id = $2',
[id, tenantId]
);
}
// โ
PASS โ returns 404 for cross-tenant access
/agile-code-tdd Fix S1: validate invoice amounts server-side
๐ด RED:
test('negative unit_price rejected with 400', async () => {
const res = await request(app)
.post('/invoices')
.set('Authorization', `Bearer ${token}`)
.send({ items: [{ description: 'Item', quantity: 1,
unit_price: -50.00 }] });
expect(res.status).toBe(400);
});
// โ FAIL โ returns 201 with negative amount
๐ข GREEN:
// Add validation: quantity > 0, unit_price >= 0
if (item.unit_price < 0 || item.quantity <= 0) {
return res.status(400).json({
error: 'unit_price must be >= 0, quantity must be > 0'
});
}
// โ
PASS
๐ฌ /agile-security-audit --recheck
๐ก๏ธ @sec: Re-audit results
#1 IDOR: โ
Fixed โ all findById queries now tenant-scoped
#2 Amounts: โ
Fixed โ server-side validation on all numeric fields
#3 PDF XSS: โ
Fixed โ HTML entities escaped in PDF text fields
/agile-security-scan/agile-security-scan
๐ก๏ธ @sec: Dependency CVE Scan
Packages scanned: 147
Vulnerabilities found: 1
๐ก CVE-2025-XXXX in nodemailer < 6.9.8 (ReDoS in email parsing)
Fix: npm update nodemailer โ 6.9.9 โ
Post-fix scan: โ
0 vulnerabilities
/agile-security-review/agile-security-review Review auth middleware, tenant isolation, and payment handling
๐ก๏ธ @sec: Security Review
Auth middleware: โ
JWT validated, expiry checked, tenant_id extracted
Tenant isolation: โ
All queries scoped (verified after S0 fix)
Payment handling: โ
DECIMAL arithmetic, overpayment prevented
Password storage: โ
bcrypt with cost factor 12
Overall: โ
PASS โ ready for production
๐จ @ux builds the invoice creation form and verifies accessibility:
๐จ @ux: Invoice Form + Dashboard
### WCAG AA Accessibility Checklist
| Criterion | Status |
|----------------------------------|--------|
| Color contrast ratio โฅ 4.5:1 | โ
|
| All form inputs have labels | โ
|
| Keyboard navigation works | โ
|
| Screen reader announces errors | โ
|
| Focus management on add/remove | โ
|
| Responsive: mobile invoice form | โ
|
### Responsive Verification
- Desktop (1200px+): full table layout โ
- Tablet (768px): stacked cards โ
- Mobile (375px): single-column, touch-friendly โ
/agile-code-refactor/agile-code-refactor Analyze the codebase after 3 sprints. Focus on duplication, complexity, and consistency.
## /agile-code-refactor โ Analysis + Refactoring
### Before
| Metric | Value |
|---------------------|----------|
| Code duplication | 14% |
| Avg cyclomatic complexity | 8.2 |
| Inconsistent error handling | 6 locations |
### Changes Made
1. Extracted tenant-scoped query builder (eliminated 8 duplicate WHERE clauses)
2. Unified error response format across all endpoints
3. Extracted validation middleware (DRY)
### After
| Metric | Before | After | Change |
|---------------------|--------|--------|--------|
| Code duplication | 14% | 3% | -79% |
| Avg cyclomatic complexity | 8.2 | 4.1 | -50% |
| Inconsistent errors | 6 | 0 | -100% |
Tests: โ
All 47 tests still pass
/agile-explore-audit/agile-explore-audit
๐๏ธ @arch: Architecture Health After 3 Sprints
### Health Score: 8.5/10
| Dimension | Score | Notes |
|--------------------|-------|------------------------------------------|
| Separation of concerns | 9/10 | Clean layers: routes โ services โ repos |
| Dependency management | 8/10 | No circular deps, clean imports |
| Testability | 9/10 | All services injectable, 94% coverage |
| Security posture | 8/10 | STRIDE mitigations implemented |
| Scalability path | 8/10 | Tenant isolation ready for schema split |
### Recommendation
Architecture is healthy. When tenant count exceeds ~500,
consider migrating to separate schemas (the #2 option from Sprint 0).
The tenant_id abstraction makes this migration straightforward.
/agile-sprint-review
Sprint Goal Met: โ
Yes โ security hardened, UI accessible, code refactored
Velocity: 7 points (security + refactoring stories)
Cumulative: 19 + 13 + 7 = 39 points across 3 sprints
/agile-sprint-retro
What went well: STRIDE analysis in Sprint 0 predicted exact security findings
What went well: Refactoring after 3 sprints brought duplication from 14% to 3%
What went well: All 10 roles contributed meaningfully
To improve: Should run /agile-security-audit after each sprint, not just Sprint 3
Action items:
| Action | Owner | Due |
|-------------------------------------------|-------|---------|
| Add security audit to sprint checklist | @sm | Future |
| Monitor tenant count for schema migration | @arch | Ongoing |
/agile-memory-learn โ Final sprint learnings saved
โ Velocity trend: 19 โ 13 โ 7 (stabilized, security sprints are lower velocity)
โ STRIDE ROI: 100% of predicted threats were found and fixed
/agile-ship-changelog## Changelog: v1.0.0 โ Multi-Tenant Invoicing SaaS
### Sprint 1 โ Foundation
- Tenant registration with unique slugs (US-001)
- User registration scoped to tenant (US-002a)
- JWT authentication with tenant claims (US-002b)
- Tenant isolation middleware โ all queries tenant-scoped (US-002c)
- Invoice CRUD with line items and auto-calculated totals (US-003)
### Sprint 2 โ Business Logic
- PDF invoice generation via PDFKit (US-004)
- Email delivery with PDF attachment and retry tracking (US-005)
- Payment tracking: paid, partially paid, overdue statuses (US-006)
### Sprint 3 โ Security & Quality
- Fixed IDOR vulnerability in invoice lookup (S0)
- Added server-side validation for invoice amounts (S1)
- Sanitized HTML in PDF generation (S2)
- Updated nodemailer to patch CVE
- WCAG AA accessible invoice form and dashboard
- Codebase refactored: duplication 14% โ 3%
/agile-ship-release/agile-ship-release v1.0.0
๐ @devops: Release v1.0.0
Branch: release/v1.0.0 from develop
CI: โ
Green (47 tests, 0 vulnerabilities)
Merged to main โ
Tagged: v1.0.0 โ
Back-merged to develop โ
/agile-ship-deploy/agile-ship-deploy v1.0.0 --env production
๐ @devops: Deployed v1.0.0 to production
Smoke tests:
โ
Tenant registration
โ
User login + JWT
โ
Invoice CRUD (tenant-scoped)
โ
PDF generation (380ms avg)
โ
Payment recording
Health check: โ
Monitoring: 30-minute observation window started
Rollback plan: /agile-ship-rollback v0.0.0 (empty state)
โ Safety net ready if issues emerge during observation
| Metric | Value |
|---|---|
| Sprints completed | 3 |
| Stories completed | 10 (including split stories) |
| Story points delivered | 39 (19 + 13 + 7) |
| Sprint goals met | โ All 3 |
| Security findings fixed | 3 (1 S0, 1 S1, 1 S2) |
| Release version | v1.0.0 |
| Test coverage | 94% (47 tests) |
| Roles involved | All 10: @po, @sm, @arch, @lead, @dev, @qa, @dba, @sec, @ux, @devops |
This project used nearly every command in the framework. Here's the full checklist:
| Category | Command | Used? | Where |
|---|---|---|---|
| Setup | /agile-setup-init | โ | Sprint 0 โ project initialization |
/agile-setup-onboard | โ | Sprint 0 โ coding rules | |
/agile-setup-health | โ | Sprint 0 โ verify setup | |
/agile-setup-doctor | โ | Not needed (no config issues) | |
| Story | /agile-story-create | โ | All sprints โ 6 stories created |
/agile-story-estimate | โ | Sprint planning | |
/agile-story-plan | โ | Every story implementation | |
/agile-story-implement | โ | Execution of implementation plans | |
/agile-story-split | โ | US-002 split into 3 stories | |
/agile-story-dod | โ | Every story completion | |
/agile-story-accept | โ | Every story acceptance | |
/agile-story-reject | โ | Not needed (all stories passed) | |
| Sprint | /agile-sprint-planning | โ | All 3 sprint starts |
/agile-sprint-status | โ | Sprint 2 mid-sprint check | |
/agile-sprint-refine | โ | Before Sprint 2 planning | |
/agile-sprint-review | โ | All 3 sprint ends | |
/agile-sprint-retro | โ | All 3 sprint ends | |
/agile-sprint-cancel | โ | Not needed (no cancelled sprints) | |
| Code | /agile-code-branch | โ | Every story branch |
/agile-code-tdd | โ | Every story + security fixes | |
/agile-code-ci | โ | Every story CI check | |
/agile-code-commit | โ | Every story commit | |
/agile-code-pr | โ | Every story PR | |
/agile-code-pr-review | โ | Every story review | |
/agile-code-merge | โ | Every story merge | |
/agile-code-refactor | โ | Sprint 3 โ codebase cleanup | |
| Code (cont.) | /agile-code-perf | โ | Sprint 2 โ PDF optimization |
/agile-code-debug | โ | Not needed (no runtime bugs) | |
| Explore | /agile-explore-brainstorm | โ | Sprint 0 โ multi-tenant strategy |
/agile-explore-spike | โ | Sprint 2 โ PDF library evaluation | |
/agile-explore-poc | โ | Not needed (spike was sufficient) | |
/agile-explore-audit | โ | Sprint 3 โ architecture health | |
| Security | /agile-security-threat-model | โ | Sprint 0 โ STRIDE analysis |
/agile-security-audit | โ | Sprint 3 โ OWASP Top 10 scan | |
/agile-security-scan | โ | Sprint 3 โ dependency CVE check | |
/agile-security-review | โ | Sprint 3 โ auth + payment review | |
| Ship | /agile-ship-changelog | โ | Release โ 3-sprint changelog |
/agile-ship-release | โ | Release โ v1.0.0 | |
/agile-ship-deploy | โ | Release โ production deploy | |
/agile-ship-hotfix | โ | Not needed (no production incidents) | |
/agile-ship-rollback | โ | Mentioned as safety net | |
| Memory | /agile-memory-remember | โ | Sprint 0 โ architecture decision, Sprint 2 โ PDF choice |
/agile-memory-recall | โ | Referenced across sprints | |
/agile-memory-learn | โ | All 3 sprint retros |
| Role | Key Contribution |
|---|---|
๐ @po | Created 6 stories with tenant-scoped acceptance criteria, split US-002, accepted all stories |
๐ @sm | Managed 3 sprints, tracked velocity trend (19 โ 13 โ 7), facilitated 3 retros with actionable items |
๐๏ธ @arch | Evaluated multi-tenant strategies, recorded ADR, validated architecture health after 3 sprints |
๐จโ๐ป @lead | Caught silent email failure, reviewed tenant isolation code for cross-tenant leaks |
โจ๏ธ @dev | TDD on all stories, PDF performance optimization (3s โ 380ms), security fixes |
๐งช @qa | Verified tenant isolation ("can tenant A see tenant B's data?"), 94% coverage |
๐๏ธ @dba | Designed schema with tenant_id FKs, DECIMAL for money, composite indexes, migration review |
๐ก๏ธ @sec | STRIDE analysis predicted exact vulnerabilities, OWASP audit found IDOR, CVE scan cleared deps |
๐จ @ux | Accessible invoice form (WCAG AA), responsive dashboard, mobile-friendly layout |
๐ @devops | CI pipeline on every story, production deploy with smoke tests + 30-min monitoring window |
You've delivered a production SaaS using the complete framework. From STRIDE threat modeling to PDF performance tuning, from tenant isolation testing to accessible invoice forms โ every role contributed. This is how professional teams ship complex software: structured process, specialized roles, relentless quality gates.
The framework scales from a simple todo API (Project 01) to a multi-tenant SaaS (this project). The core loop never changes โ create stories โ plan sprint โ branch โ TDD โ CI โ PR โ review โ merge โ accept โ release โ but the roles and commands you activate grow with the complexity of what you're building.
Why do STRIDE analysis before Sprint 1, not after?
Why use DECIMAL and not FLOAT for money in the database?
What does the tenant isolation middleware do?