Skip to content

fit/predict: bind features by name, not position (v0.3) #21

Description

@mstrathman

Problem

fit and predict bind features positionally. The trained model stores no
feature identity, so predict trusts the caller to pass the same columns, in the
same order, that fit learned from. A count mismatch fails loud
(PREDICT_ERR_SCHEMA: "predict got N features but the model expects M"), but an
order mismatch does not: same count, wrong order, and you get a confidently
wrong prediction with no error.

The leading-name sugar just shipped (fit('churn', f..., label) mirroring
predict('churn', f...)) makes the two calls read in parallel, which helps a
human eyeball that the feature lists line up. It does not make the binding safe:
nothing enforces that they do.

Why it matters

This is the last silent-failure path in the tabular surface, and it contradicts
the project's fail-loud contract. A reordered SELECT, a refactor that swaps two
columns, or an ORM that emits columns in a different order than training all
produce a wrong answer that looks fine.

Proposed direction

Bind features by name/role, captured at fit time and checked at predict
time. Sketches, not yet decided:

  1. Store feature names, check on serve. fit synthesizes f0..fN
    internally today; instead capture the caller's column names where available,
    or an explicit '{"features":["tenure","spend",...]}', and persist them in
    the model. predict binds by name when given named inputs and errors on an
    unknown or missing feature instead of trusting position.
  2. Predict from a row. A form that pulls features from a record by key, e.g.
    predict('churn', json_object('tenure', tenure, 'spend', spend)) or a
    table-valued predict_rows(model, ...), so binding is by name.
  3. Keep positional as the fast path, name-checked when identity is available;
    never silently accept a positional list that disagrees with a stored schema.

Acceptance

  • A model carries enough feature identity that a reordered predict fails loud
    (a distinct PREDICT_ERR_*) rather than scoring the wrong columns.
  • Positional fit/predict still works for the common case.
  • The stored feature schema deserializes with the same bounds-checked,
    deterministic guarantees as the rest of the registry.
  • Adversarial tests: reordered features, renamed features, a missing or extra
    named feature, and an old model with no stored schema (back-compat).

This changes the model format, so it targets v0.3. It is the robustness half
of the leading-name ergonomic change (which was the readability half).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions