Repository: mitogen-hq/mitogen (language: python)

Issue:
mitogen.utils.cast() can silently fail to cast string subtypes (e.g. AnsibleUnsafeText)
`mitogen.utils.cast(v)` is intended to recursively replace subtypes of builtins in `v`. In versions of Ansible that have addressed [CVE-2023-5764](https://github.com/advisories/GHSA-7j69-qfc3-2fq9) it silently fails to do so for `ansible.utils.unsafe_proxy.AnsibleUnsafeText()` et al. The cause is that `cast()` calls `str()` on the value, and in these versions of Ansible `AnsibleUnsafeText()` overrides `__str__()` to return itself.

This was one of the root causes of #221, but this was obscured by the lack of an immediate warning or error.

Further discussion/investigation in https://github.com/mitogen-hq/mitogen/wiki/AnsibleUnsafe-notes

Fix/mitigations in progress.

```pycon
>>> import ansible, ansible.utils.unsafe_proxy, mitogen.utils
>>> ansible.__version__
'2.14.14'
>>> unsafe_text = ansible.utils.unsafe_proxy.wrap_var('abc')
>>> type(unsafe_text)
<class 'ansible.utils.unsafe_proxy.AnsibleUnsafeText'>
>>> type(mitogen.utils.cast(unsafe_text))
<class 'ansible.utils.unsafe_proxy.AnsibleUnsafeText'>
```


Discussion / hints:

Fix patch:
diff --git a/.ci/azure-pipelines.yml b/.ci/azure-pipelines.yml
index 468fd5bf9..94919e350 100644
--- a/.ci/azure-pipelines.yml
+++ b/.ci/azure-pipelines.yml
@@ -8,6 +8,13 @@
 #variables:
   #ANSIBLE_VERBOSITY: 3
 
+trigger:
+  branches:
+    include:
+      - "*"
+    exclude:
+      - docs-master
+
 jobs:
 - job: mac11
   # vanilla Ansible is really slow
@@ -27,15 +34,15 @@ jobs:
 
       Loc_27_210:
         tox.env: py27-mode_localhost-ansible2.10
-      Loc_312_7:
+      Loc_312_9:
         python.version: '3.12'
-        tox.env: py312-mode_localhost-ansible7
+        tox.env: py312-mode_localhost-ansible9
 
       Van_27_210:
         tox.env: py27-mode_localhost-ansible2.10-strategy_linear
-      Van_312_7:
+      Van_312_9:
         python.version: '3.12'
-        tox.env: py312-mode_localhost-ansible7-strategy_linear
+        tox.env: py312-mode_localhost-ansible9-strategy_linear
 
 - job: Linux
   pool:
@@ -150,3 +157,9 @@ jobs:
       Ans_312_7:
         python.version: '3.12'
         tox.env: py312-mode_ansible-ansible7
+      Ans_312_8:
+        python.version: '3.12'
+        tox.env: py312-mode_ansible-ansible8
+      Ans_312_9:
+        python.version: '3.12'
+        tox.env: py312-mode_ansible-ansible9
diff --git a/ansible_mitogen/loaders.py b/ansible_mitogen/loaders.py
index 754ec6cba..9729b8a13 100644
--- a/ansible_mitogen/loaders.py
+++ b/ansible_mitogen/loaders.py
@@ -49,7 +49,7 @@
 
 
 ANSIBLE_VERSION_MIN = (2, 10)
-ANSIBLE_VERSION_MAX = (2, 14)
+ANSIBLE_VERSION_MAX = (2, 16)
 
 NEW_VERSION_MSG = (
     "Your Ansible version (%s) is too recent. The most recent version\n"
diff --git a/docs/ansible_detailed.rst b/docs/ansible_detailed.rst
index 09db3cd86..b395bc25e 100644
--- a/docs/ansible_detailed.rst
+++ b/docs/ansible_detailed.rst
@@ -148,9 +148,25 @@ Noteworthy Differences
 * Mitogen 0.2.x supports Ansible 2.3-2.9; with Python 2.6, 2.7, or 3.6.
   Mitogen 0.3.1+ supports
 
-    - Ansible 2.10, 3, and 4; with Python 2.7, or 3.6-3.11
-    - Ansible 5; with Python 3.8-3.11
-    - Ansible 6 and 7; with Python 3.8-3.12
+  +-----------------+-----------------+
+  | Ansible version | Python versions |
+  +=================+=================+
+  | 2.10            |                 |
+  +-----------------+                 |
+  | 3               | 2.7, 3.6 - 3.11 |
+  +-----------------+                 |
+  | 4               |                 |
+  +-----------------+-----------------+
+  | 5               | 3.8 - 3.11      |
+  +-----------------+-----------------+
+  | 6               |                 |
+  +-----------------+ 3.8 - 3.12      |
+  | 7               |                 |
+  +-----------------+-----------------+
+  | 8               | 3.9 - 3.12      |
+  +-----------------+-----------------+
+  | 9               | 3.10 - 3.12     |
+  +-----------------+-----------------+
 
   Verify your installation is running one of these versions by checking
   ``ansible --version`` output.
diff --git a/docs/changelog.rst b/docs/changelog.rst
index b2c8682dc..0b8eeb203 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -17,6 +17,15 @@ Release Notes
 To avail of fixes in an unreleased version, please download a ZIP file
 `directly from GitHub <https://github.com/mitogen-hq/mitogen/>`_.
 
+
+v0.3.7 (2024-04-08)
+-------------------
+
+* :gh:issue:`1021` Support for Ansible 8 (ansible-core 2.15)
+* tests: Replace uses of ``include:`` & ``import:``, unsupported in Ansible 9
+* :gh:issue:`1053` Support for Ansible 9 (ansible-core 2.16)
+
+
 v0.3.6 (2024-04-04)
 -------------------
 
diff --git a/docs/conf.py b/docs/conf.py
index 0e201d44b..ad9771b46 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -2,7 +2,7 @@
 
 sys.path.append('.')
 
-VERSION = '0.3.6'
+VERSION = '0.3.7'
 
 author = u'Network Genomics'
 copyright = u'2021, the Mitogen authors'
diff --git a/mitogen/__init__.py b/mitogen/__init__.py
index dd85245ce..d72df040a 100644
--- a/mitogen/__init__.py
+++ b/mitogen/__init__.py
@@ -35,7 +35,7 @@
 
 
 #: Library version as a tuple.
-__version__ = (0, 3, 6)
+__version__ = (0, 3, 7)
 
 
 #: This is :data:`False` in slave contexts. Previously it was used to prevent
diff --git a/scripts/pogrep.py b/scripts/pogrep.py
index b837bcfd6..80094c74f 100644
--- a/scripts/pogrep.py
+++ b/scripts/pogrep.py
@@ -6,6 +6,7 @@
 #   - apt-get source libpam0g
 #   - cd */po/
 #   - python ~/pogrep.py "Password: "
+from __future__ import print_function
 
 import sys
 import shlex
@@ -31,7 +32,7 @@
         if last_word == 'msgid' and word == 'msgstr':
             if last_rest == sys.argv[1]:
                 thing = rest.rstrip(': ').decode('utf-8').lower().encode('utf-8').encode('base64').rstrip()
-                print '    %-60s # %s' % (repr(thing)+',', path)
+                print('    %-60s # %s' % (repr(thing)+',', path))
 
         last_word = word
         last_rest = rest
diff --git a/tox.ini b/tox.ini
index f2b464136..696392333 100644
--- a/tox.ini
+++ b/tox.ini
@@ -11,7 +11,7 @@
 # 2.5                        <= 3.7.1   <= 1.4.22             <= 1.3.1   <= 2.1.3   <= 2.8.7   <= 1.6.1   <= 1.9.1
 # 2.6  <= 2.6.20  <= 2.12    <= 4.5.4   <= 1.6.11  <= 2.10.3  <= 9.0.3   <= 5.9.0   <= 3.2.5   <= 2.9.1   <= 15.2.0
 # 2.7  <= 2.11               <= 5.5     <= 1.11.29 <= 2.11.3  <= 20                 <= 4.6.11  <= 3.28    <= 20.15²
-# 3.5  <= 2.11    <= 2.13    <= 5.5     <= 2.2.28  <= 2.11.3  <= 20      <= 5.9.5   <= 6.1.0   <= 3.28    <= 20.15²
+# 3.5  <= 2.11    <= 2.15    <= 5.5     <= 2.2.28  <= 2.11.3  <= 20      <= 5.9.5   <= 6.1.0   <= 3.28    <= 20.15²
 # 3.6  <= 2.11               <= 6.2     <= 3.2.20  <= 3.0.3   <= 21                 <= 7.0.1   <= 3.28    <= 20.17²
 # 3.7  <= 2.12               <= 7.2.7   <= 3.2.20                                   <= 7.4.4   <= 4.8.0
 # 3.8  <= 2.12
@@ -57,7 +57,7 @@ envlist =
     init,
     py{27,36}-mode_ansible-ansible{2.10,3,4},
     py{311}-mode_ansible-ansible{2.10,3,4,5},
-    py{312}-mode_ansible-ansible{6,7},
+    py{312}-mode_ansible-ansible{6,7,8,9},
     py{27,36,312}-mode_mitogen-distro_centos{6,7,8},
     py{27,36,312}-mode_mitogen-distro_debian{9,10,11},
     py{27,36,312}-mode_mitogen-distro_ubuntu{1604,1804,2004},
@@ -84,6 +84,8 @@ deps =
     ansible5: ansible~=5.0
     ansible6: ansible~=6.0
     ansible7: ansible~=7.0
+    ansible8: ansible~=8.0
+    ansible9: ansible~=9.0
 install_command =
     python -m pip --no-python-version-warning --disable-pip-version-check install {opts} {packages}
 commands_pre =
@@ -119,9 +121,12 @@ setenv =
     distro_ubuntu1804: DISTRO=ubuntu1804
     distro_ubuntu2004: DISTRO=ubuntu2004
     # Note the plural, only applicable to MODE=ansible
-    # Ansible >= 6 (ansible-core >= 2.13) require Python 2.7 or >= 3.5 on targets
+    # Ansible 6 - 8 (ansible-core 2.13 - 2.15) require Python 2.7 or >= 3.5 on targets
     ansible6: DISTROS=centos7 centos8 debian9 debian10 debian11 ubuntu1604 ubuntu1804 ubuntu2004
     ansible7: DISTROS=centos7 centos8 debian9 debian10 debian11 ubuntu1604 ubuntu1804 ubuntu2004
+    ansible8: DISTROS=centos7 centos8 debian9 debian10 debian11 ubuntu1604 ubuntu1804 ubuntu2004
+    # Ansible >= 9 (ansible-core >= 2.16) require Python 2.7 or >= 3.6 on targets
+    ansible9: DISTROS=centos7 centos8 debian9 debian10 debian11 ubuntu1804 ubuntu2004
     distros_centos: DISTROS=centos6 centos7 centos8
     distros_centos5: DISTROS=centos5
     distros_centos6: DISTROS=centos6


Test patch:
diff --git a/tests/ansible/integration/action/make_tmp_path.yml b/tests/ansible/integration/action/make_tmp_path.yml
index 8e9812506..1f710c5ac 100644
--- a/tests/ansible/integration/action/make_tmp_path.yml
+++ b/tests/ansible/integration/action/make_tmp_path.yml
@@ -10,8 +10,7 @@
 - name: integration/action/make_tmp_path.yml
   hosts: test-targets
   tasks:
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     #
     # non-root
@@ -161,3 +160,4 @@
         fail_msg: out={{out}}
   tags:
     - make_tmp_path
+    - mitogen_only
diff --git a/tests/ansible/integration/async/runner_timeout_then_polling.yml b/tests/ansible/integration/async/runner_timeout_then_polling.yml
index 2f71ebe42..e3aa5d37d 100644
--- a/tests/ansible/integration/async/runner_timeout_then_polling.yml
+++ b/tests/ansible/integration/async/runner_timeout_then_polling.yml
@@ -3,6 +3,7 @@
 - name: integration/async/runner_timeout_then_polling.yml
   hosts: test-targets
   tasks:
+  - include_tasks: ../_mitogen_only.yml
 
   # Verify async-with-timeout-then-poll behaviour.
   # This is semi-broken in upstream Ansible, it does not bother to update the
@@ -13,7 +14,6 @@
     async: 1
     poll: 0
     register: job
-    when: is_mitogen
 
   - name: busy-poll up to 500 times
     async_status:
@@ -22,7 +22,6 @@
     until: result.finished
     retries: 500
     delay: 0
-    when: is_mitogen
     ignore_errors: true
 
   - assert:
@@ -31,6 +30,6 @@
         - result.finished == 1
         - result.msg == "Job reached maximum time limit of 1 seconds."
       fail_msg: result={{result}}
-    when: is_mitogen
   tags:
+    - mitogen_only
     - runner_timeout_then_polling
diff --git a/tests/ansible/integration/become/sudo_requiretty.yml b/tests/ansible/integration/become/sudo_requiretty.yml
index 19b49b285..20f106fc6 100644
--- a/tests/ansible/integration/become/sudo_requiretty.yml
+++ b/tests/ansible/integration/become/sudo_requiretty.yml
@@ -3,6 +3,7 @@
 - name: integration/become/sudo_requiretty.yml
   hosts: test-targets
   tasks:
+    # - include_tasks: ../_mitogen_only.yml
 
     # TODO: https://github.com/dw/mitogen/issues/692
     # - name: Verify we can login to a non-passworded requiretty account
@@ -10,12 +11,10 @@
     #   become: true
     #   become_user: mitogen__require_tty
     #   register: out
-    #   when: is_mitogen
 
     # - assert:
     #     that:
     #       - out.stdout == 'mitogen__require_tty'
-    #   when: is_mitogen
 
 
     # ---------------
@@ -28,12 +27,11 @@
     #   vars:
     #     ansible_become_pass: require_tty_pw_required_password
     #   register: out
-    #   when: is_mitogen
 
     # - assert:
     #     that:
     #       - out.stdout == 'mitogen__require_tty_pw_required'
-    #   when: is_mitogen
   tags:
+    - mitogen_only
     - sudo
     - sudo_requiretty
diff --git a/tests/ansible/integration/connection/become_same_user.yml b/tests/ansible/integration/connection/become_same_user.yml
index 5ff4f95b3..f1b7a0fc6 100644
--- a/tests/ansible/integration/connection/become_same_user.yml
+++ b/tests/ansible/integration/connection/become_same_user.yml
@@ -5,13 +5,13 @@
   hosts: bsu-joe
   gather_facts: no
   tasks:
+  - include_tasks: ../_mitogen_only.yml
 
   # bsu-joe's login user is joe, so become should be ignored.
   - mitogen_get_stack:
     become: true
     become_user: joe
     register: out
-    when: is_mitogen
 
   - assert:
       that:
@@ -19,7 +19,6 @@
       - out.result[0].kwargs.username == "joe"
       - out.result|length == 1  # no sudo
       fail_msg: out={{out}}
-    when: is_mitogen
 
 
   # Now try with a different account.
@@ -27,7 +26,6 @@
     become: true
     become_user: james
     register: out
-    when: is_mitogen
 
   - assert:
       that:
@@ -37,6 +35,6 @@
       - out.result[1].kwargs.username == "james"
       - out.result|length == 2  # no sudo
       fail_msg: out={{out}}
-    when: is_mitogen
   tags:
     - become_same_user
+    - mitogen_only
diff --git a/tests/ansible/integration/connection/disconnect_during_module.yml b/tests/ansible/integration/connection/disconnect_during_module.yml
index 5c74b0ed8..926291dfe 100644
--- a/tests/ansible/integration/connection/disconnect_during_module.yml
+++ b/tests/ansible/integration/connection/disconnect_during_module.yml
@@ -6,8 +6,7 @@
   gather_facts: no
   any_errors_fatal: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - name: Run _disconnect_during_module.yml
     delegate_to: localhost
@@ -32,3 +31,4 @@
   tags:
     - disconnect
     - disconnect_during_module
+    - mitogen_only
diff --git a/tests/ansible/integration/connection/disconnect_resets_connection.yml b/tests/ansible/integration/connection/disconnect_resets_connection.yml
index 95f969106..d9879a71f 100644
--- a/tests/ansible/integration/connection/disconnect_resets_connection.yml
+++ b/tests/ansible/integration/connection/disconnect_resets_connection.yml
@@ -13,8 +13,7 @@
   hosts: test-targets
   gather_facts: no
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - mitogen_action_script:
       script: |
@@ -47,3 +46,4 @@
   tags:
     - disconnect
     - disconnect_resets_connection
+    - mitogen_only
diff --git a/tests/ansible/integration/connection/home_dir.yml b/tests/ansible/integration/connection/home_dir.yml
index d41657bcb..07ce9dd1e 100644
--- a/tests/ansible/integration/connection/home_dir.yml
+++ b/tests/ansible/integration/connection/home_dir.yml
@@ -3,18 +3,18 @@
 - name: integration/connection/home_dir.yml
   hosts: test-targets
   tasks:
+    - include_tasks: ../_mitogen_only.yml
+
     - name: "Find out root's homedir."
       # Runs first because it blats regular Ansible facts with junk, so
       # non-become run fixes that up.
       setup:
       become: true
       register: root_facts
-      when: is_mitogen
 
     - name: "Find regular homedir"
       setup:
       register: user_facts
-      when: is_mitogen
 
     - name: "Verify Connection.homedir correct when become:false"
       mitogen_action_script:
@@ -24,7 +24,6 @@
             "connection homedir": self._connection.homedir,
             "homedir from facts": "{{user_facts.ansible_facts.ansible_user_dir}}"
           }
-      when: is_mitogen
 
     - name: "Verify Connection.homedir correct when become:true"
       become: true
@@ -35,6 +34,6 @@
             "connection homedir": self._connection.homedir,
             "homedir from facts": "{{root_facts.ansible_facts.ansible_user_dir}}"
           }
