Relationships
| As of | |
|---|---|
| Version | 1.3.0 |
| Kind | prd, for everyone |
| Length | 1,738 words, about 8 min |
| Tags | prdfeaturesynergy |
A relationship is a first-class stored link: its own id, an optional label, an optional type, tags, and a snapshot of the target's version and revision when it was made. A Sneak Peek Link is how a relationship appears in a document; the relationship is the data.
Single and list relationships
Both live in one table.
- Single: one row with
from,to, label, type and the version snapshot. - List: a header row (label, type, tags, no
to) plus member rows, each withparent_id, its ownto, a position and its own snapshot. "Why? Guilt, new ideas, perfectionism" is one list of three.
The same pair of entities can be linked more than once, with different labels.
Labels
A label is optional free text that says what the target answers from the source's point of view: "Am I lying to myself?", "Why?". The author decides whether a link has one. A labeled link always renders as a minimal link (see Sneak Peek Link).
Types: references and associations
A link points from the dependent entity to what it relies on. A plan built from a PRD links plan derived-from PRD.
- A reference derives meaning from its target. It goes stale when the target changes, and it blocks the target's delete.
- An association only connects. It never goes stale and never blocks anything.
- An untyped link is a reference.
Starter vocabulary (MVP), each with an inverse name for backlinks:
| Kind | Types |
|---|---|
| Reference | derived-from ↔ source-of, cites ↔ cited-by, supported-by ↔ evidence-for, explained-by ↔ explains, exception-to ↔ has-exception |
| Association | inspired-by ↔ inspiration-for, next ↔ previous, related-to, contradicts, defined-by ↔ defines, violates ↔ violated-by |
| Special | superseded-by ↔ supersedes: a reference from the old entity to each replacement, any number of them. Creating the first one marks the source superseded, after a confirmation that says how many entities reference it; removing the last one makes it active again |
User-defined types are post-MVP. Types and tags can be deprecated: existing uses stay, new ones are refused. A type in use can't be deleted.
Violations and exceptions
Decided 2026-11-01 (Pratik), from dogfooding Synergy on Arkive: pointing at a rule and saying "this breaks it" needed a directed type, because contradicts is symmetric and can't say which side is at fault.
violates↔violated-byis an association from the offender to the rule, principle or decision it breaks. The offender's meaning doesn't come from the rule, so it never goes stale and never blocks deleting the rule (dropping the rule is one fair way to settle a violation), and it can't be refused as a cycle: a spec can violate a planning decision in a plan derived from that spec. The link still shows the rule's status.exception-to↔has-exceptionis a reference from an exception to the rule it limits ("Synergy's queries see tombstones" is an exception to "every query filters tombstones"). An exception takes its meaning from its rule, so it goes stale when the rule changes, which prompts a check that the exception still holds, and the rule can't be deleted while it has one. The exception's own content says where it applies; the link carries no scope, only the optional label.- An open violation is a live
violateslink. There is no status. Resolving one (the offender was fixed, or the claim was wrong) is unlinking it. Waiving one (breaking the rule is accepted here) is changing its type toexception-to: the same relationship, so its mentions keep working, and the outbox records the change. - A type change from an association to a reference takes a fresh snapshot of the target. The author looked at the rule when they waived, so the link doesn't open with a dot left over from before. The cycle check runs as on any type change.
- Listing. An entity's links can be filtered by type in either direction: "what violates this rule?" and "what does this plan break?". Every module's links can also be listed by type across the whole graph, so one list shows every open violation, whichever module it comes from (plans, chats, notes). Issue tracking on violations (a status, who resolved it and why, a history of closed ones) is a post-MVP candidate; a waiver would still end as an
exception-tolink, with the closed violation kept beside it.
Both gaps were closed with seeded rows, which is all a new type costs, so the deferral of user-defined types holds. The reasons and the rejected alternatives are in Violations and exceptions.
Cycles
References may never form a cycle; associations may. With no reference cycles, the delete rule never deadlocks.
- The link picker greys out entities that already reach the source through references, with "would create a loop".
- The server checks again on create, under a per-user lock, because bots and the API don't use the picker. A refused link names the chain and suggests an association.
- How the lookup scales (recursive query, depth cap or closure table) is for system design.
So two entities never reference each other, directly or through others. When both want to talk about one dependency, they mention the same relationship (see Mentions) instead of adding a reverse reference.
Mentions
A doc that explains why project X started (A) and the doc about project X (B) share one dependency: B is explained-by A. Both want to point at it, several times. That is still one relationship.
- A mention is a place in an entity that shows a relationship. A relationship can have any number of mentions, in its source and in its target. A may mention it three times and B once. There are three kinds:
- Direct mention: shows the link itself as a Sneak Peek Link (minimal, inline, card or preview). The display type belongs to the mention.
- Alias mention: the author's own words carry the link.
- Cited mention: the link is held outside the content, in a field (such as
related-to) or a links panel. Creating a link through the API without a content mention creates a cited one.
- Mentions in the target read with the inverse name: in A it reads "explains", in B "explained by".
- Staleness, the delete block and the cycle check follow the stored direction only. B sees a dot when A changes. A's mentions of B never go stale.
- A relationship lives while its source mentions it in any of the three ways. Unlinking, or removing the source's last mention of any kind, deletes it. Removing the target's mentions never does; they are only a view of it.
- When the relationship goes, the target's mentions show No longer linked. The text stays, the link is disabled, and the copy is neutral, because B may have been deleted or simply stopped linking. Synergy never edits A or bumps its counters. A's author dismisses the mention (the smart link goes, the plain text stays) or relinks it.
- The deleted relationship is a tombstone (soft delete with actor and reason). Undo, or restoring a deleted B, brings back the same relationship and A's mentions come back with it. Linking the pair again later creates a new relationship with a fresh snapshot; A's author is offered "relink" to point old mentions at it.
Anchored links
- Source side. A user selects text (a word, a sentence, a paragraph) and links it: an alias mention, a mark in the source's content carrying one relationship id. A mark carries one link and marks don't overlap, but one link can have many marks.
- Passages. A link can point at part of its target, a passage: a block (a paragraph, heading or list item, by a stable block id) or words and sentences inside one, marked by an anchor in the target's content and stored on the relationship as
to_anchor(anchor id plus a quote of the text). See Anchors in content. - Staleness stays per entity, never per block.
- A referenced passage is protected like a referenced entity. The editor marks passages that have incoming references. Deleting one stops and offers three choices:
- Keep the passage.
- Save as a new version. The passage goes, with an optional change note. Referencers get the red dot, and their link shows "passage removed" until its author re-anchors it, points it at the whole entity, or unlinks.
- Supersede. The edit becomes a new entity; the old one keeps the passage intact.
- Editing a referenced passage asks "3 links rely on this passage. Is this a new version?" The author decides whether it is a rephrase (revision) or a change in meaning (version).
- Synergy checks every save, autosave included: a save that removes a referenced passage is refused with
passage_referencedand the list, unless it is a new version. The editor catches it first; the server is the backstop for bots and the API, as with cycles. The check costs the size of the change, not of the document, so it stays fast at autosave rates. - Associations into a passage don't protect it. Deleting the passage deletes them; the source's leftover mark renders as plain text. Synergy doesn't write the source's content, so it moves only the source's
updated_at(actor: system, with the reason), never its revision or version: a link changing state isn't an edit, and the source's own referencers get no false "edited" dot.
Disabled modules
A relationship into a module the user no longer has stays, and renders locked. While locked nothing changes on its own. The user may unlink from the side they can open, which is permanent. No new links into a module the account doesn't have; the API refuses too.
Terms: link and mention
Decided 2026-10-30 (Pratik). The lifetime rules that follow from these terms are in Mentions and link lifetime.
- Link (relationship): the stored connection from a source to one target, or to several ordered targets (a list link). Its kind comes from its type: reference or association; untyped is a reference. Its target is a whole entity or a passage (Anchors in content).
- Mention: a place in the source or the target that shows a link. Either side may mention a link any number of times.
- Direct mention: shows the link itself, as minimal, inline, card or preview. Without a label, a minimal direct mention shows the target's title.
- Alias mention: the author's own words carry the link.
- Cited mention: the link is held outside the content, in a field (such as
related-to) or a links panel. A field is a view of the source's cited links of that type.