Skip to content

Sync Write Enforcement Lite Runbook

Provide a practical, low-overhead checklist to keep syncable domain-table writes on the approved path.

  1. Syncable domain tables are configured.
  2. pgxsinkit artifact backend is available.
  3. 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:

Terminal window
bun run --cwd packages/db db:migrate:local

Another test database with an explicit DATABASE_URL:

Terminal window
DATABASE_URL=postgres://... bun run --cwd packages/db db:migrate:url

Small 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, or VITE_SUPABASE_URL
  • SUPABASE_SECRET_KEY (required for admin create user flow)
  1. Seed a deterministic small dataset:
Terminal window
bun run db:seed:small

Use the broader deterministic profile when you need fuller baseline coverage:

Terminal window
bun run db:seed:medium
  1. Optional password overrides (defaults shown):
Terminal window
EMERGENT_SMALL_SEED_TEACHER_PASSWORD="TeacherPass!2026" \
EMERGENT_SMALL_SEED_LEARNER_PASSWORD="LearnerPass!2026" \
bun run db:seed:small
  1. Login with seeded learner anchor email and password:
  • Email: learner+<seed>@integration.emergent.local
  • Password: EMERGENT_SMALL_SEED_LEARNER_PASSWORD or LearnerPass!2026
  1. Login with seeded teacher anchor email and password:
  • Email: teacher+<seed>@integration.emergent.local
  • Password: EMERGENT_SMALL_SEED_TEACHER_PASSWORD or TeacherPass!2026
  • Expected seed behavior: the teacher anchor always has at least one active class it can open and manage in the teacher portal.
  1. 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.
  1. 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.
  1. Compose integration lane with small profile:
Terminal window
bun run test:integration:small
  1. Confirm WRITE_API_BACKEND=bulk-plpgsql-artifact.
  2. Confirm WRITE_API_OPS_LOG_ENABLED=true.
  3. Verify sync function artifact is installed and verified.
  4. Run contract test: per-route writes for syncable tables return 405.
  5. Run batch write test: create/update/delete through /api/mutations.
  6. Confirm operations log rows are written for success and failure paths.

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.

  1. Recent batch write status counts by status:
SELECT
apply_status,
COUNT(*) AS count
FROM operations_log
WHERE created_at >= NOW() - INTERVAL '15 minutes'
AND source = 'batch'
GROUP BY apply_status
ORDER BY count DESC;
  1. Rows where source/backend is not expected for syncable workloads:
SELECT
created_at,
mutation_id,
target_table,
source,
backend,
apply_status
FROM operations_log
WHERE created_at >= NOW() - INTERVAL '24 hours'
AND (
source <> 'batch'
OR backend <> 'bulk-plpgsql-artifact'
)
ORDER BY created_at DESC
LIMIT 200;
  1. Error rows over time for regression detection:
SELECT
date_trunc('hour', created_at) AS hour_bucket,
apply_status,
COUNT(*) AS count
FROM operations_log
WHERE created_at >= NOW() - INTERVAL '72 hours'
AND apply_status IN ('validation_failed', 'execution_failed', 'rejected')
GROUP BY hour_bucket, apply_status
ORDER BY hour_bucket DESC, apply_status;
  1. Top rejected targets for fast triage:
SELECT
target_table,
COALESCE(rejection_reason, 'unspecified') AS rejection_reason,
COUNT(*) AS count
FROM operations_log
WHERE 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 DESC
LIMIT 20;

Keep queries simple in V1; do not block deploys on advanced analytics.

If syncable writes fail:

  1. Check write-api backend mode and ops-log flag.
  2. Check artifact function presence/verification.
  3. Check operations log for validation vs execution failures.
  4. Retry failed client mutations.
  5. Run reconciliation for impacted tables.

Manual write is permitted only when:

  1. Production incident requires immediate mitigation.
  2. Change is documented with actor, reason, and affected keys.
  3. Reconciliation checks are run after mitigation.
  4. Follow-up task is created to restore normal ingress-only flow.

Runbook execution is complete when:

  1. Syncable writes are succeeding via /api/mutations.
  2. No active disallowed-route writes remain.
  3. Operations log reflects healthy status distribution.