UmweltDocs
Recipes/Spies and secrets

Spies and secrets

A secret meant for one captain reaches a smuggler, then the other side. Who leaked it, which faction knows, and who still thinks the enemy is in the dark.

The path from the spymaster to the enemy, as trace walks it back from the Crown’s own record. Orrin, the one person allowed to know, is not on it.

The idea

Intrigue is knowledge with edges: who may know a secret, who actually does, which faction knows it as a faction, and who believes the other side is still in the dark. Your game decides who talks. Umwelt records each telling, flags the one that broke the circle, and answers every question about it, including questions about beliefs.

Your game decides

  • who a secret may be shared with
  • who tells it anyway, and why
  • when a faction takes something as known
  • what each side thinks the other knows

Umwelt records and answers

  • the secret, with secrecy.sharedWith
  • a SECRET_BREACH warning, never a refusal
  • every breach, with who told and who heard
  • what a faction knows, apart from its members

Calder

A port city with two powers. The Harbor Guild runs the docks; the Crown’s Eye watches the Guild.

At the first bell, the great chain across the harbor mouth snaps. Sera, the Guild’s spymaster, sees it. With the chain down, the port is open to anyone’s ships, and she means to keep that quiet.

SeraGuild spymastersera
OrrinGuild captainorrin
TildeGuild smuggler, and a Crown informanttilde
VaneCrown’s Eye agentvane
PellCrown clerkpell
The factionstwo groups that hold knowledge of their ownguild · crown

The secret

Memberships are Facts. Sera’s Holding carries secrecy: { sharedWith: ["orrin"] }: she may tell Orrin and nobody else.

calder.ts
// Who belongs to which faction. Tilde is a guild smuggler, and nobody knows she sells to the Crown.
const member = (who: string, faction: string) => claim.entity(who, 'umwelt:member-of', faction);
const broken = claim.unary('chain', 'is-broken');
await client.commit({
  projectId, attemptId: 'first-bell',
  event: { id: 'first-bell', type: 'scene', title: 'The chain snaps', position: at(1), participants: ['sera'], provenance },
  records: [
    ...[['sera', 'guild'], ['orrin', 'guild'], ['tilde', 'guild'], ['vane', 'crown'], ['pell', 'crown']].map(([who, faction]) =>
      fact(member(who, faction), { id: `${who}-in-${faction}` })),
    fact(broken, { id: 'canon-broken' }),
    // Sera's secret: she may share it with Orrin, and nobody else.
    knows('sera', broken, witnessed('first-bell'), {
      id: 'sera-secret', secrecy: { sharedWith: ['orrin'] }, text: 'The harbor chain is broken. The port is open to anyone.',
    }),
  ],
});
Program.cs
// Who belongs to which faction. Tilde is a guild smuggler, and nobody knows she sells to the Crown.
Claim Member(string who, string faction) => Claim.Entity(who, "umwelt:member-of", faction);
var broken = Claim.Unary("chain", "is-broken");
var records = new[] { ("sera", "guild"), ("orrin", "guild"), ("tilde", "guild"), ("vane", "crown"), ("pell", "crown") }
    .Select(m => (RecordInput)Records.Fact(Member(m.Item1, m.Item2), id: $"{m.Item1}-in-{m.Item2}"))
    .ToList();
records.Add(Records.Fact(broken, id: "canon-broken"));
// Sera's secret: she may share it with Orrin, and nobody else.
records.Add(Records.Knows("sera", broken, Acquired.Witnessed("first-bell"), id: "sera-secret",
    secrecy: new Secrecy { SharedWith = new() { "orrin" } }, text: "The harbor chain is broken. The port is open to anyone."));
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "first-bell",
    Event = new EventInput { Id = "first-bell", Type = "scene", Title = "The chain snaps", Position = Position.At(1), Participants = new() { "sera" }, Provenance = provenance },
    Records = records,
});

The leak

Sera tells Orrin, as she may. Then she needs a boat out of the harbor and tells Tilde, who is not in the circle. Tilde sells it to Vane, and the Crown’s Eye meets and takes it as its own: a Holding whose holder is the faction.

