Skip to content

Relationships

Entities rarely stand alone. Componecat connects them with relationships — the parent/child hierarchy, dependencies between services, and any custom connection your organization wants to model. Every relationship is directed and has a kind that governs how it behaves.

Like entity kinds, relationship kinds are configurable. Componecat seeds a hierarchical parent-of kind and dependency kinds such as depends-on, and you can add your own. Each kind controls:

  • Cardinality — one-to-many or many-to-many.
  • Cycle policy — whether cycles are forbidden, allowed with a warning, or allowed outright.
  • Delete policy — whether deleting one side is blocked, cascades, or is allowed while the relationship is dropped.
  • Labels — the wording shown for each side (for example “depends on” / “is used by”).
  • Hierarchy — whether the kind contributes to the catalog tree.
  • Dependency — whether impact analysis follows the kind. The seeded depends-on kind is a dependency kind; mark your own kinds on their Relationship Kinds page.
  • API provider — whether the source provides an API that the target consumes.

You can also restrict which entity kinds may participate on each side of a relationship, so, for example, only a Component can depend on a Resource.

A relationship can carry attributes — a small map of named values that describe the edge itself rather than either entity: the protocol a service calls another over, the sla it relies on, or free-form notes. Attribute keys start with a letter and contain only letters, digits, - and _; a relationship holds at most 50 attributes.

By default a relationship kind accepts any attributes. A kind can instead define an attribute schema — a list of attribute definitions, each with a key, a type, an optional display name and description, and whether it is required. Attribute types and options work exactly like custom fields on entity kinds (text, number, boolean, date, URL, select, multi-select, JSON, and so on). Once a kind defines attributes:

  • Required attributes must be present on every new relationship of that kind.
  • Values are validated against their type and options — a select attribute only accepts one of its listed options.
  • Only defined attributes are accepted — an attribute the schema doesn’t list is rejected.

A validation failure names the attribute at fault (for example attributes.protocol), so you can fix the input directly. Changing a kind’s schema doesn’t rewrite existing relationships; the new rules apply the next time a relationship of that kind is created or its attributes are written. When a descriptor declares attributes that break the schema, that sync reports a warning and leaves the relationships of that kind unchanged.

When the same relationship is asserted by more than one source, attributes follow the same rule as the description: a weaker source (for example an inferred re-observation) never overwrites the attributes a descriptor set.

In an entity’s Relationships tab, each relationship shows its attributes as compact chips under the related entity (for example Protocol: grpc). The Add Relationship dialog, and the Edit button on a manual or descriptor relationship, show one input per attribute the kind defines — required ones are marked with an asterisk, and the dialog won’t save until they are filled in. For a kind without a schema, the dialog lets you add any key/value pairs instead. Inferred relationships can’t be edited by hand. Editing a descriptor relationship warns you that the next sync may overwrite the change unless you also change the descriptor.

To manage a kind’s schema in the UI, open Relationship Kinds, select a kind, and use the Attributes card: add an attribute, give it a key, an optional display name and description, a type, and whether it is required. Select and multi-select attributes take a comma-separated list of options. Save the kind to apply the schema. You can also define attributes when you create a kind, and the Relationship Kinds list shows each kind’s attributes (or “Free-form” when it has none).

Attributes are returned with every relationship — in the GraphQL CatalogRelationship type (attributesJson), including the edges of a graph traversal, the REST relationship endpoints (attributes), and the MCP get_relationships tool — and they are carried by catalog export and import files. Define a kind’s schema through the GraphQL relationship-kind mutations (attributeDefinitions) or the MCP relationship-kind tools (attributeDefinitionsJson) as well.

The parent-of relationship builds the catalog tree: a System contains Components, a Component contains sub-components, and so on. The tree view lets you expand and collapse branches to navigate large catalogs, and a kind’s allowed child kinds keep the hierarchy sensible.

Dependency relationships (like depends-on) capture how your software actually fits together — which service calls which, which component reads which datastore. These edges are what make the catalog useful for reasoning about change.

Because relationships are directed and stored uniformly, Componecat can answer “what does a change to this affect?” before you make the change. Impact analysis starts at one entity and walks the dependency graph, returning its blast radius:

  • Kinds — by default it follows every relationship kind your organization marks as a dependency kind (the Is Dependency switch on the Relationship Kinds page), so an organization that splits dependencies into depends-on, calls and reads-from gets all three. Name one or more kinds to follow just those instead. If no kind is marked as a dependency kind and you don’t name any, impact analysis tells you so rather than returning an empty result.
  • Direction — downstream (the default) follows each relationship from source to target: from A in “A depends on B”, it reaches B. Upstream follows them the other way: from B it reaches A — everything that depends on B. Both follows either way.
  • Depth — how many hops out to go, 1–50 (10 by default).

Each entity in the result comes with its shortest hop count from the starting entity and the relationship kind (or kinds) it was reached by. For example, with A depends-on B and B depends-on C, an upstream analysis of C returns B at one hop and A at two.

Impact analysis is one of the things AI agents can query over MCP (get_impact_analysis), and it’s available through the GraphQL and REST APIs as well. It shares graph traversal’s size caps: a very large blast radius is cut short and flagged as truncated rather than failing.

An entity page’s Graph tab draws the blast radius, so you can judge a change’s risk at a glance and show it in a review. Choose Impact above the graph:

  1. Start from the entity. Impact always opens on Dependents (what depends on this) — the entities that depend on this one, which is the blast radius — whichever view you switched from, and a link to ?mode=impact with no direction opens there too. Only a direction= in the link or the Direction select changes it. Switching in from the neighborhood’s default 1 hop goes five hops out instead; any other depth is kept. (Choosing exactly 1 hop before switching cannot be told from the default, so it is always widened.) The controls are the analysis’s own: Max hops (1–10), Direction (Dependents (what depends on this), Dependencies (what this depends on) or Both (dependents and dependencies)) and Relationship kinds. In the address, direction=inbound is the dependents (the API’s upstream direction), direction=outbound the dependencies (the API’s downstream direction) and direction=both both; in the neighborhood and path views the same three values mean relationships pointing at the entity or away from it. With A depends-on B, the dependents of B are A and the dependencies of A are B. With no kind ticked, the menu reads Dependency kinds and the analysis follows the kinds marked Is Dependency; tick kinds to follow just those. If no kind is marked and none is ticked, a message says so instead of drawing an empty result.
  2. Read the summary. Chips above the graph give the count the analysis returned — “12 affected” for dependents, “12 dependencies” for dependencies, “12 related” for both — and how many entities sit at each hop distance — “3 at depth 1”, “5 at depth 2”. The same list is what the graph highlights, so the chips and the graph always agree. A line beneath names the relationship kinds the analysis followed. If the analysis hit the traversal’s size cap (2 000 entities or 10 000 relationships), a note says the count is partial. When nothing is found, the summary says so in the direction’s terms — for dependents, “Nothing depends on Checkout API within 5 hops, so nothing is affected.”.
  3. Read the graph. Every affected entity wears a ring and a small badge with its hop distance. The ring runs from red at one hop through orange, yellow and green to cyan at five or more hops, and the badge carries the number, so the distance never depends on color alone. The relationships that reach affected entities are drawn thick. The entity’s direct neighbors are shown for context; the ones the analysis does not reach, and anything you expand from them, are dimmed. The legend names each hop distance that occurs. A screen reader hears each entity’s distance (“affected, 2 hops from Checkout API”) or “not affected” (for dependencies, “dependency, …” and “not a dependency”; for both directions, “dependent or dependency, …” and “not reached”), and Show as list repeats it as text.

Select an entity or relationship to open its details, exactly as in the neighborhood view.

The mode and its settings are part of the page address (?mode=impact&depth=5&direction=inbound&kinds=…), so a bookmark or a pasted link reopens the same analysis. Copy link puts the address of the current view on your clipboard. Impact and path views are not remembered between entities; only the neighborhood’s own controls are.

Export PNG and Export SVG download the graph as it is on screen — as currently panned and zoomed, with the highlight — and the legend beneath it, so a review, an ADR or a ticket gets the picture and the key to read it. Files are named after the entity and the mode (checkout-api-impact.svg) and are built in your browser; nothing is uploaded. The SVG carries the colors of the theme you are viewing in.

Impact analysis follows your dependency kinds. To ask a more general question — “from this service, follow depends-on and provides-api-for, outbound, up to three hops” — use graph traversal. It starts at one entity and returns the whole reachable subgraph in one request:

  • Kinds — follow one relationship kind, several, or all of them.
  • Direction — outbound (what this entity points at), inbound (what points at it), or both.
  • Depth — how many hops out to go. Each entity comes back with its shortest distance from the starting entity.
  • Filter — a search query the entities must match. Traversal doesn’t continue through an entity that fails the filter, so a filter like lifecycle:production keeps a walk inside production.

The result lists the entities reached and the relationships between them, so you can lay out a graph or reason about it directly. A very large graph is cut short at a size cap and flagged as truncated rather than failing. Traversal is available through the GraphQL and REST APIs.

To see how a whole set of entities connects — a system, a team’s services, a collection — switch the catalog or a collection to its graph view: it draws the entities in the current results and every relationship among them. To see everything at once, open the Graph page.

Graph in the sidebar opens the whole catalog as one graph on a page of its own: every entity in your organization is a node, and every relationship between them is an edge. It is the place to start when you want the shape of the estate rather than one entity’s surroundings — which systems cluster together, what sits alone, where the dependencies run.

  • Relationships picks which relationship kinds to draw; All draws every kind. The entities stay the same — only the edges change.
  • Color by colors each entity by its kind, by its source (the integration, descriptor or hand entry it came from) or by its lifecycle stage. The legend names every color and line style.
  • Hide inferred leaves inferred relationships out of the drawing. A note says how many that hid; the entities stay.
  • Select an entity (click it, or Tab to it and press Space) to see a summary with a link to its page and Explore neighborhood, which opens its Graph tab for depth, direction, impact and paths. Select a relationship to see its kind, provenance, description and attributes.
  • Open an entity by double-clicking its node, or by focusing it and pressing Enter. Show as list gives the whole graph as text, and Export PNG and Export SVG download the view as drawn.

The controls are part of the page address, so Copy link shares the exact view, and Componecat remembers the controls you used last in each organization. The graph redraws itself when a relationship or entity changes anywhere in the organization. A very large catalog is cut at 2,000 entities (in display-name order) and 10,000 relationships; when that happens a note above the graph says it is partial — draw fewer relationship kinds, or explore from an entity’s Graph tab, to see the rest.

An entity page’s Graph tab draws the entity and everything around it as a graph, so you can see how a component fits in without reading lists. It opens on the entity’s direct neighbors — one hop, in both directions, across every relationship kind — with the entity at the center.

  • Edges are colored by relationship kind, dashed when inferred, and fainter the lower their confidence. Entities are colored by kind, or by source (the integration, descriptor or hand entry they came from) with Color by. A legend below the graph names every color and line style.
  • Depth goes out up to five hops; Direction follows outbound relationships (what this entity points at), inbound ones (what points at it), or both; Relationship kinds narrows the walk to the kinds you tick, listed in your organization’s display order for relationship kinds.
  • Hide inferred leaves out inferred relationships, together with any entity only they connected. A note says how many entities that hid.
  • Select an entity (click it, or Tab to it and press Space) to see a summary — kind, reference, lifecycle, owner and description — with a link to open its page.
  • Expand an entity by one more hop in place: double-click it, press Enter (or E) on it, or use Expand one hop in its summary. Changing depth, direction or kinds starts again from the entity, and switching back doesn’t bring earlier expansions back. If an expansion fails, the rest of the graph stays on screen and a note above it lets you retry that expansion or dismiss it.
  • Select a relationship to see its kind, where it came from — the provenance source, what asserted it, and its confidence — its description and its attributes.

The controls are part of the page address, so you can bookmark or share the exact view, and a reload brings it back. Componecat also remembers the controls you used last in each organization, in your browser, and opens the Graph tab with them when the address doesn’t say otherwise. Relationship kinds your organization no longer defines — say, a kind deleted since you last ticked it, or one named in a shared link — are dropped from the controls, and the graph follows the rest.

A large neighborhood is cut short at the traversal’s size cap. When that happens a banner says the graph is partial — narrow the depth or relationship kinds to see everything. An entity with no relationships yet shows a link to add one; if it has relationships but none match the controls, Reset controls takes you back to the defaults. Show as list gives the whole graph as text, with the same select and expand actions, for keyboard and screen reader users.

The Graph tab is also a place to fix the catalog. You need to be a member of the organization, and the entity a relationship is declared by (its source) must be one you can edit — an owning team can limit editing to its members. Without that the controls below are hidden, or disabled with the reason beside them.

  • Add a relationship by dragging. Press on an entity and drag onto another, then let go. The same Add Relationship dialog as on the entity page opens with the entity you dragged from as the source and the one you dropped on as the target. Choose the relationship kind, add a description and any attributes the kind defines, and select Add. Letting go on empty canvas, or on the entity you started from, does nothing, and Escape cancels a drag. Dragging from empty canvas still pans the view.
  • Add one without dragging. Select an entity and use Add relationship to… in its summary, press C on a focused entity, or use Add relationship next to an entity in Show as list. The dialog opens with the source filled in and a search box for the target. Focus returns to where you started when the dialog closes.
  • Edit or remove a relationship. Select it, then use Edit to change its description and attributes (the same form as the entity page), or Remove and confirm. Inferred relationships belong to automation and can’t be edited or removed here; removing a descriptor relationship warns that the next sync re-creates it.

