Resolve #338: per-call PublicAuthPreference encoded in Database#340
Conversation
The dispatcher's `makePublicTokenManager` silently preferred S2S over web-auth whenever both credential sets were configured. Surfaced via the #337 database picker: records written through "MistKit + Public" were attributed to the developer key, not the iCloud user, so the "You" badge never lit up. Per #338's "broader Option C", attribution becomes per-call. Two departures from the issue's design comment: 1. No defaults. Every public-DB call site picks attribution explicitly; reintroducing `.prefers(.serverToServer)` as a defaulted parameter would recreate the silent-policy problem at a different layer. 2. Encoded in `Database`, not a separate `auth:` parameter, so the knob appears only where it's meaningful — `.private` / `.shared` callers don't type meaningless `auth:` arguments. Changes: - New `PublicAuthPreference` struct with `prefers(_:)` / `requires(_:)` factories (internal init keeps the four valid combinations the only reachable ones). - `Database.public` carries a `PublicAuthPreference` payload; gained `pathSegment` accessor in place of `String: RawRepresentable`. - `Credentials.makeTokenManager(for:)` rewritten against a resolution table. The `requiresUserContext` flag is gone; user-context routes pass `.public(.requires(.webAuth))` directly — the carve-out collapses into the same machinery #338 proposed. - `CloudKitError.missingCredentials` gains a `CredentialAvailability` distinguishing `.notConfigured` (no creds of the needed type) from `.preferenceRequired` (caller used `.requires(_:)` but creds absent). - Every public op that took `database: Database = .public` loses the default; callers must pick. The `RecordManaging` protocol conformance targets `.public(.prefers(.serverToServer))` to preserve prior "S2S when present, fall back" behavior for callers stuck on the deprecated database-agnostic surface. Out of scope (follow-up branch off 330-interactive-mistdemo): the demo's Option C toggle that exposes `.prefers`/`.requires` as a UI control and re-lights the "You" badge under MistKit + Public + `.requires(.webAuth)`. Verification: swift build clean; 485/485 tests pass; ./Scripts/lint.sh "Linting completed successfully". 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 @@
## 330-interactive-mistdemo #340 +/- ##
============================================================
- Coverage 70.54% 70.39% -0.15%
============================================================
Files 549 551 +2
Lines 15317 15422 +105
============================================================
+ Hits 10806 10857 +51
- Misses 4511 4565 +54
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:
|
Code Review: PR #340 — Per-call PublicAuthPreference encoded in DatabaseOverviewThis PR eliminates the silent "S2S wins when present" dispatch policy by encoding the auth signing choice directly in Strengths
Issues and Suggestions1. Fallback chain for
|
| Scenario | Covered |
|---|---|
prefers(.serverToServer) + S2S only |
✅ |
prefers(.serverToServer) + both creds |
✅ |
prefers(.serverToServer) + webAuth only (fallback) |
✅ |
prefers(.serverToServer) + API token only (fallback) |
✅ |
prefers(.webAuth) + both creds |
✅ |
prefers(.webAuth) + S2S only (fallback) |
✅ |
prefers(.webAuth) + API token only |
❌ missing |
requires(.serverToServer) + both creds |
✅ |
requires(.serverToServer) without S2S → preferenceRequired |
✅ |
requires(.webAuth) + both creds |
✅ |
requires(.webAuth) without webAuth → preferenceRequired |
✅ |
requires(.webAuth) + API token only → preferenceRequired |
✅ |
Summary
The design is correct and well-motivated. Main items to address before merging:
- Clarify or fix the
prefers(.webAuth)+ bare-API-token fallback — document it or remove it from the fallback chain, and update the matrix comment to match. - Rename or simplify
makeAPITokenManagerto reflect that it always returnsAPITokenManagerat every real call-site. - Add the missing test for the bare-API-token fallback path.
- Document the breaking change in the PR description.
🤖 Generated with Claude Code
After 0976aff changed `Database.public` to carry a `PublicAuthPreference` and dropped default `database:` parameters on `CloudKitService` record/ asset/sync operations, MistDemo failed to compile (~25 errors, 17 files). Threads `database:` through every command/phase service call, replaces `.public` literals with `.public(.prefers(.serverToServer))` (matching existing `toPrimaryCredentials()` resolution order), swaps `Database(rawValue:)` / `.rawValue` for a new `MistDemoConfig.parseDatabase(_:)` helper and `Database.pathSegment`, and converts equality checks against `.public` to pattern matches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Same Database/.public(.prefers(.serverToServer)) sync as 245e9f8 for the MistDemoTests target. swift test (Examples/MistDemo) now passes 931 tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — PR #340: Per-call
|
|
PR 340 Review: Per-call PublicAuthPreference Summary: Encodes the signing choice for .public requests in the Database type via a new PublicAuthPreference, removes the requiresUserContext flag from the dispatcher, and promotes CredentialAvailability as a first-class error distinction. Well-reasoned design and clean implementation. Strengths
Issues
No-action notes
Overall: Architecture is a clear improvement. The dispatch logic is far easier to follow now that the signing choice travels with the database value. The four items above are mostly minor; the missing test for .prefers(.webAuth) + bare-API-token fallback is the one worth addressing before merge. |
- prefers(.webAuth) + bare API token: codifies the silent fallback to APITokenManager that the PR review flagged. - CloudKitError.missingCredentials.errorDescription: covers both .notConfigured and .preferenceRequired availability branches (CloudKitError.swift:134-141). - fetchExistingRecordNames(database:) and the deprecated RecordManaging.queryRecords(recordType:) wrapper: exercises the new database: parameter plumbing in CloudKitService+Classification.swift and the deprecated wrapper in CloudKitService+RecordManaging.swift. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review — PR #340: Per-call
|
…) (#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>
Summary
Database.publicnow carries aPublicAuthPreference(.prefers(...)or.requires(...)); the dispatcher's silent "S2S wins when present" policy is gone. Caller picks attribution explicitly at every public-DB call site.Credentials.makeTokenManager(for:)lost therequiresUserContextflag — user-context routes (users/*) pass.public(.requires(.webAuth))directly, collapsing the carve-out into the same machinery (per MistKit + Public signing: S2S vs web-auth identity attribution #338).CloudKitError.missingCredentialsgainedCredentialAvailabilityso.preferenceRequired(caller used.requires(_:)but creds absent) is distinguishable from.notConfigured.PublicAuthPreference— a defaultedauth:parameter would recreate the silent-policy problem at a different layer.Database, not a separateauth:parameter — so the knob appears only where it's meaningful..private/.sharedcallers don't type meaninglessauth:arguments.Library-only PR. The demo's Option C toggle (and the "You" badge fix under MistKit + Public) live in a follow-up branch off
330-interactive-mistdemo.Test plan
swift build— cleanswift test— 485/485 pass (rewroteCredentialsTokenManagerTests+PublicDatabase.swiftfor the newprefers/requiresmatrix; rewrote+UserContext.swiftto assert the carve-out collapse; updatedDatabaseTestsforpathSegment; mechanical sweep added explicitdatabase:arguments to 14 Service test files)./Scripts/lint.sh—Linting completed successfullymistdemo web+ "You" badge under MistKit + Public +.requires(.webAuth)) — lives in the follow-up branch off330-interactive-mistdemo🤖 Generated with Claude Code