Lost Lily/Grid ToolkitDocs 1.0
Table of Contents

Runtime extensions and detached read models

Use case

Use this contract when a board or cell needs project-owned runtime data that must participate in copying, freezing, snapshots, or detached observation.

Contract and ownership

Choose the interface that matches where the data lives:

Implement the clone and read-model lifecycle for that scope. Return an IGridExtensionReadModel when a consumer needs an independent read-only value. The generic GridRuntimeExtensionBase<TReadModel> centralizes the shared validation rules.

The caller’s instance is never installed directly. The state or plan clones it. A read request creates another detached object that cannot mutate the owner.

Implementation

Excerpt — implement cloned board-owned runtime data. Tested using public APIs.

public sealed class TurnCounterExtension :
    GridRuntimeExtensionBase<TurnCounterReadModel>,
    IGridBoardStateExtension
{
    public const string Id = "game.turn-counter";

    public TurnCounterExtension(int turn)
    {
        Turn = turn;
    }

    public override string ExtensionId => Id;
    public int Turn { get; }

    protected override GridRuntimeExtensionBase<TurnCounterReadModel> CreateOwnedClone(
        GridExtensionRuntimeCloneContext context) =>
        new TurnCounterExtension(Turn);

    protected override TurnCounterReadModel CreateDetachedReadModel() =>
        new TurnCounterReadModel(Turn);
}

Excerpt — expose an independent read-only value. Tested using public APIs.

public sealed class TurnCounterReadModel : IGridExtensionReadModel
{
    public TurnCounterReadModel(int turn)
    {
        Turn = turn;
    }

    public string ExtensionId => TurnCounterExtension.Id;
    public int Turn { get; }
}

For mutable counters, return a new extension value from an action mutation rather than exposing a settable property through the read model.

Registration

Call SetBoardExtension(...) on GridBoardStateBuilder. During runtime, implement an IGridAction whose public plan-builder calls set or mutate the extension.

Verification

The base rejects blank/mismatched IDs, null clones/read models, returning this, and incompatible clone types.

Tests

Assert distinct reference identity for input/owned/read values, equal stable IDs, and no read-model change after the owner later changes.

Done when

The extension clones into state ownership, freezes at the correct boundary, exposes detached reads, and restores without sharing mutable objects across owners.