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:
@@ -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() }
|
||||
|
||||
Reference in New Issue
Block a user