Single-commit clean baseline after security scrub of niche-tells, project codenames, internal jargon, and contributor-email leaks. Contents: - 100 Rust crates (_primitives/_rust/) - 37 agent manifests (_manifests/) + generated specs (_generated/) - 67 user-invocable skills (skills/) - 33 hooks (hooks/) - Composition blocks (_blocks/) - Documentation (docs/, README.md) - TS adapter packages (_ts_packages/) - Assembler (_assembler/) - Roles (_roles/) - Templates (_templates/) - Forgejo CI (.forgejo/) Author: Denis Parfionovich <info@greendragon.info> License: see LICENSE.
4.9 KiB
4.9 KiB
Phase 3 — Generate SQL DDL (tables, indexes, FKs, constraints)
Emit a full db/schema.sql file based on DB, ORM, STYLE, and
ENTITIES. Then ONE AskUserQuestion to review/revise.
3a — Pick primary-key strategy (inline, no AskUserQuestion — deterministic)
- Postgres 17:
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY(SQL-standard, PG 10+; avoid legacySERIAL). Seedb-postgres.md. - SQLite:
id INTEGER PRIMARY KEY(rowid alias; autoincrement discouraged unless monotonic guarantee required). Seedb-sqlite.md. - MySQL:
id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY.
If the user's fields include id already, respect it; otherwise prepend
the surrogate PK. Record the choice in state for Phase 4 traceability.
3b — Per-entity DDL generation rules
For each entity in ENTITIES:
- CREATE TABLE:
CREATE TABLE IF NOT EXISTS <snake_case_name>(plural unless user named it singular — default to pluralize:User→users,Organization→organizations; junctionOrganizationUser→organization_users). - Columns: infer SQL type from field name + heuristics:
*_id,id→BIGINT(Postgres/MySQL) /INTEGER(SQLite).*_at,created_at,updated_at→TIMESTAMPTZ(PG) /DATETIME(SQLite) /TIMESTAMP(MySQL),DEFAULT now()(PG) /DEFAULT CURRENT_TIMESTAMP(others).email,*_email→TEXT/VARCHAR(320)(RFC 5321 limit) +NOT NULL+CHECK (email LIKE '%@%')(cheap sanity, real validation is app-side).*_count,*_amount→INTEGERorNUMERIC(18,2)(money); default0.is_*,has_*→BOOLEAN NOT NULL DEFAULT false.- Freeform →
TEXT NOT NULLunless user said "optional".
- Timestamps default: unless user opts out, add
created_at+updated_atto every non-junction entity. - Soft-delete: if
SCALE = Production multi-replica, adddeleted_at TIMESTAMPTZ NULL+ partial index (PG only) onWHERE deleted_at IS NULL.
3c — Foreign keys
For each relations entry (kind ≠ None):
REFERENCES <parent_table>(id) ON DELETE <ACTION>.- Default
ON DELETEaction: junction →CASCADE; one-to-one →CASCADEfrom owning side; one-to-many →RESTRICT(safer default; explicit cascade is a product decision). Seedb-migration-hygiene.md. - Mandatory FK index: every FK column gets
CREATE INDEX IF NOT EXISTS idx_<table>_<col> ON <table>(<col>);. Unindexed FKs = join explosion (seedb-postgres.md"Forbidden").
3d — Indexes + constraints
- Unique:
email,slug,username, any field user markedUNIQUE→UNIQUEcolumn constraint. - Composite indexes: for junction tables, PK is composite; add a reverse-order index if both directions of lookup are common.
- Check constraints:
CHECK (status IN (...))for enum-ish text fields;CHECK (amount >= 0)for non-negative numerics. - Triggers: if
updated_atpresent ANDDB = Postgres, emit aCREATE OR REPLACE FUNCTION set_updated_at()+CREATE TRIGGERper table. SQLite usesAFTER UPDATEtriggers; MySQL usesON UPDATE CURRENT_TIMESTAMPin the column definition.
3e — Emit the file
Write db/schema.sql with a top-level comment:
-- Generated by /schema-design — <YYYY-MM-DD>
-- DB: <DB> ORM: <ORM> Style: <STYLE>
-- Entities: <N> Relations: <M>
-- Edit freely; Phase 4 will package the first migration from this file.
Print the first ~40 lines of the file inline in chat (for review) plus a
<path>:<total lines> footer. Full contents live on disk.
3f — Review click (AskUserQuestion)
{
"questions": [
{
"question": "Schema review — accept or revise?",
"header": "Review",
"multiSelect": false,
"options": [
{"label": "Accept — proceed to Phase 4", "description": "Schema is correct; package into a migration"},
{"label": "Revise entities (return to Phase 2)", "description": "Add/drop entities or fix a relation"},
{"label": "Revise types (edit db/schema.sql)", "description": "Skill will emit a 1-AskUserQuestion per-column-type fix loop"},
{"label": "Revise indexes (edit db/schema.sql)", "description": "Skill will emit a 1-AskUserQuestion per-index fix loop"}
]
}
]
}
If the user picks Revise, loop the relevant sub-step; then re-emit and re-ask. Max 3 revise loops before the skill asks whether to accept-as-is or escalate to code-implementer handoff (NO DOWNGRADE).
Verify-criterion
db/schema.sqlexists on disk and is non-empty.- Every FK has a companion index.
- Every non-junction entity has a PK +
created_at. - If
ORM = DrizzlewithSTYLE = code-first: also emitdb/schema.tsalongsidedb/schema.sql(Drizzle introspection artefact; seedb-drizzle.md). ForORM = SQLx: record in state that Phase 4's migration will also be picked up bysqlx migrate run. - Accept click sets
DDL = "db/schema.sql"and hands off to Phase 4.