feat(programs): first-class program model — init_/compile_/root_ ladder, seeded root-kubernetes-foundations
- migrations/019: programs table (slug PK, matching assessment_definitions/cohorts convention), family (init/compile/root) and delivery_mode (self_paced/cohort/hybrid) as real CHECK-constrained enums, versioned like assessment_definitions so an edit to curriculum_path/delivery_mode never silently changes what an existing enrollment means. Seeds exactly one program wrapping the existing RootNode/Kubernetes curriculum and capstone — no new curriculum content invented. - programs.js: list/get/create/update, with automatic version bump only when a snapshot-relevant field actually changes.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
-- Phase 5B §2 — programs: first-class learning products, distinct from curriculum JSON itself
|
||||
-- (a program is the commercial/academic unit a student enrolls in; curriculum_path is still
|
||||
-- just the content it points at, unchanged since Phase 1). program_id IS the slug — same
|
||||
-- convention as assessment_definitions/cohorts (a single TEXT primary key, no separate numeric
|
||||
-- id + slug pair).
|
||||
--
|
||||
-- family is the init_/compile_/root_ youth-engineering ladder (see
|
||||
-- docs/PHASE5B_SELF_PACED_ENROLLMENT.md §1) — only 'root' has real curriculum content today;
|
||||
-- init/compile rows can be added later without a schema change, they just won't have a working
|
||||
-- curriculum_path yet. delivery_mode describes how the program is normally taken; an
|
||||
-- individual enrollment can still snapshot a different mode (see 020_enrollments.sql).
|
||||
--
|
||||
-- version is a plain incrementing integer, matching assessment_definitions' existing
|
||||
-- versioning convention — enrollments snapshot the version+curriculum_path they were created
|
||||
-- against so a later edit to a program never silently changes what an already-enrolled
|
||||
-- student is being held to.
|
||||
CREATE TABLE IF NOT EXISTS programs (
|
||||
program_id TEXT PRIMARY KEY, -- slug, e.g. 'root-kubernetes-foundations'
|
||||
name TEXT NOT NULL,
|
||||
description TEXT,
|
||||
family TEXT NOT NULL CHECK (family IN ('init', 'compile', 'root')),
|
||||
delivery_mode TEXT NOT NULL DEFAULT 'self_paced' CHECK (delivery_mode IN ('self_paced', 'cohort', 'hybrid')),
|
||||
curriculum_path TEXT NOT NULL DEFAULT 'rootnode-path',
|
||||
completion_assessment_id TEXT REFERENCES assessment_definitions(assessment_definition_id),
|
||||
estimated_duration_hours INTEGER,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
status TEXT NOT NULL DEFAULT 'active' CHECK (status IN ('active', 'inactive')),
|
||||
created_by UUID REFERENCES instructors(instructor_id),
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
|
||||
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_programs_status ON programs(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_programs_family ON programs(family);
|
||||
|
||||
-- Seed: the one legitimate self-paced program this phase ships, wrapping the EXISTING
|
||||
-- RootNode/Kubernetes curriculum and capstone — no new curriculum content invented. init_/
|
||||
-- compile_ programs are intentionally NOT seeded (no real curriculum for them yet, per this
|
||||
-- phase's explicit scope line).
|
||||
INSERT INTO programs (program_id, name, description, family, delivery_mode, curriculum_path, completion_assessment_id, estimated_duration_hours)
|
||||
VALUES (
|
||||
'root-kubernetes-foundations', 'ROOT — Kubernetes Foundations',
|
||||
'Self-paced Linux, containers, and Kubernetes operations — the existing RootNode curriculum, from Foundations through the RootNode Capstone.',
|
||||
'root', 'self_paced', 'rootnode-path', 'rootnode-capstone', 40
|
||||
) ON CONFLICT (program_id) DO NOTHING;
|
||||
Reference in New Issue
Block a user