Files
krow_backend/migrations/000002_application_interview_id.up.sql
2026-08-24 13:06:29 +05:30

83 lines
4.0 KiB
SQL

-- ============================================================================
-- Frontend reconciliation gaps
--
-- Three columns the frontend reads or writes but migration 000001 had no place
-- for. All three were found by sweeping runtime write paths, not just seed data.
--
-- ── 1. job_applications.interview_id ──────────────────────────────────────
--
-- WRITE components/krow/AIInterviewModal.jsx:180
-- PATCH {status:'interview', interview_id, ai_score} — reachable from
-- admin/CandidateProfile.jsx, admin/Candidates.jsx, PositionDetail.jsx
-- READ components/krow/CandidateExpandedDetails.jsx:41,142
-- via CandidateCard.jsx ← admin/Candidates.jsx, PositionDetail.jsx
-- SEED 5 of 24 applications in src/api/seed.js carry it
--
-- Deliberately NOT a foreign key. `app_devon` references `int_devon`, for which
-- no AIInterview record exists in the seed; a REFERENCES constraint would
-- either fail the seed or force that value to NULL, and nulling it would be
-- silently transforming source data. The column is a soft reference, which is
-- also how the frontend treats it — every read is a truthiness check, never a
-- lookup.
-- ============================================================================
SET search_path = public;
ALTER TABLE public.job_applications
ADD COLUMN interview_id uuid;
COMMENT ON COLUMN public.job_applications.interview_id IS
'Soft reference to ai_interviews.id. Intentionally unconstrained: seed data '
'contains a dangling reference, and the frontend only ever tests it for '
'presence.';
CREATE INDEX job_applications_interview_idx
ON public.job_applications (interview_id)
WHERE interview_id IS NOT NULL;
-- ── 2. courses.training_outline ───────────────────────────────────────────
--
-- WRITE components/forge/AddSkillTraining.jsx:103 ← pages/University.jsx
-- (mounted at /admin/university)
-- READ components/forge/challengeMeta.js:139, ai-assistant/insights.js:177,
-- ai-assistant/capabilities/admin.js:1547, lib/skills/actions.js:369
-- SEED 0 of 40 courses carry it — the Forge authoring flow writes it, and
-- every shipped course predates that flow.
--
-- text[] rather than jsonb: the writer is
-- `form.outline.map((s) => s.trim()).filter(Boolean)`, a plain list of strings,
-- and every reader guards with Array.isArray then filters. This matches the
-- shape of completion_criteria and verification_criteria on the same table.
ALTER TABLE public.courses
ADD COLUMN training_outline text[] NOT NULL DEFAULT '{}';
COMMENT ON COLUMN public.courses.training_outline IS
'Ordered written training steps, authored through the Forge flow. Empty for '
'every course that predates it.';
-- ── 3. worker_profiles.score_breakdown ────────────────────────────────────
--
-- WRITE lib/krowScore.js:64 recalcProfilePatch() → spread into the profile
-- patch at lib/krowHooks.js:682, the live challenge-submission path
-- reached through CourseDetail.jsx.
-- READ No reader consumes it *from a profile*: every score_breakdown reader
-- in the frontend works on a job application. The column exists so the
-- write lands rather than being silently discarded.
--
-- jsonb, mirroring job_applications.score_breakdown, which holds the output of
-- the same scoring engine.
ALTER TABLE public.worker_profiles
ADD COLUMN score_breakdown jsonb NOT NULL DEFAULT '{}'::jsonb;
ALTER TABLE public.worker_profiles
ADD CONSTRAINT worker_profiles_score_breakdown_object
CHECK (jsonb_typeof(score_breakdown) = 'object');
COMMENT ON COLUMN public.worker_profiles.score_breakdown IS
'Per-dimension KROW score detail from recalcProfilePatch(). Written by the '
'challenge flow; no frontend reader consumes it from a profile yet.';