-      when: is_mitogen
   tags:
     - home_dir
+    - mitogen_only
diff --git a/tests/ansible/integration/connection/put_large_file.yml b/tests/ansible/integration/connection/put_large_file.yml
index ce8f8fe06..c70e033d2 100644
--- a/tests/ansible/integration/connection/put_large_file.yml
+++ b/tests/ansible/integration/connection/put_large_file.yml
@@ -8,7 +8,7 @@
     file_name: large-file
     file_size: 512
   tasks:
-  - include: _put_file.yml
+    - include_tasks: _put_file.yml
   tags:
     - put_file
     - put_large_file
diff --git a/tests/ansible/integration/connection/put_small_file.yml b/tests/ansible/integration/connection/put_small_file.yml
index 3e0eaaa9f..2dc100a1f 100644
--- a/tests/ansible/integration/connection/put_small_file.yml
+++ b/tests/ansible/integration/connection/put_small_file.yml
@@ -8,7 +8,7 @@
     file_name: small-file
     file_size: 123
   tasks:
-  - include: _put_file.yml
+    - include_tasks: _put_file.yml
   tags:
     - put_file
     - put_small_file
diff --git a/tests/ansible/integration/connection/reset.yml b/tests/ansible/integration/connection/reset.yml
index 1ee38817f..f3ddb2c41 100644
--- a/tests/ansible/integration/connection/reset.yml
+++ b/tests/ansible/integration/connection/reset.yml
@@ -6,8 +6,7 @@
 - name: integration/connection/reset.yml
   hosts: test-targets
   tasks:
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - debug: msg="reset.yml skipped on Ansible<2.5.6"
       when:
@@ -47,4 +46,5 @@
         - out_become.ppid != out_become2.ppid
         fail_msg: out={{out}} out2={{out2}} out_become={{out_become}} out_become2={{out_become2}}
   tags:
+    - mitogen_only
     - reset
diff --git a/tests/ansible/integration/connection_delegation/delegate_to_template.yml b/tests/ansible/integration/connection_delegation/delegate_to_template.yml
index 7d33a1619..0c3f35c24 100644
--- a/tests/ansible/integration/connection_delegation/delegate_to_template.yml
+++ b/tests/ansible/integration/connection_delegation/delegate_to_template.yml
@@ -15,8 +15,7 @@
   hosts: test-targets
   gather_facts: no
   tasks:
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - meta: end_play
       when:
@@ -88,3 +87,4 @@
         ]
   tags:
     - delegate_to_template
+    - mitogen_only
diff --git a/tests/ansible/integration/connection_delegation/local_action.yml b/tests/ansible/integration/connection_delegation/local_action.yml
index 6176d770c..1f5cd7671 100644
--- a/tests/ansible/integration/connection_delegation/local_action.yml
+++ b/tests/ansible/integration/connection_delegation/local_action.yml
@@ -3,8 +3,7 @@
 - name: integration/connection_delegation/local_action.yml
   hosts: cd-newuser-normal-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - local_action: mitogen_get_stack
       become: true
@@ -35,3 +34,4 @@
         ]
   tags:
     - local_action
+    - mitogen_only
diff --git a/tests/ansible/integration/connection_delegation/osa_container_standalone.yml b/tests/ansible/integration/connection_delegation/osa_container_standalone.yml
index 249ab31ab..98788d396 100644
--- a/tests/ansible/integration/connection_delegation/osa_container_standalone.yml
+++ b/tests/ansible/integration/connection_delegation/osa_container_standalone.yml
@@ -4,8 +4,7 @@
   hosts: dtc-container-1
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - mitogen_get_stack:
     register: out
@@ -28,5 +27,6 @@
         },
       ]
   tags:
+    - mitogen_only
     - osa
     - osa_container_standalone
diff --git a/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml b/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml
index 4598e9897..f97d1c01c 100644
--- a/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml
+++ b/tests/ansible/integration/connection_delegation/osa_delegate_to_self.yml
@@ -6,8 +6,7 @@
     target: osa-container-1
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - mitogen_get_stack:
     delegate_to: "{{target}}"
@@ -31,5 +30,6 @@
         },
       ]
   tags:
+    - mitogen_only
     - osa
     - osa_delegate_to_self
diff --git a/tests/ansible/integration/connection_delegation/stack_construction.yml b/tests/ansible/integration/connection_delegation/stack_construction.yml
index 279a7b2b1..784d2af20 100644
--- a/tests/ansible/integration/connection_delegation/stack_construction.yml
+++ b/tests/ansible/integration/connection_delegation/stack_construction.yml
@@ -19,9 +19,7 @@
 - name: integration/connection_delegation/stack_construction.yml
   hosts: cd-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     # used later for local_action test.
     - local_action: custom_python_detect_environment
       register: local_env
@@ -31,9 +29,7 @@
 
 - hosts: cd-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -52,14 +48,13 @@
           }
         ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       delegate_to: cd-alias
       register: out
@@ -96,14 +91,13 @@
               },
           ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-alias
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -139,14 +133,13 @@
               },
           ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-normal-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -193,14 +186,13 @@
               },
           ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-normal-alias
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -269,9 +261,7 @@
 
 - hosts: cd-newuser-normal-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -318,14 +308,13 @@
               },
           ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-newuser-normal-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       delegate_to: cd-alias
       register: out
@@ -362,14 +351,13 @@
               },
           ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-newuser-normal-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - local_action: mitogen_get_stack
       register: out
     - assert_equal:
@@ -383,14 +371,13 @@
           },
         ]
   tags:
+    - mitogen_only
     - stack_construction
 
 
 - hosts: cd-newuser-doas-normal
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
+    - include_tasks: ../_mitogen_only.yml
     - mitogen_get_stack:
       register: out
     - assert_equal:
@@ -420,4 +407,5 @@
           },
         ]
   tags:
+    - mitogen_only
     - stack_construction
diff --git a/tests/ansible/integration/context_service/disconnect_cleanup.yml b/tests/ansible/integration/context_service/disconnect_cleanup.yml
index 2dd10b2cb..22ba12eba 100644
--- a/tests/ansible/integration/context_service/disconnect_cleanup.yml
+++ b/tests/ansible/integration/context_service/disconnect_cleanup.yml
@@ -4,8 +4,7 @@
 - name: integration/context_service/disconnect_cleanup.yml
   hosts: test-targets[0]
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - meta: end_play
     when:
@@ -50,3 +49,4 @@
   #     that: out.dump|length == play_hosts|length # just the ssh account
   tags:
     - disconnect_cleanup
+    - mitogen_only
diff --git a/tests/ansible/integration/context_service/remote_name.yml b/tests/ansible/integration/context_service/remote_name.yml
index 79fb8dead..545d64b3d 100644
--- a/tests/ansible/integration/context_service/remote_name.yml
+++ b/tests/ansible/integration/context_service/remote_name.yml
@@ -3,8 +3,7 @@
 - name: integration/context_service/remote_name.yml
   hosts: test-targets[0]
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   # Too much hassle to make this work for OSX
   - meta: end_play
@@ -32,4 +31,5 @@
       - out.stdout is match('.*python([0-9.]+)?\(mitogen:ansible\)')
       fail_msg: out={{out}}
   tags:
+    - mitogen_only
     - remote_name
