@mmerterden/multi-agent-pipeline 20.2.1 → 20.3.1

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 (622) hide show
  1. package/CHANGELOG.md +919 -1
  2. package/README.md +104 -81
  3. package/README.tr.md +103 -62
  4. package/docs/FIGMA_PIPELINE.md +35 -35
  5. package/docs/adr/0006-skills-core-external-split.md +1 -1
  6. package/docs/adr/0007-multi-tool-adapter-framework.md +6 -0
  7. package/docs/adr/0008-installer-modularization-and-secret-leak-defense.md +7 -0
  8. package/docs/architecture.md +50 -14
  9. package/docs/best-practices.md +1 -1
  10. package/docs/ecosystem.md +56 -32
  11. package/docs/facts.json +10 -10
  12. package/docs/features.md +97 -5
  13. package/docs/recovery-guide.md +7 -14
  14. package/docs/server-readiness.md +31 -24
  15. package/index.js +1 -1
  16. package/install/_common.mjs +3 -5
  17. package/install/_platform-filter.mjs +23 -1
  18. package/install/_unattended-profile.mjs +321 -75
  19. package/install/claude.mjs +51 -10
  20. package/install/codex.mjs +2 -0
  21. package/install/copilot.mjs +2 -0
  22. package/install/index.mjs +30 -17
  23. package/install/templates/claude-hooks.json +16 -5
  24. package/install/templates/copilot-instructions.md +1 -1
  25. package/install/templates/multi-agent-autopilot-awake.plist.template +48 -0
  26. package/install/templates/multi-agent-autopilot.plist.template +12 -5
  27. package/install/unattended-profile-legacy.json +80 -0
  28. package/manifest.json +617 -483
  29. package/package.json +8 -3
  30. package/pipeline/agents/code-reviewer.md +10 -0
  31. package/pipeline/agents/plan-critic.md +98 -0
  32. package/pipeline/agents/security-auditor.md +10 -0
  33. package/pipeline/agents/task-clarifier.md +10 -0
  34. package/pipeline/commands/multi-agent/SKILL.md +2 -2
  35. package/pipeline/commands/multi-agent/analysis/SKILL.md +6 -2
  36. package/pipeline/commands/multi-agent/analysis-jira/SKILL.md +12 -1
  37. package/pipeline/commands/multi-agent/analysis-resolve/SKILL.md +4 -0
  38. package/pipeline/commands/multi-agent/autopilot/SKILL.md +6 -2
  39. package/pipeline/commands/multi-agent/autopilot-off/SKILL.md +36 -6
  40. package/pipeline/commands/multi-agent/autopilot-on/SKILL.md +77 -12
  41. package/pipeline/commands/multi-agent/autopilot-status/SKILL.md +29 -8
  42. package/pipeline/commands/multi-agent/build-optimize/SKILL.md +4 -0
  43. package/pipeline/commands/multi-agent/channels/SKILL.md +9 -9
  44. package/pipeline/commands/multi-agent/complaint-analysis/SKILL.md +5 -1
  45. package/pipeline/commands/multi-agent/create-jira/SKILL.md +4 -0
  46. package/pipeline/commands/multi-agent/design-check/SKILL.md +16 -11
  47. package/pipeline/commands/multi-agent/diff-explain/SKILL.md +4 -0
  48. package/pipeline/commands/multi-agent/doctor/SKILL.md +4 -0
  49. package/pipeline/commands/multi-agent/feedback/SKILL.md +5 -1
  50. package/pipeline/commands/multi-agent/forget/SKILL.md +4 -0
  51. package/pipeline/commands/multi-agent/garbage-collect/SKILL.md +4 -0
  52. package/pipeline/commands/multi-agent/graph/SKILL.md +4 -0
  53. package/pipeline/commands/multi-agent/help/SKILL.md +37 -39
  54. package/pipeline/commands/multi-agent/ios-coding-standard/SKILL.md +4 -0
  55. package/pipeline/commands/multi-agent/issue/SKILL.md +7 -1
  56. package/pipeline/commands/multi-agent/jira/SKILL.md +7 -1
  57. package/pipeline/commands/multi-agent/kill/SKILL.md +9 -3
  58. package/pipeline/commands/multi-agent/language/SKILL.md +4 -0
  59. package/pipeline/commands/multi-agent/log/SKILL.md +4 -0
  60. package/pipeline/commands/multi-agent/manual-test/SKILL.md +5 -0
  61. package/pipeline/commands/multi-agent/model/SKILL.md +4 -0
  62. package/pipeline/commands/multi-agent/prune-logs/SKILL.md +4 -0
  63. package/pipeline/commands/multi-agent/prune-prompts/SKILL.md +4 -0
  64. package/pipeline/commands/multi-agent/purge/SKILL.md +4 -0
  65. package/pipeline/commands/multi-agent/refactor/SKILL.md +5 -0
  66. package/pipeline/commands/multi-agent/research/SKILL.md +49 -0
  67. package/pipeline/commands/multi-agent/resume/SKILL.md +29 -6
  68. package/pipeline/commands/multi-agent/review/SKILL.md +28 -9
  69. package/pipeline/commands/multi-agent/review-analysis/SKILL.md +5 -1
  70. package/pipeline/commands/multi-agent/review-issue/SKILL.md +4 -0
  71. package/pipeline/commands/multi-agent/review-jira/SKILL.md +4 -0
  72. package/pipeline/commands/multi-agent/route-off/SKILL.md +4 -0
  73. package/pipeline/commands/multi-agent/route-on/SKILL.md +4 -0
  74. package/pipeline/commands/multi-agent/route-status/SKILL.md +4 -0
  75. package/pipeline/commands/multi-agent/routines/SKILL.md +4 -0
  76. package/pipeline/commands/multi-agent/save/SKILL.md +6 -2
  77. package/pipeline/commands/multi-agent/scaffold/SKILL.md +47 -0
  78. package/pipeline/commands/multi-agent/scan/SKILL.md +4 -0
  79. package/pipeline/commands/multi-agent/search/SKILL.md +12 -8
  80. package/pipeline/commands/multi-agent/security-review/SKILL.md +4 -0
  81. package/pipeline/commands/multi-agent/serve/SKILL.md +62 -0
  82. package/pipeline/commands/multi-agent/setup/SKILL.md +27 -36
  83. package/pipeline/commands/multi-agent/stack/SKILL.md +4 -0
  84. package/pipeline/commands/multi-agent/status/SKILL.md +10 -9
  85. package/pipeline/commands/multi-agent/steer/SKILL.md +5 -1
  86. package/pipeline/commands/multi-agent/store-ready/SKILL.md +4 -0
  87. package/pipeline/commands/multi-agent/sync/SKILL.md +13 -13
  88. package/pipeline/commands/multi-agent/test/SKILL.md +4 -0
  89. package/pipeline/commands/multi-agent/test-accessibility/SKILL.md +4 -0
  90. package/pipeline/commands/multi-agent/test-dark-mode/SKILL.md +4 -0
  91. package/pipeline/commands/multi-agent/test-dynamic-type/SKILL.md +4 -0
  92. package/pipeline/commands/multi-agent/test-screenshots/SKILL.md +5 -1
  93. package/pipeline/commands/multi-agent/testflight-validation/SKILL.md +4 -0
  94. package/pipeline/commands/multi-agent/uninstall/SKILL.md +5 -1
  95. package/pipeline/commands/multi-agent/update/SKILL.md +4 -0
  96. package/pipeline/contract/CHANGELOG.md +74 -0
  97. package/pipeline/contract/README.md +126 -0
  98. package/pipeline/contract/build.mjs +427 -0
  99. package/pipeline/contract/fixtures/answer-result.json +11 -0
  100. package/pipeline/contract/fixtures/error-invalid-request.json +8 -0
  101. package/pipeline/contract/fixtures/error-unauthorized.json +5 -0
  102. package/pipeline/contract/fixtures/error-unsigned.json +5 -0
  103. package/pipeline/contract/fixtures/issues-empty.json +18 -0
  104. package/pipeline/contract/fixtures/launch-plan.json +31 -0
  105. package/pipeline/contract/fixtures/runs-awaiting-question.json +70 -0
  106. package/pipeline/contract/fixtures/runs-empty.json +6 -0
  107. package/pipeline/contract/fixtures/runs-failed.json +84 -0
  108. package/pipeline/contract/fixtures/runs-old-schema.json +70 -0
  109. package/pipeline/contract/fixtures/runs-pr-opened-redacted.json +101 -0
  110. package/pipeline/contract/fixtures/runs-pr-opened.json +106 -0
  111. package/pipeline/contract/fixtures/runs-running.json +84 -0
  112. package/pipeline/contract/fixtures/worktrees-empty.json +4 -0
  113. package/pipeline/contract/frozen/toolbox.json +107 -0
  114. package/pipeline/contract/manifest.json +263 -0
  115. package/pipeline/contract/types/index.d.ts +343 -0
  116. package/pipeline/lib/_jira-auth.sh +6 -2
  117. package/pipeline/lib/account-resolver.sh +1 -1
  118. package/pipeline/lib/autopilot-state.sh +19 -0
  119. package/pipeline/lib/context-link-extractor.sh +12 -5
  120. package/pipeline/lib/credential-inventory.sh +12 -5
  121. package/pipeline/lib/credential-store.sh +116 -185
  122. package/pipeline/lib/fetch-confluence.sh +44 -3
  123. package/pipeline/lib/fetch-document.sh +3 -4
  124. package/pipeline/lib/fetch-fortify.sh +1 -1
  125. package/pipeline/lib/figma-mcp-refresh.sh +2 -2
  126. package/pipeline/lib/figma-token.sh +5 -1
  127. package/pipeline/lib/issue-fetcher.sh +233 -16
  128. package/pipeline/lib/json-file-lock.mjs +172 -0
  129. package/pipeline/lib/model-dispatch.sh +21 -12
  130. package/pipeline/lib/model-rung.sh +6 -1
  131. package/pipeline/lib/multi-repo-pipeline.sh +1 -1
  132. package/pipeline/lib/outbound-gate.mjs +46 -16
  133. package/pipeline/lib/parse-complaints.sh +14 -7
  134. package/pipeline/lib/plan-todos.sh +3 -3
  135. package/pipeline/lib/post-pr-review.sh +9 -9
  136. package/pipeline/lib/pr-request-location.mjs +85 -0
  137. package/pipeline/lib/regular-file.mjs +153 -0
  138. package/pipeline/lib/repo-hygiene.sh +17 -0
  139. package/pipeline/lib/route-state.sh +5 -1
  140. package/pipeline/lib/run-paths.sh +3 -2
  141. package/pipeline/lib/stack-detect.sh +19 -1
  142. package/pipeline/lib/unattended-profile-check.mjs +178 -0
  143. package/pipeline/lib/unattended-settings-location.mjs +28 -0
  144. package/pipeline/lib/unattended.mjs +76 -0
  145. package/pipeline/lib/unattended.sh +32 -0
  146. package/pipeline/lib/untrusted.mjs +76 -0
  147. package/pipeline/lib/usage-endpoint.mjs +33 -0
  148. package/pipeline/lib/user-facing.mjs +82 -0
  149. package/pipeline/lib/user-facing.sh +58 -0
  150. package/pipeline/multi-agent-refs/_dev-context.md +1 -1
  151. package/pipeline/multi-agent-refs/analysis/evidence.md +4 -0
  152. package/pipeline/multi-agent-refs/analysis/locked.md +2 -2
  153. package/pipeline/multi-agent-refs/analysis/render.md +4 -3
  154. package/pipeline/multi-agent-refs/analysis/resolve.md +3 -1
  155. package/pipeline/multi-agent-refs/analysis/synthesis.md +4 -5
  156. package/pipeline/multi-agent-refs/analysis-template.md +10 -17
  157. package/pipeline/multi-agent-refs/channels/jira.md +1 -1
  158. package/pipeline/multi-agent-refs/component-dispatch.md +3 -3
  159. package/pipeline/multi-agent-refs/conventions-defaults.md +32 -32
  160. package/pipeline/multi-agent-refs/cross-cli-contract.md +10 -17
  161. package/pipeline/multi-agent-refs/features/autopilot-circuit-breaker.md +37 -9
  162. package/pipeline/multi-agent-refs/features/autopilot-operations.md +277 -0
  163. package/pipeline/multi-agent-refs/features/constitution.md +196 -0
  164. package/pipeline/multi-agent-refs/features/doctor.md +6 -3
  165. package/pipeline/multi-agent-refs/features/external-context-injection.md +6 -1
  166. package/pipeline/multi-agent-refs/features/jira-context.md +1 -1
  167. package/pipeline/multi-agent-refs/features/maturity-followup.md +43 -23
  168. package/pipeline/multi-agent-refs/features/model-fallback.md +9 -0
  169. package/pipeline/multi-agent-refs/features/phone-api.md +306 -0
  170. package/pipeline/multi-agent-refs/features/plan-critic.md +159 -0
  171. package/pipeline/multi-agent-refs/features/research.md +150 -0
  172. package/pipeline/multi-agent-refs/features/review-decision.md +185 -0
  173. package/pipeline/multi-agent-refs/features/scaffold.md +160 -0
  174. package/pipeline/multi-agent-refs/features/security-audit.md +6 -2
  175. package/pipeline/multi-agent-refs/features/skill-conformance.md +4 -1
  176. package/pipeline/multi-agent-refs/features/stack-adapters.md +116 -0
  177. package/pipeline/multi-agent-refs/features/unattended-gates.md +316 -0
  178. package/pipeline/multi-agent-refs/features/unattended-security.md +731 -0
  179. package/pipeline/multi-agent-refs/features/url-enrichment.md +2 -2
  180. package/pipeline/multi-agent-refs/features/usage-reporting.md +127 -35
  181. package/pipeline/multi-agent-refs/features/verify-by-test.md +1 -1
  182. package/pipeline/multi-agent-refs/features/visual-evidence.md +7 -7
  183. package/pipeline/multi-agent-refs/keychain.md +6 -11
  184. package/pipeline/multi-agent-refs/payload-contracts.md +2 -2
  185. package/pipeline/multi-agent-refs/phases/log-format.md +2 -3
  186. package/pipeline/multi-agent-refs/phases/modes.md +10 -12
  187. package/pipeline/multi-agent-refs/phases/operations.md +11 -5
  188. package/pipeline/multi-agent-refs/phases/phase-0-init.md +73 -81
  189. package/pipeline/multi-agent-refs/phases/phase-1-plan.md +22 -22
  190. package/pipeline/multi-agent-refs/phases/phase-2-dev.md +42 -66
  191. package/pipeline/multi-agent-refs/phases/phase-3-review.md +45 -79
  192. package/pipeline/multi-agent-refs/phases/phase-4-commit.md +28 -38
  193. package/pipeline/multi-agent-refs/phases/phase-5-report.md +27 -25
  194. package/pipeline/multi-agent-refs/phases.md +1 -1
  195. package/pipeline/multi-agent-refs/picker-contract.md +1 -1
  196. package/pipeline/multi-agent-refs/progress-contract.md +13 -16
  197. package/pipeline/multi-agent-refs/readiness-review.md +1 -1
  198. package/pipeline/multi-agent-refs/research/engine.md +91 -0
  199. package/pipeline/multi-agent-refs/rules.md +6 -4
  200. package/pipeline/multi-agent-refs/setup/repo-discovery.md +2 -2
  201. package/pipeline/multi-agent-refs/tracker-contract.md +2 -2
  202. package/pipeline/multi-agent-refs/unattended-contract.md +68 -16
  203. package/pipeline/rules/figma-pipeline.md +12 -12
  204. package/pipeline/schemas/agent-state.schema.json +480 -18
  205. package/pipeline/schemas/analysis-spec.schema.json +4 -4
  206. package/pipeline/schemas/answer-request.schema.json +24 -0
  207. package/pipeline/schemas/answer-result.schema.json +28 -0
  208. package/pipeline/schemas/autopilot-config.schema.json +111 -13
  209. package/pipeline/schemas/command-parameters.schema.json +99 -0
  210. package/pipeline/schemas/constitution.schema.json +56 -0
  211. package/pipeline/schemas/contract-error.schema.json +52 -0
  212. package/pipeline/schemas/design-check-config.schema.json +5 -1
  213. package/pipeline/schemas/issues.schema.json +61 -0
  214. package/pipeline/schemas/launch-plan.schema.json +46 -0
  215. package/pipeline/schemas/launch-request.schema.json +45 -0
  216. package/pipeline/schemas/launch.json +61 -0
  217. package/pipeline/schemas/launch.schema.json +84 -0
  218. package/pipeline/schemas/phases.json +2 -2
  219. package/pipeline/schemas/phases.schema.json +68 -0
  220. package/pipeline/schemas/phone-devices.schema.json +61 -0
  221. package/pipeline/schemas/phone-signed-request.schema.json +67 -0
  222. package/pipeline/schemas/plan-critique.schema.json +99 -0
  223. package/pipeline/schemas/plan-todos.schema.json +7 -7
  224. package/pipeline/schemas/planning-output.schema.json +5 -0
  225. package/pipeline/schemas/pr-request.schema.json +46 -0
  226. package/pipeline/schemas/prefs.schema.json +79 -8
  227. package/pipeline/schemas/research-output.schema.json +118 -0
  228. package/pipeline/schemas/review-file-exclusions.schema.json +25 -0
  229. package/pipeline/schemas/reviewer-output.schema.json +40 -4
  230. package/pipeline/schemas/run-questions.json +392 -0
  231. package/pipeline/schemas/run-questions.schema.json +118 -0
  232. package/pipeline/schemas/runs-index.schema.json +189 -0
  233. package/pipeline/schemas/scaffold-manifest.schema.json +43 -0
  234. package/pipeline/schemas/secret-patterns.schema.json +28 -0
  235. package/pipeline/schemas/stack-adapters.json +527 -0
  236. package/pipeline/schemas/stack-adapters.schema.json +184 -0
  237. package/pipeline/schemas/token-budget.json +1 -1
  238. package/pipeline/schemas/token-budget.schema.json +26 -0
  239. package/pipeline/schemas/triage-output.schema.json +64 -3
  240. package/pipeline/schemas/unattended-policy.json +139 -0
  241. package/pipeline/schemas/unattended-policy.schema.json +73 -0
  242. package/pipeline/schemas/unattended-profile.json +248 -0
  243. package/pipeline/schemas/unattended-profile.schema.json +198 -0
  244. package/pipeline/schemas/worktrees.schema.json +51 -0
  245. package/pipeline/scripts/README.md +1 -0
  246. package/pipeline/scripts/_autopilot-config.mjs +130 -0
  247. package/pipeline/scripts/_autopilot-ops.mjs +567 -0
  248. package/pipeline/scripts/_autopilot-outcomes.mjs +174 -0
  249. package/pipeline/scripts/_command-contract.mjs +384 -0
  250. package/pipeline/scripts/_cost.mjs +40 -0
  251. package/pipeline/scripts/_notices.mjs +160 -0
  252. package/pipeline/scripts/_phone-auth.mjs +485 -0
  253. package/pipeline/scripts/_pre-existing.mjs +294 -0
  254. package/pipeline/scripts/_redact.mjs +77 -0
  255. package/pipeline/scripts/_run-paths.mjs +4 -2
  256. package/pipeline/scripts/_stack-adapter.mjs +678 -0
  257. package/pipeline/scripts/_stack-routing.mjs +1 -1
  258. package/pipeline/scripts/agent-guard.py +348 -37
  259. package/pipeline/scripts/agent-guard.sh +41 -13
  260. package/pipeline/scripts/analysis-story-tree.mjs +79 -3
  261. package/pipeline/scripts/answer-question.mjs +181 -0
  262. package/pipeline/scripts/audit-log-rotate.sh +1 -4
  263. package/pipeline/scripts/audit-log.sh +4 -4
  264. package/pipeline/scripts/autopilot-arming.mjs +389 -21
  265. package/pipeline/scripts/autopilot-awake.mjs +255 -0
  266. package/pipeline/scripts/autopilot-intake.mjs +137 -36
  267. package/pipeline/scripts/autopilot-menubar.swift +156 -44
  268. package/pipeline/scripts/autopilot-publish.mjs +1625 -0
  269. package/pipeline/scripts/autopilot-runner.mjs +1678 -222
  270. package/pipeline/scripts/autopilot-status.sh +198 -33
  271. package/pipeline/scripts/build-lock.sh +120 -0
  272. package/pipeline/scripts/build-references.mjs +4 -1
  273. package/pipeline/scripts/build-stack-plugins.mjs +59 -22
  274. package/pipeline/scripts/capture-flush.sh +1 -1
  275. package/pipeline/scripts/capture-resume.sh +13 -9
  276. package/pipeline/scripts/check-derived-drift.mjs +52 -11
  277. package/pipeline/scripts/commands.mjs +88 -0
  278. package/pipeline/scripts/constitution.mjs +362 -0
  279. package/pipeline/scripts/contract-server.mjs +776 -0
  280. package/pipeline/scripts/cost-analyze.mjs +89 -39
  281. package/pipeline/scripts/diff-explain.mjs +12 -1
  282. package/pipeline/scripts/doctor.mjs +77 -28
  283. package/pipeline/scripts/evidence-gate.mjs +192 -12
  284. package/pipeline/scripts/feedback-send.mjs +4 -2
  285. package/pipeline/scripts/gate-ledger.mjs +449 -0
  286. package/pipeline/scripts/gc-abandoned.sh +132 -13
  287. package/pipeline/scripts/gen-facts.mjs +31 -15
  288. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  289. package/pipeline/scripts/github-ssh-setup.sh +140 -29
  290. package/pipeline/scripts/graph-mermaid.mjs +4 -1
  291. package/pipeline/scripts/issues.mjs +236 -0
  292. package/pipeline/scripts/jira-attach.sh +6 -2
  293. package/pipeline/scripts/jira-search.sh +4 -3
  294. package/pipeline/scripts/keychain-save.sh +125 -24
  295. package/pipeline/scripts/keychain.py +63 -93
  296. package/pipeline/scripts/launch-request.mjs +747 -0
  297. package/pipeline/scripts/localize-commands.mjs +4 -10
  298. package/pipeline/scripts/log-metric.sh +6 -5
  299. package/pipeline/scripts/maturity-followup.mjs +13 -4
  300. package/pipeline/scripts/memory-save.sh +25 -0
  301. package/pipeline/scripts/migrate-prefs.mjs +4 -3
  302. package/pipeline/scripts/open-questions-gate.mjs +276 -0
  303. package/pipeline/scripts/phase-tracker.sh +41 -27
  304. package/pipeline/scripts/phase0-exit-gate.mjs +22 -4
  305. package/pipeline/scripts/phone-devices.mjs +224 -0
  306. package/pipeline/scripts/plan-coverage-gate.mjs +200 -66
  307. package/pipeline/scripts/plan-critique-gate.mjs +591 -0
  308. package/pipeline/scripts/pr-request.mjs +188 -0
  309. package/pipeline/scripts/pre-commit-check.sh +115 -4
  310. package/pipeline/scripts/probe-evidence-capability.sh +44 -5
  311. package/pipeline/scripts/record-phase.mjs +71 -0
  312. package/pipeline/scripts/render-agent-log-cost.sh +17 -2
  313. package/pipeline/scripts/render-cost-summary.sh +1 -1
  314. package/pipeline/scripts/render-work-summary.sh +1 -1
  315. package/pipeline/scripts/require-supported-version.sh +4 -1
  316. package/pipeline/scripts/research-gate.mjs +704 -0
  317. package/pipeline/scripts/review-decision-gate.mjs +403 -0
  318. package/pipeline/scripts/routine-registry.mjs +5 -2
  319. package/pipeline/scripts/runs-index.mjs +135 -27
  320. package/pipeline/scripts/scaffold-gate.mjs +393 -0
  321. package/pipeline/scripts/skill-conformance.mjs +25 -8
  322. package/pipeline/scripts/skill-siblings.mjs +2 -1
  323. package/pipeline/scripts/smoke-cross-cli-behavior.sh +32 -6
  324. package/pipeline/scripts/smoke-schema-validation.sh +6 -2
  325. package/pipeline/scripts/spec-consistency-gate.mjs +469 -0
  326. package/pipeline/scripts/symbol-existence-gate.mjs +450 -0
  327. package/pipeline/scripts/test-gap-scan.mjs +40 -2
  328. package/pipeline/scripts/test-integrity-gate.mjs +20 -4
  329. package/pipeline/scripts/test-strength.mjs +484 -0
  330. package/pipeline/scripts/test-summary.mjs +651 -0
  331. package/pipeline/scripts/triage-memory.mjs +49 -9
  332. package/pipeline/scripts/unattended_policy.py +2786 -0
  333. package/pipeline/scripts/uninstall.mjs +10 -10
  334. package/pipeline/scripts/update-issue-progress.sh +1 -1
  335. package/pipeline/scripts/usage-identity.mjs +288 -0
  336. package/pipeline/scripts/usage-register.mjs +187 -67
  337. package/pipeline/scripts/usage-report.mjs +232 -67
  338. package/pipeline/scripts/validate-complaint-doc.mjs +28 -10
  339. package/pipeline/scripts/validate-planning.mjs +6 -0
  340. package/pipeline/scripts/verify-citations.mjs +151 -38
  341. package/pipeline/scripts/verify.mjs +58 -18
  342. package/pipeline/scripts/worktree-prepare.sh +126 -0
  343. package/pipeline/scripts/worktrees.mjs +124 -0
  344. package/pipeline/scripts/write-state.mjs +48 -17
  345. package/pipeline/skills/.skill-manifest.json +222 -226
  346. package/pipeline/skills/.skills-index.json +77 -88
  347. package/pipeline/skills/shared/README.md +44 -45
  348. package/pipeline/skills/shared/core/apple-archive-compliance/SKILL.md +3 -2
  349. package/pipeline/skills/shared/core/google-play-compliance/SKILL.md +3 -2
  350. package/pipeline/skills/shared/core/multi-agent/SKILL.md +10 -9
  351. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +6 -5
  352. package/pipeline/skills/shared/core/multi-agent-analysis-jira/SKILL.md +11 -3
  353. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +2 -1
  354. package/pipeline/skills/shared/core/multi-agent-autopilot/SKILL.md +4 -3
  355. package/pipeline/skills/shared/core/multi-agent-autopilot-off/SKILL.md +32 -6
  356. package/pipeline/skills/shared/core/multi-agent-autopilot-on/SKILL.md +66 -8
  357. package/pipeline/skills/shared/core/multi-agent-autopilot-status/SKILL.md +27 -9
  358. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +2 -1
  359. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +7 -4
  360. package/pipeline/skills/shared/core/multi-agent-complaint-analysis/SKILL.md +3 -2
  361. package/pipeline/skills/shared/core/multi-agent-create-jira/SKILL.md +2 -1
  362. package/pipeline/skills/shared/core/multi-agent-design-check/SKILL.md +7 -5
  363. package/pipeline/skills/shared/core/multi-agent-diff-explain/SKILL.md +2 -1
  364. package/pipeline/skills/shared/core/multi-agent-doctor/SKILL.md +3 -2
  365. package/pipeline/skills/shared/core/multi-agent-feedback/SKILL.md +2 -1
  366. package/pipeline/skills/shared/core/multi-agent-forget/SKILL.md +2 -1
  367. package/pipeline/skills/shared/core/multi-agent-garbage-collect/SKILL.md +2 -1
  368. package/pipeline/skills/shared/core/multi-agent-graph/SKILL.md +2 -1
  369. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +2 -1
  370. package/pipeline/skills/shared/core/multi-agent-ios-coding-standard/SKILL.md +2 -1
  371. package/pipeline/skills/shared/core/multi-agent-issue/SKILL.md +3 -2
  372. package/pipeline/skills/shared/core/multi-agent-jira/SKILL.md +2 -1
  373. package/pipeline/skills/shared/core/multi-agent-kill/SKILL.md +7 -4
  374. package/pipeline/skills/shared/core/multi-agent-language/SKILL.md +2 -1
  375. package/pipeline/skills/shared/core/multi-agent-log/SKILL.md +2 -1
  376. package/pipeline/skills/shared/core/multi-agent-manual-test/SKILL.md +2 -1
  377. package/pipeline/skills/shared/core/multi-agent-model/SKILL.md +2 -1
  378. package/pipeline/skills/shared/core/multi-agent-prune-logs/SKILL.md +2 -1
  379. package/pipeline/skills/shared/core/multi-agent-prune-prompts/SKILL.md +2 -1
  380. package/pipeline/skills/shared/core/multi-agent-purge/SKILL.md +2 -1
  381. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +2 -1
  382. package/pipeline/skills/shared/core/multi-agent-research/SKILL.md +35 -0
  383. package/pipeline/skills/shared/core/multi-agent-resume/SKILL.md +7 -3
  384. package/pipeline/skills/shared/core/multi-agent-review/SKILL.md +7 -5
  385. package/pipeline/skills/shared/core/multi-agent-review-analysis/SKILL.md +2 -1
  386. package/pipeline/skills/shared/core/multi-agent-review-issue/SKILL.md +3 -2
  387. package/pipeline/skills/shared/core/multi-agent-review-jira/SKILL.md +2 -1
  388. package/pipeline/skills/shared/core/multi-agent-route-off/SKILL.md +2 -1
  389. package/pipeline/skills/shared/core/multi-agent-route-on/SKILL.md +2 -1
  390. package/pipeline/skills/shared/core/multi-agent-route-status/SKILL.md +2 -1
  391. package/pipeline/skills/shared/core/multi-agent-routines/SKILL.md +2 -1
  392. package/pipeline/skills/shared/core/multi-agent-save/SKILL.md +3 -2
  393. package/pipeline/skills/shared/core/multi-agent-scaffold/SKILL.md +30 -0
  394. package/pipeline/skills/shared/core/multi-agent-scan/SKILL.md +2 -1
  395. package/pipeline/skills/shared/core/multi-agent-search/SKILL.md +4 -3
  396. package/pipeline/skills/shared/core/multi-agent-security-review/SKILL.md +2 -1
  397. package/pipeline/skills/shared/core/multi-agent-serve/SKILL.md +60 -0
  398. package/pipeline/skills/shared/core/multi-agent-setup/SKILL.md +8 -8
  399. package/pipeline/skills/shared/core/multi-agent-stack/SKILL.md +2 -1
  400. package/pipeline/skills/shared/core/multi-agent-status/SKILL.md +7 -8
  401. package/pipeline/skills/shared/core/multi-agent-steer/SKILL.md +2 -1
  402. package/pipeline/skills/shared/core/multi-agent-store-ready/SKILL.md +2 -1
  403. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +10 -10
  404. package/pipeline/skills/shared/core/multi-agent-test/SKILL.md +2 -1
  405. package/pipeline/skills/shared/core/multi-agent-test-accessibility/SKILL.md +2 -1
  406. package/pipeline/skills/shared/core/multi-agent-test-dark-mode/SKILL.md +2 -1
  407. package/pipeline/skills/shared/core/multi-agent-test-dynamic-type/SKILL.md +2 -1
  408. package/pipeline/skills/shared/core/multi-agent-test-screenshots/SKILL.md +2 -1
  409. package/pipeline/skills/shared/core/multi-agent-testflight-validation/SKILL.md +2 -1
  410. package/pipeline/skills/shared/core/multi-agent-uninstall/SKILL.md +3 -2
  411. package/pipeline/skills/shared/core/multi-agent-update/SKILL.md +2 -1
  412. package/pipeline/skills/shared/external/NOTICE-avdlee-swiftui-agent-skill.md +46 -0
  413. package/pipeline/skills/shared/external/NOTICE-dimillian-skills.md +9 -3
  414. package/pipeline/skills/shared/external/NOTICE-paul-hudson-skills.md +54 -0
  415. package/pipeline/skills/shared/external/NOTICE-swift-ios-skills.md +9 -11
  416. package/pipeline/skills/shared/external/NOTICE-vibeship-spawner-skills.md +203 -0
  417. package/pipeline/skills/shared/external/NOTICE-xcode-build-skills.md +10 -3
  418. package/pipeline/skills/shared/external/accessibility-compliance-accessibility-audit/SKILL.md +131 -25
  419. package/pipeline/skills/shared/external/agent-introspection-debugging/SKILL.md +4 -3
  420. package/pipeline/skills/shared/external/alarmkit/SKILL.md +2 -1
  421. package/pipeline/skills/shared/external/android-architecture/SKILL.md +85 -76
  422. package/pipeline/skills/shared/external/android-build-quality-gates/SKILL.md +51 -3
  423. package/pipeline/skills/shared/external/android-build-quality-gates/references/patterns.md +9 -0
  424. package/pipeline/skills/shared/external/android-datastore/SKILL.md +4 -3
  425. package/pipeline/skills/shared/external/android-design-tokens-codegen/SKILL.md +4 -3
  426. package/pipeline/skills/shared/external/android-jetpack-compose-expert/SKILL.md +106 -181
  427. package/pipeline/skills/shared/external/android-jetpack-compose-expert/references/patterns.md +147 -0
  428. package/pipeline/skills/shared/external/android-mvi-viewmodel/SKILL.md +45 -4
  429. package/pipeline/skills/shared/external/android-performance/SKILL.md +27 -2
  430. package/pipeline/skills/shared/external/android-performance/references/patterns.md +37 -37
  431. package/pipeline/skills/shared/external/api-patterns/SKILL.md +111 -67
  432. package/pipeline/skills/shared/external/api-patterns/references/contract-details.md +128 -0
  433. package/pipeline/skills/shared/external/api-security-best-practices/SKILL.md +136 -186
  434. package/pipeline/skills/shared/external/api-security-best-practices/references/abuse-controls.md +140 -0
  435. package/pipeline/skills/shared/external/api-security-best-practices/references/identity-and-access.md +207 -0
  436. package/pipeline/skills/shared/external/api-security-best-practices/references/operations.md +66 -0
  437. package/pipeline/skills/shared/external/api-security-best-practices/references/request-handling.md +175 -0
  438. package/pipeline/skills/shared/external/app-clips/SKILL.md +2 -0
  439. package/pipeline/skills/shared/external/app-intents/SKILL.md +2 -0
  440. package/pipeline/skills/shared/external/app-store-changelog/SKILL.md +4 -3
  441. package/pipeline/skills/shared/external/app-store-optimization/SKILL.md +2 -0
  442. package/pipeline/skills/shared/external/app-store-review/SKILL.md +2 -0
  443. package/pipeline/skills/shared/external/apple-on-device-ai/SKILL.md +2 -0
  444. package/pipeline/skills/shared/external/architecture/SKILL.md +106 -39
  445. package/pipeline/skills/shared/external/architecture/references/adr-and-review.md +76 -0
  446. package/pipeline/skills/shared/external/authentication/SKILL.md +2 -0
  447. package/pipeline/skills/shared/external/avkit/SKILL.md +2 -0
  448. package/pipeline/skills/shared/external/background-processing/SKILL.md +2 -1
  449. package/pipeline/skills/shared/external/backlog/SKILL.md +3 -2
  450. package/pipeline/skills/shared/external/callkit-voip/SKILL.md +2 -0
  451. package/pipeline/skills/shared/external/callkit-voip/evals/evals.json +1 -1
  452. package/pipeline/skills/shared/external/ci-cd-pipelines/SKILL.md +3 -2
  453. package/pipeline/skills/shared/external/clean-code/SKILL.md +192 -90
  454. package/pipeline/skills/shared/external/cloudkit-sync/SKILL.md +2 -1
  455. package/pipeline/skills/shared/external/cloudkit-sync/evals/evals.json +1 -1
  456. package/pipeline/skills/shared/external/compose-components/SKILL.md +4 -4
  457. package/pipeline/skills/shared/external/compose-navigation/SKILL.md +22 -22
  458. package/pipeline/skills/shared/external/compose-navigation/references/patterns.md +10 -10
  459. package/pipeline/skills/shared/external/compose-testing/SKILL.md +13 -6
  460. package/pipeline/skills/shared/external/compose-testing/references/patterns.md +59 -59
  461. package/pipeline/skills/shared/external/contacts-framework/SKILL.md +2 -0
  462. package/pipeline/skills/shared/external/context-compression/SKILL.md +118 -250
  463. package/pipeline/skills/shared/external/core-bluetooth/SKILL.md +2 -0
  464. package/pipeline/skills/shared/external/core-data/SKILL.md +2 -0
  465. package/pipeline/skills/shared/external/core-motion/SKILL.md +2 -0
  466. package/pipeline/skills/shared/external/core-nfc/SKILL.md +2 -0
  467. package/pipeline/skills/shared/external/coreml/SKILL.md +2 -1
  468. package/pipeline/skills/shared/external/council/SKILL.md +2 -1
  469. package/pipeline/skills/shared/external/cryptokit/SKILL.md +2 -0
  470. package/pipeline/skills/shared/external/css-modern/SKILL.md +3 -2
  471. package/pipeline/skills/shared/external/database-patterns/SKILL.md +3 -2
  472. package/pipeline/skills/shared/external/debugging-instruments/SKILL.md +2 -0
  473. package/pipeline/skills/shared/external/debugging-strategies/SKILL.md +130 -20
  474. package/pipeline/skills/shared/external/debugging-strategies/references/hard-cases.md +54 -0
  475. package/pipeline/skills/shared/external/device-integrity/SKILL.md +2 -0
  476. package/pipeline/skills/shared/external/docker-expert/SKILL.md +137 -381
  477. package/pipeline/skills/shared/external/docker-expert/references/patterns.md +98 -0
  478. package/pipeline/skills/shared/external/energykit/SKILL.md +2 -1
  479. package/pipeline/skills/shared/external/eventkit-calendar/SKILL.md +3 -1
  480. package/pipeline/skills/shared/external/eventkit-calendar/evals/evals.json +2 -2
  481. package/pipeline/skills/shared/external/fastapi-pro/SKILL.md +116 -185
  482. package/pipeline/skills/shared/external/fastapi-pro/references/app-structure.md +235 -0
  483. package/pipeline/skills/shared/external/fastapi-pro/references/testing-and-deployment.md +69 -0
  484. package/pipeline/skills/shared/external/firebase/SKILL.md +4 -3
  485. package/pipeline/skills/shared/external/github-actions-templates/SKILL.md +151 -293
  486. package/pipeline/skills/shared/external/github-actions-templates/references/patterns.md +96 -0
  487. package/pipeline/skills/shared/external/healthkit/SKILL.md +2 -0
  488. package/pipeline/skills/shared/external/hig-components-content/SKILL.md +69 -72
  489. package/pipeline/skills/shared/external/hig-components-content/references/content-views.md +111 -0
  490. package/pipeline/skills/shared/external/hig-components-layout/SKILL.md +77 -86
  491. package/pipeline/skills/shared/external/hig-components-layout/references/containers.md +98 -0
  492. package/pipeline/skills/shared/external/hig-components-status/SKILL.md +150 -78
  493. package/pipeline/skills/shared/external/hig-components-system/SKILL.md +76 -97
  494. package/pipeline/skills/shared/external/hig-components-system/references/surfaces.md +98 -0
  495. package/pipeline/skills/shared/external/hig-foundations/SKILL.md +48 -76
  496. package/pipeline/skills/shared/external/hig-foundations/references/foundations-detail.md +132 -0
  497. package/pipeline/skills/shared/external/hig-inputs/SKILL.md +147 -106
  498. package/pipeline/skills/shared/external/hig-patterns/SKILL.md +61 -77
  499. package/pipeline/skills/shared/external/hig-patterns/references/patterns.md +92 -0
  500. package/pipeline/skills/shared/external/hig-platforms/SKILL.md +147 -77
  501. package/pipeline/skills/shared/external/hig-technologies/SKILL.md +61 -121
  502. package/pipeline/skills/shared/external/hig-technologies/references/technologies.md +108 -0
  503. package/pipeline/skills/shared/external/homekit-matter/SKILL.md +2 -0
  504. package/pipeline/skills/shared/external/homekit-matter/evals/evals.json +1 -1
  505. package/pipeline/skills/shared/external/html-semantic/SKILL.md +3 -2
  506. package/pipeline/skills/shared/external/humanizer/SKILL.md +2 -1
  507. package/pipeline/skills/shared/external/ios-accessibility/SKILL.md +2 -0
  508. package/pipeline/skills/shared/external/ios-coding-standard/SKILL.md +2 -1
  509. package/pipeline/skills/shared/external/ios-coding-standard/references/STANDARD.md +14 -2
  510. package/pipeline/skills/shared/external/ios-coding-standard/references/rules.yml +2 -2
  511. package/pipeline/skills/shared/external/ios-debugger-agent/SKILL.md +4 -3
  512. package/pipeline/skills/shared/external/ios-localization/SKILL.md +8 -6
  513. package/pipeline/skills/shared/external/ios-localization/evals/evals.json +2 -2
  514. package/pipeline/skills/shared/external/ios-localization/references/string-catalogs.md +11 -11
  515. package/pipeline/skills/shared/external/ios-module-structure/SKILL.md +2 -1
  516. package/pipeline/skills/shared/external/ios-networking/SKILL.md +2 -0
  517. package/pipeline/skills/shared/external/ios-simulator/SKILL.md +2 -0
  518. package/pipeline/skills/shared/external/kotlin-coroutines-expert/SKILL.md +102 -193
  519. package/pipeline/skills/shared/external/kotlin-coroutines-expert/references/patterns.md +103 -0
  520. package/pipeline/skills/shared/external/live-activities/SKILL.md +4 -2
  521. package/pipeline/skills/shared/external/live-activities/evals/evals.json +2 -2
  522. package/pipeline/skills/shared/external/localization-reuse-map/SKILL.md +7 -9
  523. package/pipeline/skills/shared/external/localization-reuse-map/reference/sources-and-recipes.md +3 -3
  524. package/pipeline/skills/shared/external/localization-reuse-map/scripts/resolve-new-values.py +2 -2
  525. package/pipeline/skills/shared/external/macos-menubar-tuist-app/SKILL.md +4 -3
  526. package/pipeline/skills/shared/external/macos-spm-app-packaging/SKILL.md +5 -3
  527. package/pipeline/skills/shared/external/mapkit-location/SKILL.md +2 -0
  528. package/pipeline/skills/shared/external/mapkit-location/evals/evals.json +1 -1
  529. package/pipeline/skills/shared/external/metrickit-diagnostics/SKILL.md +2 -0
  530. package/pipeline/skills/shared/external/metrickit-diagnostics/evals/evals.json +1 -1
  531. package/pipeline/skills/shared/external/monorepo-architect/SKILL.md +149 -46
  532. package/pipeline/skills/shared/external/monorepo-architect/references/patterns.md +70 -0
  533. package/pipeline/skills/shared/external/musickit-audio/SKILL.md +2 -0
  534. package/pipeline/skills/shared/external/musickit-audio/evals/evals.json +1 -1
  535. package/pipeline/skills/shared/external/natural-language/SKILL.md +2 -0
  536. package/pipeline/skills/shared/external/nextjs-app-router/SKILL.md +3 -2
  537. package/pipeline/skills/shared/external/nodejs-backend-patterns/SKILL.md +91 -21
  538. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/runtime-patterns.md +247 -0
  539. package/pipeline/skills/shared/external/nodejs-backend-patterns/references/testing-and-frameworks.md +57 -0
  540. package/pipeline/skills/shared/external/observability-engineer/SKILL.md +155 -232
  541. package/pipeline/skills/shared/external/observability-engineer/references/patterns.md +75 -0
  542. package/pipeline/skills/shared/external/passkit-wallet/SKILL.md +2 -0
  543. package/pipeline/skills/shared/external/passkit-wallet/evals/evals.json +2 -2
  544. package/pipeline/skills/shared/external/passkit-wallet/references/wallet-passes.md +18 -18
  545. package/pipeline/skills/shared/external/pdfkit/SKILL.md +2 -1
  546. package/pipeline/skills/shared/external/pencilkit-drawing/SKILL.md +2 -0
  547. package/pipeline/skills/shared/external/pencilkit-drawing/evals/evals.json +1 -1
  548. package/pipeline/skills/shared/external/permissionkit/SKILL.md +2 -0
  549. package/pipeline/skills/shared/external/photos-camera-media/SKILL.md +2 -1
  550. package/pipeline/skills/shared/external/push-notifications/SKILL.md +2 -0
  551. package/pipeline/skills/shared/external/python-patterns/SKILL.md +3 -2
  552. package/pipeline/skills/shared/external/react-best-practices/SKILL.md +3 -2
  553. package/pipeline/skills/shared/external/realitykit-ar/SKILL.md +2 -2
  554. package/pipeline/skills/shared/external/realitykit-ar/evals/evals.json +1 -1
  555. package/pipeline/skills/shared/external/rest-api-design/SKILL.md +3 -2
  556. package/pipeline/skills/shared/external/retrofit-networking/SKILL.md +7 -7
  557. package/pipeline/skills/shared/external/retrofit-networking/references/patterns.md +69 -69
  558. package/pipeline/skills/shared/external/room-database/references/patterns.md +126 -126
  559. package/pipeline/skills/shared/external/search-first/SKILL.md +2 -1
  560. package/pipeline/skills/shared/external/security-review/SKILL.md +4 -3
  561. package/pipeline/skills/shared/external/shareplay-activities/SKILL.md +2 -2
  562. package/pipeline/skills/shared/external/signal-community/SKILL.md +1 -1
  563. package/pipeline/skills/shared/external/skill-creator/SKILL.md +2 -1
  564. package/pipeline/skills/shared/external/speech-recognition/SKILL.md +2 -1
  565. package/pipeline/skills/shared/external/spm-build-analysis/SKILL.md +2 -0
  566. package/pipeline/skills/shared/external/storekit/SKILL.md +2 -0
  567. package/pipeline/skills/shared/external/swift-api-design-guidelines/SKILL.md +2 -0
  568. package/pipeline/skills/shared/external/swift-architecture/SKILL.md +2 -1
  569. package/pipeline/skills/shared/external/swift-charts/SKILL.md +2 -1
  570. package/pipeline/skills/shared/external/swift-codable/SKILL.md +2 -0
  571. package/pipeline/skills/shared/external/swift-concurrency/SKILL.md +3 -1
  572. package/pipeline/skills/shared/external/swift-concurrency-expert/SKILL.md +5 -4
  573. package/pipeline/skills/shared/external/swift-concurrency-pro/SKILL.md +2 -1
  574. package/pipeline/skills/shared/external/swift-formatstyle/SKILL.md +2 -0
  575. package/pipeline/skills/shared/external/swift-language/SKILL.md +3 -1
  576. package/pipeline/skills/shared/external/swift-security/SKILL.md +2 -1
  577. package/pipeline/skills/shared/external/swift-testing/SKILL.md +2 -0
  578. package/pipeline/skills/shared/external/swift-testing-pro/SKILL.md +2 -1
  579. package/pipeline/skills/shared/external/swift-testing-pro/references/new-features.md +10 -0
  580. package/pipeline/skills/shared/external/swiftdata/SKILL.md +2 -0
  581. package/pipeline/skills/shared/external/swiftdata-pro/SKILL.md +2 -1
  582. package/pipeline/skills/shared/external/swiftlint/SKILL.md +2 -0
  583. package/pipeline/skills/shared/external/swiftui-animation/SKILL.md +2 -0
  584. package/pipeline/skills/shared/external/swiftui-expert-skill/SKILL.md +3 -1
  585. package/pipeline/skills/shared/external/swiftui-gestures/SKILL.md +2 -0
  586. package/pipeline/skills/shared/external/swiftui-layout-components/SKILL.md +2 -0
  587. package/pipeline/skills/shared/external/swiftui-liquid-glass/SKILL.md +2 -0
  588. package/pipeline/skills/shared/external/swiftui-navigation/SKILL.md +2 -0
  589. package/pipeline/skills/shared/external/swiftui-patterns/SKILL.md +3 -1
  590. package/pipeline/skills/shared/external/swiftui-performance/SKILL.md +3 -1
  591. package/pipeline/skills/shared/external/swiftui-performance-audit/SKILL.md +5 -4
  592. package/pipeline/skills/shared/external/swiftui-pro/SKILL.md +2 -1
  593. package/pipeline/skills/shared/external/swiftui-ui-patterns/SKILL.md +5 -4
  594. package/pipeline/skills/shared/external/swiftui-uikit-interop/SKILL.md +2 -0
  595. package/pipeline/skills/shared/external/swiftui-view-refactor/SKILL.md +4 -3
  596. package/pipeline/skills/shared/external/swiftui-webkit/SKILL.md +2 -0
  597. package/pipeline/skills/shared/external/tailwind-css/SKILL.md +3 -2
  598. package/pipeline/skills/shared/external/testing-backend/SKILL.md +3 -2
  599. package/pipeline/skills/shared/external/tipkit/SKILL.md +2 -0
  600. package/pipeline/skills/shared/external/typescript-patterns/SKILL.md +3 -2
  601. package/pipeline/skills/shared/external/vision-framework/SKILL.md +2 -0
  602. package/pipeline/skills/shared/external/vue-composition/SKILL.md +3 -2
  603. package/pipeline/skills/shared/external/weatherkit/SKILL.md +2 -0
  604. package/pipeline/skills/shared/external/web-accessibility/SKILL.md +3 -2
  605. package/pipeline/skills/shared/external/web-performance/SKILL.md +3 -2
  606. package/pipeline/skills/shared/external/web-testing/SKILL.md +3 -2
  607. package/pipeline/skills/shared/external/widgetkit/SKILL.md +2 -0
  608. package/pipeline/skills/shared/external/widgetkit/references/widgetkit-advanced.md +1 -1
  609. package/pipeline/skills/shared/external/xcode-build-benchmark/SKILL.md +2 -0
  610. package/pipeline/skills/shared/external/xcode-build-fixer/SKILL.md +2 -0
  611. package/pipeline/skills/shared/external/xcode-build-orchestrator/SKILL.md +2 -0
  612. package/pipeline/skills/shared/external/xcode-compilation-analyzer/SKILL.md +2 -0
  613. package/pipeline/skills/shared/external/xcode-project-analyzer/SKILL.md +2 -0
  614. package/pipeline/skills/skills-index.md +40 -41
  615. package/docs/token-budget-history.md +0 -24
  616. package/pipeline/skills/shared/external/agentflow/SKILL.md +0 -199
  617. package/pipeline/skills/shared/external/android-ui-verification/SKILL.md +0 -66
  618. package/pipeline/skills/shared/external/api-security-best-practices/references/auth.md +0 -299
  619. package/pipeline/skills/shared/external/api-security-best-practices/references/input-validation.md +0 -255
  620. package/pipeline/skills/shared/external/api-security-best-practices/references/rate-limiting.md +0 -167
  621. package/pipeline/skills/shared/external/closed-loop-delivery/SKILL.md +0 -116
  622. package/pipeline/skills/shared/external/ios-developer/SKILL.md +0 -216
