Add NBGM specification v0.1 baseline

Author the domain-agnostic graph model spec covering nodes, edges,
attributes, provenance, and inspection operations. Mark VANTAGE-WP-0002
finished with the spec deliverable documented in the workplan task.
This commit is contained in:
2026-06-24 18:34:49 +02:00
parent d109bc7906
commit 60e517d493
2 changed files with 677 additions and 4 deletions

671
docs/nbgm-spec-v0.1.md Normal file
View File

@@ -0,0 +1,671 @@
# NBGM Specification v0.1
Network-Based Graph Model (NBGM) baseline for Vantage Point.
**Status:** draft baseline
**Version:** `vantage.nbgm/v0.1`
**Related:** `INTENT.md`, `SCOPE.md`
---
## 1. Purpose
This document defines the domain-agnostic graph model that Vantage Point uses
to represent, inspect, and reason about dependency structures.
An NBGM is a typed graph of entities (nodes) and relationships (edges) with
attributes and provenance. Meaning is not baked into the core model. Domains
attach interpretation through bindings, lenses, and vantage points.
The vocabulary is grounded in Tamara Munzner's nested design model and the
Network-Based Graph Model framing in Meyer, Sedlmair, and Munzner
([BELIV 2012](https://miriah.github.io/publications/nbgm-beliv.pdf)). In that
framing, a **network** is a data-abstraction block: nodes, links, and
attributes are the structural primitives on which tasks, encodings, and
algorithms are stacked.
### 1.1 Scope of v0.1
In scope:
- core identity and typing rules for nodes and edges
- attribute model and namespaces
- provenance and lineage
- a baseline catalog of inspection operations
- alignment notes for repo-native graph bindings (for example Railiance Fabric)
Out of scope for v0.1:
- storage engines, query languages, or visualization encodings
- domain-specific node/edge type catalogs
- lens and vantage-point configuration schemas
- validation tooling and serialization formats (future workplans)
### 1.2 Design principles
1. **Neutral core, interpreted surface.** The model stores facts; bindings
supply domain semantics.
2. **Inspectable by default.** Every element should be explainable: what it
is, why it exists, and how it was derived.
3. **Provenance over assertion.** Observed and derived facts carry origin,
method, confidence, and freshness.
4. **Perspective-friendly structure.** The same graph supports multiple
inspection operations without duplicating source data.
5. **Composable bindings.** Repo-native declarations, scanners, and exports can
project into NBGM without becoming the authoring authority.
---
## 2. Terminology
| Term | Definition |
|------|------------|
| **Graph** | A bounded collection of nodes and edges sharing one identity and metadata envelope. |
| **Node** | An entity vertex with stable identity, kind, and attributes. |
| **Edge** | A directed or undirected relationship between two nodes. |
| **Kind** | A domain-defined type label for a node or edge (for example `Service`, `requires`). |
| **Attribute** | A named property on a node or edge, optionally typed and namespaced. |
| **Provenance** | Metadata describing how and when a fact entered the graph. |
| **Binding** | A domain projection that maps external declarations or observations into NBGM elements. |
| **Lens** | A named interpretation layer that selects, re-labels, or aggregates graph elements for a task. |
| **Vantage point** | A saved or ephemeral lens plus inspection context (focus, depth, filters). |
| **Inspection operation** | A read-only graph query or explanation primitive. |
---
## 3. Graph envelope
Every NBGM instance is wrapped in a graph envelope.
```yaml
apiVersion: vantage.nbgm/v0.1
kind: Graph
metadata:
id: example.ecosystem
title: Example ecosystem graph
domain: infotech # optional binding domain
created_at: "2026-06-24T00:00:00Z"
updated_at: "2026-06-24T00:00:00Z"
source_bindings:
- binding_id: railiance-fabric.export
source_ref: railiance-fabric/registry
spec:
node_count: 0 # informational; implementations may omit
edge_count: 0
default_direction: directed
```
### 3.1 Required envelope fields
| Field | Requirement |
|-------|-------------|
| `apiVersion` | Must be `vantage.nbgm/v0.1` for this revision. |
| `kind` | Must be `Graph`. |
| `metadata.id` | Stable graph identifier. Prefer dotted, lower-case slugs. |
| `metadata.title` | Human-readable graph name. |
| `metadata.updated_at` | ISO-8601 timestamp of last material change. |
| `spec.default_direction` | `directed` or `undirected`. Edge-level direction may override. |
### 3.2 Optional envelope fields
- `metadata.domain` — primary interpretation domain for the graph.
- `metadata.created_at` — first materialization time.
- `metadata.source_bindings[]` — list of bindings that produced or refreshed the graph.
- `metadata.labels` — arbitrary string tags for indexing and filtering.
- `metadata.description` — narrative summary of graph intent and coverage.
---
## 4. Nodes
A node represents one addressable entity in the modeled system.
```yaml
kind: Node
metadata:
id: railiance-platform.openbao
stable_key: railiance-platform.openbao # durable id across renames
name: OpenBao
labels:
repo: railiance-platform
domain: railiance
spec:
node_kind: Service
lifecycle: active # planned | active | deprecated | retired
layer: service # optional stratification hint
attributes:
core:
description: Runtime secrets service
display:
label: OpenBao
visual_weight: 1.0
provenance:
assertion_type: declared # declared | observed | derived | inferred
sources:
- ref: fabric/services/openbao.yaml
method: declaration_load
observed_at: "2026-06-24T00:00:00Z"
confidence: 1.0
freshness_state: current # current | stale | unknown
```
### 4.1 Identity rules
1. `metadata.id` is unique within a graph.
2. `metadata.stable_key` is optional but recommended when display ids may change.
Profile rules, deep links, and temporal comparison should prefer
`stable_key` when present.
3. IDs should be stable across re-ingestion when the underlying entity is
unchanged. Bindings must document their ID strategy.
### 4.2 Required node fields
| Field | Requirement |
|-------|-------------|
| `metadata.id` | Unique node identifier. |
| `spec.node_kind` | Domain-defined entity kind. |
| `provenance.assertion_type` | How the node fact was obtained. |
| `provenance.sources[]` | At least one source record for non-synthetic nodes. |
### 4.3 Recommended node fields
| Field | Purpose |
|-------|---------|
| `metadata.name` | Short display name. |
| `metadata.labels` | Cross-cutting indices (`repo`, `domain`, `environment`, etc.). |
| `spec.lifecycle` | Entity lifecycle state. |
| `spec.layer` | Layer or stratum for layout and filtering. |
| `attributes.core` | Domain-neutral or lightly-bound descriptive fields. |
| `provenance.confidence` | Numeric confidence in `[0, 1]`. |
| `provenance.freshness_state` | Whether the fact is current enough to trust for the active task. |
### 4.4 Node kinds
`spec.node_kind` is binding-defined. Vantage Point does not mandate a global
ontology in v0.1. Bindings should publish their kind catalog and mapping rules.
Examples from existing ecosystem graphs:
| Binding | Example node kinds |
|---------|-------------------|
| Railiance Fabric | `Repository`, `Service`, `Capability`, `Interface` |
| Repo-scoping | `Fact`, `Evidence`, `Feature`, `Capability`, `Ability`, `Scope` |
Bindings may attach additional kind metadata under `attributes.binding.*` but
must not overload `spec.node_kind` with multiple meanings.
---
## 5. Edges
An edge represents a relationship between exactly two nodes.
```yaml
kind: Edge
metadata:
id: railiance-platform.state-hub.requires.runtime-secrets
stable_key: railiance-platform.state-hub->runtime-secrets
spec:
edge_kind: requires
source_id: railiance-platform.state-hub
target_id: railiance-platform.openbao
direction: directed # directed | undirected
cardinality: many_to_one # optional: one_to_one | one_to_many | many_to_one | many_to_many
strength: required # optional qualitative or numeric weight
same_layer: false
attributes:
core:
criticality: high
environments: [dev, staging, prod]
provenance:
assertion_type: declared
sources:
- ref: fabric/dependencies/state-hub-runtime-secrets.yaml
method: declaration_load
observed_at: "2026-06-24T00:00:00Z"
confidence: 1.0
freshness_state: current
```
### 5.1 Required edge fields
| Field | Requirement |
|-------|-------------|
| `metadata.id` | Unique edge identifier within the graph. |
| `spec.edge_kind` | Domain-defined relationship type. |
| `spec.source_id` | Existing node `metadata.id`. |
| `spec.target_id` | Existing node `metadata.id`. |
| `spec.direction` | `directed` or `undirected`. |
| `provenance` | Same minimum provenance requirements as nodes. |
### 5.2 Edge semantics
- **Directed edges** express dependency, production, consumption, containment,
or influence from `source_id` to `target_id`.
- **Undirected edges** express equivalence, association, or co-location when
direction would be misleading.
- `spec.same_layer: true` marks intra-layer normalization or peer links that
should be visually and analytically distinct from cross-layer dependencies.
- `spec.strength` may be categorical (`required`, `optional`, `weak`) or
numeric. Bindings must document their scale.
### 5.3 Multi-edges
Multiple edges may connect the same node pair when they differ in `edge_kind`,
binding origin, or distinguishing attributes. Re-ingestion should update the
same logical edge in place when `metadata.stable_key` or a binding-supplied
dedupe key matches.
---
## 6. Attributes
Attributes carry descriptive, analytical, and presentational facts on nodes and
edges. They are grouped by namespace to keep the neutral core separable from
binding-specific and display-specific data.
### 6.1 Namespaces
| Namespace | Purpose | Examples |
|-----------|---------|----------|
| `core` | Stable descriptive fields useful across lenses | `description`, `owner`, `version` |
| `display` | Presentation hints for explorers | `label`, `color`, `visual_weight`, `display_state` |
| `analytical` | Metrics and derived indicators | `fan_in`, `cycle_member`, `cluster_id` |
| `binding` | Binding-private structured payload | Fabric deployment overlays, scanner hashes |
| `temporal` | Time-oriented fields | `valid_from`, `valid_to`, `observed_at` |
Namespaces are conventional in v0.1. Implementations may store them as nested
objects (`attributes.core.description`) or flattened keys with a prefix
(`core.description`).
### 6.2 Attribute records
Each attribute SHOULD be representable as:
```yaml
name: description
namespace: core
value: Runtime secrets service
value_type: string # string | number | boolean | enum | object | array | timestamp
cardinality: single # single | multi
mutable: true # whether re-ingestion may change this field in place
source: declared # declared | observed | derived | inferred
```
### 6.3 Typing and validation
v0.1 does not mandate a global attribute schema. Bindings SHOULD publish:
- allowed attributes per node/edge kind
- value types and enumerations
- required vs optional attributes
- deprecation notes for renamed attributes
Inspection operations must treat unknown attributes as opaque but returnable.
### 6.4 Display state
When a graph is prepared for interactive exploration, elements may carry a
display attribute:
| Value | Meaning |
|-------|---------|
| `show` | Fully visible with normal styling and labels. |
| `blur` | Visible but de-emphasized; details on hover or selection. |
| `hide` | Excluded from the active view but retained in the source graph. |
Display state is a vantage-point concern. It does not change graph truth data.
---
## 7. Provenance
Provenance makes graph facts auditable. Every node and edge MUST include a
provenance block sufficient to answer:
1. How was this fact introduced?
2. From what source material or observation?
3. How much should an operator trust it right now?
### 7.1 Assertion types
| Type | Meaning |
|------|---------|
| `declared` | Authored by a repo-local declaration or human assertion. |
| `observed` | Captured from runtime, repository scan, or external system query. |
| `derived` | Computed from other graph elements or transformations. |
| `inferred` | Produced by heuristic or ML extraction with weaker guarantees. |
### 7.2 Source records
Each `provenance.sources[]` entry SHOULD include:
| Field | Requirement |
|-------|-------------|
| `ref` | Pointer to source artifact (path, URL, export id, scan id). |
| `method` | Binding-specific ingestion or transformation step. |
| `observed_at` | ISO-8601 timestamp for when the source was read or captured. |
Optional source fields:
- `actor` — human, service, or agent that triggered ingestion
- `version` — source artifact version or commit
- `checksum` — content hash for reproducibility
- `notes` — free-text operator context
### 7.3 Confidence and freshness
- `confidence` is a float in `[0, 1]`. Bindings should define default confidence
by assertion type when not explicitly set.
- `freshness_state` is one of `current`, `stale`, or `unknown`.
- `freshness_evaluated_at` MAY record when freshness was last assessed.
Derived and inferred facts SHOULD reference upstream source ids or derivation
recipes under `provenance.derived_from[]`:
```yaml
provenance:
assertion_type: derived
derived_from:
- node_id: repo:railiance-fabric
- edge_id: railiance-fabric.state-hub.requires.runtime-secrets
derivation:
method: impact_closure
recipe_version: "1"
```
### 7.4 Lineage inspection
Provenance must be sufficient for the `explain` inspection operation (see
section 8.6) to reconstruct a human-readable chain from source artifacts to the
displayed fact.
---
## 8. Inspection operations
Inspection operations are read-only primitives over an NBGM graph. They are the
stable API surface between graph stores, agents, and vantage-point UIs.
Implementations MAY expose additional operations but SHOULD support the v0.1
baseline set or declare partial support explicitly.
### 8.1 Operation envelope
Operation requests and responses use a common envelope:
```yaml
apiVersion: vantage.nbgm/v0.1
kind: InspectionRequest
metadata:
operation: neighborhood
graph_id: example.ecosystem
spec:
parameters: {}
```
```yaml
apiVersion: vantage.nbgm/v0.1
kind: InspectionResponse
metadata:
operation: neighborhood
graph_id: example.ecosystem
spec:
complete: true
result: {}
warnings: []
```
### 8.2 `lookup`
**Purpose:** Fetch one node or edge by id or stable key.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `element_type` | yes | `node` or `edge` |
| `id` | one of | Primary identifier |
| `stable_key` | one of | Durable identifier |
**Result:** The matching element or `not_found`.
### 8.3 `neighborhood`
**Purpose:** Expand around a focus node to a controlled depth.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `focus_id` | yes | Starting node id |
| `depth` | yes | Hop count (1N) |
| `edge_kinds` | no | Relationship filter |
| `direction` | no | `out`, `in`, or `both` (default `both`) |
| `max_nodes` | no | Safety cap |
**Result:** Subgraph of nodes and edges reachable under the parameters.
### 8.4 `path`
**Purpose:** Find connecting paths between nodes for dependency or impact
analysis.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `source_id` | yes | Start node |
| `target_id` | yes | End node |
| `edge_kinds` | no | Allowed relationship types |
| `max_depth` | no | Search bound |
| `strategy` | no | `shortest`, `all_bounded`, or `weighted` |
**Result:** Zero or more paths, each an ordered list of node and edge ids.
### 8.5 `filter`
**Purpose:** Select a subgraph by declarative predicates.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `predicate` | yes | Structured filter over kinds, labels, attributes, lifecycle, freshness |
| `include_isolated` | no | Keep nodes with no matching edges (default false) |
**Result:** Induced subgraph containing matching elements and connecting edges
when requested.
### 8.6 `explain`
**Purpose:** Produce a human- and agent-readable justification for an element.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `element_type` | yes | `node` or `edge` |
| `id` | yes | Element identifier |
| `include_derivation` | no | Expand derived-from chain (default true) |
**Result:**
- element summary
- provenance sources and timestamps
- confidence and freshness assessment
- optional upstream path for derived/inferred facts
### 8.7 `summarize`
**Purpose:** Aggregate statistics for a subgraph or whole graph.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `scope` | no | Whole graph or subgraph selector |
| `metrics` | no | Requested aggregates |
**Default metrics:**
- counts by `node_kind` and `edge_kind`
- unresolved or stale fact counts
- top hubs by in-degree and out-degree
- connected component count
### 8.8 `compare`
**Purpose:** Diff two graph snapshots that share identity rules.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `baseline_graph` | yes | Reference graph id or snapshot |
| `candidate_graph` | yes | Graph to compare |
| `match_key` | no | `id` or `stable_key` (default `stable_key`) |
**Result:**
- `added`, `removed`, and `changed` nodes and edges
- attribute-level deltas for changed elements
- provenance changes when sources or confidence differ
### 8.9 `violations`
**Purpose:** Evaluate structural expectations declared by a binding or lens.
| Parameter | Required | Description |
|-----------|----------|-------------|
| `rule_set` | yes | Binding-published constraint set |
| `severity_at_least` | no | Minimum severity to return |
**Result:** List of violations with element refs, rule id, severity, and
recommended inspection follow-up.
### 8.10 Operation composition
Vantage points compose inspection operations rather than mutating the graph:
```text
filter(layer=service)
-> neighborhood(focus=selected, depth=2, edge_kinds=[requires])
-> summarize(metrics=[hub_rank])
-> explain(id=top_hub)
```
Bindings and lenses may publish recommended operation chains for common tasks.
---
## 9. Binding alignment
NBGM is intentionally abstract. Existing repo-native graph models should
project into it without losing source-of-truth boundaries.
### 9.1 Railiance Fabric mapping
| Fabric concept | NBGM element | Notes |
|----------------|--------------|-------|
| Declaration `metadata.id` | `Node.metadata.id` | Preserve dotted ids. |
| Declaration kind | `Node.spec.node_kind` | `Service`, `Capability`, etc. |
| Dependency requirement | `Edge.spec.edge_kind = requires` | Directed to provider capability or interface node. |
| Binding assertion | `Edge.spec.edge_kind = binds` | Resolves consumer requirement. |
| `metadata.source_links` | `provenance.sources[].ref` | Multiple links become multiple source records. |
| Graph export payload | NBGM graph envelope + elements | Explorer display fields map to `attributes.display`. |
Fabric remains authoritative for declarations. Vantage Point consumes exports as
an inspection-ready NBGM binding.
### 9.2 Repo-scoping mapping
| Repo-scoping layer | NBGM usage |
|--------------------|------------|
| `facts`, `evidence`, `features`, `capabilities`, `abilities`, `scope` | `Node.spec.layer` and `Node.spec.node_kind` |
| Evidence bridges | `Edge` with `edge_kind` reflecting support/challenge/link semantics |
| Display states in profiles | `attributes.display.display_state` |
### 9.3 Source-of-truth rule
Authoritative data lives in repo-native declarations, scans, or curated stores.
NBGM graphs are inspection-ready projections. Re-ingestion refreshes
projections; it does not rewrite authoritative sources.
---
## 10. Examples
### 10.1 Minimal service dependency graph
```yaml
apiVersion: vantage.nbgm/v0.1
kind: Graph
metadata:
id: demo.minimal
title: Minimal dependency demo
updated_at: "2026-06-24T00:00:00Z"
spec:
default_direction: directed
elements:
- kind: Node
metadata:
id: consumer.app
name: Consumer App
spec:
node_kind: Service
provenance:
assertion_type: declared
sources:
- ref: fabric/services/consumer.yaml
method: declaration_load
observed_at: "2026-06-24T00:00:00Z"
- kind: Node
metadata:
id: provider.db
name: Database
spec:
node_kind: Service
provenance:
assertion_type: declared
sources:
- ref: fabric/services/database.yaml
method: declaration_load
observed_at: "2026-06-24T00:00:00Z"
- kind: Edge
metadata:
id: consumer.app.requires.provider.db
spec:
edge_kind: requires
source_id: consumer.app
target_id: provider.db
direction: directed
provenance:
assertion_type: declared
sources:
- ref: fabric/dependencies/consumer-db.yaml
method: declaration_load
observed_at: "2026-06-24T00:00:00Z"
```
### 10.2 Neighborhood inspection
```yaml
apiVersion: vantage.nbgm/v0.1
kind: InspectionRequest
metadata:
operation: neighborhood
graph_id: demo.minimal
spec:
parameters:
focus_id: consumer.app
depth: 2
direction: out
edge_kinds: [requires]
```
---
## 11. Open questions for v0.2
- Canonical serialization format (single JSON schema vs multi-document YAML)
- Global registries for cross-domain `node_kind` and `edge_kind` aliases
- Lens and vantage-point configuration schema
- Standard binding interface for incremental graph refresh
- Normative confidence and freshness scoring recipes per assertion type
---
## 12. References
- `INTENT.md` — project purpose and guiding principles
- `SCOPE.md` — repository boundary
- Meyer, Sedlmair, Munzner — *The Four-Level Nested Model Revisited: Blocks and Guidelines* ([PDF](https://miriah.github.io/publications/nbgm-beliv.pdf))
- Railiance Fabric — `docs/declaration-schema.md`, `docs/graph-explorer-contract.md`
- Repo-scoping — dependency visualization layer model (`RREG-WP-0010`)