Repository navigation
Description
A single-valued relationship reads as a property, not as a graph edge. "This task is assigned to Sam" belongs on the task's form next to status and due date — not behind a Relationships modal. Promote One-cardinality relationships to first-class form fields, and let the modal own everything not already on the form.
Proposed behavior
On the property form — every relationship whose LOCAL end is One renders as a labeled field with an autocomplete control:
- Label from the local end's name (
assignee→ "Assigned to"; inbound groups take the declaration'sreverse_name) - Autocomplete searches candidate nodes of the declared
target_type, matching on the node's resolved title (forpersonthat istitle_template: "{first_name} {last_name}", so it must match the interpolated title, not a raw field) - Single-select: picking a value replaces any existing edge
- Clearable (unassign), since these relationships are optional (
required: None)
In the Relationships modal — render only relationships NOT already surfaced on the form, so the two views partition rather than duplicate. With assignee and project promoted to fields, a task's modal shows blocks, relates_to, duplicates and any Many inbound groups.
Why this shape
Cardinality already tells us the verb (see #2805): One → assign/set, Many → add. This issue takes the next step — a One relationship doesn't just deserve a different verb, it deserves a different surface. Users think of a task's assignee as a field of the task, and reaching it through a relationship browser is friction for the single most common edit.
The partition rule keeps one edge in exactly one place, so there's no question of which control is authoritative or of two views disagreeing after an edit.
Implementation seam
typed-form-shell.svelte already loads the full relationships view (both directions) for its gate and reduces it to a boolean:
loadNodeRelationshipsView(id).then((view) => {
if (nodeId === id) hasRelationships = view.groups.length > 0;
});The data needed is therefore already fetched at the right layer — it currently just isn't rendered. The shell owns the chrome shared by every schema-driven form, so promoting One groups to fields belongs here rather than in each form, and the same change covers task, person, and every generic schema form at once.
Writing an edge from a promoted field follows #2805's transposition rule: for an inbound group, create as create_relationship(target_id, <forward name>, node_id), because the stored relationship_type is always the declared forward name.
Dependencies
- reverse_cardinality is declared and surfaced but never enforced, so a 'one' target end accepts unlimited edges #2804 —
reverse_cardinality: Oneis unenforced at the store layer today, so a single-select field is a fiction any other writer can break. The replace-vs-reject decision there defines what re-assigning does. - Task relationships panel can't assign a person: empty inbound groups are dropped, so cardinality never drives the affordance #2805 — establishes cardinality (not direction) as what drives the affordance, and the transposition rule for inbound writes. This issue changes WHERE a
Onerelationship is edited; Task relationships panel can't assign a person: empty inbound groups are dropped, so cardinality never drives the affordance #2805 changes HOW it is offered.
Acceptance criteria
- A relationship whose local end is
Onerenders on the property form as a labeled autocomplete field - The autocomplete searches nodes of the declared
target_typeby resolved title (respectingtitle_template), not by raw content or a single field - Selecting a value creates the edge with transposed arguments for inbound groups; the far node's panel reflects it
- Selecting a value when one already exists replaces it (per reverse_cardinality is declared and surfaced but never enforced, so a 'one' target end accepts unlimited edges #2804's decision)
- The field can be cleared, removing the edge
- The Relationships modal excludes any relationship already rendered on the form, with no duplication between the two surfaces
- A task shows "Assigned to" and "Project" on its form;
blocks/relates_to/duplicatesremain in the modal - The relationships gate still hides the modal trigger when nothing is left for it to show
- Forms for types with no
Onerelationships are unchanged
Open questions
- Should a promoted field show inline (name only) or with richer identity (avatar, secondary line)? Affects how much the autocomplete result needs to carry.
- Ordering of promoted relationship fields relative to scalar property fields — grouped together, or interleaved by schema declaration order?
Activity
malibio commentedon Sep 25, 2026
Baseline — Frontend: 269 files / 5313 tests passed (one timing flake on the first run passed on re-run)