The 2026-08-08 one-version ruling (12-editions.md ▸ PIVOT 2026-08-08) carried out: Kanban/Tier/ deleted wholesale (Tier, ProEntitlement, ProProducts, ProStorefront, the never-rendered ProSettingsSection) with TierTests and Configuration.storekit, whose project.yml resource entry and scheme storeKitConfiguration go with it. AppModel loses the entitlement, the currentTier seam, BoardSession.tier, and the purchase flow's reopenOpenBoards (its only caller was the storefront); AppDelegate's launch keeps only the appearance application. The three tests pinning the recorded tier and the reopen are deleted with their subject. The network-client entitlement stays — the sync capability to come needs it regardless — and the HistoryProviding seam stands untouched. 2,686 unit tests green (2,707 minus the 21 that tested what left). Claude-Session: https://claude.ai/code/session_014PtZdPwqZuqEDLc6wZMtEy
298 lines
13 KiB
YAML
298 lines
13 KiB
YAML
name: Kanban
|
|
options:
|
|
bundleIdPrefix: dev.rzen.indie
|
|
deploymentTarget:
|
|
macOS: "26.0"
|
|
iOS: "26.0"
|
|
xcodeVersion: "26.0"
|
|
defaultConfig: Debug
|
|
|
|
packages:
|
|
Yams:
|
|
url: https://github.com/jpsim/Yams.git
|
|
from: 6.0.0
|
|
# apple/swift-markdown — the card window's Preview parser (05-card-window.md ▸ Preview).
|
|
# Pinned to the next *minor* rather than the next major because the package is pre-1.0: a
|
|
# `from:` range here would silently accept 0.9's breaking changes, which is not what `from:`
|
|
# means for Yams at 6.x.
|
|
swift-markdown:
|
|
url: https://github.com/apple/swift-markdown.git
|
|
minorVersion: 0.8.0
|
|
# The About window: app icon, version/build from the stamped Info.plist, and the bundled
|
|
# LICENSE/CHANGELOG documents. Pre-1.0 like swift-markdown, same pinning logic.
|
|
IndieAbout:
|
|
url: https://git.rzen.dev/rzen/indie-about.git
|
|
minorVersion: 0.2.2
|
|
# SwiftGitX 0.4.0 / libgit2 1.9.2 (both `github.com/ibrahimcetin/…`) left with the git excision
|
|
# (strategy/01-git-excision.md, 2026-08-08) — the exact pins for resurrection are recorded there
|
|
# and at the `pre-git-excision` tag.
|
|
|
|
settings:
|
|
base:
|
|
SWIFT_VERSION: "6.0"
|
|
MACOSX_DEPLOYMENT_TARGET: "26.0"
|
|
DEVELOPMENT_TEAM: ${APPLE_TEAM_ID}
|
|
ENABLE_USER_SCRIPT_SANDBOXING: NO
|
|
|
|
# **One app target** (12-editions.md ▸ The target, re-ruled 2026-07-30). Lanework ships as a single
|
|
# Mac App Store app, `dev.rzen.indie.Kanban` — **one version, everything in it** (12-editions.md ▸
|
|
# PIVOT 2026-08-08): there is no edition axis for this file to express. The 2026-07-27 two-target
|
|
# split — a `KanbanPro` application target, its scheme, its bundle id, the `-module-alias
|
|
# Kanban=KanbanPro` test bundle and the twin source files that existed only because two bundles
|
|
# claimed different UTI ownership — retired wholesale with the collapse.
|
|
#
|
|
# What that buys this file: no anchors, no per-target exclusion lists, no second copy of the
|
|
# resources. There is one product, one Info.plist, one entitlements file, one Swift module, and one
|
|
# `.kanban` UTI **exported** (never imported) by the app that owns it.
|
|
targets:
|
|
# MARK: - Lanework
|
|
|
|
Kanban:
|
|
type: application
|
|
platform: macOS
|
|
sources:
|
|
- Kanban
|
|
# The bundled template store (09-templates.md ▸ Definition format): real board folders,
|
|
# copied into `Contents/Resources/Templates/` verbatim as a folder reference — the same
|
|
# arrangement `Fixtures/` uses below, and for the same reason. A template is a board, so it
|
|
# must reach the bundle as directories on disk, not as flattened resource files.
|
|
- path: Templates
|
|
type: folder
|
|
buildPhase: resources
|
|
# The About window's documents (LICENSE, CHANGELOG) — bundled so IndieAbout can render
|
|
# them in-app. Apple-inline-Markdown subset only: no `#` headings, no hard-wrapped
|
|
# paragraphs (every newline renders literally).
|
|
- path: LICENSE.md
|
|
type: file
|
|
buildPhase: resources
|
|
- path: CHANGELOG.md
|
|
type: file
|
|
buildPhase: resources
|
|
dependencies:
|
|
- package: Yams
|
|
- package: swift-markdown
|
|
product: Markdown
|
|
- package: IndieAbout
|
|
postBuildScripts:
|
|
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
|
name: Update Build Info
|
|
shell: /bin/sh
|
|
basedOnDependencyAnalysis: false
|
|
inputFiles:
|
|
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
|
|
- $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
|
|
settings:
|
|
base:
|
|
# The identifier 1.x shipped under, unchanged: 2.0 updates that App Store record rather
|
|
# than opening a second one (12-editions.md ▸ Distribution, ruled 2026-07-30).
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.Kanban
|
|
MARKETING_VERSION: "2.0"
|
|
INFOPLIST_FILE: Kanban/Info.plist
|
|
# `Kanban/Kanban.entitlements` — sandbox, user-selected files, app-scope bookmarks, and
|
|
# `com.apple.security.network.client`, **declared now and exercised by nothing**: the key
|
|
# stays because the sync capability to come needs it regardless (12-editions.md ▸ PIVOT
|
|
# 2026-08-08), and a key added later is a new provisioning profile and a new review
|
|
# surface. No App Group and no keychain access group — groups exist to share *between*
|
|
# apps, and there is one app, so app-side state homes in the ordinary sandbox container
|
|
# (`AppStateHome`).
|
|
CODE_SIGN_ENTITLEMENTS: Kanban/Kanban.entitlements
|
|
GENERATE_INFOPLIST_FILE: false
|
|
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), 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:
|
|
- path: KanbanMobile
|
|
excludes:
|
|
- CHANGELOG.md
|
|
# The Settings tab's About section (indie-about skill): the shared ISC license verbatim from
|
|
# the repo root, and a changelog of the phone app's own — it is a separate product with its
|
|
# own 1.0, so the Mac app's CHANGELOG.md would narrate someone else's releases. Both ride in
|
|
# Apple's inline-Markdown subset (bold headings, one physical line per paragraph).
|
|
- path: KanbanMobile/CHANGELOG.md
|
|
type: file
|
|
buildPhase: resources
|
|
- path: LICENSE.md
|
|
type: file
|
|
buildPhase: resources
|
|
# 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
|
|
# The Settings tab's About section — same package, same pin as the Mac target above.
|
|
- package: IndieAbout
|
|
# Same stamping as the Mac target: CFBundleVersion from git commit count, BuildDate/BuildHash
|
|
# into the product's Info.plist, so the About section can show "Built YYYY-MM-DD".
|
|
postBuildScripts:
|
|
- script: '"${SRCROOT}/../indie-skills/skills/app-versioning/scripts/update_build_info.sh"'
|
|
name: Update Build Info
|
|
shell: /bin/sh
|
|
basedOnDependencyAnalysis: false
|
|
inputFiles:
|
|
- $(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)
|
|
- $(DWARF_DSYM_FOLDER_PATH)/$(DWARF_DSYM_FILE_NAME)/Contents/Info.plist
|
|
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
|
|
# The Mac icon's own 1024 master, alpha-flattened (App Store icons must be opaque) into a
|
|
# single-size iOS set — one product family, one mark.
|
|
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
|
# 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
|
|
# live store or the UI model through `@testable import Kanban`, so it needs the app's module and
|
|
# its bundle around it. `Fixtures/` rides in as a folder reference for the same reason the
|
|
# templates do — a fixture is a board, and a board is directories on disk.
|
|
|
|
KanbanTests:
|
|
type: bundle.unit-test
|
|
platform: macOS
|
|
sources:
|
|
- KanbanTests
|
|
- path: Fixtures
|
|
type: folder
|
|
buildPhase: resources
|
|
dependencies:
|
|
- target: Kanban
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanTests
|
|
GENERATE_INFOPLIST_FILE: true
|
|
SWIFT_STRICT_CONCURRENCY: complete
|
|
|
|
# MARK: - UI tests
|
|
#
|
|
# These launch the real app and drive its menu bar — the slowest, most environment-dependent part
|
|
# of the suite. The accessibility audits (10-accessibility.md ▸ Verification) walk every surface
|
|
# the design names.
|
|
|
|
KanbanUITests:
|
|
type: bundle.ui-testing
|
|
platform: macOS
|
|
sources:
|
|
# The verification documents live with the tests they belong to (10-accessibility.md
|
|
# ▸ Verification: "A manual VoiceOver smoke script lives with the test plan"), so they are
|
|
# listed in the project — visible where the suite is — but with no build phase: they are
|
|
# documentation, not resources the bundle should carry. `EndToEndVerification.md` is the same
|
|
# arrangement for the golden-flow, fail-fast and performance suites: the run command, the
|
|
# prerequisites, and the flows that stayed manual.
|
|
- path: KanbanUITests
|
|
excludes:
|
|
- "**/*.md"
|
|
- path: KanbanUITests/AccessibilityVerification.md
|
|
buildPhase: none
|
|
- path: KanbanUITests/EndToEndVerification.md
|
|
buildPhase: none
|
|
dependencies:
|
|
- target: Kanban
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanUITests
|
|
GENERATE_INFOPLIST_FILE: true
|
|
SWIFT_STRICT_CONCURRENCY: complete
|
|
|
|
# MARK: - iPhone UI tests
|
|
#
|
|
# The mobile app's first runtime coverage: launches the real app against a plain directory
|
|
# (`LANEWORK_LOCAL_ROOT`, `CloudHome`'s DEBUG override) seeded from a copy of
|
|
# `Fixtures/Valid/rich-board.kanban`, so these tests need no iCloud account and touch nothing but
|
|
# a scratch directory the test itself creates and owns. `Fixtures/` rides in as a folder reference
|
|
# for the same reason `KanbanTests`' copy does — a fixture is a board, and a board is directories
|
|
# on disk.
|
|
|
|
KanbanMobileUITests:
|
|
type: bundle.ui-testing
|
|
platform: iOS
|
|
sources:
|
|
- KanbanMobileUITests
|
|
- path: Fixtures
|
|
type: folder
|
|
buildPhase: resources
|
|
dependencies:
|
|
- target: KanbanMobile
|
|
settings:
|
|
base:
|
|
PRODUCT_BUNDLE_IDENTIFIER: dev.rzen.indie.KanbanMobileUITests
|
|
GENERATE_INFOPLIST_FILE: true
|
|
SWIFT_STRICT_CONCURRENCY: complete
|
|
|
|
schemes:
|
|
# `xcodebuild … -scheme Kanban` is the established command for this repo, and it now means the
|
|
# only app there is.
|
|
Kanban:
|
|
build:
|
|
targets:
|
|
Kanban: all
|
|
run:
|
|
config: Debug
|
|
test:
|
|
config: Debug
|
|
gatherCoverageData: false
|
|
targets:
|
|
- KanbanTests
|
|
- KanbanUITests
|
|
profile:
|
|
config: Release
|
|
analyze:
|
|
config: Debug
|
|
archive:
|
|
config: Release
|
|
|
|
# The phone app's own scheme — otherwise auto-generated, declared here only because it needs a
|
|
# test action pointed at `KanbanMobileUITests` rather than xcodegen's per-target default.
|
|
KanbanMobile:
|
|
build:
|
|
targets:
|
|
KanbanMobile: all
|
|
test:
|
|
config: Debug
|
|
gatherCoverageData: false
|
|
targets:
|
|
- KanbanMobileUITests
|