Item relations
Tasks and memories in a project can point at each other. Relations use the project-local item numbers (#284) and never cross a project boundary. Documentation pages are not part of the relation graph yet.
Kinds
| Kind | Direction | How it is created |
|---|---|---|
references |
source → target | Derived from #N in a task's content or a published memory's text |
related |
symmetric | Explicitly, with item_action relate; stored once, lower number first |
implements |
task → memory | Explicitly |
supersedes |
newer memory → older memory | Explicitly; cycles are rejected |
blocked_by |
waiting task → prerequisite task | Explicitly; see below and #346 |
discovered_from |
task → the task or memory it was found from | Explicitly; information only |
Explicit relations are removed with item_action unrelate. Neither needs a version: a relation changes neither item. Adding a relation that exists, or removing one that is gone, succeeds and changes nothing.
Blockers
blocked_by is the one kind that affects work: the waiting task cannot start before the prerequisite is done (#346). It never changes a status. A blocker links two tasks of one project, never starts from a closed task, never runs between a task and its own ancestor or descendant, and never closes a cycle; the rejection names the path it would close. Blocker changes and claims of one project take a project-scoped lock (RelationService.LockTasks: sp_getapplock on SQL Server, SQLite serialises writers), so opposite blockers added at once cannot both commit. Each added or removed blocker is recorded in the waiting task's history (blocker_added, blocker_removed) without a new version.
Derived references
RelationService.ReconcileMarkdown runs after a task is saved and after a memory revision is published. It replaces only the Markdown-derived references rows of that one source, so it is idempotent and never touches explicit relations. For memories it records the published revision the references came from. Pending and rejected revisions never reconcile, so an unreviewed proposal cannot change the graph.
Visibility
Relations keep their endpoints when an item is archived or expires. Reading them (the direct relations in item_get, the relation panel) resolves each endpoint's title with the reader's own permissions and silently drops what the reader may not see. A hidden endpoint reveals neither its title nor that a relation exists. Restoring an item makes its relations visible again.
Creating an explicit relation needs a source the caller may edit and a target the caller may read. A relation never grants access and never changes the status of the items it connects.