diff --git a/docs/includes/glossary.md b/docs/includes/glossary.md
index 4df9f9940521f78574c848c2f49bcfa76dd7899f..a29d245259baa8a8b27cd16feade66bd5fa6c732 100644
--- a/docs/includes/glossary.md
+++ b/docs/includes/glossary.md
@@ -5,6 +5,7 @@
 *[CFS]: Customer Facing Service
 *[CIDR]: Classless Inter-Domain Routing
 *[DCIM]: Datacenter Infrastructure Manager
+*[DCN]: Datacenter Network
 *[DHCP]: Dynamic Host Configuration Protocol
 *[DNS]: Domain Name System
 *[DTAP]: Development, Testing, Acceptance, and Production
diff --git a/docs/scripts/gen_wf_redirects.py b/docs/scripts/gen_wf_redirects.py
index 3b738d8aaa3e0c0367414067043165556a65c667..90b84444d2dbebfc339b24a07f4dfb5a011da74c 100644
--- a/docs/scripts/gen_wf_redirects.py
+++ b/docs/scripts/gen_wf_redirects.py
@@ -33,7 +33,7 @@ with Path.open(root / "docs" / "wf_redirects.yaml", "w") as redirect_file:
             {"gen-files": {"scripts": ["scripts/gen_ref_pages.py"]}},
             {"redirects": {"redirect_maps": redirect_map}},
             {"literate-nav": {"nav_file": "SUMMARY.md"}},
-            "mkdocstrings",
+            {"mkdocstrings": {"handlers": {"python": {"options": {"filters": [], "members_order": "source"}}}}},
         ]
     }
     yaml.dump(file_content, redirect_file)
diff --git a/docs/source/architecture/components/index.md b/docs/source/architecture/components/index.md
index bf0790f34cf09df7efab943a2437df16579319d0..76e837d67ef23a5442bf1a7b869d56cddc801a1b 100644
--- a/docs/source/architecture/components/index.md
+++ b/docs/source/architecture/components/index.md
@@ -1,7 +1,7 @@
 # Components of GAP
 
 As stated before, GAP is a platform and not a monolithic piece of software. GAP interacts with different OSS/BSS
-systems already present in GÉANT and these are tightly integrated with the automation platform.
+systems already present in GÉANT and these are integrated with the automation platform.
 
 From a high level point of view, GAP can be seen as the sum of the following parts:
 
diff --git a/docs/source/architecture/index.md b/docs/source/architecture/index.md
index 7f53acba3a6d6229d0dbe743fc907b0c1d4f00a9..da9f309c04eb4c4c409b4d4a28c894c5de95bf81 100644
--- a/docs/source/architecture/index.md
+++ b/docs/source/architecture/index.md
@@ -57,16 +57,12 @@ configuration backups of routers, switches, and any other network devices that a
 More detailed information about this integration is available in the
 [LibreNMS integration module](../admin_guide/oss_bss/librenms.md).
 
-### Kentik (planned)
+### Kentik
 
 Kentik is a Network Observability tool which collects various data points from deployed PE routers.
 For this reason it is not in scope for PHASE1.
 
-### Inventory provider (planned)
+### Inventory provider
 
 At the time of writing, the Inventory Provider gets the list of routers from the network engineering SOT servers.
 This will change and Inventory Provider is then able to directly query CoreDB.
-
-## Interaction with a technical domain: IP/MPLS
-
-TBA
diff --git a/docs/vale/.vale.ini b/docs/vale/.vale.ini
index 06742e1a587711316b5140adf0c4bb7a86099bab..a5a4c9cc53200bdab311a353d7fc2ac9233674c9 100644
--- a/docs/vale/.vale.ini
+++ b/docs/vale/.vale.ini
@@ -33,10 +33,10 @@ Microsoft.SentenceLength = NO
 
 ; This statement ignores the names of parameters in docstrings, the four spaces that prepend it and the one following it
 ; are necessary.
-TokenIgnores =     \S+:
+TokenIgnores = (?: *\S+: ), (?:`\S+`)
 
 [*.md]
 BasedOnStyles = Vale, proselint, Microsoft
 
 [formats]
-py = rst
+py = md
diff --git a/docs/vale/styles/config/vocabularies/geant-jargon/accept.txt b/docs/vale/styles/config/vocabularies/geant-jargon/accept.txt
index 6373503d95c7d4af174b0bb9a34cb988ec00a402..aeb0c40d8ae8adc250f8ab38c568ad95d4855393 100644
--- a/docs/vale/styles/config/vocabularies/geant-jargon/accept.txt
+++ b/docs/vale/styles/config/vocabularies/geant-jargon/accept.txt
@@ -11,6 +11,7 @@ CIDR
 CFS
 CNAME
 DCIM
+DCN
 DDI
 DHCP
 DNS
@@ -90,3 +91,4 @@ WFO
 dry_run
 eBGP
 iBGP
+disable?[sd]
diff --git a/gso/products/__init__.py b/gso/products/__init__.py
index 88552bc719940b2a95aaed3421c86d1f322566b6..f614447707e9180b18e74d1e3e530c8fd0c04c57 100644
--- a/gso/products/__init__.py
+++ b/gso/products/__init__.py
@@ -1,8 +1,8 @@
 """Module that updates the domain model of GSO. Should contain all types of subscriptions.
 
-.. warning::
-   Whenever a new product is added, this should be reflected in the `ProductType` enumerator.
-   This does not hold for adding a new type of already existing product.
+!!! warning
+    Whenever a new product is added, this should be reflected in the `ProductType` enumerator.
+    This does not hold for adding a new type of already existing product.
 """
 
 from orchestrator.domain import SUBSCRIPTION_MODEL_REGISTRY