@@ -1,215 +1,140 @@
1
1
  ---
2
2
  name: android-jetpack-compose-expert
3
- description: "Expert guidance for building modern Android UIs with Jetpack Compose, covering state management, navigation, performance, and Material Design 3. Use when building or reviewing Compose UI, state handling or navigation."
4
- risk: safe
5
- source: community
6
- date_added: "2026-02-27"
3
+ description: "Compose runtime expertise: state ownership, recomposition and stability, side-effect APIs, lazy lists, modifier order, Modifier.Node. Use when writing or reviewing composables, or when a screen recomposes too often or loses state."
4
+ metadata:
5
+ risk: safe
6
+ source: multi-agent-pipeline
7
+ date_added: "2026-09-25"
8
+ not-for: compose-components, android-performance
7
9
  ---
8
10
 
9
- # Android Jetpack Compose Expert
11
+ # Jetpack Compose Runtime Expertise
10
12
 
11
- ## Overview
13
+ Compose calls your functions again whenever state they read changes. Almost
14
+ every Compose bug is a disagreement between what the author thought would be
15
+ re-run and what the runtime actually re-runs. This skill is about that model.
12
16
 
13
- A comprehensive guide for building production-quality Android applications using Jetpack Compose. This skill covers architectural patterns, state management with ViewModels, navigation type-safety, and performance optimization techniques.
17
+ Sibling skills cover the rest: Material components and theming in
18
+ `compose-components`, routes and back stack in `compose-navigation`, UI and
19
+ ViewModel tests in `compose-testing`, layering and DI in `android-architecture`,
20
+ the MVI base class in `android-mvi-viewmodel`, and profiling, Baseline Profiles
21
+ and R8 in `android-performance`.
14
22
 
