data-apache-lakehouse
Use when designing, modifying, or debugging Apache Iceberg lakehouses (bronze/silver/gold medallion specifically on Iceberg) — PyIceberg +…
Skill by Evan-Kim2028
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/evan-kim2028-data-apache-lakehouse
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: backend, cloud, data, productivity
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/data-apache-lakehouse
What's inside
- Skill name
data-apache-lakehouse- Description
- Use when designing, modifying, or debugging Apache Iceberg lakehouses (bronze/silver/gold medallion specifically on Iceberg) — PyIceberg + Polars/DuckDB/Arrow/pandas writes, Spark or Flink ingestion, catalog choice (Glue/REST/Polaris/Lakekeeper/Nessie/JDBC), Spark/Trino maintenance procedures, gold aggregates, compaction/expire/orphan, branching/WAP, CDC, snapshot rollback, slow incremental jobs, lock contention, schema evolution. Includes Iceberg internals and PyIceberg metadata-table diagnostics. Don't use for Delta Lake or Databricks-native medallion, Apache Hudi, Snowflake-native, or BigQuery-native designs — even if "medallion" is mentioned. Don't use for plain Parquet on S3 with a Hive metastore (that's not Iceberg), OLTP modeling, generic Airflow/dbt orchestration unrelated to Iceberg, or Postgres/MySQL schema work — different invariants. Prefer the data hub when the right data skill is unclear or the task spans ingest→store→serve.
- License
- MIT
- Instructions
- 367 lines in SKILL.md · view on GitHub
Agent Skills spec issues
- name: "data-apache-lakehouse" does not match folder "apache-lakehouse"
- files: referenced but missing: references/o-history-leaks.md
Files (3)
- SKILL.md
- pyiceberg-capabilities.md · references/
- single-host-operations.md · references/
SKILL.md (first lines)
# Apache Iceberg Lakehouse
Domain skill for building and operating Python-first Apache Iceberg lakehouses: PyIceberg + Polars/DuckDB medallion writes, REST/Glue catalog choice, and the architectural discipline that keeps incremental jobs incremental.
> **Verified:** 2026-05-28 against PyIceberg 0.11.1, pyiceberg-core 0.8.0, Iceberg spec V2/V3.
> Claims tagged `[v0.11]` and the **PyIceberg capability matrix** (in
> [`references/pyiceberg-capabilities.md`](references/pyiceberg-capabilities.md)) are
> version-sensitive — re-check against your installed version (`pip show pyiceberg`) before trusting
> them; the matrix lists how to verify each in seconds. Everything else (Iceberg internals, the
> architectural principles) is format/architecture-level and ages slowly.
> **Part of the `data` skill family.** The cross-cutting pipeline principles (idempotency, watermarks, schema fencing, resilience, bounded memory) are stated generally in the **data** hub; this skill is their Apache Iceberg expression. For consuming or serving APIs around the lakehouse, see **data-api**.
## When to invoke this skill
- Designing a new pipeline, layer, or transform in a bronze/silver/gold lakehouse.
- Adding or modifying a gold aggregate, analytics view, or derived table.
- Debugging incremental jobs that get slower as data grows, scheduled jobs that overrun their window, lock contention, memory pressure, or "metadata bigger than data."
- Reviewing any PR that touches a layer boundary (bronze→silver, silver→gold) or the catalog config.
- Choosing or swapping a catalog backend (Glue, REST, Polaris, Lakekeeper, Nessie, JDBC, Hive).
- Picking compaction / expire-snapshots / orphan-file-removal cadence for a table.
- Deciding between Iceberg, Delta, and Hudi for a new lakehouse.
- Investigating a bad publish — finding the right snapshot to roll back to, auditing what a backfill landed.
If you're about to add a global lock, a "rebuild from full history" path, a Parquet mirror next to an Iceberg table, a cron that recomputes a snapshot from scratch, or a write that bypasses the catalog — stop and read this skill first.
## Medallion rules
1. **Bronze** — raw ingest, minimal transforms, append-friendly.
2. **Silver** — typed, deduped, schema-validated; fail closed on drift when correctness depends on it.
3. **Gold** — business aggregates; read silver/bronze only; no back-writing to lower layers in the same job.
## Iceberg internals worth knowing (the parts that change how you write code)
The skill assumes you know the layered model (datafiles → manifests → manifest list → metadata.json → catalog). These are the facts that actually change PyIceberg-writer behavior:
- **Pruning happens twice, at different layers.** Manifest entries carry per-column lower/upper bounds, evaluated at *planning* time (cheap Avro reads, zero datafiles touched). Parquet row groups carry their own min/max, evaluated at *scan* time after the file is opened. A predicate on a sorted/partitioned column gets both layers; a predicate on an unsorted high-cardinality column gets ~no manifest pruning. **This is why sort order matters: it tightens manifest bounds.**
- **Manifest lists carry partition bounds and per-snapshot sequence numbers.** Diffing two snapshots is a manifest-list-only walk — you never need to open datafiles to find what changed. This is the underlying machinery of incremental reads even when PyIceberg only exposes it indirectly via watermark columns.
- **`last-sequence-number` in `metadata.json` is the OCC primitive.** Every commit bumps it. Equality deletes are scoped by it (a delete with sequence N only applies to data files with sequence < N). A re-inserted row gets a higher sequence number and survives. This is also what `CommitFailedException` retries depend on — your exactly-once primitive in PyIceberg.
- **The catalog stores exactly one pointer per table** — the current `metadata.json` path. Atomic CAS on that pointer is the entire basis for ACID. Hadoop catalog on S3 has no atomic rename → corruption under concurrent writers. Use REST/Glue/Nessie/JDBC for any multi-writer S3 deployment.
- **Manifests are Avro, never mixed.** A manifest tracks datafiles or delete files, never both. Reading manifests is fast; rewriting them (`rewrite_manifests`) is cheap relative to data rewrites.
- **Stats are written at commit time, not by a separate job.** Unlike Hive, Iceberg manifests are always fresh because writers compute bounds inline. Don't add a post-write "stats job" — it doesn't exist.
- **Puffin files** carry precomputed sketches (currently Theta sketches for approximate distinct counts). PyIceberg does not read or write them today. Don't pin a `COUNT DISTINCT` SLA to puffin if PyIceberg is in the loop.
## Architectural principles
Load-bearing properties of any Iceberg lakehouse transform. Each principle is paired with a falsifiable test — if you can't pass the test, the principle is being violated, not "applied differently."
### Forward motion is a watermark, not a scan
Every layer-to-layer promote carries a persisted high-water mark on its time/sequence column (`event_ts`, `sequence_id`, `updated_at`, etc.). The watermark commit is part of the same atomic write as the data — not a side-effect afterward. A transform with no watermark is a global recompute waiting to happen; it passes tests today and gets slower in proportion to data growth, with no warning. "Rebuild from history" exists only behind an explicit `--rebuild` flag, never as the default code path.
PyIceberg has no `incrementalAppendScan` yet (Java does). A watermark column committed atomically with the data is the substitute, and it lines up with Iceberg's own model: each commit bumps `last-sequence-number`; your watermark carries equivalent semantics at the row level.
**Concrete implementation — the watermark is a table property committed *with* the data.** Store the last-consumed source position (max `event_ts`/`sequence_id`, or the source snapshot id) as a property on the *target* table, e.g. `wm.<source>.<kind>`. The clean form is one transaction — `with target.transaction() as tx: tx.append(batch); tx.set_properties({"wm.sales.silver": str(new_wm)})` — so data and watermark land in a single atomic commit and can never disagree. A two-commit form (data, then a separate property bump) is acceptable **only** when every write is an idempotent upsert/partition-overwrite, so a crash between the two commits merely re-processes the last window. It's metadata-only, free to read at planning time, and needs no extra sidecar table. Full recipe: [`references/single-host-operations.md`](references/single-host-operations.md).
**Test:** if the upstream table grew 10× tomorrow, would this transform's runtime grow 10×? If yes, it lacks a watermark.
### A watermarked entity delta is still a windowed scan
Selecting *keys* incrementally (`entities with ts > watermark`) then reading `WHERE entity_id IN (keys)` **without** a time predicate is not incremental. Iceberg will not prune `month(ts)` partitions; PyIceberg COW then rewrites every data file that contains those ids — often a whole month file to flip a few flag columns. That is the entity-scoped O(history) leak from the **data** hub.
Fix on the timer path:
1. Predicate **both** keys and time: `entity_id IN (...) AND ts >= now - lookback` (lookback on the **partition** column, so months drop at planning).
2. Overwrite **only the sale_ids / rows in that window**. Do not emit 2016 rows into the COW filter because you scored a 2026 sale.
3. `--full` / `--rebuild` is the lifetime path. Do not make it the default.
4. Layout (sort by entity_id inside the time partition) tightens file bounds so `IN (entity)` skips files. It is not a substitute for the time predicate. Iceberg has no B-tree index.
**Test:** one new row today for an entity that has 10 years of history — does `plan_files()` for the score/overwrite include 2016 partitions? If yes, the window is missing.
### A `latest` / `as_of` pointer is a global tip, not a per-chunk side effect
A file or table property named `latest`, `as_of`, `tip`, or `watermark` that readers use for
`latest_as_of()` is the **global tip**. Write it once after a multi-day / multi-partition loop
succeeds. Updating it inside the loop leaves the oldest (or last-iterated) day in the pointer.
Backfill helpers default `update_latest=False`; the daily product pass sets `True` only after
today's write. Per-day overwrite can be idempotent and still be tip-unsafe.
**Test:** backfill N days with a pre-seeded today pointer. Is the tip still today / max day, or
min day? If min day, the pointer was written inside the loop.
### Every aggregate has a declared shape
Each gold/analytics aggregate is one of three shapes, and the shape is named in the module:
- **Append-only ledger** — immutable primary key; `append` on the delta dedupes by key.
- **Point-update keyed snapshot** — `MERGE INTO` on the keys touched by the delta. Cost is O(delta), not O(history).
- **Windowed scan** — bounded `source.filter(ts > now - window)` with partition pruning, on its own cadence.
"Full rebuild" is none of these. If you can't put one of the three labels on an aggregate, the architecture is wrong — don't slow the cadence to mask the cost, reshape the aggregate.
**Test:** can you write `# shape: append | point_update | windowed_scan` at the top of the aggregate's module without lying? If no, redesign.
### Maintenance is owned, not hoped for
PyIceberg can now **expire snapshots** `[v0.11]` (`table.maintenance.expire_snapshots()`), but this is **metadata-only — it does not physically delete the orphaned data files** (see the capability matrix). It cannot compact or remove orphan files at all. Physical file GC and compaction are Spark procedures, Trino `ALTER TABLE ... EXECUTE`, or a managed service. Untended Iceberg tables silently grow until metadata reads dominate query time and small files defeat manifest pruning. The safe order is fixed: `rewrite_data_files` → `expire_snapshots` (≥3-day window) → `remove_orphan_files` (≥3-day window) → `rewrite_manifests`. Skipping the safety window is how you delete files a still-running query needs.
There are now three legitimate ways to own maintenance — pick one explicitly, don't leave it implicit:
1. **External engine** — Spark procedures or Trino `EXECUTE optimize` on a schedule. Most control; you operate the cluster.
2. **Managed / zero-ops storage** `[2025]` — the catalog or storage layer runs policy-driven compaction, snapshot expiration, **and** orphan-file removal on the backend, decoupled from your Python pipeline. Examples: Amazon S3 Tables (continuous compaction + expiration since 2025), Cloudflare R2 Data Catalog, Polaris-managed maintenance. This is the only option where expiry *and* physical GC both happen without you wiring a second job.
3. **Hybrid** — PyIceberg `expire_snapshots` for metadata hygiene from your pipeline, plus a scheduled external/managed job for the physical `remove_orphan_files` + compaction it cannot do.
On a single host with no cluster, the "external engine" is usually a `systemd` timer (or cron) that runs PyIceberg `expire_snapshots` plus a scheduled DuckDB/Spark compaction + orphan-removal step — a legitimate option 1, just hand-wired (see [`references/single-host-operations.md`](references/single-host-operations.md)).
**Test:** name the person/service, the cadence, and the command (or backend policy) that maintains this table — **and** confirm physical file GC is covered, not just metadata expiry. If any of those is "I'll figure it out," the maintenance does not exist.
### Validation belongs on a branch, not in production
PyIceberg supports branches and tags since 0.8. Treat the `main` branch of an Iceberg table as published state; do risky work (large backfills, schema reshuffles, suspicious silver→gold rewrites) on a named branch, validate against it, then merge or discard. This is the write-audit-publish pattern expressed natively in Iceberg. A staging mirror that lives outside the catalog is a hidden interface; an Iceberg branch is a first-class one. Nessie adds multi-table atomic branch merges if you need them.
**Test:** if this promotion failed audit, could you discard it by dropping a branch? If the answer involves manually rewinding writes to the live table, it shouldn't have been on the live table.
### Schema evolution is an operator action
Additive columns, type widenings, and partition-spec changes are not a silent writer side effect.
Evolve on a branch (or a dedicated evolve script), then readers. PyIceberg: `delete_column`, not
`remove_field` (the latter orphans the field in manifests). Revert-then-readd of the same name isFrom Evan-Kim2028/agent-skills, MIT.
More from Evan-Kim2028/agent-skills
- 0.552score
data
Evan-Kim2028
Hub for data-engineering pipeline work — storage/ingest/serve choice plus shared discipline (idempotency, incremental-not-full-recompute, schema fencing…
SkillBFreshness: B★ 1Claude Code +9 - 0.552score
ebay-listing
Evan-Kim2028
List Pokémon TCG singles on the ekcope-46 eBay store via lake-of-rage ebay-sell (Trading AddFixedPriceItem).
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-product-eval
Evan-Kim2028
Portable methodology for evaluating published estimates, marks, or forecasts against later realized outcomes — frozen snapshots, walk-forward fit vs forecast…
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-api
Evan-Kim2028
Use when consuming HTTP APIs for ingestion (rate limits, backoff, pagination, auth, schema fencing) or serving gold over HTTP (FastAPI + DuckDB, pushdown…
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-semantic-quality
Evan-Kim2028
Portable methodology for semantic (row-truth) data quality — write-time quality attributes, trust ladders, cohort fences, golden entity packs with dual error…
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
data-reels
Evan-Kim2028
Principles for high-retention short-form data video (Instagram Reels, TikTok, Shorts) built from measured competitor research on data-viz and stats accounts.
SkillBFreshness: B★ 1Claude Code +9 - 0.544score
data-pokemon-tcg-foundations
Evan-Kim2028
Use when working with a trading-card sales tape — any sale-level dataset of collectible card transactions across multiple venues (Pokémon TCG, sports cards…
SkillBFreshness: B★ 1Claude Code +9 - 0.544score
data-identity-resolution
Evan-Kim2028
Portable workflow for attaching messy records to canonical entities — evidence ladder, fail-closed assignment, quarantine on strong-vs-weak conflict…
SkillBFreshness: B★ 1Claude Code +9 - 0.544score
data-pipeline-operations
Evan-Kim2028
Use when running multiple data pipelines on one host: memory admission, systemd MemoryMax, OOM that only appears under coexistence, capacity ratchets…
SkillBFreshness: B★ 1Claude Code +9 - 0.502score
browser-verify
Evan-Kim2028
Prove UI in a real browser — Playwright e2e, visual snapshots, axe, and/or Chrome DevTools MCP (live DOM, console, network, performance).
SkillCFreshness: C★ 1Claude Code +9 - 0.502score
flutter-pro-ux-review
Evan-Kim2028
Production Flutter UX audit: ranked polish findings (dead taps, null text, a11y, keyboard, haptics, layout shift, async/offline honesty, motion, empty states…
SkillCFreshness: C★ 1Claude Code +9 - 0.502score
mobile-app-echarts-visual
Evan-Kim2028
Mobile-readable **product ECharts** (card price/volume, portfolio history, set charts) without desktop regressions — grid gutters, axisLabel floors, legend…
SkillCFreshness: C★ 1Claude Code +9
Signals
- Stars
- 1
- Forks
- 0
- Open issues
- 0
- Last commit (this folder)
- Aug 29, 2026 (2 months ago)
- Last release
- Jun 22, 2026 (4 months ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/Evan-Kim2028/agent-skills
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from Evan-Kim2028
- 0.552score
data
Evan-Kim2028
Hub for data-engineering pipeline work — storage/ingest/serve choice plus shared discipline (idempotency, incremental-not-full-recompute, schema fencing…
SkillBFreshness: B★ 1Claude Code +9 - 0.552score
ebay-listing
Evan-Kim2028
List Pokémon TCG singles on the ekcope-46 eBay store via lake-of-rage ebay-sell (Trading AddFixedPriceItem).
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-api
Evan-Kim2028
Use when consuming HTTP APIs for ingestion (rate limits, backoff, pagination, auth, schema fencing) or serving gold over HTTP (FastAPI + DuckDB, pushdown…
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-product-eval
Evan-Kim2028
Portable methodology for evaluating published estimates, marks, or forecasts against later realized outcomes — frozen snapshots, walk-forward fit vs forecast…
SkillBFreshness: B★ 1Claude Code +9 - 0.548score
data-semantic-quality
Evan-Kim2028
Portable methodology for semantic (row-truth) data quality — write-time quality attributes, trust ladders, cohort fences, golden entity packs with dual error…
SkillBFreshness: B★ 1Claude Code +9 - 0.547score
data-reels
Evan-Kim2028
Principles for high-retention short-form data video (Instagram Reels, TikTok, Shorts) built from measured competitor research on data-viz and stats accounts.
SkillBFreshness: B★ 1Claude Code +9
Cite this page
data-apache-lakehouse skill. Agent Mart. https://agentmart.dev/skills/evan-kim2028-data-apache-lakehouse (last updated 2026-10-24)