mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
a130f6b8ee
Move handlers/ecs/bus to contracts/ecs/v1 (package ecsv1) with no legacy shim. Subject strings, JSON fields and types are byte-identical; the old-agent compatibility test moves unchanged and golden pins cover the remaining message types. The README route table records that only service-reconcile has a live producer today; the agent reuses these payload types through the gateway actions. service-interfaces.yaml lists ecs.bus.* under the daemon.
502 lines
19 KiB
YAML
502 lines
19 KiB
YAML
# Service interface manifest.
|
|
#
|
|
# Source of truth for "which services produce/consume which NATS subjects"
|
|
# and "which e2e suites cover which services". Consumed by
|
|
# tests/e2e/select-suites (Bead 4) to derive targeted suite selection from
|
|
# a PR diff.
|
|
#
|
|
# See docs/development/improvements/e2e-targeted-suite-selection.md.
|
|
#
|
|
# Editing rules
|
|
# -------------
|
|
# - subscribes / publishes: subjects this service receives / sends. Both
|
|
# lists use the literal subject string, or a "<prefix>.*" wildcard for
|
|
# pattern subscriptions (e.g. "ebs.snapshot.*").
|
|
# - depends_on: subjects this service requires from peers. Used by the
|
|
# selector to compute "if X changes, which suites might break".
|
|
# - Subjects must match what is actually referenced in source. The lint
|
|
# in Bead 5 greps `"(ec2|vpc|ebs|s3|iam|elbv2|spinifex)\.[a-zA-Z._-]+"`
|
|
# and fails if any subject is absent from this manifest.
|
|
|
|
version: 1
|
|
|
|
services:
|
|
|
|
awsgw:
|
|
path: spinifex/runtime/roles/awsgw
|
|
# TLS frontend, SigV4 auth. Pure router: HTTP → NATS request/response.
|
|
# No subscribes; only publishes (via NATSRequest).
|
|
subscribes: []
|
|
publishes:
|
|
- "ec2.*"
|
|
- "elbv2.*"
|
|
- "acm.*"
|
|
- "iam.*"
|
|
- "rds.*"
|
|
- "ochre.vector.*"
|
|
depends_on:
|
|
- "ec2.*"
|
|
- "elbv2.*"
|
|
- "acm.*"
|
|
- "iam.*"
|
|
- "rds.*"
|
|
- "ochre.vector.*"
|
|
|
|
spinifex_daemon:
|
|
path: spinifex/runtime/roles/spinifex
|
|
# Orchestrator. Owns most ec2.* + all elbv2.* + iam event publication.
|
|
# Translates VPC / EBS / S3 operations into vpc.* / ebs.* / s3.* and
|
|
# forwards to vpcd / viperblockd / predastore.
|
|
#
|
|
# Source layout: spinifex/runtime/roles/spinifex (entry), spinifex/daemon
|
|
# (subscribe registration), spinifex/domains/ec2/<resource> and spinifex/handlers/{ecs,elbv2,iam,eks} (per-
|
|
# subject handler bodies). Selector treats these paths as belonging
|
|
# to this service via the `additional_paths` field.
|
|
additional_paths:
|
|
- spinifex/daemon
|
|
- spinifex/domains/ec2/account
|
|
- spinifex/domains/ec2/eigw
|
|
- spinifex/domains/ec2/eip
|
|
- spinifex/domains/ec2/igw
|
|
- spinifex/domains/ec2/image
|
|
- spinifex/domains/ec2/instance
|
|
- spinifex/domains/ec2/key
|
|
- spinifex/domains/ec2/launchtemplate
|
|
- spinifex/domains/ec2/natgw
|
|
- spinifex/domains/ec2/placementgroup
|
|
- spinifex/domains/ec2/routetable
|
|
- spinifex/domains/ec2/snapshot
|
|
- spinifex/domains/ec2/spotinstance
|
|
- spinifex/domains/ec2/tags
|
|
- spinifex/domains/ec2/volume
|
|
- spinifex/domains/ec2/volumestate
|
|
- spinifex/domains/ec2/vpc
|
|
- spinifex/handlers/elbv2
|
|
- spinifex/domains/acm
|
|
- spinifex/handlers/iam
|
|
- spinifex/handlers/ecs
|
|
- spinifex/handlers/rds
|
|
- spinifex/domains/rds/subnetgroup
|
|
- spinifex/domains/rds/parametergroup
|
|
- spinifex/domains/rds/engine
|
|
- spinifex/domains/ecs/taskdefinition
|
|
- spinifex/handlers/eks
|
|
- spinifex/domains/eks/access
|
|
- spinifex/domains/eks/addon
|
|
# Guest add-on wire contract (subject, report, staged-manifest body).
|
|
- contracts/eks/v1
|
|
# ECS agent/scheduler Layer-2 bus wire contract (subjects, message types).
|
|
- contracts/ecs/v1
|
|
- spinifex/domains/ochre/vector
|
|
subscribes:
|
|
# EC2 instance lifecycle.
|
|
- "ec2.RunInstances"
|
|
- "ec2.DescribeInstances"
|
|
- "ec2.DescribeStoppedInstances"
|
|
- "ec2.DescribeTerminatedInstances"
|
|
- "ec2.DescribeInstanceAttribute"
|
|
- "ec2.DescribeInstanceStatus"
|
|
- "ec2.DescribeInstanceTypes"
|
|
- "ec2.ModifyInstanceAttribute"
|
|
- "ec2.ModifyVolume"
|
|
- "ec2.start"
|
|
- "ec2.terminate"
|
|
- "ec2.launch"
|
|
# contracts/ec2/v1 owns the command payload and this subject vocabulary.
|
|
# The wildcard is intentionally one final instance-ID token, not `>`.
|
|
- "ec2.cmd.*"
|
|
# EC2 VPC / network ops handled by daemon before delegation to vpcd.
|
|
- "ec2.CreateVpc"
|
|
- "ec2.DescribeVpcs"
|
|
- "ec2.CreateSubnet"
|
|
- "ec2.CreateInternetGateway"
|
|
- "ec2.CreateKeyPair"
|
|
- "ec2.CreateImage"
|
|
- "ec2.DescribePlacementGroups"
|
|
# Cluster coordination subjects.
|
|
- "ec2.ReserveClusterNode"
|
|
- "ec2.FinalizeClusterInstances"
|
|
# An EIP association reaches whichever node holds the instance, which is
|
|
# rarely the node that served the request (fan-out, no queue group).
|
|
- "ec2.eni.public-ip-changed"
|
|
# contracts/cluster/v1 owns the node status/capacity payload and subject.
|
|
- "spinifex.node.status"
|
|
# contracts/cluster/v1 owns the VM inventory payload and subject.
|
|
- "spinifex.node.vms"
|
|
- "spinifex.nodes.discover"
|
|
- "spinifex.cluster.shutdown.progress"
|
|
# Coordinated cluster-shutdown phases (fan-out, no queue group).
|
|
- "spinifex.cluster.shutdown.gate"
|
|
- "spinifex.cluster.shutdown.drain"
|
|
- "spinifex.cluster.shutdown.storage"
|
|
- "spinifex.cluster.shutdown.persist"
|
|
- "spinifex.cluster.shutdown.infra"
|
|
# Operator-plane Predastore topology contract (contracts/operator/v1).
|
|
- "spinifex.storage.config"
|
|
# ELBv2 control plane (handlers/elbv2/service_nats.go topic map).
|
|
- "elbv2.CreateLoadBalancer"
|
|
- "elbv2.DeleteLoadBalancer"
|
|
- "elbv2.DescribeLoadBalancers"
|
|
- "elbv2.CreateTargetGroup"
|
|
- "elbv2.DeleteTargetGroup"
|
|
- "elbv2.DescribeTargetGroups"
|
|
- "elbv2.RegisterTargets"
|
|
- "elbv2.DeregisterTargets"
|
|
- "elbv2.DescribeTargetHealth"
|
|
- "elbv2.CreateListener"
|
|
- "elbv2.DeleteListener"
|
|
- "elbv2.DescribeListeners"
|
|
# ACM-alike certificate store (handlers/acm/service_nats.go topic map).
|
|
- "acm.ImportCertificate"
|
|
- "acm.DescribeCertificate"
|
|
- "acm.ListCertificates"
|
|
- "acm.DeleteCertificate"
|
|
- "elbv2.AddListenerCertificates"
|
|
- "elbv2.RemoveListenerCertificates"
|
|
- "elbv2.DescribeListenerCertificates"
|
|
- "elbv2.DescribeSSLPolicies"
|
|
# RDS control plane (handlers/rds/subjects.go). One wildcard rather than
|
|
# 30 literals: every action subject is registered from a constant, so a
|
|
# per-subject list would be a hand-mirror of that file with nothing
|
|
# checking it.
|
|
- "rds.*"
|
|
# Layer-2 agent bus: register and health are wildcard subscriptions over
|
|
# {account}.{db-instance}, so the subject is built at runtime and only the
|
|
# prefix is declarable.
|
|
- "rds.bus.*"
|
|
# ECS Layer-2 scheduler/agent bus (contracts/ecs/v1): account, cluster and
|
|
# instance/task IDs are runtime-built, so only the prefix is declarable.
|
|
# Only the leader holds these subscriptions, so there is no queue group.
|
|
- "ecs.bus.*"
|
|
# Ochre vector store tenant surface (domains/ochre/vector/api.go),
|
|
# gated behind config.OchreVectorConfig.Enabled (default off).
|
|
- "ochre.vector.createIndex"
|
|
- "ochre.vector.deleteIndex"
|
|
- "ochre.vector.listIndexes"
|
|
- "ochre.vector.ingest"
|
|
- "ochre.vector.describeJob"
|
|
- "ochre.vector.query"
|
|
- "ochre.vector.listJobs"
|
|
- "ochre.vector.stopJob"
|
|
# Fence route owned by contracts/viperblockd/legacy/v1: viperblockd
|
|
# announces a torn-down export so this node stops the guest that was
|
|
# using it. "ebs.fenced" is the single-node form; a named node is
|
|
# addressed as "ebs.<node>.fenced".
|
|
- "ebs.fenced"
|
|
publishes:
|
|
# → vpcd
|
|
- "vpc.create"
|
|
- "vpc.delete"
|
|
- "vpc.create-subnet"
|
|
- "vpc.delete-subnet"
|
|
- "vpc.create-sg"
|
|
- "vpc.delete-sg"
|
|
- "vpc.update-sg"
|
|
- "vpc.create-port"
|
|
- "vpc.delete-port"
|
|
- "vpc.update-port-sgs"
|
|
# EC2-to-network projection owned by contracts/ec2/v1. The network
|
|
# subscriber realizes it in OVN without reading EC2 private state.
|
|
- "vpc.igw-attach"
|
|
- "vpc.igw-detach"
|
|
- "vpc.add-nat-gateway"
|
|
- "vpc.delete-nat-gateway"
|
|
- "vpc.add-nat"
|
|
- "vpc.delete-nat"
|
|
- "vpc.dhcp.acquire"
|
|
- "vpc.dhcp.release"
|
|
# Per-subnet route-table projections owned by domains/ec2/routetable:
|
|
# IGW default-route reachability and the egress gate/ungate decision it
|
|
# implies, published fire-and-forget on every route-table mutation.
|
|
- "vpc.add-igw-route"
|
|
- "vpc.delete-igw-route"
|
|
- "vpc.gate-subnet-egress"
|
|
- "vpc.ungate-subnet-egress"
|
|
# Egress-only reroute removal for a single system instance
|
|
# (handlers/eks). Fire-and-forget. vpc.add-system-egress has no
|
|
# current producer.
|
|
- "vpc.delete-system-egress"
|
|
# → viperblockd
|
|
# The migrated legacy ebs.config and ebs.sync payloads and routes are
|
|
# owned by contracts/viperblockd/legacy/v1. They are transitional and
|
|
# distinct from the generic providers/ebs protocol; other legacy ebs.*
|
|
# routes remain to be migrated separately.
|
|
- "ebs.mount"
|
|
- "ebs.unmount"
|
|
- "ebs.delete"
|
|
- "ebs.sync"
|
|
- "ebs.snapshot.*"
|
|
- "ebs.config"
|
|
- "ebs.config.*"
|
|
# Abandon route owned by contracts/viperblockd/legacy/v1: the daemon
|
|
# asks viperblockd to tear an export down without sealing. "ebs.abandon"
|
|
# is the single-node form; a named node addresses "ebs.<node>.abandon".
|
|
- "ebs.abandon"
|
|
# → predastore (S3 / AMI / keypair store)
|
|
- "s3.*"
|
|
# → every daemon, so the one holding the instance reports the new address
|
|
- "ec2.eni.public-ip-changed"
|
|
# IAM lifecycle event
|
|
- "iam.account.created"
|
|
# → the in-guest rds-agent: set-password and the like, published on the
|
|
# per-instance command subject and answered on its reply subject.
|
|
- "rds.bus.*"
|
|
# ECS service-reconcile self-wake (contracts/ecs/v1): the worker that
|
|
# recorded a task-state change is rarely the leader, so this fans the
|
|
# wake out and the leader's own "ecs.bus.*" subscription picks it up.
|
|
- "ecs.bus.*"
|
|
depends_on:
|
|
- "vpc.*"
|
|
- "ebs.*"
|
|
- "s3.*"
|
|
|
|
vpcd:
|
|
path: spinifex/runtime/roles/vpcd
|
|
# OVN-backed VPC / subnet / SG / NAT / IGW data plane.
|
|
subscribes:
|
|
- "vpc.create"
|
|
- "vpc.delete"
|
|
- "vpc.create-subnet"
|
|
- "vpc.delete-subnet"
|
|
- "vpc.create-sg"
|
|
- "vpc.delete-sg"
|
|
- "vpc.update-sg"
|
|
- "vpc.create-port"
|
|
- "vpc.delete-port"
|
|
- "vpc.update-port-sgs"
|
|
- "vpc.igw-attach"
|
|
- "vpc.igw-detach"
|
|
- "vpc.add-nat-gateway"
|
|
- "vpc.delete-nat-gateway"
|
|
- "vpc.add-nat"
|
|
- "vpc.delete-nat"
|
|
- "vpc.dhcp.acquire"
|
|
- "vpc.dhcp.release"
|
|
- "vpc.add-igw-route"
|
|
- "vpc.delete-igw-route"
|
|
- "vpc.gate-subnet-egress"
|
|
- "vpc.ungate-subnet-egress"
|
|
- "vpc.add-system-egress"
|
|
- "vpc.delete-system-egress"
|
|
publishes:
|
|
- "vpc.port-status"
|
|
- "vpc.dhcp.lease-changed"
|
|
- "vpc.dhcp.owner-check"
|
|
depends_on: []
|
|
|
|
viperblockd:
|
|
path: spinifex/runtime/roles/viperblockd
|
|
# EBS data plane. Backed by the viperblock storage engine in the
|
|
# mulga/viperblock submodule. Submodule pointer bump → selector forces
|
|
# "run all" (contract changes can be invisible at the spinifex source
|
|
# level).
|
|
# The migrated legacy ebs.config and ebs.sync routes are versioned by
|
|
# contracts/viperblockd/legacy/v1, not by providers/ebs. Other legacy
|
|
# ebs.* routes remain to be migrated separately.
|
|
# The dirty-marker JetStream KV bucket VIPERBLOCK_VOLUME_DIRTY is owned
|
|
# by the same contract; not a NATS subject, so not listed below.
|
|
#
|
|
# Source layout: spinifex/runtime/roles/viperblockd (role: process lifecycle,
|
|
# NATS subscription wiring) and spinifex/providers/ebs/viperblock (the
|
|
# Viperblock EBS provider adapter the role wires up). Selector treats the
|
|
# adapter path as belonging to this service via `additional_paths`.
|
|
additional_paths:
|
|
- spinifex/providers/ebs/viperblock
|
|
subscribes:
|
|
- "ebs.mount"
|
|
- "ebs.unmount"
|
|
- "ebs.delete"
|
|
- "ebs.sync"
|
|
- "ebs.snapshot.*"
|
|
- "ebs.config"
|
|
- "ebs.config.*"
|
|
# Abandon route (contracts/viperblockd/legacy/v1): request/reply from
|
|
# the daemon, answered by the Viperblock adapter on the named node.
|
|
- "ebs.abandon"
|
|
publishes:
|
|
- "ebs.mount.response"
|
|
# Fence route (contracts/viperblockd/legacy/v1): announced after the
|
|
# Viperblock adapter tears an export down because its lease moved.
|
|
- "ebs.fenced"
|
|
- "ebs.unmount.response"
|
|
depends_on: []
|
|
|
|
predastore:
|
|
path: spinifex/runtime/roles/predastore
|
|
# S3 / AMI / keypair storage. External submodule under mulga/predastore;
|
|
# this dir contains only the spinifex-side adapter. Submodule pointer
|
|
# bump → selector forces "run all".
|
|
subscribes:
|
|
- "s3.*"
|
|
publishes: []
|
|
depends_on: []
|
|
|
|
nats:
|
|
path: spinifex/runtime/roles/nats
|
|
# NATS broker bootstrap / cluster config. Not a NATS pub/sub
|
|
# participant. Any change here is infra-path; selector forces
|
|
# "run all".
|
|
subscribes: []
|
|
publishes: []
|
|
depends_on: []
|
|
|
|
spinifexui:
|
|
path: spinifex/runtime/roles/spinifexui
|
|
# Web UI. Talks to awsgw over HTTP, never NATS. Diffs limited to this
|
|
# dir do not need any e2e suite to run.
|
|
subscribes: []
|
|
publishes: []
|
|
depends_on: []
|
|
covers_no_suite: true
|
|
|
|
|
|
# Fixture → service mapping. Each Ensure* helper in
|
|
# tests/e2e/harness/fixtures.go exercises this set of services; the
|
|
# Bead 4 selector AST-scans test files for harness.Ensure* call sites
|
|
# and resolves coverage via this block.
|
|
fixtures:
|
|
|
|
EnsureKeyPair:
|
|
services: [spinifex_daemon, predastore]
|
|
|
|
EnsureAMI:
|
|
services: [spinifex_daemon, predastore, viperblockd]
|
|
|
|
EnsureDefaultVPC:
|
|
services: [vpcd, spinifex_daemon]
|
|
|
|
EnsureSubnet:
|
|
services: [vpcd, spinifex_daemon]
|
|
|
|
EnsureSG:
|
|
services: [vpcd, spinifex_daemon]
|
|
|
|
EnsureInstance:
|
|
services: [spinifex_daemon, viperblockd, vpcd]
|
|
|
|
EnsureVolume:
|
|
services: [spinifex_daemon, viperblockd]
|
|
|
|
EnsureSnapshot:
|
|
services: [spinifex_daemon, viperblockd, predastore]
|
|
|
|
EnsureNATGateway:
|
|
services: [vpcd, spinifex_daemon]
|
|
|
|
EnsureLoadBalancer:
|
|
services: [spinifex_daemon, awsgw]
|
|
|
|
|
|
suites:
|
|
|
|
e2e-cert:
|
|
path: tests/e2e/cert
|
|
covers: [awsgw, spinifex_daemon, vpcd, predastore]
|
|
|
|
e2e-lb:
|
|
path: tests/e2e/lb
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# Exercises full ELBv2 control plane (handlers/elbv2) + data plane
|
|
# (LB agent on the system VM). ELBv2 is part of spinifex_daemon in
|
|
# this manifest; revisit if it splits into a standalone service.
|
|
|
|
e2e-single:
|
|
path: tests/e2e/single
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# Full single-node lifecycle: VPC, AMI, keypair, instance, volume,
|
|
# snapshot, IAM. Currently runs all phases sequentially under one
|
|
# `TestSingleNode`. Bead 3 (single-fixture-migration) splits this
|
|
# into per-test fixture-driven coverage so the selector can pick
|
|
# subtests, not just whole suites.
|
|
|
|
e2e-iam:
|
|
path: tests/e2e/iam
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# "Everything IAM": the IAM/STS control plane plus the instance-identity
|
|
# tests (instance-profile association + IMDS), which boot guest VMs — OVN
|
|
# via vpcd, AMI via predastore, root volume via viperblockd, lifecycle via
|
|
# spinifex_daemon. Single-mode, VM-booting leg.
|
|
|
|
e2e-ecr:
|
|
path: tests/e2e/ecr
|
|
covers: [awsgw, spinifex_daemon, predastore]
|
|
# ECR control plane (handlers/ecr, gateway/ecr*) + OCI data plane: blobs and
|
|
# manifests in predastore, control + auth bridge in awsgw, KV via
|
|
# spinifex_daemon. docker/crane/skopeo data-plane subtests skip when the
|
|
# client binary or registry-host resolution is absent.
|
|
|
|
e2e-bedrock:
|
|
path: tests/e2e/bedrock
|
|
covers: [awsgw]
|
|
# Ochre (AWS-Bedrock) control plane: static catalog list/get served
|
|
# straight from awsgw (gateway/bedrock.go, gateway/bedrock/) — no NATS
|
|
# hop to spinifex_daemon. Converse (Anthropic-direct / self-host vLLM)
|
|
# subtests are gated on backend availability and skip by default.
|
|
|
|
e2e-ecs:
|
|
path: tests/e2e/ecs
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# ECS data plane end-to-end: cluster + a real container instance launched
|
|
# from the spinifex-ecs-node AMI (boots, registers over the gateway, runs
|
|
# tasks through containerd), standalone tasks in awsvpc/bridge/host modes,
|
|
# and an awsvpc service fronted by an ELBv2 target group. Boots a guest VM,
|
|
# so vpcd (OVN + awsvpc ENI), viperblockd (root volume), predastore (AMI)
|
|
# are all in the path.
|
|
|
|
e2e-rds:
|
|
path: tests/e2e/rds
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# RDS create/describe/connect: CreateDBInstance boots a dual-NIC DB VM from
|
|
# the engine AMI (predastore) with a Viperblock data volume (viperblockd) and
|
|
# a customer ENI (vpcd), and the reconciler flips it to available on the
|
|
# in-guest agent's first healthy heartbeat over awsgw. Every test deletes the
|
|
# instances it creates, so the suite needs no gate beyond SPINIFEX_E2E.
|
|
|
|
e2e-ddil:
|
|
path: tests/e2e/ddil
|
|
covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# DDIL = degraded / disconnected / intermittent / low-bandwidth.
|
|
# Exercises the full daemon stack under network failure injection;
|
|
# any service touching the wire path is in scope.
|
|
|
|
# Suites below are bash-only today. Entries activate as the Go ports
|
|
# land (Bead 2 fixtures unblock new-shape ports for multinode etc.).
|
|
#
|
|
# e2e-multinode:
|
|
# path: spinifex/tests/e2e/multinode
|
|
# covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
# e2e-reboot:
|
|
# path: spinifex/tests/e2e/reboot
|
|
# covers: [spinifex_daemon, viperblockd]
|
|
# e2e-tofu:
|
|
# path: spinifex/tests/e2e/tofu
|
|
# covers: [awsgw, spinifex_daemon, vpcd]
|
|
# e2e-bm:
|
|
# path: spinifex/tests/e2e/baremetal
|
|
# covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
#
|
|
# e2e-natuplink activates once CI can provision an external_mode=nat
|
|
# node (setup-ovn.sh --nat-uplink + spx admin init --external-mode=nat).
|
|
# e2e-natuplink:
|
|
# path: spinifex/tests/e2e/natuplink
|
|
# covers: [awsgw, spinifex_daemon, vpcd, viperblockd, predastore]
|
|
|
|
|
|
# Special-case rules consumed by the selector (Bead 4). Hard-coded in
|
|
# the tool, listed here for visibility.
|
|
#
|
|
# infra_paths_force_all:
|
|
# - spinifex/runtime/roles/nats/**
|
|
# - spinifex/tests/e2e/harness/**
|
|
# - spinifex/tests/e2e/lib/**
|
|
# - spinifex/.github/workflows/e2e-go.yml
|
|
# - spinifex/.github/scripts/**
|
|
# - spinifex/Makefile
|
|
# - mulga/scripts/tofu-cluster/**
|
|
#
|
|
# submodule_force_all:
|
|
# - mulga/predastore (submodule pointer)
|
|
# - mulga/viperblock (submodule pointer)
|
|
#
|
|
# many_services_force_all_threshold: 3 # ≥ N services touched → run all
|