Metadata folder structure
The metadata/ folder contains instances of Types that mix the Metadata base Type. The C3 Agentic AI Platform loads these instances at runtime to provide provisioned configuration and data for your application. This topic explains what belongs in this folder, how the C3 Agentic AI Platform discovers and validates metadata, and how instances are loaded and remixed across packages.
Understand what belongs in metadata/
The metadata folder stores instances of Types that mix Metadata. These are provisioned data instances loaded during package initialization, such as roles, translations, transforms, and other configuration data.
Examples:
metadata/
Role/
DataAnalyst.json
SystemAdmin.json
Translation/
en.csv
fr.csv
Transform/
CanonicalAsset-Asset.js
CanonicalWorkOrder-WorkOrder.jsType requirements
Types stored in the metadata folder must meet two requirements:
- Mix the Metadata base type
- Mix either Named (using
namefield) or Identified (usingidfield)
These mixins enable the C3 Agentic AI Platform to identify and retrieve instances at runtime.
File formats
The C3 Agentic AI Platform supports three file formats for metadata:
JSON files (*.json)
JSON is the standard format for metadata instances. Each file contains a single instance or an array of instances.
metadata/Role/DataAnalyst.json
{
"id": "DataAnalyst",
"description": "Access to data analysis tools and reports.",
"nestedRoles": ["BasicUser", "ReportViewer"],
"permissions": ["allow:Dataset::read"]
}Single instance: The key field (id or name) should match the filename.
Multiple instances: Use an array to define multiple instances in one file.
metadata/Role/AnalystRoles.json
[
{
"id": "DataAnalyst",
"description": "Access to data analysis tools."
},
{
"id": "ReportAnalyst",
"description": "Access to report generation."
}
]CSV files (*.csv)
CSV format is efficient for bulk metadata instances. Each row after the header creates one instance of the Type. The first row defines column headers that map to Type fields.
metadata/Translation/en.csv
id,locale,key,value
button.save.label,en,button.save.label,Save Changes
button.delete.label,en,button.delete.label,Delete ItemThis CSV file creates two Translation instances:
Row 1 (header): Defines the field mappings - id, locale, key, and value correspond to Translation fields.
Row 2: Creates a Translation instance with:
id:"button.save.label"locale:"en"key:"button.save.label"value:"Save Changes"
Row 3: Creates a Translation instance with:
id:"button.delete.label"locale:"en"key:"button.delete.label"value:"Delete Item"
JavaScript files (*.js)
JavaScript files provide metadata instances using expressions and functions. The file must set a data variable containing the instance definition.
metadata/Transform/CanonicalAsset-Asset.js
data = {
name: 'CanonicalAsset-Asset',
source: 'CanonicalAsset',
target: 'Asset',
projection: {
assetId: id,
assetName: name
}
};JavaScript files can:
- Use functions and expressions for dynamic values.
- Reference enum values.
- Call helper functions.
- Only work with Types that mix JsSerializable.
How metadata/ relates to other folders
Several folders depend on or interact with the content in your metadata/ folder.
Source folder (
src/) The metadata folder references Types declared insrc/. When you change a Type definition, the C3 Agentic AI Platform invalidates dependent metadata instances and revalidates them against the new Type structure. See Source Folder.Test folder (
test/) Test metadata intest/metadata/remixes base metadata for testing. The C3 Agentic AI Platform merges test metadata with base metadata at the field level, allowing you to override specific fields without duplicating entire instances. See Test Folder.Seed folder (
seed/) Both metadata and seed store Type instances, but they serve different purposes. Metadata contains provisioned configuration loaded at runtime. Seed data contains initial persistent data loaded into the database. See Seed Folder.
These interactions make the metadata/ folder a key part of your package's runtime configuration.
Structural rules
The C3 Agentic AI Platform enforces structural rules in the metadata/ folder to discover instances, validate them, and load them reliably.
Path conventions
The directory structure determines which Type the metadata belongs to.
Standard path structure:
/packageName/metadata/TypeName/instanceName.extThe directory name (second-to-last path segment) identifies the Type. For example:
metadata/Role/DataAnalyst.json: The Type is Role.metadata/Transform/CanonicalAsset-Asset.js: The Type is Transform.
Generic Types: Generic Types must specify their type parameters in the type field.
metadata/MyGenericType/instance.json
{
"type": "MyGenericType<Obj>",
"id": "instance",
...
}If you omit the type parameter, the C3 Agentic AI Platform generates a validation warning.
Custom base folders: Types can declare a custom base folder using the @metadata annotation.
@metadata(baseFolder='custom/path/')
type CustomMetadata mixes Metadata, Named {
...
}Instances for this Type belong in custom/path/CustomMetadata/ instead of metadata/CustomMetadata/.
File naming
Instance filenames should match the key field value for single-instance files.
Correct:
metadata/Role/DataAnalyst.json
{
"id": "DataAnalyst",
...
}Warning: If the filename and key field don't match, the C3 Agentic AI Platform generates a validation warning but still loads the instance.
metadata/Role/Analyst.json
{
"id": "DataAnalyst", // Mismatched key
...
}Multi-instance files: Files containing arrays can use any filename since they define multiple instances.
Validation rules
The C3 Agentic AI Platform validates metadata files during package loading and reports issues through Pkg.Issue.
Below are examples of validation rules:
Required fields: Missing required fields generate errors.
// Error: Missing required field
{
"id": "DataAnalyst"
// Missing required "description" field
}Invalid fields: Fields not declared in the Type generate errors.
// Error: Invalid field
{
"id": "DataAnalyst",
"description": "Analyst role",
"invalidField": true // Type does not declare this field
}Type mismatches: Field values must match the declared Type.
// Error: Type mismatch
{
"id": "DataAnalyst",
"permissions": "should be array" // Field expects Array<string>
}Syntax errors: Invalid JSON or JavaScript syntax generates errors.
// Error: Syntax error
data = {
name: 'Transform'
source: 'Missing comma' // Syntax error
}How metadata is loaded
The C3 Agentic AI Platform loads metadata through file discovery, caching, and remix resolution. These processes determine which instances are available at runtime and ensure changes appear immediately during development.
How metadata files load
The C3 Agentic AI Platform loads metadata during package initialization. Once loaded, instances are cached until a Type or file change invalidates them.
Initial load: When you start an application or the C3 Agentic AI Platform loads a package, it:
- Discovers all metadata files for Types mixing Metadata.
- Parses and validates each file.
- Creates instances and caches them by Type and key.
- Makes instances available through
forName(),forId(), andmetadata()APIs.
Hot reloading: In development mode, changes to metadata files trigger immediate revalidation and cache invalidation. Your changes appear instantly in the Console and through APIs.
Example:
save metadata/Role/DataAnalyst.json -> UPDATE event
invalidate Role instances -> reload -> new instance available in Console and through APIsCache invalidation: The C3 Agentic AI Platform invalidates cached instances when:
- The metadata file changes.
- The Type definition changes.
- A referenced Type changes (cascading invalidation).
Remix and overlays
When multiple packages define metadata for the same Type and key, the C3 Agentic AI Platform merges them into a single effective instance using field-level merging.
Remix ordering: The C3 Agentic AI Platform applies remixes in reverse dependency order—dependent packages can override base package metadata.
Example package dependency chain:
yourPkg
└─ depends on basePkgbasePkg/metadata/Transform/CanonicalWorkOrder-WorkOrder.json (base definition):
{
"name": "CanonicalWorkOrder-WorkOrder",
"source": "CanonicalWorkOrder",
"target": "WorkOrder",
"projection": {
"workOrderNumber": "workOrderNum",
"type": "workOrderType",
"priority": "priority",
"startDate": "actualStartDatetime"
}
}yourPkg/metadata/Transform/CanonicalWorkOrder-WorkOrder.json (remix adds field):
{
"name": "CanonicalWorkOrder-WorkOrder",
"projection": {
"finishDate": "actualFinishDatetime",
"location": "functionalLocation"
}
}Effective merged instance at runtime:
{
"name": "CanonicalWorkOrder-WorkOrder",
"source": "CanonicalWorkOrder",
"target": "WorkOrder",
"projection": {
"workOrderNumber": "workOrderNum",
"type": "workOrderType",
"priority": "priority",
"startDate": "actualStartDatetime",
"finishDate": "actualFinishDatetime",
"location": "functionalLocation"
}
}The dependent package extends the instance without modifying the base definition. Fields from later packages override earlier ones if they conflict.
Test overlays: Metadata in test/metadata/ remixes base metadata in test mode only. This pattern lets you override configuration for testing without affecting production.
metadata/Transform/CanonicalAsset-Asset.json (base)
test/metadata/Transform/CanonicalAsset-Asset.json (test overlay)In test mode, the C3 Agentic AI Platform merges both files using JSON merging. In production mode, only the base file loads.
Runtime operations
The C3 Agentic AI Platform provides APIs for accessing, updating, and removing metadata instances at runtime.
Access instances
Retrieve instances by key using the Named or Identified mixin methods.
// Access by name (for Types mixing Named)
Role role = Role.forName("DataAnalyst");
// Access by id (for Types mixing Identified)
Transform transform = Transform.forId("CanonicalAsset-Asset");
// Get all instances for a Type
Map<string, Role> roles = pkg().metadata(Role.myType());Update instances
In development mode, use upsert() to create or update metadata instances. The C3 Agentic AI Platform writes changes to the metadata file immediately.
// Create or update instance
Role.make()
.withId("NewAnalyst")
.withDescription("New analyst role")
.upsert();This creates or updates metadata/Role/NewAnalyst.json in your package.
Remove instances
Use remove() or removeAll() to delete metadata instances. The C3 Agentic AI Platform deletes the actual files from your package.
// Remove single instance
Role analyst = Role.forName("DataAnalyst");
analyst.remove();
// Remove all matching instances
Role.removeAll("description contains 'analyst'");Dependency tracking: When you remove an instance that other instances reference, the C3 Agentic AI Platform invalidates dependent instances and triggers revalidation.