15
- ## When to Use This Skill
23
+ ## The three phases
16
24
 
17
- - Use when starting a new Android project with Jetpack Compose.
18
- - Use when migrating legacy XML layouts to Compose.
19
- - Use when implementing complex UI state management and side effects.
20
- - Use when optimizing Compose performance (recomposition counts, stability).
21
- - Use when setting up Navigation with type safety.
25
+ A frame runs composition (what to show), layout (where and how big), then
26
+ drawing. A state read is tracked in the phase where it happens, and only that
27
+ phase and the ones after it re-run when the value changes.
22
28
 
23
- ## Step-by-Step Guide
29
+ | Read happens in | Re-runs on change | Typical API |
30
+ |---|---|---|
31
+ | Composable body | composition, layout, draw | `Text(text = state.title)` |
32
+ | Layout lambda | layout, draw | `Modifier.offset { IntOffset(x, 0) }` |
33
+ | Draw lambda | draw only | `Modifier.drawBehind { drawRect(color) }` |
24
34
 
25
- ### 1. Project Setup & Dependencies
26
-
27
- Ensure your `libs.versions.toml` includes the necessary Compose BOM and libraries.
28
-
29
- ```kotlin
30
- [versions]
31
- composeBom = "2024.02.01"
32
- activityCompose = "1.8.2"
33
-
34
- [libraries]
35
- androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
36
- androidx-ui = { group = "androidx.compose.ui", name = "ui" }
37
- androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
38
- androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
39
- androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
40
- androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
41
- ```
42
-
43
- ### 2. State Management Pattern (MVI/MVVM)
44
-
45
- Use `ViewModel` with `StateFlow` to expose UI state. Avoid exposing `MutableStateFlow`.
35
+ Move fast-changing reads (scroll offsets, animation values) into the lambda
36
+ variants so they skip composition entirely:
46
37
 
