Skip to content

Specification: Sending Library (Phase 1)

Driving document for the implementing engineer. Scope: the common-module worktree at projects/invitation-mail-server-worktrees/common-module (branch jmpicnic/invitation-mail-server). Satisfies requirements.md; verified per verification.md. The construct-level blueprint is Design §12 — library tables — this specification sequences it and adds process gates; it does not restate file contents.

Skills to load: kotlin-coding, unit-tests-backend, build-core-modules.

Single worktree, single agent — no task worktrees needed.

Move from operations (source paths in Design §12 — PostmarkEmailSender + PostmarkServerProxy) into lib/src/main/kotlin/cards/arda/common/lib/infra/email/postmark/: wire types (PostmarkSendEmailRequest/Response, PostmarkSendAttachment), SendEmailOutcome, the stateless sendEmail proxy, and fromHeader. Package changes only; behavior byte-identical (REQ-LIB-002, REQ-LIB-007).

Lift means copy into common-module in this phase; deletion of the operations originals happens in Phase 3 (adoption), keeping the two PRs independently mergeable.

Verify: make build compiles; lifted serialization pin (BV-4-04) green.

Create EmailMessage/EmailAttachment, EmailServer, SendRetryPolicy, EmailSenderFactory, EmailSender interface, and PostmarkEmailSender (retry core lifted from operations service/EmailSender.kt, generalized to token-from-EmailServer + optional hooks) per Design §8 Key Elements (REQ-LIB-001, 003–006, 008).

Verify: BV-3-01/02, BV-4-01/02/03, BV-7-02 green with MockEngine.

EmailConfig.fromConfig + lazy emailConfiguration in ConfigurationProvider, mirroring globalDsConfiguration (REQ-LIB-009/010).

Verify: BV-2-01/02 green.

Add ktor-client-mock (testImplementation, existing Ktor version) to gradle/libs.versions.toml. No runtime dependency changes (REQ-LIB-011).

  • Full gate: make build (Kover thresholds), make clqLint.
  • kotlin-coding review pass over every new line (monadic single-exit, unitify(), no redundant construction) — non-negotiable per workspace conventions.
  • CHANGELOG entry under Added (direct-edit model): one bullet, intent and outcome (new email-sending library + config binding).

Present the diff summary, gate results, and CHANGELOG entry. Do not push or open the PR until the user approves. After approval: push, open PR, run /pr-steward. Merge (after approval) publishes 13.2.0 — the Phase 3 gate.

  • All REQ-LIB-001..011 satisfied; BV rows in verification.md implemented and green.
  • No new runtime dependencies; single artifact unchanged.
  • Wire-format golden pin proves serialization identical to pre-lift operations client.
  • Kover ≥ 67% instruction / 73% line; make clqLint green.
  • kotlin-coding review pass done.
  • No includeBuild / absolute-path strays staged.
  • CHANGELOG Added entry present; PR merged; 13.2.0 visible on GitHub Packages.
#QuestionOptionsRecommendationDecision
1Package for fromHeader — with the message model (infra/email) or beside sanitizeHeader (api/headers)?(a) infra/email (b) api/headers(a) — it is email-domain formatting, not header transport hygiene(a) per recommendation; revisit only if a second consumer outside email appears


Copyright: (c) Arda Systems 2025-2026, All rights reserved