Mechanism instructor guide
Author new mechanism arrow-pushing exercises: types, atom-index verification, multi-step, bystanderSmiles, cyclicFlow, worked example.
This guide walks an instructor or content author through authoring a new mechanism arrow-pushing exercise. Read Mechanism arrow pushing first for the student-facing description.

The MechanismExercise shape
Every exercise is a MechanismExercise record in
src/lib/education/mechanism-arrows/catalog.ts. The minimum required
fields are:
exerciseId: a stable kebab-case id used in URLs and localStorage.exerciseName: the display title.substrateSmiles: the substrate SMILES for the starting material. Multi-step exercises can leave this empty and put per-step substrates insteps[].substrateSmiles.canonicalArrows: the answer key. For single-step exercises, the list ofCurlyArrowrecords. Multi-step exercises leave this empty and put per-step arrows insteps[].canonicalArrows.flavorText: a one-paragraph teaching-friendly description shown above the puzzle.teaches: an array of concept tags consumed by the concept tree.explanation: the plain-language teaching paragraph shown in the comparison modal.
Optional fields that pay back ten-fold once you reach for them:
provenance: authorship, recency, and audience tags. Use one of the wave-specific provenance helpers at the top of the catalog module rather than reinventing the metadata each time.mechanismClass: drives the "what your spurious arrow means" diagnostic copy. One ofsn2,e2,eas,addition,radical,generic.steps: see "Multi-step exercises" below.enforceArrowOrder: when true, arrows within a step must be drawn in canonical order to earn full credit.cyclicFlow: when true, the renderer routes each arrow toward the centroid of all source / target atoms. Use for pericyclic reactions.
Verify atom indices before writing arrows
RDKit's canonical SMILES round-trip can renumber atoms. Always verify atom indices match the round-tripped form before authoring arrows.
The scripts/enumerate-rubric-bonds.ts helper takes a SMILES on the
command line and prints every bond with its canonical atom indices
plus the round-tripped SMILES.
npx tsx scripts/enumerate-rubric-bonds.ts 'C=CC=C.C=C'
# Canonical SMILES: C=C.C=CC=C
# Atom 0: C Atom 1: C (ethylene C-C)
# Atom 2: C Atom 3: C (diene first pi)
# Atom 4: C Atom 5: C (diene second pi)
# Bond [0,1] order 2
# Bond [2,3] order 2
# Bond [3,4] order 1
# Bond [4,5] order 2
Copy the round-tripped SMILES (not your input) into the
substrateSmiles field, then reference the printed atom indices in
the canonical arrows.
When to use enforceArrowOrder
Default is false. Most concerted exercises (SN2, Diels-Alder, photochemical [2+2]) have no canonical order: all arrows fire simultaneously. The student should not be penalized for clicking the backside attack arrow before the leaving-group arrow.
Set enforceArrowOrder: true when the sequence is pedagogically
meaningful. Catalytic cycles are the textbook case: doing reductive
elimination before transmetalation in a Suzuki cycle gives the wrong
product. Order-only-wrong arrows earn 50% credit rather than 0%, so
the student still sees the structural match in the comparison modal.
Multi-step exercises
A multi-step exercise has a steps[] array. Each MechanismStep is
graded independently and the overall score is the average across
steps.
Each step carries:
stepIndex(0-based, determines display order),stepName(display label, e.g. "Ionization"),substrateSmiles(the substrate AT THE START of this step; intermediates show up here for steps after the first),- optional
reagentSmiles(renders as a second molecule alongside; reagent atom indices shift to start atsubstrate.atoms.length), canonicalArrows(the answer key for this step),- optional
resonanceArrowSets(alternative valid arrow patterns earning full credit), - optional
productSmiles(used in the per-step comparison panel), - optional
explanation(plain-language step-level callout).
When a step has a reagentSmiles, atom indices in the canonical
arrows reference substrate atoms (0..n-1) OR reagent atoms
(n..n+m-1), where n is the substrate atom count.
V3 SIMPLIFICATION convention
The // V3 SIMPLIFICATION: marker in a comment block signals a place
where the catalog intentionally collapses a real chemistry detail to
keep the arrow graph readable. Examples in the V3 catalog:
- Suzuki transmetalation: the base activation of boron is collapsed
into the bond-swap step. The hydroxide is rendered as a
bystanderSmiles(V3 wave 5) so the student sees it without drawing arrows onto it. - Photochemical [2+2]: the excited-state orbital occupation is elided; the four-fishhook representation captures the electron-pairing pattern without trying to render the excited-state wavefunction.
- Diels-Alder cyclic flow: encoded as three discrete arrows; the V3
wave 5
cyclicFlowflag makes them visibly form a ring.
When you reach for the marker, write the simplification AND what's omitted, so a future author can reverse the abbreviation when the type system or renderer catches up.
bystanderSmiles for context molecules
bystanderSmiles (V3 wave 5) is a string SMILES rendered below the
substrate as a smaller, dimmed structure. Bystander atoms are NOT in
the arrow graph: they have no atom indices, no click targets, and
the scorer never sees them.
Use cases:
- The base assisting Suzuki transmetalation
(
bystanderSmiles: '[OH-].[Na+]'). - The silver salt in glycosidation activation.
- A counter-ion or solvent molecule that needs to be visible for pedagogy but shouldn't be a target of student arrows.
Set on the step, not the exercise:
{
stepIndex: 1,
stepName: 'Transmetalation',
substrateSmiles: 'OB(O)c1ccccc1.[Br][Pd][c]1ccccc1',
canonicalArrows: [/* ... */],
bystanderSmiles: '[OH-].[Na+]',
explanation: '...',
}
cyclicFlow for pericyclic exercises
cyclicFlow (V3 wave 5) is a boolean. When true, the renderer
computes the centroid of every canonical arrow's source + target
atom positions and routes each arrow's Bezier control point toward
that centroid. The visual effect: every arrow bulges inward, and the
arrows together form a textbook circular flow around the forming
ring.
Set on the exercise for concerted single-step exercises (Diels-Alder), or on the step for multi-step exercises that contain a pericyclic step (sigmatropic rearrangements in a longer cycle).
The scorer ignores cyclicFlow; it's display-only.
Provenance and audience tagging
Every exercise should carry a provenance record:
provenance: {
authorName: 'SynovAI Editorial',
authoredAt: '2026-05-11',
lastReviewedAt: '2026-05-11',
audience: 'both',
}
audience: 'both' surfaces the exercise in both /learn and
/practice. Use 'learn' for ungated exploratory exercises and
'practice' for CE-module-only content.
Use the wave-specific provenance helpers at the top of the catalog
module (v3Wave2Provenance(), v3Wave3bProvenance(), etc.) rather
than reinventing the metadata each time. They batch the author and
review dates per release.
Worked example: an aldol condensation step
Suppose you want to add a step for the rate-determining step of an aldol condensation: the enol attacks an aldehyde. The substrate is a mixture of an enolate and a benzaldehyde.
- Get the canonical SMILES with
enumerate-rubric-bonds:
npx tsx scripts/enumerate-rubric-bonds.ts 'CC(=O)[CH-].O=Cc1ccccc1'
The script will print the canonical form and atom indices.
- Identify the arrows you want to draw:
- Arrow 1: the enolate carbon lone pair attacks the aldehyde carbonyl
carbon. Source =
lone-pair-on-atomon the carbanion. Target =atomon the carbonyl C. - Arrow 2: the C=O pi bond breaks heterolytically onto oxygen.
Source =
bondon the carbonyl. Target =atomon the O.
- Encode the step:
{
stepIndex: 0,
stepName: 'Aldol addition',
substrateSmiles: '<round-tripped canonical from step 1>',
canonicalArrows: [
{
source: { kind: 'lone-pair-on-atom', atom: /* carbanion idx */ },
target: { kind: 'atom', atom: /* carbonyl C idx */ },
},
{
source: { kind: 'bond', atoms: [/* C */, /* O */] },
target: { kind: 'atom', atom: /* O idx */ },
},
],
productSmiles: '...',
explanation:
'The enolate carbon attacks the aldehyde carbonyl; the C=O pi bond breaks onto oxygen to give the beta-hydroxy ketone alkoxide.',
}
-
Add a content test in
src/lib/education/mechanism-arrows/__tests__/v3-content.test.tsthat pastes the canonical arrows back as a student submission and asserts the grade is 100. This catches atom-index drift. -
Run
npm testto confirm the happy path passes, then preview the exercise at/play/mechanism/<your-exercise-id>to confirm the arrows render where you intended.
What to read next
- Mechanism arrow pushing: the student-facing description.
- Education puzzles: the other five play exercise types.
- Instructor class management: assigning the exercise to a class.