@plasius/learning 0.6.0 → 0.8.0

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.
Files changed (48) hide show
  1. package/README.md +41 -0
  2. package/dist/chunk-UNPCTBNK.js +43 -0
  3. package/dist/chunk-UNPCTBNK.js.map +1 -0
  4. package/dist/course-suggestions-CBs5swU6.d.ts +17 -0
  5. package/dist/course-suggestions-mK2MPB7e.d.cts +17 -0
  6. package/dist/courses/adventure-mission-planner.cjs +436 -0
  7. package/dist/courses/adventure-mission-planner.cjs.map +1 -0
  8. package/dist/courses/adventure-mission-planner.d.cts +6 -0
  9. package/dist/courses/adventure-mission-planner.d.ts +6 -0
  10. package/dist/courses/adventure-mission-planner.js +210 -0
  11. package/dist/courses/adventure-mission-planner.js.map +1 -0
  12. package/dist/courses/creature-care-dashboard.cjs +425 -0
  13. package/dist/courses/creature-care-dashboard.cjs.map +1 -0
  14. package/dist/courses/creature-care-dashboard.d.cts +6 -0
  15. package/dist/courses/creature-care-dashboard.d.ts +6 -0
  16. package/dist/courses/creature-care-dashboard.js +199 -0
  17. package/dist/courses/creature-care-dashboard.js.map +1 -0
  18. package/dist/courses/robot-mission-control.cjs +428 -0
  19. package/dist/courses/robot-mission-control.cjs.map +1 -0
  20. package/dist/courses/robot-mission-control.d.cts +6 -0
  21. package/dist/courses/robot-mission-control.d.ts +6 -0
  22. package/dist/courses/robot-mission-control.js +202 -0
  23. package/dist/courses/robot-mission-control.js.map +1 -0
  24. package/dist/courses/vibe-bug-detective.cjs +416 -0
  25. package/dist/courses/vibe-bug-detective.cjs.map +1 -0
  26. package/dist/courses/vibe-bug-detective.d.cts +8 -0
  27. package/dist/courses/vibe-bug-detective.d.ts +8 -0
  28. package/dist/courses/vibe-bug-detective.js +221 -0
  29. package/dist/courses/vibe-bug-detective.js.map +1 -0
  30. package/dist/courses/vibe-game-remix-lab.cjs +424 -0
  31. package/dist/courses/vibe-game-remix-lab.cjs.map +1 -0
  32. package/dist/courses/vibe-game-remix-lab.d.cts +8 -0
  33. package/dist/courses/vibe-game-remix-lab.d.ts +8 -0
  34. package/dist/courses/vibe-game-remix-lab.js +229 -0
  35. package/dist/courses/vibe-game-remix-lab.js.map +1 -0
  36. package/dist/courses/vibe-idea-studio.cjs +386 -0
  37. package/dist/courses/vibe-idea-studio.cjs.map +1 -0
  38. package/dist/courses/vibe-idea-studio.d.cts +8 -0
  39. package/dist/courses/vibe-idea-studio.d.ts +8 -0
  40. package/dist/courses/vibe-idea-studio.js +191 -0
  41. package/dist/courses/vibe-idea-studio.js.map +1 -0
  42. package/dist/index.cjs +41 -1
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.d.cts +1 -0
  45. package/dist/index.d.ts +1 -0
  46. package/dist/index.js +38 -0
  47. package/dist/index.js.map +1 -1
  48. package/package.json +32 -2
@@ -0,0 +1,424 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/courses/vibe-game-remix-lab.ts
21
+ var vibe_game_remix_lab_exports = {};
22
+ __export(vibe_game_remix_lab_exports, {
23
+ course: () => course,
24
+ practice: () => practice,
25
+ suggestions: () => suggestions
26
+ });
27
+ module.exports = __toCommonJS(vibe_game_remix_lab_exports);
28
+
29
+ // src/mission-authoring.ts
30
+ var JUNIOR_CODER_MISSION_STAGE_ORDER_V1 = [
31
+ "learn",
32
+ "predict",
33
+ "build",
34
+ "run",
35
+ "assess",
36
+ "inspect",
37
+ "fix",
38
+ "explain",
39
+ "reward"
40
+ ];
41
+
42
+ // src/course-contracts.ts
43
+ var LEARNING_COURSE_STAGE_ORDER = JUNIOR_CODER_MISSION_STAGE_ORDER_V1;
44
+ var LEARNING_COURSE_LIMITS = Object.freeze({
45
+ missions: 6,
46
+ stagesPerMission: 9,
47
+ projectFiles: 8,
48
+ sourceCharactersPerFile: 64e3,
49
+ sourceCharactersPerProject: 96e3
50
+ });
51
+ var ID = /^[a-z0-9][a-z0-9.-]{0,159}$/u;
52
+ var FILE_PATH = /^[a-z0-9][a-z0-9_-]{0,63}\.(?:js|py|cpp|html|css|json)$/u;
53
+ var VERSION = /^\d+\.\d+\.\d+$/u;
54
+ var LANGUAGES = ["javascript", "python", "cpp", "html", "css", "blocks", "json"];
55
+ var CATEGORIES = ["game", "robot", "vibe", "web-app"];
56
+ var PLACEHOLDER = /\b(?:TODO|TBD|coming soon|placeholder|lorem ipsum)\b/iu;
57
+ var record = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
58
+ var text = (value, minimum = 1, maximum = 8e3) => typeof value === "string" && value.trim().length >= minimum && value.length <= maximum;
59
+ var id = (value) => typeof value === "string" && ID.test(value);
60
+ var integer = (value, minimum, maximum) => typeof value === "number" && Number.isSafeInteger(value) && value >= minimum && value <= maximum;
61
+ var exactKeys = (value, keys) => Object.keys(value).length === keys.length && keys.every((key) => Object.hasOwn(value, key));
62
+ var LearningCourseInputError = class extends Error {
63
+ constructor() {
64
+ super("Invalid learning course input.");
65
+ this.name = "LearningCourseInputError";
66
+ }
67
+ };
68
+ function fileDefinitions(value) {
69
+ return Array.isArray(value) && value.length >= 1 && value.length <= LEARNING_COURSE_LIMITS.projectFiles && value.every((file) => record(file) && exactKeys(file, ["path", "language", "maximumCharacters"]) && typeof file.path === "string" && FILE_PATH.test(file.path) && typeof file.language === "string" && LANGUAGES.includes(file.language) && integer(file.maximumCharacters, 1, LEARNING_COURSE_LIMITS.sourceCharactersPerFile)) && new Set(value.map((file) => file.path)).size === value.length;
70
+ }
71
+ function parseLearningProject(course2, value) {
72
+ if (!fileDefinitions(course2.projectFiles) || !record(value) || !exactKeys(value, ["files"]) || !Array.isArray(value.files) || value.files.length !== course2.projectFiles.length) throw new LearningCourseInputError();
73
+ const files = [];
74
+ const seen = /* @__PURE__ */ new Set();
75
+ let characters = 0;
76
+ for (const file of value.files) {
77
+ if (!record(file) || !exactKeys(file, ["path", "source"]) || typeof file.path !== "string" || typeof file.source !== "string" || seen.has(file.path)) throw new LearningCourseInputError();
78
+ const definition = course2.projectFiles.find((candidate) => candidate.path === file.path);
79
+ if (!definition || file.source.length > definition.maximumCharacters || file.source.includes("\0")) throw new LearningCourseInputError();
80
+ characters += file.source.length;
81
+ if (characters > LEARNING_COURSE_LIMITS.sourceCharactersPerProject) throw new LearningCourseInputError();
82
+ seen.add(file.path);
83
+ files.push({ path: file.path, source: file.source });
84
+ }
85
+ return { files: course2.projectFiles.map((definition) => files.find((file) => file.path === definition.path)) };
86
+ }
87
+ function validateLearningCourse(value) {
88
+ const issues = [];
89
+ const add = (code, path) => {
90
+ issues.push({ code, path });
91
+ };
92
+ if (!record(value)) return [{ code: "invalid-manifest", path: "$" }];
93
+ if (!exactKeys(value, [
94
+ "schemaVersion",
95
+ "moduleId",
96
+ "moduleVersion",
97
+ "slug",
98
+ "title",
99
+ "summary",
100
+ "runtimeId",
101
+ "category",
102
+ "estimatedMinutes",
103
+ "completionAssessmentId",
104
+ "projectFiles",
105
+ "starterProject",
106
+ "reference",
107
+ "missions",
108
+ "completionBadge"
109
+ ]) || value.schemaVersion !== "1" || !id(value.moduleId) || !id(value.slug) || !id(value.runtimeId) || !text(value.moduleVersion) || !VERSION.test(value.moduleVersion) || typeof value.category !== "string" || !CATEGORIES.includes(value.category) || !text(value.title, 3, 160) || !text(value.summary, 40, 2e3) || !integer(value.estimatedMinutes, 60, 3600) || !id(value.completionAssessmentId) || !record(value.completionBadge) || !exactKeys(value.completionBadge, ["id", "title"]) || !id(value.completionBadge.id) || !text(value.completionBadge.title, 3, 160)) add("invalid-manifest", "$");
110
+ if (!fileDefinitions(value.projectFiles)) add("invalid-project", "projectFiles");
111
+ else {
112
+ try {
113
+ parseLearningProject({ projectFiles: value.projectFiles }, value.starterProject);
114
+ } catch {
115
+ add("invalid-project", "starterProject");
116
+ }
117
+ }
118
+ if (!Array.isArray(value.reference) || value.reference.length < 1 || value.reference.length > 80 || value.reference.some((entry) => !record(entry) || !exactKeys(entry, ["name", "signature", "description", "example"]) || !text(entry.name) || !text(entry.signature) || !text(entry.description, 20) || !text(entry.example))) add("incomplete-content", "reference");
119
+ if (!Array.isArray(value.missions)) {
120
+ add("mission-count", "missions");
121
+ return issues;
122
+ }
123
+ if (value.missions.length !== LEARNING_COURSE_LIMITS.missions) add("mission-count", "missions");
124
+ if (value.missions.length > LEARNING_COURSE_LIMITS.missions) return issues;
125
+ const seen = /* @__PURE__ */ new Set();
126
+ const checkId = (candidate, path) => {
127
+ if (!id(candidate)) add("invalid-manifest", path);
128
+ else if (seen.has(candidate)) add("duplicate-id", path);
129
+ else seen.add(candidate);
130
+ };
131
+ let minutes = 0;
132
+ value.missions.forEach((mission, missionIndex) => {
133
+ const path = `missions[${missionIndex}]`;
134
+ if (!record(mission)) {
135
+ add("invalid-manifest", path);
136
+ return;
137
+ }
138
+ checkId(mission.id, `${path}.id`);
139
+ checkId(mission.assessmentId, `${path}.assessmentId`);
140
+ if (!exactKeys(mission, ["id", "title", "concepts", "estimatedMinutes", "goals", "assessmentId", "stages", "extension"]) || !text(mission.title, 3, 160) || !integer(mission.estimatedMinutes, 10, 600) || !Array.isArray(mission.concepts) || mission.concepts.length < 1 || mission.concepts.length > 12 || mission.concepts.some((concept) => !text(concept, 2, 120)) || !Array.isArray(mission.goals) || mission.goals.length < 1 || mission.goals.length > 12 || mission.goals.some((goal) => !text(goal, 20, 2e3)) || !text(mission.extension, 30, 2e3)) add("incomplete-content", path);
141
+ if (typeof mission.estimatedMinutes === "number") minutes += mission.estimatedMinutes;
142
+ if (!Array.isArray(mission.stages) || mission.stages.length !== LEARNING_COURSE_LIMITS.stagesPerMission) {
143
+ add("stage-order", `${path}.stages`);
144
+ return;
145
+ }
146
+ mission.stages.forEach((stage, stageIndex) => {
147
+ const stagePath = `${path}.stages[${stageIndex}]`;
148
+ if (!record(stage)) {
149
+ add("invalid-manifest", stagePath);
150
+ return;
151
+ }
152
+ checkId(stage.id, `${stagePath}.id`);
153
+ if (stage.kind !== LEARNING_COURSE_STAGE_ORDER[stageIndex]) add("stage-order", stagePath);
154
+ if (!exactKeys(stage, ["id", "kind", "title", "instruction", "help"]) || !text(stage.title, 3, 160) || !text(stage.instruction, 40) || !text(stage.help, 30) || typeof stage.instruction === "string" && PLACEHOLDER.test(stage.instruction)) add("incomplete-content", stagePath);
155
+ });
156
+ });
157
+ if (minutes !== value.estimatedMinutes) add("invalid-manifest", "estimatedMinutes");
158
+ return issues;
159
+ }
160
+ function parseLearningCourse(value) {
161
+ if (validateLearningCourse(value).length) throw new LearningCourseInputError();
162
+ return structuredClone(value);
163
+ }
164
+
165
+ // src/courses/course-authoring.ts
166
+ function authorCourse(header, missions) {
167
+ const course2 = parseLearningCourse({
168
+ ...header,
169
+ schemaVersion: "1",
170
+ moduleVersion: "2.0.0",
171
+ moduleId: `junior-coder.${header.slug}`,
172
+ runtimeId: `${header.slug}.v2`,
173
+ estimatedMinutes: missions.length * 60,
174
+ completionAssessmentId: `${header.slug}.final`,
175
+ completionBadge: { id: `${header.slug}.completed`, title: `${header.title} creator` },
176
+ missions: missions.map((mission, index) => ({
177
+ id: `${header.slug}.m${index + 1}`,
178
+ title: mission.title,
179
+ concepts: mission.concepts,
180
+ goals: mission.goals,
181
+ estimatedMinutes: 60,
182
+ assessmentId: `${header.slug}.m${index + 1}.assessment`,
183
+ extension: mission.extension,
184
+ stages: LEARNING_COURSE_STAGE_ORDER.map((kind) => ({
185
+ id: `${header.slug}.m${index + 1}.${kind}`,
186
+ kind,
187
+ title: `${kind.charAt(0).toUpperCase()}${kind.slice(1)}: ${mission.title}`,
188
+ instruction: mission.activities[kind][0],
189
+ help: mission.activities[kind][1]
190
+ }))
191
+ }))
192
+ });
193
+ const practice2 = structuredClone(missions.flatMap((mission, index) => ["learn", "predict", "explain"].map((kind) => ({ ...mission.questions[kind], stageId: `${header.slug}.m${index + 1}.${kind}` }))));
194
+ for (const question of practice2) {
195
+ if (question.question.trim().length < 20 || question.feedback.trim().length < 40 || question.choices.length !== 3 || question.choices.some((choice) => choice.trim().length < 2) || new Set(question.choices).size !== 3 || !Number.isInteger(question.correctChoice) || question.correctChoice < 0 || question.correctChoice > 2) throw new Error("Invalid course practice question.");
196
+ }
197
+ return { course: course2, practice: practice2 };
198
+ }
199
+
200
+ // src/courses/vibe-game-remix-lab.ts
201
+ var { course, practice } = authorCourse({
202
+ slug: "vibe-game-remix-lab",
203
+ title: "Vibe Game Remix Lab",
204
+ category: "vibe",
205
+ summary: "Remix a working rescue game by explaining its baseline, writing a bounded intent and implementing a purposeful change. Review real source differences, reject a tempting incorrect suggestion and protect the result with executable examples. Finish a playable three-beacon rescue with a two-cell dash. Authored suggestions make live AI unnecessary.",
206
+ projectFiles: [{ path: "game.js", language: "javascript", maximumCharacters: 32e3 }, { path: "brief.json", language: "json", maximumCharacters: 6e3 }],
207
+ starterProject: { files: [{ path: "game.js", source: `function rules() {
208
+ return { title: "Pocket Rescue", targetCount: 1, stride: 1 };
209
+ }
210
+
211
+ function initialState() {
212
+ const beacons = [{ id: "amber", x: 3, y: 3 }, { id: "mint", x: 8, y: 1 }, { id: "violet", x: 10, y: 6 }];
213
+ return { player: { x: 1, y: 3 }, beacons: beacons.slice(0, rules().targetCount), rescued: [], energy: 40, status: "ready" };
214
+ }
215
+
216
+ function canEnter(x, y) {
217
+ return Number.isInteger(x) && Number.isInteger(y) && x >= 0 && x < 12 && y >= 0 && y < 8
218
+ && !(x === 5 && y >= 2 && y <= 4);
219
+ }
220
+
221
+ function update(state, input) {
222
+ if (input.type === "restart") return initialState();
223
+ const next = JSON.parse(JSON.stringify(state));
224
+ if (input.type === "start" && next.status === "ready") next.status = "playing";
225
+ if (input.type !== "move" || next.status !== "playing") return next;
226
+ const directions = { up: [0,-1], right: [1,0], down: [0,1], left: [-1,0] };
227
+ if (!Object.hasOwn(directions, input.direction)) return next;
228
+ const [dx, dy] = directions[input.direction];
229
+ const x = next.player.x + dx;
230
+ const y = next.player.y + dy;
231
+ if (!canEnter(x, y)) return next;
232
+ next.player = { x, y };
233
+ next.energy -= 1;
234
+ for (const beacon of next.beacons) {
235
+ if (beacon.x === x && beacon.y === y && !next.rescued.includes(beacon.id)) next.rescued.push(beacon.id);
236
+ }
237
+ if (next.rescued.length === next.beacons.length) next.status = "won";
238
+ else if (next.energy === 0) next.status = "tired";
239
+ return next;
240
+ }
241
+
242
+ function describeState(state) { return {}; }
243
+ function acceptanceCases() { return []; }
244
+ ` }, { path: "brief.json", source: '{\n "audience": "",\n "goal": "",\n "constraints": []\n}\n' }] },
245
+ reference: [
246
+ { name: "rules", signature: "rules() \u2192 { title, targetCount, stride }", description: "The baseline has one beacon and stride one. The finished remix has targetCount=3 and stride=2, plus an authored title of 3\u201340 characters. These are the only rule fields. A fresh initialState uses current rules; an existing run is never silently resized.", example: 'return { title: "Three Beacon Rescue", targetCount: 3, stride: 2 };' },
247
+ { name: "initialState", signature: "initialState() \u2192 rescue game state", description: "A 12\xD78 board starts at (1,3), energy 40 and ready status. A wall occupies (5,2), (5,3), (5,4). Beacons are amber (3,3), mint (8,1), violet (10,6), selected in that order by targetCount. A fresh state has a fresh beacons list and empty rescued IDs.", example: "const fresh = initialState();" },
248
+ { name: "update", signature: "update(state, input) \u2192 next state", description: "Actions are start, move with direction up/right/down/left and optional dash boolean, and restart. Unknown actions or directions preserve values. Each valid cell move costs one energy and rescues any beacon on that cell once. Blocked movement costs nothing. Do not mutate the input state.", example: 'update(state, { type: "move", direction: "right", dash: true });' },
249
+ { name: "dash", signature: "visit each intermediate cell", description: "An ordinary move travels one cell. An explicit dash=true requests rules().stride cells, up to two. Check every intermediate cell and stop the dash on a wall, edge or terminal outcome. Collect before terminal checks; rescuing the last beacon on the last energy wins rather than becoming tired.", example: "const stride = input.dash === true ? rules().stride : 1;" },
250
+ { name: "describeState", signature: "describeState(state) \u2192 { status, x, y, rescued, remaining, energy }", description: "Return the current status, player coordinates, rescued count, remaining beacon count and energy as plain data. Read state without moving, scoring or changing it. The host can render an accessible text summary from this evidence.", example: "const remaining = state.beacons.length - state.rescued.length;" },
251
+ { name: "requestedStride", signature: "requestedStride(input) \u2192 1 or 2", description: "The reviewed refactor extracts dash selection into this helper. Only dash===true requests configured stride; absent, false or other values select one. The update function must actually use the helper while preserving intermediate checks.", example: "const stride = requestedStride(input);" },
252
+ { name: "brief.json", signature: "{ audience, goal, constraints }", description: "Write a fictional audience and goal, each 10\u2013160 characters, and three to six specific constraints of 10\u2013160 characters. Keep personal information out. The brief records intent; executable behaviour and independent assessment supply evidence that the implementation meets the rules.", example: '{ "audience": "Players learning to plan a route", "goal": "Rescue three beacons with an optional dash", "constraints": ["Check each intermediate square", "Preserve ordinary one-cell movement", "Keep stop and restart behaviour predictable"] }' },
253
+ { name: "acceptanceCases", signature: "acceptanceCases() \u2192 4\u20138 named replay cases", description: "Each case has name (3\u201360 characters), actions (1\u201380 documented game actions from fresh state) and expected {x,y,energy,rescued,status}. rescued is an ordered ID array. Include wall, board-edge, dash and restart cases. The host executes cases against saved source and compares exact projections; learner-written cases cannot award completion alone.", example: '{ name: "Left edge", actions: [{type:"start"},{type:"move",direction:"left"},{type:"move",direction:"left"}], expected: {x:0,y:3,energy:39,rescued:[],status:"playing"} }' },
254
+ { name: "terminal states", signature: 'status: "ready" | "playing" | "won" | "tired"', description: "Won means every selected beacon was rescued. Tired means zero energy without completing the rescue. Both preserve state for later actions except restart, which returns a fresh ready state and clears only this play session. Simulator state does not award course completion.", example: 'if (input.type === "restart") return initialState();' }
255
+ ]
256
+ }, [
257
+ {
258
+ title: "Know the starting game",
259
+ concepts: ["Baseline", "Reproduction", "Observable state"],
260
+ goals: ["Reproduce the supplied game's behaviour before changing its rules.", "Implement a truthful state summary without mutating gameplay."],
261
+ extension: "Record two different routes to the first beacon. Compare their energy cost and identify which observations explain the difference.",
262
+ activities: {
263
+ learn: ["The baseline is a working one-beacon game. Start at (1,3) with 40 energy; each valid cell costs one, walls cost nothing, and reaching amber at (3,3) wins. Before remixing, reproduce a short input sequence and record what it actually does.", "Knowing the original behaviour gives you something concrete to preserve or deliberately change."],
264
+ predict: ["From a fresh state, start and move right twice. Predict position, rescued count, remaining count, energy and status after each action. Then send another move after victory.", "A completed round stays frozen until restart; an extra key press is not a new round."],
265
+ build: ["Implement describeState to return status, x, y, rescued count, remaining count and energy. Derive the values from its argument without changing it. Keep update and rules unchanged while you make the baseline easier to inspect.", "The summary reports evidence; it must not manufacture a score or trigger another movement."],
266
+ run: ["Play the predicted sequence with keyboard and onscreen controls. Compare the state summary after each action, test a board edge and restart for a fresh run. Save this known baseline before experimenting.", "A text summary provides the same evidence without depending on the moving player image."],
267
+ assess: ["Check the summary on ready, moving, won and tired states, including remaining counts. Verify that reading the summary changes no source state and that the original movement, wall and restart rules still work.", "A correct-looking string is insufficient if producing it secretly changes the game."],
268
+ inspect: ["If counts disagree, inspect the difference between rescued IDs and all beacon records. If reading status moves the player, inspect calls inside describeState. Compare one expected action with its actual state transition.", "Inspect the first disagreement rather than starting from a vague claim that the whole game is broken."],
269
+ fix: ["Repair the summary and replay the short right-right sequence plus restart. Confirm baseline game behaviour remains unchanged and save the verified source as a named starting point.", "Keep your evidence small enough that another learner could reproduce it from the same fresh state."],
270
+ explain: ["Choose what makes a baseline reproducible and describe one rule you observed. Distinguish a prediction written before running from a result recorded after the run.", "Predictions and observations can disagree; that disagreement is useful evidence for investigation."],
271
+ reward: ["Save Known baseline. You can now explain the original game from observable state. Next you will define a remix whose purpose and limits are clear enough to test.", "Retain the baseline slot so future changes can be compared with a working version."]
272
+ },
273
+ questions: {
274
+ learn: { question: "Why reproduce the baseline before changing it?", choices: ["To know which behaviour a change preserves or alters", "To avoid testing any later change", "To assume every suggestion is correct"], correctChoice: 0, feedback: "A recorded baseline establishes observable behaviour against which the effect of a proposed change can be compared." },
275
+ predict: { question: "After start and two right moves in the baseline, what energy remains?", choices: ["40", "38", "Zero"], correctChoice: 1, feedback: "Both moves are valid cells and cost one energy each, taking the initial 40 to 38 when amber is rescued." },
276
+ explain: { question: "What should describeState do to its input?", choices: ["Advance one extra movement", "Clear rescued IDs", "Read it without mutation"], correctChoice: 2, feedback: "An observation helper should report the current game rather than changing the thing it is meant to explain." }
277
+ }
278
+ },
279
+ {
280
+ title: "Give the remix a purpose",
281
+ concepts: ["Audience", "Intent", "Acceptance criteria"],
282
+ goals: ["Write a bounded brief for a three-beacon rescue game.", "Change the objective while preserving the baseline's movement and evidence rules."],
283
+ extension: "Write an alternative audience for the same game and discuss which proposed change would serve it. Keep this as a separate idea rather than expanding the current task without a limit.",
284
+ activities: {
285
+ learn: ["A useful remix brief names an audience, a goal and constraints. This project grows the rescue from one beacon to three, then adds an optional two-cell dash. Preserve ordinary movement, intermediate wall checks, once-only rescues and a fresh restart throughout.", "A broad request such as make it better does not identify a result you can verify."],
286
+ predict: ["With targetCount changed from one to three, predict what happens after the same right-right sequence. Identify which state fields change meaning and which movement and energy results remain the same.", "Amber is now one part of the objective; the same position no longer necessarily means the round is won."],
287
+ build: ["Complete brief.json using fictional information and three to six concrete constraints. Give rules an authored title and targetCount=3, keeping stride=1 for now. Ensure initialState selects all three beacons and describeState reports the new remaining count.", "Changing the objective is this mission's focused implementation; keep the dash change for the next mission."],
288
+ run: ["Restart after editing rules so the new round uses them. Rescue amber and inspect the remaining two targets. Plan a route around the wall to mint and violet, comparing energy and status along the way.", "Editing rules must not silently change the beacon list inside an already-running state."],
289
+ assess: ["Check a fresh three-beacon state, authored title, bounded brief and once-only collection. The original movement, wall, energy and restart behaviour must still pass while victory now requires all selected beacons.", "The brief records intention; independent behaviour checks establish whether the implementation actually follows the course rules."],
290
+ inspect: ["If amber still ends the round, inspect a hard-coded win condition. If remaining is wrong, inspect the summary's source values. If a running round changes unexpectedly, inspect whether update rebuilds objectives from rules mid-game.", "Follow the goal from configuration into fresh state and then into the terminal decision."],
291
+ fix: ["Repair the objective flow and replay amber-only progress followed by a complete three-target route. Keep ordinary controls unchanged and compare the result against the baseline constraints in your brief.", "Avoid changing several unrelated rules while fixing the goal; a focused difference is easier to explain."],
292
+ explain: ["Choose which brief describes an observable change and explain how one constraint protected existing behaviour. State which evidence demonstrates the three-target objective rather than merely repeating the title.", "A renamed heading does not prove that the playable rules changed."],
293
+ reward: ["Save Three-beacon purpose. Your remix now has a distinct objective and a clear brief. Next you will implement the optional dash while protecting every intermediate movement rule.", "Keep the one-beacon baseline and the three-beacon version in separate named slots."]
294
+ },
295
+ questions: {
296
+ learn: { question: "Which request gives a testable remix objective?", choices: ["Make everything more exciting", "Require three beacons while preserving ordinary movement", "Change whatever seems interesting"], correctChoice: 1, feedback: "The request names an observable objective and a preservation constraint, giving the implementation a clear scope." },
297
+ predict: { question: "After rescuing amber in a fresh three-beacon game, how many remain?", choices: ["Zero", "Three", "Two"], correctChoice: 2, feedback: "One of the three selected beacons has been rescued, so two remain and the round is still playing." },
298
+ explain: { question: "What proves that the objective changed beyond its title?", choices: ["The state and win condition require all three rescues", "The heading has more characters", "The brief says the idea is finished"], correctChoice: 0, feedback: "Observable state transitions and terminal behaviour provide evidence that the actual playable objective changed." }
299
+ }
300
+ },
301
+ {
302
+ title: "One change with clear limits",
303
+ concepts: ["Incremental implementation", "Intermediate state", "Resource conservation"],
304
+ goals: ["Implement an optional two-cell dash using the existing movement rules.", "Check and account for every intermediate cell instead of jumping directly to a destination."],
305
+ extension: "Compare ordinary movement and a dash on the same route. Record their action counts and energy costs, explaining why fewer button presses should not mean free travel.",
306
+ activities: {
307
+ learn: ["A dash requests two cell moves in one action; it does not teleport. Set configured stride to two and use const stride = input.dash === true ? rules().stride : 1; to select one or two. Visit each cell in order, applying walls, energy, collection and terminal checks after each step.", "Only the explicit boolean true requests a dash. Missing, false or unrelated values preserve ordinary movement."],
308
+ predict: ["Start at x=4,y=3 facing the wall at x=5. Predict a right dash. Then consider a dash whose first cell contains the final beacon with one energy remaining. Decide whether a second step may occur.", "A blocked intermediate cell ends the action, and a terminal result ends it immediately after a valid cell."],
309
+ build: ["Refactor the one-cell movement into a loop bounded by selected stride. Compute each next cell from the latest state, stop on blocked or terminal outcomes, and charge energy only for cells actually entered. Keep collection before the tired check so a last-energy rescue wins.", "A loop bound of at most two makes the action finite and easy to trace. Do not loop until the game eventually succeeds."],
310
+ run: ["Try a clear dash, a wall on the first cell, a wall on the second cell and a beacon on an intermediate cell. Compare ordinary movement with dash from the same saved source and initial state.", "Read the coordinate and energy trace step by step rather than judging only the final position."],
311
+ assess: ["Check explicit dash selection, ordinary movement, intermediate walls and edges, energy per entered cell, once-only intermediate collection and terminal priority. The source must implement the movement sequence, not just change a display setting.", "A destination-only collision check misses obstacles between the start and end of a dash."],
312
+ inspect: ["If a dash crosses a wall, inspect each next-cell check. If it charges two energy after moving once, inspect where decrement occurs. If victory becomes tired, inspect the order of collection and terminal decisions.", "Locate the first wrong intermediate state; a later correction cannot undo an invalid earlier move."],
313
+ fix: ["Repair the specific step rule and replay its failing scenario plus a clear dash. Recheck a normal one-cell move and a complete rescue to confirm the new action preserves the constraints in your brief.", "Do not remove the dash or relax wall rules to make a failing case disappear."],
314
+ explain: ["Choose why a multi-cell action needs intermediate checks. Explain how conserving energy per real cell preserves the game's rules even though a dash reduces the number of input actions.", "One user action can contain several valid state transitions, each with its own conditions and effects."],
315
+ reward: ["Save Bounded dash. Your remix now has a purposeful new playable action. Next you will review two actual source proposals and distinguish a safe refactor from a tempting rule-breaking shortcut.", "Keep ordinary and dashed movement examples for the upcoming diff review."]
316
+ },
317
+ questions: {
318
+ learn: { question: "How should a two-cell dash check collisions?", choices: ["Only at the final destination", "Only before the course begins", "Before entering each intermediate cell"], correctChoice: 2, feedback: "Each cell is a separate movement step, so a wall or edge on the way must stop the dash before it crosses that boundary." },
319
+ predict: { question: "The first dash step rescues the final beacon using the last energy; what happens?", choices: ["The round wins and the dash ends", "A second step always occurs", "Victory is replaced by tired"], correctChoice: 0, feedback: "Collection and victory are resolved before exhaustion, and a terminal result prevents another dash step." },
320
+ explain: { question: "Why charge energy for entered cells rather than requested stride?", choices: ["To make blocked travel free of all rules", "To account for actual movement when a dash stops early", "To ignore obstacles"], correctChoice: 1, feedback: "A blocked second step never occurs, so only completed cell transitions should consume the movement resource." }
321
+ }
322
+ },
323
+ {
324
+ title: "Read what the diff changes",
325
+ concepts: ["Source diff", "Constraints", "Explicit review"],
326
+ goals: ["Reject a proposed shortcut that violates ordinary movement.", "Apply a reviewed helper refactor only when its current-source context matches."],
327
+ extension: "Write a short review of the rejected proposal without using the author's stated intent as proof. Cite the changed expression and one input that exposes its effect.",
328
+ activities: {
329
+ learn: ["A diff shows exact source before and after. One authored proposal replaces conditional stride with rules().stride, claiming to simplify movement. Another calls a named requestedStride helper. Inspect the changed behaviour and prerequisites; a suggestion's confident explanation is not evidence that it is correct.", "The suggestions are versioned teaching examples. No live AI service is needed, and neither proposal is applied automatically."],
330
+ predict: ["For the unconditional-stride proposal, predict an ordinary move with dash=false and configured stride two. For the helper proposal, predict what happens if requestedStride has not been defined yet.", "One change breaks an existing rule; the other has a dependency that must be implemented and verified before acceptance."],
331
+ build: ["Reject the unconditional-stride proposal with the ordinary-move counterexample. Define requestedStride using the explicit-boolean rule, then review and accept the helper-call proposal if its original snippet matches your current source exactly once. Otherwise reconcile your source and review again.", "Do not apply a stale proposal by guessing a location. A mismatch is a reason to inspect the current version, not overwrite unrelated work."],
332
+ run: ["Compare before and after source, run an ordinary move and a dash, and call the helper with true, false and absent dash. Inspect the actual state changes after the accepted refactor and compare them with your prediction.", "The review panel should show removed and added text with labels, not rely on red and green colours alone."],
333
+ assess: ["Check requestedStride independently and confirm update uses it while preserving all intermediate checks. Verify both source-review decisions and the current saved project's behaviour; accepting a proposal alone cannot award a passing result.", "An unused correct helper does not prove that the playable update function follows the reviewed rule."],
334
+ inspect: ["If ordinary movement now dashes, inspect which proposal was accepted. If execution fails, inspect the helper definition and call. If the diff context is stale, compare the current source against its exact original snippet before changing anything.", "Keep the review reason separate from the execution result so both can be examined."],
335
+ fix: ["Repair the refactor and rerun ordinary, dashed, blocked and terminal cases. Keep or restore the last known working save if needed, then save and assess the corrected current source.", "Rejecting a poor suggestion is a successful review decision when you can explain the violated requirement."],
336
+ explain: ["Choose why a shorter diff can still be wrong. Explain one prerequisite you checked before accepting the helper proposal and the regression evidence that supports your decision.", "Reason from the changed expression and observed behaviour rather than the source or confidence of the suggestion."],
337
+ reward: ["Save Reviewed refactor. You have practised both accepting and rejecting concrete source changes. Next you will encode important examples as repeatable regression cases for future edits.", "Keep a note of the ordinary-move counterexample; it is a useful test case as well as a review argument."]
338
+ },
339
+ questions: {
340
+ learn: { question: "What should determine whether a proposal is accepted?", choices: ["Its actual change, constraints and observed evidence", "How confident its explanation sounds", "Whether it makes the file shorter"], correctChoice: 0, feedback: "Review depends on what the source change does and whether verified behaviour satisfies the stated requirements." },
341
+ predict: { question: "Unconditional configured stride two is used for dash=false; what breaks?", choices: ["The title length", "Ordinary movement now travels two cells", "The existence of the board"], correctChoice: 1, feedback: "Removing the condition makes every movement use stride two, violating the requirement that ordinary input stays one cell." },
342
+ explain: { question: "What should happen when a proposal's original snippet no longer matches once?", choices: ["Overwrite the whole project", "Apply it to every approximate match", "Review and reconcile the current source first"], correctChoice: 2, feedback: "A stale or ambiguous context cannot safely identify the intended change, so the current version needs another review." }
343
+ }
344
+ },
345
+ {
346
+ title: "Protect the working rules",
347
+ concepts: ["Executable examples", "Regression", "Expected results"],
348
+ goals: ["Write replay cases with independent expected results for important game rules.", "Use failing cases to repair behaviour without weakening the original requirements."],
349
+ extension: "Add a regression for a last-energy final rescue. Work out the expected terminal state before running it and explain which order of operations the case protects.",
350
+ activities: {
351
+ learn: ["acceptanceCases returns four to eight named replays, each starting from initialState. Supply documented actions and an expected projection of x, y, energy, ordered rescued IDs and status. Include wall, edge, dash and restart cases, using at most 80 actions per case.", "Expected results are predictions of the requirement. Copying whatever a broken run produced would preserve the defect instead."],
352
+ predict: ["For start, left, left from the final three-beacon game, predict x=0,y=3,energy=39,rescued=[] and playing status. Explain why the blocked second left consumes no energy and does not leave the board.", "This small edge case tests movement validity and resource accounting together."],
353
+ build: ["Implement acceptanceCases with distinct useful names, bounded action lists and complete expected projections. Derive expectations before executing them. Include ordinary and dashed behaviour, a blocked intermediate step and restart after changes to state.", "A case called Wall check that never approaches a wall is not evidence for that rule; inspect what the actions actually exercise."],
354
+ run: ["Run your cases against the saved project and compare each expected projection with the actual result. Temporarily introduce the reviewed unconditional-stride defect in a separate save and confirm a regression catches it.", "Keep the passing project safe while using a deliberately wrong variant to test whether the test can detect failure."],
355
+ assess: ["Check case shape and limits, useful scenario coverage and exact expected outcomes. The independent assessment also runs scenarios beyond your examples, including terminal priority and repeat-safe collection.", "Learner-written tests help explain the project but cannot declare their own score or replace independent verification."],
356
+ inspect: ["For a failing replay, find the first action that differs from the rule. Decide whether the implementation is wrong or your expected projection was calculated incorrectly, using the documented contract to distinguish them.", "Do not decide that the expected value is wrong merely because the current programme produced something else."],
357
+ fix: ["Repair the code or an incorrectly reasoned expectation, then replay the focused case and the full set. Restore the correct version after the deliberate-defect experiment and verify every saved case again.", "Removing a failing case or relaxing a rule is not a repair of the underlying behaviour."],
358
+ explain: ["Choose what makes a regression case useful and explain which real defect your case detects. Describe a scenario that your current examples do not cover and why independent checks remain necessary.", "A passing set is evidence for the cases it exercises, not proof that every possible input is correct."],
359
+ reward: ["Save Protected rules. Your remix now carries executable examples that can challenge future edits. The final mission brings the brief, gameplay, diff review and tests together into a complete showcase.", "Keep your testable counterexample beside the reviewed source version."]
360
+ },
361
+ questions: {
362
+ learn: { question: "How should an expected test result be chosen?", choices: ["Copy the current output without checking the rule", "Derive it from the requirement before running the case", "Use any value that makes the check pass"], correctChoice: 1, feedback: "An independent prediction tests whether the implementation meets the requirement instead of merely repeating its current behaviour." },
363
+ predict: { question: "Start, left, left from (1,3) should leave which energy value?", choices: ["38", "40", "39"], correctChoice: 2, feedback: "Only the first left enters a valid new cell; the second is blocked at the edge and consumes no energy." },
364
+ explain: { question: "What makes a regression case useful after a repair?", choices: ["It detects the earlier defect if that behaviour returns", "Its name sounds reassuring", "It never executes the changed path"], correctChoice: 0, feedback: "A useful regression exercises the protected behaviour and fails when the previously repaired defect is reintroduced." }
365
+ }
366
+ },
367
+ {
368
+ title: "Your rescue remix",
369
+ concepts: ["Integration", "Evidence-based showcase", "Trade-offs"],
370
+ goals: ["Finish a playable three-beacon rescue with a bounded optional dash.", "Explain the remix through its brief, reviewed source and verified behaviour."],
371
+ extension: "Propose a further original rule in a separate saved experiment. State its intended benefit, preservation constraints and new tests before changing the completed version.",
372
+ activities: {
373
+ learn: ["A finished remix connects purpose to implementation and evidence. Your three-beacon objective, optional dash, once-only rescues, energy accounting and restart must work together. The brief explains the choices; state summaries, reviewed differences and regression cases let another person verify them.", "An attractive title or a confident proposal is not a substitute for a playable result."],
374
+ predict: ["Plan a full route to all three beacons, marking ordinary moves, dashes and wall detours. Estimate energy before playing. Predict how the final state should respond to extra movement and then to restart.", "Treat each intermediate dash cell as part of the route when counting both travel and collection."],
375
+ build: ["Finish the current source, title, brief and acceptance cases. Keep requestedStride, movement checks and describeState consistent. Remove abandoned experimental behaviour, preserving one clear implementation of the completed rules.", "An authored title can express your theme while the documented playable contract stays testable."],
376
+ run: ["Play a full rescue with keyboard input, then onscreen movement and dash controls. Compare the same action sequence with reduced motion and inspect state summaries. Exercise victory, exhaustion, restart and a saved-version reload.", "The project must remain understandable without needing to follow animation or distinguish colours alone."],
377
+ assess: ["Run final saved-source checks across objectives, ordinary and dashed movement, intermediate collisions, collection, energy, terminal priority, restart, bounded examples and the reviewed helper. Completion also requires the preceding six-mission activity sequence.", "Current-source evidence matters: a successful result from an older save cannot establish that new edits work."],
378
+ inspect: ["Find the earliest mismatch between your stated constraint and a real replay. Classify it as goal, input, movement, accounting, summary or test error, then use the smallest evidence-backed change that repairs it.", "Inspect both the source difference and its effect; a small patch can still have a wide behavioural impact."],
379
+ fix: ["Repair the failing rule and rerun its case plus a complete rescue. Save the final source in a named slot and assess it again before finishing. Keep baseline and final versions available for an honest comparison.", "Do not delete evidence of an earlier failure; use it to explain what the repair changed."],
380
+ explain: ["Choose what demonstrates the remix is complete and explain one trade-off introduced by the dash. Describe the suggestion you rejected, the refactor you accepted and the evidence behind both decisions.", "You own the final review decision whether an idea came from authored teaching material, a person or an optional future assistant."],
381
+ reward: ["Save Rescue remix and finish the final assessment. You have turned a bounded idea into a playable project, reviewed actual changes and protected important rules with tests. Replay a mission or begin another experiment while retaining earned completion.", "Authored suggestions, private play and assessment work without a live AI service. Keep your tested project before extending its rules."]
382
+ },
383
+ questions: {
384
+ learn: { question: "Which combination supports a complete remix?", choices: ["A new title and no working controls", "An accepted suggestion without testing", "A clear goal, working source and verified behaviour"], correctChoice: 2, feedback: "Completion connects the intended change to an implemented project and evidence that its documented rules work together." },
385
+ predict: { question: "After all three beacons are rescued, what should an extra move do?", choices: ["Preserve the won state until restart", "Spend hidden extra energy", "Start another round automatically"], correctChoice: 0, feedback: "Victory is terminal for the current round, so later movement preserves its result until an explicit restart." },
386
+ explain: { question: "Who remains responsible for accepting a source change?", choices: ["The proposal's confident wording", "The learner reviewing constraints and evidence", "The number of lines removed"], correctChoice: 1, feedback: "The learner makes the review decision based on the actual change and evidence, regardless of where the suggestion originated." }
387
+ }
388
+ }
389
+ ]);
390
+ var suggestions = [
391
+ { stageId: "vibe-game-remix-lab.m4.build", proposal: {
392
+ id: "remix-always-dash",
393
+ source: "authored-fallback",
394
+ intent: "Simplify stride selection without changing ordinary movement.",
395
+ constraints: ["Ordinary movement must remain one cell.", "Only explicit dash=true may select configured stride."],
396
+ permittedArtifactId: "game.js",
397
+ originalSnippet: "const stride = input.dash === true ? rules().stride : 1;",
398
+ replacementSnippet: "const stride = rules().stride;",
399
+ explanationPrompt: "Does this proposed simplification preserve the ordinary movement constraint?",
400
+ aiOptional: false,
401
+ learnerApprovalRequired: true,
402
+ alternatives: ["accept", "reject"]
403
+ } },
404
+ { stageId: "vibe-game-remix-lab.m4.build", proposal: {
405
+ id: "remix-named-stride",
406
+ source: "authored-fallback",
407
+ intent: "Use the reviewed requestedStride helper for the existing selection rule.",
408
+ constraints: ["Define requestedStride first with the same explicit-boolean rule.", "Preserve each intermediate cell check and all terminal decisions."],
409
+ permittedArtifactId: "game.js",
410
+ originalSnippet: "const stride = input.dash === true ? rules().stride : 1;",
411
+ replacementSnippet: "const stride = requestedStride(input);",
412
+ explanationPrompt: "Which checks establish that the helper refactor preserves both ordinary and dashed movement?",
413
+ aiOptional: false,
414
+ learnerApprovalRequired: true,
415
+ alternatives: ["accept", "reject"]
416
+ } }
417
+ ];
418
+ // Annotate the CommonJS export names for ESM import in node:
419
+ 0 && (module.exports = {
420
+ course,
421
+ practice,
422
+ suggestions
423
+ });
424
+ //# sourceMappingURL=vibe-game-remix-lab.cjs.map