# Hodios paste pack: Data engineering

Everything in Data engineering from Hodios, the open prompt library by Hermes IDE: 13 entries, catalog 2026.1003.0.

Every entry is dedicated to the public domain under CC0 1.0. Copy, change and share them freely, no attribution needed.

Browse and search the library at https://hermes-ide.com/prompts

## How to use

Find an entry below and copy the text inside its block into ChatGPT, claude.ai or any chat. Replace each [PLACEHOLDER] with your own material. Personas, rules and styles work best as custom instructions or project instructions.

## Contents

- Data engineering
  - [Data engineer](#data-engineer) (persona)
  - [Database administrator](#database-administrator) (persona)
  - [Design a data pipeline](#design-data-pipeline) (prompt)
  - [Design a relational database schema](#design-database-schema) (prompt)
  - [Design a search index](#design-search-index) (prompt)
  - [Design a star schema](#design-star-schema) (prompt)
  - [Generate realistic seed data](#generate-realistic-seed-data) (prompt)
  - [Plan a zero-downtime schema change](#plan-zero-downtime-schema-change) (prompt)
  - [Review a database migration](#review-database-migration) (prompt)
  - [Review database indexes against the workload](#review-database-indexes) (prompt)
  - [Write a data dictionary](#write-data-dictionary) (prompt)
  - [Write a dbt model](#write-dbt-model) (prompt)
  - [Write data-quality checks for a table](#write-data-quality-checks) (prompt)

---

<a id="data-engineer"></a>

## Data engineer

`data-engineer` · persona · Data engineering · https://hermes-ide.com/prompts/data-engineer

Acts as a data engineer who designs for idempotency, backfills and observability, treats schemas as contracts with their consumers, and asks who depends on each table before changing it.

````markdown
From now on, work as this persona: Data engineer.

You are a data engineer who has been paged for a pipeline at 3 a.m. and has rebuilt a year of history after a silent bug. You judge a pipeline by what happens when it runs twice, runs late, or runs on data nobody expected, not by how it behaves on the demo day.

How you work:
- Ask who consumes a table before you design or change it: which dashboards, models, services or people read it, how fresh they need it, and what breaks for them if it is wrong. A table without a known consumer is a candidate for deletion, not for more features.
- Treat every schema as a contract. Additive changes are safe; renames, type changes and changed meanings need a versioned path, notice to consumers, and an expand-then-contract migration.
- Make every job idempotent: rerunning it for the same period gives the same result, through partition overwrites or merges on keys, never blind appends.
- Design the backfill when you design the pipeline: parameterised by date range, throttled, isolated from scheduled runs, and verified afterwards.
- State the grain of every table in one sentence and test it.
- Build observability in from the start: freshness, volume, schema, nulls and rejected records, each with a threshold, an owner, and a decision about whether it blocks publishing.
- When you have shell access, run the query or the job and report the real numbers rather than predicting them.

What you flag:
- Appends without deduplication, incremental loads with no lookback for late data, and cursors that miss rows updated within the same timestamp.
- Joins that can fan out, and aggregates over them.
- Time zones that are not stated, money stored as floating point, and units that live only in someone's head.
- Personal data copied into places that do not need it, and retention nobody enforces.
- Streaming, extra platforms or new tools proposed for a need a scheduled batch job would meet.

Your habits:
- You prefer boring, well-understood tools and the fewest moving parts that meet the requirement.
- You show the sizing arithmetic and label assumptions.
- You write down the runbook step for every alert you add.
- You say when a question belongs to the data's owner, such as what a business term means, and ask them instead of deciding it yourself.
````

---

<a id="database-administrator"></a>

## Database administrator

`database-administrator` · persona · Data engineering · https://hermes-ide.com/prompts/database-administrator

Acts as a production DBA focused on data integrity, backups that restore, safe schema changes, query plans, capacity and least-privilege access. Use for Postgres, MySQL or similar in production.

````markdown
From now on, work as this persona: Database administrator.

You are a database administrator who has kept production relational databases alive for years, mostly PostgreSQL and MySQL. You have restored from backups at 4 a.m., watched a harmless-looking `ALTER TABLE` lock a busy table for twenty minutes, and traced a slow page to one missing index. The data is the one part of the system that cannot be redeployed, so you protect it first and optimise second.

How you work:
- Ask for the facts that change the answer before you give one: the engine and exact major version, table sizes and row counts, write and read rates, replication topology, connection pooling, managed service or self-hosted, and maintenance windows. A change that is safe on a 10,000-row table can take an outage on a 500-million-row one.
- Read the query plan before guessing. You ask for `EXPLAIN (ANALYZE, BUFFERS)` in PostgreSQL or `EXPLAIN ANALYZE` / `EXPLAIN FORMAT=TREE` in MySQL, compare estimated to actual rows, and look for the step where they diverge. You treat statistics, row estimates and data skew as part of the diagnosis.
- Treat schema changes as deploys. For every DDL statement you know which lock it takes, whether it rewrites the table, how long it holds the lock, and what queues behind it. You set `lock_timeout` and `statement_timeout`, build indexes concurrently (or with the engine's online DDL), add constraints as `NOT VALID` and validate later, and use expand and contract so old and new application code both work during the rollout.
- Count a backup as real only once it has been restored. You care about recovery point and recovery time objectives, point-in-time recovery, where backups are stored and who can delete them, and when a restore was last tested end to end.
- Enforce integrity in the database, not only in the application: primary keys, foreign keys, `NOT NULL`, check and unique constraints, appropriate types (timestamps with time zones, numeric for money), and transactions at the right isolation level.
- Plan capacity from trends: data growth, index bloat, connection counts, replication lag, autovacuum or purge progress, transaction ID age in PostgreSQL, disk and IOPS headroom. You prefer an alert at 70 percent to an outage at 100.
- Grant least privilege: application roles that cannot run DDL, read-only roles for analytics and support, no shared superuser credentials, and audit logging for access to sensitive data.
- Prefer reversible steps. Before anything destructive, you check for a recent backup, take a targeted copy when the data is small enough, and write down the rollback.

What you flag:
- Destructive or locking operations against production without a timeout, a window or a rollback: `DROP`, `TRUNCATE`, unbounded `UPDATE` or `DELETE`, column type changes that rewrite the table, and non-concurrent index builds on large tables.
- Backups that have never been restored, backups stored with the same credentials as the database, and replicas treated as backups.
- Long-running transactions, idle-in-transaction sessions and connection storms; missing connection pooling.
- `SELECT *` in hot paths, missing indexes on foreign keys, duplicate and unused indexes, and ORMs generating N+1 queries.
- Money stored in floating point, timestamps without time zones, and constraints enforced only in application code.
- Credentials in code or config files, superuser application accounts, and personal data copied into lower environments without masking.

Your boundaries:
- You run read-only diagnostic queries freely. You never run or recommend running a write, DDL or configuration change on production without stating its lock, duration, risk and rollback, and you leave the decision to run it with the person who owns the database.
- When a recommendation depends on the engine or version, you say which ones it applies to. You do not present tuning numbers as universal; you give a starting value and how to measure it.
- If you have not seen the schema, plan or metrics, you say what you would need instead of guessing.

Your habits:
- You give exact SQL, with the engine named, and comment what each statement locks.
- You test on a production-sized copy or estimate from real row counts before calling something safe.
- You write down every manual production change, with who ran it and when.
- You say plainly when the database is not the bottleneck.
````

---

<a id="design-data-pipeline"></a>

## Design a data pipeline

`design-data-pipeline` · prompt · Data engineering · https://hermes-ide.com/prompts/design-data-pipeline

Designs a batch or streaming data pipeline sized to stated volumes, covering sources, schedule, idempotency, late data, backfills and monitoring. Use before building or replacing a pipeline.

````markdown
<context>
Pipelines rarely fail on the happy path. They fail on the rerun that doubles yesterday's rows, the event that arrives two days late, the upstream column that changed type overnight, the incremental load that misses rows updated within the same second, the backfill that starves production jobs, and the partial load nobody noticed because only failures alert. Streaming is chosen because it sounds modern when the consumer reads a daily report. A good design starts from the freshness the consumers need and makes every stage safe to run twice.
</context>

<task>
Design a pipeline for:
[REQUIREMENTS]

1. Pin down requirements: each source (type, how changes can be captured, rate limits), each destination, the consumers and their freshness need, delivery semantics (exactly-once effect, or at-least-once with deduplication), retention, and personal data handling. If freshness or volume is missing and would change the design, ask; otherwise state the assumption.
2. Choose batch, micro-batch or streaming, justified by the freshness need and volume rather than preference. Size it: events or rows per second at peak, bytes per day, growth over two years, and the partitioning scheme that follows.
3. Ingestion: change data capture, incremental extraction by a cursor column, or full snapshots. For cursor-based extraction, handle ties on the cursor value, clock skew and deletes that the cursor cannot see.
4. Idempotency: make every stage safe to rerun by overwriting deterministic partitions or merging on keys, with deduplication keys and a run identifier recorded on output rows.
5. Late and out-of-order data: event time versus processing time, the watermark or lookback window, and how corrections reach downstream tables.
6. Schema evolution: the contract with each producer, what happens on a breaking change (fail, quarantine, or dead-letter), and who is told.
7. Orchestration: the dependency graph, schedule, retries with backoff, timeouts and SLAs.
8. Backfills: parameterised by date range, throttled, isolated from scheduled runs, and validated afterwards.
9. Monitoring: freshness, volume, schema, null rates, consumer lag, rejected records and cost, each with a threshold, an owner, and whether it blocks publishing.
10. List failure modes: what breaks, how it is detected, and how to recover.
</task>

<constraints>
- Use the given stack. If none is given, use the fewest components that meet the requirements, and name alternatives only as examples.
- Show the sizing arithmetic, and label numbers you supplied as assumptions.
- Do not add streaming, a lakehouse, or a message bus unless a stated requirement needs it.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Summary
One paragraph, then a Mermaid or ASCII diagram of the flow.

## Requirements and assumptions
Bullets, with assumptions marked.

## Architecture
Stage by stage: what it does, the technology, and the schedule or trigger.

## Idempotency and late data
How reruns and late events are handled at each stage.

## Backfills
The procedure and its safeguards.

## Monitoring and alerts
Table: signal | threshold | owner | blocks publishing (yes or no).

## Failure modes
Table: failure | detection | recovery.

## Sizing and cost
The arithmetic and the main cost drivers.

## Open questions
Only those whose answers would change the design.
</output_format>
````

---

<a id="design-database-schema"></a>

## Design a relational database schema

`design-database-schema` · prompt · Data engineering · https://hermes-ide.com/prompts/design-database-schema

Designs a relational schema from requirements and access patterns, with keys, constraints, types, indexes and DDL. Use when starting a new service or feature that stores data.

````markdown
<context>
A schema outlives the code around it. Mistakes such as a missing constraint, money stored as a float, a timestamp without a time zone or a tenant key left out of an index are cheap on day one and expensive after a year of data. The database should enforce the rules it can, so bad data cannot get in through any code path.
</context>

<task>
Design a postgres schema for:
[REQUIREMENTS]

1. List the entities, their relationships and cardinalities, and the business rules the data must obey. Write down every assumption you make.
2. Model to third normal form first. Denormalise only where a listed access pattern needs it, and say which one.
3. Choose keys: a surrogate primary key (identity integer, or a time-ordered UUID when ids are created outside the database or exposed publicly), plus natural unique keys as `UNIQUE` constraints.
4. Choose types deliberately: exact decimals for money (with the currency stored alongside), time-zone-aware timestamps, text with `CHECK` constraints or lookup tables for small fixed sets, and JSON only for data that is genuinely schemaless.
5. Enforce rules in the database: `NOT NULL` by default, foreign keys with an explicit `ON DELETE` behaviour, `UNIQUE` and `CHECK` constraints.
6. Derive indexes from the access patterns, one per pattern at most, with column order explained. Index foreign keys used in joins or cascading deletes.
7. For multi-tenant data, put the tenant key in every tenant-owned table, in its unique constraints and first in its indexes.
</task>

<constraints>
- Model only what the requirements need. Add audit columns, soft deletes or history tables only when a requirement asks for them, and list them under Trade-offs as options otherwise.
- Use DDL that runs on postgres as written. Do not mix dialects.
- Every index maps to a named access pattern or foreign key.
- When a requirement is ambiguous in a way that changes the model (one-to-many or many-to-many, hard or soft delete), pick one, say so in Assumptions, and add the question to Open questions.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Assumptions
Numbered.

## Diagram
A Mermaid `erDiagram` with every table, key and relationship.

## DDL
One SQL code block that creates every table, constraint and index in dependency order.

## Access patterns
| Pattern | Query shape | Index used |

## Trade-offs
Each significant choice, the alternative, and why you chose this one.

## Open questions
Questions whose answers would change the schema. "None" if none.
</output_format>
````

---

<a id="design-search-index"></a>

## Design a search index

`design-search-index` · prompt · Data engineering · https://hermes-ide.com/prompts/design-search-index

Designs a search index in Elasticsearch, OpenSearch or Postgres full-text, with mappings, analysers, relevance tuning and a reindexing plan. Use when adding search or fixing poor results.

````markdown
<context>
Search quality is decided by three things most designs skip: analysis (how text becomes tokens: language stemming, accents, synonyms, compound words, identifiers like SKUs that must not be split), the query (which fields, with what weights, how exact phrase and prefix matches rank against fuzzy ones), and a way to measure relevance against real queries. Postgres full-text search is enough for many products under a few million documents with simple ranking and no need for a separate cluster; a dedicated engine earns its operational cost with complex relevance, facets at scale, fuzzy and typo tolerance, or many languages.
</context>

<task>
Design search for:
<content_and_queries>
[CONTENT_AND_QUERIES]
</content_and_queries>
Engine: recommend

1. **Engine choice.** If "recommend", choose between Postgres full-text (with `pg_trgm` for fuzzy matching) and Elasticsearch or OpenSearch from volume, update rate, relevance needs, languages, facets and operational capacity, and state the trade-off. If an engine is given, use it and mention a serious mismatch once.
2. **Document model.** One indexed document per thing users want back. Denormalise the fields needed for matching, filtering, sorting and display; note what is copied from where and how it stays in sync.
3. **Mappings and analysers.** For each field: type (full-text, keyword, numeric, date, nested), analyser, and whether it is searched, filtered, sorted or only stored. Define custom analysers: language stemming per language, ASCII folding, lowercase, synonyms (applied at search time so they can change without reindexing), edge n-grams or a search-as-you-type field for autocomplete, and a keyword or exact sub-field for codes and identifiers. For Postgres, give the `tsvector` generated column with weights (`setweight` A to D), the text search configuration per language, and GIN indexes.
4. **Queries.** Write the main query for the example searches: multi-field matching with field boosts (title over body), phrase and exact-identifier boosts, fuzziness only on longer terms, filters in filter context (not scored), and business signals (recency, popularity, stock) through function scoring or rank expressions, capped so they cannot overwhelm text relevance. Include the highlighting and pagination approach (search-after rather than deep offset).
5. **Relevance tuning.** Walk through each example query: what currently or naively would rank first, what should, and which setting makes that happen.
6. **Indexing and reindexing.** How changes flow in (outbox or change data capture, queue, or periodic batch), handling deletes, and zero-downtime reindexing with versioned indexes behind an alias (create new index, backfill, dual-write or catch up, swap the alias, keep the old one for rollback). For Postgres, how the generated column and index are rebuilt safely.
7. **Evaluation.** A small judged query set (30 to 100 real queries with expected results), a metric (for example NDCG@10 or success at 3), zero-result and click-through monitoring, and a process for adding synonyms from failed searches.
</task>

<constraints>
- Use the engine's real syntax and say which version you assume. If unsure of an option, say so and describe the intent.
- Do not invent data volumes or query patterns; mark assumptions.
- Never mix the scoring of user-supplied filters into relevance; filters do not score.
- Keep the design operable by the team described; flag when a cluster is more than they need.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Engine choice
Choice and reasons, in a few bullets.
## Document model
A table: field, source, purpose (search, filter, sort, display).
## Mappings and analysers
One fenced block (index mapping JSON, or SQL DDL for Postgres).
## Queries
Fenced query examples for the main search and autocomplete.
## Relevance tuning
A table: example query, expected top results, settings that achieve it.
## Indexing and reindexing
Numbered steps.
## Evaluation
Bullets.
## Open questions
Numbered.
</output_format>
````

---

<a id="design-star-schema"></a>

## Design a star schema

`design-star-schema` · prompt · Data engineering · https://hermes-ide.com/prompts/design-star-schema

Designs a dimensional model from the questions analysts need answered: business processes, grain, facts, dimensions, slowly changing dimension types and DDL. Use when building a warehouse layer.

````markdown
<context>
A dimensional model is judged by whether analysts can answer their questions correctly with simple joins. Models fail when the grain is never stated, so facts at different grains share a table and sums double count; when ratios or balances are stored as if they could be summed; when a dimension attribute that changes over time is overwritten, so last year's revenue moves to this year's region; and when each fact table has its own private version of customer or product, so results cannot be compared. Kimball's sequence still works: pick the business process, declare the grain, choose the dimensions, then the facts.
</context>

<task>
Questions to answer:
[BUSINESS_QUESTIONS]

Source tables:
[SOURCE_TABLES]

1. Identify the business processes behind the questions (ordering, shipping, billing, support, sign-ups…). Each process becomes at least one fact table.
2. For each fact table, declare the grain in one sentence at the most atomic level the sources support, and choose its type: transaction, periodic snapshot (for balances and levels over time), accumulating snapshot (for pipelines with milestones), or factless (for events or coverage with no measure).
3. List each fact's measures and classify them as additive, semi-additive (balances: summable across some dimensions but not across time) or non-additive (ratios and percentages: store the numerator and denominator instead).
4. Design the dimensions: surrogate keys, natural keys, attributes, conformed dimensions shared across facts, a date dimension (and time of day if needed), role-playing dates (order date, ship date), degenerate dimensions such as an order number, junk dimensions for leftover flags, and bridge tables for many-to-many relationships.
5. Choose a slowly changing dimension type for each attribute that can change: type 0 (never changes), type 1 (overwrite, history not needed) or type 2 (new row with valid_from, valid_to and is_current). Justify each choice by a question that needs, or does not need, history.
6. Plan for unknown and late-arriving members: a default "unknown" row in each dimension, and inferred members that are updated when the dimension row arrives.
7. Map every business question to the tables that answer it, with a query sketch. Flag any question the sources cannot answer and what data would be needed.
8. Write the DDL.
</task>

<constraints>
- Do not invent source columns. If a question needs data the sources lack, put it under Source gaps.
- Write portable ANSI-style DDL unless the warehouse is named. Note warehouse-specific choices such as clustering or partitioning separately.
- Prefer one wide dimension to snowflaked sub-dimensions unless the input gives a reason to normalise.
- If the questions are too vague to fix a grain, ask before designing.
</constraints>

<output_format>
## Business processes and grain
One line per fact table: process, grain sentence, fact table type.

## Bus matrix
Table: fact tables as rows, conformed dimensions as columns, marked where used.

## Fact tables
Per table: keys, degenerate dimensions, measures with additivity.

## Dimensions
Per table: keys, attributes with their SCD type, and the unknown member.

## DDL
One fenced SQL block.

## Question coverage
Table: question | tables | query sketch.

## Source gaps
Questions or attributes the sources cannot support, and what would fix it.

## Open questions
Only those that would change the grain or an SCD choice.
</output_format>
````

---

<a id="generate-realistic-seed-data"></a>

## Generate realistic seed data

`generate-realistic-seed-data` · prompt · Data engineering · https://hermes-ide.com/prompts/generate-realistic-seed-data

Generates realistic, referentially consistent fixture data for a database schema, with labelled edge cases and no real personal data. Use for local development, demos and integration tests.

````markdown
<context>
Seed data is only useful if it loads and if it looks like production. Typical generated fixtures fail on the first foreign key, use "test1, test2" names that hide layout bugs, give every customer exactly one order, and leave out the rows that break code: the longest name, the null middle name, the order with no items, the timestamp on a daylight-saving boundary. Fixtures also leak real personal data when someone copies production rows. Good seed data obeys every constraint, has realistic skew and ordering, deliberately includes edge cases, and is fictitious by construction.
</context>

<task>
Generate seed data as sql for the schema below. Row counts: 20 per table.

[SCHEMA]

1. Parse the schema. Order tables so every referenced row exists before it is referenced. Break cycles, such as a self-referencing manager_id, by inserting with nulls and updating afterwards, or with deferred constraints where the engine supports them.
2. Satisfy every constraint: types, lengths, NOT NULL, UNIQUE, CHECK, enums and foreign keys. For sql, write in the dialect the DDL implies and say which one you assumed.
3. Make it realistic:
   - skewed relationships (a few customers with many orders, most with one or two);
   - timestamps in a consistent order (created before updated, ordered before shipped) relative to a fixed anchor date you state;
   - derived values that agree (an order total equals the sum of its lines). For sql, insert the parent with a placeholder its constraints accept (such as 0) and set the value with an `UPDATE` from the children, instead of doing the arithmetic by hand; for csv and json, recheck each one before output;
   - varied, plausible, invented names and text in several locales.
4. Include edge cases on purpose and label them in the Notes: maximum-length strings, accented, non-Latin, emoji and right-to-left text, empty strings versus nulls where both are allowed, zero and boundary numbers, timestamps at month end, leap day and daylight-saving transitions, soft-deleted rows, and parents with no children.
5. Make it deterministic: fixed ids and dates, so tests can rely on specific rows.
6. Output: for sql, INSERT statements in dependency order inside one transaction; for csv, one block per table with a header row; for json, one object keyed by table name.
7. If the requested volume is too large to list usefully (more than a few hundred rows in total), write a small hand-made set with the edge cases plus a deterministic, seeded generator for the bulk, and say why.
</task>

<constraints>
- No real people, real companies' customer data, real addresses or working contact details. Use reserved example domains (example.com, example.org, example.net), fictional phone ranges such as 555-0100 to 555-0199 in North America, documentation IP ranges (192.0.2.0/24, 198.51.100.0/24, 203.0.113.0/24), and payment card numbers only from published test ranges.
- For national identifiers and similar sensitive fields, use values that are structurally invalid or from documented test ranges, and say so.
- If a column's meaning is unclear (a polymorphic type column, a JSON payload with no schema), ask or state the assumption.
</constraints>

<output_format>
## Notes
The insertion order, the anchor date, how cycles were broken, and a list of edge cases with the rows that carry them.

## Data
The data in sql, in fenced blocks.

## Constraint check
One line per constraint, saying how the data satisfies it.
</output_format>
````

---

<a id="plan-zero-downtime-schema-change"></a>

## Plan a zero-downtime schema change

`plan-zero-downtime-schema-change` · prompt · Data engineering · https://hermes-ide.com/prompts/plan-zero-downtime-schema-change

Turns current table DDL and a desired change into expand and contract steps with lock-safe SQL, app changes, backfill, verification and rollback. Use before altering a live table.

````markdown
<context>
The exact DDL decides what is safe. The same `ALTER TABLE` can be instant on one table and a table rewrite on another, depending on the column type, default, constraints, indexes, triggers and engine version. Even an instant change can stall production: it queues behind a long-running transaction while holding a lock request that blocks every query after it. And during any deploy, old and new application versions run side by side, so each intermediate schema must work with both. The safe shape is expand, migrate, contract: add the new structure, write to both, backfill, switch reads, stop writing the old, then remove it, with every step independently deployable and reversible.
</context>

<task>
Current schema (postgres):
[CURRENT_SCHEMA]

Desired change: [DESIRED_CHANGE]

1. If you can read the repository, find the current table definition and recent migrations, the migration tool's conventions, and every code path that reads or writes the affected columns (queries, ORM models, reports, other services). List what you found. If you cannot, say which of these you are assuming.
2. Read the DDL and list what affects safety: table size and write rate, column types, defaults, NOT NULL and CHECK constraints, unique indexes, foreign keys in both directions, triggers, generated columns and replication. Say what is missing and what you assume about it. If the engine version is unknown and changes the answer, give both paths.
3. Break the change into ordered steps. For each step give:
   - the SQL, in the project's migration tool format if known, using the engine's lock-safe forms (see the notes below), with a lock timeout and a retry instruction for any statement that takes a strong lock;
   - the lock it takes, whether it rewrites or scans the table, and the expected duration class (instant, proportional to table size, or batched);
   - the application change that ships with it (write both, read new behind a flag, stop writing old);
   - the verification query that must pass before the next step;
   - the rollback for that step.
4. Before the application stops writing the old structure, relax what would reject rows without it: drop its NOT NULL, give it a default, or disable the trigger that requires it.
5. For backfills: batch by primary key range, keep each batch in a short transaction outside the migration, make it idempotent so it can resume, throttle by replication lag or load, and give the query that proves completeness.
6. For dual writes, choose application-level writes or a temporary trigger, say why, and say how drift between old and new columns is detected and repaired.
7. Mark the point of no return: the first step after which rolling back means restoring data, not just redeploying.

Engine notes. Check each against the stated version:
- postgres: use `CREATE INDEX CONCURRENTLY` (outside a transaction; drop the invalid index if it fails), add constraints `NOT VALID` and then `VALIDATE CONSTRAINT`, enforce NOT NULL through a validated `CHECK (col IS NOT NULL)` before `SET NOT NULL`, and know that most type changes rewrite the table. Set `lock_timeout` on every DDL session.
- mysql: say which `ALGORITHM` (INSTANT, INPLACE or COPY) and `LOCK=NONE` apply, watch metadata locks, and use an online schema change tool (gh-ost or pt-online-schema-change) when the operation would copy the table.
- sqlite: most changes need the documented create-copy-rename table rebuild. There is one writer at a time, so plan for a short write pause rather than true zero downtime, and say so.
- sql-server: say which operations are metadata-only and which need `ONLINE = ON`, and note that online index operations depend on the edition.
- other: ask which engine and version before giving engine-specific SQL. Until then, use a new column plus batched backfill rather than an in-place change, and give a way to measure the lock behaviour on a staging copy under load.
</task>

<constraints>
- Never combine the expand and contract phases in one deploy.
- Every step must leave the currently deployed application version working.
- Do not claim an operation is instant or online unless that is true for the engine and version. If it depends on the version, say so.
- Do not drop or rename anything still read by any deployed code. Say how to confirm that nothing reads it.
- Do not run any migration or query. The plan is for the team to execute.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Summary
Two or three sentences: the approach, the number of deploys, and the riskiest step.

## Compatibility matrix
Table: step | schema state | app version that must work | reads from | writes to.

## Steps
Numbered. Each: SQL in a fenced block, lock and duration, app change, verification query, rollback.

## Point of no return
The step, what rollback means after it, and what to confirm before taking it.

## Risks
Bullets: the risk (for example replication lag, long transactions holding locks, an ORM caching the old schema), how to detect it, and the mitigation.
</output_format>
````

---

<a id="review-database-migration"></a>

## Review a database migration

`review-database-migration` · prompt · Data engineering · https://hermes-ide.com/prompts/review-database-migration

Reviews a schema migration for locking risk, table rewrites, unsafe defaults, missing indexes, irreversible steps and deploy-order problems, and returns a safer version. Use before merging.

````markdown
<context>
Migrations that pass in development cause outages in production because production tables are large and busy. The usual causes: a statement that takes an exclusive lock and then waits behind a long transaction while every other query queues behind it; a type change or default that rewrites the whole table; a constraint or `NOT NULL` that scans the table under lock; a non-concurrent index build that blocks writes; a rename or drop that breaks the old application code still running during a rolling deploy; a data backfill in the same transaction as the schema change; and a down migration that cannot bring dropped data back. Lock behaviour differs by engine and version, so the review must be specific to the database named.
</context>

<task>
Review this migration for [DATABASE]:

<migration>
[MIGRATION]
</migration>

1. If it is a framework migration, translate each operation into the SQL the framework will actually run, including implicit transactions and anything the framework adds (default indexes, constraint names, column type mappings).
2. For each statement, determine for this engine and version: the lock it takes and what that lock blocks; whether it rewrites the table or scans it while holding the lock; and how long it would run at the given table sizes. When sizes are missing, say how the risk changes with size.
3. Check each risk:
   - Locking without `lock_timeout` (PostgreSQL) or with long metadata-lock waits (MySQL), and the queue that forms behind a waiting DDL statement.
   - Table rewrites: column type changes, volatile defaults, and engine-specific cases (in MySQL, which operations support `ALGORITHM=INSTANT` or `INPLACE` with `LOCK=NONE` and which fall back to `COPY`).
   - Constraints validated under lock: foreign keys, check constraints and `NOT NULL` on existing columns, and the safer path (`NOT VALID` then `VALIDATE CONSTRAINT` in PostgreSQL).
   - Index builds that are not concurrent or online, and `CONCURRENTLY` used inside a transaction (which fails), including how the framework disables its transaction.
   - Missing indexes on new foreign-key columns or on columns the shipped code will filter by.
   - Unique indexes or constraints added over data that may already contain duplicates.
   - Deploy-order breakage: renames, drops and new `NOT NULL` columns without defaults that old code still running cannot handle, and ORMs that cache column lists.
   - Data changes mixed with schema changes: unbatched `UPDATE` or `DELETE` on large tables, long transactions and replication lag.
   - Irreversibility: drops, narrowing type changes and down migrations that cannot restore data.
4. Write a safer version: split into separate migrations where needed, set timeouts, use concurrent or online operations, move backfills into batched jobs, and follow expand and contract for anything that old and new code must both survive.
5. Give the deploy order relative to application releases, the pre-flight queries to run (duplicate checks, long-running transactions, table sizes), and the rollback for each step.

If the engine version is ambiguous in a way that changes lock behaviour, state the version you assumed.
</task>

<constraints>
- Base every lock claim on the named engine and version; when behaviour changed between versions, say from which version it applies.
- Rank findings by outage or data-loss risk, not by style. Do not comment on naming unless it breaks something.
- Never recommend running the migration on production as a test. Pre-flight checks must be read-only.
- Keep the safer version equivalent in end state to the original unless a change is required for safety, and say when it is.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Verdict
One line: safe to merge | merge with the changes below | do not merge. Then the main reason in one sentence.

## Statement analysis
Table: statement | lock taken | blocks | rewrite or scan | estimated duration | risk (low, medium, high).

## Findings
Numbered, most severe first. Each: the statement, what goes wrong in production, and the fix.

## Safer migration
Code blocks in the same format as the input (SQL or the framework's), split into ordered migrations.

## Deploy order
Numbered steps interleaving migrations and application releases.

## Pre-flight checks
Read-only SQL to run before deploying, each with what result means stop.

## Rollback
Per step: how to undo it, and which steps cannot be undone.
</output_format>
````

---

<a id="review-database-indexes"></a>

## Review database indexes against the workload

`review-database-indexes` · prompt · Data engineering · https://hermes-ide.com/prompts/review-database-indexes

Reviews a database's indexes against its real query workload to find missing, unused, duplicate and bloated indexes, with DDL and the write cost of each change. Use for periodic index hygiene.

````markdown
<context>
Indexes drift away from the workload. Queries change, new access paths go unindexed, old indexes stay on every write long after the query that needed them was deleted, two people add the same index under different names, and heavily updated indexes bloat. Each index speeds up some reads and slows every insert, every update to its columns and every delete, uses disk and memory, and (in PostgreSQL) can stop updates from being heap-only. A useful review weighs both sides with the real workload, not rules of thumb.
</context>

<task>
Review the indexes of this PostgreSQL database.

Schema and indexes:
[SCHEMA_AND_INDEXES]

Workload and statistics:
[SLOW_QUERIES_OR_STATS]

1. Map each top query to its access path: the filter, join, sort and grouping columns, and the index it uses or should use. Note selectivity where the statistics allow.
2. Missing indexes: for queries that scan large tables or sort without an index, propose an index with the column order justified (equality columns first, then range, then sort), and consider a partial index for a selective constant filter, a covering index (`INCLUDE` in PostgreSQL, extra trailing columns in MySQL) for hot read paths, and an expression index when the query wraps the column in a function. Check foreign-key columns used in joins or cascading deletes.
3. Unused indexes: those with no or very few scans since the last statistics reset. Before proposing a drop, rule out indexes that back primary keys, unique constraints or foreign keys; indexes used only on replicas (statistics are per server); and indexes needed by rare but important jobs (month-end reports). Say how long the statistics cover.
4. Duplicate and redundant indexes: identical definitions, and indexes that are a left prefix of another index with the same properties. Keep the one that serves a constraint or the most queries.
5. Bloat and low value: indexes much larger than their data suggests, low-selectivity indexes the planner rarely uses (booleans, status columns without a partial predicate), and wide indexes on heavily updated columns.
6. For every proposed change, estimate the write cost (indexes touched per insert and update on that table, effect on heap-only updates in PostgreSQL), the storage change, and the read benefit tied to specific queries.
7. Write the DDL in a safe order: create new indexes concurrently or online first, verify that plans use them, then drop the indexes they replace. For drops, prefer a reversible step where the engine has one (`ALTER TABLE ... ALTER INDEX ... INVISIBLE` in MySQL 8.0) and keep the `CREATE` statement to restore each dropped index.

If the workload data does not cover enough time to call an index unused, say so and mark those findings as provisional.
</task>

<constraints>
- Tie every recommendation to a query or a statistic in the input. Do not propose indexes for queries you were not shown.
- Use the named engine's syntax and behaviour; say when a feature needs a minimum version.
- Never drop an index that enforces a constraint. Never propose a drop without its restore statement.
- Prefer fewer, well-chosen indexes. If a new index makes an existing one redundant, say so in the same finding.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Summary
Three to five lines: the biggest wins, the safe drops, and the overall write-cost change.

## Findings
Table: # | type (missing, unused, duplicate, bloated, low value) | table and index | evidence (query or statistic) | action | read benefit | write and storage cost | confidence.

## DDL plan
Ordered SQL in code blocks: creates first, verification, then drops with their restore statements commented next to them.

## Verification
The `EXPLAIN` or `EXPLAIN ANALYZE` to run before and after for each affected top query, and the statistics to watch for a week after the change.

## Missing data
What would raise confidence (longer statistics window, replica statistics, bloat estimates) and the queries to collect it.
</output_format>
````

---

<a id="write-data-dictionary"></a>

## Write a data dictionary

`write-data-dictionary` · prompt · Data engineering · https://hermes-ide.com/prompts/write-data-dictionary

Writes a data dictionary for database tables with each column's meaning, units, nullability, allowed values, owner and lineage, and flags every column it cannot infer. Use when documenting a schema.

````markdown
<context>
A data dictionary is only trusted if it never guesses silently. The expensive mistakes come from the columns that look obvious: `amount` stored in cents and read as currency units, `created_at` in local time read as UTC, a `status` code 3 nobody can decode, a nullable column whose nulls mean "not applicable" in one era and "unknown" in another. The value of the dictionary is as much in naming what is not known, and whom to ask, as in describing what is.
</context>

<task>
Write a data dictionary for:
[SCHEMA]

1. For each table, state the grain ("one row per …"), the primary key, and how rows appear to change (append-only, updated in place, soft-deleted), if the evidence shows it.
2. For each column, record:
   - meaning, in one plain sentence;
   - unit or format (currency and minor units, time zone, ID format, encoding);
   - nullability, declared and observed in the sample, and what a null means;
   - allowed values or range, from constraints or observed in the sample;
   - an example value (masked if sensitive);
   - personal data classification: none, personal or sensitive;
   - lineage: the foreign key it references, or what it is derived from;
   - owner, or "TBD";
   - confidence: declared (from constraints or comments), inferred (from the name or sample), or unknown.
3. Where you cannot infer the meaning or unit with confidence, write "Cannot infer" and add a precise question for the owner, for example "Is orders.amount in cents or in currency units? Sample values 1999 and 250 suggest cents."
4. Flag inconsistencies: the same concept named differently across tables, mixed units, columns that look unused or always null in the sample, and codes without a lookup table.
</task>

<constraints>
- Never present an inference as a fact. Every inferred entry is marked as inferred.
- Do not copy personal data from the sample into the dictionary. Mask example values.
- Keep each meaning to one sentence. Put detail in the questions, not in the table.
</constraints>

<output_format>
For each table, a heading `## <table name>`, a one-line summary (grain, key, change pattern), then a table: Column | Type | Meaning | Unit or format | Nullable (declared/observed) | Allowed values | Example | PII | Lineage | Owner | Confidence.

Finish with `## Questions for owners`: a numbered list grouped by table, each question answerable in one line.
</output_format>
````

---

<a id="write-dbt-model"></a>

## Write a dbt model

`write-dbt-model` · prompt · Data engineering · https://hermes-ide.com/prompts/write-dbt-model

Writes a dbt model from business logic, with declared sources, a stated grain, unique, not_null and relationships tests, column docs and a safe incremental strategy. Use when adding a dbt model.

````markdown
<context>
dbt models go wrong in quiet ways. A join fans out and nobody notices because no test pins the grain. A table name is hardcoded instead of using `ref` or `source`, so lineage and environments break. Business terms are implemented the way the author guessed. An incremental model filters on `max(updated_at)` with no lookback, so late-arriving rows are lost forever. Good dbt code states its grain, tests it, documents its columns and makes incremental loads safe to rerun.
</context>

<task>
Write a dbt model, materialised as table, for this logic:
[BUSINESS_LOGIC]

Sources and upstream models:
[SOURCE_TABLES]

1. State the grain as "one row per …" and the key that enforces it. If the business logic leaves the grain or a definition open, ask, or state the assumption and put it in Open questions.
2. Declare sources in a sources YAML file with `loaded_at_field` and freshness thresholds where a load timestamp exists. Reference upstream data only through `source()` and `ref()`.
3. Add staging models only where a source needs renaming, casting or deduplication, one per source, following the project convention (`stg_<source>__<table>` if unknown).
4. Write the model SQL as import CTEs, then logical CTEs, then a final `select` with an explicit column list. Handle nulls and duplicates in the sources explicitly, and note any time zone conversion.
5. If materialised as incremental: set `unique_key`, choose `incremental_strategy` for the warehouse (merge where supported, otherwise delete+insert or insert_overwrite; check whether the project's dbt version supports microbatch), filter new rows inside `is_incremental()` with a lookback window for late-arriving data, set `on_schema_change`, and say when a full refresh is needed.
6. Write a properties YAML file with the model and column descriptions and tests: `unique` and `not_null` on the key (or a combination-of-columns test for a composite key, naming the package it needs), `relationships` for foreign keys, `accepted_values` for categorical columns, and one singular test for the most important business rule. Use the `data_tests:` key on dbt 1.8 or later and `tests:` before that; if the project is on 1.8 or later and the rule is easier to show with fixed input rows, write a dbt unit test instead.
7. Give the commands to build and test the model and its children, and a query that checks the grain.
</task>

<constraints>
- Use only columns listed in the sources. If the logic needs a column that is not there, list it under Open questions instead of inventing it.
- Keep SQL portable unless the warehouse is known. Flag any warehouse-specific function you use.
- No `select *` in the final CTE. Keep Jinja to what the model needs.
- Follow the project's naming and folder conventions if they are visible in the input.
</constraints>

<output_format>
## Assumptions and grain
The grain statement, the key, and each assumption.

## Files
Each file in its own fenced block, preceded by its path (for example `models/marts/fct_orders.sql`, `models/marts/_marts__models.yml`, `models/staging/_sources.yml`).

## Run and verify
Commands, the grain-check query, and what a passing result looks like.

## Open questions
Definitions or columns that need confirmation.
</output_format>
````

---

<a id="write-data-quality-checks"></a>

## Write data-quality checks for a table

`write-data-quality-checks` · prompt · Data engineering · https://hermes-ide.com/prompts/write-data-quality-checks

Writes data-quality checks for a table (freshness, volume, schema, validity, uniqueness, referential integrity, distribution) with severities, thresholds and owners. Use when a table feeds decisions.

````markdown
<context>
Most bad data is not a failed job. It is a job that succeeded with half the rows, a column that turned null after an upstream release, a duplicated load, or an enum value nobody had seen before. Useful checks cover the dimensions that catch these (freshness, volume, schema, validity, uniqueness, referential integrity, distribution and business rules), distinguish failures that must block publishing from ones that only warn, and route every alert to a named owner with a first action. A check nobody owns, or one that fires every day, gets muted and then protects nothing.
</context>

<task>
Write data-quality checks in sql for this table:
[TABLE]

1. State the grain ("one row per …"), the key, the load cadence and the consumers. If the grain or cadence is unclear, ask, or state the assumption.
2. Write checks across these dimensions, skipping any that do not apply and saying why:
   - freshness: the newest load or event timestamp against the expected cadence;
   - volume: today's row count against the same weekday over recent weeks, as a ratio or z-score;
   - schema: expected columns and types;
   - validity: nulls in required columns, accepted values for categorical columns, numeric ranges, formats;
   - uniqueness of the key;
   - referential integrity: orphaned foreign keys;
   - distribution: drift in null rate, mean or percentiles, and category shares;
   - business rules across columns, such as end after start, or a total equal to the sum of its lines.
3. Give each check a severity: block (stop downstream publishing) or warn. Give a threshold derived from the sample where possible, or an explicit starting value marked to be tuned. Name an owner role or a placeholder, and give the first action on failure.
4. Implement the checks in sql:
   - sql: one query per check that returns failing rows or a single failing metric, so zero rows means pass;
   - dbt: generic tests in properties YAML plus singular tests, naming any package a test needs;
   - great-expectations: an expectation suite using the GX Core 1.x API (say which version you assumed);
   - soda: SodaCL checks in YAML.
5. Explain how to tune thresholds after two to four weeks of history, and when to retire a check that never fires.
</task>

<constraints>
- Do not invent columns. Checks must reference only columns in the table definition.
- Avoid checks that will alert on normal variation. Weekly seasonality and month-end peaks belong in the threshold.
- Keep each check independent, so one failure does not hide another.
- Separate what you verified from what you inferred. Mark inferences as such.
- When you do not know, say "I don't know" once and state what would settle it.
</constraints>

<output_format>
## Table grain and assumptions
Grain, key, cadence, consumers, and assumptions.

## Checks
Table: check | dimension | severity | threshold | owner | first action on failure.

## Implementation
The code for sql in fenced blocks, one per file.

## Tuning plan
How and when to adjust thresholds.

## Gaps
What these checks cannot catch, and what would.
</output_format>
````
