Edge resolution: a self-referential base edge is reverse for arrays and for non-pointer scalars

entity.parent_id -> entity matches every type pair in both directions, and the
discovery loop kept forward whenever reverse also matched — so an array
include compiled as `parent.parent_id = child.id` and every nested list read
back empty. An array can only be the reverse side; a scalar is forward only
when the property IS the pointer (`parent` for `parent_id`), otherwise it is
the single child that points here (a cover attachment). 1286 library tests
pass. Pinned by api's accounting suites on branch activity-task-assignment-lines.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AvkGU4kQ4wsqxHFpXtZKZM
This commit is contained in:
2026-09-02 11:41:34 -04:00
parent b699061f2f
commit 2a972d8a79

View File

@ -500,6 +500,22 @@ impl Database {
let is_reverse = p_def.hierarchy.contains(&rel.destination_type) let is_reverse = p_def.hierarchy.contains(&rel.destination_type)
&& c_def.hierarchy.contains(&rel.source_type); && c_def.hierarchy.contains(&rel.source_type);
// A self-referential edge on a shared ancestor table (entity.parent_id -> entity) matches
// both ways for every type pair. An array can only be the reverse (one-to-many) side; a
// scalar is the forward side only when the property IS the pointer (`parent` for
// `parent_id`) — otherwise it is the single child that points here (a cover attachment).
if is_forward && is_reverse {
let stem = rel
.source_columns
.first()
.map(|c| c.trim_end_matches("_id"))
.unwrap_or("");
let names_pointer = !stem.is_empty() && prop_name == stem;
if is_array || !names_pointer {
is_forward = false;
}
}
// Structural Cardinality Filtration: // Structural Cardinality Filtration:
// If the schema requires a collection (Array), it is mathematically impossible for a pure // If the schema requires a collection (Array), it is mathematically impossible for a pure
// Forward scalar edge (where the parent holds exactly one UUID pointer) to fulfill a One-to-Many request. // Forward scalar edge (where the parent holds exactly one UUID pointer) to fulfill a One-to-Many request.