# Schema & Type Contracts: Nodes, Edges, and Aliases

> The knowledge graph is defined by two Zod schemas: NodeTypeSchema (21 canonical node types such as file, function, class, domain, article) and EdgeTypeSchema (35 edge types across 8 categories: structural, behavioral, data-flow, dependencies, semantic, infrastructure, domain, knowledge). Alias maps (NODE_TYPE_ALIASES, EDGE_TYPE_ALIASES) normalize LLM-generated variants to canonical forms at assembly time. This schema is the contract between the agent pipeline and the dashboard — both sides import from @understand-anything/core/types and @understand-anything/core/schema.

- Repository: Lum1104/Understand-Anything
- GitHub: https://github.com/Lum1104/Understand-Anything
- Human wiki: https://grok-wiki.com/public/wiki/lum1104-understand-anything-3b923df96896
- Complete Markdown: https://grok-wiki.com/public/wiki/lum1104-understand-anything-3b923df96896/llms-full.txt

## Source Files

- `understand-anything-plugin/packages/core/src/schema.ts`
- `understand-anything-plugin/packages/core/src/types.test.ts`
- `understand-anything-plugin/packages/dashboard/src/store.ts`
- `understand-anything-plugin/packages/core/package.json`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:

- [understand-anything-plugin/packages/core/src/schema.ts](understand-anything-plugin/packages/core/src/schema.ts)
- [understand-anything-plugin/packages/core/src/types.ts](understand-anything-plugin/packages/core/src/types.ts)
- [understand-anything-plugin/packages/core/src/types.test.ts](understand-anything-plugin/packages/core/src/types.test.ts)
- [understand-anything-plugin/packages/core/package.json](understand-anything-plugin/packages/core/package.json)
- [understand-anything-plugin/packages/dashboard/src/store.ts](understand-anything-plugin/packages/dashboard/src/store.ts)
</details>

# Schema & Type Contracts: Nodes, Edges, and Aliases

The knowledge graph produced by Understand Anything is not a free-form JSON blob — it is a strictly typed structure governed by two complementary layers: TypeScript interfaces in `types.ts` (for compile-time safety) and Zod schemas in `schema.ts` (for runtime validation at assembly time). Every node and every edge must conform to a finite set of canonical string values. Because the graph is assembled from LLM output, a normalization layer — the alias maps — sits between raw agent output and the validated result, transparently folding common LLM variants back into canonical forms before validation runs.

Both the agent pipeline and the dashboard share these contracts by importing from the `@understand-anything/core/types` and `@understand-anything/core/schema` subpath exports. Neither side owns a private copy of the type definitions; changes to the schema are immediately visible to both.

---

## Node Types

### Canonical Set (21 types)

Nodes represent any meaningful concept in the analyzed project. The 21 canonical node types span four semantic groups:

| Group | Types | Count |
|---|---|---|
| **Code** | `file`, `function`, `class`, `module`, `concept` | 5 |
| **Non-code** | `config`, `document`, `service`, `table`, `endpoint`, `pipeline`, `schema`, `resource` | 8 |
| **Domain** | `domain`, `flow`, `step` | 3 |
| **Knowledge** | `article`, `entity`, `topic`, `claim`, `source` | 5 |

The TypeScript union is defined in `types.ts`:

```typescript
// understand-anything-plugin/packages/core/src/types.ts:2-7
export type NodeType =
  | "file" | "function" | "class" | "module" | "concept"
  | "config" | "document" | "service" | "table" | "endpoint"
  | "pipeline" | "schema" | "resource"
  | "domain" | "flow" | "step"
  | "article" | "entity" | "topic" | "claim" | "source";
```

The Zod schema mirrors it exactly as a `z.enum` in `schema.ts`, used inside `GraphNodeSchema`:

```typescript
// understand-anything-plugin/packages/core/src/schema.ts:370-376
type: z.enum([
  "file", "function", "class", "module", "concept",
  "config", "document", "service", "table", "endpoint",
  "pipeline", "schema", "resource",
  "domain", "flow", "step",
  "article", "entity", "topic", "claim", "source",
]),
```

Sources: [understand-anything-plugin/packages/core/src/types.ts:1-7](), [understand-anything-plugin/packages/core/src/schema.ts:370-376]()

### GraphNode Fields

Every node carries a fixed set of fields:

| Field | Type | Required | Notes |
|---|---|---|---|
| `id` | `string` | ✓ | Unique identifier; edges reference by id |
| `type` | `NodeType` | ✓ | One of 21 canonical values |
| `name` | `string` | ✓ | Human-readable label |
| `summary` | `string` | ✓ | LLM-generated description |
| `tags` | `string[]` | ✓ | Defaulted to `[]` if missing |
| `complexity` | `"simple" \| "moderate" \| "complex"` | ✓ | Defaulted to `"moderate"` if missing |
| `filePath` | `string` | optional | Path in the analyzed repo |
| `lineRange` | `[number, number]` | optional | Start/end line numbers |
| `languageNotes` | `string` | optional | Language-specific remarks |
| `domainMeta` | `DomainMeta` | optional | For `domain`/`flow`/`step` nodes |
| `knowledgeMeta` | `KnowledgeMeta` | optional | For `article`/`entity`/`topic`/`claim`/`source` nodes |

