@mmerterden/multi-agent-pipeline 20.34.0 → 20.36.0

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 (335) hide show
  1. package/CHANGELOG.md +69 -1
  2. package/README.md +7 -5
  3. package/README.tr.md +6 -5
  4. package/docs/architecture.md +3 -3
  5. package/docs/ecosystem.md +5 -5
  6. package/docs/facts.json +4 -4
  7. package/install/catalog-history.json +1 -1
  8. package/install/claude.mjs +16 -1
  9. package/install/templates/claude-hooks.json +23 -1
  10. package/manifest.json +338 -305
  11. package/package.json +5 -1
  12. package/pipeline/agents/bulk-reader.md +3 -0
  13. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  14. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  15. package/pipeline/commands/multi-agent/office-pair/SKILL.md +66 -0
  16. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  17. package/pipeline/commands/multi-agent/sync/SKILL.md +3 -3
  18. package/pipeline/contract/CHANGELOG.md +9 -0
  19. package/pipeline/contract/README.md +1 -0
  20. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  21. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  22. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  23. package/pipeline/contract/manifest.json +14 -1
  24. package/pipeline/contract/types/index.d.ts +24 -2
  25. package/pipeline/lib/json-file-lock.mjs +61 -15
  26. package/pipeline/lib/office-remote.mjs +228 -0
  27. package/pipeline/lib/qr-code.mjs +386 -0
  28. package/pipeline/lib/repo-hygiene.sh +1 -0
  29. package/pipeline/lib/run-completion.mjs +17 -0
  30. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  31. package/pipeline/multi-agent-refs/cross-cli-contract.md +4 -4
  32. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  33. package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
  34. package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
  35. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  36. package/pipeline/multi-agent-refs/help/en.md +1 -0
  37. package/pipeline/multi-agent-refs/help/tr.md +1 -0
  38. package/pipeline/multi-agent-refs/keychain.md +7 -0
  39. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  40. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
  41. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
  42. package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
  43. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
  44. package/pipeline/multi-agent-refs/rules.md +3 -2
  45. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  46. package/pipeline/office/NOTICE.md +4 -1
  47. package/pipeline/office/assets/char_12.png +0 -0
  48. package/pipeline/office/assets/char_13.png +0 -0
  49. package/pipeline/office/assets/char_14.png +0 -0
  50. package/pipeline/office/assets/char_15.png +0 -0
  51. package/pipeline/office/assets/char_16.png +0 -0
  52. package/pipeline/office/assets/char_17.png +0 -0
  53. package/pipeline/office/assets/char_18.png +0 -0
  54. package/pipeline/office/assets/char_19.png +0 -0
  55. package/pipeline/office/assets/char_20.png +0 -0
  56. package/pipeline/office/assets/char_21.png +0 -0
  57. package/pipeline/office/assets/char_22.png +0 -0
  58. package/pipeline/office/assets/char_23.png +0 -0
  59. package/pipeline/office/assets/char_24.png +0 -0
  60. package/pipeline/office/assets/char_25.png +0 -0
  61. package/pipeline/office/assets/char_26.png +0 -0
  62. package/pipeline/office/assets/char_27.png +0 -0
  63. package/pipeline/office/assets/char_28.png +0 -0
  64. package/pipeline/office/assets/char_29.png +0 -0
  65. package/pipeline/office/assets/char_30.png +0 -0
  66. package/pipeline/office/index.html +293 -49
  67. package/pipeline/schemas/agent-state.schema.json +18 -0
  68. package/pipeline/schemas/planning-output.schema.json +18 -0
  69. package/pipeline/schemas/pr-reach.schema.json +55 -0
  70. package/pipeline/schemas/prefs.schema.json +13 -7
  71. package/pipeline/schemas/triage-output.schema.json +12 -1
  72. package/pipeline/schemas/unattended-policy.json +2 -1
  73. package/pipeline/scripts/_guard_rules.py +449 -1
  74. package/pipeline/scripts/_test-gaming.mjs +36 -0
  75. package/pipeline/scripts/agent-guard.py +79 -19
  76. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  77. package/pipeline/scripts/answer-check.mjs +113 -0
  78. package/pipeline/scripts/answer-check.sh +20 -0
  79. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  80. package/pipeline/scripts/contract-server.mjs +35 -1
  81. package/pipeline/scripts/gate-ledger.mjs +144 -21
  82. package/pipeline/scripts/office-build.mjs +276 -5
  83. package/pipeline/scripts/office-control.mjs +70 -10
  84. package/pipeline/scripts/office-pair.mjs +438 -0
  85. package/pipeline/scripts/office-remote-client.mjs +463 -0
  86. package/pipeline/scripts/phase-tracker.sh +8 -0
  87. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  88. package/pipeline/scripts/phone-devices.mjs +9 -1
  89. package/pipeline/scripts/pre-commit-check.sh +28 -2
  90. package/pipeline/scripts/pre-push-check.sh +6 -12
  91. package/pipeline/scripts/rework-base.mjs +87 -0
  92. package/pipeline/scripts/run-commit-check.mjs +104 -0
  93. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  94. package/pipeline/scripts/run-scope-guard.sh +13 -1
  95. package/pipeline/scripts/scope-check-gate.mjs +70 -6
  96. package/pipeline/scripts/task-mirror.mjs +106 -15
  97. package/pipeline/scripts/task-mirror.sh +3 -2
  98. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  99. package/pipeline/scripts/update-check.sh +7 -1
  100. package/pipeline/scripts/update-issue-progress.sh +73 -8
  101. package/pipeline/scripts/validate-planning.mjs +105 -1
  102. package/pipeline/scripts/validate-triage.mjs +42 -2
  103. package/pipeline/scripts/work-items.mjs +119 -1
  104. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  105. package/pipeline/scripts/worktree-prepare.sh +40 -4
  106. package/pipeline/scripts/write-state.mjs +67 -24
  107. package/pipeline/skills/.skill-manifest.json +12 -8
  108. package/pipeline/skills/.skills-index.json +15 -4
  109. package/pipeline/skills/shared/README.md +4 -3
  110. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +1 -1
  111. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +6 -6
  112. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  113. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  114. package/pipeline/skills/shared/core/multi-agent-office-pair/SKILL.md +19 -0
  115. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  116. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +3 -3
  117. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  118. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  119. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  120. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  121. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  122. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  123. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  124. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  125. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  126. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  127. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  128. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  129. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  130. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  131. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  132. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  133. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  134. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  135. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  136. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  137. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  138. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  139. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  140. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  141. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  142. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  143. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  144. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  145. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  146. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  147. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  148. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  149. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  150. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  151. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  152. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  153. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  154. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  155. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  156. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  157. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  158. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  159. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  160. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  161. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  162. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  163. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  164. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  165. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  166. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  167. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  168. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  169. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  170. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  171. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  172. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  173. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  174. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  175. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  176. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  177. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  178. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  179. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  180. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  181. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  182. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  183. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  184. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  185. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  186. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  187. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  188. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  189. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  190. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  191. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  192. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  193. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  194. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  195. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  196. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  197. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  198. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  199. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  200. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  201. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  202. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  203. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  204. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  205. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  206. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  207. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  208. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  209. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  210. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  211. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  212. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  213. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  214. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  215. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  216. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  217. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  218. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  219. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  220. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  221. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  222. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  223. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  224. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  225. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  226. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  227. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  228. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  229. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  230. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  231. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  232. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  233. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  234. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  235. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  236. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  237. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  238. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  239. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  240. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  241. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  242. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  243. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  244. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  245. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  246. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  247. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  248. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  249. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  250. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  251. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  252. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  253. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  254. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  255. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  256. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  257. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  258. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  259. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  260. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  261. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  262. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  263. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  264. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  265. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  266. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  267. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  268. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  269. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  270. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  271. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  272. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  273. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  274. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  275. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  276. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  277. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  278. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  279. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  280. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  281. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  282. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  283. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  284. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  285. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  286. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  287. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  288. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  289. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  290. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  291. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  292. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  293. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  294. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  295. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  296. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  297. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  298. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  299. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  300. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  301. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  302. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  303. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  304. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  305. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  306. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  307. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  308. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  309. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  310. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  311. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  312. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  313. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  314. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  315. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  316. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  317. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  318. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  319. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  320. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  321. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  322. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  323. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  324. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  325. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  326. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  327. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  328. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  329. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  330. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  331. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  332. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  333. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  334. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
  335. package/pipeline/skills/skills-index.md +2 -1
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file test-stability-gate.mjs - the repeated runs of the new and changed
4
+ * tests agree, or the result is not GREEN.
5
+ *
6
+ * Phase 2's stability rule runs each test added or changed in the diff
7
+ * `testStability.repeatCount` times and keeps every run as
8
+ * `.pipeline/repeat-<k>.junit.xml`. This gate reads those files and compares
9
+ * each test case's outcome across the runs: a case that passed in some runs
10
+ * and failed in others is a flake, however the last run ended.
11
+ *
12
+ * A case is keyed by classname and name. Outcomes are pass, fail (a failure
13
+ * or error element) and skip; a case missing from a run counts as not run.
14
+ *
15
+ * Usage:
16
+ * test-stability-gate.mjs --dir <dir> --expect <N>
17
+ *
18
+ * Output (stdout, JSON): {ok, expected, runs, missingRuns[], cases, flaky[], failedEverywhere[]}
19
+ * flaky [{test, passed, failed, of}] outcomes disagree across runs
20
+ * failedEverywhere tests that failed in every run (stable, but not GREEN)
21
+ *
22
+ * Exit: 0 every case passed in every run; 1 a flake, a run missing, a test
23
+ * that failed, or no test cases at all; 64 usage.
24
+ */
25
+
26
+ import { existsSync, readFileSync } from "node:fs";
27
+ import { join } from "node:path";
28
+
29
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
30
+
31
+ const attr = (tag, name) => {
32
+ const m = new RegExp(`\\b${name}="([^"]*)"`).exec(tag);
33
+ return m ? m[1] : "";
34
+ };
35
+
36
+ /** Test case key -> outcome for one JUnit XML document. */
37
+ export function outcomes(xml) {
38
+ const out = new Map();
39
+ for (const m of String(xml).matchAll(/<testcase\b([^>]*?)(?:\/>|>([\s\S]*?)<\/testcase>)/g)) {
40
+ const key = `${attr(m[1], "classname")}::${attr(m[1], "name")}`;
41
+ const body = m[2] ?? "";
42
+ const outcome = /<(?:failure|error)\b/.test(body)
43
+ ? "fail"
44
+ : /<skipped\b/.test(body)
45
+ ? "skip"
46
+ : "pass";
47
+ const prev = out.get(key);
48
+ out.set(key, prev === "fail" || outcome === "fail" ? "fail" : outcome);
49
+ }
50
+ return out;
51
+ }
52
+
53
+ /** Compare the per-run outcome maps. */
54
+ export function compare(runs) {
55
+ const keys = new Set();
56
+ for (const r of runs) for (const k of r.keys()) keys.add(k);
57
+ const flaky = [];
58
+ const failedEverywhere = [];
59
+ for (const k of [...keys].sort()) {
60
+ const seen = runs.map((r) => r.get(k) ?? "missing");
61
+ const passed = seen.filter((o) => o === "pass").length;
62
+ const failed = seen.filter((o) => o === "fail" || o === "missing").length;
63
+ if (seen.every((o) => o === "skip")) continue;
64
+ if (passed && failed) flaky.push({ test: k, passed, failed, of: runs.length });
65
+ else if (!passed && failed) failedEverywhere.push(k);
66
+ }
67
+ return { cases: keys.size, flaky, failedEverywhere };
68
+ }
69
+
70
+ export function evaluate(dir, expected) {
71
+ const runs = [];
72
+ const missingRuns = [];
73
+ for (let k = 1; k <= expected; k++) {
74
+ const p = join(dir, `repeat-${k}.junit.xml`);
75
+ if (!existsSync(p)) {
76
+ missingRuns.push(k);
77
+ continue;
78
+ }
79
+ runs.push(outcomes(readFileSync(p, "utf8")));
80
+ }
81
+ const c = compare(runs);
82
+ return {
83
+ ok:
84
+ missingRuns.length === 0 &&
85
+ c.cases > 0 &&
86
+ c.flaky.length === 0 &&
87
+ c.failedEverywhere.length === 0,
88
+ expected,
89
+ runs: runs.length,
90
+ missingRuns,
91
+ ...c,
92
+ };
93
+ }
94
+
95
+ function main() {
96
+ const argv = process.argv.slice(2);
97
+ const get = (f) => {
98
+ const i = argv.indexOf(f);
99
+ return i >= 0 ? argv[i + 1] : undefined;
100
+ };
101
+ const dir = get("--dir");
102
+ const expected = Number(get("--expect"));
103
+ if (argv.length !== 4 || !dir || !Number.isInteger(expected) || expected < 1 || expected > 50) {
104
+ console.error("usage: test-stability-gate.mjs --dir <dir> --expect <N>");
105
+ return 64;
106
+ }
107
+ const out = evaluate(dir, expected);
108
+ process.stdout.write(JSON.stringify(out) + "\n");
109
+ return out.ok ? 0 : 1;
110
+ }
111
+
112
+ if (invokedDirectly(import.meta.url)) process.exit(main());
@@ -30,6 +30,7 @@
30
30
  # # updateCheck.ttlHours, else 24)
31
31
  # bash pipeline/scripts/update-check.sh --force # ignore cache
32
32
  # bash pipeline/scripts/update-check.sh --print-required # emit the floor only
33
+ # bash pipeline/scripts/update-check.sh --cache-only # cached answer at any age, never the registry
33
34
  #
34
35
  # Cache file: ~/.claude/logs/multi-agent/.update-check ("epoch|latest|required").
35
36
  # A legacy two-field cache ("epoch|latest") still reads - the missing third
@@ -47,6 +48,7 @@ FAIL_TTL_SECONDS=3600
47
48
  LOCAL_VERSION=""
48
49
  FORCE=0
49
50
  PRINT_REQUIRED=0
51
+ CACHE_ONLY=0
50
52
 
51
53
  while [ $# -gt 0 ]; do
52
54
  case "$1" in
@@ -54,6 +56,7 @@ while [ $# -gt 0 ]; do
54
56
  --ttl-hours) TTL_HOURS="${2:-}"; shift; [ $# -gt 0 ] && shift ;;
55
57
  --force) FORCE=1; shift ;;
56
58
  --print-required) PRINT_REQUIRED=1; shift ;;
59
+ --cache-only) CACHE_ONLY=1; shift ;;
57
60
  *) shift ;;
58
61
  esac