47
38
  ```kotlin
48
- // UI State Definition
49
- data class UserUiState(
50
- val isLoading: Boolean = false,
51
- val user: User? = null,
52
- val error: String? = null
53
- )
54
-
55
- // ViewModel
56
- class UserViewModel @Inject constructor(
57
- private val userRepository: UserRepository
58
- ) : ViewModel() {
59
-
60
- private val _uiState = MutableStateFlow(UserUiState())
61
- val uiState: StateFlow<UserUiState> = _uiState.asStateFlow()
62
-
63
- fun loadUser() {
64
- viewModelScope.launch {
65
- _uiState.update { it.copy(isLoading = true) }
66
- try {
67
- val user = userRepository.getUser()
68
- _uiState.update { it.copy(user = user, isLoading = false) }
69
- } catch (e: Exception) {
70
- _uiState.update { it.copy(error = e.message, isLoading = false) }
71
- }
72
- }
73
- }
74
- }
75
- ```
76
-
77
- Reduce state with `.update { copy(...) }` (atomic read-modify-write; safe under concurrent emissions) rather than assigning `_uiState.value = ...`.
39
+ // Recomposes the whole header on every scroll pixel
40
+ Header(modifier = Modifier.alpha(1f - listState.firstVisibleItemScrollOffset / 300f))
78
41
 
