Skip to content

Runtime Preconditions (read-only guards) with Blocked stop behavior #210

@blindzero

Description

@blindzero

Problem Statement

IdLE evaluates Condition only during plan creation. The plan is then executed 1:1. This makes planning deterministic, but it introduces a safety gap when time passes between plan and execution:

  • External state can change (e.g., group membership / entitlements / device state).
  • A plan that was correct at planning time may be unsafe or invalid at execution time.
  • For safety/policy scenarios we must prevent execution from performing an action if the current (live) state violates a precondition.

Concrete example (BYOD policy):

  • If the user is currently in a BYOD group (Android/iOS), company data must be wiped/retired in Intune before disabling the identity.
  • If the plan was created earlier and the user later enters/leaves those groups, a plan-only condition can become stale and either:
    • incorrectly allow disablement (policy violation), or
    • incorrectly block disablement (unnecessary stop).

We need a generic, read-only, runtime mechanism to guard execution with “fail fast” behavior without introducing side effects or turning IdLE into an interactive system.

Related / Dependencies

This issue intentionally does not define request schema naming or planning-time context enrichment. It assumes those are handled by separate issues:

  • Issue (Schema): Separate Request.Intent from Request.Context (rename DesiredState, forbid Request.Identity)
  • Issue (Snapshot): Plan snapshot/export contract for Request.Intent and Request.Context (auditability + safety)
  • Issue (Planning Context): Planning-time ContextResolvers (read-only) using provider capabilities to populate Request.Context

Runtime Preconditions may use:

  • live provider queries (preferred for safety), and/or
  • Request.Context when available.

Proposed Solution

Introduce Runtime Preconditions (read-only execution guards) that are evaluated during execution immediately before running a step.

High-level concept

  • Keep existing Condition semantics as planning-time (deterministic plan building).
  • Add an optional Preconditions capability per step that is evaluated at execution time (online evaluation).
  • Preconditions are read-only checks, allowed to query providers (live state), but must not change state.
  • If a precondition fails, the engine must stop execution in a controlled manner and surface a Blocked outcome (not a generic failure), optionally emitting a structured event payload for hosts.

Workflow schema (proposal)

Add optional properties to step definitions:

  • Preconditions: list of preconditions to evaluate at execution time (read-only).
  • OnPreconditionFalse: Blocked | Fail (default: Blocked if Preconditions exists; otherwise N/A).
  • PreconditionEvent: optional structured object that is emitted when precondition fails, e.g.:
    • Type (e.g., ManualActionRequired)
    • Message
    • Data (key-value payload, safe for logs)

Notes:

  • This issue does not require choosing the final schema names yet, but implementation MUST include an explicit, documented and testable structure.

Engine behavior

Plan creation remains unchanged:

  • Evaluate Condition during planning and set step Status accordingly.

Execution phase:

  • For each step with Status == Planned (or equivalent), evaluate Preconditions before invoking the step action.
  • If all pass, invoke step action as usual.
  • If any fails:
    • If OnPreconditionFalse == Blocked: mark step outcome as Blocked and stop the plan execution (do not continue to subsequent steps).
    • If OnPreconditionFalse == Fail: mark step outcome as Failed and stop, running existing failure semantics as appropriate.
    • Emit PreconditionEvent as a structured engine event (if provided).

Minimal API surface

  • Add a new step-result/outcome classification: Blocked.
  • Add execution logic that stops the run when a step becomes Blocked (or Failed), with predictable return objects/events.
  • Keep compatibility: steps without Preconditions behave exactly as before.

Semantics: Blocked vs Failed

Blocked is not a “technical failure”. It represents a policy / precondition gate and is intended to support human-in-the-loop or external dependency resolution.

Required clarifications/behavior:

  • Blocked MUST stop execution immediately (no subsequent steps).
  • Blocked MUST be distinguishable from Failed in the returned plan execution result.
  • Failure handlers (e.g., “on failure” steps / compensation) MUST NOT run for Blocked by default.
    • If IdLE supports configurable failure handling, document how Blocked is treated.
  • Failed remains reserved for genuine errors (provider failure, unexpected exception, etc.) and continues to follow existing failure semantics.

Alternatives Considered

  1. Gate Step (e.g., RequireManualProcedure) before actions:

    • Works, but pushes policy gating into workflow authoring and duplicates a common pattern.
    • Less generic and harder to standardize across all safety policies.
  2. Re-evaluate Conditions at runtime:

    • Makes plan vs execution nondeterministic and blurs responsibilities.
    • Harder to audit: plan no longer explains why a step executed or not.
  3. Host-only gating:

    • Host can block execution, but IdLE should provide a consistent, testable mechanism for safety guards across hosts.

Impact

Does this affect existing workflows?

  • No. Existing workflows without Preconditions are unaffected.

Backward compatibility concerns?

  • Introducing a new outcome/status Blocked requires consumers (hosts/tests) to tolerate the new value.
  • Default behavior must remain unchanged unless Preconditions is configured.

Additional Context

BYOD use case enabled by this feature

A host can:

  • Create a plan that includes DisableIdentity.
  • Configure DisableIdentity with a runtime precondition like:
    • “User is NOT currently member of BYOD groups OR a caller-provided wipe confirmation flag is true”
    • The exact request path for the confirmation flag is defined by the request schema issue (e.g., Request.Intent.Byod.WipeConfirmed), and is not introduced by this issue.

If membership is detected live at execution time, execution stops as Blocked and emits:

  • Type=ManualActionRequired
  • Message=Perform Intune retire / wipe company data for BYOD device
  • Data including platform hints and identity keys.

Definition of Done (Step-0-Ready / Agent-Safe)

Design / Contracts

  • Document the runtime preconditions concept in docs (reference + example).
  • Define the workflow schema additions (Preconditions, OnPreconditionFalse, optional event payload).
  • Define and document Blocked as a first-class outcome/status with clear semantics vs Failed.
  • Define how Blocked interacts with failure/compensation handling (default: do not run failure handlers).

Implementation

  • Add/extend domain model to represent preconditions in step definitions.
  • Extend plan execution to evaluate preconditions immediately before step invocation.
  • Implement Blocked handling:
    • Stop execution immediately on Blocked.
    • Ensure subsequent steps are not executed.
  • Emit a structured event when a precondition fails (if configured), ensuring log safety (no secrets).

Tests (Pester)

  • Unit test: step without preconditions behaves exactly as before.
  • Unit test: step with preconditions passing executes step action.
  • Unit test: failing precondition produces Blocked and stops execution (no next step).
  • Unit test: failing precondition with OnPreconditionFalse=Fail produces Failed and stops execution.
  • Unit test: Blocked does not trigger failure/compensation handlers (default behavior).
  • Unit test: configured PreconditionEvent is emitted and contains expected fields.

Docs / Examples

  • Add an example workflow snippet showing a policy guard (BYOD).
  • Ensure docs clarify deterministic planning vs runtime safety guards.

Non-Goals (explicit)

  • No interactive UI prompts in IdLE.
  • No “Terraform-like” full data engine or resource graph; only read-only runtime checks used as guards.
  • No automatic re-planning or plan mutation during execution beyond step outcome marking.

Metadata

Metadata

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions