Collapse the edition split — one target, twins merged, verify-editions retired
Phase 1 of the 2026-07-30 one-app pivot (DESIGN 0bec9a6, card c3a3ddd5):
the KanbanPro target, LaneworkPro scheme, KanbanProTests module-alias
bundle, KanbanPro/ source root and scripts/verify-editions.sh retire
wholesale. project.yml reads as a single-target file again (anchors
inlined, header rewritten in tier vocabulary).
The edition twins merge: EditionTypes -> PasteboardTypes (one
UTType(exportedAs:) home — the one app owns the family types),
EditionAbout -> AboutBox (the quiet Pro signpost survives as the About
box's one line; "…in Settings" deferred until the StoreKit phase gives
it somewhere to point). InertGitTests drops its Base prefix — the
inert-.git posture is unconditional app behavior, unsubscribed and
lapsed being one state.
Entitlements gain com.apple.security.network.client, declared now and
dormant until Pro's remotes use it; no keychain access group. The App
Group key deliberately stays — it goes with AppGroup.swift in phase 2,
since pulling it first would silently drop the app into the fallback
container. README/RELEASE.md build-and-pipeline prose updated to the
one-record world; the subscription story lands with phase 3.
1893 tests in 322 suites green.
Claude-Session: https://claude.ai/code/session_01SR4XGjmBE16ZUYWpfFHXwY
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import IndieAbout
|
||||
|
||||
/// **The About box's configuration — one of the three places the app names Lanework Pro.**
|
||||
///
|
||||
/// 12-editions.md ▸ Tier naming: "The free tier presents as a complete app, not a demo: Pro is
|
||||
/// named in exactly three places — one line in the About box, the contextual popover line on
|
||||
/// `.git` boards, and the Settings Pro section where the subscription actually lives. Nothing on
|
||||
/// the welcome screen, nothing in banners." The popover line is `BoardGitNote`'s; this is the
|
||||
/// About line, riding as the second line of the copyright text — `IndieAbout` renders
|
||||
/// `copyrightText` as one multi-line `Text`, so the signpost is literally a line in the box, no
|
||||
/// custom layout.
|
||||
///
|
||||
/// **One box for every tier.** The 2026-07-27 split gave Pro a twin of this file without the
|
||||
/// signpost, on the reasoning that a separate Pro app pointing at itself would be noise; the
|
||||
/// one-app collapse (12, re-ruled 2026-07-30) makes Pro a subscription, so the line is a pointer
|
||||
/// to something a subscriber has already taken up rather than to another download — true either
|
||||
/// way, and shown to everyone. It stays deliberately flat: what Pro adds, in one sentence, with
|
||||
/// no call to action. It gains its "…in Settings" when the Settings Pro section it would point at
|
||||
/// actually exists (the StoreKit phase); until then a direction would be a dead end.
|
||||
enum AboutBox {
|
||||
|
||||
/// The About window's content: version/build/date from the stamped Info.plist
|
||||
/// (`update_build_info.sh` — CFBundleVersion, BuildDate, BuildHash), the version line
|
||||
/// opening the bundled CHANGELOG.md, and the bundled ISC LICENSE.md as the one document
|
||||
/// link. Both documents are authored to the Apple inline-Markdown subset — see the files'
|
||||
/// own single-line paragraphs.
|
||||
static var configuration: AppInfoConfiguration {
|
||||
AppInfoConfiguration(
|
||||
copyrightText: "© 2026 rzen\nLanework Pro adds git-backed board history and sync.",
|
||||
documents: [.license(extension: "md")],
|
||||
changelogDocument: .changelog()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -209,11 +209,11 @@ public final class AppModel {
|
||||
/// **The composition root for `HistoryProviding`** (12-editions.md ▸ The provider seam): what a
|
||||
/// board session's undo stack is built by, called once per board as its session begins.
|
||||
///
|
||||
/// Base binds the native stack — a pair of step stacks over the inverses registered at the
|
||||
/// Writer boundary (13-native-undo.md, `NativeHistoryProvider`) — and that is the default here
|
||||
/// because it is the *shared* code's implementation: both targets compile it, and Pro runs it
|
||||
/// too until pro-m1 replaces this closure with the git provider (06-history-undo.md). Nothing in
|
||||
/// this file is edition-conditional; the edition difference is which closure the root installs.
|
||||
/// The free tier binds the native stack — a pair of step stacks over the inverses registered at
|
||||
/// the Writer boundary (13-native-undo.md, `NativeHistoryProvider`) — and that is the default
|
||||
/// here because it is the substrate every tier can always fall back to; pro-m1 installs the git
|
||||
/// provider over this closure when the subscription is active (06-history-undo.md). Nothing in
|
||||
/// this file is tier-conditional; the tier difference is which closure the root installs.
|
||||
///
|
||||
/// It takes the store because that is what a provider is a history *of*: the git provider needs
|
||||
/// the board root it is a repository at, and the native one's steps are computed from the same
|
||||
@@ -239,7 +239,7 @@ public final class AppModel {
|
||||
/// (13-native-undo.md ▸ Rules). It lives here for the store's reason exactly: the session is
|
||||
/// what every window over this board shares, and "undo is board-local".
|
||||
///
|
||||
/// Which implementation it is, is the edition's answer and nobody else's
|
||||
/// Which implementation it is, is the tier's answer and nobody else's
|
||||
/// (12-editions.md ▸ The provider seam) — see `AppModel.makeHistoryProvider`.
|
||||
public let history: any HistoryProviding
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ import UniformTypeIdentifiers
|
||||
// JSON `ClipboardManifest` below (04-interactions.md ▸ Clipboard: "the pasteboard carries a JSON
|
||||
// manifest + plain text"). Declared in `Info.plist` beside the two drag types, for the same reason
|
||||
// those are: a payload nobody has declared is a payload the system will not carry. The constant
|
||||
// itself lives in `EditionTypes.swift` — base exports the type, Pro imports it, and each edition's
|
||||
// twin uses the initializer its own plist can honor.
|
||||
// itself lives with its two siblings in `PasteboardTypes.swift`, exported once by the app that
|
||||
// owns it.
|
||||
|
||||
// MARK: - The manifest
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ import os
|
||||
/// missing or unreadable **refuses whole and writes nothing** (04-interactions.md ▸ Clipboard,
|
||||
/// re-ruled 2026-07-29 — Finder's invariant: an item arrives whole or not at all).
|
||||
///
|
||||
/// **⚠ one-app collapse phase 2**: the paragraph below describes a sharing arrangement that stops
|
||||
/// existing when the App Group does (12-editions.md ▸ App-side state, re-ruled 2026-07-30) — the
|
||||
/// staging store moves to the ordinary sandbox container and the sibling it tolerates is only ever
|
||||
/// the developer's own second copy. The tolerance itself is worth keeping either way.
|
||||
///
|
||||
/// **The store is shared by every installed edition** (12-editions.md ▸ Both editions installed, ruled
|
||||
/// 2026-07-29): the group container is one container, so ⌘C in base pastes full-fidelity in Pro. The
|
||||
/// lifecycle below is unchanged by that — both editions read the same machine-wide pasteboard, so both
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import IndieAbout
|
||||
|
||||
/// **Base Lanework's About-box configuration — one of the two places base names Lanework Pro.**
|
||||
///
|
||||
/// 12-editions.md ▸ Quiet signposts: "Base presents as a complete app, not a demo: Pro is named
|
||||
/// in exactly two places — one line in the About box, and the contextual popover line on `.git`
|
||||
/// boards. Nothing on the welcome screen, nothing in banners." The popover line is
|
||||
/// `BoardGitNote`'s; this is the About line, riding as the second line of the copyright text —
|
||||
/// `IndieAbout` renders `copyrightText` as one multi-line `Text`, so the signpost is literally a
|
||||
/// line in the box, no custom layout.
|
||||
///
|
||||
/// **The Pro target does not compile this file** (project.yml excludes it and compiles
|
||||
/// `KanbanPro/Edition/EditionAbout.swift` instead): Pro's About has no signpost — the app it
|
||||
/// would point to is the one showing the box — and falls back to the Info.plist copyright. The
|
||||
/// difference is a file one target builds and the other does not, per the edition split's
|
||||
/// no-`#if` doctrine (12-editions.md ▸ Targets).
|
||||
enum EditionAbout {
|
||||
|
||||
/// The About window's content: version/build/date from the stamped Info.plist
|
||||
/// (`update_build_info.sh` — CFBundleVersion, BuildDate, BuildHash), the version line
|
||||
/// opening the bundled CHANGELOG.md, and the bundled ISC LICENSE.md as the one document
|
||||
/// link. Both documents are authored to the Apple inline-Markdown subset — see the files'
|
||||
/// own single-line paragraphs.
|
||||
static var configuration: AppInfoConfiguration {
|
||||
AppInfoConfiguration(
|
||||
copyrightText: "© 2026 rzen\nLanework Pro adds git-backed board history and sync.",
|
||||
documents: [.license(extension: "md")],
|
||||
changelogDocument: .changelog()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
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")
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import Foundation
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
// MARK: - The family pasteboard types
|
||||
|
||||
/// **The three drag and clipboard types the app owns**, declared once with `UTType(exportedAs:)` to
|
||||
/// match the `UTExportedTypeDeclarations` in `Kanban/Info.plist`. `exportedAs` is a claim of
|
||||
/// ownership the system checks against the running app's plist, and one app making it is the whole
|
||||
/// story — there is no importing sibling to keep in step (12-editions.md ▸ Distribution, re-ruled
|
||||
/// 2026-07-30: the types are "declared and exported once, by the one app — no ownership twins").
|
||||
///
|
||||
/// 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")
|
||||
}
|
||||
@@ -110,6 +110,11 @@ enum TemplateEngine {
|
||||
/// and the clipboard's staging store (09-templates.md ▸ Save as Template ▸ Storage, re-homed
|
||||
/// 2026-07-29; 02-architecture.md § Per-board app state, "App-wide state has the same home").
|
||||
///
|
||||
/// **⚠ one-app collapse phase 2**: the cross-edition half of this rationale retires with the App
|
||||
/// Group (12-editions.md ▸ App-side state, re-ruled 2026-07-30); the store simply moves to the
|
||||
/// ordinary sandbox container and keeps every property below, since it never had bookmarks or
|
||||
/// grants to lose.
|
||||
///
|
||||
/// > **templates cross editions**: a template saved in base appears in Pro's chooser, honoring 12's
|
||||
/// > never-an-empty-home-screen promise (templates are plain board folders — no per-edition
|
||||
/// > semantics, no bookmark grant ceremony; the group container is directly writable by every
|
||||
|
||||
Reference in New Issue
Block a user