# Workflow Orchestration Versus Simple Scheduled Scripts: Evidence Dossier

**Research snapshot:** October 10, 2026  
**Perspective:** General  
**Decision supported:** When a bounded recurring business workflow should remain a scheduled script/native automation, and when durable workflow orchestration becomes the minimum sufficient solution.  
**Evidence base:** Public documentation, primary practitioner material, current provider behavior, and explicitly labeled synthetic design/tests. No private production systems were accessed, and no customer outcome, ROI, incident reduction, or firsthand production result is claimed.

## Executive decision and uncertainties

For the specific bounded workflow in scope—

> **source export → immutable raw preservation → ingestion → dbt/warehouse transformation → business report**

—I would **not adopt a workflow orchestrator merely because there are five steps**. I would first implement the process as **one hardened, period-aware job** invoked by a reliable scheduler, with a durable state/acceptance ledger, an overlap lock, immutable input identity, idempotent or reconcilable side effects, explicit timeouts, and a reconciliation command. That is the minimum solution I would choose while the workflow remains linear, has one operational owner, runs in one execution environment, has few concurrent backfills, and can cheaply resume or rerun one business period.

A plain collection of independently scheduled cron scripts is **below the reliability threshold in the stated requirements**. Traditional single-machine cron is fundamentally a time-trigger mechanism: Google’s SRE description of typical `crond` notes that launches are fire-and-forget and not durably tracked, and that machine downtime prevents both scheduling and execution. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) A stronger local scheduler such as systemd timers can materially improve that baseline: an already-running service is not started again by its timer, and `Persistent=true` can trigger after downtime when at least one calendar event was missed. But the application still has to determine *which business periods* are outstanding and reconcile them. [2](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html)

A managed/native scheduler is a useful middle ground, not automatically an orchestrator. Google Cloud Scheduler, for example, explicitly provides **at-least-once**, not exactly-once, delivery, may deliver the same scheduled invocation more than once, exposes the original schedule time for deduplication, and supports configurable retry/backoff. [3](https://cloud.google.com/scheduler/docs/overview) That can eliminate a local scheduler host as a failure point while leaving workflow dependency and business-state semantics in the invoked application.

A workflow orchestrator becomes the better default when **recovery complexity becomes larger than execution complexity**: several independently retryable stages, frequent partial reruns and historical backfills, multiple periods concurrently in flight, long dependency waits, branching/fan-out, cross-team handoffs, or a need for standardized run history and operational UI. Airflow, for example, persists DAG/task-run concepts, models data intervals, dependencies, catchup and historical backfill. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) Prefect likewise exposes explicit run states. These are real advantages over rebuilding the same machinery inside shell/Python code.

But an orchestrator **does not make “business complete” synonymous with “workflow green.”** Airflow documents that a successful leaf task with an `all_done` trigger rule can cause a DAG run to be marked successful even when an intermediate task failed. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) Prefect documents that programmatic state handling can leave a failed task inside a flow whose final state is nevertheless `COMPLETED`. [5](https://docs.prefect.io/v3/develop/manage-states) The final business acceptance predicate therefore belongs in the workflow design regardless of technology.

The most important finding on time is equally technology-independent: **“every 24 hours” and “every day at 06:00 local time” are different contracts.** Airflow explicitly distinguishes cron schedules, which adapt to DST, from `timedelta` schedules whose subsequent runs maintain the UTC cadence; AWS EventBridge Scheduler similarly defines `rate(1 days)` as 24 elapsed hours while local cron schedules follow timezone/DST rules. [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) Cron syntax by itself does not tell you what a provider does with skipped or repeated civil times: current AWS, GitHub Actions, and Debian/Vixie cron documentation describe different transition behavior. [7](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows)

**My decision boundary is therefore:**

| Situation | Recommended minimum |
|---|---|
| Process is still ambiguous, rare, or changing | Manual/native runbook first |
| One linear daily chain, one owner/runtime, low backfill volume, whole-period rerun is safe | **Hardened scheduled job + durable period ledger** |
| Same bounded job, but scheduler-host reliability is undesirable | **Managed/native scheduler + hardened job** |
| Frequent partial recovery/backfill, multiple dependencies or owners, several runs/periods in flight, operational UI/history materially valuable | **Workflow orchestrator** |
| Script contains a task graph, per-task persistent states, queues, retry policies, dependency waits, backfill planner and recovery UI | Stop pretending it is “just a script”; it has become a bespoke orchestrator |

The last boundary is an **editorial architecture judgment**, not a measured threshold.

**Confidence is high** in the documented scheduler, DST, retry, Airflow-state and dbt behaviors because they come from current provider/project documentation. **Confidence is moderate** in the decision boundary because it is an architectural synthesis rather than a controlled comparative experiment. **Confidence is low/unknown** for any universal ROI, incident-rate, MTTR or cost break-even: I did not identify a controlled study running this identical workload under a hardened script and an orchestrator, and this dossier therefore claims no effect size or causal business outcome.

## Bounded process and minimum baseline

The following is a **synthetic reference process**, not a report about an observed company.

| Contract element | Proposed reference design |
|---|---|
| Business purpose | Produce the daily business report for the correct reporting period and accepted source version |
| Operational owner | Analytics/Data owner; infrastructure responsibility may be the same person/team in a small implementation |
| Trigger | Nominally 06:00 in the declared business timezone |
| Business period | `business_date = B`, calculated independently from trigger time using a versioned business calendar |
| Source input | Export explicitly identifying `B` and `source_version = V`, or a generated immutable version identity plus content hash |
| Raw preservation | Immutable copy identified by `B`, `V`, and SHA-256 hash `H` |
| Processing states | `EXPECTED → RECEIVED → RAW_PRESERVED → INGESTED → TRANSFORMED → VALIDATED → PUBLISHED → ACCEPTED` |
| Non-success states | `TIMED_OUT`, `FAILED`, `SUPERSEDED`; cancellation is also non-success |
| Main exceptions | Late/missing source, wrong period, revised source version, duplicate trigger, overlap, permission failure, partial write/load, transformation failure, stale downstream data, publication uncertainty, host/scheduler outage, DST replay/backfill |
| Observable end state | Durable acceptance record proving that the published artifact corresponds to the required `B` and `V` and met—or missed—the declared deadline |

The proposed business success predicate is deliberately stricter than process exit status:

\[
BusinessSuccess(B,V) =
Raw(B,V)
\land Ingested(B,V)
\land Transformed(B,V)
\land Validated(B,V)
\land Published(B,V)
\land Accepted(B,V)
\land accepted\_at \le D(B)
\]

where:

- \(B\) = business/reporting date,
- \(V\) = accepted source version,
- \(D(B)\) = declared acceptance deadline for that business period.

If the data is repaired after the deadline, correctness may be restored, but the historical deadline result must not be rewritten. A practical acceptance record should therefore preserve `accepted_at` and `deadline_met=false`.

This separation is important because Airflow itself distinguishes a run’s data interval/logical date from actual execution time; a daily run is associated with a data interval and is normally scheduled after that interval ends. Its documentation also warns that manually supplied logical dates and data intervals should not simply be assumed equal. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) The general lesson is not “copy Airflow terminology into every script”; it is to **keep business period, nominal trigger time, actual start time, and source version as separate fields**.

The simplest baseline is not cron. It is a **manual runbook**:

A person determines `B`, checks that the correct source version has arrived, preserves it immutably, runs ingestion, invokes the transformation, validates that the result is actually for `B/V`, produces the report and records acceptance. That baseline exposes the information the automated system must eventually encode. If the human cannot state whether a revised export supersedes an already-published report, or what “late” means, adding Airflow merely converts an undefined process into an undefined DAG.

Before selecting technology, I would simplify the process in three ways.

First, **remove independent clocks where dependencies already exist**. There is little justification for “ingestion at 06:00, dbt at 06:10, report at 06:20” when what the business means is “transform the correct export after it is ingested, then publish the result.” Fixed offsets turn data readiness into an unstated probability.

Second, **standardize identity before retry policy**. Every stage should know `business_date`, `source_version` and ideally the raw content hash. Google’s distributed-cron design emphasizes that identifying only the job is insufficient; an individual launch needs a unique identity so failover does not create ambiguity about which launch already happened. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) Amazon’s Builders’ Library makes the analogous point for retryable side effects: a stable caller-supplied request identifier enables a service to recognize retries of the same intent, while merely comparing parameters can be semantically ambiguous. [8](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/)

Third, **define the acceptance gate before automation**. dbt can contribute useful mechanics: current dbt documentation says `dbt build` executes selected resources in DAG order, produces manifest/run-results artifacts, and by default can block downstream resources after upstream test failures. [9](https://docs.getdbt.com/reference/commands/build) But dbt freshness is fundamentally an age/freshness comparison—such as comparing a maximum loaded timestamp with the current time—not proof that a report uses the exact business date or source version intended by this contract. [10](https://docs.getdbt.com/reference/commands/freshness) The latter requires an explicit period/version test or equivalent lineage check.

There is no material Czech-versus-US change to this architecture boundary in the evidence inspected. The material contextual differences are the **chosen IANA timezone and business calendar**. For examples in this dossier, the Czech case uses `Europe/Prague` and the US Eastern case uses `America/New_York`. Holiday lists are treated as business-owned versioned inputs, not as something inferred from the scheduler.

## Decision boundary across implementations

The central mistake is to compare “cron” with “Airflow” as products. The more useful comparison is **where the required state lives and who owns recovery**.

| Dimension | Plain cron + independent scripts | Hardened single job + local/managed scheduler | Native automation/workflow facility | Durable orchestrator |
|---|---|---|---|---|
| Scheduling | Time trigger | Time trigger, provider/local implementation semantics explicit | Built-in platform schedule/event | Schedule plus workflow model |
| Dependency handling | Usually implicit clock offsets or custom code | Explicit sequential application code | Varies by native platform | First-class graph/state is typical |
| Business-period identity | Custom | **Custom and required** | Custom | Framework may provide logical interval/partition, but business version still custom |
| Retries | Custom shell/code | Explicit application policy | Often provider-supported at invocation/job level | Usually standardized per task/run |
| Duplicate safety | Custom | **Idempotency/reconciliation required** | Still required | Still required |
| Partial failure | Usually manual | Durable ledger + resume/reconciliation | Platform-specific | First-class task/run states make partial recovery easier |
| Backfill | Ad hoc manual rerun | CLI/API takes explicit `B`; reconciliation enumerates missing periods | Platform-specific | Commonly standardized; Airflow has catchup/backfill |
| State | Logs/process exit unless built | Small durable database/ledger | Provider run state plus application state | Metadata/state database or managed equivalent |
| Observability | OS logs/mail | Structured logs + ledger + alert on missing acceptance | Native execution UI/logs | Workflow/task history/UI is a primary benefit |
| Host interruption | Scheduler-dependent | systemd persistence or managed scheduler can improve launch recovery | Provider-dependent | Control plane usually designed around durable run state |
| Control-plane burden | Very low | Low to moderate | Provider is an additional dependency | Highest when self-hosted; managed services move rather than eliminate ownership |
| Testing surface | Script + environment | Script + ledger + recovery logic | Platform definitions + application | Workflow definition, task code, control-plane/deployment integration |
| Best fit | Failures/skips are tolerable | **Bounded linear workflow with serious correctness requirements** | Work naturally belongs inside that platform | Complex recovery/dependency state or many workflows |
| Main failure of the approach | Hidden skipped/duplicate/stale work | Bespoke state code grows into a hidden orchestrator | Provider scheduling guarantees mistaken for business completion | Platform complexity exceeds problem complexity |

The weakest baseline deserves a precise qualification. Google’s SRE chapter characterizes conventional single-machine `crond` as having a machine-sized failure domain and fire-and-forget launches with no launch tracking. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) Debian’s Vixie cron implementation does provide logging and special DST behavior, but this does not turn its timer into a durable multi-stage workflow engine. [11](https://manpages.debian.org/bookworm/cron/cron.8.en.html)

The **hardened script** is substantially stronger than “five shell scripts in crontab.” I mean one entrypoint such as:

```text
run_daily_report --business-date 2026-03-27
```

which acquires the lock for `B`, reconciles the current state, waits for exactly `B/V`, preserves raw input, executes/resumes only the necessary stages, validates the result and transactionally records acceptance. Triggering that with systemd `OnCalendar=` plus `Persistent=true`, or a managed scheduler, is a genuinely viable architecture for this bounded case. systemd documents that `Persistent=true` stores last-trigger state on disk and starts the service when the timer returns if it would have fired while inactive. [2](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html) Because that guarantee is “at least one missed occurrence existed,” rather than “materialize every missing business period as an individual workflow instance,” the proposed reconciliation ledger remains necessary; that last conclusion is an inference from the documented timer behavior.

A **managed scheduler** can further strengthen the launch plane without replacing the application contract. Google Cloud Scheduler documents at-least-once invocation and explicitly tells target code to be safe under repeated execution. It exposes the original scheduled time consistently across retries, which is useful as a trigger identifier. [3](https://cloud.google.com/scheduler/docs/overview) Its retry configuration can also cause a future scheduled execution to be skipped when the previous execution is still retrying, demonstrating why “the scheduler has retries” is not the same as “every business period will complete.” [12](https://cloud.google.com/scheduler/docs/configuring/retry-jobs)

A **native platform scheduler** may be sufficient when the entire workflow can be expressed as one durable platform job. But its clock cannot be assumed exact. GitHub currently states that scheduled Actions workflows can be delayed during high load and that sufficiently loaded queues can drop some scheduled jobs. [7](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) For a business deadline, completion should therefore be independently monitored as “expected period has not reached ACCEPTED by time X,” rather than “did the scheduling service generate an event?”

An **orchestrator** becomes attractive when the script starts accumulating reusable orchestration mechanisms. Airflow exposes DAG runs, task dependencies, data intervals, catchup and historical backfill; catchup can generate a run for each unprocessed interval when enabled. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) Those are exactly the facilities a sophisticated home-grown runner eventually has to reproduce.

The trade-off is a real additional system. Airflow’s own documentation describes continuously running DAG-processing processes, synchronization with its database, filesystem considerations, database-connection pressure, CPU/memory consumption and tuning decisions. [13](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/dagfile-processing.html) That does not prove Airflow is “expensive” or less reliable; it proves that orchestration has an operational surface of its own. With a managed orchestration product, much of that control-plane work moves to the provider, but provider dependency, configuration, upgrade compatibility, workflow testing and usage cost remain decision variables.

I would therefore choose an orchestrator for this chain when one or more of these become regular rather than exceptional: partial reruns from different intermediate stages; dozens of historical periods to backfill; more than one independent upstream prerequisite; periods running concurrently; long waits that should not consume a worker; branching/parallel execution; multiple operational owners; or operators who need standardized workflow-state inspection rather than SQL/CLI access to a bespoke ledger. This is a **decision heuristic, not an empirical threshold**.

Conversely, I would actively avoid introducing an orchestrator when there is one owner, one linear graph, perhaps one run a day, almost no partial reruns, a whole-period replay is cheap and safe, and the team would have to acquire a new control-plane skill set solely to run this one process. In that situation, complexity added “for reliability” can simply relocate failures from a small program into scheduler, worker, metadata, deployment and version-management layers. Airflow’s own deployment documentation makes those additional operational concerns concrete. [13](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/dagfile-processing.html)

**AI is outside the authority path in this design.** A bounded agent may classify logs, summarize an incident or suggest a recovery command. It should not infer that business period `B` is complete because a task “looks successful,” nor replace the deterministic acceptance predicate. No evidence inspected here establishes an AI agent as a substitute for scheduler state, source-version correlation or business completion authority.

## Time and period contract

The following contract is the most reusable output from the time-specific investigation. **The numerical times and timeout values are synthetic design assumptions**, not observed customer requirements.

| Field | Proposed contract |
|---|---|
| `business_date` | Explicit date `B`; downstream tasks never derive it from `now()` |
| Business calendar | Mon–Fri minus a versioned business-owned holiday set; source calendar may be independently versioned if different |
| Czech timezone example | `Europe/Prague` |
| US Eastern example | `America/New_York` |
| Schedule intent | **Local calendar time:** nominal kickoff 06:00 local |
| Elapsed intent | Use elapsed/UTC interval only when the requirement literally means “N hours since previous occurrence” |
| Source readiness deadline | 07:00 local, synthetic |
| Report acceptance deadline | 07:30 local, synthetic |
| Trigger timestamp | Store nominal scheduled timestamp separately from actual start |
| Source identity | `(business_date, source_version, source_hash)` |
| Correlation key | `daily-report:{business_date}:{source_version}` |
| Raw path concept | `raw/source/business_date=B/source_version=V/sha256=H/...` |
| Overlap | At most one mutating run for the same `B`; historical periods may run concurrently only if downstream operations are isolation-safe |
| Same `B/V/H` duplicate | Resume/no-op |
| Same `B/V`, different `H` | Conflict; quarantine/reconcile |
| Same `B`, new `V` | New immutable candidate; never silently overwrite an accepted report |
| Source wait | Wait for exact `B/V` until 07:00; absence thereafter is `TIMED_OUT`, not permission to use latest available data |
| Transient retries | Synthetic sequence 30 s, 60 s, 120 s, ideally with jitter |
| Permanent failures | Authentication/permission, schema incompatibility and B/V validation errors require repair before retry |
| Cancellation | Leaves durable incomplete state; reconciliation must see it as unfinished |
| Backfill | Enumerate business dates directly; do not generate periods by repeatedly adding 24 elapsed hours |
| Completion | Only explicit acceptance of exact `B/V`; scheduler/orchestrator success is insufficient |
| Deadline history | Repair after deadline can make output correct, but must preserve `deadline_met=false` |
| TZ database | Log/runtime-manage the timezone data version; current IANA tzdb at the snapshot was **2026e, released September 29, 2026**. [14](https://www.iana.org/time-zones) |

IANA explicitly notes that its timezone database is periodically updated when political bodies change UTC offsets, timezone boundaries or daylight-saving rules. [14](https://www.iana.org/time-zones) That is why “we configured `Europe/Prague` once” is not the complete operational contract. Airflow makes the same point in implementation-specific terms: its documentation warns that the underlying timezone library must be kept current and notes that its default Pendulum timezone database can lag IANA. [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html)

The schedule should state whether the business wants **civil/calendar time** or **elapsed time**:

```text
Calendar contract:
"Run for each business date at 06:00 Europe/Prague."

Elapsed contract:
"Run 24 elapsed hours after the previous invocation."
```

Those contracts diverge at DST. Airflow 3.3.2 documents that timezone-aware cron schedules follow daylight-saving changes, while `timedelta` scheduling does not readjust later runs for DST and therefore retains the UTC cadence. [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) AWS EventBridge Scheduler is even more explicit: a `rate(1 days)` period is 24 elapsed hours, including across 23- or 25-hour civil days. [15](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

The more subtle point is that **provider-specific treatment of nonexistent and repeated local times differs**:

| Implementation inspected | Documented DST behavior | Implication |
|---|---|---|
| Debian Vixie cron | For a forward clock change under three hours, fixed-time jobs in the skipped interval are run soon afterward; fixed jobs in the repeated interval are not rerun. [11](https://manpages.debian.org/bookworm/cron/cron.8.en.html) | Do not generalize “cron skips spring jobs” |
| AWS EventBridge Scheduler | A cron time that does not exist in spring is skipped; at fall-back the repeated schedule is invoked only once. [15](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) | Different from Debian behavior |
| GitHub Actions timezone-aware schedule | Current docs say a spring-forward time in the skipped hour advances to the next valid time; schedules can separately be delayed or dropped under load. [7](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) | Yet another implementation contract |
| Airflow timezone-aware cron | Cron schedule follows DST; elapsed `timedelta` cadence does not readjust after the initial date. [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) | Choose schedule type based on business semantics, not syntax familiarity |

This directly challenges any generic statement such as “cron runs twice at fall-back” or “02:30 simply does not run in spring.” **The syntax is not the execution guarantee. Inspect the actual scheduler/version.**

The synthetic test asset executed for this dossier used Python 3.13.5 and the environment’s system `zoneinfo`. It classified:

```text
Europe/Prague
2026-03-29 02:30 -> nonexistent local civil time
2026-10-25 02:30 -> ambiguous/repeated local civil time

America/New_York
2026-03-08 02:30 -> nonexistent local civil time
2026-11-01 01:30 -> ambiguous/repeated local civil time
```

It also verified that preserving **06:00 local civil time** across Prague’s spring transition produces a 23-hour UTC gap, while adding 24 elapsed hours to the pre-transition instant yields **07:00 local** on the following day. Across fall-back, the corresponding civil-time gap is 25 hours and 24 elapsed hours lands at **05:00 local**. These are firsthand **synthetic standard-library tests**, not cloud-scheduler tests and not measurements of Airflow/AWS/GitHub behavior.

This distinction is the evidence-backed core of the draft claim **“Automation runs at a different time than expected. Which time should count for the business?”** I would keep the idea but make it more precise:

> **“Does the business mean 06:00 local calendar time, or every 24 elapsed hours? They are different schedules. Name the timezone, business period, DST policy and deadline explicitly.”**

## Dependency, timeout, recovery and worked failures

A scheduler answers “when should I try to start something?” The bounded business process needs answers to several additional questions: *which period/version is this? what has completed? what is safe to retry? what timed out? and what evidence proves the final business artifact is correct?*

The following recovery table is architecture-neutral; the same predicates apply whether the implementation is a Python program or an Airflow/Prefect workflow.

| Stage | Dependency / readiness predicate | Timeout or failure | Durable evidence | Recovery / reconciliation |
|---|---|---|---|---|
| Source wait | Source declares expected `B` and `V`; completeness criterion passes | Missing/late source; wrong B/V; synthetic 07:00 timeout | `EXPECTED`, observed source metadata | Ignore/quarantine wrong period; resume exact B when it arrives; after timeout alert but keep B recoverable |
| Raw preservation | Exact source bytes available | Partial write; duplicate trigger; version/hash conflict | Immutable `(B,V,H)` artifact plus `RAW_PRESERVED` | Finalize atomically where storage permits; same H no-op; conflict blocks |
| Ingestion | `RAW_PRESERVED(B,V)` | Network/DB transient; permission error; partial load | `INGESTED(B,V)` plus load audit metadata | Retry only an idempotent/reconcilable operation; otherwise clean/replace/transactionally reconcile B |
| dbt/warehouse transform | Exact ingested B/V | dbt failure; run accidentally reads “latest” stale data | transform artifact/run reference plus period/version evidence | Re-run exact B after repair; never infer B from current time |
| Validation | Transformed B/V exists | Freshness/test/business invariant failure | test/validation artifact | Repair lowest failing stage; remain non-publishable |
| Report publication | Validated B/V | Publish timeout after side effect; duplicate publication risk | Report metadata + stable publication/idempotency key | Query external state when possible; retry same intent key; otherwise reconcile manually first |
| Acceptance | Published artifact explicitly identifies expected B/V | Yesterday’s data; wrong source revision; missed deadline | `ACCEPTED(B,V,H)` plus `accepted_at` and deadline result | Reject mismatch; newer V follows explicit supersession policy |
| Whole process | Required B has an accepted version | Scheduler/orchestrator green while no accepted business artifact exists | Independent period acceptance ledger | Scan expected B set against ACCEPTED set and reopen/resume missing periods |

Amazon’s idempotency guidance illustrates why a timeout at a side-effect boundary is particularly dangerous: after the caller loses the response, it may not know whether the operation happened; a blind retry can duplicate the side effect. Amazon describes stable client request identifiers as its preferred mechanism for distinguishing retries of the same intent, together with atomic handling of the identifier and mutation. [8](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) Google’s distributed-cron design similarly persists an identity and begin/end state for each scheduled launch so failover can distinguish an open launch from one that was never attempted. [1](https://sre.google/sre-book/distributed-periodic-scheduling/)

**Worked failure: source arrives after the fixed schedule.** This is a synthetic example, not an observed incident.

Assume Monday **March 30, 2026**, schedule 06:00 `Europe/Prague`, expected business date `B=2026-03-27`, and source version `V1`. The export is not ready at 06:00 and becomes available at 06:12.

A brittle implementation has three independent timers:

```text
06:00 ingestion
06:05 dbt
06:15 report
```

The ingestion sees no source. The dbt process can nevertheless exit successfully using whatever was previously in the warehouse—for example B=`2026-03-26`. The report process likewise succeeds technically. All three process-level signals can therefore look benign while the business output is stale. This sequence is a **synthetic failure construction**, not a measured provider result.

The hardened design instead has one period-aware chain:

```text
trigger(06:00)
  -> expect B=2026-03-27
  -> wait for source where manifest.business_date == B
  -> observe V1 at 06:12
  -> preserve(B,V1,H)
  -> ingest(B,V1)
  -> transform(B,V1)
  -> assert output.business_date == B
  -> assert output.source_version == V1
  -> publish(B,V1)
  -> accept(B,V1)
```

The orchestrator variant expresses those stages as persisted task states; the hardened-script variant expresses them as state transitions in a small durable ledger. **The correctness predicate is identical.** The choice of technology affects how much of the state/retry/backfill machinery is supplied by the platform.

dbt helps with DAG execution and tests, but its freshness mechanism should not be substituted for that exact-period assertion: current documentation defines freshness using latest-loaded timestamp/metadata against age thresholds. [10](https://docs.getdbt.com/reference/commands/freshness) A “fresh enough” table can still be the wrong contractual version for a particular report; that is an inference from what freshness measures, not a dbt defect.

**Worked failure: scheduler host interruption.** Suppose the local host is down from 05:55 to 06:20. Typical traditional `crond` does not durably track the missed launch, according to Google’s SRE description. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) A systemd calendar timer with `Persistent=true` can activate the service when the timer returns after a missed occurrence. [2](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html) But the robust runner still asks:

```text
expected_business_dates(now)
minus
accepted_business_dates()
=
periods requiring recovery
```

That reconciliation survives a much broader class of failures than asking whether a particular 06:00 trigger was emitted. If three business periods were missed, the ledger—not a single catch-up timer activation—determines that three periods remain outstanding.

**Worked failure: duplicate trigger.** Google Cloud Scheduler’s at-least-once contract means the exact same nominal schedule may produce more than one request. [3](https://cloud.google.com/scheduler/docs/overview) The workflow therefore checks `(B,V,H)` before performing a mutating stage. A duplicate of the same identity resumes/no-ops. A new version `V2` for the same `B` is *not* considered the same operation and is retained separately. If `V1` was already accepted, `V2` needs an explicit correction/supersession decision; it must not silently replace the prior report.

**Worked failure: backfill crossing a DST transition.** The executed fixture enumerated the Czech business dates:

```text
2026-03-27
2026-03-30
2026-03-31
```

directly as dates, crossing the `Europe/Prague` DST weekend. It did **not** produce them by repeatedly adding 24 hours to an execution timestamp. This is the correct conceptual separation: historical `B` values are business periods; timezone scheduling only decides when a live trigger should occur. Airflow’s data-interval/catchup model reflects the same broad separation and specifically warns that workflows intended for catchup need to operate on the interval rather than “Now.” [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

**Worked failure: publication response lost after the side effect.** The worst recovery is often not a failed computation but “maybe succeeded.” Sending a report twice may be materially different from rebuilding a warehouse partition twice. The design therefore uses a stable publication key such as:

```text
report:2026-03-27:v1
```

and either an external API with an idempotency contract or a query/reconciliation operation before retry. This follows the retry/idempotency architecture described by Amazon; it is not a claim that every report-delivery service implements such an API. [8](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/)

For the second draft claim—**“One automation waits for another. Who ensures the whole process finished?”**—I would keep the premise but sharpen the answer:

> **The component that owns the business acceptance state must ensure whole-process completion. A scheduler firing all steps is insufficient; an orchestrator helps only if its final gate is defined as the business completion predicate rather than merely “all runnable tasks ended.”**

Airflow’s leaf-state caveat and Prefect’s programmable final-state semantics are strong counterevidence to any stronger claim that an orchestrator automatically guarantees this. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

**Firsthand synthetic execution performed for this dossier:** seven assertions passed on Python 3.13.5 using system `zoneinfo`: Czech DST civil-vs-elapsed behavior, US Eastern transition classification, business-date enumeration across the Prague DST weekend, duplicate/hash conflict handling, stale-yesterday report rejection, durable acceptance reconciliation, and same-period/new-version supersession protection. No external provider, scheduler, dbt installation or orchestrator was executed.

The actual portable fixture is included:

[Download the complete evidence assets ZIP](sandbox:/mnt/data/workflow_orchestration_evidence_assets.zip)

[Open the time/period contract YAML](sandbox:/mnt/data/workflow_orchestration_evidence_assets/time_period_contract.yaml)

[Open the dependency/recovery CSV](sandbox:/mnt/data/workflow_orchestration_evidence_assets/dependency_recovery.csv)

[Open the executable synthetic Python fixture](sandbox:/mnt/data/workflow_orchestration_evidence_assets/simulate_contract.py)

The ZIP also contains `acceptance_cases.json`, the README and the captured `test-output.json`. The code uses only Python’s standard library plus the host timezone database; it deliberately does **not** pretend to validate AWS, Google, GitHub, Airflow, Prefect, Dagster or dbt provider behavior.

## Claim-to-source evidence ledger

All sources below were inspected on **2026-10-10**. “Supported” means the source directly supports the scoped formulation, not a broader universal claim.

| Claim target | Primary source and exact section | Source date/version | Exact evidence scope / type | Status and editorial advice |
|---|---|---|---|---|
| “Traditional cron tracks whether the launched business workflow completed.” | Google SRE, **Distributed Periodic Scheduling with Cron → Reliability Perspective**: https://sre.google/sre-book/distributed-periodic-scheduling/ [1](https://sre.google/sre-book/distributed-periodic-scheduling/) | Online SRE Book chapter; material was previously published in ACM Queue in 2015 | **Primary company engineering/practitioner design.** Describes common single-machine cron and Google’s distributed-cron design, not every cron implementation. | **Contradicted.** Rewrite: conventional cron is primarily a launcher; completion state must come from application/orchestration/monitoring. |
| “Job/launch identity matters during failover and retries.” | Google SRE, same chapter, **Roles of the Leader and Follower**: https://sre.google/sre-book/distributed-periodic-scheduling/ [1](https://sre.google/sre-book/distributed-periodic-scheduling/) | Same | **Reported production architecture.** Google records start/end state and uniquely identifies a scheduled launch by scheduled/start identity. | **Supported, qualified.** Keep as architecture evidence, not as proof every workflow needs Paxos. |
| “Cron has one universal DST behavior.” | Debian `cron(8)`, **NOTES**: https://manpages.debian.org/bookworm/cron/cron.8.en.html [11](https://manpages.debian.org/bookworm/cron/cron.8.en.html) | Source last updated 2023-03-02; Debian bookworm package; HTML generated later | **Implementation documentation** for Debian’s Vixie cron with Debian-specific fixes. Forward skipped fixed-time jobs are run soon after; repeated-time fixed jobs are not rerun. | **Contradicted.** Remove universal wording and always name implementation/version. |
| “A small local scheduler cannot catch a host-down missed calendar run.” | `systemd.timer(5)`, **Persistent=**, plus activation behavior: upstream primary page was inaccessible in this research environment; Debian mirror used: https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html [2](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html) | Debian testing mirror metadata observed in research; current testing systemd manpage | **Upstream-manpage mirror, not directly fetched primary source.** `Persistent=true` stores last-trigger information and activates after downtime if a run was missed; active service is not restarted by timer. | **Contradicted for systemd.** A hardened local baseline is stronger than classic cron. Qualify due source-access gap and note it does not create one workflow instance per missing business period. |
| “Managed scheduling gives exactly one invocation.” | Google Cloud Scheduler, **About Cloud Scheduler → delivery / deduplication**: https://docs.cloud.google.com/scheduler/docs/overview [3](https://cloud.google.com/scheduler/docs/overview) | Last updated 2026-10-07 UTC | **Primary provider documentation.** Cloud Scheduler explicitly states at-least-once delivery and possible repeated invocation. | **Contradicted.** Replace “once” with idempotent/reconcilable processing keyed by job + schedule/period identity. |
| “Scheduler retries ensure every future scheduled period still runs.” | Google Cloud Scheduler, **Retry jobs → Maximum retry attempts**: https://docs.cloud.google.com/scheduler/docs/configuring/retry-jobs [12](https://cloud.google.com/scheduler/docs/configuring/retry-jobs) | Last updated 2026-10-07 UTC | **Primary provider documentation.** A future scheduled execution can be skipped while retries of the previous invocation continue. | **Contradicted.** Reconciliation must compare expected business periods with completed/accepted periods. |
| “A native hosted schedule is an exact business deadline trigger.” | GitHub Docs, **Events that trigger workflows → `schedule`**: https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows#schedule [7](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows) | Page date not stated; current at inspection | **Primary provider documentation.** Scheduled workflows can be delayed under load and some queued jobs can be dropped. | **Contradicted.** Do not make “trigger observed” the only deadline monitor. |
| “Every 24 hours equals every day at the same local time.” | AWS EventBridge Scheduler, **Daylight savings time on EventBridge Scheduler**: https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html [15](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) | Page date not stated; current at inspection | **Primary provider documentation.** `rate(1 days)` means 24 elapsed hours; timezone cron follows local-time DST rules. | **Contradicted.** Rewrite to distinguish elapsed interval from civil/calendar schedule. |
| “A nonexistent local cron time is handled the same across providers.” | AWS section above; GitHub `schedule`; Debian `cron(8)` notes. [15](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) | Current versions as above | **Cross-provider documented behavior comparison.** AWS skips; current GitHub advances spring skipped time to next valid time; Debian cron runs eligible skipped fixed-time jobs soon after. | **Contradicted.** Strong candidate for article claim: cron expression syntax does not define provider delivery semantics. |
| “Timezone rules can safely be treated as static infrastructure data.” | IANA, **Time Zone Database → Latest version**: https://www.iana.org/time-zones [14](https://www.iana.org/time-zones) | tzdb **2026e**, released 2026-09-29 | **Primary standards/data-maintainer source.** IANA states rules are periodically updated in response to political changes. | **Contradicted.** Log/update TZ data and add a freshness trigger when relevant zones/rules change. |
| “Airflow calendar cron and elapsed duration schedules have identical DST semantics.” | Airflow 3.3.2, **Time Zones → Cron schedules / Time deltas**: https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) | Apache Airflow **3.3.2**; page date not stated | **Primary project documentation.** Cron follows DST; subsequent timedelta runs do not readjust for DST. Airflow also warns about timezone-library freshness. | **Contradicted.** Keep the calendar-vs-elapsed distinction explicitly. |
| “Airflow gives durable concepts for period/catchup/backfill that a script would otherwise implement.” | Airflow 3.3.2, **Dag Runs → Data Interval / Catchup / Backfill**: https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) | Airflow **3.3.2** | **Primary project documentation.** Data intervals, catchup and historical backfill behavior. | **Supported.** This is a real reason to adopt orchestration when historical/partial recovery becomes regular. |
| “A green Airflow DAG necessarily means every important task succeeded.” | Airflow 3.3.2, **Dag Run Status**: same URL above. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) | Airflow **3.3.2** | **Primary project documentation.** Leaf-node and trigger-rule semantics can produce DAG success despite an intermediate failure. | **Contradicted.** Final business acceptance must be modeled explicitly. |
| “A `COMPLETED` Prefect flow proves that no task failed.” | Prefect v3, **States → Flow return values**: https://docs.prefect.io/v3/concepts/states [5](https://docs.prefect.io/v3/develop/manage-states) | Prefect **v3**, page date not stated | **Primary project documentation.** Programmatic handling can allow an internal failed task while the flow returns successfully and is completed. | **Contradicted.** Same editorial treatment as Airflow: workflow final state is only as meaningful as its definition. |
| “dbt can execute transformation/tests in dependency order and leave execution artifacts.” | dbt Developer Hub, **dbt build → Details**: https://docs.getdbt.com/reference/commands/build [9](https://docs.getdbt.com/reference/commands/build) | Current docs updated 2026-09-16; v2 behavior noted on page | **Primary project documentation.** Build DAG ordering, manifest/run results and default skip behavior after upstream test failures. | **Supported.** Use artifacts/tests as evidence, but still pass and validate contractual B/V. |
| “dbt freshness alone proves this is the exact reporting period/source version.” | dbt Developer Hub, **dbt freshness → How freshness is evaluated**: https://docs.getdbt.com/reference/commands/freshness [10](https://docs.getdbt.com/reference/commands/freshness) | Current docs updated 2026-09-16; v2 | **Primary project documentation.** Freshness compares latest timestamp/metadata against age thresholds. | **Contradicted as a completeness claim.** Add explicit period/version tests; freshness is adjacent evidence. |
| “Retries can be blindly repeated after an uncertain response.” | AWS Builders’ Library, **Making retries safe with idempotent APIs → Retrying and side effects / Reducing client complexity**: https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/ [8](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/) | Rendered page did not expose a reliable publication date; inspected 2026-10-10 | **Primary company engineering guidance with concrete AWS practice.** Discusses uncertainty after timeout, duplicate side effects, client request IDs and atomic idempotency records. | **Contradicted.** Retry only with idempotency/reconciliation semantics appropriate to the side effect. |
| “An orchestrator has zero operational surface of its own.” | Airflow 3.3.2, **Dag File Processing / fine-tuning**: https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/dagfile-processing.html [13](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/dagfile-processing.html) | Airflow **3.3.2** | **Primary project documentation.** Continuous DAG parsing, DB/filesystem, CPU/memory and tuning considerations. | **Contradicted.** Do not say “orchestrator is always more reliable/cheaper.” Treat control-plane ownership as part of the decision. |

The source pattern is important: the strongest evidence here establishes **specific system semantics**, not a universal ranking. Provider docs can tell us whether a scheduler is at-least-once or how it handles DST; they cannot tell us that “Airflow saves 37% of operational effort” for this synthetic company. No such outcome is claimed.

## Contradictions, gaps, assets and freshness triggers

The central recommendation survives several serious challenges.

**Counterargument: “systemd or a managed scheduler is already reliable enough, so an orchestrator is unnecessary.”** For the bounded linear chain, this is often correct. systemd can catch missed local calendar activation and suppress another timer activation while its service is active; Google Cloud Scheduler externalizes the trigger and provides retry/deduplication information. [2](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html) If one program can cheaply reconcile the whole period, an orchestrator may add little. This is why the recommendation is *not* “use Airflow for five steps.”

**Counterargument: “A script can implement everything an orchestrator does.”** In principle, yes. A script plus SQL can represent task states, leases, retries, timeouts, backfills and historical run metadata. The issue is not capability but whether bespoke orchestration remains the cheapest thing to own. Once the application contains a dependency scheduler, retry queue, per-task run database, historical backfill planner and operational inspection layer, the argument for a standard orchestration abstraction becomes much stronger. That boundary is interpretive rather than experimentally measured.

**Counterargument: “Orchestration itself means reliability.”** The evidence does not support this as a general statement. Airflow can reach a successful DAG state despite an intermediate failure under documented trigger-rule conditions, and Prefect can complete a flow after a failed task has been programmatically handled without propagating failure. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) A poorly modeled orchestrator can therefore be more confidently wrong than a small script.

**Counterargument: “Just monitor source freshness.”** Freshness is useful but weaker than period/version completion. dbt’s current freshness mechanism measures age using timestamps or adapter metadata. [10](https://docs.getdbt.com/reference/commands/freshness) The requested business requirement is categorical: *is this exact accepted report built from B and V?* Those are different predicates.

**Counterargument: “DST is an edge case we can ignore.”** DST is not the main reason to buy an orchestrator, but it is a reason to specify time correctly. More importantly, the evidence shows that even schedulers using cron-like expressions do not agree on skipped-hour behavior. [11](https://manpages.debian.org/bookworm/cron/cron.8.en.html) The practical fix is cheap: avoid transition-hour schedules when possible, use an IANA timezone, keep business period separate from trigger timestamp, and reconcile missing periods independently of clock events.

**Unresolved evidence gap:** I found no controlled empirical comparison measuring the same bounded reporting workflow under (a) a hardened script and (b) a workflow orchestrator while holding failure requirements constant. There is therefore no evidence-backed universal threshold such as “after N tasks Airflow reduces incidents,” no justified MTTR effect size, and no universal cost or ROI claim. The recommendation instead combines documented component behavior with explicitly labeled architecture reasoning.

**Operational-cost gap:** exact economics depend on deployment model, cloud/provider, worker/runtime demand, existing team capability and whether an orchestrator is already an organizational standard. A team already operating Airflow has a very different marginal cost from a five-person company adopting and operating it only for one daily report. No generic dollar estimate is defensible from the inspected evidence.

**Market/context gap:** no inspected evidence established a material Czech-versus-US difference in the architectural choice itself. The concrete differences relevant to this dossier are timezone rules and business-calendar inputs. Security, residency, labor, sector regulation or contractual retention rules could alter control-plane choices in a real deployment, but those are outside the bounded evidence collected here and should not be inferred.

**Source-access gaps:** the direct freedesktop.org systemd manpage returned HTTP 403 in the research environment, so the corresponding behavior was checked against Debian’s current testing mirror of the systemd manpage and explicitly labeled as such. The attempted man7 cron pages timed out; Debian’s package-specific Vixie cron manpages were used instead, and all DST claims were restricted to that implementation rather than generalized to “cron.” No PDF evidence was required for the conclusions above.

**Candidate article angles** emerge as distinct pieces rather than one generic “cron versus Airflow” article:

| Angle | Defensible thesis | Most useful evidence/caveat |
|---|---|---|
| **A green job is not a finished business process** | Define completion in business-period/version terms, not task/process exit status | Airflow and Prefect final-state counterexamples; synthetic stale-yesterday scenario. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| **Cron is not the real enemy; hidden state is** | A hardened scheduled program can be the minimum sufficient architecture for a bounded linear workflow | Traditional cron limitation versus systemd/managed scheduler improvements. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) |
| **Every 24 hours is not every day at 06:00** | Civil/calendar scheduling and elapsed intervals diverge, and cron-like providers differ at DST | Airflow, AWS, GitHub and Debian behavior. [6](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) |
| **When a script quietly becomes a workflow engine** | The adoption boundary is recovery/state complexity, not merely number of steps | Airflow data intervals/catchup/backfill plus its own operational surface. [4](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| **One automation waits for another—who owns “done”?** | Dependency execution and whole-business-task acceptance are different responsibilities | Google launch identity, Airflow/Prefect final-state semantics, acceptance ledger. [1](https://sre.google/sre-book/distributed-periodic-scheduling/) |

**Useful assets already produced:** the YAML time/period contract can serve as a design-review template; the CSV dependency/recovery table can become a runbook worksheet; the JSON acceptance cases can seed tests; and the Python fixture demonstrates the distinction between civil and elapsed time plus minimal durable acceptance semantics. These assets are synthetic and portable text/code rather than screenshots of a particular vendor.

**Freshness triggers for this dossier:**

| Trigger | What should be rechecked |
|---|---|
| IANA publishes a timezone change affecting `Europe/Prague`, `America/New_York` or another deployed zone | DST/offset fixtures and runtime timezone-library version. IANA’s database is intentionally updated as rules change. [14](https://www.iana.org/time-zones) |
| Airflow major/minor upgrade | DAG-run leaf semantics, catchup/backfill, timezone behavior and deployment architecture |
| Prefect major version/state-model change | Final-state propagation, retries, workers and timeout semantics |
| Google Cloud Scheduler guarantee/retry update | At-least-once delivery, deduplication identifiers and overlapping retry behavior |
| AWS EventBridge Scheduler scheduling change | Rate-versus-calendar interpretation and DST skipped/repeated behavior |
| GitHub Actions schedule behavior changes | DST behavior, delay/drop caveats, minimum cadence and timezone support |
| dbt major version | Build/test propagation, artifacts, freshness behavior and suitable exact-period/version assertions |
| Process gains another independent source or owner | Re-evaluate orchestrator boundary immediately |
| Frequent partial backfills or multiple concurrent periods appear | Re-evaluate whether the bespoke ledger is becoming a hidden orchestrator |
| Publication acquires a non-idempotent external side effect | Strengthen reconciliation/idempotency contract before increasing retries |
| Business deadline/calendar changes | Update the period contract independently from scheduler syntax |

The most reusable conclusion is consequently narrower than “orchestrators are better”:

> **Use the smallest system that can durably answer four questions: which business period/version is this, what has definitely happened, what is safe to retry, and has the business outcome actually been accepted?**

For the bounded daily chain studied here, that can still be a hardened scheduled program. Once answering those questions requires a growing task-state machine, frequent partial/backfill operations, several dependencies or multiple operational owners, a durable orchestrator stops being infrastructure fashion and starts replacing machinery you would otherwise have to build and maintain yourself.

## Portable citation index

[1] [Google SRE: Distributed Periodic Scheduling with Cron Service](https://sre.google/sre-book/distributed-periodic-scheduling/)

[2] [systemd.timer(5) — systemd — Debian testing — Debian Manpages](https://manpages.debian.org/testing/systemd/systemd.timer.5.en.html)

[3] [About Cloud Scheduler  |  Google Cloud Documentation](https://cloud.google.com/scheduler/docs/overview)

[4] [Dag Runs — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

[5] [States - Prefect](https://docs.prefect.io/v3/develop/manage-states)

[6] [Time Zones — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html)

[7] [Events that trigger workflows - GitHub Docs](https://docs.github.com/en/actions/reference/workflows-and-actions/events-that-trigger-workflows)

[8] [references-details-empty](https://aws.amazon.com/builders-library/making-retries-safe-with-idempotent-APIs/)

[9] [About dbt build command | dbt Developer Hub](https://docs.getdbt.com/reference/commands/build)

[10] [About dbt freshness command | dbt Developer Hub](https://docs.getdbt.com/reference/commands/freshness)

[11] [cron(8) — cron — Debian bookworm — Debian Manpages](https://manpages.debian.org/bookworm/cron/cron.8.en.html)

[12] [Retry jobs  |  Cloud Scheduler  |  Google Cloud Documentation](https://cloud.google.com/scheduler/docs/configuring/retry-jobs)

[13] [Dag File Processing — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/dagfile-processing.html)

[14] [Time Zones](https://www.iana.org/time-zones)

[15] [Schedule types in EventBridge Scheduler - EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)