Specs in .kiro/specs/*.md describe intent before code. They become a contract: anything outside the spec is drift.

A scoped spec

.kiro/specs/add-rate-limiter.md:

# Add request rate limiter
 
## Goal
 
Limit the public `/api/*` endpoints to 100 requests/minute per IP.
 
## In scope
 
- New middleware in `src/middleware/rate-limit.ts`
- Wire it into the `/api/*` router in `src/routes/api.ts`
- Unit tests covering: under limit, at limit, exceeded
- Use `express-rate-limit` (already in `package.json`)
 
## Out of scope
 
- Authentication changes
- Database schema changes
- Touching files outside `src/middleware/` and `src/routes/`
- Adding new dependencies
- Modifying CI configuration
 
## Acceptance
 
- `pnpm test` passes
- A `429` response includes `Retry-After` header
- No new files outside the In-scope list

Why “out of scope” matters

LLMs love to be helpful. Without an explicit boundary, the agent will “improve” your auth, “modernize” your CI, “fix” lint warnings in unrelated files. Each of those edits is a chance for drift, regression, or hostile injection bleeding into work the spec didn’t authorize.

Reviewing against the spec

When the agent finishes, the diff should map line-for-line to the In-scope list:

git diff --stat
# src/middleware/rate-limit.ts | 42 +++++
# src/routes/api.ts            |  4 +-
# tests/rate-limit.test.ts     | 38 +++++
 
# Anything else? That's drift. Investigate before committing.

Spec-as-review

Specs are reviewable artifacts. Your reviewer can read 200 words of intent in 90 seconds — versus 2000 lines of diff in 20 minutes.

Counters which threats

  • Untrusted execution — off-spec commands stand out
  • Supply chain — “no new dependencies” in the spec catches hallucinated packages
  • Prompt injection — injected instructions to “also do X” violate the scope