import { type WorkItemStatus } from './store.js'; /** * Work-item relations — typed item-to-item links (Todos v2 slice 3). * * Semantics (design decisions, locked): * - `blocks` and `duplicates` are directional (src blocks dst; src duplicates * dst). `relates` is symmetric and stored ONCE in canonical order — the * lexicographically smaller Todo id as src_id; writers normalize, readers * query one way. * - The `blocks` graph is a DAG: inserting an edge runs a BFS from dst over * outgoing blocks edges inside the insert transaction; reaching src refuses * with `relation-cycle` and the cycle path. * - `duplicates` is a pure marker — no lifecycle coupling. * - Any identified caller adds; removal is the relation's creator or the * operator. Self-relations are refused (belt: the table CHECK). * - `relation_added`/`relation_removed` events land on BOTH endpoints' audit * trails in the same transaction with `versionEffect: 'state'` (both resort). */ export type RelationKind = 'blocks' | 'relates' | 'duplicates'; export interface WorkItemRelation { srcId: string; dstId: string; kind: RelationKind; createdBy: string; createdAt: string; } export interface RelatedItemRef { id: string; title: string; status: WorkItemStatus; } export interface WorkItemRelationView { kind: RelationKind; direction: 'out' | 'in'; other: RelatedItemRef; createdBy: string; createdAt: string; } export type WorkItemRelationErrorCode = 'relation-cycle' | 'relation-forbidden'; export declare class WorkItemRelationError extends Error { readonly code: WorkItemRelationErrorCode; constructor(code: WorkItemRelationErrorCode, message: string); } /** * Add a relation between two Todos. `relates` is normalized to canonical order; * a re-add of an existing relation (either order for `relates`) is idempotent — * it returns the existing row and appends no events. A `blocks` edge that would * close a cycle refuses with `relation-cycle` and the offending path. */ export declare function addRelation(srcId: string, dstId: string, kind: RelationKind, createdBy: string): WorkItemRelation; /** * Remove a relation (creator-or-operator). `relates` accepts either endpoint * order. Returns false when the relation does not exist. */ export declare function removeRelation(srcId: string, dstId: string, kind: RelationKind, remover: { actor: string; operator: boolean; }): boolean; /** List a Todo's relations in both directions, each resolved with the other * endpoint's compact ref. `relates` is symmetric, so it always reads as * direction 'out' regardless of stored order. An unknown Todo reads as empty — * existence 404s belong to the route layer. */ export declare function listRelations(workItemId: string): WorkItemRelationView[]; /** True while ANY incoming blocks edge originates from a non-terminal (not * done/cancelled) item. */ export declare function isBlocked(workItemId: string): boolean; /** Batch form of `isBlocked` for list payloads: ONE query for the whole page, * never per-item. Returns the subset of the given ids that are blocked. */ export declare function blockedSet(workItemIds: string[]): Set; //# sourceMappingURL=relations.d.ts.map