The Generate Course module is now a full smart course-creation wizard β AI thinks along with the creator, and the finished course lands with its settings applied, its cover generated, and filed into the chosen curriculum section, leaving only the admin publish step
Upload a cover image in Admin Settings > System Manuals to replace this training placeholder.
The Generate Course module is now a full smart course-creation wizard β AI thinks along with the creator, and the finished course lands with its settings applied, its cover generated, and filed into the chosen curriculum section, leaving only the admin publish step. Both apps typecheck clean and all 20 related server tests pass (one unrelated pre-existing test failure was confirmed to also fail on an untouched tree).
Step 1 β Describe (AI thinks along). As the creator types in "What do you want to learn?", a debounced call to the new assist endpoint returns 4 suggestion cards, each a β€20-word hook for a distinct course angle. Clicking a card drops that angle's full long-form description into the description field (still freely editable). A "New ideas" button refreshes the set.
Step 2 β Blueprint & placement. On Continue, the AI drafts the rest of the brief from the step-1 context: learning goals and outline arrive pre-populated (with a "Redraft with AI" button), alongside the existing style/length controls. Below that is the new placement panel: pick a company β curriculum β section from your managed workspaces (via the new curricula picker), or create a new curriculum / new section inline by title.
Step 3 β Settings & launch. Auto-generate cover image tick, Private / Free / Commercial (+price) cards, category select or add-new (AI pre-suggests one), difficulty chips, tags (AI pre-filled), require-final-exam tick, and an "auto-submit for review" tick for public courses β plus a full summary before "Generate course".
The wizard posts a CourseCreationPlan with the existing prefill; it's stored server-side inside the ClassroomPrefill row, and only the prefillId travels through the engine and back in the ingest call (course-creation-plan.ts). At ingest, commerce re-reads the plan from its own DB and verifies the prefill belongs to the course creator β the browser never gets to assert settings, which respects the metering/identity trust boundary from your audit. The plan is one-shot (ingest retries can't re-apply it) but the cover image and review submission self-heal on retry. Placement uses a new targeted appendCourseToCurriculum helper that follows every existing invariant (manage rights, company-controls-course check, unique-order safety, soft-archive awareness) instead of the full-replace sync, so the curriculum builder is untouched. Key details:
Curricula are company-owned, so the plan stamps the course to the curriculum's company first β that's what makes the placement pass the COURSE_NOT_COMPANY_CONTROLLED rule.
"Require exam" creates a real working AI-pooled final exam (the exam runtime generates questions on demand), not just a flag.
The cover image reuses the engine's multi-provider image stack with signed NON_BILLABLE metering headers, runs after the response via after() so ingest never blocks on image providers, and never clobbers a manually-set thumbnail.
Auto submit-for-review mirrors the publish-fee gate and readiness checks β it can't sidestep the fee or ship an incomplete course; commercial visibility falls back to Private if the creator's package doesn't allow marketplace publishing.
AI suggestions are platform-funded like exam generation but metered under a new course_creation category, so admin quotas and abuse controls apply; anonymous/trial users just see the classic wizard.
1. Post-generation mission report β The library page now greets the creator with a "Your course is ready β here's what the wizard did" panel instead of the course silently appearing: creation-mission-report.tsx. It shows a row per action β generated β, settings applied β, cover image, curriculum placement, final exam, admin review queue, learners enrolled β with live spinners for tasks still finishing in the background, and polls /api/lms/create/report every 5s until they settle. Statuses are derived from real course state (thumbnail set, item placed, exam exists, status IN_REVIEW), with a planTasksReport persisted on the prefill row to distinguish "still generating" from "ran and failed" β so failures show actionable messages like "Publish fee due β settle it from the publish page". It's dismissible and remembers dismissals.
2. Cover art choice β The course settings page's thumbnail section gained an "AI covers" button that generates 3 candidates via the new generate-cover route, each with a deliberately different art direction (soft-gradient abstract, bold flat geometric, cinematic photoreal) so they're a real choice. Clicking a candidate sets it as the thumbnail on save; a Regenerate button re-rolls. The generation logic was extracted into a shared course-cover.ts that both this and the wizard's auto-cover use β same signed non-billable metering, same blob storage.
3. Curriculum-aware suggestions β The placement endpoint now returns each curriculum's item titles, and when a curriculum is selected the AI brief prompt includes "this course joins X, which already contains: β¦ β complement the program, don't duplicate, fill gaps." Since placement is chosen after the first blueprint draft, the placement panel gets a "Tailor the goals & outline to this curriculum" button (clearly labeled as replacing the current draft) that redrafts with that context.
4. Streaming suggestions β The assist endpoint now has an SSE mode, and the wizard requests it: suggestion cards pop in one-by-one as the model emits them, each fading in while remaining slots stay skeletons. The streaming layer (create-assist-stream.ts) rides OpenAI-compatible platform keys β which covers your deepseek pool β with string-aware incremental JSON parsing, first-token and total timeouts, and a buffered fallback inside the same stream for Anthropic/Google keys or any streaming failure, so the client contract never changes. Usage is still metered under course_creation.
5. Auto-assign on placement β An "Enroll current learners automatically" tick appears when an existing curriculum is selected. Your seat-consumption concern is enforced server-side, not just in copy: the task runs getCurriculumAssignEntitlement first and only proceeds when the entitlement is free (company-owned path, all courses covered) β license-seat and checkout curricula are skipped with an explanatory line in the mission report ("Skipped β assigning would consume seats; use the company console"). It re-assigns current assignees (which extends their coverage to the new course item), runs in the post-response after() phase, and is one-shot across ingest retries.
Everything follows the same trust posture as round one: the auto-assign flag travels inside the server-side plan, the entitlement check runs at execution time with the creator as actor, and all task outcomes land in the report rather than being assumed. As before, I'd recommend one live run through /create β generate β /library before deploying, since the full loop needs running servers plus LLM and image keys.