calder.tsYour code
// Second bell: Sera tells Orrin. Third bell: she needs a boat, and tells Tilde too.
// Fourth bell: Tilde sells it to Vane. Fifth bell: the Crown's Eye takes it as its own.
// What the tellings have in common is said once; each one says what differs.
const tellings = await client.transmitBatch({
  shared: { projectId, from: 'sera', sourceRecordId: 'sera-secret', intent: 'honest', attitude: 'knows', confidence: 1 },
  transmits: [
    { attemptId: 'to-orrin', to: 'orrin', position: at(2), recordId: 'orrin-knows' },
    { attemptId: 'to-tilde', to: 'tilde', position: at(3), recordId: 'tilde-knows' },
    { attemptId: 'to-vane', from: 'tilde', to: 'vane', sourceRecordId: 'tilde-knows', attitude: 'believes', confidence: 0.8, position: at(4), recordId: 'vane-heard' },
  ],
});
print('warnings', tellings.map(({ recordIds, warnings }) => ({ recordIds, warnings })));

await client.commit({
  projectId, attemptId: 'council',
  event: { id: 'council', type: 'meeting', title: 'The Crown’s Eye meets', position: at(5), participants: ['vane', 'pell'], provenance },
  records: [
    believes('crown', broken, toldBy('vane', 'honest', 'vane-heard'), { id: 'crown-knows', confidence: 0.8 }),
  ],
});
Program.csYour code
// Second bell: Sera tells Orrin. Third bell: she needs a boat, and tells Tilde too.
// Fourth bell: Tilde sells it to Vane. Fifth bell: the Crown's Eye takes it as its own.
// What the tellings have in common is said once; each one says what differs.
var tellings = engine.TransmitBatch(new TransmitBatchPayload
{
    Shared = new TransmitShared { ProjectId = projectId, From = "sera", SourceRecordId = "sera-secret", Intent = Intent.Honest, Attitude = Attitude.Knows, Confidence = 1.0 },
    Transmits = new()
    {
        new TransmitBatchItem { AttemptId = "to-orrin", To = "orrin", Position = Position.At(2), RecordId = "orrin-knows" },
        new TransmitBatchItem { AttemptId = "to-tilde", To = "tilde", Position = Position.At(3), RecordId = "tilde-knows" },
        new TransmitBatchItem
        {
            AttemptId = "to-vane", From = "tilde", To = "vane", SourceRecordId = "tilde-knows",
            Attitude = Attitude.Believes, Confidence = 0.8, Position = Position.At(4), RecordId = "vane-heard",
        },
    },
});
Print("warnings", tellings.Select(outcome => new { recordIds = outcome.RecordIds, warnings = outcome.Warnings }));

var fifthBell = Position.At(5);
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "council",
    Event = new EventInput { Id = "council", Type = "meeting", Title = "The Crown’s Eye meets", Position = fifthBell, Participants = new() { "vane", "pell" }, Provenance = provenance },
    Records = new()
    {
        Records.Believes("crown", broken, Acquired.ToldBy("vane", Intent.Honest, "vane-heard"), id: "crown-knows", confidence: 0.8),
    },
});
Warnings from the batch
[
  { "recordIds": ["orrin-knows"], "warnings": [] },
  {
    "recordIds": ["tilde-knows"],
    "warnings": [
      {
        "code": "SECRET_BREACH",
        "message": "'sera' told 'tilde' the secret 'sera-secret'."
      }
    ]
  },
  { "recordIds": ["vane-heard"], "warnings": [] }
]

Telling is never blocked. The engine records the telling and warns. What a breach costs Sera is your game’s decision.

A belief about a belief

Sera believes the Crown has no idea. That is a Holding too, with the reserved predicate umwelt:holds: its subject is the Crown, its value names the claim by its key, and its polarity is false.

calder.ts
// What Sera believes about the Crown: that it has no idea. A belief about a belief.
// A second-order Holding names the claim by its key, which the engine gives.
const { claimKey: brokenKey } = await client.claimKey({ claim: broken });
await client.commit({
  projectId, attemptId: 'sera-thinks',
  event: { id: 'sera-thinks', type: 'thought', title: 'Sera counts her secrets', position: at(2), participants: ['sera'], provenance },
  records: [
    believes('sera', claim.holds('crown', brokenKey, { polarity: false }), authored(), { id: 'sera-thinks-crown-blind', confidence: 0.9 }),
  ],
});
Program.cs
// What Sera believes about the Crown: that it has no idea. A belief about a belief.
// A second-order Holding names the claim by its key, which the engine gives.
var brokenKey = engine.ClaimKey(new ClaimKeyPayload { Claim = broken }).ClaimKey;
var secondBell = Position.At(2);
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "sera-thinks",
    Event = new EventInput { Id = "sera-thinks", Type = "thought", Title = "Sera counts her secrets", Position = secondBell, Participants = new() { "sera" }, Provenance = provenance },
    Records = new()
    {
        Records.Believes("sera", Claim.Holds("crown", brokenKey, polarity: false), Acquired.Authored(), id: "sera-thinks-crown-blind", confidence: 0.9),
    },
});

