/speckit-tasks output for 003-ticketing: 39 tasks across 6 phases.
MVP scope is Setup+Foundational+US1 (T001-T019) -- every trusted
inbound request producing a real, durable, idempotent,
concurrency-safe ticket, before messages or attachments exist.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-plan output for 003-ticketing: technical context and
constitution gate check (all PASS), Phase 0 research (9 decisions:
ticket code format, explicit 12-state lifecycle transition table,
optimistic concurrency via version column, idempotency-key upsert
reusing 002's CustomerReference pattern, explicit-reference-only
recurring-problem linking, config-driven message visibility mapping,
presigned-PUT attachment pipeline, a fail-closed placeholder malware
scanner since none exists in this stack, and adding MinIO to Docker
Compose for local/test S3-compatible storage), Phase 1 data model
(Ticket/Problem/TicketMessage/TicketAttachment plus the inbound
request -> ticket creation behavior), the lifecycle/messages/
attachments contract, and a 6-scenario quickstart.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-specify output for 003-ticketing (roadmap Phase 5): 3 user
stories (immediate ticket/problem creation with idempotency, typed
messages with enforced internal-note privacy, secure attachment
pipeline) and 15 functional requirements. Explicitly scoped to Phase
5 only -- investigation/root-cause/solution/resolution (Phase 9) and
AI diagnosis (Phase 4) are out of scope. Quality checklist passes
with no NEEDS CLARIFICATION markers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements tasks T026-T032 from specs/002-saas-integration/tasks.md
(User Story 3, P3 - the final piece of this feature) plus Polish.
- New 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. A second
preHandler (checkIntegrationRateLimit) runs after
authenticateProductIntegration on the inbound route, checking the
integration-level limit then the per-user limit independently,
each throwing the existing RateLimitError (429
RATE_LIMIT_EXCEEDED) on breach.
- New integration test (inbound-rate-limit.test.ts) verifies both
limits are enforced independently against a real Postgres/Redis:
a throttled user doesn't affect others, and the integration cap
throttles even when no individual user has hit their own limit.
- Docs: contracts/quickstart updated from the placeholder
"RATE_LIMITED" code to the actual reused RATE_LIMIT_EXCEEDED code;
cleaned up a duplicated paragraph in the admin endpoints section;
added a "SaaS Integration" section to README.md documenting the
inbound contract, admin routes (and their known auth-stub
limitation), and how rate limits are configured.
All 32 tasks in tasks.md are now complete -- all three user stories
(P1 trust boundary, P2 admin lifecycle, P3 rate limiting) are
implemented and covered by integration tests verified against a
live database, in addition to unit tests for the crypto/token
primitives. Full quality gate (typecheck/lint/format/architecture/
unit tests) passes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements tasks T021-T025 from specs/002-saas-integration/tasks.md
(User Story 2, P2): an admin can register, rotate, revoke, and change
the status of a ProductIntegration, and retrieve its audit trail.
- ProductIntegrationsService: register (finds-or-creates the Product
by external id), rotate (dual-credential transition window per
research.md), revoke, updateStatus, getAuditTrail -- each writes
its own AuditLog entry via a new shared
integration-audit-log.repository.ts (extracted from the auth
plugin, which now reuses it instead of writing to Prisma directly).
- Routes: POST /admin/products/:externalProductId/integration,
POST/admin/integrations/:id/rotate|revoke, PATCH .../status,
GET .../audit-trail -- gated by the existing fastify.authenticate
(human/admin JWT) decorator.
- New integration test (product-integrations-admin.test.ts) covers
Quickstart Scenarios 5-7 end-to-end against a real Postgres/Redis:
register+rotate+audit-trail, and revoke-takes-effect-immediately.
Verified passing against a live database.
Known, pre-existing limitation flagged (not fixed here, out of
scope): fastify.authenticate is currently a no-op stub with no real
JWT verification, so these admin endpoints aren't actually
access-controlled yet -- that depends on the unimplemented
identity/auth module. Documented in the contract and checklist notes
so it isn't mistaken for done.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements tasks T001-T020 from specs/002-saas-integration/tasks.md
(Setup, Foundational, and User Story 1 - the P1 MVP: every inbound
request is authenticated and trusted before anything happens).
User Story 2 (admin onboarding/rotation/revocation) and User Story 3
(rate limiting) are not yet implemented (T021-T032 remain).
Schema (prisma/schema.prisma + initial migration):
- Replace the placeholder Product model (leftover starter-template
scaffolding: code/description/ProductStatus enum) with the real
docs/06-database-schema.md shape (externalProductId,
supportEnabled, status).
- Add ProductIntegration (credential ref, rotation/revocation state,
allowed scope, per-integration/per-user rate limits) and
CustomerReference models.
- Align AuditLog to docs/06's shape (actor/actorType/entityType/
entityId/reason/metadata) -- the placeholder shape had no fields
to satisfy this feature's audit requirements.
Auth:
- HMAC-signed short-lived tokens (issue/verify) with jti-based replay
defense via Redis and a bounded clock-skew tolerance.
- Credential secrets are AES-256-GCM encrypted at rest (new required
INTEGRATION_CREDENTIAL_ENCRYPTION_KEY env var) since no secret
manager exists in this stack yet -- see research.md "Credential
storage".
- New product-integration-auth.plugin.ts Fastify plugin runs the
validation order in contracts/inbound-request-contract.md and
populates request.reqContext only on full success; every attempt
(success or failure) is audit-logged without ever persisting the
raw token/credential. Unregistered product and invalid credential
return an identical response (FR-010).
- New POST /v1/support/requests endpoint exercises the boundary
end-to-end (ticket creation itself is a future feature).
Also:
- Fix docker-compose.test.yml's container_name collisions --
discovered while testing this change concurrently is now covered
by an app-level regression test (separate commit).
- Fix test:unit to scope to tests/unit only (it was running the
entire tests/** glob including integration tests) -- this feature's
new integration test makes real Prisma/Redis calls, unlike the
prior instantiation-only checks, so the existing glob-scoping gap
became actually harmful.
- Update Jenkinsfile with the new required credential.
Verified: full quality gate (typecheck/lint/format/architecture/
unit tests) passes; all of User Story 1's quickstart scenarios
manually verified end-to-end against a live server + Postgres +
Redis; the new integration test suite verified against a live
database (not run as part of `npm test`, matches existing
test:integration convention).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fastify resolves each encapsulated child context's error handler at
the time that context is registered. app.ts called
app.setErrorHandler()/setNotFoundHandler() AFTER bootstrapRoutes()
had already registered every domain module's routes (each
app.register(someRoutes) call creates its own child context, since
none of the route modules use fastify-plugin). A handler set on the
parent afterwards does not retroactively apply to already-registered
children, so every module's routes were 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 thrown error -- not specific to any one
feature. Discovered while building and manually verifying the
002-saas-integration feature's inbound endpoint.
Also fixed the generic error-handler branch to preserve a framework
error's own client-facing statusCode (e.g. 400 for malformed JSON)
instead of always reporting 500.
Added a regression test in tests/unit/app.test.ts that fails without
this fix and passes with it (verified both ways).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-tasks output for 002-saas-integration: 32 tasks across 6
phases. Unlike 001-ci-pipeline, this feature includes test tasks as
first-class (not optional) since it's a security boundary. MVP scope
is Setup+Foundational+US1 (T001-T020) - the inbound trust boundary
alone, before admin lifecycle tooling or rate limiting exist.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-specify output for 002-saas-integration (roadmap Phase 2):
3 user stories (authenticate/validate inbound requests, admin
onboarding/rotation/revocation, rate limiting) and 12 functional
requirements. Reserves an idempotency-key field on the inbound
contract for the future ticketing feature (docs/11 gap A1) without
implementing dedup here. Quality checklist passes with no
NEEDS CLARIFICATION markers.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Production concerns not covered by the original spec (00-10):
idempotency on ticket creation, bi-directional webhook callbacks,
row-level-security tenant isolation, AI prompt-injection defense,
optimistic concurrency on shared mutable state, RAG implementation
specifics, AI cost/token governance, knowledge effectiveness
feedback, CSAT capture, data retention/PII, API versioning/error
contract, localization, and a lower-urgency list. Indexed in
docs/00-INDEX.md as doc 11.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Implements tasks T001-T016, T018-T022, T024-T026 from
specs/001-ci-pipeline/tasks.md (T017/T023 need a real Jenkins
instance to verify and are left for manual follow-up).
- Add Jenkinsfile: checkout -> install -> environment validation
-> typecheck -> lint (+ architecture check) -> format check ->
unit -> integration -> E2E -> build -> Docker build -> publish
-> deploy, matching the constitution's required stage order.
Secrets are always injected from Jenkins credentials at runtime,
never read from a repo-committed file. Publish/Deploy are skipped
(not failed) on branches with no resolved deploy target.
- Fix docker-compose.test.yml: remove fixed container_name on
app/postgres/redis, which would have made concurrent CI runs
collide (FR-009). Verified locally that two runs under different
-p project names no longer share container/volume/network names.
- Document the pipeline and local .env setup in README.md.
- Mark completed tasks in specs/001-ci-pipeline/tasks.md and record
the container_name/compose-down-env-file findings in the spec's
requirements checklist notes.
Locally verified passing: Dockerfile build, typecheck, lint,
architecture check, format check, unit test suite, and the edited
docker-compose.test.yml bringing up postgres/redis with isolated
per-project container names.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-tasks output for 001-ci-pipeline: 26 tasks across 5 phases
(Setup, Foundational, US1 validate/build, US2 publish/deploy, Polish),
with the MVP scope being US1 alone (T001-T017).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
/speckit-specify + /speckit-plan output for 001-ci-pipeline: feature
spec with 2 user stories and 10 functional requirements, requirements
quality checklist, implementation plan with constitution gate check,
Phase 0 research (6 decisions incl. secrets-from-credentials-store),
Phase 1 data model, pipeline stage contract, and a 5-scenario
quickstart validation guide. No Jenkinsfile yet — that's the
implementation step after /speckit-tasks.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Full system blueprint (docs 01-10): product vision, integration &
security, AI support architecture, ticketing & problem management,
orchestration/SLA/escalation, database schema, backend/frontend
architecture, testing/observability/CI-CD, and the implementation
roadmap. This is the pre-implementation design reference the codebase
is being built against.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
.env.development had a real Postgres/Redis password and JWT secret
committed in plain text; .env.test and .env.prod were tracked too.
Untrack all .env* files going forward and add .env.example as the
onboarding template instead.
Note: the exposed dev credentials are still in git history and must
be rotated separately.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>