@mmerterden/multi-agent-pipeline 20.8.0 → 20.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/docs/facts.json +3 -3
  3. package/index.js +1 -0
  4. package/install/_codex-agents.mjs +1 -1
  5. package/install/_common.mjs +486 -53
  6. package/install/_mcp-register.mjs +173 -117
  7. package/install/claude.mjs +281 -220
  8. package/install/codex.mjs +7 -7
  9. package/install/copilot.mjs +13 -11
  10. package/install/index.mjs +92 -27
  11. package/install/templates/claude-hooks.json +9 -9
  12. package/manifest.json +112 -114
  13. package/package.json +5 -2
  14. package/pipeline/commands/multi-agent/update/SKILL.md +28 -17
  15. package/pipeline/lib/confusables.json +79 -33
  16. package/pipeline/lib/extract-conventions.sh +3 -3
  17. package/pipeline/lib/json-file-lock.mjs +27 -7
  18. package/pipeline/lib/normalize-text.mjs +86 -17
  19. package/pipeline/lib/outbound-gate.mjs +13 -4
  20. package/pipeline/lib/redact.mjs +87 -13
  21. package/pipeline/multi-agent-refs/analysis/evidence.md +1 -1
  22. package/pipeline/multi-agent-refs/analysis/synthesis.md +1 -1
  23. package/pipeline/multi-agent-refs/component-dispatch.md +1 -1
  24. package/pipeline/multi-agent-refs/conventions-defaults.md +1 -1
  25. package/pipeline/multi-agent-refs/features/unattended-security.md +2 -2
  26. package/pipeline/scripts/agent-guard.py +150 -25
  27. package/pipeline/scripts/audit-log.sh +3 -4
  28. package/pipeline/scripts/autopilot-runner.mjs +14 -5
  29. package/pipeline/scripts/doctor.mjs +8 -2
  30. package/pipeline/scripts/gen-skills-index.mjs +13 -1
  31. package/pipeline/scripts/log-metric.sh +9 -3
  32. package/pipeline/scripts/migrate-prefs.mjs +18 -4
  33. package/pipeline/scripts/pre-commit-check.sh +119 -27
  34. package/pipeline/scripts/scan-agent-config.sh +9 -9
  35. package/pipeline/scripts/unattended_policy.py +12 -3
  36. package/pipeline/scripts/uninstall.mjs +88 -1
  37. package/pipeline/scripts/usage-identity.mjs +1 -1
  38. package/pipeline/scripts/usage-register.mjs +1 -1
  39. package/pipeline/skills/.skill-manifest.json +30 -30
  40. package/pipeline/skills/shared/README.md +64 -64
  41. package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -2
  42. package/pipeline/skills/shared/external/alarmkit/evals/evals.json +2 -2
  43. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +6 -0
  44. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +3 -0
  45. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +3 -0
  46. package/pipeline/skills/shared/external/app-store-review/SKILL.md +3 -4
  47. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +5 -3
  48. package/pipeline/skills/shared/external/authentication/SKILL.md +29 -17
  49. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +3 -1
  50. package/pipeline/skills/shared/external/background-processing/SKILL.md +10 -8
  51. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +7 -7
  52. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +6 -3
  53. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +43 -0
  54. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +4 -2
  55. package/pipeline/skills/shared/external/core-data/SKILL.md +12 -2
  56. package/pipeline/skills/shared/external/core-nfc/SKILL.md +31 -0
  57. package/pipeline/skills/shared/external/coreml/SKILL.md +1 -1
  58. package/pipeline/skills/shared/external/cryptokit/SKILL.md +1 -1
  59. package/pipeline/skills/shared/external/device-integrity/SKILL.md +11 -5
  60. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
  61. package/pipeline/skills/shared/external/ios-module-structure/modules/_TEMPLATE.yml +1 -1
  62. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +6 -6
  63. package/pipeline/skills/shared/external/localization-reuse-map/example-mapping.json +3 -2
  64. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +20 -18
  65. package/pipeline/skills/shared/external/localization-reuse-map/reference/publish-and-snapshot.md +4 -4
  66. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -9
  67. package/pipeline/skills/shared/external/localization-reuse-map/scripts/_shared.py +201 -0
  68. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-artifact.py +64 -30
  69. package/pipeline/skills/shared/external/localization-reuse-map/scripts/build-spreadsheet.py +14 -16
  70. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-annotations.py +20 -12
  71. package/pipeline/skills/shared/external/localization-reuse-map/scripts/fetch-legacy-labels.py +7 -7
  72. package/pipeline/skills/shared/external/localization-reuse-map/scripts/publish-confluence.py +26 -12
  73. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-key-shots.py +4 -3
  74. package/pipeline/skills/shared/external/localization-reuse-map/scripts/render-overlay.py +51 -24
  75. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-legacy-values.py +19 -9
  76. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +26 -19
  77. package/pipeline/skills/shared/external/localization-reuse-map/scripts/scan-screen-keys.py +39 -46
  78. package/pipeline/skills/shared/external/localization-reuse-map/scripts/verify-map.py +37 -63
  79. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +4 -2
  80. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +5 -4
  81. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +3 -2
  82. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -2
  83. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
  84. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +4 -4
  85. package/pipeline/skills/shared/external/permissionkit/SKILL.md +15 -6
  86. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +2 -1
  87. package/pipeline/skills/shared/external/push-notifications/SKILL.md +8 -4
  88. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +1 -1
  89. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +25 -6
  90. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
  91. package/pipeline/skills/shared/external/skill-creator/template.md +7 -1
  92. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +6 -1
  93. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -2
  94. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +1 -1
  95. package/pipeline/skills/shared/external/swift-security/SKILL.md +10 -8
  96. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +8 -5
  97. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +13 -9
  98. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +7 -6
  99. package/pipeline/skills/shared/external/swift-testing/SKILL.md +8 -5
  100. package/pipeline/skills/shared/external/swift-testing/evals/evals.json +1 -1
  101. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +3 -2
  102. package/pipeline/skills/shared/external/swiftdata/SKILL.md +5 -3
  103. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +17 -9
  104. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +4 -2
  105. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +13 -6
  106. package/pipeline/skills/shared/external/vision-framework/SKILL.md +3 -1
  107. package/pipeline/skills/shared/external/weatherkit/SKILL.md +8 -5
  108. package/pipeline/skills/shared/external/widgetkit/SKILL.md +15 -7
  109. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +8 -6
  110. package/pipeline/scripts/gen-ref-toc.mjs +0 -279
  111. package/pipeline/scripts/make-manifest.mjs +0 -199
  112. package/pipeline/scripts/scorecard-snapshot.mjs +0 -178
@@ -18,7 +18,7 @@ and Best Practices".
18
18
  - [Reading](#reading)
19
19
  - [One Place That Builds Queries](#one-place-that-builds-queries)
20
20
  - [OSStatus Codes](#osstatus-codes)
21
- - [An Actor Around the Keychain (iOS 17+ / macOS 14+)](#an-actor-around-the-keychain-ios-17--macos-14)
21
+ - [An Actor Around the Keychain (iOS 13+ / macOS 10.15+)](#an-actor-around-the-keychain-ios-13--macos-1015)
22
22
  - [Performance](#performance)
23
23
  - [macOS: Two Keychains (TN3137)](#macos-two-keychains-tn3137)
24
24
  - [Accessibility at a Glance](#accessibility-at-a-glance)
@@ -85,8 +85,11 @@ Internet passwords are identified by account, server, protocol, authentication
85
85
  type, port, path, security domain, access group and synchronizable. See
86
86
  [keychain-item-classes.md](keychain-item-classes.md) for every class.
87
87
 
88
- `SecItemUpdate` cannot change `kSecAttrAccount` or `kSecClass`. To rename an
89
- account or change class, delete the old item and add a new one.
88
+ `SecItemUpdate` cannot change `kSecClass`: the class belongs to the query,
89
+ and the attributes dictionary accepts only real keychain attributes. To change
90
+ class, delete the old item and add a new one. A primary-key attribute such as
91
+ `kSecAttrAccount` can be updated; the update fails with `errSecDuplicateItem`
92
+ only when another item already has the resulting primary key.
90
93
 
91
94
  ## Saving: Add, Then Update on Duplicate
92
95
 
@@ -257,7 +260,7 @@ its merits:
257
260
  | -128 | `errSecUserCanceled` | The user dismissed the prompt; tell the UI. |
258
261
  | -25293 | `errSecAuthFailed` | Authentication failed or the protected item is no longer usable. |
259
262
  | -50 | `errSecParam` | A programming error in the dictionary; fix the query. |
260
- | -25244 | `errSecNoSuchAttr` | The attribute is not supported (seen on the data protection keychain). |
263
+ | -25303 | `errSecNoSuchAttr` | The attribute is not supported (seen on the data protection keychain). |
261
264
 
262
265
  ```swift
263
266
  struct KeychainError: Error, CustomStringConvertible {
@@ -272,7 +275,7 @@ struct KeychainError: Error, CustomStringConvertible {
272
275
  Logs may record the shape of a query (class, service, which flags were set)
273
276
  and the status code. They never record `kSecValueData`, tokens or key bytes.
274
277
 
275
- ## An Actor Around the Keychain (iOS 17+ / macOS 14+)
278
+ ## An Actor Around the Keychain (iOS 13+ / macOS 10.15+)
276
279
 
277
280
  Reads of biometric-protected items wait for the user and can take several
278
281
  seconds (WWDC 2014 Session 711). Doing that on the main actor freezes the UI.
@@ -381,7 +384,8 @@ Why an actor rather than a queue:
381
384
  | `Sendable` | Enforced | Annotate `@Sendable` by hand |
382
385
  | Swift 6 | Native | Needs care to pass strict checking |
383
386
 
384
- For iOS 13 through 16, a private serial queue does the same job:
387
+ In completion-handler code that has not moved to Swift concurrency, a private
388
+ serial queue does the same job:
385
389
 
386
390
  ```swift
387
391
  final class LegacySecretStore {
@@ -450,7 +454,7 @@ func macSafe(_ query: [CFString: Any]) -> [CFString: Any] {
450
454
 
451
455
  When a macOS keychain bug appears, first confirm which backend the call went
452
456
  to. An attribute the file-based keychain quietly drops will produce
453
- `errSecNoSuchAttr` (-25244) on the data protection keychain.
457
+ `errSecNoSuchAttr` (-25303) on the data protection keychain.
454
458
 
455
459
  ## Accessibility at a Glance
456
460
 
@@ -477,8 +481,8 @@ is a safe default for foreground use. The actor above defaults to
477
481
  2. Saves fall back to `SecItemUpdate` on -25299.
478
482
  3. Every `SecItemCopyMatching` sets at least one `kSecReturn*` key.
479
483
  4. The cast of the `CFTypeRef` result matches the return keys and match limit.
480
- 5. No SecItem call runs on `@MainActor` (actor on iOS 17+, serial queue on
481
- iOS 13 to 16).
484
+ 5. No SecItem call runs on `@MainActor` (an actor, available from iOS 13, or
485
+ a private serial queue in completion-handler code).
482
486
  6. Add, query and update dictionaries are built fresh for each call.
483
487
  7. Keys are `kSec*` constants, not string literals.
484
488
  8. Queries are specific: service and account for generic passwords,
@@ -321,12 +321,13 @@ func pqRoundTrip() throws -> Bool {
321
321
  ```
322
322
 
323
323
  Transport needs no work: in iOS 26, `URLSession` and `Network.framework`
324
- negotiate quantum-secure TLS 1.3 with X-Wing (ML-KEM768 combined with X25519)
325
- on their own, and CloudKit, push notifications and iCloud Private Relay already
326
- use it. For an app's own end-to-end encryption, prefer a hybrid of
327
- post-quantum and classical algorithms, such as the `XWingMLKEM768X25519` KEM in
328
- HPKE; an `SecureEnclave.MLKEM768.PrivateKey` performs encapsulation and
329
- decapsulation inside the hardware. The full CryptoKit catalog is in
324
+ negotiate quantum-secure TLS 1.3 on their own, offering the hybrid
325
+ `X25519MLKEM768` key-exchange group, and CloudKit, push notifications and
326
+ iCloud Private Relay already use it. For an app's own end-to-end encryption,
327
+ prefer a hybrid of post-quantum and classical algorithms, such as the CryptoKit
328
+ X-Wing KEM (`XWingMLKEM768X25519`) in HPKE; an
329
+ `SecureEnclave.MLKEM768.PrivateKey` performs encapsulation and decapsulation
330
+ inside the hardware. The full CryptoKit catalog is in
330
331
  [cryptokit-public-key.md](cryptokit-public-key.md).
331
332
 
332
333
  | Release | Enclave-related change |
@@ -218,7 +218,7 @@ API you correct.
218
218
  | API | Needs |
219
219
  |-----|-------|
220
220
  | Exit tests (`processExitsWith:`) | Swift 6.2, Xcode 26.0; not iOS, tvOS, watchOS |
221
- | Capture lists in exit tests | Swift 6.3 compiler; captured values must be `Sendable` and `Codable` |
221
+ | Capture lists in exit tests | Compiles with the Swift 6.3.1 compiler (Xcode 26.4); earlier compilers not checked. Captured values must be `Sendable` and `Codable` |
222
222
  | `Test.cancel(_:)`, `Issue.record(_:severity:)`, image attachments | Swift 6.3, Xcode 26.4 |
223
223
 
224
224
  ```swift
@@ -230,9 +230,12 @@ API you correct.
230
230
  }
231
231
  ```
232
232
 
233
- Give each captured value an explicit type (`[name = name as Type]`). A bare
234
- `[status]` fails to compile with "Type of captured value is ambiguous",
235
- because the macro cannot infer the type on its own.
233
+ Give each captured value an explicit type (`[name = name as Type]`). With the
234
+ Swift 6.3.1 compiler a bare `[status]` fails with "Type of captured value
235
+ 'status' is ambiguous", a captured value that is not `Codable` fails with "must
236
+ conform to 'Sendable' and 'Codable'", and a closure that reads `status` with no
237
+ capture list fails with "a C function pointer cannot be formed from a closure
238
+ that captures context".
236
239
 
237
240
  A test that calls `Test.cancel(_:)` must be `throws` or `async throws`.
238
241
 
@@ -241,7 +244,7 @@ A test that calls `Test.cancel(_:)` must be `throws` or `async throws`.
241
244
  | Found | Replace with |
242
245
  |-------|--------------|
243
246
  | `#expect(exitsWith:)` | `await #expect(processExitsWith: .failure) { ... }`. In an iOS app target, test the logic behind a smaller API that does not exit, or move the exit test to a supported host or tool target. |
244
- | Exit-test closure reading outer values with no capture list | An explicit, typed capture list (`[code = code as Int32]`); Swift 6.3 compiler; values `Sendable` and `Codable` |
247
+ | Exit-test closure reading outer values with no capture list | An explicit, typed capture list (`[code = code as Int32]`); verified with Swift 6.3.1; values `Sendable` and `Codable` |
245
248
  | `Test.cancel()` in a test that is not throwing | Mark the test `async throws` and call `try Test.cancel("reason")` |
246
249
  | A warning that should not fail the run | `Issue.record("message", severity: .warning)`: reported, test still passes; Swift 6.3, Xcode 26.4 |
247
250
  | `Attachment(image, named:).record()` | `Attachment.record(image, named: "...", as: .png)`; accepts `UIImage`, `CGImage`, `CIImage`, `NSImage`; Swift 6.3, Xcode 26.4 |
@@ -23,7 +23,7 @@
23
23
  "assertions": [
24
24
  "Replaces exitsWith: with await #expect(processExitsWith: .failure).",
25
25
  "States that exit tests need Swift 6.2 and Xcode 26.0 and do not run on iOS, tvOS or watchOS, and suggests testing the non-exiting logic in the app target.",
26
- "Adds an explicit capture list for badCode, requiring the Swift 6.3 compiler and Sendable plus Codable values.",
26
+ "Adds an explicit, typed capture list for badCode, notes it was verified with the Swift 6.3.1 compiler, and requires Sendable plus Codable values.",
27
27
  "Makes the cancelling test async throws (or throws) and calls try Test.cancel.",
28
28
  "Uses Issue.record with severity: .warning and names the Swift 6.3 and Xcode 26.4 gate.",
29
29
  "Uses Attachment.record(chartImage, named:, as: .png) and names the Swift 6.3 and Xcode 26.4 gate."
@@ -52,7 +52,8 @@ and reports it as cancelled: neither passed nor failed.
52
52
  ## Capturing Values in Exit Tests
53
53
 
54
54
  ST-0012 lets an exit test's closure capture values from the surrounding scope.
55
- It needs the Swift 6.3 compiler. Exit tests themselves arrived in Swift 6.2
55
+ It compiles with the Swift 6.3.1 compiler in Xcode 26.4; earlier compilers were
56
+ not checked. Exit tests themselves arrived in Swift 6.2
56
57
  (Xcode 26.0) through the `processExitsWith:` macros. Captured values must be
57
58
  `Sendable` and `Codable`, because they are sent to the child process.
58
59
 
@@ -101,7 +102,7 @@ import UIKit
101
102
  |---------|---------|
102
103
  | Attachments of standard values | Swift 6.2, Xcode 26.0 |
103
104
  | Exit tests with `processExitsWith:` | Swift 6.2, Xcode 26.0; macOS, Linux, FreeBSD, OpenBSD, Windows |
104
- | Capture lists in exit tests | Swift 6.3 compiler |
105
+ | Capture lists in exit tests | Verified with the Swift 6.3.1 compiler (Xcode 26.4) |
105
106
  | `Issue.record(_:severity:)`, `Test.cancel(_:)`, image attachments | Swift 6.3, Xcode 26.4 |
106
107
 
107
108
  iOS, tvOS and watchOS have no exit tests. Put the exiting behaviour behind a
@@ -35,9 +35,11 @@ shipped without a migration plan.
35
35
 
36
36
  ## Defining models
37
37
 
38
- `@Model` only applies to classes. It adds conformance to `PersistentModel`,
39
- `Observable` and `Sendable`, so a model needs reference semantics and a
40
- designated initializer.
38
+ `@Model` only applies to classes. It adds conformance to `PersistentModel`
39
+ (which refines `Observable`, `Hashable` and `Identifiable`), so a model needs
40
+ reference semantics and a designated initializer. Model instances are not
41
+ `Sendable`: to hand a model to another actor, pass its `PersistentIdentifier`
42
+ (which is `Sendable`) and fetch it again from that actor's context.
41
43
 
42
44
  ```swift
43
45
  import SwiftData
@@ -188,19 +188,24 @@ struct RootTabs: View {
188
188
  The custom binding sends every change through `select`, so a tab like
189
189
  "New" can run an action instead of becoming the selected tab.
190
190
 
191
- ### iOS 26 additions
191
+ ### Tab APIs from iOS 18 and iOS 26
192
192
 
193
- - `Tab(role: .search)` turns the tab bar into a search field while that tab is
194
- active.
195
- - `.tabBarMinimizeBehavior(_:)` with `.onScrollDown`, `.onScrollUp` or `.never`.
196
- iPhone only.
193
+ From iOS 18 (macOS 15):
194
+
195
+ - `Tab(role: .search)` marks the search tab; the iOS 26 tab bar shows it as a
196
+ search field while that tab is active.
197
197
  - `.tabViewSidebarHeader { }` and `.tabViewSidebarFooter { }` customise the
198
198
  sidebar on iPadOS and macOS.
199
- - `.tabViewBottomAccessory { }` pins a view just above the tab bar, such as a
200
- mini player.
201
199
  - `TabSection` collects tabs under a sidebar heading; mark it with
202
200
  `.tabPlacement(.sidebarOnly)` to keep it out of the compact bar.
203
201
 
202
+ From iOS 26 only:
203
+
204
+ - `.tabBarMinimizeBehavior(_:)` with `.onScrollDown`, `.onScrollUp` or `.never`.
205
+ iPhone only.
206
+ - `.tabViewBottomAccessory { }` pins a view just above the tab bar, such as a
207
+ mini player.
208
+
204
209
  More in [references/tabview.md](references/tabview.md).
205
210
 
206
211
  ## Deep links
@@ -259,7 +264,10 @@ Details, AASA format and scheme parsing: [references/deeplinks.md](references/de
259
264
  - Driving a model-backed sheet with `.sheet(isPresented:)` instead of
260
265
  `.sheet(item:)`.
261
266
  - Putting views in the path. The path holds small `Hashable` route values.
262
- - Holding an `@Observable` router as a property of another `@Observable` object.
267
+ - Writing `NavigationStack(path: $router.path)` where `router` is a plain `let`
268
+ or comes from `@Environment`. That gives no binding and does not compile;
269
+ declare the property `@Bindable var router: Router`, or add
270
+ `@Bindable var router = router` inside `body`.
263
271
  - Building tabs with the older `.tabItem { }` instead of `Tab(value:)` with
264
272
  `TabView(selection:)`.
265
273
  - Expecting `tabBarMinimizeBehavior` to do anything on iPad. It is iPhone only.
@@ -285,5 +293,5 @@ Details, AASA format and scheme parsing: [references/deeplinks.md](references/de
285
293
 
286
294
  - [references/navigationstack.md](references/navigationstack.md): routers, per-tab stacks, centralised destinations, data-driven tabs
287
295
  - [references/sheets.md](references/sheets.md): enum sheet routing, sizing, dismissal confirmation
288
- - [references/tabview.md](references/tabview.md): tab architecture, custom selection binding, dynamic tabs, iOS 26 tab API
296
+ - [references/tabview.md](references/tabview.md): tab architecture, custom selection binding, dynamic tabs, the iOS 18 and iOS 26 tab APIs
289
297
  - [references/deeplinks.md](references/deeplinks.md): router URL handling, universal links and AASA, custom schemes, Handoff
@@ -172,5 +172,7 @@ struct DynamicTabs: View {
172
172
  - A path shared across tabs, unless you really want one global history.
173
173
  - Route identifiers that are not stable or not `Hashable`.
174
174
  - View instances stored in the path instead of route data.
175
- - A router stored inside another `@Observable` object; keep it standalone to
176
- avoid nested observation.
175
+ - A router passed as a plain `let` or read from `@Environment` and then used as
176
+ `$router.path`: no binding exists without `@Bindable`, so the stack view needs
177
+ `@Bindable var router: Router` (as `ExploreStack` has) or a local
178
+ `@Bindable var router = router` in `body`.
@@ -107,7 +107,7 @@ enum AppTab: Hashable, Identifiable {
107
107
  }
108
108
  ```
109
109
 
110
- ## iOS 26 tab API
110
+ ## Tab APIs from iOS 18 and iOS 26
111
111
 
112
112
  ```swift
113
113
  TabView {
@@ -121,20 +121,27 @@ TabView {
121
121
  .tabViewSidebarBottomBar { SettingsButton() }
122
122
  ```
123
123
 
124
- - `TabBarMinimizeBehavior`: `.automatic` (decided by context), `.onScrollDown`,
125
- `.onScrollUp` and `.never`. The two scroll behaviours apply on iPhone only.
126
- - `Tab(role: .search)` replaces the bar with a search field while that tab is
127
- active.
124
+ iOS 18 (macOS 15, visionOS 2):
125
+
126
+ - `Tab(role: .search)` marks the search tab; the iOS 26 tab bar replaces the bar
127
+ with a search field while that tab is active.
128
128
  - Sidebar customisation: `.tabViewSidebarHeader { }`,
129
129
  `.tabViewSidebarFooter { }` and `.tabViewSidebarBottomBar { }`.
130
+ - `TabSection` and `.tabPlacement(_:)`.
131
+
132
+ iOS 26 only:
133
+
134
+ - `TabBarMinimizeBehavior`: `.automatic` (decided by context), `.onScrollDown`,
135
+ `.onScrollUp` and `.never`. The two scroll behaviours apply on iPhone only.
130
136
  - `.tabViewBottomAccessory { }` shows content just above the tab bar; its
131
137
  placement is described by `TabViewBottomAccessoryPlacement`.
132
138
 
139
+ The example mixes both groups, so as written it needs iOS 26.
140
+
133
141
  ## Pitfalls
134
142
 
135
143
  - A view model per tab. Keep state local to views or in shared `@Observable`
136
144
  services.
137
- - `@Observable` objects nested inside each other.
138
145
  - An `AppTab.id` that changes between launches; dynamic cases must hash on a
139
146
  stable identifier.
140
147
  - Special tabs such as compose that change the selection.
@@ -44,7 +44,9 @@ optional.
44
44
 
45
45
  Most requests are value types. Requests that must remember earlier frames are
46
46
  `final class` types conforming to `StatefulRequest`: `TrackObjectRequest`,
47
- `TrackRectangleRequest`, `TrackOpticalFlowRequest`, `DetectTrajectoriesRequest`,
47
+ `TrackRectangleRequest`, `TrackOpticalFlowRequest`,
48
+ `TrackTranslationalImageRegistrationRequest`,
49
+ `TrackHomographicImageRegistrationRequest`, `DetectTrajectoriesRequest`,
48
50
  `DetectHumanBodyPose3DRequest` and `GeneratePersonSegmentationRequest`. Keep one
49
51
  instance alive for the whole sequence.
50
52
 
@@ -68,8 +68,9 @@ headline = "\(now.temperature.formatted()), \(now.condition.description)"
68
68
 
69
69
  ### Hourly
70
70
 
71
- By default the hourly forecast covers 25 consecutive hours starting with the
72
- current hour. `hourlyForecast` is a `Forecast<HourWeather>`:
71
+ At the time of writing, Apple's documentation says the default hourly forecast
72
+ covers 25 consecutive hours starting with the current hour; read the count from
73
+ the result rather than hard-coding it. `hourlyForecast` is a `Forecast<HourWeather>`:
73
74
 
74
75
  ```swift
75
76
  let timeline = report.hourlyForecast.map { slot in
@@ -79,7 +80,9 @@ let timeline = report.hourlyForecast.map { slot in
79
80
 
80
81
  ### Daily
81
82
 
82
- By default the daily forecast covers 10 consecutive days starting today.
83
+ At the time of writing, Apple's documentation says the default daily forecast
84
+ covers 10 consecutive days starting today; again, read the count from the
85
+ result.
83
86
  `dailyForecast` is a `Forecast<DayWeather>`:
84
87
 
85
88
  ```swift
@@ -145,8 +148,8 @@ if let nextHour = try await forecaster.weather(for: site, including: .minute) {
145
148
  | Query | Result type | Notes |
146
149
  | --- | --- | --- |
147
150
  | `.current` | `CurrentWeather` | |
148
- | `.hourly` | `Forecast<HourWeather>` | 25 hours from now |
149
- | `.daily` | `Forecast<DayWeather>` | 10 days from today |
151
+ | `.hourly` | `Forecast<HourWeather>` | Default span per Apple's docs: 25 hours from now |
152
+ | `.daily` | `Forecast<DayWeather>` | Default span per Apple's docs: 10 days from today |
150
153
  | `.minute` | `Forecast<MinuteWeather>?` | `nil` where unsupported |
151
154
  | `.alerts` | `[WeatherAlert]?` | `nil` where unsupported |
152
155
  | `.availability` | `WeatherAvailability` | Which region-limited datasets exist here |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: widgetkit
3
- description: "WidgetKit and ActivityKit surfaces on iOS 26+: timeline-driven widgets for the Home Screen, the Lock Screen and StandBy, interactive widgets using Button or Toggle with AppIntent actions, Live Activities and their Dynamic Island layouts (compact, expanded, minimal), Control Center controls with ControlWidgetButton or ControlWidgetToggle, families, deep links, the refresh budget, reloads triggered by push, Liquid Glass accented rendering, extension setup, App Groups and entitlements. Use when implementing, reviewing or improving widgets, Live Activities or controls."
3
+ description: "WidgetKit surfaces on iOS 26+: timeline-driven widgets for the Home Screen, the Lock Screen and StandBy, interactive widgets using Button or Toggle with AppIntent actions, widget views for a Live Activity (Dynamic Island compact, expanded, minimal), Control Center controls with ControlWidgetButton or ControlWidgetToggle, families, deep links, the refresh budget, push reloads, accented rendering, extension setup, App Groups. Use when implementing or reviewing widgets, Live Activity views or controls. Not for Live Activity lifecycle or pushes (live-activities)."
4
4
  metadata:
5
5
  source: multi-agent-pipeline
6
6
  ---
@@ -13,7 +13,9 @@ surface WidgetKit and ActivityKit draw on: the Home Screen, the Lock Screen,
13
13
  Live Activities and the Dynamic Island, Control Center and StandBy.
14
14
 
15
15
  Related skills: `app-intents` for intent and entity design beyond what a widget
16
- needs, `live-activities` for deeper ActivityKit push contracts.
16
+ needs, `live-activities` for the Live Activity lifecycle (request, update, end),
17
+ push tokens and ActivityKit push contracts. This skill covers the widget views a
18
+ Live Activity renders.
17
19
 
18
20
  Longer examples are in [the advanced reference](references/widgetkit-advanced.md).
19
21
 
@@ -300,14 +302,20 @@ on its side. Read `@Environment(\.widgetLocation)` to tell where you are drawn:
300
302
  - Budget: roughly 40 to 70 refreshes a day, with entries 5 minutes apart or more.
301
303
  For countdowns use `Text(timerInterval:countsDown:)` instead of one entry per tick.
302
304
 
303
- ## iOS 26 Additions
305
+ ## iOS 18 Additions
304
306
 
305
307
  - `WidgetAccentedRenderingMode` (`.accented`, `.accentedDesaturated`,
306
- `.desaturated`, `.fullColor`) controls how images adapt to Liquid Glass tinting.
308
+ `.desaturated`, `.fullColor`), applied with `.widgetAccentedRenderingMode(_:)`,
309
+ controls how images adapt to accented (tinted) rendering. Available from
310
+ iOS 18, macOS 15 and watchOS 11; on visionOS from 26.
311
+ - `ControlPushHandler.pushTokensDidChange(controls:)` gives a push token per
312
+ control; an APNs push to it reloads the control.
313
+
314
+ ## iOS 26 Additions
315
+
307
316
  - `WidgetPushHandler.pushTokenDidChange(_:widgets:)` gives you a token to send to
308
- your server; an APNs push to it reloads the widget. `ControlPushHandler` does
309
- the same for controls. See
310
- [Reloading timelines by push](references/widgetkit-advanced.md#reloading-timelines-by-push-ios-26-and-later).
317
+ your server; an APNs push to it reloads the widget. See
318
+ [Reloading timelines by push](references/widgetkit-advanced.md#reloading-timelines-by-push).
311
319
  - `.systemSmall` widgets render in CarPlay from iOS 26. Keep them readable at a
312
320
  glance so a driver is never pulled into detail.
313
321
 
@@ -6,7 +6,7 @@ heading says otherwise.
6
6
  ## Contents
7
7
 
8
8
  - [Timeline Strategies](#timeline-strategies)
9
- - [Reloading timelines by push (iOS 26 and later)](#reloading-timelines-by-push-ios-26-and-later)
9
+ - [Reloading timelines by push](#reloading-timelines-by-push)
10
10
  - [Deep Links and Widget URLs](#deep-links-and-widget-urls)
11
11
  - [User-configurable widgets through App Intents](#user-configurable-widgets-through-app-intents)
12
12
  - [Multiple Widget Support](#multiple-widget-support)
@@ -75,9 +75,10 @@ cost budget.
75
75
  - A widget run from Xcode's debugger has no refresh limit, so budget problems
76
76
  only show up outside the debugger.
77
77
 
78
- ## Reloading timelines by push (iOS 26 and later)
78
+ ## Reloading timelines by push
79
79
 
80
- A `WidgetPushHandler` receives a token for APNs. Send it to your server.
80
+ From iOS 26, a `WidgetPushHandler` receives a token for APNs. Send it to your
81
+ server.
81
82
 
82
83
  ```swift
83
84
  struct ScoreboardPushHandler: WidgetPushHandler {
@@ -99,8 +100,9 @@ func hexString(_ bytes: Data) -> String {
99
100
  When the server sends an APNs push addressed to that token, the system calls
100
101
  `getTimeline` or `timeline(for:in:)` again.
101
102
 
102
- Controls have their own handler that receives every control at once, as
103
- `ControlInfo` values; each carries its token in an optional `pushInfo`:
103
+ Controls have their own handler, available from iOS 18, that receives every
104
+ control at once, as `ControlInfo` values; each carries its token in an optional
105
+ `pushInfo`:
104
106
 
105
107
  ```swift
106
108
  struct GaragePushHandler: ControlPushHandler {
@@ -478,7 +480,7 @@ Task {
478
480
  }
479
481
  ```
480
482
 
481
- From iOS 26 a broadcast channel works too: `pushType: .channel("ferry-route-12")`.
483
+ From iOS 18 a broadcast channel works too: `pushType: .channel("ferry-route-12")`.
482
484
 
483
485
  Update payload:
484
486