Skip to content

Feature request: durable, resumable batch deployments with validation and atomic cutover for native automations #376

Description

@jasonobrown

Summary

Please add an application-level deployment/job API that lets an MCP client submit a multi-step Hubitat native-automation migration as one durable, idempotent plan.

The server should:

  • stage new Rule Machine rules and other native apps while paused or otherwise inactive;
  • persist progress and checkpoints on the hub;
  • expose compact status and detailed audit logs;
  • validate the complete staged dependency graph;
  • perform an explicit atomic cutover only after validation succeeds; and
  • automatically roll back, or provide a deterministic rollback operation, if any cutover step fails.

This is not merely a request for JSON-RPC batch-array support. It is a request for a server-side orchestration layer tailored to Hubitat Rule Machine and classic native apps.

Relationship to existing completed work

This request builds on, but is distinct from, two completed issues:

Those capabilities are important prerequisites. The remaining gap is a durable transaction-like workflow spanning multiple rules, native apps, Button Controllers, child Button Rules, caller retargeting, validation gates, production cutover, and rollback. A client should not have to retain and reconstruct that entire migration state across dozens of separate calls.

Problem

Complex native-automation changes currently require many separate MCP round trips because Rule Machine authoring is driven through a stateful wizard. A client often has to:

  1. navigate to the correct page;
  2. start a trigger or action wizard;
  3. write one field;
  4. re-read the schema because the next field is exposed dynamically;
  5. write the next field;
  6. commit the action;
  7. read back the rendered configuration;
  8. run a health check; and
  9. repeat for every trigger, action, rule, caller, and supporting app.

For a migration involving a controller rule, supporting rules, required expressions, Button Controller children, caller retargeting, and a final old-to-new cutover, this can require dozens of successful MCP calls.

The client or agent session can end or disconnect before the workflow is complete. The resulting state may include:

  • several correctly paused staged rules;
  • partially authored or superseded drafts;
  • completed operations that the next client turn must rediscover;
  • callers still pointing to the old rule;
  • no durable record of the intended final topology; and
  • no safe, single operation that validates and commits the migration.

There are also normal Hubitat wire-format behaviors that increase orchestration cost. For example, a multi-select value may be accepted and stored in a normalized form even when a tool response reports a value mismatch. A robust deployment layer should verify the final readback and classify normalization as a warning rather than forcing the client to reconstruct the workflow manually.

Representative use case, intentionally sanitized

A user wants to replace an existing automation without modifying or deleting the original until the replacement is proven safe. The migration requires:

  • cloning a main Rule Machine controller;
  • creating several support rules;
  • adding required expressions and hub variables;
  • cloning a Button Controller with child Button Rules;
  • retargeting selected child actions from an old rule to a new rule;
  • keeping all replacements inactive during construction;
  • validating rule health, subscriptions, scheduled jobs, and caller/callee relationships;
  • pausing the old automation set; and
  • activating the replacement set as one controlled cutover.

No live device command should be sent during staging or validation.

The current low-level tools can perform the individual steps, but the MCP client must coordinate the entire transaction and retain all state across many calls. That is fragile for long-running agent workflows.

Proposed tool surface

Names are illustrative and should follow the project’s existing conventions.

hub_create_deployment

Creates a durable deployment plan and returns a jobId quickly.

Suggested inputs:

  • name
  • idempotencyKey
  • description
  • createHubBackup
  • stageInactive
  • allowDeviceCommands, default false
  • operations
  • validations
  • cutover
  • rollbackOnFailure
  • confirm
  • existing best-practice acknowledgment key, where applicable

hub_get_deployment_status

Returns compact progress for polling without replaying large Rule Machine configurations.

hub_resume_deployment

Continues a paused, interrupted, or restart-recovered deployment from its last committed checkpoint.

hub_validate_deployment

Runs all configured validation gates without changing the active production topology.

hub_commit_deployment

Performs the validated cutover. It should require the expected plan hash or confirmation token returned by validation so that a stale client cannot commit a changed plan.

hub_rollback_deployment

Restores the pre-deployment topology and any rule/app snapshots associated with the job.

hub_cancel_deployment

Cancels an uncommitted deployment and optionally removes only the staged artifacts created by that job.

Durable execution model

A single blocking Groovy execution may also have practical limits, so the implementation should be a persistent state machine, not necessarily one giant synchronous request.

Recommended behavior:

  1. hub_create_deployment validates the manifest, persists it, and returns a jobId.
  2. A bounded worker processes a safe number of operations per invocation.
  3. The worker checkpoints after every successful operation.
  4. Long work is continued with Hubitat scheduling, or by subsequent resume or status calls.
  5. A client disconnect does not lose the job state.
  6. A hub or MCP-app restart can recover the deployment from the last checkpoint.
  7. Retrying the same idempotencyKey returns the existing job instead of creating duplicate rules or apps.

Suggested phases:

  • draft
  • planning
  • staging
  • validating
  • ready_for_commit
  • committing
  • completed
  • failed
  • rolling_back
  • rolled_back
  • cancelled

A per-hub deployment lock should prevent concurrent jobs from editing the same Rule Machine or Button Controller objects.

Declarative operation types

The deployment manifest should support aliases so later operations can reference newly created app IDs without the client having to stop and read them back.

Suggested operations:

  • create a whole-hub backup;
  • create a Rule Machine rule;
  • clone a Rule Machine rule;
  • import a native-app export bundle;
  • create or clone another supported native app;
  • add, remove, modify, or replace triggers;
  • add, remove, move, or replace actions;
  • add or replace required expressions;
  • add or remove local variables;
  • create or update hub variables;
  • patch a rule or app using the existing structured shortcuts;
  • create or clone a Button Controller;
  • create or patch child Button Rules;
  • retarget a Run Actions reference from one rule alias or ID to another;
  • pause, resume, enable, or disable an app;
  • stop in-progress actions or timers when explicitly requested;
  • validate a staged object;
  • remove staged artifacts created by the job; and
  • restore a captured rule/app snapshot.

Example alias flow:

{
  "op": "cloneRule",
  "sourceAppId": "<existing-rule-id>",
  "alias": "replacement-controller",
  "newName": "Replacement Controller"
}

A later operation can use:

{
  "op": "retargetRuleAction",
  "appId": "<staged-button-rule-id>",
  "fromRuleId": "<existing-rule-id>",
  "toRule": { "alias": "replacement-controller" }
}

Example sanitized deployment request

{
  "name": "native-automation-migration",
  "idempotencyKey": "native-automation-migration-v1",
  "createHubBackup": true,
  "stageInactive": true,
  "allowDeviceCommands": false,
  "operations": [
    {
      "op": "cloneRule",
      "sourceAppId": "<old-controller-id>",
      "alias": "new-controller",
      "newName": "Replacement Controller"
    },
    {
      "op": "createRule",
      "alias": "new-wrapper",
      "name": "Replacement Trigger Wrapper",
      "spec": {
        "triggers": [],
        "actions": []
      }
    },
    {
      "op": "cloneNativeApp",
      "sourceAppId": "<old-button-controller-id>",
      "alias": "new-button-controller",
      "newName": "Replacement Button Controller"
    },
    {
      "op": "retargetRuleAction",
      "appId": "<staged-button-rule-id>",
      "fromRuleId": "<old-controller-id>",
      "toRule": { "alias": "new-wrapper" }
    }
  ],
  "validations": {
    "ruleHealth": true,
    "renderedConfig": true,
    "eventSubscriptions": true,
    "scheduledJobs": true,
    "dependencyGraph": true,
    "noDuplicateActiveCallers": true,
    "expectedTopology": true
  },
  "cutover": {
    "pause": [
      "<old-controller-id>",
      "<old-support-rule-id>"
    ],
    "resume": [
      { "alias": "new-wrapper" },
      "<new-support-rule-id>"
    ],
    "keepCalledOnlyRulesPaused": [
      { "alias": "new-controller" }
    ]
  },
  "rollbackOnFailure": true
}

Validation requirements

