Dependency-kind contract

Tracker: pm-4020c5, pm-jkbqt8, pm-q6n8sj, pm-ouyq3n, pm-gos426, pm-flnefm, pm-rggtvd, and pm-vk7zek.

Dependency rows have one canonical stored spelling per relationship meaning. Command inputs remain compatibility-friendly: hyphens normalize to underscores and the aliases below are accepted, but pm create and pm update persist the canonical kind. Existing historical rows are never rewritten implicitly.

Canonical kind Accepted legacy aliases
blocked_by depends_on, depends-on
related related_to, related-to
parent child_of, child-of, epic
child parent_child, parent-child, task

epic and task are compatibility aliases, not item types embedded in the relationship ontology. New integrations should use parent or child and express the work classification through the item type field.

The SDK relationship registry is authoritative. canonicalizeRelationshipKind() rejects unknown spellings, while resolveCanonicalRelationshipKind() supports validation flows that need an undefined result. pm contracts publishes relationship_kind_contracts with canonical names, aliases, inverses, and ordering/hierarchy semantics.

Extension kinds may opt into both ordering and hierarchy semantics. Unless they declare an explicit traversal, ordering is the primary traversal and context role family; hierarchy direction and cardinality remain enforceable integrity constraints. This ordering-first precedence is shared by registry snapshots and direct-edge context explanations.

Dependency additions and removals share the same lossless input grammar. A bare value is an item id; structured removal input uses id=<id> plus optional canonical kind (with type accepted only as an input alias), source_kind, author, and ISO created_at selectors. Supplying all stored coordinates retires exactly one dependency row without deleting siblings that share an id and kind. Punctuation-shaped shorthand such as OTHER,related is rejected with dependency_flag_value_invalid on both --dep and --dep-remove, before prefix normalization can turn it into a dangling id. A removal selector that matches no stored row fails with dependency_remove_no_match and returns the unmatched selectors plus compact available identities. Re-adding a stored dependency identity is idempotent; if legacy storage contains that exact identity more than once, the same mutation collapses the touched copies to one without creating an edge-absence window. The stored identity includes normalized id, kind, source_kind, author, and creation instant, so provenance-distinct sibling rows remain independent.

Hierarchy integrity

The registry's hierarchy, hierarchyDirection, outgoing, and incoming fields are executable contracts. Scalar parent metadata and every registered hierarchy dependency spelling normalize into the same parent-to-child relation. That normalized relation powers pm list --parent, list/get tree projections, child rollups, graph traversal, graph analysis, graph audit, validate, and health. Extensions therefore gain hierarchy behavior by registering semantics; consumers do not hard-code kind names.

Create and update transactions reject a newly introduced hierarchy cycle, registry cardinality violation, or scalar/dependency direction contradiction before persistence. Existing debt remains readable and repairable. Active debt is gate-failing in graph governance and health; defects confined to terminal items are labeled legacy and are never mutated automatically. pm validate reports cycle, cardinality, and parent-divergence counts and exact bounded rows, while pm graph analyze exposes a compact hierarchy summary.

recurs_from has no alias: a later occurrence points to an earlier occurrence. It is persistent after both items become terminal and carries temporal identity, not execution precedence. supersedes keeps replacement semantics, while duplicate_of remains item-level record identity rather than an edge between distinct events. Local create, update, and update-many mutations compare the endpoint created_at values before persistence and reject equal or reverse chronology with dependency_temporal_order_invalid; explicit cross-workspace references remain external because their target metadata is not locally available.

Direction and actionability

blocked_by and blocks are inverse storage directions with identical scheduling meaning:

  • A --blocked_by--> B means A waits for B.
  • B --blocks--> A means A waits for B.

Readiness, pm next, context blocker summaries, pm list-blocked, downstream unblocks projections, and close-time auto-unblock use that shared interpretation. This applies to mixed-direction corpora without migrating existing rows.

Legacy observability

pm deps returns legacy_alias_counts for the workspace. pm graph audit returns the same field beside canonical profile.edges_by_kind counts, profile.edge_share_by_kind composition ratios, and the semantic_edges/semantic_edge_share context-preservation census. The semantic census counts discovered_from, incident_from, recurs_from, supersedes, and verifies over all deduplicated directed edges. Empty objects and zero shares are explicit, not omitted. These diagnostics are read-only; terminal history remains untouched until an explicitly governed migration is requested.

Assurance dependency_kind measurements canonicalize both the declaration and stored row before comparing. A declaration using related and one using the accepted related_to alias therefore measure the same edge population; alias debt remains separately observable through legacy_alias_counts.

Composition policy when adding relationships

Tracked by pm-r3o7x4.

Choose the kind that states the actual relationship. Use related for honest association; never relabel it as evidence or ordering merely to pass a gate. This workspace enforces profile.semantic_edge_share over canonical, deduplicated directed edges in both graph-composition and tracker-context-quality. The raw association count and declared-row share remain diagnostics. Neither imposes a second blocking bound on otherwise healthy graph growth.

Adding a provenance or verification edge cannot lower that composition ratio. Adding only association, hierarchy, implementation, or ordering edges can lower it; a mixed addition that increases semantic share passes the composition floor whenever its baseline passed. Ordering and hierarchy still have their own integrity constraints. Inspect the active workspace policy before bulk changes:

pm assurance show gate graph-composition
pm assurance show assertion graph-semantic-edge-share-floor
pm assurance run graph-composition --trigger ci --dry-run

The floor is a declared minimum, not a promise that every small dilution fails. Real growth is allowed above it; an associative-only pass crossing the floor is refused. Both terminal and active history contribute to the same denominator.


Dependency_kind_contract remote
Ein Problem melden