# `Squidie.Runtime.ScheduleMetadata`
[🔗](https://github.com/dark-trench/squidie/blob/main/lib/squidie/runtime/schedule_metadata.ex#L1)

Normalizes scheduler metadata for cron-triggered workflow starts.

Cron activation is intentionally host-owned: a host app decides when a
declared cron trigger fires and queues a `Squidie.Executor.Payload.cron/3`
payload. This module translates that delivery payload plus the compiled
workflow trigger definition into the durable context stored on the new run.

The persisted shape is reserved under `run.context.schedule` and is meant to
answer two different questions:

- what logical schedule window was intended by the scheduler
- when Squidie actually received and started processing the signal
- which stable idempotency key, when configured, protects the logical start

Keeping both timestamps matters because delayed delivery is normal in durable
runtime systems. Workflow steps should not infer their schedule window from current
wall-clock time; they should read the intended window from the run context.

The metadata is stored in run context rather than workflow payload so it does
not participate in the workflow's business input contract. It also means the
metadata survives reload, inspection, explanation, and replay without adding a
database column for one trigger kind.

# `t`

```elixir
@type t() :: %{
  :workflow =&gt; String.t(),
  :trigger_name =&gt; String.t(),
  :cron_expression =&gt; String.t(),
  :timezone =&gt; String.t(),
  :received_at =&gt; String.t(),
  optional(:signal_id) =&gt; String.t(),
  optional(:idempotency) =&gt; :return_existing_run | :skip_duplicate,
  optional(:idempotency_key) =&gt; String.t(),
  optional(:intended_window) =&gt; map()
}
```

# `cron_context`

```elixir
@spec cron_context(module(), Squidie.Workflow.Definition.trigger(), map()) ::
  {:ok, %{schedule: t()}}
  | {:error, {:invalid_schedule_signal_id, term()}}
  | {:error, {:invalid_schedule_intended_window, term()}}
  | {:error, {:missing_schedule_idempotency_key, atom()}}
```

Builds the durable run context for one cron activation.

The workflow and trigger definition contribute stable declarative data such
as the workflow name, trigger name, cron expression, and timezone. The
payload contributes scheduler-delivery data such as `signal_id` and
`intended_window`. If the scheduler omits `signal_id`, Squidie derives one
from the workflow, trigger, and intended window when both window bounds are
present. The runtime adds
`received_at` at activation delivery time, so operators can compare scheduler
intent against actual processing. Any `received_at` value in the payload is
ignored because this timestamp belongs to the runner boundary.

When the cron trigger opts into idempotency, the runtime also stores
`idempotency` and `idempotency_key` under the schedule context. The key is the
scheduler `signal_id`, either supplied by the host scheduler or derived from a
complete intended window. Without that identity, Squidie rejects the start
because it cannot prove whether the activation is new or a duplicate.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
