# Paired FC02/K02 Plan — First FreeRTOS Task through A1–A8 Status: agreed paired design; FC02 C and K02 C++ now have local implementations that follow the shared E01–E12 contract. This plan defines one controlled teaching experiment rendered twice: - **FC02 / FreeRTOS C:** raw `TaskFunction_t`, `void *pvParameters`, `TaskHandle_t` and `xTaskCreate`; - **K02 / FreeRTOS C++:** the existing header-only `Task` CRTP wrapper, explicit `start()` and static trampoline. The design remains the comparison contract. FC02 now has a local executable, HTML and TeX implementation; PDF generation and publication remain separate operations. ## Learning question The student must be able to answer one question with source, binary and runtime evidence: > What remains identical when a FreeRTOS task is expressed through raw C or a > small C++ wrapper, and what new compile-time and runtime boundaries does the > wrapper introduce? The C and C++ cards therefore use the same calculation and kernel behaviour. Only the language-facing task abstraction changes. ## Curriculum position ```text C pointers → C structures and memory regions → heap_4 mechanics in C → FC02 raw FreeRTOS task in C → C++ language/OOP prerequisites → K02 FreeRTOS task wrapper in C++ → later lifetime, RAII, communication and synchronization cards ``` FC02 establishes the raw kernel boundary first. K02 reuses that known behaviour and makes the abstraction delta observable instead of teaching the kernel and C++ wrapper as two simultaneous unknowns. ## Controlled experiment contract Both cards must share these invariants: | Property | Required value | |---|---| | Kernel | FreeRTOS-Kernel V11.3.0, same pinned source revision | | Target | RV32I/Hazard3 simulator, cycle-exact teaching profile | | Workers | two equal-priority CPU-bound workers, created before the scheduler | | Work | sum integers `1..20000` independently | | Expected result | `200010000` for each worker | | Supervisor | one lower-priority task validates both workers | | Scheduling proof | elapsed work crosses at least two ticks and records `A -> B -> A` | | Allocation | dynamic `xTaskCreate` backed by the same `heap_4` configuration | | Completion | diagnostic state/result is published before self-delete: `NULL` in C, `nullptr` in C++ | | Replay | clean RAM and a new generation for every requested RUN checkpoint | The comparison is valid only when the two builds also use byte-identical `FreeRTOSConfig.h`, the same tick rate, time-slicing setting, priorities, stack depths, worker creation order and checkpoint-sink signature. CI must compare those inputs rather than relying on prose. Language flags differ only where the lesson requires it: - FC02: freestanding C11; - K02: freestanding C++17, no exceptions, no RTTI and no hosted C++ runtime. The workload must remain present in disassembly. Bounds, trace writes and published results are observable; a compiler-folded constant is a failed teaching build. ## A1–A8 is a palette, not a quota The stable viewer keeps all eight positions. A card enables only viewpoints that answer a real question. An unavailable tab remains visible with a short reason, and the print index repeats that reason. | Viewpoint | FC02 C | K02 C++ | Reason | |---|---:|---:|---| | A1 CONTEXT | yes | yes | system and language boundaries | | A2 STRUCTURE | yes | yes | C contexts/handles versus C++ classes/fields | | A3 DISPATCH | yes | yes | C function pointer versus CRTP trampoline | | A4 APPLICATION | no | yes | raw C module already is the application; C++ specializes a generic wrapper | | A5 FLOW | yes | yes | common E01–E12 scenario | | A6 STATE | yes | yes | diagnostic/language lifetime separated from kernel task state | | A7 RUNTIME | yes | yes | addresses, TCB, stack, registers, PC and ticks | | A8 PATTERNS | no | yes | a separate C pattern view would be decorative; K02 provides the comparison | FC02 may label local idioms such as **context object**, **opaque handle** and **C callback ABI** in A2/A3. It must not manufacture an A8 diagram merely to fill the tab. ## Teaching hierarchy Each applicable viewpoint is a normal `Block`. This preserves the existing card tree and does not add a new hierarchy level. ```text Series └── Card └── Task01 · First FreeRTOS task ├── Block · A1 CONTEXT ├── Block · A2 STRUCTURE ├── Block · A3 DISPATCH ├── Block · A4 APPLICATION K02 only ├── Block · A5 FLOW │ ├── Phase · CREATE / START │ └── Phase · DISPATCH / FINISH ├── Block · A6 STATE │ ├── Phase · LANGUAGE / DIAGNOSTIC │ └── Phase · KERNEL ├── Block · A7 RUNTIME │ ├── Phase · MEMORY │ └── Phase · CPU / SCHEDULER ├── Block · A8 PATTERNS K02 only └── Exercise · Paired timestamped trace ``` The A1–A8 top strip jumps to its block. `Up/Down` walks steps inside a block; `Ctrl+Up/Down` crosses blocks. Cursor movement is inert. `Enter` in the viewer and `F2` from Neovim explicitly activate the selected item. ## Common semantic event vocabulary Snapshot identifiers remain local to each card and container. A shared `event_id` makes the observations comparable without pretending that one debugger checkpoint can be reused across two binaries. | Event | Shared meaning | FC02 C boundary | K02 C++ boundary | |---|---|---|---| | E01 | application representation prepared | static worker contexts initialized | static `SumTask` objects constructed | | E02 | launch interface entered | first raw create request | first `workerA.start()` entry | | E03 | kernel create boundary | `xTaskCreate(entry, ctx)` | `xTaskCreate(trampoline, this)` | | E04 | task ready and handle stored | context handle is non-null | wrapper state `ready`, handle non-null | | E05 | scheduler start | `vTaskStartScheduler()` | `vTaskStartScheduler()` | | E06 | C callback entry | `sum_task_entry(void *)` | static `trampoline(void *)` | | E07 | calculation running | typed `WorkerContext *` body | `SumTask::run()` and state `running` | | E08 | deterministic time slice | worker A observes `A -> B -> A` | worker A observes `A -> B -> A` | | E09 | result published | context/global result stored | `g_results[id]` stored, `run()` returns | | E10 | diagnostic completion | context state complete, public handle cleared | wrapper state complete, public handle cleared | | E11 | current task self-deletes | `vTaskDelete(NULL)` | `vTaskDelete(nullptr)` | | E12 | lower-priority supervisor passes | raw context invariants pass | wrapper/object invariants pass | E02 is the first launch hit and is guarded by prior checkpoint state. E08 is pinned to worker A. Per-instance conditions must prevent worker B from accidentally satisfying worker A checkpoints. The supervisor executes before the idle task necessarily reclaims deleted TCBs. Heap reclamation is therefore not an E01–E12 acceptance event. A7 may measure allocator deltas, but it must describe them as observations at a defined instant. ## CODE and RUN semantics An interactive anchor has one of two modes: - **CODE** selects source or a deterministic binary query. It never resets or runs the target and does not require `snapshot_ref`. - **RUN** opens the associated source and performs a clean deterministic replay to its card-local `snapshot_ref`. Compile-time facts—types, inheritance, `friend`, field layout, absence of virtual dispatch and ABI restrictions—are CODE facts. Scheduling, registers, task states and memory values are RUN facts. A class diagram must never fake a runtime event. Repeated visible step numbers are permitted in different blocks, but tooling must address a step by its full `task/block/phase/step` key. A bare ambiguous `--step` request is rejected. ## Debug strategy attached to every step Every selectable UML anchor owns one default debugging strategy. The strategy is keyed by the full navigation position rather than embedded as unstructured prose in the step. ```text strategy ├── id and kind: code | run ├── prerequisites: card, profile, target and required artifact ├── code_ref: repository-validated file and line/range ├── action: open | replay ├── layout: source, asm, registers, stack, memory, kernel state ├── commands: deterministic GDB or artifact queries ├── expected_observations ├── assertions └── evidence_fields ``` RUN assertions reuse `debug_checkpoints.verify.expressions`. CODE assertions are exact command/pattern pairs over an identified ELF or source blob, for example `nm`, `readelf` or a bounded disassembly query. A screenshot of an unidentified terminal is not sufficient CODE evidence. The minimum Termdebug observation set is chosen per step, not displayed by habit: - source and assembly at the boundary being taught; - `a0` only where the RV32 ABI still carries the callback argument; - `sp` and task stack interval where stack ownership is the question; - object/context address, public handle and TCB address where identity is the question; - PC, tick and trace when scheduling is the question; - result and diagnostic state when completion is the question. `pxCurrentTCB` may be inspected by the debugger and is marked **kernel internal**. Card code uses public FreeRTOS APIs and does not make this symbol a program contract. ## Visual and code evidence contract Each UML element has a visible numbered anchor and a stable authored `svg_target`. Selection by mouse and keyboard controls the same cursor. - disabled content remains readable and uses a neutral outline plus an explicit unavailable marker/reason; - selectable inactive neighbours alternate two restrained treatments so their hit areas remain distinguishable; - active selection adds a stronger outline, small positional marker and light fill; - focus, selectability and completion remain distinguishable in grayscale and do not rely on hue alone; - non-selected elements remain legible rather than disappearing. Code shown beside a diagram is read-only evidence. Neovim remains the editor and live debugger. The component receives a validated `code_ref`, source blob hash and explicit highlighted line range; it never fetches an arbitrary file path supplied by the browser. The planned `CodeEvidence` pipeline uses **Shiki at generator/build time**, not as a browser editor or runtime highlighter. A fine-grained, version-pinned highlighter loads only `c`, `cpp`, `riscv`, `shellscript` and `json`, produces flat per-line token arrays, and the React viewer renders that allow-listed data. This keeps the browser bundle free of the regex/WASM engine while retaining an actual RISC-V TextMate grammar and exact line/range classes. Rendering never relies on `dangerouslySetInnerHTML`. A custom Shiki theme emits CSS-variable values rather than stock-theme hex colours. Light, dark and print treatments therefore remain viewer CSS decisions. Assembly source uses the `riscv` grammar directly. Objdump `.lst` input first passes through a small deterministic column parser: address, byte and symbol fields receive fixed semantic classes, while only the instruction tail is tokenized as RISC-V. A non-matching listing line falls back to plain text. Disassembly keeps `white-space: pre` and horizontal scrolling rather than soft wrapping. ## Audit chain and later evidence capture The intended chain is: ```text goal and scope → applicable A1–A8 viewpoints → UML anchor → checkpoint debugging strategy → CODE proof or RUN replay → Neovim + Termdebug + Hazard3 measurement → approved checkpoint → later composite screenshot → later lesson report ``` Approval must be bound to `snapshot_ref`, replay generation and verification result. A later capture must reject stale identity/generation, retain the raw PNG, store an image SHA-256 and record backend timestamp, card/version/source hash, target, event, checkpoint and viewer state. Metadata is rendered from the database rather than burned irreversibly into the original image. This plan does **not** implement screenshot capture or report generation. ## Page plan ### FC02 — seven sheets 1. goal, scope, experiment invariants and viewpoint index; 2. compact A1 plus main A2; 3. A3 callback/parameter dispatch; 4–5. joined A5 CREATE/START and DISPATCH/FINISH spread; 6. A6 diagnostic and kernel state lanes; 7. A7 runtime evidence plus exercise. A4 and A8 are printed in the index as unavailable with their reasons. ### K02 — eight sheets 1. goal, scope, experiment invariants and viewpoint index; 2. compact A1 plus main A2; 3. main A3 plus compact A4; 4–5. joined A5 CREATE/START and DISPATCH/FINISH spread; 6. A6 object/wrapper and kernel state lanes; 7. A7 runtime evidence; 8. A8 C-versus-wrapper role table plus exercise and rubric. One diagram must fit the content box. A long logical diagram is split only at a phase/group boundary and shares a `spread_group`; the viewer may join those sheets side by side without changing print pagination. ## Acceptance gates for a future implementation 1. Parity check proves common kernel/config/workload/priority/stack inputs. 2. Every enabled viewpoint has purpose, model kind and ordered anchors. 3. Every disabled viewpoint has a non-empty reason. 4. Every `svg_target` resolves after rendering and anchor order passes lint. 5. Every CODE step has a validated code/artifact proof and no snapshot. 6. Every RUN step has a card-local snapshot and non-empty assertions. 7. E01–E12 are unique, ordered and comparable across the two cards. 8. A clean replay produces two sums of `200010000` and the `A -> B -> A` trace. 9. FC02 has C-only ABI evidence; K02 has no exceptions, RTTI, vtables or hosted runtime. 10. Navigation never mutates the simulator until explicit `Enter`/`F2`. 11. No program assertion depends on undocumented kernel internals. 12. Print remains meaningful without viewer controls or colour. ## Decisions from the Claude review Accepted corrections: - treat viewpoints as blocks and A1–A8 as an optional palette; - enforce the paired-experiment parity gates in tooling; - add shared semantic `event_id` while keeping snapshots card-local; - validate viewpoint availability, anchors, RUN snapshots and strategies; - make assertions mandatory rather than merely displaying observations; - bind approval and later capture to a verified replay generation; - support deterministic text evidence for CODE steps; - pre-tokenize source with fine-grained Shiki grammars and keep the React component display-only; - pin the highlighter/grammar versions and test the objdump-column parser with fixed listing fixtures; - distinguish diagnostic state from `eTaskState` and mark kernel internals; - keep idle-task reclamation outside E01–E12. Rejected for these cards: - replacing K02 CRTP with the upstream virtual `TaskBase` design; - forcing A4/A8 into FC02; - automatic replay on cursor movement; - claiming RAII or ownership semantics before the later lifetime card; - using `pxCurrentTCB` as application code; - treating a class relationship as a runtime checkpoint. ## Deferred curriculum envelope — not part of this implementation For Year 2, a later curriculum pass will attach three coordinated layers to a lesson: 1. one FreeRTOS mechanism/concurrent algorithm being learned, for example a task or queue; 2. one algorithm selected from the course book; 3. one examination task selected from the current matura schedule/material. The current technology is the execution medium, not a decorative appendix: - a bare-C card implements and debugs the work in bare C; - a FreeRTOS-C task card places suitable work inside raw C task(s); - a FreeRTOS-C++ card expresses the same responsibility through the wrapper under study. Later work may capture approved UML/debugger evidence, compose a lesson report, generate a PDF and prepare it for Teams. None of that is implemented or assigned to FC02/K02 by this plan. ## Primary references - FreeRTOS task creation: - FreeRTOS task states: - FreeRTOS task implementation and self-deletion: - Shared A1–A8 convention: `tools/card-layouts/docs/UML-VIEWPOINTS.md` - Shiki bundled languages and transformers: ,