Skip to content

Fixed Nonisolated Web Auth Token#347

Merged
leogdion merged 4 commits into
v1.0.0-beta.1from
346-nonisolated-fetchwebtoken
May 15, 2026
Merged

Fixed Nonisolated Web Auth Token#347
leogdion merged 4 commits into
v1.0.0-beta.1from
346-nonisolated-fetchwebtoken

Conversation

@leogdion
Copy link
Copy Markdown
Member

No description provided.

leogdion and others added 2 commits May 15, 2026 09:47
Removed temporary branch from workflow triggers.
- Promote Note and ZoneRow value types into the new MistDemoKit module;
  rewrite the App-side CloudKit-aware initializers as extensions that
  delegate via self.init into the kit's public memberwise inits.
- Replace CloudKitStore's inline CKFetchWebAuthTokenOperation wrapper
  with a CKDatabase.fetchWebAuthToken extension; CloudKitStore.fetch-
  WebAuthToken now forwards through it and drops the `nonisolated`
  qualifier.
- Introduce AppMain protocol + extension so MistDemoAppMain is a thin
  conformance; RootView now owns the @State CloudKitStore.
- Add CKRecord.typedValue(forField:as:) helper that triggers
  assertionFailure on type mismatch (debug-only crash, nil in release)
  to surface schema/code drift during development.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 15, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 56d427f8-8ee0-41a8-ade0-1d2cb1be30df

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 346-nonisolated-fetchwebtoken

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Address swiftlint, swift-format, and Swift compiler warnings flagged by
`Examples/MistDemo/Scripts/lint.sh`: rename `CKDatabaseScope+Demo.swift`
so the file name matches the nested type, split inline `return` clauses,
extract `QueryView` subviews to satisfy `closure_body_length`, reorder
type contents and add public doc comments on `Note`, `ZoneRow`, and
`AppMain`, replace `try!` in `MockBackend` with `do/catch + fatalError`,
write `is any LocalizedError`, and delete the unused `ConfigError` and
`FormattingError` types.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude Bot commented May 15, 2026

Code Review

Overview

This PR fixes a Swift concurrency correctness bug where fetchWebAuthToken was marked nonisolated on an actor (CloudKitStore), causing the CKFetchWebAuthTokenOperation callback to lack @Sendable safety. The fix extracts the operation into a proper CKDatabase extension and removes the nonisolated qualifier. The PR bundles a broader refactor: Note and ZoneRow move from MistDemoApp to MistDemoKit, AppMain is extracted as a protocol, and several views are decomposed.


✅ What's Done Well

Core bug fix is correct.
CKDatabase+WebAuthToken.swift properly marks the callback @Sendable:

operation.fetchWebAuthTokenResultBlock = { @Sendable result in
    continuation.resume(with: result)
}

The original code was missing @Sendable on the closure, which the nonisolated qualifier was masking. Removing nonisolated from CloudKitStore.fetchWebAuthToken lets it run on the actor's executor where it belongs.

Model extraction to MistDemoKit is clean. Moving Note and ZoneRow to the shared module enables CLI and UI to share type definitions. Making them public with memberwise inits lets the CloudKit extension in MistDemoApp delegate to the canonical init — good separation.

fatalError over force_try in MockBackend.swift is a clear improvement — the error message now says where the failure is, which try! never does.

#expect(error is any LocalizedError) is the correct Swift 6 spelling; good catch.


Issues and Suggestions

1. Missing references after deletion — needs verification

ConfigError.swift and FormattingError.swift are deleted entirely. The diff doesn't show where their consumers were updated. Before marking this ready:

  • Grep for ConfigError and FormattingError across the repo to confirm no dangling references.
  • If they were moved elsewhere (e.g., FormattingError → some other file), that file should be in this PR.

2. CloudKitStore lifetime changed from app to view level

The old MistDemoAppMain held @State private var service at App scope (app lifetime). After this PR, RootView holds it at view scope. In practice this is stable for a navigation split root, but it's a subtle semantic change worth documenting in the PR description — especially since refreshAccountStatus() is called in .task { }, and a recreated RootView would re-trigger that call.

3. AppMain drops the .environment(service) from the WindowGroup

The new AppMain.body default implementation does not inject CloudKitStore into the environment:

// AppMain.swift
WindowGroup("MistDemo (Native CloudKit)") {
    RootView()   // ← no .environment(service) here
}

This works today because RootView injects it internally, but it means any future App conforming to AppMain that adds sibling scenes (e.g., a Settings scene) would not receive the store. The protocol contract is implicit. Consider adding a comment to AppMain.body noting that RootView is responsible for environment injection.

4. Platform guard narrowing in Note.swift / ZoneRow.swift (MistDemoApp extensions)

Note.swift and ZoneRow.swift in MistDemoApp changed from:

#if canImport(CloudKit) && !os(tvOS) && !os(watchOS)

to:

#if canImport(CloudKit)

CKRecord and CKRecordZone are available on tvOS and watchOS, so this is likely safe. However, CKDatabase+WebAuthToken.swift (same module, same PR) keeps !os(tvOS) && !os(watchOS) — it's worth a comment on why the scopes differ, or unifying them if the tvOS/watchOS exclusion was never necessary in the model files.

5. Redundant commenting in Note.swift (MistDemoKit)

The == / hash pair has both a block comment explaining why and doc comments explaining what:

// Identity-based equality: two Notes with the same recordID are equal
// regardless of field state. ...

/// Identity-based equality on `id`.
public static func == ...
/// Identity-based hashing on `id`.
public func hash(into hasher: inout Hasher)

Per the project's commenting conventions, the why block is valuable; the /// Identity-based equality on \id`doc comments add noise without adding information. Drop the///` doc lines and keep the block comment.

6. Foundation import in ZoneRow.swift (MistDemoKit) may be unnecessary

ZoneRow's public interface only uses String — a stdlib type. The internal import Foundation in the new file can likely be dropped entirely. Verify by attempting to remove it; if the build passes, delete it.

7. public import Foundation in Note.swift re-exports Foundation

public import Foundation  // Note.swift in MistDemoKit

