Decision Dispatcher Lifecycle (HyOps Module)¶
Execution context: Run this procedure from a terminal where hyops --help
succeeds. If HybridOps.Core is not installed, complete the
Quickstart.
For deployment support, use
GitHub Discussions.
- Purpose: Install the decision dispatcher that watches emitted decision records and writes normalized dispatch requests for downstream approval and execution.
- Trigger: Decision service is active and the control plane needs a separate handoff stage before any DR or burst workflow can run.
- Impact: Installs a
systemdservice on the control host, watches the decision-service records directory, and writes approval-gated request files under the dispatcher state directory. - Severity: P1
- Rollback strategy: Destroy the module state instance to remove the dispatcher service and its runtime directory.
Context¶
Module ref: platform/network/decision-dispatcher
This module exists to keep the control loop clean:
platform/network/decision-serviceevaluates signals and emits a decision record.platform/network/decision-dispatcherreads that record and turns it into a dispatch request.- a later consumer or operator approval path decides whether to execute the request.
Current execution mode is intentionally conservative:
record-only
In this mode, the dispatcher does not run hyops itself. It only writes structured request files that can be reviewed or consumed by a later execution component.
Each request also carries approval_payload_version and
approval_artifact_sha256. The digest covers the source decision identity,
decision type, execution plane, target kind, target reference, target
environment, approval posture, rationale, and checks. Generated runtime fields
such as the dispatch identifier, timestamp, and status are excluded.
Runtime paths on the control host:
- decision records:
/opt/hybridops/decision-service/state/records - dispatcher config:
/opt/hybridops/decision-dispatcher/config/config.json - dispatcher state:
/opt/hybridops/decision-dispatcher/state/state.json - dispatch requests:
/opt/hybridops/decision-dispatcher/state/requests/ - dispatcher log:
/opt/hybridops/decision-dispatcher/logs/dispatcher.log
Preconditions¶
- the control host is reachable through the normal inventory/SSH contract
platform/network/decision-serviceis already installed and healthydispatcher_routesis defined and maps decision types to target actions- route entries declare:
target_kindtarget_reftarget_envexecution_plane
Example route shape:
dispatcher_routes:
cutover:
target_kind: blueprint
target_ref: dr/postgresql-ha-failover-gcp@v1
target_env: dev
execution_plane: runner-local
requires_approval: true
failback:
target_kind: blueprint
target_ref: dr/postgresql-ha-failback-onprem@v1
target_env: dev
execution_plane: runner-local
requires_approval: true
Execute¶
hyops apply --env dev \
--module platform/network/decision-dispatcher \
--inputs "$HOME/.hybridops/envs/dev/config/modules/platform__network__decision-dispatcher/latest.inputs.yml"
Verification¶
Check module outputs:
jq '.status, .outputs' \
"$HOME/.hybridops/envs/dev/state/modules/platform__network__decision-dispatcher/latest.json"
Check live service state:
ssh -i "$HOME/.ssh/id_ed25519" opsadmin@5.161.116.216 \
'sudo cat /opt/hybridops/decision-dispatcher/state/state.json'
Success indicators:
- module state is
status=ok cap.control.decision_dispatcher = readydecision_dispatcher.status = readydecision_dispatcher.execution_mode = record-only- the
hyops-decision-dispatcherservice is active - emitted requests contain a payload version and approval artifact digest
Synthetic handoff validation¶
Use a disposable decision record to prove the control-loop handoff without executing any workflow:
- write a synthetic decision record into the decision-service records directory
- wait one dispatcher poll cycle
- confirm a request file appears under
state/requests/ - confirm the request is
awaiting-approval - remove the synthetic record and request after verification
Example validation record:
{
"decision_id": "test-cutover-001",
"decision_type": "cutover",
"rationale": "synthetic dispatcher handoff validation",
"checks": [
{"name": "gcp_billing", "status": "degraded"},
{"name": "wan_state", "status": "degraded"}
]
}
Observed live validation on the current dev control host:
- source record:
test-cutover-001 - emitted dispatch id:
dispatch-20260313T041204Z-cutover-b8239f1f - status:
awaiting-approval - target:
dr/postgresql-ha-failover-gcp@v1 - execution plane:
runner-local
This validation was executed and then cleaned back out of the live host so no fake pending request remained behind.
Destroy¶
hyops destroy --env dev \
--module platform/network/decision-dispatcher \
--inputs "$HOME/.hybridops/envs/dev/config/modules/platform__network__decision-dispatcher/latest.inputs.yml"
Notes¶
record-onlyis the correct default. It keeps signal evaluation separate from execution.- The dispatcher does not replace runner execution. It prepares the request that a runner or future consumer will act on.
- The dispatcher should remain on the shared control host alongside the decision service, DNS authority, and related control-plane services.
- Approval remains explicit at this stage. Auto-execution belongs in a later consumer layer, not in the dispatcher itself.