let pb = import "schemas/lib/playbook.ncl" in pb.make_playbook { id = "switch_to_operator_only", name = "Switch to Operator-Only Mode", description = "Stops keeper-daemon on ops-vm. All ops commands require operator manual signature until switch_to_vm_ops reverses this.", version = 1, preconditions = [ "Operator has a valid keeper-cli credential for the target workspace", "ops-vm is accessible via SSH", "No critical ops pending that would miss auto-sign SLA", ], params = [ { name = "workspace", description = "Target workspace name (e.g. libre-wuji)", required = true, default_val = "" }, { name = "ops_vm_host", description = "ops-vm SSH hostname or IP", required = true, default_val = "" }, ], steps = [ { id = "check_pending_queue", name = "Check pending queue depth before switching", script = "steps/check_queue.nu", dry_run_arg = "--dry-run", on_error = 'Continue, depends_on = [], }, { id = "stop_keeper_daemon", name = "Stop keeper-daemon on ops-vm via SSH", script = "steps/stop_keeper.nu", dry_run_arg = "--dry-run", on_error = 'Stop, depends_on = ["check_pending_queue"], }, { id = "verify_queue_accumulating", name = "Verify OPS_PENDING stream accumulates without auto-sign", script = "steps/verify_queue_accumulating.nu", dry_run_arg = "--dry-run", on_error = 'Continue, depends_on = ["stop_keeper_daemon"], }, { id = "emit_mode_change", name = "Emit audit event: mode switched to operator-only", script = "steps/emit_audit.nu", params = { event_type = "mode_switch", new_mode = "operator-only" }, dry_run_arg = "--dry-run", on_error = 'Continue, depends_on = ["stop_keeper_daemon"], }, ], rollback_strategy = 'manual, success_criteria = [ "keeper-daemon inactive on ops-vm (systemctl is-active keeper-daemon = inactive)", "OPS_PENDING queue depth increasing without auto-sign clearing it", "ops history shows mode-switch audit event", ], emit_audit = true, adr_refs = ["adr-037"], }