@plasius/learning 0.2.8 → 0.2.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/index.ts
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
+ BEACON_BOT_MISSION_ONE_AUTHORING_V1: () => BEACON_BOT_MISSION_ONE_AUTHORING_V1,
23
24
  JUNIOR_CODER_MISSION_STAGE_ORDER_V1: () => JUNIOR_CODER_MISSION_STAGE_ORDER_V1,
24
25
  JUNIOR_CODER_MODULE_PRICE_V1_1: () => JUNIOR_CODER_MODULE_PRICE_V1_1,
25
26
  JUNIOR_CODER_ROBOT_RESCUE_PATH_CURRENT: () => JUNIOR_CODER_ROBOT_RESCUE_PATH_CURRENT,
@@ -32,6 +33,7 @@ __export(index_exports, {
32
33
  RESCUE_CREW_COMMANDER_MISSION_ONE_AUTHORING_V1: () => RESCUE_CREW_COMMANDER_MISSION_ONE_AUTHORING_V1,
33
34
  ROAD_HOPPER_RALLY_MISSION_ONE_AUTHORING_V1: () => ROAD_HOPPER_RALLY_MISSION_ONE_AUTHORING_V1,
34
35
  ROBOT_MAZE_DASH_MISSION_ONE_AUTHORING_V1: () => ROBOT_MAZE_DASH_MISSION_ONE_AUTHORING_V1,
36
+ SERVO_CREATURE_MISSION_ONE_AUTHORING_V1: () => SERVO_CREATURE_MISSION_ONE_AUTHORING_V1,
35
37
  SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1: () => SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1,
36
38
  STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1: () => STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1,
37
39
  assertValidLearningPath: () => assertValidLearningPath,
@@ -1124,6 +1126,112 @@ function validateMissionAuthoringBundle(bundle, module3) {
1124
1126
  );
1125
1127
  }
1126
1128
  }
1129
+ if (learner.functionReference) {
1130
+ const functionIds = new Set(
1131
+ learner.functionReference.map((entry) => entry.id)
1132
+ );
1133
+ const invalidFunctionReference = functionIds.size !== learner.functionReference.length || learner.functionReference.length === 0 || learner.functionReference.some((entry) => {
1134
+ const parameterNames = new Set(
1135
+ entry.parameters.map((parameter) => parameter.name)
1136
+ );
1137
+ return entry.id.trim().length === 0 || entry.signature.trim().length === 0 || entry.summary.trim().length === 0 || entry.effect.trim().length === 0 || entry.example.trim().length === 0 || parameterNames.size !== entry.parameters.length || entry.parameters.some(
1138
+ (parameter) => parameter.name.trim().length === 0 || parameter.type.trim().length === 0 || parameter.description.trim().length === 0
1139
+ );
1140
+ });
1141
+ if (invalidFunctionReference) {
1142
+ issues.push(
1143
+ authoringIssue(
1144
+ "invalid-function-reference",
1145
+ "Function references require unique IDs, signatures, parameters, effects and examples.",
1146
+ "learner.functionReference"
1147
+ )
1148
+ );
1149
+ }
1150
+ }
1151
+ const hardware = bundle.hardware;
1152
+ if (hardware) {
1153
+ if (module3.category !== "robot" || module3.hardware.mode !== "physical-first" || !module3.hardware.simulatorAvailable) {
1154
+ issues.push(
1155
+ authoringIssue(
1156
+ "hardware-module-mismatch",
1157
+ "Mission hardware disclosure requires a simulator-backed physical robot module.",
1158
+ "hardware"
1159
+ )
1160
+ );
1161
+ }
1162
+ if (hardware.requirementsVersion !== module3.hardware.requirementsVersion) {
1163
+ issues.push(
1164
+ authoringIssue(
1165
+ "hardware-requirements-version-mismatch",
1166
+ `Hardware disclosure ${hardware.requirementsVersion} does not match catalog requirements ${module3.hardware.requirementsVersion}.`,
1167
+ "hardware.requirementsVersion"
1168
+ )
1169
+ );
1170
+ }
1171
+ const catalogHardwareById = new Map(
1172
+ module3.hardware.items.map((item) => [item.id, item])
1173
+ );
1174
+ const completePathIds = new Set(hardware.completePathItemIds);
1175
+ const incrementalIds = new Set(hardware.incrementalItemIds);
1176
+ const componentIds = new Set(hardware.components.map((component) => component.itemId));
1177
+ const catalogIds = new Set(catalogHardwareById.keys());
1178
+ const hasDuplicateHardwareIds = completePathIds.size !== hardware.completePathItemIds.length || incrementalIds.size !== hardware.incrementalItemIds.length || componentIds.size !== hardware.components.length;
1179
+ const hasUnknownOrMissingItems = hasDuplicateHardwareIds || completePathIds.size !== catalogIds.size || componentIds.size !== catalogIds.size || [...catalogIds].some(
1180
+ (itemId) => !completePathIds.has(itemId) || !componentIds.has(itemId)
1181
+ ) || [...incrementalIds].some((itemId) => !catalogIds.has(itemId));
1182
+ const hasMismatchedComponent = hardware.components.some((component) => {
1183
+ const catalogItem = catalogHardwareById.get(component.itemId);
1184
+ const expectedScope = incrementalIds.has(component.itemId) ? "incremental" : "complete-path";
1185
+ return !catalogItem || component.quantity !== catalogItem.quantity || component.acquisitionScope !== expectedScope;
1186
+ });
1187
+ if (hasUnknownOrMissingItems || hasMismatchedComponent) {
1188
+ issues.push(
1189
+ authoringIssue(
1190
+ "hardware-item-mismatch",
1191
+ "Complete, incremental and per-component hardware disclosures must match the immutable catalog manifest.",
1192
+ "hardware.components"
1193
+ )
1194
+ );
1195
+ }
1196
+ if (hardware.components.some(
1197
+ (component) => component.verificationStatus !== "verified" && component.compatibilityClaimed
1198
+ ) || module3.hardware.verificationStatus !== "verified" && !module3.hardware.publicSaleBlocked) {
1199
+ issues.push(
1200
+ authoringIssue(
1201
+ "hardware-verification-claim",
1202
+ "Unverified hardware cannot claim compatibility or unblock public physical sale.",
1203
+ "hardware.components"
1204
+ )
1205
+ );
1206
+ }
1207
+ const safeguards = hardware.safeguards;
1208
+ if (safeguards.adultAssemblyRequired !== true || safeguards.adultAcknowledgementRequiredForExport !== true || safeguards.websiteMayControlHardware !== false || safeguards.simulatorCompletionAvailable !== true || safeguards.physicalBadgeRequiresAdultSignoff !== true || safeguards.adultAssemblySteps.length === 0 || safeguards.powerRequirements.length === 0 || safeguards.cableRequirements.length === 0 || safeguards.softwarePrerequisites.length === 0 || safeguards.warnings.length === 0 || hardware.components.some(
1209
+ (component) => component.physicalCompletionEligible && (component.verificationStatus !== "verified" || module3.hardware.verificationStatus !== "verified")
1210
+ )) {
1211
+ issues.push(
1212
+ authoringIssue(
1213
+ "unsafe-physical-export",
1214
+ "Physical export and completion require adult acknowledgement, verified hardware and a website that never controls hardware.",
1215
+ "hardware.safeguards"
1216
+ )
1217
+ );
1218
+ }
1219
+ const simulatedBadge = module3.badges.find(
1220
+ (badge) => badge.id === safeguards.simulatedBadgeId
1221
+ );
1222
+ const physicalBadge = module3.badges.find(
1223
+ (badge) => badge.id === safeguards.physicalBadgeId
1224
+ );
1225
+ if (simulatedBadge?.evidence === "adult-physical-signoff" || physicalBadge?.evidence !== "adult-physical-signoff") {
1226
+ issues.push(
1227
+ authoringIssue(
1228
+ "invalid-hardware-reward",
1229
+ "Simulated and physical badges must be distinct, and only the physical badge may require adult sign-off.",
1230
+ "hardware.safeguards"
1231
+ )
1232
+ );
1233
+ }
1234
+ }
1127
1235
  return issues;
