How to get a section of the menu tree based on the current node?
Found a way to satisfy all the cases without having to create multiple blocks (visibility rules would be odd) or create different menus. I could not achieve this by assuming a content type like in the question, because you could have the same content type nested until multiple areas of the menu, and not really know if the current depth you were at for it qualified as the parent (where the menu block should "begin" - not necessarily the parent of the page).

I decided to cheat a bit and use the URL and the alias pattern. If the current path matched a pattern, and the URL consisted of a certain amount of segments, I could tell I was in a specific part of the menu where the behavior should be different than others. In this case, it should use the current page and its children, instead of the parent of the current page and its siblings:

class MenuBlockEnhanced extends MenuBlock {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $node = \Drupal::routeMatch()-&gt;getParameter('node');

    // If we are not on a node route, get out.
    if (!$node) {
      return parent::build();
    }

    // If the menu being rendered is not specifically the sub nav, get out.
    if ($this-&gt;configuration["suggestion"] != 'subnav') {
      return parent::build();
    }

    $current_path = \Drupal::request()-&gt;getRequestUri();
    $parts = explode('/', $current_path);
    $build = parent::build();

    /** @var \Drupal\Core\Menu\MenuLinkManager $menu_link_manager */
    $menu_link_manager = \Drupal::service('plugin.manager.menu.link');
    $route_params = array('node' =&gt; $node-&gt;id());
    $menu_links = $menu_link_manager-&gt;loadLinksByRoute('entity.node.canonical', $route_params, 'main');
    $set_parent = TRUE;

    $params = new MenuTreeParameters();

    // I don't really know of another way to 'tell' we are on a project, since
    // a project can have projects under it. We can only rely on the path and
    // how many 'parts' are in the path - anything over 4 parts would be anything
    // nested in this section.
    if (preg_match('/programs-projects/i', $current_path) &amp;&amp; count($parts) == 4) {
      $parent = reset($menu_links)-&gt;getPluginId();
      $set_parent = FALSE;
    } else {
      $parent = reset($menu_links)-&gt;getParent();
    }

    $uuid = str_replace('menu_link_content:', '', $parent);
    $parent_menu_content = current(\Drupal::entityTypeManager()
     -&gt;getStorage('menu_link_content')
     -&gt;loadByProperties(array('uuid' =&gt; $uuid)));

    $params-&gt;setRoot($parent);
    $params-&gt;setActiveTrail([$parent, reset($menu_links)-&gt;getPluginId()]);
    $tree = $this-&gt;menuTree-&gt;load($parent, $params);

    // Transform the tree using the manipulators you want.
    $manipulators = array(
      // Only show links that are accessible for the current user.
      array('callable' =&gt; 'menu.default_tree_manipulators:checkAccess'),
      // Use the default sorting of menu links.
      array('callable' =&gt; 'menu.default_tree_manipulators:generateIndexAndSort'),
    );
    $tree = $this-&gt;menuTree-&gt;transform($tree, $manipulators);

    $link = end($parent_menu_content-&gt;link-&gt;getValue())['uri'];

    $element = $this-&gt;menuTree-&gt;build($tree);
    $build['#items'] = $element['#items'][$parent]['below'];

    if ($link != 'route:&lt;nolink&gt;' &amp;&amp; $set_parent) {
      $build['#top_parent'] = $element['#items'][$parent];
    }

    if (empty($build['#cache']['contexts'])) {
      $build['#cache']['contexts'] = ['user.permissions'];
      $build['#cache']['tags'] = ['config:system.menu.main'];
    }

    return $build;
  }

}


The caching strategy comes right from what would normally be there, frankly I probably do not even need to specify it since I am already calling parent::build() and just reduce that code.

In the template, I resolve the "do I print just siblings, or this page and its children?" by using the in_active_path check, and faking a control break in Twig macro (see: break = true):

{% import _self as menus %}

{{ menus.menu_links(items, attributes, 0, top_parent) }}

{% macro menu_links(items, attributes, menu_level, top_parent) %}
  {% import _self as menus %}

  {% if items %}
    {% if top_parent %}
      &lt;ul class="secondary-nav__list"&gt;
        {%
          set parent_list_classes =
          [
            'secondary-nav__item',
            'secondary-nav__item--parent'
          ]
        %}

        {% set parent_link_classes =
          [
            'link',
            'link--left-arrow',
          ]
        %}

        &lt;li{{ top_parent.attributes.addClass(parent_list_classes) }}&gt;
          {{ link(top_parent.title, top_parent.url, { 'class': parent_link_classes }) }}
          {{ menus.menu_child_links(items, attributes, 0) }}
        &lt;/li&gt;
      &lt;/ul&gt;
    {% else %}
      {{ menus.menu_child_links(items, attributes, 0) }}
    {% endif %}
  {% endif %}
{% endmacro %}

{% macro menu_child_links(items, attributes, menu_level) %}
  {% import _self as menus %}

  &lt;ul class="secondary-nav__list"&gt;
    {% set break = false %}
    {% for item in items if not break %}
      {%
        set list_classes =
        [
          'secondary-nav__item',
          item.in_active_trail ? 'is-active'
        ]
      %}

      &lt;li{{ item.attributes.addClass(list_classes) }}&gt;
        {{ link(item.title, item.url) }}

        {% if item.below and item.in_active_trail %}
          {% set break = true %}
          {{ menus.menu_child_links(item.below, attributes, menu_level + 1) }}
        {% endif %}
      &lt;/li&gt;
    {% endfor %}
  &lt;/ul&gt;
{% endmacro %}


The menu works exactly as asked for all cases, and will go all the way down to its maximum depth and back up the chain (with the parent blue link appearing up top).