Phase 0 — Foundation: Schema Rebuild & Seed Infrastructure
Goal: Replace existing models with the new schema. No user-visible features change. Existing functionality (anonymous meetings, auth, basic job CRUD) continues to work.
0.1 Backend — Delete Old Models, Create New Ones
Delete
src/models/Question.ts— replaced byScreeningQuestion,DSAProblem,ScenarioQuestion,InterviewQuestion- Drop
Questioncollection reference fromjobOpeningController.ts(findOrCreateQuestionimport) - Remove
STAGE_TYPESstring-comparison validation fromjobOpeningController.ts
Create / Rewrite Models
| File | Action | Notes |
|---|---|---|
src/models/Plan.ts | Create | See DB Design §Plan |
src/models/Organization.ts | Create | Replaces User.organizationName |
src/models/User.ts | Rewrite | Remove organizationName. Add organizationId: ObjectId |
src/models/Participant.ts | Rewrite | Add authId? (sparse unique), userId?, preferences, stats sub-doc |
src/models/StageTypeConfig.ts | Create | Drives all frontend dropdowns |
src/models/ScreeningQuestion.ts | Create | Near-clone of old Question.ts + organizationId, tags |
src/models/DSAProblem.ts | Create | Stub — not used until Phase 3 |
src/models/ScenarioQuestion.ts | Create | Stub — not used until Phase 4 |
src/models/InterviewQuestion.ts | Create | Used in Phase 1B (create now, use later) |
src/models/JobOpening.ts | Rewrite | Replace stages[].type (string) with stageTypeKey. Polymorphic stage configs. Add organizationId. Add stats sub-doc. |
src/models/CandidatePipeline.ts | Create | Dual-status, stageProgression[], jobSnapshot |
src/models/Interview.ts | Rewrite | Replace isAutomated boolean with stageTypeKey. Add candidatePipelineId, stageId, organizationId, stageData polymorphic sub-doc |
Auth Middleware Update
src/middleware/authMiddleware.ts: after resolving req.user from Firebase UID, also attach req.organizationId:
typescript
const org = await Organization.findOne({ 'members.userId': req.user._id });
req.organizationId = org?._id;0.2 Backend — Seed Scripts
| Script | Purpose |
|---|---|
src/scripts/seedPlans.ts | Insert 4 Plan docs |
src/scripts/seedStageTypes.ts | Insert 6 StageTypeConfig docs |
src/scripts/seedScreeningQuestions.ts | Replace seedQuestions.ts — inserts 22 system questions |
src/scripts/seedDevOrg.ts | Dev-only — create org for honey.singhroi@gmail.com (requires plans seeded first) |
src/scripts/seedInterviewQuestions.ts | ~30 InterviewQuestion docs — deferred to Phase 1B, NOT in seed:all |
json
"seed:plans": "npx tsx src/scripts/seedPlans.ts",
"seed:stage-types": "npx tsx src/scripts/seedStageTypes.ts",
"seed:screening-questions": "npx tsx src/scripts/seedScreeningQuestions.ts",
"seed:dev-org": "npx tsx src/scripts/seedDevOrg.ts",
"seed:all": "npx tsx src/scripts/seedAll.ts",
"seed:interview-questions": "npx tsx src/scripts/seedInterviewQuestions.ts"seed:all runs via a master seedAll.ts script in dependency order: plans → stageTypes → screeningQuestions → devOrg.
0.3 Backend — StageTypeConfig API
GET /v1/stage-types- Auth: None (public endpoint)
- Logic:
StageTypeConfig.find({ isActive: true }).sort({ order: 1 }) - Response: Array of full
IStageTypeConfigdocuments
This is the single source of truth the frontend uses to render job creation and interview scheduling forms.
0.4 Backend — Organization Bootstrapping
For Phase 0, auto-create an Organization for every new recruiter who signs up:
typescript
// On recruiter registration:
const org = await Organization.create({
name: user.name + "'s Workspace",
planKey: 'free',
featureSnapshot: computeFromPlan('free'),
members: [{ userId: user._id, role: 'admin', joinedAt: new Date() }],
billingEmail: user.email,
isActive: true,
});
await User.findByIdAndUpdate(user._id, { organizationId: org._id });0.5 Frontend — Remove All Hardcoded Stage Configs
| File | Change |
|---|---|
src/components/jobs/JobForm.tsx | Remove STAGE_TYPES constant. On mount, call GET /v1/stage-types. Render stage type dropdown from API response. |
src/components/interviews/ScheduleInterviewForm.tsx | Remove STAGE_FIELD_CONFIG constant. Determine which fields to show from the selected stage's StageTypeConfig.schedulingConfig. |
src/components/jobs/ScreeningQuestionsConfig.tsx | Keep as-is (already fetches from API). Wire to new GET /v1/screening-questions endpoint. |
Cache stage-types in a Context or React Query key — it doesn't change between org plan changes.
0.6 Frontend — stageType.service.ts
typescript
// src/services/stageType.service.ts
export const stageTypeApi = {
getAll: () => axiosInstance.get<IStageTypeConfig[]>('/stage-types'),
};Phase 0 — Acceptance Criteria
- [ ]
npm run seed:allruns cleanly with no errors - [ ]
GET /v1/stage-typesreturns all 6 stage type documents regardless of plan - [ ]
POST /v1/jobsacceptsstageTypeKey(not free-texttype) in each stage - [ ] Job creation returns 403 when org has reached
featureSnapshot.maxActiveJobslimit - [ ]
JobFormdropdown populates from API — no hardcodedSTAGE_TYPESarray remains in frontend - [ ] Existing anonymous meeting flow (
/lobby/:roomId,/room/:roomId) unaffected