Sources: [understand-anything-plugin/packages/core/src/types.ts:39-51](), [understand-anything-plugin/packages/core/src/schema.ts:368-386]()

### Extended Metadata

Two optional metadata bags extend the core node structure for specialized node groups:

**`DomainMeta`** (for `domain`, `flow`, `step` nodes):
```typescript
// understand-anything-plugin/packages/core/src/types.ts:30-36
export interface DomainMeta {
  entities?: string[];
  businessRules?: string[];
  crossDomainInteractions?: string[];
  entryPoint?: string;
  entryType?: "http" | "cli" | "event" | "cron" | "manual";
}
```

**`KnowledgeMeta`** (for `article`, `entity`, `topic`, `claim`, `source` nodes):
```typescript
// understand-anything-plugin/packages/core/src/types.ts:22-27
export interface KnowledgeMeta {
  wikilinks?: string[];
  backlinks?: string[];
  category?: string;
  content?: string;
}
```

Both schemas use `.passthrough()` in Zod, allowing agents to include extra fields without causing validation failures.

Sources: [understand-anything-plugin/packages/core/src/types.ts:22-36](), [understand-anything-plugin/packages/core/src/schema.ts:353-386]()

---

## Edge Types

### Canonical Set (35 types across 8 categories)

The `EdgeTypeSchema` is a `z.enum` of 35 values, grouped by semantic category. The same grouping is reflected in the dashboard's `EDGE_CATEGORY_MAP`:

| Category | Edge Types |
|---|---|
| **Structural** | `imports`, `exports`, `contains`, `inherits`, `implements` |
| **Behavioral** | `calls`, `subscribes`, `publishes`, `middleware` |
| **Data flow** | `reads_from`, `writes_to`, `transforms`, `validates` |
| **Dependencies** | `depends_on`, `tested_by`, `configures` |
| **Semantic** | `related`, `similar_to` |
| **Infrastructure** | `deploys`, `serves`, `provisions`, `triggers`, `migrates`, `documents`, `routes`, `defines_schema` |
| **Domain** | `contains_flow`, `flow_step`, `cross_domain` |
| **Knowledge** | `cites`, `contradicts`, `builds_on`, `exemplifies`, `categorized_under`, `authored_by` |

Sources: [understand-anything-plugin/packages/core/src/schema.ts:4-14](), [understand-anything-plugin/packages/dashboard/src/store.ts:31-40]()

### GraphEdge Fields

| Field | Type | Required | Notes |
|---|---|---|---|
| `source` | `string` | ✓ | Node id; validated against the node set |
| `target` | `string` | ✓ | Node id; validated against the node set |
| `type` | `EdgeType` | ✓ | One of 35 canonical values; defaulted to `"depends_on"` |
| `direction` | `"forward" \| "backward" \| "bidirectional"` | ✓ | Defaulted to `"forward"` |
| `weight` | `number` | ✓ | Float in `[0, 1]`; defaulted to `0.5`, clamped if out of range |
| `description` | `string` | optional | Human-readable label for the edge |

Sources: [understand-anything-plugin/packages/core/src/types.ts:54-61](), [understand-anything-plugin/packages/core/src/schema.ts:388-395]()

---

## Alias Maps

LLMs frequently produce variant spellings for both node and edge types (`func` instead of `function`, `extends` instead of `inherits`). The alias maps translate these variants to canonical forms **before** Zod validation runs, preventing unnecessary validation failures without weakening the schema.

### NODE_TYPE_ALIASES

```typescript
// understand-anything-plugin/packages/core/src/schema.ts:17-75
export const NODE_TYPE_ALIASES: Record<string, string> = {
  func: "function",
  fn: "function",
  method: "function",
  interface: "class",
  struct: "class",
  mod: "module",
  pkg: "module",
  package: "module",
  container: "service",
  deployment: "service",
  pod: "service",
  // ... (46 entries total)
};
```

Notable design decisions captured in comments:
- `"process"` is **intentionally excluded** from domain aliases — it is ambiguous with the OS/Node.js `process` global.

### EDGE_TYPE_ALIASES

```typescript
// understand-anything-plugin/packages/core/src/schema.ts:78-125
export const EDGE_TYPE_ALIASES: Record<string, string> = {
  extends: "inherits",
  invokes: "calls",
  uses: "depends_on",
  requires: "depends_on",
  // ... (37 entries total)
};
```

A critical invariant is documented with a comment:
```typescript
// Note: "implemented_by" is intentionally NOT aliased to "implements" —
// it inverts edge direction (see commit fd0df15). The LLM should use
// "implements" with correct source/target instead.
```

This means aliasing is not purely cosmetic — it preserves directed semantics. Reversing source/target via an alias would silently corrupt graph meaning.

Sources: [understand-anything-plugin/packages/core/src/schema.ts:17-75](), [understand-anything-plugin/packages/core/src/schema.ts:78-125]()

### Additional Alias Maps

Two further alias maps cover other enum-like fields:

- **`COMPLEXITY_ALIASES`**: maps `low→simple`, `medium→moderate`, `high→complex`, etc.
- **`DIRECTION_ALIASES`**: maps `to→forward`, `from→backward`, `both→bidirectional`, etc.

Sources: [understand-anything-plugin/packages/core/src/schema.ts:128-146]()

---

## Validation Pipeline

The `validateGraph` function in `schema.ts` applies four tiers of processing in sequence before returning a `ValidationResult`:

```text
Raw LLM JSON
     │
     ▼
Tier 1: sanitizeGraph()
  – null → undefined for optional fields
  – lowercase all enum strings
     │
     ▼
normalizeGraph()
  – NODE_TYPE_ALIASES applied to node.type
  – EDGE_TYPE_ALIASES applied to edge.type
     │
     ▼
Tier 2: autoFixGraph()
  – Missing type → default ("file" for nodes, "depends_on" for edges)
  – Missing complexity → "moderate"
  – Missing tags → []
  – Missing direction → "forward"
  – Missing weight → 0.5; string weight coerced; out-of-range clamped to [0,1]
     │
     ▼
Tier 3: Per-element Zod parse
  – Invalid nodes → dropped (with GraphIssue at level "dropped")
  – Invalid edges → dropped
  – Edge referential integrity: source and target must be valid node ids
  – Layers and tour steps: drop invalid, filter dangling nodeIds
     │
     ▼
Tier 4: Fatal checks
  – Not an object → fatal
  – Collections not arrays → fatal
  – No valid project metadata → fatal
  – Zero valid nodes → fatal
     │
     ▼
ValidationResult { success, data, issues, fatal? }
```

The `GraphIssue` type carries a `level` (`"auto-corrected"` | `"dropped"` | `"fatal"`), a `category`, and a human-readable `message`. The dashboard's `WarningBanner` component surfaces these issues to the user alongside layout-time issues from ELK.

Sources: [understand-anything-plugin/packages/core/src/schema.ts:148-663](), [understand-anything-plugin/packages/dashboard/src/store.ts:236-239]()

---

## The Contract Between Pipeline and Dashboard

Both sides of the system share the same types through the core package's subpath exports:

```json
// understand-anything-plugin/packages/core/package.json:6-23
"exports": {
  "./types": {
    "types": "./dist/types.d.ts",
    "default": "./dist/types.js"
  },
  "./schema": {
    "types": "./dist/schema.d.ts",
    "default": "./dist/schema.js"
  }
}
```

The dashboard imports only the browser-safe subpaths — never the main entry point, which would pull in Node.js modules:

```typescript
// understand-anything-plugin/packages/dashboard/src/store.ts:4-9
import type { GraphIssue } from "@understand-anything/core/schema";
import type {
  GraphNode,
  KnowledgeGraph,
  TourStep,
} from "@understand-anything/core/types";
```

The dashboard also re-declares the `NodeType` union and `EDGE_CATEGORY_MAP` directly in `store.ts` to keep the store self-contained for filtering logic, but these are structurally identical to what lives in `types.ts`.

Sources: [understand-anything-plugin/packages/core/package.json:6-23](), [understand-anything-plugin/packages/dashboard/src/store.ts:1-39]()

---

## What Would Break If the Schema Changed

| Change | Impact |
|---|---|
| Add a new `NodeType` value | Must be added to both `NodeType` union and `GraphNodeSchema` enum; dashboard `ALL_NODE_TYPES` and `NodeType` type must also update |
| Remove a `NodeType` value | Existing graphs with the removed type will fail Zod parse; nodes get dropped unless an alias covers the old value |
| Add a new `EdgeType` | Must be added to `EdgeTypeSchema` enum, `types.ts` union, and `EDGE_CATEGORY_MAP` in `store.ts` |
| Add an alias | Safe — aliases are applied before validation; no schema change required |
| Alias `implemented_by` → `implements` | Direction inversion bug: edges pointing the wrong way in the graph |
| Change `weight` range | `autoFixGraph` clamps values; consumers assume `[0, 1]` |
| Make `filePath` required | Breaks non-code nodes (domain, knowledge, config) that have no file path |

The closing invariant that holds the system together: **the canonical enum values in `EdgeTypeSchema` / `GraphNodeSchema` are the contract**. The alias maps are a translation layer, not an expansion of the contract. Any string not in the canonical set and not in an alias map will be rejected at Tier 3 and the element dropped, not silently ignored.

Sources: [understand-anything-plugin/packages/core/src/schema.ts:462-497](), [understand-anything-plugin/packages/core/src/schema.ts:543-609]()
