Library Mission: HealthKitReporter is a Swift wrapper around Apple's HealthKit framework, distributed via Swift Package Manager (iOS 15+, watchOS 8+). CocoaPods is frozen at
3.1.0(trunk is read-only from 02.12.2026). It turnsHK*objects into plain,Codablepayload structs (and back), so consumers — including thehealth_kit_reporterFlutter plugin — can read, write and observe Apple Health data without touching HealthKit types directly.Example/hosts a UIKit demo app (MVVM with Combine) and a SwiftUI watch companion that exercise the public API end to end.
Strict engineering invariants, architectural rules, and operational protocols for AI agents and human contributors.
You operate as a Staff Software Engineer.
- Engineering Standards: Apply KISS, DRY, SOLID, and strict Test-First TDD.
- Zero Speculation: No code without tests. No superfluous wrappers or unnecessary abstractions.
- Refactoring Rule: Verify or write tests first. Refactoring requires a green test suite at every step.
- Match the Surroundings: New code reads like the code next to it — same file header, doc-comment shape, naming,
// MARK: -layout and parameter wrapping (§5). When in doubt, copy the nearest sibling (Quantity.swift,HealthKitReader.swift). - API Design: Follow the Swift API Design Guidelines (
CONTRIBUTING.md).
All proposals, comments, and PR descriptions must adhere to:
- BLUF (Bottom Line Up Front): Lead immediately with the core output or decision.
- Pyramid Principle: Core conclusion first, followed by structured, logical arguments.
- Plain English / Gutes Deutsch: Concise, technical, zero corporate fluff.
- Socratic Method: Guide architectural trade-offs via targeted questions rather than guessing.
- Strictly 1-2 sentences: Answer in 1-2 sentences and never add explanations, code walk-throughs or lists unless the prompt explicitly asks for them.
HealthKitReporter (facade, one HKHealthStore per instance)
├──► HealthKitReader ─┐
├──► HealthKitWriter ─┤ Services (Sources/Service/) — the only layer that runs HK queries
├──► HealthKitObserver ─┤
└──► HealthKitManager ─┘
│
▼
Types (Sources/Model/Type/) enums mapping to HKObjectType (QuantityType.stepCount → HKQuantityType)
Payloads (Sources/Model/Payload/) Codable structs mapping to HKObject (Quantity ⇄ HKQuantitySample)
│
▼
Decorators (Sources/Decorator/) Extensions+<Type>.swift — HK → payload harmonization & helpers
Sources/ and Tests/ MUST keep this layout:
Sources/
├── HealthKitReporter.swift (facade + public typealiases for result and completion handlers)
├── HealthKitError.swift (the single public error enum)
├── Decorator/
│ └── Extensions+<TypeName>.swift (one extended type per file, e.g. Extensions+HKElectrocardiogramClassification)
├── Model/
│ ├── <Protocol>.swift (Harmonizable, Original, Payload, UnitConvertable, ...)
│ ├── <ValueType>.swift (Anchor, QueryHandle, QueryDescriptor, Metadata, PreferredUnit, ...)
│ ├── Payload/
│ │ └── <PayloadName>.swift (Quantity, Category, Workout, ...)
│ └── Type/
│ ├── <TypeName>.swift (ObjectType, CharacteristicType, HealthKitObjectTypeConvertible, ...)
│ └── Sample/
│ └── <SampleTypeName>.swift
└── Service/
├── HealthKit<Role>.swift (Reader, Writer, Observer, Manager)
├── HealthKit<Role>+<Area>.swift (one area of a role, e.g. HealthKitReader+Statistics)
└── Retriever/
└── <Name>Retriever.swift (multi-step queries, stored-sample lookups; SampleResultsCollector joins them)
Tests/
├── <PayloadName>Tests.swift (one XCTestCase per payload)
├── <TypeName>Tests.swift (type mappings, e.g. ObjectTypeTests, CategoryTypeTests)
├── HealthKit<Role>Tests.swift, Query*Tests.swift, StoreOperationsTests.swift (services through the public API)
├── XCTestCase+Fixtures.swift (shared fixtures and assertions)
└── .swiftlint.yml (allows longer test bodies)
Example/
├── HealthKitReporter/ (UIKit MVVM demo app)
│ ├── Demo/ (DemoViewModel, DemoViewController, DemoView, DemoCell, DemoRow…)
│ └── Service/ (HealthKitReporterService and one DemoPerformer per library area)
├── HealthKitReporterWatch/ (SwiftUI watch companion for startWatchApp)
└── HealthKitReporter.xcodeproj (consumes the repo root as a local Swift package)
- No HK leakage: Public API takes and returns library types (
QuantityType,Quantity,QueryHandle,Anchor). RawHK*objects stayinternalbehindOriginal/Harmonizable/HealthKitObjectTypeConvertible; theno_public_healthkit_typeslint rule enforces it. - Identity: a payload's
uuidnames the stored HealthKit sample. HealthKit gives every saved object its own uuid, so writes that act on stored data (delete, add to workout, unrelate, attachments) look the stored object up byuuidthroughStoredSampleRetriever; never act on a freshasOriginal()copy. - One store:
HealthKitReporter.init()builds oneHKHealthStoreand injects it into every service. Services never create their own. - Decorators are internal glue:
Extensions+*.swifthold conversions and helpers; they never run queries.
- ❌ Ban on Singletons: No
static let shared, global instances or factory singletons. Consumers instantiateHealthKitReporter(); dependencies are passed throughinit.- Not covered — stateless static queries of the platform (
HealthKitReporter.isHealthDataAvailable) and static factories on payloads/types (Quantity.make(from:),ObjectType.make(from:),Quantity.collect(...)).
- Not covered — stateless static queries of the platform (
- ❌ Ban on Static-Only Utility Types: No
enum/struct/classthat only namespacesstatichelper functions. Put behavior in an extension of the type it belongs to (Extensions+Double.swift→Double.asDate). - ❌ Ban on Exposing
HK*Types in New Public API: New public methods and payload fields use library types; mapping happens inOriginal.asOriginal()andHarmonizable.harmonize(). - ❌ Ban on Unguarded Availability: Every API newer than the deployment target (iOS 15.0 / watchOS 8.0) MUST be gated with
@available(iOS X, *)on the declaration orif #available(iOS X, *)at the call site, with anelsethat throwsHealthKitError.notAvailable("\(type) is not available for the current iOS")or degrades gracefully. - ❌ Ban on Ad-hoc Errors: Throw only
HealthKitErrorcases with a descriptive message (HealthKitError.invalidType("Invalid HKQuantityType: \(type)")). No new error types, noNSError. - ❌ Ban on
fatalError/try!/ Force Casts: Useguard ... else { throw HealthKitError... };@unknown defaultarms throw or return a fallback. - ❌ Ban on Inputs HealthKit Raises For: HealthKit raises Objective-C exceptions, which Swift can't catch, for invalid input (unknown raw values, end before start, disallowed authorization types). Validate in Swift before calling it and throw
HealthKitError; never add Objective-C to catch them. - ❌ Ban on Silently Dropping Data: Never skip an entry that fails to convert (
try?,compactMap { try? … },catch { continue }). Convert collections withSequence.converted(name:_:)(or[HKSample].converted(_:)), so the first failure throwsHealthKitError.parsingFailednaming the entry; a nested member that fails fails its parent.- Not covered — a read metadata value no
Metadata.Valuecan express is skipped on its own (Dictionary.asMetadata, ADR 0002), so one metadata field never hides its sample; add a unit toMetadata.Value.quantityUnitsinstead of widening the skip.
- Not covered — a read metadata value no
- ❌ Ban on Catch-all Switches over Library Enums: A
switchoverQuantityType,CategoryType, etc. lists every case; nodefault:.@unknown defaultis required only for Apple's non-frozen enums. - ❌ Ban on Mutable Payloads: Payload fields are
public let. Changes go throughcopyWith(...). - ❌ Ban on Breaking the Dictionary Contract:
Payload.make(from:)keys andCodableproperty names are consumed by the Flutter plugin. Renaming one is a breaking change (!/BREAKING CHANGE:commit → major release). - ❌ Ban on Hardcoded Units in Payloads: Units are
Strings produced byHKUnit.unitStringand parsed withHKUnit(from:); SI defaults live only inHKQuantitySample.harmonize().
- ❌ Ban on
git commit --no-verify: Bypassing pre-commit git hooks, static analysis, or test suites is forbidden. - ❌ Ban on Blind Staging (
git add ./git add -A): Rungit statusfirst and stage only relevant source, test, and config files explicitly. - ❌ Ban on Direct Force Pushing: Force pushing to
masteris forbidden. Use--force-with-leaseon isolated feature branches only when necessary. - ❌ Ban on Single-Line Shortcut Commits: Omitting the detailed description,
Changes:, andTests:sections in commit messages is strictly forbidden. - ❌ Ban on AI Attribution Trailers: A commit message MUST NEVER carry
Co-Authored-By: <Model Name> <noreply@anthropic.com>, or any other trailer crediting an AI model or tool. - ❌ Ban on Committing User or Build State: Never commit
xcuserdata/,DerivedData,build/,.swiftpm/xcode/xcuserdata/or a generatedHealthKitReporter.xcodeproj. Never re-add CocoaPods files (*.podspec,Podfile,Pods/,Gemfile).
- Header: every Swift file starts with the Xcode header block, then imports (
HealthKitfor anything touching HK, otherwiseFoundation):
//
// Quantity.swift
// HealthKitReporter
//
// Created by <Name> on dd.MM.yy.
//
import HealthKit- Indentation: 4 spaces. Line length ≤ 110 in
Sources/,Tests/andExample/(.swiftlint.yml). - Wrapping: once a declaration or call doesn't fit on one line, put every argument on its own line and the closing
)on its own line. Multi-conditionguardlists each condition on its own line withelseon its own line. - Conformances: each protocol conformance lives in its own extension, preceded by
// MARK: - <ProtocolName>(// MARK: - Original,// MARK: - Payload,// MARK: - Factory,// MARK: - UnitConvertable). - Access Control:
publiconly for consumer-facing API; HK-backed initializers and protocol plumbing (Original,Harmonizable) stayinternal. Stored service dependencies areprivate let.
Every public declaration carries a doc comment in the existing format — type names in bold, defaults spelled out, thrown cases listed:
/**
Queries quantity types.
- Parameter type: **QuantityType** types
- Parameter predicate: **NSPredicate** predicate (optional). allSamples by default
- Parameter resultsHandler: returns a block with samples
- Throws: HealthKitError.invalidType
*/Short one-liners use /// **HealthKitWriter** class for HK writing operations. Completion typealiases in HealthKitReporter.swift document each labeled tuple element under - Parameters:.
- A type is a
public enum <Name>: Int, CaseIterable, <ObjectType|SampleType>. - Internal
var original: HKObjectType?maps each case with an exhaustiveswitch, returningnilunder an#availableguard for unsupported OS versions. The enum conforms to the internalHealthKitObjectTypeConvertiblein its own// MARK: - HealthKitObjectTypeConvertibleextension; other code reaches the HealthKit type throughObjectType.hkObjectType. - Public
identifierderives fromoriginal?.identifier; lookup by string goes throughObjectType.make(from:)orString.objectType. - Whether apps may write a sample type is
SampleType.isWritable; a type HealthKit only records itself goes into its read-only list inSampleType.swift. - Adding a case means updating every exhaustive switch over it (e.g.
HKQuantitySample.harmonize()unit mapping) and theCHANGELOG.md.
A payload is a public struct that mirrors one HK* class. Writable payloads follow Quantity.swift exactly:
- Nested
public struct Harmonized: Codablefor the value part (value, unit, metadata) with a public memberwiseinitandcopyWith. public letfields (uuid,identifier,startTimestamp,endTimestamp,device,sourceRevision,harmonized). Dates areDoubleseconds since 1970. Stored samples conform toSample(uuid,identifier, timestamps).internal init(<hkName>: HK...) throwsfrom the HealthKit object.public init(uuid: String = UUID().uuidString, ...)memberwise +public func copyWith(...)with every parameter= niland?? self.<field>fallbacks;copyWithkeeps theuuidunless one is passed.- Extensions, each behind a
// MARK: -:Original—asOriginal() throws -> HK...,guard+HealthKitError.invalidTypeon bad identifiers; validates what HealthKit raises for (startTimestamp.checkInterval(to:), known raw values).Payload—static func make(from dictionary: [String: Any]) throws -> Self; reads"uuid"withdictionary.payloadUUID,Doubles asNSNumberwithDouble(truncating:),Int/Boolwithdictionary.int(_:)/dictionary.bool(_:); missing required keys throwHealthKitError.invalidValue("Invalid dictionary: \(dictionary)").collect(from:)comes from thePayloadprotocol extension; don't reimplement it.Factory—static func collect(results: [HKSample]...) throws -> [Self]built onresults.converted { (sample: HK...) in try Self(...) }; a sample that fails to convert, or one of another class, throwsHealthKitError.parsingFailed.
Read-only payloads (HealthKit doesn't let apps write them: Electrocardiogram, ClinicalRecord, VerifiableClinicalRecord, ActivitySummary, Statistics, MedicationDoseEvent, UserAnnotatedMedication, Attachment, DeletedObject, Characteristic) keep steps 1–3, the public init and the Payload extension, and leave out Original; copyWith is optional for them.
Doc comments are required on every public type, initializer, function and protocol; stored fields get one when their unit or meaning isn't obvious from the name (/// seconds since 1970).
- One file per extended type:
Extensions+<TypeName>.swift. HK*sample extensions conform toHarmonizable(typealias Harmonized = <Payload>.Harmonized;func harmonize() throws -> Harmonized).- Helpers that are pure conversions are computed properties (
asDate,asMetadata,asOriginal).
public class HealthKit<Role>with a singlelet healthStore: HKHealthStoreinjected viainternal init(healthStore:). It isinternalrather thanprivatewhen the role is split overHealthKit<Role>+<Area>.swiftfiles, which share it; a role in one file keeps itprivate.- Query builders (
HealthKitReader,HealthKitObserver) validate input, build and return aQueryHandle— they never execute it. The consumer runs it withmanager.executeQuery(_:)and stops it withmanager.stopQuery(_:). Anchored queries take and hand back anAnchor; several types go in one query throughQueryDescriptor. - Callbacks use the public typealiases declared in
HealthKitReporter.swift(QuantityResultsHandler,StatusCompletionBlock,SaveCompletionBlock). A new callback shape gets a new documented typealias there, with labeled parameters. - A callback reports either results or an error, never partial results with an error; empty results with no error mean "no data", never a failed cast or conversion (report
HealthKitError.invalidTypeorHealthKitError.parsingFailedinstead). - Anchored queries hand back the anchor they started from on any error, so the next run delivers the same changes again.
- Error path in a result handler:
guard error == nil, let results = data else { handler([], error); return }. - Defaults mirror existing methods:
predicate: NSPredicate? = .allSamples,limit: Int = HKObjectQueryNoLimit, sort byHKSampleSortIdentifierStartDatedescending. - Multi-step queries (e.g. ECG + voltage, heartbeat series) live in
Service/Retriever/<Name>Retriever.swift.
- Programmatic UIKit, MVVM with Combine; no storyboards or XIBs except
LaunchScreen.xib.AppDelegatecreates the window and aUINavigationControllerwithDemoViewController. DemoViewModelexposes anInputstruct (subjects for user intents: tapped rows, launch) and anOutputstruct (subjects holding state: sections, per-row results, progress); the pipelines from input to output are built ininit.DemoViewControllerinstallsDemoViewinloadView()(view = baseView) and only binds the view model's output to the view and the view's events to the input. It never adds subviews itself.- Views (
DemoView,DemoHeaderView,DemoCell) declare each subview as a closure-initialized property that carries its own styling, and their initializers calladdSubviews()thenmakeConstraints(). HealthKitReporterServiceexposes onlypublisher(for:). It owns oneDemoPerformerper library area (ReaderDemos,WriterDemos, …), each keeping itsHealthKitReporterand any state (anchors, live queries)private.- UI updates from HealthKit callbacks reach the main queue through
receive(on: DispatchQueue.main)orDispatchQueue.main.async, with explicit[unowned self]/[weak self]captures. - Authorization is built from the
allCasesof every type enum: reads leave out correlations (HealthKit authorizes their component types), writes keep the types whoseisWritableis true, clinical records are requested separately (they start Health's records flow), and vision prescriptions and medications use per-object authorization. - The watch companion (
Example/HealthKitReporterWatch) uses SwiftUI, since watchOS has no UIKit; it handlesstartWatchAppand depends on the local package. - Every new public library method gets a
DemoRowand a demo in the matchingDemoPerformer, and a usage snippet inREADME.md. A new type case is covered automatically by theallCases-driven authorization.
The codebase enforces test-first TDD. Code without tests will be rejected.
| Category | Target | Package | Purpose & Scope |
|---|---|---|---|
| Payload Tests | Sources/Model/Payload/* |
XCTest |
Round-trip every payload: create → encoded() → JSONDecoder decode, make(from:) from a dictionary shaped like the Flutter plugin sends it, and uuid kept by make(from:) / copyWith. |
| Type Tests | Sources/Model/Type/* |
XCTest |
identifier, make(from:) and isWritable for new cases. |
| Service Tests | Sources/Service/* |
XCTest |
Through the public API on the simulator. The test host has no HealthKit entitlement, so HealthKit answers every query and write with an error: test validation (invalidType, invalidValue), that every callback fires, and that calls reach HealthKit. |
| Example App | Example/ |
manual, device | Mapping real HealthKit data to payloads needs authorization and data; verify new reader/writer/observer APIs through the demo app on a device or simulator. |
- One
class <PayloadName>Tests: XCTestCaseper payload inTests/<PayloadName>Tests.swift,import HealthKitReporter(public API only — no@testable). Service and query tests get one class per role or area. - Name the object under test
sut. Test names describe the flow:testCreateThenEncodeThenDecode,testCreateFromDictionary. - Use fixed timestamps (
Date(timeIntervalSince1970: 1626884800)), assert every field, and compare floating values withaccuracy:. - Test methods are
throws; notry!/do-catchswallowing.
# 1. Run the library test suite on a simulator (SwiftPM package scheme) with coverage
xcodebuild test -scheme HealthKitReporter \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" \
-enableCodeCoverage YES -resultBundlePath TestResults.xcresult \
CODE_SIGNING_REQUIRED=NO
xcrun xccov view --report --only-targets TestResults.xcresult
# 2. Build for watchOS (needs the watchOS platform installed in Xcode)
xcodebuild build -scheme HealthKitReporter \
-destination "generic/platform=watchOS Simulator" CODE_SIGNING_REQUIRED=NO
# 3. Lint Sources, Tests and Example — every violation fails
swiftlint lint --strict
# 4. Build the example app and its watch companion after a public API change
xcodebuild build -project Example/HealthKitReporter.xcodeproj \
-scheme HealthKitReporter_Example \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" CODE_SIGNING_ALLOWED=NO
xcodebuild build -project Example/HealthKitReporter.xcodeproj \
-scheme HealthKitReporterWatch \
-destination "generic/platform=watchOS Simulator" CODE_SIGNING_ALLOWED=NO.github/workflows/ci.yml runs all four on every PR and push to master, plus a version/changelog guard.
There is no lint baseline. An inline swiftlint:disable carries a comment with its reason.
- Coverage: every payload, type and
Payload.make(from:)path is covered. Code that only runs on HealthKit data (harmonize decorators, HK initializers, factories) can't be reached without the entitlement and is checked through the Example app. - Cycle:
- 🔴 Red: Write a failing XCTest defining the specification before production code.
- 🟢 Green: Write minimal production code to pass the test.
- 🔵 Refactor: Clean up implementation details while ensuring tests stay green.
Before any commit or PR creation, the codebase must pass all gates:
swiftlint— zero warnings or errors.xcodebuild test— all tests green; quote the executed/failed counts it prints.- watchOS build — passes (CI
Packagejob; state "verified in CI only" when the watchOS platform isn't installed locally). - Example app builds — required whenever public API changes. Otherwise state "not applicable — no public API change" in the evidence line rather than omitting it: an unstated gate reads as a skipped one.
- Coverage —
HealthKitReporterline coverage reported byxccovis ≥COVERAGE_THRESHOLDin.github/workflows/ci.yml(CIPackagejob fails below it); quote the measured percentage. A PR that adds tests raises the threshold to its new measured level (rounded down to one decimal); never lower it.
- SemVer: breaking public API or dictionary-contract changes → major; new types/features → minor; fixes → patch.
- Releases are automated by release-please (
.github/workflows/release.yml,release-please-config.json). It derives the bump from Conventional Commits onmasterand keeps achore: release X.Y.ZPR open that bumps.release-please-manifest.json, prepends the## [X.Y.Z] - dd.MM.yyyy.entry toCHANGELOG.mdand updates thex-release-please-versionline inREADME.md. - Merging the release PR creates the bare tag
X.Y.Z(novprefix) and the GitHub Release — that tag is the SwiftPM release. Never tag, bump versions or edit releasedCHANGELOG.mdentries by hand. - Commit messages are the changelog: write the summary for consumers.
- New public API is documented in
README.mdwith a usage snippet.
All branches MUST follow the strict user initials and issue structure:
<initials>/issue-<XXX>
- Example:
vk/issue-14orab/issue-102
# View assigned issue context
gh issue view <issue_number>
# Create feature branch for issue (ALWAYS branch off a freshly pulled master)
git fetch origin
git switch master && git pull --ff-only origin master
git checkout -b <initials>/issue-<issue_number>
# Create Pull Request using gh CLI
gh pr create \
--title "<type>(<scope>)[!]: <short summary>" \
--body "## Summary
<description>
## Changes
- <file_path>: <details>
## Tests
- Summary: XCTest suite green, swiftlint clean, watchOS + Example builds passing."
Refs: #<issue>"
# Check PR checks and review status
gh pr status
gh pr checksBefore committing or creating a PR, run this exact sequence:
# 1. Lint
swiftlint lint --strict
# 2. Execute test suite
xcodebuild test -scheme HealthKitReporter \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" CODE_SIGNING_REQUIRED=NO
# 3. Build the Example app (public API changes)
xcodebuild build -project Example/HealthKitReporter.xcodeproj -scheme HealthKitReporter_Example \
-destination "platform=iOS Simulator,name=iPhone 17,OS=latest" CODE_SIGNING_ALLOWED=NO
# 4. Inspect file status before staging
git status
# 5. Stage specific changed files intentionally (NO blind `git add .`)
git add Sources/Model/Payload/<Name>.swift Tests/<Name>Tests.swift
# 6. Commit using strict multi-paragraph format
git commit -m "<type>(<scope>)[!]: <short summary>" \
-m "<longer description / context>" \
-m "Changes:
- <file_path>: <details>
- <file_path>: <details>" \
-m "Tests: <summary>" \
-m "Refs: #<issue>"<type>(<scope>)[!]: <short summary>
<longer description / context>
Changes:
- <file path>: <details>
- <file path>: <details>
Tests: <summary>
[BREAKING CHANGE: <what breaks and how to migrate>]
Refs: #<issue>
- The header is a Conventional Commit — release-please parses it to pick the version bump and changelog section; a header in any other shape is silently left out of the release.
<type>:feat(→ minor),fix(→ patch), ordocs,test,refactor,ci,build,chore(no release).!after the scope or aBREAKING CHANGE:footer → major.Refs: #<issue>is required whenever an issue exists.
Before outputting code or submitting PRs, explicitly verify:
- Does every new file start with the Xcode header block and match the sibling files' style?
- Are singletons and static-only utility types absent, with
HKHealthStoreinjected viainit? - Does the change follow the
Sources/{Decorator,Model,Service}layout, one extended type perExtensions+<Type>.swift? - Is new public API free of raw
HK*types, with mapping inOriginal/Harmonizable? - Do writes that act on stored samples look them up by
uuid, and does every input HealthKit raises for get validated in Swift first? - Is every API newer than iOS 15.0 / watchOS 8.0 gated by
@available/#available, with aHealthKitError.notAvailablefallback? - Are errors thrown only as
HealthKitErrorcases with descriptive messages, withoutfatalError,try!or force casts? - Does every conversion of HealthKit results report a failure (
converted,parsingFailed) instead of skipping it withtry?orcontinue, and do anchored queries keep the caller's anchor on errors? - Does every new payload follow
Quantity.swift:Harmonized: Codable,public letfields, memberwiseinit,copyWith, and// MARK: -extensions forOriginal,Payload,Factory? - Are new enum cases handled in every exhaustive
switch, with nodefault:over library enums? - Are
Payload.make(from:)keys andCodablenames unchanged, or is the break versioned as major? - Does every
publicdeclaration carry a doc comment in the- Parameter/- Throws/- Returnsformat? - Are wrapped calls/declarations one-argument-per-line, and lines ≤ 110?
- Are
testCreateThenEncodeThenDecodeandtestCreateFromDictionary(or equivalents) written first and green? - Did
swiftlintandxcodebuild testpass, the watchOS build pass, and the Example app build if public API changed? - Were
README.mdand the Example app updated for user-visible changes, leavingCHANGELOG.mdand versions to release-please? - Does every new public method or type case have a demo in the Example app (a
DemoRowfor methods,allCasesauthorization for types)? - Is the branch named strictly
<initials>/issue-<XXX>? - Are git commits made without
--no-verifyand staged without blindgit add .? - Was
gh pr createused with structured title/body matching commit specs? - Does the commit message carry the Conventional
<type>(<scope>)[!]: <summary>header, a description,Changes:,Tests:andRefs: #<issue>(§8), with no AI trailer?