Remove the App Group wholesale — one sandbox, one bookmark, one flag

Phase 2 of the one-app pivot (DESIGN 12 ▸ App-side state, re-ruled
2026-07-30; reworks 566deab). AppGroup retires; what remains is
AppStateHome — ordinary sandbox Application Support as the one home for
the registry, clipboard staging and template stores, keeping the
unit-test-host redirect (the test host is the app and would sweep real
state). Scalar defaults return to UserDefaults.standard.

BoardRecord's per-edition grant slots and openNow flags collapse to one
bookmark + one isOpenNow; the legacy-key decode and adopt-in-memory
paths go (nothing shipped with group-era records), while the founding
four-keys-required / defaults-for-everything-since decode policy stays —
a bookmarkless record decodes as the born-orphan row rather than
quarantining the list. needsReopen and the pre-anchored re-grant panel
are removed whole: the only state that flow served — a record granted by
a sibling sandbox — is unrepresentable now, and a dead bookmark of our
own was already the orphan case by explicit comment. The
indexOfRecord path fallback dies with it; path is never a key again.

The cross-process freshness stamp (mtime+size re-read) and
BoardEditionPresence with its popover "Also open in…" line retire; the
clipboard prune keeps its atomic .sweeping/ claim-then-delete, reframed
for crash residue and open -n copies rather than sibling editions. The
application-groups entitlement key is gone.

1880 tests in 317 suites green (13 cross-edition tests retired with
their subject).

Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
2026-07-30 17:46:32 -04:00
parent 092300c7d2
commit 2c6b8fe63a
27 changed files with 385 additions and 1461 deletions
+10 -15
View File
@@ -90,18 +90,15 @@ enum LaunchPlan: Equatable, Sendable {
/// Without that, every audit run would stamp a temp folder into the user's real recents list
/// (`BoardRegistry.defaultStorageURL`), where it would sit for good as an unavailable row pointing at a
/// directory that no longer exists and its launch sweep would collect the user's real staged copy
/// (`ClipboardStore.defaultStagingRoot`). Both of those homes are now the **shared App Group
/// container** (12-editions.md Distribution, ruled 2026-07-29), so each of those side effects would
/// land on the sibling edition as well as this one. Tying them to the same flag rather than to separate
/// (`ClipboardStore.defaultStagingRoot`). Tying them to the same flag rather than to separate
/// arguments is deliberate: they are one decision "this launch is synthetic" and a second argument
/// is a second chance to apply only half of it.
///
/// The honest residual: `UserDefaults` is **not** redirected, so an audit run can still write the
/// three app-wide scalars (`AppPreferences`) into the real domain the group's shared suite since the
/// same ruling. They are a window size, a restore toggle this launch never consults, and the
/// quick-style recents list no documents, nothing destructive, and redirecting a defaults domain from
/// inside the process is not something the platform actually supports. It is stated rather than
/// fixed.
/// three app-wide scalars (`AppPreferences`) into the real domain. They are a window size, a restore
/// toggle this launch never consults, and the quick-style recents list no documents, nothing
/// destructive, and redirecting a defaults domain from inside the process is not something the
/// platform actually supports. It is stated rather than fixed.
enum UITestLaunch {
private static let logger = Logger(subsystem: "dev.rzen.indie.Kanban", category: "ui-test-launch")
@@ -206,17 +203,15 @@ enum UITestLaunch {
.appendingPathComponent("LaneworkUITestFixture", isDirectory: true)
}
/// Where the fixture launch's registry lives beside the board rather than in the shared App
/// Group container, which is the whole point (see the type's note).
/// Where the fixture launch's registry lives beside the board rather than in Application
/// Support, which is the whole point (see the type's note).
static var registryStorageURL: URL {
scratchRoot.appendingPathComponent("board-registry.json", isDirectory: false)
}
/// Where the fixture launch's clipboard snapshots live, on the registry's terms and now for a
/// sharper reason: the real staging root moved into the **shared** App Group container
/// (12-editions.md Both editions installed), so an audit run's launch sweep would otherwise
/// collect the developer's own staged copy and the sibling edition's, since there is only one
/// store now. Redirected by the same flag, because it is the same one decision.
/// Where the fixture launch's clipboard snapshots live, on the registry's terms: an audit run's
/// launch sweep would otherwise collect the developer's own staged copy. Redirected by the same
/// flag, because it is the same one decision.
static var clipboardStagingRoot: URL {
scratchRoot.appendingPathComponent("Clipboard", isDirectory: true)
}