agentleFS
owidowid/etl/.claude/skills/tool-owl/SKILL.md

tool-owl

Skill160 starsChanged 6 months agoMITView on GitHub

Working with Owl — OWID's lightweight, single-folder pipeline runner for small datasets (an alternative to the full snapshot → meadow → garden → grapher chain). Use whenever the user mentions Owl, `owl_steps/`, the `owl` CLI (`owl new`/`run`/`snapshot`/`viz`), or wants to create, migrate, run, or debug a small dataset with Owl.

We split this skill into the rules it gives an agent

4 rules. About 0 are common, 1 is distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    If a request doesn't clearly fit Owl, say so and recommend classic ETL instead of forcing it.

    line 32
  2. Very specific:

    If it was a grapher step, add an @Action(kind="grapher", default=False) that calls upsert_dataset(...).

    line 45
  3. Very specific:

    Remove the classic files (snapshot .py/.dvc, meadow, garden + meta, grapher) and their active DAG entries.

    line 46
  4. Very specific:

    Run make check before committing.

    line 94

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Owl

Owl is OWID's lightweight pipeline runner: one step folder (owl_steps/<namespace>/<dataset>/vYYYYMMDD/) holds the snapshot, transform, and metadata, and writes a normal owid.catalog dataset under data/garden/. It replaces the full snapshot → meadow → garden → grapher chain for small datasets.

Read the README first

The authoritative reference is lib/owl/owl/README.md — read it before doing anything with Owl. It covers the commands, project layout, @Snapshot/@Dataset/@Action API, snapshot capture helpers, meta.yml, loading existing ETL datasets, the Grapher action, and current limitations. Do not duplicate it from memory — open it.

The framework is small and readable; for API details beyond the README, go to the source:

FileWhat's in it
lib/owl/owl/cli.pyowl new / run / snapshot / viz commands and flags
lib/owl/owl/snapshot.py@Snapshot, SnapshotCapture (download/add/write_bytes/write_text), lock behavior
lib/owl/owl/dataset.py@Dataset, @Action, ColumnMeta, DatasetMeta, metadata merging, staleness
lib/owl/owl/catalog.pyload_snapshot (Table + origins), export
lib/owl/owl/etl_dataset.pyETLDataset (read-only deps on main-ETL datasets)

When to use Owl vs. classic ETL

  • Owl — small/single-source datasets, prototypes, demos, agent-assisted creation.
  • Classic ETL (/create-etl-steps, /update-dataset) — mature, high-traffic, or multi-stage datasets that need real DAG integration. Owl is experimental, has limited metadata validation, and no DAG integration.

If a request doesn't clearly fit Owl, say so and recommend classic ETL instead of forcing it.

Tasks

Create a new step

.venv/bin/owl new <namespace>/<dataset> [--date YYYY-MM-DD], then fill in step.py and meta.yml, then owl snapshot → owl run. Follow the README's "Creating a new step", "Snapshots", and "Datasets" sections.

Migrate a classic ETL dataset to Owl

Collapse the snapshot → meadow → garden → grapher chain into one Owl step folder:

  1. Scaffold the step (owl new, or hand-create vYYYYMMDD/), using a --date matching the source's access date.
  2. Move the snapshot fetch into a @Snapshot (e.g. snap.download(URL, suffix=".csv")).
  3. Collapse the meadow + garden transforms into one @Dataset — load with load_snapshot(...), harmonize, tb.format([...]).
  4. Port the garden .meta.yml into the Owl meta.yml: the datasets.<name> block accepts the standard catalog shape (definitions, tables → variables), so most metadata moves over verbatim. Snapshot origin goes under snapshots.<name>.origin.
  5. If it was a grapher step, add an @Action(kind="grapher", default=False) that calls upsert_dataset(...).
  6. Remove the classic files (snapshot .py/.dvc, meadow, garden + meta, grapher) and their active DAG entries.
  7. owl snapshot + owl run, then verify the output dataset matches the old one.

Run / debug

owl run <pattern> (regex; rebuilds stale steps + upstream deps — no --force needed after edits), owl run <pattern> --grapher for Grapher actions, owl run <pattern> --force to rebuild regardless, owl viz <pattern> to render the dependency DAG. owl snapshot is the only way to fetch data — run never fetches.

Recommended step.py pattern (real datasets)

For anything beyond a toy dataset, use the catalog-Table pattern (preserves origins/metadata properly), rather than the lightweight inline-DatasetMeta scaffold:

from pathlib import Path

from owid.catalog import Table
from owl import Action, Dataset, Snapshot, SnapshotCapture
from owl.catalog import load_snapshot
from owl.grapher import upsert_dataset

from etl.data_helpers import geo

URL_DOWNLOAD = "https://example.com/data.csv"
COUNTRIES_FILE = Path(__file__).with_name("my_dataset.countries.json")


@Snapshot
def raw_data(snap: SnapshotCapture) -> None:
    snap.download(URL_DOWNLOAD, suffix=".csv")


@Dataset
def my_dataset(raw_data: Snapshot) -> Table:
    tb = load_snapshot(raw_data)                       # Table with origins from meta.yml `origin`
    tb = tb.rename(columns={"country_name": "country"})
    tb = geo.harmonize_countries(df=tb, countries_file=COUNTRIES_FILE)
    tb = tb.format(["country", "year"])
    return tb


@Action(kind="grapher", default=False)
def upsert_to_grapher(my_dataset: Dataset) -> None:
    upsert_dataset(my_dataset)

Dependencies wire by parameter name (a raw_data parameter resolves to the @Snapshot/@Dataset named raw_data in the module).

Repo conventions that still apply inside step.py

  • Preserve metadata/origins in transforms: no np.where, no pd.concat/pd.to_numeric (use pr.* from owid.catalog.processing), no pd.DataFrame(tb). See the "Preserving metadata/origins" rules in CLAUDE.md.
  • Run make check before committing. Don't commit, push, or open PRs unless explicitly told to.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.