---
title: Projection initial values and labels
editUrl: https://github.com/Cratis/Chronicle.Go/edit/main/Documentation/projections/defaults.md
description: Supply initial read-model state and label the projection artifact.
lastUpdated: 2026-10-03T00:00:00.000Z
---


Use initial values for state that an event mapping does not supply. Labels describe
an artifact for organization; they **never select or filter events**.

## Declare initial state

This declaration comes from the [compiling stock example](https://github.com/Cratis/Chronicle.Go/blob/main/examples/projections/defaults.go).
It uses `chronicle`, `projections`, and `readmodels`. Call `stockDeclarations()`
before constructing your client; register and read it as shown in the
[projection guide](/chronicle/go/projections/#run-and-read-the-model).

```go
type StockRegistered struct {
    Name string `json:"name"`
}

type Stock struct {
    ID string `json:"id"`
    Name string `json:"name" chronicle:"set(StockRegistered)"`
    Available int32 `json:"available"`
    Locations []string `json:"locations"`
    Note *string `json:"note"`
}

func stockDeclarations() (*chronicle.Registry, readmodels.Model[Stock], error) {
    registry := chronicle.NewRegistry()
    registered, err := chronicle.RegisterEvent[StockRegistered](registry)
    if err != nil {
        return nil, readmodels.Model[Stock]{}, err
    }
    model, err := chronicle.RegisterReadModel[Stock](registry)
    if err != nil {
        return nil, model, err
    }
    declaration := projections.ModelBound(model,
        projections.FromEvent(registered),
        projections.WithInitialValues(Stock{Available: 10, Locations: []string{}}),
        projections.WithInitialValue(projections.Path[Stock, *string]("note"), nil),
        projections.WithLabels("inventory", "warehouse", "inventory"))
    return registry, model, registry.AddProjection(declaration)
}
```

Both `ModelBound` and `NewBuilder` accept these options. Initial values use the
registered model codec, including concepts, nested objects, explicit JSON names,
and the client's frozen naming policy. Paths use declaration-time serialized names,
just like mapping paths. Snapshots rebind by declared Go field identity; if naming
changes embedded-field promotion and hides a supplied field, client construction
fails rather than assigning its value to the field that shadows it.
Preparation copies the serialized state; later mutation
of a slice, map, pointer, builder, registry, or returned protobuf cannot change the
submitted bytes or reconnect definition. Do not mutate inputs while preparing them.

| Option | Contract |
| --- | --- |
| `WithInitialValues(modelValue)` | Typed whole model; repeated calls replace the state. No option means `{}` |
| `WithInitialValue(Path[M,V](path), value)` | Typed scalar path, including concepts and nullable scalar pointers; no collection traversal. Duplicate writes or overlapping scalar paths fail |
| `WithLabels(labels...)` | Copied artifact metadata; calls accumulate, exact duplicates keep their first occurrence. Case and nonblank whitespace remain significant |

Whole-model serialization preserves zero and false unless `omitempty`/`omitzero`
requests omission. A non-nil empty slice/map supplies `[]`/`{}` in the definition;
nil properties are omitted. For ordinary collection properties, these values can
initialize materialized state. **Projection-owned child collections are different:**
the kernel excludes child roots when applying initial state, so an `[]` initializer
does not establish materialized child presence. Child events and mappings own it.

The scalar-path option supplies an explicit zero despite omission flags, or explicit
JSON `null` for a nil scalar pointer. Strings are JSON data, not projection-expression
grammar: quotes, punctuation, `$null`, and Unicode remain literal values.
Null roots, raw JSON initializers,
foreign model types, collection-null initializers, invalid codecs, and nonfinite
numbers are not accepted. Invalid declarations fail `Build`/`NewClient` before I/O.
Labels must be nonblank UTF-8 without control characters; they are not trimmed.

## Materialization and protection boundaries

Registration alone creates no model instance. The kernel applies initial state
when an event materializes it; subsequent mappings update existing state. Ordinary
AutoMap can overwrite an initialized property with the event's matching property.
For an aggregate-only mapping, the pinned kernel suppresses name AutoMap: an
increment changes its target while leaving the initialized name unchanged. Explicit
property mappings still apply.
Removal removes the instance, and a later creating event initializes it again. Do not use
registration or successful append as a sink-completion signal.

Initializer state is definition metadata, not an appended event. It does not pass
through append-time encryption. Go therefore rejects **any supplied protected root**,
including explicit null, when its schema carries PII or confidentiality metadata.
Omitting that root is allowed. This conservative boundary also covers protected
nested values and collections; it does not promise encrypted default support.

The pinned kernel's all-instance replay used by `ReadModelScenario` omits initial
state. Scenarios with nonempty projection defaults fail with
`ErrFidelityUnavailable`; use production materialized reads instead. Scenario
support remains a [#38 follow-up](https://github.com/Cratis/Chronicle.Go/issues/38).
Explicit observer replay is separate from that scenario endpoint. An accepted replay
request or disappeared job does not prove completion, and unchanged materialized
read-back may still be pre-replay state. The kernel witness requires successful
terminal status for the exact replay job before comparing the resulting state;
if that evidence disappears, replay completion remains unverified.
Initial-value authoring does not promise history revision/redaction rewind
semantics or automatically trigger replay when only defaults change.

C# uses a whole-model callback; Go takes a typed value and snapshots it during
preparation. Model-bound options and scalar-path presence are Go additions.
Initial-state JSON object keys are sorted for deterministic identity rather than
following C# declaration order. See the [parity map](/chronicle/go/parity/#projection-initial-values-and-artifact-labels)
for the source contract and evidence boundaries.
