Design for review · 08 / 11

Proposed architecture. Pilot products, privacy policy and cost-risk acceptance remain open.

Caching, operations and deployment

Cache register

Allowed staleness below is a proposed technical maximum, subject to PRD review where it affects visible behaviour.

Data and scope TTL Invalidation Allowed staleness and requirement
Hashed static files, public CDN One year immutable New content hash Old file valid for that release; F-12
Public card catalogue, browser memory One hour Catalogue version change or refetch Up to one hour for artwork/name only; F-02
Published rule version, public cache by immutable version One day New version gets new key; active pointer never trusted from stale cache None for version selection; F-05.AC5
Verified business merchant aliases, shared KV One hour Versioned key on reviewed update One hour for readable category only; never authoritative MCC; F-07
Private Home/card/history, TanStack Query in memory 15 seconds stale time Invalidate on command, published revision change, focus and account switch Up to 15 seconds for external intake, with as-of state; corrected facts and pending state update immediately; F-08/F-09
Forwarding tags, session status and deletion markers No shared cache Primary D1 transaction Zero intentional revocation staleness; F-01

No personal data in CDN, KV shared caches, localStorage or service-worker response caches. Clear Query state on sign-out and account switch. The cache key includes account identity, exact selection and revision. Private response headers remain no-store even when client memory caches the result. Refresh while visible every 15 seconds only when work is pending; back off when hidden or idle.

KV may serve stale values across locations; therefore it does not control authorization, revocation or budgets. If cache invalidation fails, immutable version keys prevent stale rules from being selected for a new calculation.

Retry and admission controls

Proposed consumer configuration: at most two retries, three total attempts, concurrency two, small batches of five, explicit acknowledgement after durable commit and a one-day ID-only DLQ. Failed messages never retry forever. No raw email bodies in queue payloads. Maximum job age is checked by consumers even if queue retention is longer. A DLQ replay requires an engineer's scoped command, remaining budget and a valid input version.

Outbox dispatch uses leases with expiry. A scheduled application job claims at most 100 rows per run and at most the approved hourly budget. Every job has a semantic deduplication key. A ten-minute reconciliation pass finds missed dispatches. Cleanup and account deletion have a reserved job budget so stopping intake does not stop privacy work.

Application timers and schedules described here are future product infrastructure, not local agent routines. Ravi implements and tests them in the backend deployment.

Failure behaviour

Failure User outcome Recovery
R2 write fails Capture not confirmed No receipt; test SMTP retry or explicit rejection path
R2 succeeds, D1 receipt fails Capture not confirmed Orphan object expires; bounded sweep deletes it
Queue dispatch fails Setup says pending Outbox retries with the same intake ID
Duplicate delivery One fact Tenant unique key and command receipt
Parser cannot resolve card/currency Review item, excluded from affected totals Explicit user confirmation
Unknown MCC Known amount stays captured; rewards unclear Later evidence triggers revision
Calculation conflicts with a newer correction Pending or last complete result labelled stale Discard staged revision, run newest version
Quota or kill switch Capture paused, no claim of complete spending Preserve history; retry only after operator decision
Rule withdrawn Affected estimate unavailable or pending Reviewed rule replacement and explainable recompute

Observability and targets

Proposed, unmeasured acceptance targets: p95 private read under 500 ms from Singapore after warm-up; accepted alert visible or explicitly held within 60 seconds; no projection/fact mismatch across corrections. Measure with real deployed bindings and redacted fixtures. Do not report these as achieved.

Metrics include intake accepted/rejected/held, duplicate candidates, oldest outbox age, queue attempts, DLQ count, stale revision age, CPU, D1 rows scanned/written, R2 bytes, daily AI allowance and deletion deadline breaches. Alerts fire on sustained queue age, any deletion deadline miss, quota reservation failure or usage outside the pilot envelope. Logs use opaque correlation IDs and reason codes only.

Environments and release

Testing and production have distinct D1, R2, queues, secrets, forwarding domains/tags and Google OAuth configuration. Testing uses synthetic or explicitly redacted fixtures, never copied production data.

Ravi owns backend CI/CD, protection and environments; Mira owns frontend CI/CD. Main requires backend-ci and frontend-ci, including Buf compatibility checks. The shared GitHub identity means no required GitHub approval count; Kai records CTO review (Kai): APPROVED and alone merges after all must-fix items close. Main deploys testing only. Production requires Tester sign-off, Kai approval and a release tag. Kai provisions separate scoped CI tokens as environment secrets.

Deploy compatible backend before frontend. Expand schema first. Smoke-test the previous client against the new backend. Roll back application releases without dropping new columns. A bad rule version is withdrawn through a new reviewed version, never edited in place. Restore drills verify reconciliation and deletion replay before production enablement.