Tasks: 12 — Interview Prep + STAR Story Bank
Status legend:
[ ]pending •[~]in-progress •[x]done •[-]dropped
Phase 1 — Data model (story bank + sessions)
-
T01 —
interview_storiestable- Files:
packages/db/src/schema/interview-stories.ts(new); export frompackages/db/src/schema/index.ts - Acceptance:
- Table
interview_storieswith house-style PKinteger("id").primaryKey().generatedAlwaysAsIdentity();userId text("user_id").notNull().references(() => users.id, { onDelete: "cascade" }). - Columns:
title text,situation text,task text,action text,result text,reflection text(the seniority signal),tags jsonb("tags").$type<string[]>().default([]),createdAt/updatedAt timestamp().notNull().defaultNow(). - Index
interview_stories_user_id_idxon(userId). - Importable as
interviewStoriesfrom@ever-hust/db.
- Table
- Estimate: 0.5 day
- Files:
-
T02 —
interview_sessionstable- Files:
packages/db/src/schema/interview-sessions.ts(new); export frompackages/db/src/schema/index.ts - Acceptance:
- Table
interview_sessionswith identity PK;userIdFK (onDelete: "cascade");jobId integer("job_id").references(() => jobs.id, { onDelete: "cascade" }). type text("type", { enum: ["prep","mock"] }).notNull();summary jsonb("summary").$type<...>();createdAt/updatedAttimestamps.- Indexes:
interview_sessions_user_id_idxon(userId),interview_sessions_user_job_idxon(userId, jobId). - Importable as
interviewSessionsfrom@ever-hust/db.
- Table
- Estimate: 0.5 day
- Files:
-
T03 — Schema unit test + DB push
- Files:
packages/db/src/schema/interview-schema.test.ts(new); runpnpm db:push - Acceptance:
- Test asserts both tables expose expected columns + the
typeenum values["prep","mock"]. pnpm test -- --selectProjects dbgreen;pnpm db:pushapplies cleanly to the dev database with no destructive diff.
- Test asserts both tables expose expected columns + the
- Estimate: 0.5 day
- Files:
Phase 2 — Story bank tool + seed-from-#3 flow
-
T04 —
storyBankTool(CRUD + structured output)- Files:
packages/ai/src/tools/story-bank.ts(new) - Acceptance:
tool({ description, inputSchema, execute })with anactionenum["list","create","update","delete","seed"]; all string/array inputs.max()-bounded;userIdoptional + injected server-side (never LLM-supplied).create/update/delete/listread+writeinterviewStoriesscoped touserId; returns a Zod-.parse()-validated structured object alongside prose guidance (Article 5).seedreturns candidate STAR+Reflection stories (grounded in passed-in #3 Block-F seeds or user input) and does NOT persist — persistence only via a subsequent approvedcreate.- Preserves Pro gate (
subscriptionStatus in ("active","past_due")); no fabricated experience/employers/numbers.
- Estimate: 1 day
- Files:
-
T05 —
storyBankToolunit tests- Files:
packages/ai/src/tools/__tests__/story-bank.test.ts(new) - Acceptance:
- Tests cover each
action; assert the structured-output schema shape; assertseedwrites nothing (approval gate, Article 4); assert non-Pro returnsrequiresUpgrade; assert no-invent (seeded fields come only from inputs). pnpm test -- --selectProjects aigreen.
- Tests cover each
- Estimate: 0.5 day
- Files:
-
T06 — Export + register
storyBankTool- Files:
packages/ai/src/tools/index.ts;packages/ai/src/agents/orchestrator.ts - Acceptance:
export { storyBankTool } from "./story-bank"added.- Registered in the orchestrator
tools: { ... }object with a wrappedexecuteinjectinguserId(pattern offavoriteJob/savePreferences);stopWhen: stepCountIs(5)unchanged. pnpm check-types+pnpm test -- --selectProjects aigreen.
- Estimate: 0.5 day
- Files:
Phase 3 — Audience-segmented per-job prep
-
T07 — Upgrade
interviewPrepToolto emit an audience-segmented artifact- Files:
packages/ai/src/tools/interview-prep.ts - Acceptance:
- Returns, alongside the existing
context/instruction, a Zod-.parse()-validatedartifactsegmented by audiencerecruiter | hiring_manager | panel | exec, each withthemes,questions,talkingPoints, plussuggestedStoryIds(matched from the user'sinterviewStories). - Reads #3's structured evaluation when present; degrades gracefully (job + profile only) when absent.
- Existing Pro gate + input contract preserved (backward-compatible).
- Returns, alongside the existing
- Estimate: 1 day
- Files:
-
T08 — Persist a
prepsession + prep unit tests- Files:
packages/ai/src/tools/interview-prep.ts;packages/ai/src/tools/__tests__/interview-prep.test.ts(new) - Acceptance:
- On success, writes an
interviewSessionsrowtype: "prep"(userId,jobId,summaryjsonb). - Tests assert the artifact schema, the four audience segments, the graceful-degradation path (no #3 data), and that a
prepsession row is written. pnpm test -- --selectProjects aigreen.
- On success, writes an
- Estimate: 0.5 day
- Files:
Phase 4 — Mock mode (Q&A + grounded feedback)
-
T09 —
mockInterviewTool+mockFeedbackTool- Files:
packages/ai/src/tools/mock-interview.ts(new) - Acceptance:
mockInterviewToolpresents structured questions for ajobId(segmented by audience);mockFeedbackToolaccepts a captured answer (.max()-bounded) and returns Zod-.parse()-validated structured feedback grounded only in the answer text + job data (no fabricated experience/numbers, Article 7 / #6).mockFeedbackToolwrites aninterviewSessionsrowtype: "mock"with ajsonbsummary; Pro gate preserved;userIdinjected server-side.- Mock mode never submits/sends anything anywhere.
- Estimate: 1 day
- Files:
-
T10 — Mock-mode unit tests
- Files:
packages/ai/src/tools/__tests__/mock-interview.test.ts(new) - Acceptance:
- Tests assert question + feedback structured-output schemas; assert grounded/no-invent contract; assert a
mocksession row is written; assert non-Pro returnsrequiresUpgrade. pnpm test -- --selectProjects aigreen.
- Tests assert question + feedback structured-output schemas; assert grounded/no-invent contract; assert a
- Estimate: 0.5 day
- Files:
-
T11 — Export + register mock tools
- Files:
packages/ai/src/tools/index.ts;packages/ai/src/agents/orchestrator.ts - Acceptance:
mockInterviewTool+mockFeedbackToolexported and registered in the orchestratortools: { ... }withuserId-injectingexecutewrappers;stopWhen: stepCountIs(5)unchanged.pnpm check-types+pnpm test -- --selectProjects aigreen.
- Estimate: 0.5 day
- Files:
Phase 5 — Canvas + UI surfaces
-
T12 — Canvas-sync cases for the new tools
- Files:
apps/web/hooks/use-canvas-sync.ts - Acceptance:
CanvasStateextended withinterviewPrep,storyBank,mockSessionfields (+ clear callbacks likeclearSalaryInsights).handleToolResultgainscase "interviewPrep",case "storyBank",case "mockInterview",case "mockFeedback"updating canvas state (pattern of the existingsalaryInsightscase).pnpm check-typesgreen.
- Estimate: 0.5 day
- Files:
-
T13 — Interview prep + mock overlay cards
- Files:
apps/web/components/canvas/interview-prep-card.tsx(new);apps/web/components/canvas/mock-interview-card.tsx(new) - Acceptance:
- Prep card renders the four audience segments + suggested bank stories; mock card renders question → answer input → feedback; both modelled on
salary-insights-card.tsx, using@ever-hust/ui/{card,button,badge}+cn()from@ever-hust/ui/lib/utils. - Cards are wired into the canvas render path (
apps/web/components/canvas/jobs-canvas.tsx/ dashboard canvas) and render when their state is set.
- Prep card renders the four audience segments + suggested bank stories; mock card renders question → answer input → feedback; both modelled on
- Estimate: 1 day
- Files:
-
T14 — Story-bank manager card + CRUD API routes
- Files:
apps/web/components/canvas/story-bank-card.tsx(new);apps/web/app/api/interview/stories/route.ts(new);apps/web/app/api/interview/stories/[id]/route.ts(new);apps/web/lib/api-schemas.ts - Acceptance:
- Routes use
requireSessionUser(),applyRateLimit(userId, "authenticated"), Zod request schemas fromapps/web/lib/api-schemas.ts, andapiBadRequest()/apiError()fromapps/web/lib/api-response.ts; all queries scoped to the sessionuserId. GET/POSTon/stories,PATCH/DELETEon/stories/[id]; the card round-trips create/edit/delete against these routes (draft→explicit save; never auto-persists).- Validation rejects oversized/invalid payloads (Zod
.max()bounds).
- Routes use
- Estimate: 1 day
- Files:
-
T15 — API-route unit tests
- Files:
apps/web/app/api/interview/stories/route.test.ts(new) - Acceptance:
- Tests assert auth required, rate-limit applied, Zod rejection on bad payloads, and
userId-scoping (a user cannot read/edit another user's story). pnpm test -- --selectProjects web-libgreen.
- Tests assert auth required, rate-limit applied, Zod rejection on bad payloads, and
- Estimate: 0.5 day
- Files:
Phase 6 — Prompt, E2E + docs
-
T16 — System-prompt update (local + Langfuse)
- Files:
packages/ai/src/prompts.ts(mirror into Langfuseorchestrator-system, labelproduction) - Acceptance:
- Tool list documents
storyBank,mockInterview,mockFeedback; the "Interview Prep" section describes audience-segmented prep, the draft→approve story-bank flow, and grounded mock feedback (no-invent). - Existing
prompts.test.tsassertions still pass;pnpm test -- --selectProjects aigreen; zero competitor references.
- Tool list documents
- Estimate: 0.5 day
- Files:
-
T17 — E2E: generate prep + save story
- Files:
tests/e2e/interview.spec.ts(new) - Acceptance:
- Spec drives the chat to generate audience-segmented prep for a job (prep card renders) and to save a STAR+Reflection story (story appears in the bank manager and persists on reload).
- Runs against
http://localhost:8443;pnpm test:e2egreen.
- Estimate: 1 day
- Files:
-
T18 — Docs + competitor self-check + ROADMAP
- Files:
docs/specs/12-interview-prep-story-bank/spec.md(## Decisions);docs/specs/ROADMAP.md - Acceptance:
- Resolved Open Questions moved to the spec's
## Decisions; ROADMAP progress bumped for epic 12. - Grep of all changed files for competitor names returns empty (Article 11); full CI (lint, type-check, unit, E2E) green on
develop.
- Resolved Open Questions moved to the spec's
- Estimate: 0.5 day
- Files:
Notes
- Write tests alongside each implementation task; do not batch testing into a final task.
userIdis injected server-side by the orchestrator — never an LLM-supplied tool param.- Every AI artifact emits a Zod-validated object alongside prose (Article 5); story-bank seed and any save are draft → explicit approval (Article 4); mock feedback is grounded/no-invent (Article 7).
- Verify zero competitor references before every commit (constitution Article 11).
- Update
docs/specs/ROADMAP.mdprogress when this epic's tasks complete.