Prefect Flows skill for Codex

Turn a Python script into a reliable scheduled flow. Codex turns scripts into Prefect 3 flows where tasks are idempotent, retry with backoff and time out instead of hanging, and expensive steps cache on their inputs. Dates arrive as parameters so old days can be rerun, secrets come from blocks, and deployments target a named work pool with a worker, registered only after you approve.

  • Data & Analytics
  • $prefect-flows
  • 182 lines
  • 2 files
  • ~1,850 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- prefect-flows

Installs to ~/.agents/skills/prefect-flows/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $prefect-flows, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Writes and deploys Prefect 3 workflows - @flow and @task with retries, retry_delay_seconds and timeouts, cache policies, concurrent task submission and .map(), results persistence, blocks and variables for config, and deployments via flow.serve, flow.deploy, prefect.yaml and work pools. Use when the user turns a Python script into a scheduled Prefect flow, adds retries or caching to tasks, deploys a flow to a work pool, migrates from Prefect 2 agents to Prefect 3 workers, or debugs flows that hang, rerun expensive work, or never get picked up.

Use it when

  • A Python script needs scheduling, retries and observability with little infrastructure
  • Prefect tasks rerun expensive work or hang without timeouts
  • Deploying flows to Docker, Kubernetes or serverless work pools
  • Upgrading from Prefect 2 agents to Prefect 3 workers

Use something else when

  • The platform standard is Airflow: use airflow-dags
  • You want asset lineage and partitioned data products: use dagster-pipelines
  • The pipeline is a recurring shell or cron automation rather than data orchestration: use cron-scheduling

How to check the result

A local run succeeds with a real parameter, a forced task failure retries as configured, and rerunning the same date leaves the target unchanged.

The mistake it prevents

Computing yesterday inside a task instead of taking the date as a flow parameter, which makes reruns of past days impossible.

~/.agents/skills/prefect-flows/SKILL.md

--- name: prefect-flows description: Writes and deploys Prefect 3 workflows - @flow and @task with retries, retry_delay_seconds and timeouts, cache policies, concurrent task submission… license: MIT ---

Done well means: the flow is plain, testable Python; each task is a retryable, idempotent unit with a timeout; expensive steps cache on their inputs; configuration and secrets come from blocks or environment, and the deployment runs on a named work pool that someone can see.

Before writing code

  1. Read AGENTS.md and dependency files. prefect version shows client and server versions; this skill targets Prefect 3.x. Prefect 2 code mostly runs, but agents and infrastructure blocks are gone: deployments run on work pools with workers.
  2. Find where flows run: Prefect Cloud or a self-hosted server (PREFECT_API_URL), and which work pool types exist (prefect work-pool ls). Listing needs API access; in a sandbox with network off, ask the user for the output.
  3. Identify side effects in the script (writes, emails, API calls). They decide retry and caching policy.

Structure

from datetime import timedelta

import httpx
from prefect import flow, task, get_run_logger
from prefect.cache_policies import INPUTS, TASK_SOURCE

@task(
    retries=3,
    retry_delay_seconds=[10, 60, 300],          # backoff per attempt
    retry_jitter_factor=0.5,
    timeout_seconds=600,
    cache_policy=INPUTS + TASK_SOURCE,          # reuse result when inputs and code are unchanged
    cache_expiration=timedelta(days=1),
)
def fetch_page(day: str, page: int) -> list[dict]:
    r = httpx.get("https://api.example.com/orders", params={"day": day, "page": page}, timeout=30)
    r.raise_for_status()
    return r.json()["items"]

@task(retries=2, retry_delay_seconds=30)
def load(day: str, rows: list[dict]) -> int:
    return replace_partition("raw.orders", day, rows)   # delete+insert keeps retries safe

@flow(name="orders-daily", log_prints=True, timeout_seconds=3600)
def orders_daily(day: str, pages: int = 10) -> int:
    logger = get_run_logger()
    futures = fetch_page.map(day, range(pages))     # runs concurrently
    rows = [r for f in futures for r in f.result()]
    n = load(day, rows)
    logger.info("loaded %s rows for %s", n, day)
    return n

if __name__ == "__main__":
    orders_daily("2025-09-01")

Rules:

  • Flows orchestrate; tasks do I/O. Business logic lives in plain functions that tasks call, so it can be unit-tested without Prefect.
  • Pass the logical date (day) as a parameter. Never compute “yesterday” inside a task: reruns of old dates become impossible.
  • Resolve every future (.result() or wait(futures)); unresolved futures can end the flow before the work finishes.

Retries, caching and timeouts

SituationSetting
Flaky network or rate limitsretries + list of retry_delay_seconds, plus retry_jitter_factor
Retry only on specific errorsretry_condition_fn= returning True for retryable exceptions
Expensive deterministic stepcache_policy=INPUTS (add TASK_SOURCE to invalidate on code change)
Step must always runcache_policy=NO_CACHE
Hang protectiontimeout_seconds on tasks and on the flow
Results must survive across runs / processespersist_result=True and a result storage block
Outward-facing action (email, payment, publish)retries=0, separate task, explicit user confirmation before enabling in a deployment

Caching needs stored results; with caching on, Prefect persists task results by default to local storage (~/.prefect/storage). On ephemeral workers configure remote result storage or caches will never hit.

Configuration and secrets

from prefect.blocks.system import Secret
from prefect.variables import Variable

api_key = Secret.load("orders-api-key").get()
batch = Variable.get("orders_batch_size", default=500)

Never hard-code secrets or put them in flow parameters (parameters are visible in the UI). Create blocks with prefect block create or the UI; reference by name.

Concurrency control

  • Limit parallel tasks hitting one API: global concurrency limit with from prefect.concurrency.sync import concurrency then with concurrency("orders-api", occupy=1): ..., after creating it with prefect gcl create orders-api --limit 5.
  • Tag-based limits on tasks: @task(tags=["warehouse"]) and prefect concurrency-limit create warehouse 4.
  • Limit simultaneous flow runs per deployment with concurrency_limit on the deployment.

Deployments

NeedUse
Run on this machine on a schedule, simplestorders_daily.serve(name="daily", cron="0 3 * * *") (long-running process)
Run on managed infrastructure (Docker, Kubernetes, ECS, serverless)Work pool + worker, flow.deploy(...) or prefect deploy with prefect.yaml
Code pulled from git at run timeflow.from_source(source="https://github.com/org/repo.git", entrypoint="flows/orders.py:orders_daily")
# prefect.yaml
deployments:
  - name: orders-daily
    entrypoint: flows/orders.py:orders_daily
    work_pool:
      name: k8s-default
    parameters:
      pages: 10
    schedules:
      - cron: "0 3 * * *"
        timezone: UTC
prefect deploy --name orders-daily          # registers; needs API access
prefect worker start --pool k8s-default     # a worker must poll the pool
prefect deployment run 'orders-daily/orders-daily' --param day=2025-09-01

Registering or changing a production deployment and triggering runs on shared infrastructure require the user’s explicit go-ahead.

Testing

import pytest
from prefect.testing.utilities import prefect_test_harness

@pytest.fixture(autouse=True, scope="session")
def prefect_backend():
    with prefect_test_harness():
        yield

def test_orders_daily(monkeypatch):
    monkeypatch.setattr("flows.orders.replace_partition", lambda *a: len(a[2]))
    assert orders_daily.fn("2025-09-01", pages=1) >= 0   # .fn calls the function without Prefect

def test_flow_run():
    assert orders_daily("2025-09-01", pages=1) >= 0     # full Prefect run on a temp backend

Gotchas

  • Scheduled runs stay “Late” or “Pending” forever when no worker polls the work pool, or the worker type does not match the pool type.
  • Large task return values are serialized for results and caching; return paths or counts, not DataFrames with millions of rows.
  • Cache keys computed from unhashable or huge inputs fail or are slow; pass identifiers.
  • log_prints=True captures print; library logs need PREFECT_LOGGING_EXTRA_LOGGERS.
  • Flow parameters are validated with type hints; a day: date parameter rejects a malformed string at submit time, which beats failing deep inside a task.

Verification and report

  • Run the flow locally once with a real parameter and check the run in the UI or prefect flow-run ls --limit 5.
  • Force a failure (raise in a task) to confirm retries and alerting behave as expected.
  • Rerun the same day and confirm row counts in the target do not change.
**Flow:** orders-daily (Prefect 3.4), deployment on work pool k8s-default, 03:00 UTC
**Tasks:** fetch_page x10 (retries 3, cached on inputs 1 day), load (delete+insert)
**Limits:** global concurrency orders-api = 5
**Checks:** local run 2025-09-01 ok (14,220 rows); rerun idempotent; forced failure retried 3x
**Awaiting approval:** prefect deploy to production workspace

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Prefect Flows"
  short_description: "Turn a Python script into a reliable scheduled flow"
  default_prompt: "$prefect-flows Turn this ETL script into a Prefect flow that runs every night with retries"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem