The problem: representation drift

Modern teams describe their products in code, documentation, whiteboards, and notebooks. Each representation drifts from the others. The Python model says one thing, the Dart model says another, and the SQL schema diverges from both. Add AI agents to the mix and the problem compounds — agents need a single, authoritative description of the domain to operate over it reliably.

The Object Config Graph (OCG) is Aware's answer: a single grammar, a single compiler, and a single canonical graph that becomes the source of truth for every language, every runtime, and every actor in the system.

The pipeline at a glance

.aware Grammar
OCG Compiler
Graph Canonical
Materialize Py / Dart / SQL
Runtime Handlers

Everything starts in .aware files. The OCG compiler parses them into a canonical graph of nodes, relationships, functions, and enums. The materialization pipeline then generates typed packages for each target language. Runtime handlers import those packages directly. No hand-editing. No drift.

The grammar: describe the world

The .aware grammar is a minimal, language-agnostic alphabet for describing domains. Types, attributes, relationships, functions — nothing more. It deliberately avoids runtime quirks so that anyone can describe "this is a thing, these are the bridges it has to others" and know the compiler will understand the same structure.

.aware
class Tool {
    // Relationships
    service_operation_config economy.service.ServiceOperationConfig?
    tool_calls ToolCall[]

    // Attributes
    description String
    name String
    tool_schema Json
}

This is a real definition from the Aware codebase. It declares a Tool with an optional relationship to a service operation config (?), a list of tool calls ([]), and three scalar attributes. The grammar encourages clear habits:

  • ? marks optional fields
  • [] marks lists
  • unique and key declare identity constraints
  • Functions declare typed inputs and tuple outputs
  • Cross-module references use dot notation: economy.service.ServiceOperationConfig

Functions as contracts

Functions in the grammar are not implementations — they are behavior signatures. The grammar declares what a function accepts, what it returns, and its contract. The implementation lives in language-specific handlers.

.aware
class Agent {
    identity identity.Identity unique key
    processes agent.process.AgentProcess[]
    key String key = "default"

    fn build construct (
        identity_id UUID key,
        key String key = "default"
    ) -> Agent {
        """
        Creates or ensures an Agent for an Identity.
        IDs are deterministic from propagated scope + (identity_id, key).
        """
    }

    fn create_process (
        key String = "default",
        name String = "default"
    ) -> agent.process.AgentProcess {
        let created = construct processes.build(
            key = key, name = name,
        )
    }
}

The build function is a construct — a deterministic constructor whose output ID is derived from its key inputs. The create_process function shows propagation: it calls processes.build() to construct a child entity within the parent's scope. These patterns are enforced by the grammar, not left to convention.

The compiler: grammar to canonical graph

The OCG compiler reads .aware files and produces a canonical graph — a data structure containing every node, enum, function, and relationship in the domain. Each element gets a stable ID that persists across edits, renames, and migrations.

Canonical-only contract: only one canonical OCG persists. Per-language graphs are ephemeral projections. Generated code is never hand-edited. This is a hard rule.

The compiler produces several key artifacts:

  • Nodes — each class becomes a canonical node with stable ID
  • Relationships — edges between nodes with cardinality and constraints
  • Functions — behavior signatures with typed IO
  • Enums — canonical option sets
  • Changes & commits — every edit is recorded as a graph change

Augmentation and overlay builders allow layering runtime behavior (e.g., lazy loading annotations, reverse relationship hints) without mutating the canonical graph itself.

Materialization: one graph, all languages

The materialization pipeline takes the canonical OCG and generates typed packages for each target language. Today that means Python, Dart, and SQL. The pipeline runs as a sequence of stages:

Build Load OCG
Migrate Apply changes
Materialize Generate code
Package Typed imports

The output for each language is a typed, importable package with consistent roots. Python gets dataclass models and typed function signatures. Dart gets freezed models and function stubs. SQL gets table definitions and relationship constraints.

What materialization looks like

From the Tool definition above, the Python materializer generates a typed model:

Python
@dataclass
class Tool:
    description: str
    name: str
    tool_schema: dict
    service_operation_config: Optional[ServiceOperationConfig] = None
    tool_calls: list[ToolCall] = field(default_factory=list)
SQL
CREATE TABLE tool (
    id         UUID PRIMARY KEY,
    description TEXT NOT NULL,
    name       TEXT NOT NULL,
    tool_schema JSONB NOT NULL,
    service_operation_config_id UUID REFERENCES service_operation_config(id)
);

Same source. Same structure. Different languages. The developer never writes these by hand — they are generated, deterministic, and always in sync with the canonical graph.

The Object Projection Graph: scoped views

Not every actor needs to see the full canonical graph. The Object Projection Graph (OPG) is a scoped view that determines what a given actor — human or agent — can see and interact with. Same OCG underneath, different projections on top.

Projections are identified by a projection_hash — a deterministic hash of the projection's scope parameters. This means two actors with the same permissions see the exact same projection, while a restricted actor sees a narrower slice. The projection is not a copy; it is a view.

OPG bridges config and instance: the Object Config Graph defines what exists. The Object Projection Graph defines what an actor can see. The Object Instance Graph (next post) defines what has happened. Together they form the full canonical substrate.

Why this matters for AI

AI agents operate over the same canonical graph that humans use. Because the OCG is language-agnostic and fully typed, agents can:

  • Read the domain structure without parsing source code
  • Call typed functions with known input/output contracts
  • Propose graph changes that the compiler validates
  • See the same map the human sees — no translation layer

Multi-provider by design: any model (OpenAI, Anthropic, Google, open-source) calls the same typed API. The canonical graph does not care which model generated the call. It cares that the call is valid, typed, and attributed.

Why this matters for services

The OCG is the foundation of the service packaging model. When a company builds a service in Workspace, the .aware grammar defines the service's domain. The compiler turns it into a canonical package. That package is what gets published to Hub and distributed through the Network.

Because every service shares the same canonical substrate, services can compose with each other. A pricing service can reference a smart contract service. An agent service can call any other service's typed functions. The shared ontology is the interoperability rail.

One grammar. One compiler. One graph. All languages. All actors. All services. That is the Object Config Graph.