Generated and pasted board backgrounds move into .backgrounds/ — the board root stops collecting the app's own pictures

New app-written background images (Theme tab ▸ Pattern, and Edit ▸ Paste
as Board Background) now land in a hidden `.backgrounds/` folder at
board root instead of beside index.md, matching the `.trash/` app-managed
pattern. `background.image` stores the qualified relative reference
(`.backgrounds/facets.png`); the resolver needed no change at all, since
it already accepted any relative path inside the board root — the same
mechanism that already resolved `art/backdrops/sunset.png` resolves the
new location for free. `BoardWriter.writeBoardImage` now creates its
destination folder if missing, since `.backgrounds/` won't exist until a
board's first generated or pasted background.

The Finder collision-ladder (`BoardStore.boardImageName`) is rescoped to
`.backgrounds/`'s own contents, and its overwrite-in-place check now
recognizes only the qualified form as "ours" — a legacy bare
`background.image: facets.png` from before this change is read as a
foreign reference rather than migrated, so a regeneration writes a fresh
`.backgrounds/` file and orphans the old one in place, per the no-migration
ruling. The Theme tab's Pattern/Solid mode-detection was updated to
recognize both the legacy and current spellings as the generator's own
output.

The board loader needed no change: `.backgrounds/` is a hidden,
non-UUID-shaped name, and `.skipsHiddenFiles` already keeps every hidden
entry off the lane walk before any name-based exclusion is consulted —
pinned with a new loader test. Deliberately did not add `.backgrounds` to
IntegrityRules' claimed-name/squatter-displacement table: that table
mirrors a specific existing DESIGN ruling this card doesn't amend.

Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
2026-08-09 08:38:07 -04:00
parent 0a01405ced
commit d0c546179f
9 changed files with 270 additions and 88 deletions
+20
View File
@@ -225,6 +225,26 @@ struct BoardLoaderStrayTests {
#expect(result.warnings.isEmpty)
}
/// **`.backgrounds/` is invisible to the loader** (03-board-ui.md § Styling Capabilities; ruled
/// 2026-08-09) no code change earns this: it is a hidden, non-UUID-shaped name, exactly the
/// stray shape `.trash` and `.DS_Store` already exercise above. Its contents (a generated PNG, in
/// this case) never appear as a lane candidate and never draw a `nonUUIDFolderIgnored` warning,
/// because the directory walk that would notice never descends into it at all.
@Test func backgroundsFolderAndItsContentsAreIgnoredWithoutWarning() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }
let lane = uuidFolderName()
try fixture.index("", "schema: 1\n")
try fixture.index(lane, "schema: 1\norder: 1024\n")
try fixture.strayFile(".backgrounds/facets.png", contents: "not really a png")
let result = try BoardLoader.load(boardRoot: fixture.root)
#expect(result.model.lanes.map(\.id.rawValue) == [lane])
#expect(result.warnings.isEmpty)
}
@Test func directorySymlinkIsTreatedAsStrayNotFollowed() throws {
let fixture = try BoardFixture()
defer { fixture.tearDown() }