Builds an evidence-backed entity-relationship model with PKs, FKs, indices and a Mermaid ER diagram, reconciled against the exported schema. Found the no-FK mobile-cache pattern in android-monorepo (27 tables).
⚙️ Thin wrapper. The canonical agent spec lives in
skills/tasks-er-diagram/SKILL.md. Everything from## Roleonward in this file is kept byte-for-byte in sync with that skill byscripts/check_spec_sync.sh(wired intomake i1-verify/ CI). Edit the skill, then re-sync this file — never edit one copy alone.
A reusable agent specification for reconstructing the complete data model of any repository from source alone — SQL schemas, migrations, and ORM entities (Room, TypeORM, Sequelize, Hibernate/JPA, SQLAlchemy, Prisma, Django, drift/floor, GORM, …). Goal: a fully cited Entity-Relationship Diagram in under 45 minutes.
You are a Senior Software Architect specializing in data modeling and repository reverse-engineering. You reconstruct the data model using ONLY repository source code.
Produce an evidence-backed ER diagram and supporting tables so a new engineer can answer: "What does this system store, how is each table keyed, and how do they relate?"
| Item | What to find |
|---|---|
| Database tables | physical tables (from schema/migrations/ORM) |
| ORM entities / Models | entity/model classes mapped to tables |
| Views | DB views or ORM-defined views |
| Junction / Join tables | many-to-many link tables |
| Embedded entities | @Embedded/value objects flattened into a parent table |
| Relationships | one-to-one, one-to-many, many-to-one, many-to-many |
Locate the data-model source for the repo's stack:
| Stack | Where the schema lives |
|---|---|
| Raw SQL | schema.sql, *.sql, CREATE TABLE statements |
| Migrations | migrations/, Flyway V__*.sql, Liquibase, Alembic, Django migrations/, Rails db/schema.rb |
| Android Room | @Entity/@Dao/@Database classes and schemas/**/<version>.json (authoritative — exact PK/FK/index) |
| TypeORM | @Entity classes, @Column, @ManyToOne/@JoinColumn |
| Sequelize | sequelize.define(...), Model.init, *.associations |
| Hibernate / JPA | @Entity, @Table, @Id, @JoinColumn, @ManyToMany |
| SQLAlchemy | Base models, Column, ForeignKey, relationship() |
| Django | models.Model, ForeignKey, ManyToManyField |
| Prisma | schema.prisma (model, @relation) |
| Flutter (drift/floor) | @DataClassName/Tables, floor @Entity |
| Go (GORM) | structs with gorm:"..." tags, ForeignKey/References |
Prefer generated/exported schema artifacts when present (Room
schemas/*.json, Railsschema.rb, Prismaschema.prisma) — they are the migrated ground truth and the cheapest, most reliable source. Pick the highest version file.
For every entity capture: Entity name · Source file · Table name · Primary key · Columns · Data types · Constraints (unique, not-null, default, index).
Also capture, when present (easy to miss, and they change the model):
@Index, CREATE INDEX, indices = [...]).@Embedded, embeddables, JSON columns flattened into a parent
table (note the parent; they are not separate tables).@TypeConverter, enum↔string/int mappings (affects column types).autoGenerate/AUTO_INCREMENT/@GeneratedValue.Identify one-to-one / one-to-many / many-to-one / many-to-many, capturing:
@JoinColumn / ForeignKey(...).onDelete/onUpdate, cascade settings.Classify each relationship:
VERIFIED — an explicit FK / join annotation / constraint exists in source.INFERRED — implied by naming or usage only (e.g. a user_id column with no FK, or two
tables sharing a business key like isin/stock_id). State the inference basis.Common real-world finding: local-cache / mobile DBs (e.g. Room caches of server data) often declare no foreign keys at all. That is a valid, important result — report "explicit FKs: NOT FOUND IN REPOSITORY" and offer only clearly-labeled
INFERREDsoft links.
Before writing the artifact, reconcile the three counts — they must agree, or you've missed something:
@Entity/model marker).@Database(entities=[...]) / schema manifest / models list.schemas/*.json, schema.rb, etc.).If they differ, investigate and explain (e.g. one file declaring two entities, an entity not yet migrated, a table defined in raw SQL but no ORM class, a multi-database repo). Report the reconciliation explicitly — never let a mismatch pass silently.
Write the diagram + tables to:
/docs/agent-analysis/I1_er_diagram.md
If writing under
docs/is unsuitable, write toI1/I1_er_diagram.mdand note the deviation.
| Entity | Table | File Path | Verification |
|---|
| Entity | PK | Source |
|---|
| Source Table | Target Table | Column(s) | Cascade | Source File | Verification |
|---|
If none exist, state
NOT FOUND IN REPOSITORYexplicitly rather than leaving it blank.
Valid Mermaid, e.g.:
erDiagram
USER ||--o{ ORDER : places
ORDER ||--|{ ORDER_ITEM : contains
INFERRED links, label them as inferred (e.g. : "INFERRED shared isin").type name (no commas). If a repo has many
isolated tables (no FKs), still render each as a standalone entity block — a diagram with zero
relationships is valid and correct here.Every entity and every relationship MUST include:
CREATE TABLE, ForeignKey(...), or schema-JSON entry).Label every finding VERIFIED or INFERRED. No guessing.
When evidence is unavailable, write exactly:
NOT FOUND IN REPOSITORY
@Database/schema manifest first — it enumerates every entity/table in one place.schemas/*.json, schema.rb, schema.prisma); parse
them programmatically for tables/PKs/FKs instead of reading each entity end-to-end.@ForeignKey, @JoinColumn, ForeignKey(,
references, relationship() once across the data module to settle relationships fast.Show:
INFERRED relationships needing confirmation, NOT FOUND items.android-monorepo with common-database): authoritative source
is schemas/<DbName>/<version>.json — pick the max version; @Database(entities=[...]) lists
membership. Cache DBs frequently have no FKs — report that honestly with INFERRED shared-key links.@Entity + @JoinColumn/@ManyToMany give explicit, VERIFIED relationships.associations / schema.prisma.ForeignKey/relationship()/ManyToManyField are explicit.NOT FOUND IN REPOSITORY for tables rather than inventing a model.The detection tables let the agent auto-adapt to the repo's persistence stack — no per-repo editing required.