diff --git a/gso/products/product_blocks/lan_switch_interconnect.py b/gso/products/product_blocks/lan_switch_interconnect.py
index b62156afb2fea7bea50651173e8cb85b8380e534..624f40152f747c61911e06a57cdbb50ef26dcc75 100644
--- a/gso/products/product_blocks/lan_switch_interconnect.py
+++ b/gso/products/product_blocks/lan_switch_interconnect.py
@@ -131,17 +131,21 @@ class LanSwitchInterconnectBlockProvisioning(
 
 
 class LanSwitchInterconnectBlock(LanSwitchInterconnectBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """A LAN Switch Interconnect that's currently deployed in the network."""
+    """A LAN Switch Interconnect that's currently deployed in the network.
+
+    Attributes:
+        lan_switch_interconnect_description: A human-readable description of this LAN Switch Interconnect.
+        minimum_links: The minimum amount of links the LAN Switch Interconnect should consist of.
+        switch_management_vlan_id: VLAN ID for the switch management network.
+        dcn_management_vlan_id: VLAN ID for the DCN management network, if the site of this product contains optical
+            equipment.
+        router_side: The router side of the LAN Switch Interconnect.
+        switch_side: The switch side of the LAN Switch Interconnect.
+    """
 
-    #: A human-readable description of this LAN Switch Interconnect.
     lan_switch_interconnect_description: str
-    #: The minimum amount of links the LAN Switch Interconnect should consist of.
     minimum_links: int
-    #: VLAN ID for the switch management network.
     switch_management_vlan_id: VLAN_ID
-    #: VLAN ID for the DCN management network, if the site of this product contains optical equipment.
     dcn_management_vlan_id: VLAN_ID | None
-    #: The router side of the LAN Switch Interconnect.
     router_side: LanSwitchInterconnectRouterSideBlock
-    #: The switch side of the LAN Switch Interconnect.
     switch_side: LanSwitchInterconnectSwitchSideBlock
diff --git a/gso/products/product_blocks/opengear.py b/gso/products/product_blocks/opengear.py
index 9d1571f66afbaede69fedc0b653e3d25bf8acb28..0daca4899edda40c51fe39a926284e83d007c918 100644
--- a/gso/products/product_blocks/opengear.py
+++ b/gso/products/product_blocks/opengear.py
@@ -31,21 +31,24 @@ class OpengearBlockProvisioning(OpengearBlockInactive, lifecycle=[SubscriptionLi
 
     opengear_hostname: str
     opengear_site: SiteBlockProvisioning
-    opengear_wan_address: ipaddress.IPv4Address | None = None
-    opengear_wan_netmask: ipaddress.IPv4Address | None = None
-    opengear_wan_gateway: ipaddress.IPv4Address | None = None
+    opengear_wan_address: ipaddress.IPv4Address | None
+    opengear_wan_netmask: ipaddress.IPv4Address | None
+    opengear_wan_gateway: ipaddress.IPv4Address | None
 
 
 class OpengearBlock(OpengearBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """An Opengear that's currently deployed in the network."""
+    """An Opengear that's currently deployed in the network.
+
+    Attributes:
+        opengear_hostname: The hostname of the Opengear device.
+        opengear_site: The site where the Opengear device is located.
+        opengear_wan_address: The WAN address of the Opengear device.
+        opengear_wan_netmask: The WAN netmask of the Opengear device.
+        opengear_wan_gateway: The WAN gateway of the Opengear device.
+    """
 
-    #: The hostname of the Opengear device.
     opengear_hostname: str
-    #: The site where the Opengear device is located.
     opengear_site: SiteBlock
-    #: The WAN address of the Opengear device.
     opengear_wan_address: ipaddress.IPv4Address
-    #: The WAN netmask of the Opengear device.
     opengear_wan_netmask: ipaddress.IPv4Address
-    #: The WAN gateway of the Opengear device.
     opengear_wan_gateway: ipaddress.IPv4Address
diff --git a/gso/products/product_blocks/pop_vlan.py b/gso/products/product_blocks/pop_vlan.py
index a883477de2a535f29f4f127d1805621a30afb4db..a26e072d11089a0ecadbcfbc7b7cb75cdedffde7 100644
--- a/gso/products/product_blocks/pop_vlan.py
+++ b/gso/products/product_blocks/pop_vlan.py
@@ -84,19 +84,22 @@ class PopVlanBlockProvisioning(PopVlanBlockInactive, lifecycle=[SubscriptionLife
 
 
 class PopVlanBlock(PopVlanBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """A Pop VLAN that's currently deployed in the network."""
+    """A Pop VLAN that's currently deployed in the network.
+
+    Attributes:
+        vlan_id: The VLAN ID of the Pop VLAN.
+        pop_vlan_description: The description of the Pop VLAN.
+        lan_switch_interconnect: The LAN Switch Interconnect that this Pop VLAN is connected to.
+        ports: The ports of the Pop VLAN.
+        layer_preference: The level of the layer preference for the Pop VLAN (L2 or L3).
+        ipv4_network: IPv4 network for the Pop VLAN if layer preference is L3.
+        ipv6_network: IPv6 network for the Pop VLAN if layer preference is L3.
+    """
 
-    #: The VLAN ID of the Pop VLAN.
     vlan_id: int
-    #: The description of the Pop VLAN.
     pop_vlan_description: str
-    #: The LAN Switch Interconnect that this Pop VLAN is connected to.
     lan_switch_interconnect: LanSwitchInterconnectBlock
-    #: The ports of the Pop VLAN.
     ports: PortList[PopVlanPortBlock]  # type: ignore[assignment]
-    #: The level of the layer preference for the Pop VLAN (L2 or L3).
     layer_preference: LayerPreference
-    #: IPv4 network for the Pop VLAN if layer preference is L3.
     ipv4_network: IPv4Network | None
-    #: IPv6 network for the Pop VLAN if layer preference is L3.
     ipv6_network: IPv6Network | None
diff --git a/gso/products/product_blocks/site.py b/gso/products/product_blocks/site.py
index 4d5e479c1b41d9f307282a8e018bcbae1a93c3ed..22cc4940094ca50d494bc90507466a57b125e299 100644
--- a/gso/products/product_blocks/site.py
+++ b/gso/products/product_blocks/site.py
@@ -58,31 +58,35 @@ class SiteBlockProvisioning(SiteBlockInactive, lifecycle=[SubscriptionLifecycle.
 
 
 class SiteBlock(SiteBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """A site that's currently available for routers and services to be hosted at."""
+    """A site that's currently available for routers and services to be hosted at.
+
+    Attributes:
+        site_name: The name of the site, that will dictate part of the FQDN of routers that are hosted at this site. For
+            example: `router.X.Y.geant.net`, where X denotes the name of the site.
+        site_city: The city at which the site is located.
+        site_country: The country in which the site is located.
+        site_country_code: The code of the corresponding country. This is also used for the FQDN, following the example
+            given for the site name, the country code would end up in the Y position.
+        site_latitude: The latitude of the site, used for SNMP purposes.
+        site_longitude: Similar to the latitude, the longitude of a site.
+        site_internal_id: The internal ID used within GÉANT to denote a site.
+        site_bgp_community_id: The BGP community ID of a site, used to advertise routes learned at this site.
+        site_tier: The tier of a site, as described in `SiteTier`.
+        site_ts_address: The address of the terminal server that this router is connected to. The terminal server
+            provides out of band access. This is required in case a link goes down, or when a router is initially added
+            to the network, and it does not have any IP trunks connected to it.
+        site_contains_optical_equipment: Whether this site contains optical equipment, which dictates the need for a DCN
+            management VLAN.
+    """
 
-    #:  The name of the site, that will dictate part of the FQDN of routers that are hosted at this site. For
-    #:  example: `router.X.Y.geant.net`, where X denotes the name of the site.
     site_name: SiteName
-    #:  The city at which the site is located.
     site_city: str
-    #:  The country in which the site is located.
     site_country: str
-    #:  The code of the corresponding country. This is also used for the FQDN, following the example given for
-    #:  the site name, the country code would end up in the Y position.
     site_country_code: str
-    #:  The latitude of the site, used for SNMP purposes.
     site_latitude: LatitudeCoordinate
-    #:  Similar to the latitude, the longitude of a site.
     site_longitude: LongitudeCoordinate
-    #:  The internal ID used within GÉANT to denote a site.
     site_internal_id: int
-    #:  The BGP community ID of a site, used to advertise routes learned at this site.
     site_bgp_community_id: int
-    #:  The tier of a site, as described in `SiteTier`.
     site_tier: SiteTier
-    #:  The address of the terminal server that this router is connected to. The terminal server provides out of band
-    #:  access. This is required in case a link goes down, or when a router is initially added to the network and it
-    #:  does not have any IP trunks connected to it.
     site_ts_address: IPAddress
-    #:  Whether this site contains optical equipment, which dictates the need for a DCN management VLAN
     site_contains_optical_equipment: bool
diff --git a/gso/products/product_blocks/super_pop_switch.py b/gso/products/product_blocks/super_pop_switch.py
index 7c0b1c4a5ed358136d9e73168a7293753d7a1ddd..3c436e071b0d0f86d6f5264be637e733532a1b25 100644
--- a/gso/products/product_blocks/super_pop_switch.py
+++ b/gso/products/product_blocks/super_pop_switch.py
@@ -23,29 +23,33 @@ class SuperPopSwitchBlockInactive(
     super_pop_switch_ts_port: PortNumber | None = None
     super_pop_switch_mgmt_ipv4_address: IPv4AddressType | None = None
     super_pop_switch_site: SiteBlockInactive | None
-    vendor: Vendor | None = None
+    vendor: Vendor | None = Vendor.JUNIPER
 
 
 class SuperPopSwitchBlockProvisioning(SuperPopSwitchBlockInactive, lifecycle=[SubscriptionLifecycle.PROVISIONING]):
     """A Super PoP switch that's being provisioned. See `SuperPoPSwitchBlock`."""
 
-    super_pop_switch_fqdn: str | None = None
-    super_pop_switch_ts_port: PortNumber | None = None
-    super_pop_switch_mgmt_ipv4_address: IPv4AddressType | None = None
+    super_pop_switch_fqdn: str | None
+    super_pop_switch_ts_port: PortNumber | None
+    super_pop_switch_mgmt_ipv4_address: IPv4AddressType | None
     super_pop_switch_site: SiteBlockProvisioning | None
-    vendor: Vendor | None = None
+    vendor: Vendor | None
 
 
 class SuperPopSwitchBlock(SuperPopSwitchBlockProvisioning, lifecycle=[SubscriptionLifecycle.ACTIVE]):
-    """A Super PoP switch that's currently deployed in the network."""
+    """A Super PoP switch that's currently deployed in the network.
+
+    Attributes:
+        super_pop_switch_fqdn: Super PoP switch FQDN.
+        super_pop_switch_ts_port: The port of the terminal server that this Super PoP switch is connected to. Used to
+            offer out of band access.
+        super_pop_switch_mgmt_ipv4_address: The IPv4 management address of the Super PoP switch.
+        super_pop_switch_site: The `Site` that this Super PoP switch resides in. Both physically and computationally.
+        vendor: The vendor of a Super PoP switch. Defaults to Juniper.
+    """
 
-    #:  Super PoP switch FQDN.
     super_pop_switch_fqdn: str
-    #:  The port of the terminal server that this Super PoP switch is connected to. Used to offer out of band access.
     super_pop_switch_ts_port: PortNumber
-    #:  The IPv4 management address of the Super PoP switch.
     super_pop_switch_mgmt_ipv4_address: IPv4AddressType
-    #:  The `Site` that this Super PoP switch resides in. Both physically and computationally.
     super_pop_switch_site: SiteBlock
-    #:  The vendor of a Super PoP switch. Defaults to Juniper.
-    vendor: Vendor = Vendor.JUNIPER
+    vendor: Vendor
diff --git a/gso/services/infoblox.py b/gso/services/infoblox.py
index a0f5df25907aec7f248d898dfb1035078c5c1716..49ca5d3ec25e78d3b12287d24e3b62bbbdaf1116 100644
--- a/gso/services/infoblox.py
+++ b/gso/services/infoblox.py
@@ -86,7 +86,7 @@ def create_v4_network_by_ip(
     """Register an IPv4 network at the given location.
 
     Raises:
-        AllocationError on failure.
+        AllocationError: on failure.
     """
     conn, _ = _setup_connection()
     created_net = objects.NetworkV4.create(
@@ -105,7 +105,7 @@ def create_v6_network_by_ip(
     """Register an IPv6 network at the given location.
 
     Raises:
-        AllocationError on failure.
+        AllocationError: on failure.
     """
     conn, _ = _setup_connection()
     created_net = objects.NetworkV6.create(
@@ -123,9 +123,9 @@ def hostname_available(hostname: str) -> bool:
 
     Check whether Infoblox already has a ``infoblox_client.objects.HostRecord`` that matches the given hostname.
 
-    .. warning::
-       This method only checks within the Infoblox instance, and not the rest of the internet. The hostname could
-       therefore still be taken elsewhere.
+    !!! danger
+        This method only checks within the Infoblox instance, and not the rest of the internet. The hostname could
+        therefore still be taken elsewhere.
 
     Args:
         hostname: The hostname to be checked.
diff --git a/gso/services/kentik_client.py b/gso/services/kentik_client.py
index 3e524ccb95f4c5e2e294817d1f915a62ef66b895..86e81a0c418d251e9c385b8273706388f1f9efc6 100644
--- a/gso/services/kentik_client.py
+++ b/gso/services/kentik_client.py
@@ -101,10 +101,8 @@ class KentikClient:
 
         If the site is not found, return an empty dict.
 
-        .. vale off
-
         Args:
-            site_slug: The name of the site, should be a three-letter slug like COR or POZ.
+            site_slug: The name of the site, should be a three-letter slug like `COR` or `POZ`.
         """
         sites = self.get_sites()
         for site in sites:
@@ -116,40 +114,42 @@ class KentikClient:
     def get_plans(self) -> list[dict[str, Any]]:
         """Get all Kentik plans available.
 
+        Returns a list of `plan` objects that each have the following shape:
+
+        <!-- vale off -->
+        ```py
+        "plan": {
+            "active": true,
+            "bgp_enabled": true,
+            "cdate" "1970-01-01T01:01:01.000Z",
+            "company_id": 111111,
+            "description": "A description of this plan",
+            "deviceTypes": [
+                {"device_type": "router"},
+                {"device_type": "host-nprobe-dns-www"}
+            ],
+            "devices": [
+                {
+                    "id": "111111",
+                    "device_name": "rt0.city.tld.internal",
+                    "device_type": "router"
+                },
+            ],
+            "edate": "2999-01-01T09:09:09.000Z",
+            "fast_retention": 10,
+            "full_retention": 5,
+            "id": 11111,
+            "max_bigdata_fps": 100,
+            "max_devices": 9001,
+            "max_fps": 200,
+            "name": "KENTIK-PLAN-01",
+            "metadata": {},
+        }
+        ```
+        <!-- vale on -->
+
         Returns:
-            a list of `plans` that each have the following shape:
-
-            .. vale off
-            .. code-block:: json
-
-                "plan": {
-                    "active": true,
-                    "bgp_enabled": true,
-                    "cdate" "1970-01-01T01:01:01.000Z",
-                    "company_id": 111111,
-                    "description": "A description of this plan",
-                    "deviceTypes": [
-                        {"device_type": "router"},
-                        {"device_type": "host-nprobe-dns-www"}
-                    ],
-                    "devices": [
-                        {
-                            "id": "111111",
-                            "device_name": "rt0.city.tld.internal",
-                            "device_type": "router"
-                        },
-                    ],
-                    "edate": "2999-01-01T09:09:09.000Z",
-                    "fast_retention": 10,
-                    "full_retention": 5,
-                    "id": 11111,
-                    "max_bigdata_fps": 100,
-                    "max_devices": 9001,
-                    "max_fps": 200,
-                    "name": "KENTIK-PLAN-01",
-                    "metadata": {},
-                }
-            .. vale on
+            A list of plans configured in Kentik.
         """
         return self._send_request("GET", "v5/plans")["plans"]
 
diff --git a/gso/services/lso_client.py b/gso/services/lso_client.py
index 2557986095b9b4ef1391cdc97459d65a82fd077d..7c6e49a28ca5d83e985037174aa0e31d0b4a5822 100644
--- a/gso/services/lso_client.py
+++ b/gso/services/lso_client.py
@@ -73,37 +73,37 @@ def _execute_playbook(
     For example, an inventory consisting of two hosts, which each a unique host variable assigned to them looks as
     follows:
 
-    .. vale off
-    .. code-block:: json
-
-        "inventory": {
-            "all": {
-                "hosts": {
-                    "host1.local": {
-                        "foo": "bar"
-                    },
-                    "host2.local": {
-                        "key": "value"
-                    },
-                    "host3.local": None
-                }
+    ```py
+    "inventory": {
+        "all": {
+            "hosts": {
+                "host1.local": {
+                    "foo": "bar"
+                },
+                "host2.local": {
+                    "key": "value"
+                },
+                "host3.local": None
             }
         }
-    .. vale on
+    }
+    ```
 
-    .. warning::
-       Note the fact that the collection of all hosts is a dictionary, and not a list of strings. Ansible expects each
-       host to be a key-value pair. The key is the FQDN of a host, and the value always `null`.
+    !!! danger
+        Note the fact that the collection of all hosts is a dictionary, and not a list of strings. Ansible expects each
+        host to be a key-value pair. The key is the FQDN of a host, and the value always `null`.
 
     The extra vars can be a simple dict consisting of key-value pairs, for example:
 
-    .. code-block:: json
-
-        "extra_vars": {
-            "dry_run": true,
-            "commit_comment": "I am a robot!",
-            "verb": "deploy"
-        }
+    <!-- vale off -->
+    ```py
+    "extra_vars": {
+        "dry_run": true,
+        "commit_comment": "I am a robot!",
+        "verb": "deploy"
+    }
+    ```
+    <!-- vale on -->
 
     Args:
         playbook_name: Filename of the playbook that is to be executed. It must be present on the remote system running
@@ -111,7 +111,7 @@ def _execute_playbook(
         callback_route: The endpoint at which GSO expects a callback to continue the workflow executing this step.
         inventory: An inventory of machines at which the playbook is targeted. Must be in YAML-compatible format.
         extra_vars: Any extra variables that the playbook relies on. This can include a subscription object, a boolean
-            value indicating a dry run, a commit comment, etc.  All unicode character values are decoded to prevent
+            value indicating a dry run, a commit comment, etc. All unicode character values are decoded to prevent
             sending special characters to remote machines that don't support this.
     """
     parameters = {
@@ -169,7 +169,7 @@ def _clean_state() -> State:
     }
 
 
-def _inventory_is_set(state: State) -> bool:
+def validate_inventory_is_set(state: State) -> bool:
     """Validate whether the passed Ansible inventory is empty.
 
     If the inventory is empty, which can happen in select cases, there should be no playbook run. This conditional will
@@ -186,7 +186,7 @@ def _inventory_is_set(state: State) -> bool:
     return state["inventory"]["all"]["hosts"]
 
 
-_inventory_is_not_empty = conditional(_inventory_is_set)
+_inventory_is_not_empty = conditional(validate_inventory_is_set)
 
 
 def lso_interaction(provisioning_step: Step) -> StepList:
@@ -228,9 +228,9 @@ def indifferent_lso_interaction(provisioning_step: Step) -> StepList:
     Whereas the `lso_interaction()` will make the workflow step fail on unsuccessful interaction, this step will not.
     It is therefore indifferent about the outcome of the Ansible playbook that is executed.
 
-    .. warning::
-       Using this interaction requires the operator to carefully evaluate the outcome of a playbook themselves. If a
-       playbook fails, this will not cause the workflow to fail.
+    !!! danger
+        Using this interaction requires the operator to manually evaluate the outcome of a playbook. If a playbook
+        fails, this will not cause the workflow to fail.
 
     Args:
         provisioning_step: A workflow step that performs an operation remotely using the provisioning proxy.
diff --git a/gso/services/netbox_client.py b/gso/services/netbox_client.py
index 4b6560b9cf78fb07ffde0e5b2604620c35844990..2928263c920da52e0993fbb43ddc4dbfa2d20283 100644
--- a/gso/services/netbox_client.py
+++ b/gso/services/netbox_client.py
@@ -319,7 +319,7 @@ class NetboxClient:
 
     @staticmethod
     def calculate_speed_bits_per_sec(speed: str) -> int:
-        """Extract the numeric part from the speed."""
+        """Calculate the numeric part from the speed."""
         numeric_part = int("".join(filter(str.isdigit, speed)))
         # Convert to bits per second
         return numeric_part * 1000000
diff --git a/gso/settings.py b/gso/settings.py
index 5b3beefd963591541845255ddef67a6a1e0234ab..d65ead42c5c43e1aacac30f3c44616194fc819ad 100644
--- a/gso/settings.py
+++ b/gso/settings.py
@@ -129,9 +129,12 @@ class SNMPParams(BaseSettings):
     """Parameters for SNMP in LibreNMS."""
 
     v2c: MonitoringSNMPV2Params
-    #: .. versionadded :: 2.0
-    #:    Support for SNMP v3 will get added in a later version of GSO. Parameters are optional for now.
     v3: MonitoringSNMPV3Params | None = None
+    """
+    !!! example "Optional parameter"
+
+        Support for SNMP v3 will get added in a later version of GSO. Parameters are optional for now.
+    """
 
 
 class MonitoringParams(BaseSettings):
@@ -157,7 +160,12 @@ class NetBoxParams(BaseSettings):
 
 
 class EmailParams(BaseSettings):
-    """Parameters for the email service."""
+    """Parameters for the email service.
+
+    Attributes:
+        notification_email_destinations: List of email addresses that should receive notifications when validation of a
+            subscription fails. Can be a comma-separated list of multiple addresses.
+    """
 
     from_address: EmailStr
     smtp_host: str
@@ -165,8 +173,6 @@ class EmailParams(BaseSettings):
     starttls_enabled: bool
     smtp_username: str | None = None
     smtp_password: str | None = None
-    #: List of email addresses that should receive notifications when validation of a subscription fails.
-    #: Can be a comma-separated list of multiple addresses.
     notification_email_destinations: str
 
 
diff --git a/gso/workflows/router/validate_router.py b/gso/workflows/router/validate_router.py
index b029db47ab9233f1f3624628852f2692abbf5aec..89a05004d48742ebdecff943b95f2914a651edba 100644
--- a/gso/workflows/router/validate_router.py
+++ b/gso/workflows/router/validate_router.py
@@ -55,7 +55,7 @@ def check_netbox_entry_exists(subscription: Router) -> None:
 
 @step("Verify P BGP P-ONLY neighbors")
 def verify_p_ibgp(subscription: dict[str, Any]) -> LSOState:
-    """Verify PE neighbors in P-ONLY group on a P router."""
+    """Verify PE neighbors in `P-ONLY` group on a P router."""
     extra_vars = {
         "dry_run": True,
         "subscription": subscription,
@@ -100,7 +100,7 @@ def verify_pe_mesh_in_pe(subscription: dict[str, Any]) -> LSOState:
 
 @step("Verify PE BGP P-ONLY neighbors")
 def verify_all_p_in_pe(subscription: dict[str, Any]) -> LSOState:
-    """Verify P neighbors in P-ONLY group on a PE router."""
+    """Verify P neighbors in `P-ONLY` group on a PE router."""
     extra_vars = {
         "dry_run": True,
         "subscription": subscription,
diff --git a/gso/workflows/site/terminate_site.py b/gso/workflows/site/terminate_site.py
index 8f13e3589f2c9b04737567448fb8265587fd97a1..92da5ccae5479a50211e7cf89958991a1c9ceadb 100644
--- a/gso/workflows/site/terminate_site.py
+++ b/gso/workflows/site/terminate_site.py
@@ -1,8 +1,8 @@
 """A workflow for terminating a site subscription.
 
 The `terminate_site` workflow will take an existing and active site subscription from an `ACTIVE` to a `TERMINATED`
-state. This requires all dependant subscription instances to already be terminated. If this is not the case, the
-workflow will be unavailable for an operator to run, accompanied by an error message explaining this fact.
+state. This requires all dependant subscription instances to already be terminated. If not, the workflow will be
+unavailable for an operator to run, accompanied by an error message explaining this fact.
 """
 
 from orchestrator.forms import FormPage
diff --git a/gso/workflows/tasks/validate_geant_products.py b/gso/workflows/tasks/validate_geant_products.py
index a7bfcdaaaaca2a1b7d3b84766629829716530e3b..8fe39f61b762f46683336f802af1885d447ab339 100644
--- a/gso/workflows/tasks/validate_geant_products.py
+++ b/gso/workflows/tasks/validate_geant_products.py
@@ -1,6 +1,6 @@
 """A task that checks for all products in the database to be well-kept."""
 
-# .. vale off
+# <!-- vale off -->
 # Copyright 2019-2020 SURF.
 # Copyright 2024 GÉANT Vereniging.
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -14,7 +14,7 @@
 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 # See the License for the specific language governing permissions and
 # limitations under the License.
-# .. vale on
+# <!-- vale on -->
 
 from orchestrator.targets import Target
 from orchestrator.workflow import StepList, done, init, workflow
diff --git a/test/fixtures/site_fixtures.py b/test/fixtures/site_fixtures.py
index c6a884b1eb8fe54e482057c7e5f0fd452b23e3bd..2467bd89067602cfd8b99270b81a4a350efe93e5 100644
--- a/test/fixtures/site_fixtures.py
+++ b/test/fixtures/site_fixtures.py
@@ -52,7 +52,7 @@ def site_subscription_factory(faker, geant_partner):
         site_subscription.site.site_latitude = site_latitude or str(faker.latitude())
         site_subscription.site.site_longitude = site_longitude or str(faker.longitude())
         site_subscription.site.site_bgp_community_id = site_bgp_community_id or faker.pyint()
-        site_subscription.site.site_internal_id = site_internal_id or faker.pyint(max_value=254)
+        site_subscription.site.site_internal_id = site_internal_id or faker.pyint(min_value=1, max_value=254)
         site_subscription.site.site_tier = site_tier or SiteTier.TIER1
         site_subscription.site.site_ts_address = site_ts_address or faker.ipv4()
         site_subscription.site.site_contains_optical_equipment = site_contains_optical_equipment