# Scriba App — Q1 Sprint 0 Platform Integration

> **Date:** May 2026  
> **Scope:** Frontend (`/scriba`) changes to connect the SaaS app to the new Scriba Engine platform API (tenant-scoped uploads, runs, usage, webhooks).  
> **Related engine doc:** `PLATFORM-Q1-SPRINT0.md` (engine-side milestone).

---

## Executive summary

The Scriba web app can now drive migrations through the engine’s **tenant-aware platform API** (`/uploads` + `/runs`), not only the legacy operator endpoint (`/convert`). This aligns the product with multi-tenant deployment, resumable runs, per-run cost visibility, and webhook-based integrations.

**What this means for the business:**

- The UI can run migrations without exposing engine filesystem paths or API keys to the browser.
- Runs are **tenant-scoped** and **resumable** (SSE reconnect after tab refresh or network blip).
- Customers can see **live token/cost usage** during a run.
- External systems can receive **push notifications** when a run finishes (via engine → Scriba webhook).
- Repo-based and folder-based projects are both supported.

The legacy `/convert` path is **retained as a fallback** for advanced project options the platform API does not yet expose (dependency mapping, plugin overrides, etc.).

---

## What was added

### 1. Engine proxy improvements

**File:** `src/app/api/engine/[...path]/route.ts`

| Change | Why |
|--------|-----|
| Multipart body forwarding | Enables `POST /uploads` (file bundles) through the browser proxy |
| SSE support for `/runs/:id/events` | Live run progress streams correctly (previously only `/stream` worked) |
| Preserved `/convert` behaviour | VCS token injection for private Git clones still works on legacy path |

### 2. Tenant auth & request signing

**File:** `src/lib/engine-upstream-headers.ts`

| Change | Why |
|--------|-----|
| Parse `tenantA:secretA` API key format | Matches engine multi-tenant bearer auth |
| HMAC header `sha256=<hex>` | Aligns outbound signing with engine expectations |

### 3. Typed API client (platform endpoints)

**File:** `src/lib/api.ts`

New methods on `api.engine`:

| Method | Engine endpoint | Purpose |
|--------|-----------------|---------|
| `uploadBundle()` | `POST /uploads` | Upload source folder from browser |
| `listUploads()` | `GET /uploads` | List tenant uploads |
| `startRun()` | `POST /runs` | Start migration from an `uploadId` |
| `streamRun()` | `GET /runs/:id/events` | SSE progress stream |
| `getRunResult()` | `GET /runs/:id/result` | Poll final result (fallback) |
| `getRunUsage()` | `GET /runs/:id/usage` | Per-run tokens, cost, redactions |
| `cancelRun()` | `DELETE /runs/:id` | Cancel in-flight run |
| `prepareUpload()` | `POST /api/conversions/:id/prepare-upload` | Server-side clone → upload |

Legacy methods (`startConvert`, `streamConversion`, `cancelConversion`, approve/diff/scaffold/tests) are unchanged.

### 4. Server-side source preparation

**Route:** `POST /api/conversions/[id]/prepare-upload`  
**Libs:** `src/lib/prepare-source-upload.ts`, `src/lib/vcs-token.ts`, `src/lib/engine-server.ts`

For Git-linked projects, the Next.js server:

1. Clones the repository (using stored OAuth/PAT tokens — never sent to the browser)
2. Applies include/exclude glob patterns from project config
3. Uploads the bundle to engine `POST /uploads`
4. Stores `uploadId` on the project for reuse

This closes the gap that `/runs` requires an upload and cannot clone repos directly.

### 5. Webhook receiver

**Route:** `POST /api/engine/webhook`

Receives engine outbound events (`run.completed`, `run.failed`, `run.cancelled`):

- Verifies HMAC signature (`X-Scriba-Webhook-Signature`)
- Matches project by `activeRunId` / `activeConversionId`
- Records event metadata on the project config

Enables server-to-server integrations without holding an SSE connection.

### 6. Migration flow UI (`MigrationFlow.tsx`)

The main migration screen now:

| Feature | Detail |
|---------|--------|
| **Platform run path (default)** | `prepare-upload` → `startRun` → `streamRun` for standard repo/folder projects |
| **Legacy fallback** | Uses `/convert` when dependency mapping, disabled plugins, stages/repair flags, or engine-local paths require it |
| **Reconnect** | Restores in-progress runs via `activeRunId` + `activeConversionId` in project config |
| **Live usage panel** | Polls `/runs/:id/usage` every 8s — shows cost, tokens, redaction count |
| **Folder upload** | “Select project folder” when no Git repo is linked |
| **Cancel** | Uses `cancelRun` or `cancelConversion` depending on path |

