Data is data. Code is logic. So the the thought process goes...

...what if I could store my configuration (data! right?!) in a way that is nicely separated from the logic?! No more scripting for me!

Oh wait. Some of the configuration can't be generalized about universally. Configurations fundamentally contain logic, I guess.

Oh, well then why don't I just represent the logic in the data?! That will be much better than representing the data in the logic!

....but now, you are back where you started, only instead of using something nice, standard, and powerful like Python, you have to use this... language... thing. This YAML convention you cooked up.

Separate the data from logic. Read the data into the logic. Make a nice organized place to call custom logic from... like, you know, a file directory full of scripts, which are called according to some scheme. It could be another data file. Stop there.

Like this:

    $ ls -Ra
    ./config/do_something.yaml
    ./config/do_something_else.yaml
    ./config/config.yaml
    ./logs/ping.log
    ./scripts/do_something.py
    ./scripts/do_something_else.py
---

    $ cat ./config/config.yaml

    do_something:
        target: my.stupid.server
        exec_frequency: daily
        ...
    do_something_else:
        target: my.stupid.server
        exec_frequency: monthly
        ...
---

    $ cat ./config/do_something.yaml

    ping: true
    output: 
       directory: ./logs/ping.log
       mode: append
---

    $ cat ./config/do_something_else.yaml

    delete: ./logs/ping.log
---

    $ cat ./scripts/do_something.py

    from stupid.library.task import Task, subscribe

    class DoSomething(Task):

        @subscribe
        def ping(self, target):
            return super().ping(target)
Then you code the program that runs collects all of the task methods, put them in an ordered list, and run them if the conditions in the config.yaml is met.

Every other thing is done in a task method in python, or something like it. I don't think we need more abstraction than that.

This is more or less the structure I followed with this testing framework: https://github.com/hitchdev/hitchstory

The whole idea being that you don't want the story to be turing complete (there are no loops or conditionals with a story), but the code that executes it needs to be turing complete.