Home app-side state in the shared App Group container
Every edition declares group.dev.rzen.indie.Kanban and homes its
app-side state there from day one (12-editions.md ruling 2026-07-29):
- AppGroup namespace: container resolution with per-edition fallback
when unprovisioned, shared UserDefaults suite, edition identity, and
a unit-test-host redirect (the test host IS the app — its launch
sweep and recents refresh must not touch the real shared container).
- BoardRecord: bookmark/isOpenNow replaced by per-edition grants and
openNow keyed by bundle id; hand-written Codable keeps legacy keys
decoding (adopted in memory as the running edition's slots, upgraded
on first save); every other field stays common.
- RecentBoard gains needsReopen: no grant of ours but somebody's —
first click runs an open panel pre-anchored at the recorded path,
prompt "Grant"; recordOpen mints this edition's slot onto the
matched shared record (path fallback only after identity fails and
only against records holding no grant of ours, so re-granting never
forks the record).
- Cross-edition freshness: stat-cheap mtime+size stamp re-reads the
registry when the sibling edition wrote it, so one edition's save
never erases the other's records wholesale.
- restorables() filters on this edition's open-now flags; the board
popover gains BoardEditionPresence ("Also open in Lanework Pro"),
pid-liveness-checked so crash residue never lies.
- Clipboard staging store moves to the group container; the sweep
claims doomed trees by atomic rename into .sweeping/ then deletes,
so the sibling's concurrent sweep is a non-event.
- Template store re-homed to the group container per the 09-templates
re-ruling; scalars (quick-style recents, window size) move to the
shared suite.
- verify-editions.sh: 30 checks (each edition carries exactly the
family group). No pathfinder 1.x migrator: 1.x predates the
registry; state starts fresh in the group container.
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -79,8 +79,7 @@ struct OpenRecentMenu: View {
|
||||
Menu("Open Recent") {
|
||||
ForEach(rows) { row in
|
||||
Button(row.displayName) {
|
||||
guard let url = row.url else { return }
|
||||
appModel.openBoard(at: url)
|
||||
appModel.open(row)
|
||||
}
|
||||
.disabled(!row.canOpen)
|
||||
}
|
||||
@@ -291,7 +290,7 @@ struct DuplicateBoardCommand: View {
|
||||
/// rule covers File ▸ Duplicate". So the shape here is `DuplicateBoardCommand`'s, step for step —
|
||||
/// flush, then a cancellable copy off the main actor under an in-progress row — and the differences
|
||||
/// are all in the engine (`TemplateEngine.saveAsTemplate(boardAt:titled:into:)`): the destination is
|
||||
/// Application Support rather than a sibling, `.git` and `.trash/` are dropped rather than forked, a
|
||||
/// the app-side template store rather than a sibling, `.git` and `.trash/` are dropped rather than forked, a
|
||||
/// collision auto-renames rather than failing, and a `template:` key lands on the copy.
|
||||
///
|
||||
/// **No save panel, ever.** The store is the app's own container — "friction-free sandbox writes, no
|
||||
@@ -313,7 +312,7 @@ struct DuplicateBoardCommand: View {
|
||||
///
|
||||
/// Board window only, and disabled under the read-only lock — with the exception 09 spells out and
|
||||
/// 02-architecture.md ▸ Live-reload resilience scopes: **under the unwritable-location lock alone it
|
||||
/// stays live**, because it "reads the board and writes Application Support" (archiving the
|
||||
/// stays live**, because it "reads the board and writes the app-side store" (archiving the
|
||||
/// read-only DMG board being inspected is a legitimate errand), *unless* an open Edit or raw-source
|
||||
/// session holds unsaved content — content that lock's suspended saves cannot flush, and which the
|
||||
/// template would therefore silently miss. The other two locks disable it outright: a vanished root
|
||||
|
||||
Reference in New Issue
Block a user