Examples

Board

Board, columns and cards, with rank ordering from a per-parent string sequence.

Card ordering belongs to a column. Adding more cards later should continue that column's ranks without changing another column's sequence. This example uses string ranks to show that sequences are useful beyond integer IDs.

A kanban board: Board → Column → Card. Cards are ordered by a string rank ("a", "aa", "aaa"…, in the spirit of LexoRank) that restarts for every column. Source: tests/Zeeq.Biome.Tests/Board.

The blueprints

Board/Domain.cs
// PerParent resumes for the same column reference;
// it restarts for a different column.
internal static readonly Sequence<string> Ranks = Sequence
    .Of("a", rank => rank + "a")
    .PerParent();

internal static readonly Blueprint<BoardSeed, Board> Board = Blueprint.For<
    BoardSeed,
    Board
>(_ => new());

internal static readonly ChildBlueprint<BoardSeed, Board, Column> Column =
    Blueprint.ChildOf<BoardSeed, Board, Column>(
        (board, _) => new() { Board = board }
    );

// Rank stays null until Derive; empty string would count as explicitly set.
internal static readonly ChildBlueprint<BoardSeed, Column, Card> Card =
    Blueprint
        .ChildOf<BoardSeed, Column, Card>(
            (column, _) => new() { Column = column }
        )
        .Derive(c => c.Rank, Ranks);
Rank is declared string Rank { get; set; } = null!; and is null until the Derive runs. A default of "" would count as explicitly set, and the sequence would never fill it.

Ranks resume per column, and .Under revisits

await TestBiome
    .Start(out _)
    .AddBoard(out var board)
    .AddColumns(out var columns, 2)
    .AddCards(out var cards, 2) // "a", "aa" | "a", "aa"
    .Under(columns[0])
    .AddCard(out var next) // "aaa": resumes only this column
    .Under(board)
    .AddColumn(out var sibling)
    .AddCard(out var siblingCard) // "a": a new column restarts
    .BuildAsync();

Assertions in the real test: cards[0].Rank == "a", cards[1].Rank == "aa", cards[2].Rank == "a", next.Rank == "aaa", siblingCard.Rank == "a".

Scope is declared on the sequence, not chosen by .Under or a customizer. PerParent keys state by the column's reference identity, so two columns never share a counter, and .Under(columns[0]) always resumes the right one.

An explicit rank is reserved only in its own column

BoardTests.cs
await TestBiome
    .Start(out _)
    .AddBoard(out _)
    .AddColumns(out var columns, 2)
    .Under(columns[0])
    .AddCards(out var first, _ => { }, c => c.Rank = "a")
    .Under(columns[1])
    .AddCard(out var other)
    .BuildAsync();

// first[0].Rank == "aa"   (the "a" explicit value is skipped)
// other.Rank == "a"       (the second column is untouched)