# Distribution and release

## The channel decision (recorded 2026-07-29)

**Lanework ships on the Mac App Store**, as version 2.0 of the existing `dev.rzen.indie.Kanban` App Store record — the 1.x app already listed there under the name Lanework. This was not an open question by the time this document was written; three prior rulings settle it:

- The one-app collapse (DESIGN/12-editions.md, re-ruled 2026-07-30, superseding the 2026-07-27 two-app split) puts the whole family on **one record**: `dev.rzen.indie.Kanban`, the id 1.x already ships under, so 2.0 is simply its update. Lanework Pro arrives inside it as a subscription after pro-m1/pro-m2, never as a second record.
- The marketing version has been 2.0 since m3 — the successor numbering of the shipped 1.x.
- The portfolio's whole release tooling (appstore-publish skill, `asc-*.swift` scripts, the account's API key) is App-Store-shaped, and the 1.x pipeline for this very record already exists and works.

Notarized direct distribution is deliberately **not** pursued for 2.0: a second channel means a second update path and a second set of listing assets for no current audience, and nothing in the app requires escaping the App Store's sandbox rules (the app is already sandboxed with user-selected file access, and its network-client entitlement stays dormant until Pro's remotes use it). Revisit only if App Review rejects something architectural.

## The pipeline

Carried over from the 1.x release (same record, same scheme names — the pathfinder repo's pipeline, now living here):

- `scripts/release.sh` — XcodeGen regenerate, `xcodebuild archive` (Release, generic/platform=macOS), `-exportArchive` with `scripts/ExportOptions-macOS.plist` (`destination=upload` — straight to App Store Connect). Build number = git commit count, stamped by the existing post-build phase.
- `.env.release` (gitignored; `.env.release.example` committed) — account-level App Store Connect API credentials.
- `scripts/metadata/` — the listing source of truth (name, subtitle, description, keywords, what's-new, URLs, categories, pricing, availability, review notes). Pushed with the canonical scripts, **always with `--platform macos`**:

```sh
set -a; source .env.release; set +a; swift ../indie-skills/skills/appstore-publish/scripts/asc-metadata.swift --bundle dev.rzen.indie.Kanban --version 2.0 --platform macos --dry-run
```

- Screenshots: `scripts/metadata/screenshots/en-US/APP_DESKTOP/` — needs fresh 2.0 captures (see checklist).

## First-release (2.0) checklist

Run top to bottom; nothing below "PUSH POINT" may run without a deliberate decision — every step after it writes to the live App Store record of a shipping 1.x app.

1. [ ] All milestone work committed and pushed (build number = commit count).
2. [ ] Unit suite green: `xcodebuild test -project Kanban.xcodeproj -scheme Kanban -destination 'platform=macOS,arch=arm64' -only-testing:KanbanTests`.
3. [ ] UI test pass on a real display (KanbanUITests: accessibility audits + end-to-end golden flows), plus the manual checklists (`KanbanUITests/AccessibilityVerification.md`, toolbar/undo/trash/drag-preview lists).
4. [ ] CHANGELOG.md current (it is the in-app release notes) — What's New (`scripts/metadata/version/en-US/whats_new.txt`) tells the same story.
5. [ ] Screenshots: capture the 2.0 board on a real display (APP_DESKTOP sizes come straight from screenshots of the running app; numeric filename prefixes set the store order).
6. [ ] Archive dry run: `scripts/release.sh` at least through the archive step completes locally.
7. **— PUSH POINT (everything below touches the live record) —**
8. [ ] `scripts/release.sh` — archive + upload the build.
9. [ ] Metadata dry run, then push (`asc-metadata.swift --platform macos`, commands above).
10. [ ] Verify pricing (currently free, carried from 1.x) and availability (ALL territories) still match intent — Pro will be the paid tier later.
11. [ ] Stage: `asc-submit.swift --bundle dev.rzen.indie.Kanban --version 2.0 --platform macos` (waits for build processing, attaches, stages the review submission — does not submit).
12. [ ] Review the staged listing at appstoreconnect.apple.com (age rating, privacy labels, and content rights carry over from 1.x — the stage step's errors will name anything missing).
13. [ ] Submit: the same command with `--submit`.

## Lanework Pro (the subscription)

Pro is **a subscription inside this app, not a second record** (DESIGN/12-editions.md ▸ Distribution, re-ruled 2026-07-30): the two-app split that once wanted its own bundle id, metadata folder and scheme here is retired. Nothing in the pipeline above changes for it — same scheme, same archive, same upload. The added work is App Store Connect configuration and review materials, and it is all below.

The **app side is built** (the entitlement, the tier seam, the Settings Pro section, the local `Configuration.storekit`). The **store side is not** and must not be until pro-m1 ships something to sell: a live subscription product against a build whose Pro tier binds the same native undo stack the free tier does would be selling nothing. So every step here is future work for the **pro-m1 release**, not for 2.0.

**2.0 does not ship before pro-m1 is ready — the tiers launch together** (user-ruled 2026-07-31). This closes the would-be window where a shipped Settings Pro section faces a nonexistent product and shows "App Store can't be reached" — a false sentence (store reachable, product not configured). With the window ruled out of existence, that state only ever shows when something is genuinely wrong, and its shared wording for product-not-found vs network-down stays accepted as-is. The push-point items below remain user-gated; this rule adds the ordering constraint above them.

### What exists in the repo today

- `Kanban/Tier/ProProducts.swift` — the identifiers, mirrored from App Store Connect: product `dev.rzen.indie.kanban.pro.monthly`, group reference name `Lanework Pro`. The group's numeric id is read off the loaded product at runtime, never hardcoded.
- `Configuration.storekit` (repo root) — the same group and product as an Xcode-local StoreKit configuration, attached to the `Kanban` scheme's **run** action (`project.yml` ▸ `schemes.Kanban.run.storeKitConfiguration`). It makes the subscribe/manage/restore flow exercisable with no ASC product and no sandbox account. `buildPhase: none` — it never ships.
- Entitlements: no in-app-purchase key is needed. In-app purchase is an App Store Connect fact about the record, not a sandbox capability; the app calls StoreKit with the entitlements it already has.

### Checklist — pro-m1 release (not 2.0)

Steps 1–4 are App Store Connect work done by hand at appstoreconnect.apple.com; there is no script for them and no API step in this repo's tooling. They are all **below the PUSH POINT in spirit** — each one touches the live record of a shipping app.

1. [ ] Create the subscription group on `dev.rzen.indie.Kanban`: reference name **Lanework Pro**. One group — a second would let a user hold two Pro subscriptions at once.
2. [ ] Create the auto-renewable subscription in it: product id **`dev.rzen.indie.kanban.pro.monthly`**, reference name "Lanework Pro Monthly", duration **1 month**. The product id must match `ProProducts.monthly` exactly and can never be changed or reused once created.
3. [ ] Set the price (choose the tier; all territories, matching the app's ALL-territories availability) and the localized display name and description. The app never spells a price — it renders `product.displayPrice`, so the store is the only source.
4. [ ] Subscription review materials, which are separate from the app's: a **review screenshot of the Settings Pro section** showing the price, Subscribe and Restore Purchases; review notes naming Settings (⌘,) as the path to the purchase surface, that the subscription unlocks git-backed history and sync, and that there is no account or login of any kind.
5. [ ] Confirm the app's own listing carries the required subscription furniture: a link to the privacy policy and to the terms of use (EULA), both reachable from the listing, plus the auto-renewing terms in the description. Apple rejects auto-renewable subscriptions without them.
6. [ ] Update `Configuration.storekit`'s `displayPrice` to whatever step 3 set, so a development run and the store agree.
7. [ ] Verify in a **sandbox** account before submitting: subscribe, confirm the Settings section flips to "Subscribed — renews …", confirm the reopen offer appears with boards open, then Manage Subscription and Restore Purchases.
8. [ ] Then run the ordinary release checklist above for the pro-m1 version. The subscription is attached to that version's review submission.
