Rewrite the tutorials as a single pass once the fastcs.demo example controllers exist, sourcing all tutorial code directly from those living modules. Implements #388 §6.
Decisions (settled with Tom, 2026-07-22)
-
Single source of truth = fastcs.demo. Tutorials literalinclude the finished example modules (consolidated into src/fastcs/demo/, mirroring ophyd-async); CI (tox -e docs) keeps them green so docs can't drift.
-
Four tutorials — a hello-world → complicated-device ladder (five example modules; the old "reusable io= object" rung is gone, because io= objects were replaced by getter/setter callables):
-
Finished-example walkthrough pedagogy (not the old 15-diff "Code N" build). Retire docs/snippets/static01–15.py and docs/snippets/dynamic.py.
-
One umbrella docs pass, after the examples land (see Blocked by:).
Scope
- Four tutorial pages replacing the current
static-drivers / dynamic-drivers split; update tutorials.md nav; keep installation.md.
- All code blocks come from the
fastcs.demo modules via literalinclude with region markers (:start-after: / :end-before:). Add the markers to each module as part of writing its tutorial here.
- State the declarative (class-body) / procedural (instance-scope) rule up front, mirroring ophyd-async's
declarative-vs-procedural page.
- Teach the post-refactor API only:
getter/setter callables and @attr (no io= objects, no AttributeIORef, no DataType, no hand-rolled pydantic filler).
- Framing to reinforce: the io= tutorial is replaced by a recommendation in tut 2 to use declarative for families; tut 3's SCPI is not introspectable (static
SCPIParam; SCPIController/SCPIParam in the demo layer, not core); tut 4's backend switch is the lesson.
Key files
docs/tutorials/*, docs/tutorials.md, docs/snippets/* (removed), docs/explanations/, src/fastcs/demo/* (+ its README), docs/conf.py.
Acceptance
uv run --locked tox -e docs green (real CI — docs env doesn't run in the nightly sandbox); every tutorial renders code pulled live from fastcs.demo; no docs/snippets/static*.py remain.
Labels / process
documentation. Deliberately NOT api-convergence — human pedagogy pass, can't verify in the nightly sandbox, depends on the examples; the autonomous nightly job ignores it.
ADR: #388 §6 (0013 declarative/procedural rule)
Parent: #388
Blocked by: #398, #404, #390, #405, #391
Rewrite the tutorials as a single pass once the
fastcs.demoexample controllers exist, sourcing all tutorial code directly from those living modules. Implements #388 §6.Decisions (settled with Tom, 2026-07-22)
Single source of truth =
fastcs.demo. Tutorialsliteralincludethe finished example modules (consolidated intosrc/fastcs/demo/, mirroring ophyd-async); CI (tox -e docs) keeps them green so docs can't drift.Four tutorials — a hello-world → complicated-device ladder (five example modules; the old "reusable
io=object" rung is gone, becauseio=objects were replaced by getter/setter callables):@attrdecoratorhello_world.py(Example 1 — hello-world: pure-soft @attr decorator device #398)getter/settercallables in__init__; closes with "for a real family, go declarative →"temperature_attr.py(Example 2 — attr getter/setter via __init__ (temperature backend) #404)SCPIParam+ filler (static metadata) plus composition — sub-controllers/vector +@scan+@commandon the full temp controllertemperature_scpi.py+scpi.py(Example 4 — SCPI device: annotated attributes + per-attribute filler data #405), drawing oncontrollers.py(Example — composition & methods (temperature controller) #390)eiger.py(Example 5 — cut-down Eiger: introspectable controller + REST sim #391)Finished-example walkthrough pedagogy (not the old 15-diff "Code N" build). Retire
docs/snippets/static01–15.pyanddocs/snippets/dynamic.py.One umbrella docs pass, after the examples land (see
Blocked by:).Scope
static-drivers/dynamic-driverssplit; updatetutorials.mdnav; keepinstallation.md.fastcs.demomodules vialiteralincludewith region markers (:start-after:/:end-before:). Add the markers to each module as part of writing its tutorial here.declarative-vs-proceduralpage.getter/settercallables and@attr(noio=objects, noAttributeIORef, noDataType, no hand-rolled pydantic filler).SCPIParam;SCPIController/SCPIParamin the demo layer, not core); tut 4's backend switch is the lesson.Key files
docs/tutorials/*,docs/tutorials.md,docs/snippets/*(removed),docs/explanations/,src/fastcs/demo/*(+ its README),docs/conf.py.Acceptance
uv run --locked tox -e docsgreen (real CI — docs env doesn't run in the nightly sandbox); every tutorial renders code pulled live fromfastcs.demo; nodocs/snippets/static*.pyremain.Labels / process
documentation. Deliberately NOTapi-convergence— human pedagogy pass, can't verify in the nightly sandbox, depends on the examples; the autonomous nightly job ignores it.ADR: #388 §6 (0013 declarative/procedural rule)
Parent: #388
Blocked by: #398, #404, #390, #405, #391