Cutting SCORM packaging from 4 hours to 10 minutes

Role: Senior Instructional Designer & SCORM Team Lead, Kidvento Team: 8 (4 developers, 4 interns) Output affected: 100+ K-12 SCORM packages

The problem

After every Storyline publish, the team ran a manual checklist before a module could ship:

  1. Unzip the published package.
  2. Open imsmanifest.xml, verify the SCO metadata and mastery score.
  3. Inject the LMS-specific reporting hook into the launch HTML.
  4. Patch the completion-status flag — Storyline marks incomplete on close even after a passed assessment.
  5. Sanity-check that every interaction has a unique cmi.interactions.N.id.
  6. Re-zip with the right directory structure.
  7. Smoke-test in the staging LMS.
  8. Hand off.

On a clean module this took roughly 4 hours per package. On a module that came back from QA, the four hours started over.

The change

I built a Node CLI that automates steps 2–6 and replaces step 7 with a deterministic check. Per-module effort dropped to about 10 minutes.

What the script actually does:

What I learned

The 4-hour packaging step had been hiding bugs we hadn't noticed.

Three findings drove the rewrite:

  1. **The lesson_status race.** Storyline writes incomplete on beforeunload even after a passed assessment. Our LMS read the last write and ignored the earlier passed. The override has to be set after Storyline's own unload handler — load order matters.
  2. Manifest namespace drift. Storyline 360 occasionally emits manifests where the adlcp namespace declaration sits on a child element. Most LMSs forgive this; two of our targets did not. The patcher normalises it at the root regardless.
  3. Silent interaction-ID collisions. Storyline's auto-numbered interaction IDs reset between scenes. A copy-pasted slide across scenes produces two interactions with the same id. The LMS records the last one and discards the first. Worth catching at build time, because the data loss is otherwise invisible.

The auditor catches all three now. Two of them were not on my radar before I started building the pipeline. The interesting outcome of automating a tedious manual step is that you often find the bugs the manual step was masking.

Impact

Stack

JavaScript (Node), shell, headless Chromium via Puppeteer, XML parsing, the SCORM 1.2 RTE spec.

Ownership

The script is internal to Kidvento. The findings — lesson_status race, namespace drift, interaction-ID collisions — generalise across Storyline 360 workflows, which is why this writeup exists. The open-source equivalent of the auditor is scorm-lint, in the same portfolio.