# Workflow Orchestration versus Simple Scheduled Scripts: Evidence Dossier

**Research snapshot:** 2026-10-10  
**Perspective:** Academic / systems-design evidence  
**Decision supported:** How to choose between a scheduled script, native scheduler/automation, and a workflow orchestrator for the same bounded task and failure requirements  
**Scope:** Public original sources plus an explicitly synthetic daily reporting process and locally executed synthetic tests. No private production systems, provider accounts, customer results, or prior project reports were used.

## Decision answer and uncertainty

For the bounded daily chain studied here—**source export → immutable raw preservation → ingestion → dbt/warehouse transformation → business report**—the best default is **not “use an orchestrator” and not “just put five jobs in cron.”** It is:

> **First define one business-completion contract. Then use the simplest mechanism that can preserve that contract under failure.**

For a small, serial, single-owner workflow, that can legitimately be **one scheduled program plus a durable per-period ledger, lock/idempotency keys, explicit version checks, bounded waits, reconciliation, and alerts**. A full workflow orchestrator becomes preferable when per-step retries, historical backfills, multiple concurrent periods, branching dependencies, long waits, cross-team handover, or operator visibility become routine enough that the script is accumulating its own orchestration control plane.

This conclusion is an **architectural interpretation**, not an empirical finding that one technology has a universal lower incident rate or lower total cost. I found strong documentation for scheduler and orchestrator semantics, and strong older systems research supporting explicit dependency and end-to-end correctness models, but **no controlled study that establishes a general reliability or ROI threshold for a small warehouse team choosing cron/script versus Airflow-like orchestration**. The closest workflow research focuses on modeling expressiveness or scientific/distributed workloads rather than this small-team operational decision. [1](https://link.springer.com/article/10.1023/A%3A1022883727209)

The central distinction is:

\[
\text{Trigger success} \neq \text{task success} \neq \text{business completion}
\]

Schedulers principally decide **when an attempt should be made**. Workflow engines additionally model dependencies and execution state. Neither automatically knows whether the report represents the **correct business period and source version**. That last property belongs in the application/workflow contract. This follows the classic end-to-end design principle: a lower layer can help, but correctness that requires application knowledge must ultimately be checked where that knowledge exists. [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

### Practical decision summary

| Approach | Verdict for this bounded chain | Main reason |
|---|---|---|
| Manual checklist | Valid initial baseline | Makes the actual business contract visible before automation; poor scalability and handover |
| Several independent scheduled jobs | **Reject for the stated failure requirements** | Uses clock order as a proxy for dependency/version correctness |
| One scheduled script, no durable state | Acceptable only for low-consequence best-effort work | Easy to operate, but weak recovery/reconciliation semantics |
| One scheduled script + durable ledger/lock/checks | **Best simplicity baseline** | Can meet the bounded requirements without a separate orchestration control plane |
| Managed scheduler + the same robust script | Strong intermediate option | Adds provider retry/time-zone/DLQ facilities while leaving business-state logic in the worker |
| Workflow orchestrator | **Prefer as complexity grows** | Standardizes task dependencies, state, selective retry, backfill and operator visibility |
| Durable managed workflow service | Alternative when long waits/external service coordination dominate | Can persist/checkpoint control-flow state without self-hosting Airflow-like infrastructure |

Representative schedulers themselves document that duplicate or missed invocations can occur: Kubernetes says a CronJob may under some circumstances create two Jobs or none and therefore recommends idempotent Jobs; Google Cloud Scheduler says rare duplicate requests are possible and recommends idempotent handlers. [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) Airflow, meanwhile, has a persistent scheduler and metadata database and can model data intervals, dependencies, catchup and backfill—but those capabilities introduce their own operational system and upgrade lifecycle. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

**Confidence by claim type:** high for documented behavior of the inspected versions/services; high for the need to distinguish business period from trigger time; medium for the recommended escalation threshold from script to orchestrator; low for any universal cost, incident-rate or ROI comparison because the required comparative empirical evidence does not appear to exist in the inspected literature.

## Bounded process and the alternatives being compared

### The recognizable process

The dossier uses one **synthetic but realistic** reporting workflow. Nothing in this process is claimed as a customer deployment or firsthand production experience.

| Element | Synthetic process contract |
|---|---|
| Business purpose | Deliver the correct daily management report |
| Process owner | Data/Analytics on-call owner |
| Trigger | 06:30 local civil time on eligible business days |
| Business time zone | `Europe/Prague` |
| Reporting period | Previous eligible business date, determined independently of trigger timestamp |
| Primary input | Source export plus manifest containing `business_date`, immutable `source_version`, checksum/control metadata |
| Processing | Preserve raw → ingest → transform/dbt → render report |
| Identity propagated downstream | `(business_date, source_version)` |
| Soft source timeout | 07:30 local |
| Business deadline | 08:30 local |
| Success | The durably published report passes checks and declares the same `business_date` and `source_version` as the accepted source manifest |
| Key exceptions | Late source, duplicate trigger, overlap, partial failure, stale downstream state, permission/configuration failure, scheduler/host outage, DST transition, backfill |
| Recovery unit | The exact `(business_date, source_version)` rather than “whatever is latest now” |

The times are **synthetic design inputs**, not industry recommendations.

The most important process-design move happens before selecting technology: **eliminate implicit synchronization by clock time.** Instead of saying “ingest at 06:45 because export should be done by then, run dbt at 07:00 because ingest should be done,” every stage consumes an explicitly identified predecessor. That is consistent with workflow research that deliberately separates workflow requirements and patterns from particular workflow languages and products. [1](https://link.springer.com/article/10.1023/A%3A1022883727209)

Lamport's classic distributed-systems treatment is also relevant, with an important qualification: it is not a study of batch schedulers. Its formal point is that causal order and physical-clock order are different concepts. Applying that principle here is an **inference**: “task B happened later on a clock than task A” is not proof that B consumed A's intended result. An explicit dependency/version identity is stronger evidence. [5](https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/?lang=ko-kr)

### Simplest manual/native baseline

Before automation, an operator can execute the same contract manually:

The owner checks for `manifest(B,v)`, verifies that it is the expected period, preserves the raw export without overwriting another version, launches ingestion, launches the transformation after verifying the ingested `(B,v)`, renders the report, and accepts it only after verifying its period/version and control totals.

That manual baseline is operationally expensive, but it is conceptually useful: **automation should preserve those decisions, not merely automate the button presses.**

A second simplification is even more important: if the chain is strictly serial, **one process that executes all four technical stages sequentially is safer and simpler than four independent cron expressions**. Sequential process execution supplies basic control-flow ordering mechanically. Durable state is still needed because a process can die between stages, and explicit version checks are still needed because a successful command can consume stale external data.

### Same requirements, different implementations

| Requirement | Independent schedules | One script + durable state | Managed native scheduler + script | Airflow-like orchestrator |
|---|---|---|---|---|
| Trigger at configured time | Yes | Yes | Yes, provider-specific semantics | Yes |
| Exact dependency correlation `(B,v)` | Must be custom | **Custom, straightforward** | Custom | Workflow can carry it, but application must define it |
| Sequential dependency | Clock convention unless custom checks added | Direct control flow | Direct control flow in target worker | Native DAG dependency model; scheduler launches ready tasks after dependencies complete [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) |
| Retry | Per-script/custom | Custom bounded retry | Often provider retry + application retry | Task retry is modeled by orchestrator |
| Duplicate protection | Custom | Custom lock/idempotency key | Still required; scheduler duplicates can occur [6](https://docs.cloud.google.com/scheduler/docs/creating) | Still required for externally visible side effects |
| Partial-failure resume | Awkward | Ledger identifies committed step | Same as script | Task state supports selective rerun/backfill; application-side idempotency still matters [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| Backfill by historical period | Custom CLI/loop | Simple if period is first-class | Same | Explicit catchup/backfill facilities [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| Durable execution history | Logs/custom table | **Small custom ledger** | Worker ledger + provider invocation logs | Metadata/control plane |
| Whole-workflow status | No | Custom acceptance state | Custom acceptance state | Native DAG status, but DAG status still needs careful design [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| Operator visibility | Fragmented | Small dashboard/query/alerts needed | Provider + application state | Usually materially better standardized visibility |
| Infrastructure/upkeep | Low | Low-to-moderate | Moderate vendor dependency | Higher when self-managed: scheduler, metadata DB, deployments and migrations [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) |
| Best fit | Best-effort independent jobs | **Bounded serial workflow** | Bounded workflow wanting managed triggering | Growing dependency/backfill/operator complexity |

A significant counterexample to “orchestration necessarily means a heavy self-hosted control plane” is a managed workflow service. Google Workflows says it checkpoints state after each step, replicates across zones, supports retries/timeouts/error handling, waits, and integrated logging/monitoring. That is **provider-documented architecture**, not independently measured reliability, but it demonstrates that the control-plane trade-off depends heavily on deployment model. [8](https://cloud.google.com/workflows)

### Why a scheduler is not a dependency manager

Provider documentation is unusually helpful here because different real schedulers visibly make different choices.

Google Cloud Scheduler may send duplicate requests; it exposes the original scheduled time in a header for deduplication, skips later scheduled runs while an earlier run is still in progress, and can retry failures with configurable exponential backoff. [6](https://docs.cloud.google.com/scheduler/docs/creating) Kubernetes CronJob provides `startingDeadlineSeconds` and same-CronJob concurrency policies, yet still documents approximate Job creation and possible duplicate or absent Jobs. [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) AWS EventBridge Scheduler offers retries/DLQ integration and calendar/rate scheduling semantics. [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

These are useful trigger-level facilities. None of those facilities, by themselves, can know that a warehouse table reflects **source export version `v` for business date `B`**. That information has to be carried and checked by the target application or by a workflow whose tasks explicitly model it.

### Why an orchestrator is not automatically “correct”

Airflow's own documentation gives a particularly useful counterexample to simplistic “green DAG = correct workflow” reasoning. DAG-run state is determined from leaf tasks, and Airflow warns that a leaf task with an `all_done` trigger rule can succeed and cause the whole DAG run to be reported as successful even though something failed earlier. [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

Airflow's data-interval model is valuable: a run has an explicit interval, logical date denotes the interval rather than execution time, catchup can create runs for missing intervals, and backfill can re-run historical intervals. [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) But the engine cannot infer whether an external export stamped `2026-10-09` is the correct source version just because a task returned exit status zero. The end-to-end acceptance condition remains necessary. [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

That is the strongest argument against framing the decision as **“cron versus reliability.”** The actual choice is closer to:

> **Where should durable state, dependency semantics, recovery logic and operator visibility live?**

## Evidence synthesis around time, dependency and recovery

### Time is part of the business contract

There are at least four time concepts in this workflow, and collapsing them is a common design error:

1. **Business period** — e.g. reporting date `B`.
2. **Local civil/calendar schedule** — e.g. “06:30 Europe/Prague on a business day.”
3. **UTC instant** — unambiguous timestamp for logging/events.
4. **Elapsed duration** — e.g. “retry after 15 minutes.”

These are not interchangeable.

AWS explicitly distinguishes cron/calendar scheduling from rate scheduling. Its documentation says a `rate(1 days)` schedule represents a **24-hour duration**, so DST does not redefine the duration; its local cron schedules, in contrast, are evaluated in the specified IANA time zone. On spring-forward, a nonexistent scheduled local time is skipped; on fall-back, EventBridge Scheduler says the repeated scheduled local time runs once rather than twice. [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

Google Cloud Scheduler similarly describes its cron execution as wall-clock based, supports explicit time zones, defaults to UTC, and warns that DST can make local-time executions behave unexpectedly around repeated times; it recommends UTC when a precise cadence rather than local civil time is required. The inspected page was last updated 2026-10-07. [10](https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules)

Those two providers do **not** establish a universal cron rule. They establish the opposite: **the semantics belong to the implementation contract and must be inspected.** Kubernetes has yet another explicit model: a CronJob can specify `.spec.timeZone`; if omitted, the schedule is interpreted relative to the `kube-controller-manager` local zone. [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)

Airflow stores datetime data internally in UTC while supporting IANA-zone-dependent schedules, and its documentation specifically flags DST as a source of local-time errors. It also warns that Pendulum's bundled timezone database may be less current than the IANA database unless configured to use system timezone data. [11](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html)

### Inline time/period contract

For the synthetic case, the contract is:

\[
B = \operatorname{PreviousEligibleBusinessDate}
  (\operatorname{LocalDate}(\text{trigger instant}, \text{Europe/Prague}), C)
\]

where \(C\) is the organization's explicit business calendar.

The workflow identity is:

\[
R=(\text{workflow\_id}, B, v)
\]

where \(v\) is the immutable accepted source version.

Business completion is:

\[
\begin{aligned}
Accepted(B,v) = {} &
ManifestValid(B,v) \\
&\land RawChecksum(B,v)=ManifestChecksum(B,v) \\
&\land Ingested(B,v) \\
&\land Transformed(B,v) \\
&\land ReportPublished(B,v) \\
&\land ControlChecksPass(B,v)
\end{aligned}
\]

and timeliness is a separate property:

\[
OnTime(B,v)=Accepted(B,v)\land accepted\_at\le D(B)
\]

A report can therefore be **correct but late**, or **on time but incorrect**. Those should not collapse into one boolean.

| Time/period field | Contract | Rationale |
|---|---|---|
| Business time zone | `Europe/Prague` | Explicit organizational choice, not host default |
| Calendar trigger | 06:30 local on eligible business days | “Same local business time” requirement |
| Business date | Previous eligible business date | Independent of run timestamp |
| Weekend/holiday policy | Explicit supplied business calendar | Weekday cron is not a business-calendar definition |
| Storage/log timestamps | UTC instants | Unambiguous event timestamps; Airflow similarly stores datetimes in UTC [11](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) |
| Retry delays | Elapsed durations, e.g. 5m/15m/30m | Retry duration must not redefine reporting period |
| Dependency ID | `(business_date, source_version)` | Correlates the right upstream result rather than “latest” |
| Soft source timeout | 07:30 local | Synthetic owner decision |
| Business deadline | 08:30 local | Synthetic owner decision |
| DST nonexistent time | Never silently invent an instant; apply documented scheduler policy or reject configuration | AWS explicitly skips nonexistent cron occurrence; other providers may differ [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) |
| DST repeated time | Declare first/second/once policy rather than assume | AWS chooses once; Google warns of wall-clock anomalies [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) |
| Backfill | Enumerate `B` values | Avoid making a 23/25-hour civil day equivalent to 24 elapsed hours |
| Completion | Acceptance of exact `(B,v)` | The final business state, not merely process exit status |

For a US deployment the architecture does not materially change: replace `Europe/Prague` with the intended IANA business zone, such as `America/New_York`, and provide the organization's US business calendar. AWS's own documentation uses `America/New_York` as a scheduling example. [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) There is no useful Czech-versus-US technology recommendation here; the meaningful local differences are the calendar, zone rules and business deadlines.

### Dependency state, timeout and recovery contract

| State | May enter when | May advance when | Timeout/deadline | Recovery | Whole task complete? |
|---|---|---|---|---|---|
| `waiting_source` | Period \(B\) created | Exact `manifest(B,v)` passes validation | 07:30 synthetic soft cutoff | Mark `blocked_source_late`; alert/poll; resume same \(B\) when exact source arrives | No |
| `raw_preserved` | Manifest accepted | Immutable raw checksum matches manifest | Example 10 min step timeout | Idempotent retry; never overwrite another version | No |
| `ingested` | Raw `(B,v)` verified | Warehouse landing state records `(B,v)` and control totals | Example 20 min | Retry/repair exact `(B,v)`; reconcile duplicates | No |
| `transformed` | Ingestion accepted | Transformation/tests correspond to `(B,v)` | Example 30 min | Retry failed transformation only; do not fall through to “latest” input | No |
| `report_rendered` | Transform accepted | Report metadata/checks identify `(B,v)` | 08:15 synthetic recovery target | Re-render same `(B,v)`; version/atomically publish | No |
| `accepted` | End-to-end checks pass | Terminal | 08:30 business deadline | If later, retain correctness but mark `late` | **Yes** |
| `failed_retryable` | Transient failure | Retry budget remains and prerequisites still valid | Elapsed-duration retry budget | Same idempotency key, then escalate | No |
| `failed_terminal` | Invalid data/config/permission or exhausted budget | Human/system repair explicitly clears condition | Owner-defined | Repair cause; resume exact `(B,v)`; record intervention | No |

A scheduler retry policy alone does not implement this table. Google Cloud Scheduler, for example, retries lack of acknowledgement according to its configured backoff and notes that retries may overlap enough to cause a later scheduled execution to be skipped. [12](https://docs.cloud.google.com/scheduler/docs/configuring/retry-jobs) AWS's DLQ contains failed invocation information after retries are exhausted, which is useful evidence for recovery, but the workflow still has to reconcile that failed invocation with the intended business period. [13](https://docs.aws.amazon.com/scheduler/latest/UserGuide/configuring-schedule-dlq.html)

### Worked failure scenario: every process is green, the report is wrong

**This is a synthetic scenario, not a reported incident.**

Assume report period \(B\) is due today.

A fragile design has four independent schedules:

| Time | Scheduled event | Naïve outcome |
|---|---|---|
| 06:30 | source/export process expected | Source is unexpectedly late |
| 06:45 | ingestion | Finds no new export; implementation exits successfully/no-ops |
| 07:00 | dbt transformation | Successfully transforms the last available warehouse input |
| 07:15 | report | Successfully renders a report from that transformed data |
| 08:30 | business deadline | All technical jobs appear green, but report contains the previous version/period |

Clock order gave no proof of data dependency. This is precisely the kind of distinction for which explicit causal/state relationships are stronger than time ordering, although applying Lamport's formal distributed-system argument to this concrete warehouse case is an architectural inference rather than a result tested by his paper. [5](https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/?lang=ko-kr)

Under the proposed contract, ingestion cannot advance until it sees `manifest(B,v)`. When the source arrives at 07:05, the same run resumes with that exact identity. Raw preservation records its checksum, ingestion records `(B,v)`, transformation may only consume that accepted landing state, and reporting may only be accepted with the same `(B,v)`. If the source remains absent at 07:30, the state is **`blocked_source_late`**, not `success`; the system must not silently publish an older report.

This can be implemented by either technology:

**Scheduled script:** the program executes the stages serially and persists a small ledger keyed by `(workflow_id, B)` plus step-level idempotency keys `(workflow_id,B,v,step)`. On restart, it reads the ledger and resumes.

**Orchestrator:** each state becomes a task/dependency/run property, with retries and historical period execution delegated to the orchestration engine. Airflow's scheduler natively waits for dependencies, while catchup/backfill can create historical interval runs. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

The **correctness rule is identical in both implementations**. The difference is whether you implement the control plane yourself.

### Host interruption and reconciliation

It is unsafe to assume that “the scheduler will catch up.” Provider behavior differs.

Google Cloud Scheduler documents one specific condition: after its API has been disabled and later re-enabled, jobs missed in the interval run immediately rather than waiting for the next normal schedule. [10](https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules) Kubernetes instead applies `startingDeadlineSeconds`, counts missed schedules, and documents cases where sufficiently many missed schedules prevent a catch-up Job from being created. [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) Airflow's catchup behavior is explicitly configurable and is off by default in the documented Airflow 3.3.2 configuration example; with catchup enabled, separate runs are created for missing intervals. [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

Therefore the business workflow should have a scheduler-independent reconciliation query:

\[
Missing =
\{B \mid B \text{ is due and eligible}\}
-
\{B \mid Accepted(B,v)\text{ exists}\}
\]

A missing period produces a replay/backfill of the **period key**, not an attempt to reconstruct what a scheduler “probably would have done.”

### DST crossing and actual synthetic execution

A portable synthetic asset was created and executed for this dossier:

**[Download the workflow reliability asset](sandbox:/mnt/data/workflow_orchestration_evidence_asset.zip)**

It contains:

`time_period_contract.yaml`, `dependency_state_table.csv`, `acceptance_cases.csv`, `synthetic_workflow.py`, `test_synthetic_workflow.py`, and the captured `observed_test_run.txt`.

**Actually executed:** 12 Python 3.13.5 unit tests; all passed. The tests covered exact-version acceptance, a late source, rejection of a stale report, duplicate trigger deduplication, partial-failure resume, host-interruption reconciliation, Prague spring-forward nonexistent local time, Prague fall-back ambiguous local time, local-calendar versus 24-hour elapsed scheduling across DST, a DST-crossing period backfill, and a supplied business-calendar holiday/weekend case.

**Not executed:** Airflow, Kubernetes, EventBridge Scheduler, Google Cloud Scheduler, Google Workflows, dbt, a warehouse, cloud APIs, or any production system. The timezone tests exercised Python's installed `zoneinfo` data; they **must not be read as a test of any provider's scheduling implementation**.

The synthetic tests show the specific model is internally capable of keeping `business_date` independent from elapsed time and of rejecting stale period/version combinations. They do **not** demonstrate production reliability, performance, availability or cost.

## Claim-to-primary-source evidence table

The table below intentionally mixes evidence classes only when they are labeled. Provider documentation establishes a provider's stated contract, not independent empirical performance. Academic papers establish narrower principles/results and should not be inflated into product-comparison evidence.

| Claim target / source | Direct URL / DOI and exact inspected scope | Source date; inspected | Evidence type | Assessment | Editorial advice |
|---|---|---|---|---|---|
| Workflow requirements can be described independently of implementation language/product | `https://doi.org/10.1023/A:1022883727209` — *Workflow Patterns*, abstract and bibliographic metadata; full Springer article behind subscription preview | July 2003; 2026-10-10 | Peer-reviewed workflow-modeling research | **Supported**, within modeling scope; not a reliability comparison | **Keep**, qualify as process-modeling evidence [1](https://link.springer.com/article/10.1023/A%3A1022883727209) |
| Clock order should not be conflated with causal dependency | `https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/` — author publication page, discussion of partial/causal ordering and logical clocks | July 1978; 2026-10-10 | Foundational formal distributed-systems work | **Supported formally**; warehouse application is inference | **Keep with explicit inference label** [5](https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/?lang=ko-kr) |
| Final correctness requiring application knowledge must be checked end-to-end | `https://doi.org/10.1145/357401.357402`; inspected MIT author-hosted PDF, pp. 1–2 | Nov. 1984 revised article; 2026-10-10 | Peer-reviewed systems design principle | **Supported as design principle**, not batch-specific experiment | **Keep and qualify** [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf) |
| Workflow abstraction can separate logical workflow from execution resources | `https://doi.org/10.1155/2005/128026` — Pegasus abstract; reports workflow mapping and performance restructuring using an astronomy application | 2005; 2026-10-10 | Peer-reviewed system paper + application study | **Supported in scientific-workflow context**; not evidence of small-team operational reliability | **Keep only as historical/contextual evidence** [14](https://onlinelibrary.wiley.com/doi/abs/10.1155/2005/128026) |
| Airflow scheduler launches tasks when dependencies are complete and is a persistent service | `https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html` — “Scheduler,” including HA scheduler/metadata DB | Airflow 3.3.2, page date unstated; 2026-10-10 | Project/provider documentation | **Supported for Airflow 3.3.2** | **Keep; do not generalize to all orchestrators** [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) |
| Airflow explicitly models data intervals; logical date is not run time | `https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html` — “Data Interval,” “Manual Triggering and Data Intervals” | Airflow 3.3.2; 2026-10-10 | Project documentation | **Supported** | **Keep** [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| Airflow supplies catchup/backfill but it must operate on properly period-bounded workflow logic | Same URL — “Catchup” and “Backfill” | Airflow 3.3.2; 2026-10-10 | Project documentation | **Supported** | **Keep; qualify that product feature does not guarantee app correctness** [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| A successful Airflow DAG state can be misleading if leaf trigger rules are designed incorrectly | Same URL — “Dag Run Status,” `all_done` leaf warning | Airflow 3.3.2; 2026-10-10 | Project documentation | **Supported** | **Keep; strong counterevidence to “orchestrator = correctness”** [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html) |
| Airflow time-zone handling distinguishes UTC storage from zone-dependent schedules and exposes DST concerns | `https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html` — “Time Zones” | Airflow 3.3.2; 2026-10-10 | Project documentation | **Supported** | **Keep** [11](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) |
| Cron/calendar intervals and duration intervals are distinct concepts | `https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html` — `DeltaDataIntervalTimetable` vs `CronDataIntervalTimetable`; plus AWS schedule types below | Airflow 3.3.2; 2026-10-10 | Project/provider documentation | **Supported in inspected implementations** | **Keep; do not call it universal cron syntax behavior** [15](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html) |
| EventBridge local cron uses IANA zones; spring nonexistent occurrence is skipped, fall repeat fires once; `rate(1 days)` means 24 hours | `https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html` — “Time zones,” “Daylight savings time,” rate schedules | Living documentation, page date unstated; 2026-10-10 | Provider contract/documentation | **Supported for EventBridge Scheduler** | **Keep provider name in claim** [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) |
| EventBridge can send failed invocations to a DLQ after retries | `https://docs.aws.amazon.com/scheduler/latest/UserGuide/configuring-schedule-dlq.html` — DLQ behavior | Living documentation, page date unstated; 2026-10-10 | Provider documentation | **Supported** | **Keep as trigger-level recovery evidence, not business reconciliation** [13](https://docs.aws.amazon.com/scheduler/latest/UserGuide/configuring-schedule-dlq.html) |
| Google Cloud Scheduler is wall-clock based, supports zones, and warns about DST anomalies | `https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules` — “Time zone” | Last updated 2026-10-07; 2026-10-10 | Provider documentation | **Supported for Cloud Scheduler** | **Keep and qualify provider scope** [10](https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules) |
| Duplicate scheduled invocations remain possible and handlers should be idempotent | `https://docs.cloud.google.com/scheduler/docs/creating` — duplicate-instance/idempotency note and schedule-time header | Last updated 2026-10-07; 2026-10-10 | Provider documentation | **Supported** | **Keep** [6](https://docs.cloud.google.com/scheduler/docs/creating) |
| Cloud Scheduler retry semantics can interact with subsequent scheduled executions | `https://docs.cloud.google.com/scheduler/docs/configuring/retry-jobs` — maximum attempts/duration/backoff | Current inspected page; 2026-10-10 | Provider documentation | **Supported** | **Keep** [12](https://docs.cloud.google.com/scheduler/docs/configuring/retry-jobs) |
| Kubernetes CronJobs have deadlines/concurrency controls but Job creation remains approximate | `https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/` — “Deadline,” “Concurrency policy,” “Job creation” | Last modified 2026-05-13; 2026-10-10 | Open-source project documentation | **Supported for inspected Kubernetes docs** | **Keep; strong reason for idempotency** [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) |
| Kubernetes CronJob time zone is explicit; absent it uses controller local zone | Same Kubernetes URL — “Time zones” | Last modified 2026-05-13; 2026-10-10 | Project documentation | **Supported** | **Keep** [3](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/) |
| An orchestrator has its own upkeep and failure surface | `https://airflow.apache.org/docs/apache-airflow/stable/installation/upgrading.html` — DB migrations, backups and migration failure recovery | Airflow 3.3.2; 2026-10-10 | Project operational documentation | **Supported for self-managed Airflow** | **Keep; do not generalize maintenance burden to fully managed services** [16](https://airflow.apache.org/docs/apache-airflow/stable/installation/upgrading.html) |
| Managed workflow engines can checkpoint execution state and survive infrastructure outages without self-hosted Airflow | `https://cloud.google.com/workflows` — “All features,” checkpointing, retries/timeouts, logging | Living provider page; 2026-10-10 | Provider architecture claim | **Supported as documented design; operational result unverified independently** | **Keep as counterexample, qualify strongly** [8](https://cloud.google.com/workflows) |
| dbt can explicitly evaluate source freshness rather than assuming a scheduled source is current | `https://docs.getdbt.com/reference/commands/freshness` — freshness command/artifact behavior | Last updated 2026-09-16; 2026-10-10 | Provider/project documentation | **Supported**, but freshness alone is not the `(B,v)` acceptance contract | **Optional supporting context only** [17](https://docs.getdbt.com/reference/commands/freshness?utm_source=chatgpt.com) |
| “Automatizace běží v jiném čase, než čekáte. Který čas má pro firmu platit?” | AWS/GCP/Airflow/Kubernetes time-zone and interval documentation above | Current versions/dates above | Cross-provider documented behavior + editorial synthesis | **Supported if rewritten as a design question, not universal failure claim** | **Keep/rewrite:** “A schedule needs an explicit time contract: business period, local zone, UTC instant and elapsed duration are different things.” [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html) |
| “Jedna automatizace čeká na druhou. Kdo hlídá, že celý postup doběhl?” | Airflow dependency/DAG-state docs + end-to-end paper | Sources above | Documented workflow behavior + systems-design interpretation | **Supported, with qualification** | **Keep/rewrite:** “Define a single end-to-end acceptance state; individual green tasks do not establish business completion.” [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) |

### Source-access qualifications

The *Workflow Patterns* Springer page exposed the abstract and bibliographic record but labeled the full article as subscription content; the claims here therefore stay within the accessible abstract/metadata. [1](https://link.springer.com/article/10.1023/A%3A1022883727209)

The Saltzer/Reed/Clark paper was available as an MIT-hosted PDF and was visually inspected with page screenshots before using its argument. [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

The Pegasus publisher page exposed the abstract and study summary; this dossier does not claim details beyond that accessible scope. [14](https://onlinelibrary.wiley.com/doi/abs/10.1155/2005/128026)

No provider console, production deployment, service SLA measurement, billing record, incident database, private benchmark or customer account was accessed. Accordingly, provider documentation is treated as a **documented contract/design claim**, never as measured field reliability.

## Contradictions, limitations and practical implications

### Claims the evidence does not support

**“Cron is unreliable; orchestration is reliable.” — Remove.**  
That framing is much too crude. Some schedulers expose retries, deadlines, deduplication hints, time zones and DLQs; Kubernetes and Google both document duplicate-delivery possibilities, but that says nothing about the relative incident rate of a well-designed one-script system versus an orchestrator. [6](https://docs.cloud.google.com/scheduler/docs/creating) An orchestrator also has operational dependencies of its own: Airflow uses a persistent scheduler and metadata database, and upgrades can require database migrations and backups. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

**“If the DAG is green, the report is correct.” — Contradicted.**  
Even Airflow's own documented DAG-status rules can produce a successful DAG while an earlier internal task failed if terminal trigger rules are misconfigured. More fundamentally, an engine does not know the business semantics of the source version unless the workflow encodes them. [7](https://airflow.apache.org/docs/apache-airflow/stable/core-concepts/dag-run.html)

**“UTC solves scheduling.” — Rewrite.**  
UTC removes ambiguity from instants and is a good representation for event timestamps, but it does not answer “06:30 according to which business's wall clock?” or “what counts as the prior working day?” AWS demonstrates concretely that a 24-hour rate interval and a local-time cron schedule behave differently across DST. [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

**“Every daily automation should use an orchestrator.” — Unsupported.**  
The literature and documentation establish capabilities, not such a threshold. For one serial workflow, putting every step in one executable materially reduces dependency complexity. Adding a ten-component orchestration platform solely to run one serial command can increase rather than reduce the number of things the team must own. Airflow's documented scheduler/metadata database and migration requirements make that trade-off concrete for self-managed Airflow. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

**“Five cron jobs are equivalent to one five-step workflow.” — Usually false under this dossier's requirements.**  
They become equivalent only after the individual jobs share durable dependency state, period/version correlation, locking, retries, reconciliation and whole-task acceptance. At that point, the team has effectively implemented a small orchestration layer of its own. That last characterization is an architectural interpretation, not a measured product result.

### The point at which I would introduce an orchestrator

For this specific chain, I would begin with **one sequential executable + durable run ledger** rather than independent schedules. I would move to an orchestrator when one or more of these stops being exceptional and starts being normal operating behavior:

| Signal | Why it changes the decision |
|---|---|
| Multiple branches or fan-out/fan-in | Dependency state becomes awkward to encode procedurally |
| Different steps require independent retry/timeout policies | Per-task lifecycle becomes first-class |
| Several historical periods can execute concurrently | Run identity, concurrency and operator control become harder |
| Frequent backfills/reprocessing | Native interval/run tooling gains operational value |
| Long waits on upstream events | Persistent workflow state becomes more valuable than a long-running process |
| Several engineers/teams operate the process | Standardized UI/history/ownership becomes leverage |
| Operators often ask “what is waiting on what?” | The observability benefit moves from convenience to requirement |
| The custom ledger starts acquiring schedulers, task queues and generalized dependency rules | You are building an orchestrator; buying/adopting one deserves serious consideration |

Airflow's explicit dependency scheduler, interval/catchup/backfill constructs and execution history are concrete examples of the facilities gained at that point. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html) The qualification is that self-managed Airflow also brings a database/scheduler lifecycle, whereas managed workflow engines can move some of that burden back to a provider. [16](https://airflow.apache.org/docs/apache-airflow/stable/installation/upgrading.html)

### What should remain outside the orchestrator

Regardless of technology, the following belong in the process/application contract:

`business_date`; source/version identity; raw immutability/checksum; idempotency semantics; what a “complete” report means; how stale data is rejected; and the late-report policy.

The orchestrator can persist and expose those values, but it cannot invent their business meaning.

Likewise, an **AI agent should not be the authoritative scheduler, period selector, or completion oracle by default**. A bounded AI component could summarize failure logs, classify incidents for operator triage, or propose a recovery action. The deterministic run ledger, period/version checks and acceptance predicate should remain authoritative. This is an editorial architecture recommendation; this dossier did not test an AI agent and found no empirical evidence requiring one.

### Falsifiable comparison for a real team

A fair evaluation should test **the same contract and failure injections**, rather than comparing a fragile cron example with a carefully engineered orchestrator.

The proposed experiment—not performed here—is to implement:

**System S:** one scheduled script + durable ledger/lock/idempotency/reconciliation.

**System O:** the same stage functions and the same `(B,v)` acceptance contract under the candidate orchestrator.

Then inject identical cases: late source, duplicate invocation, crash after each committed stage, permission failure, timeout, stale source/version, scheduler outage, overlapping period, DST-crossing backfill, and manual replay.

Measure only observable outcomes such as:

\[
\text{wrong accepted outputs}
\]

\[
\text{duplicate accepted outputs}
\]

\[
\text{missing periods undetected by deadline}
\]

\[
\text{recovery actions required per injected failure}
\]

\[
\text{time from failure injection to deterministic detection}
\]

\[
\text{time/steps required for handover to another operator}
\]

\[
\text{maintenance effort for upgrades/configuration changes}
\]

No comparative result for those measures is claimed here. The test is useful precisely because it can falsify the recommendation: if the robust script handles the team's real failure envelope with materially less ownership burden, orchestration is unnecessary. Conversely, if meeting the same requirements causes the script to grow a generalized task-state machine, backfill engine, queue, operator UI and dependency model, the “simple script” argument has largely disappeared.

### Unresolved evidence gaps

The largest gap is empirical. The academic workflow literature provides formal/modeling foundations, while provider documentation provides feature contracts, but neither answers a practical question such as **“At what workflow complexity does an orchestrator reduce net operating burden for a five-person analytics team?”** The Pegasus paper reports a performance result for workflow restructuring in an astronomy application, which is interesting but not transferable as evidence of business-data workflow reliability or staffing outcomes. [14](https://onlinelibrary.wiley.com/doi/abs/10.1155/2005/128026)

Similarly, no evidence inspected here supports a universal claim that orchestration lowers mean time to recovery, incidents, engineering hours, or cost. Those would require either controlled comparative experiments or carefully scoped operational datasets.

The provider documents also differ in what they promise. AWS precisely documents a skip-on-spring/once-on-fall DST policy for EventBridge Scheduler, whereas Google's Cloud Scheduler documentation warns more generally of wall-clock anomalies. Those statements are **not contradictory** because they describe different products. They are evidence against assuming a universal “cron DST behavior.” [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

Timezone rule freshness remains an operational dependency. Airflow specifically warns that Pendulum's database may lag the IANA database unless configured otherwise. [11](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html) Therefore both the scheduler and the application/runtime timezone data belong in the system's freshness review.

## Reusable editorial angles, assets and freshness triggers

The strongest article angle is not **“cron versus Airflow.”** That invites an unhelpfully tool-centric debate. The evidence supports several more distinct treatments.

**“Your automation ran successfully. Your report can still be wrong.”**  
Use the late-source/stale-report scenario to distinguish trigger success, task success and business acceptance. The academic anchor is end-to-end correctness; Airflow's own leaf-status warning supplies a useful product-level counterexample to equating workflow green with business correct. [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

**“A schedule is not a definition of time.”**  
Build around the four-way distinction between business period, local civil time, UTC instant and elapsed duration. AWS's documented `rate(1 days)` versus local cron behavior is a particularly concrete illustration, alongside Google Scheduler's wall-clock warning. [9](https://docs.aws.amazon.com/scheduler/latest/UserGuide/schedule-types.html)

**“When does a script become an orchestrator?”**  
Start from the robust one-script baseline rather than a straw-man cron script. Add requirements one at a time—durable state, independent retries, backfills, overlapping runs, cross-team visibility—and identify the point at which continuing to build the control plane internally ceases to be simpler. Airflow supplies a concrete reference architecture, while its upgrade documentation prevents the piece from treating orchestration as free infrastructure. [4](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

**“Stop scheduling dependencies; correlate them.”**  
The important transformation is from “run B at 07:00 because A probably finished” to “run B for exact `(business_date, source_version)` when A's accepted state exists.” Lamport's causal-order framing provides the formal intellectual anchor, with the warehouse application clearly labeled as inference. [5](https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/?lang=ko-kr)

**“One green status for the whole business task.”**  
A useful management-facing version: five green subprocesses are weaker evidence than one explicit acceptance predicate for the result customers actually consume. The end-to-end paper and Airflow status semantics support the distinction. [2](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

### Reusable assets

The downloadable research asset contains the actual synthetic contract, dependency table, acceptance cases and executable tests:

**[workflow_orchestration_evidence_asset.zip](sandbox:/mnt/data/workflow_orchestration_evidence_asset.zip)**

It is portable in the sense that it uses Python's standard library only. The captured run records **12 passing synthetic tests**. It is deliberately not production-ready and does not contain provider-specific deployment code.

An additional useful publication asset would be a single state diagram:

`period due → waiting_source → raw_preserved → ingested → transformed → report_rendered → accepted`

with failure branches into `blocked_source_late`, `failed_retryable`, and `failed_terminal`, and with `(B,v)` printed on every edge. That diagram is proposed, not supplied as a separate image file in this dossier.

### Freshness triggers

This dossier should be rechecked when any selected scheduler/orchestrator materially changes scheduling, retry, catchup/backfill or timezone semantics; when the deployed Airflow major/minor version changes; when Kubernetes CronJob semantics/version changes; or when cloud provider documentation changes. Airflow upgrades can include metadata-database migrations, so version upgrades are themselves an operational review trigger. [16](https://airflow.apache.org/docs/apache-airflow/stable/installation/upgrading.html)

It should also be refreshed following relevant IANA timezone-rule changes or changes in national/EU daylight-saving policy. The scheduler's timezone database and the application's timezone database should be reviewed independently because Airflow already documents a potential difference between Pendulum's bundled data and IANA/system data. [11](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timezone.html)

The Google Cloud Scheduler source is particularly fresh at this snapshot—last updated **2026-10-07**, three days before inspection—while Kubernetes' inspected CronJob page was last modified **2026-05-13**. [10](https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules) AWS's inspected scheduler pages and several Airflow pages do not expose a comparable page-publication date in the retrieved material; their product/version scope and the 2026-10-10 inspection date should therefore remain attached whenever their behavior is quoted.

The durable conclusion is narrower than any product recommendation: **design the business period, dependency identity, failure states, reconciliation and acceptance condition first. A simple scheduled script remains genuinely simple only while it can satisfy that contract without growing an improvised workflow engine.**

## Portable citation index

[1] [Workflow Patterns | Distributed and Parallel Databases | Springer Nature Link](https://link.springer.com/article/10.1023/A%3A1022883727209)

[2] [web.mit.edu](https://web.mit.edu/saltzer/www/publications/endtoend/endtoend.pdf)

[3] [CronJob | Kubernetes](https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/)

[4] [Scheduler — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/administration-and-deployment/scheduler.html)

[5] [Time, Clocks and the Ordering of Events in a Distributed System - Microsoft Research](https://www.microsoft.com/en-us/research/publication/time-clocks-ordering-events-distributed-system/?lang=ko-kr)

[6] [Manage cron jobs  |  Cloud Scheduler  |  Google Cloud Documentation](https://docs.cloud.google.com/scheduler/docs/creating)

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

[8] [Workflows | Google Cloud](https://cloud.google.com/workflows)

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

[10] [Cron job format and time zone  |  Cloud Scheduler  |  Google Cloud Documentation](https://docs.cloud.google.com/scheduler/docs/configuring/cron-job-schedules)

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

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

[13] [Configuring a schedule's dead-letter queue in EventBridge Scheduler - EventBridge Scheduler](https://docs.aws.amazon.com/scheduler/latest/UserGuide/configuring-schedule-dlq.html)

[14] [Pegasus: A Framework for Mapping Complex Scientific Workflows onto Distributed Systems - Deelman - 2005 - Scientific Programming - Wiley Online Library](https://onlinelibrary.wiley.com/doi/abs/10.1155/2005/128026)

[15] [Timetables — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/authoring-and-scheduling/timetable.html)

[16] [Upgrading Airflow® to a newer version — Airflow 3.3.2 Documentation](https://airflow.apache.org/docs/apache-airflow/stable/installation/upgrading.html)

[17] [About dbt freshness command | dbt Developer Hub](https://docs.getdbt.com/reference/commands/freshness?utm_source=chatgpt.com)