diff --git a/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml b/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml
index 0d7cf1b61..279735c9f 100644
--- a/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml
+++ b/tests/ansible/integration/runner/custom_python_new_style_missing_interpreter.yml
@@ -4,8 +4,7 @@
   tasks:
     # FIXME Without Mitogen Ansible often reads stdin before the module.
     #       Either don't read directly from stdin, or figure out the cause.
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - custom_python_new_style_missing_interpreter:
         foo: true
@@ -22,3 +21,4 @@
         fail_msg: out={{out}}
   tags:
     - custom_python_new_style_module
+    - mitogen_only
diff --git a/tests/ansible/integration/runner/custom_python_new_style_module.yml b/tests/ansible/integration/runner/custom_python_new_style_module.yml
index 8435b1580..332072828 100644
--- a/tests/ansible/integration/runner/custom_python_new_style_module.yml
+++ b/tests/ansible/integration/runner/custom_python_new_style_module.yml
@@ -3,8 +3,7 @@
   tasks:
     # FIXME Without Mitogen Ansible often reads stdin before the module.
     #       Either don't read directly from stdin, or figure out the cause.
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - custom_python_new_style_module:
         foo: true
@@ -30,3 +29,4 @@
         fail_msg: out={{out}}
   tags:
     - custom_python_new_style_module
+    - mitogen_only
diff --git a/tests/ansible/integration/runner/custom_python_prehistoric_module.yml b/tests/ansible/integration/runner/custom_python_prehistoric_module.yml
index ebe34cc8b..4ccdc9d52 100644
--- a/tests/ansible/integration/runner/custom_python_prehistoric_module.yml
+++ b/tests/ansible/integration/runner/custom_python_prehistoric_module.yml
@@ -7,13 +7,14 @@
 - name: integration/runner/custom_python_prehistoric_module.yml
   hosts: test-targets
   tasks:
+    - include_tasks: ../_mitogen_only.yml
+
     - custom_python_prehistoric_module:
       register: out
-      when: is_mitogen
 
     - assert:
         that: out.ok
         fail_msg: out={{out}}
-      when: is_mitogen
   tags:
     - custom_python_prehistoric_module
+    - mitogen_only
diff --git a/tests/ansible/integration/runner/etc_environment.yml b/tests/ansible/integration/runner/etc_environment.yml
index 6ed3e9c26..79e5bfa65 100644
--- a/tests/ansible/integration/runner/etc_environment.yml
+++ b/tests/ansible/integration/runner/etc_environment.yml
@@ -6,7 +6,7 @@
   hosts: test-targets[0]
   gather_facts: true
   tasks:
-    - include: _etc_environment_user.yml
+    - include_tasks: _etc_environment_user.yml
       when: ansible_system == "Linux" and is_mitogen
 
     - include_tasks: _etc_environment_global.yml
diff --git a/tests/ansible/integration/runner/forking_active.yml b/tests/ansible/integration/runner/forking_active.yml
index 5198ee1c6..97ffcc699 100644
--- a/tests/ansible/integration/runner/forking_active.yml
+++ b/tests/ansible/integration/runner/forking_active.yml
@@ -1,6 +1,7 @@
 - name: integration/runner/forking_active.yml
   hosts: test-targets
   tasks:
+  - include_tasks: ../_mitogen_only.yml
 
   # Verify mitogen_task_isolation=fork triggers forking.
 
@@ -13,21 +14,19 @@
     register: fork_proc1
     vars:
       mitogen_task_isolation: fork
-    when: is_mitogen
 
   - name: get force-forked process ID again.
     custom_python_detect_environment:
     register: fork_proc2
     vars:
       mitogen_task_isolation: fork
-    when: is_mitogen
 
   - assert:
       that:
       - fork_proc1.pid != sync_proc1.pid
       - fork_proc1.pid != fork_proc2.pid
       fail_msg: fork_proc1={{fork_proc1}} sync_proc1={{sync_proc1}} fork_proc2={{fork_proc2}}
-    when: is_mitogen
 
   tags:
     - forking_active
+    - mitogen_only
diff --git a/tests/ansible/integration/runner/forking_correct_parent.yml b/tests/ansible/integration/runner/forking_correct_parent.yml
index 8f360bb74..8732627af 100644
--- a/tests/ansible/integration/runner/forking_correct_parent.yml
+++ b/tests/ansible/integration/runner/forking_correct_parent.yml
@@ -2,6 +2,7 @@
 - name: integration/runner/forking_correct_parent.yml
   hosts: test-targets
   tasks:
+  - include_tasks: ../_mitogen_only.yml
 
   # Verify mitogen_task_isolation=fork forks from "virginal fork parent", not
   # shared interpreter, but only if forking is enabled (e.g. that's never true
@@ -14,35 +15,34 @@
           self._connection.init_child_result['fork_context'] is not None
         )
     register: forkmode
-    when: is_mitogen
 
   - name: get regular process ID.
     custom_python_detect_environment:
     register: regular_proc
-    when: is_mitogen
 
   - name: get force-forked process ID again.
     custom_python_detect_environment:
     register: fork_proc
     vars:
       mitogen_task_isolation: fork
-    when: is_mitogen
 
   - assert:
       that:
       - fork_proc.pid != regular_proc.pid
       fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}}
-    when: is_mitogen
 
   - assert:
       that: fork_proc.ppid != regular_proc.pid
       fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}}
-    when: is_mitogen and forkmode.uses_fork
+    when:
+      - forkmode.uses_fork
 
   - assert:
       that: fork_proc.ppid == regular_proc.pid
       fail_msg: fork_proc={{fork_proc}} regular_proc={{regular_proc}}
-    when: is_mitogen and not forkmode.uses_fork
+    when:
+      - not forkmode.uses_fork
 
   tags:
     - forking_correct_parent
+    - mitogen_only
diff --git a/tests/ansible/integration/runner/forking_inactive.yml b/tests/ansible/integration/runner/forking_inactive.yml
index 913554898..b781aa74e 100644
--- a/tests/ansible/integration/runner/forking_inactive.yml
+++ b/tests/ansible/integration/runner/forking_inactive.yml
@@ -3,22 +3,21 @@
 - name: integration/runner/forking_inactive.yml
   hosts: test-targets
   tasks:
+  - include_tasks: ../_mitogen_only.yml
 
   - name: get process ID.
     custom_python_detect_environment:
     register: sync_proc1
-    when: is_mitogen
 
   - name: get process ID again.
     custom_python_detect_environment:
     register: sync_proc2
-    when: is_mitogen
 
   - assert:
       that:
       - sync_proc1.pid == sync_proc2.pid
       fail_msg: sync_proc1={{sync_proc1}} sync_proc2={{sync_proc2}}
-    when: is_mitogen
 
   tags:
     - forking_inactive
+    - mitogen_only
diff --git a/tests/ansible/integration/ssh/config.yml b/tests/ansible/integration/ssh/config.yml
index 4e6748b18..c4fedc338 100644
--- a/tests/ansible/integration/ssh/config.yml
+++ b/tests/ansible/integration/ssh/config.yml
@@ -6,8 +6,7 @@
   vars:
     ansible_private_key_file: ~/fakekey
   tasks:
-    - meta: end_play
-      when: not is_mitogen
+    - include_tasks: ../_mitogen_only.yml
 
     - mitogen_get_stack:
       register: out
@@ -20,3 +19,4 @@
         fail_msg: out={{out}}
   tags:
     - config
+    - mitogen_only
diff --git a/tests/ansible/integration/ssh/timeouts.yml b/tests/ansible/integration/ssh/timeouts.yml
index d4aba5d2a..a7691bc32 100644
--- a/tests/ansible/integration/ssh/timeouts.yml
+++ b/tests/ansible/integration/ssh/timeouts.yml
@@ -3,6 +3,8 @@
 - name: integration/ssh/timeouts.yml
   hosts: test-targets
   tasks:
+    - include_tasks: ../_mitogen_only.yml
+
     - name: Cause Ansible connection timeout
       connection: local
       environment:
@@ -19,7 +21,6 @@
         chdir: ../..
       register: out
       ignore_errors: true
-      when: is_mitogen
 
     - name: Verify connection timeout occurred
       assert:
@@ -31,6 +32,6 @@
         - |
           '"msg": "Connection timed out."' in out.stdout
         fail_msg: out={{out}}
-      when: is_mitogen
   tags:
+    - mitogen_only
     - timeouts
diff --git a/tests/ansible/integration/ssh/variables.yml b/tests/ansible/integration/ssh/variables.yml
index 6041d304a..f51509db3 100644
--- a/tests/ansible/integration/ssh/variables.yml
+++ b/tests/ansible/integration/ssh/variables.yml
@@ -13,6 +13,8 @@
       -o "ControlPath /tmp/mitogen-ansible-test-{{18446744073709551615|random}}"
 
   tasks:
+    - include_tasks: ../_mitogen_only.yml
+
     - name: ansible_ssh_user, ansible_ssh_pass
       shell: >
         ANSIBLE_ANY_ERRORS_FATAL=false
@@ -28,7 +30,6 @@
       args:
         chdir: ../..
       register: out
-      when: is_mitogen
 
     - name: ansible_ssh_user, wrong ansible_ssh_pass
       shell: >
@@ -46,13 +47,11 @@
         chdir: ../..
       register: out
       ignore_errors: true
-      when: is_mitogen
 
     - assert:
         that:
           - out.rc == 4  # ansible.executor.task_queue_manager.TaskQueueManager.RUN_UNREACHABLE_HOSTS
         fail_msg: out={{out}}
-      when: is_mitogen
 
 
     - name: ansible_user, ansible_ssh_pass
@@ -70,7 +69,6 @@
       args:
         chdir: ../..
       register: out
-      when: is_mitogen
 
     - name: ansible_user, wrong ansible_ssh_pass
       shell: >
@@ -88,13 +86,11 @@
         chdir: ../..
       register: out
       ignore_errors: true
-      when: is_mitogen
 
     - assert:
         that:
           - out.rc == 4  # ansible.executor.task_queue_manager.TaskQueueManager.RUN_UNREACHABLE_HOSTS
         fail_msg: out={{out}}
-      when: is_mitogen
 
 
     - name: ansible_user, ansible_password
@@ -112,7 +108,6 @@
       args:
         chdir: ../..
       register: out
-      when: is_mitogen
 
     - name: ansible_user, wrong ansible_password
       shell: >
@@ -130,13 +125,11 @@
         chdir: ../..
       register: out
       ignore_errors: true
-      when: is_mitogen
 
     - assert:
         that:
           - out.rc == 4  # ansible.executor.task_queue_manager.TaskQueueManager.RUN_UNREACHABLE_HOSTS
         fail_msg: out={{out}}
-      when: is_mitogen
 
 
     - name: setup ansible_ssh_private_key_file
@@ -159,7 +152,6 @@
       args:
         chdir: ../..
       register: out
-      when: is_mitogen
 
     - name: ansible_user, wrong ansible_ssh_private_key_file
       shell: >
@@ -177,10 +169,10 @@
         chdir: ../..
       register: out
       ignore_errors: true
-      when: is_mitogen
 
     - assert:
         that:
           - out.rc == 4  # ansible.executor.task_queue_manager.TaskQueueManager.RUN_UNREACHABLE_HOSTS
         fail_msg: out={{out}}
-      when: is_mitogen
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/kubectl.yml b/tests/ansible/integration/stub_connections/kubectl.yml
index 47777ba8e..e3e927c05 100644
--- a/tests/ansible/integration/stub_connections/kubectl.yml
+++ b/tests/ansible/integration/stub_connections/kubectl.yml
@@ -3,8 +3,7 @@
   hosts: test-targets
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - meta: end_play
     when:
@@ -23,3 +22,4 @@
       fail_msg: out={{out}}
   tags:
     - kubectl
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/lxc.yml b/tests/ansible/integration/stub_connections/lxc.yml
index abe354b6e..ae71a603f 100644
--- a/tests/ansible/integration/stub_connections/lxc.yml
+++ b/tests/ansible/integration/stub_connections/lxc.yml
@@ -3,8 +3,7 @@
   hosts: test-targets
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - custom_python_detect_environment:
     vars:
@@ -19,3 +18,4 @@
       fail_msg: out={{out}}
   tags:
     - lxc
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/lxd.yml b/tests/ansible/integration/stub_connections/lxd.yml
index 5314f82ce..a2f8f80ad 100644
--- a/tests/ansible/integration/stub_connections/lxd.yml
+++ b/tests/ansible/integration/stub_connections/lxd.yml
@@ -3,8 +3,7 @@
   hosts: test-targets
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - custom_python_detect_environment:
     vars:
@@ -19,3 +18,4 @@
       fail_msg: out={{out}}
   tags:
     - lxd
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/mitogen_doas.yml b/tests/ansible/integration/stub_connections/mitogen_doas.yml
index 87764d125..338bc09b7 100644
--- a/tests/ansible/integration/stub_connections/mitogen_doas.yml
+++ b/tests/ansible/integration/stub_connections/mitogen_doas.yml
@@ -3,8 +3,7 @@
   hosts: test-targets
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - custom_python_detect_environment:
     vars:
@@ -22,3 +21,4 @@
       fail_msg: out={{out}}
   tags:
     - mitogen_doas
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/mitogen_sudo.yml b/tests/ansible/integration/stub_connections/mitogen_sudo.yml
index 6da0e5d69..0ab18f3b0 100644
--- a/tests/ansible/integration/stub_connections/mitogen_sudo.yml
+++ b/tests/ansible/integration/stub_connections/mitogen_sudo.yml
@@ -3,8 +3,7 @@
   hosts: test-targets
   gather_facts: false
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: ../_mitogen_only.yml
 
   - custom_python_detect_environment:
     vars:
@@ -23,4 +22,5 @@
       left: (out.env.ORIGINAL_ARGV|from_json)[1:9]
       right: ['-u', 'root', '-H', '-r', 'somerole', '-t', 'sometype', '--']
   tags:
+    - mitogen_only
     - mitogen_sudo
diff --git a/tests/ansible/integration/stub_connections/setns_lxc.yml b/tests/ansible/integration/stub_connections/setns_lxc.yml
index 489f9883c..889a15e9e 100644
--- a/tests/ansible/integration/stub_connections/setns_lxc.yml
+++ b/tests/ansible/integration/stub_connections/setns_lxc.yml
@@ -8,10 +8,8 @@
   any_errors_fatal: false
   connection: local
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
-    - include: _end_play_if_not_sudo_linux.yml
+    - include_tasks: ../_mitogen_only.yml
+    - include_tasks: _end_play_if_not_sudo_linux.yml
 
     - name: Run stub-lxc-info.py
       command: |
@@ -35,3 +33,4 @@
         fail_msg: result={{result}}
   tags:
     - stns_lxc
+    - mitogen_only
diff --git a/tests/ansible/integration/stub_connections/setns_lxd.yml b/tests/ansible/integration/stub_connections/setns_lxd.yml
index d654b7bc5..81a52dbb3 100644
--- a/tests/ansible/integration/stub_connections/setns_lxd.yml
+++ b/tests/ansible/integration/stub_connections/setns_lxd.yml
@@ -8,10 +8,8 @@
   any_errors_fatal: false
   connection: local
   tasks:
-    - meta: end_play
-      when: not is_mitogen
-
-    - include: _end_play_if_not_sudo_linux.yml
+    - include_tasks: ../_mitogen_only.yml
+    - include_tasks: _end_play_if_not_sudo_linux.yml
 
     - name: Run ansible stub-lxc.py
       command: |
@@ -34,4 +32,5 @@
         that: result.rc == 0
         fail_msg: result={{result}}
   tags:
+    - mitogen_only
     - sens_lxd
diff --git a/tests/ansible/integration/transport/all.yml b/tests/ansible/integration/transport/all.yml
index 623ee00c6..c0cb2fc7a 100644
--- a/tests/ansible/integration/transport/all.yml
+++ b/tests/ansible/integration/transport/all.yml
@@ -1,4 +1,4 @@
 
-- include: kubectl.yml
+- include_playbook: kubectl.yml
   tags:
     - kubectl
diff --git a/tests/ansible/integration/transport_config/become.yml b/tests/ansible/integration/transport_config/become.yml
index 51b698eb7..4d9f54f06 100644
--- a/tests/ansible/integration/transport_config/become.yml
+++ b/tests/ansible/integration/transport_config/become.yml
@@ -5,7 +5,7 @@
 - name: integration/transport_config/become.yml
   hosts: tc-become-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -13,11 +13,13 @@
       - out.result[0].method == "ssh"
       - out.result[0].kwargs.username == "ansible-cfg-remote-user"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-become-unset
   vars: {mitogen_via: becomeuser@tc-become-set}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -31,6 +33,8 @@
       - out.result[2].method == "ssh"
       - out.result[2].kwargs.hostname == "tc-become-unset"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # Become set.
