Relationship inference
Opt-in contract
Inference is opt-in and records its evidence. Imports apply candidates only with --infer-relationships; pure-junction collapse additionally needs --many-to-many.
Name/type matching
Candidates use complete primary keys with dialect-equivalent types. Single keys match parent_key or singular_parent_id; composite keys match every key column in order.
Unique indexes
When a unique index exactly covers a valid child tuple, the inferred child cardinality is zero-or-one with unique_index_primary_key evidence. Otherwise it is many with column_name_primary_key evidence.
Junction recognition
Pure junction recognition requires exactly two foreign keys whose union is the complete primary key, without payload columns or unrelated constraints. The physical table remains while the logical entity becomes an association.
Evidence
Every candidate has an evidence kind, exact reason and confidence from 0 to 100. Text and JSON import reports include every accepted inference.
Ambiguity
Only a unique highest-scoring candidate is inferred. Equal best candidates produce a sorted ambiguity note and no relationship.
Pull acceptance
Pull plans mark inferred items with [?] inferred: and leave them unchecked even with --yes. Use --accept-inferred-relationships to select them; edited plan files still require that flag.
Reports
Reports carry accepted evidence plus relevant ambiguity and non-pure notes. Callers can inspect an inference report without mutating the logical model.
CLI examples
For example: bluep import ddl schema.sql --dialect postgresql --out model --infer-relationships --many-to-many --report inference.json.
Safety
Inference never mutates a model without an explicit option. It does not profile data, make fuzzy guesses, infer ternary relationships or apply suggestions on ordinary commands.