Conversation
|
Warning Review limit reached
Next review available in: 3 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (7)
📝 WalkthroughWalkthroughSpinetail migrates from the legacy Prch-based Mailchimp API surface to generated Swift OpenAPI code and a focused async ChangesSpinetail OpenAPI migration
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant Application
participant MailchimpClient
participant AuthenticationMiddleware
participant OpenAPIClient
participant MailchimpAPI
Application->>MailchimpClient: request campaigns or campaign content
MailchimpClient->>OpenAPIClient: invoke generated operation
OpenAPIClient->>AuthenticationMiddleware: process outgoing request
AuthenticationMiddleware->>MailchimpAPI: send Basic-authenticated HTTP request
MailchimpAPI-->>OpenAPIClient: JSON or problem response
OpenAPIClient-->>MailchimpClient: typed operation output
MailchimpClient-->>Application: campaigns, content, or ClientError
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #28 +/- ##
=======================================
Coverage ? 43.52%
=======================================
Files ? 7
Lines ? 1050
Branches ? 0
=======================================
Hits ? 457
Misses ? 593
Partials ? 0
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
cf9e7e8 to
ce4d5b6
Compare
…CI (#29) * Phase 4 — OpenAPI & dependency migration (#109) * Replace legacy Swift-5.x CI with Swift 6.4 template (Spinetail, SwiftTube) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: add macOS + Windows nightly-6.4 CI legs across all 5 standalone repos Extend ButtondownKit/Spinetail/SwiftTube/Contribute/SyndiKit standalone CI to the BrightDigit multi-platform template now that a self-hosted macOS runner with /Applications/Xcode-beta.app (Swift 6.4) is available: - build-macos: [self-hosted, macOS] + swift-build xcode=Xcode-beta. Blocking on all 5. Contribute & SyndiKit migrated off macos-15; their Ubuntu + lint legs migrated swift:6.3-noble -> swiftlang/swift:nightly-6.4.x-noble. - build-windows: hosted windows-2022/2025, swift.org nightly snapshot 6.4.x-DEVELOPMENT-SNAPSHOT-2026-06-01-a. Gated to full-matrix runs via a single-package configure job. continue-on-error on the OpenAPI repos + Contribute (unverified deps); blocking on SyndiKit. - SyndiKit also gains build-macos-platforms (iOS/watchOS/tvOS on released Xcode_26.4 — not nightly 6.4). WASM + Android deferred: no nightly 6.4 support yet. WASM is a swift-build limitation (brightdigit/swift-build#115 — no input to override the auto-derived -RELEASE wasm SDK URL); Android is blocked upstream (no nightly 6.4 SDK). Each workflow documents re-adding them. Contribute WASM is permanently N/A (Yams on the Musl/wasm SDK). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: wire WASM + Android nightly-6.4 CI legs via swift-build SDK-bundle inputs Now that swift.org publishes nightly 6.4 SDK bundles on swift-6.4.x-branch and brightdigit/swift-build#116 adds inputs to install caller-supplied bundles, add: - build-wasm to ButtondownKit/Spinetail/SwiftTube/SyndiKit (NOT Contribute — Yams fails on the Musl/wasm SDK). Uses wasm-sdk-url + wasm-sdk-checksum pointing at the swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-06-15-a_wasm artifactbundle, with WASI emulation + memory flags. - build-android to all 5 repos. Uses android-sdk-url + android-sdk-id (+ matching android-swift-version) for the swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-06-15-a_android artifactbundle via skiptools custom-sdk-url; build-only (android-run-tests: false). Both legs are full-matrix-gated and continue-on-error: they reference @v1 and are inert until swift-build#116 is released and the v1 tag moved, after which they should be confirmed green and promoted to blocking (SyndiKit first). Bump the snapshot SDK URLs/checksums periodically. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: run all Apple-platform suites on self-hosted Xcode-beta (Swift 6.4) Per the rule that Swift 6.4 builds use the self-hosted runners (the only macOS Swift 6.4 toolchain is /Applications/Xcode-beta.app = Xcode 27 / Swift 6.4): - Move SyndiKit + Contribute build-macos-platforms off hosted macos-26/Xcode_26.4 (which is only Swift 6.2) onto [self-hosted, macOS] + Xcode-beta. - Add build-macos-platforms (iOS/watchOS/tvOS) to ButtondownKit/Spinetail/SwiftTube on the same self-hosted runner — these are swift-tools-version:6.4, so hosted released-Xcode runners can't even parse their manifests. All use the iOS/watchOS/tvOS 27.0 simulator runtimes present on the runner (iPhone 17 Pro / Apple Watch Ultra 3 (49mm) / Apple TV 4K (3rd generation)), full-matrix-gated and continue-on-error (simulator-on-nightly is the most fragile leg; promote once green). lint now needs build-macos-platforms. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: give Windows its own (stricter) matrix tier Three CI tiers instead of two: small set (build-ubuntu/build-macos/lint, always) < full-matrix (macОS-platforms/wasm/android) < +Windows. configure now emits a second output, run-windows, equal to full-matrix MINUS PRs into semver branches. build-windows (the most expensive leg — hosted windows-2022/2025 ×2) gates on run-windows; the rest keep full-matrix. So PRs into semver branches still exercise the full non-Windows matrix but skip Windows. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: test against swift-build feature branch + fold build-wasm into build-ubuntu - Point every brightdigit/swift-build@v1 reference at @sdk-url-checksum-nightly-6.4 (PR #116) TEMPORARILY so CI exercises the new wasm-sdk-url/android-sdk-url code before it ships in @v1. Revert to @v1 once #116 is merged + the v1 tag moved. - Fold build-wasm into build-ubuntu as a continue-on-error step in the same nightly-6.4.x-noble container (one spin-up; runs on every push/PR now, not just full-matrix). Removed the standalone build-wasm job from ButtondownKit/Spinetail/ SwiftTube/SyndiKit (Contribute has no wasm — Yams on Musl/wasm). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: only run push CI on main (drop integration branch from push trigger) Push events now trigger only on main (+ release tags). Feature/integration branches like brightdigit-com-260406 get CI through their PRs instead of a redundant branch-push run. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: unify the 5 CI workflows to byte-identical (except name) - Windows is now blocking everywhere (dropped continue-on-error from build-windows in all 5, not just SyndiKit). - The only per-package functional difference — the wasm step (Contribute can't build Yams on wasm) — is externalized to the ENABLE_WASM repo variable: `if: ${{ vars.ENABLE_WASM != 'false' }}`. Set ENABLE_WASM=false on Contribute. - Normalized all comments to a shared template. Result: the five workflow files are byte-identical except the `name:` line; the only behavioral knob is each repo's ENABLE_WASM variable. Filed Contribute#10 to fix Yams-on-wasm and re-enable it there. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Reconcile tree to brightdigit-com-260406 (drop v1.0.0 legacy Prch files) The rebase onto v1.0.0 carried over legacy files from v1.0.0's superseded Prch2 commits that the OpenAPI rebuild never touched. Force the tree to match the original brightdigit-com-260406 content exactly so no stray files land. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: remove continue-on-error from CI workflow Make the WASM step, the Apple-platforms job (iOS/watchOS/tvOS), and the Android job blocking. Tidy the now-stale continue-on-error comments. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: gate watchOS leg on ENABLE_WATCHOS (disable where failing) Gate the build-macos-platforms watchOS leg on the ENABLE_WATCHOS repo variable (set false on this repo). watchOS-27 SDK rejects deps inferring an 8.0 deployment target (SwiftPM #10188). Tracked in brightdigit.com#119. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: build-ubuntu matrix + guard URLSession behind #if !os(WASI) build-ubuntu fans out over [standard, wasm, wasm-embedded] (configure → ubuntu-type, gated by ENABLE_WASM). Make OpenAPIURLSession conditional via Platform.withoutWASI and guard the URLSession-based initializer behind #if !os(WASI) so the wasm/embedded legs build. Mirrors brightdigit/MistKit. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Subrepos: make wasm/wasm-embedded legs build-only WasmKit can't mount the Foundation resource bundles the tests load, and embedded hits OpenAPIRuntime keypath limits at runtime. Build-only validates wasm/embedded compilation; the standard leg runs the full suite. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * CI: revert swift-build action to @v1 (temp sdk-url-checksum-nightly-6.4 branch deleted) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add devcontainer, CI workflows, and update README Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (3)
.github/workflows/Spinetail.yml (3)
29-241: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winAdd an explicit
permissions:block; jobs currently rely on default (broad)GITHUB_TOKENpermissions.zizmor's
excessive-permissionswarning fires for every job that lacks apermissions:key (build-ubuntu29-79,configure81-117,build-macos118-144,build-windows145-170,build-macos-platforms171-211,build-android212-240). None of these jobs appear to need more than read access to repo contents. Scoping tocontents: read(top-level or per-job) follows least-privilege for theGITHUB_TOKEN.🔒 Proposed fix
jobs: + # top-level default; override per-job if a job legitimately needs more +permissions: + contents: read + jobs: build-ubuntu:🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/Spinetail.yml around lines 29 - 241, Add an explicit GitHub Actions permissions block scoped to contents: read, preferably at workflow level so it applies to build-ubuntu, configure, build-macos, build-windows, build-macos-platforms, and build-android. Preserve all existing job behavior while eliminating reliance on broad default GITHUB_TOKEN permissions.Source: Linters/SAST tools
92-116: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winPass
github.ref/github.base_ref/vars.ENABLE_WASMviaenv:instead of interpolating into the script.
REF="${{ github.ref }}"andBASE_REF="${{ github.base_ref }}"are spliced directly into therun:shell script; ref names can contain shell metacharacters, making this a classic GitHub Actions script-injection vector (flagged by zizmor astemplate-injection, error severity at line 98). Prefer passing these throughenv:and referencing$REF/$BASE_REFso the values are never shell-interpreted from the workflow expression.🔒 Proposed fix
- id: check name: Determine matrix scope + env: + REF: ${{ github.ref }} + EVENT_NAME: ${{ github.event_name }} + BASE_REF: ${{ github.base_ref }} + ENABLE_WASM: ${{ vars.ENABLE_WASM }} run: | FULL=false; WIN=false - REF="${{ github.ref }}"; EVENT="${{ github.event_name }}"; BASE_REF="${{ github.base_ref }}" + EVENT="$EVENT_NAME" if [[ "$REF" == "refs/heads/main" ]]; then FULL=true; WIN=true ... - if [[ "${{ vars.ENABLE_WASM }}" != "false" ]]; then + if [[ "$ENABLE_WASM" != "false" ]]; then🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/Spinetail.yml around lines 92 - 116, Update the workflow step that computes FULL, WIN, and ubuntu-type to pass github.ref, github.base_ref, and vars.ENABLE_WASM through the step’s env configuration, then reference those environment variables as $REF, $BASE_REF, and an appropriate variable inside the run script. Remove the direct GitHub expression interpolations from the shell commands while preserving the existing branch, event, and wasm-selection behavior.Source: Linters/SAST tools
44-44: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick winSet
persist-credentials: falseon everyactions/checkoutstep across the CI workflows. All three workflows checkout the repo without disabling credential persistence, so the ephemeralGITHUB_TOKENremains in the local git config for the rest of each job (zizmorartipacked); none of these jobs push back to the repo, so persistence isn't needed.
.github/workflows/Spinetail.yml#L44-L44: addwith: persist-credentials: falseto this and the other sixactions/checkout@v6steps in the file (lines 89, 123, 158, 185, 222, 253)..github/workflows/check-unsafe-flags.yml#L18-L19: addwith: persist-credentials: falseto this checkout step..github/workflows/swift-source-compat.yml#L27-L28: addwith: persist-credentials: falseto this checkout step.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/Spinetail.yml at line 44, Update every actions/checkout@v6 step to set persist-credentials: false: all seven checkout steps in .github/workflows/Spinetail.yml (lines 44, 89, 123, 158, 185, 222, and 253), the checkout step in .github/workflows/check-unsafe-flags.yml (lines 18-19), and the checkout step in .github/workflows/swift-source-compat.yml (lines 27-28). Add the setting under each action’s with configuration.Source: Linters/SAST tools
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.claude/skills/claude-handoff/SKILL.md:
- Around line 8-10: Update the handoff command in the skill instructions to pass
the summary and descriptive name as safely separated argument values, preventing
conversation content from being interpreted as shell syntax. Use a wrapper or
robust shell-escaping approach around the existing background-agent launch,
while preserving the required --bg and --name behavior.
In @.claude/skills/loop-me/SKILL.md:
- Line 8: Update the stateful /grilling workflow in the loop-me skill so
creating, editing, or deleting workflow specs is deferred until the user
confirms shared understanding; alternatively, require explicit confirmation
before each destructive overwrite or deletion. Preserve the
one-question-at-a-time grilling process and workflow-only output.
In @.claude/skills/to-spec/SKILL.md:
- Around line 17-19: Update the workflow instructions after “Check with the
user” to require explicit user approval before publishing the spec to the issue
tracker. If the user rejects the spec, revise it and seek approval again; only
publish after approval, then apply the ready-for-agent label.
In @.github/workflows/cleanup-caches.yml:
- Line 15: Update the ref construction in the cleanup workflow to use
context.payload.ref_type, selecting refs/heads/ for branch deletions and
refs/tags/ for tag deletions. Keep the existing cache cleanup behavior unchanged
after producing the correct ref for either delete event.
In `@CLAUDE.md`:
- Around line 21-27: Update the Build, Build incl. tests, Test (all), and Test
(single) command examples in CLAUDE.md to invoke Swift through the pinned
toolchain using mise exec --, keeping the existing command arguments and test
filter unchanged.
In `@README.md`:
- Around line 19-20: Update the two badge image labels in the README to use
descriptive alt text: identify the Swift versions badge as “Supported Swift
versions” and the platforms badge as “Supported platforms,” while preserving
their existing badge URLs and links.
- Around line 42-59: Update the README’s MailchimpClient API description to
document the public campaignContent(forCampaignID:) and
plainText(forCampaignID:) methods as convenience methods backed by the two
generated operations, while preserving the existing description of listing
campaigns and fetching archive HTML.
- Around line 164-179: Update the Error handling documentation around the “Both
methods throw” statement to acknowledge that generated-client and transport
errors propagate unchanged, rather than claiming every failure is a
MailchimpClient.ClientError. Keep the existing ClientError cases and missingHTML
example, and clearly distinguish mapped errors from propagated transport errors.
In `@Scripts/lint.sh`:
- Line 66: Guard the directory-stack operations in the script: update pushd
before the run_command steps and the corresponding popd near the end to fail
immediately when either command fails. Preserve the existing command flow while
ensuring subsequent formatting, linting, and build operations cannot run from an
unintended directory.
In `@Sources/Spinetail/MailchimpClient.swift`:
- Around line 160-169: The pagination termination logic must not treat
collected.count as the total when body.total_items is absent. Update the loop
around body.total_items and the page.isEmpty check to stop on total_items only
when present, while continuing to fetch subsequent pages until an empty page
when it is nil.
- Around line 107-121: Update serverURL(forAPIKey:) to validate the extracted
datacenter token against the allowed Mailchimp datacenter format, rejecting URL
delimiters and any other invalid characters before interpolation. Preserve
invalidAPIKey errors for rejected tokens, and add a test covering a suffix such
as “key-evil.com/” to ensure no non-Mailchimp URL is constructed.
---
Nitpick comments:
In @.github/workflows/Spinetail.yml:
- Around line 29-241: Add an explicit GitHub Actions permissions block scoped to
contents: read, preferably at workflow level so it applies to build-ubuntu,
configure, build-macos, build-windows, build-macos-platforms, and build-android.
Preserve all existing job behavior while eliminating reliance on broad default
GITHUB_TOKEN permissions.
- Around line 92-116: Update the workflow step that computes FULL, WIN, and
ubuntu-type to pass github.ref, github.base_ref, and vars.ENABLE_WASM through
the step’s env configuration, then reference those environment variables as
$REF, $BASE_REF, and an appropriate variable inside the run script. Remove the
direct GitHub expression interpolations from the shell commands while preserving
the existing branch, event, and wasm-selection behavior.
- Line 44: Update every actions/checkout@v6 step to set persist-credentials:
false: all seven checkout steps in .github/workflows/Spinetail.yml (lines 44,
89, 123, 158, 185, 222, and 253), the checkout step in
.github/workflows/check-unsafe-flags.yml (lines 18-19), and the checkout step in
.github/workflows/swift-source-compat.yml (lines 27-28). Add the setting under
each action’s with configuration.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f521d9dd-263e-4718-bfd1-996a40b41a64
⛔ Files ignored due to path filters (2)
Package.resolvedis excluded by!**/Package.resolvedSources/Spinetail/Spinetail.docc/Resources/logo.pngis excluded by!**/*.png
📒 Files selected for processing (321)
.circleci/config.yml.claude/agent-notes.md.claude/skills/claude-handoff/SKILL.md.claude/skills/claude-handoff/agents/openai.yaml.claude/skills/grill-me/SKILL.md.claude/skills/grill-me/agents/openai.yaml.claude/skills/grill-with-docs/SKILL.md.claude/skills/grill-with-docs/agents/openai.yaml.claude/skills/grilling/SKILL.md.claude/skills/grilling/agents/openai.yaml.claude/skills/handoff/SKILL.md.claude/skills/handoff/agents/openai.yaml.claude/skills/loop-me/SKILL.md.claude/skills/loop-me/agents/openai.yaml.claude/skills/research/SKILL.md.claude/skills/research/agents/openai.yaml.claude/skills/to-spec/SKILL.md.claude/skills/to-spec/agents/openai.yaml.claude/skills/to-tickets/SKILL.md.claude/skills/to-tickets/agents/openai.yaml.claude/skills/writing-great-skills/GLOSSARY.md.claude/skills/writing-great-skills/SKILL.md.claude/skills/writing-great-skills/agents/openai.yaml.devcontainer/devcontainer.json.github/actions/setup-tools/action.yml.github/workflows/Spinetail.yml.github/workflows/check-unsafe-flags.yml.github/workflows/cleanup-caches.yml.github/workflows/swift-source-compat.yml.hound.yml.mise.toml.spi.yml.swift-format.swift-version.swiftformat.swiftlint.ymlCLAUDE.mdOpenAPI/openapi.yamlPackage.swiftPackage@swift-5.5.swiftREADME.mdRELEASE_NOTES.mdScripts/generate-openapi-spinetail.shScripts/lint.shSources/Spinetail/AuthenticationMiddleware.swiftSources/Spinetail/Mailchimp.API.swiftSources/Spinetail/Mailchimp.swiftSources/Spinetail/MailchimpCampaign.swiftSources/Spinetail/MailchimpClient.swiftSources/Spinetail/Models/DefaultResponse.swiftSources/Spinetail/Models/Links.swiftSources/Spinetail/Requests/Lite/CampaignFolders/DeleteCampaignFoldersId.swiftSources/Spinetail/Requests/Lite/CampaignFolders/GetCampaignFolders.swiftSources/Spinetail/Requests/Lite/CampaignFolders/GetCampaignFoldersId.swiftSources/Spinetail/Requests/Lite/CampaignFolders/PatchCampaignFoldersId.swiftSources/Spinetail/Requests/Lite/CampaignFolders/PostCampaignFolders.swiftSources/Spinetail/Requests/Lite/Campaigns/DeleteCampaignsId.swiftSources/Spinetail/Requests/Lite/Campaigns/DeleteCampaignsIdFeedbackId.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaigns.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaignsId.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdContent.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdFeedback.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdFeedbackId.swiftSources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdSendChecklist.swiftSources/Spinetail/Requests/Lite/Campaigns/PatchCampaignsId.swiftSources/Spinetail/Requests/Lite/Campaigns/PatchCampaignsIdFeedbackId.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaigns.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsCancelSend.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsCreateResend.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsPause.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsReplicate.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsResume.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsSchedule.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsSend.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsTest.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsUnschedule.swiftSources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdFeedback.swiftSources/Spinetail/Requests/Lite/Campaigns/PutCampaignsIdContent.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdInterestCategoriesId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdInterestCategoriesIdInterestsId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdMembersId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdMembersIdNotesId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdMergeFieldsId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdSegmentsId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdSegmentsIdMembersId.swiftSources/Spinetail/Requests/Lite/Lists/DeleteListsIdWebhooksId.swiftSources/Spinetail/Requests/Lite/Lists/GetListMemberTags.swiftSources/Spinetail/Requests/Lite/Lists/GetLists.swiftSources/Spinetail/Requests/Lite/Lists/GetListsId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdAbuseReports.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdAbuseReportsId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdActivity.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdClients.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdGrowthHistory.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdGrowthHistoryId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdInterestCategories.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdInterestCategoriesId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdInterestCategoriesIdInterests.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdInterestCategoriesIdInterestsId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdLocations.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembers.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdActivity.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdActivityFeed.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdEvents.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdGoals.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdNotes.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdNotesId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMergeFields.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdMergeFieldsId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdSegmentsId.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdSegmentsIdMembers.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdSignupForms.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdWebhooks.swiftSources/Spinetail/Requests/Lite/Lists/GetListsIdWebhooksId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdInterestCategoriesId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdInterestCategoriesIdInterestsId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdMembersId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdMembersIdNotesId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdMergeFieldsId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdSegmentsId.swiftSources/Spinetail/Requests/Lite/Lists/PatchListsIdWebhooksId.swiftSources/Spinetail/Requests/Lite/Lists/PostListMemberEvents.swiftSources/Spinetail/Requests/Lite/Lists/PostListMemberTags.swiftSources/Spinetail/Requests/Lite/Lists/PostLists.swiftSources/Spinetail/Requests/Lite/Lists/PostListsId.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdInterestCategories.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdInterestCategoriesIdInterests.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdMembers.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdMembersHashActionsDeletePermanent.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdMembersIdNotes.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdMergeFields.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdSegments.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdSegmentsId.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdSegmentsIdMembers.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdSignupForms.swiftSources/Spinetail/Requests/Lite/Lists/PostListsIdWebhooks.swiftSources/Spinetail/Requests/Lite/Lists/PreviewASegment.swiftSources/Spinetail/Requests/Lite/Lists/PutListsIdMembersId.swiftSources/Spinetail/Requests/Lite/Lists/SearchTagsByName.swiftSources/Spinetail/Requests/Lite/Reports/GetReports.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdAbuseReportsId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdAbuseReportsIdId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdAdvice.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdClickDetails.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdClickDetailsId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdClickDetailsIdMembers.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdClickDetailsIdMembersId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdDomainPerformance.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdEcommerceProductActivity.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdEepurl.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdEmailActivity.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdEmailActivityId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdLocations.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdOpenDetails.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdOpenDetailsIdMembersId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdSentTo.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdSentToId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdSubReportsId.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdUnsubscribed.swiftSources/Spinetail/Requests/Lite/Reports/GetReportsIdUnsubscribedId.swiftSources/Spinetail/Requests/Lite/Root/GetRoot.swiftSources/Spinetail/Requests/Lite/SearchCampaigns/GetSearchCampaigns.swiftSources/Spinetail/Requests/Lite/SearchMembers/GetSearchMembers.swiftSources/Spinetail/Requests/Lite/TemplateFolders/DeleteTemplateFoldersId.swiftSources/Spinetail/Requests/Lite/TemplateFolders/GetTemplateFolders.swiftSources/Spinetail/Requests/Lite/TemplateFolders/GetTemplateFoldersId.swiftSources/Spinetail/Requests/Lite/TemplateFolders/PatchTemplateFoldersId.swiftSources/Spinetail/Requests/Lite/TemplateFolders/PostTemplateFolders.swiftSources/Spinetail/Requests/Lite/Templates/DeleteTemplatesId.swiftSources/Spinetail/Requests/Lite/Templates/GetTemplates.swiftSources/Spinetail/Requests/Lite/Templates/GetTemplatesId.swiftSources/Spinetail/Requests/Lite/Templates/GetTemplatesIdDefaultContent.swiftSources/Spinetail/Requests/Lite/Templates/PatchTemplatesId.swiftSources/Spinetail/Requests/Lite/Templates/PostTemplates.swiftSources/Spinetail/Requests/Regular/ActivityFeed/GetActivityFeedChimpChatter.swiftSources/Spinetail/Requests/Regular/AuthorizedApps/GetAuthorizedApps.swiftSources/Spinetail/Requests/Regular/AuthorizedApps/GetAuthorizedAppsId.swiftSources/Spinetail/Requests/Regular/Automations/ArchiveAutomations.swiftSources/Spinetail/Requests/Regular/Automations/DeleteAutomationsIdEmailsId.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomations.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsId.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdEmails.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdEmailsId.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdEmailsIdQueue.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdEmailsIdQueueId.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdRemovedSubscribers.swiftSources/Spinetail/Requests/Regular/Automations/GetAutomationsIdRemovedSubscribersId.swiftSources/Spinetail/Requests/Regular/Automations/PatchAutomationEmailWorkflowId.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomations.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdActionsPauseAllEmails.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdActionsStartAllEmails.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdEmailsIdActionsPause.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdEmailsIdActionsStart.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdEmailsIdQueue.swiftSources/Spinetail/Requests/Regular/Automations/PostAutomationsIdRemovedSubscribers.swiftSources/Spinetail/Requests/Regular/BatchWebhooks/DeleteBatchWebhookId.swiftSources/Spinetail/Requests/Regular/BatchWebhooks/GetBatchWebhook.swiftSources/Spinetail/Requests/Regular/BatchWebhooks/GetBatchWebhooks.swiftSources/Spinetail/Requests/Regular/BatchWebhooks/PatchBatchWebhooks.swiftSources/Spinetail/Requests/Regular/BatchWebhooks/PostBatchWebhooks.swiftSources/Spinetail/Requests/Regular/Batches/DeleteBatchesId.swiftSources/Spinetail/Requests/Regular/Batches/GetBatches.swiftSources/Spinetail/Requests/Regular/Batches/GetBatchesId.swiftSources/Spinetail/Requests/Regular/Batches/PostBatches.swiftSources/Spinetail/Requests/Regular/ConnectedSites/DeleteConnectedSitesId.swiftSources/Spinetail/Requests/Regular/ConnectedSites/GetConnectedSites.swiftSources/Spinetail/Requests/Regular/ConnectedSites/GetConnectedSitesId.swiftSources/Spinetail/Requests/Regular/ConnectedSites/PostConnectedSites.swiftSources/Spinetail/Requests/Regular/ConnectedSites/PostConnectedSitesIdActionsVerifyScriptInstallation.swiftSources/Spinetail/Requests/Regular/Conversations/GetConversations.swiftSources/Spinetail/Requests/Regular/Conversations/GetConversationsId.swiftSources/Spinetail/Requests/Regular/Conversations/GetConversationsIdMessages.swiftSources/Spinetail/Requests/Regular/Conversations/GetConversationsIdMessagesId.swiftSources/Spinetail/Requests/Regular/CustomerJourneys/PostCustomerJourneysJourneysIdStepsIdActionsTrigger.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCartsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCartsLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCustomersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdOrdersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdOrdersIdLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsIdImagesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsIdVariantsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdPromocodesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdPromorulesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceOrders.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStores.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCarts.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsIdLines.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsIdLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCustomers.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCustomersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrders.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersIdLines.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersIdLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProducts.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdImages.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdImagesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdVariants.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdVariantsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromocodes.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromocodesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromorules.swiftSources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromorulesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdCartsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdCartsIdLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdCustomersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdOrdersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdOrdersIdLinesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdProductsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdProductsIdImagesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdProductsIdVariantsId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdPromocodesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdPromorulesId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStores.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdCarts.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdCartsIdLines.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdCustomers.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdOrders.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdOrdersIdLines.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdProducts.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdProductsIdImages.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdProductsIdVariants.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdPromocodes.swiftSources/Spinetail/Requests/Regular/Ecommerce/PostEcommerceStoresIdPromorules.swiftSources/Spinetail/Requests/Regular/Ecommerce/PutEcommerceStoresIdCustomersId.swiftSources/Spinetail/Requests/Regular/Ecommerce/PutEcommerceStoresIdProductsIdVariantsId.swiftSources/Spinetail/Requests/Regular/FacebookAds/GetAllFacebookAds.swiftSources/Spinetail/Requests/Regular/FacebookAds/GetFacebookAdsId.swiftSources/Spinetail/Requests/Regular/FileManager/DeleteFileManagerFilesId.swiftSources/Spinetail/Requests/Regular/FileManager/DeleteFileManagerFoldersId.swiftSources/Spinetail/Requests/Regular/FileManager/GetFileManagerFiles.swiftSources/Spinetail/Requests/Regular/FileManager/GetFileManagerFilesId.swiftSources/Spinetail/Requests/Regular/FileManager/GetFileManagerFolders.swiftSources/Spinetail/Requests/Regular/FileManager/GetFileManagerFoldersId.swiftSources/Spinetail/Requests/Regular/FileManager/PatchFileManagerFilesId.swiftSources/Spinetail/Requests/Regular/FileManager/PatchFileManagerFoldersId.swiftSources/Spinetail/Requests/Regular/FileManager/PostFileManagerFiles.swiftSources/Spinetail/Requests/Regular/FileManager/PostFileManagerFolders.swiftSources/Spinetail/Requests/Regular/LandingPages/DeleteLandingPageId.swiftSources/Spinetail/Requests/Regular/LandingPages/GetAllLandingPages.swiftSources/Spinetail/Requests/Regular/LandingPages/GetLandingPageId.swiftSources/Spinetail/Requests/Regular/LandingPages/GetLandingPageIdContent.swiftSources/Spinetail/Requests/Regular/LandingPages/PatchLandingPageId.swiftSources/Spinetail/Requests/Regular/LandingPages/PostAllLandingPages.swiftSources/Spinetail/Requests/Regular/LandingPages/PostLandingPageIdActionsPublish.swiftSources/Spinetail/Requests/Regular/LandingPages/PostLandingPageIdActionsUnpublish.swiftSources/Spinetail/Requests/Regular/Ping/GetPing.swiftSources/Spinetail/Requests/Regular/Reporting/GetReportingFacebookAds.swiftSources/Spinetail/Requests/Regular/Reporting/GetReportingFacebookAdsId.swiftSources/Spinetail/Requests/Regular/Reporting/GetReportingFacebookAdsIdEcommerceProductActivity.swiftSources/Spinetail/Requests/Regular/Reporting/GetReportingLandingPages.swiftSources/Spinetail/Requests/Regular/Reporting/GetReportingLandingPagesId.swiftSources/Spinetail/Requests/Regular/VerifiedDomains/CreateVerifiedDomain.swiftSources/Spinetail/Requests/Regular/VerifiedDomains/DeleteVerifiedDomain.swiftSources/Spinetail/Requests/Regular/VerifiedDomains/GetVerifiedDomain.swiftSources/Spinetail/Requests/Regular/VerifiedDomains/GetVerifiedDomains.swiftSources/Spinetail/Requests/Regular/VerifiedDomains/VerifyDomain.swiftSources/Spinetail/Spinetail.docc/Spinetail.mdSources/SpinetailOpenAPI/Client.swiftSources/SpinetailOpenAPI/Types.swiftSources/SpinetailOpenAPI/openapi-generator-config.yamlTests/LinuxMain.swiftTests/SpinetailTests/Fixtures.swiftTests/SpinetailTests/Helpers/Client.Helpers.swiftTests/SpinetailTests/Helpers/Settings.swiftTests/SpinetailTests/Helpers/String.swiftTests/SpinetailTests/MailchimpClientTests.swiftTests/SpinetailTests/MockTransport.swiftTests/SpinetailTests/Tests/CampaignTests.swiftTests/SpinetailTests/Tests/ListsTests.swiftTests/SpinetailTests/XCTestManifests.swift
💤 Files with no reviewable changes (165)
- .swiftformat
- Tests/LinuxMain.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsResume.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsSchedule.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdSendChecklist.swift
- Sources/Spinetail/Models/Links.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdInterestCategoriesIdInterestsId.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdMergeFieldsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/DeleteCampaignsIdFeedbackId.swift
- Sources/Spinetail/Spinetail.docc/Spinetail.md
- Sources/Spinetail/Mailchimp.API.swift
- Tests/SpinetailTests/Tests/CampaignTests.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdEvents.swift
- Tests/SpinetailTests/Helpers/String.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdOrdersIdLinesId.swift
- Sources/Spinetail/Requests/Regular/VerifiedDomains/GetVerifiedDomain.swift
- Sources/Spinetail/Models/DefaultResponse.swift
- .hound.yml
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdSegmentsIdMembersId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsReplicate.swift
- Sources/Spinetail/Requests/Regular/BatchWebhooks/DeleteBatchWebhookId.swift
- Sources/Spinetail/Requests/Lite/CampaignFolders/GetCampaignFolders.swift
- Sources/Spinetail/Requests/Regular/Batches/GetBatchesId.swift
- Package@swift-5.5.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMergeFields.swift
- Sources/Spinetail/Requests/Regular/ConnectedSites/PostConnectedSites.swift
- .circleci/config.yml
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdSegmentsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsUnschedule.swift
- Sources/Spinetail/Requests/Regular/BatchWebhooks/PatchBatchWebhooks.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCustomers.swift
- Sources/Spinetail/Requests/Lite/CampaignFolders/GetCampaignFoldersId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsIdVariantsId.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdWebhooksId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PutCampaignsIdContent.swift
- Sources/Spinetail/Requests/Regular/BatchWebhooks/PostBatchWebhooks.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsCancelSend.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdSegmentsId.swift
- Sources/Spinetail/Requests/Regular/VerifiedDomains/GetVerifiedDomains.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdWebhooks.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdPromorulesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStores.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdFeedbackId.swift
- Sources/Spinetail/Requests/Lite/CampaignFolders/PostCampaignFolders.swift
- Sources/Spinetail/Requests/Lite/Lists/PreviewASegment.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCartsLinesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdImagesId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdActivityFeed.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProducts.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdMembersIdNotesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdVariantsId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdGoals.swift
- Sources/Spinetail/Requests/Regular/ConnectedSites/DeleteConnectedSitesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdOrdersId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdProductsIdImagesId.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdInterestCategoriesIdInterestsId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromorulesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdMergeFields.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCustomersId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdPromocodesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsId.swift
- Sources/Spinetail/Requests/Regular/Reporting/GetReportingLandingPagesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdWebhooks.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromorules.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsPause.swift
- Sources/Spinetail/Requests/Lite/CampaignFolders/DeleteCampaignFoldersId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListMemberTags.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdNotes.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdInterestCategoriesIdInterestsId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListMemberTags.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsIdLinesId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdFeedback.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresId.swift
- Tests/SpinetailTests/XCTestManifests.swift
- Tests/SpinetailTests/Tests/ListsTests.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdInterestCategoriesIdInterests.swift
- Sources/Spinetail/Requests/Regular/VerifiedDomains/VerifyDomain.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersIdLines.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCustomersId.swift
- Sources/Spinetail/Requests/Regular/VerifiedDomains/DeleteVerifiedDomain.swift
- Sources/Spinetail/Requests/Regular/BatchWebhooks/GetBatchWebhooks.swift
- Sources/Spinetail/Requests/Regular/ConnectedSites/GetConnectedSitesId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PatchCampaignsIdFeedbackId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCarts.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdInterestCategoriesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdMembersId.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdMembersId.swift
- Sources/Spinetail/Requests/Regular/Batches/DeleteBatchesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdInterestCategories.swift
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/DeleteCampaignsId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdLocations.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMergeFieldsId.swift
- Tests/SpinetailTests/Helpers/Client.Helpers.swift
- Sources/Spinetail/Requests/Regular/VerifiedDomains/CreateVerifiedDomain.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsTest.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdSegmentsIdMembers.swift
- Sources/Spinetail/Mailchimp.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdAbuseReports.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdContent.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdWebhooksId.swift
- Sources/Spinetail/Requests/Regular/Batches/PostBatches.swift
- Tests/SpinetailTests/Helpers/Settings.swift
- Sources/Spinetail/Requests/Regular/ConnectedSites/PostConnectedSitesIdActionsVerifyScriptInstallation.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdImages.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdSegmentsIdMembers.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembers.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdAbuseReportsId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromocodes.swift
- Sources/Spinetail/Requests/Regular/Reporting/GetReportingLandingPages.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresIdCartsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsSend.swift
- Sources/Spinetail/Requests/Lite/Lists/PostLists.swift
- Sources/Spinetail/Requests/Regular/BatchWebhooks/GetBatchWebhook.swift
- Sources/Spinetail/Requests/Lite/Lists/SearchTagsByName.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/DeleteEcommerceStoresIdCartsId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdNotesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrders.swift
- Sources/Spinetail/Requests/Regular/ConnectedSites/GetConnectedSites.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdSignupForms.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersId.swift
- Sources/Spinetail/Requests/Regular/Conversations/GetConversationsId.swift
- Sources/Spinetail/Requests/Lite/Lists/PutListsIdMembersId.swift
- Sources/Spinetail/Requests/Lite/CampaignFolders/PatchCampaignFoldersId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdMembersHashActionsDeletePermanent.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsId.swift
- Sources/Spinetail/Requests/Regular/Conversations/GetConversations.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdMembers.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdMergeFieldsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaignsIdActionsCreateResend.swift
- Sources/Spinetail/Requests/Regular/Conversations/GetConversationsIdMessages.swift
- OpenAPI/openapi.yaml
- Sources/Spinetail/Requests/Lite/Lists/DeleteListsIdInterestCategoriesId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceOrders.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdProductsIdVariants.swift
- Sources/Spinetail/Requests/Regular/Reporting/GetReportingFacebookAdsIdEcommerceProductActivity.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdSegmentsId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdPromocodesId.swift
- Sources/Spinetail/Requests/Lite/Lists/GetLists.swift
- Sources/Spinetail/Requests/Regular/Batches/GetBatches.swift
- Sources/Spinetail/Requests/Regular/CustomerJourneys/PostCustomerJourneysJourneysIdStepsIdActionsTrigger.swift
- Sources/Spinetail/Requests/Regular/Conversations/GetConversationsIdMessagesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListMemberEvents.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdOrdersIdLinesId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdSignupForms.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/PatchEcommerceStoresId.swift
- Sources/Spinetail/Requests/Lite/Reports/GetReports.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdWebhooksId.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdMembersIdNotes.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdSegmentsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaigns.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PatchCampaignsId.swift
- Sources/Spinetail/Requests/Regular/Ecommerce/GetEcommerceStoresIdCartsIdLines.swift
- Sources/Spinetail/Requests/Lite/Lists/PostListsIdSegments.swift
- Sources/Spinetail/Requests/Lite/Campaigns/PostCampaigns.swift
- Sources/Spinetail/Requests/Lite/Lists/GetListsIdMembersIdActivity.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaignsId.swift
- Sources/Spinetail/Requests/Lite/Campaigns/GetCampaignsIdFeedback.swift
- Sources/Spinetail/Requests/Lite/Lists/PatchListsIdMembersIdNotesId.swift
| Write a handoff summary of the current conversation so a fresh agent can continue the work. Instead of saving it, launch a background agent seeded with the summary as its prompt: `claude --bg --name "<descriptive name>" "<handoff summary>"`. It starts in the current working directory and returns immediately; the user manages it with `claude agents`. | ||
|
|
||
| Always pass `-n`/`--name` with a descriptive name (e.g. `--name "Fix login bug"`) — it sets the display name shown in the job list, session picker, and terminal title. |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file=".claude/skills/claude-handoff/SKILL.md"
printf '--- file exists: %s ---\n' "$file"
wc -l "$file"
printf '\n--- lines 1-40 ---\n'
sed -n '1,40p' "$file" | cat -n
printf '\n--- search for related references ---\n'
rg -n --hidden --glob '!**/.git/**' 'claude --bg|handoff summary|--name|claude agents|handoff' .claude . 2>/dev/null || trueRepository: brightdigit/Spinetail
Length of output: 250
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file=".claude/skills/claude-handoff/SKILL.md"
echo "--- file exists: $file ---"
wc -l "$file"
echo
echo "--- lines 1-60 ---"
sed -n '1,60p' "$file" | cat -n
echo
echo "--- related references ---"
rg -n --hidden --glob '!**/.git/**' 'claude --bg|handoff summary|--name|claude agents|handoff' .claude . 2>/dev/null || trueRepository: brightdigit/Spinetail
Length of output: 4062
Pass the summary and name as separate argv values. Interpolating conversation text into claude --bg ... "<handoff summary>" lets $(), backticks, quotes, or newlines break quoting or trigger substitution; use a wrapper or robust shell escaping instead.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/skills/claude-handoff/SKILL.md around lines 8 - 10, Update the
handoff command in the skill instructions to pass the summary and descriptive
name as safely separated argument values, preventing conversation content from
being interpreted as shell syntax. Use a wrapper or robust shell-escaping
approach around the existing background-agent launch, while preserving the
required --bg and --name behavior.
| argument-hint: "A workflow to design, or nothing to go find one" | ||
| --- | ||
|
|
||
| Run a stateful `/grilling` session whose only output is **workflow** specs. Use the grilling discipline — relentless, one question at a time, a recommended answer attached to each — aimed at the vocabulary and goal below. Create, edit, and delete specs as the grilling resolves things. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Defer destructive spec changes until confirmation.
This permits creating, editing, and deleting workflow source files while decisions are still unresolved, conflicting with the confirmation gate in .claude/skills/grilling/SKILL.md. Defer writes until the user confirms shared understanding, or require explicit confirmation before each deletion/overwrite.
Suggested wording
-Create, edit, and delete specs as the grilling resolves things.
+Do not modify workflow specs during the interview. After the user confirms shared understanding, create or edit the required specs; require separate explicit confirmation before deleting or overwriting an existing spec.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Run a stateful `/grilling` session whose only output is **workflow** specs. Use the grilling discipline — relentless, one question at a time, a recommended answer attached to each — aimed at the vocabulary and goal below. Create, edit, and delete specs as the grilling resolves things. | |
| Run a stateful `/grilling` session whose only output is **workflow** specs. Use the grilling discipline — relentless, one question at a time, a recommended answer attached to each — aimed at the vocabulary and goal below. Do not modify workflow specs during the interview. After the user confirms shared understanding, create or edit the required specs; require separate explicit confirmation before deleting or overwriting an existing spec. |
🧰 Tools
🪛 SkillSpector (2.3.11)
[warning] 27: [EA2] Autonomous Decision Making: Skill enables autonomous high-impact decisions without human-in-the-loop verification. Critical operations (destructive commands, financial transactions, data deletion) should require explicit user confirmation.
Remediation: Add human-in-the-loop confirmation for destructive, irreversible, or high-impact operations. Never auto-execute commands that modify files, send data, or alter system state.
(Excessive Agency (EA2))
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/skills/loop-me/SKILL.md at line 8, Update the stateful /grilling
workflow in the loop-me skill so creating, editing, or deleting workflow specs
is deferred until the user confirms shared understanding; alternatively, require
explicit confirmation before each destructive overwrite or deletion. Preserve
the one-question-at-a-time grilling process and workflow-only output.
Source: Linters/SAST tools
| Check with the user that these seams match their expectations. | ||
|
|
||
| 3. Write the spec using the template below, then publish it to the project issue tracker. Apply the `ready-for-agent` triage label - no need for additional triage. |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Require explicit approval before publishing the spec.
“Check with the user” does not clearly require pausing or iterating; the next step publishes to the tracker. State that publication must wait for explicit user approval, and revise the spec if the user rejects it.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.claude/skills/to-spec/SKILL.md around lines 17 - 19, Update the workflow
instructions after “Check with the user” to require explicit user approval
before publishing the spec to the issue tracker. If the user rejects the spec,
revise it and seek approval again; only publish after approval, then apply the
ready-for-agent label.
| Tooling is pinned in `.mise.toml` and run through `mise exec --`. Install once | ||
| with `mise install`. | ||
|
|
||
| - **Build:** `swift build` | ||
| - **Build incl. tests:** `swift build --build-tests` | ||
| - **Test (all):** `swift test` | ||
| - **Test (single):** `swift test --filter MailchimpClientTests/serverURLDerivedFromAPIKey` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Make the toolchain invocation match the documented pinning.
The file says tools run through mise exec --, but the canonical build/test examples invoke raw swift commands. Prefix those examples with mise exec --, or explicitly document the required activation step.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@CLAUDE.md` around lines 21 - 27, Update the Build, Build incl. tests, Test
(all), and Test (single) command examples in CLAUDE.md to invoke Swift through
the pinned toolchain using mise exec --, keeping the existing command arguments
and test filter unchanged.
| SWIFTLINT_OPTIONS="" | ||
| fi | ||
|
|
||
| pushd $PACKAGE_DIR |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Guard pushd (and popd) against failure.
Since set -e is intentionally removed, a failed pushd does not stop the script — subsequent run_command steps (in-place swift-format, swiftlint --fix, swift build) would then run from the wrong directory and either corrupt results or mask real failures. Apply the same guard to popd on Line 97.
🛡️ Proposed fix
-pushd $PACKAGE_DIR
+pushd "$PACKAGE_DIR" || exit 1And at Line 97:
-popd
+popd || exit 1🧰 Tools
🪛 Shellcheck (0.11.0)
[warning] 66-66: Use 'pushd ... || exit' or 'pushd ... || return' in case pushd fails.
(SC2164)
[info] 66-66: Double quote to prevent globbing and word splitting.
(SC2086)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Scripts/lint.sh` at line 66, Guard the directory-stack operations in the
script: update pushd before the run_command steps and the corresponding popd
near the end to fail immediately when either command fails. Preserve the
existing command flow while ensuring subsequent formatting, linting, and build
operations cannot run from an unintended directory.
Source: Linters/SAST tools
| internal static func serverURL(forAPIKey apiKey: String) throws -> URL { | ||
| guard let datacenter = apiKey.split(separator: "-").last, | ||
| !datacenter.isEmpty, | ||
| datacenter != apiKey[...] | ||
| else { | ||
| throw ClientError.invalidAPIKey | ||
| } | ||
| guard | ||
| let url = URL( | ||
| string: "https://\(datacenter).api.mailchimp.com/3.0" | ||
| ) | ||
| else { | ||
| throw ClientError.invalidAPIKey | ||
| } | ||
| return url |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
Reject URL delimiters in the datacenter suffix.
Line 108 interpolates the entire suffix into the URL. For example, key-evil.com/ produces a URL with evil.com as the host, redirecting requests and their Basic-auth header away from Mailchimp. Allowlist the datacenter token before constructing the URL and add a rejection test.
Proposed fix
guard let datacenter = apiKey.split(separator: "-").last,
!datacenter.isEmpty,
- datacenter != apiKey[...]
+ datacenter != apiKey[...],
+ datacenter.range(
+ of: #"^[A-Za-z0-9]+$"#,
+ options: .regularExpression
+ ) != nil
else {
throw ClientError.invalidAPIKey
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| internal static func serverURL(forAPIKey apiKey: String) throws -> URL { | |
| guard let datacenter = apiKey.split(separator: "-").last, | |
| !datacenter.isEmpty, | |
| datacenter != apiKey[...] | |
| else { | |
| throw ClientError.invalidAPIKey | |
| } | |
| guard | |
| let url = URL( | |
| string: "https://\(datacenter).api.mailchimp.com/3.0" | |
| ) | |
| else { | |
| throw ClientError.invalidAPIKey | |
| } | |
| return url | |
| internal static func serverURL(forAPIKey apiKey: String) throws -> URL { | |
| guard let datacenter = apiKey.split(separator: "-").last, | |
| !datacenter.isEmpty, | |
| datacenter != apiKey[...], | |
| datacenter.range( | |
| of: #"^[A-Za-z0-9]+$"#, | |
| options: .regularExpression | |
| ) != nil | |
| else { | |
| throw ClientError.invalidAPIKey | |
| } | |
| guard | |
| let url = URL( | |
| string: "https://\(datacenter).api.mailchimp.com/3.0" | |
| ) | |
| else { | |
| throw ClientError.invalidAPIKey | |
| } | |
| return url |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Sources/Spinetail/MailchimpClient.swift` around lines 107 - 121, Update
serverURL(forAPIKey:) to validate the extracted datacenter token against the
allowed Mailchimp datacenter format, rejecting URL delimiters and any other
invalid characters before interpolation. Preserve invalidAPIKey errors for
rejected tokens, and add a test covering a suffix such as “key-evil.com/” to
ensure no non-Mailchimp URL is constructed.
Drops the redundant Mailchimp prefix from the campaign DTO (the module is already Mailchimp-specific); MailchimpClient keeps its prefix. Updates all references and doc mentions in CLAUDE.md and README.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…/CI nits. Keep paging until an empty page if Mailchimp omits total_items, clean tag caches correctly, and document the full MailchimpClient surface plus error propagation. Co-authored-by: Cursor <cursoragent@cursor.com>
Summary by CodeRabbit
New Features
Breaking Changes
Documentation