@codio-ai/opencode-authoring-agent 0.1.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 (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +119 -0
  3. package/bin/codio-authoring-register.js +77 -0
  4. package/bin/codio-authoring-unregister.js +45 -0
  5. package/dist/agents/assessment-author.d.ts +3 -0
  6. package/dist/agents/assessment-author.d.ts.map +1 -0
  7. package/dist/agents/assessment-author.test.d.ts +2 -0
  8. package/dist/agents/assessment-author.test.d.ts.map +1 -0
  9. package/dist/agents/index.d.ts +6 -0
  10. package/dist/agents/index.d.ts.map +1 -0
  11. package/dist/agents/index.test.d.ts +2 -0
  12. package/dist/agents/index.test.d.ts.map +1 -0
  13. package/dist/agents/orchestrator.d.ts +4 -0
  14. package/dist/agents/orchestrator.d.ts.map +1 -0
  15. package/dist/agents/orchestrator.test.d.ts +2 -0
  16. package/dist/agents/orchestrator.test.d.ts.map +1 -0
  17. package/dist/agents/outline-architect.d.ts +3 -0
  18. package/dist/agents/outline-architect.d.ts.map +1 -0
  19. package/dist/agents/outline-architect.test.d.ts +2 -0
  20. package/dist/agents/outline-architect.test.d.ts.map +1 -0
  21. package/dist/agents/page-author.d.ts +3 -0
  22. package/dist/agents/page-author.d.ts.map +1 -0
  23. package/dist/agents/page-author.test.d.ts +2 -0
  24. package/dist/agents/page-author.test.d.ts.map +1 -0
  25. package/dist/agents/source-ingester.d.ts +3 -0
  26. package/dist/agents/source-ingester.d.ts.map +1 -0
  27. package/dist/agents/source-ingester.test.d.ts +2 -0
  28. package/dist/agents/source-ingester.test.d.ts.map +1 -0
  29. package/dist/agents/types.d.ts +39 -0
  30. package/dist/agents/types.d.ts.map +1 -0
  31. package/dist/agents/types.test.d.ts +2 -0
  32. package/dist/agents/types.test.d.ts.map +1 -0
  33. package/dist/agents/validator.d.ts +3 -0
  34. package/dist/agents/validator.d.ts.map +1 -0
  35. package/dist/agents/validator.test.d.ts +2 -0
  36. package/dist/agents/validator.test.d.ts.map +1 -0
  37. package/dist/config/bedrock-models.d.ts +11 -0
  38. package/dist/config/bedrock-models.d.ts.map +1 -0
  39. package/dist/config/index.d.ts +26 -0
  40. package/dist/config/index.d.ts.map +1 -0
  41. package/dist/config/index.test.d.ts +2 -0
  42. package/dist/config/index.test.d.ts.map +1 -0
  43. package/dist/index.d.ts +5 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +1398 -0
  46. package/dist/index.test.d.ts +2 -0
  47. package/dist/index.test.d.ts.map +1 -0
  48. package/dist/tools/create-assessment.d.ts +310 -0
  49. package/dist/tools/create-assessment.d.ts.map +1 -0
  50. package/dist/tools/create-assessment.test.d.ts +2 -0
  51. package/dist/tools/create-assessment.test.d.ts.map +1 -0
  52. package/dist/tools/create-page.d.ts +49 -0
  53. package/dist/tools/create-page.d.ts.map +1 -0
  54. package/dist/tools/create-page.test.d.ts +2 -0
  55. package/dist/tools/create-page.test.d.ts.map +1 -0
  56. package/dist/tools/index.d.ts +62 -0
  57. package/dist/tools/index.d.ts.map +1 -0
  58. package/dist/tools/index.test.d.ts +2 -0
  59. package/dist/tools/index.test.d.ts.map +1 -0
  60. package/dist/tools/validate-guide.d.ts +17 -0
  61. package/dist/tools/validate-guide.d.ts.map +1 -0
  62. package/dist/tools/validate-guide.test.d.ts +2 -0
  63. package/dist/tools/validate-guide.test.d.ts.map +1 -0
  64. package/dist/utils/codio-paths.d.ts +13 -0
  65. package/dist/utils/codio-paths.d.ts.map +1 -0
  66. package/dist/utils/codio-paths.test.d.ts +2 -0
  67. package/dist/utils/codio-paths.test.d.ts.map +1 -0
  68. package/dist/utils/taskid.d.ts +7 -0
  69. package/dist/utils/taskid.d.ts.map +1 -0
  70. package/dist/utils/taskid.test.d.ts +2 -0
  71. package/dist/utils/taskid.test.d.ts.map +1 -0
  72. package/dist/utils/uuid.d.ts +3 -0
  73. package/dist/utils/uuid.d.ts.map +1 -0
  74. package/dist/utils/uuid.test.d.ts +2 -0
  75. package/dist/utils/uuid.test.d.ts.map +1 -0
  76. package/package.json +70 -0
  77. package/src/skills/reference-assessment-types/SKILL.md +309 -0
  78. package/src/skills/reference-authoring-notes/SKILL.md +297 -0
  79. package/src/skills/reference-mcq-quality/SKILL.md +56 -0
  80. package/src/skills/reference-page-flow/SKILL.md +62 -0
  81. package/src/skills/reference-project-defaults/SKILL.md +80 -0
  82. package/src/skills/reference-style/SKILL.md +14 -0
  83. package/src/skills/workflow-add-assessment/SKILL.md +29 -0
  84. package/src/skills/workflow-add-page/SKILL.md +32 -0
  85. package/src/skills/workflow-import-source/SKILL.md +31 -0
  86. package/src/skills/workflow-new-assignment/SKILL.md +69 -0
  87. package/src/skills/workflow-reorder/SKILL.md +24 -0
  88. package/src/skills/workflow-revise-page/SKILL.md +30 -0
@@ -0,0 +1,309 @@
1
+ ---
2
+ name: reference-assessment-types
3
+ description: Payload field reference for multiple-choice, code-output-compare (Standard Code Test), fill-in-the-blanks, parsons-puzzle, and llm-based-auto-rubric assessments. Use when building any assessment payload.
4
+ ---
5
+
6
+ # Assessment types
7
+
8
+ Five Codio assessment types are supported by the `assessment-author` subagent. All five share the same orchestration shape:
9
+
10
+ 1. The agent produces a **payload** (only the creative parts — name, instructions, answer content, test cases, etc.).
11
+ 2. The orchestrator pipes the payload to the `create_assessment` tool (handled by the `create_assessment` tool), which stamps it into the canonical template, generates the taskId, writes `<workspace>/.guides/assessments/<taskId>.json`, and (for code tests) writes the starter file. The tool prints the full taskId.
12
+ 3. The orchestrator splices an embed line into the parent page's markdown:
13
+
14
+ ```
15
+ {<short label> |assessment}(<taskId>)
16
+ ```
17
+
18
+ TaskId convention is uniform: `<type>-<10-digit-numeric>` (no leading zero). The tool generates this — never hand-author taskIds.
19
+
20
+ ---
21
+
22
+ ## Tool invocation
23
+
24
+ Assessment files are created by the `create_assessment` tool. Pass the payload as structured arguments. The tool exits non-zero on payload errors (missing required fields, malformed JSON, missing `.guides/` directory, MCQ without a correct answer, FITB without `<<<blank>>>` markers).
25
+
26
+ ---
27
+
28
+ ## Type: `multiple-choice`
29
+
30
+ Single-correct multiple-choice with four options (one correct, three plausible distractors). MCQ pedagogy lives in [`mcq-quality.md`](mcq-quality.md) — read it before authoring.
31
+
32
+ ### Payload
33
+
34
+ ```json
35
+ {
36
+ "name": "Question display name",
37
+ "instructions": "Question text shown to the student.",
38
+ "answers": [
39
+ { "answer": "Correct answer text", "correct": true },
40
+ { "answer": "Distractor 1" },
41
+ { "answer": "Distractor 2" },
42
+ { "answer": "Distractor 3" }
43
+ ],
44
+ "guidance": "One sentence why correct, plus one sentence per distractor on the misconception.",
45
+ "lo": "SWBAT ...",
46
+ "blooms": "2",
47
+ "tags": []
48
+ }
49
+ ```
50
+
51
+ The tool fills: `_id` UUIDs per answer, `taskId`, `metadata.tags` (prepends `Assessment Type: Multiple Choice`), all schema defaults (`isRandomized: true`, `points: 20`, etc.).
52
+
53
+ ### Optional payload fields
54
+
55
+ - `multiple_response` — `true` if more than one answer is correct. Default `false`.
56
+ - `randomize` — answer-order shuffle. Default `true`.
57
+ - `points` — default `20`.
58
+ - `max_attempts` — default `1`.
59
+
60
+ ---
61
+
62
+ ## Type: `code-output-compare` (Standard Code Test)
63
+
64
+ Runs student code with one or more test cases (stdin / argv / expected stdout). Requires a workspace file the student edits in the left panel of a 2-panel layout.
65
+
66
+ **Layout requirement:** the parent page must be `2-panels-tree` (or another 2-panel layout) with the starter file opened in panel 0. The orchestrator switches the page layout if it isn't already correct, then calls this tool.
67
+
68
+ ### Payload
69
+
70
+ ```json
71
+ {
72
+ "name": "Short test name",
73
+ "instructions": "Submit your code when ready",
74
+ "command": "python3 code/path/to/student-file.py",
75
+ "guidance": "Markdown-formatted explanation, may include a worked solution in a fenced block.",
76
+ "lo": "SWBAT ...",
77
+ "blooms": "3",
78
+
79
+ "starter_file_path": "code/path/to/student-file.py",
80
+ "starter_file_content": "# DO NOT ALTER THIS CODE\n<scaffolding>\n\n# Write your code here\n",
81
+
82
+ "test_cases": [
83
+ { "arguments": "1,2,3*4,5,6", "input": "", "output": "21", "feedback": "" },
84
+ { "arguments": "10,20*30,40", "input": "", "output": "100", "feedback": "" }
85
+ ],
86
+
87
+ "tags": [
88
+ { "name": "Programming Language", "value": "Python" },
89
+ { "name": "Category", "value": "Inheritance" },
90
+ { "name": "Content", "value": "Extending the parent class" }
91
+ ]
92
+ }
93
+ ```
94
+
95
+ Tool fills: `taskId`, `metadata.files` and `metadata.opened` from `starter_file_path`, `Assessment Type: Standard Code Test` tag, and option defaults (see below). Writes `starter_file_content` to `<workspace>/<starter_file_path>` **only if the file does not already exist** — so re-running on an existing exercise won't overwrite student work.
96
+
97
+ ### Authoring the starter file
98
+
99
+ The starter file is the scaffolding students extend. It typically has three parts:
100
+
101
+ 1. A `# DO NOT ALTER THIS CODE` block with provided helpers, parsing logic, or parent classes.
102
+ 2. A blank section with a `# Write your code here` marker.
103
+ 3. (Optional) Print/output statements that produce the test-case outputs.
104
+
105
+ Make starter and test cases coherent: the test arguments must reach the student's code through the existing scaffolding, and the expected outputs must be reachable from a correct student solution.
106
+
107
+ ### Test-case design
108
+
109
+ - **3 cases minimum, 5 typical.** First case is "happy path", subsequent cases probe edges (empty input, large numbers, boundary conditions).
110
+ - `arguments` is the program's argv (single string; the student's code is responsible for parsing it).
111
+ - `input` is stdin (used when the program reads from stdin).
112
+ - `output` is the **exact stdout** expected. The default options (`ignoreWhitespaces`, `ignoreNewline`, `ignoreCase`, `matchSubstring` all `true`) make matching forgiving but not arbitrary.
113
+ - `feedback` is shown only if `show_feedback: true` on that case — usually leave empty.
114
+
115
+ ### Optional payload fields
116
+
117
+ - `partial_points` — default `true`.
118
+ - `points` — default `20`.
119
+ - `one_time_test` — default `true`. Set `false` to allow re-submission.
120
+ - `pre_execute_command` — runs before `command`. Use for setup (e.g. installing a package). Default empty.
121
+ - `options` — overrides the default matching options:
122
+ ```json
123
+ "options": {
124
+ "ignoreCase": true, "ignoreNewline": true, "ignoreWhitespaces": true,
125
+ "matchSubstring": true, "timeout": 30
126
+ }
127
+ ```
128
+ - `show_expected_answer`, `show_guidance_after_response`, `show_name` — default `true`, `true`, `false`.
129
+
130
+ ---
131
+
132
+ ## Type: `fill-in-the-blanks`
133
+
134
+ Cloze-style questions. Students fill blanks (free text or pick from a distractor pool).
135
+
136
+ ### Payload
137
+
138
+ ```json
139
+ {
140
+ "name": "Internal name",
141
+ "instructions": "**Fill in the blanks below.**",
142
+ "text": "* A <<<child>>> class inherits attributes from a parent class\n* The <<<super()>>> function calls the parent's method",
143
+ "distractors": "sibling\nself\nbase",
144
+ "guidance": "Explains the correct answer for each blank.",
145
+ "lo": "SWBAT ...",
146
+ "blooms": "",
147
+ "tags": []
148
+ }
149
+ ```
150
+
151
+ The `text` field uses `<<<answer>>>` to mark each blank. The tool parses these into the canonical `tokens.blank` array (the answers in order) and `tokens.text` array (alternating string segments and `0` placeholders) automatically — never hand-build the tokens object.
152
+
153
+ `distractors` is a newline-separated list of additional wrong-answer options shown in the distractor pool (Codio mixes them with the correct answers when rendering). One per line.
154
+
155
+ ### Pedagogy
156
+
157
+ - **One concept per blank.** A blank should test a single fact or term, not a long phrase.
158
+ - **Cap at 6 blanks** per question. More than that and students lose track.
159
+ - **Distractors should be plausible siblings** of the correct answers — same grammatical category, same domain, common misconceptions.
160
+ - **Don't make blanks trivially copy-pasted from earlier sentences** in the same `text`.
161
+
162
+ ### Optional payload fields
163
+
164
+ - `points` — default `20`.
165
+ - `max_attempts` — default `1`.
166
+ - `partial_points` — default `false`. Set `true` to score per-blank.
167
+ - `show_name`, `show_values` — default `true`, `true`.
168
+
169
+ ---
170
+
171
+ ## Embedding
172
+
173
+ After the tool returns the `<taskId>`, the orchestrator inserts one line into the target page's markdown:
174
+
175
+ ```
176
+ {<short label> |assessment}(<taskId>)
177
+ ```
178
+
179
+ When inserting more than one assessment into a page, group them under a single `## Check your understanding` heading (preceded by a blank line). Order them in increasing Bloom's level when the page covers a progression.
180
+
181
+ The orchestrator does the embedding — `assessment-author` only writes JSON.
182
+
183
+ ---
184
+
185
+ ## Type: `parsons-puzzle`
186
+
187
+ Students drag-and-drop code blocks into the correct order. Supports indentation for Python. The `options` field is a structured object at input time; the tool serializes it as a JSON-encoded string in the output JSON (Codio expects a string there).
188
+
189
+ ### Payload
190
+
191
+ ```json
192
+ {
193
+ "name": "Python Loop Structure",
194
+ "instructions": "Arrange the code blocks to produce the correct output.",
195
+ "initial": "for i in range(2, 11):\n if i % 2 == 0:\n print(i)\nfor i in range(2, 10): #distractor\nif i % 2 == 1: #distractor",
196
+ "options": {
197
+ "max_wrong_lines": 10,
198
+ "python3": true,
199
+ "lang": "en"
200
+ },
201
+ "grader": "1",
202
+ "guidance": "The correct solution uses range(2, 11) to include 10...",
203
+ "lo": "SWBAT correctly structure a for loop in Python",
204
+ "blooms": "3",
205
+ "points": 20
206
+ }
207
+ ```
208
+
209
+ The tool fills: `taskId`, `metadata.tags` (prepends `Assessment Type: Parsons Puzzle`), all `options` defaults, and serializes `options` to a JSON string. `metadata.files` and `metadata.opened` are always empty arrays for Parsons puzzles.
210
+
211
+ ### `options` fields (all optional, defaults shown)
212
+
213
+ | Field | Default | Description |
214
+ |---|---|---|
215
+ | `sortableId` | `"sortable"` | DOM id of the sortable answer area |
216
+ | `max_wrong_lines` | `10` | Maximum distractor lines shown |
217
+ | `exec_limit` | `2500` | Execution time limit (ms) |
218
+ | `can_indent` | `true` | Whether students can indent blocks |
219
+ | `x_indent` | `50` | Pixel width of one indent level |
220
+ | `lang` | `"en"` | UI language |
221
+ | `show_feedback` | `true` | Whether to show correctness feedback |
222
+ | `python3` | `true` | Python 3 mode |
223
+ | `trashId` | `"sortableTrash"` | DOM id of the trash/unused-blocks area |
224
+
225
+ ### Optional payload fields
226
+
227
+ - `grader` — default `"1"`.
228
+ - `points` — default `20`.
229
+ - `max_attempts` — default `0` (unlimited).
230
+ - `use_maximum_score` — default `false`.
231
+ - `show_name` — default `true`.
232
+ - `show_guidance_after_attempts` — default `2`.
233
+
234
+ ### Distractor design (Parsons)
235
+
236
+ - Place the correct solution lines first in `initial`, then append distractors each ending in ` #distractor`.
237
+ - Aim for **2–4 distractors** per puzzle — enough to require careful thinking without overwhelming.
238
+ - Choose distractors that reflect **real, common mistakes**: off-by-one in range bounds, wrong relational operator (`==` vs `!=`), wrong variable name, wrong indentation level.
239
+ - For Python puzzles, the indentation of each block in `initial` matters — the tool passes it verbatim to Codio's renderer, which uses it to determine the block's starting indent level.
240
+
241
+ ---
242
+
243
+ ## Type: `llm-based-auto-rubric`
244
+
245
+ An open-ended question graded by an LLM using a rubric. The grader receives the student's submission alongside the rubric items and a reference solution. All solution files go under `.guides/secure/` — never `.guides/content/` (which is student-visible).
246
+
247
+ ### Payload
248
+
249
+ ```json
250
+ {
251
+ "name": "Test",
252
+ "instructions": "Write a Python program that prints all even numbers from 2 to 10.",
253
+ "files": ["loops.py"],
254
+ "rubrics": [
255
+ {
256
+ "title": "Loop Structure and Syntax",
257
+ "description": "Evaluates whether the student correctly implements loop structures with proper syntax including correct indentation, colons, and loop variable initialization.",
258
+ "points": 1
259
+ },
260
+ {
261
+ "title": "Loop Logic and Control Flow",
262
+ "description": "Assesses the student's understanding of loop control flow including proper use of loop conditions, iteration variables, and logical flow.",
263
+ "points": 1
264
+ }
265
+ ],
266
+ "rubrics_requirements": "rubrics to test loops application",
267
+ "solutions": [
268
+ {
269
+ "path": ".guides/secure/solution.py",
270
+ "content": "for i in range(2, 11):\n if i % 2 == 0:\n print(i)\n"
271
+ }
272
+ ],
273
+ "guidance": "The correct solution uses range(2, 11) inclusive...",
274
+ "lo": "SWBAT use for loops with conditional logic",
275
+ "blooms": "3",
276
+ "points": 5
277
+ }
278
+ ```
279
+
280
+ The tool fills: `taskId`, `metadata.tags` (always empty — no auto-tag for this type), `metadata.files` (the `files` array), `metadata.opened` (one `{type: "file", panelNumber: 0, content: path}` per file), auto-generated string `id` values for each rubric item (`"1"`, `"2"`, ...). The tool also writes each solution to its `path` relative to the workspace, using the same no-overwrite atomic pattern as code-output-compare starter files.
281
+
282
+ ### `metadata.opened` key
283
+
284
+ Note: auto-rubric uses `panelNumber` (not `panel`) in the opened entries.
285
+
286
+ ### Optional payload fields
287
+
288
+ - `files` — paths opened in the student's panel. Default `[]`.
289
+ - `rubrics_requirements` — free-text requirements context for the LLM grader. Default `""`.
290
+ - `solutions` — reference solutions written to `.guides/secure/`. Default `[]`.
291
+ - `points` — default `5`.
292
+ - `partial_points` — default `true`.
293
+ - `max_attempts` — default `0` (unlimited).
294
+ - `show_name` — default `false`.
295
+ - `show_guidance_after_attempts` — default `2`.
296
+
297
+ ### Rubric item authoring
298
+
299
+ - Write **3–5 rubric items** per question, each scoped to one observable dimension.
300
+ - Good dimensions: syntax correctness, algorithmic logic, boundary/edge case handling, output format, code style/readability.
301
+ - `title` — short label (4–8 words). The LLM grader sees this.
302
+ - `description` — concrete grading criteria written for the LLM (not the student). Be specific: "checks whether the loop uses `range(2, 11)` or equivalent to include 10" beats "correct loop bounds."
303
+ - Each rubric item's `points` typically equals `total_points / rubric_count` (integer division). All items summing to `points` is the convention.
304
+
305
+ ### Solution path discipline
306
+
307
+ - All solutions: `.guides/secure/<filename>` — hidden from students.
308
+ - Never: `.guides/content/<anything>` — student-visible.
309
+ - The tool writes the file only if it does not already exist (safe to re-run).
@@ -0,0 +1,297 @@
1
+ ---
2
+ name: reference-authoring-notes
3
+ description: Codio guide JSON schemas, markdown conventions, button syntax, callout block types, folder layout. Use when authoring or validating page JSON or markdown.
4
+ ---
5
+
6
+ # Codio authoring notes
7
+
8
+ ## Purpose
9
+ Use this reference when generating Codio guide assignments to produce output that matches Codio's import format exactly.
10
+
11
+ ---
12
+
13
+ ## Folder structure
14
+
15
+ All guide files live under `.guides/`:
16
+
17
+ ```
18
+ .guides/
19
+ ├── content/ # index.json (root config) + page/section/chapter files and folders
20
+ ├── assessments/ # one JSON file per assessment
21
+ ├── img/ # image assets (referenced as .guides/img/filename.png)
22
+ └── secure/ # files hidden from students
23
+ ```
24
+
25
+ Workspace files (source code, data files, etc.) live outside `.guides/` in normal project directories.
26
+
27
+ ---
28
+
29
+ ## Root configuration: `.guides/content/index.json`
30
+
31
+ ```json
32
+ {
33
+ "title": "Assignment Title",
34
+ "theme": "light",
35
+ "order": ["page-stem-xxxx", "chapter-folder-xxxx"],
36
+ "useSubmitButtons": true,
37
+ "useMarkAsComplete": false,
38
+ "hideMenu": false,
39
+ "allowGuideClose": false,
40
+ "protectLayout": false,
41
+ "suppressPageNumbering": true,
42
+ "collapsedOnStart": false,
43
+ "hideSectionsToggle": false,
44
+ "hideBackToDashboard": false
45
+ }
46
+ ```
47
+
48
+ The `order` array lists stems (for pages) or folder names (for sections/chapters) in reading sequence.
49
+
50
+ ---
51
+
52
+ ## Page files
53
+
54
+ Each page is a pair of files with the same stem, e.g. `intro-to-loops-a1b2`:
55
+
56
+ - `.guides/content/intro-to-loops-a1b2.json` — metadata
57
+ - `.guides/content/intro-to-loops-a1b2.md` — markdown content
58
+
59
+ ### Page JSON
60
+
61
+ ```json
62
+ {
63
+ "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
64
+ "title": "Page Title",
65
+ "type": "page",
66
+ "contentType": "markdown",
67
+ "layout": "1-panel",
68
+ "files": [],
69
+ "teacherOnly": false,
70
+ "learningObjectives": ""
71
+ }
72
+ ```
73
+
74
+ **Layout options:** `1-panel`, `2-panels-tree`, `3-cell`, `3-columns-guides-left`, `4-cell`
75
+
76
+ Use `2-panels-tree` when the page interacts with workspace files; `1-panel` for conceptual/text-only pages.
77
+
78
+ ### Files array
79
+
80
+ Controls which files and terminals open when the student lands on the page:
81
+
82
+ ```json
83
+ "files": [
84
+ { "path": "solution.py", "panel": 0, "action": "open" }
85
+ ]
86
+ ```
87
+
88
+ Special path values: `#tabs` targets the open editor group; `#terminal:` opens a terminal. Panel indices: 0–3.
89
+
90
+ ---
91
+
92
+ ## Sections and chapters
93
+
94
+ Sections and chapters are optional containers. Each lives in its own subfolder with an `index.json`:
95
+
96
+ **`.guides/content/chapter-intro-xxxx/index.json`**
97
+
98
+ ```json
99
+ {
100
+ "id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
101
+ "title": "Chapter Title",
102
+ "type": "chapter",
103
+ "order": ["page-stem-xxxx", "page-stem-yyyy"],
104
+ "files": [],
105
+ "layout": "1-panel",
106
+ "teacherOnly": false,
107
+ "closeTerminalSession": false,
108
+ "learningObjectives": ""
109
+ }
110
+ ```
111
+
112
+ Use `"type": "section"` for a lower-level grouping or `"type": "chapter"` for a top-level grouping. Containers do not need their own markdown unless they display content directly.
113
+
114
+ ---
115
+
116
+ ## Multiple-choice assessment JSON
117
+
118
+ **File location:** `.guides/assessments/multiple-choice-<10-digit-number>.json`
119
+
120
+ ```json
121
+ {
122
+ "type": "multiple-choice",
123
+ "taskId": "multiple-choice-1234567890",
124
+ "source": {
125
+ "name": "Question display name",
126
+ "showName": true,
127
+ "instructions": "Question text shown to the student.",
128
+ "multipleResponse": false,
129
+ "isRandomized": true,
130
+ "answers": [
131
+ { "_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "correct": true, "answer": "Correct answer text" },
132
+ { "_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "correct": false, "answer": "Distractor 1" },
133
+ { "_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "correct": false, "answer": "Distractor 2" },
134
+ { "_id": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", "correct": false, "answer": "Distractor 3" }
135
+ ],
136
+ "metadata": {
137
+ "tags": [{ "name": "Assessment Type", "value": "Multiple Choice" }],
138
+ "files": [],
139
+ "opened": []
140
+ },
141
+ "bloomsObjectiveLevel": "",
142
+ "learningObjectives": "",
143
+ "guidance": "",
144
+ "showGuidanceAfterResponseOption": { "type": "Attempts", "passedFrom": 1 },
145
+ "maxAttemptsCount": 1,
146
+ "showExpectedAnswerOption": { "type": "Always" },
147
+ "points": 20,
148
+ "incorrectPoints": 0,
149
+ "arePartialPointsAllowed": false,
150
+ "useMaximumScore": false
151
+ }
152
+ }
153
+ ```
154
+
155
+ **Embed the assessment in a page's markdown:**
156
+
157
+ ```
158
+ {Question Label |assessment}(multiple-choice-1234567890)
159
+ ```
160
+
161
+ ### Field reference
162
+
163
+ | Field | Options |
164
+ |---|---|
165
+ | `multipleResponse` | `false` = single correct answer, `true` = multiple correct |
166
+ | `isRandomized` | `true` to shuffle answer order (default) |
167
+ | `showGuidanceAfterResponseOption.type` | `"Attempts"`, `"Score"`, `"Always"`, `"Never"`, `"WhenGradesReleased"` |
168
+ | `showExpectedAnswerOption.type` | `"Always"`, `"Never"`, `"WhenGradesReleased"` |
169
+ | `maxAttemptsCount` | `0` = unlimited, `1`, `9`, or any integer |
170
+ | `points` | `0`, `20`, `50`, or custom integer |
171
+ | `bloomsObjectiveLevel` | `""`, `"1"`, `"2"` |
172
+
173
+ ---
174
+
175
+ ## Custom buttons (process launch only — no section buttons)
176
+
177
+ Embed directly in page markdown. Three variants:
178
+
179
+ ```
180
+ {Button Label}(command arg1 arg2)
181
+ {Button Label | terminal}(command)
182
+ {Button Label | background}(command)
183
+ ```
184
+
185
+ - **Default:** runs the command and shows output inline within the guide panel.
186
+ - **`| terminal`:** opens a terminal window — required when the program reads standard input.
187
+ - **`| background`:** launches the command asynchronously without blocking the guide.
188
+
189
+ Examples:
190
+
191
+ ```
192
+ {Run Code}(python3 solution.py)
193
+ {Compile & Run | terminal}(javac Main.java && java Main)
194
+ {Run Tests}(bash run_tests.sh)
195
+ ```
196
+
197
+ Do not add section buttons. Only add process-launch buttons when the assignment includes runnable code or commands that materially support the learning activity.
198
+
199
+ ---
200
+
201
+ ## Markdown styles
202
+
203
+ ### Headings
204
+
205
+ ```
206
+ # Heading 1
207
+ ## Heading 2
208
+ ### Heading 3
209
+ ```
210
+
211
+ ### Emphasis
212
+
213
+ ```
214
+ **bold** *italic*
215
+ ```
216
+
217
+ ### Lists
218
+
219
+ ```
220
+ - Bullet item
221
+ - Another item
222
+ - Nested (2-space indent)
223
+
224
+ 1. Numbered item
225
+ 1. Auto-increments
226
+ ```
227
+
228
+ ### Code
229
+
230
+ Fenced block with language label:
231
+
232
+ ```python
233
+ def greet(name):
234
+ return f"Hello, {name}"
235
+ ```
236
+
237
+ Suppress copy button:
238
+
239
+ ```python `-hide-clipboard`
240
+ # students cannot copy this block
241
+ ```
242
+
243
+ Inline: `` `variable_name` ``
244
+
245
+ ### Math
246
+
247
+ Block (LaTeX):
248
+
249
+ ```math
250
+ F = G \frac{m_1 m_2}{r^2}
251
+ ```
252
+
253
+ Inline: `$a \ne 0$`
254
+
255
+ Multi-line: `$$...$$`
256
+
257
+ ### Callout blocks
258
+
259
+ ```
260
+ |||info
261
+ # Title
262
+ Content here
263
+
264
+ |||
265
+
266
+ |||important
267
+ Content here
268
+
269
+ |||
270
+
271
+ |||warning
272
+ Content here
273
+
274
+ |||
275
+ ```
276
+
277
+ Supported types: `important`, `info`, `warning`, `topic`, `definition`, `challenge`, `guidance`, `debugging`.
278
+
279
+ ### Collapsible content
280
+
281
+ ```html
282
+ <details>
283
+ <summary>Click to expand</summary>
284
+
285
+ Hidden content here.
286
+
287
+ </details>
288
+ ```
289
+
290
+ ---
291
+
292
+ ## File output rules
293
+
294
+ - All guide files are written directly into the live Codio workspace under `.guides/content/` and `.guides/assessments/`.
295
+ - Filenames, folder names, and stems must be consistent across `index.json` order arrays and paired files.
296
+ - When revising an existing assignment, preserve structure unless the requested change requires reorganisation.
297
+ - This tooling itself lives in `.guides/secure/` and never appears in student-visible output.
@@ -0,0 +1,56 @@
1
+ ---
2
+ name: reference-mcq-quality
3
+ description: MCQ pedagogy and distractor quality rules. Use when authoring multiple-choice questions or reviewing them.
4
+ ---
5
+
6
+ # MCQ pedagogy and distractor quality
7
+
8
+ This reference is loaded by `assessment-author` (when authoring multiple-choice questions) and `validator`. It defines what makes a Codio MCQ a *good* MCQ — not just well-formatted JSON.
9
+
10
+ ## Question stem
11
+
12
+ - Tests understanding, not trivial wording recall. Avoid questions that can be answered by string-matching the most recent paragraph of the lesson.
13
+ - One clearly stated problem per question. No nested clauses, no multi-part questions joined by "and".
14
+ - Use positive phrasing. If you must use negation, capitalise the negation word ("Which is **NOT** an example of…") so students don't miss it.
15
+ - Match the cognitive level to the learning objective: recall ≠ apply ≠ analyse. If the LO is "apply X", the stem should require applying X, not naming X.
16
+
17
+ ## Correct answer
18
+
19
+ - Exactly one correct answer (unless the user explicitly requested `multipleResponse: true`).
20
+ - Unambiguous — a domain expert reading the question with no other context picks the same answer every time.
21
+ - Not the longest option by a noticeable margin (a common test-taking heuristic exploits this).
22
+
23
+ ## Distractors
24
+
25
+ - All distractors must be **plausibly wrong** — they reflect a real misconception a learner might hold, not a random nonsense option.
26
+ - Aim for one distractor per common misconception or error pattern relevant to the topic. If you can't think of three real misconceptions, the question may not be worth asking.
27
+ - Distractors should be roughly the same length and grammatical form as the correct answer.
28
+ - Avoid:
29
+ - "All of the above" / "None of the above" unless there's a strong pedagogical reason.
30
+ - Joke options or absurd distractors.
31
+ - Distractors that are technically also correct under some interpretation.
32
+ - Distractors that overlap (e.g. "between 1 and 10" and "between 5 and 8" — students who know the answer is 7 can't choose).
33
+
34
+ ## Guidance text
35
+
36
+ - Always populate `guidance` when the topic supports it.
37
+ - Format: one sentence on why the correct answer is right, followed by one short sentence per distractor explaining the specific misconception it represents.
38
+ - Don't restate the question. Don't lecture. Treat it as a hint a tutor would whisper after the student commits an answer.
39
+
40
+ ## Set-level rules (across all MCQs in an assignment)
41
+
42
+ - **Answer-position balance.** Authored correct-answer positions should be roughly uniform across A/B/C/D. With `isRandomized: true` Codio shuffles at runtime, but clustering at authoring time is still a code smell — usually a sign the author wrote correct answers first and distractors second.
43
+ - **Difficulty consistency.** Don't mix trivial recall with deep application questions in one assessment unless the user explicitly asked for a difficulty progression.
44
+ - **No content-leak across questions.** A later question's stem shouldn't give away an earlier question's answer.
45
+ - **No duplicates.** Two MCQs that test the same fact in slightly different words is a sign the assignment over-assesses one LO.
46
+
47
+ ## Common failure modes (validator should flag these)
48
+
49
+ | Symptom | Why it's a problem |
50
+ |---|---|
51
+ | All distractors are obviously wrong | Question becomes a recall task with no thought required |
52
+ | Distractors of very different lengths | Correct answer becomes guessable by length |
53
+ | Two options say essentially the same thing | Confusing; one of them is wrong by definition |
54
+ | Question stem contains the answer (e.g. "What is the *primary* reason X happens, primarily?") | Trivially solvable |
55
+ | Negation buried in the middle of the stem | Students misread; failure becomes about reading, not knowledge |
56
+ | Same correct-answer position 4+ times in a row | Authoring laziness; student pattern-matching |