Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/COURSE_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,3 +112,36 @@ Remaining minor deviations (not requested to fix):
`trackingPassed="80%"`, `trackingMode="full"`, per-quiz `trackingWeight`
1×7 + 21). Backups in `course_backup/`. The course now fulfills
the specification in all major respects.

## LMS completion fix (post-0.0.3)

**Problem reported on LMS **: the 0.0.3 release reported
**0% completion** to the LMS even though the learner finished every quiz.
A HAR capture showed `cmi.core.score.raw` was sent (75, then 100) but
`cmi.core.lesson_status` was never reported as `passed`/`completed`/`failed`,
and `cmi.core.suspend_data.completedPages` was `[false, true, true, …]` —
index 0 (the first content page, "Welcome") was never marked complete.

**Root cause**: a Xerte Nottingham off-by-one between `toCompletePages`
(built from `currActPage` over the 43 content nodes, before the built-in
Table-of-Contents menu page is inserted as `x_pages[0]`) and `page_nr` (used
by `exitInteraction`, which is `currActPage + 1` for every content page
because the menu takes `page_nr == 0`). `completedPages[0]` could only be set
by exiting the menu (which never happens), so `getSuccessStatus()` always
returned `'incomplete'`. Full analysis in `PROJECT_CONTEXT.md` § "SCORM
completion bug fix".

**Fix applied**: `unmarkForCompletion="true"` on the Welcome page
(`linkID="PG1781882348053"`) in `source/data.xml` + `source/preview.xml`,
shifting `toCompletePages` to `[1,2,…,42]` so it aligns with the content
pages' `page_nr`. Verified locally with a SCORM 1.2 mock API harness: a
completed run now sends `cmi.core.lesson_status="passed"` (+ `exit`,
`score.raw`, `score.min`, `score.max`, `session_time`).

**Pending (convention 9 — MANDATORY before merge)**: push the updated
`source/data.xml` + `source/preview.xml` into a running XOT instance,
confirm `play.php` returns 200, export a SCORM package, and grep the exported
`template.xml` to confirm `unmarkForCompletion="true"` is present on the
Welcome page and the question count / `trackingWeight` / `trackingPassed` /
empty-option count are unchanged. Then re-test on LMS that a
completed attempt reports `lesson_status=passed` and a non-zero grade.
53 changes: 53 additions & 0 deletions docs/PROJECT_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ with agents and building agents.
score reporting verified (`apiwrapper_1.2.js` + `xttracking_scorm1.2.js` →
`cmi.core.score.raw` + `lesson_status`).
- **No Trial MCQ** (an early trial page was removed).
- **Welcome page carries `unmarkForCompletion="true"`** — see "SCORM completion
bug fix" below for why; it is the fix for LMS completion tracking.
- `play.php` returns 200; the export is `Secure_code_development_scorm.zip`.

## Conventions (MANDATORY for any content changes)
Expand Down Expand Up @@ -138,6 +140,57 @@ These were agreed with the course owner and must be preserved:
rich HTML renders correctly, and `delaySecs="0"` disables the timed reveal).
Plain Text would be "purer" but converting 27 pages is not worth the risk.

## SCORM completion bug fix (0.0.4 — `unmarkForCompletion` on Welcome)

**Symptom**: on LMS the course reported **0% completion**
even though the learner finished every quiz and `cmi.core.score.raw` was
being sent (75, then 100). `cmi.core.lesson_status` was never reported as
`passed`/`completed`/`failed`.

**Root cause** (a Xerte Nottingham engine bug, worked around at the content
level): with `navigation="Menu with Page Controls"`, Xerte inserts a built-in
Table-of-Contents page as `x_pages[0]` **after** `toCompletePages` is built
from `currActPage` over the 43 content nodes. So every content page's
`page_nr` (used by `exitInteraction` to mark `completedPages[i]`) is
`currActPage + 1` — a systematic **off-by-one**:

