Skip to content

v1.0.0 beta.1#298

Merged
leogdion merged 31 commits into
mainfrom
v1.0.0-beta.1
May 17, 2026
Merged

v1.0.0 beta.1#298
leogdion merged 31 commits into
mainfrom
v1.0.0-beta.1

Conversation

@leogdion
Copy link
Copy Markdown
Member

@leogdion leogdion commented May 7, 2026

No description provided.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 7, 2026

Important

Review skipped

Too many files!

This PR contains 300 files, which is 150 over the limit of 150.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 606c9352-48a8-4cf9-8b94-1985d34839d7

📥 Commits

Reviewing files that changed from the base of the PR and between 38f0d77 and a436054.

📒 Files selected for processing (300)
  • .claude/docs/data-sources-api-research.md
  • .claude/docs/protocol-extraction-continuation.md
  • .codefactor.yml
  • .devcontainer/devcontainer.json
  • .devcontainer/swift-6.1/devcontainer-lock.json
  • .devcontainer/swift-6.3-nightly/devcontainer.json
  • .devcontainer/swift-6.3/devcontainer.json
  • .github/actions/setup-tools/action.yml
  • .github/workflows/MistDemo-Integration.yml
  • .github/workflows/MistDemo.yml
  • .github/workflows/MistKit.yml
  • .github/workflows/check-unsafe-flags.yml
  • .github/workflows/cleanup-caches.yml
  • .github/workflows/codeql.yml
  • .github/workflows/examples.yml
  • .github/workflows/swift-source-compat.yml
  • .gitignore
  • .swift-format
  • .swiftlint.yml
  • CLAUDE.md
  • Examples/BushelCloud/.claude/s2s-auth-details.md
  • Examples/BushelCloud/.github/actions/setup-mistkit/action.yml
  • Examples/BushelCloud/.github/workflows/BushelCloud.yml
  • Examples/BushelCloud/.github/workflows/bushel-cloud-build.yml
  • Examples/BushelCloud/.gitrepo
  • Examples/BushelCloud/.swift-format
  • Examples/BushelCloud/.swiftlint.yml
  • Examples/BushelCloud/CLAUDE.md
  • Examples/BushelCloud/Mintfile
  • Examples/BushelCloud/Package.resolved
  • Examples/BushelCloud/Scripts/bootstrap.sh
  • Examples/BushelCloud/Scripts/lint.sh
  • Examples/BushelCloud/Sources/BushelCloudCLI/Commands/SyncCommand.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/BushelCloud.docc/CloudKitIntegration.md
  • Examples/BushelCloud/Sources/BushelCloudKit/CloudKit/BushelCloudKitError.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/CloudKit/BushelCloudKitService.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/CloudKit/KeyIDValidator.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/Configuration/ConfigurationKeys.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/Extensions/SwiftVersionRecord+CloudKit.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/Extensions/XcodeVersionRecord+CloudKit.swift
  • Examples/BushelCloud/Sources/BushelCloudKit/Utilities/ConsoleOutput.swift
  • Examples/BushelCloud/Tests/BushelCloudKitTests/CloudKit/MockCloudKitServiceTests.swift
  • Examples/BushelCloud/Tests/BushelCloudKitTests/ErrorHandling/CloudKitErrorHandlingTests.swift
  • Examples/BushelCloud/Tests/BushelCloudKitTests/Mocks/MockCloudKitService.swift
  • Examples/BushelCloud/mise.toml
  • Examples/CelestraCloud/.claude/IMPLEMENTATION_NOTES.md
  • Examples/CelestraCloud/.github/actions/setup-mistkit/action.yml
  • Examples/CelestraCloud/.github/workflows/CelestraCloud.yml
  • Examples/CelestraCloud/.github/workflows/update-feeds.yml
  • Examples/CelestraCloud/.gitrepo
  • Examples/CelestraCloud/.swift-format
  • Examples/CelestraCloud/.swiftlint.yml
  • Examples/CelestraCloud/CLAUDE.md
  • Examples/CelestraCloud/Mintfile
  • Examples/CelestraCloud/Package.resolved
  • Examples/CelestraCloud/Package.swift
  • Examples/CelestraCloud/README.md
  • Examples/CelestraCloud/Scripts/header.sh
  • Examples/CelestraCloud/Scripts/lint.sh
  • Examples/CelestraCloud/Sources/CelestraCloud/Celestra.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/AddFeedCommand.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/ClearCommand.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/ExitError.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/UpdateCommand+Reporting.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/UpdateCommand.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/UpdateCommandError.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Commands/UpdateSummary.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Services/FeedUpdateProcessor+Fetch.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Services/FeedUpdateProcessor.swift
  • Examples/CelestraCloud/Sources/CelestraCloud/Services/FeedUpdateResult.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/CelestraConfig.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/CelestraConfiguration.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/CloudKitConfiguration.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/ConfigSource.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/ConfigurationKeys.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/ConfigurationLoader.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/EnhancedConfigurationError.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/UpdateCommandConfiguration.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Configuration/ValidatedCloudKitConfiguration.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Errors/CloudKitConversionError.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Errors/ConfigurationError.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Extensions/Article+MistKit.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Extensions/Feed+MistKit.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Extensions/RecordInfo+Parsing.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Models/ArticleSyncResult.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Models/BatchOperationResult.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Models/UpdateReport+JSONOutput.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Models/UpdateReport.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Protocols/CloudKitConvertible.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Protocols/CloudKitRecordOperating.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/ArticleCategorizer.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/ArticleCloudKitService.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/ArticleOperationBuilder.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/ArticleSyncService.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/CelestraError.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/CelestraLogger.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/CloudKitService+Celestra.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/FeedCloudKitService.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/FeedMetadataBuilder.swift
  • Examples/CelestraCloud/Sources/CelestraCloudKit/Services/FeedMetadataUpdate.swift
  • Examples/CelestraCloud/Tests/CelestraCloudTests/Configuration/UpdateCommandConfigurationTests.swift
  • Examples/CelestraCloud/Tests/CelestraCloudTests/Errors/CelestraErrorTests+Description.swift
  • Examples/CelestraCloud/Tests/CelestraCloudTests/Errors/CelestraErrorTests+RecoverySuggestion.swift
  • Examples/CelestraCloud/Tests/CelestraCloudTests/Errors/CelestraErrorTests.swift
  • Examples/CelestraCloud/Tests/CelestraCloudTests/Mocks/MockCloudKitRecordOperator.swift
  • Examples/CelestraCloud/mise.toml
  • Examples/MistDemo/.env.example
  • Examples/MistDemo/.periphery.yml
  • Examples/MistDemo/.swift-format
  • Examples/MistDemo/.swiftlint.yml
  • Examples/MistDemo/App/MistDemoApp.swift
  • Examples/MistDemo/Makefile
  • Examples/MistDemo/MistDemoApp.entitlements
  • Examples/MistDemo/Package.resolved
  • Examples/MistDemo/Package.swift
  • Examples/MistDemo/README.md
  • Examples/MistDemo/Scripts/header.sh
  • Examples/MistDemo/Scripts/lint.sh
  • Examples/MistDemo/Sources/ConfigKeyKit/Command.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/CommandConfiguration.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/CommandLineParser.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/CommandRegistry.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/CommandRegistryError.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/ConfigKey+Bool.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/ConfigKey+Debug.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/ConfigKey.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/ConfigKeySource.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/ConfigurationParseable.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/NamingStyle.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/OptionalConfigKey+Debug.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/OptionalConfigKey.swift
  • Examples/MistDemo/Sources/ConfigKeyKit/StandardNamingStyle.swift
  • Examples/MistDemo/Sources/MistDemo/CloudKit/MistKitClientFactory.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/AuthTokenCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/CreateCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/CurrentUserCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/DemoInFilterCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/FetchChangesCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/LookupZonesCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/QueryCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/TestIntegrationCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/TestPrivateCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/UpdateCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Commands/UploadAssetCommand.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/AuthTokenConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/ConfigurationError.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/CreateConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/CurrentUserConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/FetchChangesConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/Field.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/FieldParsingError.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/FieldType.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/LookupZonesConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/MistDemoConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/MistDemoConfiguration.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/QueryConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/TestIntegrationConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/TestPrivateConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/UpdateConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Configuration/UploadAssetConfig.swift
  • Examples/MistDemo/Sources/MistDemo/Constants/MistDemoConstants.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/ConfigError.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/CreateError.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/ErrorOutput.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/QueryError.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/UpdateError.swift
  • Examples/MistDemo/Sources/MistDemo/Errors/UploadAssetError.swift
  • Examples/MistDemo/Sources/MistDemo/Extensions/ConfigKey+MistDemo.swift
  • Examples/MistDemo/Sources/MistDemo/Extensions/FieldValue+FieldType.swift
  • Examples/MistDemo/Sources/MistDemo/Integration/IntegrationTestData.swift
  • Examples/MistDemo/Sources/MistDemo/Integration/IntegrationTestError.swift
  • Examples/MistDemo/Sources/MistDemo/Integration/IntegrationTestRunner.swift
  • Examples/MistDemo/Sources/MistDemo/MistDemo.swift
  • Examples/MistDemo/Sources/MistDemo/Models/AuthRequest.swift
  • Examples/MistDemo/Sources/MistDemo/Models/AuthResponse.swift
  • Examples/MistDemo/Sources/MistDemo/Models/CloudKitData.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Escapers/CSVEscaper.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Escapers/JSONEscaper.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Escapers/OutputEscaperFactory.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Escapers/TableEscaper.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Escapers/YAMLEscaper.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Formatters/CSVFormatter.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Formatters/OutputFormatterFactory.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Formatters/TableFormatter.swift
  • Examples/MistDemo/Sources/MistDemo/Output/Formatters/YAMLFormatter.swift
  • Examples/MistDemo/Sources/MistDemo/Output/FormattingError.swift
  • Examples/MistDemo/Sources/MistDemo/Output/OutputEscaping.swift
  • Examples/MistDemo/Sources/MistDemo/Output/OutputFormatter.swift
  • Examples/MistDemo/Sources/MistDemo/Protocols/OutputFormatting+Implementations.swift
  • Examples/MistDemo/Sources/MistDemo/Protocols/OutputFormatting+Records.swift
  • Examples/MistDemo/Sources/MistDemo/Protocols/OutputFormatting+Users.swift
  • Examples/MistDemo/Sources/MistDemo/Protocols/OutputFormatting.swift
  • Examples/MistDemo/Sources/MistDemo/Resources/index.html
  • Examples/MistDemo/Sources/MistDemo/Types/AnyCodable.swift
  • Examples/MistDemo/Sources/MistDemo/Types/FieldInputValue.swift
  • Examples/MistDemo/Sources/MistDemo/Types/FieldsInput.swift
  • Examples/MistDemo/Sources/MistDemo/Utilities/AsyncChannel.swift
  • Examples/MistDemo/Sources/MistDemo/Utilities/AsyncHelpers.swift
  • Examples/MistDemo/Sources/MistDemo/Utilities/AuthenticationHelper.swift
  • Examples/MistDemo/Sources/MistDemo/Utilities/BrowserOpener.swift
  • Examples/MistDemo/Sources/MistDemo/Utilities/FieldValueFormatter.swift
  • Examples/MistDemo/Sources/MistDemoApp/Models/CKRecord+TypedField.swift
  • Examples/MistDemo/Sources/MistDemoApp/Models/Note.swift
  • Examples/MistDemo/Sources/MistDemoApp/Models/ZoneRow.swift
  • Examples/MistDemo/Sources/MistDemoApp/Services/CKDatabase+WebAuthToken.swift
  • Examples/MistDemo/Sources/MistDemoApp/Services/CKDatabase.Scope+Demo.swift
  • Examples/MistDemo/Sources/MistDemoApp/Services/CloudKitStore.swift
  • Examples/MistDemo/Sources/MistDemoApp/Services/CloudKitStoreError.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/AccountView+Actions.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/AccountView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/AppMain.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/DetailColumnRoot.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/NoteEditView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/QueryView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/RecordDetailView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/RootView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/SidebarItem.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/SidebarView.swift
  • Examples/MistDemo/Sources/MistDemoApp/Views/ZoneListView.swift
  • Examples/MistDemo/Sources/MistDemoKit/CloudKit/MistKitClientFactory.swift
  • Examples/MistDemo/Sources/MistDemoKit/CloudKitCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/AuthTokenCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/CreateCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/CurrentUserCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DeleteCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DeleteResult.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DemoErrorsCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DemoErrorsRunner+Output.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DemoErrorsRunner.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/DemoInFilterCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/FetchChangesCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/LookupCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/LookupZonesCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/MistDemoCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/ModifyCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/ModifyOutput.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/ModifyResultRow.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/QueryCommand+FilterParsing.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/QueryCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/TestPrivateCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/TestPublicCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/UpdateCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/UploadAssetCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Commands/WebCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/AuthTokenConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/BrowserFlagResolver.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/ConfigurationError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/CreateConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/CurrentUserConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/DeleteConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/DemoErrorsConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/DemoErrorsError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/ErrorScenario.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/FetchChangesConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/Field.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/FieldParsingError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/FieldType.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/LookupConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/LookupZonesConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/MistDemoConfig+DatabaseConfiguration.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/MistDemoConfig+Parsing.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/MistDemoConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/MistDemoConfiguration.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/ModifyConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/ModifyOperationInput.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/ModifyOperationKind.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/QueryConfig+Parsing.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/QueryConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/SortOrder.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/TestPrivateConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/TestPublicConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/UpdateConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/UploadAssetConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Configuration/WebConfig.swift
  • Examples/MistDemo/Sources/MistDemoKit/Constants/MistDemoConstants+Defaults.swift
  • Examples/MistDemo/Sources/MistDemoKit/Constants/MistDemoConstants+Messages.swift
  • Examples/MistDemo/Sources/MistDemoKit/Constants/MistDemoConstants.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/AuthTokenError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/CreateError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/CurrentUserError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/DeleteError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/ErrorOutput+Convenience.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/ErrorOutput.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/FieldConversionError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/LookupError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/MistDemoError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/ModifyError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/OutputFormattingError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/QueryError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/UpdateError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Errors/UploadAssetError.swift
  • Examples/MistDemo/Sources/MistDemoKit/Extensions/Array+Field.swift
  • Examples/MistDemo/Sources/MistDemoKit/Extensions/Command+AnyCommand.swift
  • Examples/MistDemo/Sources/MistDemoKit/Extensions/ConfigKey+MistDemo.swift
  • Examples/MistDemo/Sources/MistDemoKit/Extensions/FieldValue+FieldType.swift
  • Examples/MistDemo/Sources/MistDemoKit/Extensions/String+Padding.swift
  • Examples/MistDemo/Sources/MistDemoKit/Integration/AssetUploadReceipt+PhaseState.swift
  • Examples/MistDemo/Sources/MistDemoKit/Integration/CleanupPhaseMarker.swift
  • Examples/MistDemo/Sources/MistDemoKit/Integration/CreatedRecordNames.swift
  • Examples/MistDemo/Sources/MistDemoKit/Integration/IncrementalSyncInput.swift

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

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch v1.0.0-beta.1

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

❤️ Share

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

@codecov
Copy link
Copy Markdown

codecov Bot commented May 7, 2026

Codecov Report

❌ Patch coverage is 24.86339% with 550 lines in your changes missing coverage. Please review.
✅ Project coverage is 59.85%. Comparing base (38f0d77) to head (a436054).

Files with missing lines Patch % Lines
...ources/MistDemoKit/Commands/DemoErrorsRunner.swift 0.00% 116 Missing ⚠️
...ces/MistDemoKit/Commands/DemoInFilterCommand.swift 0.00% 78 Missing ⚠️
...ces/MistDemoKit/Commands/FetchChangesCommand.swift 0.00% 58 Missing ⚠️
...ources/MistDemoKit/Commands/AuthTokenCommand.swift 3.44% 56 Missing ⚠️
...o/Sources/MistDemoKit/Commands/ModifyCommand.swift 4.25% 45 Missing ⚠️
...mo/Sources/MistDemoKit/Commands/QueryCommand.swift 5.88% 32 Missing ⚠️
...o/Sources/MistDemoKit/Commands/DeleteCommand.swift 21.05% 30 Missing ⚠️
...rces/MistDemoKit/Commands/LookupZonesCommand.swift 0.00% 25 Missing ⚠️
...MistDemoKit/Commands/DemoErrorsRunner+Output.swift 14.28% 24 Missing ⚠️
...o/Sources/MistDemoKit/Commands/CreateCommand.swift 27.58% 21 Missing ⚠️
... and 8 more
Additional details and impacted files
@@             Coverage Diff             @@
##             main     #298       +/-   ##
===========================================
+ Coverage   25.58%   59.85%   +34.27%     
===========================================
  Files          95      532      +437     
  Lines        8232    19784    +11552     
===========================================
+ Hits         2106    11842     +9736     
- Misses       6126     7942     +1816     
Flag Coverage Δ
mistdemo-spm-macos 46.76% <24.86%> (?)
mistdemo-swift-6.2-jammy 46.76% <24.86%> (?)
mistdemo-swift-6.2-noble 46.77% <24.86%> (?)
mistdemo-swift-6.3-jammy 46.77% <24.86%> (?)
mistdemo-swift-6.3-noble 46.77% <24.86%> (?)
spm 35.76% <ø> (+10.19%) ⬆️
swift-6.1-jammy 35.72% <ø> (+10.15%) ⬆️
swift-6.1-noble 35.70% <ø> (+10.13%) ⬆️
swift-6.2-jammy 35.64% <ø> (+10.07%) ⬆️
swift-6.2-noble 35.69% <ø> (+10.10%) ⬆️
swift-6.3-jammy 35.61% <ø> (?)
swift-6.3-jammynightly ?
swift-6.3-noble 35.66% <ø> (?)
swift-6.3-noblenightly ?

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

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

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

@claude
Copy link
Copy Markdown

claude Bot commented May 7, 2026

Code Review: v1.0.0 beta.1

This is a large, well-structured release covering CI modernization, example improvements, and library hardening. The overall quality is high — strong test coverage, strict linting, and solid concurrency safety. Below are the findings by area.


Overview

  • 43,756 additions / 24,212 deletions across 300+ files
  • Core changes: CI matrix refactoring, MistDemo improvements, CelestraCloud error tests, and example app updates
  • Library source (Sources/MistKit/) appears stable; main concerns are in the path helper pattern and availability annotations

Code Quality

CloudKitService.swift — Repetitive path helpers (lines 73–208)

Every createXPath(containerIdentifier:) method has an identical body — it constructs the same .init(version:container:environment:database:) triple with only the return type differing:

internal func createQueryRecordsPath(containerIdentifier: String) -> Operations.queryRecords.Input.Path { ... }
internal func createLookupRecordsPath(containerIdentifier: String) -> Operations.lookupRecords.Input.Path { ... }
// ... 8 more identical methods

These are compiler-safe but are a maintenance burden. If a field like version ever changes from "1", it must be updated in 10 places. A single generic helper or a local makePath() closure inside each operation call would eliminate the duplication.

@available annotations don't match declared minimums

Package.swift declares macOS 10.15 / iOS 13.0 as the minimum, but CloudKitService and all its extensions are annotated @available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *). This is presumably intentional (swift-openapi-runtime requires newer OS), but there's no comment explaining the gap. This will silently fail for any consumer targeting the declared minimum.

public import Foundation vs implicit internal imports

CloudKitError.swift uses public import Foundation and import OpenAPIRuntime (no qualifier). With InternalImportsByDefault enabled, an unqualified import becomes internal — but OpenAPIRuntime types appear in the public CloudKitError interface (via underlyingError). This compiles today but may generate warnings or become an error in a future Swift version. Audit all files that expose OpenAPIRuntime types publicly.


Potential Bugs

misdirectedRequestResponse type/name mismatch (CloudKitResponseType.swift:54)

var misdirectedRequestResponse: Components.Responses.UnprocessableEntity? { get }

The property is named for HTTP 421 (Misdirected Request) but the type is UnprocessableEntity (normally HTTP 422). The generated code comment confirms this is a CloudKit-specific quirk — the API uses 421 with the UnprocessableEntity response schema. However, the error constructor in CloudKitError+OpenAPI.swift hard-codes statusCode: 422 when handling this case, which will produce incorrect HTTP status codes in errors for what is actually a 421 response. The status code should be 421.

AdaptiveTokenManager.hasCredentials is misleading (AdaptiveTokenManager.swift:47-50)

public var hasCredentials: Bool {
    get async { !apiToken.isEmpty }
}

The protocol name implies "valid credentials available," but this only checks that the API token string is non-empty. An expired, malformed, or revoked token will return true. If downstream code branches on this to skip authentication or error handling, it will behave incorrectly. The docstring should explicitly call this out, or the implementation should call validateCredentials().

CloudKitError missing redirectURL and typed serverErrorCode

PR #279's article code shows:

} catch let error as CloudKitError where error.serverErrorCode == .authenticationRequired {
    if let redirectURL = error.redirectURL {

Neither serverErrorCode nor redirectURL exist as properties on CloudKitError. The redirectURL is available on the underlying Components.Schemas.ErrorResponse in generated code but is not surfaced. Before publishing the article, either add these properties to CloudKitError or update the article snippet to use pattern matching on .httpErrorWithDetails.


Performance Considerations

errorExtractors static array (CloudKitError+OpenAPI.swift:33–45)

The 11-element closure array is scanned linearly for every error response. At this scale it's not a practical concern, but because only one case can match per response type, a switch on the response's active case would be O(1) instead of O(n). Not urgent.

Platform.without() in Package.swift (line 134)

This helper allocates a new array every call. Since it's used at manifest evaluation time only, this is harmless. But the formatter left inconsistent style in the trailing closure:

result.removeAll{
$0 == platform      // missing leading space, not 2-space indented
}

swift-format should catch this — check that the manifest is not excluded from formatting.


Security

The SecureLogging pattern and the critical warning around AssetUploader transport separation are well-handled. No new security concerns found in the reviewed code.

The no_unchecked_sendable custom SwiftLint rule (.swiftlint.yml) is a strong guard — keep it.


CI/CD

lint waits on all platform builds (.github/workflows/MistKit.yml)

The lint job lists needs: [build-ubuntu, build-macos, build-macos-platforms, build-windows, build-android]. Android builds are slow and gated on full-matrix. On PRs that don't trigger full-matrix, build-android is skipped — does the lint job's if condition handle this correctly when some needs jobs are skipped? Worth verifying that skipped jobs don't block lint on non-full-matrix PRs.

ci skip check only works on push events

if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}

github.event.head_commit is populated on push, but on pull_request events it's the PR's head commit message — this may behave unexpectedly. Consider using github.event.pull_request.title or the [skip ci] convention which GitHub Actions recognizes natively.

actions/checkout@v6 — this is a pre-release version. Pinning to a SHA for security would be preferable for an open-source library workflow. If @v6 is intentional (tracking latest), add a comment.

Experimental Swift features in Package.swift

20+ .enableExperimentalFeature(...) calls. Some of these (MoveOnlyClasses, NoncopyableGenerics, VariadicGenerics) can cause silent behavioral differences across Swift 6.1/6.2/6.3. The CI matrix covers all three toolchains which mitigates this, but if any feature is unused in the actual source, it should be removed — dead experimental feature flags add compile noise.


Test Coverage

139 test files for 104 source files is an excellent ratio. The concurrent token refresh suite and the mock uploader infrastructure for asset upload tests are particularly well-designed. No gaps identified in the reviewed areas.


Summary

Area Status
Architecture ✅ Clean, well-separated
Test coverage ✅ Comprehensive
Concurrency safety ✅ Strong (@unchecked Sendable banned, strict checking)
CI matrix coverage ✅ Ubuntu/macOS/Windows/Android/WASM
Repetitive path helpers ⚠️ Maintenance risk
@available gap vs declared minimum ⚠️ Needs comment or fix
misdirectedRequest → 421 but codes 422 ⚠️ Likely bug
hasCredentials semantics ⚠️ Misleading, document clearly
redirectURL/serverErrorCode missing from error ⚠️ Blocks PR #279 article from compiling
ci skip on PRs ℹ️ Minor

The 421→422 status code mismatch is the item I'd most want to verify before tagging the release. Everything else is either documentation/cleanup or low-risk.

@claude
Copy link
Copy Markdown

claude Bot commented May 7, 2026

Code Review: v1.0.0 beta.1

This PR has 300+ changed files (43,756 additions / 24,212 deletions), which exceeds the GitHub diff API limit. A full line-by-line review isn't possible without cloning locally.

For a release PR of this scope, I'd suggest:

  • Breaking the review into focused passes: generated code, manual Swift sources, tests, CI/CD config
  • Ensuring swift test passes clean on the branch before merge
  • Running the PR through /ultrareview for a multi-agent cloud review that can handle the full diff

If you'd like me to review specific files or subsystems from this PR, share the paths and I can dig in.


Review by Claude Sonnet 4.6

@claude
Copy link
Copy Markdown

claude Bot commented May 8, 2026

Code Review — v1.0.0-beta.1 (#298)

Overview

This is a significant beta release bringing together multiple improvements merged via sub-PRs: typed TokenManagerError, a refactored AuthenticationMiddleware, the new BatchSyncResult/OperationClassification pattern for tracking creates vs. updates, query pagination with continuation markers, substantial test reorganisation, and CI hardening. Overall the architecture is clean, the async/Sendable usage is consistent, and the documentation is excellent. A few concrete issues below.


Bugs

AdaptiveTokenManager.storage is declared but never used
storage is accepted in init(apiToken:storage:) and stored, but no TokenStorage calls appear anywhere in the actor. Credentials will never actually be persisted, silently defeating the persistence parameter.

// AdaptiveTokenManager.swift
internal let storage: (any TokenStorage)?   // stored, never written/read

AdaptiveTokenManager.webAuthToken has no mutation API
The property is var (implying upgradeability) but there is no func upgradeToWebAuth(token:) or equivalent public method. The actor cannot transition to web authentication after init, making the "adaptive" naming misleading.

ServerToServerAuthenticator — locale-sensitive PEM error detection

if error.localizedDescription.contains("PEM")
    || error.localizedDescription.contains("format")

localizedDescription content varies by locale and OS version. Prefer pattern-matching on the concrete error type (CryptoKitError) or always throw .invalidPEMFormat for any parse failure from P256.Signing.PrivateKey(pemRepresentation:).

lint.sh — unquoted pushd

pushd $PACKAGE_DIR   # breaks if PACKAGE_DIR contains spaces

Should be pushd "$PACKAGE_DIR" (and the popd/swift-format calls too).


Code Quality

CloudKitService+Classification.fetchExistingRecordNames silent truncation
The 200-record cap is documented in a - Important: block, which is good, but callers will silently misclassify updates-as-creates beyond the cap. Consider returning a tuple with a isTruncated: Bool flag, or throwing if limit exceeds 200, so callers can't accidentally get wrong results.

InMemoryTokenStorage broad decode catch
Failed authenticator decoding is swallowed into .corruptedStorage with no structured log. At minimum, log the underlying error at .debug level through MistKitLogger so storage corruption is diagnosable without attaching a debugger.

AuthenticationMiddleware — authenticator fetched per request
tokenManager.currentAuthenticator() is called on every HTTP request. For ServerToServerAuthenticator this reconstructs a P256 key wrapper each time. The result is immutable, so it could be cached across requests (invalidated only on credential changes). Not a correctness issue but worth a follow-up for request-heavy paths.

EnvironmentConfig token masking exposes too much
Showing the first 8 characters of an API token in logs (getMaskedEnvironment) leaks meaningful entropy. Consider showing only "<set>" / "<not set>" or at most the first 4 characters.

Repeated #available() guards in pagination tests
All 8 tests in CloudKitServiceQueryPaginationTests+SuccessCases.swift carry an identical #available guard. Hoist it to @Suite (or a withKnownIssue/XCTSkipUnless equivalent) to keep tests DRY.


Test Coverage

Performance suite is skeletal
ConcurrentTokenRefreshTests+Performance.swift contains a single test with no timing assertions. Either add clock.measure-style assertions or rename the suite to avoid implying performance coverage that isn't there.

Pagination tests are success-only
CloudKitServiceQueryPaginationTests+SuccessCases.swift covers the happy path well (single page, multi-page, stuck-marker detection), but there are no corresponding error tests. What happens when the continuation marker is malformed? When a mid-pagination request fails? A +ErrorHandling extension parallel to the other service test directories would complete the picture.

ConcurrentTokenRefreshTests+Performance undefined helpers
runConcurrent() and MockTokenManagerWithRateLimiting are referenced but not visible in the file. Verify these are in shared helpers and not accidentally omitted from the PR.


CI / Scripts

MistKit.ymlci skip check breaks on PRs

if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}

github.event.head_commit is null for pull_request events, so this condition evaluates to false and skips all PR checks. Use github.event.commits[0].message for push events and guard by event type, or drop the ci skip check in favour of GitHub's native [skip ci] support.

lint.shheader.sh runs unconditionally in CI
$PACKAGE_DIR/Scripts/header.sh is called outside any $CI guard. If that script has side effects (file writes) it will modify files during the lint CI job. Wrap it in if [ -z "$CI" ].

lint.sh$PERIPHERY_OPTIONS undefined in normal mode
periphery scan $PERIPHERY_OPTIONS expands to an empty string outside STRICT mode; no issue today but add PERIPHERY_OPTIONS="" at the top of the else-branch to be explicit.


Security

ServerToServerAuthenticator.encoded() stores raw private key material
The WireFormat JSON includes the P-256 private key as a base64 string. The in-code comment acknowledges this, but it is worth a prominent ⚠️ in the public API documentation so callers don't inadvertently persist the output to disk or transmit it over a non-encrypted channel.


Positives

  • Typed TokenManagerError with associated reason enums is a big improvement over stringly-typed errors.
  • The BatchSyncResult/OperationClassification pattern elegantly solves a real CloudKit API limitation and is well-documented.
  • AuthenticationMiddleware refactor (each Authenticator applies itself) is clean and matches the protocol-oriented design goals.
  • Test file organisation (CloudKitServiceTests.Query+*, etc.) is now much easier to navigate.
  • Fixing lint.sh to count all failures (not just STRICT-mode ones) is a valuable correctness fix.
  • Comprehensive DocC-style inline documentation throughout.

Summary of Action Items

Priority Item
🔴 Bug AdaptiveTokenManager.storage stored but never used
🔴 Bug AdaptiveTokenManager has no public API to set webAuthToken
🔴 Bug PEM error detection via localizedDescription is locale-sensitive
🔴 CI ci skip check silently skips all PR jobs
🟡 Quality Unquoted pushd "$PACKAGE_DIR" in lint.sh
🟡 Quality fetchExistingRecordNames silent 200-record truncation
🟡 Test Pagination error-path tests missing
🟡 Test Performance suite has no measurements
🟢 Minor Token masking shows too many chars in EnvironmentConfig
🟢 Minor Repeated #available guards in pagination test suite
🟢 Minor header.sh runs unconditionally in CI

@claude
Copy link
Copy Markdown

claude Bot commented May 9, 2026

Code Review: v1.0.0 beta.1

Overview

This is a large release-candidate PR (~49k additions, ~25k deletions, 300+ files) touching three major areas:

  1. CI/CD overhaul — dedicated MistDemo workflow, smart full/minimal matrix, Android, WASM, and multi-platform macOS builds, CodeQL integration
  2. CelestraCloud refactoringUpdateCommand decomposed into focused files, JSON report output, new CelestraError test coverage, CloudKitRecordOperating protocol for testability
  3. BushelCloud improvementsKeyIDValidator for S2S key validation, a new PEM-string initializer for CI/CD environments

Overall quality is high and patterns align well with project conventions. The issues below are refinements.


Issues

Bug: Wrong field used for error logging in BushelCloudKitService

File: Examples/BushelCloud/Sources/BushelCloudKit/CloudKit/BushelCloudKitService.swift:247

// Current — logs recordType, not the error reason
Self.logger.debug("Error: recordName=\(result.recordName), reason=\(result.recordType)")

result.recordType is the record type identifier (e.g., "Feed"), not the error reason. This will produce misleading debug output when diagnosing batch operation failures.


Missing JSON serialization for computed properties in UpdateReport

File: Examples/CelestraCloud/Sources/CelestraCloudKit/Models/UpdateReport.swift:170,56

public var duration: TimeInterval { endTime.timeIntervalSince(startTime) }
public var successRate: Double { ... }

Swift's Codable synthesis does not encode computed properties. Consumers of the JSON report won't see duration or successRate in the output despite them being obvious report fields. Either compute and store them as let properties set at init time, or implement encode(to:) explicitly.


Type-unsafe status strings in UpdateReport.FeedResult

File: Examples/CelestraCloud/Sources/CelestraCloudKit/Models/UpdateReport.swift:130

public let status: String  // "success", "error", "skipped", "notModified"

A plain String status can silently produce invalid values through typos. A Codable enum would give compile-time exhaustiveness checks and safe decoding:

public enum Status: String, Codable, Sendable {
    case success, error, skipped, notModified
}

actions/checkout@v6 may not exist

Files: .github/workflows/MistKit.yml, .github/workflows/MistDemo.yml, example workflows

- uses: actions/checkout@v6

actions/checkout@v4 is the latest stable major version at time of writing. Using @v6 will either resolve to a pre-release or fail at runtime. Please verify this tag exists before merging.


MockCloudKitRecordOperator is not safe under parallel test execution

File: Examples/CelestraCloud/Tests/CelestraCloudTests/Mocks/MockCloudKitRecordOperator.swift:56-63

nonisolated(unsafe) internal private(set) var queryCalls: [QueryCall] = []
nonisolated(unsafe) internal var queryRecordsResult: Result<[RecordInfo], CloudKitError> = .success([])

The comment says "single-threaded test use only," but Swift Testing runs tests in parallel by default. Any two tests that mutate this mock concurrently will race. Either mark the test suite @Suite(.serialized) or wrap shared state in an Actor.


Overfetching in fetchExistingRecordNames

File: Examples/BushelCloud/Sources/BushelCloudKit/CloudKit/BushelCloudKitService.swift:169

let records = try await queryRecords(recordType: recordType)
let recordNames = Set(records.map(\.recordName))

All fields are fetched just to extract record names. Passing desiredKeys: [] would reduce response payload significantly for large record sets.


Minor / Nits

  • updateFeedMetadata success semantics (FeedUpdateProcessor+Fetch.swift:103): Returns .error when metadata.failureCount > 0, even if articles synced successfully. A partial-success variant would give consumers more accurate data than a blanket error.

  • ExitError is a one-liner struct with no payload. If used only to signal a CLI exit, consider ExitCode from swift-argument-parser directly, or add a message: String for call sites to provide context.

  • Copyright year inconsistency: CelestraErrorTests+Description.swift and CelestraErrorTests+RecoverySuggestion.swift carry © 2025 while all other new files use © 2026. Worth aligning if these files are new in this PR.


Strengths worth calling out

  • KeyIDValidator: Excellent defensive validation with actionable error messages. The "trim first, then check for whitespace" approach catches a very common copy-paste error cleanly.
  • CloudKitRecordOperating protocol + MockCloudKitRecordOperator: Good abstraction enabling unit testing without hitting CloudKit.
  • CelestraError.isRetriable: Thoughtful classification of transient vs. permanent errors. The HTTP status code logic (retry 5xx and 429, not other 4xx) is correct.
  • Smart CI matrix: Minimal matrix on feature branches, full matrix on main/semver/PRs-to-main is a sensible cost/coverage trade-off.
  • MistDemo-specific workflow: Path filtering means MistDemo builds only trigger on relevant changes, avoiding wasted CI minutes.
  • BushelCloudKitService PEM-string initializer: The CI/CD-friendly initializer accepting PEM content directly from an environment variable (instead of requiring a file path) is the right pattern for GitHub Actions secrets.

leogdion added a commit that referenced this pull request May 11, 2026
…rd returns

CodeFactor reported 19 violations on PR #298:

- `CredentialsTokenManagerTests.swift` (274 lines, 16× `conditional_returns_on_newline`):
  Convert the single suite struct into a namespace enum with four nested
  sub-suites (PublicDatabase, PrivateShared, UserContext, PrivateKeyLoad)
  following the existing `CloudKitServiceTests.FetchCaller` layout. Each
  `guard #available(...) else { return }` becomes multi-line.

- `CloudKitError+OpenAPI.swift` (237 lines):
  Move the 11 per-response private initializers into a sibling
  `CloudKitError+OpenAPI+Responses.swift`. Bump them from `private` to
  `internal` so the extractor array in the main file can still call them.

Also pick up filename-comment corrections in `OpenAPI/Components/*.swift`
that `Scripts/header.sh` (part of `Scripts/lint.sh`) normalized — stale
`+MistKit.swift` suffixes were trimmed to match the actual filenames.

Verified:
- `./Scripts/lint.sh` → 0 violations
- `swift test --filter CredentialsTokenManagerTests` → 18 tests pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
leogdion added a commit that referenced this pull request May 11, 2026
Adds tests that don't require live CloudKit access — covers the cheapest
slice of the patch-coverage gap on PR #298 (codecov/patch: 13%).

New test files (all @testable, no network):
- Configuration/AuthTokenConfigTests.swift — memberwise init + parsed init via
  InMemoryProvider, including the missing/empty api.token error path
- Configuration/UploadAssetConfigTests.swift — memberwise init for every field
- Configuration/TestIntegrationConfigTests.swift — defaults + custom values
- Configuration/TestPrivateConfigTests.swift — defaults + private-DB pinning
- Commands/QueryCommand/QueryCommandTests+ParseFilter.swift — full coverage
  of parseFilter / inferFieldValue / shouldIncludeField / buildComparisonFilter
  (parameterized over every operator alias and error path)
- Commands/CreateCommand/CreateCommandTests+GenerateRecordName.swift —
  prefix, three-part format, suffix range, distinctness across 200 calls
- Commands/DemoErrorsRunnerOutputTests.swift — describe(_:) placeholder + echo

Source changes are minimal seams that mirror the existing
DeleteCommand.mapConflict pattern (internal-static for testability):
- QueryCommand: filter parsing helpers extracted into
  QueryCommand+FilterParsing.swift; private → internal static
- CreateCommand.generateRecordName: private → internal

901 MistDemo tests pass; lint + swift-format clean.

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

claude Bot commented May 11, 2026

Code Review

Note: PR #298's diff exceeds GitHub's 300-file display limit, so this review combines direct file analysis of the repository state with the full diff of follow-up PR #316 (now closed), which contained fixes/additions from this branch.


Overview

PR #298 ("v1.0.0 beta.1") is a large release PR that includes:

  • New MistDemo commands and their configuration types
  • Availability bump for asset upload APIs (macOS 11 → 12 / iOS 14 → 15)
  • Logging redaction changes in CloudKitError+OpenAPI.swift
  • A large MistDemo test suite expansion (~1,300 lines of new tests)
  • CI/workflow updates and example app changes

Positive Highlights

  • Consistent availability bumps. The macOS 12.0 / iOS 15.0 bump is applied uniformly across URLSession+AssetUpload.swift, CloudKitService+AssetOperations.swift, CloudKitService+AssetUpload.swift, and all matching guard #available checks in test files. No stale guards remain.
  • Swift Testing adoption. All new tests use @Test, #expect, and @Suite as required by CLAUDE.md. No XCTest leftovers.
  • MistDemoConfiguration+Testing.swift is a clean test infrastructure. The InMemoryProvider-backed factory avoids boilerplate across all config test files, and the .-split key logic mirrors how production code reads config — good design.
  • Error-path coverage. Most config tests exercise the throwing path (await #expect(throws:)), including missing required fields and bad file paths. The UploadAssetCommand test that generates a random UUID path to guarantee file absence is particularly solid.
  • Parameterized test for ErrorScenario. DemoErrorsConfigTests.parsesScenarioRawValues(expected:) uses arguments: ErrorScenario.allCases — this is exactly the right use of parameterized tests.

Issues and Suggestions

1. Breaking availability change — needs changelog entry

// Before
@available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *)

// After
@available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *)

This is a breaking API change for any caller deployed on macOS 11 / iOS 14. The PR description and CHANGELOG.md (if one exists) should explicitly document this narrowing. The motivation (using URLSession.upload(for:from:delegate:) which requires iOS 15+) should also be stated — it's not clear from the diff alone why the floor moved.

2. Logging redaction regression risk

File: Sources/MistKit/Service/CloudKitError+OpenAPI.swift

The original code had:

MistKitLogger.logDebug(
    "Unhandled response (HTTP \(statusCode)): \(response)",
    logger: MistKitLogger.api,
    shouldRedact: false   // ← explicit
)

The new code drops shouldRedact: false, relying on the default (redaction on). The old comment said "Full body lives at debug level — may contain server-echoed request data (e.g. emails passed to lookupUsersByEmail). Warning stays sanitized" — meaning the warning was already sanitized, but the debug log was intentionally unredacted for local debugging.

Redacting debug-level logs of unhandled CloudKit responses makes debugging new/unexpected error shapes harder. Consider keeping shouldRedact: false for the debug-level call and the default (redact) only for the warning-level call, matching the original intent.

3. Smoke tests don't assert anything

Several tests in DemoErrorsRunnerOutputTests.swift pass by not throwing, but verify nothing:

@Test("printRunnerHeader does not throw")
internal func headerDoesNotThrow() async throws {
    let runner = try await DemoErrorsRunner(config: MistDemoConfig())
    runner.printRunnerHeader()
    // No assertion
}

If these methods print to stdout, capturing output (or even checking a return value where possible) would make these tests meaningful rather than just crash guards. For methods that truly have no observable output besides side effects, consider at minimum noting this limitation in the test name, e.g. "printRunnerHeader does not crash".

4. Force-unwrap in test file

File: CreateConfigTests+ParseFromConfiguration.swift

try json.data(using: .utf8)!.write(to: tmp)

While .utf8 encoding is infallible in practice, using Data(json.utf8) is cleaner and avoids the force-unwrap pattern, which the project avoids elsewhere:

try Data(json.utf8).write(to: tmp)

5. Multi-line doc comment in test helper

File: MistDemoConfiguration+Testing.swift

The doc comment block (lines explaining .-splitting of config keys) spans 6 lines. CLAUDE.md says to default to no comments and keep any comments to one short line. The behavior being documented (that "container.identifier" splits on .) is non-obvious and worth noting — but it can be condensed to a single line:

// Keys are split on "." to form AbsoluteConfigKey components.
internal static func testing(
    _ values: [String: ConfigValue]
) -> MistDemoConfiguration {

6. Inconsistent smoke-test commentary

UploadAssetCommandTests.swift has:

// Smoke test: type-check that the value is constructable.
_ = command

But equivalent smoke tests in DemoInFilterCommandTests.swift and FetchChangesCommandTests.swift have _ = command without any comment. Either add consistent comments or — per CLAUDE.md — remove them all (the _ = command idiom is self-explanatory).

7. PR title mismatch

The PR title "Update README with fixed serialization and integration test" does not match the actual changes (availability bumps, new test suite, logging changes). A more accurate title would help future git log readers.


Security

No security concerns found. The logging redaction change (item #2 above) is the closest thing — it actually increases default redaction, which is conservative. The risk is diagnostic, not security-related.

Test Coverage Assessment

The new config tests are thorough for the happy path and most error paths. One gap: there is no test verifying that FetchChangesConfig.parsesDefaults output format is .table (not .json) when unset — this asymmetry from other configs (which default to .json) is worth explicitly documenting as a test case (it is already covered implicitly in FetchChangesConfigTests+ParseFromConfiguration.parsesDefaults, so this is minor).


Summary

The PR is in good shape overall. The three items most worth addressing before merge are:

  1. Document the macOS 12 / iOS 15 availability bump as a breaking change.
  2. Restore shouldRedact: false on debug-level unhandled-response logging (or explicitly confirm the behavior change is intentional).
  3. Replace the force-unwrap in CreateConfigTests+ParseFromConfiguration.swift.

Everything else is low-priority style/polish.

@claude
Copy link
Copy Markdown

claude Bot commented May 17, 2026

Code Review — v1.0.0 beta.1

This is a large release-candidate PR (57k additions / 26k deletions, 1046 files) introducing the v1.0.0-beta.1 API surface, updated CI matrix, two new example apps (BushelCloud, CelestraCloud), and significant MistDemo expansion. Below is a structured review covering the six failing CI checks plus code-level findings.


Failing Checks

1. 🔴 BushelCloud Ubuntu — compile error (blocking)

CloudKitService.init no longer accepts a database: parameter, but BushelCloudKitService.swift passes it at two call sites:

BushelCloudKitService.swift:113 — error: extra argument 'database' in call
BushelCloudKitService.swift:151 — error: extra argument 'database' in call

Fix: Drop database: .public from both initialisers. Database selection is now per-call, not per-service.

Bonus cleanup: The same job warns about unused public imports — these should be internal import or removed:

  • SwiftVersionRecord+CloudKit.swift:30-32BushelFoundation, BushelUtilities, Foundation
  • XcodeVersionRecord+CloudKit.swift:31-32BushelUtilities, Foundation

2. 🔴 CelestraCloud Ubuntu — deprecation-as-error (blocking)

Two call sites in CloudKitService+Celestra.swift still use the deprecated queryRecords(recordType:filters:sortBy:limit:desiredKeys:database:) overload, which the build treats as an error:

CloudKitService+Celestra.swift:92  — 'queryRecords(...)' is deprecated
CloudKitService+Celestra.swift:114 — 'queryRecords(...)' is deprecated

Fix:

  • Line 92 (single-page): migrate to queryAllRecords(...).
  • Line 114 (inside a while true pagination loop): refactor the loop to use queryRecords(...) -> QueryResult and chain on result.continuationMarker, or replace the loop with a single queryAllRecords(...) call.

Bonus cleanup:

  • CloudKitService+Celestra.swift:32 — demote public import Logging to internal import
  • FeedMetadataBuilder.swift:31 — demote public import Foundation to internal import

3. 🟡 watchOS Platforms — intermittent test failure

✘ Test "withTimeout cancels other tasks in group" recorded an issue at
  AsyncHelpersTests+ConcurrentTimeout.swift:46:13:
  Expectation failed: an error was expected but none was thrown and "done" was returned

This is a simulator timing/scheduling issue — on the watchOS cooperative executor the inner Task.sleep completes before the timeout's polling loop detects the deadline. The sibling tests already use withKnownIssue(isIntermittent: true) in AsyncHelpersTests+Timeout.swift:58, :83.

Fix: Wrap the body of cancelsOtherTasks() the same way:

internal func cancelsOtherTasks() async throws {
    await withKnownIssue(isIntermittent: true) {
        await #expect(throws: AsyncTimeoutError.self) {
            try await withTimeout(seconds: 0.1) {
                try await Task.sleep(nanoseconds: 500_000_000)
                return "done"
            }
        }
    }
}

4. 🔴 CodeQL — 2 high-severity "Cleartext logging of sensitive information"

Both alerts point to Sources/MistKit/Logging/MistKitLogger.swift:73, :95, traced from lookupUsersByEmail(_:). The email address flows into a log call without explicit routing through SecureLogging.safeLogMessage(...).

Since these are debug-level paths that do go through SecureLogging when MISTKIT_DISABLE_LOG_REDACTION is unset, and the email is literal caller-supplied input, the pragmatic fix is to dismiss both alerts in the GitHub Security tab as "Won't fix" with a note:

Debug-only logging of caller-supplied email; redacted by SecureLogging unless MISTKIT_DISABLE_LOG_REDACTION is set.

Optional: add a brief inline comment at the call sites pointing future reviewers to SecureLogging and the env-var override.

5. 🟡 CodeFactor — transient

The check errored ("Something went wrong.", 0 annotations) — not an actionable finding. Re-running or re-pushing should clear it.

6. 🔴 codecov/patch — 15.61% vs 25.58% target

The cheapest path to closing the gap (~144 more covered lines) is the Configuration/*Config.swift files — pure decoders, no mocks required, and there are clear existing patterns to follow (LookupConfigTests.swift, DeleteConfigTests.swift).

Highest-ROI targets:

File Uncovered lines
DemoErrorsRunner.swift 112
UploadAssetCommand.swift 103
QueryCommand.swift 97
FetchChangesConfig.swift 35
LookupZonesConfig.swift 29
DemoErrorsConfig.swift 18

If closing the gap before beta.1 isn't realistic given the PR scope, an alternative is relaxing the codecov target on release branches (coverage.status.patch.target: auto) and filing a follow-up issue.


Code-Level Findings

Bug: Wrong field logged for batch errors in BushelCloudKitService.swift:247

// logs recordType, not the error reason
Self.logger.debug("Error: recordName=\(result.recordName), reason=\(result.recordType)")

result.recordType is the CloudKit record type identifier (e.g. "Feed"), not an error reason. This produces misleading output on batch failures. Fix by using the actual error/status field (check RecordInfo for the correct field name).

Bug: Computed properties not serialised in UpdateReport

public var duration: TimeInterval { endTime.timeIntervalSince(startTime) }
public var successRate: Double { ... }

Swift's Codable synthesis skips computed properties. JSON consumers of UpdateReport will never see duration or successRate. Fix: convert to stored let properties computed at init time, or implement encode(to:) explicitly.

Type-unsafe status strings in UpdateReport.FeedResult

public let status: String  // "success", "error", "skipped", "notModified"

Replace with a Codable enum:

public enum Status: String, Codable, Sendable {
    case success, error, skipped, notModified
}
public let status: Status

This catches invalid status values at the type level and serialises cleanly.

MockCloudKitRecordOperator not safe under parallel test execution

nonisolated(unsafe) internal private(set) var queryCalls: [QueryCall] = []
nonisolated(unsafe) internal var queryRecordsResult: Result<[RecordInfo], CloudKitError> = .success([])

The comment says "single-threaded test use only," but Swift Testing parallelises by default. Either convert the mock to an actor (call sites become await, which is fine inside async test bodies) or annotate every consumer suite with @Suite(.serialized).

Performance: overfetching in fetchExistingRecordNames (BushelCloudKitService.swift:169)

let records = try await queryRecords(recordType: recordType)
let recordNames = Set(records.map(\.recordName))

All fields are fetched just to extract record names. Pass desiredKeys: [] to reduce CloudKit's response payload for large record sets.


Lint Debt (non-blocking but worth a sweep)

SwiftLint structural violations:

  • Sources/MistKit/Service/CloudKitService.swift — 244 lines vs 225-line cap. The "path builders" extension (lines ~85-244) is a natural extraction into CloudKitService+Paths.swift.
  • Sources/MistKit/Authentication/Credentials+TokenManager.swift:54 — cyclomatic complexity 9 (cap 6), body 55 lines (cap 50). Extract three private helpers (makeUserContextTokenManager, makePublicTokenManager, makePrivateSharedTokenManager).

Compiler warnings (will become errors under strict concurrency):

  • MistKitClientFactory.swift:75, :105 — spurious try on non-throwing CloudKitService(...) init calls.
  • DemoErrorsRunner.swift:96 — deprecated queryRecords(recordType:) (silently truncates).
  • OutputEscaperFactory.swift:37, OutputFormatterFactory.swift:39, AnyCodable.swift:36, :59, FieldsInput.swift:37, :61 — bare protocol types used as existentials; add any prefix.

Strengths

  • KeyIDValidator — defensive validation with actionable error messages; "trim first, then check for whitespace" catches a common copy-paste error.
  • CloudKitRecordOperating protocol + mock — good abstraction boundary for unit testing without hitting CloudKit.
  • CelestraError.isRetriable — correct HTTP status logic (retry 5xx and 429, not other 4xx).
  • CI matrix design — minimal matrix on feature branches, full matrix on main/semver/PRs-to-main is a sensible cost/coverage tradeoff.
  • BushelCloudKitService PEM-string initializer — accepting PEM content directly from an env var (vs. a file path) is the right pattern for GitHub Actions secrets.

Suggested Merge Order

  1. Drop database: .public from BushelCloud (unblocks Ubuntu CI)
  2. Migrate deprecated queryRecords in CelestraCloud (unblocks Ubuntu CI)
  3. Wrap watchOS test in withKnownIssue(isIntermittent: true)
  4. Dismiss CodeQL alerts via the Security tab (no code change needed)
  5. Bug fixes — log field, UpdateReport computed props + status enum, mock thread-safety, overfetch
  6. Lint sweep — compiler warnings → SwiftLint structural → import visibility
  7. Coverage — start with FetchChangesConfigTests, LookupZonesConfigTests, DemoErrorsConfigTests

The compile-blockers (#1, #2) are the only hard dependencies for green CI; everything else can follow in subsequent commits.


Review generated by Claude Code

@claude
Copy link
Copy Markdown

claude Bot commented May 17, 2026

Code Review — PR #298 (v1.0.0 beta.1)

This is a large release-candidate PR (~57k additions, ~26k deletions across 300+ files) covering pagination, user-identity endpoints, authentication refactoring, new example-app features, and CI improvements. My review focused on the MistKit core, the BushelCloud and CelestraCloud examples, and the test suite. The diff was too large to retrieve in full, so I reviewed the key changed files directly from the branch.


What Changed

  • Pagination: queryRecords now returns QueryResult with continuationMarker; the old [RecordInfo]-returning overload is @available(*, deprecated). queryAllRecords auto-paginates using moreComing/continuationMarker.
  • Auth refactor: CloudKitService no longer accepts a database: argument at init time. Database is chosen per call via Database enum. PublicAuthPreference (.prefers/.requires .serverToServer/.webAuth) encodes the signing choice per operation.
  • User-identity endpoints: fetchCaller(), lookupUsersByEmail(_:), lookupUsersByRecordName(_:), discoverUserIdentities(lookupInfos:) all routed against .public(.requires(.webAuth)).
  • New CI: MistDemo-Integration workflow for live CloudKit runs; smart branch-aware matrix.
  • Examples: BushelCloud gets KeyIDValidator; CelestraCloud gets UpdateReport, ConfigurationError, protocol-based error suite.

Fixes Confirmed ✅

The following issues flagged in the initial CI review have been addressed in the current HEAD:

Issue Status
BushelCloud database: .public compile error (:113, :151) Fixed — init no longer takes database:
BushelCloud misleading error log (result.recordType as reason) Fixed — now logs only recordName
BushelCloud fetchExistingRecordNames overfetch Fixed — passes desiredKeys: []
CelestraCloud deprecated queryRecords (Ubuntu compile failure) FixedqueryFeeds uses queryAllRecords; deleteAllFeeds uses QueryResult + continuationMarker loop
CelestraCloud public import Logging/Foundation Fixed — demoted to internal import
UpdateReport.successRate and UpdateReport.duration not serialized Fixed — both stored let properties computed at init
FeedResult.status: String type-unsafe Fixed — replaced with enum Status: String, Codable, Sendable
MockCloudKitRecordOperator unsafe under parallel tests Fixed — uses Mutex from Synchronization; properly Sendable
watchOS cancelsOtherTasks intermittent test failure Fixed — wrapped in withKnownIssue(isIntermittent: true)
multipleConcurrentTimeouts watchOS flake Fixed — same treatment applied to the inner slow-path
Credentials+TokenManager cyclomatic complexity / body length Fixed — decomposed into makePublicTokenManager, makePublicWithS2SPreference, makePublicWithWebAuthPreference, makePrivateOrSharedTokenManager
CloudKitService.swift over 225-line cap Fixed — now 87 lines; path builders likely extracted
MistKitLogger.swift (the file containing the CodeQL-flagged log calls) Removed — replaced by Logger+Subsystem.swift extension; CodeQL alerts should clear on next scan

Remaining Issues

Security

LoggingMiddleware logs all request headers at debug level (LoggingMiddleware.swift:60)

logger.debug("   Headers: \(request.headerFields)")

headerFields will contain X-Apple-CloudKit-Request-KeyID, X-Apple-CloudKit-Request-SignatureV1, and potentially X-Apple-CloudKit-Request-ISO8601Date. Logging raw signatures is low-risk (they're time-bound) but it also logs the Key ID, which is semi-sensitive. Consider redacting auth headers in debug output:

let safeHeaders = request.headerFields.filter { !$0.name.rawName.contains("CloudKit-Request") }
logger.debug("   Headers: \(safeHeaders)")

Also logBodyData at line 120 logs the complete response body, which for user-identity endpoints includes email addresses and record names. This is debug-only, but worth a comment noting the sensitivity.

CodeQL — Dismissal Still Needed

The two CodeQL "Cleartext logging of sensitive information" alerts were traced from lookupUsersByEmail(_:) into the old MistKitLogger.swift, which no longer exists. They should resolve automatically on the next CodeQL run, but if they persist, dismiss them as "Won't fix" with the note that MistKitLogger.swift was removed.

Copyright Year Inconsistency

MockCloudKitRecordOperator.swift:7 still reads © 2025 despite being substantially rewritten (Mutex-based rewrite) for this PR. Should be © 2026 to match all other new/revised files.

Test Coverage Gap

codecov/patch is at 15.61% vs the 25.58% target (needs ~144 more covered lines). The uncovered lines are concentrated in Examples/MistDemo/Sources/MistDemoKit/DemoErrorsRunner.swift (0%, 112 lines), UploadAssetCommand.swift (0%, 103 lines), AuthTokenCommand+Routes.swift (0%, 78 lines), and several *Config.swift decoders. The cheapest wins are the pure-decoder *Config.swift files — they require no mocks and mirror existing test patterns (FetchChangesConfig, LookupZonesConfig, DemoErrorsConfig).


Design / Code Quality

PublicAuthPreference — clean API ✅

The prefers(_:) / requires(_:) factory pattern is clear and prevents invalid (mode, required) combinations from construction. The internal init and Sendable/Hashable conformances are correct.

Credentials+TokenManager — nice decomposition ✅

The availability guard #available(macOS 11.0, ...) inside makePublicWithS2SPreference creates a subtle asymmetry: on platforms older than macOS 11 with auth.required = false, the S2S path is silently skipped without explanation even when S2S credentials are present. Consider logging a warning on the #available fallthrough path so operators know why S2S was bypassed on older platforms.

queryAllRecords / deleteAllFeeds pagination — correct ✅

deleteAllFeeds in CloudKitService+Celestra.swift uses repeat { … } while continuationMarker != nil with the break-on-empty guard. This is correct; the guard ensures termination when CloudKit returns an empty page without a continuation marker (both conditions must be checked).

executeBatchOperations — minor concern

BushelCloudKitService.executeBatchOperations (line 186-191) constructs an OperationClassification(proposedRecords: [], existingRecords: []) with empty arrays to satisfy the protocol, then discards the result with _ =. If the protocol-conforming overload is ever called in a code path that cares about the result, the silent discard will produce misleading zero counts. Consider naming the protocol-conforming overload clearly or asserting that neither creates nor updates has content when called this way.

MockCloudKitRecordOperator.queryAllRecords signature

The mock's queryAllRecords accepts a maxPages: Int parameter, but CloudKitRecordOperating.queryAllRecords in the protocol may not. Verify the mock fully satisfies the protocol's signature to avoid a type mismatch when the protocol is updated.


Strengths Worth Calling Out

  • KeyIDValidator — defensive validation with actionable error messages; "trim first, then check for whitespace" catches copy-paste leading/trailing spaces cleanly.
  • Per-call PublicAuthPreference — the decision to encode auth preference per call rather than per service is the right model for apps that mix server-attributed and user-attributed writes in the public database.
  • Mutex-based MockCloudKitRecordOperator — using Synchronization.Mutex instead of nonisolated(unsafe) is the correct, future-proof approach for Swift 6 Sendable conformance in mocks.
  • withKnownIssue(isIntermittent: true) pattern — correctly applied to both the single-sleep and the inner slow-path of the concurrent test; this matches the existing pattern in AsyncHelpersTests+Timeout.swift.
  • MistKit-Integration workflow path filtering — triggering the live CloudKit integration run only on relevant path changes is a sensible cost-vs-coverage trade-off for CI.
  • CelestraError.isRetriable — HTTP 5xx + 429 retry, not other 4xx: this is the correct classification for transient vs permanent CloudKit errors.

Summary

The PR is well-structured for a release candidate. The compile blockers, deprecated-API migration, test flake fix, and major code-review items are all resolved in the current HEAD. The two remaining actionable items before merge are:

  1. Bump MockCloudKitRecordOperator.swift copyright to © 2026.
  2. Monitor CodeQL on next push (expect the cleartext-logging alerts to auto-close) and dismiss if they persist.
  3. Optionally: add FetchChangesConfig/LookupZonesConfig/DemoErrorsConfig tests to clear the codecov/patch target, or relax the target for release branches.

The header-logging note in LoggingMiddleware is a hardening suggestion, not a blocker.


Review performed by Claude Sonnet 4.6 via Claude Code

@claude
Copy link
Copy Markdown

claude Bot commented May 17, 2026

Code Review — v1.0.0-beta.1 (PR #298)

This is a large release-candidate merge (1,046 files, +57K/−26K lines, 25 commits) consolidating a significant wave of features and refactors. Overall the PR is in very good shape — almost every CI failure from the initial run has been resolved.


CI Status

Check Status
BushelCloud / CelestraCloud Ubuntu ✅ Passing (was failing)
watchOS Platforms flake ✅ Passing (was failing)
CodeQL cleartext-logging alerts ✅ Passing (was failing)
CodeFactor ✅ Passing (was transient error)
codecov/patch Still failing — 15.61% vs 25.58% target
All other matrix jobs (Ubuntu 6.1/6.2/6.3, WASM, Android, Windows, macOS, Lint) ✅ Passing

The codecov/patch gate is the only remaining blocker.


Architecture / Design

Per-call credential resolution (Credentials.makeTokenManager(for:)) is a substantial improvement over the old service-level database: parameter. The routing logic — S2S for .public when configured, web-auth for .private/.shared, with prefers/requires fallback semantics — is well-structured and the PublicAuthPreference factory pattern (prefers(_:) / requires(_:)) makes the intent clear at every call site.

#if swift(>=6.2) on @available(*, unavailable) for processDiscoverAllUserIdentitiesResponse is a correct and well-commented workaround for the Swift 6.1/6.2 @available cascade incompatibility. The inline note about the one-line cleanup once 6.1 is dropped is appreciated.

Cleartext-logging split (debug full body vs. warning type/status only) addresses the CodeQL finding cleanly without removing diagnostic value.

Protocol-based IntegrationTestRunner phase pipeline and the PhaseContext threading of userContextService make the public+web-auth user-identity runs composable and easy to extend.


Issues Confirmed Fixed

The following items raised in prior review passes are confirmed resolved by reading the current source:

  • BushelCloudKitService.swift compile error (database: .public arg dropped)
  • BushelCloudKitService.swift:247 misleading log field (reason=\(result.recordType) removed)
  • BushelCloudKitService.fetchExistingRecordNames overfetch — now queryAllRecords(..., desiredKeys: [])
  • UpdateReport.Summary.successRate — now a stored let property computed at init (Codable-safe)
  • UpdateReport.duration — stored let property set at init
  • FeedResult.status: String — replaced with a Codable Status enum
  • MockCloudKitRecordOperator thread safety — nonisolated(unsafe) replaced with Synchronization.Mutex
  • Credentials+TokenManager.makeTokenManager cyclomatic complexity — decomposed into makePublicWithS2SPreference / makePublicWithWebAuthPreference / makePrivateOrSharedTokenManager helpers
  • CelestraCloud deprecation-as-error — both call sites migrated off the deprecated queryRecords overload

Remaining Items

1. codecov/patch — only blocking CI failure

Patch coverage is 15.61% vs. the 25.58% target (≈144 more covered lines needed). The gap is concentrated in Examples/MistDemo/Sources/MistDemoKit/ — command and configuration files with 0% patch coverage. The cheapest path to closing it remains the Configuration/*Config.swift decoders (no mocking required, high lines-per-test ratio):

  • FetchChangesConfig (35 lines)
  • LookupZonesConfig (29 lines)
  • DemoErrorsConfig (18 lines)
  • Filling out existing LookupConfig, CreateConfig, AuthTokenConfig tests

These three new files alone (~82 reachable lines) plus minor expansions to existing suites should clear the target. If meaningful test coverage isn't practical before tagging beta.1, consider setting coverage.status.patch.target: auto in .codecov.yml for this branch and filing a follow-up issue to track the gap.

2. Copyright year inconsistency (minor)

Several CelestraCloud test files still carry © 2025 headers:

  • CelestraErrorTests+Description.swift
  • CelestraErrorTests+RecoverySuggestion.swift
  • MockCloudKitRecordOperator.swift
  • UpdateCommandConfigurationTests.swift
  • FeedMetadataBuilder+Success.swift

These are nits but inconsistent with the © 2026 headers on other new files. Easy one-liner fix per file.

3. executeBatchOperations(_:) protocol overload — silent empty classification (minor)

// BushelCloudKitService.swift
public func executeBatchOperations(_ operations: [RecordOperation]) async throws {
    guard let recordType = operations.first?.recordType else { return }
    let classification = OperationClassification(proposedRecords: [], existingRecords: [])
    _ = try await executeBatchOperations(operations, recordType: recordType, classification: classification)
}

The protocol-conforming overload constructs an empty OperationClassification and discards the SyncEngine.TypeSyncResult. This means callers using the protocol interface silently get no create/update tracking. The current design is intentional (comment says "without tracking"), but a _ = discard with no explanation is easy to misread. Consider either:

  • A brief inline comment: // creates/updates not tracked — use the classification overload for accounting
  • Or making the discarded return value explicit with @discardableResult on the tracking overload

Not a correctness issue, just a readability concern.

4. Future: CodeQL Action v3 → v4

The workflows still reference github/codeql-action/init@v3 / analyze@v3 (Node.js 20; v3 deprecates December 2026). Low urgency but worth tracking before that deadline.


Strengths Worth Calling Out

  • KeyIDValidator with trim-then-whitespace-check catches a common copy-paste failure mode; good defensive design.
  • CelestraError.isRetriable classifies 5xx/429 as retryable and other 4xx as permanent — correct HTTP semantics.
  • BushelCloudKitService PEM-string initializer (accept PEM content from env var, not only file path) is the right pattern for CI/CD secrets.
  • Smart CI matrix — minimal on feature branches, full on main/semver/PRs-to-main is a sensible cost/coverage tradeoff.
  • Live CloudKit integration workflow (MistDemo-Integration) with path filtering is a solid addition to the CI story.
  • discoverAllUserIdentities unavailability is handled correctly — @available(*, unavailable) with a fatalError fallback replaced by throw CloudKitError.unsupportedOperationType keeps the error recoverable, and the #if swift(>=6.2) gate is well-documented.

Summary

The PR is ready to merge once the codecov/patch gate is resolved — either by adding the Configuration test files (fastest path) or relaxing the target for this release branch. All other previously-failing checks are green. The architecture improvements to credential resolution, per-call database routing, and the user-identity endpoint surface are well-designed and clearly tested. The follow-up items above (copyright, batch classification comment, CodeQL Action bump) are all low-risk nits suitable for a post-beta cleanup issue.

leogdion added 3 commits May 17, 2026 20:09
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"
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"
@claude
Copy link
Copy Markdown

claude Bot commented May 17, 2026

Code Review — PR #298: v1.0.0 beta.1

Note: This PR has 300+ changed files exceeding GitHub's diff API limit. Review is based on PR metadata, commit history, file-level change summary, and prior review context from this thread.


Overview

This PR consolidates the v1.0.0 beta.1 milestone across three areas:

  1. CI/CD overhaul — new MistDemo.yml and MistDemo-Integration.yml workflows, refactored MistKit.yml matrix, added cleanup-caches.yml and setup-tools action
  2. CelestraCloud refactoringUpdateCommand decomposed into focused extension files (UpdateCommand+Reporting, FeedUpdateProcessor+Fetch), new UpdateSummary, ExitError, ConfigurationError, and UpdateReport+JSONOutput types, expanded CloudKitRecordOperating protocol
  3. BushelCloud improvements — new KeyIDValidator (89 lines), updated BushelCloudKitService, new error types

The core MistKit library (Sources/MistKit/) is unchanged in this PR.


Findings

🔴 Missing PR description
The PR body is empty. For a versioned release PR, even a brief changelog summary makes the intent clear and helps future git log --merges traversal. The 20+ commits here already have good individual messages — a one-paragraph summary pointing to the key features landed would suffice.

🟡 Low patch coverage (18.68% per Codecov)
Most new production code in the examples has no corresponding new tests. The notable exceptions are the CelestraErrorTests+Description.swift and CelestraErrorTests+RecoverySuggestion.swift extensions (201 lines of new tests), which is good. However:

  • KeyIDValidator.swift (+89 lines, no new tests visible in the PR)
  • UpdateCommand+Reporting.swift (+134 lines, no new tests)
  • FeedUpdateProcessor+Fetch.swift (+111 lines, no new tests)
  • UpdateReport+JSONOutput.swift (+44 lines, no new tests)

For a beta-1 milestone these are reasonable to land, but each should have a follow-up issue tracking test coverage before stable 1.0.

🟡 CloudKitRecordOperating protocol expansion (+74 lines, -4 lines)
Adding 70 net lines to a protocol means either default implementations or required conformances for all adopters. If these are new required methods without defaults, existing conformers outside this repo will break at 1.0 stable. Worth confirming these additions have default implementations or are clearly marked @available for pre-stable.

🟡 UpdateCommand.swift significant removal (+4, -128)
128 lines deleted in favor of the new extension files is a good decomposition, but verify the extracted logic is fully equivalent — this is the kind of refactor that can accidentally drop an edge-case branch.

✅ Strengths

  • Strong commit message discipline — each of the 23 commits points back to a numbered issue, making the history traversable
  • Good architectural decomposition: UpdateCommand+Reporting, FeedUpdateProcessor+Fetch follow the pattern already established in CloudKitService+*.swift
  • ExitError as a dedicated type (rather than raw exit(1) calls) is the right call for testability
  • Copyright headers consistently updated across all new files to 2026
  • devcontainer updated to Swift 6.3 — good for anyone onboarding via GitHub Codespaces

Recommendation

✅ Ready to merge with the following tracked follow-ups:

  1. Add a PR body / release notes summary
  2. File issues for missing test coverage on KeyIDValidator, UpdateCommand+Reporting, FeedUpdateProcessor+Fetch, and UpdateReport+JSONOutput
  3. Confirm CloudKitRecordOperating new methods have default implementations before stable 1.0

leogdion and others added 3 commits May 17, 2026 21:14
…ud 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>
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"
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"
@claude
Copy link
Copy Markdown

claude Bot commented May 17, 2026

Code Review — v1.0.0-beta.1

This PR accumulates ~25 commits across several sub-PRs and represents a substantial, well-designed release. The overall architecture is excellent. Notes below are in priority order.


Bugs / Logic Issues

1. Silent platform fall-through in Credentials+TokenManager.swift (lines 77–81, 117–120)

In makePublicWithS2SPreference:

if let s2s = serverToServer {
  if #available(macOS 11.0, iOS 14.0, tvOS 14.0, watchOS 7.0, *) {
    return try makeServerToServerManager(s2s)
  }
  // If #available fails, falls through silently — no error
}
if auth.required {
  throw CloudKitError.missingCredentials(...)

If a caller provides serverToServer credentials but the platform is below macOS 11.0 / iOS 14.0, the credentials are silently ignored and a misleading "expected serverToServer or apiAuth credentials" error is thrown. The same pattern appears in makePublicWithWebAuthPreference. Consider adding an else branch that throws a clearer "serverToServer requires macOS 11.0+ / iOS 14.0+" error, or at minimum document this in the PrivateKeyMaterial doc comment. Given that the supported platform floor in Package.swift should already enforce this, this may be a non-issue in practice — worth a quick check.


2. ZoneInfo silently substitutes "Unknown" for a missing zoneName (CloudKitService+ZoneOperations.swift:70, 139)

ZoneInfo(zoneName: zoneID.zoneName ?? "Unknown", ...)

If CloudKit returns a zone without a zoneName (e.g. after an API change), the record silently becomes ZoneInfo(zoneName: "Unknown") rather than being filtered out. The surrounding compactMap already handles nil zoneIDs — extend that guard to cover a nil zoneName:

guard let zoneName = zoneID.zoneName else { return nil }
return ZoneInfo(zoneName: zoneName, ...)

Security / Privacy

3. LoggingMiddleware logs raw request headers at debug level (LoggingMiddleware.swift:60)

logger.debug("   Headers: \(request.headerFields)")

CloudKit authentication headers (X-Apple-CloudKit-Request-Keyid, X-Apple-CloudKit-Request-SignedBody, etc.) will appear verbatim in debug logs. The rest of MistKit uses SecureLogging for redaction — this path bypasses it. At minimum, filter or redact Authorization / X-Apple-CloudKit-Request-* headers before logging:

let safeHeaders = request.headerFields.filter { !$0.name.rawName.hasPrefix("X-Apple-CloudKit-Request") }
logger.debug("   Headers: \(safeHeaders)")

4. LoggingMiddleware buffers up to 1 MB of response body in memory (LoggingMiddleware.swift:108)

let bodyData = try await Data(collecting: responseBody, upTo: 1_024 * 1_024)

This runs on every request when logLevel <= .debug, including large asset or sync responses. Consider limiting it to a smaller cap (e.g. 64 KB) or logging only when Content-Type is application/json.


API Design

5. Pre-call validation uses httpErrorWithRawResponse for client-side errors (CloudKitService+ZoneOperations.swift:106–116, CloudKitService+Operations.swift:158–170)

throw CloudKitError.httpErrorWithRawResponse(statusCode: 400, rawResponse: "zoneIDs cannot be empty")

No HTTP request was made, so reporting statusCode: 400 is misleading — callers checking error.httpStatusCode will see a synthetic 400. A dedicated CloudKitError.invalidArgument(String) case (or reusing the existing .unsupportedOperationType) would communicate "this is a caller mistake, not a server response" more clearly.


6. CloudKitResponseProcessor exhaustiveness (CloudKitResponseProcessor.swift)

The default: assertionFailure(...) pattern works but relies on CloudKitError(response) catching every non-OK case. If a new response variant is added to the OpenAPI spec, the assertionFailure path fires in debug and invalidResponse fires silently in release. An exhaustive switch without default: would make this a compile-time guarantee.


Positive Observations

  • Credentials / PublicAuthPreference / Database design — encoding auth preference in the Database.public associated value is clean and prevents misuse at the type level. Per-call token manager resolution from a single service is the right architecture.
  • throws(CloudKitError) throughout — typed throws is used consistently; no boxing overhead and callers get full type information.
  • paginationLimitExceeded carries accumulated records — allows callers to resume or surface partial results rather than discarding all work.
  • queryAllRecords stuck-marker guard — defends against infinite loops caused by a misbehaving server.
  • PrivateKeyMaterial.loadPEM() normalises \n — real-world env var PEM strings frequently use literal \n; good catch.
  • fetchCurrentUser() deprecated shim — forwards correctly to fetchCaller() with an actionable message.
  • discoverAllUserIdentities unavailability — the #if swift(>=6.2) gate for the @available(*, unavailable) cascade is clever and well-documented.
  • Test coverage — the test suite is comprehensive, with parameterised routing-matrix tests for credential resolution, concurrent token refresh, and full middleware coverage.

Summary

Two bugs worth fixing before merge (silent platform fall-through, "Unknown" zone name), one security item (header redaction in LoggingMiddleware), and a few API-design nits. The core credential architecture and pagination implementation are solid.

🤖 Reviewed by Claude Sonnet 4.6

Drops speaker-prep artifacts and blog-source files that shouldn't ship
in the repo, and polishes the remaining articles so they read as
release-ready reference docs.

Removed (preserved on branch docs/talk-prep-archive and tag
archive/talk-prep-2026-05-17, both pushed to origin before deletion):

- docs/talk-feedback.md — dry-run debrief notes
- docs/transcriptions/ — raw Riverside transcripts (5 files)
- docs/cloudkit-guide/articles/rebuilding-mistkit-claude-code-part-{1,2}.md
  — blog posts with brightdigit.com CMS frontmatter; the surviving
  articles now link to the canonical brightdigit.com URLs instead

Polished:

- deploying-mistkit-server-side.md: dropped DRAFT banner, removed all
  [VERIFY] markers (rewritten as hedged statements where load-bearing,
  removed where the surrounding prose already covered it), removed
  invalid featuredImage frontmatter
- authenticating-cloudkit-backend-services.md: same VERIFY-marker
  pass; fixed two broken [Celestra](github.com/brightdigit/Celestra)
  links to point at the actual CelestraCloud repo
- field-type-polymorphism.md: reframed "Known Gap" + "Future work"
  section as "Known Limitation" with descriptive design rationale,
  so it reads as reference documentation rather than a sprint plan
- cloudkit-guide/README.md: removed two empty ###### Web Application /
  ###### Background Job placeholder headers

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

claude Bot commented May 17, 2026

v1.0.0-beta.1 Code Review

This is a large, well-structured release. The credential-resolution refactor and new user-identity endpoints are solid work. Notes are ordered by severity.


Strengths

  • Per-call credential resolution (Credentials+TokenManager.swift) is elegant -- a single CloudKitService serves public-S2S record ops and public-web-auth user-identity routes without juggling multiple service instances. PublicAuthPreference (prefers/requires) gives callers fine-grained control at the call site.
  • Typed throws (throws(CloudKitError)) throughout the public API is exactly right for Swift 5.7+.
  • Sendable compliance on all public types is correct for a server-side Swift library.
  • discoverAllUserIdentities marked @available unavailable with a recoverable error instead of fatalError is the correct holding pattern while issue 28 is under investigation.
  • Stuck-marker/stuck-token detection in queryAllRecords and fetchAllRecordChanges guards against infinite loops on a misbehaving server.
  • Test coverage is thorough: 179 test files covering concurrent token refresh, middleware, credential routing matrix, and pagination edge cases.

Issues

Issue 1: Silent OS-version fallthrough gives a misleading error message
Credentials+TokenManager.swift lines 77-81 (and mirror at line 117)

When serverToServer is non-nil but the availability check fails (pre-macOS 11 / iOS 14), execution falls through and the error says credentials "are not configured" -- but they ARE configured, just unsupported on this platform. Consider a dedicated unsupportedPlatform error case or a corrected reason string that distinguishes "credentials absent" from "credentials present but platform too old".

Issue 2: Client-side validation uses httpErrorWithRawResponse
CloudKitService+Operations.swift lines 159-171

httpErrorWithRawResponse implies the error originated from an HTTP response. The empty-recordType and invalid-limit guards are local argument-validation failures; callers who inspect httpStatusCode will be misled. A dedicated invalidArgument(String) case would be more honest about the error origin.

Issue 3: Cooperative cancellation loses type information
CloudKitService+QueryPagination.swift lines 80-83, CloudKitService+SyncOperations.swift lines 186-189

Task.checkCancellation() errors are mapped through mapToCloudKitError, wrapping CancellationError inside CloudKitError.underlyingError(...). With typed throws, callers cannot detect cancellation without unwrapping. Consider rethrowing CancellationError before the mapping step, or adding a .cancelled case to CloudKitError.

Issue 4: AdaptiveTokenManager appears unused by the library path
Authentication/AdaptiveTokenManager.swift

Credentials+TokenManager.swift always constructs concrete managers (APITokenManager, WebAuthTokenManager, ServerToServerAuthManager) directly. AdaptiveTokenManager is public but never reached via Credentials. If it is intended for callers who hold a live reference and upgrade it after a web-auth flow, that use case should be documented. Otherwise this reads as dead public API before 1.0 stable.


Minor Nits

  • defaultQueryLimit not configurable (CloudKitService.swift line 70): It is a let with no way to configure it at construction. Minor ergonomics issue.
  • Import access modifier inconsistency: CloudKitService+Operations.swift uses bare import Foundation / import OpenAPIRuntime while most other files use internal import. Behavior is identical but inconsistent.
  • fetchAllRecordChanges returns a raw tuple (records: [RecordInfo], syncToken: String?). For consistency with QueryResult / RecordChangesResult, a named struct would be cleaner -- not a blocker for beta.
  • Deprecated queryRecords docstring is much longer than the replacement, creating noise in IDE autocomplete.

Summary

Issues 1 and 2 could confuse operators debugging auth failures in production. Issue 3 makes Structured Concurrency cancellation patterns awkward. Issue 4 risks shipping dead public API at 1.0. The rest is polish. Overall this is in good shape for beta; addressing issues 1-4 before 1.0 stable would be ideal.

@leogdion leogdion merged commit 9e1ad1d into main May 17, 2026
78 of 80 checks passed
@leogdion leogdion deleted the v1.0.0-beta.1 branch May 17, 2026 22:23
leogdion added a commit that referenced this pull request May 18, 2026
leogdion added a commit that referenced this pull request May 18, 2026
leogdion added a commit that referenced this pull request May 18, 2026
commit de82483
Author: Leo Dion <leogdion@brightdigit.com>
Date:   Sun May 17 21:14:35 2026 +0100

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

    Fixed Nonisolated Web Auth Token (#347)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Addresses all four review threads on PR #315:

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

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

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

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

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

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

    Resolves the architectural feedback in the PR-315 review:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ---------

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

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

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

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

    Add query pagination support with continuation markers (#306)

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

    updating example packages

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

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

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

    Strengthen environment and database configuration validation (#293)

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

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

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

    Move CloudKitResponseType default implementations to protocol extension (#292)

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

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

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

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

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

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

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

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

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

    CI Updates for May 2026 (#277)

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

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

    Fixed Nonisolated Web Auth Token (#347)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Addresses all four review threads on PR #315:

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

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

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

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

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

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

    Resolves the architectural feedback in the PR-315 review:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ---------

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

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

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

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

    Add query pagination support with continuation markers (#306)

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

    updating example packages

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

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

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

    Strengthen environment and database configuration validation (#293)

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

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

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

    Move CloudKitResponseType default implementations to protocol extension (#292)

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

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

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

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

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

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

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

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

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

    CI Updates for May 2026 (#277)

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

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

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

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

    Fixed Nonisolated Web Auth Token (#347)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    Addresses all four review threads on PR #315:

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

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

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

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

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

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

    Resolves the architectural feedback in the PR-315 review:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    ---------

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

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

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

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

    Add query pagination support with continuation markers (#306)

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

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

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

    git subrepo push Examples/CelestraCloud

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

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

    git subrepo push Examples/BushelCloud

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

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

    updating example packages

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

    Refactor AuthenticationMiddleware so each Authenticator applies itself (#294)

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

    Strengthen environment and database configuration validation (#293)

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

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

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

    Move CloudKitResponseType default implementations to protocol extension (#292)

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

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

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

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

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

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

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

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

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

    CI Updates for May 2026 (#277)

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

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

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

    First Draft Revision of Docs (#268)
@claude claude Bot mentioned this pull request May 19, 2026
@claude claude Bot mentioned this pull request May 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants