ID |
|
|---|---|
Status |
Backlog |
Bucket |
architecture |
Priority |
3 |
Theme |
model-cleanup |
Created |
2026-09-03 |
Updated |
2026-09-03 |
A producing field’s result is a field-grain fact; it is unioned into a type-grain binding instead
A graph type is bound to a catalog table by carrying @table, and by nothing else. A field carrying
a producing directive has a result, and that result is a fact about the field. Where a producing
field’s result reaches a type that is table bound, the two must agree; where it reaches a type that
is not, the result is simply what the type is backed by. Those are three separate statements and the
schema currently makes one relation out of the first two.
intent_resolved_type_binding is the union of intent_bound_table, which is the @table
population, and intent_routine_return_binding, which is where a field’s @routine chain lands. So
a field-grain fact becomes a source of type-grain boundness, and fifteen views read the result
without any way to say which of the two they meant.
What is wrong, precisely
The grain. A type can be reached by several producing fields, so the result is keyed by the producing field and not by the type. Unioning it into a type-keyed relation loses that, and the arity the union then computes is counting two different kinds of thing.
The disagreement is reported as ambiguity. A type carrying @table(name: "film") whose chain
lands on films_for_actor becomes two rows with candidates = 2. Eleven of the fifteen readers
filter candidates = 1, so the type silently becomes unbound. That is a compatibility violation
between a field’s result and its type’s binding, and it should be a named defect rather than a
disappearance. ReferenceStepTargetTest and RoutineReturnBindingTest pin today’s reading.
A routine’s result is not a table. It is a user-defined type that jOOQ models as a table so it
can appear in a query; unjoined it is a Record. @service and @externalField results are
likewise shapes that may or may not be a TableRecord. intent_type_backing argues the opposite in
its own comment, that a routine chain’s return binding "stands for one exactly as a written @table
does", and that is the step to revisit.
The producing directives have two partial spellings and no whole one.
intent_field_payload_producer covers SERVICE, DML and ROUTINE and omits @externalField.
intent_field_producer_reference covers @service and @externalField and omits @routine. So
the set of directives that produce a field’s value exists twice and completely nowhere, which is why
a reader wanting "what does this field produce" has no relation to ask.
@reference moves where the result lands. A reference step can pick a result up and join it
onward, so what reaches the type is the end of the chain rather than the raw call result. Any
remodelling has to keep that distinction rather than collapse it.
What a fix looks like
One relation over all four producing directives at the coordinate grain, carrying what the field
produces. intent_type_backing’s table arm reading the `@table population alone. The agreement
between a field’s result and its type’s binding stated as a defect, and the agreement between two
producing fields reaching one type stated the same way. Then intent_resolved_type_binding has
nothing left to be and its fifteen readers each say which question they were asking.
Two readers are known to be relying on the conflation and need the field grain instead rather than a
repointing: intent_field_column_scope’s `PARENT_BINDING arm, which resolves a child column of a
routine-returned type through a type-grain binding that was never really a table binding, and
intent_field_separate_fetch, which asks whether a type is bound at all.
Why it is not R876’s
R876 is remodelling the type-to-table binding itself, and graphitron_tabletype is the @table
population captured with a key. That work needs none of this: the nodehood family reads the @table
arm or should be reading it, since @node only takes effect on a type that also carries @table.
This item is what the remaining union readers wait for, and it is a different subject.
Measured
On the captured consumer schema (sis-2026-08-31): 635 types have a binding, none of them from
anything but @table, and there are zero @routine applications. The whole second arm is
fixture territory today, so this is a correctness and modelling item rather than a repair.