The pasteboard UTType constants become edition twins — Pro stops claiming ownership it doesn't have

Latent since the m7 split: base's Info.plist exports the three family
pasteboard types (cards, lanes, clipboard) and Pro's imports them, but
the shared constants declared all three UTType(exportedAs:) — a claim
of ownership the system checks against the running app's plist. In Pro
that claim is false, and the first touch (the clipboard type, via the
launch sweep) raised a runtime fault that blocked board loading under
the debugger.

EditionAbout's twin-file pattern, applied to an initializer: the
constants move to Kanban/App/EditionTypes.swift (exportedAs) with a
KanbanPro/Edition/EditionTypes.swift twin (importedAs, identifiers
verbatim); project.yml excludes base's copy from Pro. Type identity is
the string, so payloads cross editions unchanged. Verified: Pro
fixture launch opens the board with zero UTI warnings; both suites
green; verify-editions 30/30.

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-29 21:46:27 -04:00
parent 27158a06cd
commit 546ed94412
5 changed files with 67 additions and 21 deletions
+7 -9
View File
@@ -3,15 +3,13 @@ import Foundation
import UniformTypeIdentifiers import UniformTypeIdentifiers
// MARK: - The clipboard type // MARK: - The clipboard type
//
extension UTType { // `UTType.laneworkClipboard` what a Lanework copy puts on the pasteboard under its own type, the
// JSON `ClipboardManifest` below (04-interactions.md Clipboard: "the pasteboard carries a JSON
/// What a Lanework copy puts on the pasteboard under its own type the JSON `ClipboardManifest` // manifest + plain text"). Declared in `Info.plist` beside the two drag types, for the same reason
/// (04-interactions.md Clipboard: "the pasteboard carries a JSON manifest + plain text"). // those are: a payload nobody has declared is a payload the system will not carry. The constant
/// Declared as an exported type in `Info.plist` beside the two drag types, for the same reason // itself lives in `EditionTypes.swift` base exports the type, Pro imports it, and each edition's
/// those are: a payload nobody has declared is a payload the system will not carry. // twin uses the initializer its own plist can honor.
static let laneworkClipboard = UTType(exportedAs: "dev.rzen.indie.kanban.clipboard")
}
// MARK: - The manifest // MARK: - The manifest
+26
View File
@@ -0,0 +1,26 @@
import Foundation
import UniformTypeIdentifiers
// MARK: - The family pasteboard types, as base declares them
/// **Base's half of the edition UTI posture** (12-editions.md Targets: base *exports* the family
/// types, Pro *imports* them, declarations verbatim identical). `UTType(exportedAs:)` is a claim of
/// ownership the system checks against the running app's `Info.plist` in Pro, whose plist imports
/// these, that claim is a runtime fault. So the three constants live in an edition twin rather than
/// beside their payloads: Pro compiles `KanbanPro/Edition/EditionTypes.swift`, which declares the
/// same names with `UTType(importedAs:)` the edition split's file-level doctrine, applied to an
/// initializer.
///
/// What each type *is* stays documented at its payload `DragPayload` for cards and lanes,
/// `ClipboardManifest` for the clipboard type.
extension UTType {
/// A drag carrying board **cards** see `DragPayload`.
static let laneworkCards = UTType(exportedAs: "dev.rzen.indie.kanban.cards")
/// A drag carrying board **lanes** see `DragPayload`.
static let laneworkLanes = UTType(exportedAs: "dev.rzen.indie.kanban.lanes")
/// A Lanework copy's pasteboard manifest see `ClipboardManifest`.
static let laneworkClipboard = UTType(exportedAs: "dev.rzen.indie.kanban.clipboard")
}
+8 -12
View File
@@ -3,18 +3,14 @@ import Foundation
import UniformTypeIdentifiers import UniformTypeIdentifiers
// MARK: - The drag types // MARK: - The drag types
//
extension UTType { // `UTType.laneworkCards` a drag carrying board **cards**, live faces or trash rows
// (04-interactions.md Drag and drop, The trash) and `.laneworkLanes`, the strip's own reorder
/// A drag carrying board **cards** live card faces or trash rows (04-interactions.md Drag // and the cross-board lane transfer. Declared in `Info.plist` beside the clipboard type, because a
/// and drop, The trash). Declared as an exported type in `Info.plist`, because a system drag // system drag session is what crosses window boundaries, shows the copy badge, and gives the
/// session is what crosses window boundaries, shows the copy badge, and gives the full-size // full-size replica preview (DRAG-REORDER.md § Cross-board sessions). The constants themselves live
/// replica preview (DRAG-REORDER.md § Cross-board sessions). // in `EditionTypes.swift` base exports the types, Pro imports them, and each edition's twin uses
static let laneworkCards = UTType(exportedAs: "dev.rzen.indie.kanban.cards") // the initializer its own plist can honor.
/// A drag carrying board **lanes** the strip's own reorder and the cross-board lane transfer.
static let laneworkLanes = UTType(exportedAs: "dev.rzen.indie.kanban.lanes")
}
// MARK: - What a drag carries // MARK: - What a drag carries
+21
View File
@@ -0,0 +1,21 @@
import Foundation
import UniformTypeIdentifiers
// MARK: - The family pasteboard types, as Pro declares them
/// **Pro's half of the edition UTI posture** the twin of base's `Kanban/App/EditionTypes.swift`,
/// which owns the full story (12-editions.md Targets: base *exports* the family types, Pro
/// *imports* them). `UTType(importedAs:)` is the initializer that matches Pro's
/// `UTImportedTypeDeclarations`; the identifiers and their `public.data` conformance are verbatim
/// base's, so a payload written by one edition reads in the other by type identity alone.
extension UTType {
/// A drag carrying board **cards** see `DragPayload`.
static let laneworkCards = UTType(importedAs: "dev.rzen.indie.kanban.cards")
/// A drag carrying board **lanes** see `DragPayload`.
static let laneworkLanes = UTType(importedAs: "dev.rzen.indie.kanban.lanes")
/// A Lanework copy's pasteboard manifest see `ClipboardManifest`.
static let laneworkClipboard = UTType(importedAs: "dev.rzen.indie.kanban.clipboard")
}
+5
View File
@@ -109,6 +109,11 @@ targets:
# Quiet signposts); Pro compiles its own `KanbanPro/Edition/EditionAbout.swift` twin # Quiet signposts); Pro compiles its own `KanbanPro/Edition/EditionAbout.swift` twin
# without it — the edition split's file-level doctrine, applied to a string. # without it — the edition split's file-level doctrine, applied to a string.
- App/EditionAbout.swift - App/EditionAbout.swift
# Base declares the family pasteboard types `UTType(exportedAs:)` to match its
# UTExportedTypeDeclarations; Pro's plist *imports* them (12-editions.md ▸ Targets), so it
# compiles its own `KanbanPro/Edition/EditionTypes.swift` twin using `importedAs` — the
# same doctrine, applied to an initializer.
- App/EditionTypes.swift
# Pro's own source root — reserved now, filled by pro-m1. See KanbanPro/Edition/ProEdition.swift. # Pro's own source root — reserved now, filled by pro-m1. See KanbanPro/Edition/ProEdition.swift.
- path: KanbanPro - path: KanbanPro
excludes: excludes: