KeiSeiKit-1.0/skills/schema-design/phase-2-entities.md
Parfii-bot 0be354a920 KeiSeiKit-public — clean state
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.
2026-05-01 12:09:03 +08:00

3 KiB
Raw Permalink Blame History

Phase 2 — Entities + relations matrix

Collect the entity list (typed) once, then click the relations matrix. This is the second (and last) phase that accepts typed input.

2a — Ask for entities (plain message, NOT AskUserQuestion)

List the entities (tables) and for each a short comma-separated field list. One entity per line, format: <Entity>: field1, field2, .... Example:

User: email, name, created_at
Organization: name, plan
Membership: user_id, org_id, role

315 entities is typical. Keep it short — we'll refine fields in Phase 3.

Parse the reply into ENTITIES = [{name, fields: [...]}, ...]. Validate:

  • Entity names are PascalCase (normalize if user types user_profileUserProfile, record normalization in state).
  • Each entity has ≥1 field.
  • No duplicate entity names.
  • If parse fails → re-ask once with a corrected example.

2b — Relations matrix click (AskUserQuestion, multi-select)

For each UNORDERED PAIR of entities (A, B), ask one multi-select row. Skip pairs the user hasn't mentioned any cross-reference for (heuristic: if A's fields include b_id or B's fields include a_id, or the user's intake paragraph mentions both).

Build ONE AskUserQuestion call with up to 5 questions. If the entity count yields > 5 candidate pairs, batch into multiple calls (still counts toward the ≥1 AskUserQuestion minimum).

Per-pair question template:

{
  "question": "Relation between <A> and <B>?",
  "header": "<A>↔<B>",
  "multiSelect": false,
  "options": [
    {"label": "None",              "description": "No direct FK; entities are independent"},
    {"label": "One-to-one",        "description": "A.b_id UNIQUE FK to B.id (or vice versa)"},
    {"label": "One-to-many (A→B)", "description": "B.a_id FK to A.id; one A has many B"},
    {"label": "One-to-many (B→A)", "description": "A.b_id FK to B.id; one B has many A"},
    {"label": "Many-to-many",      "description": "Requires a junction table; skill will auto-name it <A><B>"}
  ]
}

Store the result in ENTITIES as .relations = [{from, to, kind}, ...].

2c — Auto-generate junction tables

For each pair marked Many-to-many, append a synthetic entity to ENTITIES:

<A><B>:
  <a>_id FK → <A>.id  (ON DELETE CASCADE)
  <b>_id FK → <B>.id  (ON DELETE CASCADE)
  PRIMARY KEY (<a>_id, <b>_id)
  created_at TIMESTAMPTZ DEFAULT now()

Names must be deterministic (alphabetical order: OrganizationUser, not UserOrganization, for pair (User, Organization)). Record the rule in state so Phase 3 renders it consistently.

Verify-criterion

  • ENTITIES has ≥1 entry after parse.
  • Every relation in ENTITIES[*].relations references two distinct existing entities.
  • Every Many-to-many has produced a junction entity.
  • No entity is orphaned (zero relations AND not mentioned in INTAKE) — warn the user with "Entity X has no relations; keep it?" (NO DOWNGRADE: offer keep / drop / add relation as follow-up click).