# ADR-0011: Foreign-key column type compatibility ## Status Accepted ## Context Two of our user-facing types (per ADR-0005) carry insert-time auto-generation semantics that only apply on the primary-key side of a relationship: - `serial` is `INTEGER PRIMARY KEY` with rowid-alias / auto-increment behaviour. - `shortid` is `TEXT` populated client-side with a 10–12 char base58 random value when no value is supplied. A foreign-key column referencing such a primary key does *not* auto-generate values — it stores the looked-up value of the primary key. Asking the user to declare the FK column with the same user-facing type as the PK would be wrong: `serial` on the FK side would imply the FK column has its own auto-increment counter (it doesn't), and similar for `shortid`. Without this rule, the future FK declaration grammar (C3, C4) would either generate incorrect DDL or rely on the user to remember to use a different type on the FK side — an easy foot-gun. ## Decision Define `Type::fk_target_type(self) -> Type` returning the appropriate user-facing type for a foreign-key column that references a primary key of `self`: | User-facing type | `fk_target_type()` | |------------------|--------------------| | `text` | `text` | | `int` | `int` | | `real` | `real` | | `decimal` | `decimal` | | `bool` | `bool` | | `date` | `date` | | `datetime` | `datetime` | | `blob` | `blob` | | `serial` | **`int`** | | `shortid` | **`text`** | All types other than `serial` and `shortid` are identity mappings. The two exceptions strip the auto-generation semantics by mapping to the underlying value type. ## Consequences - The future FK declaration grammar uses `fk_target_type()` in one of two ways: - **Auto-typing** — when the FK column does not yet exist and `--create-fk` is given (or whatever the equivalent flag becomes), the column is created with `pk_column.ty.fk_target_type()`. - **Validation** — when the FK column already exists, its type is compared against `fk_target_type()`. A mismatch yields a clear diagnostic ("Customers.id is `serial`; the FK column should be `int`, not `text`"). This is the teaching moment ADR-0009's design philosophy targets. - Adding a new user-facing type forces an explicit decision about its `fk_target_type()`. The type system is therefore closed under FK declaration. - The advisory feedback module (foreshadowed in V4 and the hint surface) can use the same mapping to surface recommendations during command typing — e.g. "you used `serial` for an FK; conventionally `int` is the right fit here." This is *advice* not gating, consistent with ADR-0009's separation of required grammar from optional guidance. - `fk_target_type()` is implemented and tested *before* the FK declaration grammar lands, so the FK iteration is grammar work only.