Sync Write Enforcement Lite Runbook
Purpose
Section titled “Purpose”Provide a practical, low-overhead checklist to keep syncable domain-table writes on the approved path.
Preconditions
Section titled “Preconditions”- Syncable domain tables are configured.
- pgxsinkit artifact backend is available.
- Operations log table exists.
Apply the latest migrations before seeding. The seed script resets rows inside the existing schema; it does not create or upgrade tables, enums, auth hooks, or claim projection functions.
Local development database:
bun run --cwd packages/db db:migrate:localAnother test database with an explicit DATABASE_URL:
DATABASE_URL=postgres://... bun run --cwd packages/db db:migrate:urlSmall Seed Workflow (Learner And Teacher UI Validation)
Section titled “Small Seed Workflow (Learner And Teacher UI Validation)”Use this when you want a deterministic learner plus a deterministic teacher-managed class without paying the cost of the broader medium profile.
Required environment (loaded from .env.development.local by default):
SUPABASE_URL,SUPABASE_HOST_INTERNAL_URL, orVITE_SUPABASE_URLSUPABASE_SECRET_KEY(required for admin create user flow)
- Seed a deterministic small dataset:
bun run db:seed:smallUse the broader deterministic profile when you need fuller baseline coverage:
bun run db:seed:medium- Optional password overrides (defaults shown):
EMERGENT_SMALL_SEED_TEACHER_PASSWORD="TeacherPass!2026" \EMERGENT_SMALL_SEED_LEARNER_PASSWORD="LearnerPass!2026" \bun run db:seed:small- Login with seeded learner anchor email and password:
- Email:
learner+<seed>@integration.emergent.local - Password:
EMERGENT_SMALL_SEED_LEARNER_PASSWORDorLearnerPass!2026
- Login with seeded teacher anchor email and password:
- Email:
teacher+<seed>@integration.emergent.local - Password:
EMERGENT_SMALL_SEED_TEACHER_PASSWORDorTeacherPass!2026 - Expected seed behavior: the teacher anchor always has at least one active class it can open and manage in the teacher portal.
- Validate mutation UI flow:
- Join class from learner classes view.
- Start, save, submit, evaluate, and finalize an assessment attempt.
- Confirm mutation queue progress in sync monitor.
- Validate teacher workflow UI flow:
- Open the seeded class inventory in the teacher portal.
- Open the seeded class detail page and confirm roster access.
- Add an existing learner or invite a new learner from the roster form.
- Create or update an assessment from the class detail page.
- Compose integration lane with small profile:
bun run test:integration:smallRelease Checklist
Section titled “Release Checklist”- Confirm
WRITE_API_BACKEND=bulk-plpgsql-artifact. - Confirm
WRITE_API_OPS_LOG_ENABLED=true. - Verify sync function artifact is installed and verified.
- Run contract test: per-route writes for syncable tables return
405. - Run batch write test: create/update/delete through
/api/mutations. - Confirm operations log rows are written for success and failure paths.
Quick Verification Queries
Section titled “Quick Verification Queries”Use these patterns against operations log telemetry.
Assumption for examples: operations_log(created_at, source, backend, apply_status, mutation_id, target_table, rejection_reason).
If your pgxsinkit deployment uses different names, adapt column names only.
- Recent batch write status counts by status:
SELECT apply_status, COUNT(*) AS countFROM operations_logWHERE created_at >= NOW() - INTERVAL '15 minutes' AND source = 'batch'GROUP BY apply_statusORDER BY count DESC;- Rows where source/backend is not expected for syncable workloads:
SELECT created_at, mutation_id, target_table, source, backend, apply_statusFROM operations_logWHERE created_at >= NOW() - INTERVAL '24 hours' AND ( source <> 'batch' OR backend <> 'bulk-plpgsql-artifact' )ORDER BY created_at DESCLIMIT 200;- Error rows over time for regression detection:
SELECT date_trunc('hour', created_at) AS hour_bucket, apply_status, COUNT(*) AS countFROM operations_logWHERE created_at >= NOW() - INTERVAL '72 hours' AND apply_status IN ('validation_failed', 'execution_failed', 'rejected')GROUP BY hour_bucket, apply_statusORDER BY hour_bucket DESC, apply_status;- Top rejected targets for fast triage:
SELECT target_table, COALESCE(rejection_reason, 'unspecified') AS rejection_reason, COUNT(*) AS countFROM operations_logWHERE created_at >= NOW() - INTERVAL '24 hours' AND apply_status IN ('validation_failed', 'execution_failed', 'rejected')GROUP BY target_table, COALESCE(rejection_reason, 'unspecified')ORDER BY count DESCLIMIT 20;Keep queries simple in V1; do not block deploys on advanced analytics.
Incident Triage (Lite)
Section titled “Incident Triage (Lite)”If syncable writes fail:
- Check write-api backend mode and ops-log flag.
- Check artifact function presence/verification.
- Check operations log for validation vs execution failures.
- Retry failed client mutations.
- Run reconciliation for impacted tables.
Emergency Manual Change Policy
Section titled “Emergency Manual Change Policy”Manual write is permitted only when:
- Production incident requires immediate mitigation.
- Change is documented with actor, reason, and affected keys.
- Reconciliation checks are run after mitigation.
- Follow-up task is created to restore normal ingress-only flow.
Exit Criteria
Section titled “Exit Criteria”Runbook execution is complete when:
- Syncable writes are succeeding via
/api/mutations. - No active disallowed-route writes remain.
- Operations log reflects healthy status distribution.