diff --git a/docs/conduit_filters_bypass.svg b/docs/conduit_filters_bypass.svg
new file mode 100644
index 0000000..aa13003
--- /dev/null
+++ b/docs/conduit_filters_bypass.svg
@@ -0,0 +1 @@
+
diff --git a/docs/conduit_filters_bypass.ymmsl b/docs/conduit_filters_bypass.ymmsl
new file mode 100644
index 0000000..7275f25
--- /dev/null
+++ b/docs/conduit_filters_bypass.ymmsl
@@ -0,0 +1,31 @@
+ymmsl_version: v0.2
+
+description: macro-meso-micro with a bypass conduit needing filters
+
+models:
+ macro_meso_micro_filtered:
+ components:
+ macro:
+ ports:
+ o_i: bc_out bypass_out
+ s: bc_in bypass_in
+ description: ''
+ meso:
+ ports:
+ f_init: init_in
+ o_i: bc_out
+ s: bc_in
+ o_f: final_out
+ description: ''
+ micro:
+ ports:
+ f_init: init_in bypass_in
+ o_f: bypass_out final_out
+ description: ''
+ conduits:
+ macro.bc_out: meso.init_in
+ macro.bypass_out: repeat micro.bypass_in
+ meso.final_out: macro.bc_in
+ meso.bc_out: micro.init_in
+ micro.final_out: meso.bc_in
+ micro.bypass_out: last macro.bypass_in
diff --git a/docs/describing_models.rst b/docs/describing_models.rst
index 531ab77..54afcc2 100644
--- a/docs/describing_models.rst
+++ b/docs/describing_models.rst
@@ -132,6 +132,127 @@ separated by periods. Depending on the context, this may represent a name in a n
or an attribute of an object (as we will see below with Conduits).
+Timelines
+`````````
+
+Different components of a coupled simulation typically run at their own pace: a fast,
+detailed micro model may take many small steps for every single step of the macro model
+driving it, and a meso model may sit somewhere in between the two. yMMSL captures this
+idea of "running at a different pace" as a *timeline*. Wiring a component's ``o_i``/``s``
+ports to another component's ``f_init``/``o_f`` ports puts that other component, and anything
+it in turn drives, on a timeline nested inside the first. yMMSL works this out automatically
+from how components are wired together with conduits.
+
+A component that nobody calls sits on the outermost, root timeline, written ``:``. Every
+level of nesting adds one more name, giving each timeline in the model an addressable
+path, a bit like a folder structure.
+
+Take a macro model that calls a meso model in a loop, and where that meso model in turn
+calls a micro model in its own loop. This produces three levels of timelines: the root
+timeline for ``macro``, a timeline nested inside it for ``meso``, and a timeline nested
+inside *that* for ``micro``:
+
+.. literalinclude:: timelines_macro_meso_micro.ymmsl
+ :caption: ``docs/timelines_macro_meso_micro.ymmsl``
+ :language: yaml
+
+.. figure:: timelines_macro_meso_micro.svg
+ :align: center
+ :alt: macro connects to meso through F_INIT/O_F and O_I/S ports, and meso connects to
+ micro the same way, producing three nested timelines.
+
+ The same model, visualized with `ymmsl2svg
+ `_. The order of the boxes in the figure,
+ from top to bottom, mirrors the nesting in time: ``macro`` first, then ``meso``
+ below it, then ``micro`` below ``meso``.
+
+A single component can also be connected to more than one timeline at once, for example
+when it drives two other components that run at different rates. ``macro`` calling
+``micro1`` in one loop and ``micro2`` in a separate loop puts ``micro1`` and ``micro2`` on
+two independent sub-timelines of ``macro``. The following example shows how you can
+use ``timeline :`` to indicate that the ports connecting to ``micro1`` belong to
+a different subtimeline than the ports connecting to ``micro2``:
+
+.. literalinclude:: timelines_two_subtimelines.ymmsl
+ :caption: ``docs/timelines_two_subtimelines.ymmsl``
+ :language: yaml
+
+.. figure:: timelines_two_subtimelines.svg
+ :align: center
+ :alt: macro has two separate pairs of O_I/S ports, one connecting down to micro1 and
+ one connecting down to micro2, side by side.
+
+ The same model, visualized with `ymmsl2svg
+ `_. ``macro``'s two named timelines are drawn
+ side by side beneath it, each with its own pair of ports, one leading to ``micro1``
+ and the other to ``micro2``.
+
+Matching timelines
+^^^^^^^^^^^^^^^^^^^
+
+The timeline hierarchy above is worked out automatically from how ``f_init``/``o_f``
+and ``o_i``/``s`` ports are wired together, and a conduit can only connect ports whose
+timelines match. Sometimes, though, two components are expected to produce matching
+time points without one being nested inside the other's timeline this way, for example
+two components that call each other directly and are expected to stay in lock-step, or
+a component that adapts its output to another component's timeline, as time bridges do.
+``matching_timelines`` lets you declare such timelines equivalent, so that a conduit can
+still connect ports on them directly:
+
+.. code-block:: yaml
+ :caption: Declaring matching timelines
+
+ components:
+ left:
+ ports:
+ o_i: out
+ s: in
+ description: Left side of the domain
+ right:
+ ports:
+ o_i: out
+ s: in
+ description: Right side of the domain
+
+ matching_timelines:
+ left: right
+
+ conduits:
+ left.out: right.in
+ right.out: left.in
+
+``left`` and ``right`` call each other directly rather than through a shared driver, so
+their O_I and S ports live on their own default timelines, ``:left`` and ``:right``,
+named after the component as usual. A conduit between these ports would therefore not be
+allowed. The entry under ``matching_timelines`` declares ``left`` and ``right``'s
+timelines equivalent, so that the conduits connecting them are valid after all.
+
+On the Python side, ``matching_timelines`` is a list of
+:class:`.ymmsl.v0_2.MatchingTimelines` objects, each representing a set of equivalent
+timelines, with a ``head`` attribute and a ``matches`` attribute holding the full set,
+including the head.
+
+A head can have more than one match, for example if ``left`` is expected to stay in
+lock-step with both ``right`` and ``top``. The matches can then be written as a
+whitespace-separated string:
+
+.. code-block:: yaml
+ :caption: A head with multiple matches
+
+ matching_timelines:
+ left: right top
+
+or, equivalently, as a YAML list:
+
+.. code-block:: yaml
+ :caption: The same, as a YAML list
+
+ matching_timelines:
+ left:
+ - right
+ - top
+
+
Conduits
````````
@@ -186,6 +307,63 @@ sender:
print(conduits[0]) # output: Conduit(sender.port -> receiver1.port)
print(conduits[1]) # output: Conduit(sender.port -> receiver2.port)
+Conduit filters
+^^^^^^^^^^^^^^^
+
+A conduit connects two components that call each other directly, for example ``macro``
+and ``meso``, or ``meso`` and ``micro``. ``macro`` and ``micro`` are not directly
+connected in this sense: ``meso`` sits between them. Connecting ``macro`` and ``micro``
+directly, bypassing ``meso``, means their pace no longer matches: ``micro`` is still
+called many times for every step ``macro`` takes, and still produces a message on
+every one of those calls, even though there is no longer a ``meso`` in between to
+absorb the difference. A conduit filter reconciles that mismatch.
+
+Extending the macro-meso-micro example from :ref:`Timelines` with a conduit that
+bypasses ``meso`` to connect ``macro`` and ``micro`` directly shows both filters in
+use:
+
+.. literalinclude:: conduit_filters_bypass.ymmsl
+ :caption: ``docs/conduit_filters_bypass.ymmsl``
+ :language: yaml
+
+.. figure:: conduit_filters_bypass.svg
+ :align: center
+ :alt: macro and micro have an extra pair of ports directly connecting them,
+ bypassing meso, labeled "repeat" and "last".
+
+ The same model, visualized with `ymmsl2svg
+ `_.
+
+``macro`` produces the ``bypass_out`` message once, but ``micro`` is called many times
+for every step of ``macro`` and needs the message on each of those calls. The conduit
+from ``macro.bypass_out`` to ``micro.bypass_in`` uses a ``repeat`` filter for this: the
+single message ``macro`` sends is resent to ``micro`` every time it runs, without
+``meso`` having to relay it.
+
+The reverse happens on the way back: ``micro`` produces a ``bypass_out`` message on
+every one of its many runs, but ``macro`` still expects only one message per call. The
+conduit from ``micro.bypass_out`` to ``macro.bypass_in`` uses a ``last`` filter to
+reduce those many messages down to the single most recently produced one.
+
+- ``repeat`` and ``pad`` go from the shallower side to the deeper one: a single message
+ is repeated, or followed by empty messages, to match every time the deeper side
+ receives.
+- ``last`` goes from the deeper side back to the shallower one: of the many messages
+ produced, only the last one is passed on.
+
+A conduit between two components that call each other directly doesn't skip anything,
+so it can't take a filter at all.
+
+Filters are written in front of the receiver and may be combined:
+
+.. code-block:: yaml
+ :caption: Specifying conduit filters in yMMSL
+
+ conduits:
+ macro.init_out: repeat micro.init_in
+ micro.state_out: last macro.final_in
+
+
Nesting models
``````````````
diff --git a/docs/timelines_macro_meso_micro.svg b/docs/timelines_macro_meso_micro.svg
new file mode 100644
index 0000000..f19f6b6
--- /dev/null
+++ b/docs/timelines_macro_meso_micro.svg
@@ -0,0 +1 @@
+
diff --git a/docs/timelines_macro_meso_micro.ymmsl b/docs/timelines_macro_meso_micro.ymmsl
new file mode 100644
index 0000000..f815651
--- /dev/null
+++ b/docs/timelines_macro_meso_micro.ymmsl
@@ -0,0 +1,29 @@
+ymmsl_version: v0.2
+
+description: A macro-meso-micro model, three levels of timelines
+
+models:
+ macro_meso_micro_model:
+ components:
+ macro:
+ ports:
+ o_i: bc_out
+ s: bc_in
+ description: ''
+ meso:
+ ports:
+ f_init: init_in
+ o_i: bc_out
+ s: bc_in
+ o_f: final_out
+ description: ''
+ micro:
+ ports:
+ f_init: init_in
+ o_f: final_out
+ description: ''
+ conduits:
+ macro.bc_out: meso.init_in
+ meso.final_out: macro.bc_in
+ meso.bc_out: micro.init_in
+ micro.final_out: meso.bc_in
diff --git a/docs/timelines_two_subtimelines.svg b/docs/timelines_two_subtimelines.svg
new file mode 100644
index 0000000..4126c23
--- /dev/null
+++ b/docs/timelines_two_subtimelines.svg
@@ -0,0 +1 @@
+
diff --git a/docs/timelines_two_subtimelines.ymmsl b/docs/timelines_two_subtimelines.ymmsl
new file mode 100644
index 0000000..221da3d
--- /dev/null
+++ b/docs/timelines_two_subtimelines.ymmsl
@@ -0,0 +1,31 @@
+ymmsl_version: v0.2
+
+description: One component (macro) connected to two independent timelines
+
+models:
+ two_subtimelines_model:
+ components:
+ macro:
+ ports:
+ timeline tl1:
+ o_i: micro1_out
+ s: micro1_in
+ timeline tl2:
+ o_i: micro2_out
+ s: micro2_in
+ description: ''
+ micro1:
+ ports:
+ f_init: init_in
+ o_f: final_out
+ description: ''
+ micro2:
+ ports:
+ f_init: init_in
+ o_f: final_out
+ description: ''
+ conduits:
+ macro.micro1_out: micro1.init_in
+ micro1.final_out: macro.micro1_in
+ macro.micro2_out: micro2.init_in
+ micro2.final_out: macro.micro2_in