@@ -39,7 +43,7 @@
   become: true
   become_user: becomeuser
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -49,13 +53,15 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.username == "becomeuser"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-become-set
   vars: {mitogen_via: tc-become-unset}
   become: true
   become_user: becomeuser
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -70,3 +76,5 @@
       - out.result[2].method == "sudo"
       - out.result[2].kwargs.username == "becomeuser"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/become_method.yml b/tests/ansible/integration/transport_config/become_method.yml
index e4efbabd9..ffe5a54dc 100644
--- a/tests/ansible/integration/transport_config/become_method.yml
+++ b/tests/ansible/integration/transport_config/become_method.yml
@@ -6,7 +6,7 @@
   hosts: tc-become-method-unset
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -14,11 +14,13 @@
       - out.result[0].method == "ssh"
       - out.result[1].method == "sudo"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-become-method-unset
   vars: {mitogen_via: becomeuser@tc-become-method-su}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -29,6 +31,8 @@
       - out.result[2].method == "ssh"
       - out.result[2].kwargs.hostname == "tc-become-method-unset"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_method=su
@@ -36,7 +40,7 @@
   become: true
   become_user: becomeuser
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -45,13 +49,15 @@
       - out.result[1].method == "su"
       - out.result[1].kwargs.username == "becomeuser"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-become-method-su
   vars: {mitogen_via: tc-become-method-unset}
   become: true
   become_user: becomeuser
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -65,6 +71,8 @@
       - out.result[2].method == "su"
       - out.result[2].kwargs.username == "becomeuser"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 
@@ -72,7 +80,7 @@
 - hosts: tc-become-method-unset
   vars: {mitogen_via: "doas:doasuser@tc-become-method-su"}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -86,3 +94,5 @@
       - out.result[2].method == "ssh"
       - out.result[2].kwargs.hostname == "tc-become-method-unset"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/become_pass.yml b/tests/ansible/integration/transport_config/become_pass.yml
index 317e0522b..8fbe72511 100644
--- a/tests/ansible/integration/transport_config/become_pass.yml
+++ b/tests/ansible/integration/transport_config/become_pass.yml
@@ -6,7 +6,7 @@
   hosts: tc-become-pass-unset
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -15,13 +15,15 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.password == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # Not set, unbecoming mitogen_via=
 - hosts: tc-become-pass-unset
   become: true
   vars: {mitogen_via: tc-become-pass-password}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -31,13 +33,15 @@
       - out.result[2].method == "sudo"
       - out.result[2].kwargs.password == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # Not set, becoming mitogen_via=
 - hosts: tc-become-pass-unset
   become: true
   vars: {mitogen_via: viapass@tc-become-pass-password}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -49,13 +53,15 @@
       - out.result[3].method == "sudo"
       - out.result[3].kwargs.password == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_password= set.
 - hosts: tc-become-pass-password
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -64,6 +70,8 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.password == "apassword"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_password=, via=
@@ -71,7 +79,7 @@
   vars: {mitogen_via: root@tc-become-pass-pass}
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -83,13 +91,15 @@
       - out.result[3].method == "sudo"
       - out.result[3].kwargs.password == "apassword"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_pass=
 - hosts: tc-become-pass-pass
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -98,6 +108,8 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.password == "apass"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_pass=, via=
@@ -105,7 +117,7 @@
   vars: {mitogen_via: root@tc-become-pass-password}
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -117,12 +129,14 @@
       - out.result[3].method == "sudo"
       - out.result[3].kwargs.password == "apass"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 - hosts: tc-become-pass-both
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -133,13 +147,15 @@
       # to ansible_become_pass over ansible_become_password.
       - out.result[1].kwargs.password == "bpass"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # both, mitogen_via
 - hosts: tc-become-pass-unset
   vars: {mitogen_via: root@tc-become-pass-both}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -149,3 +165,5 @@
       - out.result[1].kwargs.password == "bpass"
       - out.result[2].method == "ssh"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/become_user.yml b/tests/ansible/integration/transport_config/become_user.yml
index 1a8b9fc94..0e30dfe25 100644
--- a/tests/ansible/integration/transport_config/become_user.yml
+++ b/tests/ansible/integration/transport_config/become_user.yml
@@ -6,7 +6,7 @@
   hosts: tc-become-user-unset
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -15,13 +15,15 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.username == "root"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # Not set, unbecoming mitogen_via=
 - hosts: tc-become-user-unset
   become: true
   vars: {mitogen_via: tc-become-user-set}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -31,13 +33,15 @@
       - out.result[2].method == "sudo"
       - out.result[2].kwargs.username == "root"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # Not set, becoming mitogen_via=
 - hosts: tc-become-user-unset
   become: true
   vars: {mitogen_via: viauser@tc-become-user-set}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -49,13 +53,15 @@
       - out.result[3].method == "sudo"
       - out.result[3].kwargs.username == "root"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_user= set.
 - hosts: tc-become-user-set
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -64,6 +70,8 @@
       - out.result[1].method == "sudo"
       - out.result[1].kwargs.username == "ansi-become-user"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_user=, unbecoming via=
@@ -71,7 +79,7 @@
   vars: {mitogen_via: tc-become-user-unset}
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -85,6 +93,8 @@
       - out.result[2].method == "sudo"
       - out.result[2].kwargs.username == "ansi-become-user"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # ansible_become_user=, becoming via=
@@ -92,7 +102,7 @@
   vars: {mitogen_via: "doas:doasuser@tc-become-user-unset"}
   become: true
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -109,4 +119,6 @@
       - out.result[3].method == "sudo"
       - out.result[3].kwargs.username == "ansi-become-user"
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
diff --git a/tests/ansible/integration/transport_config/password.yml b/tests/ansible/integration/transport_config/password.yml
index ac236d66f..c55939f7f 100644
--- a/tests/ansible/integration/transport_config/password.yml
+++ b/tests/ansible/integration/transport_config/password.yml
@@ -6,16 +6,18 @@
 - name: integration/transport_config/password.yml
   hosts: tc-password-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
       right: ""  # actually null, but assert_equal limitation
+  tags:
+    - mitogen_only
 
 - hosts: tc-password-unset
   vars: {mitogen_via: tc-password-explicit-ssh}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
@@ -23,22 +25,26 @@
   - assert_equal:
       left: out.result[1].kwargs.password
       right: ""
+  tags:
+    - mitogen_only
 
 
 # ansible_ssh_user=
 
 - hosts: tc-password-explicit-ssh
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
       right: "ansi-ssh-pass"
