feat(nesting): nest higher priority numbers first

Priority worked like a rank (lower first) with a default of 0, and the
Edit Drawing form stopped at 0, so moving one part ahead of the rest
meant renumbering every other drawing. Higher-first lets a user raise a
single part to 1 and leave everything else at the default.

Every engine, filler and candidate comparer now serves the highest tier
first. Stored values keep their order:
- .nest files add `nestPriority` and keep writing the negated value under
  `priority`, so older readers nest in the same order; files without
  `nestPriority` are negated on read. Drawing packages strip both, so
  package hashes are unchanged.
- Library settings send both keys the same way for older clients.
- Server schema v18 negates stored drawing priorities (an older server
  refuses the upgraded database).
- Job documents move to version 2; version 1 jobs are converted, and an
  older worker refuses version 2 instead of reversing the order.

The Edit Drawing form now accepts negative priorities and says higher
numbers nest first.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ajandClaude Opus 5.5 committed 2026-11-03 11:02:07 -05:00
1 parent 2721375e8b
commit fbc23d6b64
51 files changed
+364 -79

No files matched your search

@@ -23,7 +23,7 @@ public sealed class DrawingLibraryDatabaseTests
using (var database = new NestDatabase(factory.DatabasePath, pooling: false))
Assert.Empty(database.QueryDrawings(new DrawingLibraryQuery { IncludeArchived = true }).Items);
Assert.Equal(before, NestDigest(factory.DatabasePath));
Assert.Equal(17L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
Assert.Equal(18L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
}
using var upgraded = new NestDatabase(factory.DatabasePath, pooling: false);
@@ -44,7 +44,7 @@ public sealed class DrawingLibraryDatabaseTests
using (var database = new NestDatabase(factory.DatabasePath, pooling: false))
{
Assert.Equal(17L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
Assert.Equal(18L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
Assert.Equal(before, NestDigest(factory.DatabasePath));
// Two distinct blobs across four revisions: the bracket's notes edit and the
// plate both reuse the bracket's first package.
@@ -81,11 +81,37 @@ public sealed class DrawingLibraryDatabaseTests
{
using var factory = new ServerFactory();
using (new NestDatabase(factory.DatabasePath, pooling: false)) { }
Execute(factory.DatabasePath, "PRAGMA user_version=18");
Execute(factory.DatabasePath, "PRAGMA user_version=19");
Assert.Throws<InvalidOperationException>(() => new NestDatabase(factory.DatabasePath, pooling: false));
}
[Fact]
public void V18Upgrade_NegatesLowerFirstPrioritiesInEveryRevision()
{
using var factory = new ServerFactory();
var record = Record();
var other = Record("Synthetic plate");
using (var database = new NestDatabase(factory.DatabasePath, pooling: false))
{
database.InsertDrawing(record.Id, record, Package());
database.InsertDrawing(other.Id, other, Package("other"));
}
// A v17 library stored lower-first values: 3 nested before 0, and int.MinValue before both.
Execute(factory.DatabasePath, $"""
UPDATE drawings SET priority = 3 WHERE id = '{record.Id}';
UPDATE drawing_revisions SET priority = 3 WHERE id = '{record.Id}';
UPDATE drawings SET priority = -2147483648 WHERE id = '{other.Id}';
PRAGMA user_version=17;
""");
using var upgraded = new NestDatabase(factory.DatabasePath, pooling: false);
Assert.Equal(18L, Scalar(factory.DatabasePath, "PRAGMA user_version"));
Assert.Equal(-3, upgraded.GetDrawing(record.Id)!.Settings.Priority);
Assert.Equal(-3, Assert.Single(upgraded.DrawingHistory(record.Id, 0, 100)!.Items).Settings.Priority);
Assert.Equal(int.MaxValue, upgraded.GetDrawing(other.Id)!.Settings.Priority);
}
[Fact]
public void FailedDrawingMigration_RollsBackAndCanRestart()
{