A relationship you add that the graph does not draw — for example between two neighbors of the entity at 1 hop — is saved, but no edge appears: the graph shows a note (and announces it) saying why when it can tell. It names the Relationship kinds filter when the filter leaves out the new relationship’s kind, the direction setting when that hides it from the entity, and the depth when both ends are on screen; otherwise it asks you to adjust the depth, direction and kind settings. In the impact and path views it points you to the Neighborhood view. If the graph could not be refreshed after your change, it says so instead of claiming the edge is not drawn. Changing the setting it names shows the edge.

An edge is only drawn after the API accepts it. If a rule refuses the change — a kind’s cardinality, a cycle it forbids, a kind the entities aren’t assigned to, or missing permission — the dialog stays open and shows the API’s message, and the graph is unchanged.

Changes to relationships made through the app or the API by anyone in your organization — adding, editing or removing one — redraw the Graph tab without a reload when either end is on screen, and a status message names what changed (an end that is not on screen is called “another entity”). A bulk update of the relationships an entity declares (bulkUpsertCatalogRelationships) reports only that declaring entity, so it redraws the graph when that entity is on screen, not when only the entities it points at are. Your own changes are announced once (for example “Added a relationship from A to B”), not again when they come back from the server, whether it arrives just before or just after the confirmation (after an edit, the first update to that relationship that follows is refreshed but not announced again); a change someone else makes to the same relationship afterwards is announced as theirs. If a change you make is refused, the dialog shows the reason once and Escape closes it. If you closed the dialog before the refusal arrived, the reason appears in a message and is announced instead. Relationships written by descriptor sync, integration runs or catalog import appear the next time the graph is loaded or refreshed rather than live.

Traversal tells you everything reachable from one entity. When the question is how two particular entities are connected — “how does checkout end up depending on ledger-db?” — use path finding. Name the two entities, and optionally the relationship kinds and direction to follow, and you get back the paths between them, shortest first, each as the ordered list of relationships that make it up. It is the quickest way to explain a dependency, confirm a link you assumed, or spot a coupling you didn’t expect. If the entities aren’t connected you get no paths rather than an error. Path finding is available through the GraphQL and REST APIs.

On an entity page’s Graph tab, choose Path to draw the paths from that entity to another:

  1. Pick the target. Search by name or display name and choose a match (the entity itself is left out); Change target starts again. Max hops (1–10), Direction and Relationship kinds work as in the neighborhood; switching in from it goes five hops out and follows outbound relationships, unless you had already set a depth other than 1 hop or a direction other than both directions.
  2. Read the result. A list gives each path found — up to 20, ordered by hop count, with its hop count and the entities along it — and the first is marked shortest found. Very dense graphs can exhaust the search before every path is found, so there may be fewer paths than exist, and a shorter one than the first may exist. The selected path is drawn thick, with a numbered badge on each of its entities from the start; alternatives are drawn thinner, and everything else is dimmed. If the entities aren’t connected within the hops you allow, the tab says so and suggests more hops, more kinds or both directions.
  3. Cycle through the alternatives. Choose a path in the list (the arrow keys move between them), or use Previous path and Next path; the graph and the page address follow.

Like impact views, a path view is part of the address (?mode=path&target=<entity id>&path=2, where path is the 1-based position of the selected path in the list ordered by hop count, and is left out for the first), can be copied with Copy link, and exports as a PNG or SVG with its legend. A link naming more than 10 hops opens at 10, and one whose target is not a valid entity id opens at the target picker.

You can add relationships from an entity’s page in the UI, declare them in a componecat.yaml descriptor, or create them through the APIs. In a descriptor, a relationship names the kind and the target entity’s reference:

relationships:
- kind: depends-on
target: resource://datastores/payments-db
attributes:
protocol: postgres

A relationship can also carry attributes: in a descriptor, an attributes map; through the GraphQL mutations, an attributesJson JSON object; through the MCP add_relationship tool, an attributesJson argument; and through REST, an attributes object.

