@mmerterden/multi-agent-pipeline 11.2.0 → 11.3.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 (331) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +2 -2
  3. package/docs/FIGMA_PIPELINE.md +12 -25
  4. package/docs/architecture.md +3 -5
  5. package/docs/features.md +8 -8
  6. package/install/_dev-only-files.mjs +0 -6
  7. package/install/claude.mjs +33 -15
  8. package/install/copilot.mjs +16 -14
  9. package/install/templates/copilot-instructions.md +3 -3
  10. package/package.json +1 -1
  11. package/pipeline/commands/{multi-agent.md → multi-agent/SKILL.md} +39 -39
  12. package/pipeline/commands/multi-agent/{analysis.md → analysis/SKILL.md} +18 -18
  13. package/pipeline/commands/multi-agent/{analysis-resolve.md → analysis-resolve/SKILL.md} +4 -4
  14. package/pipeline/commands/multi-agent/{autopilot.md → autopilot/SKILL.md} +4 -4
  15. package/pipeline/commands/multi-agent/{build-optimize.md → build-optimize/SKILL.md} +1 -1
  16. package/pipeline/commands/multi-agent/{channels.md → channels/SKILL.md} +12 -12
  17. package/pipeline/commands/multi-agent/{dev.md → dev/SKILL.md} +4 -4
  18. package/pipeline/commands/multi-agent/{dev-autopilot.md → dev-autopilot/SKILL.md} +4 -4
  19. package/pipeline/commands/multi-agent/{dev-local.md → dev-local/SKILL.md} +5 -5
  20. package/pipeline/commands/multi-agent/{dev-local-autopilot.md → dev-local-autopilot/SKILL.md} +4 -4
  21. package/pipeline/commands/multi-agent/{finish.md → finish/SKILL.md} +6 -6
  22. package/pipeline/commands/multi-agent/{generate.md → generate/SKILL.md} +3 -3
  23. package/pipeline/commands/multi-agent/{help.md → help/SKILL.md} +4 -4
  24. package/pipeline/commands/multi-agent/{local.md → local/SKILL.md} +5 -5
  25. package/pipeline/commands/multi-agent/{local-autopilot.md → local-autopilot/SKILL.md} +5 -5
  26. package/pipeline/commands/multi-agent/{manual-test.md → manual-test/SKILL.md} +1 -1
  27. package/pipeline/commands/multi-agent/refactor/SKILL.md +257 -0
  28. package/pipeline/commands/multi-agent/{resume.md → resume/SKILL.md} +1 -1
  29. package/pipeline/commands/multi-agent/{review.md → review/SKILL.md} +5 -5
  30. package/pipeline/commands/multi-agent/{setup.md → setup/SKILL.md} +1 -1
  31. package/pipeline/commands/multi-agent/{sync.md → sync/SKILL.md} +20 -58
  32. package/pipeline/{commands/multi-agent → multi-agent-refs}/_account-picker.md +2 -2
  33. package/pipeline/{commands/multi-agent → multi-agent-refs}/_dev-context.md +2 -2
  34. package/pipeline/{commands/multi-agent → multi-agent-refs}/_repo-picker.md +2 -2
  35. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/android-guide.md +1 -1
  36. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/jira.md +1 -1
  37. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/wiki.md +7 -7
  38. package/pipeline/multi-agent-refs/component-dispatch.md +92 -0
  39. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/cross-cli-contract.md +10 -77
  40. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/verify-by-test.md +1 -1
  41. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/generate-issue.md +2 -2
  42. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/issue-jira-triad.md +1 -1
  43. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/keychain.md +1 -1
  44. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/log-format.md +1 -1
  45. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/modes.md +2 -2
  46. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/operations.md +1 -1
  47. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-0-init.md +10 -10
  48. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-1-analysis.md +6 -6
  49. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-2-planning.md +3 -3
  50. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-3-dev.md +10 -10
  51. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-4-review.md +8 -8
  52. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-5-test.md +3 -3
  53. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-6-commit.md +4 -4
  54. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases/phase-7-report.md +6 -6
  55. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/phases.md +12 -12
  56. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/progress-contract.md +4 -4
  57. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/swiftui-guide.md +1 -1
  58. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/tracker-contract.md +3 -3
  59. package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/wiki-capture.md +4 -4
  60. package/pipeline/preferences-template.json +17 -1
  61. package/pipeline/rules/figma-pipeline.md +2 -2
  62. package/pipeline/schemas/prefs.schema.json +0 -34
  63. package/pipeline/scripts/README.md +1 -3
  64. package/pipeline/scripts/fixtures/install-layout.tsv +6 -5
  65. package/pipeline/scripts/gen-mode-dispatch.mjs +3 -3
  66. package/pipeline/scripts/phase-banner.sh +2 -2
  67. package/pipeline/scripts/review-scope.mjs +94 -0
  68. package/pipeline/scripts/scan-agent-config.sh +2 -2
  69. package/pipeline/scripts/smoke-agent-log-cost.sh +1 -1
  70. package/pipeline/scripts/smoke-agent-model-routing.sh +4 -4
  71. package/pipeline/scripts/smoke-autopilot-circuit-breaker.sh +2 -2
  72. package/pipeline/scripts/smoke-channels-approval-gate.sh +2 -2
  73. package/pipeline/scripts/smoke-channels-flow.sh +5 -5
  74. package/pipeline/scripts/smoke-clarify.sh +1 -1
  75. package/pipeline/scripts/smoke-commands-skills-parity.sh +3 -3
  76. package/pipeline/scripts/smoke-community-gates.sh +1 -1
  77. package/pipeline/scripts/smoke-compliance-skills.sh +2 -2
  78. package/pipeline/scripts/smoke-cost-summary.sh +1 -1
  79. package/pipeline/scripts/smoke-cross-cli-behavior.sh +17 -43
  80. package/pipeline/scripts/smoke-cross-phase-cohesion.sh +1 -1
  81. package/pipeline/scripts/smoke-dev-critic.sh +1 -1
  82. package/pipeline/scripts/smoke-diff-explain.sh +1 -1
  83. package/pipeline/scripts/smoke-diff-risk.sh +1 -1
  84. package/pipeline/scripts/smoke-evidence-gate.sh +1 -1
  85. package/pipeline/scripts/smoke-figma-dispatch.sh +46 -57
  86. package/pipeline/scripts/smoke-gate-hooks.sh +1 -1
  87. package/pipeline/scripts/smoke-generate-issue.sh +8 -8
  88. package/pipeline/scripts/smoke-handoff-contract.sh +3 -3
  89. package/pipeline/scripts/smoke-identity-isolation.sh +2 -2
  90. package/pipeline/scripts/smoke-intent-guard.sh +1 -1
  91. package/pipeline/scripts/smoke-issue-comment-template.sh +2 -2
  92. package/pipeline/scripts/smoke-issue-jira-triad.sh +4 -4
  93. package/pipeline/scripts/smoke-language-axis.sh +6 -6
  94. package/pipeline/scripts/smoke-learnings-ledger.sh +3 -3
  95. package/pipeline/scripts/smoke-mode-dispatch-drift.sh +16 -16
  96. package/pipeline/scripts/smoke-model-fallback.sh +3 -3
  97. package/pipeline/scripts/smoke-multi-repo-integration.sh +2 -2
  98. package/pipeline/scripts/smoke-multi-repo-worktree.sh +1 -1
  99. package/pipeline/scripts/smoke-no-token-prompt.sh +5 -5
  100. package/pipeline/scripts/smoke-phase-0-multi-repo.sh +1 -1
  101. package/pipeline/scripts/smoke-phase-6-multi.sh +1 -1
  102. package/pipeline/scripts/smoke-phase4-gates.sh +45 -0
  103. package/pipeline/scripts/smoke-plan-approval-gate.sh +5 -5
  104. package/pipeline/scripts/smoke-plan-todos.sh +3 -3
  105. package/pipeline/scripts/smoke-pr-review-actions.sh +3 -3
  106. package/pipeline/scripts/smoke-prefs-language.sh +1 -1
  107. package/pipeline/scripts/smoke-progress-contract.sh +2 -2
  108. package/pipeline/scripts/smoke-push-retry.sh +1 -1
  109. package/pipeline/scripts/smoke-repo-map.sh +2 -2
  110. package/pipeline/scripts/smoke-shadow-git.sh +1 -1
  111. package/pipeline/scripts/smoke-skill-authoring.sh +4 -9
  112. package/pipeline/scripts/smoke-tasklist-ordering.sh +10 -10
  113. package/pipeline/scripts/smoke-test-gap.sh +1 -1
  114. package/pipeline/scripts/smoke-token-budget.sh +1 -1
  115. package/pipeline/scripts/smoke-token-preflight.sh +2 -2
  116. package/pipeline/scripts/smoke-tracker-contract.sh +13 -13
  117. package/pipeline/scripts/smoke-tracker-tokens-invocation.sh +5 -5
  118. package/pipeline/scripts/smoke-triage-memory.sh +2 -2
  119. package/pipeline/scripts/smoke-update-check.sh +1 -1
  120. package/pipeline/scripts/smoke-url-enrichment.sh +2 -2
  121. package/pipeline/scripts/smoke-validator-gates.sh +1 -1
  122. package/pipeline/scripts/smoke-verify-by-test.sh +3 -3
  123. package/pipeline/scripts/smoke-wiki-integration.sh +9 -33
  124. package/pipeline/scripts/smoke-work-summary.sh +1 -1
  125. package/pipeline/scripts/smoke-worktree-path-convention.sh +4 -4
  126. package/pipeline/scripts/test-integrity-gate.mjs +83 -0
  127. package/pipeline/scripts/uninstall.mjs +2 -2
  128. package/pipeline/skills/.skill-manifest.json +40 -160
  129. package/pipeline/skills/.skills-index.json +3 -372
  130. package/pipeline/skills/shared/README.md +1 -1
  131. package/pipeline/skills/shared/core/multi-agent/SKILL.md +1 -1
  132. package/pipeline/skills/shared/core/multi-agent-analysis/SKILL.md +2 -2
  133. package/pipeline/skills/shared/core/multi-agent-analysis-resolve/SKILL.md +3 -3
  134. package/pipeline/skills/shared/core/multi-agent-build-optimize/SKILL.md +1 -1
  135. package/pipeline/skills/shared/core/multi-agent-channels/SKILL.md +1 -1
  136. package/pipeline/skills/shared/core/multi-agent-finish/SKILL.md +1 -1
  137. package/pipeline/skills/shared/core/multi-agent-help/SKILL.md +1 -1
  138. package/pipeline/skills/shared/core/multi-agent-refactor/SKILL.md +116 -51
  139. package/pipeline/skills/shared/core/multi-agent-sync/SKILL.md +8 -30
  140. package/pipeline/skills/shared/external/compose-components/SKILL.md +14 -14
  141. package/pipeline/skills/skills-index.md +2 -43
  142. package/pipeline/commands/multi-agent/refactor.md +0 -188
  143. package/pipeline/commands/multi-agent/refs/component-dispatch.md +0 -92
  144. package/pipeline/scripts/smoke-existing-discovery-gate.sh +0 -71
  145. package/pipeline/scripts/smoke-figma-android-parity.sh +0 -148
  146. package/pipeline/scripts/smoke-figma-credential-store.sh +0 -105
  147. package/pipeline/scripts/smoke-figma-cross-cli-inventory.sh +0 -177
  148. package/pipeline/scripts/smoke-figma-sync.sh +0 -149
  149. package/pipeline/scripts/smoke-sync-delegation.sh +0 -74
  150. package/pipeline/skills/figma-android/README.md +0 -38
  151. package/pipeline/skills/figma-android/figma-component-code-connect/SKILL.md +0 -62
  152. package/pipeline/skills/figma-android/figma-component-implement/SKILL.md +0 -158
  153. package/pipeline/skills/figma-android/figma-component-test/SKILL.md +0 -120
  154. package/pipeline/skills/figma-android/figma-component-wiki/SKILL.md +0 -35
  155. package/pipeline/skills/figma-android/figma-to-component/SKILL.md +0 -124
  156. package/pipeline/skills/figma-common/README.md +0 -64
  157. package/pipeline/skills/figma-common/figma-bottom-sheets/SKILL.md +0 -152
  158. package/pipeline/skills/figma-common/figma-cli-iterate/SKILL.md +0 -282
  159. package/pipeline/skills/figma-common/figma-cli-iterate-mend/SKILL.md +0 -503
  160. package/pipeline/skills/figma-common/figma-cli-lean-iterate/SKILL.md +0 -288
  161. package/pipeline/skills/figma-common/figma-cli-skip/SKILL.md +0 -362
  162. package/pipeline/skills/figma-common/figma-commit/COMMON_REBASE.md +0 -206
  163. package/pipeline/skills/figma-common/figma-commit/REVIEW.md +0 -337
  164. package/pipeline/skills/figma-common/figma-commit/SKILL.md +0 -216
  165. package/pipeline/skills/figma-common/figma-component-confluence-sync/SKILL.md +0 -223
  166. package/pipeline/skills/figma-common/figma-component-start/SKILL.md +0 -251
  167. package/pipeline/skills/figma-common/figma-component-status-update/SKILL.md +0 -78
  168. package/pipeline/skills/figma-common/figma-evolve-component/SKILL.md +0 -61
  169. package/pipeline/skills/figma-common/figma-fix/SKILL.md +0 -321
  170. package/pipeline/skills/figma-common/figma-form-integration/SKILL.md +0 -542
  171. package/pipeline/skills/figma-common/figma-issue/SKILL.md +0 -750
  172. package/pipeline/skills/figma-common/figma-iterate/SKILL.md +0 -208
  173. package/pipeline/skills/figma-common/figma-iteration-commit/SKILL.md +0 -1020
  174. package/pipeline/skills/figma-common/figma-mend/SKILL.md +0 -331
  175. package/pipeline/skills/figma-common/figma-navigation/SKILL.md +0 -156
  176. package/pipeline/skills/figma-common/figma-overlays/SKILL.md +0 -142
  177. package/pipeline/skills/figma-common/figma-price-integration/SKILL.md +0 -398
  178. package/pipeline/skills/figma-common/figma-remote-mcp-auth/SKILL.md +0 -104
  179. package/pipeline/skills/figma-common/figma-review/SKILL.md +0 -400
  180. package/pipeline/skills/figma-common/figma-setup/SKILL.md +0 -514
  181. package/pipeline/skills/figma-common/figma-setup/scripts/fetch-mcp-token.py +0 -592
  182. package/pipeline/skills/figma-common/figma-skip/SKILL.md +0 -129
  183. package/pipeline/skills/figma-common/figma-ui-patterns/SKILL.md +0 -105
  184. package/pipeline/skills/figma-common/figma-ui-patterns/patterns/animated-gradient-border.md +0 -116
  185. package/pipeline/skills/figma-common/figma-utility/SKILL.md +0 -274
  186. package/pipeline/skills/figma-common/figma-utility/scripts/figma-utility.py +0 -808
  187. package/pipeline/skills/figma-common/figma-validate/SKILL.md +0 -633
  188. package/pipeline/skills/figma-common/performance-iteration-commit-all/SKILL.md +0 -716
  189. package/pipeline/skills/figma-common/performance-review-next/SKILL.md +0 -238
  190. package/pipeline/skills/figma-common/performance-start/SKILL.md +0 -430
  191. package/pipeline/skills/figma-common/performance-swiftui/SKILL.md +0 -706
  192. package/pipeline/skills/figma-common/performance-tour/SKILL.md +0 -423
  193. package/pipeline/skills/figma-ios/REVIEW_CHECKLIST.md +0 -67
  194. package/pipeline/skills/figma-ios/figma-component-code-connect/SKILL.md +0 -178
  195. package/pipeline/skills/figma-ios/figma-component-implement/SKILL.md +0 -184
  196. package/pipeline/skills/figma-ios/figma-component-test/SKILL.md +0 -219
  197. package/pipeline/skills/figma-ios/figma-component-wiki/SKILL.md +0 -274
  198. package/pipeline/skills/figma-ios/figma-to-component/SKILL.md +0 -416
  199. package/pipeline/skills/figma-ios/figma-to-component/halt-return-protocol.md +0 -57
  200. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-0-init.md +0 -307
  201. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1-gathering.md +0 -119
  202. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-1.5-existing-discovery.md +0 -174
  203. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2-orchestrator.md +0 -333
  204. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2a-testing-identifiers.md +0 -368
  205. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2b-localization.md +0 -393
  206. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2c-accessibility.md +0 -617
  207. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-2d-analytics.md +0 -352
  208. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3-orchestrator.md +0 -337
  209. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3a-location.md +0 -206
  210. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3b-tokens.md +0 -235
  211. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3c-nested.md +0 -214
  212. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3d-patterns.md +0 -902
  213. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3e-assets.md +0 -156
  214. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3f-utilities.md +0 -175
  215. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3g-property-coverage.md +0 -176
  216. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-3h-variant-config.md +0 -333
  217. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4-orchestrator.md +0 -412
  218. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4a-configuration.md +0 -336
  219. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4b-view.md +0 -705
  220. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4c-documentation.md +0 -332
  221. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4d-preview.md +0 -380
  222. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-4e-modifiers.md +0 -262
  223. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5-orchestrator.md +0 -482
  224. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5a-viewinspector.md +0 -274
  225. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5b-snapshot.md +0 -636
  226. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-5c-unit.md +0 -142
  227. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-6-code-connect.md +0 -547
  228. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7-wiki.md +0 -39
  229. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-confluence-generate.md +0 -659
  230. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-7a-wiki-generate.md +0 -580
  231. package/pipeline/skills/figma-ios/figma-to-component/phases/phase-8-cleanup.md +0 -51
  232. package/pipeline/skills/figma-ios/figma-to-component/reference/accessibility.md +0 -183
  233. package/pipeline/skills/figma-ios/figma-to-component/reference/analytics-events.md +0 -64
  234. package/pipeline/skills/figma-ios/figma-to-component/reference/code-connect.md +0 -531
  235. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-api.md +0 -89
  236. package/pipeline/skills/figma-ios/figma-to-component/reference/confluence-xhtml.md +0 -155
  237. package/pipeline/skills/figma-ios/figma-to-component/reference/figma-to-swiftui-effects.md +0 -196
  238. package/pipeline/skills/figma-ios/figma-to-component/reference/halt-return-protocol.md +0 -57
  239. package/pipeline/skills/figma-ios/figma-to-component/reference/localization-naming.md +0 -89
  240. package/pipeline/skills/figma-ios/figma-to-component/reference/macros.md +0 -227
  241. package/pipeline/skills/figma-ios/figma-to-component/reference/missing-tokens.md +0 -157
  242. package/pipeline/skills/figma-ios/figma-to-component/reference/orchestrator-discipline.md +0 -90
  243. package/pipeline/skills/figma-ios/figma-to-component/reference/registry.md +0 -116
  244. package/pipeline/skills/figma-ios/figma-to-component/reference/remote-mcp-script.md +0 -153
  245. package/pipeline/skills/figma-ios/figma-to-component/reference/rest-api-script.md +0 -130
  246. package/pipeline/skills/figma-ios/figma-to-component/reference/scripts-inventory.md +0 -218
  247. package/pipeline/skills/figma-ios/figma-to-component/reference/snapshot-testing.md +0 -188
  248. package/pipeline/skills/figma-ios/figma-to-component/reference/subcomponent-graph.md +0 -93
  249. package/pipeline/skills/figma-ios/figma-to-component/reference/testing-identifiers-naming.md +0 -98
  250. package/pipeline/skills/figma-ios/figma-to-component/reference/tools.md +0 -261
  251. package/pipeline/skills/figma-ios/figma-to-component/reference/viewinspector.md +0 -147
  252. package/pipeline/skills/figma-ios/figma-to-component/reference/wiki-to-confluence-mapping.md +0 -182
  253. package/pipeline/skills/figma-ios/figma-to-component/scripts/apply-author-login-map.py +0 -185
  254. package/pipeline/skills/figma-ios/figma-to-component/scripts/backfill-status.py +0 -609
  255. package/pipeline/skills/figma-ios/figma-to-component/scripts/build-author-registry.py +0 -332
  256. package/pipeline/skills/figma-ios/figma-to-component/scripts/bulk-sync-issues.py +0 -261
  257. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-data-gather.py +0 -184
  258. package/pipeline/skills/figma-ios/figma-to-component/scripts/code-connect-publish.sh +0 -188
  259. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status-upload.py +0 -768
  260. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-component-status.py +0 -191
  261. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-data-gather.py +0 -420
  262. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-page-ids.example.json +0 -9
  263. package/pipeline/skills/figma-ios/figma-to-component/scripts/confluence-publish.py +0 -336
  264. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-subcomponent-graph.py +0 -391
  265. package/pipeline/skills/figma-ios/figma-to-component/scripts/figma-update.py +0 -292
  266. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/__init__.py +0 -1
  267. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/issue_sync_propagate.py +0 -93
  268. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/registry_writer.py +0 -299
  269. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_backfill_status.py +0 -343
  270. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update.py +0 -206
  271. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_figma_update_http.py +0 -149
  272. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_phase_clis.py +0 -281
  273. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_registry_writer.py +0 -332
  274. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_issue.py +0 -176
  275. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_skill_figma_review.py +0 -98
  276. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue.py +0 -298
  277. package/pipeline/skills/figma-ios/figma-to-component/scripts/lib/test_update_issue_gh.py +0 -195
  278. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase1-gather.py +0 -1298
  279. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase2-finalize.py +0 -228
  280. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase3-scripts.py +0 -1089
  281. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase4-finalize.py +0 -141
  282. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase5-finalize.py +0 -106
  283. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase6-finalize.py +0 -162
  284. package/pipeline/skills/figma-ios/figma-to-component/scripts/phase7-finalize.py +0 -105
  285. package/pipeline/skills/figma-ios/figma-to-component/scripts/register-icons-codeconnect.py +0 -179
  286. package/pipeline/skills/figma-ios/figma-to-component/scripts/remote-mcp-fetch.py +0 -260
  287. package/pipeline/skills/figma-ios/figma-to-component/scripts/resolve-author-logins.py +0 -260
  288. package/pipeline/skills/figma-ios/figma-to-component/scripts/run-uicomponents-tests.sh +0 -86
  289. package/pipeline/skills/figma-ios/figma-to-component/scripts/sidebar-generator.py +0 -321
  290. package/pipeline/skills/figma-ios/figma-to-component/scripts/update-issue-from-registry.py +0 -1470
  291. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase4.sh +0 -176
  292. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase6.sh +0 -147
  293. package/pipeline/skills/figma-ios/figma-to-component/scripts/validate-phase7a.py +0 -629
  294. /package/pipeline/commands/multi-agent/{delete.md → delete/SKILL.md} +0 -0
  295. /package/pipeline/commands/multi-agent/{diff-explain.md → diff-explain/SKILL.md} +0 -0
  296. /package/pipeline/commands/multi-agent/{garbage-collect.md → garbage-collect/SKILL.md} +0 -0
  297. /package/pipeline/commands/multi-agent/{issue.md → issue/SKILL.md} +0 -0
  298. /package/pipeline/commands/multi-agent/{jira.md → jira/SKILL.md} +0 -0
  299. /package/pipeline/commands/multi-agent/{kill.md → kill/SKILL.md} +0 -0
  300. /package/pipeline/commands/multi-agent/{language.md → language/SKILL.md} +0 -0
  301. /package/pipeline/commands/multi-agent/{log.md → log/SKILL.md} +0 -0
  302. /package/pipeline/commands/multi-agent/{prune-logs.md → prune-logs/SKILL.md} +0 -0
  303. /package/pipeline/commands/multi-agent/{purge.md → purge/SKILL.md} +0 -0
  304. /package/pipeline/commands/multi-agent/{scan.md → scan/SKILL.md} +0 -0
  305. /package/pipeline/commands/multi-agent/{search.md → search/SKILL.md} +0 -0
  306. /package/pipeline/commands/multi-agent/{stack.md → stack/SKILL.md} +0 -0
  307. /package/pipeline/commands/multi-agent/{status.md → status/SKILL.md} +0 -0
  308. /package/pipeline/commands/multi-agent/{test.md → test/SKILL.md} +0 -0
  309. /package/pipeline/commands/multi-agent/{update.md → update/SKILL.md} +0 -0
  310. /package/pipeline/{commands/multi-agent → multi-agent-refs}/_input-parser.md +0 -0
  311. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/analysis-template.md +0 -0
  312. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/audit-guide.md +0 -0
  313. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/backend-guide.md +0 -0
  314. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/confluence.md +0 -0
  315. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/issue-comment.md +0 -0
  316. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr-review-actions.md +0 -0
  317. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/channels/pr.md +0 -0
  318. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/conventions-defaults.md +0 -0
  319. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/autopilot-circuit-breaker.md +0 -0
  320. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/dev-critic.md +0 -0
  321. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/external-context-injection.md +0 -0
  322. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/model-fallback.md +0 -0
  323. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/plan-todos.md +0 -0
  324. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/prior-fix-detection.md +0 -0
  325. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/repo-map.md +0 -0
  326. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/features/shadow-git.md +0 -0
  327. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/frontend-guide.md +0 -0
  328. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/knowledge.md +0 -0
  329. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/multi-repo-integration-build.md +0 -0
  330. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/picker-contract.md +0 -0
  331. /package/pipeline/{commands/multi-agent/refs → multi-agent-refs}/rules.md +0 -0