Ask the spymaster

calder.ts
for (const bell of [3, 5]) {
  print(`sera-about-crown-${bell}`, await client.beliefsAbout({ projectId, holder: 'sera', about: 'crown', at: { tick: bell } }));
}
print('breaches', await client.breaches({ projectId, owner: 'sera' }));

const path = await client.trace({ projectId, recordId: 'crown-knows' });
print('path', path.chain.map(({ record, acquisition }) => ({
  holder: record.kind === 'holding' ? record.holder : null,
  from: acquisition?.kind === 'told-by' ? acquisition.sourceHolderId : null,
})));

for (const faction of ['guild', 'crown']) {
  const coverage = await client.groupCoverage({ projectId, group: faction, claim: broken, at: { tick: 5 } });
  print(`coverage-${faction}`, {
    members: coverage.members,
    holding: coverage.holding.map((item) => item.holder),
    missing: coverage.missing,
    groupHolds: coverage.groupHolding !== undefined && coverage.groupHolding !== null,
  });
}
print('guild-members-who-know', await client.whoHolds({ projectId, claim: broken, at: { tick: 5 }, filter: { memberOf: 'guild' } }));
await client.close();
Program.cs
foreach (var bell in new long[] { 3, 5 })
{
    Print($"sera-about-crown-{bell}", engine.BeliefsAbout(new BeliefsAboutPayload { ProjectId = projectId, Holder = "sera", About = "crown", At = new At { Tick = bell } }));
}
Print("breaches", engine.Breaches(new BreachesPayload { ProjectId = projectId, Owner = "sera" }));

var path = engine.Trace(new RecordId { ProjectId = projectId, RecordIdValue = "crown-knows" });
Print("path", path.Chain.Select(link => new
{
    holder = (link.Record as Record.Holding)?.Holder,
    from = (link.Acquisition as Acquisition.ToldBy)?.SourceHolderId,
}));

foreach (var faction in new[] { "guild", "crown" })
{
    var coverage = engine.GroupCoverage(new GroupCoveragePayload { ProjectId = projectId, Group = faction, Claim = broken, At = new At { Tick = 5 } });
    Print($"coverage-{faction}", new
    {
        members = coverage.Members,
        holding = coverage.Holding.Select(item => item.Holder),
        missing = coverage.Missing,
        groupHolds = coverage.GroupHolding is not null,
    });
}
Print("guild-members-who-know", engine.WhoHolds(new WhoHoldsPayload
{
    ProjectId = projectId, Claim = broken, At = new At { Tick = 5 }, Filter = new WhoHoldsFilter { MemberOf = "guild" },
}));

Is she right about the Crown?

Third bellTilde has just heard

Sera believesThe Crown does not know
The CrownDoes not knowactuallyHolds: false

Fifth bellafter the Crown’s Eye meets

Sera believesThe Crown does not know
The CrownKnowscrown-knows
beliefsAbout, fifth bell
[
  {
    "actualRecordId": "crown-knows",
    "actuallyHolds": true,
    "attitude": "believes",
    "claimKey": "c887f55d44e8bbd4e59934218877bf841a55b7b8ad19608ba0875ead14f1eea1",
    "confidence": 0.9,
    "polarity": false,
    "recordId": "sera-thinks-crown-blind"
  }
]

Every breach of her secrets

Response
[
  {
    "eventId": "01a1cbb8-0124-7dd1-8e3a-e01f01177f4b",
    "owner": "sera",
    "position": { "tick": 3 },
    "recipient": "tilde",
    "recordId": "tilde-knows",
    "secretRecordId": "sera-secret",
    "toldBy": "sera"
  }
]

What each faction knows

3 of 3Guild members hold it. The Guild itself: no record of its own.
1 of 2Crown members hold it (Pell does not). The Crown’s Eye itself: yes.
1breach, at the third bell: Sera to Tilde.

Members knowing is not the group knowing. Every member of the Guild knows the chain is down, and the Guild, as a faction, holds nothing about it. The Crown’s Eye knows it because it has a Holding of its own.

Try next

Find the informantGive Sera trace and breaches as an in-game tool, and let the player work out who sold the secret.
Feed a false secretHave Sera tell Tilde a distortedClaim on purpose, and see what the Crown ends up believing.
Who suspects the leakA second-order Holding for Orrin: he believes Tilde does not know. beliefsAbout says otherwise.
A defectionEnd Tilde’s Guild membership with a transition, and read the factions before and after.

The whole program

One file that builds the world, runs it and prints every answer on this page. The TypeScript program and the .NET one print the same answers: the script that runs them for these pages checks it.

calder.ts
import { at, authored, believes, by, claim, createClient, fact, knows, toldBy, witnessed } from '@kallamo/umwelt';
const client = createClient({ file: 'calder.umw', create: true });
const projectId = 'calder';
const provenance = by.human();
const print = (label: string, value: unknown) => console.log(`@@${label}\n${JSON.stringify(value)}`);

// The world, declared once: sending the same manifest again changes nothing.
await client.worldEnsure({
  project: { id: projectId, name: 'Calder', timescale: 'bell' },
  entityTypes: [
    { id: 'agent', name: 'Agent', kind: 'agent' },
    { id: 'faction', name: 'Faction', kind: 'group' },
    { id: 'thing', name: 'Thing', kind: 'other' },
  ],
  entities: [
    { id: 'sera', typeId: 'agent', name: 'Sera, the guild spymaster' },
    { id: 'orrin', typeId: 'agent', name: 'Orrin, a guild captain' },
    { id: 'tilde', typeId: 'agent', name: 'Tilde, a guild smuggler' },
    { id: 'vane', typeId: 'agent', name: 'Vane, of the Crown’s Eye' },
    { id: 'pell', typeId: 'agent', name: 'Pell, a crown clerk' },
    { id: 'guild', typeId: 'faction', name: 'The Harbor Guild' },
    { id: 'crown', typeId: 'faction', name: 'The Crown’s Eye' },
    { id: 'chain', typeId: 'thing', name: 'The harbor chain' },
  ],
  predicates: [
    { id: 'is-broken', label: 'is broken', object: 'none', cardinality: 'one' },
  ],
});
// Who belongs to which faction. Tilde is a guild smuggler, and nobody knows she sells to the Crown.
const member = (who: string, faction: string) => claim.entity(who, 'umwelt:member-of', faction);
const broken = claim.unary('chain', 'is-broken');
await client.commit({
  projectId, attemptId: 'first-bell',
  event: { id: 'first-bell', type: 'scene', title: 'The chain snaps', position: at(1), participants: ['sera'], provenance },
  records: [
    ...[['sera', 'guild'], ['orrin', 'guild'], ['tilde', 'guild'], ['vane', 'crown'], ['pell', 'crown']].map(([who, faction]) =>
      fact(member(who, faction), { id: `${who}-in-${faction}` })),
    fact(broken, { id: 'canon-broken' }),
    // Sera's secret: she may share it with Orrin, and nobody else.
    knows('sera', broken, witnessed('first-bell'), {
      id: 'sera-secret', secrecy: { sharedWith: ['orrin'] }, text: 'The harbor chain is broken. The port is open to anyone.',
    }),
  ],
});
// Second bell: Sera tells Orrin. Third bell: she needs a boat, and tells Tilde too.
// Fourth bell: Tilde sells it to Vane. Fifth bell: the Crown's Eye takes it as its own.
// What the tellings have in common is said once; each one says what differs.
const tellings = await client.transmitBatch({
  shared: { projectId, from: 'sera', sourceRecordId: 'sera-secret', intent: 'honest', attitude: 'knows', confidence: 1 },
  transmits: [
    { attemptId: 'to-orrin', to: 'orrin', position: at(2), recordId: 'orrin-knows' },
    { attemptId: 'to-tilde', to: 'tilde', position: at(3), recordId: 'tilde-knows' },
    { attemptId: 'to-vane', from: 'tilde', to: 'vane', sourceRecordId: 'tilde-knows', attitude: 'believes', confidence: 0.8, position: at(4), recordId: 'vane-heard' },
  ],
});
print('warnings', tellings.map(({ recordIds, warnings }) => ({ recordIds, warnings })));

await client.commit({
  projectId, attemptId: 'council',
  event: { id: 'council', type: 'meeting', title: 'The Crown’s Eye meets', position: at(5), participants: ['vane', 'pell'], provenance },
  records: [
    believes('crown', broken, toldBy('vane', 'honest', 'vane-heard'), { id: 'crown-knows', confidence: 0.8 }),
  ],
});
// What Sera believes about the Crown: that it has no idea. A belief about a belief.
// A second-order Holding names the claim by its key, which the engine gives.
const { claimKey: brokenKey } = await client.claimKey({ claim: broken });
await client.commit({
  projectId, attemptId: 'sera-thinks',
  event: { id: 'sera-thinks', type: 'thought', title: 'Sera counts her secrets', position: at(2), participants: ['sera'], provenance },
  records: [
    believes('sera', claim.holds('crown', brokenKey, { polarity: false }), authored(), { id: 'sera-thinks-crown-blind', confidence: 0.9 }),
  ],
});
for (const bell of [3, 5]) {
  print(`sera-about-crown-${bell}`, await client.beliefsAbout({ projectId, holder: 'sera', about: 'crown', at: { tick: bell } }));
}
print('breaches', await client.breaches({ projectId, owner: 'sera' }));

const path = await client.trace({ projectId, recordId: 'crown-knows' });
print('path', path.chain.map(({ record, acquisition }) => ({
  holder: record.kind === 'holding' ? record.holder : null,
  from: acquisition?.kind === 'told-by' ? acquisition.sourceHolderId : null,
})));

for (const faction of ['guild', 'crown']) {
  const coverage = await client.groupCoverage({ projectId, group: faction, claim: broken, at: { tick: 5 } });
  print(`coverage-${faction}`, {
    members: coverage.members,
    holding: coverage.holding.map((item) => item.holder),
    missing: coverage.missing,
    groupHolds: coverage.groupHolding !== undefined && coverage.groupHolding !== null,
  });
}
print('guild-members-who-know', await client.whoHolds({ projectId, claim: broken, at: { tick: 5 }, filter: { memberOf: 'guild' } }));
await client.close();
Program.cs
using System.Text.Json;
using System.Text.Json.Serialization;
using Kallamo.Umwelt;
using var engine = UmweltEngine.Create("calder.umw");
const string projectId = "calder";
var provenance = Provenance.Human();
var json = new JsonSerializerOptions
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
    Converters = { new JsonStringEnumConverter(JsonNamingPolicy.KebabCaseLower) },
};
void Print(string label, object? value) => Console.WriteLine($"@@{label}\n{JsonSerializer.Serialize(value, json)}");

// The world, declared once: sending the same manifest again changes nothing.
engine.WorldEnsure(new EnsureWorldInput
{
    Project = new WorldProject { Id = projectId, Name = "Calder", Timescale = "bell" },
    EntityTypes = new()
    {
        new WorldEntityType { Id = "agent", Name = "Agent", Kind = EntityKind.Agent },
        new WorldEntityType { Id = "faction", Name = "Faction", Kind = EntityKind.Group },
        new WorldEntityType { Id = "thing", Name = "Thing", Kind = EntityKind.Other },
    },
    Entities = new()
    {
        new WorldEntity { Id = "sera", TypeId = "agent", Name = "Sera, the guild spymaster" },
        new WorldEntity { Id = "orrin", TypeId = "agent", Name = "Orrin, a guild captain" },
        new WorldEntity { Id = "tilde", TypeId = "agent", Name = "Tilde, a guild smuggler" },
        new WorldEntity { Id = "vane", TypeId = "agent", Name = "Vane, of the Crown’s Eye" },
        new WorldEntity { Id = "pell", TypeId = "agent", Name = "Pell, a crown clerk" },
        new WorldEntity { Id = "guild", TypeId = "faction", Name = "The Harbor Guild" },
        new WorldEntity { Id = "crown", TypeId = "faction", Name = "The Crown’s Eye" },
        new WorldEntity { Id = "chain", TypeId = "thing", Name = "The harbor chain" },
    },
    Predicates = new()
    {
        new PredicateDef { Id = "is-broken", Label = "is broken", Object = ObjectKind.None, Cardinality = Cardinality.One },
    },
});
// Who belongs to which faction. Tilde is a guild smuggler, and nobody knows she sells to the Crown.
Claim Member(string who, string faction) => Claim.Entity(who, "umwelt:member-of", faction);
var broken = Claim.Unary("chain", "is-broken");
var records = new[] { ("sera", "guild"), ("orrin", "guild"), ("tilde", "guild"), ("vane", "crown"), ("pell", "crown") }
    .Select(m => (RecordInput)Records.Fact(Member(m.Item1, m.Item2), id: $"{m.Item1}-in-{m.Item2}"))
    .ToList();
