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
+40
View File
@@ -1382,6 +1382,46 @@ struct BoardWriterCopyTests {
#expect(try fixture.entryNames("A.kanban/\(id.rawValue)/\(card)").contains("attachments"))
}
/// A symlink surviving a copy as a symlink never resolved, never followed to its target's
/// bytes is `FileManager.copyItem`'s own default behavior (01-storage-format.md § Fractal
/// layout Rules, "Symlinks are never traversed": "Copy flows copy the link itself, never its
/// target"). Nothing in `copyItem` arranges this on purpose it falls out of leaning on
/// `FileManager.copyItem` for the whole subtree rather than reading files one by one so this
/// pins the behavior against a future change (a different copy mechanism, a Foundation
/// update) silently starting to dereference links instead.
@Test func copyItemPreservesASymlinkVerbatimInsideACopiedSubtree() throws {
let fixture = try WriterFixture()
defer { fixture.tearDown() }
try board(fixture)
let cardPath = "A.kanban/\(Ident.lane1)/\(Ident.card1)"
try fixture.file("\(cardPath)/attachments/real.png", Data([0x89, 0x50, 0x4E, 0x47]))
try FileManager.default.createSymbolicLink(
atPath: fixture.url("\(cardPath)/attachments/linked.png").path,
withDestinationPath: "real.png"
)
let id = try BoardWriter.copyItem(
at: fixture.url("A.kanban/\(Ident.lane1)"),
toParent: fixture.url("A.kanban"),
order: nil,
stamps: .fork
)
let copied = try childrenByTitle(of: "A.kanban/\(id.rawValue)", in: fixture)
let card = try #require(copied["Card One"])
let copiedLink = fixture.url("A.kanban/\(id.rawValue)/\(card)/attachments/linked.png")
let values = try copiedLink.resourceValues(forKeys: [.isSymbolicLinkKey])
#expect(values.isSymbolicLink == true)
#expect(try FileManager.default.destinationOfSymbolicLink(atPath: copiedLink.path) == "real.png")
// The source link is untouched too a copy never touches what it reads from.
#expect(
try FileManager.default.destinationOfSymbolicLink(
atPath: fixture.url("\(cardPath)/attachments/linked.png").path
) == "real.png"
)
}
/// Template instantiation: born today, not forked `created` and `modified` both fresh, and
/// the same `Date` for the whole tree.
@Test func bornStampsCreatedAndModifiedFreshAtEveryLevel() throws {