Skip to content

fix: hook re-registration silently adopts an existing feature table, even with a mismatched column shape #141

@rorybyrne

Description

@rorybyrne

Problem

feature_tables has a global UNIQUE(hook_name) constraint — one physical features.<hook> table exists per hook name, node-wide. When a convention registers a hook whose name already has a table, CreateFeatureTables catches the ConflictError and logs a warning (domain/feature/handler/create_feature_tables.py), silently adopting the existing table.

That's fine when the two hooks genuinely share a shape, but nothing checks this. If a second convention registers a hook with the same name and a different declared column schema, its writes target a table whose columns don't match its declaration:

  • inserts with extra columns fail at the DB layer;
  • inserts missing columns silently produce rows that don't conform to the hook's declared FeatureSchema;
  • the /data/{schema}/{feature} manifest reports the catalog's stored feature_schema, which only reflects whichever hook registered first.

The read side was hardened in #139 (feature streams and counts are now scoped to the requested schema via a records join), but the write-side registration hazard remains.

Options

  1. Reject loudly on shape mismatch: on conflict, compare the incoming FeatureSchema against the catalog row; identical → reuse (current behavior, now safe), different → fail convention registration with a clear error. Minimal change, keeps shared tables for genuinely shared hooks.
  2. Schema-scope physical tables (e.g. key feature_tables on (schema_id, hook_name)): removes cross-schema sharing entirely, at the cost of a migration and changes to the feature write path and the /data/ read joins.

Option 1 is the cheap, safe fix; option 2 is the cleaner model if shared hook tables turn out to have no intentional use case. Needs a design call on whether cross-schema hook sharing is a feature or an accident.

Context

Surfaced while reviewing PR #139 — the read-side scoping bug fixed there (9c95694) had this registration behavior as its root cause.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingdesign-neededNeeds architectural discussion before implementation

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions