feat(server): record set releases and the nests they made

Releasing a drawing set makes one nest per material group, so a kit run
spans several nests. The server now records which set revision was
released, the multiplier and the nests it made, so a run can be tracked
across its nests and a nest can say where it came from.

The client uploads every nest first and posts the release last: a failure
part-way leaves ordinary nests and no release, never a release pointing at
missing nests. For the same reason a release is accepted for an older set
revision or an archived set, since the nests already exist by then. The
release id is client-allocated and a nest belongs to at most one release,
so a retried post gets 409 with the release that already holds it.

Releases are append-only and read joined to each nest's current metadata,
so status and deletion are always current. The tables join schema v7, as
the set tables have not shipped yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-10-20 13:27:27 -04:00
1 parent be057bf337
commit 44666b289d
14 files changed
+999 -46

No files matched your search

@@ -43,7 +43,7 @@ public sealed class DrawingSetDatabaseTests
Assert.Throws<SqliteException>(() => new NestDatabase(factory.DatabasePath, pooling: false));
Assert.Equal(6L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
Assert.Equal(0L, Scalar(factory.DatabasePath, "SELECT COUNT(*) FROM sqlite_master WHERE name LIKE 'drawing_set%'"));
Assert.Equal(0L, Scalar(factory.DatabasePath, "SELECT COUNT(*) FROM sqlite_master WHERE name LIKE 'drawing_set%' OR name LIKE 'set_release%'"));
Assert.Equal(before, Digest(factory.DatabasePath));
Execute(factory.DatabasePath, "DROP TABLE ix_drawing_set_members_drawing");
using var database = new NestDatabase(factory.DatabasePath, pooling: false);
@@ -295,7 +295,7 @@ public sealed class DrawingSetDatabaseTests
}
/// <summary>
/// A real schema-v6 file with nests and a library drawing: the set tables are additive,
/// A real schema-v6 file with nests and a library drawing: the set and release tables are additive,
/// so dropping them restores v6 exactly. Returns the drawing's id.
/// </summary>
private static Guid CreateVersion6Database(string path)
@@ -314,7 +314,10 @@ public sealed class DrawingSetDatabaseTests
database.UpdateDrawing(drawing, edit, null, 1);
}
Execute(path, "DROP TABLE drawing_set_members; DROP TABLE drawing_sets; DROP TABLE drawing_set_revisions; PRAGMA user_version=6;");
Execute(path, """
DROP TABLE set_release_nests; DROP TABLE set_releases;
DROP TABLE drawing_set_members; DROP TABLE drawing_sets; DROP TABLE drawing_set_revisions; PRAGMA user_version=6;
""");
return drawing;
}