Skip to content

chore(ci): overhaul CI matrix, add Windows/Android builds, clean up tooling scaffolding#78

Merged
leogdion merged 18 commits intov2.0.0-alpha.2from
atleast-v1.0.0-beta.4
Apr 10, 2026
Merged

chore(ci): overhaul CI matrix, add Windows/Android builds, clean up tooling scaffolding#78
leogdion merged 18 commits intov2.0.0-alpha.2from
atleast-v1.0.0-beta.4

Conversation

@leogdion
Copy link
Copy Markdown
Member

@leogdion leogdion commented Apr 7, 2026

No description provided.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Important

Review skipped

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

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

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b8cbd756-e3ef-4761-a0f9-54d427192da0

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 atleast-v1.0.0-beta.4

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.

Copy link
Copy Markdown
Member Author

@leogdion leogdion left a comment

Choose a reason for hiding this comment

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

update swift-format and SwiftLint rules

Comment on lines 27 to 29
@@ -29,4 +29,4 @@
}
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.

these need to be updated to use the new swift extension

Comment on lines +64 to +72
exclude:
- swift: {version: "6.1"}
type: wasm
- swift: {version: "6.1"}
type: wasm-embedded
- swift: {version: "6.2"}
type: wasm
- swift: {version: "6.2"}
type: wasm-embedded
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.

comment these out

scheme: ${{ env.PACKAGE_NAME }}
- uses: sersoft-gmbh/swift-coverage-action@v4
type: ${{ matrix.type }}
wasmtime-version: 41.0.3
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.

remove this for now

Comment on lines +80 to +84
- name: Install curl
if: steps.build.outputs.contains-code-coverage == 'true'
run: |
apt-get update -q
apt-get install -y curl
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.

comment this out for now

Comment on lines +118 to +120
- name: Enable git symlinks
shell: pwsh
run: git config --global core.symlinks true
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.

comment this out

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.

make all matrices fail fast false temporarily

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.

keep Xcode 16.4 runs

- Comment out wasm builds, windows job, and install curl step in CI
- Add fail-fast: false to build-ubuntu matrix
- Update devcontainers to use swift-server.swift extension
- Migrate .swiftlint.yml and .swift-format from Swift-App-Template

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 7, 2026

PR Review: CI/CD & Tooling Configuration Update

This is a solid infrastructure modernization. Here's my feedback organized by severity:


Potential Issues

actions/checkout@v6 may not exist
The PR bumps actions/checkout to @v6 across SundialKit.yml, but the latest stable release is @v4. sundial-demo.yml still uses @v4, creating an inconsistency. If v6 doesn't exist this will break every CI job. Recommend pinning to @v4 until v6 is actually released.

Same concern applies to codecov/codecov-action@v6 and sersoft-gmbh/swift-coverage-action@v5 — verify these versions exist before merging.

Release tags run a reduced test matrix
The configure job only sets full-matrix=true for refs/heads/main pushes and PRs. Tag pushes (v*.*.*) fall through to the reduced matrix, skipping build-macos-full (iOS, watchOS, tvOS, visionOS) and build-android. For a release cut this seems like the wrong tradeoff — you probably want the full suite on release tags.

# In configure job, consider:
if [[ "${{ github.ref }}" == "refs/heads/main" || "${{ github.event_name }}" == "pull_request" || "${{ github.ref }}" == refs/tags/* ]]; then

LINT_MODE: STRICT silently removed
The lint job previously set LINT_MODE: STRICT. It's now removed, so CI runs in non-strict mode unless lint.sh defaults to strict. If CI is supposed to enforce strict linting this is a regression. Worth making this explicit either way.


Observations / Minor Items

Commented-out code is noisy
There are substantial commented-out blocks: the entire Windows build job, WASM type matrix, and some exclude rules. If these are planned for a follow-up PR, that's fine, but consider tracking them as GitHub issues instead so the workflow stays readable.

.swift-format rule relaxations
Several rules were toggled to less strict:

  • AllPublicDeclarationsHaveDocumentation: false — removes the enforcement of docs on public API
  • ValidateDocumentationComments: false — stops checking doc comment correctness
  • NoLeadingUnderscores: false — permits _-prefixed names

If these changes are intentional (e.g. the leading underscore change is useful for actor isolation patterns), they're reasonable. But the doc comment relaxations may allow API documentation to degrade over time unless enforced elsewhere.

.swiftlint.yml line_length is aggressive
Setting both warning and error thresholds to 90 gives zero tolerance — the first violation immediately errors. Previous 108/200 allowed warnings before hard failures. Consider a small gap like 90/110 for a better developer experience.

one_declaration_per_file enabled
This is a potentially large-scope change. Any existing file with more than one type/enum/struct will now fail linting. If the codebase is already compliant this is fine, but worth confirming before the rule lands.

function_body_length tightened to 35/50
Down from 50/76. This is quite strict — async/await patterns and switch statements can burn through lines quickly. Worth running the linter locally against the full codebase to see how many new violations this introduces.


Positive Changes

  • Dynamic matrix via configure job is a clean pattern for avoiding wasted CI minutes on feature branches
  • concurrency group with cancel-in-progress is a good addition
  • Splitting build-macos into fast and full variants is sensible
  • Android support is a nice addition
  • Extension rename sswg.swift-langswift-server.swift is the correct current extension ID
  • Removing the hardcoded mise version and duplicate Swift 6.2 matrix entry are good cleanups
  • !cancelled() && needs.configure.result == 'success' guards are more robust than the old ci skip pattern

Summary: The architecture of this workflow update is good. The main blocker to address is verifying actions/checkout@v6 exists (or downgrading to @v4), and deciding whether release tags should run the full matrix.

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 7, 2026

Codecov Report

❌ Patch coverage is 85.96491% with 16 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.66%. Comparing base (02a6830) to head (bf062e2).
⚠️ Report is 1 commits behind head on v2.0.0-alpha.2.

Files with missing lines Patch % Lines
Sources/SundialKitCore/SundialLogger.swift 0.00% 9 Missing ⚠️
Sources/SundialKitCore/SundialLogger+Custom.swift 0.00% 4 Missing ⚠️
Sources/SundialKitCore/SundialError.swift 0.00% 2 Missing ⚠️
Sources/SundialKitNetwork/PingCoordinator.swift 95.23% 1 Missing ⚠️
Additional details and impacted files
@@                Coverage Diff                 @@
##           v2.0.0-alpha.2      #78      +/-   ##
==================================================
+ Coverage           65.20%   65.66%   +0.45%     
==================================================
  Files                  31       32       +1     
  Lines                 526      533       +7     
==================================================
+ Hits                  343      350       +7     
  Misses                183      183              
Flag Coverage Δ
spm 67.04% <95.14%> (+0.44%) ⬆️
swift-6.1 65.91% <87.27%> (?)
swift-6.1-jammy ?
swift-6.1-noble ?
swift-6.2 65.91% <87.27%> (?)
swift-6.2-jammy ?
swift-6.2-jammy-nightly ?
swift-6.2-noble ?
swift-6.2-noble-nightly ?
swift-6.3 65.91% <87.27%> (?)
ubuntu 65.91% <87.27%> (?)

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.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 7, 2026

PR Review: CI/CD & Tooling Overhaul

This is a solid infrastructure upgrade overall. Here are observations organized by area:


✅ Positives

  • Concurrency groups with cancel-in-progress: true is a great addition — prevents redundant runs on rapid pushes.
  • Dynamic matrix via configure job cleanly separates full vs. fast builds for PRs/main vs. feature tags.
  • Conditional coverage upload (if: steps.build.outputs.contains-code-coverage == 'true') is much cleaner than always running it.
  • Expanding to Windows and Android targets broadens platform coverage nicely.
  • paths-ignore for docs/markdown avoids wasteful CI runs on documentation-only changes.

Issues & Concerns

CI Workflow (SundialKit.yml)

1. LINT_MODE: STRICT removed from lint job

The lint job previously had LINT_MODE: STRICT set explicitly. Scripts/lint.sh checks for this env var to enable strict mode. Without it, CI lint will run in normal mode instead of strict, which is a quality regression. Per CLAUDE.md, strict mode is the CI standard.

# Should restore:
env:
  LINT_MODE: STRICT

2. Feature branch push CI is now opt-in via PR only

Previously, pushes to any non-*WIP branch triggered CI. Now, only pushes to main/tags and PRs against main trigger builds. Pushes to feature branches (e.g., feature/something) with no open PR will get no CI. This means broken commits can accumulate undetected until a PR is opened.

Consider adding a broader branches pattern or a workflow_dispatch trigger to catch this.

3. Commented-out type matrix in build-ubuntu

# type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }}
# exclude:
#   - swift: {version: "6.1"}
#     type: wasm

This looks like incomplete/WIP work left in. Either enable wasm builds or remove the commented-out block before merging.

4. actions/checkout@v6 vs @v4 inconsistency

sundial-demo.yml still uses actions/checkout@v4 in several places while SundialKit.yml moves to @v6. This inconsistency should be resolved — either update all references or pin to the same version intentionally.

5. Windows coverage upload missing files: parameter

The Ubuntu job includes files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} for precise file targeting. The Windows coverage upload omits this, relying on auto-discovery which may be less reliable on Windows paths.

6. Android only tests Swift 6.2 and 6.3

swift:
  - version: "6.2"
  - version: "6.3"

The library targets Swift 6.1+, but Android builds skip 6.1 testing. This could miss compatibility issues specific to Swift 6.1 on Android.

7. Missing fail_ci_if_error: true on macOS coverage uploads

Ubuntu job has fail_ci_if_error: true but both build-macos and build-macos-full omit it. Codecov failures would silently pass on macOS.

8. brightdigit/swift-build@v1 (floating tag) vs pinned versions

Using @v1 instead of a pinned version like @v1.4.0 means the action could silently change behavior on the next v1.x release. sundial-demo.yml uses the more specific @v1.5.2. Consider using a pinned version or at minimum documenting the intentional use of the floating tag.


.swift-format Changes

9. AllPublicDeclarationsHaveDocumentation: false (was true)

Disabling public API documentation enforcement alongside ValidateDocumentationComments: false is a meaningful quality step down. If this is intentional (e.g., docs are enforced elsewhere), it's fine — but worth calling out explicitly in the PR description.

10. NoLeadingUnderscores: false (was true)

Allowing _-prefixed names goes against Swift API Design Guidelines. If there's a specific case driving this (e.g., generated code or async underscore patterns), it's worth documenting why.


.swiftlint.yml Changes

11. todo added to disabled_rules

TODO comments will no longer be flagged. For a library aiming for production quality, TODO warnings in CI serve as a useful reminder. Consider using warning_threshold on the todo rule instead of disabling it entirely.

12. line_length warning == error (both 90)

Setting both warning and error to the same value (- 90 / - 90) removes the warning grace period. Any line over 90 chars immediately fails. This is unusually strict and may cause friction. Consider - 90 / - 110 or similar to give a buffer.

13. cyclomatic_complexity error threshold lowered: 12 → 8

This is a fairly aggressive tightening. Ensure existing code doesn't already exceed 8 before merging, or existing CI will break immediately.


Minor Nits

  • The configure job's if condition only checks head_commit.message for ci skip on non-PR events, which is correct — but documenting this intent in a comment would help future maintainers.
  • build-macos now only runs with Xcode_26.4.app. If that runner image doesn't have that Xcode version installed, it will fail silently. The full matrix job handles older versions, which is a good split.

Summary

The architecture of the workflow (dynamic matrix, conditional full builds, platform expansion) is a significant improvement. The main concerns worth addressing before merging are: restoring LINT_MODE: STRICT in CI, resolving the commented-out wasm matrix, and the checkout action version inconsistency. The .swift-format changes that disable documentation enforcement deserve an explicit call-out in the PR description since they're a notable policy change.

- Set LINT_MODE=STRICT in CI lint job so failures actually fail the job
- Fix all SwiftLint strict-mode violations:
  - Wrap long doc comment lines (line_length > 90 chars) across sources and tests
  - Break long function signatures onto multiple lines
  - Split large test structs exceeding 125-line body length into extensions
  - Extract TestBinaryMessage and TestObserverImpl to dedicated files
    to satisfy one_declaration_per_file rule
  - Rename NetworkMonitorConcurrencyTests to NetworkMonitorTests+ConcurrencyTests
    to satisfy file_name rule

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

This is a substantial infrastructure PR with a workflow overhaul, tooling cleanup, source refactors, and test reorganization. Overall direction is good — here are the observations:


CI/CD Workflow (.github/workflows/SundialKit.yml)

Potential blocking issues:

  • actions/checkout@v6 — All three build jobs (build-ubuntu, build-windows, build-macos/build-macos-full) reference actions/checkout@v6. The latest stable version is v4. If v6 hasn't been released, all CI jobs will fail with a resolution error. Worth verifying this version exists before merge.

  • build-windows missing coverage processing step — The macOS jobs have a dedicated Process Coverage step (using sersoft-gmbh/swift-coverage-action@v5) before uploading to Codecov. The Windows job skips this and uploads directly. This may produce malformed or empty coverage data for Windows builds.

Incomplete/dead code:

  • The configure job correctly generates ubuntu-type with ["","wasm","wasm-embedded"], but in build-ubuntu the type matrix is fully commented out:
    # type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }}
    This makes the WASM configuration logic in configure dead code. Either remove the WASM outputs from configure or un-comment the type matrix (and its exclude block).

Improvements (good changes):

  • Fixing the duplicate Swift 6.2 matrix entry from the old workflow.
  • Adding Swift 6.3 to the build matrix.
  • cancel-in-progress: true concurrency control is a nice CI speedup.
  • Split into build-macos (fast, single Xcode) + build-macos-full (full matrix, PR/main only) is a solid pattern.
  • paths-ignore filters for docs/markdown prevent unnecessary CI runs.

SundialLogger Refactor

The refactor from two #if canImport(os) split enums to a single package enum SundialLogger with conditional inner content is cleaner. Moving custom() to SundialLogger+Custom.swift is good separation.

Minor: the FallbackLogger struct is defined inside #if !canImport(os) but is also referenced in the #else block — this works correctly in Swift since both live within the same enum, but the nesting may feel counter-intuitive at first glance.


Test Reorganization

The splitting of ObserverRegistryTests and NetworkMonitorTests into focused extension files (+NotifyTests, +ObserverTests, +ConcurrencyTests) improves discoverability.

Issues:

  • TestObserverImpl.swift file comment mismatch — The in-file comment says ObserverRegistryTestHelpers.swift but the actual filename is TestObserverImpl.swift. These should match.

  • TestObserverImpl lost protocol conformance — The TestObserver protocol was removed and TestObserverImpl no longer conforms to any protocol. This is a minor reduction in type safety for the test helpers. Intentional?

  • Copyright year inconsistency — New test extension files (ObserverRegistryTests+NotifyTests.swift, NetworkMonitorTests+ObserverTests.swift, NetworkMonitorTests+ConcurrencyTests.swift) have copyright © 2025 while the source files were updated to © 2026. These new files should consistently use 2026 (or whichever year is standard going forward).


.swift-format Rule Changes

  • AllPublicDeclarationsHaveDocumentation: false — This relaxes the documentation requirement for all public API surface. For a published library, this risks undocumented types/methods slipping in unnoticed. Consider keeping this true or using a linter rule instead.
  • AlwaysUseLiteralForEmptyCollectionInit: true and BeginDocumentationCommentWithOneLineSummary: true — Both are sensible improvements.

.swiftlint.yml Rule Changes

  • closure_body_length commented out, first_where and multiline_arguments removed — These relax style enforcement. Worth a comment in the PR as to why these were loosened (formatter conflicts? too noisy?).
  • one_declaration_per_file added — Good addition given the new test file splitting pattern.

Minor / Cosmetic

  • The sundial-demo.yml update from swift-build@v1.5.2 while the main workflow uses swift-build@v1 (major version alias) is a minor inconsistency — not a bug, but worth noting.
  • .mcp.json and .env.example removal is appropriate; they contain placeholder API key patterns that shouldn't be in a Swift library repo.

Summary: The workflow structure improvements are solid, but the actions/checkout@v6 reference and missing Windows coverage processing step are worth addressing before this graduates from draft. The WASM dead code in the configure job should also be resolved one way or the other.

@leogdion leogdion marked this pull request as ready for review April 8, 2026 01:23
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review

Note: The PR title says "chore: Update SundialKit.yml" but this PR touches far more than the CI workflow — it includes new source files, new tests, copyright updates, formatter/linter config changes, devcontainer updates, and a SundialLogger refactor. Consider a broader title or splitting into focused PRs per the component-based conventions in CLAUDE.md.


Missing Labels / Conventions

Per CLAUDE.md, component labels are required. Based on the changes, at minimum these apply:

  • component:infrastructure — CI workflow, devcontainer, tooling
  • component:coreSundialLogger, error types, ObserverRegistry
  • component:networkNetworkMonitor and related files
  • component:watchconnectivityConnectivityManager, WatchConnectivitySession
  • component:tests — the large new test additions

CI Workflow (.github/workflows/SundialKit.yml) — Good improvements, a few concerns

Positives:

  • Adding pull_request trigger is a significant improvement — builds now run on PRs, not just push
  • Concurrency group with cancel-in-progress: true prevents redundant runs
  • Dynamic configure job for matrix selection (minimal on feature branches, full on main/PRs) is a smart optimization
  • Adding Windows 2022/2025 and Android API 33–36 build targets is great cross-platform coverage
  • Conditional coverage upload (if: steps.build.outputs.contains-code-coverage == 'true') avoids spurious failures

Concerns:

  1. actions/checkout@v6 likely doesn't exist. As of writing, the latest stable is v4. If v6 is unavailable, every job will fail immediately. Verify this is intentional or revert to v4.

  2. Commented-out WASM matrix in build-ubuntu — the type and exclude block is left as a dead comment. Either enable it or remove it to avoid confusion:

    # type: ${{ fromJSON(needs.configure.outputs.ubuntu-type) }}
    # exclude:
    #   - swift: {version: "6.1"}
    #     type: wasm
  3. build-windows missing fail_ci_if_error: true — the Ubuntu job sets it for Codecov uploads but Windows doesn't. Inconsistency.

  4. jlumbroso/free-disk-space@v1.3.1 in build-android — third-party actions should ideally be pinned to a commit SHA for security, especially on a public repo. Using a mutable tag means a compromised release could execute arbitrary code in CI.

  5. build-ubuntu removes the scheme parameter — the old config passed scheme: ${{ env.PACKAGE_NAME }}. The new config removes it and passes type: ${{ matrix.type }}. Confirm brightdigit/swift-build@v1 correctly infers the scheme when type is set, otherwise Ubuntu builds may silently test the wrong target.

  6. configure job skip condition — for push events with ci skip in the commit message, the configure job is skipped, but the downstream jobs depend on configure outputs. Verify the if: ${{ !cancelled() && needs.configure.result == 'success' }} correctly short-circuits all downstream jobs in that case (not just !cancelled()).


.swift-format Changes

Several rule changes reduce enforcement:

  • AllPublicDeclarationsHaveDocumentation: false — public API documentation is no longer required
  • ValidateDocumentationComments: false — doc comment correctness won't be checked
  • BeginDocumentationCommentWithOneLineSummary: true — this is a tightening, good
  • NoLeadingUnderscores: false — allows _-prefixed identifiers, which can make private/internal intent ambiguous

If these are intentional relaxations (e.g., because auto-generated files caused noise), consider adding a comment to the config file explaining why.


.swiftlint.yml Changes

  • line_length: [90, 90] (same warning and error threshold) — this is very strict and may produce a flood of warnings on existing code. A graduated threshold like [100, 120] is more common.
  • function_body_length: [35, 50] — reduced from [50, 76]. 35 lines is quite tight for functions with switch statements or guard chains.
  • todo added to disabled_rules — TODO comments will no longer be flagged. This is usually fine during active development but worth noting.
  • strict_fileprivate disabled — allows mixing fileprivate and private without enforcement.

SundialLogger.swift Refactor — Good improvement

The restructuring from a single @available-annotated enum to a single top-level enum with per-property @available annotations is cleaner and more accurate (each property has its own availability, not tied to the enum). The FallbackLogger struct for non-Apple platforms is a solid addition for Linux/Windows/Android support.

One observation: the custom() factory methods were removed. If any internal code in the packages uses SundialLogger.custom(...), this is a breaking internal API change. Confirm no callers exist.


Deleted Files

  • .mcp.json — contained only placeholder API key strings (YOUR_ANTHROPIC_API_KEY_HERE). Correct to remove from the repo.
  • .env.example — same, template only. Fine to remove.

New Test Files

The PR adds a large number of new test files (ConnectivityErrorComprehensiveTests, NetworkMonitorTests+ConcurrencyTests, ObserverRegistryTests+NotifyTests, etc.). These are hard to review without the corresponding source changes in context, but the naming follows the project convention (Swift Testing framework, Mock-prefixed mocks). The concurrency-specific test files (+ConcurrencyTests) suggest race condition coverage is being added, which is valuable given the actor-based architecture.


Summary

Area Assessment
CI workflow structure ✅ Good overall, see checkout@v6 concern
New platform targets (Windows, Android) ✅ Solid addition
.swift-format changes ⚠️ Some relaxations, document rationale
.swiftlint.yml changes ⚠️ line_length: [90, 90] may be too aggressive
SundialLogger refactor ✅ Cleaner and correct
PR scope/title ⚠️ Misleading title, many unrelated changes bundled
Labels ❌ Missing required component labels per CLAUDE.md

The most critical item to resolve before merge is actions/checkout@v6 — if that version doesn't exist, CI will fail on every job in this workflow.

@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

This PR is a significant infrastructure overhaul covering CI/CD refactoring, linting configuration, test organization, and cleanup of committed AI tooling files. Overall direction is positive, but there are a few issues worth addressing.


Critical: actions/checkout@v6 Does Not Exist

In .github/workflows/SundialKit.yml, every job uses actions/checkout@v6, but the latest stable release of that action is v4. This will cause all jobs to fail with a resolution error.

Note that sundial-demo.yml still correctly uses actions/checkout@v4, so there's an inconsistency between the two workflows. Fix by pinning to actions/checkout@v4 (or a specific SHA-pinned version).


Warning: WASM Build Types Are Configured But Disabled

The Ubuntu matrix in SundialKit.yml outputs ubuntu-type values including "wasm" and "wasm-embedded", but the matrix expansion line is commented out. The configure job is doing unnecessary work computing a value that isn't consumed. Either wire it in or remove the ubuntu-type output entirely to avoid confusion about future intent.


Code Quality: TestObserverImpl.swift File Header Mismatch

The newly created file Tests/SundialKitCoreTests/TestObserverImpl.swift has an incorrect comment header — the top comment says ObserverRegistryTestHelpers.swift. This is likely a copy-paste artifact; the file name in the header should match the actual file name.


Minor: Copyright Year Inconsistency

Source files have their copyright year updated to 2026, but newly added test files (e.g., TestObserverImpl.swift, ObserverRegistryTests+NotifyTests.swift, NetworkMonitorTests+ObserverTests.swift, SerializationErrorLocalizedErrorTests+BinaryTests.swift) use 2025. Consider aligning all new files to 2026 for consistency.


Observation: Swift Format Rules Relaxation

The .swift-format changes disable several documentation enforcement rules that are relevant for a library:

  • AllPublicDeclarationsHaveDocumentation: true -> false
  • ValidateDocumentationComments: true -> false
  • NoLeadingUnderscores: true -> false
  • UseLetInEveryBoundCaseVariable: true -> false

Disabling AllPublicDeclarationsHaveDocumentation and ValidateDocumentationComments together means public API can now go undocumented and existing docs won't be validated. If the motivation is to accommodate new code that isn't yet documented, consider keeping validation enabled and only relaxing AllPublicDeclarationsHaveDocumentation temporarily.


Positive Changes

  • Dynamic build matrix: The configure job cleanly separates full vs. reduced builds by branch, avoiding expensive full-matrix runs on tag pushes.
  • Windows CI: Adding Windows 2022/2025 with Swift 6.1/6.2/6.3 is excellent for cross-platform library coverage.
  • Concurrency group: cancel-in-progress: true correctly prevents redundant runs on rapid pushes.
  • Coverage guards: Conditionalizing coverage steps on steps.build.outputs.contains-code-coverage avoids spurious failures.
  • Test file extraction: Splitting TestObserverImpl, TestBinaryMessage, and ObserverRegistryTests+NotifyTests into separate files aligns with the new one_declaration_per_file lint rule — no test logic was lost.
  • Action version bumps: Upgrading codecov-action, swift-coverage-action, and mise-action is appropriate maintenance.
  • Line length tightening (108 -> 90): The resulting reformatting throughout source files improves readability.
  • Cleanup: Removing .mcp.json, .env.example, .claude/ Task Master files, and .cursor/ files is the right call — those belong in .gitignore, not the repo.

Summary

Severity Issue
Critical actions/checkout@v6 is not a valid action version — will break all CI jobs
Warning WASM ubuntu-type matrix output is computed but never consumed (commented out)
Warning AllPublicDeclarationsHaveDocumentation + ValidateDocumentationComments disabled together
Minor File header says ObserverRegistryTestHelpers.swift in TestObserverImpl.swift
Minor Copyright year inconsistency (2025 in new test files, 2026 elsewhere)

The actions/checkout@v6 issue should be resolved before merging.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

This PR does a lot of good work — CI modernization, cross-platform expansion, and lint enforcement. Here's targeted feedback by area.


CI/CD: .github/workflows/SundialKit.yml

Strengths:

  • Dynamic configure job for conditional matrix (full vs. minimal) is a clean pattern.
  • concurrency group with cancel-in-progress: true is good resource management.
  • Conditional coverage upload (steps.build.outputs.contains-code-coverage == 'true') is more correct than unconditional uploads.
  • Adding Android and Windows CI coverage is a meaningful improvement.

Issues:

  1. Commented-out wasm matrix lines — The type and exclude lines for wasm builds are commented out in the build-ubuntu matrix. If wasm support is deferred, track it in an issue and remove the dead comments. Commented-out CI config is harder to reason about than clean config.

  2. actions/checkout@v6v4 was the latest stable release as of early 2026. Using v6 risks referencing a tag that doesn't yet exist or is a pre-release. Consider pinning to a verified version like v4 or a specific SHA for predictable behavior.

  3. Missing install: true in lint job's mise setup — The lint job uses jdx/mise-action@v4 with only cache: true, omitting install: true. The sundial-demo.yml lint steps still include it. If mise tools aren't pre-cached, this could silently fail to install them.

  4. PACKAGE_NAME env duplicationPACKAGE_NAME: SundialKit is defined at the workflow-level env block and repeated in both build-macos and build-macos-full job-level env blocks. The job-level definitions are redundant and could diverge silently.

  5. build-ubuntu drops the scheme input — The old config passed scheme: ${{ env.PACKAGE_NAME }} to brightdigit/swift-build. The new version omits it, relying on the action to auto-detect the scheme. If swift-build@v1 handles this correctly it's fine, but it's a silent behavior change worth verifying.


Source Code: SundialLogger.swift / SundialLogger+Custom.swift

SundialLogger.swift — Good refactor. Lifting the type out from behind the #if canImport(os) guard and adding FallbackLogger for non-Apple platforms is the right approach for cross-platform support.

SundialLogger+Custom.swift — The custom(subsystem:category:) factory method returns Logger on Apple platforms and FallbackLogger on others. These are unrelated types, so callers must branch on #if canImport(os) to store or use the result. Consider whether FallbackLogger should conform to a shared protocol (e.g., SundialLogging) so callers can write a single code path against a common interface.


Tests

TestObserverImpl.swift:

  • The file header comment says ObserverRegistryTestHelpers.swift but the file is named TestObserverImpl.swift. Mismatch should be corrected.
  • The class uses @unchecked Sendable. CLAUDE.md notes the project targets zero @unchecked Sendable outside legacy compatibility. Even for test helpers, consider whether a simple actor or Mutex-guarded struct could replace this.

TestBinaryMessage.swift:

  • Copyright reads © 2025 BrightDigit while most files in this PR update to 2026. Minor inconsistency.
  • The binary encoding/decoding uses raw memory layout (withUnsafeBytes { $0.load(as: UInt32.self) }) without byte-order guarantees. Fine for test-only code, but a comment noting the same-endian assumption would help.

ConnectivityErrorComprehensiveTests.swift:

  • Removing the unused descriptionExists/failureExists/suggestionExists variable assignments is correct cleanup.

SwiftLint Configuration (.swiftlint.yml)

Removing first_where, multiline_arguments, and commenting out closure_body_length relaxes rules to make existing code pass CI rather than fixing the code to meet the rules. Worth flagging:

  • first_where enforces idiomatic first(where:) over filter { }.first — a worthwhile rule to keep.
  • multiline_arguments — removing this may make long call sites harder to read over time.
  • closure_body_length — commented out (#- closure_body_length) rather than removed; if being disabled intentionally, removing it entirely is cleaner than a #- comment.

Where specific callsites genuinely can't satisfy a rule, inline // swiftlint:disable:next suppressions with a comment are preferable to globally relaxing the rules.


Cleanup (Positive)

  • Removing .claude/, .cursor/, .env.example, and .mcp.json is correct — these are local development artifacts that shouldn't be in version control.
  • Updating the devcontainer extension from sswiftlang.swift to swift-server.swift is the correct extension ID.

leogdion and others added 2 commits April 8, 2026 09:29
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ort(Dispatch)

DispatchQueue is not available on WASM, so wrap NetworkMonitoring,
PathMonitor, NetworkMonitor, and PingCoordinator with #if canImport(Dispatch).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

Note: This PR title undersells the scope — it covers CI modernization, Windows CI, lint/format reconfiguration, devcontainer updates, source copyright updates, and cleanup of committed AI tooling. Worth updating the title for future readers.


✅ Positives

  • Dynamic build matrix via configure job is a clean improvement. On push to tags (non-main), CI runs a minimal matrix, while PRs and main get the full coverage. This is the right tradeoff between speed and completeness.
  • Concurrency groups with cancel-in-progress: true prevent redundant runs on rapid pushes.
  • Windows CI (build-windows) is a meaningful addition for cross-platform library validation.
  • Conditional coverage upload (if: steps.build.outputs.contains-code-coverage == 'true') is cleaner than unconditionally running the coverage step and having it fail silently.
  • Removing AI tooling files (.claude/, .cursor/, .mcp.json, .env.example) from the repo is correct — these are developer-local tools that don't belong committed in a library repo.
  • DevContainer updated to Swift 6.3 and VS Code extension ID corrected from sswg.swift-langswift-server.swift.

Issues & Observations

CI/CD

1. wasm and wasm-embedded types in the Ubuntu build matrix

echo 'ubuntu-type=["","wasm","wasm-embedded"]' >> "$GITHUB_OUTPUT"

SundialKit uses WatchConnectivity (Apple-platform-only) and Network.framework. Neither is available for WASM targets. These build types will fail unless brightdigit/swift-build@v1 silently skips unavailable targets. This needs verification — you may want to validate this runs successfully before merging.

2. sundial-demo.yml inconsistency

The main workflow was updated to actions/checkout@v6, but sundial-demo.yml was only bumped to @v4 for checkout. Minor but worth keeping consistent.

3. build-windows coverage upload missing files: parameter

- name: Upload coverage to Codecov
  if: steps.build.outputs.contains-code-coverage == 'true'
  uses: codecov/codecov-action@v6
  with:
    fail_ci_if_error: true
    flags: swift-${{ matrix.swift.version }},windows
    ...
    # No 'files:' parameter

Unlike the Ubuntu job which passes files: ${{ join(fromJSON(steps.coverage-files.outputs.files), ',') }} from swift-coverage-action, the Windows job skips the coverage file discovery step entirely and doesn't specify files:. Codecov may not find the coverage data. Either add sersoft-gmbh/swift-coverage-action@v5 for Windows too, or document that Windows coverage isn't expected.

4. build-macos drops older Xcode versions from the fast path

The non-full build-macos job now contains only Xcode_26.4 / macos-26. On tag pushes, CI won't validate against Xcode 16.4 / macOS 15. This is likely fine given the intent of the matrix split, but worth noting that older-Xcode regressions won't surface on tag builds.


Linting & Formatting

5. AllPublicDeclarationsHaveDocumentation: false + ValidateDocumentationComments: false

These two changes together substantially reduce documentation enforcement for a library package. Public API consumers rely on inline docs for discovery. If these were relaxed to address specific noisy false positives, it's worth commenting why in the config. If the intent is to let docs slide, that warrants a separate discussion — the CLAUDE.md architecture section implies this codebase prioritizes documentation.

6. Line length set to 90/90 (warning = error)

line_length:
  - 90
  - 90

Setting both thresholds to the same value eliminates the warning-before-error gradient. Any line exceeding 90 characters immediately errors. This is quite strict — consider 90/120 or similar so developers have a warning window before it blocks CI.

7. Tightened thresholds may break existing code in CI

The following are significantly tightened from the previous config:

  • cyclomatic_complexity: error 12 → 8
  • function_body_length: 50/76 → 35/50
  • type_body_length: newly added at 125/200

Have these been validated against the existing source? If not, CI may fail immediately on merge.

8. Inconsistent commenting style in swiftlint.yml

#- closure_body_length   # ← no space after #
# - discouraged_optional_boolean  # ← space after #

These commented-out entries in the opt_in_rules list create visual noise. If a rule is intentionally disabled, remove it from the list. If it's a "maybe later," a trailing comment is clearer.


Minor

  • identifier_name.excluded swaps no for db. If any Swift pattern matches use no as a binding variable name (common in boolean-flavored patterns), that identifier will now trigger the linter.
  • NoLeadingUnderscores: false in swift-format allows underscore-prefixed names — ensure this doesn't conflict with SwiftLint's identifier_name which still enforces naming conventions separately.

Summary

The CI restructure (dynamic matrix, Windows support, concurrency control) is solid and represents a genuine improvement. The main things to address before merging:

  1. Validate wasm/wasm-embedded builds actually succeed (or exclude them if not intended).
  2. Fix or document missing coverage upload on Windows.
  3. Verify the tightened SwiftLint thresholds don't immediately break CI on existing code.
  4. Reconsider the full relaxation of AllPublicDeclarationsHaveDocumentation + ValidateDocumentationComments for a library package.

- Add isWasm boolean (via canImport(Dispatch)) to SundialKitCoreTests
  and SundialKitNetworkTests
- Wrap MockPathMonitor (both targets) in #if canImport(Dispatch)
- Guard each test function body with #if canImport(Dispatch)/#else,
  recording an issue in the #else branch as a safety net
- Add .disabled(if: isWasm) trait to NetworkMonitorTests suite

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

This PR touches a large surface area (159 files, +2375/-6739 lines) despite the narrow title. Here's a breakdown by area:


PR Title / Scope

Suggestion: The title "chore: Update SundialKit.yml" significantly understates the scope. This PR also includes test reorganization, swift-format rule changes, devcontainer updates, and removal of tooling files (.claude/, .cursor/, .mcp.json). A more accurate title would help reviewers and the git history.


CI Workflow (.github/workflows/SundialKit.yml) — Main Change

Overall this is a solid improvement. A few points:

Positives:

  • Concurrency control (cancel-in-progress: true) is a great addition — avoids wasted minutes on stale runs.
  • Dynamic configure job cleanly separates full-matrix (main/PR) from minimal-matrix (tag) builds.
  • paths-ignore for .md/Docs avoids unnecessary CI runs on doc-only changes.
  • Windows and Android build support broadens the coverage matrix.
  • Conditional coverage upload (if: steps.build.outputs.contains-code-coverage == 'true') is the right pattern for WASM/non-coverage build types.
  • Action upgrades (codecov v4→v6, swift-coverage v4→v5, mise-action v2→v4) are appropriate.

Concerns:

  1. Nightly Swift builds removed. The old config had a nightly: true entry for Swift 6.2. The new config has no nightly builds. Is this intentional? Nightly builds are useful for catching regressions before official Swift releases.

  2. pull_request trigger no longer covers non-main target branches. The new config only triggers PRs targeting main or version tags. If you ever open a PR targeting a feature branch (e.g., for stacked PRs), CI won't run. The old config ran on all pushes not matching *WIP. Worth confirming this is deliberate.

  3. sundial-demo.yml inconsistency. The demo workflow still uses actions/checkout@v4 while the main workflow moved to v6. Minor, but worth aligning.

  4. Lint job needs array. Adding build-windows, build-macos-full, build-android as dependencies of lint means the lint job is blocked until all platform builds complete. Given that !failure() is used to allow skipped jobs, this should work correctly — just verify the intent is to always lint after all available builds.

  5. build-ubuntu matrix explosion. The new matrix is os × swift × type (2 × 3 × 3 = 18 jobs). Confirm the WASM and WASM-embedded types are properly supported for all OS/Swift version combinations, or add exclusions for unsupported pairs.


swift-format Rule Changes (.swift-format)

Rule Before After Impact
AllPublicDeclarationsHaveDocumentation true false Public API may ship without docs
AlwaysUseLiteralForEmptyCollectionInit false true Enforces [] over Array<T>() — good
BeginDocumentationCommentWithOneLineSummary false true Enforces one-line summary in doc comments — good
NoLeadingUnderscores true false Allows _-prefixed public symbols
UseLetInEveryBoundCaseVariable true false Allows var in pattern bindings

Concerns:

  • Disabling AllPublicDeclarationsHaveDocumentation reduces documentation enforcement for a public library. This seems at odds with enabling BeginDocumentationCommentWithOneLineSummary in the same commit. Consider keeping the documentation requirement on — or clarify the rationale.
  • Disabling NoLeadingUnderscores for a public library is unusual. _-prefixed names in public APIs are unconventional in Swift. If this is needed to allow internal naming patterns, consider scoping it more carefully.

Test Reorganization

The split of large test files into focused extension files (e.g., ConnectivityErrorTests+HashableTests.swift, ObserverRegistryTests+NotifyTests.swift) follows Swift conventions and improves navigability. Good practice.

The addition of WASMSupport.swift shims in test targets suggests WASM build compatibility is being systematically addressed — aligns well with the CI matrix additions.


Tooling Cleanup (.claude/, .cursor/, .mcp.json, .taskmaster/)

Large deletion of Task Master commands, agent definitions, Cursor rules, and MCP config. If these were committed inadvertently (e.g., local dev tooling that shouldn't be in the repo), this cleanup is correct. Just confirm none of these are shared team tooling that others depend on.


Minor Nits

  • Several Swift source files show only copyright year changes (2025→2026) and doc comment reformatting. If these are purely mechanical, consider whether they add review noise without benefit — they make the diff harder to read.
  • The old build-ubuntu matrix had a duplicate version: "6.2" entry (one regular, one nightly). The new config correctly deduplicates — good catch.

Summary

The CI restructuring is well-thought-out. The main open questions are around the removal of nightly builds, the AllPublicDeclarationsHaveDocumentation rule change, and whether the PR title should better reflect the scope. Everything else looks solid.

leogdion and others added 2 commits April 8, 2026 10:14
…agable tests

WASM32 requires aligned memory access; Data buffers are not guaranteed
4-byte aligned, causing a fatal crash with load(as:). loadUnaligned
is safe and correct on all platforms.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

PR Review: chore: Update SundialKit.yml

Substantial infrastructure PR covering CI/CD overhaul, linting config tightening, WASM/cross-platform support, and housekeeping. Overall direction is solid.


CI/CD Workflow (.github/workflows/SundialKit.yml)

Positive changes:

  • Concurrency groups (cancel-in-progress: true) are a great improvement for PRs
  • Dynamic matrix via the configure job is a clean pattern for tiered builds (fast feedback on branches, full matrix on main/PRs)
  • Conditional coverage upload prevents spurious failures on platforms without coverage output
  • Updated action versions (checkout@v6, codecov-action@v6, swift-coverage-action@v5)
  • New Windows and Android CI jobs broaden platform coverage
  • Removed the duplicate Swift 6.2 matrix entry from the old workflow

Potential issue - tagged releases get the reduced matrix:

The configure job selects full vs. minimal matrix based on github.ref == refs/heads/main or github.event_name == pull_request. For a version tag push (refs/tags/v2.0.0), neither condition matches - releases would run only noble + Swift 6.3, skipping the full compatibility matrix. Consider adding a check for the refs/tags/ prefix to trigger the full matrix on releases.

Minor: build-macos step passes undefined matrix keys

The matrix only defines runs-on and xcode, but the build step also passes type, deviceName, osVersion, and download-platform. The comment explains matrix.type is intentionally empty but does not cover the others. Worth updating the comment or removing the unused inputs from the step.


Swift Format Config (.swift-format)

Two doc-related changes pull in opposite directions:

  • AllPublicDeclarationsHaveDocumentation: true to false - relaxes requirements
  • BeginDocumentationCommentWithOneLineSummary: false to true - tightens existing docs

Result: docs are no longer required, but when present must open with a one-liner. Reasonable stance, but a deliberate policy change worth calling out.

Other notable changes:

  • NoLeadingUnderscores: true to false (allows the _backing convention)
  • ValidateDocumentationComments: true to false (disables doc comment syntax validation)

SwiftLint Config (.swiftlint.yml)

  • cyclomatic_complexity error threshold tightened from 12 to 8: good
  • line_length set to 90/90 (same warning and error threshold): strict; confirm lint still passes cleanly on existing code
  • todo added to disabled rules: reasonable for a library
  • Adding one_declaration_per_file is consistent with extracting TestBinaryMessage into its own file

Source Code Changes

#if canImport(Dispatch) guards on NetworkMonitoring, NetworkMonitor, and PingCoordinator correctly gates Dispatch-dependent code for WASM/non-Dispatch targets - consistent with the new wasm/wasm-embedded CI build types.

SundialLogger refactor - consolidating two conditional enum SundialLogger definitions into one with internal #if canImport(os) guards is cleaner. Moving @available to individual static let properties is the right approach for mixed-availability declarations.

TestBinaryMessage - safety fix: The extracted file replaces .load(as: UInt32.self) with .loadUnaligned(as: UInt32.self). The original had undefined behavior on unaligned memory. Good catch.


Deleted Files (.claude/, .mcp.json)

Removing Task Master agent definitions, command guides, and .mcp.json is correct - local developer tooling configs should not live in version control.


Nits

  • Tests/SundialKitConnectivityTests/TestBinaryMessage.swift still has copyright year 2025 while all other files were updated to 2026.
  • sundial-demo.yml pins swift-build to v1.5.2 while SundialKit.yml uses the floating v1. Worth aligning one way or the other.

Summary

The main functional concern before merge: tagged releases run the reduced matrix, which could miss compatibility regressions at release time. The loadUnaligned fix is a genuine correctness improvement worth highlighting separately. Everything else is infrastructure cleanup or policy refinement.

@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

Code Review

This PR primarily updates the CI/CD workflow (SundialKit.yml) with significant infrastructure improvements, alongside copyright updates, code formatting, test refactoring, and devcontainer updates.


CI/CD Workflow (SundialKit.yml) — The Core Change

Positives:

  • Dynamic build matrix via the new configure job is a solid pattern. Running the full matrix only on main and PRs while using a minimal matrix elsewhere reduces CI costs meaningfully.
  • Concurrency groups (cancel-in-progress: true) are a welcome addition — no more duplicate runs from rapid pushes.
  • paths-ignore for docs/markdown skips unnecessary CI on documentation PRs.
  • Expanded platform coverage (Windows, Android, tvOS, macOS native builds) is good for a cross-platform Swift library.
  • !failure() guard on lint job is the correct approach when some upstream jobs are conditionally skipped. The inline comment explaining this is helpful.
  • Codecov flags now use comma-separated platform + swift-version format, which improves coverage organization.

Issues to verify:

  1. actions/checkout@v6 — Used throughout SundialKit.yml but sundial-demo.yml still uses actions/checkout@v4. Please verify @v6 is a valid published release; if not, @v4 is the current stable tag. Mixing versions between workflow files is also worth aligning.

  2. Swift 6.3 container availability — The ubuntu-swift matrix includes {"version":"6.3"}, which requires swift:6.3-noble and swift:6.3-jammy Docker images to exist on Docker Hub. Worth confirming before merging.

  3. Xcode 26.4 — Several matrix entries reference /Applications/Xcode_26.4.app on macos-26 runners. Please verify this version is available on that runner image.

  4. build-ubuntu WASM excludes — The excludes correctly prevent WASM/WASM-embedded builds with Swift 6.1. Consider also adding a comment explaining why (WASM support was added in Swift 6.2+) for future maintainers.

  5. build-macos uses matrix.type where the matrix only defines runs-on and xcode (no type key). This evaluates to an empty string, which is likely handled gracefully by brightdigit/swift-build@v1, but it's worth a quick confirmation that the action accepts an absent/empty type.


TestBinaryMessage.swift — Bug Fix

// Before (unsafe for unaligned Data buffers):
value = data.withUnsafeBytes { $0.load(as: UInt32.self) }

// After (correct):
value = data.withUnsafeBytes { $0.loadUnaligned(as: UInt32.self) }

Good fix. Data buffers don't guarantee alignment, so load(as:) could trigger a trap on strict-alignment architectures. loadUnaligned is the right choice here.

The test helper struct was also correctly moved to its own file (TestBinaryMessage.swift) to comply with the newly added one_declaration_per_file SwiftLint rule — good consistency.


Copyright year inconsistency

The new TestBinaryMessage.swift file (line 8397) has Copyright © 2025 while all other source files in this PR are being updated from 2025 → 2026. This is a minor nit, but worth aligning.


Cleanup — Good Hygiene

  • Removing .mcp.json (which contained placeholder strings like "YOUR_ANTHROPIC_API_KEY_HERE") avoids confusing contributors who might copy it without filling in real values.
  • Removing .claude/ command files and .cursor/ rules cleans up project-specific AI assistant configs that shouldn't be in the main repo.
  • VS Code extension ID update (sswg.swift-langswift-server.swift) reflects the extension rename.

Summary

The CI restructuring is well thought out. The main pre-merge items to confirm are: (1) actions/checkout@v6 validity, (2) Swift 6.3 Docker image availability, and (3) Xcode 26.4 on macos-26. The loadUnaligned fix is a genuine correctness improvement. Everything else is cleanup and maintenance.

@claude
Copy link
Copy Markdown

claude bot commented Apr 8, 2026

Code Review

Overview

The PR title says "chore: Update SundialKit.yml" but this touches ~100 files including source code, tests, devcontainers, and extensive tooling cleanup. Worth considering a more descriptive title (e.g., feat(ci): add Windows/Android builds, WASM support, and CI/CD overhaul) so the scope is clear in the git history.


CI/CD Workflow (.github/workflows/SundialKit.yml)

Positives:

  • Dynamic matrix via configure job is a clean pattern for controlling build scope on PRs vs. main vs. tags
  • concurrency group with cancel-in-progress: true is a good addition
  • Coverage conditioned on steps.build.outputs.contains-code-coverage avoids spurious failures
  • paths-ignore for **.md / Docs/** saves CI minutes on docs-only changes

Issues:

  1. actions/checkout@v6, codecov/codecov-action@v6, jdx/mise-action@v4 — If these versions don't exist yet on the runner marketplace, the entire workflow will fail on first run. Verify each action version tag resolves before merging.

  2. Nightly Swift builds silently dropped — The old matrix included a nightly: true entry for Swift 6.2. The new configure job outputs only 6.1, 6.2, 6.3 (no nightly). If nightly builds were intentionally removed, a comment in the workflow or PR description would help future maintainers understand why.

  3. Old Ubuntu matrix had a duplicate entry (both entries had version: "6.2", one was presumably meant to be nightly). This is fixed by the new approach — good catch.

  4. build-macos now has a single entry (Xcode 26.4 on macos-26 only). The old matrix covered multiple Xcode versions on both macos-15 and macos-26. Confirm build-macos-full fills in what's needed.

  5. Lint step: Removing set -e is fine since GitHub Actions exits on non-zero by default. The change is equivalent.


Source Code

SundialLogger.swift — Unifying the two separate #if canImport(os) enum declarations into a single package enum SundialLogger with internal conditional sections is cleaner.

One concern: FallbackLogger.init changed from fileprivate (old) to internal (new). This widens visibility so package-internal callers can directly construct FallbackLogger without going through the static logger properties. If FallbackLogger is an implementation detail, keeping the initializer private or fileprivate is more appropriate. Check whether SundialLogger+Custom.swift actually requires the internal access.

NetworkMonitoring.swift — Wrapping the entire protocol in #if canImport(Dispatch) for WASM support is the right approach. Make sure all conforming types and any code that references NetworkMonitoring is similarly guarded.


Test Coverage

ObserverRegistryTests.swift — Five tests were removed without explanation:

  • testStrongReferencesKeepObserversAlive — memory management behavior
  • testNotify — core notification path
  • testNotifyWithStrongReferences
  • testSnapshotEmpty
  • testSnapshot

TestObserverImpl was extracted to its own file (good), but the tests themselves appear deleted rather than moved. If these test the core ObserverRegistry.notify / snapshot behavior and that implementation hasn't changed, this is a coverage regression. Please either restore them or explain why they're no longer valid.

NetworkMonitorTests.swift — Guarding tests with #if canImport(Dispatch) and adding Issue.record() fallbacks for non-Dispatch platforms is the right approach for WASM builds.


Tooling Cleanup

Removing .claude/, .cursor/, .env.example, .mcp.json, and all TaskMaster slash-command files is reasonable housekeeping. Confirm this won't break other contributors' local setups or any CI integrations that reference these files.


Summary

Area Status
CI/CD: Windows, Android, dynamic matrix, concurrency Good direction
Action version availability (@v6, @v4) Verify before merging
Nightly Swift build coverage Confirm intentional removal
SundialLogger unification Cleaner
FallbackLogger.init access widening (fileprivate → internal) Revisit if it should stay private
NetworkMonitoring WASM guard Correct approach
ObserverRegistry test deletions Coverage regression — needs explanation or restoration
PR title vs. actual scope Misleading — consider updating

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@leogdion leogdion changed the title chore: Update SundialKit.yml chore(ci): overhaul CI matrix, add Windows/Android builds, clean up tooling scaffolding Apr 10, 2026
@leogdion leogdion added component:infrastructure Build, CI/CD, tooling, git-subrepo component:tests Testing infrastructure and Swift Testing migration component:core SundialKitCore protocols and types labels Apr 10, 2026
@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

PR Review: CI Overhaul, Windows/Android Builds, Tooling Cleanup

Overall this is a solid infrastructure improvement. The concurrency group, matrix factorization into a configure job, and action upgrades are all good. Below are findings organized by area.


CI Workflow — .github/workflows/SundialKit.yml

Positive changes

  • Concurrency control (cancel-in-progress: true) prevents wasted runner minutes on stale runs.
  • Dynamic configure job cleanly separates matrix decisions from execution — feature branches get a slim matrix, main/PRs get the full one.
  • Consolidated coverage flags (matrix.type && format(...)) are more maintainable than duplicated per-job blocks.
  • Path-based triggers (paths-ignore: '**.md') avoid spurious CI runs on docs changes.
  • build-macos now consolidates the shared steps (coverage, upload) that were previously duplicated across Xcode version variants.

Issues

1. build-macos matrix is missing all but one entry

The diff shows only a single SPM entry survived the refactor:

matrix:
  include:
    # SPM Build — no platform type; matrix.type evaluates to '' by design
    - runs-on: macos-26
      xcode: "/Applications/Xcode_26.4.app"

The Xcode 16.4 / macOS 15 row and all iOS/watchOS/tvOS/visionOS entries moved to build-macos-full, but build-macos-full is only triggered on full-matrix == 'true' (main/PRs). This means on PRs, iOS/watchOS/tvOS tests never run via build-macos, they only run through build-macos-full. That's probably intentional — but worth confirming the build-macos job is still worth keeping if it only tests one SPM configuration.

2. Duplicate if: key in Ubuntu build steps

The diff shows two consecutive if: steps.build.outputs.contains-code-coverage == 'true' lines before the codecov/codecov-action@v6 step. In YAML, duplicate keys are undefined behavior (last one wins in most parsers, but GitHub Actions may reject this). Verify the rendered YAML doesn't have this issue.

# Appears in the diff as:
        if: steps.build.outputs.contains-code-coverage == 'true'
        if: steps.build.outputs.contains-code-coverage == 'true'  # duplicate
        uses: codecov/codecov-action@v6

3. Windows/Android builds — scope clarification needed

Adding these is fine if the intent is to verify that SundialKitCore (pure Swift, no Apple framework dependencies) compiles cross-platform. The #if canImport(Dispatch) guards added to NetworkMonitor.swift and test helpers support this goal. However:

  • SundialKitConnectivity is already gated behind #if canImport(WatchConnectivity), so it will silently produce an empty module on these platforms — which is correct, but it's worth adding a comment in the workflow explaining this is intentional.
  • The swift_project: SundialKit key in the Windows Codecov upload step is non-standard; the standard key is directory: or files:. Verify this is supported by brightdigit/swift-build@v1.

4. build-windows / build-android use if: needs.configure.outputs.full-matrix == 'true' without !cancelled()

Compare to build-macos:

# build-macos (correct):
if: ${{ !cancelled() && needs.configure.result == 'success' }}

# build-windows / build-android (missing !cancelled()):
if: needs.configure.outputs.full-matrix == 'true'

If configure is cancelled, full-matrix output won't exist and this evaluates to false (skipped, not failed), which is benign — but inconsistent. Consider aligning for clarity.

5. swift-build unpinned to @v1

Changed from @v1.4.0@v1. This will pick up any future v1.x patch/minor automatically. Fine if intentional, but @v1.4.0 was more reproducible. Consider whether the looser pin is desired.


Source Code Changes — Sources/

Changes are minimal and correct:

  • #if canImport(Dispatch) wrapping in NetworkMonitor.swift — enables WASM compilation (Dispatch isn't available on WASM). Clean and correctly scoped.
  • Copyright year (2025 → 2026) — appropriate.
  • Line-length reformatting in doc comments and string literals — matches swift-format line-length rule enforcement.

No concurrency regressions. Actor isolation and Sendable conformances are preserved throughout.


Tests — Tests/

  • ObserverRegistryTests split into ObserverRegistryTests.swift + ObserverRegistryTests+NotifyTests.swift + TestObserverImpl.swift — directly driven by the new one_declaration_per_file SwiftLint rule. All original test coverage is retained.
  • WASMSupport.swift — the isWasm constant is a clean way to skip WASM-incompatible tests. Good pattern.
  • MockPathMonitor wrapped in #if canImport(Dispatch) — consistent with the production code guard.

No test coverage regressions identified.


Dev Tooling Cleanup

Removing .claude/ (Task Master commands/agents) and .cursor/ (IDE rules/MCP config) is appropriate — these are AI-assistant scaffolding files that shouldn't live in the library repo. No impact on library code or CI.


.swiftlint.ymlone_declaration_per_file addition

Good rule to add. It's already enforced by the test reorganization in this PR.


Summary

Area Status Notes
Concurrency control / matrix config Clean improvement
build-macos matrix entries ⚠️ Only 1 entry remains — intentional?
Duplicate if: key (Ubuntu steps) ⚠️ Verify YAML renders correctly
Windows/Android scope ⚠️ Fine if intent is core-only compilation; add a comment
build-windows/android missing !cancelled() ℹ️ Minor inconsistency
swift-build@v1 unpinned ℹ️ Minor; intentional?
Source code changes Correct, minimal
Test reorganization Coverage maintained
Dev tooling cleanup Appropriate
SwiftLint rule addition Good practice

The two items worth addressing before merge are the build-macos matrix completeness question and the duplicate if: key in the Ubuntu build steps. The Windows/Android scope comment is a nice-to-have for future maintainers.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

PR Review: CI Matrix Overhaul + Windows/Android Builds

Overall this is a solid, well-scoped infrastructure improvement. The dynamic matrix approach, cross-platform expansion, and source-level WASM guards are all good additions. A few issues are worth addressing before merge.


Issues

1. actions/checkout@v6 likely does not exist

All workflows now pin actions/checkout@v6, but the latest stable release is v4 (v5 is unreleased as of this writing). This will likely cause every CI job to fail on first run. Recommend pinning v4 (or confirming v6 is now available) before merging.

2. Undefined matrix.type in build-macos quick job

# build-macos matrix only defines: runs-on, xcode
- runs-on: macos-26
  xcode: "/Applications/Xcode_26.4.app"

The steps reference matrix.type, matrix.deviceName, matrix.osVersion, and matrix.download-platform, which are all undefined for this entry. They evaluate to empty strings — which is likely intentional per the comment — but swift-build@v1 should be verified to accept an empty type gracefully. A type: "" explicit default in the matrix entry would make this less surprising.

3. Non-standard swift_project in build-windows Codecov upload

- name: Upload coverage to Codecov
  uses: codecov/codecov-action@v6
  with:
    swift_project: SundialKit   # not a standard codecov-action input

This field is not documented in codecov/codecov-action. It will be silently ignored, but may indicate a copy-paste from somewhere else. The Windows job also lacks a files: input — coverage upload relies on auto-discovery, which works differently on Windows. Consider whether coverage upload is even expected here.

4. Missing fail_ci_if_error on macOS coverage uploads

The Ubuntu job sets fail_ci_if_error: true on Codecov upload. The macOS jobs (build-macos, build-macos-full) do not. This inconsistency means a Codecov failure would silently pass on macOS but hard-fail on Linux. Either add the flag everywhere or remove it from Ubuntu for consistency.


Minor Notes

  • Android build cost: 2 Swift versions × 4 API levels = 8 jobs on every PR/main push. This could be expensive. Consider whether all 4 API levels are needed on PRs, or if the Android build should also be gated behind full-matrix.

    • Actually, build-android already has if: needs.configure.outputs.full-matrix == 'true' — good. Disregard this if full-matrix is only set for main/PRs intentionally.
  • jdx/mise-action@v4 removes explicit version pin: Previously pinned to 2024.11.0. Removing the pin improves maintenance but makes builds slightly less reproducible for debugging. This is acceptable if .mise.toml pins tool versions (which it does).

  • SundialLogger refactor: Moving FallbackLogger inside the enum and unifying the two #if canImport(os) blocks into one is a cleaner design. Splitting custom() to SundialLogger+Custom.swift for one_declaration_per_file compliance is correct.

  • PathMonitor wrapped in #if canImport(Dispatch): Correct for WASM support. The corresponding MockPathMonitor and PingCoordinator guards are consistent. Good.

  • .mcp.json and .env.example removal: Good — these contained placeholder API key strings that shouldn't live in the repo root. No functional impact.

  • .claude/ scaffolding removal: Removes Task Master command scaffolding. Assuming this was development tooling not intended for the library repo long-term.

  • Copyright year 2025 → 2026: Routine, no concern.


Summary

Area Status
actions/checkout@v6 existence ⚠️ Verify before merge
Undefined matrix fields in build-macos ⚠️ Minor — explicit defaults would clarify intent
swift_project non-standard Codecov field ⚠️ Minor — likely harmless, but should be cleaned up
Inconsistent fail_ci_if_error ⚠️ Minor
WASM platform guards ✅ Correct and consistent
Dynamic CI matrix (configure job) ✅ Clean design
Concurrency cancellation group ✅ Good addition
build-windows / build-android additions ✅ Valuable cross-platform coverage
SundialLogger unification ✅ Cleaner than the old split enum approach
one_declaration_per_file SwiftLint rule ✅ Good enforcement

Add is_key_content_base64: true to app_store_connect_api_key calls to
resolve OpenSSL::PKey::ECError invalid curve name when using a base64-
encoded APP_STORE_CONNECT_API_KEY_KEY secret.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

PR Review: CI Matrix Overhaul & Tooling Cleanup

Overall this is a well-structured CI improvement. The dynamic matrix approach and new platform coverage are solid additions. A few items worth discussing:


Action Version Pinning — Security Concern

Several actions are now using floating major/minor version tags:

uses: brightdigit/swift-build@v1        # was @v1.4.0
uses: sersoft-gmbh/swift-coverage-action@v5  # was @v4
uses: codecov/codecov-action@v6         # was @v4
uses: actions/checkout@v6              # was @v4
uses: mise-action@v4                   # was @v2

Floating tags (@v1, @v6) are mutable — they can be force-pushed to point at a different commit at any time. For security-sensitive workflows (coverage upload, checkout), pinning to a full SHA or at minimum a @vMAJOR.MINOR.PATCH tag is strongly preferred. The brightdigit/swift-build@v1.4.0 pinning that was removed was actually better than the new @v1. Consider using SHA pinning with a comment for auditability:

uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v6

Android Disk Space Cleanup

The aggressive free-disk-space step removes Android tooling, haskell, dotnet, and docker images before the Android build. Make sure the swift-build action doesn't rely on any of those (particularly the Android SDK — the action likely brings its own, but worth verifying the build doesn't fail silently if SDK paths change).


Windows Git Symlink Configuration

git config --global core.symlinks true

This is set at the job level before checkout. This is the right approach for Windows CI, but note it applies globally to the runner's git config. Ensure the checkout step correctly handles symlinks (e.g. .devcontainer symlink conventions, if any).


!failure() Lint Job Condition

The lint job now depends on all platform builds with !failure(). This is fine but non-obvious — !failure() means the job runs even if upstream jobs were skipped (e.g. on feature branches where Android/Windows are excluded). This is intentional and correct, but worth a comment in the YAML to explain the logic for future maintainers.


.env.example Deletion

The .env.example file was deleted without an obvious replacement for documenting required environment variables. If this file was documenting anything project-relevant (API keys, tokens), that context should live somewhere else (CONTRIBUTING.md, README, or GitHub repo secrets docs). If it was purely Task Master scaffolding, the deletion is fine.


WASM Support Files

Tests/SundialKitCoreTests/WASMSupport.swift and Tests/SundialKitNetworkTests/WASMSupport.swift are new. The pattern of gating on canImport(Dispatch) is correct for WASM. Just confirm these files are guarded consistently with how the CI WASM build types (wasm, wasm-embedded) exclude Swift 6.1 — if wasm-embedded has different Dispatch availability, the guard may need to be more specific.


Minor: SwiftLint one_declaration_per_file

Adding one_declaration_per_file as an opt-in rule is a good convention. The existing Swift source files appear to already conform (based on the formatting-only changes in this PR), so this shouldn't cause new violations.


Summary

Area Verdict
Dynamic CI matrix design ✅ Good approach — reduces feature branch CI cost
Windows/Android coverage ✅ Valuable additions
Devcontainer VS Code extension (swift-server.swift) ✅ Correct, better-maintained extension
Copyright year 2025→2026 ✅ Fine
Action version pinning ⚠️ Recommend restoring full version pins or using SHA pinning
.env.example removal ⚠️ Verify nothing documenting actual project vars is lost
Tooling scaffolding cleanup (.claude/, .cursor/) ✅ Clean removal of generated scaffolding

The core CI restructuring is solid. The main ask before merging is addressing the action version pinning for supply-chain security.

…project

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

SundialKit v2.0.0 is a Swift 6.1+ communications library for Apple platforms with a modern three-layer architecture:

Layer 1: Core Protocols (SundialKitCore, SundialKitNetwork, SundialKitConnectivity)

  • Protocol-based abstractions over Apple's Network and WatchConnectivity frameworks
  • Minimal concurrency annotations (Sendable constraints)
  • No observer patterns - pure wrappers

Layer 2: Observation Plugins (Choose your concurrency model)

  • SundialKitStream: Modern actor-based observers with AsyncStream APIs
  • SundialKitCombine: @MainActor-based observers with Combine publishers

Key Features:

  • Network connectivity monitoring using Apple's Network framework
  • WatchConnectivity abstraction for iPhone/Apple Watch communication
  • Multiple concurrency models: Choose actors or @mainactor based on your needs
  • Swift 6.1 strict concurrency compliant (zero @unchecked Sendable in plugins)
  • Full async/await support alongside Combine publishers

Build & Test Commands

Using Make (Recommended)

make build          # Build the package
make test           # Run tests with coverage
make lint           # Run linting and formatting (strict mode)
make format         # Format code only
make clean          # Clean build artifacts
make help           # Show all available commands

Using Swift Directly

swift build
swift test
swift test --enable-code-coverage
swift test --filter <TestName>

Formatting & Linting (Mise)

SundialKit uses mise to manage development tools:

  • swift-format (swiftlang/swift-format@602.0.0) - Official Apple Swift formatter
  • SwiftLint (realm/SwiftLint@0.61.0) - Swift style and conventions linter
  • Periphery (peripheryapp/periphery@3.2.0) - Unused code detection

Install mise (macOS)

curl https://mise.run | sh
# or
brew install mise

Install Development Tools

mise install  # Installs tools from .mise.toml

Run linting script

./Scripts/lint.sh              # Normal mode
LINT_MODE=STRICT ./Scripts/lint.sh  # Strict mode (CI)
FORMAT_ONLY=1 ./Scripts/lint.sh     # Format only

Architecture

Three-Layer Architecture (v2.0.0)

SundialKit v2.0.0 uses a layered architecture separating protocols, wrappers, and observation patterns:

┌─────────────────────────────────────────────────────────────┐
│ Layer 1: SundialKitCore (Protocols)                         │
│ - PathMonitor, NetworkPing, ConnectivitySession protocols   │
│ - Sendable-safe type aliases and errors                     │
│ - No observers, no concurrency primitives                   │
└─────────────────────────────────────────────────────────────┘
         │
    ┌────┴────┐
    │         │
┌───┴────────┐ ┌──┴──────────────┐
│ Layer 1:   │ │ Layer 1:        │
│ Network    │ │ Connectivity    │
│            │ │                 │
│ Raw        │ │ Raw             │
│ wrappers   │ │ wrappers        │
│ over       │ │ over            │
│ NWPath     │ │ WCSession       │
│ Monitor    │ │                 │
└────────────┘ └─────────────────┘
    │                  │
    └────┬─────────────┘
         │
    ┌────┴────┐
    │         │
┌───┴────────┐│       ┌──────────────┐
│ Layer 2:   ││       │ Layer 2:     │
│ Stream     ││       │ Combine      │
│            ││       │              │
│ Actors +   ││       │ @MainActor + │
│ AsyncStream││       │ Publishers   │
│ (modern)   ││       │ (SwiftUI)    │
└────────────┘│       └──────────────┘

Layer 1: Core Protocols & Wrappers

SundialKitCore (Sources/SundialKitCore/)

  • Protocol definitions: PathMonitor, NetworkPing, ConnectivitySession, ConnectivitySessionDelegate
  • Type-safe aliases: ConnectivityMessage = [String: any Sendable]
  • Typed errors: ConnectivityError, NetworkError, SerializationError
  • No concrete implementations, no observers

SundialKitNetwork (Sources/SundialKitNetwork/)

  • Concrete implementations: NWPathMonitor extensions, NeverPing
  • Protocol wrappers around Apple's Network framework
  • PathStatus: Enum representing network state (satisfied, unsatisfied, requiresConnection, unknown)
    • Contains Interface OptionSet (cellular, wifi, wiredEthernet, loopback, other)
    • Contains UnsatisfiedReason enum for failure details

SundialKitConnectivity (Sources/SundialKitConnectivity/)

  • Concrete implementations: WatchConnectivitySession, NeverConnectivitySession
  • Protocol wrappers around Apple's WatchConnectivity framework
  • Delegate pattern support via ConnectivitySessionDelegate
  • Message encoding/decoding via Messagable protocol

Layer 2: Observation Plugins (Choose One)

SundialKitStream (Packages/SundialKitStream/) - Modern Async/Await

  • NetworkObserver: Actor-based network monitoring

    • Generic over PathMonitor and NetworkPing protocols
    • AsyncStream APIs: pathUpdates(), pathStatusStream, isExpensiveStream, pingStatusStream
    • Call start(queue:) to begin monitoring
    • Zero @unchecked Sendable (naturally Sendable actors)
  • ConnectivityObserver: Actor-based WatchConnectivity

    • AsyncStream APIs: activationStates(), messageStream(), reachabilityStream()
    • Async methods: activate(), sendMessage(_:) returns ConnectivitySendResult
    • Automatic message routing (sendMessage when reachable, updateApplicationContext when not)
    • Zero @unchecked Sendable (naturally Sendable actors)

SundialKitCombine (Packages/SundialKitCombine/) - Combine + SwiftUI

  • NetworkObserver: @MainActor-based network monitoring

    • Generic over PathMonitor and NetworkPing & Sendable protocols
    • @published properties: pathStatus, isExpensive, isConstrained, pingStatus
    • Call start(queue:) to begin monitoring (defaults to .main)
    • Zero @unchecked Sendable (@mainactor isolation)
  • ConnectivityObserver: @MainActor-based WatchConnectivity

    • @published properties: activationState, isReachable, isPairedAppInstalled
    • PassthroughSubject publishers: messageReceived, sendResult
    • Async methods: activate(), sendMessage(_:) returns ConnectivitySendResult
    • Automatic message routing
    • Zero @unchecked Sendable (@mainactor isolation)

Message Encoding/Decoding

Messagable Protocol

  • Allows type-safe message encoding/decoding for WatchConnectivity
  • Requires: static key (type identifier), init?(from:), parameters()
  • Extension provides message() method to convert to ConnectivityMessage ([String:Any])

MessageDecoder

  • Initialize with array of Messagable.Type
  • Builds internal dictionary keyed by Messagable.key
  • Use decode(_:) to convert ConnectivityMessage back to typed Messagable

Testing Architecture

  • All tests in Tests/SundialKitTests/
  • Testing Framework: Swift Testing (requires Swift 6.1+) - v2.0.0 migration from XCTest
  • Mock implementations prefixed with "Mock" (MockSession, MockPathMonitor, MockNetworkPing, MockMessage)
  • Protocol-based abstractions enable dependency injection for testing
  • Never* types (NeverPing, NeverConnectivitySession) used for platforms without certain capabilities

Key Design Patterns

  • Protocol-oriented: Core types are protocols (PathMonitor, NetworkPing, ConnectivitySession)
  • Platform availability: Heavy use of #if canImport(Combine) and @available attributes
  • Reactive: All state changes published via Combine publishers
  • PassthroughSubject extension: Custom anyPublisher(for:) helper maps KeyPath to publisher

Platform Support

  • Swift Version: Swift 6.1+ required (v2.0.0+)
  • Deployment Targets: iOS 16, watchOS 9, tvOS 16, macOS 11.0
  • Requires Combine framework (macOS 10.15+, iOS 13+, watchOS 6+, tvOS 13+)
  • WatchConnectivity only available on iOS and watchOS (not macOS/tvOS)
  • Note: v2.0.0 dropped support for Swift 5.9, 5.10, and 6.0 to enable Swift Testing framework migration

Development Notes

Development Tools

Development tools (formatter, linter, unused code detector) are managed via mise and defined in .mise.toml. The Scripts/lint.sh script orchestrates formatting, linting, and code quality checks. Use make lint for local development.

Important Type Aliases

  • ConnectivityMessage = [String: any Sendable] (Sendable-safe WatchConnectivity dictionary)
  • ConnectivityHandler = @Sendable (ConnectivityMessage) -> Void
  • SuccessfulSubject<Output> = PassthroughSubject<Output, Never> (in SundialKitCore for legacy support)

Usage Examples

Network Monitoring with SundialKitStream (Async/Await)

import SundialKitStream
import SundialKitNetwork

// Create observer (actor-based)
let observer = NetworkObserver(
  monitor: NWPathMonitorAdapter(),
  ping: nil  // or provide a NetworkPing implementation
)

// Start monitoring
observer.start(queue: .global())

// Consume path updates using AsyncStream
Task {
  for await status in observer.pathStatusStream {
    print("Network status: \(status)")
  }
}

// Or get raw path updates
Task {
  for await path in observer.pathUpdates() {
    print("Path: \(path)")
  }
}

Network Monitoring with SundialKitCombine (Combine + SwiftUI)

import SundialKitCombine
import SundialKitNetwork
import Combine

// Create observer (@MainActor-based)
let observer = NetworkObserver(
  monitor: NWPathMonitorAdapter(),
  ping: nil
)

// Start monitoring on main queue
observer.start()

// Use @Published properties in SwiftUI
var cancellables = Set<AnyCancellable>()

observer.$pathStatus
  .sink { status in
    print("Network status: \(status)")
  }
  .store(in: &cancellables)

observer.$isExpensive
  .sink { isExpensive in
    print("Is expensive: \(isExpensive)")
  }
  .store(in: &cancellables)

WatchConnectivity with SundialKitStream (Async/Await)

import SundialKitStream
import SundialKitConnectivity

// Create observer (actor-based)
let observer = ConnectivityObserver()

// Activate session
try await observer.activate()

// Listen for messages using AsyncStream
Task {
  for await result in observer.messageStream() {
    switch result.context {
    case .replyWith(let handler):
      print("Message: \(result.message)")
      handler(["response": "acknowledged"])
    case .applicationContext:
      print("Context update: \(result.message)")
    }
  }
}

// Send messages
let result = try await observer.sendMessage(["key": "value"])
print("Sent via: \(result.context)")

WatchConnectivity with SundialKitCombine (Combine + SwiftUI)

import SundialKitCombine
import SundialKitConnectivity
import Combine

// Create observer (@MainActor-based)
let observer = ConnectivityObserver()

// Activate session
try observer.activate()

// Use publishers
var cancellables = Set<AnyCancellable>()

observer.messageReceived
  .sink { result in
    switch result.context {
    case .replyWith(let handler):
      print("Message: \(result.message)")
      handler(["response": "acknowledged"])
    case .applicationContext:
      print("Context update: \(result.message)")
    }
  }
  .store(in: &cancellables)

observer.$activationState
  .sink { state in
    print("State: \(state)")
  }
  .store(in: &cancellables)

// Send messages asynchronously
Task {
  let result = try await observer.sendMessage(["key": "value"])
  print("Sent via: \(result.context)")
}

Common Pitfalls

  • Observers require explicit start/activate: Both NetworkObserver and ConnectivityObserver need start(queue:) or activate() calls
  • Platform-specific APIs: WatchConnectivity guarded with @available and #if (behavior differs on iOS vs watchOS)
  • Messages must be property list types: ConnectivityMessage values must be Sendable property list types
  • Actor isolation: When using SundialKitStream, remember to use await for actor-isolated properties and methods
  • Main thread access: When using SundialKitCombine, all observer access is on MainActor (safe for UI updates)

Repository Structure & GitHub Workflow Integration

Package Structure (v2.0.0)

SundialKit v2.0.0 uses a modular Swift Package Manager architecture:

Repository Structure:

  • Main Repository (brightdigit/SundialKit): Contains SundialKitCore, SundialKitNetwork, SundialKitConnectivity, SundialKit umbrella, and built-in features (SundialKitBinary, SundialKitMessagable)
  • Plugin Packages: SundialKitStream and SundialKitCombine are distributed as separate Swift packages
    • brightdigit/SundialKitStream (tag: 1.0.0-alpha.1) - Modern async/await observers
    • brightdigit/SundialKitCombine (tag: 1.0.0-alpha.1) - Combine-based observers

Package Dependencies:

SundialKit uses standard Swift Package Manager dependencies. The Package.swift file references plugin packages as remote dependencies:

dependencies: [
    .package(url: "https://github.com/brightdigit/SundialKitStream.git", from: "1.0.0-alpha.1"),
    .package(url: "https://github.com/brightdigit/SundialKitCombine.git", from: "1.0.0-alpha.1")
]

Working with Dependencies:

# Update all package dependencies
swift package update

# Resolve dependencies
swift package resolve

# Clean and rebuild
swift package clean
swift build

GitHub Issues & Pull Requests

This project uses GitHub Issues and Pull Requests with component-based organization:

  • Each major feature gets a GitHub issue and feature branch
  • Subtasks are tracked as task lists in the issue or as sub-issues
  • Component/Package labeling is required for all issues and PRs
    • Issue titles prefixed with component: [Core] Feature: ..., [Network] Feature: ..., [WatchConnectivity] Feature: ...
    • GitHub labels applied: component:core, component:network, component:watchconnectivity, component:combine, component:stream, etc.
  • Feature branches follow the pattern: feature/[component-]<description>
  • Commit messages reference component: feat(core): description (#issue-number)
  • Pull requests include component scope: feat(core): Feature Description
  • Pull requests are created when work is complete, closing the related issue

Component Labels:

  • component:core - SundialKitCore protocols and types
  • component:network - SundialKitNetwork implementation
  • component:watchconnectivity - SundialKitConnectivity implementation
  • component:combine - SundialKitCombine plugin package (v1 compatibility)
  • component:messagable - SundialKitMessagable built-in (v1 compatibility)
  • component:stream - SundialKitStream plugin package (modern async/await)
  • component:binary - SundialKitBinary built-in (modern serialization)
  • component:infrastructure - Build, CI/CD, tooling, package management
  • component:docs - Documentation and examples
  • component:tests - Testing infrastructure and Swift Testing migration

@claude
Copy link
Copy Markdown

claude bot commented Apr 10, 2026

PR Review: CI Matrix Overhaul, Windows/Android/WASM Builds, Tooling Cleanup

This is a solid infrastructure PR that significantly expands platform coverage and cleans up tooling scaffolding.


Positives

  • Dynamic matrix via configure job: full matrix on main/PRs, reduced elsewhere. Saves CI minutes on tag and non-critical-branch runs.
  • cancel-in-progress: true prevents redundant runs on fast-push branches.
  • WASM guards (#if canImport(Dispatch)): Correct approach to gate DispatchQueue-dependent code for WASM targets.
  • loadUnaligned for WASM: Good catch — alignment faults on WASM are silent crashes.
  • Strict SwiftLint in CI (LINT_MODE=STRICT): Enforcing one_declaration_per_file and line_length in CI catches quality drift early.
  • Scaffolding cleanup: Removing ~6k lines of Task Master .claude/ and .cursor/ content that does not belong in the repo.
  • Devcontainer extension ID: sswg.swift-lang to swift-server.swift is the correct updated publisher ID for the Swift VS Code extension.

Issues / Concerns

1. Action versions not pinned to SHAs (security)

Actions are pinned to mutable tags (actions/checkout@v6, brightdigit/swift-build@v1, jlumbroso/free-disk-space@v1.3.1, etc.). Tags can be overwritten, creating a supply chain risk. GitHub security guidance recommends pinning to full commit SHAs. This is especially important for third-party actions like jlumbroso/free-disk-space and sersoft-gmbh/swift-coverage-action.

2. actions/checkout@v6 — verify this version exists

Please confirm actions/checkout@v6 actually exists and resolves to the intended version. At last check, actions/checkout was at v4. If this was a typo for v4, the workflow may fail or silently use an unexpected version.

3. fail-fast: false only on build-ubuntu

fail-fast: false was added to the Ubuntu matrix but not to build-macos, build-windows, or build-android. For a cross-platform project a failure on one variant (e.g., Swift 6.1 on Windows) should not cancel all other runs. Consider adding fail-fast: false to the remaining matrix jobs.

4. Missing PR description

The PR body is empty. With 18 commits spanning WASM support, Windows/Android CI, lint fixes, Fastlane base64 API key fix, and Swift 6.3 adoption, a short summary would help reviewers and future readers.

5. .env.example deleted without replacement

The removed .env.example documented environment variables (App Store Connect API key fields, etc.) needed for CI and Fastlane. New contributors/CI maintainers now have no record of required secrets. Consider migrating this documentation to the README, a docs/ci-secrets.md file, or comments in Fastfile/SundialKit.yml.

6. is_key_content_base64: true in Fastfile — potential local dev breaking change

This is correct for CI where secrets are stored as base64, but developers running Fastlane locally with a raw .p8 file path will have their invocations broken. Document the expected secret format.


Nits

  • The configure job matrix JSON is hardcoded inline. If the matrix grows complex, consider extracting it to a separate file or reusable workflow.
  • swift-6.2-nightly devcontainer was deleted but swift-6.1 and swift-6.2 stable containers remain. Ensure the CI matrix stays aligned with available devcontainer versions.
  • Gemfile.lock Fastlane bump to 2.229.0 — confirm this was intentional.

Summary

The core infrastructure work is solid and the WASM/Windows/Android coverage additions are meaningful. Main asks before merging:

  1. Pin actions to SHA hashes (or at minimum verify actions/checkout@v6 resolves correctly)
  2. Add a PR description summarizing the changes
  3. Document required CI secrets now that .env.example is gone
  4. Consider fail-fast: false on remaining build matrix jobs

The Swift source changes (copyright year, doc comment reformatting, WASM guards) all look correct and appropriate.

@leogdion leogdion merged commit 0af4314 into v2.0.0-alpha.2 Apr 10, 2026
42 checks passed
@leogdion leogdion deleted the atleast-v1.0.0-beta.4 branch April 10, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component:core SundialKitCore protocols and types component:infrastructure Build, CI/CD, tooling, git-subrepo component:tests Testing infrastructure and Swift Testing migration

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant