> ## Documentation Index
> Fetch the complete documentation index at: https://docs-terra.withunify.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Functional Logic

> Core workflows — form submission, notifications, async operations, integrations

# Functional Logic

> The security review proves the system can't be exploited. This section proves it works correctly.

***

## Form Submission Flow

The most important code path. Everything flows through `submitFormResponse()`:

```mermaid theme={null}
sequenceDiagram
    participant U as User
    participant S as Server
    participant DB as Database
    participant Q as Async Queue

    U->>S: Submit form
    S->>S: Rate limit check (30/min)
    S->>S: Validate form is published
    S->>S: Encrypt PII fields
    S->>S: Link applicant identity
    S->>S: Capture geolocation
    S->>DB: Generate reference ID (retry on collision)
    S->>DB: Insert submission
    S-->>U: Success + reference ID
    S->>Q: Enqueue hooks (non-blocking)
    Q-->>Q: Drive rename
    Q-->>Q: Notifications
    Q-->>Q: Webhooks
    Q-->>Q: Airtable sync
```

<Note>
  Steps 1–8 are synchronous — the submission must save before the user gets a
  response. Step 9 (hooks) is non-blocking. If Airtable is down or a webhook
  fails, the submission is already safe in the database.
</Note>

**Source:** `src/app/actions.ts`, `submitFormResponse()` (\~line 2512)

**Tests:** 3 cases in `form-renderer.test.tsx` (rendering), integration coverage across encryption, rate limiting, and queue tests.

```bash theme={null}
pnpm --dir apps/terra test -- -t "FormRenderer"
```

**Try to break it:**

* Submit to a closed/unpublished form — should return a clear error, not a 500
* Submit 31 times in a minute — rate limiting should kick in
* Disable a webhook endpoint, submit a form — submission should still save

***

## Notifications

Email (Resend) and SMS (Twilio) notifications fire on form events. Admin-only test sends verify provider configuration.

```typescript theme={null}
// Only admins can send test notifications
const result = await testEmailProvider(recipientEmail);
// Non-admin → { success: false, error: "Not authorized — admin access required" }
```

**Tests:** 37+ cases across 3 files

```bash theme={null}
pnpm --dir apps/terra test -- -t "Notification"
```

<Accordion title="What the tests prove">
  * Event retrieval with filtering by form, channel, status - Pagination returns
    distinct results - Stats math: sent count = (sent + delivered), excludes
    failed/pending - Error categorization: invalid address, bounced, carrier
    rejected, rate limited, provider error - Admin-only enforcement on test
    email/SMS - Missing provider config caught (RESEND\_API\_KEY, Twilio
    credentials) - Per-form notification settings: auth enforced, insert vs update
    path, null handling
</Accordion>

***

## Async Queue

Post-submission hooks use an async queue with exponential backoff:

```typescript theme={null}
// src/lib/async-queue.ts
// Backoff formula: 2^(attempts-1) * 30 seconds

// Webhook:    30s → 60s → 120s → 240s → 480s  (5 attempts)
// Airtable:   30s → 60s → 120s                  (3 attempts)
// Plaid:      30s → 60s → 120s                  (3 attempts)
// Email:      30s → 60s → 120s                  (3 attempts)
```

Queue operations are resilient — errors return `null` instead of throwing:

```typescript theme={null}
const result = await enqueueWebhook(payload);
if (!result) {
  logger.error("Failed to enqueue webhook"); // Logged, not thrown
}
```

**Tests:** 20+ cases in `src/lib/__tests__/async-queue.test.ts`

```bash theme={null}
pnpm --dir apps/terra test -- -t "Async Queue"
```

<Accordion title="What the tests prove">
  * Correct RPC payload structure for enqueue - Webhook gets 5 retries, others
    get 3 - Queue errors return null (no crash) - Database timeout and connection
    reset handled gracefully - Complex nested payloads preserved (unicode, special
    chars, nested objects) - Backoff formula verified: `2^(n-1) * 30s`
</Accordion>

***

## Airtable Sync

Bidirectional sync with loop prevention. System fields use `_` prefix convention:

```typescript theme={null}
// Field mapping conventions
const SYSTEM_FIELDS = ["_submission_id", "_status", "_payment_status"];
const FORM_FIELDS = ["full_name", "email"]; // No prefix

// Loop prevention: airtable_sync source blocks reverse trigger
const CHANGE_SOURCES = ["manual", "airtable_sync", "webhook", "system"];
```

Safety constraint: the Airtable client exposes `create`, `update`, and `upsert` — **no delete operation**.

**Tests:** 15 cases in `airtable-status-sync.test.ts`

```bash theme={null}
pnpm --dir apps/terra test -- -t "Airtable Status Sync"
```

<Accordion title="What the tests prove">
  * Missing/disabled connections skip gracefully - Unmapped status fields skip
    (no crash) - Only valid statuses accepted (submitted, pending, processing,
    approved, rejected, withdrawn) - `_submission_id` mapping required to find
    Airtable record - No delete operation exposed — records preserved on
    withdrawal - Sync loop prevention: `airtable_sync` source blocks reverse
    trigger - Change source tracked for audit
</Accordion>

***

## Import Security

Forms can be imported from PDFs, images, and HTML. The import pipeline prevents user impersonation:

```typescript theme={null}
// User ID always comes from the session, never from the request
const jobId = `import_${session.user.id}_${timestamp}`;

// Even if an attacker passes a userId parameter, it's ignored
startImageImportJob({ userId: "attacker-999" });
// → Uses session.user.id, not "attacker-999"
```

**Tests:** 10 cases in `import-security.test.ts`

```bash theme={null}
pnpm --dir apps/terra test -- -t "Import Security"
```

<Accordion title="What the tests prove">
  * Job ID contains authenticated user's ID, not attacker's - Unauthenticated
    users rejected - `userId` parameter in request body ignored - Separate jobs
    for different users (no cross-contamination) - Max 8 images, max 10MB PDF,
    only PDF file type accepted
</Accordion>

***

<CardGroup cols={2}>
  <Card title="Test Coverage" icon="vial" href="/review/test-coverage">
    Full inventory of every test
  </Card>

  <Card title="Security" icon="shield" href="/review/security">
    Auth, RBAC, input validation
  </Card>
</CardGroup>
