The phone joins the format — KanbanMobile MVP: shared storage verbatim over an iCloud container
A second product, not a second edition: dev.rzen.indie.KanbanMobile (iOS 26,
iPhone-only) compiles Kanban/Storage as source files, so a format change that
breaks the phone breaks this build the day it's made. Boards live in
iCloud.dev.rzen.indie.Kanban — named after the Mac bundle id so the Mac app can
adopt the container later without a migration; until then the folder is
"Lanework" in iCloud Drive and the Mac opens boards there through the open
panel.
EchoLedger grows #if os(macOS) gates around its three consumer surfaces
(verdicts/BoardDiff, harvest/HarvestedReceipt, comment retirement/CommentPath)
— the recording side BoardWriter stamps compiles on every platform, and the
gates are the seam a future phone verdict surface lands behind. AgentGuide
stays Mac-only.
The phone's watcher is NSMetadataQuery: BoardIndexStore (one query, package
UTI export makes a .kanban directory one item, equality-gated rescans,
download kicks per pass), BoardSession (materialization sweep before every
fail-fast walk, NSFileCoordinator brackets, perform{} = coordinated write then
awaited reload, ParseMemo threaded), CloudHome (off-main container resolution,
LANEWORK_LOCAL_ROOT DEBUG override for simulator work without an account).
Screens: Boards -> lanes -> cards -> card editor, value-routed by ItemID with
every screen re-reading the live snapshot; leading swipe moves a card via
confirmationDialog, trailing swipe sends it to .trash/; the editor commits
title through the Mac's canonical rename path and body through writeBody, with
drafts that survive reloads; attributes are the three typed style fields
(icon, iconColor, background) — labels is a reserved unknown-field key and
deliberately has no editor. Settings carries IndieBackup (backup root = the
container's Documents, restore rebuild = an index rescan, controller
constructed only once the home resolves).
Arbiter: KanbanMobile green for iOS Simulator, Kanban green for macOS, 3006
unit tests / 517 suites passed (PointerLatencyTests excluded — mid-rework
uncommitted in a parallel session).
Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
This commit is contained in:
+67
@@ -3,6 +3,7 @@ options:
|
||||
bundleIdPrefix: dev.rzen.indie
|
||||
deploymentTarget:
|
||||
macOS: "26.0"
|
||||
iOS: "26.0"
|
||||
xcodeVersion: "26.0"
|
||||
defaultConfig: Debug
|
||||
|
||||
@@ -51,6 +52,15 @@ packages:
|
||||
libgit2:
|
||||
url: https://github.com/ibrahimcetin/libgit2.git
|
||||
exactVersion: 1.9.2
|
||||
# Local backup/restore for the phone app's Settings tab (indie-backup skill). `KanbanMobile`
|
||||
# only — see that target's dependencies below; the Mac app has no Settings surface for this
|
||||
# yet. Pinned to `from: "2.0.0"`, the tag the persistence-agnostic API (a single `backupRoot`
|
||||
# plus a rebuild-cache-on-restore callback — here, `BoardIndexStore.refresh()`, no database in
|
||||
# the loop) shipped under; 1.0.x resolves to the older iCloud-required, CloudKit-coupled
|
||||
# surface this app doesn't want.
|
||||
IndieBackup:
|
||||
url: https://git.rzen.dev/rzen/indie-backup.git
|
||||
from: "2.0.0"
|
||||
|
||||
settings:
|
||||
base:
|
||||
@@ -143,6 +153,63 @@ targets:
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
||||
|
||||
# MARK: - Lanework for iPhone
|
||||
#
|
||||
# **The mobile app is a second product, not a second edition** (distinct bundle id, ruled
|
||||
# 2026-08-07): `dev.rzen.indie.KanbanMobile`, its own App Store record, iPhone-only for the MVP
|
||||
# (iPad joins by widening TARGETED_DEVICE_FAMILY, not by a new target). It compiles
|
||||
# `Kanban/Storage` **verbatim — the same source files, not a copy and not a package** — so the
|
||||
# two apps cannot drift on the format contract: a storage change that breaks the phone breaks
|
||||
# this project's build, in this repo, on the day it's made.
|
||||
#
|
||||
# What it deliberately does NOT compile: LiveStore (FSEvents — macOS-only machinery; the phone's
|
||||
# change signal is NSMetadataQuery over the ubiquity container), Git/History (no git support in
|
||||
# the mobile MVP), Tier, and all of `Kanban/UI`. The mobile app's own sources live under
|
||||
# `KanbanMobile/`.
|
||||
#
|
||||
# Boards live in the iCloud Drive container `iCloud.dev.rzen.indie.Kanban` — named after the
|
||||
# *Mac* app's bundle id because container ids are not bundle ids, and that name is the one the
|
||||
# Mac app can adopt later without migrating anyone's data (see KanbanMobile.entitlements).
|
||||
|
||||
KanbanMobile:
|
||||
type: application
|
||||
platform: iOS
|
||||
sources:
|
||||
- KanbanMobile
|
||||
# The shared storage engine — frontmatter, loader, writer, ranks, integrity. Pure
|
||||
# Foundation + Yams + swift-markdown; no AppKit anywhere in the directory, which is what
|
||||
# makes this line possible. One exclusion: AgentGuide reaches up into LiveStore's
|
||||
# EchoLedger (its bracketed-write seam), and the AGENTS.md guide it writes is a Mac
|
||||
# feature — agents work where the Mac app runs.
|
||||
- path: Kanban/Storage
|
||||
excludes:
|
||||
- AgentGuide.swift
|
||||
# The writer layer's one upward dependency: BoardWriter stamps every write into the
|
||||
# task-local receipt ledger so a store can tell its own echoes from foreign changes. The
|
||||
# recording side is pure CryptoKit + Foundation + Synchronization and compiles here; the
|
||||
# ledger's *consumer* surfaces — verdicts (BoardDiff), harvest (HarvestedReceipt), comment
|
||||
# retirement (CommentPath) — are the Mac watcher/committer's vocabulary and sit behind
|
||||
# `#if os(macOS)` gates inside the file. On the phone the "watcher" is the metadata-query
|
||||
# observer; when it grows a verdict surface, those gates are the seam it lands behind.
|
||||
- path: Kanban/LiveStore/EchoLedger.swift
|
||||
dependencies:
|
||||
- package: Yams
|
||||
# BodyMarkup (Kanban/Storage) imports Markdown, so the dependency rides along even though
|
||||
# the MVP has no preview surface yet.
|
||||
- package: swift-markdown
|
||||
product: Markdown
|
||||
- package: IndieBackup
|
||||
settings:
|
||||
base:
|
||||
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanMobile
|
||||
MARKETING_VERSION: "1.0"
|
||||
INFOPLIST_FILE: KanbanMobile/Info.plist
|
||||
CODE_SIGN_ENTITLEMENTS: KanbanMobile/KanbanMobile.entitlements
|
||||
GENERATE_INFOPLIST_FILE: false
|
||||
SWIFT_STRICT_CONCURRENCY: complete
|
||||
# iPhone only. The iPad milestone widens this to "1,2" rather than adding a target.
|
||||
TARGETED_DEVICE_FAMILY: "1"
|
||||
|
||||
# MARK: - Unit tests
|
||||
#
|
||||
# The suite, hosted by the app: every test in `KanbanTests/` exercises the storage format, the
|
||||
|
||||
Reference in New Issue
Block a user