Downstream views (`CodeComparison`, `Artifacts`, `Export`, etc.) are **unchanged** — they still read `config.analysisResults` produced after run completion.

### 7. Platform routing logic

**File:** `src/lib/platform-run.ts`

- `needsLegacyConvert()` — decides platform vs legacy path
- `buildStartRunOptions()` — maps wizard config to `/runs` request body (quality level, iterations, patterns, budget caps, etc.)

### 8. Environment documentation

**File:** `.env.example`

- Clarified `SCRIBA_ENGINE_API_KEY` when engine uses `tenant:secret` format
- Added note to point engine `SCRIBA_WEBHOOK_URL` at `http://localhost:3000/api/engine/webhook`

---

## End-to-end flow (new default path)

```
User clicks "Start Migration"
        │
        ▼
MigrationFlow (browser)
        │
        ├─► POST /api/conversions/:id/prepare-upload
        │         clone repo (server) → POST engine /uploads
        │         returns uploadId
        │
        ├─► POST /api/engine/runs  { uploadId, sourceLanguage, targetLanguage, ... }
        │         returns runId + conversionId
        │
        └─► EventSource /api/engine/runs/:runId/events
                  phase / step / progress / log / done
                  │
                  ├─► Poll GET /runs/:runId/usage (live cost)
                  │
                  └─► Save analysisResults → PostgreSQL project config
                            │
                            ▼
                  CodeComparison, Artifacts, Export, Verification…

Engine (parallel) ──► POST /api/engine/webhook on terminal status
```

---

## Deployment checklist

### Scriba app (`.env`)

```env
SCRIBA_ENGINE_URL=http://localhost:3100
SCRIBA_ENGINE_API_KEY=secretA          # when engine has SCRIBA_API_KEY=tenantA:secretA
SCRIBA_WEBHOOK_SECRET=<shared-secret>  # verify inbound webhooks from engine
OPENAI_API_KEY=<key>                   # sandbox auto-repair (unchanged)
```

### Scriba engine (`.env`)

```env
SCRIBA_API_KEY=tenantA:secretA
SCRIBA_WEBHOOK_URL=http://localhost:3000/api/engine/webhook
SCRIBA_WEBHOOK_SECRET_OUT=<same as SCRIBA_WEBHOOK_SECRET above>
```

### Server requirement

`prepare-upload` runs `git clone` on the Next.js host — **Git must be installed** on the app server.

---

## Known limitations (honest scope)

| Item | Status |
|------|--------|
| Platform API for projects with **dependency mapping** (post pre-analysis) | Falls back to legacy `/convert` |
| Platform API when **pipeline plugins are disabled** in wizard | Falls back to legacy `/convert` |
| `useStages` / `useRepair` wizard flags | Legacy path only |
| `GET /runs/:id/result` poll fallback on SSE drop | Client helpers exist; full auto-recover not yet wired in UI error handler |
| Per-tenant webhook URLs | Single global URL (engine posts `tenantId` in payload) |
| S3-backed uploads | Engine is local FS; S3 adapter is future engine work |

---

## Files touched (summary)

| Area | Files |
|------|-------|
| **Modified** | `.env.example`, `MigrationFlow.tsx`, `api.ts`, `engine/[...path]/route.ts`, `engine-upstream-headers.ts` |
| **New routes** | `conversions/[id]/prepare-upload/route.ts`, `engine/webhook/route.ts` |
| **New libs** | `platform-run.ts`, `prepare-source-upload.ts`, `engine-server.ts`, `vcs-token.ts` |

**Approximate diff:** ~340 lines added/changed across 5 modified files + 6 new files.

---

## What was explicitly *not* changed

- Database schema (uses existing `projects.config` JSONB)
- Auth / billing / admin flows
- Post-migration views (comparison, export, compliance, sandbox repair still uses GPT 5.5 via OpenAI)
- Scriba Engine translator logic (all engine work is separate)

---

## Bottom line

Scriba is now **platform-ready**: the SaaS app speaks the same tenant-scoped uploads/runs/usage/webhook contract described in the engine’s Q1 Sprint 0 milestone. Standard migrations run through the new API with live cost visibility and reconnect support; advanced configurations safely fall back to the proven `/convert` path until the engine extends `/runs`.
