5.5 KiB
Classroom Copilot Supabase migrations and deterministic dev seed
Status: branch implementation validated on Supabase dev host 192.168.0.94 using an isolated throwaway database.
Consolidated init chain
The Docker Compose db service now mounts a single ordered Classroom Copilot chain:
volumes/db/cc/61-gais-reference.sql— GAIS reference table schema and open-data read policies only.volumes/db/cc/62-application-schema.sql— canonical app schema, storage/file metadata tables, class/lesson/CIS tables, indexes, and base RLS.volumes/db/cc/63-academic-calendar.sql— school timetable, academic year/term/week/day, teacher timetable, and teacher slot tables.volumes/db/cc/64-extended-schema.sql— term breaks, academic periods, invitations, taught lessons, and week-cycle slot uniqueness.volumes/db/cc/65-phase-c.sql— Phase C cleanup after taught lessons exist; links lesson deliveries to taught lessons.volumes/db/cc/66-taught-lessons-nullable.sql— nullable taught lessonclass_idand teacher slot class FK.volumes/db/cc/67-dev-seed.sql— deterministic, non-sensitive dev fixtures.
The old 61-core-schema.sql through 70-add-directory-support.sql bootstrap files were removed from the active chain because they represented an older ClassConcepts/filesystem schema and stale role vocabulary. The Git history remains the archive.
Deterministic dev seed contents
67-dev-seed.sql creates only fixture data:
- 1 platform admin in
admin_profiles. - 1 school/institute.
- 1 school admin, 2 teachers, 3 students.
- institute memberships for the school admin, teachers, and students.
- 2 classes with class-teacher and class-student rows.
- 1 school timetable, 1 academic year, 1 term, 1 week, 4 academic days, and 16 academic periods.
- 2 teacher timetables, 3 teacher timetable slots, and 3 taught lessons.
- 2 planned lessons and 1 delivered lesson fixture.
- Storage buckets
cc.users,cc.public.snapshots, andcc.examboards. - TLDraw default snapshot paths on the teacher whiteboard rooms; object rows are not pre-created.
Fixture emails use the classroomcopilot.dev domain and are not real users. Do not replace this seed with live student/teacher data.
Validation pattern used on Supabase dev
Do not run schema experiments on production. To validate this branch without mutating the live dev database, create a throwaway database on the Supabase dev Postgres container, clone only the auth and storage schema definitions from dev, apply the ordered chain, check row counts, then drop the throwaway database.
The 2026-05-28 validation used this shape on ubuntu-ct-supabase-dev (192.168.0.94):
DB=cc_mig_validate_<timestamp>
BASE=/tmp/cc-supabase-migration-validate
docker exec supabase-db psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c "DROP DATABASE IF EXISTS $DB WITH (FORCE);"
docker exec supabase-db psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c "CREATE DATABASE $DB;"
# The dev auth schema has a trigger that references public.handle_new_user();
# create a no-op stub before restoring auth/storage schema-only into the throwaway DB.
cat >/tmp/create_dummy.sql <<'SQL'
create or replace function public.handle_new_user()
returns trigger
language plpgsql
as $$
begin
return new;
end;
$$;
SQL
docker exec -i supabase-db psql -U postgres -d "$DB" -v ON_ERROR_STOP=1 < /tmp/create_dummy.sql
docker exec supabase-db pg_dump -U postgres -d postgres --schema-only --no-owner --no-privileges -n auth -n storage > /tmp/${DB}_auth_storage_schema.sql
docker exec -i supabase-db psql -U postgres -d "$DB" -v ON_ERROR_STOP=1 < /tmp/${DB}_auth_storage_schema.sql
for f in \
"$BASE"/volumes/db/cc/61-gais-reference.sql \
"$BASE"/volumes/db/cc/62-application-schema.sql \
"$BASE"/volumes/db/cc/63-academic-calendar.sql \
"$BASE"/volumes/db/cc/64-extended-schema.sql \
"$BASE"/volumes/db/cc/65-phase-c.sql \
"$BASE"/volumes/db/cc/66-taught-lessons-nullable.sql \
"$BASE"/volumes/db/cc/67-dev-seed.sql; do
docker exec -i supabase-db psql -U postgres -d "$DB" -v ON_ERROR_STOP=1 < "$f"
done
# Smoke counts, then cleanup.
docker exec supabase-db psql -U postgres -d "$DB" -Atc "select 'profiles='||count(*) from public.profiles union all select 'institutes='||count(*) from public.institutes union all select 'memberships='||count(*) from public.institute_memberships union all select 'classes='||count(*) from public.classes union all select 'academic_periods='||count(*) from public.academic_periods union all select 'teacher_timetable_slots='||count(*) from public.teacher_timetable_slots union all select 'taught_lessons='||count(*) from public.taught_lessons union all select 'planned_lessons='||count(*) from public.planned_lessons union all select 'lesson_deliveries='||count(*) from public.lesson_deliveries union all select 'buckets='||count(*) from storage.buckets where id like 'cc.%';"
docker exec supabase-db psql -U postgres -d postgres -v ON_ERROR_STOP=1 -c "DROP DATABASE IF EXISTS $DB WITH (FORCE);"
Expected smoke counts from the deterministic seed:
profiles=7
institutes=1
memberships=6
classes=2
academic_periods=16
teacher_timetable_slots=3
taught_lessons=3
planned_lessons=2
lesson_deliveries=1
buckets=3
Production rule
This branch is not a production migration by itself. Before production use:
- Take a production schema/data backup.
- Compare live production schema drift against this consolidated chain.
- Prepare explicit forward migrations for any live-only objects or data transforms.
- Validate those forward migrations on Supabase dev first.
- Only apply to production after human approval.