FunCS Flow Lesson Authoring
The data-authored flow is the canonical beta lesson format. A lesson is one JSON-compatible JavaScript object. Shared components render the standard five stages, chapter navigation, code, memory, console, questions, and footer controls. An optional sequence can insert shared Intro pages while retaining one copy of every canonical stage.
Start from flow-lesson-template.jsx. Use
flow-ch0-1-program-tour.jsx as the smallest complete beginner lesson and
tb-ch1-branching.jsx as the richer branching example.
Canonical entry points
flow-lesson-kit.jsx: shared question primitives and presentation helpers.flow-lesson-stages.jsx: the five canonical stage renderers, shared Intro renderer, sequence resolver, validator, andFlowLessonSequence.flow-lesson.css: canonical presentation selected through the design-skill comparison.flow-lesson-template.jsx: empty JSON-compatible fixture skeleton.- Backlog
doc-21: exported component reference. - Backlog
doc-22: authoring contract and workflow. - Backlog
doc-23: local preview and browser-verification workflow.
Write a lesson in this order
- State the target procedure and learner prerequisites.
- Name two or three functional goals and reusable subgoals. Avoid labels that merely repeat syntax or interface actions.
- Write
fullExample.code.lines; give every executable row anid,num,text,subgoal, and plain-languagetranslation. - Add
fullExample.code.goals,.subgoals, and aframethat groups rows by instructional function. - Write
fullExample.states[]and, when execution order matters,fullExample.executionTrace[]. State and console output must show the evidence produced by each executable step. When a state includes expression evaluation, authorevalDetailwith the mandatory vertical stepping rule below. - Write the three
mainLesson.acts[], one idea per act, then one local check per act inflow.mainLesson.checks[]. When an act introduces vocabulary or a reusable code reading, adddefinitions[], displayedtranslations[], and one or two post-checkrecall.cards[]. - Write a transfer program with the same procedure and different surface details.
- Write pre-quiz details and rigorous-quiz cards using only the shared
choice,chips,order, androwquestion shapes. - Write five open-ended exercises: warm-up, core work, repair, and transfer. Add a concise model answer and feedback when the lesson requires them.
- Keep the standard five-page sequence unless the approved Chapter Map calls
for Intro pages. When it does, author
flow.sequence[]andflow.intros{}with data only. - Run
flowValidateLesson(lesson)and fix every missing path before browser review.
Evaluation stepping is a mandatory rule
Every evalDetail uses one spatial grammar: evaluate upward, then simplify
across.
- Opening the detail shows only the complete base expression at the bottom.
- Draw an evaluation bar over the selected term. Each Next action reveals exactly one immediate result above the previous layer.
- If an upward result is still an expression, keep evaluating upward inside the same block.
- On the Next action after the upward result is a value or cannot be evaluated further, strike + arrow carries that result across and reveals the next containing expression.
- A future upward layer, arrow, or right-hand block never appears early.
- Authors order
blocks[]andlevels[]; the shared engine derives reveal timing. Do not coordinate click behavior with authoredshowAtnumbers. - Use
layout: 'verticalStack'andblocks[]. The horizontalframespayload is invalid and is rejected by both the shared validator andflowValidateLesson. - Supply at least two blocks, at least one block with two or more levels, and
arrowAfteron every non-final block. Supply onesteps[]caption for the opening base, every higher level, and every rightward substitution. - When a recursive substitution must repeat several waiting context levels in
the next block, set
baseLevelCounton that block. It defaults to1; the opening block always has one base expression.
evalDetail: {
title: 'Create the branching value',
sourceLine: 'bool isReady = answer == "yes";',
steps: [
{ label: 'Start', note: 'Start with the complete declaration.' },
{ label: 'Read', note: 'Read the string value from answer.' },
{ label: 'Substitute', note: 'Substitute the string into the declaration.' },
{ label: 'Compare', note: 'Compare the two string values.' },
{ label: 'Substitute result', note: 'Substitute the Boolean result into the declaration.' },
],
layout: 'verticalStack',
blocks: [
{
levels: [
{
expression: 'bool isReady = answer == "yes"',
evalSpan: [15, 21],
label: 'var',
strike: { span: [15, 21] },
},
{ expression: '"yes"' },
],
arrowAfter: {},
},
{
levels: [
{
expression: 'bool isReady = "yes" == "yes"',
evalSpan: [15, 29],
label: '==',
strike: { span: [15, 29] },
},
{ expression: 'true' },
],
arrowAfter: {},
},
{
levels: [{ expression: 'bool isReady = true' }],
},
],
}The block array records evaluation structure, not source-row timing. The shared engine converts the ordered structure above into five reveal states: base, lookup result, substitution, comparison result, and final substitution.
Standard and extended sequences
When flow.sequence is absent, FlowLessonSequence renders the standard order:
fullExamplepreQuizmainLessonrigorousQuizexercises
An extended sequence may add any number of intro pages. It must keep the
canonical stage order and include each canonical blockType exactly once.
Every page needs a unique id. Each Intro descriptor names a key from
flow.intros:
flow: {
sequence: [
{ id: 'intro-start', blockType: 'intro', intro: 'start', title: 'How This Lesson Works' },
{ id: 'goal', blockType: 'fullExample' },
{ id: 'intro-pre-quiz', blockType: 'intro', intro: 'preQuiz', title: 'Prepare for the Pre-Quiz' },
{ id: 'pre-quiz', blockType: 'preQuiz' },
{ id: 'main-lesson', blockType: 'mainLesson' },
{ id: 'intro-rigorous-quiz', blockType: 'intro', intro: 'rigorousQuiz', title: 'Try a New Program' },
{ id: 'rigorous-quiz', blockType: 'rigorousQuiz' },
{ id: 'intro-exercises', blockType: 'intro', intro: 'exercises', title: 'Practice Independently' },
{ id: 'exercises', blockType: 'exercises' },
],
intros: {
start: {
title: 'How you interact with a program',
lede: 'Short opening copy.',
sections: [
{ id: 'interface', title: 'Read the interface', body: ['One paragraph.'] },
],
callout: { label: 'Remember', body: 'One focused note.' },
},
},
}Intro sections support authored body, cards, steps, and code arrays.
They do not accept React elements, callbacks, or lesson-specific components.
Required fixture surface
const LESSON = {
id: '',
chapterId: '',
title: '',
kicker: '',
learningTarget: '',
chapterNav: { chapters: [] },
chapterExamples: [],
availableSyntax: [],
fullExample: {
header: {},
code: { layout: 'frame', goals: {}, subgoals: {}, frame: [], lines: [], tokens: {} },
states: [],
executionTrace: [],
},
preQuiz: { title: '', prompt: '' },
mainLesson: {
title: '',
label: 'Main Lesson',
intro: '',
acts: [{
n: 1,
title: '',
body: [],
definitions: [{ term: '', definition: '' }],
code: [],
translations: [{ code: '', text: '' }],
recall: { cards: [{ id: '', prompt: '', answer: '' }] },
}],
},
rigorousQuiz: { title: '', prompt: '', transferCode: { lines: [] } },
exercises: { title: '', label: 'Exercises', intro: '' },
flow: {
// Optional: sequence: [], intros: {},
goal: {},
preQuiz: { categories: [], shuffled: [], details: {} },
mainLesson: { checks: [] },
rigorousQuiz: { cards: [] },
exercises: { problems: [] },
},
};The fixture must not contain React elements, render callbacks, or lesson-specific components. Arrays, object fields, call frames, Intro content, and console values remain data rendered by the shared grammar.
Main Lesson language support
The shared Main Lesson renderer accepts three optional fields on an act:
definitions[]renders a compact definition callout. Put the exact term interm; the renderer bolds it. Keepdefinitionto one direct sentence.translations[]renders a displayed translation block. Put the source form incodeand its English reading intext; the renderer places them on separate lines.recall.cards[]renders one or two unscored flashcards after the act’s attention check. Each card needsid,prompt, andanswer.
The learner answers the attention check before the recall cards appear. The next act remains locked until every recall answer has been revealed. Recall is not scored, and correctness never blocks progress. Lessons that do not use these fields continue to follow the existing attention-check gate.
Definitions and translations support the code-function subgoals; they do not become subgoals themselves. Keep labels attached to observable code jobs, and put definition or translation requests in prose, checks, recall cards, and Exercises.
Quiz implementation rules
- Make every quiz question self-contained. Include the source needed to answer
it on the same page through
part1Code,contextCode, or the transfer-code block; do not require recall of an earlier example’s exact code. - Use bounded, immediately checkable
choice,chips,order, orrowanswers. Put broader explanation and reflection in Main Lesson prose or Exercises instead of open quiz responses. - Do not expose labels, ordinals, ordering, or styling that reveals the answer.
Use
hideOrderOrdinals: truewhen subgoal numbers would cue an order task, and distribute correct choice indices so the first option is not a default. - Keep question evidence visually available. Reference and transfer code used
to answer a quiz stays at full opacity throughout the quiz; never pass an
empty
runKeysset merely to make a code block noninteractive. Dimming or blurring is reserved for content that is intentionally gated and not needed to answer the current question. - Goals and subgoals describe observable jobs performed by the code. Learner directions such as trace, predict, remember, or explain belong in prose, prompts, and Exercises rather than code-frame labels.
Validation and preview
For Chapter 1 and later, the entry HTML loads chapter-overview.css,
chapter-overview-fixtures.jsx, and chapter-overview-kit.jsx before the
concept kit, stage renderer, and lesson fixture. Wrap the lesson in
.funcs-edition-page, identify the Beta edition, and link to that lesson’s
Primary counterpart. Every lesson in one chapter must use the same shared
chapter-selector fixture so its available lessons, coming-soon lessons, current
marker, and overview link do not change from page to page.
In the chapter selector, an available lesson is one semantic row-level link.
Its number, title, arc label, whitespace, and visible Open beta treatment all
open the same exact lesson route, and the row receives the keyboard focus
outline. Do not nest a second link inside that row. A route that is still being
checked or is unavailable remains a non-link row with a disabled Open beta
button.
Cross-chapter route tables use absolute, normalized Beta entry routes. Keep these routes in the shared chapter data, cache-bust every active HTML consumer when they change, and verify the complete round trip against the generated site. The source HTTP preview loads components directly from their authored filenames; Quartz normalizes generated filenames and owns the publishable route surface.
The page wrapper then renders:
<div className="candidate-shell flow-authoring-canonical">
<div className="candidate-frame">
<window.FlowLessonSequence lesson={window.LESSON} />
</div>
</div>Canonical lesson pages are standalone teaching surfaces, so this wrapper fills
the viewport edge to edge. Do not give .candidate-shell page padding or make
.candidate-frame look like a floating browser window with a maximum width,
rounded corners, border, or drop shadow. The frozen comparison pages keep their
own experiment-specific wrappers.
Give new standalone lesson HTML files their normalized, hyphenated public filename, and use that exact filename in fixture and roadmap links. Cache-bust a roadmap or shared navigation fixture when its destination data changes.
FlowLessonSequence calls flowValidateLesson before rendering. For an
explicit browser check, evaluate:
flowValidateLesson(CH0_PROGRAM_TOUR_LESSON)
// { valid: true, missing: [] }Serve the content/ directory over HTTP:
cd /Users/markholcomb/classes/FunCS/content
python3 -m http.server 8123Then open a page under http://127.0.0.1:8123/beta/Funcs/. Do not use
file://, and do not add a backend.
Author review checklist
- The standard flow renders five tabs. An approved extended flow renders its authored tab count and still contains all five canonical stages.
- Extended sequences use only known
blockTypevalues, unique IDs, and shared data-authored Intro pages. - Goal focus precedes executable source steps.
- Code highlighting, memory state, and console output describe the same moment.
- Every
evalDetailopens base-only, reveals one upward result per action, and uses a later action for each rightward substitution; no active lesson fixture contains a horizontalframespayload. - Pre-quiz ordering gates details; all details gate Main Lesson.
- Quiz questions include their own required source, avoid ordinal and answer-position cues, and keep reference code at full opacity.
- Each lesson act has one check. When recall cards are authored, they appear after the check and later acts stay locked until every answer is revealed.
- Rigorous Quiz uses the transfer program and advances one card at a time.
- Exercises are open-ended and do not introduce a new renderer.
- Model answers and feedback diagnose the failed code subgoal when they are part of the approved content.
- Keyboard focus is visible and controls retain usable touch targets.
- Desktop and 390px views have no page-level horizontal overflow.
- The fixture and touched JSX files parse; the static route and Quartz build pass.
When the fixture contract, shared renderer, page sequence, or authoring workflow
changes, update this guide, doc-21, doc-22, doc-23, and
FUNCS_FLOW_LESSON_KIT_DOC_BREADCRUMBS together.
Co-authoring governance
Use Backlog doc-27, Beta Lesson Co-authoring Playbook Specification, for
the human-agent approval gates, Lesson Blueprint contract, curriculum map, and
production sequence. The authoritative source dispositions live in the chapter
Coverage Ledgers:
doc-28: Chapter 0 Beta Coverage Ledger.doc-29: Chapter 1 Beta Coverage Ledger.doc-30: Chapter 2 Beta Coverage Ledger.doc-31: Chapter 3 Beta Coverage Ledger.doc-32: Chapter 4 Beta Coverage Ledger.
Do not begin student-facing prose before the Lesson Blueprint Gate or fixture implementation before the Authored Content Gate. Proof prototypes demonstrate renderer and visualizer capability; they do not determine Concept Lesson boundaries. This governance layer does not change the fixture or renderer contract described above.