79
- For a stream-backed screen, expose the state with `.stateIn` and let the first load fire lazily from `onStart` instead of `init {}`. `onStart` runs when the UI actually starts collecting, so a screen that is created but never shown does no work, and the fetch restarts correctly with the subscription.
80
-
81
- ```kotlin
82
- private val _uiState = MutableStateFlow(UserUiState())
83
-
84
- val uiState: StateFlow<UserUiState> = _uiState
85
- .onStart { loadInitialData() }
86
- .stateIn(
87
- scope = viewModelScope,
88
- started = SharingStarted.WhileSubscribed(5_000),
89
- initialValue = UserUiState()
90
- )
42
+ // Reads the value in the draw phase only
43
+ Header(modifier = Modifier.graphicsLayer {
44
+ alpha = 1f - listState.firstVisibleItemScrollOffset / 300f
45
+ })
91
46
  ```
92
47
 
93
- Prefer `WhileSubscribed(5_000)` over `Lazily` or `Eagerly`. The 5-second stop timeout keeps the upstream alive across a configuration change (rotation, night-mode toggle) so the flow is not torn down and re-collected for a transient loss of subscribers, while still cancelling upstream work when the screen truly goes away. `Eagerly`/`Lazily` never stop, so they hold upstream resources for the ViewModel's whole life.
48
+ ## Derived values
94
49
 
