Merge branch 'claude/main-page-group-hierarchy-3a3210'
ci / build and test (push) Successful in 1m27s
ci / android head (push) Failing after 5s
ci / api image (push) Successful in 21s

This commit is contained in:
2026-08-04 10:26:20 +02:00
8 changed files with 214 additions and 33 deletions
@@ -98,10 +98,27 @@ public sealed class LocalCacheProtector : IDisposable
/// Maps a syncable entity type onto the resource type its AAD binds.
/// </summary>
/// <remarks>
/// <para>
/// A switch rather than a cast, even though the two enums happen to be adjacent. They are not the
/// same list: <see cref="CryptoSpec.AadResourceType"/> also covers users, devices and vaults, so the
/// numbers do not line up, and a cast would bind an item's ciphertext to the wrong resource type
/// without failing anywhere a test would notice.
/// </para>
/// <para>
/// <b>It has to name every syncable type, and the throw is not a safety net.</b>
/// <c>ConflictStore.RecordAsync</c> calls this unconditionally, so a type with no arm here is a type
/// whose <em>conflicts</em> cannot be recorded — and the conflict log is the only reason the merge is
/// allowed to pick a winner at all. Three types went two phases without one: the two logs and the
/// buckets were added to both enums and to the reconciler registry, and this switch was not touched,
/// so a merge over any of them turned a recorded conflict into an
/// <see cref="ArgumentOutOfRangeException"/>. The two other callers hid it —
/// <c>ItemStore</c> and <c>OutboxStore</c> only reach this when an item carries plaintext fields, and
/// none of those three does.
/// </para>
/// <para>
/// <c>AadResourceTypeTests.EverySyncableType_HasAnArmInTheStorageMapping</c> is what says so now, and it
/// is a name comparison rather than a list to keep by hand.
/// </para>
/// </remarks>
internal static class AadResourceTypes
{
@@ -117,6 +134,14 @@ internal static class AadResourceTypes
SyncEntityType.Snippet => CryptoSpec.AadResourceType.Snippet,
SyncEntityType.PortForward => CryptoSpec.AadResourceType.PortForward,
SyncEntityType.KnownHostKey => CryptoSpec.AadResourceType.KnownHostKey,
// Appended in the order the enums grew, which is why these three are not beside their
// neighbours by number. See docs/crypto.md §4.3 on why 1416 are not one-behind their wire
// counterparts the way the arms above are.
SyncEntityType.ConnectionLogEntry => CryptoSpec.AadResourceType.ConnectionLogEntry,
SyncEntityType.ActivityLogEntry => CryptoSpec.AadResourceType.ActivityLogEntry,
SyncEntityType.ObjectStore => CryptoSpec.AadResourceType.ObjectStore,
_ => throw new ArgumentOutOfRangeException(
nameof(entityType), entityType, "No AAD resource type is defined for this entity type."),
};