Tasks: 18 — Career-Growth Advisor
Status legend:
[ ]pending •[~]in-progress •[x]done •[-]dropped
Phase 1 — Gap aggregation (pure core)
-
T01 — Define career-advisor TS types
- Files:
packages/ai/src/career/types.ts - Acceptance:
- Exports
EvaluationRowInput(subset of anevaluationsrow:jobId,score,band,archetype,jobFamily,blocks.cvMatch.gaps),GapAggregate(normalizedlabel,frequency,affectedArchetypes,affectedFamilies,avgFitImpact,exampleJobIds),GrowthAction(type: "skill"|"project"|"certification",title,sourceGapLabel,effort,impact). - DX-only types; no runtime imports of Zod, DB, or AI SDK.
pnpm check-typesgreen.
- Exports
- Estimate: 0.5 day
- Files:
-
T02 — Implement pure
aggregateGaps+ tests- Files:
packages/ai/src/career/gap-aggregation.ts,packages/ai/src/career/gap-aggregation.test.ts - Acceptance:
aggregateGaps(rows: EvaluationRowInput[]): GapAggregate[]is pure (no DB, no LLM, no I/O); normalizes/merges near-duplicate gap strings; ranks by frequency × fit-impact × archetype-recurrence; applies a minimum-recurrence threshold (≥2) so one-off gaps are dropped.- Tests cover: empty history →
[]; single non-recurring gap → dropped; recurring gap across two archetypes → ranked first; near-identical gap wording merged; deterministic tie-break. pnpm test -- --selectProjects ai --testPathPattern gap-aggregationgreen.
- Estimate: 1 day
- Files:
Phase 2 — Structured artifact + careerAdvisor tool
-
T03 — Growth-plan structured artifact (#5 contract) + tests
- Files:
packages/ai/src/structured/schemas/growth-plan.ts,packages/ai/src/structured/schemas/growth-plan.test.ts,packages/ai/src/structured/index.ts - Acceptance:
growthPlanArtifactdefined viadefineArtifact; exportsgrowthPlanSummarySchema(gaps: GapAggregate[],actions: GrowthAction[]where every action'ssourceGapLabelmust match a listed gap, optionalprojectVerdict: { movesTheNeedle: boolean; rationale; alignedArchetype }) andGROWTH_PLAN_SCHEMA_VERSION.- All string/array fields
.max()-bounded (Article 8.3). Re-exported fromstructured/index.ts. - Tests assert valid object passes
assertArtifact; an action citing a non-existent gap and an over-length string both reject. pnpm test -- --selectProjects ai --testPathPattern growth-plangreen.
- Estimate: 1 day
- Files:
-
T04 — Implement
careerAdvisorTool(grounded, subscription-gated)- Files:
packages/ai/src/tools/career-advisor.ts - Acceptance:
careerAdvisorTool = tool({ description, inputSchema, execute });inputSchemaisz.object({ projectDescription: z.string().max(4000).optional() })(nouserIdparam — injected by orchestrator).execute({ userId, projectDescription }): readsevaluationsforuserIdviadb(@ever-hust/db), runsaggregateGaps, callsrunValidatedGenerationagainstgrowthPlanArtifactwith a prompt grounded strictly in the aggregates (no invented skills/courses), and optionally scoresprojectDescriptionagainst the dominant archetype.- Empty/low history → returns a plain object signalling "not enough history" (no fabricated actions).
- Subscription gate: free-tier (
subscriptionStatusnot inactive/past_due) returns a graceful upgrade object, not a throw. - Returns the validated plain object (canvas-shaped). No auto-action, no external send (Article 4).
- Estimate: 1 day
- Files:
-
T05 —
careerAdvisorToolunit tests- Files:
packages/ai/src/tools/career-advisor.test.ts - Acceptance:
- Mocks
db+ the model. Covers: recurring-gap fixture → schema-valid artifact with each action citing a real gap; empty history → "not enough history" path; project-verdict path returns aligned archetype; free-tier → upgrade object; model returning an ungrounded action → rejected byassertArtifact. pnpm test -- --selectProjects ai --testPathPattern career-advisorgreen.
- Mocks
- Estimate: 1 day
- Files:
Phase 3 — Registration, prompt, persistence
-
T06 — Export + register the tool in the orchestrator
- Files:
packages/ai/src/tools/index.ts,packages/ai/src/agents/orchestrator.ts - Acceptance:
careerAdvisorToolexported fromtools/index.tsand imported inorchestrator.ts.careerAdvisoradded to thetools: { ... }object insidestreamText, injectinguserIdserver-side and applying the subscription check (pattern mirrorsinterviewPrep/resumeBuilder);stopWhen: stepCountIs(5)unchanged.pnpm test -- --selectProjects ai --testPathPattern orchestratorgreen;pnpm check-typesgreen.
- Estimate: 0.5 day
- Files:
-
T07 — Document
careerAdvisorin the system prompt- Files:
packages/ai/src/prompts.ts,packages/ai/src/prompts.test.ts - Acceptance:
getOrchestratorPrompttext describescareerAdvisor(what it does, when to call it, that it drafts development advice — never "apply more", never auto-acts) alongside the existing tool docs.- Mirror the same section into the Langfuse
orchestrator-systemprompt (note in PR; local fallback is source of truth). prompts.test.tsasserts the prompt mentionscareerAdvisor;pnpm test -- --selectProjects ai --testPathPattern promptsgreen.
- Estimate: 0.5 day
- Files:
-
T08 — Add
growthPlanstable + export- Files:
packages/db/src/schema/growth-plans.ts,packages/db/src/schema/index.ts - Acceptance:
- Table follows house style:
idinteger identity PK;userIdtext.notNull().references(() => users.id, { onDelete: "cascade" });archetypetext;summaryjsonb.$type<GrowthPlanSummaryRow>().notNull();createdAttimestamp.notNull().defaultNow();index("growth_plans_user_id_idx").on(table.userId). - Exported from
schema/index.ts(table + row type). pnpm check-typesgreen;pnpm test -- --selectProjects dbgreen.
- Table follows house style:
- Estimate: 0.5 day
- Files:
-
T09 — Apply schema with
pnpm db:push+ best-effort persist- Files:
packages/db/src/schema/growth-plans.ts,packages/ai/src/tools/career-advisor.ts(persist call) - Acceptance:
pnpm db:pushappliesgrowth_plansto the dev DB (idempotent; no existing-table changes).careerAdvisorToolbest-effort inserts each run intogrowthPlans; a write failure is caught and does not fail the tool (artifact still returned). Covered by an added case incareer-advisor.test.ts.
- Estimate: 0.5 day
- Files:
Phase 4 — Canvas sync + Growth UI
-
T10 — Wire canvas sync for
careerAdvisor- Files:
apps/web/hooks/use-canvas-sync.ts - Acceptance:
CanvasStategainsgrowthPlan: GrowthPlanData | null;handleToolResultgainscase "careerAdvisor"that sets it (guarding the empty-history/upgrade shapes); aclearGrowthPlancallback is exported.- Unknown-tool
defaultbranch unchanged.pnpm check-typesgreen.
- Estimate: 0.5 day
- Files:
-
T11 — Growth-plan canvas card
- Files:
apps/web/components/canvas/growth-plan-card.tsx,apps/web/components/canvas/jobs-canvas.tsx,apps/web/components/canvas/dashboard-canvas.tsx - Acceptance:
- New
GrowthPlanCard(template:salary-insights-card.tsx) renders ranked gaps, prioritized actions (each tagged effort/impact + the gap it closes), and the project verdict when present; uses@ever-hust/ui/card/badge+cn(); renders an empty/upgrade state. - Card mounted in
jobs-canvas.tsx/dashboard-canvas.tsxwhengrowthPlanis set, with dismiss viaclearGrowthPlan. pnpm lintandpnpm check-typesgreen.
- New
- Estimate: 1 day
- Files:
-
T12 — Growth page + API route
- Files:
apps/web/app/(dashboard)/growth/page.tsx,apps/web/app/api/growth/route.ts,apps/web/lib/api-schemas.ts,apps/web/components/layout/(sidebar link) - Acceptance:
/growthpage (underrequireSessionUserauth) lists current gaps → recommended actions beside the funnel Insights page; sidebar gains a Growth link./api/growthusesrequireSessionUser(),applyRateLimit(userId, "authenticated"), a Zod body schema inapi-schemas.ts, andapiBadRequest()/apiError()fromapi-response.ts.- Page renders for a fixture artifact;
pnpm lint+pnpm check-typesgreen.
- Estimate: 1 day
- Files:
Phase 5 — E2E + verification
-
T13 — Playwright E2E for the Growth flow
- Files:
tests/e2e/growth.spec.ts,tests/e2e/fixtures/ - Acceptance:
- Seeds a user with recurring-gap
evaluations; opening/growth(or asking the assistant for growth advice) shows prioritized, gap-cited actions and no auto-action. - A free-tier user sees the upgrade nudge and no advisor output.
pnpm test:e2e --grep growthgreen againsthttp://localhost:8443.
- Seeds a user with recurring-gap
- Estimate: 1 day
- Files:
-
T14 — Full CI gate + competitor self-check + roadmap
- Files:
docs/specs/ROADMAP.md - Acceptance:
pnpm lint,pnpm check-types,pnpm test,pnpm test:e2eall green.- Article-11 grep over the diff returns zero competitor references.
ROADMAP.mdmarks epic #18 progress.
- Estimate: 0.5 day
- Files:
Notes
- Write tests alongside each implementation task; do not batch testing into a final task.
- Verify zero competitor references before every commit (see constitution Article 11).
userIdis injected server-side by the orchestrator — it is never an LLM-supplied tool param.- The advisor drafts only — it never enrols, sends, or auto-acts (Article 4); recommendations must be grounded in real gaps (Article 7).
- Update
docs/specs/ROADMAP.mdprogress when an epic's tasks complete.