If a descriptor references a target that doesn’t exist yet — common when several descriptors land in the same push — Componecat records the link and completes it automatically once the target appears, so the order of ingestion doesn’t matter.

Every relationship records its provenance — how it came to exist, what asserted it, and when it was last confirmed — so you can tell a hand-entered guess from a fact a system observed, and clean up stale edges with confidence.

Source How it is created What it records
Manual Added from the UI or through the APIs or MCP The user who added it, and when
Descriptor Declared in a componecat.yaml descriptor The descriptor’s repository and path, the commit it was last synced from, and when that sync confirmed it
Inferred Proposed by automation, such as an integration run, or a catalog intake, from manifests or from AI The run or suggestion that proposed it, a confidence score, and when it was last observed

Manual and descriptor relationships always have a confidence of 1.0.

Reading provenance in the Relationships tab

Section titled “Reading provenance in the Relationships tab”

Each relationship in an entity’s Relationships tab carries a source badge that reads Manual, Descriptor or Inferred. An inferred relationship also shows its confidence as a percentage next to the badge — for example Inferred 60% — so a low-confidence guess stands out at a glance.

Hover the badge, or move focus to it with the keyboard, to see the details:

  • What asserted it — “Hand-entered” and the user who added it for a manual relationship, or “Approved in a review” and the user if it was approved from a suggestion; “Declared in owner/repo:componecat.yaml @ abc1234” for a descriptor relationship; the integration run or AI suggestion for an inferred one.
  • When — when the relationship was asserted, and when automation last confirmed it. A descriptor or inferred relationship that has never been re-confirmed says so.
  • Confidence — for inferred relationships.

When Componecat can work out where the descriptor lives, the Descriptor badge is a link that opens the descriptor file at the commit that declared the relationship, in GitHub, GitLab or Bitbucket. It finds the file in one of two ways:

  • Through your git integration — when an entity synced from that repository’s descriptors is connected to a GitHub, GitLab or Bitbucket integration, the link points at that integration’s host. This includes self-hosted GitHub Enterprise, GitLab and Bitbucket Server.
  • Through a public repository URL — otherwise, when either entity’s repository URL is that repository on github.com, gitlab.com or bitbucket.org.

If neither applies, the badge is shown without a link.

Use the All sources / Manual / Descriptor / Inferred buttons above the list to show only the relationships from one source — for example, to review everything automation inferred before you rely on it.

When the same relationship is asserted by more than one source, the stronger source wins: Descriptor over Manual over Inferred. If you add a relationship by hand and later declare it in a descriptor, Componecat upgrades it to a descriptor relationship in place, and notes the change in the source entity’s activity log when the entity’s kind has one. A source is never downgraded — adding a descriptor relationship again by hand changes nothing.

A descriptor owns the relationships it declares: when a descriptor sync no longer declares a manual or descriptor relationship of a kind it lists, that relationship is removed. If you remove a descriptor relationship by hand, the next sync re-creates it — the removal confirmation warns you, so remove it from the descriptor instead. Automation never removes relationships: inferred relationships are left alone by descriptor syncs, inference never removes a manual or descriptor relationship, and inferred relationships can’t be removed by hand.

Catalog import follows the same rule: importing a file never downgrades a relationship that already exists in the organization, so a relationship your descriptors own stays a descriptor relationship even if the file lists it as manual.

Scoring drift between declared and observed dependencies

Section titled “Scoring drift between declared and observed dependencies”

Because provenance tells a declared dependency from an observed one, a scorecard can hold the two to each other:

  • An Undeclared dependency rule fails an entity with an inferred dependency, above a confidence you choose, that nobody declared. Declaring it — by hand or in a descriptor — upgrades the inferred relationship in place and fixes the result on the next evaluation.
  • A Stale dependency rule fails an entity with a declared dependency that automation has not observed (its last observed time) within a number of days you choose, or ever.

Both rules pass as not applicable in an organization where automation has never inferred or re-confirmed a relationship, so they never penalize an organization that only declares. The scorecard report’s Dependency drift section lists every drifting relationship with a Declare this dependency or Remove stale edge fix.

Provenance is returned by the GraphQL CatalogRelationship type (source, confidence, provenanceRef, assertedAt, lastObservedAt, assertedByUserId, plus provenanceUrl, the descriptor file link described above), the REST relationship endpoints, and the MCP get_relationships tool, and it is carried by catalog export and import files. The asserting user isn’t exported, so imported manual relationships don’t record who added them.