Docs: [CultIndex] is unique, [CultRole] names a selection role

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Libby 2026-11-11 00:49:52 +00:00
commit a9b955660e
4 changed files with 22 additions and 13 deletions

View file

@ -33,9 +33,9 @@ Recovered from the legacy "Database Tools" window (Aetheria `d3db1730`,
**The attribute is not `[CultIndex]`.** `[CultIndex]` means unique (operator, 2026-09-30;
`CultDocumentContracts.cs:34`). Grouping is `CultInspectorGroupByAttribute`, in
`CultInspectorAttributes.cs`, read only by `CultInspectorModel`. It changes no schema id,
catalog entry, index, or store byte. The index cut's deletion of grouping-only `[CultIndex]`
declarations (CultMesh, Geometry) adds no `[CultInspectorGroupBy]` in their place: those are
service stores, not Studio-authored catalogs.
catalog entry, index, or store byte. The index cut converts the grouping and selection `[CultIndex]`
declarations (CultMesh, Geometry) to `[CultRole]` rather than deleting them, and adds no
`[CultInspectorGroupBy]` in their place: those are service stores, not Studio-authored catalogs.
### Invariants

View file

@ -110,7 +110,9 @@ which route wrote it.
already agree when publication starts.
- An admission runs a record's `[CultName]` and `[CultIndex]` getters once per
landing record, before the store is written and before any `Sequence` is
minted. A conditional commit on a clean file store is judged a second time
minted. An admission is refused when it would leave two records of one type
holding one `[CultIndex]` value (blank is no value), and a load of a store
holding such a pair refuses naming both keys and the index. A conditional commit on a clean file store is judged a second time
against the file it merges onto whenever the cache holds variants in any store,
the batch lands a variant, or the file holds one. That second read runs the
getters again for the records the batch lands, for any variant it re-resolves,

View file

@ -22,9 +22,12 @@ public sealed class ItemData
[Key(1)]
[CultIndex]
public string Category = string.Empty;
public string Sku = string.Empty;
[Key(2)]
public string Category = string.Empty;
[Key(3)]
public int Value;
}
```
@ -33,7 +36,8 @@ Available cache attributes:
- `CultDocument(schemaName, schemaVersion)`
- `CultName`
- `CultIndex(alias?)`
- `CultIndex(alias?)`: a unique lookup. Within one document type, a non-blank value names one record; a commit that would leave two records holding one is refused, and a store holding such a pair refuses to load.
- `CultRole(alias?)`: a selection and reference role. Many records may share a value; it is never a lookup.
- `CultGlobal`
- `CultReference(targetType?, many: false)`
@ -53,6 +57,7 @@ var cache = new CultCache();
var handle = await cache.AddAsync(new ItemData
{
Name = "Potion",
Sku = "POT-1",
Category = "Consumable",
Value = 50
});
@ -66,7 +71,7 @@ Name and index lookups are driven from attributes, not manual registration.
```csharp
var byName = cache.GetByName<ItemData>("Potion");
var byIndex = cache.GetByIndex<ItemData>("Category", "Consumable");
var byIndex = cache.GetByIndex<ItemData>("Sku", "POT-1");
```
### Explicit references
@ -130,6 +135,7 @@ var cache = await CultCacheMessagePack.OpenAsync("Data.msgpack");
var handle = await cache.UpsertAsync(new ItemData
{
Name = "Potion",
Sku = "POT-1",
Category = "Consumable",
Value = 50
});

View file

@ -321,15 +321,16 @@ var bySchema = documents.DocumentBySchema("gamecult.aetheria.station_refit.v1");
Typed collections use the same one-call style:
```csharp
var allies = CultMesh.CollectionByIndex<PlayerShipDocument>(
// A [CultIndex] value is unique within its type, so the handle holds at most one record.
var flagship = CultMesh.CollectionByIndex<PlayerShipDocument>(
node,
"Faction",
"au");
"CallSign",
"au-1");
var currentAllies = await allies.LatestAsync();
using var changes = allies.WatchChanges(change => UpdateRoster(change));
var currentFlagship = await flagship.LatestAsync();
using var changes = flagship.WatchChanges(change => UpdateRoster(change));
var collections = CultMesh.Collections(allies);
var collections = CultMesh.Collections(flagship);
var roster = await collections.LatestAsync<PlayerShipDocument>();
using var rosterChanges = collections.WatchChanges<PlayerShipDocument>(RenderRoster);
```