All notable changes to this project are documented in this file.
The format is based on Keep a Changelog and this project adheres to Semantic Versioning.
- Publish the scoped Simulator runtime with 14 MCP tools: installed Xcode/device discovery, native Simulator display, private sidebar preview, build/test/install/launch and screenshots with optional MCP image output.
- Add a loopback browser viewer with token-protected screenshot access, bounded refresh, pause/fit controls and cleanup on stop. Native Simulator handles touch/keyboard interaction.
- Add dated iPhone Duo guidance grounded in Apple's September 9 announcement. Discover actual installed profiles rather than inventing Duo support; this development Mac has no Duo device type.
- Refresh Apple technology and updates snapshots; bundle the new local guidance in MCP 2.4.0 and integration archives.
- Reduce the always-loaded skill entry point and move the full engineering rules into an on-demand guide.
- Bundle canonical local guides, source files, templates and assets in the knowledge MCP; store identical bodies once by content hash.
- Add offline source search, heading outlines and bounded exact-content reads with continuation offsets. Technology lookup defaults to a compact overview; full guide retrieval remains explicit.
- Add an original AppleRecipes Swift package with source, tests and Apple guidance attribution. Keep implementation coverage separate from the 405-technology directory.
- Ship MCP 2.3.1 and updated ChatGPT/Codex/Gemini integration archives with the offline source workflow.
- Complete 405-technology Apple directory, 96 update/release-note landing pages, and layer-by-layer Icon Composer guidance.
ios-agent-mcp2.2.0 includes a separate knowledge server with five tools, stdio and Streamable HTTP, bundled public reference data, and protocol tests.@nagarjuna2002/ios-agent0.2.0 adds app briefs, optional XcodeGen scaffolding and editable SVG icon layers with overwrite protection.- Portable ChatGPT skills-only and Codex plugin ZIPs, Gemini CLI extension, and explicit client setup instructions.
- GitHub Release assets include npm tarballs, self-contained plugin bundles and SHA-256 checksums. Public marketplace acceptance and a hosted endpoint are separate deployment steps.
ios-simulator-mcp/-- the first executable slice of the v4 Build -> Run -> See -> Fix loop. It is a separate MCP package fromios-agent-mcp, with a macOS + Xcode runtime contract instead of a lightweight static-analysis contract.- Implemented safe runtime tools for
simulator_list,simulator_boot,simulator_shutdown,build_project,run_tests,install_app,launch_app,terminate_app,open_deep_link, andscreenshot. - The package wraps
xcrun simctlandxcodebuildwith typed command construction, bounded timeouts, structured MCP output, and tests that pin the generated command arguments. Destructive simulator erase/reset remains intentionally absent. - README,
SKILL.md,ROADMAP.md, anddocs/tooling/ios-simulator-mcp.mdnow distinguish the implemented first package from the planned video/log/UI-driving tier.
- Apple Platform Intelligence -- the v3.0 release direction. The repository now has a generated Apple technology catalog (
frameworks.json->docs/apple-framework-index.md), README coverage statistics derived from that catalog, and CI/hook checks that fail stale catalog output. - P0 domain hubs for AI, professional UI/UX, animation, graphics/3D/spatial, data, networking, WebKit, security, performance, and XCUIAutomation. These are routing guides with decision matrices, examples, anti-patterns, production checklists, and links to existing deep framework guides rather than empty placeholder files.
- Native web-animation translation guidance for Anime.js, GSAP, Framer Motion, Motion One, Three.js, PixiJS, p5.js, Matter.js, WebGL/WebGPU, and CSS animation vocabulary. Native SwiftUI/UIKit/RealityKit/SpriteKit/Metal remains preferred unless WKWebView is an intentional product requirement.
- Eleven new read-only specialist agents:
swiftui-expert,uikit-expert,core-ai-expert,app-intents-expert,realitykit-expert,metal-expert,webkit-expert,testing-expert,xcode-expert,security-reviewer, andapp-store-reviewer. The repo now exposes 24 subagents, and the MCP skill linter tests assert that count. - Reusable P0 pattern indexes for animation, RealityKit, Metal, AI, WebKit, testing, and accessibility.
- MCP static-analysis roadmap expansion for
review_webkit,review_networking,review_persistence,review_storekit, andreview_permissions, while keeping simulator execution documented as the future separateios-simulator-mcppackage.
-
The
clijob's first run on Windows failed, and the defect was in the test, not the product.globalCacheDirresolves theXDG_CACHE_HOMEvalue withpath.resolve, which anchors a drive-less absolute path to the current drive:/xdgbecomesD:\xdgon a Windows runner and stays/xdgon Linux. The expectation was built withpath.join, which does not anchor, so it passed on Linux and macOS and failed on Windows.Reproduced exactly with
path.win32against a drive-rooted cwd before changing anything -- old expectation\xdg\ios-agent, function returnsD:\xdg\ios-agent. The expectation now uses the same call the function makes, so the two agree by construction on any host rather than by coincidence on most of them.The trap is documented on
globalCacheDiritself: itsplatformandhomeparameters let one machine exercise all three branches, butpathstill uses the host's rules, so a hardcoded expected string is host-dependent even though the input is not.The other five jobs passed, including the two that had never run anywhere:
samples/SkillPatternscompiles on macOS, which is the first real verification of the Accelerate patterns, and the CLI passes on ubuntu and macOS.
A field report from building a SwiftData notes app with a RealityKit gallery against this skill (Xcode 26.6 / iOS 26.5) found thirteen defects. Every claim was verified against the repository before anything changed. Three came back different from the report in ways that changed the fix, and one came back considerably worse.
-
F1 --
Color(hex:)fragmentation. Confirmed: 329 string call sites, 7 integer ones, three definitions. The mechanism is not quite "cannot coexist" -- theUInt32andStringforms are distinct overloads and would compile side by side. The actual compile error is thatcolor-system.mdanddesign-system.swiftboth declaredinit(hex: String)with different bodies: copy both into one target and you getinvalid redeclaration of 'init(hex:)'. Separately,SKILL.mdroutes agents todesign-tokens.md, which carried only theUInt32form, so a project that follows the routing table breaks all 329 string sites.One canonical definition now lives in
design-tokens.mdaccepting both forms. The string form no longer falls back to black on a malformed literal -- black is indistinguishable from a deliberate colour and so shipped unnoticed. It traps in DEBUG and renders magenta in release, which appears in none of the palettes. -
F2 --
install.shcouldgit pullinside the user's own repository. Confirmed exactly, sequentialifs included. Detection is anelifchain;INSTALL_DIRis never the project root; and the update path now checks the remote URL rather than the presence of.git, because a directory can be a git repository and still be someone else's work -- which is precisely the case this got wrong. Regression-tested against a fake user repo with a stubbedgit. -
F3 -- the shipped template violated the skill's own non-negotiables. Confirmed, and three more templates had the same
@Observable-without-@MainActordefect. The template now has a typed error, an injected repository,CancellationErroras the deliberate no-op, five previews (one per state, none touching the network), and a composition root. It also dropped a.font(.system(size: 64))that the repo's own hook rejects. -
F4 -- RealityKit pitfall 7 was factually wrong, contradicting the platform table eight lines below it. Narrowed to
ImmersiveSpaceand hand/eye input, which genuinely are visionOS-only. -
F5-F8 -- no non-AR RealityKit guidance existed. All nine search terms returned zero files; verified. New section 16 covers
content.camera = .virtualand why omitting it starts an AR session behind your scene, the light rig a virtual scene needs, the ~27 degree portrait horizontal field with the arithmetic and the depth-not-width rule it implies, both forms of the orbit-control camera override, theImageRenderertexture route sincegenerateTextis not in the iOS SDK, and theSwiftUI.Sceneambiguity. -
F9 -- the SortDescriptor section did not merely omit the
Boolconstraint, it demonstrated it.SortDescriptor(\Task.isCompleted)was a shipped example that does not compile. Fixed, with both workarounds and an explanation of why theNSObjectdiagnostic points somewhere else entirely. -
F10 --
VisualEffect's supported surface documented as a table, with the.shadowcase and why "unable to type-check in reasonable time" misdirects. -
F13 -- worse than reported, and the fix is the opposite of re-picking hexes. The report found one palette colour failing 4.5:1. Measuring all forty found that 34 of them fail against white -- and that all forty pass against black. The defect was never the palettes; it was
SKILL.mdtelling agents to put white text on saturated pills.#34C759with white is 2.22:1, a third of the required ratio, on a colour that looks like it should take white.The rule now says to choose the foreground by measurement, every palette row publishes its measured foreground and ratio, and
scripts/check-contrast.mjskeeps them honest in CI. Mutation-tested both ways: a falsified ratio and an omitted one each fail the build.
Four new checks, all mutation-tested, so none of the above can regress: one Color.init(hex:) definition; no template with @Observable lacking @MainActor or a dependency defaulted to a live implementation; and every published contrast ratio matching its measurement.
The template check's first draft had two false positives -- it read Swift attribute order as significant and flagged a doc comment quoting the anti-pattern -- which is why the two files it accused were inspected rather than "fixed".
-
cli/--ios-agent, a scaffolding CLI, anddocs/tooling/project-scaffolding.md, the design behind it. A generated project shows the user three entries; everything the tool owns lives in a hidden.ios-agent/.The rule that decides where anything goes is authorship, not importance. If a human writes it, it is visible; if the tool writes it, it is hidden. Importance is the tempting axis and has no edge -- everything feels important to whoever added it, so a layout sorted that way grows a root directory per release. Authorship has a sharp edge, and two things fall out of it that are worth more than the tidiness:
cleanneeds no confirmation prompt because nothing in there was authored, and "delete.ios-agent/and re-run" becomes structurally safe advice rather than a risk.The leverage is not the directory name, it is that one declaration produces four behaviours. A row in
INTERNAL_ENTRIESautomatically yields its line in the generated.ios-agent/.gitignore, its inclusion in or exclusion fromclean, its path inwhere --json, and adoctorcheck that it has not leaked to the project root. The alternative -- a constant here, a gitignore line there, a clean list elsewhere -- is three places that must be edited together and eventually are not, and the failure is quiet: a new cache directory thatcleanskips and git happily commits. The test asserting the two sets are complements does not care what the entries are; it fails the day someone adds a row that is both tracked and deletable..ios-agent/doubles as a root marker, the way.git/does, which is how the CLI and the MCP server agree on a project without either configuring the other. Both now report how the root was resolved, not just what it is -- an implicit root is unfalsifiable, and without it "0 Swift files" is identical whether the project is empty or the tool is pointed somewhere else.ios-agent-mcpstays read-only. It reads the marker and never creates it, so thefilesystem: read, network: nonecontract is unchanged. Scaffolding writes, so it is a separate package rather than a quiet turn from analyzer into something that mutates your project.Cross-platform throughout: user-level caches use each platform's own location (not
~/.ios-agent, which is on no platform's list), Windows reserved device names are rejected everywhere so a Mac-created project still checks out on Windows, and tracked config stores POSIX paths because that file crosses machines by design. CI runs the CLI on ubuntu, macOS, and Windows, and asserts end to end that the project root contains exactlyApp,LICENSE,README.md.42 CLI tests, plus 7 in
ios-agent-mcp(123 -> 130) covering marker discovery -- including the case that a file named.ios-agentis not a marker.doctor's checks are mutation-tested: each one is shown to fail when the layout is actually broken.Notably not included: an
.xcodeprojgenerator. That is a build-system artifact whose format Xcode owns, and a generated one drifts from what Xcode would have made.Repo surfaces caught up with what ships: the README gained a CLI row, an install line, and a What's New in 2.1 section (2.1.0 had shipped with no entry), and
docs/mcp/installation.mddocuments the four-step root resolution including the.ios-agent/marker. One stale claim removed rather than updated --examples.mdpinned a Swift test count this environment cannot verify, and writing a number I have not seen reported is worse than writing none.Second pass, applying the same one-declaration move to the command surface:
COMMANDSdrives dispatch,help, and the bash and zsh completion scripts, so help cannot describe a flag the parser rejects.--jsononwhere,info,clean, anddoctor; exit codes split so a script can tell "the project is unhealthy" (2) from "you called it wrong" (1) without parsing stderr; anddoctor --fix, which repairs only defects with a derivable correct value -- a stale generated gitignore, a config behind the current layout version. It deliberately will not create a missingApp/: there is no safe automatic answer, and a--fixthat guesses turns the safe command into a source of surprise changes. A test asserts exactly that. 42 -> 53 CLI tests. -
docs/frameworks/accelerate.md-- Accelerate, and the first framework doc whose central point is an isolation rule rather than an API surface.Accelerate functions are synchronous C: they inherit whatever isolation calls them and never yield. A
@MainActor @Observableview model callingvDSPblocks the main thread for the entire computation, and nothing in that code looks wrong -- there is no warning, no runtime check, and the vDSP call itself is correct. It is the concrete case of the pitfallSKILL.mdalready states abstractly, so that rule now names the synchronous C frameworks it applies to.Routing first, because reaching for the wrong sub-library is the common failure and it goes both ways: hand-writing a loop over 100,000 samples wastes the vector unit, and calling
vDSP.addon a three-element vector costs more in call overhead than the loop it replaced -- that is whatsimdis for. Then depth on the two that matter, vDSP and vImage.The FFT section documents the two things that produce a spectrum that looks right and is wrong: bin 0 is not purely DC (the real-to-complex transform packs the Nyquist term into its imaginary part), and the output carries a factor of two that never moves a peak and invalidates every absolute magnitude. Plus the rule that catches both -- test transforms against a known answer, not a snapshot. A signal with eight periods across the frame must peak in bin eight; that fails when the transform is wrong rather than when the numbers change.
vImage is covered as an ownership problem, because that is what it is:
vImageBuffer_Inithands youmalloced memory nothing will free, and per video frame the result is a jetsam kill that reads as the OS killing the app for no reason.8 anti-patterns, a 19-item checklist, and honest scoping -- BLAS/LAPACK gets the one fact that actually bites (they are column-major, so a row-major Swift array solves the transposed system and returns a plausible wrong answer), and BNNS routes to Core ML, since hand-building a network on a CPU-only library gives up the Neural Engine entirely.
-
samples/SkillPatterns/Sources/SkillPatterns/SignalProcessing.swift-- the Accelerate patterns, compile-checked.AccelerateSpectrumAnalyzeris anactorfor a stated reason:vDSP.FFTis expensive to construct and is notSendable, so it cannot be stored in aSendablevalue type -- the same shape as owning anNWConnectionor aModelContext. The protocol requirement isasyncbecause that is the only part of the signature preventing a caller from doing the work on the main actor by accident, and a synchronous stub witnesses it, so a preview needs no audio and no Accelerate at all.Behind
#if canImport(Accelerate)so the package still builds on Linux. 13 new tests, four of them known-answer transforms. -
docs/design/liquid-glass-adoption.md-- the migration half of Liquid Glass.design-tokens.mdcovered applying the material to a view you own; nothing covered what an SDK rebuild does to an app you already shipped, which is the part that actually costs time.A coverage check found the gap was near-total:
UIDesignRequiresCompatibility,backgroundExtensionEffect,tabBarMinimizeBehavior,Tab(role: .search),ToolbarSpacer,ConcentricRectangle,safeAreaBar, the scroll edge effect, and Icon Composer appeared in zero files.The organising rule: the system now owns the background of controls and navigation. Custom backgrounds on bars, split views, sheets, and popovers no longer just look dated -- they sit on top of Liquid Glass and defeat the scroll edge effect, so content scrolling underneath loses the contrast the system would have supplied.
Two changes ship silently and are worth the doc on their own. Rebuilding adopts the new design with no code change, so an unaudited app ships a changed interface;
UIDesignRequiresCompatibilityis the escape hatch, documented as a stopgap with a removal date rather than a decision. And section headers are no longer force-capitalised -- a header written"recently played"used to renderRECENTLY PLAYEDand now renders exactly as typed. Nothing warns; it just ships.8 anti-patterns, an 18-item checklist, and a per-setting test matrix (Reduce Transparency, Reduce Motion, Increase Contrast, dark mode, accessibility text sizes). The last anti-pattern is the one this skill flags most often: a blanket
#available(iOS 26, *)around every new API. Liquid Glass arrived in iOS 26 but these APIs did not all land together, so each symbol's floor gets checked in Xcode rather than assumed --check_availability_guardscatches the over-restrictive direction.Cross-linked from
design-tokens.md§4 andios-deployment-migration.md, so whichever door you come in by leads to the other half.
Eleven MCP tools, three resources, and a version identity that cannot drift. Tests 41 -> 123. The first release where every claim below was checked by CI rather than asserted -- including the Swift, which had never been compiled before this cycle.
-
lint_skill-- a seventh MCP tool, and the first that does not read Swift. It validates a skill repository's own metadata:SKILL.mdfrontmatter (required keys, kebab-case name, semver version, and the description length limits either side of which the skill stops triggering reliably), subagent definitions in.claude/agents/, generated mirror drift, and backtick-quoted doc paths that do not resolve. Shipped as part ofios-agent-mcp2.1.0.Three of its rules exist because the failure is otherwise silent:
agent-read-only-holds-write-toolcatches a subagent whose description promises read-only while its frontmatter grantsEditorWrite-- the main agent delegates on that promise, and a reviewer that can edit will fix what it was supposed to report;agent-name-filename-mismatchcatches a rename that leaves every delegation prompt pointing at an identifier the loader never registered; andagent-unknown-toolcatches a misspelled tool name, which is not granted and not reported.Mirror checking is self-calibrating -- mirrors are compared only when at least one already matches
SKILL.mdbyte-for-byte, so a project with a hand-writtenCLAUDE.mdis not told all 24 mirrors have drifted. Every report opens with what it inspected, because a clean report that never states its scope is indistinguishable from a check that never ran.31 new tests (41 -> 72), including a mutation check: neutering the read-only boundary rule fails two of them. Dogfooding it against this repository immediately found one real defect -- see below.
-
docs/frameworks/authentication-services.md-- Sign in with Apple, passkeys, andASWebAuthenticationSession. Closes a gap whereSKILL.md's framework selection table named AuthenticationServices for auth but no documentation existed behind it. Covers the three failure modes that only surface in production: name and email are returned on the first authorization only, the user can revoke access from Settings sogetCredentialStatemust be checked on launch, and the identity token -- not the user identifier -- is the only value a server may trust. Also the delegate-to-asyncbridge with its resume-exactly-once requirement, nonce generation for replay protection, passkey registration and assertion, OAuth viaASWebAuthenticationSessionwith PKCE, Keychain storage, and 13 anti-patterns. -
scripts/eval-agents.sh-- verifies that every subagent's declared tool grant matches the instructions relying on it.--tableprints the grant matrix;--self-testbuilds agents each broken in exactly one way and asserts every rule fires. Wired into CI and the Stop hook.The obvious design -- prompt each agent to edit something and check it refuses -- does not test the boundary. Tool restriction is enforced by the harness from the
tools:line before the model is ever consulted, so a prompt check passes for an agent whose frontmatter wrongly grantsWrite(the model simply chose not to use it) and fails intermittently for one that is correctly restricted. What decides the boundary is the declaration, so the declaration is what gets checked.Building it surfaced the same false-positive trap three times, each caught by running against the real definitions: read-only status must be read from the description, not the body, because bodies carry scoped prohibitions that mean something else (
ios-docs: "never edit a generated file";swift-refactorer: "do not change access levels"); a shell command counts only inside a fence or after a$prompt, sinceios-plandiscusses "Swift Testing" the framework and runs nothing; and a$prefix is decisive on its own, becausefoundation-modelsmandates a literal$ <build/test command>in its output template. All four phrasings are now regression cases in--self-test. A planned over-grant rule forEdit/Writewas dropped rather than shipped wrong -- "extract subviews, introduce protocol seams" all mean editing, and no regex generalizes over that. -
SwiftData and
AsyncSequenceinsamples/SkillPatterns/--Persistence.swiftadds@Model, a@ModelActorstore, andModelContainerinjection through a composition root;Streaming.swiftadds a push source behind anAsyncSequenceprotocol plus aGateactor for deterministic suspension in tests. 24 tests -> 49.The store conforms to the
ArticleRepositoryprotocol that already existed in the domain layer, which is the point of adding it here:testTheSameViewModelDrivesSwiftDataUnchangedruns the sameArticleListModelover a realModelContainerwith nothing changed. If that had required touching the view model, the boundary protocols were decorative.Rules the new code exists to pin down:
@Modelis a persistence type and never the entity the UI renders -- conflating them compiles and then corrupts data the first time a background import touches a model the UI is reading; everything leaving the actor is converted to value types inside it; aPersistentIdentifiermay cross the boundary but the model object may not; and one container is created in one place, because a second container over the same store is a second source of truth whose symptoms (writes that vanish, stale reads) look nothing like the cause.Timing-sensitive transitions (
isLoadingwhile in flight, a second load superseding a first) go throughGaterather than sleeping. A test that sleeps and hopes passes locally and flakes in CI, which is worse than no test -- it teaches people to re-run until green. -
Three framework docs from the parked backlog, each led by the failure that only shows up in production:
-
docs/frameworks/local-authentication.md--LAContext, the biometry policies, lockout and fallback. Leads with the fact that decides whether any of it is worth writing:evaluatePolicyreturning true is a UI event, not a security boundary. It reports that the system showed a prompt and the user satisfied it; it protects nothing, and the branch is patchable. The real boundary is a KeychainSecAccessControlnaming biometry, where the Secure Enclave never releases the bytes. Also: oneLAContextper authentication (a reused context caches its success, so the second screen unlocks with no prompt at all),canEvaluatePolicybeforebiometryType(read the other way it reports.noneon a device with working Face ID),.biometryCurrentSetover.biometryAny, and the missingNSFaceIDUsageDescriptionthat crashes on first prompt rather than at launch. 12 anti-patterns, 14-item checklist. -
docs/frameworks/swift-charts.md-- marks, scales, axes, selection, andAXChartDescriptorfor Audio Graphs. Two rules carry most of the value: a truncated y-axis on a bar chart renders a 3% difference as a 300% one, and there is no virtualisation -- 50,000 marks drops frames while showing no more information than 400 does at that pixel width, so the answer is peak-preserving downsampling off the main actor, never stride-sampling (which deletes the spike the user opened the chart to see). Also.valuelabels are user-facing and read aloud, meaning must be carried by more than hue, and iOS 17+ API (SectorMark, selection, scrolling) guarded at 17. 13 anti-patterns, 14-item checklist. -
docs/frameworks/network-framework.md--NWConnection,NWListener, framing, TLS pinning,NWPathMonitor. Opens by sending most readers away:URLSessionalready sits on this framework and reimplementing HTTP overNWConnectionarrives somewhere worse. For the cases that remain: TCP is a byte stream, not a message stream, so code treating one receive as one message works on localhost and fails on a real network;NWConnectionis notSendableand its handlers land on your queue, so it belongs inside an actor rather than behind aDispatchQueue.main.async; and.waitingis not.failed-- tearing down on the former discards the recovery the framework is already doing. Also unbounded length prefixes, continuations resumed twice across.waiting -> .ready -> .failed, unretained accepted connections, and pinning a certificate rather than a public key (routine renewal bricks every installed copy). 12 anti-patterns, 14-item checklist.
-
-
MCP resources --
ios://project/info,ios://project/dependencies, andios://project/issues. Tools are verbs the model chooses to call; resources are nouns a client can read without being asked, so a project's shape can be attached to context up front rather than after the model thinks to run an analysis.Resources are addressed by a fixed URI with no arguments, so
ios://project/...only means something if the server knows which project it is. The root comes from--project PATH, thenIOS_AGENT_PROJECT, then the working directory the client spawned the server in -- which matches how MCP servers are actually deployed, since.cursor/mcp.jsonlives in the repository andclaude mcp addis run from it. Every payload reports the root it used, because a reader who cannot see which source won has no way to tell an empty project from a wrong path.There is deliberately no
ios://project/build-status, which was on the request list. It would have to runxcodebuild-- that needs macOS and Xcode, and it breaks thefilesystem: read, network: nonecontract that lets this package install anywhere in ~26 KB. Build and simulator state belong in the separate package that already requires a full toolchain.7 new tests (116 -> 123), including a second server instance launched with
--project-- the root is a launch-time decision, so the shared test client cannot exercise it. -
Four new review tools and structured output -- 7 tools -> 11, 72 tests -> 116.
review_swift_memory-- retain cycles and lifetime. Deliberately narrow: a closure capturingselfis not a leak, since most closures are consumed immediately. A leak needs the closure to be stored by something the object owns, so the rules fire on the storing APIs (repeatingTimer, block-basedNotificationCenterobservers, Combinesink, stored closure properties, non-weakdelegates) rather than onself.anywhere, which would bury the real findings.unowned-selfis included as a crash, not a leak -- unlikeweakit does not nil out, so an escaping closure running after deallocation traps.review_swift_security-- hardcoded secrets, credentials inUserDefaults, disabled ATS, cleartext HTTP, TLS trust accepted without evaluation, MD5/SHA-1, over-permissive Keychain accessibility, non-cryptographic randomness for nonces, secrets in logs, and interpolation into evaluated JavaScript.hardcoded-secretskips the placeholders people legitimately commit (YOUR_API_KEY,<your-key>,changeme) -- flagging those trains readers to ignore the rule, which is worse than not having it.review_swift_testing-- the inverse of every other analyzer: it runs only on test files. A flaky or vacuous test is worse than a missing one, because it costs the same to run and reports success either way. Catches tests that wait by sleeping, tests with no assertion at all, liveURLSessionin tests, order-dependent static state, andawaitinside anXCTAssertautoclosure (which does not compile).review_swift_performance-- work on the render path. Rules that only matter insidevar body: some Vieware scoped to that block and silent elsewhere, so the sameDateFormatter()is serious inbodyand minor outside it.
Structured output via the MCP
outputSchema/structuredContentcontract rather than JSON stuffed into a text block: every review tool now returns markdown and typed data withsummary,score,counts,files_checked,issues, andsuggestions.suggestionsdeduplicates by rule -- forty literal-spacing findings produce one instruction, not forty.scoreis a published formula (100 x (1 - penalty/capacity),penalty = 10*blockers + 3*serious + 1*minor) so it is reproducible rather than a vibe, and it is documented as not comparable between projects.analyze_swift_projectgained project shape: UI framework, inferred architecture, dependencies, and DI detection. The architecture read always ships its evidence, and saysnot determinedrather than guessing when signals are weak. DI is detected fromany Protocolinitializer parameters, not from a directory name.
-
A test fixture with unstable identity. Six
PersistenceTestsfailed in CI reportingnotFoundagainst rows that were demonstrably in the store.Article.sampleswas a computedstatic var, so it rebuilt the array on every access -- andArticle.initdefaultsidtoUUID(). Reading it twice produced two disjoint sets of identifiers, so a test that importedArticle.samplesand then looked upArticle.samples.firstwas searching for a row that had never been written. It is now astatic letwith fixed identifiers derived fromUUID(uuid:), which is total and needs no force-unwrap.Worth recording honestly: the first fix went after the wrong cause. From the truncated CI log the failures looked like SwiftData failing to translate a
UUIDin#Predicate, and the identity was changed to aStringon that theory. It did not help, because the predicate was never the problem. That change has been reverted rather than kept as a harmless-looking extra -- it carried a doc comment asserting a SwiftData limitation there is no evidence for, and folklore in a teaching repository is worse than no comment at all.The log was truncated because the workflow pipes
swift testthroughtail -60; with 49 tests the failing assertions scrolled off and the job reported "6 failures" while showing only passing tests. Aif: failure()step now prints the failing assertions and test names, and it identified the real cause on its first run. -
stripCommenttruncated every line at the first//, including inside string literals.let base = "http://api.example.com"becamelet base = "http:, so every analyzer was blind to the rest of any line containing a URL -- and the newcleartext-httprule could therefore never fire. Now string-literal aware, with escape handling. Found because a new test failed for a reason that made no sense. -
await-inside-xctassertfired onXCTAssertTrue(x, "must be set before the await, not after it")-- the word was in the assertion message, not the expression. Found by dogfooding the new analyzers against this repository's own SwiftData tests, where it reported a blocker in correct code. Fixed with awithoutStringLiteralshelper; both directions are regression-tested. -
.claude/agents/swift-reviewer.mdandperformance-reviewer.mdwere enforced read-only by their tool grants but never said so in their descriptions. Since the main agent decides delegation from the description alone, the guarantee that made them safe to delegate verification to was invisible at the point of the decision. Both now state it. -
.claude/agents/swiftui-modernization.mdwas the only one of the ten subagents whose description stated what the agent is without stating when to use it. Delegation is decided by matching a task against that text, so the agent was measurably less likely to be selected than its nine peers. Found by running the newlint_skillagainst this repository.
-
ios-agent-mcpreported version1.0.0from--versionand, more consequentially, from the MCPinitializehandshake -- so every connected client saw a version that did not match the package it had just installed. The version was a hardcoded constant inmcp-server/src/index.tsand nothing readpackage.jsonat runtime, so publishing under a new version could not correct it on its own. The constant,mcp-server/package.json,skill.json, and theSKILL.mdfrontmatter are now aligned on a single version.Superseded by the generator described above. Aligning the four files by hand fixed the symptom; it did not stop the next hand-edit from re-introducing it, which is what
mcp-server/scripts/sync-version.mjsdoes by generating three of the four frompackage.json.
Turns the repository from something you read into something you install. The skill teaches an agent how to write iOS code; the MCP server lets it check code that already exists.
mcp-server/--ios-agent-mcp, a TypeScript MCP server (stdio transport) exposing six iOS-specific tools:analyze_swift_project-- structure (file/line counts, deployment target, Swift tools version, frameworks, test presence) plus a finding count per category with the tool that explains each.review_swift_concurrency--@Observablewithout@MainActor,Task.detached,DispatchQueue.main.async,await MainActor.runinside an isolated type,@unchecked Sendable,nonisolated(unsafe), unstructuredTaskinonAppear, emptycatch, non-final observable classes, and types namedTaskthat shadow_Concurrency.Task.review_swift_architecture-- initializers defaulting to live implementations, presentation code namingURLSession/APIClient/ModelContext, singletons resolved inside view models, the domain layer importing SwiftUI, nestedNavigationStacks, andNavigationView.review_swiftui-- fixed font sizes and heights,AnyView, deprecated.cornerRadius, literal spacing instead of tokens, materials over solid backgrounds, transient view state on models,ObservableObject,@EnvironmentObject, andtry!.check_availability_guards-- missing guards and over-restrictive ones: an iOS 26 API guarded at#available(iOS 27, *)compiles, ships, and silently drops every iOS 26 device to the fallback. Also flags Foundation Models used without a runtimeSystemLanguageModel.availabilitycheck.audit_app_store_readiness-- permission-gated frameworks with no Info.plist purpose string, a missingPrivacyInfo.xcprivacy(apps only -- libraries are never submitted), unlocalized user-facing strings, unlabeled icon-only buttons, andprint()used for diagnostics.
- Every finding carries a file, a line, the severity, the consequence, the specific fix, and a link into this repo's docs. Analyzers are pure
(path, content) -> Finding[]functions, so they are unit-testable without the MCP transport. - 38 tests -- unit coverage for every analyzer plus
test/server.smoke.test.js, which launches the real server and speaks the real MCP protocol over stdio. Unit tests cannot tell you whether the server actually starts; that one can. docs/mcp/--installation.md(Claude Code, Claude Desktop, Cursor, from source, troubleshooting, privacy),tools.md(every rule with its severity, and the limits of static analysis),examples.md(worked sessions, including how the tools pair with the subagents).mcp-server/mcp.json-- manifest declaring runtime, transport, filesystem-read-only permissions, and the tool list.- CI gained an
mcp-serverjob: install, typecheck, build, test, validate the manifest, and verify the manifest's tool list matches the tools actually registered insrc/index.ts-- a tool advertised but not registered would be a broken promise to any client.
- Dogfooding the server against this repo's own
samples/SkillPatternssurfaced two false positives in the analyzers, both fixed with regression tests:empty-catchmatchedcatch { }inside a doc comment because that one check used a raw regex instead of the comment-stripping line walker every other rule uses; andmissing-privacy-manifestfired on an SPM library, which has no Info.plist and is never submitted to App Review. Package.swiftis no longer analyzed as application source -- it is build configuration, and including it inflated file counts and produced findings against code that is not part of the app.- A
linefield in the availability analyzer held the matched source string rather than the line number; it type-checked only because of an unsafeas unknown ascast. Fixed, cast removed, regression test added.
Shifts focus from adding documentation to making the repository verifiable and easy to adopt. The headline change is that the skill's core patterns are now compile-checked in CI rather than asserted in prose -- the standing caveat from every previous release.
samples/SkillPatterns/-- an SPM package implementing the skill's core patterns as real, buildable Swift:@MainActor @Observableview models, inbound/outbound boundary protocols, a protocol composition root, typedHashable/Codableroutes, pure deep-link parsing, and actor test doubles. Strict concurrency is enabled, so an isolation regression fails the build. Several tests exist specifically to fail when a rule is broken -- a stale-index revert after anawait, aCancellationErrorsurfaced as a user-facing error, a launch-time deep link dropped instead of queued. Scoped deliberately to stable APIs (iOS 17 / macOS 14, no SwiftUI view code) so it builds on standard runners withswift build.docs/compatibility-matrix.md-- the canonical version reference. Separates the three things that get conflated (toolchain version, SDK version, deployment target), lists per-feature availability floors for iOS 17/18/26/27 and Swift 5.9-6.4, framework minimums, and the rule that a compile-time@availableguard does not replace a runtime availability check.docs/migration/swift-6-migration.md-- Swift 5.9 -> 6 -> 6.4, organized around the compiler errors you actually hit (main-actor isolation,Sendableconformance,@Sendablecapture, non-concurrency-safe statics, non-Sendable across actor boundaries, delegate callbacks) with the fix for each, a recommended migration order, and what not to do.docs/migration/ios-deployment-migration.md-- separates rebuilding against a newer SDK from raising a deployment target, since they are independent decisions. Covers the iOS 26 Liquid Glass adoption and the iOS 27 app resizability opt-in that happens without a code change.docs/migration/xcode-migration.md-- Xcode 15 -> 16 -> 27, explicitly built modules, the stricter Previews engine, and an ordered procedure for diagnosing a post-upgrade failure.
- CI (
docs-consistency.yml) extended repo-wide: relative markdown links, backtick path references, code-fence languages and closure, and frontmatter consistency -- previously onlySKILL.mdandREADME.mdwere checked. All four checks are fence-aware and skip placeholder paths, so Swift like[UInt8](data)and template ellipses do not produce false positives. - CI gained a
sample-packagejob onmacos-latestthat runsswift buildandswift testagainstsamples/SkillPatterns. SKILL.md: routing-table entries for the compatibility matrix and the three migration guides; the toolchain section now points at the matrix as canonical rather than restating floors; new Versions & Migration and Samples & Templates index sections. Version 1.4.0.- README: What's New in 1.4, a Versions & Migration documentation index, a Compile-Checked Sample section, and a link from Supported Platforms to the canonical matrix.
Content verified against Apple's current developer documentation (Xcode 27 beta, Swift 6.4, WWDC26 sessions) rather than written from model memory -- the API surface below post-dates the authoring model's training data.
docs/frameworks/foundation-models.md-- the framework reference:LanguageModelSessionlifecycle,@Generable/@Guidestructured output,PartiallyGeneratedstreaming, theToolprotocol, built-in Vision-backed system tools, model selection (SystemLanguageModelvs.PrivateCloudComputeLanguageModel), the openLanguageModel/LanguageModelExecutorprovider protocols, Dynamic Profiles with baton-pass and phone-a-friend orchestration, multimodalAttachmentprompts, context/token/usage APIs, concurrency rules, error handling, the two-layer availability model, and testing that asserts shape rather than exact output.docs/frameworks/apple-intelligence.md-- which framework to reach for (App Intents vs. Foundation Models is the most common mistake), the privacy model for on-device / Private Cloud Compute / third-party models and what may honestly be claimed in UI, App Intents, Image Playground, Visual Intelligence, and designing features that degrade when no model is available.docs/tooling/xcode-27-agents.md-- Xcode coding agents, routing between an in-Xcode agent and Claude Code, agent-assisted localization (and what still needs a human: plurals, RTL, truncation), agent-assisted testing, the Swift Concurrency instrument for actor contention, and keeping non-Claude agents bound by this skill's rules via hooks.docs/tooling/device-hub.md-- Device Hub, the device/configuration test matrix, iOS 27 app resizability (rebuilding against the SDK auto-opts you in), accessibility passes, and reproducing device-specific bugs on their exact configuration.- Four new subagents --
foundation-models(availability gating and graceful degradation),swiftui-modernization(behavior-preserving legacy migration with an ordered migration table),accessibility-reviewer(read-only VoiceOver/Dynamic Type/contrast audit with greps),performance-reviewer(measures before recommending; never optimizes on suspicion). Ten subagents total.
SKILL.mdfrontmatter: addedswift-version: 6.4,xcode-version: 27,ios-sdk-version: 27, and asupportslist (Foundation Models, Apple Intelligence, Private Cloud Compute, Xcode Coding Agents, Device Hub, Liquid Glass, SwiftData, Swift 6 strict concurrency). Deployment floor staysminimum-ios: 17.0/minimum-swift: 5.9-- the toolchain version and the deployment floor are different things and are now named separately. Version 1.3.0.SKILL.md: new Target Platforms and Toolchain section with a per-feature version-floor table, and the rule that availability guards use the version where a symbol was introduced, not the newest SDK.SKILL.md: new Xcode 27 agent integration subsection under How You Operate -- routing between Xcode agents and Claude Code, and the three rules that hold regardless of which agent wrote the code. This propagates toAGENTS.mdand the other 23 mirrors, which is the only way to add a section toAGENTS.md(it is generated).docs/swift/swift-concurrency.md: Swift 6.4 ergonomics (weak let,~Sendable, unhandled-task-error warnings, async indefer,@diagnosefor ratcheting strictness), a 16-point Actor Isolation Review Checklist, and a Foundation Models thread-safety section (single-flight sessions, off-main-actor tools, cancellation in streaming loops).docs/swiftui/state-and-data-flow.md: states explicitly that Observation is the default for new code and whyObservableObjectis legacy.docs/design/design-tokens.md: Liquid Glass noted as refined in iOS 27, with the availability guard deliberately kept at iOS 26 -- bumping it to 27 would drop every iOS 26 device to the fallback for no reason.docs/orchestration/router.md: routes for Foundation Models, accessibility, performance, and modernization work..claude/agents/ios-plan.md: a Version compatibility section -- Swift 6.4 / Xcode 27 / iOS 27 SDK baseline, per-symbol availability floors, and the rule that raising a deployment target is a product decision..claude/agents/swift-reviewer.md: review checks for Swift 6.4 isolation (discarded task errors,@unchecked Sendablethatweak let/~Sendablewould solve, unexplained@diagnose(ignore,)), availability correctness, and Foundation Models / Apple Intelligence usage including privacy-claim accuracy.- README: a Supported Platforms section and a What's New in 1.3 section; tooling and AI documentation index entries; the subagent table extended to ten.
The repository taught the main agent what to write but never how to operate. This release adds the orchestration layer: how to split work, verify it, and scale it out. Because AGENTS.md and the other 23 rule files are generated from SKILL.md, the operating model lands in all of them.
docs/orchestration/-- six documents forming the agent-ops layer:router.md-- the entry point. One table deciding inline vs. delegate vs. loop vs./batchvs. dynamic workflow, plus standard sequences (feature, bug, drive-a-red-suite-green, codebase-wide change) and what the main agent stays accountable for after delegating.subagents.md-- why delegate (context preservation, independent verification, parallelism), frontmatter reference, why thedescriptionis the routing interface, tool restriction as a correctness feature, writing delegation prompts for a cold agent, and the distinction between subagents (hub-and-spoke, report only to the main agent) and agent teams (peer-to-peer, experimental, disabled by default).looping.md-- turn-based, goal-based, time-based, and proactive patterns; the GOAL / CHECK / MAX / ON-STALL contract every loop declares up front; stall detection (identical failure, oscillation, growing blast radius); and the rule against reaching a stop condition by weakening the check.verification.md-- the evidence contract. Every claim labelled VERIFIED, INSPECTED, or UNVERIFIED; what counts as evidence and what does not; iOS-specific verification commands; separation of duties so the author never grades the work; and the three enforcement layers ordered cheapest-first.dynamic-workflows.md-- the scale ladder, when/batchfits (5-30 isolated PRs, worktree per unit) and when it does not (shared files, ordering constraints, exploratory work), script-driven orchestration, failure policies, and cold-start cost control.hooks.md-- hook vs. CI vs. reviewer subagent, lifecycle events, the exit-code contract, and design rules including graceful degradation.
.claude/agents/-- six subagent definitions with restricted tool sets and explicit return formats:ios-explore(read-only, parallel-safe search),ios-plan(read-only planner),swift-reviewer(read + Bash, deliberately no write tools),swift-debugger(reproduce -> isolate -> fix -> prove, with a Swift failure-pattern table),swift-refactorer(behavior-preserving, requires a green baseline), andios-docs(enforces the doc structure and mirror sync). Names are prefixedios-/swift-so they cannot shadow Claude Code's built-in subagents.- Hooks in this repository --
.claude/settings.jsonnow wires three hooks implemented inscripts/hooks/:guard-generated-files.sh(PreToolUse -- denies edits to the 24 generated mirrors and points atSKILL.md),sync-mirrors-on-edit.sh(PostToolUse -- regenerates mirrors wheneverSKILL.mdchanges), andverify-repo.sh(Stop -- runs the CI checks before a turn can end). templates/hooks/-- drop-in hooks for real iOS projects:swift-format.sh(SwiftFormat + SwiftLint autocorrect),forbid-antipatterns.sh(blocks theSKILL.mdanti-patterns at write time with line numbers and the fix; exempts test/mock/preview files from app-code-only rules),build-check.sh(Stop-time build and test verification that reports UNVERIFIED rather than implying a build it could not run), plussettings.json.exampleand installation notes.SKILL.md-- "How You Operate" section -- the verification evidence rule, when to delegate and when not to, the subagent roster, the loop contract, the scale-up table, and the instruction to let hooks decide what hooks can decide. This propagates toAGENTS.md,CLAUDE.md,.cursorrules, and the other 21 mirrors.- CI (
docs-consistency.yml) and the Stop hook now also validate subagent frontmatter (name,description, kebab-case naming).
SKILL.mdversion bumped to 1.2.0;skill.jsonfollows.SKILL.mddocument-routing table extended with the six orchestration triggers.- README gained a "What's New in 1.2" section and an Agent Operations documentation index covering the orchestration docs, the six subagents, and the three hook templates.
docs/design/design-tokens.md-- three-tier token architecture (primitive -> semantic -> component), swappable themes via@Environment, dark-mode elevation rules, a Dynamic Type compliance checklist, materials vs. Liquid Glass (glassEffect,GlassEffectContainer) with an availability fallback, and a WCAG contrast helper you can assert in tests.docs/swiftui/deep-linking-and-routing.md-- typedRouteenums, a@MainActorRouter, deep-link parsing split into a pure testable parser plus an applier, universal links viaonContinueUserActivity, queuing links that arrive before the app is ready,NavigationPathstate restoration through@SceneStorage, and per-tab stacks.docs/frameworks/data-concurrency.md-- the "pass the identifier, not the object" rule for SwiftData and Core Data,@ModelActorbackground importers with batched saves,performBackgroundTask, batch delete/update with change merging, and in-memory stores for tests.docs/testing/mocking-strategy.md-- the three-tier strategy: Tier 1 test doubles (stub/fake/spy/mock, and why a reconfigured double must be a reference type), Tier 2 rich#if DEBUGmocks with one preview per screen state, Tier 3AppConfigurationlaunch flags and a QA debug menu, each with a release branch that ignores it.scripts/sync-mirrors.sh-- regenerates all 24 agent rule files fromSKILL.md, with a--checkmode..github/workflows/docs-consistency.yml-- CI enforcing mirror sync,SKILL.mdfrontmatter validity, existence of every referenced documentation path, and absence of placeholder stubs in Swift templates.
SKILL.mdfrontmatter expanded to the full Agent Skills schema (name,description,version,license,allowed-tools,metadata), with a trigger-rich description so agents load it on the right tasks.SKILL.mdgained a When to Load This Skill section (including when not to load it and a trigger-to-document routing table) and a How These Docs Are Structured section defining the Context -> Pattern -> Anti-Patterns convention plus six non-negotiable code rules.patterns/clean-architecture.mdnow declares explicit inbound (use-case) and outbound (repository) boundary protocols. The presentation layer depends onany …UseCaseProtocolexistentials only, previews run with no network, theDependencyContainer.sharedsingleton is replaced by anAppDependenciesprotocol injected through@Environment, and an IoC review checklist with verification greps was added.patterns/mvvm.mdrewritten around@MainActor @Observable final classview models: isolation rationale, re-entrancy and stale-index guidance,TaskvsTask.detached, child-view observation traps, and reference-type test doubles.docs/swiftui/state-and-data-flow.mdgained sections on@Observableisolation, five child-view observation traps, async boundaries in views, a property-wrapper decision checklist, and an anti-pattern summary.docs/swift/swift-concurrency.mdgained an "Isolation in SwiftUI Code" section covering escaping the main actor, four isolation-leak shapes,MainActor.assumeIsolated,nonisolated(unsafe), re-entrancy, and Sendable across the SwiftUI boundary.CONTRIBUTING.mdmirror-sync instructions now point at the script instead of a hand-rolledcploop that would have copied the YAML frontmatter into all 24 mirrors.
patterns/clean-architecture.md:catch { self.error = nil }silently discarded every non-DomainErrorfailure.patterns/mvvm.md: the sample model was namedTask, shadowing_Concurrency.Taskso thatTask { … }in the same file did not compile; renamed toTodoItem.patterns/mvvm.md: thetoggleCompletionrevert test mutated a struct double after injecting it, so the view model never sawshouldFailand the test asserted nothing.patterns/mvvm.md: optimistic updates wrote back through an array index captured before anawait.- Removed
.github/workflows/ruby.yml, a leftover GitHub starter workflow that ranbundle exec rakeagainst a repository with no Ruby in it.
LICENSE(MIT) -- previously referenced in README but absent from the repo.docs/frameworks/arkit.md-- complete ARKit guide covering world/face/body/image/object/geo tracking, plane and mesh detection, RealityKit integration, world map persistence, and lifecycle handling.docs/frameworks/realitykit.md-- complete RealityKit guide covering ECS,RealityView,ARView, PBR materials, animation, physics, gestures, audio, and platform differences across iOS/macOS/visionOS.CONTRIBUTING.md-- contributor workflow, house style, and instructions for keeping the 25+ rule files in sync.CODE_OF_CONDUCT.md-- Contributor Covenant 2.1..github/ISSUE_TEMPLATE/bug_report.mdandfeature_request.md..github/PULL_REQUEST_TEMPLATE.md.templates/ios-app/Tests/AppTests.swift-- Swift Testing example (modern, iOS 18+).templates/ios-app/Tests/AppXCTests.swift-- XCTest example (works back to iOS 13).- README references for the new framework docs and tests template.
1.0.0 -- 2026-04
- Xcode project-first workflow in README and
SKILL.md-- generators produce.swiftfiles, not.xcodeproj. - Color contrast and readability rules promoted to non-negotiable status in the agent brain.
- AI setup guide (
docs/ai-setup-guide.md) covering 28 AI tools across macOS and Windows. - Universal AI-agent compatibility --
.cursorrules,.clinerules,.continuerules,.kilocoderules,.roorules,.rules,.windsurfrules, plus rule files under.aiassistant/,.amazonq/,.augment/,.continue/,.cursor/,.junie/,.kilocode/,.roo/,.tabnine/,.trae/,.windsurf/, and.github/copilot-instructions.md. - Codex compatibility:
SKILL.md,skill.json,install.sh. - iOS 18 animations, UIKit animation system, third-party Lottie/Rive integration guides.
- 26 new framework docs: AI/ML (
coreml,vision,natural-language,speech,on-device-ai), hardware (core-bluetooth,core-motion,core-nfc,healthkit,homekit), services (contacts,eventkit,passkit,weatherkit), security (cryptokit,device-integrity), and the TCA architecture pattern. - Ultimate font catalog (
docs/design/fonts-catalog.md) -- every iOS font, 100+ Google Fonts, 15 pairings, variable fonts, international families. - Interaction standards, button styles,
ViewStatepattern, and full coverage across the four checklists (App Store submission, performance, security, testing). - Complete UI design system -- color palettes, typography, stunning UI patterns.
- Initial commit: agent brain, MVVM/Clean Architecture/Coordinator/Repository/Error Handling patterns, iOS-app and multiplatform-app templates, GitHub Actions + Fastfile CI/CD templates.