This re-exports Foundation to any module that imports MistDemoKit. Since Note's public API exposes URL and Date, the re-export is technically required for callers to use those types without their own import Foundation. This is acceptable but worth a conscious decision — if it's intentional, a brief comment noting why would help (e.g., // Re-exported: Note's public API surfaces URL and Date).


Summary

Core bug fix ✅ Correct — nonisolated removed, @Sendable added
Model extraction ✅ Clean, good public API design
ConfigError/FormattingError deletion ⚠️ Needs grep to confirm no dangling references
CloudKitStore lifetime change ⚠️ Implicit — document in PR description
Platform guard inconsistency 🔍 Minor — worth a comment or unified approach
Redundant doc comments on Note equality 🔧 Minor style issue
Unnecessary Foundation import in ZoneRow 🔧 Minor cleanup

The core fix is sound. The main thing to verify before un-drafting is that ConfigError and FormattingError have no surviving callers after deletion.

🤖 Generated with Claude Code

@codecov
Copy link
Copy Markdown

codecov Bot commented May 15, 2026

Codecov Report

❌ Patch coverage is 27.27273% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.71%. Comparing base (f799128) to head (9ba3f3c).
⚠️ Report is 1 commits behind head on v1.0.0-beta.1.

Files with missing lines Patch % Lines
...les/MistDemo/Sources/MistDemoKit/Models/Note.swift 0.00% 11 Missing ⚠️
.../MistDemo/Sources/MistDemoKit/Models/ZoneRow.swift 0.00% 4 Missing ⚠️
...tDemo/Tests/MistDemoTests/Server/MockBackend.swift 83.33% 1 Missing ⚠️
Additional details and impacted files
@@                Coverage Diff                @@
##           v1.0.0-beta.1     #347      +/-   ##
=================================================
+ Coverage          71.44%   71.71%   +0.27%     
=================================================
  Files                552      552              
  Lines              15330    15315      -15     
=================================================
+ Hits               10952    10983      +31     
+ Misses              4378     4332      -46     
Flag Coverage Δ
mistdemo-spm-macos 60.46% <27.27%> (+0.08%) ⬆️
mistdemo-swift-6.2-jammy 60.47% <27.27%> (+0.10%) ⬆️
mistdemo-swift-6.2-noble 60.47% <27.27%> (+0.08%) ⬆️
mistdemo-swift-6.3-jammy 60.49% <27.27%> (+0.10%) ⬆️
mistdemo-swift-6.3-noble 60.47% <27.27%> (+0.09%) ⬆️
spm 55.72% <ø> (+0.42%) ⬆️
swift-6.1-jammy 55.44% <ø> (-0.26%) ⬇️
swift-6.1-noble 55.84% <ø> (+0.42%) ⬆️
swift-6.2-jammy 56.15% <ø> (+0.84%) ⬆️
swift-6.2-noble 55.36% <ø> (-0.37%) ⬇️
swift-6.3-jammy 55.33% <ø> (+0.02%) ⬆️
swift-6.3-noble 55.41% <ø> (-0.26%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@claude
Copy link
Copy Markdown

claude Bot commented May 15, 2026

Code Review — PR #347: Fixed Nonisolated Web Auth Token

Overview

This PR fixes a concurrency bug where CloudKitStore.fetchWebAuthToken() was marked nonisolated while accessing actor-isolated state (container). The fix is accompanied by a healthy amount of architectural cleanup in the demo app: shared models are moved to MistDemoKit, platform guards are narrowed to specific unavailable APIs rather than entire files, and an AppMain protocol removes boilerplate from executable targets.


Core Bug Fix ✅

CloudKitStore+WebAuthToken.swift / CKDatabase+WebAuthToken.swift

The original code had two problems:

  1. nonisolated on an @MainActor-bound method forced access to container off the actor — a data race.
  2. The fetchWebAuthTokenResultBlock closure was not annotated @Sendable, potentially causing sendability warnings under strict concurrency.

The fix correctly:

  • Removes nonisolated so the method runs with proper actor isolation
  • Moves the CKFetchWebAuthTokenOperation setup into a CKDatabase extension where @Sendable is applied correctly to the callback
  • CloudKitStore.fetchWebAuthToken becomes a clean one-liner: try await container.privateCloudDatabase.fetchWebAuthToken(apiToken: apiToken)

This is exactly the right decomposition. 👍


Architecture / Code Quality

MistDemoKit/Models/Note.swift — model promoted to shared target

Moving Note and ZoneRow to MistDemoKit is the right call. The public memberwise init with default-nil arguments is convenient. The CKRecord-specific init stays in MistDemoApp as an extension — clean separation.

Minor nit: there is a redundant plain comment immediately above the Hashable conformance methods:

// Identity-based equality: two Notes with the same recordID are equal
// regardless of field state. Lets SwiftUI selection bindings track a
// record across edits without losing focus when fields change.

/// Identity-based equality on `id`.
public static func == ...
/// Identity-based hashing on `id`.
public func hash(into hasher: inout Hasher) ...

The block comment and the doc comments say the same thing. Consider keeping only the doc comment, or only the block comment — not both.

MistDemoKit/Models/ZoneRow.swift — minor

internal import Foundation is present but Foundation types are not used (all fields are String). The import can be removed.

CKRecord+TypedField.swift

typedValue(forField:as:) is a well-designed utility. Using assertionFailure for schema/type drift is exactly right: loud in debug, graceful in release. The generic T.Type = T.self default makes call sites cleaner.

AppMain protocol

Eliminates duplicated body wiring across executable targets. Straightforward and appropriate.

CloudKitStore environment ownership change

@State private var service was moved from the @main struct (MistDemoAppMain) to RootView, with .environment(service) applied to the NavigationSplitView. This changes the lifetime: CloudKitStore now lives with RootView rather than the entire App. For a demo with a single root view this is benign, but worth a brief comment explaining the intentional lifetime choice in RootView.


Platform Guard Narrowing ✅

Dropping !os(tvOS) && !os(watchOS) from file-level #if guards down to specific APIs (UIPasteboard, Stepper, .fileImporter, .textFieldStyle(.roundedBorder), .textSelection, .swipeActions) is the correct direction. It allows the majority of view code to compile on more platforms while isolating only the unavailable API calls.

AccountView+Actions.swift — one subtle guard

#elseif canImport(UIKit) && !os(tvOS) && !os(watchOS)
  UIPasteboard.general.string = value

UIPasteboard.general is available on tvOS, just with restrictions. The guard here is correct per how it was before, but a comment explaining why tvOS/watchOS are excluded here (vs. the rest of the file) would help future readers.


Tests

AsyncHelpersTests fix
error is any LocalizedError is correct modern Swift existential syntax.

MockBackend.swift
try! + swiftlint:disabledo/catch/fatalError is cleaner and gives a useful error message if stub JSON ever drifts out of sync.

Gap: There are no new unit tests for the core bug fix. Testing CKFetchWebAuthTokenOperation requires mocking the CloudKit container, which is non-trivial. At minimum, consider a comment in CKDatabase+WebAuthToken.swift or a test placeholder noting this is covered by integration tests.


CI

Removing the temporary claude/plan-issue-325-aQGRH branch from MistDemo-Integration.yml is the correct cleanup before merge.


Summary

Area Assessment
Core bug fix (nonisolated + @sendable) ✅ Correct and clean
Model promotion to MistDemoKit ✅ Good separation
Platform guard narrowing ✅ Right approach
AppMain protocol ✅ Removes boilerplate
Test coverage ⚠️ No new tests for core fix (acceptable given CK testing constraints)
Redundant comment in Note.swift Minor cleanup opportunity
Unused Foundation import in ZoneRow.swift Minor cleanup opportunity

This is a well-executed PR. The two minor nits (redundant comment, unused import) are not blockers. Once the draft status is removed and the tvOS/watchOS pasteboard guard gets a note, this looks ready to merge.

🤖 Generated with Claude Code

@leogdion leogdion marked this pull request as ready for review May 15, 2026 16:56
@leogdion leogdion merged commit 7023a31 into v1.0.0-beta.1 May 15, 2026
70 of 71 checks passed
@leogdion leogdion deleted the 346-nonisolated-fetchwebtoken branch May 15, 2026 16:57
leogdion added a commit that referenced this pull request May 18, 2026
commit de82483
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:35 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "ea897c3"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "ea897c3"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 24c8719
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:31 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "5bb4490"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "5bb4490"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit eee0670
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:13 2026 +0100

    docs: sync README/CLAUDE examples to v1.0.0-beta.1 API; pin BushelCloud CI; exclude internal Python from CodeFactor

    - README.md, Examples/BushelCloud/{CLAUDE.md,.docc,.claude/s2s-auth-details.md},
      Examples/CelestraCloud/{CLAUDE.md,README.md,.claude/IMPLEMENTATION_NOTES.md}:
      drop `try CloudKitService(... database: .public)` from init examples (init is
      non-throwing, `database:` moved per-call); rewrite Quick Start auth around
      `Credentials` + `APICredentials` / `ServerToServerCredentials` and show
      `database: .public(.prefers(.serverToServer))` at the call site.
    - Examples/BushelCloud/.github/workflows/{BushelCloud.yml,bushel-cloud-build.yml}:
      pin MISTKIT_BRANCH to v1.0.0-beta.1 (matches CelestraCloud) so the subrepo PR
      builds against the branch that actually carries the new API. Revert to `main`
      once #298 merges.
    - .codefactor.yml: exclude Scripts/mermaid-to-pptx.py (internal-use helper).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 4d60b19
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:45 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "c44dc4f"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "c44dc4f"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 5bc403d
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:40 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "55f2092"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "55f2092"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit bce1f23
Author: leogdion <leogdion@brightdigit.com>
Date:   Sun May 17 20:09:47 2026 +0100

    refactor!: prep for talk — shrink API, refactor auth, split OpenAPI (#279)

commit 7023a31
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 15 12:56:58 2026 -0400

    Fixed Nonisolated Web Auth Token (#347)

commit f799128
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 20:27:28 2026 -0400

    Add MistDemo-Integration workflow for live CloudKit runs (#345)

commit 418e2e4
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 16:03:04 2026 -0400

    Resolve #342: v1.0.0-beta.1 follow-ups (#341 #327 #321 #317) + CI fixes (#343)

commit d65d20b
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 11:25:10 2026 -0400

    Resolve #330: interactive MistDemo (web toggle + native app refresh) (#332)

commit a28ab3c
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 11 16:31:10 2026 -0400

    Resolve #313: paginationLimitExceeded carries accumulated records (#326)

commit 7a5da7a
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 17:09:53 2026 -0400

    Fix CI failures + Claude review nits on PR #298 (v1.0.0-beta.1) (#322)

commit b3626c0
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 16:06:20 2026 -0400

    Resolve #312: public+web-auth user-identity endpoints (#310, #311, #27, #28, #34, #35) (#315)

    * #312 library: add public+web-auth user-identity endpoints and users/caller migration

    Implements the library side of #312 — adding/renaming user-identity endpoints
    that require public-database routing with web-auth (user-context) credentials,
    and unblocking the convenience initializers from their hardcoded database/
    environment defaults.

    #310: `CloudKitService` convenience initializers now accept `database:` and
    `environment:` parameters with defaults that preserve current behavior.

    #311: `users/current` → `users/caller`. Renamed in openapi.yaml and the
    generated client; added a hand-written `fetchCaller()` plus an
    `@available(*, deprecated, renamed: "fetchCaller")` `fetchCurrentUser()`
    shim that forwards to the new method.

    #28: GET `/users/discover` (`discoverAllUserIdentities`).

    #34: POST `/users/lookup/email` (`lookupUsersByEmail`).

    #35: POST `/users/lookup/id` (`lookupUsersByRecordName`).

    The three new endpoints reuse `DiscoverResponse` for parsing — Apple returns
    `{ users: [UserIdentity] }` for all of them. Each ships with a 5-file
    test suite mirroring the existing `DiscoverUserIdentities` pattern.

    #33 (`users/lookup/contacts`) intentionally not implemented: Apple has marked
    the endpoint deprecated. To be closed as not-planned with a pointer to #34/#35.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312 MistDemo: separate database from authentication and add user-context phases

    Refactors MistDemo's CloudKit configuration model and integration runner to
    support the public+web-auth combination required by the user-identity
    endpoints landed in the prior commit.

    **Configuration refactor.** Replaces the `DatabaseCredentials` enum (which
    coupled database choice to a single auth method per case, baking in a
    public⇒S2S/private⇒webAuth assumption) with two orthogonal types:

      - `AuthenticationCredentials` — `serverToServer(keyID:privateKey:)` /
        `webAuth(apiToken:webAuthToken:)`
      - `DatabaseConfiguration` — pairs a `MistKit.Database` with an
        `AuthenticationCredentials`. The `make(database:authentication:)` factory
        rejects private+S2S and shared+S2S (which CloudKit rejects) so invalid
        combinations remain unrepresentable, while public+webAuth is now a valid
        construction.

    `MistKitClientFactory.create(for:)` consumes `toPrimaryConfiguration()`;
    the new `createUserContext(for:)` returns the optional public+web-auth
    service from `toUserContextConfiguration()` when web-auth tokens are
    configured.

    **Phase plumbing.** `PhaseContext` and `IntegrationTestRunner` now thread an
    optional `userContextService: CloudKitService?`. `PublicDatabaseTest` takes
    `includeUserContextPhases:` and conditionally appends the new user-identity
    phases:

      - `FetchCallerPhase` (renamed from `FetchCurrentUserPhase`)
      - `DiscoverUserIdentitiesPhase` (existed; updated to use userContextService)
      - `DiscoverAllUserIdentitiesPhase` (#28)
      - `LookupUsersByEmailPhase` (#34)
      - `LookupUsersByRecordNamePhase` (#35)

    `PrivateDatabaseTest` no longer includes `FetchCurrentUserPhase`: CloudKit
    rejects `users/caller` against the private database, matching the rest of
    the user-identity family.

    **Call-site updates.** `CurrentUserCommand` and `DemoErrorsRunner` swap
    `fetchCurrentUser()` → `fetchCaller()`. `TestIntegrationCommand` and
    `TestPrivateCommand` now build and pass `userContextService`.

    Tests for `AuthenticationCredentials`, `DatabaseConfiguration.make`
    validation, and `MistDemoConfig.toPrimaryConfiguration` /
    `toUserContextConfiguration` ship alongside.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: mark discoverAllUserIdentities() unavailable pending #28 investigation

    Live verification on 2026-05-08 against iCloud.com.brightdigit.MistDemo
    returned HTTP 500 from Apple's GET /users/discover. The first 12 phases
    of mistdemo test-integration --verbose run green (the 8 base public+S2S
    phases plus FetchCallerPhase, DiscoverUserIdentitiesPhase,
    LookupUsersByEmailPhase, LookupUsersByRecordNamePhase) — only
    discoverAllUserIdentities fails, blocking phases beyond it.

    The endpoint is referenced in CloudKitJS but does not appear in Apple's
    CloudKit Web Services REST documentation. The actual REST shape is still
    under investigation under #28.

    Changes:
    - Marked `CloudKitService.discoverAllUserIdentities()`
      `@available(*, unavailable, message: ...)` with a pointer to #28.
    - Removed `DiscoverAllUserIdentitiesPhase` from MistDemo and from
      `PublicDatabaseTest.phases`.
    - Removed the `CloudKitServiceDiscoverAllUserIdentities` test directory
      (the unavailable method cannot be called from Swift code).

    The OpenAPI definition, generated client, path builder, response
    processor, Output extension, and Swift wrapper are all retained.
    Unblocking is a one-line `@available` removal once the correct REST
    shape is determined under #28.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: resolve PR review — Credentials API, per-call database, cascade unavailable

    Addresses all four review threads on PR #315:

    - Comment #1 (error wording): removed `unsupportedDatabaseAuthCombination`
      along with `MistDemo.DatabaseConfiguration`; invalid combos now surface as
      `CloudKitError.missingCredentials` from the library.
    - Comment #2 (per-call database): user-identity ops in
      `CloudKitService+UserOperations` hardcode `.public`; record/zone/asset/sync
      ops accept `database: Database? = nil` falling back to a service-level
      default.
    - Comment #3 (unified credentials): new `Credentials` /
      `ServerToServerCredentials` / `APICredentials` value types replace the
      legacy `apiToken:`/`webAuthToken:` initializers. The token manager is
      selected based on the target database (S2S for `.public`, web-auth for
      `.private`/`.shared`). Lifted `PrivateKeyMaterial` into the library.
    - Comment #4 (cascade unavailable): removed
      `Operations.discoverAllUserIdentities.Output: CloudKitResponseType`
      conformance entirely; `processDiscoverAllUserIdentitiesResponse` is now
      `@available(*, unavailable)` with a `fatalError` body.

    Also migrates ~15 MistKit test helpers and the MistDemo factory to the new
    Credentials API.

    Breaking changes (pre-1.0): removed legacy `CloudKitService` initializers
    taking `apiToken:`/`webAuthToken:`; `CloudKitService.apiToken` is removed,
    `.database` is now `internal`.

    Out of scope: per-call `TokenManager` dispatch (would let one service mix
    S2S-for-public and web-auth-for-user-context). MistDemo still constructs a
    separate `userContextService` for that scenario.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: drop service-level database, per-call credential resolution [skip ci]

    Resolves the architectural feedback in the PR-315 review:

    * CloudKitService no longer carries `database` — operations take
      `database:` per call (defaulting to `.public`); user-identity routes
      drop the parameter since CloudKit pins them to `.public`. Subsumes
      Claude's "fetchCaller bypasses self.database" finding.
    * Credentials.makeTokenManager(for:requiresUserContext:) resolves the
      appropriate token manager at dispatch time. A single service can now
      serve public-database S2S record ops and user-identity web-auth
      routes from one fully-populated `Credentials`. MistKitClient.swift is
      obsolete and removed; per-call dispatch lives in
      CloudKitService+ClientDispatch.
    * Credentials.swift split per SwiftLint one_file_per_declaration into
      ServerToServerCredentials.swift + APICredentials.swift +
      Credentials.swift. New typed CredentialsValidationError; init asserts
      in debug, throws in release (no more precondition crash for dynamic
      config).
    * MistDemo: userContextService workaround collapsed — single service
      handles all phases via per-call resolution.
    * CI hotfix: 11 unused `public import` lines demoted to `internal`
      (the warnings-as-errors regression flagged in the review).
    * Tests: 12-case routing-matrix unit suite for makeTokenManager and a
      fetchCaller suite parallel to LookupUsers* (success + validation).
      Obsolete MistKitClient tests removed.
    * Polish: shorter @available message on discoverAllUserIdentities,
      structural comment for GET /users/discover in openapi.yaml,
      ConfigurationError.missingAPIToken (unused) removed.

    475/475 tests pass. Library + MistDemo build clean.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * Per review on PR #315: listZones, lookupZones, fetchZoneChanges now
    default to .private since the public database only contains
    _defaultZone, making .public a degenerate default. MistDemo callers
    pass context.database / config.base.database explicitly so the
    --database flag still drives the test runs.

    Also repairs MistDemo test breakage from 7debe8d:
    toUserContextCredentials() was removed but tests still referenced it;
    rewritten against the replacement surface (toPrimaryCredentials embeds
    apiAuth on .public, plus the new hasUserContextCredentials boolean).
    The CredentialsValidationTests suite was deleted — it asserted
    init-time validation that no longer exists under per-call credential
    resolution; the equivalent .missingCredentials behavior is covered in
    MistKitTests.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: gate @available(*,unavailable) on processDiscoverAllUserIdentitiesResponse to Swift 6.2+

    Swift 6.1 rejects calls to an unavailable function from within another
    unavailable function; 6.2 relaxed that rule. The internal helper
    processDiscoverAllUserIdentitiesResponse is unavailable in lockstep with
    its only caller — the also-unavailable CloudKitService.discoverAllUserIdentities() —
    which built fine on 6.2+ but failed on Swift 6.1 with:

        error: 'processDiscoverAllUserIdentitiesResponse' is unavailable:
               Pending #28: discoverAllUserIdentities is not yet ready.

    Wrap just the attribute in `#if swift(>=6.2)` so the body is shared and
    6.1 compiles. Inline doc records the intent and the one-line cleanup
    (delete the #if/#endif) once 6.1 is dropped from the matrix.

    A `swiftlint:disable:next unavailable_function` is required because
    swiftlint does not evaluate #if and otherwise sees a fatalError-only
    function without the attribute.

    Verified: swift build + swift test pass on Swift 6.1.3 (Linux container)
    and on macOS Swift 6.2+ (475/475 tests).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: split unhandled-response logging into debug (full body) + warning (type/status only)

    CodeQL's swift/cleartext-logging flagged the existing warning logs
    because lookupUsersByEmail(_:) propagates email-PII taint through the
    response object. Move full \(response) interpolation to .debug so the
    detail stays available for development without flowing into ops logs;
    keep .warning at type(of:) + HTTP status code only.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: add --lookup-email / CLOUDKIT_LOOKUP_EMAIL to exercise users/lookup/email

    LookupUsersByEmailPhase previously skipped whenever fetchCaller() didn't
    return an email (which is the common case). Plumb a configurable lookup
    email through TestIntegrationConfig / TestPrivateConfig → PhaseContext so
    the phase can be driven against a known-discoverable iCloud account.
    Falls back to caller email, then to a clearer skip message naming the
    flag/env var.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * docs: point CLAUDE.md lint section at mise (and Scripts/lint.sh)

    swift-format / swiftlint / periphery are pinned in mise.toml; the
    previous "requires swiftlint installation" wording led to PATH lookups
    that fail in this repo. Replace with `mise exec --` invocations and
    flag the full ./Scripts/lint.sh pipeline.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: address review punch list — invalidPrivateKey, recoverable unavailable response, supportsUserContextPhases derivation

    - CloudKitError: add invalidPrivateKey(path:underlying:) so PEM-load failures
      carry the file path + original error instead of bare Foundation NSError.
      Wrap loadPEM() at the single call site in Credentials+TokenManager. Add
      PrivateKeyMaterial.filePath accessor for the diagnostic.

    - processDiscoverAllUserIdentitiesResponse: replace fatalError with
      throw CloudKitError.unsupportedOperationType so a stray Swift 6.1 caller
      (where the @available cascade does not apply) gets a recoverable error
      instead of a crash.

    - TestPrivateCommand: derive supportsUserContextPhases from
      config.base.hasUserContextCredentials, mirroring TestIntegrationCommand,
      so user-identity phases skip cleanly when web-auth env vars are absent.

    - toPrimaryCredentials: replace try? with do/catch + stderr INFO line so
      operators see when web-auth is missing on a .public setup.

    - CLAUDE.md: annotate discoverAllUserIdentities() as unavailable pending #28.

    - CredentialsTokenManagerTests: fill the missing routing-matrix branches
      (user-context × .private/.shared, .shared + token-only) and cover the new
      .invalidPrivateKey path.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 6f92a71
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 13:16:56 2026 -0400

    Resolve #308: docs refresh + CI fixes + sub-issues #165, #285 (#309)

commit a1e2162
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 07:16:10 2026 -0400

    Add query pagination support with continuation markers (#306)

commit c62bf44
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 15:52:45 2026 -0400

    Improve error handling: typed TokenManagerError and safe RecordOperation conversion (#305)

commit 7c4b678
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:10 2026 -0400

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "4244497"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "4244497"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit f14e751
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:07 2026 -0400

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "123a732"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "123a732"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit a0f0af9
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:26:32 2026 -0400

    updating example packages

commit 125dab5
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:01:18 2026 -0400

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

commit f989fd1
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:23:23 2026 -0400

    Strengthen environment and database configuration validation (#293)

commit b0f00a7
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:18:52 2026 -0400

    Add operation classification and batch sync result tracking (#296)

commit 63a4e50
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:09:27 2026 -0400

    Move CloudKitResponseType default implementations to protocol extension (#292)

commit ae1af15
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed May 6 20:20:44 2026 -0400

    Test suite improvements for v1.0.0-beta.1 (#286) (#287)

commit 5475bfa
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:32 2026 -0400

    MistDemo: --database flag + demo-errors command (closes #259, #269) (#282)

commit 8b21425
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:17 2026 -0400

    Refactor IntegrationTestRunner into protocol-based phase pipeline (#254) (#283)

commit 9709f3d
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 08:54:16 2026 -0400

    Replace custom AsyncChannel with swift-async-algorithms (#280)

commit d53467a
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 4 12:49:25 2026 -0400

    CI Updates for May 2026 (#277)

commit d7b1a21
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Thu Apr 30 09:39:09 2026 -0400

    MistDemo improvements: test split, CRUD, auth fix, native app (#271) (#273)

commit 0ab2ab6
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed Apr 29 15:49:34 2026 -0400

    First Draft Revision of Docs (#268)
leogdion added a commit that referenced this pull request May 18, 2026
commit de82483
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:35 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "ea897c3"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "ea897c3"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 24c8719
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:31 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "5bb4490"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "5bb4490"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit eee0670
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:13 2026 +0100

    docs: sync README/CLAUDE examples to v1.0.0-beta.1 API; pin BushelCloud CI; exclude internal Python from CodeFactor

    - README.md, Examples/BushelCloud/{CLAUDE.md,.docc,.claude/s2s-auth-details.md},
      Examples/CelestraCloud/{CLAUDE.md,README.md,.claude/IMPLEMENTATION_NOTES.md}:
      drop `try CloudKitService(... database: .public)` from init examples (init is
      non-throwing, `database:` moved per-call); rewrite Quick Start auth around
      `Credentials` + `APICredentials` / `ServerToServerCredentials` and show
      `database: .public(.prefers(.serverToServer))` at the call site.
    - Examples/BushelCloud/.github/workflows/{BushelCloud.yml,bushel-cloud-build.yml}:
      pin MISTKIT_BRANCH to v1.0.0-beta.1 (matches CelestraCloud) so the subrepo PR
      builds against the branch that actually carries the new API. Revert to `main`
      once #298 merges.
    - .codefactor.yml: exclude Scripts/mermaid-to-pptx.py (internal-use helper).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 4d60b19
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:45 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "c44dc4f"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "c44dc4f"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 5bc403d
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:40 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "55f2092"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "55f2092"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit bce1f23
Author: leogdion <leogdion@brightdigit.com>
Date:   Sun May 17 20:09:47 2026 +0100

    refactor!: prep for talk — shrink API, refactor auth, split OpenAPI (#279)

commit 7023a31
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 15 12:56:58 2026 -0400

    Fixed Nonisolated Web Auth Token (#347)

commit f799128
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 20:27:28 2026 -0400

    Add MistDemo-Integration workflow for live CloudKit runs (#345)

commit 418e2e4
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 16:03:04 2026 -0400

    Resolve #342: v1.0.0-beta.1 follow-ups (#341 #327 #321 #317) + CI fixes (#343)

commit d65d20b
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 11:25:10 2026 -0400

    Resolve #330: interactive MistDemo (web toggle + native app refresh) (#332)

commit a28ab3c
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 11 16:31:10 2026 -0400

    Resolve #313: paginationLimitExceeded carries accumulated records (#326)

commit 7a5da7a
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 17:09:53 2026 -0400

    Fix CI failures + Claude review nits on PR #298 (v1.0.0-beta.1) (#322)

commit b3626c0
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 16:06:20 2026 -0400

    Resolve #312: public+web-auth user-identity endpoints (#310, #311, #27, #28, #34, #35) (#315)

    * #312 library: add public+web-auth user-identity endpoints and users/caller migration

    Implements the library side of #312 — adding/renaming user-identity endpoints
    that require public-database routing with web-auth (user-context) credentials,
    and unblocking the convenience initializers from their hardcoded database/
    environment defaults.

    #310: `CloudKitService` convenience initializers now accept `database:` and
    `environment:` parameters with defaults that preserve current behavior.

    #311: `users/current` → `users/caller`. Renamed in openapi.yaml and the
    generated client; added a hand-written `fetchCaller()` plus an
    `@available(*, deprecated, renamed: "fetchCaller")` `fetchCurrentUser()`
    shim that forwards to the new method.

    #28: GET `/users/discover` (`discoverAllUserIdentities`).

    #34: POST `/users/lookup/email` (`lookupUsersByEmail`).

    #35: POST `/users/lookup/id` (`lookupUsersByRecordName`).

    The three new endpoints reuse `DiscoverResponse` for parsing — Apple returns
    `{ users: [UserIdentity] }` for all of them. Each ships with a 5-file
    test suite mirroring the existing `DiscoverUserIdentities` pattern.

    #33 (`users/lookup/contacts`) intentionally not implemented: Apple has marked
    the endpoint deprecated. To be closed as not-planned with a pointer to #34/#35.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312 MistDemo: separate database from authentication and add user-context phases

    Refactors MistDemo's CloudKit configuration model and integration runner to
    support the public+web-auth combination required by the user-identity
    endpoints landed in the prior commit.

    **Configuration refactor.** Replaces the `DatabaseCredentials` enum (which
    coupled database choice to a single auth method per case, baking in a
    public⇒S2S/private⇒webAuth assumption) with two orthogonal types:

      - `AuthenticationCredentials` — `serverToServer(keyID:privateKey:)` /
        `webAuth(apiToken:webAuthToken:)`
      - `DatabaseConfiguration` — pairs a `MistKit.Database` with an
        `AuthenticationCredentials`. The `make(database:authentication:)` factory
        rejects private+S2S and shared+S2S (which CloudKit rejects) so invalid
        combinations remain unrepresentable, while public+webAuth is now a valid
        construction.

    `MistKitClientFactory.create(for:)` consumes `toPrimaryConfiguration()`;
    the new `createUserContext(for:)` returns the optional public+web-auth
    service from `toUserContextConfiguration()` when web-auth tokens are
    configured.

    **Phase plumbing.** `PhaseContext` and `IntegrationTestRunner` now thread an
    optional `userContextService: CloudKitService?`. `PublicDatabaseTest` takes
    `includeUserContextPhases:` and conditionally appends the new user-identity
    phases:

      - `FetchCallerPhase` (renamed from `FetchCurrentUserPhase`)
      - `DiscoverUserIdentitiesPhase` (existed; updated to use userContextService)
      - `DiscoverAllUserIdentitiesPhase` (#28)
      - `LookupUsersByEmailPhase` (#34)
      - `LookupUsersByRecordNamePhase` (#35)

    `PrivateDatabaseTest` no longer includes `FetchCurrentUserPhase`: CloudKit
    rejects `users/caller` against the private database, matching the rest of
    the user-identity family.

    **Call-site updates.** `CurrentUserCommand` and `DemoErrorsRunner` swap
    `fetchCurrentUser()` → `fetchCaller()`. `TestIntegrationCommand` and
    `TestPrivateCommand` now build and pass `userContextService`.

    Tests for `AuthenticationCredentials`, `DatabaseConfiguration.make`
    validation, and `MistDemoConfig.toPrimaryConfiguration` /
    `toUserContextConfiguration` ship alongside.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: mark discoverAllUserIdentities() unavailable pending #28 investigation

    Live verification on 2026-05-08 against iCloud.com.brightdigit.MistDemo
    returned HTTP 500 from Apple's GET /users/discover. The first 12 phases
    of mistdemo test-integration --verbose run green (the 8 base public+S2S
    phases plus FetchCallerPhase, DiscoverUserIdentitiesPhase,
    LookupUsersByEmailPhase, LookupUsersByRecordNamePhase) — only
    discoverAllUserIdentities fails, blocking phases beyond it.

    The endpoint is referenced in CloudKitJS but does not appear in Apple's
    CloudKit Web Services REST documentation. The actual REST shape is still
    under investigation under #28.

    Changes:
    - Marked `CloudKitService.discoverAllUserIdentities()`
      `@available(*, unavailable, message: ...)` with a pointer to #28.
    - Removed `DiscoverAllUserIdentitiesPhase` from MistDemo and from
      `PublicDatabaseTest.phases`.
    - Removed the `CloudKitServiceDiscoverAllUserIdentities` test directory
      (the unavailable method cannot be called from Swift code).

    The OpenAPI definition, generated client, path builder, response
    processor, Output extension, and Swift wrapper are all retained.
    Unblocking is a one-line `@available` removal once the correct REST
    shape is determined under #28.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: resolve PR review — Credentials API, per-call database, cascade unavailable

    Addresses all four review threads on PR #315:

    - Comment #1 (error wording): removed `unsupportedDatabaseAuthCombination`
      along with `MistDemo.DatabaseConfiguration`; invalid combos now surface as
      `CloudKitError.missingCredentials` from the library.
    - Comment #2 (per-call database): user-identity ops in
      `CloudKitService+UserOperations` hardcode `.public`; record/zone/asset/sync
      ops accept `database: Database? = nil` falling back to a service-level
      default.
    - Comment #3 (unified credentials): new `Credentials` /
      `ServerToServerCredentials` / `APICredentials` value types replace the
      legacy `apiToken:`/`webAuthToken:` initializers. The token manager is
      selected based on the target database (S2S for `.public`, web-auth for
      `.private`/`.shared`). Lifted `PrivateKeyMaterial` into the library.
    - Comment #4 (cascade unavailable): removed
      `Operations.discoverAllUserIdentities.Output: CloudKitResponseType`
      conformance entirely; `processDiscoverAllUserIdentitiesResponse` is now
      `@available(*, unavailable)` with a `fatalError` body.

    Also migrates ~15 MistKit test helpers and the MistDemo factory to the new
    Credentials API.

    Breaking changes (pre-1.0): removed legacy `CloudKitService` initializers
    taking `apiToken:`/`webAuthToken:`; `CloudKitService.apiToken` is removed,
    `.database` is now `internal`.

    Out of scope: per-call `TokenManager` dispatch (would let one service mix
    S2S-for-public and web-auth-for-user-context). MistDemo still constructs a
    separate `userContextService` for that scenario.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: drop service-level database, per-call credential resolution [skip ci]

    Resolves the architectural feedback in the PR-315 review:

    * CloudKitService no longer carries `database` — operations take
      `database:` per call (defaulting to `.public`); user-identity routes
      drop the parameter since CloudKit pins them to `.public`. Subsumes
      Claude's "fetchCaller bypasses self.database" finding.
    * Credentials.makeTokenManager(for:requiresUserContext:) resolves the
      appropriate token manager at dispatch time. A single service can now
      serve public-database S2S record ops and user-identity web-auth
      routes from one fully-populated `Credentials`. MistKitClient.swift is
      obsolete and removed; per-call dispatch lives in
      CloudKitService+ClientDispatch.
    * Credentials.swift split per SwiftLint one_file_per_declaration into
      ServerToServerCredentials.swift + APICredentials.swift +
      Credentials.swift. New typed CredentialsValidationError; init asserts
      in debug, throws in release (no more precondition crash for dynamic
      config).
    * MistDemo: userContextService workaround collapsed — single service
      handles all phases via per-call resolution.
    * CI hotfix: 11 unused `public import` lines demoted to `internal`
      (the warnings-as-errors regression flagged in the review).
    * Tests: 12-case routing-matrix unit suite for makeTokenManager and a
      fetchCaller suite parallel to LookupUsers* (success + validation).
      Obsolete MistKitClient tests removed.
    * Polish: shorter @available message on discoverAllUserIdentities,
      structural comment for GET /users/discover in openapi.yaml,
      ConfigurationError.missingAPIToken (unused) removed.

    475/475 tests pass. Library + MistDemo build clean.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * Per review on PR #315: listZones, lookupZones, fetchZoneChanges now
    default to .private since the public database only contains
    _defaultZone, making .public a degenerate default. MistDemo callers
    pass context.database / config.base.database explicitly so the
    --database flag still drives the test runs.

    Also repairs MistDemo test breakage from 7debe8d:
    toUserContextCredentials() was removed but tests still referenced it;
    rewritten against the replacement surface (toPrimaryCredentials embeds
    apiAuth on .public, plus the new hasUserContextCredentials boolean).
    The CredentialsValidationTests suite was deleted — it asserted
    init-time validation that no longer exists under per-call credential
    resolution; the equivalent .missingCredentials behavior is covered in
    MistKitTests.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: gate @available(*,unavailable) on processDiscoverAllUserIdentitiesResponse to Swift 6.2+

    Swift 6.1 rejects calls to an unavailable function from within another
    unavailable function; 6.2 relaxed that rule. The internal helper
    processDiscoverAllUserIdentitiesResponse is unavailable in lockstep with
    its only caller — the also-unavailable CloudKitService.discoverAllUserIdentities() —
    which built fine on 6.2+ but failed on Swift 6.1 with:

        error: 'processDiscoverAllUserIdentitiesResponse' is unavailable:
               Pending #28: discoverAllUserIdentities is not yet ready.

    Wrap just the attribute in `#if swift(>=6.2)` so the body is shared and
    6.1 compiles. Inline doc records the intent and the one-line cleanup
    (delete the #if/#endif) once 6.1 is dropped from the matrix.

    A `swiftlint:disable:next unavailable_function` is required because
    swiftlint does not evaluate #if and otherwise sees a fatalError-only
    function without the attribute.

    Verified: swift build + swift test pass on Swift 6.1.3 (Linux container)
    and on macOS Swift 6.2+ (475/475 tests).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: split unhandled-response logging into debug (full body) + warning (type/status only)

    CodeQL's swift/cleartext-logging flagged the existing warning logs
    because lookupUsersByEmail(_:) propagates email-PII taint through the
    response object. Move full \(response) interpolation to .debug so the
    detail stays available for development without flowing into ops logs;
    keep .warning at type(of:) + HTTP status code only.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: add --lookup-email / CLOUDKIT_LOOKUP_EMAIL to exercise users/lookup/email

    LookupUsersByEmailPhase previously skipped whenever fetchCaller() didn't
    return an email (which is the common case). Plumb a configurable lookup
    email through TestIntegrationConfig / TestPrivateConfig → PhaseContext so
    the phase can be driven against a known-discoverable iCloud account.
    Falls back to caller email, then to a clearer skip message naming the
    flag/env var.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * docs: point CLAUDE.md lint section at mise (and Scripts/lint.sh)

    swift-format / swiftlint / periphery are pinned in mise.toml; the
    previous "requires swiftlint installation" wording led to PATH lookups
    that fail in this repo. Replace with `mise exec --` invocations and
    flag the full ./Scripts/lint.sh pipeline.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: address review punch list — invalidPrivateKey, recoverable unavailable response, supportsUserContextPhases derivation

    - CloudKitError: add invalidPrivateKey(path:underlying:) so PEM-load failures
      carry the file path + original error instead of bare Foundation NSError.
      Wrap loadPEM() at the single call site in Credentials+TokenManager. Add
      PrivateKeyMaterial.filePath accessor for the diagnostic.

    - processDiscoverAllUserIdentitiesResponse: replace fatalError with
      throw CloudKitError.unsupportedOperationType so a stray Swift 6.1 caller
      (where the @available cascade does not apply) gets a recoverable error
      instead of a crash.

    - TestPrivateCommand: derive supportsUserContextPhases from
      config.base.hasUserContextCredentials, mirroring TestIntegrationCommand,
      so user-identity phases skip cleanly when web-auth env vars are absent.

    - toPrimaryCredentials: replace try? with do/catch + stderr INFO line so
      operators see when web-auth is missing on a .public setup.

    - CLAUDE.md: annotate discoverAllUserIdentities() as unavailable pending #28.

    - CredentialsTokenManagerTests: fill the missing routing-matrix branches
      (user-context × .private/.shared, .shared + token-only) and cover the new
      .invalidPrivateKey path.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 6f92a71
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 13:16:56 2026 -0400

    Resolve #308: docs refresh + CI fixes + sub-issues #165, #285 (#309)

commit a1e2162
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 07:16:10 2026 -0400

    Add query pagination support with continuation markers (#306)

commit c62bf44
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 15:52:45 2026 -0400

    Improve error handling: typed TokenManagerError and safe RecordOperation conversion (#305)

commit 7c4b678
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:10 2026 -0400

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "4244497"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "4244497"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit f14e751
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:07 2026 -0400

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "123a732"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "123a732"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit a0f0af9
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:26:32 2026 -0400

    updating example packages

commit 125dab5
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:01:18 2026 -0400

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

commit f989fd1
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:23:23 2026 -0400

    Strengthen environment and database configuration validation (#293)

commit b0f00a7
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:18:52 2026 -0400

    Add operation classification and batch sync result tracking (#296)

commit 63a4e50
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:09:27 2026 -0400

    Move CloudKitResponseType default implementations to protocol extension (#292)

commit ae1af15
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed May 6 20:20:44 2026 -0400

    Test suite improvements for v1.0.0-beta.1 (#286) (#287)

commit 5475bfa
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:32 2026 -0400

    MistDemo: --database flag + demo-errors command (closes #259, #269) (#282)

commit 8b21425
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:17 2026 -0400

    Refactor IntegrationTestRunner into protocol-based phase pipeline (#254) (#283)

commit 9709f3d
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 08:54:16 2026 -0400

    Replace custom AsyncChannel with swift-async-algorithms (#280)

commit d53467a
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 4 12:49:25 2026 -0400

    CI Updates for May 2026 (#277)

commit d7b1a21
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Thu Apr 30 09:39:09 2026 -0400

    MistDemo improvements: test split, CRUD, auth fix, native app (#271) (#273)

commit 0ab2ab6
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed Apr 29 15:49:34 2026 -0400

    First Draft Revision of Docs (#268)
leogdion added a commit that referenced this pull request May 19, 2026
commit de82483
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:35 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "ea897c3"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "ea897c3"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 24c8719
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:31 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "5bb4490"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "5bb4490"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit eee0670
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:13 2026 +0100

    docs: sync README/CLAUDE examples to v1.0.0-beta.1 API; pin BushelCloud CI; exclude internal Python from CodeFactor

    - README.md, Examples/BushelCloud/{CLAUDE.md,.docc,.claude/s2s-auth-details.md},
      Examples/CelestraCloud/{CLAUDE.md,README.md,.claude/IMPLEMENTATION_NOTES.md}:
      drop `try CloudKitService(... database: .public)` from init examples (init is
      non-throwing, `database:` moved per-call); rewrite Quick Start auth around
      `Credentials` + `APICredentials` / `ServerToServerCredentials` and show
      `database: .public(.prefers(.serverToServer))` at the call site.
    - Examples/BushelCloud/.github/workflows/{BushelCloud.yml,bushel-cloud-build.yml}:
      pin MISTKIT_BRANCH to v1.0.0-beta.1 (matches CelestraCloud) so the subrepo PR
      builds against the branch that actually carries the new API. Revert to `main`
      once #298 merges.
    - .codefactor.yml: exclude Scripts/mermaid-to-pptx.py (internal-use helper).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 4d60b19
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:45 2026 +0100

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "c44dc4f"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "c44dc4f"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit 5bc403d
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 20:10:40 2026 +0100

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "55f2092"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "55f2092"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "6f293daa9f"

commit bce1f23
Author: leogdion <leogdion@brightdigit.com>
Date:   Sun May 17 20:09:47 2026 +0100

    refactor!: prep for talk — shrink API, refactor auth, split OpenAPI (#279)

commit 7023a31
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 15 12:56:58 2026 -0400

    Fixed Nonisolated Web Auth Token (#347)

commit f799128
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 20:27:28 2026 -0400

    Add MistDemo-Integration workflow for live CloudKit runs (#345)

commit 418e2e4
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 16:03:04 2026 -0400

    Resolve #342: v1.0.0-beta.1 follow-ups (#341 #327 #321 #317) + CI fixes (#343)

commit d65d20b
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 14 11:25:10 2026 -0400

    Resolve #330: interactive MistDemo (web toggle + native app refresh) (#332)

commit a28ab3c
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 11 16:31:10 2026 -0400

    Resolve #313: paginationLimitExceeded carries accumulated records (#326)

commit 7a5da7a
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 17:09:53 2026 -0400

    Fix CI failures + Claude review nits on PR #298 (v1.0.0-beta.1) (#322)

commit b3626c0
Author: leogdion <leogdion@brightdigit.com>
Date:   Sat May 9 16:06:20 2026 -0400

    Resolve #312: public+web-auth user-identity endpoints (#310, #311, #27, #28, #34, #35) (#315)

    * #312 library: add public+web-auth user-identity endpoints and users/caller migration

    Implements the library side of #312 — adding/renaming user-identity endpoints
    that require public-database routing with web-auth (user-context) credentials,
    and unblocking the convenience initializers from their hardcoded database/
    environment defaults.

    #310: `CloudKitService` convenience initializers now accept `database:` and
    `environment:` parameters with defaults that preserve current behavior.

    #311: `users/current` → `users/caller`. Renamed in openapi.yaml and the
    generated client; added a hand-written `fetchCaller()` plus an
    `@available(*, deprecated, renamed: "fetchCaller")` `fetchCurrentUser()`
    shim that forwards to the new method.

    #28: GET `/users/discover` (`discoverAllUserIdentities`).

    #34: POST `/users/lookup/email` (`lookupUsersByEmail`).

    #35: POST `/users/lookup/id` (`lookupUsersByRecordName`).

    The three new endpoints reuse `DiscoverResponse` for parsing — Apple returns
    `{ users: [UserIdentity] }` for all of them. Each ships with a 5-file
    test suite mirroring the existing `DiscoverUserIdentities` pattern.

    #33 (`users/lookup/contacts`) intentionally not implemented: Apple has marked
    the endpoint deprecated. To be closed as not-planned with a pointer to #34/#35.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312 MistDemo: separate database from authentication and add user-context phases

    Refactors MistDemo's CloudKit configuration model and integration runner to
    support the public+web-auth combination required by the user-identity
    endpoints landed in the prior commit.

    **Configuration refactor.** Replaces the `DatabaseCredentials` enum (which
    coupled database choice to a single auth method per case, baking in a
    public⇒S2S/private⇒webAuth assumption) with two orthogonal types:

      - `AuthenticationCredentials` — `serverToServer(keyID:privateKey:)` /
        `webAuth(apiToken:webAuthToken:)`
      - `DatabaseConfiguration` — pairs a `MistKit.Database` with an
        `AuthenticationCredentials`. The `make(database:authentication:)` factory
        rejects private+S2S and shared+S2S (which CloudKit rejects) so invalid
        combinations remain unrepresentable, while public+webAuth is now a valid
        construction.

    `MistKitClientFactory.create(for:)` consumes `toPrimaryConfiguration()`;
    the new `createUserContext(for:)` returns the optional public+web-auth
    service from `toUserContextConfiguration()` when web-auth tokens are
    configured.

    **Phase plumbing.** `PhaseContext` and `IntegrationTestRunner` now thread an
    optional `userContextService: CloudKitService?`. `PublicDatabaseTest` takes
    `includeUserContextPhases:` and conditionally appends the new user-identity
    phases:

      - `FetchCallerPhase` (renamed from `FetchCurrentUserPhase`)
      - `DiscoverUserIdentitiesPhase` (existed; updated to use userContextService)
      - `DiscoverAllUserIdentitiesPhase` (#28)
      - `LookupUsersByEmailPhase` (#34)
      - `LookupUsersByRecordNamePhase` (#35)

    `PrivateDatabaseTest` no longer includes `FetchCurrentUserPhase`: CloudKit
    rejects `users/caller` against the private database, matching the rest of
    the user-identity family.

    **Call-site updates.** `CurrentUserCommand` and `DemoErrorsRunner` swap
    `fetchCurrentUser()` → `fetchCaller()`. `TestIntegrationCommand` and
    `TestPrivateCommand` now build and pass `userContextService`.

    Tests for `AuthenticationCredentials`, `DatabaseConfiguration.make`
    validation, and `MistDemoConfig.toPrimaryConfiguration` /
    `toUserContextConfiguration` ship alongside.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: mark discoverAllUserIdentities() unavailable pending #28 investigation

    Live verification on 2026-05-08 against iCloud.com.brightdigit.MistDemo
    returned HTTP 500 from Apple's GET /users/discover. The first 12 phases
    of mistdemo test-integration --verbose run green (the 8 base public+S2S
    phases plus FetchCallerPhase, DiscoverUserIdentitiesPhase,
    LookupUsersByEmailPhase, LookupUsersByRecordNamePhase) — only
    discoverAllUserIdentities fails, blocking phases beyond it.

    The endpoint is referenced in CloudKitJS but does not appear in Apple's
    CloudKit Web Services REST documentation. The actual REST shape is still
    under investigation under #28.

    Changes:
    - Marked `CloudKitService.discoverAllUserIdentities()`
      `@available(*, unavailable, message: ...)` with a pointer to #28.
    - Removed `DiscoverAllUserIdentitiesPhase` from MistDemo and from
      `PublicDatabaseTest.phases`.
    - Removed the `CloudKitServiceDiscoverAllUserIdentities` test directory
      (the unavailable method cannot be called from Swift code).

    The OpenAPI definition, generated client, path builder, response
    processor, Output extension, and Swift wrapper are all retained.
    Unblocking is a one-line `@available` removal once the correct REST
    shape is determined under #28.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: resolve PR review — Credentials API, per-call database, cascade unavailable

    Addresses all four review threads on PR #315:

    - Comment #1 (error wording): removed `unsupportedDatabaseAuthCombination`
      along with `MistDemo.DatabaseConfiguration`; invalid combos now surface as
      `CloudKitError.missingCredentials` from the library.
    - Comment #2 (per-call database): user-identity ops in
      `CloudKitService+UserOperations` hardcode `.public`; record/zone/asset/sync
      ops accept `database: Database? = nil` falling back to a service-level
      default.
    - Comment #3 (unified credentials): new `Credentials` /
      `ServerToServerCredentials` / `APICredentials` value types replace the
      legacy `apiToken:`/`webAuthToken:` initializers. The token manager is
      selected based on the target database (S2S for `.public`, web-auth for
      `.private`/`.shared`). Lifted `PrivateKeyMaterial` into the library.
    - Comment #4 (cascade unavailable): removed
      `Operations.discoverAllUserIdentities.Output: CloudKitResponseType`
      conformance entirely; `processDiscoverAllUserIdentitiesResponse` is now
      `@available(*, unavailable)` with a `fatalError` body.

    Also migrates ~15 MistKit test helpers and the MistDemo factory to the new
    Credentials API.

    Breaking changes (pre-1.0): removed legacy `CloudKitService` initializers
    taking `apiToken:`/`webAuthToken:`; `CloudKitService.apiToken` is removed,
    `.database` is now `internal`.

    Out of scope: per-call `TokenManager` dispatch (would let one service mix
    S2S-for-public and web-auth-for-user-context). MistDemo still constructs a
    separate `userContextService` for that scenario.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: drop service-level database, per-call credential resolution [skip ci]

    Resolves the architectural feedback in the PR-315 review:

    * CloudKitService no longer carries `database` — operations take
      `database:` per call (defaulting to `.public`); user-identity routes
      drop the parameter since CloudKit pins them to `.public`. Subsumes
      Claude's "fetchCaller bypasses self.database" finding.
    * Credentials.makeTokenManager(for:requiresUserContext:) resolves the
      appropriate token manager at dispatch time. A single service can now
      serve public-database S2S record ops and user-identity web-auth
      routes from one fully-populated `Credentials`. MistKitClient.swift is
      obsolete and removed; per-call dispatch lives in
      CloudKitService+ClientDispatch.
    * Credentials.swift split per SwiftLint one_file_per_declaration into
      ServerToServerCredentials.swift + APICredentials.swift +
      Credentials.swift. New typed CredentialsValidationError; init asserts
      in debug, throws in release (no more precondition crash for dynamic
      config).
    * MistDemo: userContextService workaround collapsed — single service
      handles all phases via per-call resolution.
    * CI hotfix: 11 unused `public import` lines demoted to `internal`
      (the warnings-as-errors regression flagged in the review).
    * Tests: 12-case routing-matrix unit suite for makeTokenManager and a
      fetchCaller suite parallel to LookupUsers* (success + validation).
      Obsolete MistKitClient tests removed.
    * Polish: shorter @available message on discoverAllUserIdentities,
      structural comment for GET /users/discover in openapi.yaml,
      ConfigurationError.missingAPIToken (unused) removed.

    475/475 tests pass. Library + MistDemo build clean.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * Per review on PR #315: listZones, lookupZones, fetchZoneChanges now
    default to .private since the public database only contains
    _defaultZone, making .public a degenerate default. MistDemo callers
    pass context.database / config.base.database explicitly so the
    --database flag still drives the test runs.

    Also repairs MistDemo test breakage from 7debe8d:
    toUserContextCredentials() was removed but tests still referenced it;
    rewritten against the replacement surface (toPrimaryCredentials embeds
    apiAuth on .public, plus the new hasUserContextCredentials boolean).
    The CredentialsValidationTests suite was deleted — it asserted
    init-time validation that no longer exists under per-call credential
    resolution; the equivalent .missingCredentials behavior is covered in
    MistKitTests.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: gate @available(*,unavailable) on processDiscoverAllUserIdentitiesResponse to Swift 6.2+

    Swift 6.1 rejects calls to an unavailable function from within another
    unavailable function; 6.2 relaxed that rule. The internal helper
    processDiscoverAllUserIdentitiesResponse is unavailable in lockstep with
    its only caller — the also-unavailable CloudKitService.discoverAllUserIdentities() —
    which built fine on 6.2+ but failed on Swift 6.1 with:

        error: 'processDiscoverAllUserIdentitiesResponse' is unavailable:
               Pending #28: discoverAllUserIdentities is not yet ready.

    Wrap just the attribute in `#if swift(>=6.2)` so the body is shared and
    6.1 compiles. Inline doc records the intent and the one-line cleanup
    (delete the #if/#endif) once 6.1 is dropped from the matrix.

    A `swiftlint:disable:next unavailable_function` is required because
    swiftlint does not evaluate #if and otherwise sees a fatalError-only
    function without the attribute.

    Verified: swift build + swift test pass on Swift 6.1.3 (Linux container)
    and on macOS Swift 6.2+ (475/475 tests).

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: split unhandled-response logging into debug (full body) + warning (type/status only)

    CodeQL's swift/cleartext-logging flagged the existing warning logs
    because lookupUsersByEmail(_:) propagates email-PII taint through the
    response object. Move full \(response) interpolation to .debug so the
    detail stays available for development without flowing into ops logs;
    keep .warning at type(of:) + HTTP status code only.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #312: add --lookup-email / CLOUDKIT_LOOKUP_EMAIL to exercise users/lookup/email

    LookupUsersByEmailPhase previously skipped whenever fetchCaller() didn't
    return an email (which is the common case). Plumb a configurable lookup
    email through TestIntegrationConfig / TestPrivateConfig → PhaseContext so
    the phase can be driven against a known-discoverable iCloud account.
    Falls back to caller email, then to a clearer skip message naming the
    flag/env var.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * docs: point CLAUDE.md lint section at mise (and Scripts/lint.sh)

    swift-format / swiftlint / periphery are pinned in mise.toml; the
    previous "requires swiftlint installation" wording led to PATH lookups
    that fail in this repo. Replace with `mise exec --` invocations and
    flag the full ./Scripts/lint.sh pipeline.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    * #315: address review punch list — invalidPrivateKey, recoverable unavailable response, supportsUserContextPhases derivation

    - CloudKitError: add invalidPrivateKey(path:underlying:) so PEM-load failures
      carry the file path + original error instead of bare Foundation NSError.
      Wrap loadPEM() at the single call site in Credentials+TokenManager. Add
      PrivateKeyMaterial.filePath accessor for the diagnostic.

    - processDiscoverAllUserIdentitiesResponse: replace fatalError with
      throw CloudKitError.unsupportedOperationType so a stray Swift 6.1 caller
      (where the @available cascade does not apply) gets a recoverable error
      instead of a crash.

    - TestPrivateCommand: derive supportsUserContextPhases from
      config.base.hasUserContextCredentials, mirroring TestIntegrationCommand,
      so user-identity phases skip cleanly when web-auth env vars are absent.

    - toPrimaryCredentials: replace try? with do/catch + stderr INFO line so
      operators see when web-auth is missing on a .public setup.

    - CLAUDE.md: annotate discoverAllUserIdentities() as unavailable pending #28.

    - CredentialsTokenManagerTests: fill the missing routing-matrix branches
      (user-context × .private/.shared, .shared + token-only) and cover the new
      .invalidPrivateKey path.

    Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

commit 6f92a71
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 13:16:56 2026 -0400

    Resolve #308: docs refresh + CI fixes + sub-issues #165, #285 (#309)

commit a1e2162
Author: leogdion <leogdion@brightdigit.com>
Date:   Fri May 8 07:16:10 2026 -0400

    Add query pagination support with continuation markers (#306)

commit c62bf44
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 15:52:45 2026 -0400

    Improve error handling: typed TokenManagerError and safe RecordOperation conversion (#305)

commit 7c4b678
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:10 2026 -0400

    git subrepo push Examples/CelestraCloud

    subrepo:
      subdir:   "Examples/CelestraCloud"
      merged:   "4244497"
    upstream:
      origin:   "git@github.com:brightdigit/CelestraCloud.git"
      branch:   "mistkit"
      commit:   "4244497"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit f14e751
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:27:07 2026 -0400

    git subrepo push Examples/BushelCloud

    subrepo:
      subdir:   "Examples/BushelCloud"
      merged:   "123a732"
    upstream:
      origin:   "git@github.com:brightdigit/BushelCloud.git"
      branch:   "mistkit"
      commit:   "123a732"
    git-subrepo:
      version:  "0.4.9"
      origin:   "https://github.com/Homebrew/brew"
      commit:   "b9763ee528"

commit a0f0af9
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:26:32 2026 -0400

    updating example packages

commit 125dab5
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 11:01:18 2026 -0400

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

commit f989fd1
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:23:23 2026 -0400

    Strengthen environment and database configuration validation (#293)

commit b0f00a7
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:18:52 2026 -0400

    Add operation classification and batch sync result tracking (#296)

commit 63a4e50
Author: leogdion <leogdion@brightdigit.com>
Date:   Thu May 7 10:09:27 2026 -0400

    Move CloudKitResponseType default implementations to protocol extension (#292)

commit ae1af15
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed May 6 20:20:44 2026 -0400

    Test suite improvements for v1.0.0-beta.1 (#286) (#287)

commit 5475bfa
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:32 2026 -0400

    MistDemo: --database flag + demo-errors command (closes #259, #269) (#282)

commit 8b21425
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 20:21:17 2026 -0400

    Refactor IntegrationTestRunner into protocol-based phase pipeline (#254) (#283)

commit 9709f3d
Author: leogdion <leogdion@brightdigit.com>
Date:   Tue May 5 08:54:16 2026 -0400

    Replace custom AsyncChannel with swift-async-algorithms (#280)

commit d53467a
Author: leogdion <leogdion@brightdigit.com>
Date:   Mon May 4 12:49:25 2026 -0400

    CI Updates for May 2026 (#277)

commit d7b1a21
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Thu Apr 30 09:39:09 2026 -0400

    MistDemo improvements: test split, CRUD, auth fix, native app (#271) (#273)

commit 0ab2ab6
Author: leogdion <leogdion@brightdigit.com>
Date:   Wed Apr 29 15:49:34 2026 -0400

    First Draft Revision of Docs (#268)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant