Resolve #330: interactive MistDemo (web toggle + native app refresh)#332
Conversation
…kenCommand AuthTokenCommand now only owns lifecycle and channel plumbing. Route construction moves to a reusable AuthTokenServer; loopback validation moves to a standalone LoopbackAuthority helper. Both gain dedicated unit + router-level tests via HummingbirdTesting. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The CloudKit auth-flow page moves out of four Swift raw-string files and into a single auth-token-index.html resource in MistDemoKit's bundle. AuthTokenIndexHTML becomes a thin Bundle.module loader. The original CodeSign concern doesn't apply: MistDemoApp (the iOS target) has no dependency on MistDemoKit, so the resource never ships in an iOS app bundle. The mistdemo CLI executable that does consume MistDemoKit is macOS / Linux only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## v1.0.0-beta.1 #332 +/- ##
=================================================
+ Coverage 69.46% 70.53% +1.06%
=================================================
Files 529 551 +22
Lines 14457 15426 +969
=================================================
+ Hits 10042 10880 +838
- Misses 4415 4546 +131
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
- AuthTokenIndexHTML drops the guard-let-else pyramid in favor of a single try! / force-unwrap; the resource is shipped in MistDemoKit's bundle so failure here is a build-system bug, not a runtime condition. (review nit r3225504184) - AuthTokenCommandTests+Timeout's "throws on timeout" duplicates AsyncHelpersTests+Timeout's coverage but lacked its withKnownIssue gate. Under visionOS-simulator CI load the operation's single 1s Task.sleep can outrun the polling timeout's many short sleeps, so the test recorded "Should have timed out" instead of catching AsyncTimeoutError. Mirror the AsyncHelpers gate. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
CLAUDE.mdThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. Project OverviewMistKit is a Swift Package for Server-Side and Command-Line Access to CloudKit Web Services. It targets cross-platform Swift (including Linux, WASI, and Windows) using modern Swift concurrency and code generated from Apple's CloudKit Web Services OpenAPI specification. Key Project Context
Development CommandsSwift Package Commands# Build the package
swift build
# Run tests
swift test
# Run tests with coverage
swift test --enable-code-coverage
# Build for release
swift build -c release
# Clean build artifacts
swift package clean
# Update dependencies
swift package update
# Resolve package dependencies
swift package resolve
# Generate Xcode project (if needed)
swift package generate-xcodeprojOpenAPI Code Generation# Generate OpenAPI client code (run this after modifying openapi.yaml)
./Scripts/generate-openapi.sh
# Or manually with swift-openapi-generator
swift run swift-openapi-generator generate \
--output-directory Sources/MistKit/Generated \
--config openapi-generator-config.yaml \
openapi.yamlDevelopment Workflow# Run specific test
swift test --filter TestClassName.testMethodName
# Run tests in parallel
swift test --parallel
# Show test output
swift test --verbose
# Format + lint
# swift-format, swiftlint, periphery, and swift-openapi-generator are pinned
# in mise.toml — do NOT invoke them from PATH directly. Run them THROUGH mise:
mise exec -- swift-format -i -r Sources/ Tests/
mise exec -- swiftlint # lint
mise exec -- swiftlint --fix # auto-fix
# Or run the full lint pipeline (build + swiftlint + header.sh + periphery):
./Scripts/lint.shMistDemo Commands# MistDemo is located in Examples/MistDemo and must be run from there
cd Examples/MistDemo
# Build MistDemo
swift build
# Run MistDemo commands
swift run mistdemo --help
swift run mistdemo auth-token
swift run mistdemo current-user
swift run mistdemo query
swift run mistdemo lookup
swift run mistdemo create
swift run mistdemo update
swift run mistdemo modify
swift run mistdemo delete
swift run mistdemo upload-asset
swift run mistdemo lookup-zones
swift run mistdemo fetch-changes
swift run mistdemo demo-in-filter
swift run mistdemo demo-errors
swift run mistdemo test-integration
swift run mistdemo test-private
# Run with specific configuration
swift run mistdemo --config-file ~/.mistdemo/config.json queryArchitecture ConsiderationsFieldValue Type ArchitectureMistKit uses separate types for requests and responses at the OpenAPI schema level to accurately model CloudKit's asymmetric API behavior: Type Layers:
Why Separate Request/Response Types?
Generated Types:
Conversion:
Modern Swift Features to Utilize
Package StructureCloudKitService Operations
Sync/Change Operations:
User-Identity Operations (public DB + web-auth required):
In MistDemo, integration runs targeting these endpoints use Result Types (Sources/MistKit/Service/):
Protocols:
Key Design Principles
LoggingMistKit uses swift-log for cross-platform logging support, enabling usage on macOS, Linux, Windows, and other platforms. Key Logging Components:
Logging Subsystems: MistKitLogger.api // CloudKit API operations
MistKitLogger.auth // Authentication and token management
MistKitLogger.network // Network operationsHelper Methods: MistKitLogger.logError(_:logger:shouldRedact:) // Error level
MistKitLogger.logWarning(_:logger:shouldRedact:) // Warning level
MistKitLogger.logInfo(_:logger:shouldRedact:) // Info level
MistKitLogger.logDebug(_:logger:shouldRedact:) // Debug levelPrivacy Controls:
Asset Upload Transport DesignWhen providing a custom
Why URLSession instead of ClientTransport? Asset uploads use
Design:
Implementation Details:
Future Consideration:
FilterBuilder Extensions
IN/NOT_IN serialization: Uses CloudKit Web Services Integration
Testing Strategy
Asset Upload TestingIntegration Test Requirements:
Test Files:
MistDemo Integration Test Runner
Run via Important Implementation Notes
OpenAPI-Driven DevelopmentThe Swift package uses Apple's swift-openapi-generator to create type-safe client code from the OpenAPI specification. Generated code is placed in
The
Key endpoints documented in the OpenAPI spec:
Reference DocumentationApple's official CloudKit documentation is available in When to Consult Each Documentwebservices.md (289 KB) - CloudKit Web Services REST API
cloudkitjs.md (188 KB) - CloudKit JS Framework
testing-enablinganddisabling.md (126 KB) - Swift Testing Framework
swift-openapi-generator.md (235 KB) - Swift OpenAPI Generator Documentation
See MistDemo Documentation
CloudKit Schema Languagecloudkit-schema-reference.md - CloudKit Schema Language Quick Reference
sosumi-cloudkit-schema-source.md - Apple's Official Schema Language Documentation
Comprehensive Schema GuidesFor detailed schema workflows and integration:
Additional Notes
|
Review: PR #332 — Extract auth-token server scaffolding + resource bundleOverall this is a solid refactor. The architecture is cleaner and testability is improved. What's good
Minor observationsLoopbackAuthority on non-Hummingbird platforms: The utility lives in Sources/MistDemoKit/Utilities/ outside a #if canImport(Hummingbird) guard. Verify this compiles cleanly on WASI/platforms where Hummingbird is unavailable, or add a compile guard if it is only ever used by Hummingbird-gated code. AuthTokenIndexHTML.content error path: Using preconditionFailure for a missing bundle resource is fine for a demo tool, but a comment clarifying the intentional crash-vs-silent-failure choice would help future readers. Import access in test files: The new test files import without internal qualifiers, inconsistent with the source files in this PR. Non-critical since tests are module-internal, but consistency is nice. These are all minor — the refactoring is well-executed. Note: there is also a stale comment above this one that was posted by mistake — it can be ignored. |
Review: PR #332 — Extract auth-token server scaffolding + resource bundleSummary: Phase 1 of #330. Two issues resolved (#288, #289). Routes extracted from What's well doneArchitecture
Testing
Resource bundle
Issues / suggestions1.
2. All four 3.
4. let host = authority.split(separator: ":", maxSplits: 1).first
return host.map(String.init) ?? authorityThe only time 5. The struct stores the enum's string representation. Make sure the conversion from 6. For a loopback demo this is acceptable and useful for debugging. Worth a note in the README security section if this surface ever expands beyond loopback. SummaryThe refactoring is clean, well-tested, and directionally correct. The main item worth resolving before merge is issue #1 (HummingbirdTesting in the executable target). The rest are minor observations or documentation suggestions. High-quality work overall. Generated with Claude Code |
…d/Modified (#336) * Rework mistdemo web UI to table+form; sortable Created/Modified Iterates on top of #329's CloudKit JS mode toggle. The single-mode JSON- textarea CRUD grid is replaced by a Notes table beside a Title/Index form: clicking a row loads it for edit, per-row Delete buttons, "New" to clear. Auto-refreshes after every mutation and after mode switches, so the same notes can be observed fetched through either backend. WebUI: - Two-column responsive layout: Notes table left, edit/create form right; stacks to one column below 820px. - Created and Modified columns formatted with the locale's dateStyle:short/timeStyle:short (e.g., "5/12/26, 4:30 PM"); full ISO is in each cell's tooltip. - Clickable Created/Modified column headers cycle unsorted → ascending → descending. Sort forwards to both backends: MistKit body `sortBy:[{field, ascending}]`, CloudKit JS `sortBy:[{fieldName, ascending}]`. Default is no sort, so the demo still lists records before the new schema deploys. - Record name is removed as a column and surfaced as a row tooltip. Note schema: - Drop custom `createdAt` (TIMESTAMP) and `modified` (INT64) — they duplicated CloudKit's system metadata. CKRecord.creationDate / .modificationDate and the Web Services `created.timestamp` / `modified.timestamp` cover the same information without manual bookkeeping. Schema, native Note model, RecordDetailView, QueryView, NativeCloudKitService, integration phases, README, and the CLI query examples are updated. - Add `___createTime` and `___modTime` to the schema with QUERYABLE SORTABLE so the sort feature actually works against the live container (system fields default to non-sortable; the schema must explicitly opt them in). Server: - New WebJSON.encoder()/.decoder() with .millisecondsSince1970 date strategy. The browser receives created/modified timestamps as plain epoch-millis numbers, matching CloudKit JS's Date shape. - WebRequests.Update + .Delete grow optional `recordChangeTag` — the browser holds it from the last query, so MistKit-mode update/delete no longer need a server-side fetch round-trip. Fixes CloudKit's `BadRequestException: missing required field 'recordChangeTag'`. - WebRequests.Create + .Update.fields are `[String: FieldValue]` decoded through MistKit's FieldValue Codable (which accepts raw JSON primitives — string/int/double). Fixes the 400 thrown when the form sent `"index": 5` (a JSON number) against the prior `[String: String]` type. - WebRequests.Query gains `sortBy: [QuerySortField]?`; WebBackend takes the request-shape sort directly (no MistKit-internal type leakage). CloudKitService extension is the only site that knows about MistKit's QuerySort. WebCommand: - Catch AsyncTimeoutError.cancelled so Ctrl+C is a normal shutdown rather than a top-level fatal error. Tests: - WebServerTests+QuerySort — sort forwarding + nil default. - Updated CRUD tests cover recordChangeTag forwarding (update + delete), mixed-type fields (int + double in create), and absent recordChangeTag tolerance on update. - MockBackend.QueryCall.sortBy captures the request-shape sort; flatten() handles int64/double for assertion. The new SORTABLE system fields in schema.ckdb need to be deployed to the live CloudKit container before sort works end-to-end. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address PR #336 review: route table-Delete status; tidy comments/tests - index.html: deleteNote(note, statusEl = tableStatusEl) so per-row Delete feedback lands above the table; the form panel's Delete passes formStatusEl explicitly. Removes a dead recordType fallback reachable only if normalizeRecords() dropped recordType — which it doesn't. Adds a comment that per-row Delete intentionally skips confirm(); the raw response panel makes accidents visible. - WebJSON.swift: removes unused decoder() (FieldValue's own Codable handles request-side dates; Hummingbird's request.decode runs the framework decoder). Encoder remains as the singular response-side contract; docstring updated accordingly. - WebRequests.swift: doc comment on QuerySortField.field flagging that CloudKit JS calls the same concept `fieldName` and the browser maps between them. - MockBackend.swift: comment on flatten()'s default case explaining the intentional drop of asset/date/reference/location/list/bytes — tests needing those should inspect the FieldValue directly. - WebJSONTests.swift (new): locks the encoder's epoch-millis contract with a round-trip test. The browser's `toDate(value)` in index.html depends on receiving plain millis numbers. Skipped from the review with reasoning preserved in the plan file (/Users/leo/.claude/plans/async-wibbling-pearl.md): - server-side recordChangeTag guard (CloudKit's 400 is already clear, browser is the only realistic caller) - generic CancellationError catch in WebCommand (the explicit AsyncTimeoutError.cancelled is more meaningful) - sort response with timestamps test (encoder round-trip covers the same contract more directly) - schema.ckdb trailing blank line (was already present pre-PR) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* Resolve #275 (web side): public/private database picker Adds a database picker to the long-running `mistdemo web` demo so the mode toggle's four profiles can be exercised side-by-side: - MistKit + private → API token + browser-captured web-auth token - MistKit + public → server-to-server signing (CLOUDKIT_KEY_ID + CLOUDKIT_PRIVATE_KEY[_PATH]) - CloudKit JS + private → API token + web-auth (shared with MistKit) - CloudKit JS + public → API token only (browser → CloudKit directly) WebConfig now accepts optional key-id / private-key inputs and computes `publicDatabaseAvailable`. WebBackendFactory.live builds the CloudKitService from `Credentials` so a single service can route operations to either database based on the request's `database` field. The `/api/config` endpoint advertises `publicDatabaseAvailable` so the UI disables the "MistKit + Public" option when the server isn't holding S2S credentials. CloudKit-JS-mode requests pick `container.publicCloudDatabase` vs `privateCloudDatabase` based on the toggle. Unknown database values return 400 rather than silently defaulting. The app side of #275 is intentionally not addressed here; it's absorbed by #328 (replace NativeCloudKitService with the CloudKit framework). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WebUI: loading states, post-create delay, "You" badge, default sort Four UX papercuts surfaced in first hands-on use of the new public/private database picker: - Switching databases triggered a query with no feedback while the network round-trip was in flight. queryNotes now sets a `.status.loading` message ("Loading <db> via <mode>") and disables refresh/db/mode/save/ delete buttons for the duration; a finally block re-enables them and re-runs refreshDatabasePicker so the public-availability gate wins. - Auto-refresh after Create raced CloudKit's eventual consistency on Public — the new record was often missing. saveNote now pauses REFRESH_DELAY_MS (1.2s) on Create only, with a visible "waiting for CloudKit to settle" status. Update/Delete still refresh immediately. - No way to tell which records the signed-in user owned (most useful on Public). handleAuthentication now stores userIdentity.userRecordName in currentUserRecordName; normalizeRecords projects createdBy from the MistKit-mode `created.userRecordName` envelope; renderRows appends a green "You" badge when they match. Cleared on sign-out. CloudKit-JS mode doesn't surface the creator on records, so the badge is MistKit-only — refreshDatabasePicker now spells that out as a hint in CloudKit-JS mode. - Default sort was "whatever CloudKit returned." currentSort now initializes to ___createTime descending, and refreshSortIndicators fires once during init so the column arrow renders before the first query. Coverage: WebServerTests+Index gains "Index HTML carries the post-database-picker UX additions" asserting all four new strings (.status.loading, REFRESH_DELAY_MS, currentUserRecordName/badge-you, the default-sort initializer). 931 tests pass; swiftlint clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * WebUI: rework database-picker hint after MistKit/CloudKit-JS identity audit Two corrections to the hint that appears under the database toggle: - Drop the special CloudKit-JS branch that claimed CloudKit JS doesn't surface the record creator. A real-session diagnostic showed the opposite: CloudKit JS returns `created.userRecordName` in the same envelope shape MistKit does (alongside `timestamp` and `deviceID`), and `normalizeRecords` was already picking it up correctly. - Add a new MistKit + Public hint explaining the iCloud-vs-S2S identity split: records you write via that path are owned by the S2S key's effective identity, not your iCloud user, so they intentionally don't carry the "You" badge. Tracked for broader follow-up in #338. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Code Review PR 332 - Resolve 288 289: extract auth-token server scaffolding and resource bundle. Overview: Extracts the embedded HTTP server logic from AuthTokenCommand into a reusable WebServer struct, introduces WebCommand for a long-running interactive CRUD demo, and moves the HTML page into a resource bundle. Two commands now share one server implementation distinguished by the terminatesAfterAuth flag. Architecture Strengths: Clean separation of concerns across WebServer, WebBackend protocol, and WebBackendFactory. WebAuthTokenStore is correctly an actor preventing data races. LoopbackOnlyMiddleware is scoped to the /api router group so the HTML index stays accessible while the sensitive API surface is restricted to loopback. terminatesAfterAuth elegantly drives the 205 vs 204 behavior. Suggestions: (1) WebIndexHTML.loadContent() uses preconditionFailure - a descriptive message hinting at the resource target would help contributors who hit this during development. (2) All CRUD routes use POST even for delete and update - worth a brief comment so future contributors do not accidentally fix it and break the client JS. (3) runOperation maps all errors to 500 - CloudKit typed errors would benefit from more specific status codes in a future pass. (4) The 300-second timeout in captureToken is hardcoded with no user-visible progress feedback - a log line at start would improve UX. (5) MockBackend.stubRecord uses string interpolation for JSON which is fragile with special characters in record names. Security: LoopbackAuthority correctly handles IPv6 bracketed form, trailing ports, and empty authorities. API token is only served behind the loopback guard. Session token in WebAuthTokenStore has no expiry which is acceptable for a local demo but worth noting in the help text. Test Coverage: Coverage is solid. Gap: LoopbackOnlyMiddleware is only tested in isolation via LoopbackAuthorityTests - a routing-level test that sends a request with a non-loopback Host header and expects 403 would close this. Minor nits: addIndexEndpoint captures indexBytes via an extra closure layer that is not necessary since ByteBuffer is Sendable. WebRequests.decodeDatabase is fileprivate on an enum where private would be equally correct. Summary: Clean architecture, good test coverage, follows project conventions. The WebAuthTokenStore actor plus AsyncStream pattern for token capture is particularly clean. Three items worth addressing before leaving DRAFT: (1) routing-level test for LoopbackOnlyMiddleware rejecting non-loopback requests, (2) user-visible log message for the auth timeout countdown, (3) comment on the POST-for-all-verbs decision. |
…) (#339) * Resolve #328: MistDemoApp CloudKit refresh (CKRecord-first, @observable, public/private picker) - Rename `NativeCloudKitService`/`Error` to `CloudKitStore`/`Error` — the app target no longer depends on MistKit, so the "Native" disambiguator is dead weight; "Store" reads as the SwiftUI source-of-truth idiom. - `Note` wraps `CKRecord` instead of copying fields out of it. Update is now "mutate the held record, save" — no extra fetch round-trip to refresh the change tag. - `@Observable` + `@MainActor` on `CloudKitStore`; views use `@Environment(CloudKitStore.self)` and `@Bindable` for the picker. App entry switches to `@State` + `.environment(_:)`. - Public/private database picker in `AccountView`; `QueryView` and `ZoneListView` re-fetch on `.onChange(of: store.databaseScope)` and show the active scope in their navigation title. - Drop web-auth-token UI (`AccountView+Actions.swift`, related state) and the `CLOUDKIT_API_TOKEN` scheme env var — the native app authenticates via the signed-in iCloud user. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * [CodeFactor] Apply fixes * Gate WebBackendFactory on canImport(Hummingbird) to fix wasm build WebBackendFactory.live calls CloudKitService's URLSession-defaulted convenience init, which is gated behind #if !os(WASI). The rest of the Server/ folder is already wrapped in #if canImport(Hummingbird); this file was missed. Wrapping it the same way unblocks the wasm, wasm 6.2, and wasm-embedded CI jobs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Address PR #339 review: roll back Note CKRecord wrapper, restore web-auth-token UI Two review comments from #pullrequestreview-4286058024: 1. Note: revert from CKRecord wrapper back to value-struct (id/title/index/ imageAssetURL + system metadata). updateNote now fetches by ID before apply+save instead of mutating the original record in place; deleteNote reconstructs CKRecord.ID from the recordName. Views switch from note.recordName to note.id. 2. AccountView: restore the API-token TextField, "Fetch Web Auth Token" button, copyable token display, and CLOUDKIT_API_TOKEN env-var seed, ported from the pre-#328 NativeCloudKitService design onto the new @observable CloudKitStore + @Environment binding. Database picker stays. Adds CloudKitStore.fetchWebAuthToken via CKFetchWebAuthTokenOperation and a webAuthTokenUnavailable error case. Recreates AccountView+Actions.swift (deleted in #328). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Resolve #338: per-call PublicAuthPreference encoded in Database (#340) * Address PR #339 review: use CKDatabase.Scope, fix web-auth-token routing + scheme env - Replace CloudKitStore.DatabaseScope with CKDatabase.Scope; new CKDatabaseScope+Demo.swift extension provides the demo-scoped selectable list ([.public, .private]) and label. - Route CKFetchWebAuthTokenOperation through container.privateCloudDatabase unconditionally; the operation is documented to require the private database and was previously running against the user-selected scope. - Migrate fetchWebAuthTokenCompletionBlock -> fetchWebAuthTokenResultBlock (the completion-block API is deprecated in macOS 12+); drop the now- unreachable webAuthTokenUnavailable error case. - Bake CLOUDKIT_API_TOKEN into the macOS + iOS scheme run actions so xcodegen substitutes the .env value AccountView already reads from ProcessInfo at launch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Mark CloudKitStore.fetchWebAuthToken nonisolated to fix CK callback crash The continuation body inherited @mainactor isolation from CloudKitStore, which tripped a dispatch_assert_queue assertion on com.apple.cloudkit.callback when CKFetchWebAuthTokenOperation's result block fired — crashing with EXC_BREAKPOINT in _dispatch_assert_queue_fail on macOS 26.5. Marking the bridge nonisolated lets the operation enqueue + callback dispatch run off the main actor. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Add owner "You" badge and newest-first sort to native MistDemo Mirrors the web demo: track the signed-in user's record name via CKContainer.userRecordID, capture each note's creator from CKRecord.creatorUserRecordID, and tag matching rows in QueryView. Also sorts Notes by creationDate desc with modificationDate desc as the tiebreaker, matching the web demo's default ordering. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: codefactor-io <support@codefactor.io>
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)
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)
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)
Summary
Closes out the full #330 plan on the umbrella branch
330-interactive-mistdemo. All six sub-issues land here.Phase 1 — Foundation refactors
AuthTokenServer+LoopbackAuthorityfromAuthTokenCommand. Routes move to a reusable server type; loopback validation moves to a standalone helper. New tests: 26-case parameterized validator coverage + 4 router-level smoke tests viaHummingbirdTesting. (b20cc09)AuthTokenIndexHTML*.swiftraw-string constants into a singleResources/auth-token-index.htmlresource;AuthTokenIndexHTMLbecomes a thinBundle.moduleloader. (10ce538)Phase 2 — Web CRUD + comparison
mistdemo webcommand with Hummingbird CRUD routes (/api/records/{query,create,update,delete,lookup}),WebAuthTokenStoreactor, and anindex.htmlresource. Top-level mode toggle scaffolded; CloudKit JS side stubbed until Integrate CloudKit JS into MistDemo web for MistKit-vs-CloudKit-JS comparison #329. (62c7b6c, PR Resolve #274: mistdemo web command with Hummingbird CRUD routes #333)ckWebAuthTokenwith the MistKit side. (1e8b907, PR Resolve #329: CloudKit JS alternate backend + browser-flag defaults #335)Phase 3 — Database selection
bad7b1e, PR Resolve #275 (web side): public/private database picker #337)CloudKitStore.databaseScope+AccountViewpicker, absorbed by Replace NativeCloudKitService with CloudKit framework in MistDemoApp #328 as planned. (6be4939)Phase 4 — Native app rework
NativeCloudKitServicewith a CKRecord-first,@ObservableCloudKitStoreusingCKContainer/CKDatabasedirectly. Public/private scope toggle ships with it. (6be4939, PR Resolve #328: MistDemoApp CloudKit refresh (CKRecord-first, @Observable) #339)Adjacent polish that rode along
31a4168, PR WebUI: table+form rework, system-metadata timestamps, sortable Created/Modified #336)PublicAuthPreferenceencoded inDatabase(c891d2f, PR Resolve #338: per-call PublicAuthPreference encoded in Database #340, MistKit + Public signing: S2S vs web-auth identity attribution #338) — supports the public-DB story in MistKit itselfTest plan
swift buildclean on the branchswift test— full suite green at each sub-PR merge./Scripts/lint.sh— cleanmistdemo webagainst a real container, exercise both MistKit and CloudKit JS toggles on public and private DBsMistDemoAppon iOS — confirm public/private picker behavesResolves #288
Resolves #289
Resolves #274
Resolves #329
Resolves #275
Resolves #328
Resolves #330
🤖 Generated with Claude Code