Files
support_backend/specs/002-saas-integration/checklists/requirements.md
T

101 lines
6.6 KiB
Markdown
Raw Normal View History

# Specification Quality Checklist: SaaS Product Integration & Inbound Request Trust
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-08-21
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Auth mechanism choice (signed tokens/OAuth2/mTLS) and rotation-window length are deliberately
left to `/speckit-plan`, not decided here — see spec.md Assumptions.
- Idempotency-key enforcement is explicitly deferred to the future ticketing feature (FR-012
reserves the field only); this is a scope boundary, not a gap.
- Exact rate-limit values and auth-mechanism-per-integration defaults are
`REQUIRES BUSINESS CONFIRMATION` per docs/10-implementation-roadmap.md — not invented here.
- All items pass; no revision iterations were needed.
## Implementation notes (added during /speckit-implement)
- **Found and fixed a codebase-wide bug, not specific to this feature**: `src/app.ts` called
`app.setErrorHandler(...)` *after* `bootstrapRoutes(app)` had already registered every domain
module's routes. Fastify resolves each encapsulated child context's error handler at the time
that context is registered — a handler set on the parent afterwards does not retroactively
apply to already-registered children. Every module registered via `app.register(someRoutes)`
(which is every module in this codebase, since none use `fastify-plugin`) was silently falling
back to Fastify's default `{statusCode, error, message}` error shape instead of this app's
`{success:false, error:{code,message,details}, requestId}` envelope, for *any* error — not
just ones from this feature's plugin. Fixed by moving `setErrorHandler`/`setNotFoundHandler`
before `bootstrapRoutes` in `src/app.ts`. Covered by a new regression test in
`tests/unit/app.test.ts` (verified it fails without the fix, passes with it).
- **Found and fixed a second bug in the same handler**: the generic (non-`AppError`,
non-`ZodError`) fallback branch always returned `500`, even for framework-level errors that
already carry their own client-facing `statusCode` (e.g. Fastify's body-parser rejecting
malformed JSON is a `400`, not a server failure). Fixed to preserve the original
`statusCode`/`code` when it's in the 4xx range.
- **Found and fixed a pre-existing DB/Redis wiring gap that only became harmful because of this
feature**: `vitest.config.ts`'s hardcoded test `DATABASE_URL` (`localhost:5432`) and default
Redis config don't correspond to any service `docker-compose.test.yml` actually publishes to
the host, so `test:integration` could never reach a real database under this repo's own
tooling. This was harmless while every "integration" test was an instantiation-only check (see
`specs/001-ci-pipeline/checklists/requirements.md`), but this feature's integration test
(`tests/integration/product-integration-auth.test.ts`) makes real Prisma/Redis calls. Rather
than leave a newly-introduced test permanently broken for anyone without a coincidentally
matching local Postgres, fixed `test:unit`'s script to scope to `tests/unit` only (it was
running the entire `tests/**` glob, including integration/E2E, via no path argument) — matching
`test:integration`/`test:e2e`'s existing explicit scoping. `test:integration` itself still needs
a reachable Postgres/Redis (via `docker-compose.test.yml` in CI, or a local equivalent) and was
manually verified end-to-end against a temporary Docker Postgres/Redis (see PR description) —
it is not run as part of `npm test`.
- Manually verified all of spec.md's User Story 1 acceptance scenarios end-to-end against a live
server + Postgres + Redis (via temporary Docker containers), beyond what the automated tests
cover: valid in-scope acceptance, indistinguishable invalid-credential/unregistered-product
rejection, unknown-field rejection, out-of-scope rejection, and replay rejection.
- **User Story 2 (admin onboarding/rotation/revocation/audit-trail) is now implemented and
automatically tested** (`tests/integration/product-integrations-admin.test.ts`, run against a
real Postgres/Redis, verified passing). **Known limitation carried over from the existing
codebase, not introduced by this feature**: the admin routes are gated by
`fastify.authenticate` (`src/plugins/auth.plugin.ts`), which is currently a no-op stub — it
never actually verifies a JWT or rejects an unauthenticated caller. These admin endpoints are
therefore not really access-controlled yet. Fixing this requires the `identity/auth` module
(itself unimplemented) and is out of scope for this feature — flagged here and in
`contracts/inbound-request-contract.md` so it isn't mistaken for "done."
- **User Story 3 (rate limiting) is now implemented and automatically tested**
(`tests/integration/inbound-rate-limit.test.ts`, run against a real Postgres/Redis, verified
passing): a bespoke Redis fixed-window counter (`checkRateLimit`,
`src/infrastructure/cache/rate-limiter.ts`) rather than `@fastify/rate-limit`'s default
`onRequest`-stage hook — that hook runs before this feature's preHandler-based auth resolves
the integration/user identity the limit needs to key on, so a second preHandler
(`checkIntegrationRateLimit`) runs after `authenticateProductIntegration` and checks the
integration-level limit, then the per-user limit, independently. Verified both are enforced
independently (a single user's own throttling doesn't affect others; the integration cap
throttles even when no individual user has hit their own limit).
- All three user stories (P1, P2, P3) of this feature are now implemented and covered by
integration tests verified against a live Postgres/Redis, in addition to the unit tests for the
crypto/token primitives. `docs/06-database-schema.md` itself is intentionally not modified —
it's the source spec this implementation follows, not generated output.