59
62
  done
@@ -124,12 +127,15 @@ if [ "$FORCE" -eq 0 ] && [ -f "$CACHE_FILE" ]; then
124
127
  case "$cached_epoch" in (*[!0-9]*|"") cached_epoch=0 ;; esac
125
128
  # A stamp in the future is a clock or copy mistake, not a fresh answer.
126
129
  [ "$cached_epoch" -gt "$now" ] && cached_epoch=0
127
- if [ $((now - cached_epoch)) -lt $((TTL_HOURS * 3600)) ] && [ -n "$cached_latest" ]; then
130
+ if { [ "$CACHE_ONLY" -eq 1 ] || [ $((now - cached_epoch)) -lt $((TTL_HOURS * 3600)) ]; } && [ -n "$cached_latest" ]; then
128
131
  latest="$cached_latest"
129
132
  required="$cached_required"
130
133
  fi
131
134
  fi
132
135
 
136
+ # A cache-only caller gets no answer rather than a network wait.
137
+ [ -z "$latest" ] && [ "$CACHE_ONLY" -eq 1 ] && exit 0
138
+
133
139
  # Stale or missing cache -> one bounded registry call (silent on any failure).
134
140
  # The abbreviated packument carries dist-tags at a quarter of the full document.
135
141
  # A failed read is remembered for FAIL_TTL_SECONDS in "<cache>.fail", so an
@@ -11,7 +11,13 @@
11
11
  # never hand-edited.
12
12
  #
13
13
  # Usage:
14
- # update-issue-progress.sh <task-id>
14
+ # update-issue-progress.sh <task-id> [--comment-file <path>]
15
+ #
16
+ # --comment-file posts that comment on the issue first (through the outbound
17
+ # gate, with the `Ref: #N` footer appended when missing), then the flags, so
18
+ # a caller with no separate comment step (the unattended runner) still writes
19
+ # both. `--body-file` is the same flag under the name the runner's script
20
+ # runner passes. Without it the flags wait for a comment already on the issue.
15
21
  #
16
22
  # Inputs:
17
23
  # $HOME/.claude/logs/multi-agent/{project}/{taskId}/agent-state.json
@@ -26,6 +32,9 @@
26
32
  # 2 missing agent-state.json
27
33
  # 3 missing required flags
28
34
  # 4 GitHub edit failed
35
+ # 5 no issue comment from this run yet: the comment is posted first and the
36
+ # flags follow it, both or neither (channels/issue-comment.md)
37
+ # 6 issue coordinates in the state that are not an owner/repo and a number
29
38
 
30
39
  set -euo pipefail
31
40
 
@@ -118,9 +127,17 @@ EOF
118
127
 
119
128
  TASK_ID="${1:-}"
120
129
  if [ -z "$TASK_ID" ]; then
121
- echo "usage: update-issue-progress.sh <task-id>" >&2
130
+ echo "usage: update-issue-progress.sh <task-id> [--comment-file <path>]" >&2
122
131
  exit 64
123
132
  fi
133
+ COMMENT_FILE=""
134
+ if [ "$TASK_ID" != "--rewrite" ] && [ "$#" -gt 1 ]; then
135
+ if [ "$#" -ne 3 ] || { [ "$2" != "--comment-file" ] && [ "$2" != "--body-file" ]; } || [ ! -f "$3" ]; then
136
+ echo "usage: update-issue-progress.sh <task-id> [--comment-file <path>]" >&2
137
+ exit 64
138
+ fi
139
+ COMMENT_FILE="$3"
140
+ fi
124
141
 
125
142
  # Internal test seam: `--rewrite <body-file>` runs only the Progress-block
126
143
  # rewrite (placeholder glyphs) and prints the result. No state, no gh.
@@ -155,6 +172,11 @@ if [ -z "$ORG_REPO" ] || [ -z "$ISSUE_NUM" ]; then
155
172
  echo "update-issue-progress: no githubIssue in state for task=$TASK_ID (Jira-originated task, or issue not yet linked)" >&2
156
173
  exit 3
157
174
  fi
175
+ if ! printf '%s' "$ISSUE_NUM" | grep -Eq '^[0-9]{1,9}$' ||
176
+ ! printf '%s' "$ORG_REPO" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9._-]{1,100}$'; then
177
+ echo "update-issue-progress: githubIssue in state is not an owner/repo and a number; nothing written" >&2
178
+ exit 6
179
+ fi
158
180
 
159
181
  # Read the four flags (default false).
160
182
  F_IMPL=$(jq -r '.flags.implementation // false' "$AGENT_STATE")
@@ -190,17 +212,60 @@ NEW_BLOCK=$(build_progress_block "$G_IMPL" "$G_TEST" "$G_CC" "$G_WIKI")
190
212
  # comment, next heading, or EOF) - see rewrite_progress_body above.
191
213
  rewrite_progress_body "$NEW_BLOCK" "$TMP_BODY" > "$TMP_NEW"
192
214
 
193
- # Check if body actually changed (idempotent no-op).
194
- if cmp -s "$TMP_BODY" "$TMP_NEW"; then
215
+ # Whether the body changes (an unchanged body is an idempotent no-op for the
216
+ # flags), and the new body's outbound check before anything is posted, so a
217
+ # refused body never leaves a comment behind without its flags.
218
+ CHANGED=true
219
+ cmp -s "$TMP_BODY" "$TMP_NEW" && CHANGED=false
220
+ if [ "$CHANGED" = true ] && ! ma_outbound_gate "$TMP_NEW"; then
221
+ echo "update-issue-progress: outbound gate refused the new body; #$ISSUE_NUM left unchanged" >&2
222
+ exit 7
223
+ fi
224
+
225
+ # Both or neither: the flags follow this run's issue comment, never stand in
226
+ # for it. With --comment-file the comment is posted here, first, whether or
227
+ # not the flags change. Otherwise it
228
+ # is the one the authenticated account posted on the issue since the run
229
+ # started, carrying the template's `Ref: #N` footer.
230
+ if [ -n "$COMMENT_FILE" ]; then
231
+ TMP_COMMENT=$(mktemp "/tmp/issue-comment-${TASK_ID}.XXXXXX")
232
+ trap 'rm -f "$TMP_BODY" "$TMP_NEW" "$TMP_COMMENT"' EXIT
233
+ cat "$COMMENT_FILE" > "$TMP_COMMENT"
234
+ grep -qF "Ref: #$ISSUE_NUM" "$TMP_COMMENT" || printf '\n\nRef: #%s\n' "$ISSUE_NUM" >> "$TMP_COMMENT"
235
+ if ! ma_outbound_gate "$TMP_COMMENT"; then
236
+ echo "update-issue-progress: outbound gate refused the comment; #$ISSUE_NUM left unchanged" >&2
237
+ exit 7
238
+ fi
239
+ if ! gh issue comment "$ISSUE_NUM" --repo "$ORG_REPO" --body-file "$TMP_COMMENT" >/dev/null; then
240
+ echo "update-issue-progress: gh issue comment failed for #$ISSUE_NUM; flags left unchanged" >&2
241
+ exit 4
242
+ fi
243
+ elif [ "$CHANGED" = true ]; then
244
+ # The comments API reads `since` as an ISO 8601 instant; an offset's `+`
245
+ # would arrive as a space, so the start time goes out in UTC.
246
+ STARTED=$(jq -r '.startedAt // empty' "$AGENT_STATE")
247
+ STARTED=$(node -e 'const d = new Date(process.argv[1]); if (process.argv[1] && !isNaN(d)) process.stdout.write(d.toISOString().replace(/\.\d{3}Z$/, "Z"));' "$STARTED" 2>/dev/null || true)
248
+ ME=$(gh api user --jq .login 2>/dev/null || true)
249
+ if ! printf '%s' "$ME" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9-]{0,38}(\[bot\])?$'; then
250
+ echo "update-issue-progress: could not read the GitHub account; #$ISSUE_NUM left unchanged" >&2
251
+ exit 5
252
+ fi
253
+ COMMENTS_PATH="repos/$ORG_REPO/issues/$ISSUE_NUM/comments"
254
+ [ -n "$STARTED" ] && COMMENTS_PATH="$COMMENTS_PATH?since=$STARTED"
255
+ if ! gh api --paginate "$COMMENTS_PATH" \
256
+ --jq ".[] | select(.user.login == \"$ME\") | select(.body | contains(\"Ref: #$ISSUE_NUM\")) | .id" 2>/dev/null |
257
+ grep -q .; then
258
+ echo "update-issue-progress: no issue comment from this run on #$ISSUE_NUM; post it first (comment and flags go together)" >&2
259
+ exit 5
260
+ fi
261
+ fi
262
+
263
+ if [ "$CHANGED" = false ]; then
195
264
  echo "update-issue-progress: no change for #$ISSUE_NUM (idempotent no-op)" >&2
196
265
  exit 0
197
266
  fi
198
267
 
199
268
  # Apply.
200
- if ! ma_outbound_gate "$TMP_NEW"; then
201
- echo "update-issue-progress: outbound gate refused the new body; #$ISSUE_NUM left unchanged" >&2
202
- exit 7
203
- fi
204
269
  if ! gh issue edit "$ISSUE_NUM" --repo "$ORG_REPO" --body-file "$TMP_NEW" >/dev/null; then
205
270
  echo "update-issue-progress: gh issue edit failed for #$ISSUE_NUM" >&2
206
271
  exit 4
@@ -27,6 +27,7 @@
27
27
 
28
28
  import { readFileSync } from "node:fs";
29
29
  import { planOverlaps } from "./_plan-overlap.mjs";
30
+ import { sectionBody, tableDataRows } from "../lib/analysis-sections.mjs";
30
31
 
31
32
  const ALLOWED_TYPES = new Set(["test", "code", "refactor", "docs", "chore"]);
32
33
  const TASK_ID_RE = /^T\d+$/;
@@ -35,6 +36,85 @@ const REVIEW_FOCUS_MAX = 5;
35
36
  const SPEC_RATIO = 4;
36
37
  const SPEC_MIN_CHARS = 400;
37
38
 
39
+ const FIGMA_LINK = /\bfigma\.com\/(?:design|file|proto)\//i;
40
+ const DESIGN_VALUE = /\b\d+(?:\.\d+)?\s?(?:px|pt)\b/i;
41
+ const INVENTORY_KEYWORDS = ["Bileşen Envanteri", "Component Inventory"];
42
+ const UI_FILE = /(?:View|Screen|Scene|Content|Cell|Sheet)\.(?:swift|kt)$|\.figma\.(?:swift|kt)$/;
43
+ const MAPPINGS = new Set(["code-connect", "new", "best-fit"]);
44
+
45
+ /**
46
+ * The analysis Section 6 rows: the component name (first cell) and whether it
47
+ * exists already (second cell, yes / no; anything else is unknown).
48
+ */
49
+ function inventoryComponents(specText) {
50
+ const body = sectionBody(String(specText).split("\n"), INVENTORY_KEYWORDS, 2);
51
+ if (!body) return [];
52
+ return tableDataRows(body)
53
+ .map((row) => {
54
+ const cells = row.split("|").map((c) => c.replace(/[`*]/g, "").trim());
55
+ const existing = (cells[2] || "").toLowerCase();
56
+ return {
57
+ name: cells[1] ?? "",
58
+ existing: /^(yes|evet)\b/.test(existing)
59
+ ? true
60
+ : /^(no|hayır|hayir)\b/.test(existing)
61
+ ? false
62
+ : null,
63
+ };
64
+ })
65
+ .filter((r) => r.name && !/^<.*>$/.test(r.name));
66
+ }
67
+
68
+ /**
69
+ * Errors and warnings for the plan's UI tasks against Section 6: a task that
70
+ * touches a view file names its component, and the name is a Section 6 row.
71
+ */
72
+ function designCitations(tasks, components) {
73
+ const errors = [];
74
+ const warnings = [];
75
+ if (!components.length || !Array.isArray(tasks)) return { errors, warnings };
76
+ const known = new Map(components.map((c) => [c.name, c.existing]));
77
+ for (const t of tasks) {
78
+ if (!t || typeof t !== "object") continue;
79
+ const d = t.design;
80
+ if (d !== undefined) {
81
+ if (!d || typeof d.component !== "string" || !MAPPINGS.has(d.mapping)) {
82
+ errors.push(`tasks ${t.id}.design: needs {component, mapping: code-connect|new|best-fit}`);
83
+ continue;
84
+ }
85
+ const name = d.component.trim();
86
+ if (!known.has(name)) {
87
+ errors.push(
88
+ `tasks ${t.id}.design.component: "${d.component}" is not a Section 6 row; use the inventory's name verbatim, never a sound-alike`,
89
+ );
90
+ } else if (d.mapping === "new" && known.get(name) === true) {
91
+ errors.push(
92
+ `tasks ${t.id}.design: "${name}" exists already per Section 6; reuse it, do not build it new`,
93
+ );
94
+ } else if (d.mapping === "code-connect" && known.get(name) === false) {
95
+ errors.push(
96
+ `tasks ${t.id}.design: "${name}" does not exist yet per Section 6, so it has no Code Connect mapping`,
97
+ );
98
+ }
99
+ if (d.mapping === "best-fit") {
100
+ warnings.push(
101
+ `tasks ${t.id}: best-fit component needs a Risk row; Phase 3 gates it review_blocking`,
102
+ );
103
+ }
104
+ continue;
105
+ }
106
+ const ui = (Array.isArray(t.files) ? t.files : []).some(
107
+ (f) => typeof f === "string" && UI_FILE.test(f),
108
+ );
109
+ if (ui && (t.type === "code" || t.type === "refactor")) {
110
+ errors.push(
111
+ `tasks ${t.id}: touches a UI file but cites no Section 6 component (design.component)`,
112
+ );
113
+ }
114
+ }
115
+ return { errors, warnings };
116
+ }
117
+
38
118
  function specPath() {
39
119
  const i = process.argv.indexOf("--spec");
40
120
  return i > 0 ? process.argv[i + 1] : null;
@@ -241,11 +321,35 @@ async function main() {
241
321
  const spec = specPath();
242
322
  if (spec) {
243
323
  let specChars = 0;
324
+ let specText = "";
244
325
  try {
245
- specChars = readFileSync(spec, "utf-8").length;
326
+ specText = readFileSync(spec, "utf-8");
327
+ specChars = specText.length;
246
328
  } catch {
247
329
  warnings.push(`spec not readable: ${spec}`);
248
330
  }
331
+ // With a design behind the plan, values come from its tokens, never from a
332
+ // number someone read off a screenshot.
333
+ if (FIGMA_LINK.test(specText) && Array.isArray(parsed.tasks)) {
334
+ for (const t of parsed.tasks) {
335
+ const text = [
336
+ t?.title,
337
+ t?.description,
338
+ ...(Array.isArray(t?.acceptanceCriteria) ? t.acceptanceCriteria : []),
339
+ ]
340
+ .filter((v) => typeof v === "string")
341
+ .join("\n");
342
+ const m = text.match(DESIGN_VALUE);
343
+ if (m) {
344
+ errors.push(
345
+ `tasks ${t.id}: "${m[0]}" is a raw design value; name the design token (Spacing.*, Radius.*, a type style) the analysis mapped instead`,
346
+ );
347
+ }
348
+ }
349
+ }
350
+ const cited = designCitations(parsed.tasks, inventoryComponents(specText));
351
+ errors.push(...cited.errors);
352
+ warnings.push(...cited.warnings);
249
353
  if (specChars >= SPEC_MIN_CHARS && raw.length > specChars * SPEC_RATIO) {
250
354
  warnings.push(
251
355
  `plan is ${Math.round(raw.length / specChars)}x the length of its spec; record decisions, not a transcript`,
@@ -13,6 +13,15 @@
13
13
  // cat triage.json | node pipeline/scripts/validate-triage.mjs -
14
14
  // [--reviewers a.json,b.json] # every reviewer cannotVerify row needs a triage row
15
15
  // [--reviewers-dir <dir>] # same, for every reviewer-*.json in <dir>
16
+ // [--anonymized] # the verdict was judged on anonymized input:
17
+ // # no finding may carry `reviewer`, labels only
18
+ //
19
+ // An accepted finding names its source as `reviewer` (a model family),
20
+ // `foundBy` (an anonymized "Source A" label, or "security-auditor") or `tag`
21
+ // (a deterministic gate finding). Triage judges anonymized findings and
22
+ // carries `foundBy` through; anonymize-findings.mjs --restore writes the
23
+ // family back afterwards, so a `reviewer` in the verdict itself means the
24
+ // judge was told whose findings it graded.
16
25
  //
17
26
  // Exit codes:
18
27
  // 0 - valid, safe to act on
@@ -27,6 +36,8 @@ import { readdirSync, readFileSync } from "node:fs";
27
36
  import { join } from "node:path";
28
37
 
29
38
  const ALLOWED_REVIEWERS = new Set(["fable", "opus", "sonnet", "gpt"]);
39
+ const FOUND_BY_RE = /^(Source [A-Z]|security-auditor)$/;
40
+ const ANONYMIZED = process.argv.includes("--anonymized");
30
41
  const ALLOWED_SEVERITIES = new Set(["blocking", "important", "suggestion"]);
31
42
  const ALLOWED_CONSENSUS_VERDICTS = new Set([
32
43
  "unanimous-pass",
@@ -174,11 +185,33 @@ function validateVerification(v, label, errors) {
174
185
  }
175
186
  }
176
187
 
188
+ /** The finding's source kind, or null when none is valid. */
189
+ function sourceOf(f) {
190
+ if (f?.foundBy !== undefined) return FOUND_BY_RE.test(String(f.foundBy)) ? "label" : null;
191
+ if (typeof f?.tag === "string" && f.tag) return "tag";
192
+ return ALLOWED_REVIEWERS.has(f?.reviewer) ? "reviewer" : null;
193
+ }
194
+
195
+ /** With --anonymized: every finding that names a reviewer, by list and index. */
196
+ function identityLeaks(parsed) {
197
+ const out = [];
198
+ (parsed.accepted || []).forEach((f, i) => {
199
+ if (f && Object.hasOwn(f, "reviewer")) out.push(`accepted[${i}]`);
200
+ });
201
+ for (const key of ["deferred", "rejected"]) {
202
+ (parsed[key] || []).forEach((e, i) => {
203
+ if (e?.finding && Object.hasOwn(e.finding, "reviewer")) out.push(`${key}[${i}].finding`);
204
+ });
205
+ }
206
+ return out;
207
+ }
208
+
177
209
  function validateAccepted(f, i, errors) {
178
210
  validateRawFinding(f, `accepted[${i}]`, errors);
179
- if (!ALLOWED_REVIEWERS.has(f.reviewer)) {
211
+ const source = sourceOf(f);
212
+ if (source === null) {
180
213
  errors.push(
181
- `accepted[${i}]: reviewer must be one of fable|opus|sonnet|gpt (got ${JSON.stringify(f.reviewer)}; haiku was removed in v2.1.0)`,
214
+ `accepted[${i}]: name the source as foundBy ("Source A".."Source Z" or "security-auditor"), tag, or reviewer fable|opus|sonnet|gpt (got reviewer ${JSON.stringify(f?.reviewer)}, foundBy ${JSON.stringify(f?.foundBy)})`,
182
215
  );
183
216
  }
184
217
  if (typeof f.fix !== "string" || f.fix.length < 4) {
@@ -279,6 +312,13 @@ function validate(raw) {
279
312
  parsed.accepted.forEach((f, i) => validateAccepted(f, i, errors));
280
313
  parsed.deferred.forEach((f, i) => validateClassified(f, "deferred", i, errors));
281
314
  parsed.rejected.forEach((f, i) => validateClassified(f, "rejected", i, errors));
315
+ if (ANONYMIZED) {
316
+ for (const at of identityLeaks(parsed)) {
317
+ errors.push(
318
+ `${at}: carries reviewer in an anonymized verdict; carry foundBy through, the family is restored after validation`,
319
+ );
320
+ }
321
+ }
282
322
 
283
323
  // Every requirement a reviewer could not verify from the diff is settled here:
284
324
  // checked in the unchanged code (verified), turned into a finding, or ruled
@@ -7,6 +7,13 @@
7
7
  * /multi-agent:jira, /multi-agent:issue and Phase 0 run;
8
8
  * title, type, status, score, blockers, warnings, summary
9
9
  * and a description excerpt, scrubbed
10
+ * reach how far each open GitHub pull request reaches: its
11
+ * changed files (`gh pr view --json files`) looked up in
12
+ * the repository's own code graph, and the files that
13
+ * depend on them counted (graph-affected, depth 2). A
14
+ * repository without a graph, a Bitbucket pull request or
15
+ * a name that is not owner/repo is listed with its reason
16
+ * and costs no gh call
10
17
  * prs open pull requests on GitHub where you are asked to
11
18
  * review or are the author (`gh search prs`, the queries
12
19
  * /multi-agent:review lists). Bitbucket is reported as a
@@ -34,6 +41,7 @@
34
41
  * Usage:
35
42
  * work-items.mjs maturity <key>
36
43
  * work-items.mjs prs
44
+ * work-items.mjs reach
37
45
  * work-items.mjs verdict <task-id> [--post]
38
46
  *
39
47
  * MA_GH_BIN stands in for gh, MA_ISSUE_FETCHER and MA_POST_PR_REVIEW for the
@@ -57,6 +65,8 @@ import { runMain } from "../lib/fatal.mjs";
57
65
  import { invokedDirectly } from "../lib/invoked-directly.mjs";
58
66
  import { resolveTokenAsync } from "../lib/credential-resolve.mjs";
59
67
  import { homedir } from "node:os";
68
+ import { affected } from "./graph-affected.mjs";
69
+ import { loadGraph } from "./graph-query.mjs";
60
70
 
61
71
  export const CONTRACT_VERSION = "1.1.0";
62
72
  const HERE = dirname(fileURLToPath(import.meta.url));
@@ -349,6 +359,113 @@ export async function pullRequests({ env = process.env, redact = false } = {}) {
349
359
  return { contractVersion: CONTRACT_VERSION, items, sources };
350
360
  }
351
361
 
362
+ const REACH_MAX = 20;
363
+ const REACH_PARALLEL = 3;
364
+ const REACH_DEPTH = 2;
365
+ const FILES_MAX = 300;
366
+ const NAME_PART = /^(?!\.{1,2}$)[A-Za-z0-9._-]{1,100}$/;
367
+
368
+ /** owner/repo with two plain parts; anything else never reaches gh. */
369
+ function plainRepo(repo) {
370
+ const parts = String(repo).split("/");
371
+ return parts.length === 2 && parts.every((x) => NAME_PART.test(x));
372
+ }
373
+
374
+ async function inBatches(items, size, fn) {
375
+ const out = [];
376
+ for (let i = 0; i < items.length; i += size) {
377
+ out.push(...(await Promise.all(items.slice(i, i + size).map(fn))));
378
+ }
379
+ return out;
380
+ }
381
+
382
+ /** The changed file paths of one GitHub pull request, or null when gh could not say. */
383
+ async function changedFiles(env, repo, number) {
384
+ const gh = env.MA_GH_BIN || "gh";
385
+ const r = await spawnAsync(
386
+ gh,
387
+ ["pr", "view", String(number), "--repo", repo, "--json", "files"],
388
+ {
389
+ env,
390
+ timeoutMs: TIMEOUT_MS,
391
+ },
392
+ );
393
+ if (r.error || r.status !== 0) return null;
394
+ try {
395
+ const files = JSON.parse(r.stdout)?.files;
396
+ return (Array.isArray(files) ? files : [])
397
+ .map((f) => f?.path)
398
+ .filter((path) => typeof path === "string" && path.length <= 1024)
399
+ .slice(0, FILES_MAX);
400
+ } catch {
401
+ return null;
402
+ }
403
+ }
404
+
405
+ /**
406
+ * How far each open pull request reaches, widest first. Changed paths are
407
+ * used only as keys into the local graph: nothing is read from disk by them
408
+ * and none is echoed back, only counts.
409
+ */
410
+ export async function prReach({ env = process.env, list = null } = {}) {
411
+ const prs = list ?? (await pullRequests({ env }));
412
+ const envByLogin = new Map();
413
+ for (const a of await ghAccounts(env)) {
414
+ const aEnv = await accountEnv(env, a);
415
+ if (aEnv) envByLogin.set(a.login, aEnv);
416
+ }
417
+ const knowledge = join(env.HOME || homedir(), ".claude", "knowledge");
418
+ const graphs = new Map();
419
+ const graphFor = (project) => {
420
+ if (!graphs.has(project)) {
421
+ const path = join(knowledge, project, "code-graph.json");
422
+ let graph;
423
+ try {
424
+ graph = existsSync(path) ? loadGraph(path) : null;
425
+ } catch {
426
+ graph = null;
427
+ }
428
+ graphs.set(project, graph);
429
+ }
430
+ return graphs.get(project);
431
+ };
432
+ const candidates = prs.items
433
+ .filter((p) => p.source !== "github" || plainRepo(p.repo))
434
+ .slice(0, REACH_MAX);
435
+ const rows = await inBatches(candidates, REACH_PARALLEL, async (p) => {
436
+ const row = { source: p.source, repo: p.repo, number: p.number, project: null };
437
+ if (p.source !== "github") return { ...row, reach: null, reason: "not measured off GitHub" };
438
+ const project = p.repo.split("/")[1];
439
+ const graph = graphFor(project);
440
+ if (!graph) return { ...row, reach: null, reason: `no code graph for ${project}` };
441
+ const files = await changedFiles(envByLogin.get(p.account) ?? env, p.repo, p.number);
442
+ if (!files) return { ...row, project, reach: null, reason: "gh could not list its files" };
443
+ const ids = new Set(graph.nodes.filter((n) => n.kind === "file").map((n) => n.id));
444
+ const seeds = files.map((f) => `file:${f}`).filter((id) => ids.has(id));
445
+ const byId = new Map(graph.nodes.map((n) => [n.id, n]));
446
+ const dependents = seeds.length
447
+ ? affected({ graph, seeds: seeds.map((id) => byId.get(id)), depth: REACH_DEPTH })
448
+ : [];
449
+ const reachedFiles = new Set(
450
+ dependents.filter((d) => d.node?.kind === "file").map((d) => d.id),
451
+ );
452
+ return {
453
+ ...row,
454
+ project,
455
+ changed: files.length,
456
+ inGraph: seeds.length,
457
+ reach: reachedFiles.size,
458
+ };
459
+ });
460
+ const updated = new Map(prs.items.map((p) => [`${p.repo}#${p.number}`, p.updatedAt ?? ""]));
461
+ rows.sort(
462
+ (a, b) =>
463
+ (b.reach ?? -1) - (a.reach ?? -1) ||
464
+ (updated.get(`${a.repo}#${a.number}`) < updated.get(`${b.repo}#${b.number}`) ? 1 : -1),
465
+ );
466
+ return { contractVersion: CONTRACT_VERSION, depth: REACH_DEPTH, items: rows };
467
+ }
468
+
352
469
  function stateOf(taskId) {
353
470
  const { found, records } = selectRuns({ taskId });
354
471
  if (!found || !records.length) throw new WorkError("no-run", "no such run");
@@ -449,11 +566,12 @@ async function main(argv) {
449
566
  let out;
450
567
  if (cmd === "maturity" && arg) out = await issueMaturity(arg);
451
568
  else if (cmd === "prs" && !arg) out = await pullRequests();
569
+ else if (cmd === "reach" && !arg) out = await prReach();
452
570
  else if (cmd === "verdict" && arg && (flag === undefined || flag === "--post")) {
453
571
  out = flag ? await postReviewVerdict(arg) : reviewVerdictPreview(arg);
454
572
  } else {
455
573
  process.stderr.write(
456
- "usage: work-items.mjs maturity <key> | prs | verdict <task-id> [--post]\n",
574
+ "usage: work-items.mjs maturity <key> | prs | reach | verdict <task-id> [--post]\n",
457
575
  );
458
576
  return 2;
459
577
  }