Skip to content

LXPack upgrades

Upgrade plan for LXPack maintainers

Forward-looking priorities, responsibility shifts, and API proposals for the LXPack project:

Executive summary

LessonKit authors React courses; LXPack validates, packages, and runs them in LMS contexts. v0.4.0–v0.6.2 delivered SPA lessons, @lxpack/api, packageLessonkit(), @lxpack/spa-bridge, shared conformance, and interchange schema v1. LessonKit 1.0.0 ships @lessonkit/lxpack and lessonkit package on top of that stack.

Ongoing direction: shrink duplicate YAML/bridge logic in @lessonkit/lxpack as LXPack APIs mature; keep LessonKit focused on React authoring and the author CLI.


Current integration (baseline)

What LessonKit does today (@lessonkit/lxpack)

Step Owner today Notes
Validate LessonkitCourseDescriptor LessonKit Uses @lessonkit/core id rules + layout rules
Copy Vite dist/ into LXPack tree LessonKit single-spa{outDir}/dist; per-lesson-spa → per-lesson paths
Emit course.yaml LessonKit Custom minimal YAML emitter (packages/lxpack/src/yaml.ts)
Emit assessments/*.yaml LessonKit From descriptor; also passes structured assessments to buildCourse
Emit lessonkit.json interchange LessonKit format: "lessonkit", version: "1"
Map theme → runtime.cssVariables LessonKit Depends on @lessonkit/themes
validateCourse / buildCourse LXPack (@lxpack/api) After files are on disk
Staging + atomic promote to outDir LessonKit Temp dir; rollback on failure
Runtime bridge (browser) Split LXPack exposes lxpackBridge.v1; LessonKit normalizes scores + forwards telemetry

What LXPack already owns (keep)

  • SCORM 1.2 / 2004 / standalone / xAPI / cmi5 packaging
  • course.yaml schema + path containment
  • Learner shell, flow, native quiz engine, LMS APIs
  • SPA iframe + parent bridge host
  • @lxpack/tracking-schema (native LXPack courses)

Pain points driving this plan

  1. Duplicate manifest authoring — LessonKit re-implements YAML generation and assessment shaping that LXPack validators already understand.
  2. Two sources of truth for assessments — On-disk YAML and buildCourse({ assessments }); easy to drift.
  3. Bridge contract is implicit — Score scale (0–1 vs points), method names, and versioning are documented in LessonKit, not as a typed LXPack package.
  4. No first-class “package from interchange” API — Adapter must write a full LXPack project tree before every build.
  5. Tracking vocabularies diverge — LessonKit telemetry events vs LXPack track() / xAPI export paths are aligned by convention, not schema.
  6. Preview gap — Authors use Vite for dev; LXPack preview requires a materialized project; parity testing is manual.
  7. Conformance — shared matrix exists (@lxpack/conformance); LessonKit 1.0 runs conformance:lxpack and Playwright export parity in CI.

flowchart TB
  subgraph lk [LessonKit — keep]
    REACT["@lessonkit/react\ncomponents, hooks, a11y"]
    CORE["@lessonkit/core\nids, telemetry catalog"]
    XAPI["@lessonkit/xapi\nauthor-time statements"]
    CLI["@lessonkit/cli\ninit, dev, vite build"]
  end

  subgraph lxp [LXPack — expand]
    API["@lxpack/api\nvalidate, build, packageLessonkit"]
    VAL["@lxpack/validators\nlessonkit interchange schema"]
    BRIDGE["@lxpack/spa-bridge\ntyped parent/child SDK"]
    RT["@lxpack/runtime\niframe host + LMS APIs"]
    PKG["@lxpack/scorm, xapi, cmi5"]
    CONF["@lxpack/conformance\nshared fixtures + matrix"]
  end

  subgraph thin [Thin adapter — shrink]
    LKL["@lessonkit/lxpack\noptional: descriptor → interchange only"]
  end

  REACT --> LKL
  CLI --> LKL
  LKL --> API
  API --> VAL
  API --> PKG
  RT --> BRIDGE
  PKG --> LMS["LMS / LRS"]
  RT --> LMS
Concern Should live in Rationale
React components, block catalog, WCAG helpers LessonKit Authoring UX and Studio (future)
courseId / lessonId / checkId validation rules LessonKit (@lessonkit/core) Framework identity; LXPack should accept ids, not define pedagogy
Interchange schema (lessonkit.json v1+) LXPack validators Single schema for any React toolchain, not only LessonKit
Materialize SPA + course.yaml from interchange LXPack API Removes YAML emitters from LessonKit
Assessment normalization (MCQ shape, passingScore semantics) LXPack validators One canonical assessment model at build time
SCORM/xAPI/cmi5 ZIP creation LXPack Already core competency
lxpackBridge contract + client SDK LXPack (@lxpack/spa-bridge) SPA authors (LessonKit or not) import one package
Map LessonKit telemetry → bridge / track() LessonKit (@lessonkit/react) Framework knows event names; calls LXPack SDK
Theme token → runtime.cssVariables LXPack (generic) + LessonKit preset map (thin) LXPack should not depend on @lessonkit/themes
End-to-end conformance fixtures Shared repo or @lxpack/conformance Both CI pipelines run the same matrix
lessonkit package CLI LessonKit UX wrapper calling LXPack API

Proposed LXPack upgrades (prioritized)

P0 — Package LessonKit without a hand-built project tree

Problem: @lessonkit/lxpack writes course.yaml, assessment YAML, copies dist/, writes lessonkit.json, then calls buildCourse. LessonKit maintains parallel YAML emitters.

Proposal: Add to @lxpack/api:

import type { ExportTarget } from "@lxpack/api";

export type PackageLessonkitOptions = {
  /** Merged interchange + course metadata (see schema below). */
  interchange: LessonkitInterchangeV1;
  /** SPA payloads: lesson id → absolute path to folder with index.html */
  spaDirs: Record<string, string>;
  /** Optional in-memory assessments (same shape as buildCourse today). */
  assessments?: AssessmentInput[];
  target: ExportTarget;
  /** Output zip or directory; optional staging courseDir for debugging */
  output?: string;
  dir?: boolean;
  courseDir?: string; // default: temp; kept on failure if debug: true
};

