Realign read-side rules — width range coercion, finite order, symlink pins

The design corpus ratified that ranges are part of a sensible reading:
an exact-integer width below 1 now coerces to 1 read-side (bytes
untouched) instead of reading as malformed — the width division must
never see a zero or negative unit — while a non-finite order (.nan,
.inf) is now the same loud malformed-order rejection as a non-numeric
one, guarded at the single point where the double arrives so loader
and Writer inherit it together. The symlink-never-traversed rule
turned out to be already enforced (the loader has filtered symlinks
ahead of the directory check since the first commit); it and the
copy-preserves-the-link-verbatim behavior are now pinned by tests,
alongside the two hostile shapes the corpus names (width: 0,
order: .nan). Five new tests.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-27 14:21:03 -04:00
parent b4c90838b4
commit bea6d02d1d
6 changed files with 155 additions and 27 deletions
+44
View File
@@ -241,6 +241,33 @@ struct BoardLoaderStrayTests {
#expect(result.model.lanes.map(\.id.rawValue) == [realLane])
#expect(result.warnings.isEmpty)
}
/// The identity predicate is shape-only "identity-shaped name or not" (01-storage-format.md
/// § Fractal layout Rules, "Symlinks are never traversed", settled). This pins the case the
/// previous test's non-UUID name doesn't: a symlink whose *name itself* would pass
/// `isUUIDShaped` and which points at a real directory must still be excluded before that
/// name shape is ever consulted `directoryCandidates` filters on `isSymbolicLink` ahead of
/// `isDirectory`, so a link is never mistaken for the directory it points to, UUID-shaped name
/// or not.
@Test func uuidShapedSymlinkToADirectoryIsTreatedAsStrayNotFollowed() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }
let realLane = uuidFolderName()
let linkedName = uuidFolderName()
try fixture.index("", "schema: 1\n")
let realLaneURL = try fixture.index(realLane, "schema: 1\norder: 1024\n")
try FileManager.default.createSymbolicLink(
at: fixture.root.appendingPathComponent(linkedName),
withDestinationURL: realLaneURL
)
let result = try BoardLoader.load(boardRoot: fixture.root)
#expect(result.model.lanes.map(\.id.rawValue) == [realLane])
#expect(!result.model.lanes.map(\.id.rawValue).contains(linkedName))
#expect(result.warnings.isEmpty)
}
}
// MARK: - Non-UUID-shaped folders are strays (01-storage-format.md § Fractal layout Rules,
@@ -728,6 +755,23 @@ struct BoardLoaderFailFastTests {
}
}
/// A non-finite `order` (`.nan`, `.inf`) is the same loud rejection as a non-numeric one
/// (01-storage-format.md § Frontmatter, settled) NaN has no place in the total order the
/// tie-break and midpoint math assume.
@Test func nonFiniteOrderOnUUIDLaneThrows() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }
let lane = uuidFolderName()
try fixture.index("", "schema: 1\n")
try fixture.index(lane, "schema: 1\norder: .nan\n")
expectFailure(.malformedOrder(raw: ".nan"), path: "\(lane)/index.md") {
_ = try BoardLoader.load(boardRoot: fixture.root)
}
}
@Test func missingOrderOnUUIDCardThrows() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }