@mmerterden/multi-agent-pipeline 20.33.0 → 20.35.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 (390) hide show
  1. package/CHANGELOG.md +98 -1
  2. package/README.md +3 -1
  3. package/README.tr.md +2 -1
  4. package/docs/adr/0018-cross-repo-links-not-a-global-graph.md +43 -0
  5. package/docs/adr/README.md +2 -1
  6. package/docs/facts.json +5 -4
  7. package/docs/features.md +1 -1
  8. package/install/_dev-only-files.mjs +1 -0
  9. package/install/catalog-history.json +1 -1
  10. package/install/claude.mjs +24 -2
  11. package/install/templates/claude-hooks.json +34 -1
  12. package/manifest.json +393 -339
  13. package/package.json +3 -1
  14. package/pipeline/agents/bulk-reader.md +3 -0
  15. package/pipeline/agents/code-reviewer.md +9 -0
  16. package/pipeline/agents/explorer.md +4 -0
  17. package/pipeline/agents/plan-critic.md +4 -2
  18. package/pipeline/commands/multi-agent/SKILL.md +17 -136
  19. package/pipeline/commands/multi-agent/channels/SKILL.md +16 -298
  20. package/pipeline/commands/multi-agent/graph/SKILL.md +5 -2
  21. package/pipeline/commands/multi-agent/language/SKILL.md +3 -3
  22. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  23. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  24. package/pipeline/commands/multi-agent/resume/SKILL.md +8 -22
  25. package/pipeline/commands/multi-agent/review/SKILL.md +7 -131
  26. package/pipeline/commands/multi-agent/setup/SKILL.md +19 -224
  27. package/pipeline/contract/CHANGELOG.md +9 -0
  28. package/pipeline/contract/README.md +6 -2
  29. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  30. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  31. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  32. package/pipeline/contract/manifest.json +14 -1
  33. package/pipeline/contract/types/index.d.ts +24 -2
  34. package/pipeline/lib/json-file-lock.mjs +61 -15
  35. package/pipeline/lib/office-remote.mjs +228 -0
  36. package/pipeline/lib/qr-code.mjs +360 -0
  37. package/pipeline/lib/repo-hygiene.sh +1 -0
  38. package/pipeline/lib/run-completion.mjs +17 -0
  39. package/pipeline/multi-agent-refs/channels/generator.md +146 -0
  40. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  41. package/pipeline/multi-agent-refs/channels/menu.md +55 -0
  42. package/pipeline/multi-agent-refs/channels/multi-repo.md +6 -0
  43. package/pipeline/multi-agent-refs/channels/preview.md +25 -0
  44. package/pipeline/multi-agent-refs/channels/reference.md +98 -0
  45. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +1 -1
  46. package/pipeline/multi-agent-refs/features/code-graph.md +27 -0
  47. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  48. package/pipeline/multi-agent-refs/features/office-threat-model.md +2 -2
  49. package/pipeline/multi-agent-refs/features/plan-critic.md +5 -2
  50. package/pipeline/multi-agent-refs/features/review-delta.md +4 -1
  51. package/pipeline/multi-agent-refs/features/scope-check.md +25 -1
  52. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  53. package/pipeline/multi-agent-refs/keychain.md +7 -0
  54. package/pipeline/multi-agent-refs/knowledge.md +9 -0
  55. package/pipeline/multi-agent-refs/orchestrator/dispatcher.md +163 -0
  56. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  57. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +8 -10
  58. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +18 -20
  59. package/pipeline/multi-agent-refs/phases/phase-3-review.md +20 -14
  60. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +8 -7
  61. package/pipeline/multi-agent-refs/phases/phase-5-report.md +6 -3
  62. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  63. package/pipeline/multi-agent-refs/resume/details.md +35 -0
  64. package/pipeline/multi-agent-refs/review/inputs.md +87 -0
  65. package/pipeline/multi-agent-refs/review/module-guides.md +25 -0
  66. package/pipeline/multi-agent-refs/review/valven.md +42 -0
  67. package/pipeline/multi-agent-refs/rules.md +3 -2
  68. package/pipeline/multi-agent-refs/setup/accounts.md +27 -0
  69. package/pipeline/multi-agent-refs/setup/app-store-connect.md +5 -0
  70. package/pipeline/multi-agent-refs/setup/firebase.md +5 -0
  71. package/pipeline/multi-agent-refs/setup/google-play.md +4 -0
  72. package/pipeline/multi-agent-refs/setup/hooks-and-plugins.md +21 -0
  73. package/pipeline/multi-agent-refs/setup/language.md +46 -0
  74. package/pipeline/multi-agent-refs/setup/multi-repo-group.md +51 -0
  75. package/pipeline/multi-agent-refs/setup/summaries.md +63 -0
  76. package/pipeline/multi-agent-refs/setup/token-save-flow.md +51 -0
  77. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  78. package/pipeline/office/NOTICE.md +4 -1
  79. package/pipeline/office/assets/char_12.png +0 -0
  80. package/pipeline/office/assets/char_13.png +0 -0
  81. package/pipeline/office/assets/char_14.png +0 -0
  82. package/pipeline/office/assets/char_15.png +0 -0
  83. package/pipeline/office/assets/char_16.png +0 -0
  84. package/pipeline/office/assets/char_17.png +0 -0
  85. package/pipeline/office/assets/char_18.png +0 -0
  86. package/pipeline/office/assets/char_19.png +0 -0
  87. package/pipeline/office/assets/char_20.png +0 -0
  88. package/pipeline/office/assets/char_21.png +0 -0
  89. package/pipeline/office/assets/char_22.png +0 -0
  90. package/pipeline/office/assets/char_23.png +0 -0
  91. package/pipeline/office/assets/char_24.png +0 -0
  92. package/pipeline/office/assets/char_25.png +0 -0
  93. package/pipeline/office/assets/char_26.png +0 -0
  94. package/pipeline/office/assets/char_27.png +0 -0
  95. package/pipeline/office/assets/char_28.png +0 -0
  96. package/pipeline/office/assets/char_29.png +0 -0
  97. package/pipeline/office/assets/char_30.png +0 -0
  98. package/pipeline/office/index.html +304 -55
  99. package/pipeline/office/toolkit.json +1 -1
  100. package/pipeline/rules/response-shape.md +6 -7
  101. package/pipeline/schemas/agent-state.schema.json +36 -0
  102. package/pipeline/schemas/phases.json +4 -4
  103. package/pipeline/schemas/plan-critique.schema.json +9 -2
  104. package/pipeline/schemas/planning-output.schema.json +43 -2
  105. package/pipeline/schemas/pr-reach.schema.json +55 -0
  106. package/pipeline/schemas/prefs.schema.json +37 -8
  107. package/pipeline/schemas/reviewer-output.schema.json +20 -2
  108. package/pipeline/schemas/token-budget.json +4 -4
  109. package/pipeline/schemas/triage-output.schema.json +32 -3
  110. package/pipeline/schemas/unattended-policy.json +2 -1
  111. package/pipeline/scripts/_graph-analysis.mjs +270 -0
  112. package/pipeline/scripts/_guard_rules.py +449 -1
  113. package/pipeline/scripts/_memory-policy.mjs +165 -0
  114. package/pipeline/scripts/_secret-scan.mjs +105 -0
  115. package/pipeline/scripts/_test-gaming.mjs +36 -0
  116. package/pipeline/scripts/_unified-diff.mjs +2 -2
  117. package/pipeline/scripts/agent-guard.py +79 -19
  118. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  119. package/pipeline/scripts/answer-check.mjs +113 -0
  120. package/pipeline/scripts/answer-check.sh +20 -0
  121. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  122. package/pipeline/scripts/capture-flush.sh +12 -2
  123. package/pipeline/scripts/check-read-size.sh +9 -0
  124. package/pipeline/scripts/code-graph-rules/android.json +8 -3
  125. package/pipeline/scripts/contract-server.mjs +71 -16
  126. package/pipeline/scripts/debt-ledger.mjs +323 -0
  127. package/pipeline/scripts/file-context.mjs +261 -0
  128. package/pipeline/scripts/file-context.sh +20 -0
  129. package/pipeline/scripts/gate-ledger.mjs +144 -21
  130. package/pipeline/scripts/graph-links.mjs +230 -0
  131. package/pipeline/scripts/graph-query.mjs +133 -1
  132. package/pipeline/scripts/graph-report.mjs +65 -0
  133. package/pipeline/scripts/learn-from-transcripts.mjs +12 -48
  134. package/pipeline/scripts/learnings-ledger.mjs +143 -24
  135. package/pipeline/scripts/office-build.mjs +276 -5
  136. package/pipeline/scripts/office-control.mjs +70 -10
  137. package/pipeline/scripts/office-pair.mjs +407 -0
  138. package/pipeline/scripts/office-remote-client.mjs +463 -0
  139. package/pipeline/scripts/phase-tracker.sh +8 -0
  140. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  141. package/pipeline/scripts/phone-devices.mjs +9 -1
  142. package/pipeline/scripts/plan-critique-gate.mjs +11 -2
  143. package/pipeline/scripts/pre-commit-check.sh +28 -2
  144. package/pipeline/scripts/pre-push-check.sh +6 -12
  145. package/pipeline/scripts/rework-base.mjs +87 -0
  146. package/pipeline/scripts/rework-rulings.mjs +106 -0
  147. package/pipeline/scripts/run-commit-check.mjs +104 -0
  148. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  149. package/pipeline/scripts/run-scope-guard.sh +13 -1
  150. package/pipeline/scripts/scope-check-gate.mjs +213 -2
  151. package/pipeline/scripts/task-mirror.mjs +106 -15
  152. package/pipeline/scripts/task-mirror.sh +3 -2
  153. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  154. package/pipeline/scripts/triage-memory.mjs +64 -37
  155. package/pipeline/scripts/update-check.sh +7 -1
  156. package/pipeline/scripts/update-issue-progress.sh +73 -8
  157. package/pipeline/scripts/validate-planning.mjs +172 -4
  158. package/pipeline/scripts/validate-reviewer.mjs +20 -0
  159. package/pipeline/scripts/validate-triage.mjs +149 -4
  160. package/pipeline/scripts/work-items.mjs +119 -1
  161. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  162. package/pipeline/scripts/worktree-prepare.sh +40 -4
  163. package/pipeline/scripts/write-state.mjs +67 -24
  164. package/pipeline/skills/.skill-manifest.json +9 -9
  165. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +5 -5
  166. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  167. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  168. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +5 -2
  169. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +4 -4
  170. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  171. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +2 -0
  172. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +4 -4
  173. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  174. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  175. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  176. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  177. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  178. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  179. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  180. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  181. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  182. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  183. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  184. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  185. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  186. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  187. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  188. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  189. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  190. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  191. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  192. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  193. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  194. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  195. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  196. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  197. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  198. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  199. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  200. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  201. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  202. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  203. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  204. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  205. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  206. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  207. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  208. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  209. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  210. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  211. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  212. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  213. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  214. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  215. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  216. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  217. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  218. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  219. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  220. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  221. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  222. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  223. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  224. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  225. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  226. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  227. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  228. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  229. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  230. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  231. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  232. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  233. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  234. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  235. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  236. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  237. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  238. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  239. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  240. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  241. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  242. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  243. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  244. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  245. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  246. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  247. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  248. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  249. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  250. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  251. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  252. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  253. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  254. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  255. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  256. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  257. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  258. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  259. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  260. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  261. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  262. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  263. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  264. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  265. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  266. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  267. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  268. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  269. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  270. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  271. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  272. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  273. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  274. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  275. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  276. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  277. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  278. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  279. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  280. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  281. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  282. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  283. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  284. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  285. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  286. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  287. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  288. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  289. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  290. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  291. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  292. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  293. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  294. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  295. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  296. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  297. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  298. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  299. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  300. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  301. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  302. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  303. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  304. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  305. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  306. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  307. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  308. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  309. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  310. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  311. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  312. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  313. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  314. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  315. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  316. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  317. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  318. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  319. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  320. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  321. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  322. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  323. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  324. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  325. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  326. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  327. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  328. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  329. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  330. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  331. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  332. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  333. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  334. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  335. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  336. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  337. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  338. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  339. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  340. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  341. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  342. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  343. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  344. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  345. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  346. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  347. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  348. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  349. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  350. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  351. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  352. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  353. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  354. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  355. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  356. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  357. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  358. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  359. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  360. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  361. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  362. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  363. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  364. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  365. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  366. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  367. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  368. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  369. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  370. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  371. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  372. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  373. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  374. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  375. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  376. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  377. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  378. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  379. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  380. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  381. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  382. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  383. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  384. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  385. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  386. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  387. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  388. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  389. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  390. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
@@ -10,21 +10,38 @@
10
10
  * Usage:
11
11
  * git diff --name-only <base>...HEAD | node scope-check-gate.mjs --check .pipeline/scope-check.json --diff-files -
12
12
  * node scope-check-gate.mjs --check scope-check.json --diff-files files.txt [--advisory]
13
+ * [--patch <unified diff> | --repo <dir> --patch-range <a>...<b>]
14
+ * [--rejected <triage-output.json> --repo <dir> --rework-base <rev>]
13
15
  *
14
- * Output (stdout, JSON): {ok, justified, unjustified[], unlisted[], notDone}
16
+ * Output (stdout, JSON): {ok, justified, unjustified[], unlisted[], notDone, formatOnly[]}
15
17
  * unjustified = in the diff, missing from files[] (or with an empty reason)
16
18
  * unlisted = in files[], not in the diff (stale entry; reported, never fatal)
19
+ * formatOnly = with --patch, hunks whose removed and added lines differ only
20
+ * in whitespace: a reformat of lines the task did not need to
21
+ * touch. Reported, never fatal. --patch-range reads that diff
22
+ * with git itself, its output format pinned, so a caller needs
23
+ * no file or process substitution for it.
24
+ * rejectedTouched = with --rejected and --rework-base, findings triage
25
+ * rejected whose lines a rework hunk (rework base against the
26
+ * working tree) changed: Phase 2 acts on accepted findings
27
+ * only, so a rework that edits a rejected finding's lines is
28
+ * "fixing" a finding triage ruled out. Reported, never fatal.
17
29
  *
18
30
  * Exit codes: 0 every diff file justified (or --advisory), 1 otherwise or on
19
31
  * unreadable input, 64 usage.
20
32
  */
21
33
 
34
+ import { execFileSync } from "node:child_process";
22
35
  import { readFileSync } from "node:fs";
23
36
 
24
37
  import { normalizeFile } from "./_fingerprint.mjs";
38
+ import { parseUnifiedDiff } from "./_unified-diff.mjs";
25
39
 
26
40
  const USAGE =
27
- "usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory]";
41
+ "usage: scope-check-gate.mjs --check <scope-check.json> --diff-files <path|-> [--advisory] [--patch <diff> | --repo <dir> --patch-range <a>...<b>] [--rejected <triage.json> --repo <dir> --rework-base <rev>]";
42
+
43
+ const REV = /^[A-Za-z0-9_./~^@{}-]+$/;
44
+ const NEAR = 2;
28
45
 
29
46
  function parseArgs(argv) {
30
47
  const opts = {};
@@ -33,15 +50,188 @@ function parseArgs(argv) {
33
50
  if (a === "--check") opts.check = argv[++i];
34
51
  else if (a === "--diff-files") opts.diffFiles = argv[++i];
35
52
  else if (a === "--advisory") opts.advisory = true;
53
+ else if (a === "--patch") opts.patch = argv[++i];
54
+ else if (a === "--patch-range") opts.patchRange = argv[++i];
55
+ else if (a === "--repo") opts.repo = argv[++i];
56
+ else if (a === "--rejected") opts.rejected = argv[++i];
57
+ else if (a === "--rework-base") opts.reworkBase = argv[++i];
36
58
  else return null;
37
59
  }
60
+ for (const rev of [opts.patchRange, opts.reworkBase]) {
61
+ if (rev !== undefined && (!REV.test(rev) || rev.startsWith("-"))) return null;
62
+ }
63
+ if (Boolean(opts.rejected) !== Boolean(opts.reworkBase)) return null;
38
64
  return opts.check && opts.diffFiles ? opts : null;
39
65
  }
40
66
 
67
+ /** The diff of a revision range (or a revision against the working tree), in the one shape the hunk readers take. */
68
+ function rangeDiff(repo, range, unified = []) {
69
+ return execFileSync(
70
+ "git",
71
+ [
72
+ "-C",
73
+ repo || ".",
74
+ "-c",
75
+ "core.quotePath=false",
76
+ "diff",
77
+ "--no-ext-diff",
78
+ "--no-color",
79
+ "--no-textconv",
80
+ "--src-prefix=a/",
81
+ "--dst-prefix=b/",
82
+ ...unified,
83
+ range,
84
+ "--",
85
+ ],
86
+ { encoding: "utf8", maxBuffer: 64 * 1024 * 1024, timeout: 30_000 },
87
+ );
88
+ }
89
+
41
90
  function readText(path) {
42
91
  return path === "-" ? readFileSync(0, "utf-8") : readFileSync(path, "utf-8");
43
92
  }
44
93
 
94
+ const INDENT_SENSITIVE = /(\.(py|pyi|ya?ml|coffee|haml|slim|pug)|(^|\/)(GNU)?[Mm]akefile)$/;
95
+
96
+ const LINE_COMMENT = /(\.(py|pyi|ya?ml|sh|bash|zsh|rb|toml|coffee)|(^|\/)(GNU)?[Mm]akefile)$/;
97
+ // Languages where a space next to a bracket, comma, semicolon or colon never
98
+ // carries meaning. YAML, shell, Makefile and CSS are left out on purpose.
99
+ const TIGHT_LANGS =
100
+ /\.(js|jsx|mjs|cjs|ts|tsx|java|go|cs|swift|kt|kts|json|rs|dart|scala|py|pyi|c|h|cc|cpp|hpp|m|mm)$/;
101
+ const LEADING_OPERATOR_LANGS = /\.(kt|kts|swift|go|scala)$/;
102
+ const BREAK_KEEPERS = /\b(return|throw|yield|break|continue)$/;
103
+ const LEADING_OPERATOR = /^[+\-*/%&|^.?]/;
104
+ const TIGHT = /\s*([(){}[\],;:])\s*/g;
105
+
106
+ /** One line with string literals and comments verbatim and every other whitespace run as one space. */
107
+ function foldLine(line, hashComments) {
108
+ const out = [];
109
+ let quote = null;
110
+ let gap = false;
111
+ for (let i = 0; i < line.length; i++) {
112
+ const c = line[i];
113
+ if (quote) {
114
+ out.push(c);
115
+ if (c === "\\") out.push(line[++i] ?? "");
116
+ else if (c === quote) quote = null;
117
+ continue;
118
+ }
119
+ if (c === " " || c === "\t" || c === "\r") {
120
+ gap = true;
121
+ continue;
122
+ }
123
+ if (gap && out.length) out.push(" ");
124
+ gap = false;
125
+ if ((c === "/" && line[i + 1] === "/") || (hashComments && c === "#")) {
126
+ out.push(line.slice(i));
127
+ break;
128
+ }
129
+ if (c === '"' || c === "'" || c === "`") quote = c;
130
+ out.push(c);
131
+ }
132
+ return out.join("");
133
+ }
134
+
135
+ /**
136
+ * The layout-free form of some lines. Each line is folded on its own (linear
137
+ * in the hunk), then the lines are joined by a space, except that a break
138
+ * after return, throw, yield, break or continue, and in Kotlin, Swift, Go or
139
+ * Scala a break before a leading operator, stays a break. Space next to
140
+ * brackets, commas, semicolons and colons is dropped only in TIGHT_LANGS, and
141
+ * never in a hunk that holds a C preprocessor line.
142
+ */
143
+ function outsideStrings(text, file = "") {
144
+ const hashComments = LINE_COMMENT.test(file);
145
+ const lines = text.split("\n").map((l) => foldLine(l, hashComments));
146
+ let joined = "";
147
+ lines.forEach((line, i) => {
148
+ if (i === 0) {
149
+ joined = line;
150
+ return;
151
+ }
152
+ const keepBreak =
153
+ BREAK_KEEPERS.test(lines[i - 1]) ||
154
+ (LEADING_OPERATOR_LANGS.test(file) && LEADING_OPERATOR.test(line));
155
+ joined += (keepBreak ? "\u23ce" : " ") + line;
156
+ });
157
+ const preprocessor = lines.some((l) => l.startsWith("#")) && !hashComments;
158
+ const tight = TIGHT_LANGS.test(file) && !preprocessor;
159
+ return (tight ? joined.replace(TIGHT, "$1") : joined).trim();
160
+ }
161
+
162
+ const indentOf = (line) => /^[ \t]*/.exec(line)[0];
163
+
164
+ /** True when the removed and added lines differ only in layout. */
165
+ function layoutOnly(file, removed, added) {
166
+ if (!removed.length || !added.length) return false;
167
+ if (INDENT_SENSITIVE.test(file)) {
168
+ if (removed.length !== added.length) return false;
169
+ return removed.every(
170
+ (r, i) =>
171
+ indentOf(r.text) === indentOf(added[i].text) &&
172
+ outsideStrings(r.text, file) === outsideStrings(added[i].text, file),
173
+ );
174
+ }
175
+ const joined = (lines) => outsideStrings(lines.map((d) => d.text).join("\n"), file);
176
+ return joined(removed) === joined(added);
177
+ }
178
+
179
+ /**
180
+ * Hunks of a unified diff whose change is layout only: whitespace outside
181
+ * string literals, and in indentation-sensitive files never a leading indent.
182
+ */
183
+ export function formatOnlyHunks(patch) {
184
+ const found = [];
185
+ for (const [file, diff] of parseUnifiedDiff(patch)) {
186
+ for (const h of diff.hunks) {
187
+ if (!h.removed.length && !h.added.length) continue;
188
+ if (layoutOnly(file, h.removed, h.added)) {
189
+ found.push({ file: normalizeFile(file), hunk: h.header });
190
+ }
191
+ }
192
+ }
193
+ return found;
194
+ }
195
+
196
+ /**
197
+ * Rejected findings whose line a hunk of the rework diff changed. The diff
198
+ * runs from the reviewed tree, so a finding's line is read on the hunk's old
199
+ * side: a hunk that removes or rewrites lines within NEAR of it, or inserts
200
+ * next to it, touches it.
201
+ */
202
+ export function rejectedTouched(triage, patch) {
203
+ const rejected = (Array.isArray(triage?.rejected) ? triage.rejected : [])
204
+ .map((r) => r?.finding)
205
+ .filter((f) => f && typeof f.file === "string" && Number.isInteger(f.line) && f.line > 0);
206
+ if (!rejected.length) return [];
207
+ const ranges = new Map();
208
+ for (const [file, diff] of parseUnifiedDiff(patch)) {
209
+ const list = [];
210
+ for (const h of diff.hunks) {
211
+ const m = /^@@ -(\d+)(?:,(\d+))?/.exec(h.header);
212
+ if (!m) continue;
213
+ const start = Number(m[1]);
214
+ const count = m[2] === undefined ? 1 : Number(m[2]);
215
+ list.push({ from: start - NEAR, to: start + Math.max(count, 1) - 1 + NEAR, hunk: h.header });
216
+ }
217
+ ranges.set(normalizeFile(file), list);
218
+ }
219
+ const out = [];
220
+ for (const f of rejected) {
221
+ const hit = (ranges.get(normalizeFile(f.file)) || []).find(
222
+ (r) => f.line >= r.from && f.line <= r.to,
223
+ );
224
+ if (hit)
225
+ out.push({
226
+ file: normalizeFile(f.file),
227
+ line: f.line,
228
+ issue: f.issue ?? null,
229
+ hunk: hit.hunk,
230
+ });
231
+ }
232
+ return out;
233
+ }
234
+
45
235
  export function evaluate(check, diffFiles) {
46
236
  const files = Array.isArray(check?.files) ? check.files : [];
47
237
  const reasons = new Map();
@@ -81,6 +271,27 @@ function main() {
81
271
  return fail;
82
272
  }
83
273
  const result = evaluate(check, diffFiles);
274
+ if (opts.patch || opts.patchRange) {
275
+ try {
276
+ const patch = opts.patch ? readText(opts.patch) : rangeDiff(opts.repo, opts.patchRange);
277
+ result.formatOnly = formatOnlyHunks(patch);
278
+ } catch (err) {
279
+ result.formatOnly = [];
280
+ result.patchError = err.message;
281
+ }
282
+ }
283
+ if (opts.rejected) {
284
+ try {
285
+ const triage = JSON.parse(readText(opts.rejected));
286
+ result.rejectedTouched = rejectedTouched(
287
+ triage,
288
+ rangeDiff(opts.repo, opts.reworkBase, ["-U0"]),
289
+ );
290
+ } catch (err) {
291
+ result.rejectedTouched = [];
292
+ result.rejectedError = err.message;
293
+ }
294
+ }
84
295
  process.stdout.write(JSON.stringify(result) + "\n");
85
296
  return result.ok ? 0 : fail;
86
297
  }
@@ -21,12 +21,21 @@
21
21
  * is left alone.
22
22
  * The task goes under the phase in progress when it was created.
23
23
  *
24
- * Exits 0 on every path and prints nothing: a mirror, not a gate. A failure is
25
- * written to the hook failure log.
24
+ * Phase tiles: a TaskCreate of "Phase N ..." records its id as the phase's
25
+ * `tasklist_id` (what :resume rebuilds the widget from), replacing the id a
26
+ * previous session left. The widget renders tiles in creation order, so a
27
+ * tile created after a higher-numbered one in the same session shows out of
28
+ * order (tracker-contract.md, "TaskCreate ordering"); the hook says so to the
29
+ * model as additionalContext. Which tiles this session created is kept in
30
+ * `tile-session.json` beside the tracker and starts over with a new session,
31
+ * so tiles a resume recreates are not compared with the old session's.
32
+ *
33
+ * Exits 0 on every path, blocks nothing, and prints only that ordering note. A
34
+ * failure is written to the hook failure log.
26
35
  */
27
36
 
28
37
  import { spawnSync } from "node:child_process";
29
- import { readFileSync } from "node:fs";
38
+ import { readFileSync, renameSync, writeFileSync } from "node:fs";
30
39
  import { dirname, join } from "node:path";
31
40
  import { fileURLToPath } from "node:url";
32
41
 
@@ -37,6 +46,7 @@ const HOOK = "task-mirror";
37
46
  const SESSION_BOUND = new Set(["session", "host-session"]);
38
47
  const STATUSES = new Set(["pending", "in_progress", "completed"]);
39
48
  const TILE = /^Phase \d+\b/;
49
+ const TILE_NO = /^Phase (\d+)\b/;
40
50
  const MAX_INPUT = 256 * 1024;
41
51
  const HERE = dirname(fileURLToPath(import.meta.url));
42
52
 
@@ -102,6 +112,43 @@ export function mirrorArgs(payload, tracker) {
102
112
  return null;
103
113
  }
104
114
 
115
+ /**
116
+ * For a phase tile's TaskCreate: the phase it is, the id it got, and the
117
+ * higher-numbered phases whose tiles already exist. Null for anything else.
118
+ */
119
+ export function tileOf(payload, tracker) {
120
+ if (payload?.tool_name !== "TaskCreate") return null;
121
+ const m = TILE_NO.exec(String(payload.tool_input?.subject ?? "").trim());
122
+ if (!m) return null;
123
+ const n = Number(m[1]);
124
+ const phases = Array.isArray(tracker?.phases) ? tracker.phases : [];
125
+ const phase = phases.find((p) => Number(p.id) === n) ?? null;
126
+ return {
127
+ phase,
128
+ id: createdId(payload.tool_response),
129
+ recorded: phase ? String(phase.meta?.tasklist_id ?? "") : "",
130
+ };
131
+ }
132
+
133
+ /**
134
+ * This session's tile record after creating phase `n`, and the phases whose
135
+ * tiles this session created before it with a higher number.
136
+ */
137
+ export function sessionTiles(previous, sessionId, n) {
138
+ if (!sessionId) return { record: previous, after: [] };
139
+ const same =
140
+ previous &&
141
+ previous.session === sessionId &&
142
+ previous.tiles &&
143
+ typeof previous.tiles === "object";
144
+ const tiles = same ? { ...previous.tiles } : {};
145
+ const after = Object.keys(tiles)
146
+ .filter((p) => Number(p) > n)
147
+ .sort((a, b) => Number(a) - Number(b));
148
+ tiles[String(n)] = true;
149
+ return { record: { session: sessionId, tiles }, after };
150
+ }
151
+
105
152
  export function mirror(payload, env = process.env) {
106
153
  if (!payload || !["TaskCreate", "TaskUpdate"].includes(payload.tool_name)) return null;
107
154
  const run = findActiveRun({
@@ -119,26 +166,70 @@ export function mirror(payload, env = process.env) {
119
166
  } catch {
120
167
  return null;
121
168
  }
169
+ const tracked = (args) => {
170
+ const r = spawnSync("bash", [join(HERE, "phase-tracker.sh"), ...args], {
171
+ env: {
172
+ ...env,
173
+ MULTI_AGENT_TASK_ID: run.state.taskId,
174
+ TRACKER_FILE: trackerPath,
175
+ TRACKER_QUIET: "1",
176
+ },
177
+ encoding: "utf8",
178
+ timeout: 8000,
179
+ });
180
+ if (r.status !== 0) logHookFailure(HOOK, `phase-tracker ${args[0]} exited ${r.status}`, env);
181
+ };
182
+ const tile = tileOf(payload, tracker);
183
+ if (tile) {
184
+ if (!tile.phase) return null;
185
+ if (tile.id && tile.recorded !== tile.id)
186
+ tracked(["meta", String(tile.phase.id), "tasklist_id", tile.id]);
187
+ const sidecar = join(runDirOf(run.statePath), "tile-session.json");
188
+ let previous;
189
+ try {
190
+ previous = JSON.parse(readFileSync(sidecar, "utf8"));
191
+ } catch {
192
+ previous = null;
193
+ }
194
+ const { record, after } = sessionTiles(
195
+ previous,
196
+ String(payload.session_id ?? ""),
197
+ Number(tile.phase.id),
198
+ );
199
+ if (record)
200
+ try {
201
+ const tmp = `${sidecar}.${process.pid}.tmp`;
202
+ writeFileSync(tmp, JSON.stringify(record));
203
+ renameSync(tmp, sidecar);
204
+ } catch (e) {
205
+ logHookFailure(HOOK, `tile record: ${String(e?.message || e).slice(0, 120)}`, env);
206
+ }
207
+ return after.length
208
+ ? {
209
+ note:
210
+ `Phase ${tile.phase.id}'s tile was created after the tiles of phase ${after.join(", ")}, ` +
211
+ "so the task widget shows it out of order. Tiles are created in phase-number order before any " +
212
+ 'TaskUpdate (tracker-contract.md, "TaskCreate ordering").',
213
+ }
214
+ : null;
215
+ }
122
216
  const args = mirrorArgs(payload, tracker);
123
217
  if (!args) return null;
124
- const r = spawnSync("bash", [join(HERE, "phase-tracker.sh"), ...args], {
125
- env: {
126
- ...env,
127
- MULTI_AGENT_TASK_ID: run.state.taskId,
128
- TRACKER_FILE: trackerPath,
129
- TRACKER_QUIET: "1",
130
- },
131
- encoding: "utf8",
132
- timeout: 8000,
133
- });
134
- if (r.status !== 0) logHookFailure(HOOK, `phase-tracker task exited ${r.status}`, env);
218
+ tracked(args);
135
219
  return args;
136
220
  }
137
221
 
138
222
  if (invokedDirectly(import.meta.url)) {
139
223
  try {
140
224
  const raw = readFileSync(0, "utf8").slice(0, MAX_INPUT);
141
- mirror(JSON.parse(raw));
225
+ const out = mirror(JSON.parse(raw));
226
+ if (out?.note) {
227
+ process.stdout.write(
228
+ JSON.stringify({
229
+ hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: out.note },
230
+ }) + "\n",
231
+ );
232
+ }
142
233
  } catch (e) {
143
234
  try {
144
235
  logHookFailure(HOOK, String(e?.message || e).slice(0, 200));
@@ -6,8 +6,9 @@
6
6
  # logic is in task-mirror.mjs; this wrapper exists because every hook the
7
7
  # pipeline registers is a `bash "$HOME/.<host>/scripts/<name>"` command.
8
8
  #
9
- # Exits 0 on every path and prints nothing: a missing node or any internal
10
- # error means no mirror, never a blocked tool call.
9
+ # Exits 0 on every path; its only output is a tile-ordering note for the
10
+ # model. A missing node or any internal error means no mirror, never a blocked
11
+ # tool call.
11
12
 
12
13
  set -u
13
14
 
@@ -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());