# 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.