export type PackageLessonkitResult =
  | { ok: true; outputPath?: string; outputDir?: string; fileCount: number; courseDir: string }
  | { ok: false; issues: Array<{ path?: string; message: string; severity?: "error" | "warning" }> };

export function packageLessonkit(options: PackageLessonkitOptions): Promise<PackageLessonkitResult>;

LXPack responsibilities:

  • Generate course.yaml internally from interchange (no consumer YAML emitter).
  • Copy SPA dirs with path containment (reuse existing rules).
  • Prefer only in-memory assessments for LessonKit-sourced quizzes; skip writing assessments/*.yaml unless writeAuthoringFiles: true.
  • Return structured issues (same shape as validateCourse).

Acceptance criteria:

  • examples/lessonkit-spa builds using only @lxpack/api (no checked-in course.yaml required for LessonKit path).
  • LessonKit deletes yaml.ts, assessmentYaml.ts, and most of writeProject.ts.
  • CI: golden LessonKit course packages identically before/after migration (ZIP hash or manifest comparison).

LessonKit follow-up: packageLessonkitCourse() becomes a thin wrapper mapping LessonkitCourseDescriptorPackageLessonkitOptions.


P0 — Formalize lxpackBridge as @lxpack/spa-bridge

Problem: Bridge types and score normalization live in @lessonkit/lxpack/bridge. Non-LessonKit SPAs duplicate logic; versioning is informal.

Proposal: New package (or @lxpack/runtime/bridge export):

// Host (LXPack runtime) — register once
export function createLxpackBridgeHost(options: BridgeHostOptions): LxpackBridgeV1;

// Child (SPA) — safe access from iframe
export function getLxpackBridge(): LxpackBridgeV1 | null;
export function normalizeScore(raw: { score: number; maxScore?: number }): number | null;
export function normalizePassingThreshold(raw: { passingScore?: number; maxScore?: number }): number;

export type LxpackBridgeV1 = {
  completeLesson(lessonId: string): void;
  completeCourse(): void;
  submitAssessment(payload: { id: string; score: number; passingScore?: number }): void;
  track?(event: TrackingSchemaEvent): void;
};

Document in LXPack docs:

Method Score / threshold scale When to call
submitAssessment 0–1 scaled After quiz graded in SPA
YAML passingScore in author assessments absolute points Native LXPack quizzes only

Versioning: Keep window.parent.lxpackBridge.v1; add v2 alongside with a capability negotiation helper (bridge.supportedVersions).

Acceptance criteria:

  • Published TypeScript types on npm.
  • examples/lessonkit-spa imports child SDK from @lxpack/spa-bridge.
  • LessonKit removes duplicate bridge.ts and depends on LXPack package.
  • Validator warns when SPA index.html references deprecated window.parent.lxpack without bridge.

P1 — Own the lessonkit.json interchange schema

Problem: Schema is implied by LessonKit’s LessonkitInterchangeV1 type; LXPack merges file at build time but does not publish a versioned JSON Schema / Zod module.

Proposal:

  • Add @lxpack/validators/lessonkit (or lessonkitInterchangeSchema export).
  • Document fields:
{
  "format": "lessonkit",
  "version": "1",
  "course": { "id": "course-id", "title": "Title" },
  "lessons": [{ "id": "lesson-id", "title": "...", "type": "spa", "path": "dist" }],
  "assessments": [{ "id": "check-id", "passingScore": 1, "questions": [] }],
  "tracking": { "completion": { "threshold": 1 } },
  "runtime": { "theme": "default", "cssVariables": { "--lk-color-primary": "#2563eb" } }
}
  • validateCourse accepts interchange-only projects (generates missing course.yaml).
  • Breaking changes bump version: "2" with migration notes.

Shift from LessonKit: Stop emitting interchange by hand in descriptorToInterchange; optionally generate from descriptor in LessonKit until descriptors are deprecated.

Acceptance criteria:

  • JSON Schema published under docs/reference/lessonkit-interchange.md.
  • Invalid interchange fails validateCourse with path-qualified errors.
  • LessonKit CI imports schema from @lxpack/validators (devDependency) instead of duplicating rules.

P1 — Unified tracking map (LessonKit telemetry ↔ LXPack)

Problem: LessonKit emits lesson_completed, quiz_completed, etc. (@lessonkit/core catalog). LXPack has @lxpack/tracking-schema. Adapters guess mappings for bridge and xAPI.

Proposal:

  • Publish mapLessonkitTelemetryToLxpack(event): TrackingSchemaEvent | null in @lxpack/tracking-schema (or @lxpack/api).
  • Publish verb / activity IRI recommendations for packaged courses (xAPI export).
  • Optional: bridge.track() accepts canonical events so SPAs can forward rich telemetry without custom LMS code.

Reference mapping (starter):

LessonKit TelemetryEventName LXPack / xAPI intent
course_started course initialized
course_completed course completed
lesson_started lesson initialized
lesson_completed completeLesson + completed verb
quiz_answered interaction / answered
quiz_completed submitAssessment + scored
interaction track({ type, id, data })

Acceptance criteria:

  • Table is documented and unit-tested in LXPack.
  • LessonKit @lessonkit/react uses exported mapper (delete bespoke switch in lxpackBridge.ts).
  • xAPI ZIPs from LessonKit courses use consistent activity ids derived from interchange course.id / lesson ids.

P1 — Theme interchange without @lessonkit/themes

Problem: themeToLxpackRuntime() in LessonKit depends on @lessonkit/themes to produce runtime.cssVariables.

Proposal:

  • Interchange carries optional runtime.cssVariables (already natural in YAML).
  • LXPack applies variables to learner shell (already supports runtime.cssVariables in course.yaml).
  • Optional: runtime.themePreset: "lessonkit:brand" resolved via a registered preset table in LXPack or passed fully expanded in interchange.

Shift: LessonKit only expands presets at package time (one function), or authors commit expanded variables in lessonkit.json. LXPack does not import LessonKit packages.


P2 — Preview and validate from SPA build output

Problem: Developers run lessonkit dev (Vite) but must materialize .lxpack/course to use lxpack preview with LMS chrome.

Proposal:

lxpack preview --lessonkit ./lessonkit.json --spa dist/
  • Starts runtime with SPA lesson iframe + bridge host.
  • Optional SCORM simulator flags (existing lxpack.config.json preview modes).

Acceptance criteria:

  • Documented workflow in LessonKit interoperability guide.
  • No assessments/*.yaml required on disk when passing --assessments JSON path or embedded in interchange.

P2 — SCORM layout recipes for LessonKit

Problem: LessonKit supports single-spa (one SCO, in-app navigation) vs per-lesson-spa (multi-SCO). Authors lack LXPack guidance on SCORM 2004 sequencing for each.

Proposal:

  • Document recipes in LXPack:
  • Recipe A — Single SCO SPA (default LessonKit): one type: spa lesson; completion via bridge.
  • Recipe B — Multi SCO: one SPA folder per lessonId; flow optional; map lesson_completed per SCO.
  • Validators emit warnings when interchange lessons omit path or id collisions would break SCORM 2004.
  • SCORM SPA layout is determined by build/packageLessonkit target (SCORM 1.2 = single SCO; SCORM 2004 = multi-SCO) and lesson count; use inferScormSpaLayout() from @lxpack/validators for docs/warnings only.

Open question for maintainers: Should multi-lesson React apps ever share one SPA build (hash router), or should LXPack enforce per-lesson builds for true multi-SCO?


P2 — Shared conformance harness (@lxpack/conformance or test/fixtures/lessonkit)

Problem: Export parity is covered by LessonKit 1.0 e2e/conformance scripts and @lxpack/conformance; keep fixtures aligned when changing export targets.

Proposal:

  • Git submodule or npm package with:
  • Minimal interchange + tiny SPA fixture (static HTML + one quiz call to bridge).
  • Matrix: standalone, scorm12, scorm2004, xapi, cmi5.
  • Expected: launch succeeds, completeLesson marks complete, submitAssessment records score.
  • Both repos run npx @lxpack/conformance in CI.

Acceptance criteria:

  • LXPack release workflow runs conformance on tag.
  • LessonKit packaging smoke imports same package (no forked fixture copies).

P3 — Optional @lxpack/lessonkit meta-package

Problem: Consumers install @lessonkit/lxpack + @lxpack/api and must understand Node 18 or 20, interchange, bridge.

Proposal:

  • @lxpack/lessonkit re-exports packageLessonkit, bridge SDK, schema types.
  • Peer dependency: react not required (packaging only).
  • LessonKit deprecates @lessonkit/lxpack over a major version with migration guide.

What should stay in LessonKit (non-goals for LXPack)

  • React component library and hooks (Course, Lesson, Quiz, …).
  • Authoring-time telemetry sinks and @lessonkit/xapi client (distinct from LMS-export xAPI inside LXPack).
  • Vite template, lessonkit init, lessonkit dev, lessonkit build.
  • Block catalog for AI/Studio (block-catalog.v1.json).
  • WCAG primitives (@lessonkit/accessibility).

Non-goals (both projects):

  • Merging repositories.
  • Replacing LXPack markdown/HTML authoring with LessonKit-only workflows.
  • Re-implementing SCORM manifest generation in LessonKit.

LXPack release history and future work

Shipped (v0.5.0–v0.6.3):

Release Theme Key deliverables
v0.5.0 Thin packaging packageLessonkit(), interchange Zod schema, lxpack build --lessonkit
v0.6.0 Bridge SDK + conformance @lxpack/spa-bridge, telemetry map, lxpack preview --lessonkit, @lxpack/conformance, @lxpack/lessonkit
v0.6.1 Packaging fixes Omit interchange from LMS ZIPs; config resolution beside lessonkit.json
v0.6.2 Node 18 + runtime fixes Node 18/20 CI matrix; navigation fixes; packageLessonkit({ configDir })
v0.6.3 Flow + nav patch Position-aware flow (from); sidebar/Prev/Next UX; CLI lessonkit config parity

Future (v0.7+):

Release Theme Key deliverables
v0.7+ Platform Plugin slot for custom lesson runtimes; bridge v2 negotiation
v0.7+ AI tooling Claude integration, AI repair (Phase 5)

Coordinate with LessonKit 1.0.0 (stable @lessonkit/* public API; lessonkit.readthedocs.io).


API stability commitments (request to LXPack)

LessonKit 1.0.0 needs these to remain stable (or versioned with migration):

  1. lxpackBridge.v1 method signatures and 0–1 score semantics for submitAssessment.
  2. lessonkit.json format + version with documented migration path.
  3. ExportTarget enum and buildCourse / packageLessonkit result shapes.
  4. SPA lesson type: spa + path pointing at folder with index.html.

How to validate with LessonKit today

Maintainers can verify changes against the LessonKit repo without adopting the full monorepo:

git clone https://github.com/eddiethedean/lessonkit.git
cd lessonkit
npm ci
npm run build
npm -w lessonkit-example-lxpack-golden run build
npm -w lessonkit-example-lxpack-golden run package:scorm12

Artifacts: examples/lxpack-golden/.lxpack/course/.lxpack/out/course-scorm12.zip

Contact surface: open issues in either repo with label interop/lessonkit; attach interchange JSON and buildCourse / packageLessonkit structured errors.


Summary for LXPack maintainers

  1. Absorb project materializationpackageLessonkit() so adapters stop writing YAML by hand.
  2. Own interchange + bridge — schema, typed SDK, explicit score semantics.
  3. Publish tracking maps — one table from LessonKit telemetry to LXPack/LRS.
  4. Ship shared conformance — both projects gate releases on the same fixture matrix.
  5. Keep LessonKit thin — React authoring only; LMS delivery stays in LXPack.

Document Audience
Product ROADMAP — Phase 0.6 Shipped v0.6.0–v0.6.2 scope
LessonKit packaging guide LessonKit authors
Historical upgrade checklist What v0.4.0–v0.6.2 shipped
LXPack LessonKit interoperability LXPack docs (maintain alongside this plan)
LessonKit ROADMAP 1.0 framework; LessonKit Studio (visual authoring) post-1.0

Historical checklist (LessonKit team)

What LessonKit requested before LXPack v0.4.0, and integration status from the LessonKit side:

This document captures the improvements we wanted in LXPack so it works better as the packaging and LMS export layer for LessonKit.

LessonKit is React-first authoring (@lessonkit/react). LXPack is a manifest-driven compiler and runtime (course.yaml, markdown/HTML/component lessons, SCORM/xAPI/cmi5 export). The two projects are complementary: LessonKit owns the developer experience; LXPack owns validation, preview, and LMS artifacts.

Related LessonKit docs (github.com/eddiethedean/lessonkit):


Current integration (LessonKit 1.0)

LessonKit’s shipped path:

  1. Author courses in React with @lessonkit/react (courseId, lessonId, checkId).
  2. Describe the course in a LessonkitCourseDescriptor or root lessonkit.json (schemaVersion: 1).
  3. Build with Vite (lessonkit builddist/).
  4. Package with @lessonkit/lxpack (packageLessonkitCourse) or lessonkit package --target scorm12.

LXPack validates, runs the learner shell, and emits SCORM/xAPI/cmi5 artifacts via @lxpack/api 0.6.4+.

See LessonKit packaging and examples/lxpack-golden.


Historical integration plan (pre–SPA lesson type)

Before LXPack type: spa and @lessonkit/lxpack, exporting React courses required lossy translation to markdown/HTML. The goals below drove the v0.4–v0.6 interoperability work (now shipped).

Design goals

Goal Why it matters
Preserve React authoring LessonKit users should not rewrite courses as YAML/markdown to ship to an LMS.
Stable identity model courseId, lessonId, assessment ids must map 1:1 into tracking, xAPI, and SCORM suspend data.
Shared tracking semantics Completion, quiz pass/fail, and time-on-task should mean the same thing in both runtimes.
Programmatic packaging @lessonkit/lxpack needs library APIs, not only CLI subprocesses.
npm-first consumption LessonKit uses npm workspaces; LXPack packages should install cleanly without requiring pnpm for consumers.

Gap analysis

1. Authoring model mismatch

LXPack today LessonKit today
Declarative course.yaml + file-based lessons JSX component tree (Course, Lesson, Quiz, …)
Lesson types: markdown, html, component Rich React composition, custom layout, app state
Built-in widgets (callout, image-card, …) Framework primitives + user-defined UI

Pain: Exporting LessonKit → LXPack today implies serializing React to markdown/HTML or re-implementing interactions as LXPack component lessons. That breaks fidelity and accessibility work done in React.

2. No first-class “hosted React bundle” lesson type

LXPack can package standalone web apps, but there is no documented lesson type for:

  • A Vite/React build output as a lesson SCO with known entry (index.html)
  • Wiring that lesson into flow, completion rules, and multi-SCO SCORM 2004

Pain: LessonKit’s natural artifact is a built SPA, not a folder of markdown files.

3. CLI-centric integration surface

LXPack’s primary interface is @lxpack/cli (init, preview, validate, build). LessonKit needs:

  • validateCourse(project) / buildCourse(project, target) as importable functions
  • Typed options and structured errors (for CI and @lessonkit/lxpack)

Pain: Subprocess + stdout parsing is fragile for monorepo CI and IDE integrations.

4. Tracking and xAPI vocabulary alignment

LessonKit (0.1.x) emits telemetry events and minimal xAPI statements (started, completed). LXPack has mature tracking, completion thresholds, quiz YAML, and export-time embedding.

Pain: Without a shared event/verb map, adapters guess at semantics and LRS reports diverge.

5. Assessment model differences

LXPack LessonKit
Author YAML in assessments/; keys embedded at build Inline Quiz / KnowledgeCheck in React
passingScore, maxAttempts, shuffle, feedback modes Simple correct/incorrect + useQuizState hooks

Pain: Export must invent assessment YAML from React props or lose quiz metadata.

6. Theming and accessibility

LessonKit targets WCAG 2.1 AA with React semantics and @lessonkit/accessibility helpers. LXPack runtime uses markdown sanitization, HTML interactions, and runtime.theme CSS classes.

Pain: Branding and a11y behavior may differ between preview (LXPack) and author preview (LessonKit/Vite) unless theme contracts align.


Prioritized from highest leverage for LessonKit to nice-to-have.

P0 — React / SPA lesson type

Proposal: Add a lesson type (working name: react or spa) to course.yaml:

lessons:
  - id: phishing-101
    title: Phishing Awareness
    type: spa
    path: dist/lessons/phishing-101   # folder with index.html + assets
    runtime:
      mount: root                      # optional; default #root

Behavior:

  • Package the folder as a launchable unit in standalone, SCORM 1.2, SCORM 2004 (SCO), xAPI, cmi5.
  • Expose a stable parent bridge (window.parent.lxpack or postMessage) for:
  • completeLesson({ lessonId })
  • reportAssessment({ id, score, passed })
  • optional xAPI statement passthrough

Why: Lets LessonKit ship vite build output per lesson without converting UI to markdown.

Acceptance criteria:

  • Example course in LXPack repo: one spa lesson + one markdown lesson in the same package.
  • SCORM 2004 multi-SCO export launches each SPA in an iframe with correct sequencing.
  • Documented bridge API versioned (lxpackBridge.v1).

P0 — Programmatic build and validate API

Proposal: Export stable functions from @lxpack/cli or a new @lxpack/api package:

import { validateCourse, buildCourse } from "@lxpack/api";

const result = await validateCourse({ courseDir: "/path/to/course", target: "scorm12" });
const artifact = await buildCourse({ courseDir, target: "scorm2004", output: "./out.zip" });

Requirements:

  • Structured result: { ok, errors: [{ path, rule, message }], warnings }
  • No global process cwd assumptions; all paths explicit
  • Works when imported from npm (LessonKit) without pnpm

Why: Enables @lessonkit/lxpack to run in CI and tests without shelling out.


P1 — Import / interchange schema (lessonkit.json or lxpack.import)

Proposal: Support an optional interchange file at course root:

{
  "format": "lessonkit",
  "version": "1",
  "course": { "id": "cyber-basics", "title": "Cybersecurity Basics" },
  "lessons": [
    {
      "id": "phishing-101",
      "title": "Phishing Awareness",
      "type": "spa",
      "build": { "command": "npm run build", "outputDir": "dist" }
    }
  ],
  "assessments": [],
  "tracking": { "completion": { "threshold": 0.9 } }
}

Behavior:

  • lxpack validate merges interchange + generated course.yaml (or generates yaml at build time).
  • Validators understand LessonKit ids and required fields.

Why: Reduces duplication between LessonKit metadata and hand-maintained course.yaml.


P1 — Shared tracking event catalog

Proposal: Document and export a shared enum / JSON schema for learning events:

Event xAPI verb (suggested) SCORM mapping
lesson_started initialized / custom cmi.core.lesson_status
lesson_completed completed completion
quiz_answered answered interaction
quiz_completed completed score
course_completed completed course complete

Publish as @lxpack/tracking-schema (or extend @lessonkit/core with LXPack-compatible exports).

Why: LessonKit and LXPack runtimes report the same analytics to LRS and internal sinks.


P1 — Assessment interchange from structured data

Proposal: Allow assessments to be defined in JSON/YAML or supplied at build time:

buildCourse({
  courseDir,
  target: "scorm12",
  assessments: [{ id: "final_quiz", questions: [...] }], // validated by @lxpack/validators
});

Why: @lessonkit/lxpack can extract Quiz props / config from React without writing assessments/*.yaml to disk.


P2 — Plugin slot for custom lesson runtimes

Proposal: Formal plugin API in @lxpack/cli / @lxpack/runtime:

registerLessonRuntime("lessonkit-react", {
  validate(lesson, ctx) { ... },
  bundle(lesson, ctx) { ... },
  preview(lesson, ctx) { ... },
});

Why: LessonKit can register a runtime once instead of forking LXPack lesson types.


P2 — Theme token bridge

Proposal: Accept external design tokens (CSS variables) from a theme/tokens.json or @lessonkit/themes export:

runtime:
  theme: lessonkit-default
  cssVariables:
  --lk-color-primary: "#2563eb"

Why: Visual parity between LessonKit dev preview and LXPack-packaged learner view.


P3 — Documentation and examples (shipped)

Delivered in LXPack v0.6.x:

Suggested division of responsibility

flowchart TB
  subgraph author [Authoring]
    LK["LessonKit React app\n(@lessonkit/react)"]
  end

  subgraph bridge [Bridge - shipped]
    LKL["@lessonkit/lxpack\nexport + metadata"]
  end

  subgraph lxpack [LXPack]
    VAL["@lxpack/validators"]
    CLI["@lxpack/cli / @lxpack/api"]
    PKG["@lxpack/scorm / xapi / cmi5"]
    RT["@lxpack/runtime"]
  end

  subgraph lms [Delivery]
    LMS["LMS / LRS / browser"]
  end

  LK --> LKL
  LKL --> VAL
  LKL --> CLI
  CLI --> PKG
  PKG --> LMS
  RT --> LMS
Layer Owner Responsibility
Authoring UX LessonKit Components, hooks, a11y, Vite templates
Export adapter @lessonkit/lxpack Build SPA(s), emit interchange + invoke LXPack
Validation & packaging LXPack Schema, path containment, SCORM/xAPI/cmi5 ZIPs
Learner runtime LXPack (+ SPA bridge) Navigation, flow, LMS APIs, quiz engine where applicable

Phased rollout (cross-repo)

Phase LXPack LessonKit
1 Document SPA lesson type + bridge API (even if experimental) Spike @lessonkit/lxpack export to static dist/
2 Ship @lxpack/api validate/build Wire lessonkit packagelxpack build
3 Tracking schema + assessment build injection Align @lessonkit/xapi verbs with schema
4 Plugin runtime registration Optional: embed @lxpack/runtime navigation shell around SPA

Non-goals (for now)

  • Merging the two repos into one monorepo
  • Replacing LXPack markdown authoring with LessonKit-only workflows
  • Requiring LessonKit authors to learn full course.yaml before they can ship

Open questions for LXPack maintainers

  1. Single-SCO vs multi-SCO: Should a LessonKit Course map to one SCORM package or one SCO per Lesson?
  2. Answer keys in SPA lessons: Should quiz scoring stay in LXPack runtime only, or allow client-side scoring inside the SPA with signed/embedded config?
  3. Versioning: How should lxpackBridge.v1 evolve without breaking published LessonKit courses?
  4. npm vs pnpm: Can release CI guarantee @lxpack/* packages work as npm dependencies in LessonKit’s workspace?

Summary

LXPack already solves problems LessonKit should not rebuild (SCORM manifests, ZIP packaging, xAPI/cmi5, validation, preview). The highest-value upgrades for LessonKit interoperability are:

  1. SPA/React lesson type with a stable LMS bridge API
  2. Programmatic validate/build APIs for tooling and CI
  3. Shared tracking and assessment interchange so React authoring maps cleanly to exports

Implementing P0 items unblocks @lessonkit/lxpack and delivers LMS-ready packages without forcing authors out of React.