Files
lab-rv32i-freertos-c-scheduler/json/card_source.json
T

587 lines
45 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"$schema": "../../../tools/card-layouts/schemas/card-source.schema.json",
"schema": "esc-card-source.v1",
"card": {
"id": "mpabi-freertos-c-03-scheduler",
"series": "freertos-c",
"series_title": "FreeRTOS C",
"number": "03",
"count": "15",
"slug": "tick-priority-preemption-timeslicing",
"title": "Tick, priorytety, wywłaszczenie i time slicing",
"topic": "Scheduler FreeRTOS obserwowany w C i Hazard3",
"project": "Freestanding C nad FreeRTOS",
"subject": "Informatyka",
"level": "Rok 2 · L03 · RV32I/Hazard3",
"revision_date": "2026-07-18T00:00:00+02:00",
"status": "Gotowa",
"version": "v00.01",
"uuid": "c4bf7933-9a39-4968-b40e-158bafbb3eea",
"author": "M. Pabiszczak",
"year": "2026"
},
"generated": {
"tex": "doc/generated/main.tex",
"html": "web/index.html",
"html_css": "web/style.css",
"html_tree_inspector": false,
"react_app": true
},
"render_dictionary": false,
"viewpoints": [
{"id": "A1", "label": "CONTEXT", "subtitle": "Timer → tick ISR → scheduler → taski", "status": "enabled", "target_label": "fig:a1-context"},
{"id": "A2", "label": "STRUCTURE", "subtitle": "Konteksty i ograniczone rekordy dowodowe", "status": "enabled", "target_label": "fig:a2-structure"},
{"id": "A3", "label": "DISPATCH", "subtitle": "Callback i context", "status": "unavailable", "reason": "Mechanizm TaskFunction_t i void* został udowodniony w FC02 i w FC03 nie ulega zmianie."},
{"id": "A4", "label": "APPLICATION", "subtitle": "Cztery taski i kontrakt priorytetów", "status": "enabled", "target_label": "fig:a4-application"},
{"id": "A5", "label": "FLOW", "subtitle": "Deterministyczny przebieg E01E12", "status": "enabled", "target_label": "fig:a5-flow-a"},
{"id": "A6", "label": "STATE", "subtitle": "Ready, Running, Deleted i zmiana priorytetu", "status": "enabled", "target_label": "fig:a6-state"},
{"id": "A7", "label": "RUNTIME", "subtitle": "Tick, CSRs, TCB, stosy i ślady", "status": "enabled", "target_label": "fig:a7-runtime"},
{"id": "A8", "label": "PATTERNS", "subtitle": "Wzorce aplikacyjne", "status": "unavailable", "reason": "Polityka schedulera jest konfiguracją kernela, a nie wzorcem projektowym aplikacji."}
],
"debug_strategies": {
"code.tick-boundary": {
"id": "code.tick-boundary",
"kind": "code",
"action": "open",
"prerequisites": ["repozytorium FC03", "zweryfikowany source blob"],
"layout": ["read-only source", "granica timer/port/kernel/aplikacja"],
"commands": ["rg -n 'configTICK_RATE_HZ|vApplicationTickHook|xTaskIncrementTick' include src vendor/FreeRTOS-Kernel"],
"expected_observations": ["tick jest okazją do planowania", "aplikacja zapisuje tylko ograniczony rekord"],
"assertions": ["tick hook nie drukuje i nie blokuje", "aplikacja nie zależy od pxCurrentTCB"],
"evidence_fields": ["code_ref", "source_git_blob", "kotwica UML"]
},
"code.scheduler-data": {
"id": "code.scheduler-data",
"kind": "code",
"action": "open",
"prerequisites": ["freestanding C11", "include/task01_scheduler.h"],
"layout": ["konteksty C", "priorytety", "bufory śladów"],
"commands": ["rg -n 'PeerContext|HighProbeContext|g_switch_task|g_preemption_order' include src"],
"expected_observations": ["taski mają jawne konteksty", "rekordy mają stałą pojemność"],
"assertions": ["high ma kontrakt 0→3", "verifier ma priorytet 1"],
"evidence_fields": ["layout pól", "stałe priorytetów", "pojemność śladu"]
},
"run.prepare": {
"id": "run.prepare",
"kind": "run",
"action": "replay",
"prerequisites": ["Hazard3 online", "czysta RAM", "aktualny ELF"],
"layout": ["source", "konteksty", "uchwyty", "priorytety"],
"commands": ["print g_high_probe", "print g_peer_a", "print g_peer_b", "print g_verifier"],
"expected_observations": ["high jest Ready przy priorytecie 0", "peery mają priorytet 2, verifier 1"],
"assertions": ["wszystkie uchwyty są ważne przed startem schedulera", "publiczne API zwraca oczekiwane priorytety"],
"evidence_fields": ["event_id", "handle", "priority", "timestamp"]
},
"run.tick": {
"id": "run.tick",
"kind": "run",
"action": "replay",
"prerequisites": ["scheduler uruchomiony", "tick hook włączony"],
"layout": ["tick hook", "CSR", "ISR stack", "przerwany task"],
"commands": ["print g_tick_hook_count", "print/x g_first_tick_mcause", "print/x g_first_tick_mepc", "print/x g_first_tick_sp"],
"expected_observations": ["mcause oznacza machine-timer interrupt", "mepc wskazuje przerwany kod"],
"assertions": ["checkpoint ticka jest jednorazowy", "sink ISR nie korzysta z task-context API"],
"evidence_fields": ["tick", "mcause", "mepc", "ISR SP", "timestamp"]
},
"run.timeslice": {
"id": "run.timeslice",
"kind": "run",
"action": "replay",
"prerequisites": ["dwa CPU-bound taski priority=2", "time slicing włączony"],
"layout": ["pętle peerów", "task-change trace", "ticki"],
"commands": ["print g_switch_task", "print g_switch_tick", "print g_aba_task", "print g_peer_changes"],
"expected_observations": ["zatwierdzony ślad zawiera A→B→A", "ticki są niemalejące"],
"assertions": ["zapisywana jest jedna pozycja na obserwowaną zmianę taska", "dowód nie zakłada równego udziału CPU"],
"evidence_fields": ["triplet A/B/A", "ticks", "change count", "timestamp"]
},
"run.preempt": {
"id": "run.preempt",
"kind": "run",
"action": "replay",
"prerequisites": ["A→B→A wykryte", "high Ready przy priorytecie 0"],
"layout": ["call site vTaskPrioritySet", "high task", "markery kolejności"],
"commands": ["print g_preemption_order", "print g_high_probe", "info registers pc sp a0 a1"],
"expected_observations": ["high uruchamia się przy priorytecie 3", "caller nie zapisuje after przed HIGH"],
"assertions": ["projekcja markerów to before/HIGH/after", "verifier jeszcze nie działa"],
"evidence_fields": ["marker projection", "priority", "PC", "SP", "timestamp"]
},
"run.state": {
"id": "run.state",
"kind": "run",
"action": "replay",
"prerequisites": ["kanoniczny checkpoint FC03"],
"layout": ["kontekst aplikacyjny", "publiczny stan taska", "source"],
"commands": ["print g_peer_a.phase", "print g_peer_b.phase", "print g_high_probe.phase", "print g_peers_finished"],
"expected_observations": ["Ready nie oznacza Running", "każdy task czyści własny publiczny handle"],
"assertions": ["high i peery kończą się przed verifierem", "brak suspend/resume w eksperymencie"],
"evidence_fields": ["phase", "handle", "event_id", "PC"]
},
"run.runtime": {
"id": "run.runtime",
"kind": "run",
"action": "replay",
"prerequisites": ["Hazard3/GDB online", "symbole ELF"],
"layout": ["timer/CSR", "handle/TCB", "task stack", "committed evidence"],
"commands": ["print g_peer_a.created_handle", "print g_peer_a.tcb_address", "print g_peer_a.stack_low", "print g_peer_a.stack_high", "info registers sp pc"],
"expected_observations": ["handle/TCB i stack są rozdzielone", "SP należy do stosu wybranego taska"],
"assertions": ["created_handle == tcb_address", "źródło, ELF i obraz są identyfikowalne"],
"evidence_fields": ["handle/TCB", "stack range", "SP", "PC", "hash"]
},
"run.pass": {
"id": "run.pass",
"kind": "run",
"action": "replay",
"prerequisites": ["high i oba peery zakończone", "verifier wybrany"],
"layout": ["konteksty", "ślady", "końcowe asercje"],
"commands": ["print g_scheduler_pass", "print g_aba_task", "print g_preemption_order", "print g_tick_hook_count"],
"expected_observations": ["A/B/A i before/HIGH/after są obecne", "program kończy się PASS"],
"assertions": ["PASS=1", "g_peers_finished=2", "tick hook count>=2"],
"evidence_fields": ["PASS", "traces", "ticks", "source blob", "image hash", "timestamp"]
}
},
"template": "templates/karta-klasyczna.json",
"title_block": {
"category": "KARTA PRACY · INFORMATYKA",
"prepared_by": "M. Pabiszczak",
"prepared_on": "2026-07-18T00:00:00+02:00",
"title": "Tick, priorytety, wywłaszczenie i time slicing",
"url": "https://dce7fb9d-7b2f-5d49-96a2-3a30d3070b84.mpabi.pl/2214a13f-34c6-40a1-a561-70a00ec98285",
"repository_url": "https://zsl-gitea.mpabi.pl/edu-freertos-c/lab-rv32i-freertos-c-scheduler",
"url_host_uuid": "dce7fb9d-7b2f-5d49-96a2-3a30d3070b84",
"url_domain": "mpabi.pl",
"doc_uuid": "2214a13f-34c6-40a1-a561-70a00ec98285",
"revision": "v00.01",
"issued_on": "2026-07-18T00:00:00+02:00",
"series": "FREERTOS-C-03",
"document_type": "karta pracy",
"tool": "card-layouts",
"show_qr": true,
"show_repository_qr": true,
"height_cm": 2.6,
"repeat_on_every_page": true,
"replace_front_matter": true
},
"front_page_scope": {
"title": "Cel karty",
"content_tex": "Uczeń rozróżnia wybór najwyższego priorytetu od time slicing między równymi priorytetami. Odtwarza tick oraz natychmiastowe wywłaszczenie po zmianie priorytetu i wiąże ślad aplikacji ze stanem Hazard3/GDB.",
"scope_title": "Zakres karty",
"scope_content_tex": "Dwa CPU-bound taski o priorytecie 2 tworzą ślad A-B-A. Peer A podnosi Ready task z priorytetu 0 do 3; HIGH musi wykonać się przed powrotem vTaskPrioritySet. Verifier o priorytecie 1 publikuje PASS dopiero po zakończeniu wyższych tasków. Poza zakresem są delay, kolejki, semafory, ISR-safe API i C++.",
"scope_table": {
"headers": ["Lekcja", "Task", "Najważniejsza idea", "Priorytet", "Status", "Version"],
"rows": [{"chapter": "FC03", "task": "Task01", "idea_tex": "Tick, priority selection, preemption i time slicing", "priority": "główny", "status": "ready", "version": "v00.01", "key": true}]
}
},
"side_margin_tree_layout": {
"columns": [
{"id": "zawodowe", "label": "TECH", "side": "left", "tree": "WE -> EK -> KW", "description": "Dowody schedulera w Hazard3."},
{"id": "ogolne", "label": "OG", "side": "right", "tree": "WE -> EN -> KW", "description": "Model czasu i priorytetów."}
]
},
"learning_effects": {
"FC03.EN01": {"bloom_level": "Analiza", "label": "Model schedulera", "text": "Uczeń odróżnia priorytet, Ready/Running, tick i time slicing.", "assessment_criteria": ["FC03.KW01"]},
"FC03.EK01": {"bloom_level": "Zastosowanie", "label": "Replay wywłaszczenia", "text": "Uczeń odtwarza E01E12 i dowodzi A-B-A oraz before-HIGH-after.", "assessment_criteria": ["FC03.KW01"]}
},
"assessment_criteria": {
"FC03.KW01": {"text": "Program kończy się PASS; ślad zawiera A-B-A, markery before-HIGH-after, poprawny machine-timer mcause i zgodne zakresy stosów.", "learning_effects": ["FC03.EN01", "FC03.EK01"]}
},
"educational_requirements": {
"FC03.WE01": {
"text": "Zaprojektowanie i zbadanie kontrolowanego eksperymentu schedulera FreeRTOS w C.",
"label": "FreeRTOS scheduler experiment",
"learning_effects": ["FC03.EN01", "FC03.EK01"],
"learning_tree": {
"schema": "we-learning-tree.v1",
"policy": "Każda aktywna kotwica UML wskazuje kod lub zweryfikowany checkpoint.",
"ogolne": [{
"effect_ref": "FC03.EN01",
"display": "EN LOCAL RTOS.03",
"source": "LOCAL",
"official": "RTOS",
"local": "03",
"kind": "EN",
"tree_id": "FC03.WE01.OG.LOCAL.RTOS.03",
"text": "Analizuje reguły wyboru i stany tasków.",
"kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL RTOS.03", "source": "LOCAL", "kind": "KW", "official": "RTOS", "local": "03", "text": "Rozdziela priorytet od time slicing."}]
}],
"zawodowe": [{
"effect_ref": "FC03.EK01",
"display": "EK LOCAL DBG.03",
"source": "LOCAL",
"official": "DBG",
"local": "03",
"kind": "EK",
"tree_id": "FC03.WE01.TECH.LOCAL.DBG.03",
"text": "Odtwarza scheduler w Hazard3/GDB.",
"kw": [{"criterion_ref": "FC03.KW01", "display": "KW LOCAL DBG.03", "source": "LOCAL", "kind": "KW", "official": "DBG", "local": "03", "text": "Łączy checkpoint, UML, rejestry i pamięć."}]
}]
}
}
},
"debug_checkpoints": {
"schema": "stem-debug-checkpoints.v1",
"semantics": "deterministic-replay",
"artifact": {
"source": "src/tasks/task01_scheduler.c",
"source_git_blob": "34b18bfc69ac51f2ead2cd38ea02f2a5005ac802",
"elf": "build/task01_scheduler/prog.elf",
"hazard3_elf_sha256": "47a2f8aa02953de388a52d03cba99b25e750309f11d46894ee8a31872a285496",
"hazard3_image": "build/task01_scheduler/prog.bin",
"hazard3_image_sha256": "59b2cc89a674f6b04896c269a0d25f75a4e7759ad6c236887b0a49843ed2cda6"
},
"targets": {
"hazard3-sim": {"adapter": "hazard3-reset-load-replay.v1", "baseline": "restart-backend", "clean_ram": true}
},
"items": {
"task01.config": {
"event_id": "E01",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 1"},
"verify": {"expressions": [{"expr": "g_last_checkpoint", "equals": 1}, {"expr": "g_high_probe.handle == 0", "equals": 1}]}
},
"task01.high-dormant": {
"event_id": "E02",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 2 && $a1 == &g_high_probe"},
"verify": {"expressions": [{"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_high_probe.phase", "equals": 1}]}
},
"task01.priorities": {
"event_id": "E03",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 3 && $a1 == &g_high_probe"},
"verify": {"expressions": [{"expr": "g_high_probe.observed_initial_priority", "equals": 0}, {"expr": "g_peer_a.observed_priority", "equals": 2}, {"expr": "g_peer_b.observed_priority", "equals": 2}, {"expr": "g_verifier.observed_priority", "equals": 1}]}
},
"task01.scheduler": {
"event_id": "E04",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 4"},
"verify": {"expressions": [{"expr": "g_peer_a.handle != 0", "equals": 1}, {"expr": "g_peer_b.handle != 0", "equals": 1}, {"expr": "g_high_probe.handle != 0", "equals": 1}, {"expr": "g_verifier.handle != 0", "equals": 1}]}
},
"task01.peer-entry": {
"event_id": "E05",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 5"},
"verify": {"expressions": [{"expr": "g_peer_a.iterations + g_peer_b.iterations >= 0", "equals": 1}]}
},
"task01.first-tick": {
"event_id": "E06",
"stop": {"symbol": "task01_tick_checkpoint_committed", "offset": 0, "condition": "g_tick_hook_count == 1"},
"verify": {"expressions": [{"expr": "g_tick_hook_count", "equals": 1}, {"expr": "g_first_tick_mcause", "equals": 2147483655}, {"expr": "g_first_tick_sp != 0", "equals": 1}, {"expr": "g_first_tick_mepc != 0", "equals": 1}]}
},
"task01.timeslice": {
"event_id": "E07",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 7 && $a1 == &g_peer_a"},
"verify": {"expressions": [{"expr": "g_aba_seen", "equals": 1}, {"expr": "g_aba_task[0]", "equals": 0}, {"expr": "g_aba_task[1]", "equals": 1}, {"expr": "g_aba_task[2]", "equals": 0}, {"expr": "g_peer_changes >= 2", "equals": 1}]}
},
"task01.before-raise": {
"event_id": "E08",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 8 && $a1 == &g_peer_a"},
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 1}, {"expr": "g_preemption_order[0]", "equals": 1}, {"expr": "g_peer_after_raise", "equals": 0}]}
},
"task01.high-preempts": {
"event_id": "E09",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 9 && $a1 == &g_high_probe"},
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 2}, {"expr": "g_preemption_order[1]", "equals": 2}, {"expr": "g_high_probe.observed_priority", "equals": 3}, {"expr": "g_peer_after_raise", "equals": 0}]}
},
"task01.after-raise": {
"event_id": "E10",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 10 && $a1 == &g_peer_a"},
"verify": {"expressions": [{"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_preemption_order[2]", "equals": 3}, {"expr": "g_stop_peers", "equals": 1}]}
},
"task01.peers-finished": {
"event_id": "E11",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 11"},
"verify": {"expressions": [{"expr": "g_peers_finished", "equals": 2}, {"expr": "g_peer_a.handle == 0", "equals": 1}, {"expr": "g_peer_b.handle == 0", "equals": 1}]}
},
"task01.pass": {
"event_id": "E12",
"stop": {"symbol": "task01_scheduler_checkpoint_committed", "offset": 0, "condition": "$a0 == 12 && $a1 == &g_verifier"},
"verify": {"expressions": [{"expr": "g_scheduler_pass", "equals": 1}, {"expr": "g_peers_finished", "equals": 2}, {"expr": "g_aba_seen", "equals": 1}, {"expr": "g_preemption_order_count", "equals": 3}, {"expr": "g_tick_hook_count >= 2", "equals": 1}]}
}
}
},
"sections": [
{
"title": "A1 — Context",
"order": 10,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "portrait",
"content_tex": "Timer Hazard3 wywołuje port RV32I, kernel podejmuje decyzję planistyczną, a aplikacja zapisuje ograniczony dowód.",
"assets": [{
"path": "assets/a1-context.png",
"html_path": "assets/a1-context.svg",
"source_path": "assets/a1-context.puml",
"caption": "A1 CONTEXT — timer, port, scheduler i taski aplikacji.",
"label": "fig:a1-context",
"alt": "Pionowy kontekst ticka i schedulera FreeRTOS na Hazard3.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 660, "page_height": 760, "overview": true, "step_tiles": {"timer": 1, "port": 1, "scheduler": 1, "application": 1}},
"interactive": {
"kind": "uml-class",
"storage_key": "fc03-a1-context",
"title": "A1 · granice ticka i schedulera",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a1-context", "label": "A1 CONTEXT", "description": "Kierunek wykonania i odpowiedzialności."},
"phases": [{"id": "tick-boundary", "label": "TICK / SCHEDULER", "steps": [
{"id": "timer", "number": 1, "label": "mtime reaches mtimecmp", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "description": "Hazard3 generuje machine-timer interrupt.", "evidence": "Adresy mtime i mtimecmp pochodzą z konfiguracji portu."},
{"id": "port", "number": 2, "label": "RV32I port handles the tick", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "02", "svg_target": "Port", "code_ref": "vendor/FreeRTOS-Kernel/portable/GCC/RISC-V/portASM.S:1", "description": "Port przełącza domenę ISR i wchodzi do kernela.", "evidence": "Tick hook działa w kontekście przerwania."},
{"id": "scheduler", "number": 3, "label": "scheduler selects a Ready task", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "03", "svg_target": "Scheduler", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "description": "Priorytet wybiera klasę, time slicing rotuje równych.", "evidence": "Reguły pozostają własnością kernela."},
{"id": "application", "number": 4, "label": "application records evidence", "mode": "CODE", "strategy_ref": "code.tick-boundary", "svg_label": "04", "svg_target": "Application", "code_ref": "src/tasks/task01_scheduler.c:116", "description": "Aplikacja zapisuje tylko bounded evidence.", "evidence": "Brak drukowania, alokacji i blokowania w tick hook."}
]}]
}
}]
},
{
"title": "A2 — Structure",
"order": 20,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Jawne konteksty C przechowują publiczne uchwyty i pomiary; dwa ograniczone bufory dowodzą niezależnie time slicing oraz wywłaszczenia.",
"assets": [{
"path": "assets/a2-structure.png",
"html_path": "assets/a2-structure.svg",
"source_path": "assets/a2-structure.puml",
"caption": "A2 STRUCTURE — konteksty tasków i rekordy śladów.",
"label": "fig:a2-structure",
"alt": "Struktury peer, high probe, verifier i dwa rekordy dowodowe.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peers": 1, "probe": 1, "verifier": 1, "switch-trace": 1, "order-trace": 1}},
"interactive": {
"kind": "uml-class",
"storage_key": "fc03-a2-structure",
"title": "A2 · dane eksperymentu",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a2-structure", "label": "A2 STRUCTURE", "description": "Jawne dane aplikacji, nie kopia prywatnych struktur kernela."},
"phases": [{"id": "contexts-traces", "label": "CONTEXTS / TRACES", "steps": [
{"id": "peers", "number": 1, "label": "two equal-priority peer contexts", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "01", "svg_target": "Peers", "code_ref": "include/task01_scheduler.h:23", "description": "Peery dzielą typ, ale mają osobne context, handle i stosy.", "evidence": "configured_priority=2 dla obu."},
{"id": "probe", "number": 2, "label": "high probe priority contract 0 to 3", "mode": "CODE", "strategy_ref": "code.scheduler-data", "svg_label": "02", "svg_target": "Probe", "code_ref": "include/task01_scheduler.h:43", "description": "Probe jest Ready, lecz początkowo poniżej verifiera.", "evidence": "initial_priority=0, target_priority=3."},
{"id": "verifier", "number": 3, "label": "verifier is deliberately lowest runnable task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "svg_target": "Verify", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Verifier ma priorytet 1.", "evidence": "Nie może opublikować PASS przed zakończeniem peerów."},
{"id": "switch-trace", "number": 4, "label": "task-change trace stores id and tick", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "04", "svg_target": "SwitchTrace", "code_ref": "src/tasks/task01_scheduler.c:172", "snapshot_ref": "task01.timeslice", "description": "Powtórzenia w tej samej obserwowanej zmianie nie są logowane.", "evidence": "Zatwierdzony podciąg A/B/A."},
{"id": "order-trace", "number": 5, "label": "preemption markers are separate", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "OrderTrace", "code_ref": "src/tasks/task01_scheduler.c:305", "snapshot_ref": "task01.after-raise", "description": "Marker projection nie udaje pełnego task trace.", "evidence": "before/HIGH/after."}
]}]
}
}]
},
{
"title": "A4 — Application",
"order": 30,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Cztery taski tworzą kontrolowany eksperyment: dwa równorzędne peery, probe podnoszony do priorytetu 3 i verifier uruchamiany na końcu.",
"assets": [{
"path": "assets/a4-application.png",
"html_path": "assets/a4-application.svg",
"source_path": "assets/a4-application.puml",
"caption": "A4 APPLICATION — topologia tasków i inwarianty priorytetów.",
"label": "fig:a4-application",
"alt": "Dwa peery, high probe i verifier w kontrolowanym eksperymencie.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"peer-a": 1, "peer-b": 1, "high": 1, "verifier": 1}},
"interactive": {
"kind": "uml-class",
"storage_key": "fc03-a4-application",
"title": "A4 · topologia eksperymentu",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a4-application", "label": "A4 APPLICATION", "description": "Konkretny przypadek użycia schedulera."},
"phases": [{"id": "priority-topology", "label": "TASKS / PRIORITIES", "steps": [
{"id": "peer-a", "number": 1, "label": "peer A runs CPU-bound at priority 2", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "01", "svg_target": "PeerA", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Peer A rejestruje zmiany i inicjuje raise.", "evidence": "Własny context, TCB i stos."},
{"id": "peer-b", "number": 2, "label": "peer B competes at equal priority", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "02", "svg_target": "PeerB", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.timeslice", "description": "B jest konieczny do podciągu A/B/A.", "evidence": "Oba peery wykonują pracę."},
{"id": "high", "number": 3, "label": "peer A raises high probe to 3", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "High preemptuje caller i self-delete.", "evidence": "Marker HIGH poprzedza after_raise."},
{"id": "verifier", "number": 4, "label": "verifier runs last at priority 1", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "04", "svg_target": "Verifier", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier publikuje końcowy dowód.", "evidence": "PASS dopiero po self-delete tasków o priorytecie 2/3."}
]}]
}
}]
},
{
"title": "A5 — Flow",
"order": 40,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"spread_group": "fc03-scheduler-flow",
"spread_label": "FC03 · scheduler experiment",
"spread_columns": 2,
"content_tex": "E01--E12 tworzą jeden logiczny przebieg rozłożony na dwie poziome strony.",
"assets": [
{
"path": "assets/a5-flow-a.png",
"html_path": "assets/a5-flow-a.svg",
"source_path": "assets/a5-flow-a.puml",
"caption": "A5 część 1/2 — konfiguracja, start, pierwszy tick i A-B-A.",
"label": "fig:a5-flow-a",
"alt": "Pierwsza połowa sekwencji eksperymentu schedulera.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"config": 1, "high-ready": 1, "priorities": 1, "scheduler": 1, "peer-entry": 1, "first-tick": 1, "timeslice": 1}},
"interactive": {
"kind": "uml-sequence",
"storage_key": "fc03-a5-flow-a",
"title": "część 1/2 · przygotowanie i time slicing",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Jeden przebieg E01E12 i wspólni uczestnicy."},
"phases": [{"id": "prepare-timeslice", "label": "PREPARE / TIME SLICE", "steps": [
{"id": "config", "number": 1, "label": "scheduler configuration committed", "mode": "RUN", "event_id": "E01", "strategy_ref": "run.prepare", "svg_label": "01", "code_ref": "src/tasks/task01_scheduler.c:486", "snapshot_ref": "task01.config", "description": "Eksperyment zaczyna się z czystą RAM.", "evidence": "One core, preemption, time slicing i tick hook są w kodzie konfiguracji."},
{"id": "high-ready", "number": 2, "label": "high probe Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.prepare", "svg_label": "02", "code_ref": "src/tasks/task01_scheduler.c:488", "snapshot_ref": "task01.high-dormant", "description": "High istnieje, lecz nie może jeszcze wyprzedzić verifiera.", "evidence": "handle != NULL, phase=READY."},
{"id": "priorities", "number": 3, "label": "public API confirms 0,2,2,1", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "03", "code_ref": "src/tasks/task01_scheduler.c:527", "snapshot_ref": "task01.priorities", "description": "Priorytety są odczytywane przez publiczne API.", "evidence": "high=0, A=2, B=2, verifier=1."},
{"id": "scheduler", "number": 4, "label": "vTaskStartScheduler", "mode": "RUN", "event_id": "E04", "strategy_ref": "run.prepare", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:545", "snapshot_ref": "task01.scheduler", "description": "Ostatni checkpoint na startup stack.", "evidence": "Wszystkie handle są ważne."},
{"id": "peer-entry", "number": 5, "label": "first equal-priority peer runs", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.timeslice", "svg_label": "05", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Pierwszy peer przechodzi do Running.", "evidence": "SP należy do jego task stack."},
{"id": "first-tick", "number": 6, "label": "first machine-timer tick observed", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "ISR-safe sink przechwytuje pierwszy tick.", "evidence": "mcause=0x80000007, mepc i ISR SP."},
{"id": "timeslice", "number": 7, "label": "trace contains A to B to A", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "07", "code_ref": "src/tasks/task01_scheduler.c:290", "snapshot_ref": "task01.timeslice", "description": "Checkpoint emituje peer A po wykryciu podciągu.", "evidence": "A/B/A i niemalejące ticki."}
]}]
}
},
{
"path": "assets/a5-flow-b.png",
"html_path": "assets/a5-flow-b.svg",
"source_path": "assets/a5-flow-b.puml",
"caption": "A5 część 2/2 — priority raise, natychmiastowe wywłaszczenie i PASS.",
"label": "fig:a5-flow-b",
"alt": "Druga połowa sekwencji eksperymentu schedulera.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"before-raise": 1, "high-preempts": 1, "after-raise": 1, "peers-finished": 1, "pass": 1}},
"interactive": {
"kind": "uml-sequence",
"storage_key": "fc03-a5-flow-b",
"title": "część 2/2 · preemption i zakończenie",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a5-flow", "label": "A5 FLOW", "description": "Kontynuacja tych samych tasków i rekordów."},
"phases": [{"id": "preempt-finish", "label": "PREEMPT / FINISH", "steps": [
{"id": "before-raise", "number": 8, "label": "peer A commits before_raise", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:299", "snapshot_ref": "task01.before-raise", "description": "Pierwszy marker poprzedza vTaskPrioritySet.", "evidence": "order={before}, after flag=0."},
{"id": "high-preempts", "number": 9, "label": "high runs before call returns", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "09", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Zmiana 0→3 czyni high najwyższym Ready taskiem.", "evidence": "order={before,HIGH}, caller after flag=0."},
{"id": "after-raise", "number": 10, "label": "peer A commits after_raise and stop", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.preempt", "svg_label": "10", "code_ref": "src/tasks/task01_scheduler.c:303", "snapshot_ref": "task01.after-raise", "description": "Caller wraca dopiero po self-delete high.", "evidence": "order={before,HIGH,after}, stop=1."},
{"id": "peers-finished", "number": 11, "label": "both peers clear handles and self-delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "11", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Każdy peer kończy własny lifetime taska.", "evidence": "g_peers_finished=2, oba handle=NULL."},
{"id": "pass", "number": 12, "label": "low-priority verifier publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "12", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Verifier sprawdza wszystkie niezależne dowody.", "evidence": "PASS=1 i poprawny exit code."}
]}]
}
}
]
},
{
"title": "A6 — State",
"order": 50,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Priorytet wybiera najwyższy Ready task; time slicing dotyczy tylko równych priorytetów. Stan diagnostyczny aplikacji nie zastępuje stanu kernela.",
"assets": [{
"path": "assets/a6-state.png",
"html_path": "assets/a6-state.svg",
"source_path": "assets/a6-state.puml",
"caption": "A6 STATE — przejścia high probe, peerów i verifiera.",
"label": "fig:a6-state",
"alt": "Stany Ready, Running i Deleted oraz zmiana priorytetu high probe.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"high-low": 1, "high-ready": 1, "high-run": 1, "high-deleted": 1, "peers": 1, "peers-done": 1, "verify-ready": 1, "verify-run": 1}},
"interactive": {
"kind": "uml-class",
"storage_key": "fc03-a6-state",
"title": "A6 · priorytet i stan",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a6-state", "label": "A6 STATE", "description": "Ready nie oznacza Running."},
"phases": [
{"id": "high-probe", "label": "HIGH PROBE", "steps": [
{"id": "high-low", "number": 1, "label": "high Ready at priority 0", "mode": "RUN", "event_id": "E02", "strategy_ref": "run.state", "svg_label": "01", "svg_target": "HighLow", "code_ref": "src/tasks/task01_scheduler.c:497", "snapshot_ref": "task01.high-dormant", "description": "Task istnieje, ale nie jest wybrany.", "evidence": "phase=READY, priority=0."},
{"id": "high-ready", "number": 2, "label": "priority changes from 0 to 3", "mode": "RUN", "event_id": "E08", "strategy_ref": "run.preempt", "svg_label": "02", "svg_target": "HighReady", "code_ref": "src/tasks/task01_scheduler.c:300", "snapshot_ref": "task01.before-raise", "description": "API zmienia pozycję taska w polityce wyboru.", "evidence": "Caller zapisał before marker."},
{"id": "high-run", "number": 3, "label": "high selected and Running", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "03", "svg_target": "HighRun", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "Najwyższy Ready task wywłaszcza caller.", "evidence": "priority=3, PC/SP należą do high."},
{"id": "high-deleted", "number": 4, "label": "high self-deletes", "mode": "RUN", "event_id": "E10", "strategy_ref": "run.state", "svg_label": "04", "code_ref": "src/tasks/task01_scheduler.c:366", "snapshot_ref": "task01.after-raise", "description": "High czyści handle przed vTaskDelete(NULL).", "evidence": "Caller może wrócić i zapisać after."}
]},
{"id": "peers-verifier", "label": "PEERS / VERIFIER", "steps": [
{"id": "peers", "number": 5, "label": "peers alternate Ready and Running", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.timeslice", "svg_label": "05", "svg_target": "Peers", "code_ref": "src/tasks/task01_scheduler.c:279", "snapshot_ref": "task01.timeslice", "description": "Tick rotuje równorzędne peery.", "evidence": "A/B/A."},
{"id": "peers-done", "number": 6, "label": "both peers complete and delete", "mode": "RUN", "event_id": "E11", "strategy_ref": "run.state", "svg_label": "06", "code_ref": "src/tasks/task01_scheduler.c:325", "snapshot_ref": "task01.peers-finished", "description": "Stop request nie usuwa taska z zewnątrz.", "evidence": "Każdy peer self-delete."},
{"id": "verify-ready", "number": 7, "label": "verifier waits as lower Ready task", "mode": "RUN", "event_id": "E03", "strategy_ref": "run.prepare", "svg_label": "07", "svg_target": "VerifyReady", "code_ref": "src/tasks/task01_scheduler.c:519", "snapshot_ref": "task01.priorities", "description": "Priorytet 1 przegrywa z peerami 2.", "evidence": "Verifier nie wykonuje się przed stop."},
{"id": "verify-run", "number": 8, "label": "verifier runs and publishes PASS", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "08", "code_ref": "src/tasks/task01_scheduler.c:386", "snapshot_ref": "task01.pass", "description": "Po usunięciu wyższych tasków verifier staje się najwyższy.", "evidence": "PASS=1."}
]}
]
}
}]
},
{
"title": "A7 — Runtime",
"order": 60,
"content_kind": "prose",
"asset_page_mode": "one-per-page",
"page_orientation": "landscape",
"content_tex": "Dowód runtime łączy timer CSRs, ISR stack, Ready lists obserwowane w debuggerze, TCB/stosy tasków oraz aplikacyjne rekordy commit.",
"assets": [{
"path": "assets/a7-runtime.png",
"html_path": "assets/a7-runtime.svg",
"source_path": "assets/a7-runtime.puml",
"caption": "A7 RUNTIME — timer, ISR, scheduler, taski i końcowy dowód.",
"label": "fig:a7-runtime",
"alt": "Runtime evidence od timer CSRs do śladu aplikacji.",
"kind": "diagram",
"width": 1.0,
"page_grid": {"columns": 1, "rows": 1, "page_width": 940, "page_height": 560, "overview": true, "step_tiles": {"timer": 1, "isr": 1, "kernel": 1, "peer": 1, "high": 1, "evidence": 1}},
"interactive": {
"kind": "uml-class",
"storage_key": "fc03-a7-runtime",
"title": "A7 · mapa dowodów runtime",
"task": {"id": "task01", "label": "Task01 · scheduler experiment"},
"block": {"id": "a7-runtime", "label": "A7 RUNTIME", "description": "Kernel internals są tylko obserwacją debuggera."},
"phases": [
{"id": "timer-cpu", "label": "TIMER / CPU", "steps": [
{"id": "timer", "number": 1, "label": "timer CSRs define the interrupt", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "01", "svg_target": "Timer", "code_ref": "include/FreeRTOSConfig.h:4", "snapshot_ref": "task01.first-tick", "description": "Mtime/mtimecmp prowadzą do machine-timer trap.", "evidence": "mcause=0x80000007."},
{"id": "isr", "number": 2, "label": "ISR evidence captures tick PC and SP", "mode": "RUN", "event_id": "E06", "strategy_ref": "run.tick", "svg_label": "02", "svg_target": "ISR", "code_ref": "src/tasks/task01_scheduler.c:116", "snapshot_ref": "task01.first-tick", "description": "Mepc opisuje przerwany task, SP domenę ISR.", "evidence": "Niezerowe mepc/SP i one-shot checkpoint."},
{"id": "kernel", "number": 3, "label": "Ready lists are debugger-only evidence", "mode": "RUN", "event_id": "E07", "strategy_ref": "run.runtime", "svg_label": "03", "svg_target": "Kernel", "code_ref": "vendor/FreeRTOS-Kernel/tasks.c:1", "snapshot_ref": "task01.timeslice", "description": "Aplikacja nie odczytuje pxCurrentTCB.", "evidence": "Publiczne rekordy wystarczają do PASS."}
]},
{"id": "tasks-evidence", "label": "TASKS / EVIDENCE", "steps": [
{"id": "peer", "number": 4, "label": "peer handle TCB stack PC and SP", "mode": "RUN", "event_id": "E05", "strategy_ref": "run.runtime", "svg_label": "04", "svg_target": "Peer", "code_ref": "src/tasks/task01_scheduler.c:248", "snapshot_ref": "task01.peer-entry", "description": "Context, TCB i stack są osobnymi obszarami.", "evidence": "created_handle==tcb_address i SP w stack range."},
{"id": "high", "number": 5, "label": "high runs at changed priority", "mode": "RUN", "event_id": "E09", "strategy_ref": "run.preempt", "svg_label": "05", "svg_target": "High", "code_ref": "src/tasks/task01_scheduler.c:346", "snapshot_ref": "task01.high-preempts", "description": "PC/SP i run_tick identyfikują wybrany task.", "evidence": "priority=3 i marker HIGH."},
{"id": "evidence", "number": 6, "label": "committed evidence binds source and ELF", "mode": "RUN", "event_id": "E12", "strategy_ref": "run.pass", "svg_label": "06", "svg_target": "Evidence", "code_ref": "src/tasks/task01_scheduler.c:450", "snapshot_ref": "task01.pass", "description": "Końcowy snapshot wiąże wynik z artefaktami.", "evidence": "event_id, source blob, ELF/image hash, PASS i timestamp."}
]}
]
}
}]
},
{"title": "Task01 — Scheduler experiment", "order": 70, "content_kind": "tasks", "task_refs": ["task01"]}
],
"tasks": {
"task01": {
"title": "Kontrolowany eksperyment schedulera FreeRTOS",
"uuid": "2214a13f-34c6-40a1-a561-70a00ec98285",
"prompt_tex": "Przejdź po A1, A2, A4, A5, A6 i A7. Dla kroków RUN użyj Enter lub F2 i udowodnij oddzielnie time slicing A-B-A oraz natychmiastowe wywłaszczenie before-HIGH-after.",
"criterion": "Program wypisuje PASS; trace zawiera A-B-A; marker projection to before-HIGH-after; pierwszy tick ma machine-timer mcause; TCB i stack są spójne.",
"conclusion_tex": "Priorytet wybiera najwyższy Ready task. Time slicing rotuje wyłącznie taski o tym samym priorytecie, a uczynienie wyższego taska Ready może wywłaszczyć caller przed powrotem API.",
"flow": [
{
"kind": "block",
"id": "scheduler-model",
"title": "A — model ticka i wyboru taska",
"content_tex": "Najpierw odczytaj A1, A2 i A4 oraz zapisz przewidywaną kolejność.",
"steps": [
{"id": "read-priorities", "title": "Odczytaj kontrakt priorytetów.", "content_tex": "Wyjaśnij, dlaczego high=0, peery=2, verifier=1 nie powodują przedwczesnego uruchomienia high ani verifiera."},
{"id": "predict-traces", "title": "Przewidź dwa ślady.", "content_tex": "Rozdziel task-change trace A-B-A od marker projection before-HIGH-after."}
]
},
{
"kind": "block",
"id": "hazard3-replay",
"title": "B — deterministyczny replay E01E12",
"content_tex": "Każdy RUN replay zaczyna się od czystej RAM i zweryfikowanego obrazu.",
"steps": [
{"id": "follow-tick", "title": "Zbadaj pierwszy tick.", "content_tex": "Na E06 zapisz mcause, mepc, ISR SP i tick."},
{"id": "follow-timeslice", "title": "Zbadaj A-B-A.", "content_tex": "Na E07 zapisz triplet tasków, ticki i liczbę obserwowanych zmian."},
{"id": "follow-preemption", "title": "Zbadaj natychmiastowe wywłaszczenie.", "content_tex": "Porównaj E08, E09 i E10; pokaż, że HIGH wykonał się przed \\texttt{after\\_raise}."},
{"id": "finish-pass", "title": "Zamknij dowód.", "content_tex": "Na E11 sprawdź self-delete peerów, a na E12 końcowe inwarianty i PASS."}
]
},
{
"kind": "exercise",
"id": "no-timeslicing-bounded-observation",
"title": "Ćwiczenie — bounded observation bez time slicing",
"prompt_tex": "Ustaw \\texttt{configUSE\\_TIME\\_SLICING=0}, nie zmieniając dwóch CPU-bound peerów o priorytecie 2. Uruchom tylko ograniczoną obserwację debuggera; nie oczekuj PASS. Zapisz, który peer pozostaje Running, jaki jest tick count i dlaczego drugi może być głodzony. Następnie przywróć konfigurację.",
"evidence_tex": "Timestampowany snapshot PC/SP, tick count i obu liczników iteracji oraz krótkie wyjaśnienie expected noncompletion.",
"criterion": "Uczeń nie interpretuje braku PASS jako błędu harnessu i poprawnie wiąże głodzenie z brakiem rotacji równych priorytetów.",
"based_on": ["scheduler-model", "hazard3-replay"]
}
],
"educational_requirement_refs": ["FC03.WE01"],
"learning_effect_refs": ["FC03.EN01", "FC03.EK01"],
"assessment_criterion_ref": "FC03.KW01"
}
},
"tasks_order": ["task01"]
}