1128
1236
  }
1129
1237
  function assertValidMissionAuthoringBundle(bundle, module3) {
@@ -2227,6 +2335,782 @@ var STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1 = {
2227
2335
  ]
2228
2336
  }
2229
2337
  };
2338
+ var BEACON_BOT_MISSION_ONE_AUTHORING_V1 = {
2339
+ version: MISSION_AUTHORING_CONTRACT_VERSION_V1,
2340
+ moduleId: "junior-coder.beacon-bot",
2341
+ moduleVersion: "1.1.0",
2342
+ missionId: "beacon-bot-mission-1",
2343
+ learner: {
2344
+ estimatedMinutes: 20,
2345
+ stages: [
2346
+ {
2347
+ kind: "learn",
2348
+ instruction: "Read what setVisibleSignal(), waitMs(), repeatSignal() and readIrReceiver() do in the private Beacon Bot simulator.",
2349
+ artifactIds: ["beacon-bot-m1-art"]
2350
+ },
2351
+ {
2352
+ kind: "predict",
2353
+ instruction: "Predict the visible signal order, elapsed time and simulated IR reading before the sequence runs.",
2354
+ artifactIds: []
2355
+ },
2356
+ {
2357
+ kind: "build",
2358
+ instruction: "Adjust the four documented C++-style calls to create one bounded rescue signal.",
2359
+ artifactIds: ["beacon-bot-m1-code"]
2360
+ },
2361
+ {
2362
+ kind: "run",
2363
+ instruction: "Use the Run action button to start the private Beacon Bot simulator.",
2364
+ artifactIds: ["beacon-bot-m1-code"]
2365
+ },
2366
+ {
2367
+ kind: "assess",
2368
+ instruction: "Run the visible and protected deterministic beacon checks.",
2369
+ artifactIds: []
2370
+ },
2371
+ {
2372
+ kind: "inspect",
2373
+ instruction: "Compare the highlighted C++-style line with the first signal goal that did not pass.",
2374
+ artifactIds: []
2375
+ },
2376
+ {
2377
+ kind: "fix",
2378
+ instruction: "Change one signal, wait, repeat or simulated IR setting, then rerun and inspect the text telemetry.",
2379
+ artifactIds: ["beacon-bot-m1-code"]
2380
+ },
2381
+ {
2382
+ kind: "explain",
2383
+ instruction: "Explain how the function calls created a timed signal and how the simulated receiver changed the result.",
2384
+ artifactIds: []
2385
+ },
2386
+ {
2387
+ kind: "reward",
2388
+ instruction: "Collect the simulated badge when the score and private-runtime safety check pass; physical completion remains adult-only.",
2389
+ artifactIds: []
2390
+ }
2391
+ ],
2392
+ readinessChecks: [
2393
+ {
2394
+ id: "beacon-bot-m1-find-wait",
2395
+ prompt: "Point to the documented call that controls how long a visible signal stays on.",
2396
+ scored: false
2397
+ }
2398
+ ],
2399
+ artifacts: [
2400
+ {
2401
+ id: "beacon-bot-m1-code",
2402
+ kind: "starter-code",
2403
+ audience: "learner",
2404
+ solutionBearing: false
2405
+ },
2406
+ {
2407
+ id: "beacon-bot-m1-art",
2408
+ kind: "starter-assets",
2409
+ audience: "learner",
2410
+ solutionBearing: false
2411
+ },
2412
+ {
2413
+ id: "beacon-bot-m1-printable",
2414
+ kind: "printable",
2415
+ audience: "learner",
2416
+ solutionBearing: false
2417
+ }
2418
+ ],
2419
+ goals: [
2420
+ {
2421
+ id: "beacon-bot-m1-starts",
2422
+ statement: "The documented C++-style settings are valid and the private simulator starts.",
2423
+ visibility: "visible",
2424
+ criterionIds: ["beacon-bot-build"],
2425
+ completionRequired: true,
2426
+ aiRequired: false
2427
+ },
2428
+ {
2429
+ id: "beacon-bot-m1-signal-sequence",
2430
+ statement: "The beacon produces a bounded timed pattern and reports one simulated IR receiver state.",
2431
+ visibility: "visible",
2432
+ criterionIds: ["beacon-bot-goal-one", "beacon-bot-goal-two"],
2433
+ completionRequired: true,
2434
+ aiRequired: false
2435
+ },
2436
+ {
2437
+ id: "beacon-bot-m1-private-runtime",
2438
+ statement: "The program stays inside the private simulator and never accesses physical hardware, the network or browser storage.",
2439
+ visibility: "visible",
2440
+ criterionIds: ["beacon-bot-safety"],
2441
+ completionRequired: true,
2442
+ aiRequired: false
2443
+ }
2444
+ ],
2445
+ interactions: [
2446
+ {
2447
+ id: "beacon-bot-m1-run-control",
2448
+ description: "Start the private Beacon Bot signal simulation.",
2449
+ primaryMode: "pointer",
2450
+ alternativeIds: ["beacon-bot-m1-keyboard-run"]
2451
+ },
2452
+ {
2453
+ id: "beacon-bot-m1-code-control",
2454
+ description: "Edit the documented signal, timing, repeat and receiver calls.",
2455
+ primaryMode: "keyboard",
2456
+ alternativeIds: []
2457
+ },
2458
+ {
2459
+ id: "beacon-bot-m1-signal-colour",
2460
+ description: "Observe the visible rescue signal without relying on colour alone.",
2461
+ primaryMode: "colour",
2462
+ alternativeIds: ["beacon-bot-m1-signal-telemetry"]
2463
+ },
2464
+ {
2465
+ id: "beacon-bot-m1-signal-motion",
2466
+ description: "Observe the bounded signal sequence and receiver state changes.",
2467
+ primaryMode: "motion",
2468
+ alternativeIds: ["beacon-bot-m1-signal-telemetry"]
2469
+ }
2470
+ ],
2471
+ accessibilityAlternatives: [
2472
+ {
2473
+ id: "beacon-bot-m1-keyboard-run",
2474
+ modes: ["keyboard"],
2475
+ equivalentOutcome: true,
2476
+ description: "Press Enter or Space on the play-icon Run button to start the same simulator."
2477
+ },
2478
+ {
2479
+ id: "beacon-bot-m1-signal-telemetry",
2480
+ modes: ["text", "shape", "symbol", "reduced-motion"],
2481
+ equivalentOutcome: true,
2482
+ description: "Read the signal name, step count, elapsed milliseconds and receiver state without colour or animation."
2483
+ }
2484
+ ],
2485
+ evidenceRequirements: [
2486
+ {
2487
+ id: "beacon-bot-m1-assessment",
2488
+ goalIds: [
2489
+ "beacon-bot-m1-starts",
2490
+ "beacon-bot-m1-signal-sequence",
2491
+ "beacon-bot-m1-private-runtime"
2492
+ ],
2493
+ kind: "assessment-result",
2494
+ retention: "entitlement",
2495
+ containsPersonalData: false
2496
+ },
2497
+ {
2498
+ id: "beacon-bot-m1-explanation",
2499
+ goalIds: ["beacon-bot-m1-signal-sequence"],
2500
+ kind: "learner-explanation",
2501
+ retention: "attempt",
2502
+ containsPersonalData: false
2503
+ }
2504
+ ],
2505
+ sideAdventures: [
2506
+ {
2507
+ id: "beacon-bot-m1-remix",
2508
+ prompt: "Invent an original rescue-signal name and describe a text or shape cue that makes it understandable without colour.",
2509
+ completionRequired: false
2510
+ }
2511
+ ],
2512
+ rewardBindings: [
2513
+ {
2514
+ id: "beacon-bot-m1-simulated-badge",
2515
+ badgeId: "beacon-bot-mission-complete",
2516
+ goalIds: [
2517
+ "beacon-bot-m1-starts",
2518
+ "beacon-bot-m1-signal-sequence",
2519
+ "beacon-bot-m1-private-runtime"
2520
+ ],
2521
+ deterministic: true,
2522
+ random: false,
2523
+ tokenConvertible: false
2524
+ }
2525
+ ],
2526
+ functionReference: [
2527
+ {
2528
+ id: "beacon-bot-function-visible-signal",
2529
+ signature: "setVisibleSignal(colour)",
2530
+ summary: "Chooses the named visible signal used by the next bounded step.",
2531
+ parameters: [
2532
+ {
2533
+ name: "colour",
2534
+ type: "string",
2535
+ description: "Use red, amber or green."
2536
+ }
2537
+ ],
2538
+ effect: "Updates the simulator's labelled light and equivalent shape cue without accessing a physical LED.",
2539
+ example: 'setVisibleSignal("green");'
2540
+ },
2541
+ {
2542
+ id: "beacon-bot-function-wait",
2543
+ signature: "waitMs(duration)",
2544
+ summary: "Adds one safe wait to the simulated signal timeline.",
2545
+ parameters: [
2546
+ {
2547
+ name: "duration",
2548
+ type: "whole number",
2549
+ description: "A bounded number of milliseconds from 100 to 1000."
2550
+ }
2551
+ ],
2552
+ effect: "Advances simulated elapsed time; it never blocks the website or controls hardware.",
2553
+ example: "waitMs(250);"
2554
+ },
2555
+ {
2556
+ id: "beacon-bot-function-repeat",
2557
+ signature: "repeatSignal(count)",
2558
+ summary: "Repeats the current visible signal a safe number of times.",
2559
+ parameters: [
2560
+ {
2561
+ name: "count",
2562
+ type: "whole number",
2563
+ description: "A bounded repeat count from 1 to 4."
2564
+ }
2565
+ ],
2566
+ effect: "Adds a fixed number of labelled signal steps to the private simulator timeline.",
2567
+ example: "repeatSignal(3);"
2568
+ },
2569
+ {
2570
+ id: "beacon-bot-function-ir-receiver",
2571
+ signature: "readIrReceiver()",
2572
+ summary: "Reads the simulator's fictional infrared receiver state.",
2573
+ parameters: [],
2574
+ effect: "Returns detected or clear from simulator state only; it cannot access a real sensor.",
2575
+ example: "const receiverState = readIrReceiver();"
2576
+ }
2577
+ ]
2578
+ },
2579
+ facilitator: {
2580
+ artifacts: [
2581
+ {
2582
+ id: "beacon-bot-m1-answer-key",
2583
+ kind: "answer-key",
2584
+ audience: "facilitator",
2585
+ solutionBearing: true
2586
+ },
2587
+ {
2588
+ id: "beacon-bot-m1-protected-tests",
2589
+ kind: "protected-test",
2590
+ audience: "facilitator",
2591
+ solutionBearing: true
2592
+ },
2593
+ {
2594
+ id: "beacon-bot-m1-adult-hardware-guide",
2595
+ kind: "facilitator-note",
2596
+ audience: "facilitator",
2597
+ solutionBearing: true
2598
+ }
2599
+ ],
2600
+ protectedGoals: [
2601
+ {
2602
+ id: "beacon-bot-m1-protected-resilience",
2603
+ statement: "The simulator rejects unsupported signals, excessive waits or repeats and any hardware, network or storage request.",
2604
+ visibility: "protected",
2605
+ criterionIds: ["beacon-bot-edge-one", "beacon-bot-edge-two"],
2606
+ completionRequired: false,
2607
+ aiRequired: false
2608
+ }
2609
+ ],
2610
+ prompts: [
2611
+ "Ask the learner to predict the labelled signal timeline before suggesting one bounded change.",
2612
+ "Use the function reference and visible telemetry; never provide wiring or physical power advice to a learner.",
2613
+ "Physical export stays unavailable until an adult acknowledges the exact manifest and every component has verified bench-test evidence."
2614
+ ]
2615
+ },
2616
+ hardware: {
2617
+ requirementsVersion: "1.0.0",
2618
+ hardwareIncluded: false,
2619
+ completePathItemIds: [
2620
+ "pico-2-w",
2621
+ "breadboard",
2622
+ "usb-data-cable",
2623
+ "jumper-wires",
2624
+ "led-pack",
2625
+ "led-resistors",
2626
+ "ir-pair"
2627
+ ],
2628
+ incrementalItemIds: ["led-pack", "led-resistors", "ir-pair"],
2629
+ components: [
2630
+ {
2631
+ itemId: "pico-2-w",
2632
+ quantity: 1,
2633
+ acquisitionScope: "complete-path",
2634
+ verificationStatus: "pending-bench-test",
2635
+ compatibilityClaimed: false,
2636
+ physicalCompletionEligible: false
2637
+ },
2638
+ {
2639
+ itemId: "breadboard",
2640
+ quantity: 1,
2641
+ acquisitionScope: "complete-path",
2642
+ verificationStatus: "pending-bench-test",
2643
+ compatibilityClaimed: false,
2644
+ physicalCompletionEligible: false
2645
+ },
2646
+ {
2647
+ itemId: "usb-data-cable",
2648
+ quantity: 1,
2649
+ acquisitionScope: "complete-path",
2650
+ verificationStatus: "pending-bench-test",
2651
+ compatibilityClaimed: false,
2652
+ physicalCompletionEligible: false
2653
+ },
2654
+ {
2655
+ itemId: "jumper-wires",
2656
+ quantity: 12,
2657
+ acquisitionScope: "complete-path",
2658
+ verificationStatus: "pending-bench-test",
2659
+ compatibilityClaimed: false,
2660
+ physicalCompletionEligible: false
2661
+ },
2662
+ {
2663
+ itemId: "led-pack",
2664
+ quantity: 3,
2665
+ acquisitionScope: "incremental",
2666
+ verificationStatus: "pending-bench-test",
2667
+ compatibilityClaimed: false,
2668
+ physicalCompletionEligible: false
2669
+ },
2670
+ {
2671
+ itemId: "led-resistors",
2672
+ quantity: 3,
2673
+ acquisitionScope: "incremental",
2674
+ verificationStatus: "pending-bench-test",
2675
+ compatibilityClaimed: false,
2676
+ physicalCompletionEligible: false
2677
+ },
2678
+ {
2679
+ itemId: "ir-pair",
2680
+ quantity: 1,
2681
+ acquisitionScope: "incremental",
2682
+ verificationStatus: "pending-bench-test",
2683
+ compatibilityClaimed: false,
2684
+ physicalCompletionEligible: false
2685
+ }
2686
+ ],
2687
+ safeguards: {
2688
+ adultAssemblyRequired: true,
2689
+ adultAcknowledgementRequiredForExport: true,
2690
+ websiteMayControlHardware: false,
2691
+ simulatorCompletionAvailable: true,
2692
+ simulatedBadgeId: "beacon-bot-mission-complete",
2693
+ physicalBadgeId: "beacon-bot-physical-builder",
2694
+ physicalBadgeRequiresAdultSignoff: true,
2695
+ adultAssemblySteps: [
2696
+ "Confirm every exact component identity against the requirements manifest.",
2697
+ "Assemble and inspect the disconnected breadboard circuit before learner use.",
2698
+ "Run known-good recovery firmware and complete the adult bench-test record."
2699
+ ],
2700
+ powerRequirements: [
2701
+ "Use Pico USB power only for the published Beacon Bot reference circuit.",
2702
+ "Disconnect USB power before changing any wiring."
2703
+ ],
2704
+ cableRequirements: [
2705
+ "One known data-capable USB cable compatible with the Pico 2 W.",
2706
+ "Insulated male-to-male breadboard jumper wires matching the manifest quantity."
2707
+ ],
2708
+ softwarePrerequisites: [
2709
+ "Supported Pico SDK toolchain on Raspberry Pi OS or a documented desktop environment.",
2710
+ "Known-good Beacon Bot recovery firmware prepared by an adult."
2711
+ ],
2712
+ warnings: [
2713
+ "Hardware is not included with the module.",
2714
+ "No listed component currently claims compatibility or physical-completion eligibility.",
2715
+ "The simulator and simulated badge remain available without physical equipment."
2716
+ ],
2717
+ unrelatedHardwareNotRequired: [
2718
+ "Camera Module 3",
2719
+ "motor driver or motors",
2720
+ "servo"
2721
+ ]
2722
+ }
2723
+ }
2724
+ };
2725
+ var SERVO_CREATURE_MISSION_ONE_AUTHORING_V1 = {
2726
+ version: MISSION_AUTHORING_CONTRACT_VERSION_V1,
2727
+ moduleId: "junior-coder.servo-creature",
2728
+ moduleVersion: "1.1.0",
2729
+ missionId: "servo-creature-mission-1",
2730
+ learner: {
2731
+ estimatedMinutes: 20,
2732
+ stages: [
2733
+ {
2734
+ kind: "learn",
2735
+ instruction: "Read what setServoAngle(), waitMs(), repeatMovement(), setCreatureMood() and readTouchSensor() do in the private Servo Creature simulator.",
2736
+ artifactIds: ["servo-creature-m1-art"]
2737
+ },
2738
+ {
2739
+ kind: "predict",
2740
+ instruction: "Predict the creature's labelled angle, mood, repeat count and simulated touch response before the sequence runs.",
2741
+ artifactIds: []
2742
+ },
2743
+ {
2744
+ kind: "build",
2745
+ instruction: "Adjust the five documented C++-style calls to create one bounded creature movement sequence.",
2746
+ artifactIds: ["servo-creature-m1-code"]
2747
+ },
2748
+ {
2749
+ kind: "run",
2750
+ instruction: "Use the Run action button to start the private Servo Creature simulator.",
2751
+ artifactIds: ["servo-creature-m1-code"]
2752
+ },
2753
+ {
2754
+ kind: "assess",
2755
+ instruction: "Run the visible and protected deterministic pose, mood and interaction checks.",
2756
+ artifactIds: []
2757
+ },
2758
+ {
2759
+ kind: "inspect",
2760
+ instruction: "Compare the highlighted C++-style line with the first movement goal that did not pass.",
2761
+ artifactIds: []
2762
+ },
2763
+ {
2764
+ kind: "fix",
2765
+ instruction: "Change one angle, wait, repeat, mood or simulated touch call, then rerun and inspect the text telemetry.",
2766
+ artifactIds: ["servo-creature-m1-code"]
2767
+ },
2768
+ {
2769
+ kind: "explain",
2770
+ instruction: "Explain how the bounded calls created a safe movement and how the simulated interaction changed the creature's response.",
2771
+ artifactIds: []
2772
+ },
2773
+ {
2774
+ kind: "reward",
2775
+ instruction: "Collect the simulated badge when the score and private-runtime safety check pass; physical completion remains adult-only.",
2776
+ artifactIds: []
2777
+ }
2778
+ ],
2779
+ readinessChecks: [
2780
+ {
2781
+ id: "servo-creature-m1-find-angle-limit",
2782
+ prompt: "Find the documented safe minimum and maximum angle before changing the creature's pose.",
2783
+ scored: false
2784
+ }
2785
+ ],
2786
+ artifacts: [
2787
+ {
2788
+ id: "servo-creature-m1-code",
2789
+ kind: "starter-code",
2790
+ audience: "learner",
2791
+ solutionBearing: false
2792
+ },
2793
+ {
2794
+ id: "servo-creature-m1-art",
2795
+ kind: "starter-assets",
2796
+ audience: "learner",
2797
+ solutionBearing: false
2798
+ },
2799
+ {
2800
+ id: "servo-creature-m1-printable",
2801
+ kind: "printable",
2802
+ audience: "learner",
2803
+ solutionBearing: false
2804
+ }
2805
+ ],
2806
+ goals: [
2807
+ {
2808
+ id: "servo-creature-m1-starts",
2809
+ statement: "The documented C++-style settings are valid and the private simulator starts.",
2810
+ visibility: "visible",
2811
+ criterionIds: ["servo-creature-build"],
2812
+ completionRequired: true,
2813
+ aiRequired: false
2814
+ },
2815
+ {
2816
+ id: "servo-creature-m1-movement-sequence",
2817
+ statement: "The creature completes a bounded angle, timing, mood and interaction sequence.",
2818
+ visibility: "visible",
2819
+ criterionIds: ["servo-creature-goal-one", "servo-creature-goal-two"],
2820
+ completionRequired: true,
2821
+ aiRequired: false
2822
+ },
2823
+ {
2824
+ id: "servo-creature-m1-private-runtime",
2825
+ statement: "The program stays inside the private simulator and never accesses physical hardware, the network or browser storage.",
2826
+ visibility: "visible",
2827
+ criterionIds: ["servo-creature-safety"],
2828
+ completionRequired: true,
2829
+ aiRequired: false
2830
+ }
2831
+ ],
2832
+ interactions: [
2833
+ {
2834
+ id: "servo-creature-m1-run-control",
2835
+ description: "Start the private Servo Creature movement simulation.",
2836
+ primaryMode: "pointer",
2837
+ alternativeIds: ["servo-creature-m1-keyboard-run"]
2838
+ },
2839
+ {
2840
+ id: "servo-creature-m1-code-control",
2841
+ description: "Edit the documented angle, timing, repeat, mood and interaction calls.",
2842
+ primaryMode: "keyboard",
2843
+ alternativeIds: []
2844
+ },
2845
+ {
2846
+ id: "servo-creature-m1-pose-motion",
2847
+ description: "Observe the bounded creature pose and movement sequence.",
2848
+ primaryMode: "motion",
2849
+ alternativeIds: ["servo-creature-m1-telemetry"]
2850
+ }
2851
+ ],
2852
+ accessibilityAlternatives: [
2853
+ {
2854
+ id: "servo-creature-m1-keyboard-run",
2855
+ modes: ["keyboard"],
2856
+ equivalentOutcome: true,
2857
+ description: "Press Enter or Space on the play-icon Run button to start the same simulator."
2858
+ },
2859
+ {
2860
+ id: "servo-creature-m1-telemetry",
2861
+ modes: ["text", "shape", "symbol", "reduced-motion"],
2862
+ equivalentOutcome: true,
2863
+ description: "Read the angle, mood, repeat count, elapsed milliseconds and touch state without animation."
2864
+ }
2865
+ ],
2866
+ evidenceRequirements: [
2867
+ {
2868
+ id: "servo-creature-m1-assessment",
2869
+ goalIds: [
2870
+ "servo-creature-m1-starts",
2871
+ "servo-creature-m1-movement-sequence",
2872
+ "servo-creature-m1-private-runtime"
2873
+ ],
2874
+ kind: "assessment-result",
2875
+ retention: "entitlement",
2876
+ containsPersonalData: false
2877
+ },
2878
+ {
2879
+ id: "servo-creature-m1-explanation",
2880
+ goalIds: ["servo-creature-m1-movement-sequence"],
2881
+ kind: "learner-explanation",
2882
+ retention: "attempt",
2883
+ containsPersonalData: false
2884
+ }
2885
+ ],
2886
+ sideAdventures: [
2887
+ {
2888
+ id: "servo-creature-m1-remix",
2889
+ prompt: "Invent an original creature mood and describe a text or symbol cue that makes its pose understandable without movement.",
2890
+ completionRequired: false
2891
+ }
2892
+ ],
2893
+ rewardBindings: [
2894
+ {
2895
+ id: "servo-creature-m1-simulated-badge",
2896
+ badgeId: "servo-creature-mission-complete",
2897
+ goalIds: [
2898
+ "servo-creature-m1-starts",
2899
+ "servo-creature-m1-movement-sequence",
2900
+ "servo-creature-m1-private-runtime"
2901
+ ],
2902
+ deterministic: true,
2903
+ random: false,
2904
+ tokenConvertible: false
2905
+ }
2906
+ ],
2907
+ functionReference: [
2908
+ {
2909
+ id: "servo-creature-function-angle",
2910
+ signature: "setServoAngle(degrees)",
2911
+ summary: "Chooses one safe labelled creature pose in the private simulator.",
2912
+ parameters: [
2913
+ {
2914
+ name: "degrees",
2915
+ type: "whole number",
2916
+ description: "A bounded angle from 30 to 150 degrees."
2917
+ }
2918
+ ],
2919
+ effect: "Updates the simulator's labelled angle and pose cue without generating PWM or accessing a physical servo.",
2920
+ example: "setServoAngle(90);"
2921
+ },
2922
+ {
2923
+ id: "servo-creature-function-wait",
2924
+ signature: "waitMs(duration)",
2925
+ summary: "Adds one safe wait to the simulated movement timeline.",
2926
+ parameters: [
2927
+ {
2928
+ name: "duration",
2929
+ type: "whole number",
2930
+ description: "A bounded number of milliseconds from 100 to 1000."
2931
+ }
2932
+ ],
2933
+ effect: "Advances simulated elapsed time; it never blocks the website or holds a physical servo under load.",
2934
+ example: "waitMs(300);"
2935
+ },
2936
+ {
2937
+ id: "servo-creature-function-repeat",
2938
+ signature: "repeatMovement(count)",
2939
+ summary: "Repeats the current simulated pose a safe number of times.",
2940
+ parameters: [
2941
+ {
2942
+ name: "count",
2943
+ type: "whole number",
2944
+ description: "A bounded repeat count from 1 to 4."
2945
+ }
2946
+ ],
2947
+ effect: "Adds a fixed number of labelled pose steps to the private simulator timeline.",
2948
+ example: "repeatMovement(3);"
2949
+ },
2950
+ {
2951
+ id: "servo-creature-function-mood",
2952
+ signature: "setCreatureMood(mood)",
2953
+ summary: "Chooses the creature's labelled expression for the simulated pose.",
2954
+ parameters: [
2955
+ {
2956
+ name: "mood",
2957
+ type: "string",
2958
+ description: "Use calm, curious or happy."
2959
+ }
2960
+ ],
2961
+ effect: "Updates the simulator's text and symbol mood cue without moving physical parts.",
2962
+ example: 'setCreatureMood("curious");'
2963
+ },
2964
+ {
2965
+ id: "servo-creature-function-touch",
2966
+ signature: "readTouchSensor()",
2967
+ summary: "Reads the simulator's fictional touch state for one interaction response.",
2968
+ parameters: [],
2969
+ effect: "Returns touched or clear from simulator state only; it cannot access a physical sensor.",
2970
+ example: "const touchState = readTouchSensor();"
2971
+ }
2972
+ ]
2973
+ },
2974
+ facilitator: {
2975
+ artifacts: [
2976
+ {
2977
+ id: "servo-creature-m1-answer-key",
2978
+ kind: "answer-key",
2979
+ audience: "facilitator",
2980
+ solutionBearing: true
2981
+ },
2982
+ {
2983
+ id: "servo-creature-m1-protected-tests",
2984
+ kind: "protected-test",
2985
+ audience: "facilitator",
2986
+ solutionBearing: true
2987
+ },
2988
+ {
2989
+ id: "servo-creature-m1-adult-hardware-guide",
2990
+ kind: "facilitator-note",
2991
+ audience: "facilitator",
2992
+ solutionBearing: true
2993
+ }
2994
+ ],
2995
+ protectedGoals: [
2996
+ {
2997
+ id: "servo-creature-m1-protected-resilience",
2998
+ statement: "The simulator rejects unsupported moods, out-of-range angles, excessive waits or repeats and any physical-hardware request.",
2999
+ visibility: "protected",
3000
+ criterionIds: ["servo-creature-edge-one", "servo-creature-edge-two"],
3001
+ completionRequired: false,
3002
+ aiRequired: false
3003
+ }
3004
+ ],
3005
+ prompts: [
3006
+ "Ask the learner to predict the labelled pose timeline before suggesting one bounded change.",
3007
+ "Use the function reference and visible telemetry; never provide servo wiring, power or movement advice to a learner.",
3008
+ "Physical export stays unavailable until an adult acknowledges the exact manifest and every servo power component has verified bench-test evidence."
3009
+ ]
3010
+ },
3011
+ hardware: {
3012
+ requirementsVersion: "1.0.0",
3013
+ hardwareIncluded: false,
3014
+ completePathItemIds: [
3015
+ "pico-2-w",
3016
+ "breadboard",
3017
+ "usb-data-cable",
3018
+ "jumper-wires",
3019
+ "micro-servo",
3020
+ "servo-power"
3021
+ ],
3022
+ incrementalItemIds: ["micro-servo", "servo-power"],
3023
+ components: [
3024
+ {
3025
+ itemId: "pico-2-w",
3026
+ quantity: 1,
3027
+ acquisitionScope: "complete-path",
3028
+ verificationStatus: "pending-bench-test",
3029
+ compatibilityClaimed: false,
3030
+ physicalCompletionEligible: false
3031
+ },
3032
+ {
3033
+ itemId: "breadboard",
3034
+ quantity: 1,
3035
+ acquisitionScope: "complete-path",
3036
+ verificationStatus: "pending-bench-test",
3037
+ compatibilityClaimed: false,
3038
+ physicalCompletionEligible: false
3039
+ },
3040
+ {
3041
+ itemId: "usb-data-cable",
3042
+ quantity: 1,
3043
+ acquisitionScope: "complete-path",
3044
+ verificationStatus: "pending-bench-test",
3045
+ compatibilityClaimed: false,
3046
+ physicalCompletionEligible: false
3047
+ },
3048
+ {
3049
+ itemId: "jumper-wires",
3050
+ quantity: 12,
3051
+ acquisitionScope: "complete-path",
3052
+ verificationStatus: "pending-bench-test",
3053
+ compatibilityClaimed: false,
3054
+ physicalCompletionEligible: false
3055
+ },
3056
+ {
3057
+ itemId: "micro-servo",
3058
+ quantity: 1,
3059
+ acquisitionScope: "incremental",
3060
+ verificationStatus: "pending-bench-test",
3061
+ compatibilityClaimed: false,
3062
+ physicalCompletionEligible: false
3063
+ },
3064
+ {
3065
+ itemId: "servo-power",
3066
+ quantity: 1,
3067
+ acquisitionScope: "incremental",
3068
+ verificationStatus: "pending-bench-test",
3069
+ compatibilityClaimed: false,
3070
+ physicalCompletionEligible: false
3071
+ }
3072
+ ],
3073
+ safeguards: {
3074
+ adultAssemblyRequired: true,
3075
+ adultAcknowledgementRequiredForExport: true,
3076
+ websiteMayControlHardware: false,
3077
+ simulatorCompletionAvailable: true,
3078
+ simulatedBadgeId: "servo-creature-mission-complete",
3079
+ physicalBadgeId: "servo-creature-physical-builder",
3080
+ physicalBadgeRequiresAdultSignoff: true,
3081
+ adultAssemblySteps: [
3082
+ "Confirm the exact servo, external supply and connector identities against the requirements manifest.",
3083
+ "Assemble and inspect the disconnected signal and common-ground wiring before learner use.",
3084
+ "Secure the creature linkage, lift or restrain moving parts and complete the adult bench-test record."
3085
+ ],
3086
+ powerRequirements: [
3087
+ "Use an external regulated servo supply sized for the verified servo; do not power the servo from a Pico GPIO pin.",
3088
+ "Connect one common signal ground between the verified servo supply and Pico only as shown in the adult guide.",
3089
+ "Disconnect every power source before changing wiring or creature linkages."
3090
+ ],
3091
+ cableRequirements: [
3092
+ "One known data-capable USB cable compatible with the Pico 2 W.",
3093
+ "Insulated jumper leads and a verified servo connector arrangement documented by the adult guide."
3094
+ ],
3095
+ softwarePrerequisites: [
3096
+ "Supported Pico SDK toolchain on Raspberry Pi OS or a documented desktop environment.",
3097
+ "Known-good Servo Creature recovery firmware with adult-owned neutral-pose and stop behaviour."
3098
+ ],
3099
+ warnings: [
3100
+ "Hardware is not included with the module.",
3101
+ "No listed servo or power arrangement currently claims compatibility or physical-completion eligibility.",
3102
+ "Pinch points, stalled servos and unsuitable power supplies can cause heat or movement; adult assembly and testing are mandatory.",
3103
+ "The simulator and simulated badge remain available without physical equipment."
3104
+ ],
3105
+ unrelatedHardwareNotRequired: [
3106
+ "Camera Module 3 or Raspberry Pi Zero 2 W",
3107
+ "motor driver, motors or rover chassis",
3108
+ "physical touch or IR sensor",
3109
+ "LED or infrared beacon parts"
3110
+ ]
3111
+ }
3112
+ }
3113
+ };
2230
3114
  var RESCUE_CREW_COMMANDER_MISSION_ONE_AUTHORING_V1 = {
2231
3115
  version: MISSION_AUTHORING_CONTRACT_VERSION_V1,
2232
3116
  moduleId: "junior-coder.rescue-crew-commander",
@@ -3022,6 +3906,7 @@ ${summary}`);
3022
3906
  }
3023
3907
  // Annotate the CommonJS export names for ESM import in node:
3024
3908
  0 && (module.exports = {
3909
+ BEACON_BOT_MISSION_ONE_AUTHORING_V1,
3025
3910
  JUNIOR_CODER_MISSION_STAGE_ORDER_V1,
3026
3911
  JUNIOR_CODER_MODULE_PRICE_V1_1,
3027
3912
  JUNIOR_CODER_ROBOT_RESCUE_PATH_CURRENT,
@@ -3034,6 +3919,7 @@ ${summary}`);
3034
3919
  RESCUE_CREW_COMMANDER_MISSION_ONE_AUTHORING_V1,
3035
3920
  ROAD_HOPPER_RALLY_MISSION_ONE_AUTHORING_V1,
3036
3921
  ROBOT_MAZE_DASH_MISSION_ONE_AUTHORING_V1,
3922
+ SERVO_CREATURE_MISSION_ONE_AUTHORING_V1,
3037
3923
  SKYWING_SPRINT_MISSION_ONE_AUTHORING_V1,
3038
3924
  STAR_DEFENDER_SQUADRON_MISSION_ONE_AUTHORING_V1,
3039
3925
  assertValidLearningPath,