Repository navigation
Introduction to TutorScript
TutorScript makes teaching decisions explicit: production rules determine which instructional moves are eligible, learner models or student choice select the next move, and AI generates the instruction.
Last Updated: 2026-10-13 | For: Educators, content authors, researchers, and developers
TutorScript is the JSON representation of instructional productions and the documents they act on. A production states when an instructional action is applicable and what happens when that production is selected. The tutor matches those conditions against learner evidence and instructional state, selects an eligible production, and executes its actions.
This page explains the current architecture through the newer dynamic-instruction policies. The same language also represents authored exercises and other instructional policies. Language constructs are distinguished from policy choices: the language can express different teaching strategies without making one strategy part of its syntax.
The direction is to express instructional decisions in productions and reuse primitives to carry them out. The current dynamic policies already use that structure. Automatic switching between agent and student authority is a possible extension, not an implemented feature.
- What TutorScript does
- How the system selects a production
- Agent selection and student selection
- A production written in TutorScript
- What the selected production can create or change
- State, evidence, and continuity
- Language reference
- Special conditions and retained mechanisms
Three objects have different jobs:
| Object | Job |
|---|---|
| Production | Defines eligibility in when, optional additional checks in tests, and effects in then. |
| Primitive action | Performs an operation requested by a selected production, such as evaluation, feedback, instruction generation, or navigation. |
| Document node | Represents something the learner can see or interact with, such as text, a figure-bearing tutor message, a question, or a button. |
A production is not itself a primitive. Its then can update facts and invoke a registered action. An instructional action can compose several primitives into one move. A language model realizes the selected instruction; it does not replace the production's eligibility conditions or the selection mechanism.
For ordinary authored exercises, display.productionRules contains the executable rules. For the current SPARC AutoTutor policies, the runtime builds those same TutorScript rule objects from the selected policy. Authors select the controller and supply target content; they must not copy the runtime-owned rules into the lesson's productionRules. The examples below show the actual generated rule objects, not additional package fields.
The current dynamic-instruction cycle is:
flowchart TD
A[Student response] --> B[Evaluation production updates evidence]
B --> C[Feedback production saves and displays feedback]
C --> D{Any instruction remains?}
D -->|No| E[Stop production]
D -->|Yes| F[Target and cycle conditions constrain eligible productions]
F --> G{Configured authority}
G -->|Agent| H[Compare eligible moves for the same target]
G -->|Student| I[Menu production offers choices]
I --> J[Choice event matches a production]
H --> K[Selected production executes its then effects]
J --> K
K --> L[Primitives produce instruction and document changes]
L --> A
Each step has prerequisites expressed through facts. The evaluation production responds to response-submitted and produces an assessment tied to that event. The feedback production requires that assessment. Instructional productions require both the assessment and its saved feedback. A production cannot use an unrelated earlier assessment simply because some scores exist in memory.
Target eligibility comes before competition among instructional moves. The active target is retained while its cycle remains eligible. At a target boundary, candidate facts express which expectation is highest-ranked under the target policy. An eligible misconception can interrupt an expectation cycle under explicit production conditions. The competing move activations must all refer to one target; the selector rejects a competition spanning different targets.
Candidate ranking uses authored knowledge relationships and current evidence. It does not mean that every unfinished knowledge component competes for a new move on every response. Explicit student next-KC navigation is another production path through the same target machinery.
A matching rule plus its bound values is an activation. The engine selects an activation, executes its effects, updates the facts, and matches again. Completed activations are not repeated within that execution. This is how evaluation, feedback, and instruction form a sequence without being one large procedural rule.
The newer agent policy compares the assistance of each eligible instructional move with the learner's expectancy for the selected target. In logit units:
utility(move) = -abs(expectancyLogit + assistance(move) - targetLogit)
P(move) = exp(utility(move) / T) / sum(exp(utility(other eligible move) / T))
An assistance level that puts the expected outcome closest to the instructional target receives the highest utility. Boltzmann selection turns those utilities into probabilities and samples one eligible production. The implementation subtracts the maximum utility before exponentiation for numerical stability; this leaves the probabilities unchanged.
Expectancy, assistance, and the instructional target are on the logit scale in the comparison. A probability-authored target is converted to a logit. The inverse logit of expectancyLogit + assistance gives the corresponding expected success probability; that probability is not itself another logit. T controls selection randomness, not the language model's wording temperature.
Lower positive T concentrates selection on the highest utility; higher T spreads probability across alternatives. Low temperature is nearly deterministic when one move has a unique advantage. Equal best utilities remain tied probabilistically, and T = 0 is rejected. Strict deterministic execution is a separate retained mechanism described at the bottom.
The newer dynamic agent uses a fixed instructional target. The earlier competitive scaffold policy increases OET through production actions. These are distinct policy choices. Exact numerical defaults, assistance values, thresholds, and account restrictions belong in the SPARC Instructional Cycle Specification, not in a second parameter table here.
The student version uses the same evaluated evidence, target facts, and instructional primitives, but it does not sample the offered move using Boltzmann probabilities. A menu production invokes instruction.offer-menu with predefined options in its then. The selected option generates a choice-selected event. A corresponding production matches that option and target and invokes the instruction.
The menu does not ask the learner to answer another subject-matter question. It asks which instructional move to take. Choosing an option reuses the accepted assessment; it is not scored as another answer. The runtime checks that the choice belongs to the outstanding menu.
The current dynamic menu offers question; explanation and question; explanation with example or figure and question; and next-KC navigation. The navigation choice is not a fourth instructional composite. The two policies are separately configured; the runtime does not infer when the learner should take over authority.
The following two examples are complete objects extracted from createSparcDynamicInstructionRules(). They both request instruction.dynamic.explanation-rich-question for an expectation: an explanation, a worked example or teaching figure, and an isomorphic question. The rich-content primitive chooses the appropriate support; a figure is supported but is not required on every invocation. Their conditions differ because one is an agent alternative and the other matches a student's choice. They share the decision/action structure. Start with the plain-English and annotated composite production for a learning-science explanation of this same agent rule. Its numbered comments connect the teaching decision to the runtime objects, and it includes an uncommented JSON copy.
Read when as the eligibility contract: this response has been evaluated, its feedback exists, the lesson is not complete, no decision exists for this assessment, and this is the applicable expectation at a new cycle boundary. The production also binds expectancy, assistance, and instructional target. Its competition module lets the selector compare it with the other eligible moves for that same expectation.
The selector supplies utility, temperature, and selectionProbability to the selected activation. These are explicit selector-provided values, not unbound authoring mistakes or expression functions. The production's then saves the decision and selected action, starts the target cycle, and invokes autotutor.generate-move.
Complete current agent production: explanation, example or figure, and question
{
"id": "instruction.dynamic.agent.expectation.start.instruction.dynamic.explanation-rich-question",
"module": "dialogue.instructional-competition",
"when": [
{
"factType": "interface-event",
"slots": {
"eventType": {"type":"literal","value":"response-submitted"},
"eventId": {"type":"bind","variable":"snapshotId"}
}
},
{
"factType": "instructional.assessmentSnapshot",
"slots": {"snapshotId":{"type":"bound","variable":"snapshotId"}}
},
{
"factType": "dialogue.utterance",
"slots": {
"eventId": {"type":"bound","variable":"snapshotId"},
"action": {"type":"literal","value":"instruction.feedback"},
"speaker": {"type":"literal","value":"tutor"}
}
},
{"factType":"controller.completionState","slots":{"completed":{"type":"literal","value":false}}},
{
"type": "not",
"pattern": {"factType":"instructional.decision","slots":{"snapshotId":{"type":"bound","variable":"snapshotId"}}}
},
{
"factType": "instructional.cycleStatus",
"slots": {"snapshotId":{"type":"bound","variable":"snapshotId"},"continuable":{"type":"literal","value":false}}
},
{
"factType": "instructional.candidate",
"slots": {
"snapshotId": {"type":"bound","variable":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"bind","variable":"targetId"},
"targetKey": {"type":"bind","variable":"targetKey"},
"currentValue": {"type":"bind","variable":"currentValue"},
"eligible": {"type":"literal","value":true},
"isMaximumWithinKind": {"type":"literal","value":true}
}
},
{
"type": "not",
"pattern": {
"factType": "instructional.candidate",
"slots": {
"snapshotId": {"type":"bound","variable":"snapshotId"},
"targetKind": {"type":"literal","value":"misconception"},
"eligible": {"type":"literal","value":true}
}
}
},
{
"factType": "instructional.candidate",
"slots": {
"snapshotId": {"type":"bound","variable":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"bound","variable":"targetId"},
"eligible": {"type":"literal","value":true},
"expectancyLogit": {"type":"bind","variable":"expectancyLogit"},
"oetLogit": {"type":"bind","variable":"oetLogit"}
}
},
{
"factType": "instructional.move",
"slots": {
"action": {"type":"literal","value":"instruction.dynamic.explanation-rich-question"},
"assistance": {"type":"bind","variable":"assistance"}
}
},
{"factType":"session.turnState","slots":{"turnCount":{"type":"bind","variable":"turnNumber"}}}
],
"then": [
{
"type": "assert-fact",
"persist": true,
"identitySlots": ["snapshotId"],
"fact": {
"factType": "instructional.decision",
"slots": {
"snapshotId": {"type":"variable","name":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"action": {"type":"literal","value":"instruction.dynamic.explanation-rich-question"},
"transition": {"type":"literal","value":"start"},
"sourceRuleId": {
"type": "literal",
"value": "instruction.dynamic.agent.expectation.start.instruction.dynamic.explanation-rich-question"
},
"reason": {"type":"literal","value":"oet-distance"},
"expectancyLogit": {"type":"variable","name":"expectancyLogit"},
"assistance": {"type":"variable","name":"assistance"},
"oetLogit": {"type":"variable","name":"oetLogit"},
"utility": {"type":"variable","name":"utility"},
"temperature": {"type":"variable","name":"temperature"},
"selectionProbability": {"type":"variable","name":"selectionProbability"}
}
}
},
{
"type": "assert-fact",
"persist": true,
"identitySlots": [],
"fact": {
"factType": "controller.selectedAction",
"slots": {
"targetType": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"action": {"type":"literal","value":"instruction.dynamic.explanation-rich-question"},
"sourceRuleId": {
"type": "literal",
"value": "instruction.dynamic.agent.expectation.start.instruction.dynamic.explanation-rich-question"
},
"snapshotId": {"type":"variable","name":"snapshotId"}
}
}
},
{
"type": "assert-fact",
"persist": true,
"identitySlots": [],
"fact": {
"factType": "instructional.activeCycle",
"slots": {
"cycleId": {"type":"variable","name":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"stage": {"type":"literal","value":"DYNAMIC"},
"priorValue": {"type":"variable","name":"currentValue"},
"startedAtTurn": {"type":"variable","name":"turnNumber"},
"cycleTurnCount": {"type":"literal","value":0},
"status": {"type":"literal","value":"active"}
}
}
},
{"type":"invoke-action","actionId":"autotutor.generate-move"}
]
}Here when matches choice-selected with optionId: "explanation-rich-question", the offered expectation, and the assessment associated with the choice. There is no assistance comparison in this production. Its then records the student's decision and invokes the same generation action with the selected move recorded in working memory.
Complete current student production: the learner chooses Explanation with example or figure and question
{
"id": "instruction.dynamic.student.choose.expectation.explanation-rich-question",
"when": [
{
"factType": "interface-event",
"slots": {
"eventType": {"type":"literal","value":"choice-selected"},
"eventId": {"type":"bind","variable":"snapshotId"}
}
},
{
"factType": "instructional.assessmentSnapshot",
"slots": {"snapshotId":{"type":"bound","variable":"snapshotId"}}
},
{"factType":"controller.completionState","slots":{"completed":{"type":"literal","value":false}}},
{
"type": "not",
"pattern": {"factType":"instructional.decision","slots":{"snapshotId":{"type":"bound","variable":"snapshotId"}}}
},
{
"factType": "interface-event",
"slots": {
"optionId": {"type":"literal","value":"explanation-rich-question"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"bind","variable":"targetId"}
}
},
{
"factType": "instructional.candidate",
"slots": {
"snapshotId": {"type":"bound","variable":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"bound","variable":"targetId"},
"targetKey": {"type":"bind","variable":"targetKey"},
"currentValue": {"type":"bind","variable":"currentValue"}
}
},
{"factType":"session.turnState","slots":{"turnCount":{"type":"bind","variable":"turnNumber"}}}
],
"then": [
{
"type": "assert-fact",
"persist": true,
"identitySlots": ["snapshotId"],
"fact": {
"factType": "instructional.decision",
"slots": {
"snapshotId": {"type":"variable","name":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"action": {"type":"literal","value":"instruction.dynamic.explanation-rich-question"},
"transition": {"type":"literal","value":"student-choice"},
"sourceRuleId": {"type":"literal","value":"instruction.dynamic.student.choose.expectation.explanation-rich-question"},
"reason": {"type":"literal","value":"offered-option-selected"}
}
}
},
{
"type": "assert-fact",
"persist": true,
"identitySlots": [],
"fact": {
"factType": "controller.selectedAction",
"slots": {
"targetType": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"action": {"type":"literal","value":"instruction.dynamic.explanation-rich-question"},
"sourceRuleId": {"type":"literal","value":"instruction.dynamic.student.choose.expectation.explanation-rich-question"},
"snapshotId": {"type":"variable","name":"snapshotId"}
}
}
},
{
"type": "assert-fact",
"persist": true,
"identitySlots": [],
"fact": {
"factType": "instructional.activeCycle",
"slots": {
"cycleId": {"type":"variable","name":"snapshotId"},
"targetKind": {"type":"literal","value":"expectation"},
"targetId": {"type":"variable","name":"targetId"},
"targetKey": {"type":"variable","name":"targetKey"},
"stage": {"type":"literal","value":"DYNAMIC"},
"priorValue": {"type":"variable","name":"currentValue"},
"startedAtTurn": {"type":"variable","name":"turnNumber"},
"cycleTurnCount": {"type":"literal","value":0},
"status": {"type":"literal","value":"active"}
}
}
},
{"type":"invoke-action","actionId":"autotutor.generate-move"}
]
}The corresponding menu production supplies these options through the literal args.options of its instruction.offer-menu invocation. This array is an excerpt, not a complete standalone rule:
[
{"id": "question", "label": "Question", "action": "instruction.dynamic.question"},
{
"id": "explanation-question",
"label": "Explanation and question",
"action": "instruction.dynamic.explanation-question"
},
{
"id": "explanation-rich-question",
"label": "Explanation with example or figure and question",
"action": "instruction.dynamic.explanation-rich-question"
},
{"id": "next-target", "label": "Move to the next KC", "action": "instruction.next-target"}
]autotutor.generate-move is a registered action entry point. It reads the selected move and dispatches its implementation, including the necessary component primitives. No second generic production is needed merely to execute the move that has just won. The separate student-choice production is needed because a button event has different conditions from agent competition.
The dynamic instruction moves compose question, explanation, and rich support. Rich support can be a worked example or a teaching figure. Visible order is explanation, support, question when all three are present. The generation dependency can differ: the actual support is obtained first so the explanation and question can refer to it accurately.
The student-facing renderer places a worked example in a compact bordered box with a small, centered Worked Example heading. The explanation and follow-up question remain outside that box. This presentation applies to saved as well as newly generated dynamic moves; it does not alter their instructional content.
The parts form one learner-facing instructional move, while their saved node identities remain separate. Only the question component asks the learner a question. Feedback is its own preceding action, not an extra question hidden inside the explanation or figure description. A teaching figure may show a complete relationship; the incomplete-figure constraint belongs specifically to the retained supported-prompt move.
Productions can also classify a response, assert a fact, change addressed node state, write a message, record model practice, or grow the document. Atomic nodes represent individual objects; groups organize them; semantic exercise nodes compile into concrete controls, response mappings, and correctness rules. The language reference lists these constructs.
Figures are represented by the generated instruction and its saved asset reference and accessible description. TutorScript does not require a production to contain drawing code or image bytes. Provider settings, shared figure-library storage, retrieval policy, and access control belong to the system's instructional-cycle specification.
Working-memory facts are the evidence available to productions. Some are authored givens; others describe an interface event, an assessment, the active target cycle, a saved decision, or the result of an action. derivedFacts names intermediate inferences that later productions can reuse. Facts and document nodes have different roles: a retained decision does not have to become a visible widget.
initialState supplies the document's authored baseline. Replay applies saved state transitions and document operations to that baseline. Stable page and node identities keep references meaningful across responses and reopening. assert-fact with persistence records durable facts; identitySlots controls replacement of facts with the same declared identity.
The current instructional cycle saves accepted assessment evidence, decisions, feedback, and completed instruction components through existing history transitions. If a later generation step fails, retry reuses earlier saved work and the accepted decision. It does not intentionally rescore the answer or resample the move. This differs from the retained deterministic dialogue path's whole-turn commit contract.
The expectancy model draws on saved logit observations rather than a separately persisted accumulator. Expectations and triggered misconceptions supply evidence with opposite meanings: expectation coverage represents knowledge, while misconception support represents the incorrect belief. The competitive model uses the complement of misconception support. An untriggered misconception does not become an instructional target simply because it exists in the authored list.
The evaluator preserves prior target evidence when an update omits that target. It assesses the learner's contributions, with tutor messages as context; generated teaching is not automatically credited as learner knowledge. The particular scoring, floors, completion thresholds, and observation calculations remain policy contracts documented in the instructional-cycle specification.
For ordinary model-linked exercises, model-practice resolves a model target and records practice evidence. This is distinct from merely recording credit for a KC. The two mechanisms are not interchangeable. The syntax for model references is below; database ownership, history authorization, and general resume loading belong in the Developer Reference Guide.
The following is a reference to the current JSON language, not a package-upload tutorial. Node, page, and effect examples are fragments unless identified as complete production objects. The machine-readable authoring catalog and contract types are the implementation sources; runtime actions have a broader contract than the visual author's palette.
SPARC TutorScript is a declarative document tree. Authors describe nodes, groups, layout intent, learner actions, and rules. The runtime renders and executes that document.
The enclosing setspec.sparcPages[].pageId is the single authored page identity. Runtime
derives the neutral pageKey used in state addresses from that pageId; authors
must not add a separate display.pageKey.
Top-level SPARC display fields commonly include:
| Field | Meaning |
|---|---|
schema |
Schema name: tutorscript-sparc/2.0; standalone delivery supplies it when omitted and rejects a different authored value |
unitType |
Author-facing kind of SPARC activity |
layout |
Document layout, zones, and frontiers |
nodes |
Authored document nodes |
behavior |
SAI response mapping and step structure |
initialState |
Authored baseline writes to addressed state cells before history replay |
workingMemoryFacts |
Initial facts available to rules |
productionRules |
Executable rule objects for authored-rule SPARC pages; omit for SPARC AutoTutor pages, whose selected controller policy supplies runtime-owned rules |
clusterTargets |
Cluster-index mappings to shared model-practice identities |
derivedFacts |
Fact-producing rules evaluated from working-memory conditions |
instructionalController |
Explicit adapter and policy selection for runtime-owned instructional control |
worksheet |
Fixed-question work and review durations and question-order randomization |
progressReporter |
Optional request for document or sidebar model-progress display |
response |
Scoring/evaluation metadata |
Package-level page selection is documented under Stimulus Files. Nodes have stable ids:
{
"id": "node-intro-paragraph",
"nodeType": "group",
"groupType": "text-panel",
"placement": {
"region": "chapterFlowBox",
"order": 1
},
"children": [
{
"id": "node-intro-text",
"nodeType": "atomic",
"atomType": "text-block",
"value": "In April 1775, tensions between British troops and colonial militia turned into open fighting."
}
]
}Nodes come in three forms: atomic nodes are individual visible or interactive objects; group nodes contain children; semantic nodes express a complete exercise that the compiler expands into concrete controls, response intents, and rules. Stable IDs connect nodes to state, feedback, and history.
Atomic nodes are single visible or interactive objects:
| Atom type | Purpose |
|---|---|
html-block |
Sanitized rich HTML, including headings, emphasis, code, tables, images, iframes, OLI callouts, and diagnostics |
text-block |
Plain text output |
dialogue-utterance |
Learner or tutor message, with speaker, text, and optional turn/action/target metadata |
message-box |
Rule-addressed feedback, hint, success, or buggy-message target |
button |
Learner action button |
text-input |
Single-line learner text or numeric input |
dropdown |
Renderer-backed select-one input |
select |
Additional select-one form recognized by the SPARC trial-display bridge |
checkbox |
Boolean/select-many input |
panel-selector |
Compact tab row that shows one panel at a time |
skill-bar |
Static authored progress-style visual; it does not read adaptive model probabilities |
learning-progress |
Model-backed adaptive progress reporter, rendered inline when placed as a SPARC node |
operator |
Math/operator cell |
header-cell |
Table-like header cell |
text |
Inline plain text atom |
Use html-block for rich content and dropdown for new select-one content. The renderer also recognizes the select form.
The renderer also recognizes fraction-box and fraction-input atoms. Adjacent numerator/denominator fraction atoms are normalized into a groupType: "fraction" pair before rendering. The source catalog exposes the fraction authoring pattern as a group entry; editor UI can still show numerator and denominator cells inside that group.
Groups are containers with child nodes. The current catalog includes renderer-supported groups and OLI-generated group patterns:
| Group type | Purpose |
|---|---|
section |
Page or document section |
dialogue-thread |
Ordered learner/tutor utterances |
multiple-choice |
Prompt, answer-list, choices, and feedback |
answer-list |
Vertical answer controls |
targeted-cata |
Select-many exercise |
checkbox-choice |
Checkbox plus label row |
dropdown-exercise |
Dropdown-list exercise |
dropdown-row |
Rich label plus dropdown |
text-input-exercise |
Text-input-list exercise |
text-input-row |
Single text-input answer row |
short-answer |
Stem, text input, submit button, and feedback |
choice-tabs |
Tabbed group showing one child panel |
fraction |
Numerator-over-denominator fraction group |
alternative-panel |
Panel content for tabbed or selector layouts |
oli-group |
Generic converted OLI group |
OLI-specific names such as oli-example, oli-learnbydoing, and oli-didigetthis identify purpose-labeled group variants. Converted OLI content uses the same SPARC document runtime.
Semantic nodes expand before rendering. Current forms are:
| Semantic type | Expansion |
|---|---|
multiple-choice |
Prompt, answer list, button choices, and feedback |
select-many |
Select-many group with checkbox choices and submission feedback |
dropdown |
Dropdown exercise and answer rows |
text-input |
Text-input exercise and answer rows |
numeric-input |
Text-input exercise with numeric input mode |
short-answer |
Stem, text input, submit button, and feedback |
When an exercise supplies scoring/model metadata, its clusterIndex must resolve to the page's clusterTargets. The semantic compiler generates the matching response intents and executable correctness/model-practice rules. Do not maintain a second answer table.
Semantic multiple choice is the clean authoring form for multiple-choice questions:
{
"id": "node-mc-problem",
"nodeType": "semantic",
"semanticType": "multiple-choice",
"clusterIndex": 0,
"label": "Multiple choice",
"feedbackNodeId": "node-mc-feedback-message",
"prompt": {
"id": "node-mc-question",
"value": "Which event marked the beginning of open fighting in the American Revolution?"
},
"answerGroupId": "node-mc-answers",
"choices": [
{
"id": "node-choice-lexington-concord",
"label": "Lexington and Concord",
"value": "Lexington and Concord",
"correct": true
},
{
"id": "node-choice-bunker-hill",
"label": "Bunker Hill",
"value": "Bunker Hill"
}
]
}At runtime, that semantic node is expanded into a group with a prompt, an answer-list group, button children, and an optional header feedback message box. The semantic object is the authored content; the expanded group is the execution/rendering detail.
Converter-generated groups use the same node language; conversion and import procedures belong in Quick Start: Content Creation.
The catalog exposes layout fields that document validation and the renderer already understand:
| Field | Allowed values |
|---|---|
scrollAxis |
none, vertical
|
layoutMode |
document, stack, columns, sidebar, tabs
|
visualPreset |
assignment, chapter, section, practice-panel, feedback-panel, callout, control-panel
|
density |
compact, comfortable, spacious
|
wideContent |
constrain, reflow, shrink, stack
|
overflowX |
clip, hidden, visible
|
Renderer glue is stored under layout.glue.mode. Current modes include:
multiple-choice, answer-list, checkbox-list, dropdown-list, text-input-list, short-answer, inline-control, choice-tabs, intro-feedback, fill-in, term-column, and footer-actions.
Layout glue selects concrete rendering behavior; use one of the supported modes rather than an arbitrary label.
Allowed values do not imply every combination is valid. Width constraints require a wideContent policy; columns and sidebar require reflow or stack. Document validation checks layout and node references before execution.
SPARC behavior currently uses SAI-style response mapping:
{
"behavior": {
"schema": "sparc-behavior/1.0",
"orderPolicy": "progressive",
"responseFormalism": "SAI",
"steps": [
{
"id": "answer-opening-multiple-choice",
"responses": [
{
"selection": "choiceLexingtonConcord",
"action": "ButtonPressed",
"input": "Lexington and Concord",
"nodeRef": "node-choice-lexington-concord"
}
]
}
]
}
}behavior.steps.responses maps learner actions on nodes into rule facts. Native SPARC controls can also emit direct node-id actions such as ButtonPressed, UpdateTextField, UpdateComboBox, and UpdateCheckbox; use explicit behavior mappings when authored rules need custom SAI names or path-specific selections.
For authored-rule SPARC pages, executable rules live in top-level productionRules, not in descriptive behavior.authoredProductionRules. Current runtime validation fails clearly when behavior.authoredProductionRules is present because that field is not executable. SPARC AutoTutor is the explicit exception: its generic controller productions are runtime-owned, and an AutoTutor display that supplies top-level productionRules is rejected instead of overriding the selected controller policy.
A production rule matches facts about learner actions, document state, or instructional state, binds variables, checks optional tests, and applies effects. Rules can classify an answer, give feedback, update model practice, grow the document, or select a tutoring move. reactiveRules and node-level visibleWhen/enabledWhen authoring have been removed; they are not current authoring mechanisms.
derivedFacts declares supporting facts computed from working-memory conditions. Each entry needs a stable id, when conditions, optional tests, and a fact template with a factType. The runtime validates these through the same production-rule machinery. They let authors name an intermediate inference once and use it in subsequent rules, instead of repeating the same conditions in every action rule.
A production rule has:
| Field | Meaning |
|---|---|
id |
Stable rule id |
module |
Optional rule grouping; the competitive runtime identifies instructional alternatives by its designated module |
when |
Working-memory fact patterns |
tests |
Optional comparisons after variables bind |
then |
Effects to apply when the rule fires |
Production rules match working-memory facts. A rule has when, optional tests, and then.
Allowed production-rule condition forms:
| Condition | Purpose |
|---|---|
| Fact pattern | Match a fact by factType and optional slot patterns |
Negated fact pattern (not) |
Match only when a fact pattern is absent |
any |
Require at least one child condition to match |
all |
Require every child condition to match |
Allowed slot pattern forms:
| Slot pattern | Purpose |
|---|---|
literal |
Require an exact value |
bind |
Bind a slot value to a variable |
bound |
Require a slot to match an already-bound variable |
range |
Require a numeric value within optional inclusive/exclusive bounds; bounds may be expressions |
Common generated OLI learner events use factType: "interface-event" with slots such as pageKey, selection, action, and input.
Tests run after fact patterns bind variables.
Allowed operators:
eq, neq, in, not-in, gt, gte, lt, lte, truthy, falsy, regex, and not-regex. truthy and falsy need only the left expression; comparisons and regular-expression tests use the right expression. in and not-in require an array on the right and compare values without type coercion. not-regex is the complement of regex.
For semantic-node scoring, an authored regex is the response matcher rather than an additional exact-answer requirement. Without a regex, acceptedValues supplies alternatives, followed by value, expected, or the input's expected answer. Generated default feedback excludes all authored response matchers, including regular expressions and accepted-value lists.
Allowed expression forms:
| Expression | Purpose |
|---|---|
literal |
Fixed value |
variable |
Bound variable reference |
function |
Nested arithmetic function |
Allowed functions:
add, subtract, multiply, divide, mod, gcd, and lcm.
Supported production-rule effect types include:
| Effect | Purpose |
|---|---|
invoke-action |
Call an explicitly registered runtime capability with an actionId and optional expression-valued args; exposed by runtime-owned rules, not the current visual palette |
classify |
Mark the learner action as correct, incorrect, partial, study, skipped, unknown, or buggy
|
message |
Send hint, buggy, success, or feedback text to a target message node |
write-state |
Write addressed node state |
assert-fact |
Add a working-memory fact; persist and identitySlots control durable state and replacement identity |
terminate-production-phase |
Stop the current production execution after this rule fires |
credit |
Credit a KC |
model-practice |
Write adaptive model-practice history for a resolved model target |
append-node |
Add or replace a node at a frontier/box |
append-node-if-missing |
Add a node only if that id is not already present |
append-text |
Append text to an existing text node |
insert-node |
Insert a node before or after another top-level node |
For ordinary correctness feedback, pair these effects:
-
classifyrecords the outcome. -
write-statewith keycorrectnesstargets the learner-facing node and drives correct/incorrect highlighting. -
messagetargets amessage-boxnode and supplies the learner-facing feedback text. -
creditrecords KC credit.
For progressive feedback, use append-node-if-missing once and then append-text:
[
{
"type": "append-node-if-missing",
"frontier": "main",
"boxId": "chapterFlowBox",
"node": {
"id": "node-remediation-feedback",
"nodeType": "atomic",
"atomType": "text-block",
"placement": {
"region": "chapterFlowBox"
},
"value": ""
}
},
{
"type": "append-text",
"nodeId": "node-remediation-feedback",
"separator": " ",
"text": "Bunker Hill was an early major battle, but the war had already begun at Lexington and Concord."
}
]This creates one growing remediation paragraph instead of a new paragraph for every wrong answer.
Action arguments use the expression forms above. Actions run only after selection, and execution awaits their results before continuing. An unknown action is not an instruction to invent an implementation. The synchronous evaluator rejects action invocations; runtime capabilities require the action-aware execution path. Message effects can become replayed node-state writes; they are not inherently transient.
credit records knowledge-component (KC) credit. model-practice connects learner response evidence to the shared adaptive model and writes ordinary practice history. A KC is the concept or skill identified by the author for model tracking.
A model-practice effect resolves an explicit clusterIndex, or a node attached to exactly one cluster through clusterIndex/clusterIndices, against display-level clusterTargets. Missing mappings, duplicate indices, and ambiguous node attachments fail clearly. A node-only modelTarget does not replace the cluster attachment for a production-rule effect.
{
"type": "model-practice",
"outcome": "correct",
"clusterIndex": 0,
"nodeId": { "type": "variable", "name": "node" },
"responseValue": { "type": "variable", "name": "input" },
"input": { "type": "variable", "name": "input" }
}The resolved display supplies the corresponding shared model identity. In standalone SPARC delivery, the unit engine constructs these mappings from the actual referenced lesson clusters. This fragment illustrates the resulting mapping:
{
"clusterTargets": [
{
"clusterIndex": 0,
"label": "Determine least common denominator",
"stimuliSetId": "sparc-fractions-addition",
"stimulusKC": "fractions.lcd",
"clusterKC": "fractions.addition",
"KCId": "fractions.lcd",
"KCDefault": "fractions.lcd",
"KCCluster": "fractions.addition"
}
]
}Keep these identities consistent with the lesson's actual clusters and model targets. They allow SPARC practice and ordinary card practice to use the shared model without treating screen nodes as separate model stimuli.
Model-backed progress has two authoring surfaces:
| Surface | Behavior |
|---|---|
atomType: "learning-progress" |
Inline document progress chart; document placement is requested by the node itself |
display.progressReporter |
Optional object with placement: "document" or "sidebar", plus optional nodeId, label, showReferenceLines, and compact
|
An inline learning-progress node requests document placement even without progressReporter. A sidebar request keeps the shared progress panel enabled. skill-bar is a static visual and does not read model probabilities.
The current dynamic policies select the same registered adapter with different rule families:
{
"instructionalController": {
"adapterId": "sparc-autotutor-v1",
"policyId": "dynamic-instruction-agent-v1",
"policyVersion": 1
}
}Use dynamic-instruction-student-v1 for the corresponding student-choice family. Current dynamic delivery is restricted to admin accounts; that is an access policy, not a TutorScript language requirement. Other retained policies are described at the bottom.
For a unitType: "sparc-autotutor-dialogue" document, the dialogue thread contains a nonblank opening-tutor-message. That authored problem remains context for evaluation and instruction generation. Expectation targets use clusterKC; their clean target text comes from autoTutorTargets.expectations when supplied or from the selected cluster cases. Misconceptions use id and text from misconceptionTable.misconceptions or the normalized autoTutorTargets.misconceptions view. clusterTargets connects numeric cluster references to shared model identities.
Do not duplicate those targets as learningTarget.source, diagnostic.misconceptionSource, or dialogue.moveContent facts. The runtime rejects the obsolete target-source structures. Top-level authored productionRules are also rejected on these AutoTutor pages: the selected controller owns the productions. New policy development changes the runtime rule family, not a private copied array in every package.
| Information | Owning wiki section |
|---|---|
| Creating, packaging, uploading, and testing a lesson | Quick Start: Content Creation |
| TDF fields and unit setup | Content Creation Reference Tables |
| Stimulus files, page placement, and media references | Stimulus Files |
| Video checkpoint setup | Video Units and Adaptive Logic |
| General runtime, persistence, and server ownership | Developer Reference Guide |
| Current instructional policy parameters and figure services | SPARC Instructional Cycle Specification |
Dedicated autotutorsession unit |
AutoTutor Sessions |
The language reference should explain the meaning of TutorScript constructs. Editor button locations, package instructions, and database/service procedures should have one home in the relevant workflow or system documentation.
These mechanisms apply to explicitly selected alternatives or specialized documents. They do not define the newer dynamic-instruction selection cycle above.
Salience is being written out of the intended instructional move-selection approach. It is still implemented in the general evaluator and the deterministic scaffold rule family. It has not been removed from TutorScript's accepted rule type.
Without a custom selector, the evaluator orders rules by descending salience (default zero), then by rule ID using localeCompare, and selects the first eligible activation. Competitive instructional policies instead use the explicit selector described above. They do not use salience to weight move probabilities. Retaining this distinction lets the deterministic reference remain available while the newer selection mechanism develops.
The optional field therefore remains valid for that retained path:
| Field | Retained meaning |
|---|---|
salience |
Numeric priority used by default rule ordering; higher values precede lower values. |
progressive-scaffolding-v1 retains Pump, Prompt, Hint, and Assertion progression through explicit target and progress conditions. Its Pump-to-Prompt transition belongs to that deterministic family, not the current dynamic-instruction family illustrated above.
The competitive scaffold policy oet-boltzmann-v2 and student scaffold policy student-selected-v2 also remain intentional alternatives. They use the Pump / Prompt unsupported / Prompt supported / Hint / Assertion move set, with explicit next-target choice in the student version. The runtime action identifier prompt denotes the unsupported prompt; prompt-supported denotes the figure-supported prompt. Their detailed parameters and eligibility rules belong in the instructional-cycle specification.
Current account routing preserves explicitly selected dynamic policies for admins and rejects non-admin dynamic access. For other recognized policy selections, non-admin delivery uses the deterministic reference. Admin delivery preserves the student scaffold policy and otherwise selects the competitive scaffold policy. This is current routing behavior, not an author-controlled switch between selection authorities during a dialogue.
The deterministic dialogue path commits a completed turn after successful evaluation and generation. It has a different retry boundary from the checkpointed instructional cycle described above. Its general graduation calculation can apply a maximum-turn limit; that is not an operative cutoff for the newer dynamic policies. Do not generalize the deterministic path's completion or whole-turn persistence rules to every TutorScript policy.
A specialized page can declare worksheet with positive workDurationSeconds, positive reviewDurationSeconds, and Boolean randomizeQuestions; all three are required. It contains 1–500 top-level semantic multiple-choice questions with valid model references. The semantic compiler supplies controls and scoring definitions.
{
"worksheet": {
"workDurationSeconds": 600,
"reviewDurationSeconds": 300,
"randomizeQuestions": true
}
}The work phase accepts answers without displaying correctness. Submission or the work deadline waits for pending answer writes, then opens read-only review. Review uses the latest saved answer for each question and overlays correct answers. An unanswered question remains blank, receives no fabricated answer record, and is indicated as incorrect in review. Continue or the review deadline finishes the worksheet. Both durations are maximums; early continuation is available.
Question randomization happens once and preserves answer-choice order. Saved attempt state retains that realized order, phase, and deadlines. Answer changes write ordinary model-eligible practice history without applying a live adaptive-model update; reopening does not create a new completed attempt. Feedback exposure is recorded on actual appearances, including reopening, rather than on ordinary redraws. These are worksheet semantics, not the ordinary production-driven move-selection cycle.
For video delivery, follow Worksheet checkpoints; video unit configuration is maintained there. Worksheet controls follow interface language while authored content retains its authored language.
Production-rule SPARC documents own their interaction sequence and suppress the ordinary card response timeout. A static display does not acquire that behavior merely by containing nodes. Timed worksheets use their explicit work/review durations. General card timing belongs in Content Creation Reference Tables.
Getting Started
- Teacher: First Hour
- Quick Start: Content Creation
- Class Setup and Assignment Workflow
- Anki Import Guide
Authoring Reference
- Introduction to TutorScript
- Content Creation Reference Tables
- Stimulus Files (Content)
- Trial Types Reference
- Audio and Speech Settings
Advanced Authoring
Reports and Help
- Researcher Guide
- TDF field tables
- Gaze tracking prototype
- Data Output and Reports
- Data Privacy, IRB, and FERPA Notes
- Experiment Routes
- SPARC Instructional Cycles
- Public Demonstrations
- Learning Algorithms Reference
- Video Units and Adaptive Logic
- AutoTutor Sessions
- Deployment Guide
- Security Audit Operations
- Remote Install
- Runtime Settings Reference
- Admin Features Reference
- Custom Help Page Setup
- Troubleshooting
- Local Install
- Developer Reference Guide
- Learning Algorithms Reference
- Introduction to TutorScript
- AutoTutor Sessions
- Runtime Settings Reference
- Deployment Guide
- Glossary
- FAQ
- Screenshots
- License Guidance
- Implementation Cost Analysis
- GitHub Repository
- GitHub Issues