Shapes, Observations, and Expressions
Cohesive Core is the foundational semantic block of the Cohesive toolchain.
It defines the shared language used by higher-level Cohesive libraries to describe data, facts, computation, and behavior. At the center of that language are three ideas:
- Shapes: semantic definitions of structured data.
- Observations: concrete facts captured against a shape.
- Expressions: portable computations over observations, parameters, fields, and derived values.
Together, these form the substrate used by Cohesive.Relations, Cohesive.Transitions, Cohesive.Processes, and Cohesive.Presentation. Relations project and transform observations. Transitions mutate entity state through expression-defined preconditions and updates. Processes coordinate work across transitions, effects, reads, queries, and waits. Presentation projects system semantics into user-facing views, actions, forms, and workflows.
The core goal is simple: define meaning once, then attach different interpretations to it.
- Shapes, Observations, and Expressions
- Why the Core Block Matters
- Shapes
- Shape Graphs
- Observations
- Observation Values
- Expressions
- Why Expressions Are Shared
- Use in Cohesive.Relations
- Use in Cohesive.Entities
- Use in Cohesive.Processes
- Use in Cohesive.Presentation
- A Common Semantic Substrate
- What This Enables
- References
Why the Core Block Matters
Most systems carry their meaning implicitly.
A field name in a DTO means one thing in an API, another thing in a database row, and something else in a frontend form. A workflow step might depend on an entity state field, but that dependency is hidden inside application code. A mapping might compute a target value, but the lineage of that computation is not preserved. A UI action might submit a value, but the relationship between the action, the input shape, and the backend transition is scattered across separate code paths.
Cohesive Core exists to make those meanings explicit.
It gives the rest of the toolchain a common vocabulary for structured data and computation. That lets each higher-level layer preserve semantic intent instead of collapsing immediately into transport-specific, framework-specific, or vendor-specific code.
Shapes
A Shape is a semantic definition of structured data.
It identifies a kind of value or entity state, declares its fields, records shape-level constraints, and carries annotations that can attach domain meaning, role information, or adapter-specific extension data.
At a practical level, a shape answers questions like:
- What is this structured value?
- What fields does it expose?
- Which field names are canonical?
- Which constraints apply?
- What role does this shape play in the broader system?
- Which annotations should interpreters preserve?
Shapes are deliberately more semantic than a raw JSON schema and more portable than a single CLR or TypeScript type. A shape can be generated from code, serialized as part of a shape graph, projected into frontend contracts, used to validate observations, referenced by relation definitions, and connected to transition or presentation models.
In Cohesive, the shape is the anchor. It gives other systems a stable way to talk about structure without committing too early to one runtime representation.
Shape Graphs
A single shape is useful, but systems usually need many related shapes.
A shape graph captures the collection of shapes that make up a semantic model. It allows the toolchain to reason over related definitions, named type references, constraints, evolution, generated contracts, and validation behavior.
That graph can be used to support:
- Type and contract generation.
- Shape comparison and evolution.
- Observation validation.
- Relation source and target definitions.
- Transition entity state definitions.
- Presentation form and view bindings.
- Adapter projections into storage, APIs, frontend runtimes, and workflow systems.
This matters because Cohesive is designed for incremental adoption. Teams can start with a few shapes, then gradually attach relations, transitions, processes, presentation surfaces, and infrastructure adapters as the model becomes more valuable.
Observations
An Observation is a concrete fact captured against a shape.
It carries:
- A
ShapeId, identifying the shape it claims to observe. - A stable observation identity.
- A version for incremental cache invalidation and state tracking.
- Field values keyed by canonical field name.
- An indexed field layout for efficient access.
- Optional lineage metadata explaining how emitted fields were produced.
Today, Observation lives in Cohesive.Relations.Model, while ObservationValue already lives in Cohesive.Model. Architecturally, observation belongs with the core block and is expected to move toward Cohesive Core because it is not relation-specific. Relations use observations, but so do transitions, processes, presentation, validation, mapping, and entity-state snapshots.
An observation is not just "some JSON." It is a shaped payload with identity, version, layout, field access, validation, and lineage.
That distinction is important. It allows the system to preserve the difference between:
- The semantic shape of a thing.
- A concrete observed instance of that thing.
- The values within the instance.
- The computation or source lineage that produced those values.
Observation Values
ObservationValue is the compact value container used inside observations and entity-state snapshots.
It represents JSON-like values while preserving important scalar kinds:
- undefined and null
- booleans
- integers and floating-point numbers
- strings
- temporal values
- bytes
- arrays
- objects
Because ObservationValue is shared by the core model, expression language, relations, and transitions, values can move through the system without being repeatedly converted through loosely typed runtime objects.
That gives Cohesive a common value representation for validation, expression evaluation, mapping, state updates, relation execution, presentation projection, and serialization.
Expressions
The Cohesive expression language is the portable computation IR for the core model.
An Expr can reference fields, parameters, constants, typed field references, literals, unary operators, binary operators, conditionals, function calls, aggregates, joins, grouping, and related-field access.
Expressions are used when the system needs a computation that should remain inspectable and portable instead of disappearing into opaque code.
Examples include:
- Mapping a source field into a target field.
- Computing a derived relation value.
- Checking a transition precondition.
- Producing a transition field update.
- Building an effect payload.
- Deriving presentation state.
- Controlling action enablement or visibility.
- Supplying data-source parameters.
- Computing metrics or labels.
The expression language is intentionally an IR rather than just an authoring syntax. Higher-level authoring APIs can compile familiar C# expressions or domain-specific builders into Expr, but the result is a semantic tree that can be serialized, inspected, validated, transformed, evaluated, or projected.
Why Expressions Are Shared
The same computation often matters to multiple parts of the system.
A relation may compute a canonical field from a source payload. A transition may later depend on that canonical field. A process may route based on transition output. A presentation view may display the same derived value or disable an action based on the same predicate.
If each layer owns its own expression model, those meanings drift.
Cohesive uses a shared expression IR so the system can preserve:
- Field dependencies.
- Parameter dependencies.
- Return type hints.
- Read sets and write sets.
- Lineage and traceability.
- Projection into multiple runtimes.
- Validation against known shapes.
- Future optimization opportunities.
This is a key architectural point: expressions are not only for execution. They are also metadata about behavior.
Use in Cohesive.Relations
Cohesive.Relations uses shapes, observations, and expressions to define semantic projections between structured data.
A relation consumes one or more source observations and emits target observations. Field assignments use Expr to compute target values from source fields, constants, joins, aggregates, functions, or related observations.
This makes a relation more than a mapper:
- Source and target shapes are explicit.
- Assignments are inspectable.
- Field dependencies can be traced.
- Output observations can carry lineage.
- Relation execution can be planned.
- Hydration requirements can be inferred.
- Mapping behavior can be serialized and replayed.
This is why Ari can build automated relation inference on top of Cohesive.Relations. The relation model has a real semantic surface: shapes define what can be mapped, observations provide facts, and expressions describe the computations that connect them.
Use in Cohesive.Entities
Cohesive.Transitions uses the same core block to model entity state changes.
Entity state is represented as an observation carrying identity, version, and canonical field values. A transition definition declares inputs, preconditions, updates, effects, read sets, and write sets.
Expressions appear in several places:
- Preconditions decide whether a transition may execute.
- Field update expressions compute new state values.
- Effect payload expressions describe emitted side effects.
- Field references allow read sets to be inferred.
- Update targets define write sets.
Because transitions operate over shaped observations, they can be validated against entity definitions and reasoned about as semantic state changes rather than arbitrary mutations.
This supports a cleaner model of invariants, determinism, concurrency, auditability, and generated infrastructure bindings.
Use in Cohesive.Processes
Cohesive.Processes builds on the same foundation for multistep workflows.
A process can coordinate reads, creates, queries, effects, transition invocations, waits, and terminal outputs. Process nodes use expressions or expression-like authoring delegates to resolve request payloads, entity references, transition inputs, wait keys, query values, and results from the current execution context.
The long-term direction is to keep process behavior connected to the shared semantic model:
- Inputs and outputs are shaped.
- Entity state is observed.
- Transition invocations are explicit.
- Effect payloads can be described semantically.
- Process determinism can be tested.
- Runtime infrastructure can interpret the same process model.
Processes are where the system model becomes temporal. Shapes describe what exists, observations capture what is true, expressions compute what should happen, and processes coordinate when work happens.
Use in Cohesive.Presentation
Cohesive.Presentation uses the core block to declare user-facing surfaces from backend-owned semantics.
Presentation modules can reference shapes, data sources, fields, forms, views, actions, flows, workspaces, and target bindings. Presentation expressions use the canonical Cohesive Expr IR for predicates, projections, metrics, visibility, enablement, labels, navigation, data-source parameters, and state derivation.
That lets product surfaces stay tied to the same source of truth as the backend:
- A form can edit a value with a known shape.
- A query form can lower input into a relation query.
- A view can bind to a shaped data source.
- An action can expose semantic enablement and endpoint projection.
- A route can target a semantic page host.
- Tests can use stable presentation identifiers instead of incidental CSS.
The presentation layer becomes another interpretation of the semantic model rather than a separate frontend-only reinvention of it.
A Common Semantic Substrate
The core Cohesive block is intentionally small but consequential.
It gives the rest of the toolchain a shared semantic substrate:
- Shapes define structure.
- Observations capture shaped facts.
- Observation values represent portable runtime values.
- Expressions describe portable computation.
- Shape graphs organize related definitions.
- Annotations carry extension meaning without collapsing the model into one adapter.
From there, each Cohesive library attaches a different interpretation:
- Relations interpret the core model as projection and mapping.
- Transitions interpret it as state change and invariants.
- Processes interpret it as coordinated temporal behavior.
- Presentation interprets it as user-facing product surfaces.
- Adapters interpret it as storage, APIs, messaging, runtimes, design systems, or other infrastructure.
This is the central Cohesive idea: isolate semantics first, then attach infrastructure-specific interpretations.
What This Enables
The core block enables teams to:
- Keep semantic structure separate from transport and framework details.
- Reuse the same shape definitions across backend, frontend, relations, and languages.
- Validate facts against declared shapes.
- Preserve field dependencies and lineage.
- Infer read and write sets from expressions.
- Generate contracts from backend-owned definitions.
- Project the same semantics into multiple runtimes.
- Build higher-level tools such as Ari on a stable semantic foundation.
For engineers, this creates a more inspectable and reusable system model. For architects, it creates a path to semantic consistency across data, behavior, and presentation. For product and executive stakeholders, it turns system knowledge into an asset that can compound across integrations, workflows, and product surfaces.