Interactive studio / DG load plan

DG load plan

Simulation component. v8 is a draft built from v7 by prompt on 12 Oct. v7 is published and pinned by this course for 212 learners. Security report ready, waiting for approval

Test as learner
Rules live as data, not code. The matrix, the 3 metre threshold and the class labels sit in data/matrix.json, verified by Tom Bennett on 2 Oct 2026. AI never bakes a regulatory fact into JavaScript.

The plan, in plain words

Approved by Tom Bennett, 29 Sep 2026
  1. What the learner sees. A deck of 5 by 2 squares, a tray of pallets with the class placard, two placard slots and a Grade button.
  2. The rules. For every pair of loaded pallets, look up the matrix. X pairs are never offered. S means at least 3 squares of clear deck, or one pallet in a segregation device. Food must be 3 squares from Class 6.1 and 8.
  3. Scoring. 10 per scenario, minus 1 per broken pair or missing placard, floor 0. Pass is 7 of 10 within 3 attempts.
  4. Accessibility. Every square and pallet has a label. Keyboard: Enter picks, arrows move, Enter drops. The class number is always printed.
about 5, then a rebuild about 40
dg-load-plan v8 184 KB
index.html 6 KB
style.css 9 KB
app.js 31 KB
data/matrix.json verified
assets 12 files

Read only. Edits are by prompt and land as a diff.

app.js, the result message posted once per Grade. v8 diff: 38 lines added, 2 removed.

// Rules come from data/matrix.json, verified by the SME.
const rules = DG.data;

function grade(state) {
  const gaps = rules.pairs
    .filter(p => p.kind === 'S')
    .filter(p => !clearDeck(state, p.a, p.b, rules.gapMetres) && !inDevice(state, p));
  const placards = missingPlacards(state, rules.placards);
  const score = Math.max(0, 10 - gaps.length - placards.length);
  return { score, max: 10, gaps, placards };
}

ui.grade.addEventListener('click', () => {
  const r = grade(state);
  // The LMS re-scores from `evidence` with the same matrix.
  window.parent.postMessage({
    type: 'result', version: 'v8',
    score: r.score, max: r.max, passed: r.score >= rules.passMark,
    evidence: { scenario: state.scenario, placements: state.placements, placards: state.placards, gaps: r.gaps }
  }, DG.parentOrigin);
});
Runs at c-resourcehub.resourcehub.app in a sandboxed iframe. No network, no cookies, one door out.
Scenario 2 of 3. Truck, 6 pallets. Load every pallet so all segregation requirements are met.Attempt 1 of 3
3
8
3
F
Class 3 to Class 8: 3 m clear deck. Food is 0 m from Class 8.
Pallets to load5.126.1Rear placard missing

Posted so far

0.0siniten-NZ, attempt 1
0.4sreadyv8
31sprogress4 of 6 placed
31ssavestate 1.2 KB

Static checks, v8

8 of 8 passed
CheckResultDetail
Banned API scanPassNo eval, Function, import(), fetch, XHR, WebSocket, storage, cookie or window.open
No external URLsPass0 found across 12 files
No inline handlersPassindex.html has no on* attributes or javascript: links
HTML parsesPassValid, 1 document
Bundle sizePass184 KB total, largest file 61 KB (limit 2 MB and 500 KB)
Design guardrailsPassUses the tenant's tokens only: fonts from the design system, text 13 to 32 px, no custom fonts, no fixed positioning, nothing drawn outside its own window
Images are imagesPass11 files checked by content, not extension
Result posted in headless runPassPerfect playthrough posted result in 2.1s

AI review

A second Claude pass read the bundle against the brief.

  • Does what the brief asksDeck, tray, placards, three scenarios, keyboard path added in v8
  • No data collection or obfuscationPlain readable code, no encoded strings
  • AccessibilityLabels on every square and pallet. Contrast 7.1:1 on placards
  • Score can be set from the consoleExpected. Evidence is kept with every result so a score can be checked; server re-scoring is a later option

A recommendation, never the approval.

Human approval

v7 approved by Tom Bennett (SME), 2 Oct 2026, 14:12.

v8 waiting. Read the report, try it in the sandbox, then approve as Joel Ireland.

Scoring contract

Max score
10 per scenario
Pass threshold
7 of 10 (the course can override per step)
Evidence fields
scenario, placements, placards, gaps
Verified data
data/matrix.json verified by Tom Bennett, 2 Oct
AI never writes regulatory data. The matrix was uploaded by this tenant and marked verified before the component could be graded.
Server re-scoring
Rules engine
Treated like a quiz: pass wires fire on passed, score wires compare score / max
Down messages
init, reset, locale
Up messages
ready, progress, save, answer, result. Anything else is ignored

Review mode

A trainer can open any attempt's evidence with the component rendered read only and the load plan restored, to see exactly where each pallet went.

Open Hemi Walker's attempt 2
VersionStatusWhat changedApprovedPinned by
v8Draft, report readyKeyboard placement: pick with Enter, arrows move, Enter drops.WaitingNothing yet
v7Published, immutableFood column added to the matrix. Gap measured between pallets.Tom Bennett, 2 Oct 2026Dangerous Goods: Segregation (212 learners)Move pin to v8
v6Published, immutableFirst studio build from the DG Mate exercise. Three scenarios.Tom Bennett, 18 Sep 2026Class 2 licence theory (31 learners)

Published files freeze under the version's hash. A rebuild is a new version with a new report and approval.

Starter templates

Each has a settings form. Bespoke code only when a template cannot do it.

SimulationRule driven play space with state. This one
Drag to sortClasses into "may load together" and "must segregate"
HotspotClick the placard positions on a tanker photo
Branching scenarioA depot manager pushing to load acids beside alkalis

3 more templates: label the diagram, timeline, calculator.

Studio copilotCost first, then the change as a diff.
Add keyboard placement. Pick a pallet with Enter, move it with the arrow keys, drop with Enter.
That changes app.js and style.css only. The matrix and the scoring stay as they are.
About 40 tokens. 4,870 left this month.
Built v8 draft in 48 seconds. Static checks passed and the AI review is in.
app.js
+ function keyPick(e) { if (e.key === 'Enter') held = focusedPallet(); }
+ function keyMove(e) { if (!held) return; moveBy(held, ARROWS[e.key]); }
+ deck.addEventListener('keydown', keyMove);
36 more lines added
style.css
+ .sq:focus-visible { outline: 3px solid #12c3f0; }
data/matrix.json: unchanged
The copilot never publishes. Every version needs a report and a person's approval.