Understanding Output
Learn to read contracts, test results, and agent reports.
Table of contents
- What Specflow Generates
- 1. Understanding Contracts
- 2. Understanding Contract Tests
- 3. Understanding E2E Tests
- 4. Understanding Agent Reports
- 5. Reading Build Failures
- 6. Visualization Gallery
- Compiler Analogy Recap
- Next Steps
What Specflow Generates
When waves-controller executes, you get four types of output:
- Contracts (YAML files) — Architectural rules
- Contract Tests (TypeScript/JavaScript) — Enforce contracts via static analysis
- E2E Tests (Playwright) — Enforce journeys via browser automation
- Implementation Code (Your stack) — Feature code that satisfies contracts
Think of it like TypeScript:
- Contracts = Type definitions
- Contract tests = Type checker
- E2E tests = Runtime assertions
- Implementation = The actual code
1. Understanding Contracts
Contracts live in docs/contracts/*.yml.
Feature Contract Example
File: docs/contracts/feature_authentication.yml
contract_type: feature
feature_name: authentication
description: User authentication with email/password and JWT tokens
invariants:
- id: AUTH-001
rule: "Passwords MUST be hashed using bcrypt before storage"
severity: critical
enforcement: contract_test
test_file: src/__tests__/contracts/auth.test.ts
- id: AUTH-002
rule: "JWT tokens MUST expire after 24 hours"
severity: critical
enforcement: e2e_test
test_file: tests/e2e/journey_auth.spec.ts
- id: AUTH-003
rule: "Failed login attempts MUST be rate limited (5 per minute)"
severity: important
enforcement: contract_test
protected_files:
- path: src/features/auth/signup.ts
reason: "Password hashing logic"
- path: src/features/auth/login.ts
reason: "JWT token generation"
compliance_checklist:
- item: "All password fields use bcrypt.hash()"
required: true
- item: "JWT_EXPIRY set to 24h in config"
required: true
- item: "Rate limiting middleware on /auth/* routes"
required: true
How to read this:
| Field | Meaning | Example |
|---|---|---|
contract_type |
Feature or Journey | feature (architectural rule) |
feature_name |
What area this covers | authentication |
invariants[].id |
Unique rule identifier | AUTH-001 |
invariants[].rule |
What MUST hold true | “Passwords MUST be hashed” |
invariants[].severity |
How critical | critical (blocks release) |
invariants[].enforcement |
How verified | contract_test or e2e_test |
protected_files |
Files this contract watches | src/features/auth/signup.ts |
Critical vs Important vs Future:
- Critical: MUST pass before release (blocks merge if failing)
- Important: SHOULD pass before release (warning if failing)
- Future: CAN fail without blocking (aspirational)
Journey Contract Example
File: docs/contracts/journey_user_signup.yml
contract_type: journey
journey_name: user_signup
description: User can create account and access dashboard
preconditions:
- "Database is empty (no existing user with test email)"
- "User is logged out"
steps:
- step: 1
action: "Navigate to /signup"
expected: "Signup form visible"
- step: 2
action: "Fill email and password, submit form"
expected: "Loading state shown"
- step: 3
action: "Wait for redirect"
expected: "Redirected to /dashboard"
- step: 4
action: "Check welcome message"
expected: "User email displayed in welcome message"
postconditions:
- "users table contains new row with hashed password"
- "User session exists in database"
- "JWT token stored in localStorage"
dod_criticality: critical
test_file: tests/e2e/journey_user_signup.spec.ts
How to read this:
| Field | Meaning |
|---|---|
journey_name |
Unique identifier for this workflow |
preconditions |
State that MUST exist before test runs |
steps[] |
Ordered user actions and expectations |
postconditions |
State that MUST exist after test completes |
dod_criticality |
Release gating: critical, important, future |
test_file |
Where the Playwright test lives |
Definition of Done (DoD):
- Critical journeys MUST pass before release
- Important journeys SHOULD pass (warning if failing)
- Future journeys are aspirational (can fail without blocking)
2. Understanding Contract Tests
Contract tests scan your code for patterns. They run on every commit.
Example Contract Test
File: src/__tests__/contracts/auth.test.ts
import { describe, it, expect } from 'vitest'
import { glob } from 'glob'
import fs from 'fs'
describe('Contract: AUTH-001 - Password hashing', () => {
it('All password storage uses bcrypt.hash()', async () => {
// Find all files in auth feature
const files = await glob('src/features/auth/**/*.ts')
for (const file of files) {
const content = fs.readFileSync(file, 'utf-8')
// Check for password storage without hashing
if (content.includes('password:') && !content.includes('bcrypt.hash')) {
throw new Error(`
❌ CONTRACT VIOLATION: AUTH-001
File: ${file}
Rule: Passwords MUST be hashed using bcrypt before storage
Found: password field without bcrypt.hash()
See: docs/contracts/feature_authentication.yml
`)
}
}
})
})
What it does:
- Scans all files in
src/features/auth/ - Looks for pattern:
password:withoutbcrypt.hash - Fails with
CONTRACT VIOLATIONif found
Think of it like ESLint, but for architecture.
Running Contract Tests
# Run all contract tests
npm test -- contracts
# Output (passing):
✓ Contract: AUTH-001 - Password hashing (23ms)
✓ Contract: AUTH-002 - JWT expiry (12ms)
✓ Contract: AUTH-003 - Rate limiting (18ms)
3 passed, 0 failed
# Output (failing):
❌ Contract: AUTH-001 - Password hashing (45ms)
CONTRACT VIOLATION: AUTH-001
File: src/features/auth/signup.ts:23
Rule: Passwords MUST be hashed using bcrypt before storage
Found: password field without bcrypt.hash()
Expected:
password_hash: await bcrypt.hash(password, 10)
Actual:
password: password
See: docs/contracts/feature_authentication.yml
FAIL: 1 failed, 2 passed
The build is now blocked until you fix the violation.
3. Understanding E2E Tests
E2E tests run your app in a real browser. They verify journeys work end-to-end.
Example E2E Test (Generated by Playwright-from-Specflow)
File: tests/e2e/journey_user_signup.spec.ts
import { test, expect } from '@playwright/test'
test.describe('J-USER-SIGNUP: User can sign up and access dashboard', () => {
test.beforeEach(async ({ page }) => {
// Precondition: Ensure test user doesn't exist
await page.goto('/api/test/cleanup')
})
test('User can sign up with valid credentials', async ({ page }) => {
// Step 1: Navigate to signup
await page.goto('/signup')
await expect(page.locator('[data-testid="signup-form"]')).toBeVisible()
// Step 2: Fill form and submit
await page.fill('[data-testid="email-input"]', 'test@example.com')
await page.fill('[data-testid="password-input"]', 'SecurePass123!')
await page.click('[data-testid="signup-btn"]')
// Step 3: Wait for redirect
await page.waitForURL('/dashboard', { timeout: 5000 })
// Step 4: Verify welcome message
const welcome = page.locator('[data-testid="welcome-msg"]')
await expect(welcome).toContainText('test@example.com')
})
test.afterEach(async ({ page, browserName }) => {
// Postcondition: Verify database state
const response = await page.goto('/api/test/verify-user')
const data = await response.json()
expect(data.user_exists).toBe(true)
expect(data.password_is_hashed).toBe(true) // bcrypt hash detected
expect(data.session_exists).toBe(true)
})
})
How to read this:
| Section | Purpose | Maps to Contract |
|---|---|---|
beforeEach |
Set up preconditions | journey.preconditions |
test('...') |
Execute journey steps | journey.steps[] |
data-testid selectors |
Find UI elements reliably | Specified in GitHub issue |
afterEach |
Verify postconditions | journey.postconditions |
Running E2E Tests
# Run all E2E tests
npm run test:e2e
# Output (passing):
Running 3 tests in journey_user_signup.spec.ts
✓ J-USER-SIGNUP: User can sign up with valid credentials (2.3s)
✓ J-USER-LOGIN: User can log in with credentials (1.8s)
✓ J-USER-LOGOUT: User can log out (1.2s)
3 passed (5.3s)
# Output (failing):
Running 1 test in journey_user_signup.spec.ts
✗ J-USER-SIGNUP: User can sign up with valid credentials (5.1s)
Error: Timeout 5000ms exceeded waiting for URL '/dashboard'
Expected: /dashboard
Actual: /signup (still on same page)
Possible causes:
- Form submission not triggering redirect
- Backend signup endpoint returning error
- Frontend not handling success response
Screenshot: test-results/signup-failure.png
Video: test-results/signup-failure.webm
FAIL: 1 failed, 2 passed
The build is blocked until the journey works end-to-end.
4. Understanding Agent Reports
When waves-controller finishes, each agent reports what it did.
Specflow-Writer Report
📝 SPECFLOW-WRITER REPORT
Issue: #42 "Add user authentication"
Generated Contracts:
✓ docs/contracts/feature_authentication.yml
- 4 invariants defined (AUTH-001 to AUTH-004)
- 2 protected files
- 3 compliance checklist items
✓ docs/contracts/journey_user_signup.yml
- 4 steps defined
- 3 preconditions
- 3 postconditions
- DoD: critical
GitHub Issue Updates:
✓ Added "Contracts Created" comment to #42
✓ Labeled issue with "specflow-ready"
Next Agent: migration-builder (if database changes needed)
Migration-Builder Report
🗄️ MIGRATION-BUILDER REPORT
Issue: #42 "Add user authentication"
Database Changes Needed: YES
Generated Files:
✓ supabase/migrations/20260203_create_users_table.sql
- CREATE TABLE users (id, email, password_hash, created_at)
- RLS policies: users can read/update own row
- Indexes: email (unique), created_at
Analysis:
- No foreign key dependencies
- Estimated migration time: <1s
- RLS coverage: 100%
Contract Compliance:
✓ AUTH-001: password_hash column (bcrypt compatible)
✓ ARCH-004: RLS enabled on users table
Next Agent: edge-function-builder (check if RPC needed)
Playwright-from-Specflow Report
🎭 PLAYWRIGHT-FROM-SPECFLOW REPORT
Issue: #42 "Add user authentication"
Source Contract: docs/contracts/journey_user_signup.yml
Generated Tests:
✓ tests/e2e/journey_user_signup.spec.ts
- 4 test steps from contract
- 3 preconditions in beforeEach
- 3 postconditions in afterEach
- data-testid selectors: 4 (from issue spec)
Test Status:
⚠️ Tests not yet run (implementation incomplete)
To run: npm run test:e2e -- journey_user_signup
Estimated Coverage:
- Signup flow: ✓
- Login flow: ✓
- Logout flow: ✓
- Error cases: ⚠️ (add manually if needed)
Next Agent: test-runner (verify tests pass after implementation)
Test-Runner Report
🧪 TEST-RUNNER REPORT
Execution Summary:
Contract Tests: ✓ 12/12 passed
E2E Tests: ✗ 1/3 failed
Failed Tests:
❌ J-USER-SIGNUP: User can sign up with valid credentials
Error: Timeout waiting for [data-testid="dashboard"]
Screenshot: test-results/signup-failure.png
Root cause analysis:
- Backend signup endpoint (/api/auth/signup) returns 500
- Error: "bcrypt is not defined"
- Fix: Install bcrypt package (npm install bcrypt)
Passing Tests:
✓ AUTH-001: Password hashing check
✓ AUTH-002: JWT expiry check
✓ AUTH-003: Rate limiting check
... (9 more)
Recommended Action:
1. Install bcrypt: npm install bcrypt
2. Re-run tests: npm run test:e2e
3. If passing, close issue #42
Issue Status: BLOCKED (1 test failing)
5. Reading Build Failures
When Specflow blocks a build, you’ll see clear output:
Contract Violation Example
$ npm test -- contracts
❌ CONTRACT VIOLATION DETECTED
Contract: AUTH-001
File: src/features/auth/signup.ts
Line: 23
Rule: Passwords MUST be hashed using bcrypt before storage
Severity: CRITICAL (blocks release)
Current code:
22 | const user = {
23 | password: password, // ❌ Plaintext password
24 | }
Expected:
22 | const user = {
23 | password_hash: await bcrypt.hash(password, 10), // ✅ Hashed
24 | }
Why this matters:
Storing passwords in plaintext violates security best practices.
If the database is compromised, all user passwords are exposed.
To fix:
1. Import bcrypt: import bcrypt from 'bcrypt'
2. Hash password before storage
3. Update column name to password_hash (matches migration)
Reference: docs/contracts/feature_authentication.yml
Build failed. Fix violation to continue.
E2E Failure Example
$ npm run test:e2e
❌ E2E TEST FAILED
Journey: J-USER-SIGNUP
File: tests/e2e/journey_user_signup.spec.ts
Step: "Wait for redirect to /dashboard"
Error:
Timeout 5000ms exceeded waiting for URL '/dashboard'
Expected URL: /dashboard
Actual URL: /signup
Form submission did not trigger redirect.
Debug artifacts:
Screenshot: test-results/signup-5000ms.png
Video: test-results/signup.webm
Trace: test-results/trace.zip
Possible causes:
1. Backend /api/auth/signup endpoint failing (check logs)
2. Frontend not handling success response (check network tab)
3. Redirect logic not implemented (check SignupPage.tsx)
To debug:
npm run test:e2e:ui # Open Playwright UI
# Or manually test: curl -X POST http://localhost:3000/api/auth/signup
Build failed. Fix journey to continue.
6. Visualization Gallery
When waves-controller executes, it renders 5 mandatory ASCII visualizations at specific phases. Use /specflow status to render all 5 on demand at any point.
EXECUTION TIMELINE (Phase 1, Phase 8)
Shows where you are, what’s done, what’s next:
EXECUTION TIMELINE
═══════════════════════════════════════════════════════════════
START NOW
| |
[════ Wave 1 ════][════ Wave 2 ════][═ Wave 3 ══>
Commit a1b2c3d Commit e4f5g6h (active)
#50, #53 #51, #54 #52, #55
Wave 1: 2 issues COMPLETE Contracts: 2 Tests: 6
Wave 2: 2 issues COMPLETE Contracts: 2 Tests: 4
Wave 3: 2 issues ACTIVE Contracts: 1 Tests: 2 (pending)
Closed: 4/6 issues | Elapsed: 45 min | Est remaining: 1 wave
═══════════════════════════════════════════════════════════════
ENFORCEMENT MAP (Phase 1 per wave)
The key visualization – shows exactly what gets tested, by what mechanism, and when:
ENFORCEMENT MAP -- Wave 3
═══════════════════════════════════════════════════════════════
Issue #52: Billing Integration
├─ CONTRACT TESTS (build-time, pattern scan):
│ ├─ SEC-001: No hardcoded Stripe keys → src/billing/**
│ ├─ SEC-002: No SQL concatenation → src/billing/**
│ ├─ BILL-001: Must use paymentMiddleware → src/routes/billing*
│ └─ BILL-002: Amounts must use Decimal → src/billing/**
│
└─ PLAYWRIGHT TESTS (post-build, E2E):
├─ J-BILLING-CHECKOUT: User completes checkout flow
├─ J-BILLING-CANCEL: User cancels subscription
└─ J-BILLING-INVOICE: User views invoice history
Issue #55: Invoice PDF Export
├─ CONTRACT TESTS:
│ ├─ SEC-005: No path traversal in export → src/export/**
│ └─ INV-001: Must sanitize filenames → src/export/**
│
└─ PLAYWRIGHT TESTS:
└─ J-BILLING-INVOICE: (shared with #52)
TOTALS: 6 contract rules enforced | 4 journey tests | 2 issues
═══════════════════════════════════════════════════════════════
DEPENDENCY TREE (Phase 1 per wave)
Shows execution order and what blocks what:
DEPENDENCY TREE
═══════════════════════════════════════════════════════════════
#50 User Profile [P:18] ─── Wave 1
├──▶ #51 Profile Settings [P:22] ─── Wave 2
│ └──▶ #52 Notifications [P:15] ─── Wave 3
└──▶ #54 Profile Analytics [P:12] ─── Wave 2
#53 Admin Dashboard [P:15] ─── Wave 1 (independent)
#48 Payments [P:25] ─── Wave 1
├──▶ #55 Billing History [P:14] ─── Wave 2
└──▶ #56 Invoices [P:11] ─── Wave 3
└──▶ #57 PDF Export [P:8] ─── Wave 4
Legend: [P:N] = priority score | ──▶ = blocks
Parallel: Wave 1 runs #50, #53, #48 simultaneously
═══════════════════════════════════════════════════════════════
PARALLEL AGENT MODEL (Phase 4)
Shows who is working on what right now:
WAVE 3 EXECUTION -- Team Brigid's Forge
═══════════════════════════════════════════════════════════════
┌─────────────────┐ ┌─────────────────┐
│ Heaney (#52) │ │ Goibniu (#55) │
│ Billing Integ. │ │ Invoice Export │
│ │ │ │
│ [spec] done │ │ [spec] done │
│ [contract] done │ │ [contract] done │
│ [build] ██░░░░ │ │ [build] done │
│ [test] pending │ │ [test] ██░░░░ │
└─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐
│ Hamilton │ │ Keane │
│ db-coordinator │ │ quality-gate │
│ │ │ │
│ Migrations: 2 │ │ Contracts: PASS │
│ Conflicts: 0 │ │ E2E: pending │
└─────────────────┘ └─────────────────┘
Active: 4 agents | Files touched: 12 | Dependencies: 1/2 resolved
═══════════════════════════════════════════════════════════════
SPRINT SUMMARY TABLE (Phase 8)
Running total across all completed waves:
SPRINT SUMMARY
═══════════════════════════════════════════════════════════════
Wave │ Team │ Issues │ Files │ LOC │ Key Outputs
─────┼────────────────┼───────────┼───────┼───────────┼──────────────────
1 │ Fianna │ #50,#53,#48│ 15 │ +847/-23 │ Auth, admin, payments
2 │ Red Branch │ #51,#54,#55│ 12 │ +612/-31 │ Settings, analytics, billing
3 │ Brigid's Forge │ #52,#55 │ 8 │ +404/-12 │ Notifications, invoices
─────┼────────────────┼───────────┼───────┼───────────┼──────────────────
TOTAL│ │ 8 issues │ 35 │ +1863/-66 │ 8 contracts, 14 tests
Contracts: 8 generated, 8 passing
Tests: 14 (6 contract, 8 Playwright) -- all green
Duration: 1h 12m (sequential estimate: 3h 40m → 67% time saved)
═══════════════════════════════════════════════════════════════
Compiler Analogy Recap
| TypeScript | Specflow |
|---|---|
Type definitions (.d.ts) |
Contracts (.yml) |
Type checker (tsc) |
Contract tests (vitest) |
| Runtime assertions | E2E tests (playwright) |
Type 'string' is not assignable to 'number' |
CONTRACT VIOLATION: AUTH-001 |
| Build blocked until fixed | Build blocked until fixed |
Same enforcement model. Different domain.
Next Steps
Now that you understand output:
- Explore Core Concepts – Dive deeper into contracts, agents, journeys
- Browse Agent System – Learn about the 23+ agent types
- Read Background – Understand the academic foundation