records.Add(Records.Fact(broken, id: "canon-broken"));
// Sera's secret: she may share it with Orrin, and nobody else.
records.Add(Records.Knows("sera", broken, Acquired.Witnessed("first-bell"), id: "sera-secret",
    secrecy: new Secrecy { SharedWith = new() { "orrin" } }, text: "The harbor chain is broken. The port is open to anyone."));
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "first-bell",
    Event = new EventInput { Id = "first-bell", Type = "scene", Title = "The chain snaps", Position = Position.At(1), Participants = new() { "sera" }, Provenance = provenance },
    Records = records,
});
// Second bell: Sera tells Orrin. Third bell: she needs a boat, and tells Tilde too.
// Fourth bell: Tilde sells it to Vane. Fifth bell: the Crown's Eye takes it as its own.
// What the tellings have in common is said once; each one says what differs.
var tellings = engine.TransmitBatch(new TransmitBatchPayload
{
    Shared = new TransmitShared { ProjectId = projectId, From = "sera", SourceRecordId = "sera-secret", Intent = Intent.Honest, Attitude = Attitude.Knows, Confidence = 1.0 },
    Transmits = new()
    {
        new TransmitBatchItem { AttemptId = "to-orrin", To = "orrin", Position = Position.At(2), RecordId = "orrin-knows" },
        new TransmitBatchItem { AttemptId = "to-tilde", To = "tilde", Position = Position.At(3), RecordId = "tilde-knows" },
        new TransmitBatchItem
        {
            AttemptId = "to-vane", From = "tilde", To = "vane", SourceRecordId = "tilde-knows",
            Attitude = Attitude.Believes, Confidence = 0.8, Position = Position.At(4), RecordId = "vane-heard",
        },
    },
});
Print("warnings", tellings.Select(outcome => new { recordIds = outcome.RecordIds, warnings = outcome.Warnings }));

var fifthBell = Position.At(5);
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "council",
    Event = new EventInput { Id = "council", Type = "meeting", Title = "The Crown’s Eye meets", Position = fifthBell, Participants = new() { "vane", "pell" }, Provenance = provenance },
    Records = new()
    {
        Records.Believes("crown", broken, Acquired.ToldBy("vane", Intent.Honest, "vane-heard"), id: "crown-knows", confidence: 0.8),
    },
});
// What Sera believes about the Crown: that it has no idea. A belief about a belief.
// A second-order Holding names the claim by its key, which the engine gives.
var brokenKey = engine.ClaimKey(new ClaimKeyPayload { Claim = broken }).ClaimKey;
var secondBell = Position.At(2);
engine.Commit(new CommitInput
{
    ProjectId = projectId,
    AttemptId = "sera-thinks",
    Event = new EventInput { Id = "sera-thinks", Type = "thought", Title = "Sera counts her secrets", Position = secondBell, Participants = new() { "sera" }, Provenance = provenance },
    Records = new()
    {
        Records.Believes("sera", Claim.Holds("crown", brokenKey, polarity: false), Acquired.Authored(), id: "sera-thinks-crown-blind", confidence: 0.9),
    },
});
foreach (var bell in new long[] { 3, 5 })
{
    Print($"sera-about-crown-{bell}", engine.BeliefsAbout(new BeliefsAboutPayload { ProjectId = projectId, Holder = "sera", About = "crown", At = new At { Tick = bell } }));
}
Print("breaches", engine.Breaches(new BreachesPayload { ProjectId = projectId, Owner = "sera" }));

var path = engine.Trace(new RecordId { ProjectId = projectId, RecordIdValue = "crown-knows" });
Print("path", path.Chain.Select(link => new
{
    holder = (link.Record as Record.Holding)?.Holder,
    from = (link.Acquisition as Acquisition.ToldBy)?.SourceHolderId,
}));

foreach (var faction in new[] { "guild", "crown" })
{
    var coverage = engine.GroupCoverage(new GroupCoveragePayload { ProjectId = projectId, Group = faction, Claim = broken, At = new At { Tick = 5 } });
    Print($"coverage-{faction}", new
    {
        members = coverage.Members,
        holding = coverage.Holding.Select(item => item.Holder),
        missing = coverage.Missing,
        groupHolds = coverage.GroupHolding is not null,
    });
}
Print("guild-members-who-know", engine.WhoHolds(new WhoHoldsPayload
{
    ProjectId = projectId, Claim = broken, At = new At { Tick = 5 }, Filter = new WhoHoldsFilter { MemberOf = "guild" },
}));

More recipes

Was this page helpful?