# VL Metadata Spec 3.0

> Status: canonical metadata schema for VLCode-Lite and DocCenter Path `4`
>
> Scope:
> - Defines the canonical `ProjectMeta` JSON consumed by metadata diff, workflow regeneration, project context, and DocCenter-backed tooling
> - Normalizes legacy extractor output into one stable shape
> - Aligns Theme metadata with VL 3.7 / THEME 6.6

## 1. Purpose

`ProjectMeta` is the machine-readable project model for a VL workspace.

It exists to support:

- deterministic workflow regeneration
- project-wide diff and impact analysis
- DocCenter-backed prompt context
- IDE visualization and debug tooling
- stable references across `Apps/`, `Sections/`, `ExtComponents/`, `Services/`, `Database/`, and `Theme/`

This spec defines the canonical schema. Legacy field names may still be accepted by importers, but they are not canonical output.

## 2. Canonical Rules

### 2.1 Root schema

Canonical root object:

```json
{
  "$schema": "VL-ProjectMeta/3.0",
  "projectName": "SmartCampus",
  "projectDescription": "optional",
  "vlVersion": "3.7",
  "config": {
    "deviceTarget": "web",
    "screenResolution": "1440x900"
  },
  "fileManifest": [],
  "valueDomains": {},
  "apps": [],
  "sections": [],
  "components": [],
  "services": [],
  "dataSchema": {
    "tables": [],
    "relations": []
  },
  "theme": null,
  "dependencyGraph": null
}
```

### 2.2 Identifier policy

- `apps[*].id` is the canonical app identifier
- `sections[*].id` is the canonical section identifier
- `components[*].id` is the canonical component identifier
- `services[*].domainId` is the canonical service-domain identifier
- `services[*].methods[*].id` is the canonical method identifier
- `services[*].methods[*].serviceId` is the canonical fully-qualified service identifier, normally `domainId.methodId`
- `dataSchema.tables[*].id` is the canonical table identifier
- `theme.id` is the canonical theme identifier

Legacy aliases such as `appId`, `sectionId`, `componentId`, `tableName`, `serviceDomains`, `servicesUsed`, and `componentRefs` are compatibility inputs only.

### 2.3 File path policy

Canonical default file locations:

- app: `Apps/<id>.vx`
- section: `Sections/<id>.sc`
- component: `ExtComponents/<id>.cp`
- service: `Services/<domainId>.vs`
- database: `Database/<projectName>.vdb` or explicit per-table/file-level mapping when available
- theme: `Theme/Theme.vth`

If `filePath` is omitted during normalization, tooling may synthesize the conventional path above.

## 3. Entity Schemas

### 3.1 App

```json
{
  "id": "AdminApp",
  "filePath": "Apps/AdminApp.vx",
  "vlVersion": "3.7",
  "device": "web",
  "resolution": "1440x900",
  "description": "",
  "globalVars": [],
  "pages": [
    {
      "id": "Dashboard",
      "route": "/dashboard",
      "sections": ["DashboardMain"],
      "sectionRefs": [
        {
          "sectionId": "DashboardMain",
          "instanceId": "mainSection",
          "layoutProps": {}
        }
      ],
      "componentRefs": [],
      "layout": []
    }
  ],
  "routeMap": {
    "/dashboard": "DashboardMain"
  },
  "homeRoute": "/dashboard",
  "wiring": [],
  "navSectionInstanceId": null,
  "navEventName": null
}
```

Rules:

- `pages[*].id` must be stable inside the app
- `pages[*].sections` contains section IDs only
- `pages[*].sectionRefs[*].sectionId` must resolve to an existing section
- `routeMap` is a convenience index, not a second source of truth

### 3.2 Section

```json
{
  "id": "DashboardMain",
  "filePath": "Sections/DashboardMain.sc",
  "vlVersion": "3.7",
  "previewSize": null,
  "publicProps": [],
  "publicEvents": [],
  "publicMethods": [],
  "globalVars": [],
  "derivedVars": [],
  "consumesServices": [
    "CampusService.getOverview",
    "CampusService.listAlerts"
  ],
  "usesComponents": [
    "StatCard",
    "AlertTable"
  ],
  "interactiveElements": [],
  "internalMethods": [],
  "pipeFuncs": [],
  "isNavSection": false,
  "navMenuItems": [],
  "navItemInstanceId": null,
  "keyStates": {},
  "description": ""
}
```

Rules:

- `consumesServices` is an array of service ID strings, not nested service objects
- `usesComponents` is an array of component ID strings, not component ref objects
- `consumesServices[*]` must resolve to an existing `services[*].methods[*].serviceId`
- `usesComponents[*]` must resolve to an existing `components[*].id`

### 3.3 Component

```json
{
  "id": "StatCard",
  "filePath": "ExtComponents/StatCard.cp",
  "vlVersion": "3.7",
  "previewSize": null,
  "publicProps": [],
  "publicEvents": [],
  "derivedVars": [],
  "interactiveElements": [],
  "internalMethods": [],
  "pipeFuncs": [],
  "description": ""
}
```

### 3.4 Service domain

```json
{
  "domainId": "CampusService",
  "filePath": "Services/CampusService.vs",
  "vlVersion": "3.7",
  "envVars": [],
  "methods": [
    {
      "id": "getOverview",
      "serviceId": "CampusService.getOverview",
      "type": null,
      "params": [],
      "returns": {},
      "expose": null,
      "sig": null
    }
  ],
  "virtualTables": [
    {
      "id": "OverviewView",
      "source": "campus_overview",
      "fields": ["id", "title"],
      "extraSpecs": {}
    }
  ],
  "transactions": [],
  "backendComponents": []
}
```

Rules:

- `domainId` is canonical; `id`/`name` are compatibility inputs only
- `methods[*].id` is canonical; `methods[*].name` is compatibility input only
- `methods[*].serviceId` should be emitted even when it can be derived
- `virtualTables[*].source` must reference an existing table ID when it points to a physical table

### 3.5 Database schema

```json
{
  "dataSchema": {
    "tables": [
      {
        "id": "campus_overview",
        "name": "campus_overview",
        "filePath": "",
        "fields": [
          {
            "name": "id",
            "type": "STRING",
            "notNull": true,
            "default": null,
            "enumRef": null,
            "sourceField": null
          }
        ],
        "indexes": [],
        "seedData": null
      }
    ],
    "relations": [
      {
        "id": "overview_to_building",
        "from": "campus_overview",
        "to": "building",
        "cardinality": "N:1"
      }
    ]
  }
}
```

Rules:

- canonical table key is `id`, not `tableName`
- canonical column collection is `fields`; `columns` is compatibility input only
- `enumRef` must resolve into `valueDomains.enums[*].name` when used

### 3.6 Theme

```json
{
  "theme": {
    "id": "Theme",
    "name": "Theme",
    "filePath": "Theme/Theme.vth",
    "vlVersion": "3.7",
    "rootTag": "Theme-Enterprise-6.6",
    "meta": {
      "mode": "light",
      "version": "6.6.0",
      "styleSpaceVersion": "1.6",
      "base_theme": "Platform/Theme-Default-Light@1",
      "profile": "enterprise"
    },
    "slots": {
      "intent.primary.intentBg": "#2563EB",
      "size.md.sizeMinHeight": "40px",
      "state.focus.stateShadow": "@intent.intentFocusRing"
    },
    "designTokens": [],
    "componentVariants": [],
    "bindingRules": [],
    "overrides": []
  }
}
```

Rules:

- for VL 3.7 / THEME 6.6, the canonical theme model is `# Meta` plus `# Point Slot Values`
- `theme.slots` is the canonical compiled metadata view of `.vth` point-slot assignments
- `designTokens`, `componentVariants`, `bindingRules`, and `overrides` are compatibility carry-through fields only; new tooling should not require them

## 4. Consistency Constraints

Validation must reject or flag at least the following:

- section consumes a non-existent service ID
- section uses a non-existent component ID
- service virtual table points at a non-existent table
- app wiring references an instance ID not present in layout refs
- field `enumRef` points at a non-existent enum

## 5. Legacy Compatibility Mapping

Normalizers may accept the following legacy inputs:

| Legacy input | Canonical output |
| --- | --- |
| `project.name` / `project.projectName` | `projectName` |
| `database` | `dataSchema` |
| `serviceDomains` | `services` |
| `appId` | `apps[*].id` |
| `sectionId` | `sections[*].id` |
| `componentId` | `components[*].id` |
| `servicesUsed` / `services` / `serviceDomains` on section | `consumesServices` |
| `componentRefs` / `components` on section | `usesComponents` |
| `tableName` / `entityId` | `dataSchema.tables[*].id` |
| `columns` | `fields` |
| `methods[*].name` | `methods[*].id` |
| `theme.pointSlotValues` / `theme.pointSlots` | `theme.slots` |

Compatibility input does not change canonical output names.

## 6. Producer Requirements

Any producer that claims compliance with Metadata Spec 3.0 must emit:

- `$schema: "VL-ProjectMeta/3.0"`
- canonical IDs and field names
- `sections[*].consumesServices` as string IDs
- `sections[*].usesComponents` as string IDs
- `services[*].methods[*].id`
- `dataSchema.tables[*].id`
- `theme.slots` when theme slot data is available

## 7. Consumer Requirements

Consumers should:

- read canonical fields first
- optionally normalize known legacy aliases on import
- never write new metadata using legacy key names
- treat DocCenter path references and workflow prompts as external configuration, not as metadata schema fields

## 8. Canonical Schema String

Use exactly:

```text
VL-ProjectMeta/3.0
```
