@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,7 +10,9 @@
10
10
  * Storage: append-only JSONL, one file per repo:
11
11
  * $HOME/.claude/memory/multi-agent/<repo-slug>/triage-corpus.jsonl
12
12
  *
13
- * Each line conforms to pipeline/schemas/triage-corpus.schema.json.
13
+ * Each line conforms to pipeline/schemas/triage-corpus.schema.json. Every row
14
+ * passes the secret scan in _secret-scan.mjs before it is written; one that
15
+ * fails is counted in `droppedBySecretScan` and left out.
14
16
  *
15
17
  * Why JSONL not SQLite: zero deps, Node 18+ compatible, easy to grep/diff,
16
18
  * easy to delete a single row by line. The cost of a full corpus scan is
@@ -55,6 +57,8 @@ import { readFileSync, existsSync, mkdirSync, appendFileSync, statSync } from "n
55
57
  import { homedir } from "node:os";
56
58
  import { execFileSync } from "node:child_process";
57
59
  import { join, basename, dirname, posix } from "node:path";
60
+ import { checkedSlug, memoryExcluded } from "./_memory-policy.mjs";
61
+ import { openScanner, stripPrivate } from "./_secret-scan.mjs";
58
62
  import {
59
63
  bm25,
60
64
  recencyRank,
@@ -91,6 +95,10 @@ function die(msg, code = 1) {
91
95
  // prior-art recall always miss. `--git-common-dir` points at the main
92
96
  // repository's .git for every linked worktree.
93
97
  function repoSlug() {
98
+ return checkedSlug(rawRepoSlug());
99
+ }
100
+
101
+ function rawRepoSlug() {
94
102
  if (flags["repo-slug"]) return String(flags["repo-slug"]);
95
103
  if (process.env.MULTI_AGENT_REPO_SLUG) return process.env.MULTI_AGENT_REPO_SLUG;
96
104
  try {
@@ -249,9 +257,14 @@ function ingest() {
249
257
  const fromState = stateDefaults();
250
258
  const taskId = flags["task-id"] || fromState.taskId;
251
259
  if (!taskId) die("ingest needs --task-id <id> (or --state with a taskId)", 1);
252
- const taskTitle = flags["task-title"] || fromState.taskTitle || null;
260
+ const taskTitle = stripPrivate(flags["task-title"] || fromState.taskTitle || "").trim() || null;
253
261
  const stack = flags.stack || fromState.stack || null;
254
262
  const slug = repoSlug();
263
+ if (memoryExcluded(slug)) {
264
+ process.stdout.write(JSON.stringify({ ok: true, slug, ingested: 0, excluded: true }) + "\n");
265
+ process.exitCode = 2;
266
+ return;
267
+ }
255
268
 
256
269
  let triage;
257
270
  try {
@@ -274,35 +287,51 @@ function ingest() {
274
287
  ];
275
288
 
276
289
  let written = 0;
277
- for (const [classification, rows] of buckets) {
278
- for (const f of rows) {
279
- const issue = (f.issue || "").trim();
280
- if (!issue) continue;
281
- const file = f.file || "";
282
- const key = `${taskId}::${classification}::${file}::${issue}`;
283
- if (seen.has(key)) continue;
284
- const row = {
285
- v: "1.0.0",
286
- ts,
287
- task_id: taskId,
288
- task_title: taskTitle,
289
- stack,
290
- classification,
291
- severity: f.severity || "suggestion",
292
- file,
293
- line: typeof f.line === "number" ? f.line : null,
294
- issue,
295
- fix: f.fix || null,
296
- reason: f._reason || null,
297
- reviewer: f.reviewer || null,
298
- file_sha: fileSha(file, top),
299
- };
300
- writeRow(slug, row);
301
- seen.add(key);
302
- written++;
290
+ let droppedBySecretScan = 0;
291
+ const scan = openScanner();
292
+ try {
293
+ for (const [classification, rows] of buckets) {
294
+ for (const f of rows) {
295
+ const issue = stripPrivate(f.issue || "").trim();
296
+ if (!issue) continue;
297
+ const file = f.file || "";
298
+ const key = `${taskId}::${classification}::${file}::${issue}`;
299
+ if (seen.has(key)) continue;
300
+ const fix = f.fix ? stripPrivate(f.fix).trim() || null : null;
301
+ const reason = f._reason ? stripPrivate(f._reason).trim() || null : null;
302
+ const reviewer = f.reviewer || null;
303
+ const stored = [issue, fix, reason, file, taskTitle, taskId, stack, reviewer, f.severity];
304
+ if (!scan.isClean(stored.filter(Boolean).join("\n"))) {
305
+ droppedBySecretScan++;
306
+ continue;
307
+ }
308
+ const row = {
309
+ v: "1.0.0",
310
+ ts,
311
+ task_id: taskId,
312
+ task_title: taskTitle,
313
+ stack,
314
+ classification,
315
+ severity: f.severity || "suggestion",
316
+ file,
317
+ line: typeof f.line === "number" ? f.line : null,
318
+ issue,
319
+ fix,
320
+ reason,
321
+ reviewer,
322
+ file_sha: fileSha(file, top),
323
+ };
324
+ writeRow(slug, row);
325
+ seen.add(key);
326
+ written++;
327
+ }
303
328
  }
329
+ } finally {
330
+ scan.close();
304
331
  }
305
- process.stdout.write(JSON.stringify({ ok: true, slug, ingested: written }) + "\n");
332
+ process.stdout.write(
333
+ JSON.stringify({ ok: true, slug, ingested: written, droppedBySecretScan }) + "\n",
334
+ );
306
335
  // exitCode, not exit(): even this one-line write is on the same rule, and a
307
336
  // rule with an exception is the one that gets copied into the next branch.
308
337
  // 2 = nothing new, which callers treat as an idempotent success.
@@ -313,7 +342,7 @@ function ingest() {
313
342
  function query() {
314
343
  const cfg = recallConfig();
315
344
  const topN = flags.top ? Math.max(1, Number(flags.top)) : cfg.maxResults;
316
- const slug = flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug();
345
+ const slug = repoSlug();
317
346
  const corpus = readCorpus(slug);
318
347
  if (corpus.length === 0) {
319
348
  process.stdout.write(JSON.stringify({ ok: true, slug, hits: [], total: 0 }) + "\n");
@@ -347,7 +376,7 @@ function priorArt() {
347
376
  : [];
348
377
  const cfg = recallConfig();
349
378
  const topN = flags.top ? Math.max(1, Number(flags.top)) : cfg.maxResults;
350
- const slug = flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug();
379
+ const slug = repoSlug();
351
380
  const corpus = readCorpus(slug);
352
381
  const out = list.map((f) => {
353
382
  if (corpus.length === 0) return [];
@@ -436,7 +465,7 @@ function rank(corpus, issueText, fileGlob, topN, cfg) {
436
465
  function showCmd() {
437
466
  const id = flags.id && flags.id !== true ? String(flags.id) : null;
438
467
  if (!id) die("show needs --id <id>", 1);
439
- const slug = flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug();
468
+ const slug = repoSlug();
440
469
  const want = id.startsWith("T:") ? id : `T:${id}`;
441
470
  const row = readCorpus(slug).find((r) => rowId(r) === want);
442
471
  if (!row) {
@@ -465,7 +494,7 @@ function showCmd() {
465
494
  function timelineCmd() {
466
495
  const id = flags.anchor && flags.anchor !== true ? String(flags.anchor) : null;
467
496
  if (!id) die("timeline needs --anchor <id>", 1);
468
- const slug = flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug();
497
+ const slug = repoSlug();
469
498
  const before = flags.before && flags.before !== true ? Math.max(0, Number(flags.before)) : 3;
470
499
  const after = flags.after && flags.after !== true ? Math.max(0, Number(flags.after)) : 3;
471
500
  const want = id.startsWith("T:") ? id : `T:${id}`;
@@ -495,15 +524,13 @@ function timelineCmd() {
495
524
  }
496
525
 
497
526
  function pathCmd() {
498
- process.stdout.write(
499
- corpusPath(flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug()) + "\n",
500
- );
527
+ process.stdout.write(corpusPath(repoSlug()) + "\n");
501
528
  process.exitCode = 0;
502
529
  return;
503
530
  }
504
531
 
505
532
  function statsCmd() {
506
- const slug = flags["repo-slug"] ? String(flags["repo-slug"]) : repoSlug();
533
+ const slug = repoSlug();
507
534
  const corpus = readCorpus(slug);
508
535
  const counts = { accepted: 0, deferred: 0, rejected: 0 };
509
536
  for (const r of corpus) if (counts[r.classification] !== undefined) counts[r.classification]++;
@@ -30,6 +30,7 @@
30
30
  # # updateCheck.ttlHours, else 24)
31
31
  # bash pipeline/scripts/update-check.sh --force # ignore cache
32
32
  # bash pipeline/scripts/update-check.sh --print-required # emit the floor only
33
+ # bash pipeline/scripts/update-check.sh --cache-only # cached answer at any age, never the registry
33
34
  #
34
35
  # Cache file: ~/.claude/logs/multi-agent/.update-check ("epoch|latest|required").
35
36
  # A legacy two-field cache ("epoch|latest") still reads - the missing third
@@ -47,6 +48,7 @@ FAIL_TTL_SECONDS=3600
47
48
  LOCAL_VERSION=""
48
49
  FORCE=0
49
50
  PRINT_REQUIRED=0
51
+ CACHE_ONLY=0
50
52
 
51
53
  while [ $# -gt 0 ]; do
52
54
  case "$1" in
@@ -54,6 +56,7 @@ while [ $# -gt 0 ]; do
54
56
  --ttl-hours) TTL_HOURS="${2:-}"; shift; [ $# -gt 0 ] && shift ;;
55
57
  --force) FORCE=1; shift ;;
56
58
  --print-required) PRINT_REQUIRED=1; shift ;;
59
+ --cache-only) CACHE_ONLY=1; shift ;;
57
60
  *) shift ;;
58
61
  esac
59
62
  done
@@ -124,12 +127,15 @@ if [ "$FORCE" -eq 0 ] && [ -f "$CACHE_FILE" ]; then
124
127
  case "$cached_epoch" in (*[!0-9]*|"") cached_epoch=0 ;; esac
125
128
  # A stamp in the future is a clock or copy mistake, not a fresh answer.
126
129
  [ "$cached_epoch" -gt "$now" ] && cached_epoch=0
127
- if [ $((now - cached_epoch)) -lt $((TTL_HOURS * 3600)) ] && [ -n "$cached_latest" ]; then
130
+ if { [ "$CACHE_ONLY" -eq 1 ] || [ $((now - cached_epoch)) -lt $((TTL_HOURS * 3600)) ]; } && [ -n "$cached_latest" ]; then
128
131
  latest="$cached_latest"
129
132
  required="$cached_required"
130
133
  fi
131
134
  fi
132
135
 
136
+ # A cache-only caller gets no answer rather than a network wait.
137
+ [ -z "$latest" ] && [ "$CACHE_ONLY" -eq 1 ] && exit 0
138
+
133
139
  # Stale or missing cache -> one bounded registry call (silent on any failure).
134
140
  # The abbreviated packument carries dist-tags at a quarter of the full document.
135
141
  # A failed read is remembered for FAIL_TTL_SECONDS in "<cache>.fail", so an
@@ -11,7 +11,13 @@
11
11
  # never hand-edited.
12
12
  #
13
13
  # Usage:
14
- # update-issue-progress.sh <task-id>
14
+ # update-issue-progress.sh <task-id> [--comment-file <path>]
15
+ #
16
+ # --comment-file posts that comment on the issue first (through the outbound
17
+ # gate, with the `Ref: #N` footer appended when missing), then the flags, so
18
+ # a caller with no separate comment step (the unattended runner) still writes
19
+ # both. `--body-file` is the same flag under the name the runner's script
20
+ # runner passes. Without it the flags wait for a comment already on the issue.
15
21
  #
16
22
  # Inputs:
17
23
  # $HOME/.claude/logs/multi-agent/{project}/{taskId}/agent-state.json
@@ -26,6 +32,9 @@
26
32
  # 2 missing agent-state.json
27
33
  # 3 missing required flags
28
34
  # 4 GitHub edit failed
35
+ # 5 no issue comment from this run yet: the comment is posted first and the
36
+ # flags follow it, both or neither (channels/issue-comment.md)
37
+ # 6 issue coordinates in the state that are not an owner/repo and a number
29
38
 
30
39
  set -euo pipefail
31
40
 
@@ -118,9 +127,17 @@ EOF
118
127
 
119
128
  TASK_ID="${1:-}"
120
129
  if [ -z "$TASK_ID" ]; then
121
- echo "usage: update-issue-progress.sh <task-id>" >&2
130
+ echo "usage: update-issue-progress.sh <task-id> [--comment-file <path>]" >&2
122
131
  exit 64
123
132
  fi
133
+ COMMENT_FILE=""
134
+ if [ "$TASK_ID" != "--rewrite" ] && [ "$#" -gt 1 ]; then
135
+ if [ "$#" -ne 3 ] || { [ "$2" != "--comment-file" ] && [ "$2" != "--body-file" ]; } || [ ! -f "$3" ]; then
136
+ echo "usage: update-issue-progress.sh <task-id> [--comment-file <path>]" >&2
137
+ exit 64
138
+ fi
139
+ COMMENT_FILE="$3"
140
+ fi
124
141
 
125
142
  # Internal test seam: `--rewrite <body-file>` runs only the Progress-block
126
143
  # rewrite (placeholder glyphs) and prints the result. No state, no gh.
@@ -155,6 +172,11 @@ if [ -z "$ORG_REPO" ] || [ -z "$ISSUE_NUM" ]; then
155
172
  echo "update-issue-progress: no githubIssue in state for task=$TASK_ID (Jira-originated task, or issue not yet linked)" >&2
156
173
  exit 3
157
174
  fi
175
+ if ! printf '%s' "$ISSUE_NUM" | grep -Eq '^[0-9]{1,9}$' ||
176
+ ! printf '%s' "$ORG_REPO" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9-]{0,38}/[A-Za-z0-9._-]{1,100}$'; then
177
+ echo "update-issue-progress: githubIssue in state is not an owner/repo and a number; nothing written" >&2
178
+ exit 6
179
+ fi
158
180
 
159
181
  # Read the four flags (default false).
160
182
  F_IMPL=$(jq -r '.flags.implementation // false' "$AGENT_STATE")
@@ -190,17 +212,60 @@ NEW_BLOCK=$(build_progress_block "$G_IMPL" "$G_TEST" "$G_CC" "$G_WIKI")
190
212
  # comment, next heading, or EOF) - see rewrite_progress_body above.
191
213
  rewrite_progress_body "$NEW_BLOCK" "$TMP_BODY" > "$TMP_NEW"
192
214
 
193
- # Check if body actually changed (idempotent no-op).
194
- if cmp -s "$TMP_BODY" "$TMP_NEW"; then
215
+ # Whether the body changes (an unchanged body is an idempotent no-op for the
216
+ # flags), and the new body's outbound check before anything is posted, so a
217
+ # refused body never leaves a comment behind without its flags.
218
+ CHANGED=true
219
+ cmp -s "$TMP_BODY" "$TMP_NEW" && CHANGED=false
220
+ if [ "$CHANGED" = true ] && ! ma_outbound_gate "$TMP_NEW"; then
221
+ echo "update-issue-progress: outbound gate refused the new body; #$ISSUE_NUM left unchanged" >&2
222
+ exit 7
223
+ fi
224
+
225
+ # Both or neither: the flags follow this run's issue comment, never stand in
226
+ # for it. With --comment-file the comment is posted here, first, whether or
227
+ # not the flags change. Otherwise it
228
+ # is the one the authenticated account posted on the issue since the run
229
+ # started, carrying the template's `Ref: #N` footer.
230
+ if [ -n "$COMMENT_FILE" ]; then
231
+ TMP_COMMENT=$(mktemp "/tmp/issue-comment-${TASK_ID}.XXXXXX")
232
+ trap 'rm -f "$TMP_BODY" "$TMP_NEW" "$TMP_COMMENT"' EXIT
233
+ cat "$COMMENT_FILE" > "$TMP_COMMENT"
234
+ grep -qF "Ref: #$ISSUE_NUM" "$TMP_COMMENT" || printf '\n\nRef: #%s\n' "$ISSUE_NUM" >> "$TMP_COMMENT"
235
+ if ! ma_outbound_gate "$TMP_COMMENT"; then
236
+ echo "update-issue-progress: outbound gate refused the comment; #$ISSUE_NUM left unchanged" >&2
237
+ exit 7
238
+ fi
239
+ if ! gh issue comment "$ISSUE_NUM" --repo "$ORG_REPO" --body-file "$TMP_COMMENT" >/dev/null; then
240
+ echo "update-issue-progress: gh issue comment failed for #$ISSUE_NUM; flags left unchanged" >&2
241
+ exit 4
242
+ fi
243
+ elif [ "$CHANGED" = true ]; then
244
+ # The comments API reads `since` as an ISO 8601 instant; an offset's `+`
245
+ # would arrive as a space, so the start time goes out in UTC.
246
+ STARTED=$(jq -r '.startedAt // empty' "$AGENT_STATE")
247
+ STARTED=$(node -e 'const d = new Date(process.argv[1]); if (process.argv[1] && !isNaN(d)) process.stdout.write(d.toISOString().replace(/\.\d{3}Z$/, "Z"));' "$STARTED" 2>/dev/null || true)
248
+ ME=$(gh api user --jq .login 2>/dev/null || true)
249
+ if ! printf '%s' "$ME" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9-]{0,38}(\[bot\])?$'; then
250
+ echo "update-issue-progress: could not read the GitHub account; #$ISSUE_NUM left unchanged" >&2
251
+ exit 5
252
+ fi
253
+ COMMENTS_PATH="repos/$ORG_REPO/issues/$ISSUE_NUM/comments"
254
+ [ -n "$STARTED" ] && COMMENTS_PATH="$COMMENTS_PATH?since=$STARTED"
255
+ if ! gh api --paginate "$COMMENTS_PATH" \
256
+ --jq ".[] | select(.user.login == \"$ME\") | select(.body | contains(\"Ref: #$ISSUE_NUM\")) | .id" 2>/dev/null |
257
+ grep -q .; then
258
+ echo "update-issue-progress: no issue comment from this run on #$ISSUE_NUM; post it first (comment and flags go together)" >&2
259
+ exit 5
260
+ fi
261
+ fi
262
+
263
+ if [ "$CHANGED" = false ]; then
195
264
  echo "update-issue-progress: no change for #$ISSUE_NUM (idempotent no-op)" >&2
196
265
  exit 0
197
266
  fi
198
267
 
199
268
  # Apply.
200
- if ! ma_outbound_gate "$TMP_NEW"; then
201
- echo "update-issue-progress: outbound gate refused the new body; #$ISSUE_NUM left unchanged" >&2
202
- exit 7
203
- fi
204
269
  if ! gh issue edit "$ISSUE_NUM" --repo "$ORG_REPO" --body-file "$TMP_NEW" >/dev/null; then
205
270
  echo "update-issue-progress: gh issue edit failed for #$ISSUE_NUM" >&2
206
271
  exit 4
@@ -8,8 +8,12 @@
8
8
  // Zero deps. Reads JSON from path arg or STDIN.
9
9
  //
10
10
  // Usage:
11
- // node validate-planning.mjs path/to/plan.json
12
- // cat plan.json | node validate-planning.mjs -
11
+ // node validate-planning.mjs path/to/plan.json [--spec <path>]
12
+ // cat plan.json | node validate-planning.mjs - [--spec <path>]
13
+ //
14
+ // --spec names the analysis document or task text the plan was written from.
15
+ // A plan more than SPEC_RATIO times its length gets a warning: a plan should
16
+ // record decisions, and one far longer than its spec is usually a transcript.
13
17
  //
14
18
  // Exit codes:
15
19
  // 0 - valid
@@ -23,15 +27,113 @@
23
27
 
24
28
  import { readFileSync } from "node:fs";
25
29
  import { planOverlaps } from "./_plan-overlap.mjs";
30
+ import { sectionBody, tableDataRows } from "../lib/analysis-sections.mjs";
26
31
 
27
32
  const ALLOWED_TYPES = new Set(["test", "code", "refactor", "docs", "chore"]);
28
33
  const TASK_ID_RE = /^T\d+$/;
29
34
  const NAME_LISTS = ["creates", "removes", "produces", "consumes"];
35
+ const REVIEW_FOCUS_MAX = 5;
36
+ const SPEC_RATIO = 4;
37
+ const SPEC_MIN_CHARS = 400;
38
+
39
+ const FIGMA_LINK = /\bfigma\.com\/(?:design|file|proto)\//i;
40
+ const DESIGN_VALUE = /\b\d+(?:\.\d+)?\s?(?:px|pt)\b/i;
41
+ const INVENTORY_KEYWORDS = ["Bileşen Envanteri", "Component Inventory"];
42
+ const UI_FILE = /(?:View|Screen|Scene|Content|Cell|Sheet)\.(?:swift|kt)$|\.figma\.(?:swift|kt)$/;
43
+ const MAPPINGS = new Set(["code-connect", "new", "best-fit"]);
44
+
45
+ /**
46
+ * The analysis Section 6 rows: the component name (first cell) and whether it
47
+ * exists already (second cell, yes / no; anything else is unknown).
48
+ */
49
+ function inventoryComponents(specText) {
50
+ const body = sectionBody(String(specText).split("\n"), INVENTORY_KEYWORDS, 2);
51
+ if (!body) return [];
52
+ return tableDataRows(body)
53
+ .map((row) => {
54
+ const cells = row.split("|").map((c) => c.replace(/[`*]/g, "").trim());
55
+ const existing = (cells[2] || "").toLowerCase();
56
+ return {
57
+ name: cells[1] ?? "",
58
+ existing: /^(yes|evet)\b/.test(existing)
59
+ ? true
60
+ : /^(no|hayır|hayir)\b/.test(existing)
61
+ ? false
62
+ : null,
63
+ };
64
+ })
65
+ .filter((r) => r.name && !/^<.*>$/.test(r.name));
66
+ }
67
+
68
+ /**
69
+ * Errors and warnings for the plan's UI tasks against Section 6: a task that
70
+ * touches a view file names its component, and the name is a Section 6 row.
71
+ */
72
+ function designCitations(tasks, components) {
73
+ const errors = [];
74
+ const warnings = [];
75
+ if (!components.length || !Array.isArray(tasks)) return { errors, warnings };
76
+ const known = new Map(components.map((c) => [c.name, c.existing]));
77
+ for (const t of tasks) {
78
+ if (!t || typeof t !== "object") continue;
79
+ const d = t.design;
80
+ if (d !== undefined) {
81
+ if (!d || typeof d.component !== "string" || !MAPPINGS.has(d.mapping)) {
82
+ errors.push(`tasks ${t.id}.design: needs {component, mapping: code-connect|new|best-fit}`);
83
+ continue;
84
+ }
85
+ const name = d.component.trim();
86
+ if (!known.has(name)) {
87
+ errors.push(
88
+ `tasks ${t.id}.design.component: "${d.component}" is not a Section 6 row; use the inventory's name verbatim, never a sound-alike`,
89
+ );
90
+ } else if (d.mapping === "new" && known.get(name) === true) {
91
+ errors.push(
92
+ `tasks ${t.id}.design: "${name}" exists already per Section 6; reuse it, do not build it new`,
93
+ );
94
+ } else if (d.mapping === "code-connect" && known.get(name) === false) {
95
+ errors.push(
96
+ `tasks ${t.id}.design: "${name}" does not exist yet per Section 6, so it has no Code Connect mapping`,
97
+ );
98
+ }
99
+ if (d.mapping === "best-fit") {
100
+ warnings.push(
101
+ `tasks ${t.id}: best-fit component needs a Risk row; Phase 3 gates it review_blocking`,
102
+ );
103
+ }
104
+ continue;
105
+ }
106
+ const ui = (Array.isArray(t.files) ? t.files : []).some(
107
+ (f) => typeof f === "string" && UI_FILE.test(f),
108
+ );
109
+ if (ui && (t.type === "code" || t.type === "refactor")) {
110
+ errors.push(
111
+ `tasks ${t.id}: touches a UI file but cites no Section 6 component (design.component)`,
112
+ );
113
+ }
114
+ }
115
+ return { errors, warnings };
116
+ }
117
+
118
+ function specPath() {
119
+ const i = process.argv.indexOf("--spec");
120
+ return i > 0 ? process.argv[i + 1] : null;
121
+ }
122
+
123
+ /** The plan path: the first argument that is neither a flag nor a flag's value. */
124
+ function inputArg() {
125
+ const args = process.argv.slice(2);
126
+ for (let i = 0; i < args.length; i++) {
127
+ if (args[i] === "--spec") i++;
128
+ else if (!args[i].startsWith("--")) return args[i];
129
+ }
130
+ return undefined;
131
+ }
30
132
 
31
133
  function readInput() {
32
- const arg = process.argv[2];
134
+ const arg = inputArg();
33
135
  if (!arg) {
34
- console.error("usage: validate-planning.mjs <path|->");
136
+ console.error("usage: validate-planning.mjs <path|-> [--spec <path>]");
35
137
  process.exit(64);
36
138
  }
37
139
  if (arg === "-") {
@@ -173,6 +275,33 @@ async function main() {
173
275
  }
174
276
  }
175
277
 
278
+ if (parsed.reviewFocus !== undefined) {
279
+ if (!Array.isArray(parsed.reviewFocus)) {
280
+ errors.push("reviewFocus: must be array if present");
281
+ } else {
282
+ if (parsed.reviewFocus.length > REVIEW_FOCUS_MAX) {
283
+ errors.push(`reviewFocus: at most ${REVIEW_FOCUS_MAX} entries`);
284
+ }
285
+ parsed.reviewFocus.forEach((r, i) => {
286
+ if (typeof r?.input !== "string" || r.input.trim().length < 4) {
287
+ errors.push(`reviewFocus[${i}].input: must name the input`);
288
+ }
289
+ if (!ids.has(r?.ownerTaskId)) {
290
+ errors.push(`reviewFocus[${i}].ownerTaskId: unknown task "${r?.ownerTaskId}"`);
291
+ }
292
+ });
293
+ }
294
+ }
295
+ if (
296
+ parsed.globalConstraints !== undefined &&
297
+ !(
298
+ Array.isArray(parsed.globalConstraints) &&
299
+ parsed.globalConstraints.every((c) => typeof c === "string" && c.length > 0)
300
+ )
301
+ ) {
302
+ errors.push("globalConstraints: must be an array of non-empty strings if present");
303
+ }
304
+
176
305
  // approved=false requires userFeedback
177
306
  if (errors.length === 0 && parsed.approved === false) {
178
307
  if (typeof parsed.userFeedback !== "string" || parsed.userFeedback.length === 0) {
@@ -189,6 +318,45 @@ async function main() {
189
318
  errors.push(...found.contradictions);
190
319
  }
191
320
 
321
+ const spec = specPath();
322
+ if (spec) {
323
+ let specChars = 0;
324
+ let specText = "";
325
+ try {
326
+ specText = readFileSync(spec, "utf-8");
327
+ specChars = specText.length;
328
+ } catch {
329
+ warnings.push(`spec not readable: ${spec}`);
330
+ }
331
+ // With a design behind the plan, values come from its tokens, never from a
332
+ // number someone read off a screenshot.
333
+ if (FIGMA_LINK.test(specText) && Array.isArray(parsed.tasks)) {
334
+ for (const t of parsed.tasks) {
335
+ const text = [
336
+ t?.title,
337
+ t?.description,
338
+ ...(Array.isArray(t?.acceptanceCriteria) ? t.acceptanceCriteria : []),
339
+ ]
340
+ .filter((v) => typeof v === "string")
341
+ .join("\n");
342
+ const m = text.match(DESIGN_VALUE);
343
+ if (m) {
344
+ errors.push(
345
+ `tasks ${t.id}: "${m[0]}" is a raw design value; name the design token (Spacing.*, Radius.*, a type style) the analysis mapped instead`,
346
+ );
347
+ }
348
+ }
349
+ }
350
+ const cited = designCitations(parsed.tasks, inventoryComponents(specText));
351
+ errors.push(...cited.errors);
352
+ warnings.push(...cited.warnings);
353
+ if (specChars >= SPEC_MIN_CHARS && raw.length > specChars * SPEC_RATIO) {
354
+ warnings.push(
355
+ `plan is ${Math.round(raw.length / specChars)}x the length of its spec; record decisions, not a transcript`,
356
+ );
357
+ }
358
+ }
359
+
192
360
  if (errors.length > 0) {
193
361
  emit({ ok: false, code: 1, errors, warnings, overlaps });
194
362
  process.exitCode = 1;
@@ -264,6 +264,26 @@ async function main() {
264
264
  errors.push("approved must be boolean");
265
265
  }
266
266
 
267
+ // Requirements the diff cannot show: they live in code the change did not touch.
268
+ if (parsed.cannotVerify !== undefined) {
269
+ if (!Array.isArray(parsed.cannotVerify)) {
270
+ errors.push("cannotVerify must be an array when present");
271
+ } else {
272
+ parsed.cannotVerify.forEach((row, i) => {
273
+ const label = `cannotVerify[${i}]`;
274
+ if (typeof row?.requirement !== "string" || row.requirement.trim().length < 4) {
275
+ errors.push(`${label}: requirement must name what could not be verified`);
276
+ }
277
+ if (typeof row?.where !== "string" || !row.where.trim()) {
278
+ errors.push(`${label}: where must name the unchanged file or symbol`);
279
+ }
280
+ if (typeof row?.why !== "string" || row.why.trim().length < 4) {
281
+ errors.push(`${label}: why must say what the diff does not show`);
282
+ }
283
+ });
284
+ }
285
+ }
286
+
267
287
  // Conformance checklist, only when the orchestrator supplied criteria.
268
288
  const critIdx = process.argv.indexOf("--criteria");
269
289
  if (critIdx !== -1) {