Translation. Authoritative:../../design/decision-registry-v1.md. Design source of truth. Closes questionnaire v1 (50 questions) and debate rounds 1–4 (August 2026). Drafts v0.1–v0.3 remain design narrative; this registry is what the constitution, the standards, the ADRs and the specs cite. Real pending items: P-1G1-Engage dateclosed · P-2 lawyer for the legal package · P-3 unit prices (commercial exercise, does not block design).
A. Identity and contacts
| ID | Decision |
|---|---|
| DEC-A1 | contact_kind = person | company. Every contact may be a natural person or a legal entity. Companies: legal_name, no birth_date; date triggers apply per kind. |
| DEC-A2 | core.national_id_types v1 catalog: EVERY South American country (CL: RUT · AR: DNI/CUIT/CUIL · BR: CPF/CNPJ · UY: CI/RUT · PY: CI/RUC · BO: CI/NIT · PE: DNI/RUC/CE · EC: CI/RUC · CO: CC/CE/NIT · VE: CI/RIF · GY/SR: generic) + passport + foreign_id. Each type: country, applicable kind, format regex, normalizer, check-digit algorithm where one exists. |
| DEC-A3 | Validation: format and normalization ALWAYS (rejected if it does not comply); check digit where an algorithm exists, with a dv_validated flag. |
| DEC-A4 | national_id protection: masked by default in the UI, dedicated RBAC permission for the full value, audited access in core.audit_log. The data-classification standard applies the strictest law among supported countries (LGPD + Ley 21.719 as the floor). Column encryption: evaluable if an enterprise customer requires it. |
| DEC-A5 | national_id optional by default; requirable by tenant configuration. |
| DEC-A6 | Duplicates (tenant, type, id): rejected on direct creation; on bulk import → a “suggested merge” queue with human review. Uniqueness: partial unique index per tenant. The same document across different tenants: always allowed. |
B. Data model and standards
| ID | Decision |
|---|---|
| DEC-B1 | Grouping by Postgres schemas: core.*, loyalty.*, messaging.*, crm.* (via Drizzle’s pgSchema). Extended by ADR-022, which adds identity. |
| DEC-B2 | Business enums = parametric tables ALWAYS. Standard columns: code (business key), label, is_system, is_active, sort_order, metadata. FK by text code. Catalogs cached (memory + Redis); zero joins on hot paths. is_system rows generate the Zod/TS union types. Adding a row ≠ adding behaviour: the API rejects codes the business does not support. |
| DEC-B3 | Cross-cutting catalogs live in core (currencies, notification_channels, national_id_types); the rest in their own schema. |
| DEC-B4 | Tenant-extensible parametric tables: YES for crm.activity_types and loyalty.reward_types; NO for ledger types or state machines. Explicit rule in data.md: extensible ⇔ the value is the tenant’s business taxonomy; never ⇔ the value governs a state machine or system logic. |
| DEC-B5 | Money: amount BIGINT minor units + currency_code FK ISO 4217. Never floats, never amounts without currency. Points ≠ money (own ledger). |
| DEC-B6 | F1 = single currency per tenant. base_currency IMMUTABLE after creation (change only via an assisted migration process). Multi-currency + FX deferred to F2 with its own ADR. |
| DEC-B7 | Retention per plan (industry standard): tracked_events and sends detail 13 months (starter) / 25 months (pro, covers YoY analysis) / 37+ months (enterprise). Automatic export to Supabase Storage (compressed NDJSON) before purging a partition; statistical aggregates kept forever. |
| DEC-B8 | Partitioning by criterion (NOT everything): monthly RANGE on core.tracked_events, messaging.sends, messaging.send_status_history, core.audit_log, core.usage_snapshots (+ evaluate outbox and ledger with real data). DDL in custom SQL migrations; a job pre-creates partitions N+2 months; DEFAULT partition with an alert; pg_partman spike on Supabase. Queries against partitioned tables MUST include the partition key. |
C. Platform architecture
| ID | Decision |
|---|---|
| DEC-C1 | CQRS-lite ratified (ADR-017): per command, ONE transaction = state + outbox + audit. Reads = rebuildable projections. Full event sourcing DISCARDED as a general norm (the append-only domains — ledger, metering, audit — already are by nature). |
| DEC-C2 | core.audit_log: full old→new diff, typed actor (user/member/api_key/system), correlation_id. Retention = the events policy per plan, with the legal minimum applying separately. “The most complete possible audit platform” as a principle. |
| DEC-C3 | Constitution amendment approved: TWO new port categories — QueuePort and NotificationChannelPort. |
| DEC-C4 | Hexagonal WITHOUT EXCEPTION across the whole project. Adapter binding is declared in EACH module’s composition root (makeDeps): different modules may use different adapters for the same port (e.g. messaging→QStash, loyalty→Vercel Queues). A new vendor is still gated by an ADR. F1: every module on Vercel Queues. |
| DEC-C5 | Retries: industry standard — n=5, exponential backoff + jitter, DLQ. EVIDENCE GUARANTEE: every failure and every exhausted retry is persisted in core.dead_letters (payload, attempts, last error, status: pending_review/replayed/discarded) with a BetterStack alert and a replay tool in the Ops backoffice. High-volume successes → metrics, not rows. |
| DEC-C6 | Consumer idempotency: standard core.processed_jobs table (dedupe key + TTL), mandatory for every worker. |
D. API
| ID | Decision |
|---|---|
| DEC-D1 | Canonical path: https://api.softcrum.com/v1/{module}/{resource} (no /api). |
| DEC-D2 | NO Segment-style aliases in v1: the canonical per-module routes prevail (maintainability and context). /v1/core/track, /v1/core/identify, /v1/core/batch. A Segment compatibility layer only if a real integration demands it. |
| DEC-D3 | Deprecation: minimum 6-month window + Sunset header + Mintlify changelog. |
| DEC-D4 | Rate limits per plan PUBLISHED in the docs (anti-opacity positioning). |
| DEC-D5 | Unified authorization: every endpoint declares EXACTLY ONE permission {module}.{resource}.{action} → permissions group into roles → roles attach to users AND to machines (API keys, OAuth clients = “machine roles”). OAuth scopes = permission bundles. The consent screen shows permissions. The authorization-code flow (marketplace) is designed in the spec and implemented later. |
| DEC-D6 | Third-party access WITHOUT a mandatory IP allowlist: auth by API keys + HMAC + rate limits. An IP allowlist is available as extra per-tenant security, off by default. Public write keys (track only) arrive with the widget in F1b; F1a is server-side. |
| DEC-D7 | Runtime API (p95 <300 ms; track <100 ms; member reads <150 ms) / Management API (p95 <1 s) separation, within the per-module convention. Budgets = DoD items + k6 in certification. |
E. Notifications
| ID | Decision |
|---|---|
| DEC-E1 | F1 channels: email, in_app, webhook, push, SMS. F2: whatsapp, live_activity. SMS NUANCE: the channel and the port exist from day 1; the ADAPTER lands in F1b with a provider ADR (new dependency + per-message cost + per-country sender registration). |
| DEC-E2 | Dispatch cascade: level 0 platform → 1 tenant → 2 module → 3 event → recipient preferences/consents/suppressions. resolveChannels() as a pure function in core. |
| DEC-E3 | Member preference center by channel AND by category (transactional/marketing/product). Transactional = no opt-out. |
| DEC-E4 | Cross-channel fallback (push fails → email after X min): F2, configurable per event. NOTED in the roadmap. |
| DEC-E5 | Quiet hours default per tenant, override per campaign; frequency caps only for the marketing category. |
| DEC-E6 | Live Activities (F2): use cases = important ongoing activities to report to the member (redemptions or orders in progress, program milestones). Requires an Expo dev client + native module → ADR when it arrives. |
| DEC-E7 | End-to-end traceability: correlation_id from the origin event to send_status_history (queued→sent→delivered→opened→clicked/bounced/complained/failed) and outgoing webhooks. |
| DEC-E8 | Per-channel queues with rate-limit-aware workers (token bucket per provider AND per tenant). The transactional rail ALWAYS outranks marketing. |
F. Experiences
| ID | Decision |
|---|---|
| DEC-F1 | Two distinct backoffices: (1) tenant console — clients manage all their modules (web + mobile); (2) Softcrum Ops — internal administration of the suite: tenants, plans/entitlements, per-metric configurable enforcement, impersonation with audit, flags, health of queues/partitions/dead letters with replay. |
| DEC-F2 | Mobile white-label: first proposal = a single multi-tenant app with runtime theming, ARCHITECTED to extend easily to the premium tier of a dedicated binary under the client’s Apple/Google account (per-tenant config separated from code from day 1: EAS build profiles). Wallet passes as the middle layer (F2). |
| DEC-F3 | Mobile stack: Expo + React Native (confirmed). Push: FCM/APNs. |
| DEC-F4 | Tiered domains: default subdomain {tenant}.softcrum.com; custom domain = paid add-on; included at enterprise. |
| DEC-F5 | The member portal and the widget consume the public API EXCLUSIVELY via packages/api-client (the golden headless rule). |
G. Metering, consumption and pricing
| ID | Decision |
|---|---|
| DEC-G1 | core.usage_snapshots every 30 minutes for everyone, with a t1→t2 delta. Contracted-versus-used dashboard in the console; 80/90/100% alerts through the platform’s own notification system. |
| DEC-G2 | Per-metric enforcement CONFIGURABLE per tenant from Softcrum Ops. Defaults: contacts = grace + notice; messages = hard at 110%; events = soft with overage. |
| DEC-G3 | Vercel-style pricing model: published unit prices, included allowances per plan, metered consumption, projected-spend panel, tenant-configurable spend caps (the self-service face of enforcement). Billable base: the marketable contact; historical/cold storage free; no ratchet; re-evaluated each cycle in both directions. |
| DEC-G4 | In-house rating engine: at cycle close computes base + overages × unit price from usage_snapshots → variable charge via Fintoc (enrolled card) / Paddle (usage-based) → LibreDTE for the SII. The design MUST support it from day 1. |
| DEC-G5 | ”Data freshness” add-on: a SINGLE package (drift reconciliation + full RFM + full segment recompute + analytical snapshots) with tiers nightly (default) / 12h / 6h / 3h / 1h. contact_balances is near-real-time ALWAYS and is not part of the add-on. |
H. Roadmap and scope
| ID | Decision |
|---|---|
| DEC-H1 | Official re-sequencing of the suite: CRM + Loyalty is the first commercial module; Tracker follows. G1-Engage = design partner running CRM + Loyalty end to end, then weekly growth. Target date: 2026-11-01. |
| DEC-H2 | ⚠️ SUPERSEDED by ADR-021 (2026-08-17): multi-program is a first-class capability in F1. Original text: multi-program schema-only in F1 — program_id on every loyalty table from the first migration + an auto-created, invisible default program. |
| DEC-H3 | Payments↔loyalty seam CONFIRMED as a requirement: mixed points + money redemption is a contract-winning feature. F1 leaves the seam (redemption with a nullable money_component + a domain event); implementation with payments in a later phase. |
| DEC-H4 | apply_discount effect (promotions engine) ratified for F2. F1: rule_effect_types parametric and open (zero cost). |
| DEC-H5 | Wallet passes (Apple/Google) ratified for F2. Small ADR for the signing libraries. |
| DEC-H6 | Naming: modules are NOT commercialised as separate brands. Commercial brand = Softcrum Suite; modules carry descriptive names. No per-module INAPI registrations. |
| DEC-H7 | Vertical templates as DATA (not code): installable event taxonomies + an onboarding wizard (choose a vertical or start blank). The product must be applicable to any industry (design partners from diverse sectors). First template: subscriptions/services. |
I. Documentation and process
| ID | Decision |
|---|---|
| DEC-I1 | Language: repository docs (constitution, standards, ADRs, specs) in ENGLISH. Mintlify BILINGUAL (EN/ES) with processes, procedures and diagrams. This debate material (ES) = source. |
| DEC-I2 | Diagrams: the diagram-design skill (cathrynlavery/diagram-design — validated: 27 types, self-contained HTML+SVG, brand onboarding from the website, SVG/PNG export for Mintlify). HTML sources versioned in the repository. |
| DEC-I3 | Mintlify-first: the monorepo’s /docs is the Mintlify source; everything per module is generated and evidenced from there. Generation order: see documentation-plan-v1.md. |
| DEC-I4 | ADRs: a single cross-cutting series (ADR-09+) in the same /docs (they are generic to the whole platform). |
| DEC-I5 | TWO canonical slices coexist: createInitiative (synchronous command archetype) + trackEvent (asynchronous pipeline archetype). Every task spec declares which archetype it follows. |
| DEC-I6 | Pilot reference throughout the documentation: “design partner (subscriptions/services vertical)”. No company names. |
J. Compliance and legal
| ID | Decision |
|---|---|
| DEC-J1 | Softcrum = processor; tenant = controller. Applicable standard: the strictest among the laws of supported countries (floor: Ley 21.719 + LGPD). |
| DEC-J2 | The COMPLETE legal package is produced as professional drafts (batch 7 of the plan): DPA, privacy policy, ToS, internal RAT + per-tenant RAT generator, sub-processor list, DPIA template, annex of security measures, DPO designation record (Daniel, provisional). EVERYTHING requires review by a Chilean data-protection lawyer before signing or publishing (P-2). |
| DEC-J3 | Right to erasure: anonymize ledger transactions, delete profile and events. Committed execution ≤30 days, automated. Incorporated into company policy as legal backing. |
| DEC-J4 | The 72-hour breach runbook is mandatory BEFORE commercial launch (blocking, analogous to tenant-restore pre-G1). |