Feat/s3 storage #1
Reference in New Issue
Block a user
Delete Branch "feat/s3-storage"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
renderTemplate(raw, vars) → {subject, html, text}. HTML body auto-escaped (customer names into email = XSS risk); subject/text verbatim. Throws on a missing declared variable — never send a half-rendered receipt. 5 tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>TemplateService.render(source, vars, opts) → { content, provenance }. Stored key → resolve+render; inline → render without a DB row (key/version null); explicit version pins. Provenance = {templateKey, version, locale, sha256(content)}. 10 tests (svc + repo). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>- IiosOutboundCommand gains templateKey/version/locale/renderedHash (migration). - OutboundService.send accepts an optional opaque `provenance` and writes it into the command on BOTH the PENDING and RATE_LIMITED create paths — the only way to stamp provenance by construction, since send() creates the row itself (review finding). OutboundService still neither renders nor resolves templates. - TemplatedSender.sendTemplated: render -> OutboundService.send -> provenance, one entry point. EMAIL payload = {subject,html,text}; SMS = {text}. Idempotent per key. Tests: 4 sender + 16 adapters regression green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>An email/SMS command holds PII: the recipient address (target) and the rendered body (payload). RetentionService now snapshots outbound commands and, once aged, redacts target->'[redacted]' and payload->{redacted:true} in place while KEEPING the template provenance (key/version/locale/hash) — so 'which template version did we send?' stays answerable after the PII is gone. - ensureOutboundSnapshots: one snapshot per command, dataClass 'outbound', archiveAfter==deleteAfter (PII goes straight to redact, no archive phase). Nullable command scope uses an 'unscoped' tag so the global sweep still reaches it. - applySweep redact branch switches on targetType; compliance holds honored; audit 'retention.redacted' resourceType 'outbound_command'. Closes lever #2 of the PII-minimization plan. 3 new + 6 regression tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Makes external email actually leave the building (was sandbox-only). SmtpProvider implements CapabilityProvider; env-activated (accounts@ primary, ceo@ fallback); transporter injected for tests. - send() maps target+payload -> {from,to,subject,html,text,inReplyTo,references}; providerRef = nodemailer's real Message-ID (so replies thread via In-Reply-To). - Fallback ONLY on pre-acceptance failures (connect/auth/timeout) — a post-acceptance error is terminal, so a message the server already took can't be double-delivered. - Never throws — transport failure -> FAILED, per the adapter doctrine. - Registry precedence via registration order: SMTP > HTTP relay > sandbox for EMAIL. Verified: 13 unit tests (config/envelope/fallback/precedence) + a REAL SMTP round-trip against nodemailer Ethereal (SENT, genuine Message-ID). Full suite 281/281, boundary+build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>Email can now carry attachments (e.g. an invoice PDF). The kernel already stored attachments as message parts + the media StoragePort holds the bytes; the gap was the email envelope. - EMAIL payload carries attachment REFS ({filename, contentRef, mimeType}), not bytes — the ledger + T8 PII redaction stay small; bytes are fetched at send time. - SmtpProvider takes an AttachmentResolver; resolves each ref via storage and attaches (nodemailer). FAILS CLOSED if a declared attachment can't be resolved (or no resolver is wired) — never send a receipt/invoice missing its file; a FAILED command retries. - CapabilityProviderRegistry injects the resolver from STORAGE_PORT (@Optional); MediaModule exports STORAGE_PORT, CapabilityModule imports MediaModule. No cycle. - TemplatedSender + MailService + the /v1/mail/send DTO pass attachments through. Verified: 6 new unit tests (attach, fail-closed x2, plain-unaffected, resolver, pass-through) + a REAL Ethereal SMTP send WITH a PDF attachment (SENT). Full suite 294/294, boundary + build clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>