@@ -1,343 +0,0 @@
1
- """Unit tests for backfill-status.py.
2
-
3
- Covers the pure decision function and the full run_backfill orchestration
4
- with a synthetic sources tree.
5
-
6
- Run: python3 -m unittest test_backfill_status.py
7
- """
8
-
9
- import importlib.util
10
- import json
11
- import sys
12
- import tempfile
13
- import unittest
14
- from pathlib import Path
15
-
16
- SCRIPTS_DIR = Path(__file__).parent.parent
17
- spec = importlib.util.spec_from_file_location(
18
- "backfill_status", SCRIPTS_DIR / "backfill-status.py"
19
- )
20
- backfill_status = importlib.util.module_from_spec(spec)
21
- sys.modules["backfill_status"] = backfill_status
22
- spec.loader.exec_module(backfill_status)
23
-
24
-
25
- class BuildSwiftFileIndexTests(unittest.TestCase):
26
- def test_index_walks_sources(self):
27
- with tempfile.TemporaryDirectory() as td:
28
- root = Path(td)
29
- (root / "UI/Buttons").mkdir(parents=True)
30
- (root / "UI/Cards").mkdir(parents=True)
31
- (root / "UI/Buttons/Button.swift").write_text("struct Button {}")
32
- (root / "UI/Cards/Card.swift").write_text("struct Card {}")
33
- index = backfill_status.build_swift_file_index(root)
34
- self.assertIn("Button", index)
35
- self.assertIn("Card", index)
36
- self.assertTrue(str(index["Button"]).endswith("Button.swift"))
37
-
38
- def test_index_ignores_non_swift(self):
39
- with tempfile.TemporaryDirectory() as td:
40
- root = Path(td)
41
- (root / "README.md").write_text("# hi")
42
- (root / "Script.py").write_text("pass")
43
- index = backfill_status.build_swift_file_index(root)
44
- self.assertEqual(index, {})
45
-
46
- def test_index_nonexistent_dir_returns_empty(self):
47
- index = backfill_status.build_swift_file_index(Path("/nope"))
48
- self.assertEqual(index, {})
49
-
50
-
51
- class DecideBackfillTests(unittest.TestCase):
52
- def test_skip_entry_marked_skip(self):
53
- entry = {"nodeId": "1:1", "componentName": {"ios": "Button"}, "skip": True}
54
- index = {"Button": Path("/fake/Button.swift")}
55
- d = backfill_status.decide_backfill(entry, index)
56
- self.assertEqual(d["impl_action"], "skip_marked_skip")
57
- self.assertEqual(d["codeconnect_action"], "skip_marked_skip")
58
-
59
- def test_skip_entry_with_existing_status(self):
60
- entry = {
61
- "nodeId": "1:1",
62
- "componentName": {"ios": "Button"},
63
- "status": {
64
- "ios": {
65
- "implemented": {"value": True, "version": 2, "hash": None, "at": "..."},
66
- },
67
- },
68
- }
69
- index = {"Button": Path("/fake/Button.swift")}
70
- d = backfill_status.decide_backfill(entry, index)
71
- self.assertEqual(d["impl_action"], "skip_existing_status")
72
- self.assertEqual(d["codeconnect_action"], "skip_existing_status")
73
-
74
- def test_skip_when_no_component_name_ios(self):
75
- entry = {"nodeId": "1:1", "componentName": {"android": "Button"}}
76
- index = {}
77
- d = backfill_status.decide_backfill(entry, index)
78
- self.assertEqual(d["impl_action"], "skip_no_name")
79
-
80
- def test_skip_when_no_swift_file(self):
81
- entry = {"nodeId": "1:1", "componentName": {"ios": "MissingButton"}}
82
- index = {"OtherButton": Path("/fake/OtherButton.swift")}
83
- d = backfill_status.decide_backfill(entry, index)
84
- self.assertEqual(d["impl_action"], "skip_no_swift")
85
- self.assertEqual(d["codeconnect_action"], "skip_no_impl")
86
-
87
- def test_set_impl_with_legacy_cc_true(self):
88
- entry = {
89
- "nodeId": "1:1",
90
- "componentName": {"ios": "Button"},
91
- "codeConnect": {"ios": True, "android": False},
92
- }
93
- index = {"Button": Path("/fake/Button.swift")}
94
- d = backfill_status.decide_backfill(entry, index)
95
- self.assertEqual(d["impl_action"], "set")
96
- self.assertEqual(d["codeconnect_action"], "set")
97
- self.assertTrue(d["swift_path"].endswith("Button.swift"))
98
-
99
- def test_set_impl_with_legacy_cc_false(self):
100
- entry = {
101
- "nodeId": "1:1",
102
- "componentName": {"ios": "Button"},
103
- "codeConnect": {"ios": False, "android": False},
104
- }
105
- index = {"Button": Path("/fake/Button.swift")}
106
- d = backfill_status.decide_backfill(entry, index)
107
- self.assertEqual(d["impl_action"], "set")
108
- self.assertEqual(d["codeconnect_action"], "skip_legacy_false")
109
-
110
- def test_set_impl_without_legacy_cc_block(self):
111
- entry = {"nodeId": "1:1", "componentName": {"ios": "Button"}}
112
- index = {"Button": Path("/fake/Button.swift")}
113
- d = backfill_status.decide_backfill(entry, index)
114
- self.assertEqual(d["impl_action"], "set")
115
- self.assertEqual(d["codeconnect_action"], "skip_legacy_false")
116
-
117
- def test_legacy_status_string_is_skipped(self):
118
- # Real registry data has entries like {"status": "implemented"}
119
- entry = {
120
- "nodeId": "1:1",
121
- "componentName": {"ios": "Button"},
122
- "status": "implemented",
123
- "codeConnect": {"ios": True, "android": False},
124
- }
125
- index = {"Button": Path("/fake/Button.swift")}
126
- d = backfill_status.decide_backfill(entry, index)
127
- self.assertEqual(d["impl_action"], "skip_legacy_status_string")
128
- self.assertEqual(d["codeconnect_action"], "skip_legacy_status_string")
129
-
130
- def test_component_name_as_bare_string(self):
131
- # Legacy entries have componentName as "ButtonScan" not a dict
132
- entry = {
133
- "nodeId": "1:1",
134
- "componentName": "ButtonScan",
135
- "codeConnect": {"ios": True, "android": False},
136
- }
137
- index = {"ButtonScan": Path("/fake/ButtonScan.swift")}
138
- d = backfill_status.decide_backfill(entry, index)
139
- self.assertEqual(d["impl_action"], "set")
140
- self.assertEqual(d["component_name_ios"], "ButtonScan")
141
-
142
- def test_non_dict_codeconnect_treated_as_missing(self):
143
- entry = {
144
- "nodeId": "1:1",
145
- "componentName": {"ios": "Button"},
146
- "codeConnect": "legacy_string",
147
- }
148
- index = {"Button": Path("/fake/Button.swift")}
149
- d = backfill_status.decide_backfill(entry, index)
150
- self.assertEqual(d["impl_action"], "set")
151
- self.assertEqual(d["codeconnect_action"], "skip_legacy_false")
152
-
153
-
154
- class RunBackfillTests(unittest.TestCase):
155
- def setUp(self):
156
- self.tmp = tempfile.TemporaryDirectory()
157
- self.root = Path(self.tmp.name)
158
- self.registry = self.root / "registry"
159
- self.sources = self.root / "sources"
160
- self.registry.mkdir()
161
- self.sources.mkdir()
162
-
163
- def tearDown(self):
164
- self.tmp.cleanup()
165
-
166
- def _write_reg(self, filename: str, data: dict):
167
- (self.registry / filename).write_text(json.dumps(data))
168
-
169
- def _write_swift(self, rel_path: str):
170
- p = self.sources / rel_path
171
- p.parent.mkdir(parents=True, exist_ok=True)
172
- p.write_text("// stub\n")
173
-
174
- def test_dry_run_makes_no_writes(self):
175
- self._write_reg("1-1.json", {
176
- "nodeId": "1:1",
177
- "componentName": {"ios": "Button"},
178
- "codeConnect": {"ios": True, "android": False},
179
- })
180
- self._write_swift("UI/Button.swift")
181
-
182
- summary = backfill_status.run_backfill(
183
- registry_dir=self.registry,
184
- sources_dir=self.sources,
185
- dry_run=True,
186
- )
187
-
188
- self.assertEqual(summary["status"], "ok")
189
- self.assertTrue(summary["dry_run"])
190
- self.assertEqual(summary["total_files"], 1)
191
- self.assertEqual(summary["swift_files_indexed"], 1)
192
-
193
- # Original file untouched
194
- data = json.loads((self.registry / "1-1.json").read_text())
195
- self.assertNotIn("status", data)
196
-
197
- def test_real_run_writes_status_block(self):
198
- self._write_reg("1-1.json", {
199
- "nodeId": "1:1",
200
- "componentName": {"ios": "Button"},
201
- "codeConnect": {"ios": True, "android": False},
202
- "confluence": {"page": None, "ios": None, "android": None},
203
- "testingIdentifiers": [],
204
- "localizationKeys": [],
205
- "accessibilityKeys": [],
206
- "analyticsEvents": [],
207
- "fileKey": "abc",
208
- })
209
- self._write_swift("UI/Button.swift")
210
-
211
- summary = backfill_status.run_backfill(
212
- registry_dir=self.registry,
213
- sources_dir=self.sources,
214
- dry_run=False,
215
- )
216
- self.assertEqual(summary["status"], "ok")
217
-
218
- data = json.loads((self.registry / "1-1.json").read_text())
219
- self.assertTrue(data["status"]["ios"]["implemented"]["value"])
220
- self.assertEqual(data["status"]["ios"]["implemented"]["version"], 1)
221
- self.assertTrue(data["status"]["ios"]["codeConnect"]["value"])
222
- self.assertEqual(data["status"]["ios"]["codeConnect"]["against"], 1)
223
- # Legacy field preserved
224
- self.assertTrue(data["codeConnect"]["ios"])
225
- # Other legacy fields intact
226
- self.assertEqual(data["fileKey"], "abc")
227
- self.assertEqual(data["componentName"], {"ios": "Button"})
228
-
229
- def test_real_run_skips_component_without_swift_file(self):
230
- self._write_reg("1-1.json", {
231
- "nodeId": "1:1",
232
- "componentName": {"ios": "MissingButton"},
233
- "codeConnect": {"ios": False, "android": False},
234
- })
235
- # No Swift file written
236
-
237
- summary = backfill_status.run_backfill(
238
- registry_dir=self.registry,
239
- sources_dir=self.sources,
240
- dry_run=False,
241
- )
242
- self.assertEqual(summary["status"], "ok")
243
-
244
- data = json.loads((self.registry / "1-1.json").read_text())
245
- self.assertNotIn("status", data) # nothing written
246
-
247
- def test_real_run_skips_component_marked_skip(self):
248
- self._write_reg("1-1.json", {
249
- "nodeId": "1:1",
250
- "componentName": {"ios": "Button"},
251
- "codeConnect": {"ios": True, "android": False},
252
- "skip": True,
253
- })
254
- self._write_swift("UI/Button.swift")
255
-
256
- backfill_status.run_backfill(
257
- registry_dir=self.registry,
258
- sources_dir=self.sources,
259
- dry_run=False,
260
- )
261
- data = json.loads((self.registry / "1-1.json").read_text())
262
- self.assertNotIn("status", data)
263
-
264
- def test_real_run_sets_impl_but_not_cc_when_legacy_cc_false(self):
265
- self._write_reg("1-1.json", {
266
- "nodeId": "1:1",
267
- "componentName": {"ios": "Button"},
268
- "codeConnect": {"ios": False, "android": False},
269
- })
270
- self._write_swift("UI/Button.swift")
271
-
272
- backfill_status.run_backfill(
273
- registry_dir=self.registry,
274
- sources_dir=self.sources,
275
- dry_run=False,
276
- )
277
- data = json.loads((self.registry / "1-1.json").read_text())
278
- self.assertTrue(data["status"]["ios"]["implemented"]["value"])
279
- self.assertFalse(data["status"]["ios"]["codeConnect"]["value"])
280
- self.assertFalse(data["codeConnect"]["ios"])
281
-
282
- def test_limit_honoured(self):
283
- for i in range(5):
284
- self._write_reg(f"1-{i}.json", {
285
- "nodeId": f"1:{i}",
286
- "componentName": {"ios": f"Comp{i}"},
287
- "codeConnect": {"ios": True, "android": False},
288
- })
289
- self._write_swift(f"UI/Comp{i}.swift")
290
-
291
- summary = backfill_status.run_backfill(
292
- registry_dir=self.registry,
293
- sources_dir=self.sources,
294
- dry_run=False,
295
- limit=2,
296
- )
297
- self.assertEqual(summary["total_files"], 2)
298
-
299
- # Verify only the first 2 (alphabetical: 1-0, 1-1) got a status block
300
- written_count = 0
301
- for i in range(5):
302
- data = json.loads((self.registry / f"1-{i}.json").read_text())
303
- if "status" in data:
304
- written_count += 1
305
- self.assertEqual(written_count, 2)
306
-
307
- def test_existing_status_is_not_overwritten(self):
308
- self._write_reg("1-1.json", {
309
- "nodeId": "1:1",
310
- "componentName": {"ios": "Button"},
311
- "codeConnect": {"ios": True, "android": False},
312
- "status": {
313
- "ios": {
314
- "stale": False,
315
- "implemented": {"value": True, "version": 7, "hash": "sha256:existing", "at": "2026-03-01T00:00:00Z"},
316
- "tested": {"value": True, "against": 7, "at": "..."},
317
- "codeConnect": {"value": True, "against": 7, "at": "..."},
318
- "wiki": {"value": True, "against": 7, "at": "..."},
319
- },
320
- "android": {
321
- "stale": False,
322
- "implemented": {"value": False, "version": 0, "hash": None, "at": None},
323
- "tested": {"value": False, "against": 0, "at": None},
324
- "codeConnect": {"value": False, "against": 0, "at": None},
325
- "wiki": {"value": False, "against": 0, "at": None},
326
- },
327
- },
328
- })
329
- self._write_swift("UI/Button.swift")
330
-
331
- backfill_status.run_backfill(
332
- registry_dir=self.registry,
333
- sources_dir=self.sources,
334
- dry_run=False,
335
- )
336
- data = json.loads((self.registry / "1-1.json").read_text())
337
- # Version untouched
338
- self.assertEqual(data["status"]["ios"]["implemented"]["version"], 7)
339
- self.assertEqual(data["status"]["ios"]["implemented"]["hash"], "sha256:existing")
340
-
341
-
342
- if __name__ == "__main__":
343
- unittest.main(verbosity=2)
@@ -1,206 +0,0 @@
1
- """Unit tests for figma-update.py.
2
-
3
- Tests only the pure logic (scan_registry_for_file_keys, update_file_components)
4
- and the top-level orchestration with `fetch_file_last_modified` monkey-patched.
5
- No real network calls.
6
-
7
- Run: python3 -m unittest test_figma_update.py
8
- """
9
-
10
- import importlib.util
11
- import json
12
- import sys
13
- import tempfile
14
- import unittest
15
- from pathlib import Path
16
-
17
- # Load figma-update.py as a module (its filename has a dash so it's not a
18
- # normal import target).
19
- SCRIPTS_DIR = Path(__file__).parent.parent
20
- spec = importlib.util.spec_from_file_location(
21
- "figma_update", SCRIPTS_DIR / "figma-update.py"
22
- )
23
- figma_update = importlib.util.module_from_spec(spec)
24
- sys.modules["figma_update"] = figma_update
25
- spec.loader.exec_module(figma_update)
26
-
27
-
28
- class ScanRegistryTests(unittest.TestCase):
29
- def setUp(self):
30
- self.tmp = tempfile.TemporaryDirectory()
31
- self.registry_dir = Path(self.tmp.name)
32
-
33
- def tearDown(self):
34
- self.tmp.cleanup()
35
-
36
- def _write(self, filename: str, data: dict):
37
- (self.registry_dir / filename).write_text(json.dumps(data))
38
-
39
- def test_scan_empty_dir(self):
40
- result = figma_update.scan_registry_for_file_keys(self.registry_dir)
41
- self.assertEqual(result, {})
42
-
43
- def test_scan_groups_by_file_key(self):
44
- self._write("1-1.json", {"nodeId": "1:1", "fileKey": "FILEA"})
45
- self._write("1-2.json", {"nodeId": "1:2", "fileKey": "FILEA"})
46
- self._write("2-1.json", {"nodeId": "2:1", "fileKey": "FILEB"})
47
- result = figma_update.scan_registry_for_file_keys(self.registry_dir)
48
- self.assertEqual(sorted(result["FILEA"]), ["1:1", "1:2"])
49
- self.assertEqual(result["FILEB"], ["2:1"])
50
-
51
- def test_scan_skips_invalid_json(self):
52
- (self.registry_dir / "bad.json").write_text("{not json")
53
- self._write("good.json", {"nodeId": "1:1", "fileKey": "F"})
54
- result = figma_update.scan_registry_for_file_keys(self.registry_dir)
55
- self.assertEqual(result, {"F": ["1:1"]})
56
-
57
- def test_scan_skips_entries_missing_nodeid_or_filekey(self):
58
- self._write("orphan1.json", {"nodeId": "1:1"}) # no fileKey
59
- self._write("orphan2.json", {"fileKey": "F"}) # no nodeId
60
- self._write("good.json", {"nodeId": "2:2", "fileKey": "G"})
61
- result = figma_update.scan_registry_for_file_keys(self.registry_dir)
62
- self.assertEqual(result, {"G": ["2:2"]})
63
-
64
- def test_scan_missing_dir(self):
65
- result = figma_update.scan_registry_for_file_keys(Path("/nonexistent/path"))
66
- self.assertEqual(result, {})
67
-
68
-
69
- class UpdateFileComponentsTests(unittest.TestCase):
70
- def setUp(self):
71
- self.tmp = tempfile.TemporaryDirectory()
72
- self.registry_dir = Path(self.tmp.name)
73
-
74
- def tearDown(self):
75
- self.tmp.cleanup()
76
-
77
- def _seed(self, node_id: str, **extra):
78
- path = self.registry_dir / f"{node_id.replace(':', '-')}.json"
79
- data = {"nodeId": node_id, "fileKey": "FILEA"}
80
- data.update(extra)
81
- # Seed an implemented flag so mark_stale actually flips it
82
- data["status"] = {
83
- "ios": {
84
- "stale": False,
85
- "implemented": {"value": True, "version": 1, "hash": None, "at": "2026-04-01T00:00:00Z"},
86
- "tested": {"value": False, "against": 0, "at": None},
87
- "codeConnect": {"value": False, "against": 0, "at": None},
88
- "wiki": {"value": False, "against": 0, "at": None},
89
- },
90
- "android": {
91
- "stale": False,
92
- "implemented": {"value": False, "version": 0, "hash": None, "at": None},
93
- "tested": {"value": False, "against": 0, "at": None},
94
- "codeConnect": {"value": False, "against": 0, "at": None},
95
- "wiki": {"value": False, "against": 0, "at": None},
96
- },
97
- }
98
- path.write_text(json.dumps(data))
99
-
100
- def test_real_write_marks_implemented_platforms_stale(self):
101
- self._seed("1:1")
102
- self._seed("1:2")
103
-
104
- summary = figma_update.update_file_components(
105
- registry_dir=self.registry_dir,
106
- file_key="FILEA",
107
- node_ids=["1:1", "1:2"],
108
- new_last_modified="2026-04-15T10:00:00Z",
109
- dry_run=False,
110
- )
111
-
112
- self.assertEqual(sorted(summary["updated"]), ["1:1", "1:2"])
113
- self.assertEqual(summary["skipped"], [])
114
-
115
- for nid in ("1-1", "1-2"):
116
- data = json.loads((self.registry_dir / f"{nid}.json").read_text())
117
- self.assertTrue(data["status"]["ios"]["stale"])
118
- self.assertFalse(data["status"]["android"]["stale"]) # android wasn't implemented
119
- self.assertEqual(data["figmaUpdatedAt"], "2026-04-15T10:00:00Z")
120
-
121
- def test_real_write_skips_when_figma_is_older(self):
122
- self._seed("1:1", figmaUpdatedAt="2026-04-20T00:00:00Z")
123
-
124
- summary = figma_update.update_file_components(
125
- registry_dir=self.registry_dir,
126
- file_key="FILEA",
127
- node_ids=["1:1"],
128
- new_last_modified="2026-04-10T00:00:00Z",
129
- dry_run=False,
130
- )
131
- self.assertEqual(summary["updated"], [])
132
- self.assertEqual(summary["skipped"], ["1:1"])
133
-
134
- data = json.loads((self.registry_dir / "1-1.json").read_text())
135
- self.assertFalse(data["status"]["ios"]["stale"])
136
- self.assertEqual(data["figmaUpdatedAt"], "2026-04-20T00:00:00Z")
137
-
138
- def test_dry_run_does_not_write(self):
139
- self._seed("1:1")
140
- summary = figma_update.update_file_components(
141
- registry_dir=self.registry_dir,
142
- file_key="FILEA",
143
- node_ids=["1:1"],
144
- new_last_modified="2026-04-15T10:00:00Z",
145
- dry_run=True,
146
- )
147
- self.assertEqual(summary["updated"], ["1:1"])
148
-
149
- data = json.loads((self.registry_dir / "1-1.json").read_text())
150
- self.assertFalse(data["status"]["ios"]["stale"]) # NOT written
151
- self.assertNotIn("figmaUpdatedAt", data)
152
-
153
- def test_dry_run_correctly_identifies_skippable_nodes(self):
154
- self._seed("1:1", figmaUpdatedAt="2026-04-20T00:00:00Z")
155
- summary = figma_update.update_file_components(
156
- registry_dir=self.registry_dir,
157
- file_key="FILEA",
158
- node_ids=["1:1"],
159
- new_last_modified="2026-04-10T00:00:00Z",
160
- dry_run=True,
161
- )
162
- self.assertEqual(summary["updated"], [])
163
- self.assertEqual(summary["skipped"], ["1:1"])
164
-
165
- def test_limit_truncates_node_list(self):
166
- for i in range(5):
167
- self._seed(f"1:{i}")
168
-
169
- summary = figma_update.update_file_components(
170
- registry_dir=self.registry_dir,
171
- file_key="FILEA",
172
- node_ids=[f"1:{i}" for i in range(5)],
173
- new_last_modified="2026-04-15T10:00:00Z",
174
- dry_run=False,
175
- limit=2,
176
- )
177
- self.assertEqual(len(summary["updated"]), 2)
178
- # Nodes 2, 3, 4 should still have no figmaUpdatedAt
179
- for untouched in ("1-2", "1-3", "1-4"):
180
- data = json.loads((self.registry_dir / f"{untouched}.json").read_text())
181
- self.assertNotIn("figmaUpdatedAt", data)
182
-
183
- def test_missing_registry_file_is_skipped(self):
184
- summary = figma_update.update_file_components(
185
- registry_dir=self.registry_dir,
186
- file_key="FILEA",
187
- node_ids=["99:99"],
188
- new_last_modified="2026-04-15T10:00:00Z",
189
- dry_run=False,
190
- )
191
- self.assertEqual(summary["updated"], [])
192
- self.assertEqual(summary["skipped"], ["99:99"])
193
-
194
-
195
- class FetchFunctionExistsTests(unittest.TestCase):
196
- """Smoke-check the HTTP helper has the right signature - do NOT call it."""
197
-
198
- def test_fetch_file_last_modified_exists(self):
199
- self.assertTrue(callable(figma_update.fetch_file_last_modified))
200
-
201
- def test_load_figma_token_exists(self):
202
- self.assertTrue(callable(figma_update.load_figma_token))
203
-
204
-
205
- if __name__ == "__main__":
206
- unittest.main(verbosity=2)