Docs
Tabula Kifuliiru
Applications
Tabula Kifuliiru
Overview
IntroductionArchitectureDevelopment setup
Publish pipelinePublish to LabPublish to cloud
Platform integration
HomeBrowse applications

© 2026 Wekify LLC · Kifuliiru Lab

Homellms.txtkifuliiru.com

GuidesPublish pipeline

Publish pipeline

Local → lab staging → production (Tabula) data flow.

Status: Implemented (August 2026) Audience: Admins UI: /data-management/publish-to-cloud

Related:

  • PUBLISH_TO_LAB.md — Step 1 detail
  • PUBLISH_TO_CLOUD.md — API reference
  • IMPACT_VARIABLES.md — metrics after publish

Overview

All corpus data flows to Supabase in two steps:

∑Formula
Local Postgres ──Step 1──► lab_* (staging) ──Step 2──► production (live / Tabula)
```text

| Step | Action | Target | Criteria | |------|--------|--------|----------| | **1** | Publish | `lab_verbs`, `lab_sentences`, … | Kifuliiru optional locally; insert-only | | **2** | Promote | `verbs`, `sentences`, … | Native Kifuliiru required; insert-only |

**Do not** skip staging for routine work — production is for reviewed, native-authored content.

---

## Step 1 — Local → lab_*

### UI

1. Open **`/data-management/publish-to-cloud`**
2. Select **Lab staging (lab_*)**
3. **Scan all tables** → review pending counts
4. Select tables → **Publish selected**

### API

```http
GET /api/mapper/compare?target=lab
POST /api/mapper/sync-batch
Content-Type: application/json

{ "tables": ["verbs", "sentences"], "target": "lab", "publishReady": false }
```text

### What happens

- Reads rows from **local PostgreSQL**
- Inserts **new** rows into **`lab_*`** Supabase tables
- Dedup by English or Kifuliiru-side key — skips rows already in cloud
- Never updates existing cloud rows

---

## Step 2 — lab_* → Production

### UI

Same page — **Step 2 — Promote lab_* → Production** panel:

1. **Scan promotion** — compares staging vs production per table
2. Review **Pending** column (eligible rows not yet in production)
3. Select tables → **Promote selected** (confirmation required)

### API

```http
GET /api/mapper/compare-promote
GET /api/mapper/compare-promote?table=verbs

POST /api/mapper/promote-batch
Content-Type: application/json

{ "tables": ["verbs", "sentences"], "limit": 0 }
```text

### Production promotion criteria

A lab row is promoted only if **all** apply:

1. Present in **`lab_*`** staging
2. **Native Kifuliiru** — not `[MISSING_KIFULIIRU]`, not `UkuEnglish`-style placeholder
3. Has Kifuliiru-side text (`verb_kifuliiru`, `kifuliiru`, `igambo`, …)
4. Dedup key **not already** in production
5. **Insert only** — never overwrites production

Implementation: `src/lib/utils/publish-promotion.ts`

Bridge languages (EN/FR/SWA) remain optional for production, same as existing publish policy.

---

## Admin workflow (recommended)

```bash
# 1. See gaps
npm run audit:cloud

# 2. Step 1 in UI — publish verbs, sentences, dictionary_nouns to lab_*

# 3. Re-audit — lab_* counts should increase

# 4. Step 2 in UI — scan promotion → promote eligible rows

# 5. Re-audit — production counts should increase

# 6. (Planned) Refresh impact_variables corpus slugs
```text

### Suggested first batch

**Step 1:** `verbs`, `sentences`, `dictionary_nouns`, `conjugation_rules` 
**Step 2:** Same tables after reviewing staging in deployed Lab

---

## Deployed Lab — read from `lab_*` (table name mismatch)

**Problem:** Step 1 writes to **`lab_verbs`**, **`lab_sentences`**, etc. Local dev reads **`verbs`**, **`sentences`** from PostgreSQL. On deploy there is no local DB, so pages stay empty unless reads target Supabase staging.

**Two fixes (pick by goal):**

| Goal | What to do | |------|------------| | **Deployed Lab shows your published staging data** | Set deploy env (see below). Overview / stats use `lab_*` via Supabase REST. | | **Tabula / public app reads `verbs`, `sentences` (no `lab_` prefix)** | Use **Step 2 — Promote** on `/data-management/publish-to-cloud` to copy eligible rows into production tables. |

### Deploy env (Lab reads staging)

```bash
CORPUS_DATA_SOURCE=supabase-lab
# or auto-detect: use local PG when reachable, else lab_*
# CORPUS_DATA_SOURCE=auto

SUPABASE_URL=...
SUPABASE_ANON_KEY=...
SUPABASE_SERVICE_ROLE_KEY=... # server-side reads of lab_* (recommended on deploy)
```text

**Today:** `/api/stats` and overview counts respect `CORPUS_DATA_SOURCE`. Verbs/dictionary **list pages** still use local Postgres only — wire those next if you need full browsing on deploy (Phase 3b).

**Table names:** local `dictionary_conjugated_verbs` → Supabase `lab_dictionary_conjugated_verbs`.

One-off rename (legacy `lab_dictionary_other` only, no other tables touched):

`scripts/sql/rename-lab-dictionary-other-to-conjugated-verbs.sql` — run in Supabase SQL Editor, or `npm run db:rename-lab-dictionary-other` with `SUPABASE_DB_URL` set.

---

## Escape hatch — direct local → production

Still available for emergencies (select **Production** in Step 1), but:

- Requires Kifuliiru-side text always
- Skips staging review
- UI warns to prefer lab → promote

Use only when staging table does not exist or for one-off hotfixes.

---

## Security

- Promote and compare-promote APIs require **lab admin** session (`requireLabAdminApi`)
- RLS on Supabase tables — admin / super_admin role
- Never commit service keys; use `.env` locally

---

## Code map

| Piece | Location | |-------|----------| | Local → lab sync | `src/lib/services/db-mapper.ts` → `syncToSupabase` | | Lab → prod promote | `db-mapper.ts` → `promoteLabToProduction` | | Promotion criteria | `src/lib/utils/publish-promotion.ts` | | Publish UI Step 1 | `src/app/data-management/publish-to-cloud/page.tsx` | | Publish UI Step 2 | `src/components/publish/PublishPromotionPanel.tsx` | | Compare promote API | `src/app/api/mapper/compare-promote/route.ts` | | Promote batch API | `src/app/api/mapper/promote-batch/route.ts` | | Deploy read config | `src/lib/database/corpus-data-source.ts` | | Stats from `lab_*` | `src/lib/database/corpus-supabase-stats.ts` |

---

## Troubleshooting

| Issue | Fix | |-------|-----| | Step 2 all pending = 0, lab empty | Run Step 1 first | | Promote errors on dictionary tables | Production table may not exist in Supabase yet | | Rows skipped on promote | Placeholder Kifuliiru or already in production | | 401 on promote API | Log in as admin |
PreviousDevelopment setupNextPublish to Lab

On this page

  • Overview