95
- ### 3. Creating the Screen Composable
50
+ Computing something from state inside the body is fine when it is cheap. Two
51
+ cases need care:
96
52
 
97
- Consume the state in a "Screen" composable and pass data down to stateless components.
53
+ - **Expensive work** keyed on inputs: `val sorted = remember(products, sortKey) { products.sortedWith(sortKey.comparator) }`.
54
+ - **A value that changes less often than its source**: wrap it in
55
+ `derivedStateOf` so readers recompose only when the result flips.
98
56
 
99
57
  ```kotlin
100
- @Composable
101
- fun UserScreen(
102
- viewModel: UserViewModel = hiltViewModel()
103
- ) {
104
- val uiState by viewModel.uiState.collectAsStateWithLifecycle()
105
-
106
- UserContent(
107
- uiState = uiState,
108
- onRetry = viewModel::loadUser
109
- )
110
- }
111
-
112
- @Composable
113
- fun UserContent(
114
- uiState: UserUiState,
115
- onRetry: () -> Unit
116
- ) {
117
- Scaffold { padding ->
118
- Box(modifier = Modifier.padding(padding)) {
119
- when {
120
- uiState.isLoading -> CircularProgressIndicator()
121
- uiState.error != null -> ErrorView(uiState.error, onRetry)
122
- uiState.user != null -> UserProfile(uiState.user)
123
- }
124
- }
125
- }
58
+ val showScrollToTop by remember {
59
+ derivedStateOf { listState.firstVisibleItemIndex > 0 }
126
60
  }
127
61
  ```
128
62
 
129
- ## Examples
130
-
131
- ### Example 1: Type-Safe Navigation
63
+ Do not use `derivedStateOf` to combine two ordinary parameters; it only helps
64
+ when the source changes more often than the output.
132
65
 
133
- Using the new Navigation Compose Type Safety (available in recent versions).
66
+ ## Stability and skipping
134
67
 
135
- ```kotlin
136
- // Define Destinations
137
- @Serializable
138
- object Home
139
-
140
- @Serializable
141
- data class Profile(val userId: String)
142
-
143
- // Setup NavHost
144
- @Composable
145
- fun AppNavHost(navController: NavHostController) {
146
- NavHost(navController, startDestination = Home) {
147
- composable<Home> {
148
- HomeScreen(onNavigateToProfile = { id ->
149
- navController.navigate(Profile(userId = id))
150
- })
151
- }
152
- composable<Profile> { backStackEntry ->
153
- val profile: Profile = backStackEntry.toRoute()
154
- ProfileScreen(userId = profile.userId)
155
- }
156
- }
157
- }
158
- ```
68
+ With strong skipping mode (default since Kotlin 2.0.20 with the Compose compiler
69
+ Gradle plugin), a restartable composable skips when every argument is equal to
70
+ the previous one: `equals` for stable types, reference identity for unstable
71
+ ones. Lambdas are remembered automatically. So the practical rules are:
159
72
 
160
- ### Example 2: One-Shot Events (Navigation, Toast, Snackbar)
161
-
162
- Keep navigation commands, snackbars and toasts out of the state class. State is a value that survives and is re-read on every recomposition, so an event stored in state fires again after a config change (a duplicated navigation, a re-shown snackbar). Model one-shot events as a `Channel` and expose them as a flow.
163
-
164
- ```kotlin
165
- sealed interface UserEvent {
166
- data class ShowMessage(val text: String) : UserEvent
167
- data class NavigateToProfile(val userId: String) : UserEvent
168
- }
73
+ - Pass immutable data. `data class Product(val id: String, val name: String)`
74
+ with `val` properties of stable types is stable.
75
+ - `List`, `Set` and `Map` are interfaces and count as unstable. Use
76
+ `kotlinx.collections.immutable` (`ImmutableList`) or list them in a
77
+ compiler stability configuration file if they are never mutated.
78
+ - Classes from modules without the Compose compiler (domain modules) are
79
+ unstable unless listed in the stability config or wrapped in a UI model.
80
+ - Do not allocate a new list or object in the parent body just to pass it down;
81
+ identity changes and the child cannot skip.
169
82
 
170
- private val _events = Channel<UserEvent>(Channel.RENDEZVOUS)
171
- val events: Flow<UserEvent> = _events.receiveAsFlow()
83
+ Mark a class `@Immutable` only when it truly never changes after construction,
84
+ and `@Stable` when changes are notified through snapshot state. A wrong
85
+ annotation causes stale UI, which is worse than an extra recomposition.
172
86
 
173
- fun onSaved(userId: String) {
174
- viewModelScope.launch {
175
- _events.send(UserEvent.NavigateToProfile(userId))
176
- }
177
- }
178
- ```
87
+ Measure before annotating: the Layout Inspector recomposition counts and the
88
+ compiler reports (`reportsDestination`) show what is skipping. Tooling detail
89
+ lives in `android-performance`.
179
90
 
180
- Collect lifecycle-aware so events are only delivered while the screen is at least `STARTED`.
91
+ ## Lazy lists
181
92
 
182
93
  ```kotlin
183
- @Composable
184
- fun UserScreen(viewModel: UserViewModel = hiltViewModel()) {
185
- val lifecycleOwner = LocalLifecycleOwner.current
186
- LaunchedEffect(Unit) {
187
- lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
188
- viewModel.events.collect { event ->
189
- when (event) {
190
- is UserEvent.ShowMessage -> snackbarHostState.showSnackbar(event.text)
191
- is UserEvent.NavigateToProfile -> onNavigateToProfile(event.userId)
192
- }
193
- }
194
- }
94
+ LazyColumn(state = listState) {
95
+ items(
96
+ items = state.notes,
97
+ key = { note -> note.id },
98
+ contentType = { note -> if (note.isPinned) "pinned" else "regular" },
99
+ ) { note ->
100
+ NoteRow(note = note, onClick = { onNoteClick(note.id) }, modifier = Modifier.animateItem())
195
101
  }
196
- // ...
197
102
  }
198
103
  ```
199
104
 
200
- A `Channel` with `RENDEZVOUS` capacity buffers events while there is no active collector and hands each to exactly one consumer, so nothing is lost and nothing is replayed. `SharedFlow(replay = 0)` drops any event emitted while the collector is paused (for example during a config change), which loses one-shot events; that is why the channel is the safer default here.
201
-
202
- For the full MVI base-ViewModel pattern (intent reduction, effect plumbing, a reusable base class), see the `android-mvi-viewmodel` skill.
203
-
204
- ## Best Practices
205
-
206
- - ✅ **Do:** Use `remember` and `derivedStateOf` to minimize unnecessary calculations during recomposition.
207
- - ✅ **Do:** Mark data classes used in UI state as `@Immutable` or `@Stable` if they contain `List` or other unstable types to enable smart recomposition skipping.
208
- - ✅ **Do:** Use `LaunchedEffect` for one-off side effects (like showing a Snackbar) triggered by state changes.
209
- - ❌ **Don't:** Perform expensive operations (like sorting a list) directly inside the Composable function body without `remember`.
210
- - ❌ **Don't:** Pass `ViewModel` instances down to child components. Pass only the data (state) and lambda callbacks (events).
211
-
212
- ## Troubleshooting
213
-
214
- **Problem:** Infinite Recomposition loop.
215
- **Solution:** Check if you are creating new object instances (like `List` or `Modifier`) inside the composition without `remember`, or if you are updating state inside the composition phase instead of a side-effect or callback. Use Layout Inspector to debug recomposition counts.
105
+ - `key` keeps item state and scroll position attached to the right row when
106
+ the list reorders or items are inserted. It must be stable and unique, and
107
+ `Bundle`-saveable (String, Int, Long).
108
+ - `contentType` lets the list reuse compositions between rows of the same shape.
109
+ - `Modifier.animateItem()` animates insertion, removal and moves; it needs keys.
110
+ - Never nest a vertically scrolling lazy list inside `verticalScroll`; give the
111
+ inner list a fixed height or flatten it into the outer list with `item {}`
112
+ blocks.
113
+
114
+ ## CompositionLocal
115
+
116
+ Use a `CompositionLocal` for values that are ambient to a subtree and rarely
117
+ change: theme tokens, a formatter, analytics sink. Do not use it to smuggle
118
+ screen data or ViewModels past parameter lists; that hides dependencies and
119
+ makes previews fail at runtime.
120
+
121
+ - `staticCompositionLocalOf` for values that almost never change (whole subtree
122
+ recomposes on change, reads are cheap).
123
+ - `compositionLocalOf` when the value changes and only readers should recompose.
124
+ - Always provide a sensible default or throw with a message naming the provider.
125
+
126
+ ## Review checklist
127
+
128
+ - Screen composable receives state and lambdas, not a ViewModel.
129
+ - State collected with `collectAsStateWithLifecycle`.
130
+ - No side effect, logging or allocation-heavy work in a composable body.
131
+ - Every effect has keys that match its real dependencies.
132
+ - Fast-changing values read in layout or draw lambdas.
133
+ - Lazy list items have stable keys; no nested same-direction scrolling.
134
+ - Parameters are stable or the instability is measured and accepted.
135
+ - Custom modifiers use `Modifier.Node`; `modifier` parameter applied once.
136
+ - User-facing strings via `stringResource`, sizes via theme or dimension tokens.
137
+
138
+ ## References
139
+
140
+ - `references/patterns.md`: state holders and hoisting, the side-effect API table with key rules, and modifier order plus Modifier.Node. Load it when the task reaches those topics.
@@ -0,0 +1,147 @@
1
+ # Compose runtime patterns
2
+
3
+ Loaded on demand from `../SKILL.md`.
4
+
5
+ ## Who owns a piece of state
6
+
7
+ Decide ownership before writing the composable. The rule of thumb: state lives
8
+ in the lowest common parent of everything that reads or writes it, and no lower.
9
+
10
+ | Kind of state | Owner | Survives |
11
+ |---|---|---|
12
+ | Screen data from a repository | ViewModel, exposed as `StateFlow` | configuration change |
13
+ | User input the ViewModel must validate | ViewModel | configuration change |
14
+ | Pure UI element state (expanded, scroll) | composable via `remember` / `rememberSaveable` | depends on the API |
15
+ | Shared by a group of UI elements | a plain state holder class, remembered | same as `remember` |
16
+
17
+ Collect ViewModel state with the lifecycle-aware collector so collection stops
18
+ when the screen is not visible:
19
+
20
+ ```kotlin
21
+ @Composable
22
+ fun OrderListRoute(viewModel: OrderListViewModel = hiltViewModel()) {
23
+ val state by viewModel.state.collectAsStateWithLifecycle()
24
+ OrderListScreen(
25
+ state = state,
26
+ onRefresh = viewModel::refresh,
27
+ onOrderClick = viewModel::openOrder,
28
+ )
29
+ }
30
+ ```
31
+
32
+ `OrderListScreen` takes only values and lambdas. That split keeps the screen
33
+ previewable and testable without a ViewModel, and it stops the ViewModel from
34
+ leaking into child composables.
35
+
36
+ ### remember versus rememberSaveable
37
+
38
+ - `remember` keeps a value across recompositions; it is lost when the
39
+ composable leaves the composition or the activity is recreated.
40
+ - `rememberSaveable` also writes to the saved-instance bundle, so it survives
41
+ rotation and process death. Use it for text field contents, selected tab,
42
+ expanded flags. Custom types need a `Saver` or `@Parcelize`.
43
+ - Pass keys when the remembered value depends on an input:
44
+ `remember(noteId) { NoteDraft(noteId) }` rebuilds when `noteId` changes.
45
+
46
+ ### A plain state holder
47
+
48
+ When several related UI values move together, group them in a class rather
49
+ than scattering `mutableStateOf` calls:
50
+
51
+ ```kotlin
52
+ @Stable
53
+ class FilterBarState(initial: Set<Category>) {
54
+ var selected by mutableStateOf(initial)
55
+ private set
56
+ var isExpanded by mutableStateOf(false)
57
+ private set
58
+
59
+ fun toggle(category: Category) {
60
+ selected = if (category in selected) selected - category else selected + category
61
+ }
62
+ fun expand(value: Boolean) { isExpanded = value }
63
+ }
64
+
65
+ @Composable
66
+ fun rememberFilterBarState(initial: Set<Category> = emptySet()) =
67
+ remember { FilterBarState(initial) }
68
+ ```
69
+
70
+ ## Side effects
71
+
72
+ The body of a composable must be free of side effects because it can run many
73
+ times, out of order, or be discarded. Every effect goes through one of these:
74
+
75
+ | Need | API | Restarts when |
76
+ |---|---|---|
77
+ | Run a suspend block tied to the composable | `LaunchedEffect(key)` | key changes |
78
+ | Register and unregister a listener | `DisposableEffect(key)` with `onDispose` | key changes |
79
+ | Launch from a click handler | `rememberCoroutineScope()` | never; scope dies with the composable |
80
+ | Publish Compose state to non-Compose code after every successful recomposition | `SideEffect` | every recomposition |
81
+ | Turn a callback-based source into state | `produceState` | key changes |
82
+ | Observe state as a Flow inside an effect | `snapshotFlow { ... }` | emits on change |
83
+ | Use the latest lambda inside a long effect without restarting it | `rememberUpdatedState(lambda)` | never |
84
+
85
+ ```kotlin
86
+ @Composable
87
+ fun AutoSaveDraft(draft: String, onSave: suspend (String) -> Unit) {
88
+ val currentDraft by rememberUpdatedState(draft)
89
+ val latestOnSave by rememberUpdatedState(onSave)
90
+ LaunchedEffect(Unit) {
91
+ snapshotFlow { currentDraft }
92
+ .debounce(500)
93
+ .distinctUntilChanged()
94
+ .collect { latestOnSave(it) }
95
+ }
96
+ }
97
+ ```
98
+
99
+ `snapshotFlow` only sees changes to snapshot state it reads, so the plain
100
+ `draft` parameter is wrapped in `rememberUpdatedState` first; reading the bare
101
+ parameter would capture its first value and never emit again.
102
+
103
+ Choosing keys: use the identity the effect depends on (`LaunchedEffect(orderId)`),
104
+ never `true` to mean "once" if the dependency can change, and never a fresh
105
+ object allocated in the body, which restarts the effect every recomposition.
106
+
107
+ One-shot events from a ViewModel (show a snackbar, navigate) are collected in a
108
+ `LaunchedEffect` with `repeatOnLifecycle` semantics, or modelled as state the UI
109
+ consumes and acknowledges. `android-mvi-viewmodel` covers the channel approach.
110
+
111
+ ## Modifiers
112
+
113
+ Order matters because each modifier wraps the ones after it.
114
+
115
+ ```kotlin
116
+ Modifier
117
+ .padding(16.dp) // outer spacing, not clickable
118
+ .clip(RoundedCornerShape(12.dp))
119
+ .background(MaterialTheme.colorScheme.surfaceVariant)
120
+ .clickable(onClick = onClick) // ripple is clipped to the shape
121
+ .padding(12.dp) // inner spacing, inside the touch target
122
+ ```
123
+
124
+ - Every public composable takes `modifier: Modifier = Modifier` as its first
125
+ optional parameter and applies it to the root, once.
126
+ - Write custom modifiers with `Modifier.Node` and a `ModifierNodeElement`;
127
+ `Modifier.composed` is discouraged because it allocates per call site and
128
+ cannot skip.
129
+
130
+ ```kotlin
131
+ private class DimWhenNode(var dimmed: Boolean) : Modifier.Node(), DrawModifierNode {
132
+ override fun ContentDrawScope.draw() {
133
+ drawContent()
134
+ if (dimmed) drawRect(Color.Black.copy(alpha = 0.4f))
135
+ }
136
+ }
137
+
138
+ private data class DimWhenElement(val dimmed: Boolean) : ModifierNodeElement<DimWhenNode>() {
139
+ override fun create() = DimWhenNode(dimmed)
140
+ override fun update(node: DimWhenNode) {
141
+ node.dimmed = dimmed
142
+ node.invalidateDraw()
143
+ }
144
+ }
145
+
146
+ fun Modifier.dimWhen(dimmed: Boolean) = this then DimWhenElement(dimmed)
147
+ ```
@@ -1,9 +1,10 @@
1
1
  ---