- `toCompletePages[0] == 0` is only matched by `page_nr == 0` (the menu),
which is never exited (menu pages are excluded from `x_endPageTracking`),
so `completedPages[0]` (the first content page, "Welcome") stayed `false`
forever.
- The last content page (Final quiz, `page_nr == 43`) had no
`toCompletePages` entry, so its completion was lost too.
- `getSuccessStatus()` returns `'incomplete'` whenever any `completedPages[i]`
is `false`, so `finishTracking()` set `cmi.core.lesson_status='incomplete'`
(a no-op against Moodle's pre-set 'incomplete'), and the LMS never left the
incomplete state.

**Fix**: add `unmarkForCompletion="true"` to the **first content page**
("Welcome & how to use this course", `linkID="PG1781882348053"`) in
`source/data.xml` + `source/preview.xml`. This makes `markedPages` skip
`currActPage == 0`, so `toCompletePages` becomes `[1,2,…,42]`, which now
matches the content pages' `page_nr` values (`1…42`). `completedPages[0]`
(Welcome) is now marked `true` when the learner leaves it, and when all
tracked pages are visited `getSuccessStatus()` returns `passed`/`failed`
against `trackingPassed="80%"`. Verified locally with a SCORM 1.2 mock API:
a completed run sends `cmi.core.lesson_status="passed"`, `cmi.core.exit=""`,
`cmi.core.score.raw`, `score.min`, `score.max`, and `cmi.core.session_time`.

**Trade-off**: the Final quiz (`page_nr == 43`) is still not in
`toCompletePages` (there is no index 43), so its *completion* is not tracked
in `completedPages` — but its *score* is still tracked via its interactions
and `trackingWeight="21"`, so the LMS grade and pass/fail are correct. The
Welcome page is no longer "required" for completion, which is acceptable for
an intro page. A proper fix would be in the Xerte engine (`xenith.js`: build
`toCompletePages` from `page_nr` after the menu is inserted, or do not insert
the menu into `x_pages` for tracking); this content workaround is used because
the release workflow builds from the XOT engine and an engine patch would not
persist across XOT rebuilds.

**Do not remove `unmarkForCompletion="true"` from the Welcome page** without
a replacement fix — re-introducing the off-by-one will silently break LMS
completion reporting again.

## SCORM scoring (how the LMS grade is computed)

SCORM 1.2 reports **one** `cmi.core.score.raw` (0–100) and **one**
Expand Down
2 changes: 1 addition & 1 deletion source/data.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<learningObject editorVersion="3" targetFolder="Nottingham" name="Secure code development" language="en-GB" navigation="Menu with Page Controls" textSize="12" theme="default" themeIcons="false" displayMode="fill window" responsive="true" trackingMode="full" trackingPassed="80%" trackingPageTimeout="0" forceTrackingMode="false" restartOptions="ask" hideSaveSession="false" saveSessionLabel="Save Session" closeSessionLabel="Close Session" saveSessionIc="false" saveSessionIcon="fas fa-save"><text linkID="PG1781882348053" name="Welcome &amp;amp; how to use this course"><![CDATA[<h2>Welcome to Secure code development</h2>
<learningObject editorVersion="3" targetFolder="Nottingham" name="Secure code development" language="en-GB" navigation="Menu with Page Controls" textSize="12" theme="default" themeIcons="false" displayMode="fill window" responsive="true" trackingMode="full" trackingPassed="80%" trackingPageTimeout="0" forceTrackingMode="false" restartOptions="ask" hideSaveSession="false" saveSessionLabel="Save Session" closeSessionLabel="Close Session" saveSessionIc="false" saveSessionIcon="fas fa-save"><text unmarkForCompletion="true" linkID="PG1781882348053" name="Welcome &amp;amp; how to use this course"><![CDATA[<h2>Welcome to Secure code development</h2>

<p>This course is for <strong>engineers building software with agents</strong> and for <strong>engineers building agents</strong>. Whether you are designing agentic systems, integrating LLMs, or writing the services that agents call, secure code development matters.</p>

Expand Down
2 changes: 1 addition & 1 deletion source/preview.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0"?>
<learningObject editorVersion="3" targetFolder="Nottingham" name="Secure code development" language="en-GB" navigation="Menu with Page Controls" textSize="12" theme="default" themeIcons="false" displayMode="fill window" responsive="true" trackingMode="full" trackingPassed="80%" trackingPageTimeout="0" forceTrackingMode="false" restartOptions="ask" hideSaveSession="false" saveSessionLabel="Save Session" closeSessionLabel="Close Session" saveSessionIc="false" saveSessionIcon="fas fa-save"><text linkID="PG1781882348053" name="Welcome &amp;amp; how to use this course"><![CDATA[<h2>Welcome to Secure code development</h2>
<learningObject editorVersion="3" targetFolder="Nottingham" name="Secure code development" language="en-GB" navigation="Menu with Page Controls" textSize="12" theme="default" themeIcons="false" displayMode="fill window" responsive="true" trackingMode="full" trackingPassed="80%" trackingPageTimeout="0" forceTrackingMode="false" restartOptions="ask" hideSaveSession="false" saveSessionLabel="Save Session" closeSessionLabel="Close Session" saveSessionIc="false" saveSessionIcon="fas fa-save"><text unmarkForCompletion="true" linkID="PG1781882348053" name="Welcome &amp;amp; how to use this course"><![CDATA[<h2>Welcome to Secure code development</h2>

<p>This course is for <strong>engineers building software with agents</strong> and for <strong>engineers building agents</strong>. Whether you are designing agentic systems, integrating LLMs, or writing the services that agents call, secure code development matters.</p>

Expand Down