Skip to content

v1.0.0-beta.1#277

Draft
leogdion wants to merge 42 commits intov1.0.0-beta.1from
276-ci-updates
Draft

v1.0.0-beta.1#277
leogdion wants to merge 42 commits intov1.0.0-beta.1from
276-ci-updates

Conversation

@leogdion
Copy link
Copy Markdown
Member

@leogdion leogdion commented May 2, 2026

Summary

First public beta of MistKit 1.0. Bundles the CI overhaul tracked in #276 with the MistDemo, docs, and subrepo work that's accumulated since 1.0.0-alpha.5 (#263).

CI overhaul (closes #276, #239, #246, #249, #251, #252)

  • Dynamic matrix in MistKit.yml (Optimize CI: dynamic matrix, concurrency, path filtering, and move CodeQL to Linux #249): a new configure job decides whether to run the full matrix (only on main, semver branches, and PRs targeting them) or a 3-job reduced matrix on feature branches. Cuts feature-branch jobs from 20+ to ~3. Adds path filtering and concurrency cancellation.
  • Move CodeQL to Linux attempted (Optimize CI: dynamic matrix, concurrency, path filtering, and move CodeQL to Linux #249) but reverted with a comment: GitHub's CodeQL action errors out with "Swift analysis is only supported on macOS runner images." Restored the macOS-15 runner for the Swift language entry.
  • Branch cache cleanup (Optimize CI: dynamic matrix, concurrency, path filtering, and move CodeQL to Linux #249): new cleanup-caches.yml deletes Actions caches when a branch is deleted.
  • MistDemo workflow (Add comprehensive CI workflow for MistDemo example #239): new .github/workflows/MistDemo.yml at the repo root (MistDemo is in-repo, not a subrepo) covering Ubuntu noble × Swift 6.2/6.3 plus macOS-26 + Xcode 26.4 SPM, with Codecov flag mistdemo-*.
  • Swift Source Compatibility (Add Swift Source Compatability #246): existing swift-source-compat.yml already satisfied this; aligned its matrix to stable-only [6.1, 6.2, 6.3] (no nightly).
  • Shared setup-mistkit (Update BushelCloud and CelestraCloud to reference shared setup-mistkit action from MistKit #251): BushelCloud and CelestraCloud workflows now reference brightdigit/MistKit/.github/actions/setup-mistkit@main with MISTKIT_BRANCH=main instead of duplicating the action locally; the per-subrepo copies under .github/actions/setup-mistkit/ were deleted. Subrepos pushed upstream to brightdigit/{Bushel,Celestra}Cloud:mistkit.
  • Action / Swift / Xcode bumps (Update Actions/Swift/Xcode to the Latest #252):
    • brightdigit/swift-build@v1 (major-version floating)
    • codecov/codecov-action@v6, sersoft-gmbh/swift-coverage-action@v5
    • actions/checkout@v6, actions/cache@v5, actions/github-script@v9, jlumbroso/free-disk-space@v1.3.1
    • Xcode and Apple OS pins → 26.4
    • Swift matrix is stable-only: Ubuntu [6.1, 6.2, 6.3] (subrepos: [6.2, 6.3] because their Package.swift declares 6.2), Windows [6.1, 6.2, 6.3]-RELEASE, Android [6.1, 6.2, 6.3]. No nightly toolchains anywhere.
  • visionOS pin dropped: visionOS 26.4 simulator isn't pre-installed on macos-26 runners (watchOS/tvOS/iOS 26.4 are); xcodebuild now auto-selects the latest installed runtime.

Bundled in this release branch

Test plan

  • Push to a feature branch (other than this one) and confirm MistKit.yml runs only configure + 1 Ubuntu + 2 macOS (no Windows / Android / build-macos-platforms)
  • Confirm this PR (targeting main) fires the full matrix including Windows, Android, and build-macos-platforms
  • Confirm CodeQL succeeds on macos-15
  • Confirm MistDemo.yml runs on push to this branch and on PRs touching Examples/MistDemo/**
  • Confirm cleanup-caches.yml registers in gh workflow list (won't fire until a branch is deleted)
  • Confirm BushelCloud upstream CI on mistkit branch resolves MistKit from the shared setup-mistkit@main action and passes
  • Confirm CelestraCloud upstream CI on mistkit branch passes the same way
  • Manually delete a stale branch and confirm cleanup-caches.yml deletes its caches

🤖 Generated with Claude Code


Perform an AI-assisted review on CodePeer.com

leogdion and others added 9 commits May 1, 2026 20:01
…239, #246, #249, #252)

Reworks main-repo CI per #249: gate heavy matrices behind main / semver
branches / PRs targeting them, cutting feature-branch jobs from ~20+ to ~3.
Move CodeQL from macOS to Linux. Add cleanup-caches.yml that wipes Actions
caches on branch deletion. Add a dedicated MistDemo workflow at the repo
root (#239); MistDemo is in-repo so a workflow inside Examples/MistDemo
would not be honored. Bump every action and Swift/Xcode pin (#252):
brightdigit/swift-build@v1, codecov/codecov-action@v6,
sersoft-gmbh/swift-coverage-action@v5, actions/checkout@v6, actions/cache@v5,
actions/github-script@v9, jlumbroso/free-disk-space@v1.3.1; Xcode 26.4 and
Apple OSes 26.4; Swift matrix now stable-only [6.1, 6.2, 6.3] with the
latest stable as the reduced-matrix entry. swift-source-compat.yml drops
its nightly entry and aligns to [6.1, 6.2, 6.3] — closes #246 (existing
workflow is sufficient).

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

Replace the local ./.github/actions/setup-mistkit reference with the shared
brightdigit/MistKit/.github/actions/setup-mistkit@main, threaded through a
new MISTKIT_BRANCH env (default main) so the subrepo CI can resolve MistKit
when run standalone. Bumps brightdigit/swift-build@v1.4.2 to @v1, codecov
to v6, swift-coverage-action to v5, checkout to v6, cache to v5. Updates
Xcode/Apple OS pins to 26.4 and adds Swift 6.1 alongside 6.2 and 6.3 in
the Ubuntu matrix.

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

Same updates as BushelCloud: switch to brightdigit/MistKit/.github/actions/setup-mistkit@main
with MISTKIT_BRANCH env, bump brightdigit/swift-build to @v1, codecov to v6,
swift-coverage-action to v5, checkout to v6, cache to v5. Update Xcode/Apple OS
pins to 26.4 and add Swift 6.1 alongside 6.2 and 6.3 in the Ubuntu matrix.
Windows matrix moves to 6.3-RELEASE.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "1ab86a8"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "1ab86a8"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "eaf26e3"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "eaf26e3"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
Issue #249's premise that CodeQL could move to Linux was incorrect:
the CodeQL action errors with "Swift analysis is only supported on macOS
runner images" on ubuntu-latest. Restore the conditional runs-on so
Swift uses macos-15 (the language-supported runner), preserving the
ability for any future non-Swift language entry to use Linux. Adds
a comment so this constraint isn't re-discovered.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…epos, drop visionOS pin

- BushelCloud and CelestraCloud Package.swift declare swift-tools-version: 6.2,
  so Swift 6.1 builds errored with "package is using Swift tools version 6.2.0
  but the installed version is 6.1.3". Drop 6.1 from both subrepo Ubuntu
  matrices (per project guidance: keep 6.1 only where it actually compiles)
  and add fail-fast: false so one Ubuntu failure stops cancelling siblings.
- visionOS 26.4 simulator is not pre-installed on macos-26 runners, while
  watchOS 26.4 / tvOS 26.4 / iOS 26.4 are. Drop the explicit visionOS
  osVersion in MistKit.yml and both subrepo workflows so xcodebuild
  auto-selects the latest installed runtime — survives runner image refreshes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "85acc20"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "85acc20"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "47beae9"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "47beae9"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@leogdion leogdion linked an issue May 2, 2026 that may be closed by this pull request
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 2, 2026

Important

Review skipped

Draft detected.

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

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d54bad37-48a7-4a13-bb81-ca217ea7f904

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 276-ci-updates

Tip

💬 Introducing Slack Agent: Turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get your free trial and get 200 agent minutes per Slack user (a $50 value).


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 2, 2026

Codecov Report

❌ Patch coverage is 37.92144% with 2007 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.80%. Comparing base (2c3b43e) to head (556ce65).

Files with missing lines Patch % Lines
...rces/MistDemoKit/Commands/UploadAssetCommand.swift 0.00% 117 Missing ⚠️
...stDemoKit/Protocols/OutputFormatting+Records.swift 0.00% 107 Missing ⚠️
...ources/MistDemoKit/Commands/AuthTokenCommand.swift 16.53% 106 Missing ⚠️
...Integration/IntegrationTestRunner+SyncPhases.swift 0.00% 102 Missing ⚠️
...Kit/Integration/IntegrationTestRunner+Phases.swift 0.00% 94 Missing ⚠️
...mo/Sources/MistDemoKit/Commands/QueryCommand.swift 2.59% 75 Missing ⚠️
...urces/MistDemoKit/Configuration/UpdateConfig.swift 10.84% 74 Missing ⚠️
...ources/MistDemoKit/Configuration/QueryConfig.swift 13.92% 68 Missing ⚠️
.../MistDemo/Sources/MistDemoKit/MistDemoRunner.swift 0.00% 68 Missing ⚠️
...ces/MistDemoKit/Commands/DemoInFilterCommand.swift 0.00% 67 Missing ⚠️
... and 55 more
Additional details and impacted files
@@                Coverage Diff                 @@
##           v1.0.0-beta.1     #277       +/-   ##
==================================================
+ Coverage          25.66%   53.80%   +28.13%     
==================================================
  Files                 95      244      +149     
  Lines               8240    18066     +9826     
==================================================
+ Hits                2115     9720     +7605     
- Misses              6125     8346     +2221     
Flag Coverage Δ
mistdemo-spm-macos 43.24% <37.96%> (?)
mistdemo-swift-6.2-jammy 43.24% <37.94%> (?)
mistdemo-swift-6.2-noble 43.24% <37.94%> (?)
mistdemo-swift-6.3-jammy 43.24% <37.94%> (?)
mistdemo-swift-6.3-noble 43.24% <37.94%> (?)
spm 25.53% <ø> (-0.12%) ⬇️
swift-6.1-jammy 25.58% <ø> (+0.03%) ⬆️
swift-6.1-noble 25.53% <ø> (-0.04%) ⬇️
swift-6.2-jammy 25.55% <ø> (-0.11%) ⬇️
swift-6.2-noble 25.55% <ø> (-0.11%) ⬇️
swift-6.3-jammy 25.53% <ø> (?)
swift-6.3-jammynightly ?
swift-6.3-noble 25.53% <ø> (?)
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.

leogdion and others added 4 commits May 1, 2026 20:18
Two issues from the full-matrix CI run on PR #277:

- actions/checkout@v6 fails inside Linux Swift Docker containers with
  "The following required dependencies are missing: curl" — the swift:6.x
  images are minimal and don't ship curl that v6+ requires. v4 supports
  Node 20 and works in those containers. Reverted across all workflows.
  This walks back part of #252 but the bump wasn't load-bearing.

- visionOS 26.4 simulator is not pre-installed on macos-26 runners
  (only iOS / watchOS / tvOS 26.x ship), and -downloadPlatform visionOS
  does not reliably populate it. swift-build builds an
  -destination "...,OS=<value>" string that fails with "missing value
  for key 'OS'" when osVersion is empty, so dropping the pin alone
  doesn't fix it. Comment out the visionOS matrix entries in
  MistKit.yml and both subrepo workflows with a TODO to re-enable
  once the runner image catches up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "88c535f"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "88c535f"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "0de629a"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "0de629a"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
Don't track .claude/scheduled_tasks.lock — it's a session-local lock
file written by the Claude Code harness (contains pid + sessionId)
and was accidentally committed in the previous CI fixups commit.

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

claude Bot commented May 2, 2026

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

This is a large, well-structured release PR. The core library changes are small and correct; the bulk of the diff is CI modernization and MistDemo expansion. Review organized by area.


Core Library (Sources/MistKit/)

CloudKitError — httpStatusCode property

The new computed property is clean and exhaustively switches over all cases. Using it downstream in DeleteCommand.mapConflict is the right pattern — avoids duplicating status-code extraction logic across callers. No issues.

modifyRecords(atomic:) parameter

Good addition. Default false preserves backward compatibility. The doc comment accurately describes the semantics.


CI / Workflows

Dynamic matrix in MistKit.yml ✅ with one concern

The configure job approach is solid — single source of truth for full vs. reduced matrix, and the regex for semver branches (^refs/heads/v?[0-9]+\.[0-9]+\.[0-9]+) is correct.

Potential bug — Swift 6.3 WASM exclusion removed:
The old matrix explicitly excluded the nightly 6.3 toolchain from WASM builds:

- swift: { version: "6.3", nightly: true }
  type: "wasm"

Those excludes are gone. Swift 6.3 stable is now included in the full matrix without WASM exclusion. If WASM support is confirmed for Swift 6.3 stable, this is intentional and fine. If not, the full-matrix WASM build will fail on 6.3. Worth verifying before merging.

cleanup-caches.yml — pagination gap:
getActionsCacheList is paginated (default page size 30). Branches with > 30 cache entries (common on busy feature branches with many matrix jobs) won't have all caches cleaned. Consider iterating total_count:

// After the initial fetch, check caches.data.total_count vs
// caches.data.actions_caches.length and paginate if needed

Also: the delete event fires on tag deletion too. Constructing refs/heads/${context.payload.ref} will produce a nonexistent ref for tags, resulting in 0 results — harmless, but a guard if (context.payload.ref_type !== 'branch') return; would make the intent explicit.

setup-mistkit@main — mutable ref pin:
Both subrepos now reference brightdigit/MistKit/.github/actions/setup-mistkit@main (floating). A push to main immediately affects subrepo CI. For an internal project this is probably fine, but pinning to a commit SHA is the more secure pattern if supply-chain risk is a concern.

actions/checkout version inconsistency:
The PR description says checkout is bumped to @v6, but the actual diffs retain @v4 in MistKit.yml, BushelCloud.yml, CelestraCloud.yml, and codeql.yml. Only cleanup-caches.yml uses @v9 (for github-script). If the intent was a uniform version bump, those should be updated to match.

codeql.yml — macOS-only Swift guard

Adding if: matrix.language == 'swift' to the Xcode-setup and Swift-verify steps is the correct fix. The inline comment explaining why Linux doesn't work is a good callout for future maintainers.

swift-source-compat.yml — nightly removal

Dropping continue-on-error: true alongside the nightly toolchain is correct — the flag was there specifically to tolerate nightly failures.


MistDemo — Target Split (MistDemoMistDemoKit)

Extracting the CLI logic into a testable library target is a good architectural call. All @testable import MistDemo@testable import MistDemoKit updates appear consistent.

Minor: Field.swift and FieldType.swift drop public import Foundation in favour of plain import Foundation. Correct — Foundation types aren't re-exported from these modules and public import was adding unnecessary coupling.


New Commands

DeleteCommand

  • force semantics (omit change tag → server skips optimistic locking check) are correctly documented and implemented.
  • mapConflict is a good pattern — keeps HTTP-code awareness out of execute().
  • Tests cover the conflict mapping logic across all three CloudKitError variants and all non-409 codes. Solid.

ModifyCommandpartialFailure field:
ModifyOutput.partialFailure is computed, but it's unclear how a partial failure can occur at the Swift layer. When atomic: false, CloudKit can succeed on some operations and fail on others, but the current modifyRecords implementation throws on any error and returns [RecordInfo] on success. If the implementation compares results.count < attempted to detect partial success, that logic is correct — but the check should be documented in a comment so it's obvious why succeeded might be less than attempted.

LookupCommand — appears complete from the visible diff; no issues spotted.


MistDemoApp (Native SwiftUI)

NativeCloudKitService

  • updateNote correctly fetches the current record before mutating (fresh change tag). Good.
  • queryNotes accumulates individual record failures into lastError without throwing. This is appropriate for UI resilience but means silently skipped records are only surfaced as a string message. If partial failures should ever block the UI, consider also returning the failed count as a structured value alongside [Note].
  • refreshAccountStatus silently sets accountStatus = .couldNotDetermine on error — acceptable for this demo context.

NoteEditView — security-scoped URL lifecycle:
The scopedURL state variable with releaseScopedURL() is a correct pattern for balancing startAccessingSecurityScopedResource / stopAccessingSecurityScopedResource across the view's lifetime. Well handled.


Minor / Nits

  • DeleteConfig has a redundant double-unwrap: configReader.string(forKey:default:) ?? MistDemoConstants.Defaults.zone. If the default: parameter is supposed to guarantee a non-nil return, the trailing ?? is dead code. If it can return nil despite the default: argument, the API contract is confusing. Either fix the API or remove the trailing ??.
  • The .claude/scheduled_tasks.lock .gitignore entry belongs in the .gitignore under a CI section rather than at the bottom of the file, but this is cosmetic.

Summary

Area Status
Core library changes ✅ Clean
Dynamic CI matrix ✅ with WASM 6.3 to verify
Cache cleanup workflow ⚠️ Pagination gap + tag guard
Subrepo action pin ⚠️ Floating @main ref
actions/checkout bump ⚠️ Inconsistent with PR description
MistDemo target split
New commands ✅ well-tested
Native SwiftUI app

The blocking item before merging is confirming whether Swift 6.3 stable supports WASM (to decide if the exclusion removal was intentional). The cache-cleanup pagination and checkout version inconsistency are lower priority but worth cleaning up.

🤖 Generated with Claude Code

@claude
Copy link
Copy Markdown

claude Bot commented May 2, 2026

Code Review — v1.0.0-beta.1

Overview

This PR bundles three largely independent efforts: a CI overhaul (dynamic matrix, action version bumps, new workflows), a MistDemo module restructuring (new MistDemoKit library target + native SwiftUI app), and small MistKit API additions (atomic parameter, httpStatusCode on CloudKitError). The bulk of the diff is file renames from Sources/MistDemo/Sources/MistDemoKit/, which is the right architectural move.


Issues

Bug — DeleteConfig.zone is silently ignored

DeleteConfig collects a --zone flag and stores it, but DeleteCommand.execute() never passes it to client.deleteRecord():

try await client.deleteRecord(
    recordType: config.recordType,
    recordName: config.recordName,
    recordChangeTag: effectiveChangeTag
    // config.zone is nowhere here
)

The underlying deleteRecord(recordType:recordName:recordChangeTag:) also has no zone parameter. A user who passes --zone myZone will get a silent no-op on the zone. Options:

  • Remove zone from DeleteConfig and its help text until zone support is implemented.
  • Add zone support to deleteRecord (and the underlying RecordOperation.deletemodifyRecords path) and wire it through.

The help text currently lists --zone as a valid option, which is misleading.


Style — Action version inconsistency

The PR description says actions/checkout was bumped to @v6, and that is applied in MistKit.yml, BushelCloud.yml, and CelestraCloud.yml. But two files still use @v4:

  • .github/workflows/MistDemo.yml (new file) — uses actions/checkout@v4
  • .github/workflows/codeql.yml (modified file) — uses actions/checkout@v4

These should be @v6 for consistency.


Nit — Redundant catch clause in DeleteCommand.execute()

} catch let error as DeleteError {
    throw error   // re-throw without modification
} catch let error as CloudKitError {
    ...
}

The first catch is dead weight — Swift's type system will naturally propagate DeleteError through the outer throws without an explicit re-throw. Drop it.


Nit — public import style inconsistency

This PR removes public import Foundation / public import MistKit from several existing files (e.g. FieldConversionError.swift, MistDemoError.swift), which is correct — public import re-exports the module, which is rarely what you want. But the new files use it:

  • DeleteError.swift: public import Foundation
  • LookupError.swift: public import Foundation
  • DeleteConfig.swift: public import ConfigKeyKit
  • CloudKitCommand.swift: public import MistKit

CloudKitCommand re-exporting MistKit may be intentional (callers need MistKit.Environment), but the Foundation re-exports in error files look unintentional given the cleanup applied elsewhere.


Minor — examples.yml drops Swift 6.2

The examples workflow previously ran on swift:6.2; this PR bumps it to swift:6.3 only. The main MistKit.yml still covers 6.1–6.3 on the full matrix, but examples won't catch regressions on 6.2 anymore. Consider a [6.2, 6.3] matrix to match MistDemo.yml.


Positive Observations

  • isLoopbackAuthority() security fix — The old prefix check (host.hasPrefix("localhost")) was bypassable (e.g. localhost.evil.com). The new exact-match with IPv6 bracket support ([::1]) is correct.
  • MistDemoKit library split — Moving CLI logic out of the executable target into a testable library is the right call and directly enables the native SwiftUI app.
  • Dynamic matrix in MistKit.yml — Cutting feature-branch runs from 20+ to ~3 jobs is a meaningful CI cost reduction.
  • CloudKitError.httpStatusCode — Clean property, well pattern-matched across all three HTTP error cases. The DeleteCommand dependency on it (mapConflict) is appropriately testable.
  • modifyRecords(atomic:) — Good default (false). API-safe since it's a keyword argument; callers don't need to change.
  • visionOS disabled with explanation — The inline comment is detailed enough that the next person touching this file knows exactly what to check before re-enabling.
  • Branch cache cleanup workflow — Small, correct, welcome housekeeping.
  • Shared setup-mistkit@main action — Removing the duplicate per-subrepo copies is the right call; a single source of truth is easier to maintain.

Summary

The main actionable items before merge:

  1. Fix or remove DeleteConfig.zone (it's collected but never used).
  2. Bump actions/checkout to @v6 in MistDemo.yml and codeql.yml.
  3. Drop the redundant DeleteError catch clause.
  4. Align public import usage in new error/config files.

🤖 Generated with Claude Code

leogdion and others added 7 commits May 1, 2026 20:33
- Bump actions/checkout back to @v6 across all workflows.
- Install curl + ca-certificates as the first run-step in every Linux-
  container Ubuntu job (MistKit, MistDemo, BushelCloud, CelestraCloud).
  The codecov uploader requires curl, and the swift:6.x Docker images
  on Noble/Jammy don't include it by default. Guarded by `apt-get`
  presence so it's a no-op outside Debian/Ubuntu containers.
- Re-enable the visionOS matrix entry in MistKit.yml, BushelCloud, and
  CelestraCloud with osVersion: "26.4" and download-platform: true so
  xcodebuild fetches the visionOS Simulator runtime when it's missing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "656ff6e"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "656ff6e"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "194ae80"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "194ae80"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
Same fix as the other Linux container workflows — codecov uploader
needs curl, swift:6.x-noble doesn't ship it. This step was missed in
the previous round when the lint reformat updated MistDemo.yml.

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

Move the "Install curl" step to after swift-build and gate it on
steps.build.outputs.contains-code-coverage so the apt-get only runs when
coverage is actually being uploaded. For BushelCloud and CelestraCloud,
add `id: build` to swift-build and gate the existing
sersoft-gmbh/swift-coverage-action and codecov-action steps on the same
output (matches the MistKit.yml pattern; previously they ran
unconditionally and would have errored on no-coverage configs).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "63a2cab"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "63a2cab"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "ca6d7a9"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "ca6d7a9"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@leogdion leogdion changed the base branch from main to v1.0.0-beta.1 May 2, 2026 17:33
Comment thread .github/workflows/codeql.yml Outdated

- name: Setup Xcode
if: matrix.language == 'swift'
run: sudo xcode-select -s /Applications/Xcode_16.4.app/Contents/Developer
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upgrade to Xcode 26.4

Comment thread .github/workflows/MistKit.yml Outdated
Comment on lines +232 to +327
Mint
Mint
key: ${{ runner.os }}-mint-${{ hashFiles('**/Mintfile') }}
restore-keys: |
${{ runner.os }}-mint-
${{ runner.os }}-mint-
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transition to mise from Mint

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

did we transition to the new format here too?

Comment thread Examples/BushelCloud/.github/workflows/BushelCloud.yml
leogdion and others added 3 commits May 2, 2026 17:56
…bumps

- Migrate Mint → mise (jdx/mise-action@v4) across MistKit, BushelCloud,
  CelestraCloud. swift-format and swift-openapi-generator via spm: backend
  (experimental); SwiftLint and Periphery via aqua: backend.
- Rewrite Examples/{Bushel,Celestra}Cloud workflows to mirror MistKit.yml's
  configure + build-macos / build-macos-platforms split with paths-ignore and
  concurrency. BushelCloud Windows job stays commented out.
- Fix Examples/CelestraCloud/.github/workflows/update-feeds.yml to use the
  shared brightdigit/MistKit/.github/actions/setup-mistkit@main instead of
  the deleted local action path.
- codeql.yml: move Swift analysis to macos-26 + Xcode 26.4.
- Bump iOS and visionOS simulator osVersion to 26.4.1 (watchOS / tvOS stay
  at 26.4; Xcode app pin unchanged).
- Revert actions/checkout@v6 → @v4 in claude.yml and claude-code-review.yml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "69ff754"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "69ff754"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "2a04e63"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "2a04e63"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@claude
Copy link
Copy Markdown

claude Bot commented May 2, 2026

Code Review: v1.0.0-beta.1 (PR #277)

Overview

This is a substantial CI/CD overhaul bundling several improvements: dynamic matrix selection to cut feature-branch job counts, action version bumps, migration from Mint to mise for tool management, a new MistDemo workflow, a cache-cleanup workflow, and subrepo consolidation of the setup-mistkit action. The changes are well-structured and clearly motivated. Below is a focused review of issues worth addressing before merging.


Bugs / Correctness

cleanup-caches.yml — missing pagination

getActionsCacheList is paginated (default 30 items per page). If a branch has accumulated more than 30 caches, the extra ones will be silently left behind. Add per_page: 100 and a loop or use the paginate helper:

const caches = await github.paginate(github.rest.actions.getActionsCacheList, {
  owner: context.repo.owner,
  repo: context.repo.repo,
  ref: ref,
  per_page: 100,
});
for (const cache of caches) { ... }

cleanup-caches.yml — no guard for tag deletions

The delete event fires for both branches and tags. context.payload.ref_type will be "tag" for tag deletions and there are no branch caches to clean up. Adding a guard prevents unnecessary API calls and noise:

- name: Cleanup caches for deleted branch
  if: github.event.ref_type == 'branch'
  uses: actions/github-script@v9

Swift 6.3 wasm exclusion removed — needs validation

The old matrix excluded both wasm and wasm-embedded builds for Swift 6.3 nightly. Those exclusions are now gone, meaning 6.3 stable will run wasm builds. If the Swift 6.3 wasm SDK is not yet stable/available for the noble container this will silently start failing. Worth a confirm before beta.1 ships.


Code Quality / Maintainability

configure job logic is copy-pasted three times

The branch-to-matrix decision logic is duplicated verbatim in MistKit.yml, BushelCloud.yml, and CelestraCloud.yml. A reusable workflow (.github/workflows/configure-matrix.yml) would eliminate the duplication and make future changes a single-file edit. This is not a blocking issue for beta.1, but it's a maintenance risk as the matrix grows.

mise.toml uses experimental = true

The spm: and aqua: backends are flagged experimental. This is fine for developer machines but means CI could break on future mise version bumps if the experimental API changes. Document why experimental is required (these backends are the only way to pin SPM-distributed tools and aqua-distributed binaries), or evaluate if any backends have since graduated to stable.


Consistency / Style

build-macos in MistDemo.yml — missing fail_ci_if_error and verbose

The Ubuntu MistDemo coverage upload includes fail_ci_if_error: true and verbose: true; the macOS one omits both. For consistency:

- name: Upload Coverage
  uses: codecov/codecov-action@v6
  with:
    token: ${{ secrets.CODECOV_TOKEN }}
    flags: mistdemo-spm-macos
    fail_ci_if_error: true
    verbose: true

build-ubuntu (BushelCloud) — missing fail-fast: false

BushelCloud's build-ubuntu adds fail-fast: false, but the equivalent build-macos-platforms job in BushelCloud (and CelestraCloud) relies on it through the outer strategy — verify that all matrix jobs that should not short-circuit each other have fail-fast: false.

lint dependency on build-macos-platforms (optional job)

In MistKit.yml, lint lists build-macos-platforms as a needs dependency. Since that job is conditional (full-matrix == 'true'), it is skipped on feature branches. The !cancelled() && !failure() guard correctly handles skipped jobs, so this is not a bug — but it is non-obvious and worth a comment in the workflow YAML for the next person who reads it.


Security

Floating major version tags (@v1, @v6, @v9, @v4)

Pinning actions to a commit SHA is the hardened practice for supply-chain security. Floating major tags (actions/checkout@v6, jdx/mise-action@v4, etc.) are at the mercy of whoever controls the tag. For a public library this is a judgment call, but worth being intentional about. If pinning by SHA is out of scope for this PR, at minimum document that decision in a security policy.

cleanup-caches.yml permission scope

The workflow correctly uses the minimal actions: write permission and does not grant contents: write or id-token: write. Good.


Minor Nits

  • MistDemo.yml build-macos: No version matrix (just one Xcode/runner). This appears intentional for a CLI tool, but a brief comment would confirm it's deliberate.
  • codeql.yml: Setup Xcode and Verify Swift Version steps now correctly guard on matrix.language == 'swift' — good catch.
  • Trailing whitespace removed throughout — appreciated.
  • generate-openapi.sh now has a newline at EOF — previously missing.

Summary

The overall direction is sound: the dynamic matrix design is clever, the mise migration cleans up a lot of OS-detection boilerplate, and consolidating setup-mistkit to a single canonical location is the right call. The two items most worth fixing before beta.1 are the cache pagination bug and confirming wasm/6.3 compatibility. The rest are polish that can follow in a subsequent patch.

🤖 Generated with Claude Code

@claude
Copy link
Copy Markdown

claude Bot commented May 2, 2026

Code Review: v1.0.0-beta.1 CI Overhaul

This is a well-structured CI refactor with clear benefits. Here's my analysis:


Overview

This PR bundles a significant CI/CD overhaul with MistDemo and docs improvements. The core CI changes are:

  • Dynamic matrix (cuts feature-branch jobs from 20+ to ~3)
  • Mint → mise migration
  • Shared setup-mistkit action across subrepos
  • Action/Swift/Xcode version bumps
  • New cleanup-caches.yml and MistDemo.yml workflows

Issues Found

🔴 Potential Bug: jdx/mise-action@v4 may not exist

mise-action is currently released at v2. Using @v4 in all three workflows (MistKit.yml, BushelCloud.yml, CelestraCloud.yml) will likely fail at runtime with a "not found" error.

- uses: jdx/mise-action@v4   # ← currently only v2 exists
  with:
    cache: true

Suggest checking the mise-action releases and pinning to the current stable tag (likely @v2).


🟡 github.event.head_commit is null on pull_request events

Several if: conditions use:

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

github.event.head_commit is only populated for push events, not pull_request events. On PRs, this expression evaluates to !contains(null, 'ci skip') which returns true (the job always runs), so there's no functional breakage — but if you want [ci skip] in a PR commit message to suppress CI, this silently stops working. Consistent alternatives:

if: ${{ !contains(github.event.commits[0].message || '', 'ci skip') }}
# or simply drop the check for PR triggers and rely on concurrency cancellation

🟡 cleanup-caches.yml missing pagination

getActionsCacheList defaults to 30 items per page. If a branch accumulated more than 30 caches, only the first batch would be deleted:

const caches = await github.rest.actions.getActionsCacheList({ ... });
for (const cache of caches.data.actions_caches) { ... }

A defensive fix adds per_page: 100 (the API max) or a paginator loop. For most branches 30 will be sufficient, but a long-lived feature branch could exceed it.


🟡 generate-openapi.sh silently continues if mise is not installed

The old script exited with an error on unsupported OS. The new script skips the PATH setup and falls through to swift-openapi-generator generate, which will produce an unhelpful "command not found" error:

if command -v mise >/dev/null 2>&1; then
    eval "$(mise -C "$PACKAGE_DIR" env -s bash)"
fi
# no else → silently skips mise activation

Consider adding a warning or checking that swift-openapi-generator is available before running it.


🟡 Security: curl https://mise.run | sh in bootstrap.sh

Piping curl to sh without checksum verification is a known supply-chain risk pattern. This is common for dev tools, but worth noting for a security-conscious project. Homebrew path already handles verification, so this only affects the non-Homebrew fallback path.


Good Changes

  • Dynamic matrix — the configure job pattern is clean and well-commented. The reduced matrix (3 jobs on feature branches) is a meaningful CI cost reduction.
  • cancel-in-progress: true — added to all workflows; prevents queued redundant builds on force-push.
  • Path filters — skipping CI on **.md / docs/** / LICENSE is a sensible optimization.
  • Coverage gating — wrapping all coverage steps in if: steps.build.outputs.contains-code-coverage == 'true' is correct and was missing before.
  • Removing nightly toolchains — stabilizes the matrix significantly; nightly builds were a source of flakiness.
  • Shared setup-mistkit@main — eliminates the per-subrepo action copies, good DRY improvement.
  • Mint → mise — mise is more actively maintained and handles more tool backends. Using experimental = true in mise.toml is required for spm: tools and is documented.
  • Swift 6.3 added to Windows, Android, and subrepo matrices — keeps parity.
  • CodeQL if: matrix.language == 'swift' gate on the Xcode/Swift verification steps is a clean fix.
  • Trailing whitespace cleanup throughout — appreciated.
  • Missing newline at end of generate-openapi.sh fixed.

Minor Observations

  • lint jobs use if: ${{ !cancelled() && !failure() }} which correctly handles skipped matrix jobs (skipped ≠ failure). This is the right pattern when some needs: jobs may be conditionally skipped.
  • build-macos intentionally omits needs: configure since it always runs — this is consistent with the PR description calling it "minimal macOS builds."
  • CelestraCloud Windows switched from dev snapshot (6.3-DEVELOPMENT-SNAPSHOT-2025-12-21-a) to stable 6.2/6.3-RELEASE. This is a better choice for reliability.
  • The visionOS osVersion: "26.4.1" vs watchOS/tvOS "26.4" difference is presumably intentional to match what's pre-installed on the runner.

Summary

The approach is sound and well-thought-out. The primary blocker is the jdx/mise-action@v4 version which should be verified before merging. The pull_request + head_commit issue is low-risk but worth fixing for correctness. Everything else is advisory.

🤖 Generated with Claude Code

leogdion and others added 4 commits May 3, 2026 11:00
Periphery 3.2.0 only supported macOS, causing the linting job to fail
on ubuntu-latest runners across MistKit, BushelCloud, and CelestraCloud.
Updated to 3.7.4 which adds Linux support.

MistDemoApp files now use #if canImport(CloudKit) and
#if canImport(SwiftUI) guards so the workspace auto-discovery on Linux
no longer fails on unavailable Apple frameworks.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…o and fix violations

- Sync lint.sh, header.sh, .swift-format, .swiftlint.yml, .periphery.yml across all 4 directories
- Add no_unchecked_sendable custom rule, one_declaration_per_file, type_name config to all .swiftlint.yml
- Create full linting setup for MistDemo (Scripts, configs, mise.toml)
- Update header.sh to heredoc template with % escape security fix
- Disable FileScopedDeclarationPrivacy in .swift-format (conflicts with swiftlint)
- Fix all swift-format and swiftlint violations across all 4 projects

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "45ac392"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "45ac392"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "8f81d54"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "8f81d54"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@claude
Copy link
Copy Markdown

claude Bot commented May 3, 2026

Code Review — PR #277 v1.0.0-beta.1

This is a substantial PR that lands the CI overhaul, MistDemo native app, docs, and the first public beta milestone. Overall the work is solid. A few issues worth addressing before merging.


Bugs / Blockers

claude-code-review.yml and claude.yml: missing pull-requests: write permission

Both workflow files declare pull-requests: read, but the review prompt explicitly instructs Claude to post feedback via gh pr comment. That call requires pull-requests: write. As-written, every automated review attempt will silently fail to post. Fix:

permissions:
  contents: read
  pull-requests: write  # needed for gh pr comment
  issues: read
  id-token: write

claude-code-review.yml: stale actions/checkout@v4

Every other workflow in this PR was bumped to @v6, but claude-code-review.yml was left on @v4. Inconsistency aside, the older version doesn't benefit from the Node 20 runtime used by @v6.


Medium Issues

Examples/MistDemo/Package.swift: .unsafeFlags() for standard Swift settings

The swiftSettings array uses .unsafeFlags() to pass concurrency settings that have first-class Swift Package Manager equivalents:

// ❌ Current — bypasses Swift's strict concurrency flag
.unsafeFlags(["-strict-concurrency=complete", "-warn-concurrency", ...])

// ✅ Better
.swiftLanguageMode(.v6)              // enables strict concurrency in Swift 6
// or for Swift 5 mode packages:
.enableUpcomingFeature("StrictConcurrency")

-enable-testing is also unnecessary for test targets — Swift PM handles that automatically. The -Xfrontend -warn-long-function-bodies=100 and -Xfrontend -warn-long-expression-type-checking=100 flags are fine as developer ergonomics tools, but they belong in a local .xcconfig rather than committed package settings that affect every consumer.

Note: check-unsafe-flags.yml only runs swift package dump-package on the root Package.swift, so this violation in Examples/MistDemo/Package.swift isn't caught by CI.

check-unsafe-flags.yml: unpinned swift:latest container

container:
  image: swift:latest   # ← non-deterministic

latest tracks the most recent published toolchain, meaning the same workflow can produce different results across runs and is hard to reason about in CI. Prefer a pinned version (swift:6.3) consistent with the rest of the matrix.


Minor Issues

cleanup-caches.yml: no pagination, also fires on tag deletions

getActionsCacheList returns at most 100 entries per call; branches with many cached OS/Swift combinations can exceed that. Worth adding a total_count check or looping pages.

The workflow triggers on the delete event, which fires for both branch and tag deletions. context.payload.ref_type can distinguish them:

if (context.payload.ref_type !== 'branch') return;

expirationTimeEdgeCases test: potential race condition

let exactExpiration = Date()
try await storage.store(credentials, identifier: "exact", expirationTime: exactExpiration)
let retrieved = try await storage.retrieve(identifier: "exact")
#expect(retrieved == nil)  // relies on Date() <= Date() at retrieval time

If store and retrieve happen in the same scheduler tick, exactExpiration might still be in the future. Use a date that is unambiguously in the past:

let exactExpiration = Date().addingTimeInterval(-0.001)

codeql.yml: misleading autobuild label

- run: |
    echo "Run, Build Application using script"
    swift build

The step description says "using script" but just runs swift build directly. Trivial, but the echo is noise — either remove it or update the message.

NoteEditView.swift: hardcoded minimum frame dimensions

.frame(minWidth: 420, minHeight: 360)

minWidth: 420 is a macOS idiom; on compact-width iOS layouts this forces horizontal scrolling. Consider gating it with #if os(macOS).


Positives

  • The dynamic matrix logic in configure is clean and well-commented. The regex ^refs/heads/v?[0-9]+\.[0-9]+\.[0-9]+ correctly handles both v1.0.0 and 1.0.0-style semver branches.
  • build-ubuntu correctly excludes Swift 6.1 from wasm builds via the matrix exclude: block.
  • NativeCloudKitService uses withCheckedThrowingContinuation correctly around CKFetchWebAuthTokenOperation, and the security-scoped resource bookkeeping in NoteEditView (releaseScopedURL balanced across file picker, Remove button, and onDisappear) is handled carefully.
  • The expiration and concurrency test coverage for InMemoryTokenStorage is comprehensive.
  • Documenting the CodeQL macOS-only constraint as an inline comment is a nice touch for future maintainers.

Summary: The two permission issues in the Claude workflows are blocking (the review bot can never post comments as configured). The unsafeFlags in MistDemo's Package.swift and the unpinned swift:latest in the unsafe-flags check are worth fixing before the beta tag, since they affect build reproducibility. The rest are polish items.

leogdion and others added 3 commits May 3, 2026 12:58
… head_ref

- bushel-cloud-build.yml: rewrite local MistKit path dep to remote URL so
  the standalone repo's swift build can locate Package.swift
- Workflows: group concurrency by github.head_ref || github.ref so PR runs
  cancel themselves rather than the target branch's pushes

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "e266858"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "e266858"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "a1fa226"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "a1fa226"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@claude
Copy link
Copy Markdown

claude Bot commented May 3, 2026

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

This is a large, multi-part PR. I've focused on the CI overhaul (the primary stated goal) and the new Swift source that ships alongside it.


CI / GitHub Actions

Dynamic matrix (MistKit.yml) — well-designed overall. The configure job cleanly separates scope decisions from execution, and the concurrency block prevents redundant runs.

Issues:

  • lint job needs skipped jobs: lint declares needs: [build-ubuntu, build-macos, build-macos-platforms, build-windows, build-android]. On feature branches build-macos-platforms, build-windows, and build-android are skipped (not failed). GitHub Actions treats skipped jobs as satisfied for needs, so lint will still run — but this dependency graph is misleading and fragile. Consider using if: always() or restricting lint's needs to only the jobs that always run (build-ubuntu, build-macos).

  • Inconsistent actions/checkout pin: claude-code-review.yml and claude.yml use actions/checkout@v4 while every other workflow uses @v6. Should be consistent.

  • check-unsafe-flags.yml uses swift:latest: Unpinned latest tags are non-reproducible. Use a pinned Swift version (e.g., swift:6.3-noble) matching the rest of the matrix.

  • Missing newline at end of claude-code-review.yml: Minor, but triggers no-newline-at-end-of-file in most linters.

  • codeql.yml runner: PR description says CodeQL was reverted to macos-15, but the file uses macos-26. If this is intentional (upgrading), the PR description should say so.

  • setup-mistkit/action.yml shell injection risk: The sed command interpolates ${{ inputs.branch }} directly into a bash string. While GitHub branch names can't ordinarily contain shell metacharacters, an adversarial workflow caller could supply a value like main' ; rm -rf . ; echo '. Consider using an env var instead:

    env:
      BRANCH: ${{ inputs.branch }}
    run: |
      sed -i 's|...|...|g' Package.swift
      # use $BRANCH instead of ${{ inputs.branch }}

Swift Source

SecureLogging vs MistKitLogger — env var mismatch (bug):

CLAUDE.md documents: "Set MISTKIT_DISABLE_LOG_REDACTION=1 to disable redaction for debugging" — implying redaction is on by default. MistKitLogger.isRedactionDisabled correctly checks MISTKIT_DISABLE_LOG_REDACTION. But SecureLogging.safeLogMessage() does the opposite:

// SecureLogging.swift — redaction is OFF by default here
guard ProcessInfo.processInfo.environment["MISTKIT_ENABLE_LOG_REDACTION"] != nil else {
    return message  // no redaction unless env var is set
}

These two code paths are inconsistent with each other and with the documentation. When MistKitLogger calls SecureLogging.safeLogMessage(), the net behavior is that redaction only fires if both env vars are absent/present in the right combination. Pick one env var and one default, and apply it uniformly.

MistKitClient third initializer — breaks protocol orientation:

if let serverManager = tokenManager as? ServerToServerAuthManager {
    keyID = serverManager.keyIdentifier
    privateKeyData = serverManager.privateKeyData
} else if let apiManager = tokenManager as? APITokenManager {
    apiToken = apiManager.token
}

Casting a protocol value to a concrete type in the library's own initializer is a design smell. Any custom TokenManager that wraps ServerToServerAuthManager won't be detected, and the configuration will be silently reconstructed with empty values. The MistKitConfiguration struct should hold only what it needs at construction time — if the token manager already has the credentials, there's no need to extract and re-inject them.

AuthenticationMiddleware.extractRequestBodyData — silent error swallowing:

do {
    return try await Data(collecting: body, upTo: 1_024 * 1_024)
} catch {
    return nil  // body too large or unreadable — silently ignored
}

For server-to-server auth this produces an incorrect signature (signed as if there was no body). A request body exceeding 1 MB to CloudKit is unlikely but the error should be propagated, not swallowed. At minimum, log the error.

InMemoryTokenStorage.count / isEmpty include expired tokens:

listIdentifiers() comment says "Return all stored identifiers, including expired ones", so count and isEmpty report stale entries. Callers checking isEmpty to decide whether to reauthenticate will get incorrect answers. Either fix listIdentifiers() to filter expired keys, or document the discrepancy clearly.

WebAuthTokenManager — empty deinit:

deinit {
    // Clean up any resources
}

Empty deinit is dead code and misleading. Remove it.

WebAuthTokenManager.tokenEncoder — unnecessary instance property:

CharacterMapEncoder is a stateless value type using a static let. It can be a local let inside addWebAuthTokenAuthentication in AuthenticationMiddleware or a static let on the middleware, not an instance-stored property per WebAuthTokenManager.

Over-commenting — violates CLAUDE.md conventions:

CLAUDE.md: "Default to writing no comments. Only add one when the WHY is non-obvious."

Most methods have detailed docstrings for things that are obvious from the signature. Examples:

  • "Creates a new in-memory token storage" on init() {}
  • "The API token value" on var token: String { apiToken }
  • "Checks if tokens are non-empty..." restating what the method body does

Strip these down to the non-obvious ones.


Test Coverage

The new test files are comprehensive and well-organized (split by concern, good use of @Test, parameterized tests). A few gaps:

  • No test covering the SecureLogging / MistKitLogger env var inconsistency described above.
  • No test for the extractRequestBodyData error path (body > 1 MB).
  • InMemoryTokenStorage expiration tests exist but no test verifying that count/isEmpty exclude expired tokens.

Summary

Severity Item
Bug SecureLogging and MistKitLogger use different env vars with opposite defaults
Bug extractRequestBodyData silently swallows body-read errors, producing wrong S2S signature
Design MistKitClient third init casts TokenManager to concrete types
Correctness InMemoryTokenStorage.count/isEmpty includes expired tokens
Security setup-mistkit/action.yml interpolates branch name directly into sed
Consistency actions/checkout@v4 in two workflows vs @v6 elsewhere
Stability swift:latest in check-unsafe-flags.yml
Code style Excessive docstrings throughout (contradicts CLAUDE.md)
Dead code Empty deinit in WebAuthTokenManager

🤖 Generated with Claude Code

leogdion and others added 6 commits May 3, 2026 13:15
…/version branches

Previously push and pull_request both fired on the same commit (in different
concurrency groups), producing two runs per PR. Limiting push to main means
non-main branches CI exclusively through their PR — one run per state.

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

Replaces aqua:peripheryapp/periphery with spm:peripheryapp/periphery in the
root, BushelCloud, and CelestraCloud mise.toml files — aqua-based installs
have been failing on CI; building from source via SPM is consistent with the
other spm-managed tools (swift-format, swift-openapi-generator).

Brings .github/workflows/MistDemo.yml up to the structure used by the other
example workflows: adds a configure job for dynamic matrix scope, expands
build-ubuntu (noble+jammy x 6.2+6.3 on full, noble x 6.3 on feature
branches), expands build-macos to include iOS, adds build-macos-platforms
(macOS/watchOS/tvOS/visionOS, gated on full matrix), and adds a lint job.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/BushelCloud"
  merged:   "161ba52"
upstream:
  origin:   "git@github.com:brightdigit/BushelCloud.git"
  branch:   "mistkit"
  commit:   "161ba52"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "7ad842e"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "7ad842e"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
Remove fetch/XHR interception and postMessage listener from
index.html — both are documented as defeated by cross-origin
sandboxing. Polling container._auth._ckSession plus the manual
paste fallback covers fresh sign-in and reload-while-signed-in
flows. Also fix invalid Jinja-style CSS comment that was breaking
the parser.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
subrepo:
  subdir:   "Examples/CelestraCloud"
  merged:   "fbcea8f"
upstream:
  origin:   "git@github.com:brightdigit/CelestraCloud.git"
  branch:   "mistkit"
  commit:   "fbcea8f"
git-subrepo:
  version:  "0.4.9"
  origin:   "https://github.com/Homebrew/brew"
  commit:   "b9763ee528"
@claude
Copy link
Copy Markdown

claude Bot commented May 3, 2026

Code Review — PR #277: v1.0.0-beta.1

Note: The diff exceeds GitHub's 300-file API limit, so this review is based on direct examination of the key changed files in the working tree.


Overview

This is a large, well-scoped release-branch PR bundling three distinct workstreams:

  1. CI overhaul — dynamic matrix, cleanup-caches, MistDemo workflow, subrepo action consolidation, toolchain bumps
  2. MistDemo improvements — new CRUD commands (create/update/delete), auth fix, native SwiftUI app
  3. CelestraCloud additions — new typed services, configuration layers, and tests

The architecture is generally clean and follows the repo's conventions. Below are findings organized by area.


CI / Workflows

Positives

  • Dynamic matrix (MistKit.yml) is elegantly implemented. The configure job's semver regex is correct and the logic is easy to follow.
  • Concurrency cancellation on all workflows is a nice touch for branch hygiene.
  • Centralizing setup-mistkit into brightdigit/MistKit/.github/actions/setup-mistkit@main removes duplication across subrepos.
  • CodeQL comment explaining why Swift analysis must stay on macOS is excellent documentation for future contributors.

Issues / Suggestions

1. cleanup-caches.yml — pagination gap (bug risk)

getActionsCacheList paginates at 30 entries by default (max 100). The script iterates caches.data.actions_caches without requesting additional pages. For long-lived branches with many invalidated caches this will silently miss entries. Fix:

const caches = await github.paginate(github.rest.actions.getActionsCacheList, {
  owner: context.repo.owner,
  repo: context.repo.repo,
  ref: ref,
});
for (const cache of caches) { ... }

2. setup-mistkit/action.yml — potential shell injection in sed command

The action interpolates ${{ inputs.branch }} directly into the shell command before the shell evaluates it. A branch name containing ' or | would break the sed expression. In practice MISTKIT_BRANCH: main is safe, but this is fragile. Prefer storing the input in a shell variable first:

run: |
  BRANCH="${{ inputs.branch }}"
  sed -i "s|...|.package(url: \"...\", branch: \"${BRANCH}\")|g" Package.swift

3. @main pin for shared action — fragile coupling

Both subrepo workflows reference brightdigit/MistKit/.github/actions/setup-mistkit@main. Any breaking change to that action on main will immediately break subrepo CI with no semver signal. Consider tagging the action at release points and bumping subrepo references deliberately, or at minimum document this coupling.

4. BushelCloud workflow — large commented-out Windows job

Examples/BushelCloud/.github/workflows/BushelCloud.yml has ~25 lines of commented-out Windows CI. Either remove it and open a tracking issue, or restore it. Dead YAML in CI files is hard to reason about during future edits.

5. swift-source-compat.yml — no paths-ignore

This workflow triggers on all PRs with no paths-ignore. Unlike MistKit.yml, it fires for doc-only changes. Low priority, but worth aligning.


MistDemo — Swift Code

Positives

  • Typed LocalizedError on CreateError, UpdateError, DeleteError with recoverySuggestion is excellent UX.
  • Optimistic locking via --force/recordChangeTag follows CloudKit best practice.
  • Note.init?(_ record:) identity-based equality comment is exactly the right kind — explains a non-obvious invariant.
  • public import (Swift 6 re-export syntax) is used consistently across MistDemoKit.
  • canImport(CloudKit) guards keep cross-platform targets clean.

Issues / Suggestions

6. UpdateCommand.mapConflict vs DeleteCommand.mapConflict — inconsistent approaches

UpdateCommand explicitly matches three CloudKitError cases for HTTP 409:

case .httpError(let statusCode) where statusCode == 409: ...
case .httpErrorWithDetails(let statusCode, _, let reason) where statusCode == 409: ...
case .httpErrorWithRawResponse(let statusCode, _) where statusCode == 409: ...

DeleteCommand uses the cleaner .httpStatusCode == 409 property check, then inspects the specific case only for reason. The DeleteCommand approach is easier to maintain — new CloudKitError cases won't require updating mapConflict. UpdateCommand should adopt the same pattern.

7. mapConflict visibility mismatch

UpdateCommand.mapConflict is private; DeleteCommand.mapConflict is internal. If internal is for testability, document it. Otherwise align both to private.

8. CreateCommand — zone parameter silently ignored

// Zone: config.zone - to be added when CloudKitService supports it

If a user passes --zone, it is silently dropped, yet the help text lists --zone as a supported option. Either remove --zone from the help text until it's implemented, or emit a warning when a non-default zone is specified.

9. IntegrationTestRunner.runCorePhases — repeated if database == .private branches

The cyclomatic_complexity suppress is defensible, but the same conditional appears four times. Extracting private-database-specific phases into a helper method would reduce complexity without losing clarity.

10. CreateCommand.generateRecordName — same-second collision

let timestamp = Int(Date().timeIntervalSince1970)
let randomSuffix = String(Int.random(in: ...))

Two concurrent creates within the same second share the same timestamp prefix. For a demo tool this is acceptable, but UUID().uuidString would be simpler and collision-free.


CelestraCloud — Swift Code

Positives

  • ArticleSyncService typed throws (async throws(CloudKitError)) is a great use of Swift 6's typed throws.
  • 4-step GUID deduplication strategy is well-documented with inline comments.
  • @available(macOS 13.0, *) on ArticleSyncService is correctly scoped.

Issues / Suggestions

11. ConfigurationLoader.loadConfiguration — double-key lookup

let containerID = readString(forKey: ConfigurationKeys.CloudKit.containerID)
  ?? readString(forKey: ConfigurationKeys.CloudKit.containerIDEnv)
  ?? CloudKitConfiguration.defaultContainerID

The ConfigReader already resolves priority (CLI > env). The second readString(forKey: containerIDEnv) call suggests the keys aren't fully capturing both forms in one ConfigKey. Consider consolidating, or document why the double lookup is needed.

12. ValidatedCloudKitConfiguration.privateKeyPath — logging exposure risk

privateKeyPath is a plain public String. SecureLogging is applied in log call sites (good), but the struct has no CustomDebugStringConvertible conformance. An accidental print(config) or string interpolation in a debug session would leak the key path. Consider adding a redacted debugDescription.


Test Coverage

CelestraCloud has solid unit tests for CelestraError and UpdateCommandConfiguration. MistDemo integration tests cover the full workflow end-to-end.

Gap: No unit tests for the new CreateCommand, UpdateCommand, or DeleteCommand logic (field conversion, mapConflict behavior, zone-silently-ignored path). These are good candidates for isolated unit tests that don't require a live CloudKit connection.


Summary

Area Status
CI dynamic matrix ✅ Well-implemented
cleanup-caches.yml pagination ⚠️ Silent data loss on busy branches
setup-mistkit shell injection ⚠️ Low severity, worth fixing before stable
@main action pin coupling ⚠️ Document or version the coupling
Commented-out BushelCloud Windows job 🔧 Remove or open tracking issue
CRUD command error handling ✅ Solid, with one inconsistency
Zone silently ignored in create ⚠️ Help text mismatch
mapConflict inconsistency 🔧 Minor, align approaches
Test coverage for new commands ⚠️ Missing unit tests
Swift 6 feature usage ✅ Appropriate and consistent

Overall this is solid work for a beta release. The CI overhaul is a meaningful improvement. The pagination gap in cleanup-caches.yml and the zone-silently-ignored issue are the highest-priority items to address before going stable.

@claude
Copy link
Copy Markdown

claude Bot commented May 3, 2026

Code Review — PR #277: v1.0.0-beta.1 (CI Overhaul)

Overview

Very large bundled PR (360 changed files, 16,098 additions, 14,383 deletions) combining:

  • CI overhaul with dynamic matrix, cleanup-caches, MistDemo workflow, action version bumps
  • Mint → mise migration across all three projects (MistKit, BushelCloud, CelestraCloud)
  • Centralized setup-mistkit shared action for subrepos
  • CelestraCloud code refactoring (command/service decomposition, new error types)
  • MistDemo improvements (test split, CRUD, auth, linting)

CI Architecture — Positives ✅

  • Dynamic matrix (configure job) is a significant quality-of-life improvement: feature branches get ~3 jobs instead of 20+, while main / semver branches / PRs targeting them get the full matrix. Well-designed.
  • cleanup-caches.yml on delete event prevents stale cache accumulation — a nice maintenance addition.
  • Centralized setup-mistkit action eliminates the duplicated local action in each subrepo and makes updates a single-file change.
  • Concurrency grouped on head_ref || ref ensures PR runs cancel themselves rather than killing target-branch jobs — correct pattern.
  • Push restricted to main to avoid double-runs (push + pull_request on the same commit) — clean.

CI Concerns

1. jlumbroso/free-disk-space@main — unpinned action (current main)

In the current MistKit.yml (build-android job), jlumbroso/free-disk-space@main is pinned to a floating branch rather than a version tag or SHA. This is a supply chain risk. The PR description says it was bumped to @v1.3.1 — verify this is consistently applied everywhere the action appears.

2. matrix.build-only reference in android job

In the current MistKit.yml build-android job:

- name: Free disk space
  if: matrix.build-only == false

build-only is not defined in the android matrix (only swift and android-api-level are). This means the condition is always evaluated against an undefined value, so the free-disk-space step likely never runs. This is a pre-existing bug — worth fixing here since the android job is already being touched.

3. codecov-action version inconsistency (current main)

Current MistKit.yml uses codecov/codecov-action@v4 in the Ubuntu job but @v5 in the Windows job. The PR should unify these (looks like the goal is @v6 throughout per the PR description — confirm it's consistent across all jobs).

4. visionOS toggling in commit history

There are multiple commits toggling visionOS on → off → on with download-platform: true. The final state enables it with osVersion: "26.2" and download-platform: true. This remains fragile if the simulator runtime isn't pre-installed. Consider adding a comment noting the failure mode (xcodebuild exits with "missing value for key 'OS'" if the download fails silently) so future maintainers don't have to rediscover it.

5. actions/checkout@v4 vs @v6 inconsistency

Per the PR description, claude.yml and claude-code-review.yml were reverted to @v4 while other workflows use @v6. This intentional inconsistency should be documented inline (a comment in those two files noting why they stay at v4) so reviewers don't read it as an oversight.

6. sersoft-gmbh/swift-coverage-action version

Current main uses @v4; the PR bumps it to @v5. Confirm this is uniform across all jobs — the Ubuntu and macOS jobs both reference it.


Code (CelestraCloud Refactoring) ✅

The CelestraCloud source changes are well-structured:

  • UpdateCommand+Reporting.swift (extracted 134 lines) and FeedUpdateProcessor+Fetch.swift (extracted 111 lines) are good decompositions following the project's established pattern of splitting focused responsibilities into extension files.
  • ExitError.swift, UpdateSummary.swift, ConfigurationError.swift are appropriately scoped new types.
  • UpdateReport+JSONOutput.swift cleanly separates JSON serialization from the core model.
  • Test split into CelestraErrorTests+Description.swift and CelestraErrorTests+RecoverySuggestion.swift follows the single_test_class SwiftLint rule the project enforces.

Code (MistDemo)

The Note.swift changes (+31, -29) look like a linting/formatting pass. The #if canImport(CloudKit) / #if canImport(SwiftUI) guards for MistDemoApp on Linux are the right fix — avoids workspace auto-discovery failures without excluding those files from the app target on Apple platforms.


Test Coverage

The test plan has all items unchecked. Since this is DRAFT that's expected, but the subrepo CI validations (BushelCloud and CelestraCloud upstream passing with the shared setup-mistkit@main) are the highest-risk items — they can only be confirmed end-to-end by actually triggering CI on those branches.


PR Size Observation

360 changed files is difficult to review atomically. The CI work, Mint → mise migration, and CelestraCloud refactoring are logically independent and could have been separate PRs. That said, since this is a release branch bundling accumulated work toward v1.0.0-beta.1 and everything appears intentional, it's an acceptable trade-off for a beta milestone — just noting it for future reference.


Summary

The CI architecture changes are well thought-out and address real pain points (matrix cost, double-runs, cache accumulation, shared action duplication). Main items to verify before marking ready:

  1. matrix.build-only undefined in android job — should the step be removed or the matrix updated?
  2. Confirm codecov and swift-coverage-action versions are uniform across all jobs
  3. Add inline comments for the intentional checkout@v4 exceptions and the visionOS fragility
  4. Verify jlumbroso/free-disk-space is pinned consistently to @v1.3.1 everywhere

Add tvOS/watchOS/visionOS to MistDemo's Package.swift platforms so xcodebuild
will accept those Simulator destinations. Make Hummingbird a conditional
package dep restricted to platforms it supports upstream (Apple + Linux), and
gate AuthTokenCommand + its tests + its registration with #if canImport(Hummingbird)
so the rest of MistDemoKit compiles on Windows/Android/wasm. Tighten MistDemoApp
gates with !os(tvOS) && !os(watchOS) since the SwiftUI notes editor uses
.fileImporter / .swipeActions / iOS-only toolbar placements.

Mirror the MistKit workflow pattern: extend build-ubuntu with wasm + wasm-embedded
type matrix entries, add build-windows and build-android jobs (Swift 6.2/6.3 only,
matching MistDemo's swift-tools-version: 6.2), and gate lint on the full matrix.

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

claude Bot commented May 3, 2026

Code Review: PR 277 - v1.0.0-beta.1 (CI overhaul + MistDemo improvements + subrepo updates). 16k additions / 14.5k deletions across 100+ files. Key findings:

CI / Workflows (.github/workflows/)

BUG: MistDemo.yml lint step runs ./Scripts/lint.sh from the repo root (not Examples/MistDemo), so it lints the MistKit package instead of MistDemo. The step needs 'working-directory: Examples/MistDemo' or should call ./Examples/MistDemo/Scripts/lint.sh.

Good: Dynamic matrix in MistKit.yml correctly gates the full matrix (Windows/Android/build-macos-platforms) behind PRs targeting main or semver branches. Cuts CI cost on feature branches.

Good: CodeQL on macos-26 with the explanation comment about Swift analysis not supporting Linux runners - clear and useful.

Good: cleanup-caches.yml is a clean, minimal implementation for branch cache cleanup.

Note: MistKit.yml and MistDemo.yml use different Swift matrix formats (object {version: '6.1'} vs plain string '6.2') - functional but inconsistent. Worth unifying.

MistDemo CLI (Examples/MistDemo/)

BUG: 'mistdemo help query' does not work as expected. CommandLineParser.isHelpRequested() treats 'help' as a help trigger, but parseCommandName() returns 'help' as the command name. So the runner calls printCommandHelp('help') which prints 'Unknown command: help' instead of showing query's help text. Either exclude 'help' from parseCommandName() when it is the subcommand trigger, or handle it specially in the runner.

Dead code: QueryCommand and CurrentUserCommand each define a private shouldIncludeField method that is identical to the one in OutputFormatting+Records.swift. The protocol extension version is what gets called from the extension methods. The private copies in the command types can be deleted.

Unnecessary #available guard: QueryCommand.execute() has an #available(macOS 11.0, iOS 14.0, ...) check, but Package.swift declares .macOS(.v15) as the minimum. The guard is always true and the else branch is dead code. Remove it.

Deprecated field not annotated: MistDemoConfig.skipAuth has a @deprecated comment but is not marked with @available(*, deprecated, message:). Swift's deprecation attribute would surface compiler warnings at call sites.

Error context loss: Commands like CreateCommand and QueryCommand wrap errors with throw CreateError.operationFailed(error.localizedDescription). This converts the underlying error to a string, losing type information. Consider passing the original error or using a typed cause.

Package.swift (Examples/MistDemo/)

Several .enableExperimentalFeature() entries cover proposals that have since been stabilized (e.g., VariadicGenerics landed in 5.9, SendingArgsAndResults in 6.0). Auditing which are still experimental in Swift 6.2/6.3 would reduce noise.

Using .unsafeFlags([...]) with -warn-concurrency, -strict-concurrency=complete, etc. is fine for an in-repo example, but these flags would break downstream consumers if MistDemo were a distributed library. Not an issue here since it is executable/library for demonstration purposes only.

Summary

Two actionable bugs before merge: (1) MistDemo.yml lint step working directory, (2) 'mistdemo help command' routing. The dead code and deprecated annotation issues are low priority. CI architecture is solid and the MistDemo feature additions (CRUD, native app, test split) look well-structured.

Four distinct failures surfaced when commit 315895b extended the matrix.
All four are addressed here so PR #277 can go green for v1.0.0-beta.1.

Wasm (noble/jammy x 6.2/6.3 x wasm + wasm-embedded): MistKitClientFactory
called CloudKitService's URLSession-backed convenience initializer, which
is gated #if !os(WASI). Wrap the factory bodies in #if os(WASI) / #else so
WASI throws ConfigurationError.unsupportedPlatform up front instead of
failing to compile.

Apple device platforms (iOS, tvOS, watchOS, visionOS via xcodebuild):
MistDemoKit declared .copy("Resources") containing only index.html. The
auto-generated MistDemo_MistDemoKit.bundle then failed strict CodeSign
on iOS-family simulators ("bundle format unrecognized"). Inline the HTML
as a Swift raw string in AuthTokenIndexHTML.swift (gated #if canImport
(Hummingbird)) and serve it via a route handler instead of FileMiddleware.
The resource bundle is no longer emitted, so there's no bundle to sign.

Windows + Android (AuthenticationHelper environment tests): the Android
race ("resolveWebAuthToken returns nil for empty string" intermittently
saw env-token leaked from a sibling test) and the Windows compile error
("cannot find 'setenv' / 'unsetenv' in scope") share a root cause - the
test mutated process-global env via POSIX setenv/unsetenv. Refactor
resolveAPIToken / resolveWebAuthToken to take an injectable @sendable
EnvironmentReader closure (default reads ProcessInfo). Production
callers stay unchanged via default arg. Add MockEnvironmentTrait, a
TestTrait/SuiteTrait/TestScoping that scopes a fake env dictionary into
a TaskLocal for the test - tests pass MockEnvironment.reader as the
injected reader. No global state mutation, no #if gates around tests,
suite stays fully parallel.

Verified locally: swift test runs 877 tests across 56 suites, all pass.
iOS Simulator xcodebuild no longer emits MistDemo_MistDemoKit.bundle
in the CodeSign step.

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

claude Bot commented May 3, 2026

Code Review: PR #277 — v1.0.0-beta.1

This is a substantial PR covering the CI overhaul, MistDemo improvements, and the first beta of the library itself. Review focuses on the Swift source changes and CI configuration; the .claude/docs/ and subrepo files are out of scope.


🔴 Issues

1. Generated files committed (Sources/MistKit/Generated/)

CLAUDE.md explicitly states:

IMPORTANT: Never manually edit files in Sources/MistKit/Generated/. These files are auto-generated from openapi.yaml. Any manual edits will be lost when code is regenerated.
Generated code is placed in Sources/MistKit/Generated/ and should not be committed to version control.

Yet Client.swift (3 246 lines) and Types.swift (7 783 lines) are both committed and not listed in .gitignore. This contradicts the project's own convention and will cause painful merge conflicts when openapi.yaml changes. Either:

  • Add Sources/MistKit/Generated/ to .gitignore and generate on the fly in CI, or
  • Update CLAUDE.md to reflect a deliberate decision to commit generated files and explain why.

2. Silent error swallowing in AuthenticationMiddleware.extractRequestBodyData

do {
    return try await Data(collecting: body, upTo: 1_024 * 1_024)
} catch {
    return nil  // ← swallows the error silently
}

If body collection fails (e.g. exceeds the 1 MB cap), S2S auth proceeds with nil body data. The resulting signature will be invalid, and the call will fail with a cryptic auth error at the CloudKit API level rather than a meaningful local error. The error should be rethrown, or at minimum logged before returning nil.

3. MISTKIT_ENABLE_LOG_REDACTION vs MISTKIT_DISABLE_LOG_REDACTION

CLAUDE.md documents two conflicting behaviors:

  • "By default, logs use SecureLogging.safeLogMessage() to redact sensitive information. Set MISTKIT_DISABLE_LOG_REDACTION=1 to disable redaction." (CLAUDE.md logging section)
  • SecureLogging.swift code: // Redaction disabled by default — enable with MISTKIT_ENABLE_LOG_REDACTION

The code and the docs disagree on both the env-var name and the default. Whichever is correct, one of them needs to be updated before a public beta ships.


🟡 Design concerns

4. Nine identical createXxxPath(containerIdentifier:) methods

CloudKitService.swift contains nine methods that are byte-for-byte identical except for their return type:

internal func createQueryRecordsPath(containerIdentifier: String) -> Operations.queryRecords.Input.Path {
    .init(version: "1", container: containerIdentifier, environment: .init(from: environment), database: .init(from: database))
}
// … repeated 8 more times

The containerIdentifier parameter is always self.containerIdentifier at every call site — the parameter adds no flexibility and could simply be removed. If the generated Input.Path types all share the same initializer signature, they still can't be collapsed into a single generic method — but removing the redundant parameter and inlining self.containerIdentifier would at least eliminate the noise.

5. Hardcoded .development environment in convenience initializers

CloudKitService+Initialization.swift hardcodes .development in the two most visible public initializers:

public init(containerIdentifier: String, apiToken: String, webAuthToken: String, transport: ...) throws {
    self.environment = .development   // ← hardcoded
    self.database = .private          // ← hardcoded
    ...
}

Production users must know to use the custom-TokenManager initializer to change the environment. Given this is a public API, defaulting to .development without an explicit parameter is likely to cause subtle production-vs-development confusion. Consider adding environment and database parameters with appropriate defaults.

6. self.apiToken = "" placeholder

When constructing CloudKitService with a custom TokenManager, apiToken is set to "". Since apiToken is public let, callers can read this misleading empty string. Consider making it internal or removing the stored property altogether when the auth is managed externally.


🟢 Positives

  • Dynamic CI matrix is a well-thought-out optimization. Feature branches running 3 jobs instead of 20+ is a significant DX improvement.
  • cleanup-caches.yml is a nice operational touch — branch cache accumulation is a real problem on busy repos.
  • AssetUploader closure type cleanly solves the HTTP/2 connection reuse issue without leaking the URLSession concern into the public API.
  • Test structure follows the @Test/@Suite Swift Testing patterns correctly. The mock transport and response provider setup is clean and reusable.
  • CloudKitError has thorough errorDescription implementations for all DecodingError cases — debugging decoding failures is much easier with this level of detail.
  • SecureLogging.maskToken handles edge cases (token shorter than prefix + suffix) correctly.

Minor

  • AuthenticationMiddleware.addServerToServerAuthentication contains a runtime #available(macOS 11.0, ...) guard, but the surrounding type and middleware are already @available(macOS 11.0, ...) — the guard can never be false and is dead code.
  • The 1 MB cap in extractRequestBodyData (1_024 * 1_024) is low for record-modify payloads that include embedded assets. This should be a named constant or at least a comment explaining the limit.
  • The deprecated modifyRecords(operations:atomic:) in CloudKitService+Operations.swift is fine for beta, but should be tracked for removal before 1.0.0-stable.

Summary: Two blocking issues before merge (committed generated files, logging env-var inconsistency) and one that should be fixed before a public beta (silent error swallowing in S2S auth). The CI overhaul and overall architecture are solid.

Two issues were masked by the Apple-device CodeSign and Wasm compile
failures fixed in f22ef0b. Now that those barriers are removed, both
surface as fresh red CI cells.

Bump Apple-device deployment targets to match swift-configuration's
availability requirements: iOS 18.0, tvOS 18.0, watchOS 11.0,
visionOS 2.0. swift-configuration's InMemoryProvider and ConfigReader
are gated to those versions, and MistDemoConfiguration uses both
unconditionally. Without this, MistDemoKit fails to compile on
iOS/tvOS/watchOS/visionOS (errors at MistDemoConfiguration.swift:67/75).
macOS stays at .v15 since swift-configuration's macOS availability is
already satisfied.

Gate the "Initialize FieldValue.int64 from Int64.max" test with
.enabled(if: Int.bitWidth >= 64). On wasm32 the native Int is 32-bit,
so Int(Int64.max) traps in the test's expectation. The trait keeps the
test running on every other platform and self-documents the constraint;
no #if outside the test, no platform-conditional source.

Local: swift test still 877/56 green.

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

claude Bot commented May 3, 2026

Code Review: PR 277 — v1.0.0-beta.1

This is a large release PR bundling CI infrastructure, MistDemo improvements, and CelestraCloud refactoring. The diff exceeded GitHub's 300-file limit, so this review is based on the key changed areas.


CI Overhaul (.github/workflows/)

The dynamic matrix approach in MistKit.yml is a solid improvement — cutting feature-branch jobs from 20+ down to ~3 is meaningful for iteration speed without compromising quality on release paths.

Issues found:

  • head_commit.message on pull_request events — Both MistKit.yml and MistDemo.yml gate jobs with !contains(github.event.head_commit.message, 'ci skip'). On pull_request events, github.event.head_commit is null, so this expression always evaluates to false (job always runs). The ci skip shortcut silently stops working on PRs. Consider replacing with a check against the PR title or a label, or remove the ci-skip pattern entirely in favor of GitHub's built-in path filtering.

  • cleanup-caches.yml hardcodes refs/heads/ — The workflow fires on all delete events, which includes tag deletions. When a tag is deleted, context.payload.ref is the tag name (e.g. v1.0.0), but the script constructs refs/heads/v1.0.0, which won't match any caches. This causes a no-op for tag deletions (harmless, but worth a guard or a comment acknowledging it).

  • MistDemo.yml swift matrix format inconsistencyMistKit.yml emits Ubuntu swift matrix entries as objects ({"version":"6.x"}), but MistDemo.yml emits plain strings ("6.x"). If brightdigit/swift-build@v1 handles both forms, this is fine; if not, one of them will silently fail. Worth a quick confirmation.

  • Lint job dependency on skipped jobs — The lint job depends on [build-ubuntu, build-macos, build-macos-platforms, build-windows, build-android], but build-windows and build-android are gated by full-matrix == 'true'. On a feature branch where the full matrix is skipped, GitHub marks those jobs as skipped; !cancelled() && !failure() still allows lint to proceed, which is the correct intent. This works, but deserves a comment since the dependency looks over-broad.


CelestraCloud Swift Changes

The refactoring cleanly separates concerns: UpdateCommand.swift (127 lines removed) is now a thin orchestrator delegating to FeedUpdateProcessor, UpdateSummary, and UpdateReport. This is well-structured.

Issues found:

  • FeedResult.status uses raw strings — Values like "success", "error", "skipped", "notModified" appear in both UpdateCommand+Reporting.swift and consumers. These should be an enum (or at minimum static let constants) to make exhaustiveness checkable and prevent typos.

  • Copyright year inconsistencyCelestraErrorTests+Description.swift and CelestraErrorTests+RecoverySuggestion.swift carry Copyright © 2025 while all new files in this PR use 2026. The test files were likely carried over from an earlier branch.

  • processFeeds duplicates error-recording — The invalid-URL early-exit path in UpdateCommand.swift (line ~158) manually sets summary.errorCount += 1 instead of calling summary.record(.error(message: "Invalid URL")). This bypasses the record(_:) method and risks diverging if UpdateSummary.record gains additional logic (e.g., tracking error reasons).

  • MistDemoApp target has no dependenciesPackage.swift declares MistDemoApp as a library target with an empty dependencies array. If this is intentional (placeholder for a future native app), a comment would prevent it from looking like an accidental omission.


Test Coverage

Good: new tests for CelestraError descriptions and recovery suggestions are thorough and use Swift Testing's #expect correctly.

Gaps worth noting for follow-up:

  • UpdateReport.writeJSON(to:) has no unit tests (file I/O path, encoder settings, date format)
  • UpdateSummary.record(_:) has no unit tests
  • ExitError is trivially simple but untested

Security

No concerns. Action versions are pinned with major-version tags (@v1, @v6). CODECOV_TOKEN is passed via secrets. No credentials or sensitive values in the diff.


Verdict

The CI improvements are a net positive and the Swift refactoring is clean. The head_commit.message issue on PRs is the most actionable bug — the ci-skip feature is broken silently on every pull_request event. The string-based FeedResult.status is a quality concern worth addressing before 1.0 final. Everything else is minor.


🤖 Reviewed by Claude Code

leogdion and others added 2 commits May 3, 2026 17:31
Once the wasm32 build started actually executing tests (after f22ef0b
made it compile), four classes of test failed because the tests
themselves assume Darwin/Linux runtime semantics that don't hold under
wasm32:

- MistKitClientFactoryTests (15 tests): the factory throws
  .unsupportedPlatform on WASI by design (no URLSession), so every
  create-then-assert test in the suite trips. Suite-level disabled
  trait — tests are accurate elsewhere; on WASI the factory is
  deliberately not callable.

- AuthenticationHelperTests "Server-to-server auth with keyID":
  FileManager.temporaryDirectory write fails under WASI sandbox
  (NSCocoaErrorDomain Code 3328 "operation not supported"). Per-test
  trait.

- AsyncChannelTests "Sequential receive operations": relies on
  Task.sleep wakeup ordering between two concurrent tasks (50ms vs
  100ms). wasm32's CooperativeExecutor doesn't preserve real-time
  ordering — receives can deliver "second" before "first". Per-test
  trait.

- AsyncHelpersTests "withTimeout with very short timeout": the same
  CooperativeExecutor pattern — a 1ms timeout doesn't fire before a
  100ms inner sleep completes, so the test gets "unreachable" instead
  of the expected throw. Per-test trait.

Add a tiny TestPlatform helper that exposes `arch(wasm32)` as a runtime
constant so .enabled(if:) / .disabled(if:) traits can read it without
#if around the test or the file. Keeps gating in the trait, where it
self-documents the constraint and stays inert on every other platform.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two more failures surfaced once iOS/tvOS/visionOS turned green and wasm
got far enough into test execution.

watchOS build can't resolve module 'NIOTransportServices' from
HummingbirdCore (BindAddress.swift:11). NIOTransportServices uses Network
framework patterns that don't link on watchOS. AuthTokenCommand is a CLI
HTTP-server flow that's not meaningful on watchOS anyway, so drop watchOS
from the Hummingbird platform condition. AuthTokenCommand and
AuthTokenIndexHTML are already gated `#if canImport(Hummingbird)` so
they cleanly drop out on watchOS too.

Wasm noble/jammy (6.2 + 6.3, both wasm and wasm-embedded) trap with
"out of bounds memory access" inside AsyncChannel.receive during
"Channel handles rapid send/receive". The test runs 100 tight
send-then-receive cycles in a row; on wasm32 the CooperativeExecutor
recurses through swift_task_switch and the linear-memory stack runs out.
Per-test .enabled(if: !TestPlatform.isWasm32) trait, same pattern as the
other wasm-incompatible tests.

Local: swift test still 877/56 green on macOS host.

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

claude Bot commented May 3, 2026

Code Review — PR #277: v1.0.0-beta.1

Overview: Large bundled release PR. The core change is a CI overhaul (dynamic matrix, concurrency cancellation, Mint→mise migration, new MistDemo.yml, cache cleanup workflow, action version bumps). Swift source changes are mostly license-header Unicode fixes and tooling config updates.


CI Overhaul (.github/workflows/)

What's good:

  • The dynamic configure job elegantly cuts feature-branch CI from 20+ jobs to ~3 without duplicating matrix definitions. The full-matrix output pattern is clean and reusable across workflows.
  • Concurrency groups with cancel-in-progress: true are a meaningful win for fast-moving branches.
  • cleanup-caches.yml is a nice quality-of-life addition that prevents cache accumulation on long-lived projects.
  • Replacing Mint with mise simplifies local dev setup significantly — one tool, one config file.
  • no_unchecked_sendable SwiftLint custom rule is a good enforcement of the project's strict-concurrency goals.

Issues / Suggestions:

1. Push trigger on MistKit.yml is main-only — semver branch check in configure is dead code for push events

on:
  push:
    branches:
      - main   # ← only main triggers push

The configure job's elif [[ "$REF" =~ ^refs/heads/v?[0-9]+\.[0-9]+\.[0-9]+ ]] branch can never be reached on a push event because the workflow only triggers on pushes to main. This is fine if direct pushes to semver/hotfix branches are not a workflow — but if they ever are (e.g., a v1.0.1 hotfix branch), CI would silently not run. Either document this as intentional, or add semver branches to the push.branches list.

2. cleanup-caches.yml doesn't guard against tag deletions

The delete event fires for both branch and tag deletions. The script builds refs/heads/${context.payload.ref} unconditionally. For a tag deletion, this produces refs/heads/v1.0.0 which won't match any branch caches, so the loop runs harmlessly — but it's a semantic bug. Add a guard:

if (context.payload.ref_type !== 'branch') return;

3. FileScopedDeclarationPrivacy changed to false in .swift-format

CLAUDE.md states "We are using explicit ACLs in the Swift code." The FileScopedDeclarationPrivacy rule enforces private (not internal) for file-scoped declarations, which directly supports explicit ACLs. Setting it to false means the formatter will stop enforcing this. If this is intentional, update CLAUDE.md to reflect the policy change.

4. Periphery bumped from 3.2.0 → 3.7.4 in mise.toml (5 minor versions)

This is a significant jump. Periphery 3.x has had breaking changes in its scan behavior between minor versions (e.g., new configuration keys, changed defaults for unused code detection). Worth running periphery scan locally before merging to confirm no false positives or regressions in the scan report.

5. jdx/mise-action@v4 and actions/checkout@v6 — confirm availability

actions/checkout@v6 is pinned to a major version — confirm this is a released stable version and not ahead of what GitHub provides. Same for jdx/mise-action@v4.

6. Lint job needs includes build-macos-platforms which is skipped on feature branches

lint:
  needs: [build-ubuntu, build-macos, build-macos-platforms, build-windows, build-android]
  if: ${{ !cancelled() && !failure() ... }}

Skipped jobs (not failed, not cancelled) do satisfy !failure(), so this should work correctly on feature branches where build-macos-platforms, build-windows, and build-android are skipped. Just worth confirming in the test plan — the existing test plan items don't explicitly verify that lint runs on feature branches.

7. MistDemo.yml build-ubuntu wasm excludes — none defined, is that intentional?

MistKit.yml excludes Swift 6.1 from wasm builds. MistDemo.yml starts at 6.2 (correct per swift-tools-version), but doesn't add any wasm excludes for 6.2/6.3. If MistDemo wasm builds are expected to work on all supported Swift versions, this is fine — but it should be confirmed.


Swift Source Changes

Most Swift file changes are Unicode non-breaking space → ASCII space fixes in license headers (the "AS IS" strings). This is good hygiene but creates noise in a large diff. Consider doing this kind of whitespace-only normalization in a separate, trivially-reviewable commit in the future.

The only functional Swift change is the SwiftLint/swift-format config updates noted above.


Subrepo Changes (BushelCloud / CelestraCloud)

Centralizing setup-mistkit to brightdigit/MistKit/.github/actions/setup-mistkit@main is a clean DRY improvement. The deleted per-subrepo copies were identical, so there's no behavioral change. The MISTKIT_BRANCH: main env var makes the dependency pin explicit and visible.

Note: The subrepo workflows now pin to @main of the shared action. If the setup-mistkit action on main ever changes in a breaking way, all subrepo CI will break simultaneously. Consider pinning to a SHA or tag once this stabilizes.


Summary

The CI overhaul is well-designed and the dynamic matrix approach is sound. Key items before merging:

  • Clarify the semver-branch push trigger intent (item 1)
  • Add ref_type guard to cleanup-caches.yml (item 2)
  • Resolve FileScopedDeclarationPrivacy: false vs CLAUDE.md explicit-ACLs convention (item 3)
  • Verify periphery 3.7.4 scan output locally (item 4)
  • Confirm actions/checkout@v6 and jdx/mise-action@v4 availability (item 5)

Three more AsyncHelpers tests fail on wasm32 with the same root cause
as veryShortTimeout: an outer withTimeout(Δsmall) is supposed to fire
while an inner Task.sleep(Δlarge) is suspended, but wasm32's
CooperativeExecutor doesn't preempt the sleeping task in time, so the
inner closure completes and the timeout never throws.

  - throwsOnTimeout (0.1s timeout vs 500ms sleep)
  - cancelsOtherTasks (0.1s vs 500ms)
  - multipleConcurrentTimeouts (0.05s vs 200ms inner branch)
  - zeroTimeout (0.001s vs 1s) — same as veryShortTimeout

Same per-test .enabled(if: !TestPlatform.isWasm32) trait as before.
Tests are accurate everywhere else; on wasm32 the runtime semantics
they're checking simply don't apply.

Local: swift test still 877/56 green on macOS host.

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

claude Bot commented May 3, 2026

Code Review: v1.0.0-beta.1 (#277)

This is a large release-branch PR. Given the scope, I focused on the CI infrastructure changes (the core of what's new here), plus a scan of the Package.swift and action configuration.


CI: MistKit.yml — Dynamic Matrix

The configure job is a genuine improvement. The logic is clean and the feature-branch optimization (Noble + Swift 6.3 only, no Windows/Android) is a real CI time-saver. A few notes:

  • The lint job depends on [build-ubuntu, build-macos, build-macos-platforms, build-windows, build-android] via needs:. On feature branches, build-windows, build-android, and build-macos-platforms will be skipped (not failed). The if: ${{ !cancelled() && !failure() ... }} condition correctly lets lint proceed when upstreams are skipped. But worth a smoke-test on a feature-branch push to confirm.
  • The build-macos matrix has an entry with no type key (SPM build). matrix.type will evaluate to null/empty and be passed as an empty string to brightdigit/swift-build@v1. Assuming the action defaults to SPM on empty type — works correctly, but a comment would make intent clear.

setup-mistkit Action — Shell Injection Risk

setup-mistkit/action.yml directly interpolates ${{ inputs.branch }} into a sed command:

sed -i '' 's|...|.package(url: "...", branch: "'"${{ inputs.branch }}"'")|g' Package.swift

If the input contains |, ', or other shell metacharacters, the sed command will break or behave unexpectedly. Use an env var instead:

env:
  BRANCH: ${{ inputs.branch }}
run: |
  sed -i '' "s|...|.package(url: \"...\", branch: \"$BRANCH\")|g" Package.swift

This is low-severity since inputs.branch is caller-controlled, but it's a latent issue if branch names ever include unusual characters.


setup-mistkit — Unpinned Reference

Subrepos now reference brightdigit/MistKit/.github/actions/setup-mistkit@main rather than a local copy. This centralizes the action (good), but @main means any breaking change to the action will immediately affect all downstream subrepos with no version gate. Consider pinning to a commit SHA or a semver tag once the action stabilizes.


cleanup-caches.yml — Tag Deletion Edge Case

The workflow triggers on all delete events (branches and tags). When a tag is deleted, context.payload.ref is the tag name, and refs/heads/${context.payload.ref} constructs an incorrect ref. The cache lookup would return zero results and silently succeed — no breakage, but tag caches won't be cleaned. Consider filtering to branch-only deletions:

if (context.payload.ref_type !== 'branch') {
  console.log('Not a branch deletion, skipping.');
  return;
}

check-unsafe-flags.yml — Unpinned Image

The workflow uses swift:latest as the container image. For a security-focused check, this should be pinned (e.g., swift:6.3) so the behavior is reproducible and won't silently change when a new Swift major version ships.

Also, dump-package is invoked twice (once to check, once to print the error sample). These can be combined to a single invocation stored in a variable.


Package.swift — Experimental Features

The package enables 14 experimental Swift features. Some of these (e.g., MoveOnlyClasses, MoveOnlyResilientTypes, VariadicGenerics) are noncopyable/generics features that have had compiler evolution across 6.x releases. For a beta.1 targeting Swift 6.1–6.3:

  • Verify each experimental feature is actually used in the codebase — unused flags add compiler overhead and silent upgrade risk.
  • Consider documenting which features are exercised vs. enabled speculatively.

The upcoming features (ExistentialAny, InternalImportsByDefault, etc.) are stable — no concern there.


visionOS in build-macos-platforms

The PR description says "visionOS pin dropped" but the matrix still has:

- type: visionos
  osVersion: "26.4.1"
  download-platform: true

download-platform: true should handle the missing pre-installed runtime, but the PR description comment is misleading. Update the description or add an inline comment explaining this is now downloaded on-demand.


Test Coverage

The test suite is well-structured — service operations are split across focused +SuccessCases, +Validation, +Helpers files. The service layer (query, upload, fetch changes, zones, users) has good unit test coverage. Integration tests via MistDemo provide end-to-end coverage.

No gaps observed at the unit layer for the code visible in this PR.


Summary

Area Status
CI dynamic matrix ✅ Clean implementation
setup-mistkit shell injection ⚠️ Low-severity, worth fixing
cleanup-caches tag edge case ⚠️ Silent no-op on tag deletes
check-unsafe-flags unpinned image ⚠️ Pin to specific Swift version
Package.swift experimental features ⚠️ Audit for actually-used features
visionOS description inconsistency ℹ️ Minor doc/comment update needed
@main action reference ℹ️ Consider pinning after stabilization
Test coverage ✅ Good

Overall this is solid infrastructure work. The dynamic matrix is the standout improvement. The setup-mistkit injection issue is the highest-priority fix before merge.

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.

CI Updates

1 participant