agentleFS
nonlinear-xyznonlinear-xyz/factory-kit/skills/factory-data-pipeline-engineer/SKILL.md

factory-data-pipeline-engineer

Skill9 starsChanged 4 months agoMITView on GitHub

Use when designing or implementing data ingestion, CSV imports, time-series storage, Python services that sit alongside Next.js, simulation pipelines, or external-API integration with submit/poll/fetch shapes. Carries the factory's data-pipeline conventions — TS scripts with Papa Parse, JSONB envelopes for time-series, the three-entry-point Python pattern (CLI / Cloud Run API / Pub/Sub), YAML config for service-level data, converter/service split, Cloud Run + ephemeral Neon deployment.

The file

We split this skill into the rules it gives an agent

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

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈4

    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 it doesn't match one, that's the finding — name it.

    line 17
  2. Very specific:

    TS or Python?: Default: Next.js side (TS).

    line 19
  3. Distinctive:

    Migrations?: Run in CI, not at runtime.

    line 32
  4. Distinctive:

    Idempotency?: Default to upsert-on-conflict for CSV imports.

    line 34
  5. Very specific:

    Don't pre-build libs/py-libs/ shared utilities: Wait for the second consumer.

    line 134
  6. Very specific:

    Don't write Drizzle in raw pg style: If you're considering raw SQL with row-mappers, you're doing it wrong.

    line 140
  7. Distinctive:

    If the user asks for a one-off query, a single Drizzle insert, or "just read this CSV once," do it directly without the full pipeline …

    line 145

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 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

Apply the data-pipeline-engineer specialist workflow. Design data flow that fits the factory's pipeline conventions, not bespoke ETL plumbing. Load factory-data-pipelines and factory-stack through the host's skill capability when needed.

How to think (in order)

  1. What's the data shape? Pick one:

    • One-shot or scheduled CSV → TS script in scripts/data_processing/
    • Event stream / time-series → JSONB envelope on a structured parent table
    • External API ingestion (slow operation) → submit/poll/fetch async pattern
    • Reference data (slowly changing) → YAML config (Python side)
    • Compute job (sim, optimization, ML) → Python service with three entry points

    If it doesn't match one, that's the finding — name it.

  2. TS or Python? Default: Next.js side (TS). Move to Python when:
    • Numeric / scientific libraries are non-trivial (geopandas, shapely, numpy/scipy)
    • Existing Python expertise / models
    • Compute runtime > Vercel function timeout (~10s on hobby, 60s on pro)
  3. Storage shape? Drizzle table with structured columns for what drives queries + JSONB for what doesn't. Rule: if you need to filter or sort by it at app speed, it earns a column.
  4. Deployment shape?
    • TS script → run locally or in GitHub Action; commits the data to DB
    • Cloud Run API → HTTP endpoint, FastAPI, API key dependency
    • Cloud Run Pub/Sub handler → async job processor
    • Long-running compute → Cloud Run with extended timeout, or Cloud Run Jobs
  5. Migrations? Run in CI, not at runtime. Drizzle's generate + push, or dbmate for raw SQL projects.
  6. Idempotency? Default to upsert-on-conflict for CSV imports. Wrap in a transaction. Don't assume "imported once."
  7. Converter vs service split? Pure transforms in *-converter.ts (client-safe). I/O in *-service.ts (server-only). Don't blur.

Reference: canonical TS import script

// scripts/data_processing/import-foo.ts
import { readFileSync } from 'fs';
import Papa from 'papaparse';
import { db } from '@/db';
import { foo } from '@/db/schema';

const csvText = readFileSync(process.argv[2], 'utf8');
const { data, errors } = Papa.parse<FooRow>(csvText, {
  header: true,
  skipEmptyLines: true,
  dynamicTyping: true,
});

if (errors.length) {
  console.error('Parse errors:', errors);
  process.exit(1);
}

await db.transaction(async (tx) => {
  for (const row of data) {
    await tx.insert(foo).values({
      externalId: row.external_id,
      name: row.name,
      // ... map every column
    }).onConflictDoUpdate({
      target: foo.externalId,
      set: { name: row.name, updatedAt: new Date() },
    });
  }
});

console.log(`Imported ${data.length} rows`);

Reference: canonical Python service layout

models/<service>/
├── Dockerfile             # Pub/Sub variant
├── Dockerfile.api         # API variant
├── pyproject.toml
├── main.py                # CLI entry
├── main_api.py            # FastAPI entry — Cloud Run HTTP
├── main_pubsub.py         # Pub/Sub handler entry
├── config/
│   ├── routes.yaml
│   └── vehicles.yaml
├── src/
│   ├── api/
│   │   └── deps.py        # API key, request context
│   ├── models/
│   │   ├── request.py     # Pydantic
│   │   └── message.py     # Pub/Sub message shape
│   ├── services/
│   │   ├── data_service.py    # YAML loader
│   │   ├── mapbox_service.py
│   │   └── gcs_service.py
│   └── simulation/
│       └── simulation_runner.py   # core work; mode-aware
└── tests/
    └── conftest.py

Output format

## Restated request
<one sentence>

## Pipeline shape
- Data shape: <CSV / event stream / external API / reference data / compute>
- Runtime: <TS / Python / mixed — why>
- Storage: <columns / JSONB envelope / both>
- Deploy: <local script / Vercel scripts / Cloud Run API / Cloud Run Pub/Sub>

## Files to create or modify
<bulleted list with paths>

## Code
<organized by file>

## Operational details
- Idempotency: <how>
- Migrations: <CI step, not runtime>
- Trace ID: <yes — middleware in place>
- Logging: <PostHog / Sentry / structured logs>

## Open questions
<things the user should confirm>

What you do NOT do

  • Don't pre-build libs/py-libs/ shared utilities. Wait for the second consumer.
  • Don't query inside JSONB at app speed. Promote the field to a column.
  • Don't write a Cloud Run job before the second use case. Standalone TS script is enough until proven.
  • Don't share Pydantic models across the three Python entry points by copy-paste. Define once in models/, import everywhere.
  • Don't run migrations at runtime. CI's job.
  • Don't blur converter / service boundaries. Pure-transform stays in *-converter.ts; I/O in *-service.ts.
  • Don't write Drizzle in raw pg style. If you're considering raw SQL with row-mappers, you're doing it wrong.
  • Don't put YAML paths in hardcoded strings. Env-driven base paths.

When the request is too small for this framework

If the user asks for a one-off query, a single Drizzle insert, or "just read this CSV once," do it directly without the full pipeline framework. The framework is for recurring or productionized data flow.

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.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

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.