@mmerterden/multi-agent-pipeline 20.34.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 (321) hide show
  1. package/CHANGELOG.md +55 -1
  2. package/README.md +1 -0
  3. package/docs/facts.json +2 -2
  4. package/install/catalog-history.json +1 -1
  5. package/install/claude.mjs +16 -1
  6. package/install/templates/claude-hooks.json +23 -1
  7. package/manifest.json +324 -293
  8. package/package.json +1 -1
  9. package/pipeline/agents/bulk-reader.md +3 -0
  10. package/pipeline/commands/multi-agent/channels/SKILL.md +1 -1
  11. package/pipeline/commands/multi-agent/office-on/SKILL.md +7 -3
  12. package/pipeline/commands/multi-agent/office-status/SKILL.md +3 -2
  13. package/pipeline/contract/CHANGELOG.md +9 -0
  14. package/pipeline/contract/README.md +1 -0
  15. package/pipeline/contract/fixtures/error-invalid-request.json +1 -1
  16. package/pipeline/contract/fixtures/error-unauthorized.json +1 -1
  17. package/pipeline/contract/fixtures/error-unsigned.json +1 -1
  18. package/pipeline/contract/manifest.json +14 -1
  19. package/pipeline/contract/types/index.d.ts +24 -2
  20. package/pipeline/lib/json-file-lock.mjs +61 -15
  21. package/pipeline/lib/office-remote.mjs +228 -0
  22. package/pipeline/lib/qr-code.mjs +360 -0
  23. package/pipeline/lib/repo-hygiene.sh +1 -0
  24. package/pipeline/lib/run-completion.mjs +17 -0
  25. package/pipeline/multi-agent-refs/channels/issue-comment.md +1 -1
  26. package/pipeline/multi-agent-refs/features/office-remote-protocol.md +166 -0
  27. package/pipeline/multi-agent-refs/features/office-threat-model.md +1 -1
  28. package/pipeline/multi-agent-refs/features/scope-check.md +16 -1
  29. package/pipeline/multi-agent-refs/features/verify-by-test.md +10 -0
  30. package/pipeline/multi-agent-refs/keychain.md +7 -0
  31. package/pipeline/multi-agent-refs/phases/phase-0-init.md +6 -2
  32. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +4 -6
  33. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +9 -12
  34. package/pipeline/multi-agent-refs/phases/phase-3-review.md +9 -8
  35. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +1 -4
  36. package/pipeline/multi-agent-refs/rules.md +3 -2
  37. package/pipeline/multi-agent-refs/tracker-contract.md +1 -1
  38. package/pipeline/office/NOTICE.md +4 -1
  39. package/pipeline/office/assets/char_12.png +0 -0
  40. package/pipeline/office/assets/char_13.png +0 -0
  41. package/pipeline/office/assets/char_14.png +0 -0
  42. package/pipeline/office/assets/char_15.png +0 -0
  43. package/pipeline/office/assets/char_16.png +0 -0
  44. package/pipeline/office/assets/char_17.png +0 -0
  45. package/pipeline/office/assets/char_18.png +0 -0
  46. package/pipeline/office/assets/char_19.png +0 -0
  47. package/pipeline/office/assets/char_20.png +0 -0
  48. package/pipeline/office/assets/char_21.png +0 -0
  49. package/pipeline/office/assets/char_22.png +0 -0
  50. package/pipeline/office/assets/char_23.png +0 -0
  51. package/pipeline/office/assets/char_24.png +0 -0
  52. package/pipeline/office/assets/char_25.png +0 -0
  53. package/pipeline/office/assets/char_26.png +0 -0
  54. package/pipeline/office/assets/char_27.png +0 -0
  55. package/pipeline/office/assets/char_28.png +0 -0
  56. package/pipeline/office/assets/char_29.png +0 -0
  57. package/pipeline/office/assets/char_30.png +0 -0
  58. package/pipeline/office/index.html +293 -49
  59. package/pipeline/schemas/agent-state.schema.json +18 -0
  60. package/pipeline/schemas/planning-output.schema.json +18 -0
  61. package/pipeline/schemas/pr-reach.schema.json +55 -0
  62. package/pipeline/schemas/prefs.schema.json +13 -7
  63. package/pipeline/schemas/triage-output.schema.json +12 -1
  64. package/pipeline/schemas/unattended-policy.json +2 -1
  65. package/pipeline/scripts/_guard_rules.py +449 -1
  66. package/pipeline/scripts/_test-gaming.mjs +36 -0
  67. package/pipeline/scripts/agent-guard.py +79 -19
  68. package/pipeline/scripts/anonymize-findings.mjs +99 -0
  69. package/pipeline/scripts/answer-check.mjs +113 -0
  70. package/pipeline/scripts/answer-check.sh +20 -0
  71. package/pipeline/scripts/autopilot-publish.mjs +9 -2
  72. package/pipeline/scripts/contract-server.mjs +35 -1
  73. package/pipeline/scripts/gate-ledger.mjs +144 -21
  74. package/pipeline/scripts/office-build.mjs +276 -5
  75. package/pipeline/scripts/office-control.mjs +70 -10
  76. package/pipeline/scripts/office-pair.mjs +407 -0
  77. package/pipeline/scripts/office-remote-client.mjs +463 -0
  78. package/pipeline/scripts/phase-tracker.sh +8 -0
  79. package/pipeline/scripts/phase0-exit-gate.mjs +40 -37
  80. package/pipeline/scripts/phone-devices.mjs +9 -1
  81. package/pipeline/scripts/pre-commit-check.sh +28 -2
  82. package/pipeline/scripts/pre-push-check.sh +6 -12
  83. package/pipeline/scripts/rework-base.mjs +87 -0
  84. package/pipeline/scripts/run-commit-check.mjs +104 -0
  85. package/pipeline/scripts/run-scope-guard.mjs +58 -3
  86. package/pipeline/scripts/run-scope-guard.sh +13 -1
  87. package/pipeline/scripts/scope-check-gate.mjs +70 -6
  88. package/pipeline/scripts/task-mirror.mjs +106 -15
  89. package/pipeline/scripts/task-mirror.sh +3 -2
  90. package/pipeline/scripts/test-stability-gate.mjs +112 -0
  91. package/pipeline/scripts/update-check.sh +7 -1
  92. package/pipeline/scripts/update-issue-progress.sh +73 -8
  93. package/pipeline/scripts/validate-planning.mjs +105 -1
  94. package/pipeline/scripts/validate-triage.mjs +42 -2
  95. package/pipeline/scripts/work-items.mjs +119 -1
  96. package/pipeline/scripts/worktree-prepare-all.mjs +200 -0
  97. package/pipeline/scripts/worktree-prepare.sh +40 -4
  98. package/pipeline/scripts/write-state.mjs +67 -24
  99. package/pipeline/skills/.skill-manifest.json +5 -5
  100. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +5 -5
  101. package/pipeline/skills/shared/core/google-play-compliance/references/rules.yml +1 -1
  102. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  103. package/pipeline/skills/shared/core/multi-agent-office-status/SKILL.md +2 -2
  104. package/pipeline/skills/shared/external/alarmkit/references/alarmkit-patterns.md +14 -0
  105. package/pipeline/skills/shared/external/android-architecture/references/patterns.md +6 -0
  106. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +10 -0
  107. package/pipeline/skills/shared/external/android-datastore/references/patterns.md +11 -0
  108. package/pipeline/skills/shared/external/android-design-tokens-codegen/references/patterns.md +8 -0
  109. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +6 -0
  110. package/pipeline/skills/shared/external/android-mvi-viewmodel/references/patterns.md +10 -0
  111. package/pipeline/skills/shared/external/android-performance/references/patterns.md +13 -0
  112. package/pipeline/skills/shared/external/android-security/references/patterns.md +16 -0
  113. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +9 -0
  114. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +12 -0
  115. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +13 -0
  116. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +12 -0
  117. package/pipeline/skills/shared/external/app-clips/references/data-handoff-notifications-location.md +9 -0
  118. package/pipeline/skills/shared/external/app-clips/references/routing-and-experiences.md +8 -0
  119. package/pipeline/skills/shared/external/app-clips/references/size-capabilities-and-promotion.md +10 -0
  120. package/pipeline/skills/shared/external/app-intents/references/appintents-advanced.md +21 -0
  121. package/pipeline/skills/shared/external/app-store-optimization/references/keyword-research-methodology.md +9 -0
  122. package/pipeline/skills/shared/external/app-store-optimization/references/product-page-variants.md +7 -0
  123. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -2
  124. package/pipeline/skills/shared/external/app-store-review/references/privacy-manifest.md +10 -0
  125. package/pipeline/skills/shared/external/app-store-review/references/review-checklists.md +10 -1
  126. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-conversion.md +22 -0
  127. package/pipeline/skills/shared/external/apple-on-device-ai/references/coreml-optimization.md +17 -0
  128. package/pipeline/skills/shared/external/apple-on-device-ai/references/foundation-models.md +22 -0
  129. package/pipeline/skills/shared/external/apple-on-device-ai/references/mlx-swift.md +17 -0
  130. package/pipeline/skills/shared/external/authentication/references/keychain-biometric.md +9 -0
  131. package/pipeline/skills/shared/external/authentication/references/passkeys.md +11 -0
  132. package/pipeline/skills/shared/external/avkit/references/avkit-patterns.md +13 -0
  133. package/pipeline/skills/shared/external/background-processing/references/background-task-patterns.md +9 -0
  134. package/pipeline/skills/shared/external/callkit-voip/references/callkit-patterns.md +10 -0
  135. package/pipeline/skills/shared/external/cloudkit-sync/references/cloudkit-patterns.md +15 -0
  136. package/pipeline/skills/shared/external/compose-components/references/patterns.md +7 -0
  137. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +7 -0
  138. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +15 -0
  139. package/pipeline/skills/shared/external/contacts-framework/references/contacts-patterns.md +9 -0
  140. package/pipeline/skills/shared/external/core-bluetooth/references/ble-patterns.md +11 -0
  141. package/pipeline/skills/shared/external/core-motion/references/motion-patterns.md +10 -0
  142. package/pipeline/skills/shared/external/core-nfc/references/nfc-patterns.md +13 -0
  143. package/pipeline/skills/shared/external/coreml/references/coreml-swift-integration.md +20 -0
  144. package/pipeline/skills/shared/external/cryptokit/references/cryptokit-patterns.md +16 -0
  145. package/pipeline/skills/shared/external/debugging-instruments/references/instruments-guide.md +16 -0
  146. package/pipeline/skills/shared/external/debugging-instruments/references/lldb-patterns.md +14 -0
  147. package/pipeline/skills/shared/external/device-integrity/references/device-integrity-patterns.md +13 -0
  148. package/pipeline/skills/shared/external/energykit/references/energykit-patterns.md +10 -0
  149. package/pipeline/skills/shared/external/eventkit-calendar/references/eventkit-patterns.md +10 -0
  150. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +10 -0
  151. package/pipeline/skills/shared/external/gradle-kotlin-dsl/references/patterns.md +17 -0
  152. package/pipeline/skills/shared/external/healthkit/references/healthkit-patterns.md +12 -0
  153. package/pipeline/skills/shared/external/hig-app-icons/references/icon-workflow.md +10 -0
  154. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +10 -0
  155. package/pipeline/skills/shared/external/hig-components-layout/references/bars-and-tabs.md +11 -0
  156. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +13 -0
  157. package/pipeline/skills/shared/external/hig-foundations/references/type-color-spacing.md +13 -0
  158. package/pipeline/skills/shared/external/hig-patterns/references/onboarding-and-states.md +11 -0
  159. package/pipeline/skills/shared/external/hig-platforms/references/ipad-layout.md +12 -0
  160. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +16 -0
  161. package/pipeline/skills/shared/external/homekit-matter/references/matter-commissioning.md +9 -0
  162. package/pipeline/skills/shared/external/ios-accessibility/references/a11y-patterns.md +18 -0
  163. package/pipeline/skills/shared/external/ios-accessibility/references/large-content-and-appearance.md +9 -0
  164. package/pipeline/skills/shared/external/ios-accessibility/references/media-accessibility.md +9 -0
  165. package/pipeline/skills/shared/external/ios-accessibility/references/nutrition-labels.md +7 -0
  166. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +13 -0
  167. package/pipeline/skills/shared/external/ios-design-review/references/appearance-matrix.md +9 -0
  168. package/pipeline/skills/shared/external/ios-design-review/references/checklist.md +15 -0
  169. package/pipeline/skills/shared/external/ios-haptics/references/core-haptics.md +11 -0
  170. package/pipeline/skills/shared/external/ios-iphone-duo/references/api-map.md +11 -0
  171. package/pipeline/skills/shared/external/ios-iphone-duo/references/fixed-assumptions.md +15 -0
  172. package/pipeline/skills/shared/external/ios-iphone-duo/references/poses-and-panes.md +11 -0
  173. package/pipeline/skills/shared/external/ios-localization/references/formatstyle-locale.md +18 -0
  174. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +19 -0
  175. package/pipeline/skills/shared/external/ios-networking/references/background-websocket.md +15 -0
  176. package/pipeline/skills/shared/external/ios-networking/references/file-storage-patterns.md +7 -0
  177. package/pipeline/skills/shared/external/ios-networking/references/lightweight-clients.md +8 -0
  178. package/pipeline/skills/shared/external/ios-networking/references/network-framework.md +12 -0
  179. package/pipeline/skills/shared/external/ios-networking/references/urlsession-patterns.md +17 -0
  180. package/pipeline/skills/shared/external/ios-security/references/app-review-guidelines.md +8 -0
  181. package/pipeline/skills/shared/external/ios-security/references/cryptokit-advanced.md +14 -0
  182. package/pipeline/skills/shared/external/ios-security/references/file-storage-patterns.md +8 -0
  183. package/pipeline/skills/shared/external/ios-security/references/privacy-manifest.md +9 -0
  184. package/pipeline/skills/shared/external/ios-sf-symbols/references/effects-and-custom-symbols.md +9 -0
  185. package/pipeline/skills/shared/external/ios-simulator/references/simctl-commands.md +13 -0
  186. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +8 -0
  187. package/pipeline/skills/shared/external/live-activities/references/activitykit-patterns.md +21 -0
  188. package/pipeline/skills/shared/external/localization-reuse-map/reference/format-and-output.md +9 -0
  189. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +12 -0
  190. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-corelocation-patterns.md +16 -0
  191. package/pipeline/skills/shared/external/mapkit-location/references/mapkit-patterns.md +21 -0
  192. package/pipeline/skills/shared/external/metrickit-diagnostics/references/metrickit-patterns.md +10 -0
  193. package/pipeline/skills/shared/external/musickit-audio/references/musickit-patterns.md +9 -0
  194. package/pipeline/skills/shared/external/natural-language/references/translation-patterns.md +12 -0
  195. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +11 -0
  196. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +11 -0
  197. package/pipeline/skills/shared/external/pdfkit/references/pdfkit-patterns.md +18 -0
  198. package/pipeline/skills/shared/external/pencilkit-drawing/references/pencilkit-patterns.md +11 -0
  199. package/pipeline/skills/shared/external/permissionkit/references/permissionkit-patterns.md +11 -0
  200. package/pipeline/skills/shared/external/photos-camera-media/references/av-playback.md +15 -0
  201. package/pipeline/skills/shared/external/photos-camera-media/references/camera-capture.md +14 -0
  202. package/pipeline/skills/shared/external/photos-camera-media/references/image-loading-caching.md +14 -0
  203. package/pipeline/skills/shared/external/photos-camera-media/references/photokit-patterns.md +14 -0
  204. package/pipeline/skills/shared/external/push-notifications/references/notification-patterns.md +14 -0
  205. package/pipeline/skills/shared/external/push-notifications/references/rich-notifications.md +10 -0
  206. package/pipeline/skills/shared/external/realitykit-ar/references/realitykit-patterns.md +12 -0
  207. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +14 -0
  208. package/pipeline/skills/shared/external/room-database/references/patterns.md +16 -0
  209. package/pipeline/skills/shared/external/shareplay-activities/references/shareplay-patterns.md +10 -0
  210. package/pipeline/skills/shared/external/speech-recognition/references/speechanalyzer-patterns.md +10 -0
  211. package/pipeline/skills/shared/external/spm-build-analysis/references/build-optimization-sources.md +14 -0
  212. package/pipeline/skills/shared/external/spm-build-analysis/references/spm-analysis-checks.md +19 -0
  213. package/pipeline/skills/shared/external/storekit/references/core-patterns.md +13 -0
  214. package/pipeline/skills/shared/external/storekit/references/storekit-advanced.md +31 -0
  215. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/argument-labels-and-parameters.md +8 -0
  216. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/conventions-and-special-rules.md +9 -0
  217. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/naming-and-clarity.md +10 -0
  218. package/pipeline/skills/shared/external/swift-api-design-guidelines/references/side-effects-and-mutating-pairs.md +8 -0
  219. package/pipeline/skills/shared/external/swift-charts/references/charts-patterns.md +24 -0
  220. package/pipeline/skills/shared/external/swift-concurrency/references/bridging-interop.md +8 -0
  221. package/pipeline/skills/shared/external/swift-concurrency/references/concurrency-patterns.md +14 -0
  222. package/pipeline/skills/shared/external/swift-concurrency/references/swiftui-concurrency.md +12 -0
  223. package/pipeline/skills/shared/external/swift-concurrency/references/synchronization-primitives.md +10 -0
  224. package/pipeline/skills/shared/external/swift-concurrency-expert/references/swift-6-2-concurrency.md +8 -0
  225. package/pipeline/skills/shared/external/swift-concurrency-pro/references/actors.md +10 -0
  226. package/pipeline/skills/shared/external/swift-concurrency-pro/references/cancellation.md +7 -0
  227. package/pipeline/skills/shared/external/swift-concurrency-pro/references/interop.md +10 -0
  228. package/pipeline/skills/shared/external/swift-concurrency-pro/references/new-features.md +11 -0
  229. package/pipeline/skills/shared/external/swift-concurrency-pro/references/structured.md +9 -0
  230. package/pipeline/skills/shared/external/swift-concurrency-pro/references/testing.md +13 -0
  231. package/pipeline/skills/shared/external/swift-language/references/swift-attributes-interop.md +7 -0
  232. package/pipeline/skills/shared/external/swift-language/references/swift-patterns-extended.md +14 -0
  233. package/pipeline/skills/shared/external/swift-security/references/biometric-authentication.md +16 -0
  234. package/pipeline/skills/shared/external/swift-security/references/certificate-trust.md +17 -0
  235. package/pipeline/skills/shared/external/swift-security/references/common-anti-patterns.md +19 -0
  236. package/pipeline/skills/shared/external/swift-security/references/compliance-owasp-mapping.md +16 -0
  237. package/pipeline/skills/shared/external/swift-security/references/credential-storage-patterns.md +19 -0
  238. package/pipeline/skills/shared/external/swift-security/references/cryptokit-public-key.md +18 -0
  239. package/pipeline/skills/shared/external/swift-security/references/cryptokit-symmetric.md +16 -0
  240. package/pipeline/skills/shared/external/swift-security/references/keychain-access-control.md +17 -0
  241. package/pipeline/skills/shared/external/swift-security/references/keychain-fundamentals.md +17 -0
  242. package/pipeline/skills/shared/external/swift-security/references/keychain-item-classes.md +17 -0
  243. package/pipeline/skills/shared/external/swift-security/references/keychain-sharing.md +18 -0
  244. package/pipeline/skills/shared/external/swift-security/references/migration-legacy-stores.md +21 -0
  245. package/pipeline/skills/shared/external/swift-security/references/secure-enclave.md +16 -0
  246. package/pipeline/skills/shared/external/swift-security/references/testing-security-code.md +16 -0
  247. package/pipeline/skills/shared/external/swift-testing/references/testing-advanced.md +9 -0
  248. package/pipeline/skills/shared/external/swift-testing/references/testing-patterns.md +24 -0
  249. package/pipeline/skills/shared/external/swift-testing-pro/references/async-tests.md +9 -0
  250. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +11 -0
  251. package/pipeline/skills/shared/external/swift-testing-pro/references/writing-better-tests.md +13 -0
  252. package/pipeline/skills/shared/external/swiftdata/references/core-data-coexistence.md +8 -0
  253. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-advanced.md +17 -0
  254. package/pipeline/skills/shared/external/swiftdata/references/swiftdata-queries.md +12 -0
  255. package/pipeline/skills/shared/external/swiftlint/references/adoption-and-configuration.md +11 -0
  256. package/pipeline/skills/shared/external/swiftlint/references/custom-rules-and-analyze.md +7 -0
  257. package/pipeline/skills/shared/external/swiftlint/references/plugins-run-scripts-and-integrations.md +13 -0
  258. package/pipeline/skills/shared/external/swiftlint/references/rule-reference.md +24 -0
  259. package/pipeline/skills/shared/external/swiftlint/references/rules-suppressions-and-baselines.md +12 -0
  260. package/pipeline/skills/shared/external/swiftui-animation/references/animation-advanced.md +15 -0
  261. package/pipeline/skills/shared/external/swiftui-animation/references/core-animation-bridge.md +15 -0
  262. package/pipeline/skills/shared/external/swiftui-animation/references/scroll-effects.md +12 -0
  263. package/pipeline/skills/shared/external/swiftui-expert-skill/references/accessibility-patterns.md +11 -0
  264. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-advanced.md +7 -0
  265. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-basics.md +13 -0
  266. package/pipeline/skills/shared/external/swiftui-expert-skill/references/animation-transitions.md +9 -0
  267. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts-accessibility.md +9 -0
  268. package/pipeline/skills/shared/external/swiftui-expert-skill/references/charts.md +17 -0
  269. package/pipeline/skills/shared/external/swiftui-expert-skill/references/image-optimization.md +9 -0
  270. package/pipeline/skills/shared/external/swiftui-expert-skill/references/latest-apis.md +10 -0
  271. package/pipeline/skills/shared/external/swiftui-expert-skill/references/layout-best-practices.md +12 -0
  272. package/pipeline/skills/shared/external/swiftui-expert-skill/references/list-patterns.md +12 -0
  273. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-scenes.md +13 -0
  274. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-views.md +12 -0
  275. package/pipeline/skills/shared/external/swiftui-expert-skill/references/macos-window-styling.md +12 -0
  276. package/pipeline/skills/shared/external/swiftui-expert-skill/references/performance-patterns.md +7 -0
  277. package/pipeline/skills/shared/external/swiftui-expert-skill/references/scroll-patterns.md +9 -0
  278. package/pipeline/skills/shared/external/swiftui-expert-skill/references/sheet-navigation-patterns.md +10 -0
  279. package/pipeline/skills/shared/external/swiftui-expert-skill/references/state-management.md +18 -0
  280. package/pipeline/skills/shared/external/swiftui-expert-skill/references/view-structure.md +16 -0
  281. package/pipeline/skills/shared/external/swiftui-gestures/references/gesture-patterns.md +11 -0
  282. package/pipeline/skills/shared/external/swiftui-layout-components/references/form.md +8 -0
  283. package/pipeline/skills/shared/external/swiftui-layout-components/references/grids.md +14 -0
  284. package/pipeline/skills/shared/external/swiftui-layout-components/references/scrollview.md +9 -0
  285. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/liquid-glass.md +22 -0
  286. package/pipeline/skills/shared/external/swiftui-liquid-glass/references/uikit-glass.md +14 -0
  287. package/pipeline/skills/shared/external/swiftui-navigation/references/deeplinks.md +10 -0
  288. package/pipeline/skills/shared/external/swiftui-navigation/references/navigationstack.md +10 -0
  289. package/pipeline/skills/shared/external/swiftui-navigation/references/sheets.md +13 -0
  290. package/pipeline/skills/shared/external/swiftui-navigation/references/tabview.md +10 -0
  291. package/pipeline/skills/shared/external/swiftui-patterns/references/architecture-patterns.md +14 -0
  292. package/pipeline/skills/shared/external/swiftui-patterns/references/deprecated-migration.md +30 -0
  293. package/pipeline/skills/shared/external/swiftui-patterns/references/platform-and-sharing.md +16 -0
  294. package/pipeline/skills/shared/external/swiftui-performance/references/demystify-swiftui-performance-wwdc23.md +11 -0
  295. package/pipeline/skills/shared/external/swiftui-performance/references/optimizing-swiftui-performance-instruments.md +11 -0
  296. package/pipeline/skills/shared/external/swiftui-performance/references/understanding-hangs-in-your-app.md +10 -0
  297. package/pipeline/skills/shared/external/swiftui-performance-audit/references/code-smells.md +8 -0
  298. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/app-wiring.md +12 -0
  299. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/menu-bar.md +10 -0
  300. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/navigationstack.md +11 -0
  301. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/scroll-reveal.md +12 -0
  302. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/sheets.md +14 -0
  303. package/pipeline/skills/shared/external/swiftui-ui-patterns/references/tabview.md +10 -0
  304. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/hosting-migration.md +10 -0
  305. package/pipeline/skills/shared/external/swiftui-uikit-interop/references/representable-recipes.md +13 -0
  306. package/pipeline/skills/shared/external/swiftui-view-refactor/references/mv-patterns.md +12 -0
  307. package/pipeline/skills/shared/external/swiftui-webkit/references/loading-and-observation.md +8 -0
  308. package/pipeline/skills/shared/external/swiftui-webkit/references/navigation-and-javascript.md +8 -0
  309. package/pipeline/skills/shared/external/tipkit/references/tipkit-patterns.md +14 -0
  310. package/pipeline/skills/shared/external/vision-framework/references/vision-requests.md +17 -0
  311. package/pipeline/skills/shared/external/vision-framework/references/visionkit-scanner.md +11 -0
  312. package/pipeline/skills/shared/external/weatherkit/references/weatherkit-patterns.md +11 -0
  313. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +27 -0
  314. package/pipeline/skills/shared/external/xcode-build-fixer/references/build-settings-best-practices.md +9 -0
  315. package/pipeline/skills/shared/external/xcode-build-fixer/references/fix-patterns.md +7 -0
  316. package/pipeline/skills/shared/external/xcode-build-orchestrator/references/build-settings-best-practices.md +9 -0
  317. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/build-optimization-sources.md +14 -0
  318. package/pipeline/skills/shared/external/xcode-compilation-analyzer/references/code-compilation-checks.md +8 -0
  319. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-optimization-sources.md +14 -0
  320. package/pipeline/skills/shared/external/xcode-project-analyzer/references/build-settings-best-practices.md +9 -0
  321. package/pipeline/skills/shared/external/xcode-project-analyzer/references/project-audit-checks.md +13 -0
@@ -32,6 +32,12 @@
32
32
  //
33
33
  // Usage:
34
34
  // anonymize-findings.mjs [--file <reviewers.json>] [--map <out.json>] [--seed <text>]
35
+ // anonymize-findings.mjs --restore --map <map.json> --in-place <triage.json>
36
+ //
37
+ // --restore runs after the triage verdict is validated: triage carries each
38
+ // finding's `foundBy` label through and never sees which model it stands for,
39
+ // and this writes the reviewer family back from the map for the per-reviewer
40
+ // telemetry that reads `reviewer`.
35
41
  //
36
42
  // Exit codes: 0 ok, 64 usage, 65 input is not JSON.
37
43
 
@@ -161,7 +167,100 @@ export function anonymize(input, { seed } = {}) {
161
167
  return { findings: tagged, map: { seed: seedText, labelToModel, malformed } };
162
168
  }
163
169
 
170
+ const FAMILIES = ["fable", "opus", "sonnet", "gpt"];
171
+
172
+ /** The reviewer family a map entry names, or null for a model outside the four. */
173
+ export function familyOf(model) {
174
+ const m = String(model ?? "").toLowerCase();
175
+ return FAMILIES.find((f) => m.includes(f)) ?? null;
176
+ }
177
+
178
+ /**
179
+ * The triage document with `reviewer` set on every finding whose `foundBy`
180
+ * label the map knows. Findings with no label (deterministic gate findings)
181
+ * or a label the map does not name are left as they are.
182
+ */
183
+ export function restore(triage, map) {
184
+ const labels = map && typeof map.labelToModel === "object" ? map.labelToModel : {};
185
+ const fill = (f) => {
186
+ if (!f || typeof f !== "object" || typeof f.foundBy !== "string") return f;
187
+ if (!Object.hasOwn(labels, f.foundBy)) return f;
188
+ const family = familyOf(labels[f.foundBy]);
189
+ return family ? { ...f, reviewer: family } : f;
190
+ };
191
+ const out = { ...triage };
192
+ if (Array.isArray(out.accepted)) out.accepted = out.accepted.map(fill);
193
+ for (const key of ["deferred", "rejected"]) {
194
+ if (Array.isArray(out[key]))
195
+ out[key] = out[key].map((e) =>
196
+ e && typeof e === "object" ? { ...e, finding: fill(e.finding) } : e,
197
+ );
198
+ }
199
+ return out;
200
+ }
201
+
202
+ /**
203
+ * The label map: the --map file when it reads, else the newest
204
+ * reviewIterations[].anonymizationMap in the run state, which outlives /tmp.
205
+ */
206
+ export function loadMap(mapPath, statePath) {
207
+ const read = (p) => {
208
+ try {
209
+ return p ? JSON.parse(readFileSync(p, "utf8")) : null;
210
+ } catch {
211
+ return null;
212
+ }
213
+ };
214
+ const fromFile = read(mapPath);
215
+ if (fromFile && typeof fromFile.labelToModel === "object") return fromFile;
216
+ const state = read(statePath);
217
+ const its = Array.isArray(state?.reviewIterations) ? state.reviewIterations : [];
218
+ for (let i = its.length - 1; i >= 0; i--) {
219
+ const m = its[i]?.anonymizationMap;
220
+ if (m && typeof m.labelToModel === "object") return m;
221
+ }
222
+ return null;
223
+ }
224
+
164
225
  const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
226
+ if (isMain && process.argv.includes("--restore")) {
227
+ const args = process.argv.slice(2);
228
+ const at = (name) => {
229
+ const i = args.indexOf(name);
230
+ return i === -1 ? undefined : args[i + 1];
231
+ };
232
+ const mapPath = at("--map");
233
+ const statePath = at("--state");
234
+ const target = at("--in-place");
235
+ const usage =
236
+ "usage: anonymize-findings.mjs --restore [--map <map.json>] [--state <state.json>] --in-place <triage.json>\n";
237
+ if (
238
+ !target ||
239
+ (!mapPath && !statePath) ||
240
+ args.length !== 3 + 2 * [mapPath, statePath].filter(Boolean).length
241
+ ) {
242
+ process.stderr.write(usage);
243
+ process.exit(64);
244
+ }
245
+ let triage;
246
+ try {
247
+ triage = JSON.parse(readFileSync(target, "utf8"));
248
+ } catch (e) {
249
+ process.stderr.write(`input is not JSON: ${e.message}\n`);
250
+ process.exit(65);
251
+ }
252
+ const map = loadMap(mapPath, statePath);
253
+ if (!map) {
254
+ // The verdict stands without the families; only per-reviewer telemetry
255
+ // loses them, so the triage chain is not stopped over it.
256
+ process.stderr.write(
257
+ "warning: no label map (neither --map nor the run state's anonymizationMap); reviewer families not restored\n",
258
+ );
259
+ process.exit(0);
260
+ }
261
+ writeFileSync(target, `${JSON.stringify(restore(triage, map), null, 2)}\n`);
262
+ process.exit(0);
263
+ }
165
264
  if (isMain) {
166
265
  const args = process.argv.slice(2);
167
266
  const flag = (name) => {
@@ -0,0 +1,113 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @file answer-check.mjs - PostToolUse hook (AskUserQuestion): an empty
4
+ * answer is not consent.
5
+ *
6
+ * rules.md ("Interaction") says a question submitted without a choice must be
7
+ * asked again or treated as unselected, never resolved to the option marked
8
+ * Recommended. The host returns such a submission as a normal tool result, so
9
+ * the rule depends on the model noticing. This hook names the unanswered
10
+ * questions back to the model as additionalContext whenever it happens.
11
+ *
12
+ * Where the answers come from: the structured result's `answers` map keyed by
13
+ * question text; when only the text form is present, the `"question"="answer"`
14
+ * pairs in it. A result it cannot read either way is left alone.
15
+ *
16
+ * Applies in every session, run or not: it only adds context. Exits 0 on every
17
+ * path and blocks nothing.
18
+ */
19
+
20
+ import { readFileSync } from "node:fs";
21
+
22
+ import { invokedDirectly } from "../lib/invoked-directly.mjs";
23
+
24
+ const MAX_INPUT = 256 * 1024;
25
+ const MAX_LABEL = 60;
26
+
27
+ const label = (q, i) => {
28
+ const raw = String(q?.header || q?.question || `question ${i + 1}`)
29
+ .replace(/\s+/g, " ")
30
+ .trim();
31
+ return raw.length > MAX_LABEL ? `${raw.slice(0, MAX_LABEL - 3)}...` : raw;
32
+ };
33
+
34
+ /** The question text -> answer map from a structured or text result, or null. */
35
+ export function answersOf(response) {
36
+ if (response && typeof response === "object" && !Array.isArray(response)) {
37
+ const a = response.answers;
38
+ if (a && typeof a === "object" && !Array.isArray(a)) return a;
39
+ }
40
+ const text =
41
+ typeof response === "string"
42
+ ? response
43
+ : Array.isArray(response)
44
+ ? response.map((b) => (typeof b === "string" ? b : (b?.text ?? ""))).join("\n")
45
+ : null;
46
+ if (!text || !/answered/i.test(text)) return null;
47
+ const out = {};
48
+ for (const m of text.matchAll(/"((?:[^"\\]|\\.)*)"="((?:[^"\\]|\\.)*)"/g)) out[m[1]] = m[2];
49
+ return out;
50
+ }
51
+
52
+ /** Labels of the questions with no answer, or null when the result is unreadable. */
53
+ export function unanswered(payload) {
54
+ if (payload?.tool_name !== "AskUserQuestion") return null;
55
+ const questions = Array.isArray(payload.tool_input?.questions)
56
+ ? payload.tool_input.questions
57
+ : [];
58
+ if (!questions.length) return null;
59
+ const answers = answersOf(payload.tool_response);
60
+ if (!answers) return null;
61
+ const keys = Object.keys(answers);
62
+ return questions
63
+ .map((q, i) => {
64
+ const text = String(q?.question ?? "");
65
+ const hit = Object.hasOwn(answers, text)
66
+ ? answers[text]
67
+ : keys.length === 1 && questions.length === 1
68
+ ? answers[keys[0]]
69
+ : undefined;
70
+ const value = Array.isArray(hit) ? hit.join("") : hit;
71
+ return typeof value === "string" && value.trim() ? null : label(q, i);
72
+ })
73
+ .filter(Boolean);
74
+ }
75
+
76
+ /** The hook output for one payload, or null when every question was answered. */
77
+ export function answer(payload) {
78
+ const missing = unanswered(payload);
79
+ if (!missing || !missing.length) return null;
80
+ return {
81
+ hookSpecificOutput: {
82
+ hookEventName: "PostToolUse",
83
+ additionalContext:
84
+ `The user submitted AskUserQuestion without choosing for: ${missing.map((m) => `"${m}"`).join(", ")}. ` +
85
+ "An empty answer is not consent: do not apply the option marked Recommended. " +
86
+ "Ask that question again, or treat the item as unselected and say so.",
87
+ },
88
+ };
89
+ }
90
+
91
+ function main() {
92
+ let raw;
93
+ try {
94
+ raw = readFileSync(0, "utf8");
95
+ } catch {
96
+ return;
97
+ }
98
+ if (!raw || raw.length > MAX_INPUT) return;
99
+ let payload;
100
+ try {
101
+ payload = JSON.parse(raw);
102
+ } catch {
103
+ return;
104
+ }
105
+ const out = answer(payload);
106
+ if (out) process.stdout.write(JSON.stringify(out) + "\n");
107
+ }
108
+
109
+ try {
110
+ if (invokedDirectly(import.meta.url)) main();
111
+ } catch {
112
+ /* silent by contract */
113
+ }
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env bash
2
+ # answer-check.sh - PostToolUse hook (AskUserQuestion) for the pipeline.
3
+ #
4
+ # Tells the model which questions came back with no choice, so an empty answer
5
+ # is asked again instead of resolved to the Recommended option. The logic is in
6
+ # answer-check.mjs; this wrapper exists because every hook the pipeline
7
+ # registers is a `bash "$HOME/.<host>/scripts/<name>"` command.
8
+ #
9
+ # Exits 0 on every path: a missing node or any internal error means no
10
+ # context, never a blocked tool call.
11
+
12
+ set -u
13
+
14
+ HERE="$(cd "$(dirname "$0")" 2>/dev/null && pwd || true)"
15
+ CORE="$HERE/answer-check.mjs"
16
+ [ -f "$CORE" ] || exit 0
17
+ command -v node >/dev/null 2>&1 || exit 0
18
+
19
+ node "$CORE" 2>/dev/null || true
20
+ exit 0
@@ -1509,7 +1509,9 @@ function scriptRunner(results, env) {
1509
1509
  * publish rights, which an unattended run does not get, so they are
1510
1510
  * reported as skipped.
1511
1511
  * reportIssueUpdates: update-issue-progress.sh for a GitHub issue the run
1512
- * recorded, and a PR-link Jira comment when no channel comment went out.
1512
+ * recorded, with a PR-link comment it posts before the Progress flags
1513
+ * (comment and flags go together), and a PR-link Jira comment when no
1514
+ * channel comment went out.
1513
1515
  *
1514
1516
  * Every text passes the outbound gate here before a script sees it, and every
1515
1517
  * script runs with MULTI_AGENT_UNATTENDED unset (each script's own gate runs
@@ -1569,7 +1571,12 @@ export function reportAfterPublish({
1569
1571
  }
1570
1572
  if (config?.reportIssueUpdates === true) {
1571
1573
  if (state?.githubIssue?.number && state?.taskId) {
1572
- runScript("issue-progress", progressScript, [String(state.taskId)]);
1574
+ runScript(
1575
+ "issue-progress",
1576
+ progressScript,
1577
+ [String(state.taskId)],
1578
+ `Draft PR opened: ${prUrl}\n`,
1579
+ );
1573
1580
  }
1574
1581
  if (jiraKey && !jiraPosted) {
1575
1582
  runScript("jira-pr-link", jiraScript, ["--issue", jiraKey], `Draft PR opened: ${prUrl}\n`);
@@ -305,7 +305,7 @@ import { runMain } from "../lib/fatal.mjs";
305
305
  import { invokedDirectly } from "../lib/invoked-directly.mjs";
306
306
 
307
307
  /** The contract as a whole; pipeline/contract/manifest.json carries the same. */
308
- export const CONTRACT_VERSION = "1.6.0";
308
+ export const CONTRACT_VERSION = "1.7.0";
309
309
  /** The majors `?v=` accepts. A new major keeps the previous one here for one minor. */
310
310
  export const SUPPORTED_MAJORS = [1];
311
311
  export const BIND_HOST = "127.0.0.1";
@@ -1528,6 +1528,33 @@ function evidenceRoute({ params }) {
1528
1528
  }
1529
1529
  }
1530
1530
 
1531
+ /**
1532
+ * GET /v1/prs/reach: work-items.mjs reach as a child process, because loading
1533
+ * a repository's graph is a synchronous parse of a file that can run to tens
1534
+ * of megabytes, and no other route may wait behind it.
1535
+ */
1536
+ async function prReachRoute() {
1537
+ const r = await spawnAsync(process.execPath, [join(HERE, "work-items.mjs"), "reach"], {
1538
+ env: process.env,
1539
+ timeoutMs: 180_000,
1540
+ maxBytes: 2 * 1024 * 1024,
1541
+ });
1542
+ let doc;
1543
+ try {
1544
+ doc = JSON.parse(r.stdout);
1545
+ } catch {
1546
+ doc = null;
1547
+ }
1548
+ if (r.status !== 0 || !doc || !Array.isArray(doc.items)) {
1549
+ throw new HttpError(
1550
+ 502,
1551
+ "upstream-failed",
1552
+ "the reach of the pull requests could not be measured",
1553
+ );
1554
+ }
1555
+ return doc;
1556
+ }
1557
+
1531
1558
  function workError(err) {
1532
1559
  if (!(err instanceof WorkError)) return err;
1533
1560
  if (err.code === "no-run" || err.code === "bad-key")
@@ -1899,6 +1926,13 @@ export const ROUTES = [
1899
1926
  ({ redact }) => pullRequests({ redact }),
1900
1927
  ),
1901
1928
  },
1929
+ {
1930
+ method: "GET",
1931
+ path: "/v1/prs/reach",
1932
+ query: [],
1933
+ remote: "local-only",
1934
+ handle: shared(() => "prs-reach", prReachRoute),
1935
+ },
1902
1936
  { method: "GET", path: "/v1/tools", query: [], handle: toolsRoute },
1903
1937
  {
1904
1938
  method: "POST",
@@ -54,14 +54,18 @@
54
54
 
55
55
  import { spawnSync } from "node:child_process";
56
56
  import {
57
+ copyFileSync,
57
58
  existsSync,
59
+ mkdtempSync,
58
60
  readFileSync,
59
61
  realpathSync,
60
62
  renameSync,
63
+ rmSync,
61
64
  statSync,
62
65
  writeFileSync,
63
66
  } from "node:fs";
64
- import { basename, dirname } from "node:path";
67
+ import { tmpdir } from "node:os";
68
+ import { basename, dirname, join } from "node:path";
65
69
 
66
70
  import { decisionEntry, withDecision } from "../lib/decisions.mjs";
67
71
  import { invokedDirectly } from "../lib/invoked-directly.mjs";
@@ -103,31 +107,77 @@ export const ATTENDED_RECORDED = new Set([
103
107
  "plan-coverage",
104
108
  "evidence-gate/build",
105
109
  "evidence-gate/test",
110
+ "review-decision",
106
111
  ]);
107
112
 
108
113
  /** What an attended commit is checked for (pre-commit-check.sh, `evidence`). */
114
+ /**
115
+ * Fix loops with a hard cap (rules.md: three attempts, the fourth failure
116
+ * pauses): consecutive failing verdicts of one of these gates count, and the
117
+ * append that reaches RETRY_CAP pauses the run with a halt reason.
118
+ */
119
+ export const RETRY_CAPPED = new Set(["evidence-gate/build", "evidence-gate/test"]);
120
+ export const RETRY_CAP = 4;
121
+
122
+ /** Consecutive failing verdicts at the end of `gates` for one gate. */
123
+ export function trailingFailures(gates, gate) {
124
+ let n = 0;
125
+ for (let i = gates.length - 1; i >= 0; i--) {
126
+ const e = gates[i];
127
+ if (!e || e.gate !== gate) continue;
128
+ if (e.verdict !== "fail") break;
129
+ n += 1;
130
+ }
131
+ return n;
132
+ }
133
+
109
134
  export const ATTENDED_COMMIT_EVIDENCE = ["evidence-gate/build", "evidence-gate/test"];
110
135
 
111
- /** prefs.global.attendedCommitGate.mode values; `warn` is the default. */
112
- export const ATTENDED_COMMIT_MODES = ["off", "warn", "block"];
136
+ /** Asked for too once the run is in its commit phase: the review verdict. */
137
+ export const ATTENDED_REVIEW_EVIDENCE = "review-decision";
138
+ export const ATTENDED_REVIEW_FROM_PHASE = 4;
113
139
 
114
140
  /**
115
- * The build/test evidence an attended commit on `sha` lacks: every gate in
116
- * ATTENDED_COMMIT_EVIDENCE whose latest entry for that sha, attended or not,
117
- * is missing or not passing.
141
+ * prefs.global.attendedCommitGate.mode values. `auto`, the default, blocks
142
+ * the commit when the run is bound to the committing session (the session
143
+ * driving it) and warns otherwise.
144
+ */
145
+ export const ATTENDED_COMMIT_MODES = ["off", "warn", "block", "auto"];
146
+
147
+ /**
148
+ * The evidence an attended commit on `sha` lacks: every gate in
149
+ * ATTENDED_COMMIT_EVIDENCE, plus the review verdict from the commit phase on,
150
+ * whose latest entry for that sha, attended or not, is missing or not passing.
151
+ * A checkpoint commit during development comes before any review and is held
152
+ * to build and test only.
118
153
  *
119
154
  * @param {object} state
120
155
  * @param {string} sha
121
156
  * @returns {string[]}
122
157
  */
123
- export function attendedEvidenceGaps(state, sha) {
124
- const latest = latestByGate(state?.gates, sha);
125
- return ATTENDED_COMMIT_EVIDENCE.filter((g) => {
158
+ export function attendedEvidenceGaps(state, sha, { sameTree } = {}) {
159
+ const wanted = [...ATTENDED_COMMIT_EVIDENCE];
160
+ if (Number(state?.currentPhase) >= ATTENDED_REVIEW_FROM_PHASE)
161
+ wanted.push(ATTENDED_REVIEW_EVIDENCE);
162
+ const asked = (Array.isArray(state?.gates) ? state.gates : []).filter((e) =>
163
+ wanted.includes(e?.gate),
164
+ );
165
+ const latest = sameTree
166
+ ? latestByGateWhere(asked, (s) => s === sha || sameTree(s))
167
+ : latestByGate(asked, sha);
168
+ return wanted.filter((g) => {
126
169
  const e = latest.get(g);
127
170
  return !e || !PASSING.has(e.verdict);
128
171
  });
129
172
  }
130
173
 
174
+ /** The mode that applies: an explicit off/warn/block, else block for a bound run. */
175
+ export function effectiveCommitMode(pref, bound) {
176
+ const mode = ATTENDED_COMMIT_MODES.includes(pref) ? pref : "auto";
177
+ if (mode !== "auto") return mode;
178
+ return bound ? "block" : "warn";
179
+ }
180
+
131
181
  const QUESTION_ID = /^[a-z][a-z0-9-]*$/;
132
182
  const OPTION_ID = /^[a-z0-9][a-z0-9+_-]*$/;
133
183
  const QUESTION_KEYS = ["id", "stepId", "text", "kind", "options"];
@@ -184,6 +234,54 @@ export class LedgerError extends Error {
184
234
  * HEAD of the repository at `dir`, or null outside one.
185
235
  * @param {string} dir
186
236
  */
237
+ /**
238
+ * A predicate: does a recorded sha hold the content this commit records?
239
+ * Yes when its tree is the index's, or the tree `git add -A` would stage from
240
+ * the working tree (built in a throwaway copy of the index, so the real index
241
+ * is untouched). The hook runs before a `git add ... && git commit` line
242
+ * stages anything, and Phase 4's `git reset HEAD~1` leaves a checkpoint
243
+ * commit's new files untracked; both still match the evidence they were
244
+ * tested with, while a file added after the evidence does not. Each sha is
245
+ * asked once; anything git cannot answer is "no".
246
+ */
247
+ export function sameTreeAs(dir) {
248
+ const run = (args, env) => {
249
+ const r = spawnSync("git", ["-C", dir, ...args], {
250
+ encoding: "utf8",
251
+ timeout: 20000,
252
+ env: env ? { ...process.env, ...env } : process.env,
253
+ });
254
+ return r.status === 0 ? r.stdout.trim() : null;
255
+ };
256
+ const indexTree = run(["write-tree"]);
257
+ let workTree = null;
258
+ const indexPath = run(["rev-parse", "--path-format=absolute", "--git-path", "index"]);
259
+ if (indexPath) {
260
+ const scratch = mkdtempSync(join(tmpdir(), "ma-tree-"));
261
+ const copy = join(scratch, "index");
262
+ try {
263
+ if (existsSync(indexPath)) copyFileSync(indexPath, copy);
264
+ if (run(["add", "-A"], { GIT_INDEX_FILE: copy }) !== null) {
265
+ workTree = run(["write-tree"], { GIT_INDEX_FILE: copy });
266
+ }
267
+ } catch {
268
+ workTree = null;
269
+ } finally {
270
+ rmSync(scratch, { recursive: true, force: true });
271
+ }
272
+ }
273
+ const targets = new Set([indexTree, workTree].filter(Boolean));
274
+ const seen = new Map();
275
+ return (sha) => {
276
+ if (!targets.size || typeof sha !== "string" || !/^[0-9a-f]{7,64}$/.test(sha)) return false;
277
+ if (!seen.has(sha)) {
278
+ const tree = run(["rev-parse", "--verify", "--quiet", `${sha}^{tree}`]);
279
+ seen.set(sha, Boolean(tree) && targets.has(tree));
280
+ }
281
+ return seen.get(sha);
282
+ };
283
+ }
284
+
187
285
  export function headSha(dir) {
188
286
  const r = spawnSync("git", ["-C", dir, "rev-parse", "HEAD"], {
189
287
  encoding: "utf8",
@@ -399,11 +497,22 @@ export async function record(
399
497
  detail: detail && typeof detail === "object" ? detail : { value: detail },
400
498
  ...(attended ? { attended: true } : {}),
401
499
  };
402
- await updateState(path, (s) => ({
403
- ...s,
404
- gates: [...(Array.isArray(s.gates) ? s.gates : []), entry],
405
- }));
406
- return { written: true, statePath: path, entry };
500
+ let capped = false;
501
+ await updateState(path, (s) => {
502
+ const gates = [...(Array.isArray(s.gates) ? s.gates : []), entry];
503
+ const next = { ...s, gates };
504
+ if (
505
+ RETRY_CAPPED.has(gate) &&
506
+ verdict === "fail" &&
507
+ trailingFailures(gates, gate) >= RETRY_CAP
508
+ ) {
509
+ capped = true;
510
+ next.status = "paused";
511
+ next.haltReason = `${phaseOf(s)}:retry-cap: ${gate} failed ${RETRY_CAP} times in a row; the fix loop stops here for the user`;
512
+ }
513
+ return next;
514
+ });
515
+ return { written: true, statePath: path, entry, capped };
407
516
  }
408
517
 
409
518
  /**
@@ -494,9 +603,14 @@ function phaseOf(state) {
494
603
  * Latest entry per gate for one sha. "Latest" is by `at`, then by position.
495
604
  */
496
605
  export function latestByGate(gates, sha) {
606
+ return latestByGateWhere(gates, (s) => s === sha);
607
+ }
608
+
609
+ /** latestByGate over the entries whose sha `accept` takes. */
610
+ export function latestByGateWhere(gates, accept) {
497
611
  const out = new Map();
498
612
  (Array.isArray(gates) ? gates : []).forEach((e, i) => {
499
- if (!e || typeof e !== "object" || e.sha !== sha || typeof e.gate !== "string") return;
613
+ if (!e || typeof e !== "object" || typeof e.gate !== "string" || !accept(e.sha)) return;
500
614
  const prev = out.get(e.gate);
501
615
  const t = Date.parse(e.at) || 0;
502
616
  if (!prev || t > prev.t || (t === prev.t && i > prev.i)) out.set(e.gate, { e, t, i });
@@ -570,6 +684,12 @@ async function main(argv) {
570
684
  // Gates off: nothing is written and nothing is said.
571
685
  if (!r.written && r.reason === attendedVerdict(opts.gate).reason) return 0;
572
686
  if (!r.written) process.stderr.write(`gate-ledger: nothing written: ${r.reason}\n`);
687
+ if (r.capped) {
688
+ process.stderr.write(
689
+ `gate-ledger: ${opts.gate} failed ${RETRY_CAP} times in a row; the run is paused (retry cap). Report what was tried and ask the user how to continue.\n`,
690
+ );
691
+ return 5;
692
+ }
573
693
  return r.written ? 0 : 3;
574
694
  }
575
695
  if (mode === "check") {
@@ -619,9 +739,8 @@ async function main(argv) {
619
739
  // mode that commits nothing, evidence present), 1 warn, 4 block. Never 2
620
740
  // or 3 for a missing run: outside the pipeline a commit is none of this
621
741
  // hook's business.
622
- const pref = globalPref("attendedCommitGate.mode", "warn");
623
- const gateMode = ATTENDED_COMMIT_MODES.includes(pref) ? pref : "warn";
624
- if (gateMode === "off") return 0;
742
+ const pref = globalPref("attendedCommitGate.mode", "auto");
743
+ if (pref === "off") return 0;
625
744
  let session = "";
626
745
  if (opts["hook-input"] === "-") {
627
746
  try {
@@ -638,7 +757,11 @@ async function main(argv) {
638
757
  if (modeEntry(state)?.commits === false) return 0;
639
758
  const sha = opts.sha || headSha(repo);
640
759
  if (!sha) return 0;
641
- const missing = attendedEvidenceGaps(state, sha);
760
+ const launched = process.env.MULTI_AGENT_SESSION_ID || "";
761
+ const bound =
762
+ (session && state.hostSessionId === session) || (launched && state.sessionId === launched);
763
+ const gateMode = effectiveCommitMode(pref, Boolean(bound));
764
+ const missing = attendedEvidenceGaps(state, sha, { sameTree: sameTreeAs(repo) });
642
765
  if (opts.json)
643
766
  writeFileSync(1, `${JSON.stringify({ statePath: path, sha, gateMode, missing })}\n`);
644
767
  if (!missing.length) return 0;
@@ -647,9 +770,9 @@ async function main(argv) {
647
770
  2,
648
771
  `${[
649
772
  `pre-commit-check: ${verb}: the run's gate ledger (${path}) has no passing ${missing.join(" / ")} for HEAD ${sha.slice(0, 12)}.`,
650
- " Run the build and the tests through evidence-gate.mjs (Phase 2 Gate 1) so the commit rests on recorded evidence.",
773
+ " Run the build and the tests through evidence-gate.mjs (Phase 2 Gate 1), and in the commit phase the review verdict (review-decision-gate.mjs), so the commit rests on recorded evidence.",
651
774
  gateMode === "block"
652
- ? " prefs.global.attendedCommitGate.mode is block; set it to warn to commit anyway."
775
+ ? " This session drives the run, so the commit waits for the evidence; prefs.global.attendedCommitGate.mode warn lets it through."
653
776
  : " The commit goes ahead; prefs.global.attendedCommitGate.mode block refuses it instead.",
654
777
  ].join("\n")}\n`,
655
778
  );