+  tags:
+    - mitogen_only
 
 - hosts: tc-password-explicit-ssh
   vars: {mitogen_via: tc-password-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
@@ -46,22 +52,26 @@
   - assert_equal:
       left: out.result[1].kwargs.password
       right: "ansi-ssh-pass"
+  tags:
+    - mitogen_only
 
 
 # ansible_user=
 
 - hosts: tc-password-explicit-pass
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
       right: "ansi-pass"
+  tags:
+    - mitogen_only
 
 - hosts: tc-password-explicit-pass
   vars: {mitogen_via: tc-password-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
@@ -69,22 +79,26 @@
   - assert_equal:
       left: out.result[1].kwargs.password
       right: "ansi-pass"
+  tags:
+    - mitogen_only
 
 
 # both; ansible_ssh_user= takes precedence according to play_context.py.
 
 - hosts: tc-password-explicit-both
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
       right: "c.b.a"
+  tags:
+    - mitogen_only
 
 - hosts: tc-password-explicit-both
   vars: {mitogen_via: tc-password-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.password
@@ -92,3 +106,5 @@
   - assert_equal:
       left: out.result[1].kwargs.password
       right: "c.b.a"
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/port.yml b/tests/ansible/integration/transport_config/port.yml
index 340eae799..c8e6bdaed 100644
--- a/tests/ansible/integration/transport_config/port.yml
+++ b/tests/ansible/integration/transport_config/port.yml
@@ -5,7 +5,7 @@
 - name: integration/transport_config/port.yml
   hosts: tc-port-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -13,12 +13,14 @@
       - out.result[0].method == "ssh"
       - out.result[0].kwargs.port == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # Not set, mitogen_via=
 - hosts: tc-port-explicit-ssh
   vars: {mitogen_via: tc-port-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -28,11 +30,13 @@
       - out.result[1].method == "ssh"
       - out.result[1].kwargs.port == 4321
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # ansible_ssh_port=
 - hosts: tc-port-explicit-ssh
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -40,11 +44,13 @@
       - out.result[0].method == "ssh"
       - out.result[0].kwargs.port == 4321
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-port-unset
   vars: {mitogen_via: tc-port-explicit-ssh}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -54,11 +60,13 @@
       - out.result[1].method == "ssh"
       - out.result[1].kwargs.port == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 # ansible_port=
 - hosts: tc-port-explicit-port
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -66,11 +74,13 @@
       - out.result[0].method == "ssh"
       - out.result[0].kwargs.port == 1234
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-port-unset
   vars: {mitogen_via: tc-port-explicit-port}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -80,12 +90,14 @@
       - out.result[1].method == "ssh"
       - out.result[1].kwargs.port == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 
 # both, ssh takes precedence
 - hosts: tc-port-both
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -93,11 +105,13 @@
       - out.result[0].method == "ssh"
       - out.result[0].kwargs.port == 1532
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
 
 - hosts: tc-port-unset
   vars: {mitogen_via: tc-port-both}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert:
       that:
@@ -107,3 +121,5 @@
       - out.result[1].method == "ssh"
       - out.result[1].kwargs.port == None
       fail_msg: out={{out}}
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/python_path.yml b/tests/ansible/integration/transport_config/python_path.yml
index 0c6069a00..0f04ed594 100644
--- a/tests/ansible/integration/transport_config/python_path.yml
+++ b/tests/ansible/integration/transport_config/python_path.yml
@@ -7,16 +7,18 @@
 - name: integration/transport_config/python_path.yml
   hosts: tc-python-path-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
       right: ["{{out.discovered_interpreter}}"]
+  tags:
+    - mitogen_only
 
 - hosts: tc-python-path-hostvar
   vars: {mitogen_via: tc-python-path-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
@@ -24,21 +26,25 @@
   - assert_equal:
       left: out.result[1].kwargs.python_path
       right: ["/hostvar/path/to/python"]
+  tags:
+    - mitogen_only
 
 
 # Non-localhost with explicit ansible_python_interpreter
 - hosts: tc-python-path-hostvar
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
       right: [/hostvar/path/to/python]
+  tags:
+    - mitogen_only
 
 - hosts: tc-python-path-unset
   vars: {mitogen_via: tc-python-path-hostvar}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
@@ -46,21 +52,25 @@
   - assert_equal:
       left: out.result[1].kwargs.python_path
       right: ["{{out.discovered_interpreter}}"]
+  tags:
+    - mitogen_only
 
 
 # Implicit localhost gets ansible_python_interpreter=virtualenv interpreter
 - hosts: localhost
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
       right: ["{{ansible_playbook_python}}"]
+  tags:
+    - mitogen_only
 
 - hosts: tc-python-path-unset
   vars: {mitogen_via: localhost}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
@@ -68,22 +78,26 @@
   - assert_equal:
       left: out.result[1].kwargs.python_path
       right: ["{{out.discovered_interpreter}}"]
+  tags:
+    - mitogen_only
 
 
 # explicit local connections get the same treatment as everything else.
 - hosts: tc-python-path-local-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
       right: ["{{out.discovered_interpreter}}"]
+  tags:
+    - mitogen_only
 
 
 - hosts: localhost
   vars: {mitogen_via: tc-python-path-local-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
@@ -91,21 +105,25 @@
   - assert_equal:
       left: out.result[1].kwargs.python_path
       right: ["{{ansible_playbook_python}}"]
+  tags:
+    - mitogen_only
 
 
 # explicit local connection with explicit interpreter
 - hosts: tc-python-path-local-explicit
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
       right: ["/a/b/c"]
+  tags:
+    - mitogen_only
 
 - hosts: localhost
   vars: {mitogen_via: tc-python-path-local-explicit}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.python_path
@@ -113,3 +131,5 @@
   - assert_equal:
       left: out.result[1].kwargs.python_path
       right: ["{{ansible_playbook_python}}"]
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/remote_addr.yml b/tests/ansible/integration/transport_config/remote_addr.yml
index b98872029..b1a6c5a7d 100644
--- a/tests/ansible/integration/transport_config/remote_addr.yml
+++ b/tests/ansible/integration/transport_config/remote_addr.yml
@@ -7,16 +7,18 @@
 - name: integration/transport_config/remote_addr.yml
   hosts: tc-remote-addr-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
       right: "tc-remote-addr-unset"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-addr-unset
   vars: {mitogen_via: tc-remote-addr-explicit-ssh}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
@@ -24,22 +26,26 @@
   - assert_equal:
       left: out.result[1].kwargs.hostname
       right: "tc-remote-addr-unset"
+  tags:
+    - mitogen_only
 
 
 # ansible_ssh_host=
 
 - hosts: tc-remote-addr-explicit-ssh
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
       right: "ansi.ssh.host"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-addr-explicit-ssh
   vars: {mitogen_via: tc-remote-addr-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
@@ -47,22 +53,26 @@
   - assert_equal:
       left: out.result[1].kwargs.hostname
       right: "ansi.ssh.host"
+  tags:
+    - mitogen_only
 
 
 # ansible_host=
 
 - hosts: tc-remote-addr-explicit-host
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
       right: "ansi.host"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-addr-explicit-host
   vars: {mitogen_via: tc-remote-addr-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
@@ -70,22 +80,26 @@
   - assert_equal:
       left: out.result[1].kwargs.hostname
       right: "ansi.host"
+  tags:
+    - mitogen_only
 
 
 # both; ansible_ssh_host= takes precedence according to play_context.py.
 
 - hosts: tc-remote-addr-explicit-both
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
       right: "a.b.c"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-addr-explicit-both
   vars: {mitogen_via: tc-remote-addr-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.hostname
@@ -93,3 +107,5 @@
   - assert_equal:
       left: out.result[1].kwargs.hostname
       right: "a.b.c"
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/remote_user.yml b/tests/ansible/integration/transport_config/remote_user.yml
index 5ddc5aa37..a5559edbc 100644
--- a/tests/ansible/integration/transport_config/remote_user.yml
+++ b/tests/ansible/integration/transport_config/remote_user.yml
@@ -7,17 +7,19 @@
 - name: integration/transport_config/remote_user.yml
   hosts: tc-remote-user-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
       # We set DEFAULT_REMOTE_USER in our ansible.cfg
       right: "ansible-cfg-remote-user"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-user-unset
   vars: {mitogen_via: tc-remote-user-explicit-ssh}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
@@ -25,22 +27,26 @@
   - assert_equal:
       left: out.result[1].kwargs.username
       right: "ansible-cfg-remote-user"
+  tags:
+    - mitogen_only
 
 
 # ansible_ssh_user=
 
 - hosts: tc-remote-user-explicit-ssh
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
       right: "ansi-ssh-user"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-user-explicit-ssh
   vars: {mitogen_via: tc-remote-user-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
@@ -48,13 +54,15 @@
   - assert_equal:
       left: out.result[1].kwargs.username
       right: "ansi-ssh-user"
+  tags:
+    - mitogen_only
 
 
 # ansible_user=
 
 - hosts: tc-remote-user-explicit-user
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
@@ -63,7 +71,7 @@
 - hosts: tc-remote-user-explicit-user
   vars: {mitogen_via: tc-remote-user-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
@@ -71,22 +79,26 @@
   - assert_equal:
       left: out.result[1].kwargs.username
       right: "ansi-user"
+  tags:
+    - mitogen_only
 
 
 # both; ansible_ssh_user= takes precedence according to play_context.py.
 
 - hosts: tc-remote-user-explicit-both
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
       right: "c.b.a"
+  tags:
+    - mitogen_only
 
 - hosts: tc-remote-user-explicit-both
   vars: {mitogen_via: tc-remote-user-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].kwargs.username
@@ -94,3 +106,5 @@
   - assert_equal:
       left: out.result[1].kwargs.username
       right: "c.b.a"
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/transport.yml b/tests/ansible/integration/transport_config/transport.yml
index efedc8d44..1bac788b1 100644
--- a/tests/ansible/integration/transport_config/transport.yml
+++ b/tests/ansible/integration/transport_config/transport.yml
@@ -6,16 +6,18 @@
 - name: integration/transport_config/transport.yml
   hosts: tc-transport-unset
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
       right: "ssh"
+  tags:
+    - mitogen_only
 
 - hosts: tc-transport-local
   vars: {mitogen_via: tc-transport-unset}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
@@ -23,22 +25,26 @@
   - assert_equal:
       left: out.result[1].method
       right: "local"
+  tags:
+    - mitogen_only
 
 
 # ansible_connection=local
 
 - hosts: tc-transport-local
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
       right: "local"
+  tags:
+    - mitogen_only
 
 - hosts: tc-transport-unset
   vars: {mitogen_via: tc-transport-local}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
@@ -46,3 +52,5 @@
   - assert_equal:
       left: out.result[1].method
       right: "ssh"
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/integration/transport_config/transport__smart.yml b/tests/ansible/integration/transport_config/transport__smart.yml
index f263d67e4..d2adca45f 100644
--- a/tests/ansible/integration/transport_config/transport__smart.yml
+++ b/tests/ansible/integration/transport_config/transport__smart.yml
@@ -7,16 +7,18 @@
 - name: integration/transport_config/transport__smart.yml
   hosts: tc-transport-smart
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
       right: "ssh"
+  tags:
+    - mitogen_only
 
 - hosts: tc-transport-local
   vars: {mitogen_via: tc-transport-smart}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
@@ -24,22 +26,26 @@
   - assert_equal:
       left: out.result[1].method
       right: "local"
+  tags:
+    - mitogen_only
 
 
 # ansible_connection=local
 
 - hosts: tc-transport-local
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
       right: "local"
+  tags:
+    - mitogen_only
 
 - hosts: tc-transport-smart
   vars: {mitogen_via: tc-transport-local}
   tasks:
-  - include: ../_mitogen_only.yml
+  - include_tasks: ../_mitogen_only.yml
   - {mitogen_get_stack: {}, register: out}
   - assert_equal:
       left: out.result[0].method
@@ -47,3 +53,5 @@
   - assert_equal:
       left: out.result[1].method
       right: "ssh"
+  tags:
+    - mitogen_only
diff --git a/tests/ansible/regression/_mitogen_only.yml b/tests/ansible/regression/_mitogen_only.yml
new file mode 120000
index 000000000..cf6c9cf7c
--- /dev/null
+++ b/tests/ansible/regression/_mitogen_only.yml
@@ -0,0 +1,1 @@
+../integration/_mitogen_only.yml
\ No newline at end of file
diff --git a/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml b/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml
index 80680063c..d5f0815b5 100644
--- a/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml
+++ b/tests/ansible/regression/issue_591__setuptools_cwd_crash.yml
@@ -6,8 +6,7 @@
 - name: regression/issue_591__setuptools_cwd_crash.yml
   hosts: test-targets
   tasks:
-  - meta: end_play
-    when: not is_mitogen
+  - include_tasks: _mitogen_only.yml
 
   - custom_python_run_script:
       script: |
@@ -25,3 +24,4 @@
         self._connection.get_chain().call(os.getcwd)
   tags:
     - issue_591
+    - mitogen_only
diff --git a/tests/ansible/regression/issue_615__streaming_transfer.yml b/tests/ansible/regression/issue_615__streaming_transfer.yml
index 8ec28499c..43aa1343d 100644
--- a/tests/ansible/regression/issue_615__streaming_transfer.yml
+++ b/tests/ansible/regression/issue_615__streaming_transfer.yml
@@ -8,6 +8,7 @@
   vars:
     mitogen_ssh_compression: false
   tasks:
+    - include_tasks: _mitogen_only.yml
     - block:
         - name: Create /tmp/512mb.zero
           shell: |
@@ -31,7 +32,6 @@
           become: false
           delegate_to: localhost
           run_once: true
-      when:
-        - is_mitogen
   tags:
     - issue_615
+    - mitogen_only
diff --git a/tests/ansible/soak/file_service.yml b/tests/ansible/soak/file_service.yml
index 90462cbf4..f08d6dccf 100644
--- a/tests/ansible/soak/file_service.yml
+++ b/tests/ansible/soak/file_service.yml
@@ -3,7 +3,7 @@
   tasks:
   - set_fact:
       content: "{% for x in range(126977) %}x{% endfor %}"
-  - include: _file_service_loop.yml
+  - include_tasks: _file_service_loop.yml
     with_sequence: start=1 end=100
   tags:
     - resource_intensive
diff --git a/tests/ansible/tests/connection_test.py b/tests/ansible/tests/connection_test.py
index 0ade69946..649f8b659 100644
--- a/tests/ansible/tests/connection_test.py
+++ b/tests/ansible/tests/connection_test.py
@@ -2,7 +2,11 @@
 import os
 import tempfile
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
+
 import ansible.errors
 import ansible.playbook.play_context
 
diff --git a/tests/ansible/tests/target_test.py b/tests/ansible/tests/target_test.py
index 0e588baa6..806175edd 100644
--- a/tests/ansible/tests/target_test.py
+++ b/tests/ansible/tests/target_test.py
@@ -4,7 +4,10 @@
 import tempfile
 import unittest
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import ansible_mitogen.target
 import testlib
diff --git a/tests/broker_test.py b/tests/broker_test.py
index d58d94801..41b953878 100644
--- a/tests/broker_test.py
+++ b/tests/broker_test.py
@@ -1,4 +1,7 @@
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import testlib
 
diff --git a/tests/image_prep/setup.yml b/tests/image_prep/setup.yml
index 9aa3285cc..b820e1bc2 100755
--- a/tests/image_prep/setup.yml
+++ b/tests/image_prep/setup.yml
@@ -1,6 +1,6 @@
 #!/usr/bin/env ansible-playbook
 
-- include: _container_create.yml
-- include: _container_setup.yml
-- include: _user_accounts.yml
-- include: _container_finalize.yml
+- include_playbook: _container_create.yml
+- include_playbook: _container_setup.yml
+- include_playbook: _user_accounts.yml
+- include_playbook: _container_finalize.yml
diff --git a/tests/importer_test.py b/tests/importer_test.py
index e86af8af3..33aafe931 100644
--- a/tests/importer_test.py
+++ b/tests/importer_test.py
@@ -4,7 +4,10 @@
 import zlib
 import unittest
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.core
 import mitogen.utils
diff --git a/tests/io_op_test.py b/tests/io_op_test.py
index 55adfa118..e0ad65f15 100644
--- a/tests/io_op_test.py
+++ b/tests/io_op_test.py
@@ -1,7 +1,10 @@
 import errno
 import select
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import testlib
 import mitogen.core
diff --git a/tests/log_handler_test.py b/tests/log_handler_test.py
index 43b986cbb..da929426d 100644
--- a/tests/log_handler_test.py
+++ b/tests/log_handler_test.py
@@ -1,8 +1,12 @@
 import logging
-import mock
 import sys
 import unittest
 
+try:
+    from unittest import mock
+except ImportError:
+    import mock
+
 import testlib
 import mitogen.core
 import mitogen.master
diff --git a/tests/message_test.py b/tests/message_test.py
index 2d2299d1b..fc50a3270 100644
--- a/tests/message_test.py
+++ b/tests/message_test.py
@@ -2,7 +2,10 @@
 import struct
 import unittest
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.core
 import mitogen.master
diff --git a/tests/mitogen_protocol_test.py b/tests/mitogen_protocol_test.py
index d6e3cc959..46a0cd65b 100644
--- a/tests/mitogen_protocol_test.py
+++ b/tests/mitogen_protocol_test.py
@@ -1,4 +1,7 @@
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.core
 
diff --git a/tests/parent_test.py b/tests/parent_test.py
index fa7e4fb71..558a89b6c 100644
--- a/tests/parent_test.py
+++ b/tests/parent_test.py
@@ -6,7 +6,10 @@
 import time
 import unittest
 
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 import testlib
 
 import mitogen.core
diff --git a/tests/policy_function_test.py b/tests/policy_function_test.py
index 843f8b7c8..77bd17d19 100644
--- a/tests/policy_function_test.py
+++ b/tests/policy_function_test.py
@@ -1,4 +1,7 @@
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.core
 import mitogen.parent
diff --git a/tests/reaper_test.py b/tests/reaper_test.py
index b60b13a42..8588a1bc4 100644
--- a/tests/reaper_test.py
+++ b/tests/reaper_test.py
@@ -1,7 +1,10 @@
 import signal
 
 import testlib
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.parent
 
diff --git a/tests/responder_test.py b/tests/responder_test.py
index 1131b421e..d1e658167 100644
--- a/tests/responder_test.py
+++ b/tests/responder_test.py
@@ -1,9 +1,13 @@
-import mock
 import textwrap
 import subprocess
 import sys
 import unittest
 
+try:
+    from unittest import mock
+except ImportError:
+    import mock
+
 import mitogen.master
 import testlib
 
diff --git a/tests/timer_test.py b/tests/timer_test.py
index dac4d7df5..f8f203a98 100644
--- a/tests/timer_test.py
+++ b/tests/timer_test.py
@@ -1,4 +1,7 @@
-import mock
+try:
+    from unittest import mock
+except ImportError:
+    import mock
 
 import mitogen.core
 import mitogen.parent