2
2
  name: android-mvi-viewmodel
3
- description: "A reusable generic BaseViewModel<State, Intent, Event> for MVI Compose apps: StateFlow state, one-shot events over a rendezvous Channel, a single intent dispatch, and a use-case executor with shared loading and error routing. Use when building or reviewing an MVI ViewModel base, intent-to-state reduction, or one-shot navigation and snackbar events."
4
- risk: safe
5
- source: multi-agent-pipeline
6
- date_added: "2026-09-21"
3
+ description: "A reusable generic BaseViewModel typed on State, Intent and Event for MVI Compose apps: StateFlow state, one-shot events over a rendezvous Channel, a single intent dispatch, and a use-case executor with shared loading and error routing. Use when building or reviewing an MVI ViewModel base, intent-to-state reduction, or one-shot navigation and snackbar events."
4
+ metadata:
5
+ risk: safe
6
+ source: multi-agent-pipeline
7
+ date_added: "2026-09-21"
7
8
  ---
8
9
 
9
10
  # Android MVI BaseViewModel
@@ -25,6 +26,7 @@ complete implementation to adapt; each heading there mirrors a section below.
25
26
  - [State as StateFlow](#state-as-stateflow)
26
27
  - [One-shot events over a rendezvous channel](#one-shot-events-over-a-rendezvous-channel)
27
28
  - [Single intent entry point](#single-intent-entry-point)
29
+ - [Same-frame input intents](#same-frame-input-intents)
28
30
  - [Use-case executor](#use-case-executor)
29
31
  - [Reference-counted loading](#reference-counted-loading)
30
32
  - [Active job registry](#active-job-registry)
@@ -119,6 +121,38 @@ override fun handleIntent(intent: SearchIntent) = when (intent) {
119
121
  }
120
122
  ```
121
123
 
124
+ ## Same-frame input intents
125
+
126
+ A base that launches every intent in a coroutine (so handlers can suspend) must
127
+ not send all of them to `Dispatchers.Default`. The hop costs a frame, and for an
128
+ intent that edits input state (a `TextField` value, a toggle, a radio selection)
129
+ that frame is visible: the cursor jumps or the text flickers, because Compose
130
+ renders the old value once more before the new one arrives.
131
+
132
+ Give the intent contract a flag, and run flagged intents on the ViewModel's own
133
+ context, which is `Dispatchers.Main.immediate` for `viewModelScope`:
134
+
135
+ ```kotlin
136
+ interface UiIntent {
137
+ val immediateDispatch: Boolean get() = false
138
+ }
139
+
140
+ fun processIntent(intent: Intent) {
141
+ val context = if (intent.immediateDispatch) EmptyCoroutineContext else Dispatchers.Default
142
+ viewModelScope.launch(context) { handleIntent(intent) }
143
+ }
144
+
145
+ data class QueryChanged(val text: String) : SearchIntent {
146
+ override val immediateDispatch = true
147
+ }
148
+ ```
149
+
150
+ The flag only decides where the state write happens. A flagged handler that
151
+ needs I/O still moves that work off the main thread with `withContext`. The
152
+ same contract can carry per-intent loading flags (show a blocking loader,
153
+ allow tap-to-cancel), so the executor below reads them from the intent instead
154
+ of taking extra arguments.
155
+
122
156
  ## Use-case executor
123
157
 
124
158
  A single protected helper wraps every suspending call so error handling is not
@@ -147,6 +181,12 @@ protected fun <T> execute(
147
181
  }
148
182
  ```
149
183
 
184
+ To stop a use case from being invoked outside this helper, which would bypass
185
+ loading and error routing, declare its `invoke` with a context parameter the
186
+ base class provides (`context(executor: UseCaseExecutor) operator fun invoke(...)`,
187
+ Kotlin 2.2+ with `-Xcontext-parameters`). Only code inside the executor scope
188
+ can call it. Tests call it inside a faked executor with `with(executor) { ... }`.
189
+
150
190
  Error routing has two tiers: a per-call `onError` for screen-specific handling,
151
191
  falling back to a global `routeError` that maps the throwable to an `Event`
152
192
  (snackbar) or an error field in state. Define `routeError` once in the base or
@@ -242,6 +282,7 @@ full body is in [references/patterns.md](references/patterns.md).
242
282
  them.
243
283
  - **Don't** pass the ViewModel to child composables; pass state + lambdas.
244
284
  - **Don't** toggle a boolean for loading when calls overlap; count references.
285
+ - **Don't** dispatch input-editing intents through `Dispatchers.Default`; flag them for same-frame dispatch.
245
286
 
246
287
  ## Related skills
247
288
 
@@ -17,6 +17,7 @@ or review that area; the guidance here is enough to decide what to do.
17
17
 
18
18
  - [Baseline Profiles](#baseline-profiles)
19
19
  - [Compose Stability](#compose-stability)
20
+ - [Deferred State Reads](#deferred-state-reads)
20
21
  - [Recomposition Debugging](#recomposition-debugging)
21
22
  - [Lazy Layout Performance](#lazy-layout-performance)
22
23
  - [R8 Optimization](#r8-optimization)
@@ -55,6 +56,7 @@ For this to work, parameters must be **stable** (immutable or observable).
55
56
  | `data class` with `List<T>` field | No | `List` is an interface; could be mutable |
56
57
  | `class` (non-data) | No | Compose cannot infer stability |
57
58
  | Lambda `() -> Unit` | Unstable by default | Captured variables may change |
59
+ | Class from a module not compiled with the Compose compiler (e.g. a domain module) | No | Compose cannot see its fields; map it to a UI model in the feature module, annotate it, or list it in a stability configuration file |
58
60
 
59
61
  Mark truly immutable models `@Immutable` and observable-but-stable state
60
62
  `@Stable`. Replace `List`/`Set`/`Map` parameters with
@@ -68,6 +70,27 @@ See `references/patterns.md` -> "Compose Stability" for the annotation
68
70
  examples, the immutable-collections `StateFlow` mapping, and the
69
71
  `gradle.properties` report settings.
70
72
 
73
+ ## Deferred State Reads
74
+
75
+ Where a state is read decides which phase re-runs when it changes. An animated
76
+ value read during composition recomposes the whole enclosing scope every frame.
77
+ The same value read in a layout or draw lambda re-runs only that phase.
78
+
79
+ | Phase the read happens in | Example | Re-runs |
80
+ |---|---|---|
81
+ | Composition | `Modifier.offset(x = v)`, `Modifier.alpha(v)`, `Modifier.scale(v)` | composition, layout, draw |
82
+ | Layout | `Modifier.offset { IntOffset(v.roundToPx(), 0) }` | layout, draw |
83
+ | Draw | `Modifier.graphicsLayer { alpha = v; translationX = px }`, `drawBehind { }` | draw only |
84
+
85
+ - Delegate animation state with `by` (`val offset by animateDpAsState(...)`) and
86
+ read it inside the latest phase that can use it.
87
+ - Animate colors through `drawBehind { drawRect(color) }` rather than a
88
+ `background(color)` read in composition.
89
+ - Hoist a `rememberInfiniteTransition` above a list instead of creating one per
90
+ item.
91
+ - Applies to `animate*AsState`, `Animatable`, `InfiniteTransition` and any state
92
+ that changes every frame (scroll offsets, drag positions).
93
+
71
94
  ## Recomposition Debugging
72
95
 
73
96
  Use Android Studio's Layout Inspector with "Show Recomposition Counts" enabled
@@ -88,8 +111,8 @@ Compose cannot diff efficiently and re-renders the whole list on any change.
88
111
 
89
112
  ```kotlin
90
113
  LazyColumn {
91
- items(items = flights, key = { it.id }) { flight ->
92
- FlightCard(flight = flight)
114
+ items(items = orders, key = { it.id }) { order ->
115
+ OrderCard(order = order)
93
116
  }
94
117
  }
95
118
  ```
@@ -212,6 +235,8 @@ debounced search `StateFlow`.
212
235
  - [ ] No object allocations inside composable body (formatters, regex)
213
236
  - [ ] `derivedStateOf` used for computed scroll/visibility state
214
237
  - [ ] `remember` used for expensive objects
238
+ - [ ] Animated values read in `offset { }` / `graphicsLayer { }` / `drawBehind { }`, not in composition
239
+ - [ ] No cross-module domain type in a composable parameter or UI state
215
240
  - [ ] R8 enabled with `isMinifyEnabled = true` for release
216
241
  - [ ] LeakCanary included as `debugImplementation` only
217
242
  - [ ] Coil configured with memory and disk cache limits