Before a deployment becomes ready_for_commit, the server should verify at least:

  1. Every staged Rule Machine rule has a healthy compiled state.
  2. The rendered configuration contains no broken trigger, action, or condition markers.
  3. No action block is left in a partial wizard-commit state.
  4. Required expressions are present and resolve to the expected devices, modes, or variables.
  5. Event subscriptions and scheduled jobs match the staged plan.
  6. Caller/callee references resolve to the expected app IDs or aliases.
  7. Button Controller children reference the intended replacement rules.
  8. No staged rule is active unless the manifest explicitly permits it.
  9. No old and new rule pair would create duplicate active triggers or competing device commands after cutover.
  10. Hubitat’s asynchronous writes have settled and a final readback matches the manifest.
  11. Multi-select normalization is distinguished from a true rejected write.
  12. The pre-cutover backup and all rule/app snapshots are still available.

The validation result should return a plan hash or confirmation token. hub_commit_deployment should reject a token if the deployment manifest or any staged object has changed since validation.

Atomic cutover and rollback semantics

The cutover should behave transactionally from the user’s perspective:

  1. Confirm every staged object is inactive and healthy.
  2. Capture the current active/paused/disabled topology.
  3. Pause or disable the old set.
  4. Activate the new set in the declared order.
  5. Re-read the final topology and dependency graph.
  6. Mark the deployment complete only if every expected state matches.
  7. If any step fails, restore the old topology and deactivate the new set.

Where true database atomicity is unavailable, the server should implement compensating actions and make the rollback result explicit.

Safety and privacy requirements

  • allowDeviceCommands should default to false during staging, validation, and dry runs.
  • Running rule actions should require an explicit operation and confirmation.
  • Existing backup and best-practice safety gates should remain in force.
  • Destructive cleanup should affect only artifacts recorded as created by that deployment.
  • Status and audit logs should redact access tokens, hub URLs, credentials, location data, and other sensitive values.
  • The response should not echo full app settings when a compact summary is sufficient.
  • A dry-run or plan-only mode should be available before any write.

Status response

A compact status shape would reduce MCP context consumption:

{
  "jobId": "deployment-123",
  "state": "staging",
  "phase": "retarget-callers",
  "completedOperations": 12,
  "totalOperations": 18,
  "currentOperation": "retargetRuleAction",
  "backupKey": "<redacted-or-opaque-handle>",
  "warnings": 1,
  "errors": 0,
  "confirmationRequired": false,
  "nextAction": "poll"
}

Detailed operation logs and before/after manifests could be written to File Manager and retrieved separately with pagination or cursors.

Dry run and planning

Please include a plan-only mode that reports:

  • operations that would run;
  • objects that would be created or changed;
  • active rules that would be paused;
  • staged rules that would be activated;
  • caller references that would be retargeted;
  • device commands that could occur, if any;
  • missing permissions or safety prerequisites;
  • name or alias collisions; and
  • the expected rollback plan.

Acceptance criteria

  1. An MCP client can submit a complete multi-rule/native-app migration in one declarative request.
  2. The server returns a durable jobId and persists progress independently of the client session.
  3. Retrying the same idempotency key never creates duplicate staged apps.
  4. The deployment can resume after a client disconnect or hub/MCP-app restart.
  5. All new rules/apps remain inactive during staging unless explicitly declared otherwise.
  6. The old production topology remains untouched until validation succeeds and commit is explicitly authorized.
  7. A compact status call reports deterministic progress and the next required action.
  8. The server can retarget Rule Machine and Button Rule caller references using aliases.
  9. Benign Hubitat value normalization is handled by verified readback rather than treated as an unrecoverable failure.
  10. Commit either produces the exact declared topology or performs a documented rollback.
  11. No device command is issued during staging or validation when allowDeviceCommands is false.
  12. The deployment produces an audit record containing the plan, checkpoints, validation results, cutover result, and rollback result without sensitive data.

Optional follow-on: deployment bundles

A useful extension would be export/import of a complete deployment bundle containing:

  • the declarative manifest;
  • native app exports;
  • alias mappings;
  • validation policy;
  • expected topology; and
  • rollback metadata.

This would make complex automation migrations portable, reviewable, testable, and much less dependent on a single long-lived AI-client execution window.

Privacy note

The representative examples above are intentionally sanitized. They contain no personal names, email addresses, local or public IP addresses, hub identifiers, endpoint URLs, access tokens, household device labels, location data, actual production app IDs, screenshots, or private repository information.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions