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

82 lines
3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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_profile`
`UserProfile`, 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:
```json
{
"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).