@wix/agent-skills 1.33.1 → 1.34.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 (616) hide show
  1. package/README.md +2 -0
  2. package/package.json +1 -1
  3. package/skills/wix-export/SKILL.md +83 -0
  4. package/skills/wix-harmony-editor/.publish-manifest.json +8 -0
  5. package/skills/wix-harmony-editor/COPY-SITE.md +494 -0
  6. package/skills/wix-harmony-editor/SKILL.md +317 -0
  7. package/skills/wix-harmony-editor/scripts/copy/build-description.mjs +249 -0
  8. package/skills/wix-harmony-editor/scripts/copy/check-description.mjs +225 -0
  9. package/skills/wix-harmony-editor/scripts/copy/check-mirror.mjs +89 -0
  10. package/skills/wix-harmony-editor/scripts/copy/compare-preview.mjs +76 -0
  11. package/skills/wix-harmony-editor/scripts/copy/deploy-description.mjs +212 -0
  12. package/skills/wix-harmony-editor/scripts/copy/desc-hash.mjs +44 -0
  13. package/skills/wix-harmony-editor/scripts/copy/editor-run.mjs +45 -0
  14. package/skills/wix-harmony-editor/scripts/copy/export-page.mjs +53 -0
  15. package/skills/wix-harmony-editor/scripts/copy/find-interactions.mjs +76 -0
  16. package/skills/wix-harmony-editor/scripts/copy/hide-scrollbars.mjs +16 -0
  17. package/skills/wix-harmony-editor/scripts/copy/import-media.mjs +150 -0
  18. package/skills/wix-harmony-editor/scripts/copy/link-url.mjs +27 -0
  19. package/skills/wix-harmony-editor/scripts/copy/lint-api.mjs +56 -0
  20. package/skills/wix-harmony-editor/scripts/copy/measure-source.mjs +211 -0
  21. package/skills/wix-harmony-editor/scripts/copy/merge-mirrors.mjs +29 -0
  22. package/skills/wix-harmony-editor/scripts/copy/mirror-site.mjs +92 -0
  23. package/skills/wix-harmony-editor/scripts/copy/mobile-stage.mjs +56 -0
  24. package/skills/wix-harmony-editor/scripts/copy/preview-widths.mjs +73 -0
  25. package/skills/wix-harmony-editor/scripts/copy/render-plate.mjs +67 -0
  26. package/skills/wix-harmony-editor/scripts/copy/serve-mirror.mjs +102 -0
  27. package/skills/wix-harmony-editor/scripts/copy/snapshot-capabilities.mjs +40 -0
  28. package/skills/wix-harmony-editor/scripts/copy/split-sections.mjs +191 -0
  29. package/skills/wix-harmony-editor/scripts/copy/upload-media-manager.mjs +61 -0
  30. package/skills/wix-harmony-editor/scripts/launch.sh +359 -0
  31. package/skills/wix-headless/SKILL.md +7 -2
  32. package/skills/wix-headless/references/CAPABILITIES.md +6 -1
  33. package/skills/wix-headless/references/DISCOVERY.md +2 -1
  34. package/skills/wix-headless/references/SDK_HANDOFF.md +6 -1
  35. package/skills/wix-headless/references/SEED.md +3 -2
  36. package/skills/wix-headless/references/SETUP.md +1 -0
  37. package/skills/wix-headless/references/inline-recipes/experience-donations.md +102 -0
  38. package/skills/wix-headless/references/inline-recipes/how-to-code-donations.md +146 -0
  39. package/skills/wix-headless/references/inline-recipes/setup-donations.md +15 -0
  40. package/skills/wix-headless/references/managed/CREATE.md +1 -1
  41. package/skills/wix-headless-kit/SKILL.md +110 -21
  42. package/skills/wix-headless-kit/guides/api-run.md +15 -13
  43. package/skills/wix-headless-kit/guides/existing-site.md +31 -0
  44. package/skills/wix-headless-kit/guides/migration.md +49 -17
  45. package/skills/wix-headless-kit/install/agents-md.mjs +1 -1
  46. package/skills/wix-headless-kit/install/context.mjs +61 -6
  47. package/skills/wix-headless-kit/install/pins.json +1 -1
  48. package/skills/wix-headless-kit/install/setup.mjs +107 -12
  49. package/skills/wix-headless-replatform/.publish-manifest.json +8 -0
  50. package/skills/wix-headless-replatform/SKILL.md +116 -0
  51. package/skills/wix-headless-replatform/agents/openai.yaml +6 -0
  52. package/skills/wix-headless-replatform/references/control-and-visual-fidelity-contract.md +43 -0
  53. package/skills/wix-headless-replatform/references/extraction-schema.md +187 -0
  54. package/skills/wix-headless-replatform/references/interaction-runtime-contract.md +137 -0
  55. package/skills/wix-headless-replatform/references/layout-blueprint-contract.md +41 -0
  56. package/skills/wix-headless-replatform/references/output-contract.md +93 -0
  57. package/skills/wix-headless-replatform/references/post-build-gap-contract.md +125 -0
  58. package/skills/wix-headless-replatform/references/qa-checklist.md +83 -0
  59. package/skills/wix-headless-replatform/references/safety.md +19 -0
  60. package/skills/wix-headless-replatform/references/ui-normalization-contract.md +70 -0
  61. package/skills/wix-headless-replatform/references/workflow.md +157 -0
  62. package/skills/wix-headless-replatform/registry/README.md +20 -0
  63. package/skills/wix-headless-replatform/registry/approvals/button/shadcn-default-ac60ef5.json +28 -0
  64. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/button.tsx +54 -0
  65. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/capabilities.json +60 -0
  66. package/skills/wix-headless-replatform/registry/items/button/shadcn-default-ac60ef5/contract.md +48 -0
  67. package/skills/wix-headless-replatform/registry/licenses/shadcn-ui-ac60ef5-MIT.txt +21 -0
  68. package/skills/wix-headless-replatform/registry/registry.json +36 -0
  69. package/skills/wix-headless-replatform/resources/rp-facelift/SKILL.md +32 -0
  70. package/skills/wix-headless-replatform/resources/rp-project-context/SKILL.md +62 -0
  71. package/skills/wix-headless-replatform/resources/rp-qa-gap-loop/SKILL.md +64 -0
  72. package/skills/wix-headless-replatform/resources/rp-source-evidence/SKILL.md +42 -0
  73. package/skills/wix-headless-replatform/resources/rp-ui-implementation/SKILL.md +73 -0
  74. package/skills/wix-headless-replatform/schemas/build-plan.schema.json +10 -0
  75. package/skills/wix-headless-replatform/schemas/candidate.schema.json +19 -0
  76. package/skills/wix-headless-replatform/schemas/common.schema.json +33 -0
  77. package/skills/wix-headless-replatform/schemas/decision-patch.schema.json +10 -0
  78. package/skills/wix-headless-replatform/schemas/decision-request.schema.json +10 -0
  79. package/skills/wix-headless-replatform/schemas/extraction-gap.schema.json +28 -0
  80. package/skills/wix-headless-replatform/schemas/extraction-manifest.schema.json +34 -0
  81. package/skills/wix-headless-replatform/schemas/final-report.schema.json +10 -0
  82. package/skills/wix-headless-replatform/schemas/foundation.schema.json +11 -0
  83. package/skills/wix-headless-replatform/schemas/metadata.schema.json +11 -0
  84. package/skills/wix-headless-replatform/schemas/observation-packet.schema.json +17 -0
  85. package/skills/wix-headless-replatform/schemas/page-capture.schema.json +11 -0
  86. package/skills/wix-headless-replatform/schemas/page-resolution.schema.json +33 -0
  87. package/skills/wix-headless-replatform/schemas/shared-chrome.schema.json +11 -0
  88. package/skills/wix-headless-replatform/schemas/unit.schema.json +40 -0
  89. package/skills/wix-headless-replatform/scripts/assemble-extraction.mjs +844 -0
  90. package/skills/wix-headless-replatform/scripts/benchmark-extraction.mjs +163 -0
  91. package/skills/wix-headless-replatform/scripts/browser-extraction-preflight.mjs +42 -0
  92. package/skills/wix-headless-replatform/scripts/build-extracted-homepage.mjs +228 -0
  93. package/skills/wix-headless-replatform/scripts/clear-wix-starter.mjs +87 -0
  94. package/skills/wix-headless-replatform/scripts/create-wix-project.mjs +505 -0
  95. package/skills/wix-headless-replatform/scripts/discover-urls.mjs +142 -0
  96. package/skills/wix-headless-replatform/scripts/extract-assets.mjs +140 -0
  97. package/skills/wix-headless-replatform/scripts/extract-design-system.mjs +637 -0
  98. package/skills/wix-headless-replatform/scripts/extract-interactions.mjs +1903 -0
  99. package/skills/wix-headless-replatform/scripts/extract-page.mjs +2461 -0
  100. package/skills/wix-headless-replatform/scripts/extract-seo.mjs +25 -0
  101. package/skills/wix-headless-replatform/scripts/extract-tokens.mjs +63 -0
  102. package/skills/wix-headless-replatform/scripts/finalize-extraction-report.mjs +126 -0
  103. package/skills/wix-headless-replatform/scripts/finalize-gap-review.mjs +180 -0
  104. package/skills/wix-headless-replatform/scripts/finalize-gap-review.test.mjs +98 -0
  105. package/skills/wix-headless-replatform/scripts/finalize-migration-website.mjs +103 -0
  106. package/skills/wix-headless-replatform/scripts/generate-control-state-contract.mjs +176 -0
  107. package/skills/wix-headless-replatform/scripts/generate-design-md.mjs +261 -0
  108. package/skills/wix-headless-replatform/scripts/generate-extraction-validators.mjs +25 -0
  109. package/skills/wix-headless-replatform/scripts/generate-invoice-download-backend.mjs +342 -0
  110. package/skills/wix-headless-replatform/scripts/generate-layout-blueprint.mjs +140 -0
  111. package/skills/wix-headless-replatform/scripts/generate-repeater-cms.mjs +22 -0
  112. package/skills/wix-headless-replatform/scripts/generate-route-plan.mjs +143 -0
  113. package/skills/wix-headless-replatform/scripts/generate-scene-contract.mjs +288 -0
  114. package/skills/wix-headless-replatform/scripts/generate-ui-normalization.mjs +266 -0
  115. package/skills/wix-headless-replatform/scripts/generate-visual-assets.mjs +111 -0
  116. package/skills/wix-headless-replatform/scripts/lib/browser-tooling.mjs +570 -0
  117. package/skills/wix-headless-replatform/scripts/lib/common.mjs +329 -0
  118. package/skills/wix-headless-replatform/scripts/lib/component-capabilities.mjs +121 -0
  119. package/skills/wix-headless-replatform/scripts/lib/component-registry.mjs +282 -0
  120. package/skills/wix-headless-replatform/scripts/lib/content-boundary.mjs +61 -0
  121. package/skills/wix-headless-replatform/scripts/lib/extraction-contract.mjs +374 -0
  122. package/skills/wix-headless-replatform/scripts/lib/extraction-detectors.mjs +243 -0
  123. package/skills/wix-headless-replatform/scripts/lib/extraction-validators.generated.mjs +1139 -0
  124. package/skills/wix-headless-replatform/scripts/lib/font-contract.mjs +326 -0
  125. package/skills/wix-headless-replatform/scripts/lib/frontend-automation-state.mjs +184 -0
  126. package/skills/wix-headless-replatform/scripts/lib/gap-analysis.mjs +396 -0
  127. package/skills/wix-headless-replatform/scripts/lib/html-extract.mjs +168 -0
  128. package/skills/wix-headless-replatform/scripts/lib/interaction-bootstrap.mjs +10 -0
  129. package/skills/wix-headless-replatform/scripts/lib/interaction-runtime.mjs +194 -0
  130. package/skills/wix-headless-replatform/scripts/lib/interaction-timeline.mjs +348 -0
  131. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/classifiers.mjs +141 -0
  132. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/geometry.mjs +43 -0
  133. package/skills/wix-headless-replatform/scripts/lib/layout-blueprint/vocabulary.mjs +18 -0
  134. package/skills/wix-headless-replatform/scripts/lib/ui-normalization.css +64 -0
  135. package/skills/wix-headless-replatform/scripts/optimize-svg.mjs +31 -0
  136. package/skills/wix-headless-replatform/scripts/post-build-gap.mjs +358 -0
  137. package/skills/wix-headless-replatform/scripts/provision-repeater-cms.mjs +25 -0
  138. package/skills/wix-headless-replatform/scripts/record-gap-decision.mjs +39 -0
  139. package/skills/wix-headless-replatform/scripts/replay-extraction.mjs +107 -0
  140. package/skills/wix-headless-replatform/scripts/resolve-home-page.mjs +96 -0
  141. package/skills/wix-headless-replatform/scripts/site-clone.mjs +658 -0
  142. package/skills/wix-headless-replatform/scripts/validate-font-contract.mjs +29 -0
  143. package/skills/wix-headless-replatform/scripts/verify-interactions.mjs +669 -0
  144. package/skills/wix-headless-replatform/scripts/visual-qa.mjs +370 -0
  145. package/skills/wix-headless-templates/donations/INSTRUCTIONS.md +37 -24
  146. package/skills/wix-headless-templates/donations/app/components/donations/DonateForm.tsx +49 -23
  147. package/skills/wix-headless-templates/donations/app/components/donations/DonationReceipt.tsx +6 -5
  148. package/skills/wix-headless-templates/donations/app/components/donations/GoalProgress.tsx +5 -5
  149. package/skills/wix-headless-templates/donations/app/wix/donations/campaigns.ts +4 -4
  150. package/skills/wix-headless-templates/donations/app/wix/donations/donate.ts +7 -3
  151. package/skills/wix-headless-templates/donations/app/wix/donations/donation-store.ts +2 -2
  152. package/skills/wix-headless-templates/donations/app/wix/donations/donations-core.ts +12 -9
  153. package/skills/wix-headless-templates/donations/project/src/components/donations/DonateForm.tsx +49 -23
  154. package/skills/wix-headless-templates/donations/project/src/components/donations/DonationReceipt.tsx +6 -5
  155. package/skills/wix-headless-templates/donations/project/src/components/donations/GoalProgress.tsx +5 -5
  156. package/skills/wix-headless-templates/donations/project/src/wix/donations/campaigns.ts +4 -4
  157. package/skills/wix-headless-templates/donations/project/src/wix/donations/donate.ts +7 -3
  158. package/skills/wix-headless-templates/donations/project/src/wix/donations/donation-store.ts +2 -2
  159. package/skills/wix-headless-templates/donations/project/src/wix/donations/donations-core.ts +12 -9
  160. package/skills/wix-headless-templates/donations/rest/campaigns.ts +4 -4
  161. package/skills/wix-headless-templates/donations/rest/donate.ts +7 -3
  162. package/skills/wix-headless-templates/donations/seed/SEED.md +12 -7
  163. package/skills/wix-headless-templates/donations/tests/donation-contract.test.mjs +71 -0
  164. package/skills/wix-headless-templates/donations/tests/donation-form.test.cjs +137 -0
  165. package/skills/wix-headless-templates/restaurants/INSTRUCTIONS.md +28 -14
  166. package/skills/wix-headless-templates/restaurants/app/components/restaurants/OrderCartDrawer.tsx +87 -5
  167. package/skills/wix-headless-templates/restaurants/app/hooks/restaurants/useOrderCart.ts +9 -1
  168. package/skills/wix-headless-templates/restaurants/app/wix/restaurants/order-store.ts +33 -4
  169. package/skills/wix-headless-templates/restaurants/app/wix/restaurants/ordering-core.ts +100 -5
  170. package/skills/wix-headless-templates/restaurants/app/wix/restaurants/ordering.ts +101 -15
  171. package/skills/wix-headless-templates/restaurants/app/wix/restaurants/types.ts +35 -1
  172. package/skills/wix-headless-templates/restaurants/project/src/components/restaurants/OrderCartDrawer.tsx +87 -5
  173. package/skills/wix-headless-templates/restaurants/project/src/hooks/restaurants/useOrderCart.ts +9 -1
  174. package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/order-store.ts +33 -4
  175. package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/ordering-core.ts +100 -5
  176. package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/ordering.ts +101 -15
  177. package/skills/wix-headless-templates/restaurants/project/src/wix/restaurants/types.ts +35 -1
  178. package/skills/wix-headless-templates/restaurants/rest/ordering.ts +91 -14
  179. package/skills/wix-import/SKILL.md +90 -0
  180. package/skills/wix-manage/SKILL.md +13 -3
  181. package/skills/wix-manage/references/dashboard-navigation/dashboard-navigation.md +2 -1
  182. package/skills/wix-manage/references/dashboard-navigation/routes.json +249 -0
  183. package/skills/wix-manage/references/donations/create-and-update-donation-campaigns.md +139 -0
  184. package/skills/wix-manage/references/events/events-dashboard-navigation.md +81 -0
  185. package/skills/wix-manage/references/sites/manage-oauth-apps.md +1 -0
  186. package/skills/wix-manage/references/sites/upload-static-site.md +127 -8
  187. package/skills/wix-replatform/.publish-manifest.json +5 -5
  188. package/skills/wix-replatform/CLI.md +162 -0
  189. package/skills/wix-replatform/README.md +52 -0
  190. package/skills/wix-replatform/SKILL.md +229 -684
  191. package/skills/wix-replatform/console/account.css +1 -0
  192. package/skills/wix-replatform/console/app.css +1 -0
  193. package/skills/wix-replatform/console/app.js +117 -0
  194. package/skills/wix-replatform/console/index.html +4 -0
  195. package/skills/wix-replatform/console/json.css +11 -0
  196. package/skills/wix-replatform/lib/agent-adapters/index.js +71 -0
  197. package/skills/wix-replatform/lib/app-page-routing.js +75 -0
  198. package/skills/wix-replatform/lib/artifact-freshness.js +11 -1
  199. package/skills/wix-replatform/lib/blocked-data-requests.js +427 -0
  200. package/skills/wix-replatform/lib/census-reconciliation.js +426 -0
  201. package/skills/wix-replatform/lib/completion-report.js +1393 -13
  202. package/skills/wix-replatform/lib/config-env.js +85 -11
  203. package/skills/wix-replatform/lib/confirmed-pictures.js +212 -0
  204. package/skills/wix-replatform/lib/dependency-readiness.js +423 -0
  205. package/skills/wix-replatform/lib/dependency-recovery.js +21 -0
  206. package/skills/wix-replatform/lib/destination-choice.js +163 -0
  207. package/skills/wix-replatform/lib/destination-handoff.js +62 -0
  208. package/skills/wix-replatform/lib/destination-http.js +81 -0
  209. package/skills/wix-replatform/lib/destination-route.js +98 -0
  210. package/skills/wix-replatform/lib/destination.js +919 -0
  211. package/skills/wix-replatform/lib/discovered-census.js +231 -0
  212. package/skills/wix-replatform/lib/discovered-set-status.js +255 -0
  213. package/skills/wix-replatform/lib/document-url-guard.js +203 -0
  214. package/skills/wix-replatform/lib/entity-qa-attestation.js +339 -0
  215. package/skills/wix-replatform/lib/entity-verification.js +18 -0
  216. package/skills/wix-replatform/lib/execution-capability.js +120 -0
  217. package/skills/wix-replatform/lib/execution-log.js +170 -0
  218. package/skills/wix-replatform/lib/execution-manifest.js +20 -2
  219. package/skills/wix-replatform/lib/execution-state.js +22 -0
  220. package/skills/wix-replatform/lib/expectations-ledger.js +96 -0
  221. package/skills/wix-replatform/lib/generated-plugin-handoff.js +140 -0
  222. package/skills/wix-replatform/lib/import-readiness-contract.js +23 -0
  223. package/skills/wix-replatform/lib/import-recovery.js +800 -6
  224. package/skills/wix-replatform/lib/imported-redirect-targets.js +608 -0
  225. package/skills/wix-replatform/lib/internal-link-reconciliation.js +166 -0
  226. package/skills/wix-replatform/lib/link-audit.js +173 -0
  227. package/skills/wix-replatform/lib/link-census.js +194 -0
  228. package/skills/wix-replatform/lib/link-plan.js +148 -0
  229. package/skills/wix-replatform/lib/link-verification.js +274 -0
  230. package/skills/wix-replatform/lib/link-write-journal.js +42 -0
  231. package/skills/wix-replatform/lib/local-state.js +869 -30
  232. package/skills/wix-replatform/lib/member-completion.js +658 -0
  233. package/skills/wix-replatform/lib/member-crosswalk.js +69 -0
  234. package/skills/wix-replatform/lib/member-outcome-registry.js +265 -0
  235. package/skills/wix-replatform/lib/migration-completion.js +311 -0
  236. package/skills/wix-replatform/lib/operator-identity.js +47 -0
  237. package/skills/wix-replatform/lib/orchestration-decisions.js +248 -0
  238. package/skills/wix-replatform/lib/orchestration-preflight.js +401 -19
  239. package/skills/wix-replatform/lib/orchestration-router.js +608 -32
  240. package/skills/wix-replatform/lib/orchestration-state.js +188 -12
  241. package/skills/wix-replatform/lib/order-reference-readiness.js +30 -0
  242. package/skills/wix-replatform/lib/payments-outcome-registry.js +200 -0
  243. package/skills/wix-replatform/lib/plugin-cms-fallback.js +618 -0
  244. package/skills/wix-replatform/lib/preservation-approval-stamp.js +54 -0
  245. package/skills/wix-replatform/lib/product-media-deferral.js +96 -0
  246. package/skills/wix-replatform/lib/progress-log.js +19 -3
  247. package/skills/wix-replatform/lib/read-back-approval.js +184 -0
  248. package/skills/wix-replatform/lib/read-back-comparison.js +136 -0
  249. package/skills/wix-replatform/lib/read-back-contract.js +243 -0
  250. package/skills/wix-replatform/lib/read-back-extract.js +118 -0
  251. package/skills/wix-replatform/lib/read-back-gate.js +734 -0
  252. package/skills/wix-replatform/lib/read-back-reads.js +94 -0
  253. package/skills/wix-replatform/lib/read-back-state.js +107 -0
  254. package/skills/wix-replatform/lib/read-back-summary.js +81 -0
  255. package/skills/wix-replatform/lib/rerun-conflicts.js +334 -0
  256. package/skills/wix-replatform/lib/run-controller.js +401 -0
  257. package/skills/wix-replatform/lib/seo-outcome-registry.js +45 -0
  258. package/skills/wix-replatform/lib/setup-verification.js +148 -0
  259. package/skills/wix-replatform/lib/source-access.js +231 -0
  260. package/skills/wix-replatform/lib/source-coverage.js +1130 -0
  261. package/skills/wix-replatform/lib/source-secrets.js +277 -0
  262. package/skills/wix-replatform/lib/structured-link-patch.js +59 -0
  263. package/skills/wix-replatform/lib/task-state.js +110 -0
  264. package/skills/wix-replatform/lib/url-preservation-report.js +319 -0
  265. package/skills/wix-replatform/lib/url-preservation-state.js +349 -1
  266. package/skills/wix-replatform/lib/url-redirect-plan.js +728 -0
  267. package/skills/wix-replatform/lib/url-verification.js +315 -0
  268. package/skills/wix-replatform/lib/user-action-effects.js +42 -0
  269. package/skills/wix-replatform/lib/user-actions.js +264 -0
  270. package/skills/wix-replatform/lib/website-handoff.js +1220 -0
  271. package/skills/wix-replatform/lib/wix-client-callers.js +54 -0
  272. package/skills/wix-replatform/lib/wix-dashboard-url.js +35 -0
  273. package/skills/wix-replatform/lib/write-verification-state.js +92 -0
  274. package/skills/wix-replatform/lib/write-verification.js +274 -0
  275. package/skills/wix-replatform/resources/rp-destination/SKILL.md +137 -0
  276. package/skills/wix-replatform/resources/rp-discovery/SKILL.md +189 -16
  277. package/skills/wix-replatform/resources/rp-discovery/scripts/promote-source-coverage.js +47 -0
  278. package/skills/wix-replatform/resources/rp-execute-import/SKILL.md +869 -29
  279. package/skills/wix-replatform/resources/rp-execute-import/scripts/shipping-estimate-audit.js +70 -0
  280. package/skills/wix-replatform/resources/rp-execute-setup/SKILL.md +195 -29
  281. package/skills/wix-replatform/resources/rp-execute-setup/scripts/execute-setup.js +442 -0
  282. package/skills/wix-replatform/resources/rp-execution-policy/SKILL.md +112 -0
  283. package/skills/wix-replatform/resources/rp-execution-policy/lib/coupon-currency-plan-report.js +143 -0
  284. package/skills/wix-replatform/resources/rp-execution-policy/lib/payments-report.js +244 -0
  285. package/skills/wix-replatform/resources/rp-import-codegen/SKILL.md +2082 -38
  286. package/skills/wix-replatform/resources/rp-mapper/SKILL.md +528 -11
  287. package/skills/wix-replatform/resources/rp-mapper/lib/structure-bridge-planning.js +303 -0
  288. package/skills/wix-replatform/resources/rp-mapper/scripts/coverage-reconcile.js +88 -0
  289. package/skills/wix-replatform/resources/rp-mapper/wix-business-solutions-taxonomy.md +6 -5
  290. package/skills/wix-replatform/resources/rp-preflight/SKILL.md +40 -0
  291. package/skills/wix-replatform/resources/rp-project-intake/SKILL.md +55 -0
  292. package/skills/wix-replatform/resources/rp-quick-runtime/SKILL.md +60 -0
  293. package/skills/wix-replatform/resources/rp-quick-runtime/lib/import-progress.js +89 -0
  294. package/skills/wix-replatform/resources/rp-quick-runtime/lib/limit-recovery.js +36 -0
  295. package/skills/wix-replatform/resources/rp-quick-shopify/SKILL.md +123 -0
  296. package/skills/wix-replatform/resources/rp-quick-shopify/quick-mode.json +124 -0
  297. package/skills/wix-replatform/resources/rp-quick-shopify/scripts/quick-mode.js +522 -0
  298. package/skills/wix-replatform/resources/rp-quick-woocommerce/SKILL.md +56 -0
  299. package/skills/wix-replatform/resources/rp-quick-woocommerce/quick-mode.json +91 -0
  300. package/skills/wix-replatform/resources/rp-quick-woocommerce/scripts/quick-mode.js +255 -0
  301. package/skills/wix-replatform/resources/rp-setup-discovery/SKILL.md +80 -10
  302. package/skills/wix-replatform/resources/rp-setup-discovery/scripts/quick-setup-plan.js +44 -0
  303. package/skills/wix-replatform/resources/rp-source-csv/SKILL.md +3 -0
  304. package/skills/wix-replatform/resources/rp-source-csv/lib/csv-fileset.js +0 -0
  305. package/skills/wix-replatform/resources/rp-source-csv/scripts/csv-discovery.js +21 -1
  306. package/skills/wix-replatform/resources/rp-source-inputs/SKILL.md +66 -0
  307. package/skills/wix-replatform/resources/rp-source-sfcc/SKILL.md +153 -0
  308. package/skills/wix-replatform/resources/rp-source-shopify/SKILL.md +394 -0
  309. package/skills/wix-replatform/resources/rp-source-shopify/lib/discovery-reads.js +200 -0
  310. package/skills/wix-replatform/resources/rp-source-shopify/lib/import-findings.js +34 -0
  311. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk-sqlite-worker.js +44 -0
  312. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-bulk.js +1470 -0
  313. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-cli.js +446 -0
  314. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-gift-cards.js +474 -0
  315. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-protected-fields.js +879 -0
  316. package/skills/wix-replatform/resources/rp-source-shopify/lib/shopify-session.js +426 -0
  317. package/skills/wix-replatform/resources/rp-source-shopify/queries/articles.graphql +24 -0
  318. package/skills/wix-replatform/resources/rp-source-shopify/queries/blogs.graphql +18 -0
  319. package/skills/wix-replatform/resources/rp-source-shopify/queries/collections.graphql +40 -0
  320. package/skills/wix-replatform/resources/rp-source-shopify/queries/customers.graphql +76 -0
  321. package/skills/wix-replatform/resources/rp-source-shopify/queries/delivery-profiles.graphql +51 -0
  322. package/skills/wix-replatform/resources/rp-source-shopify/queries/discounts.graphql +35 -0
  323. package/skills/wix-replatform/resources/rp-source-shopify/queries/files.graphql +20 -0
  324. package/skills/wix-replatform/resources/rp-source-shopify/queries/gift-cards.graphql +18 -0
  325. package/skills/wix-replatform/resources/rp-source-shopify/queries/inventory.graphql +31 -0
  326. package/skills/wix-replatform/resources/rp-source-shopify/queries/locations.graphql +21 -0
  327. package/skills/wix-replatform/resources/rp-source-shopify/queries/menus.graphql +21 -0
  328. package/skills/wix-replatform/resources/rp-source-shopify/queries/metaobjects.graphql +30 -0
  329. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-core.graphql +150 -0
  330. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-fulfillments.graphql +36 -0
  331. package/skills/wix-replatform/resources/rp-source-shopify/queries/orders-refunds.graphql +48 -0
  332. package/skills/wix-replatform/resources/rp-source-shopify/queries/pages.graphql +20 -0
  333. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-address.graphql +8 -0
  334. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-email.graphql +6 -0
  335. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-name.graphql +8 -0
  336. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-customers-phone.graphql +8 -0
  337. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-address.graphql +12 -0
  338. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-email.graphql +6 -0
  339. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-name.graphql +13 -0
  340. package/skills/wix-replatform/resources/rp-source-shopify/queries/probe-orders-phone.graphql +13 -0
  341. package/skills/wix-replatform/resources/rp-source-shopify/queries/products.graphql +73 -0
  342. package/skills/wix-replatform/resources/rp-source-shopify/queries/url-redirects.graphql +13 -0
  343. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-discovery.js +693 -0
  344. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-extract.js +283 -0
  345. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-gift-card-remedy.js +191 -0
  346. package/skills/wix-replatform/resources/rp-source-shopify/scripts/shopify-session-check.js +115 -0
  347. package/skills/wix-replatform/resources/rp-source-shopify/source-schema.example.json +201 -0
  348. package/skills/wix-replatform/resources/rp-source-wordpress/PLUGIN-READ-PLANS.md +146 -0
  349. package/skills/wix-replatform/resources/rp-source-wordpress/SKILL.md +736 -22
  350. package/skills/wix-replatform/resources/rp-source-wordpress/lib/blocked-data-handlers.js +73 -0
  351. package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.js +115 -0
  352. package/skills/wix-replatform/resources/rp-source-wordpress/lib/checkout-field-csv-parser.test-fixture.js +91 -0
  353. package/skills/wix-replatform/resources/rp-source-wordpress/lib/coupon-discovery.js +440 -0
  354. package/skills/wix-replatform/resources/rp-source-wordpress/lib/currency-gate.js +125 -0
  355. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.js +252 -0
  356. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-source-keys.json +183 -0
  357. package/skills/wix-replatform/resources/rp-source-wordpress/lib/gateway-summary.mjs +333 -0
  358. package/skills/wix-replatform/resources/rp-source-wordpress/lib/invoice-discovery.js +426 -0
  359. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-disposition.js +400 -0
  360. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-entity-read-plan.js +633 -0
  361. package/skills/wix-replatform/resources/rp-source-wordpress/lib/plugin-knowledge.js +1127 -0
  362. package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.js +194 -0
  363. package/skills/wix-replatform/resources/rp-source-wordpress/lib/refund-corroboration.json +100 -0
  364. package/skills/wix-replatform/resources/rp-source-wordpress/lib/sampled-ids-batch.js +157 -0
  365. package/skills/wix-replatform/resources/rp-source-wordpress/lib/secret-value-guard.js +234 -0
  366. package/skills/wix-replatform/resources/rp-source-wordpress/lib/store-currency.js +101 -0
  367. package/skills/wix-replatform/resources/rp-source-wordpress/lib/structure-bridge-reader.js +755 -0
  368. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.js +1074 -0
  369. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wix-wp-plugin-v2-client.test-fixture.js +149 -0
  370. package/skills/wix-replatform/resources/rp-source-wordpress/lib/woo-inventory.js +25 -0
  371. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-http.js +204 -9
  372. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-image-url.js +186 -0
  373. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-plugin-detect.js +1422 -0
  374. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-redirect-sources.js +343 -0
  375. package/skills/wix-replatform/resources/rp-source-wordpress/lib/wp-route-classifier.js +262 -26
  376. package/skills/wix-replatform/resources/rp-source-wordpress/lib/yoast-product-seo-evidence.js +272 -0
  377. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/README.md +373 -0
  378. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/advanced-custom-fields.json +101 -0
  379. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/back-in-stock-notifier-for-woocommerce.json +81 -0
  380. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/capabilities-pending-decision.json +128 -0
  381. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/cms-fallback-eligible.json +18 -0
  382. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/contact-form-7.json +104 -0
  383. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/facebook-for-woocommerce.json +44 -0
  384. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/fingerprint-aliases.json +49 -0
  385. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/free-gifts-for-woocommerce.json +94 -0
  386. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/index.json +863 -0
  387. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mailpoet.json +52 -0
  388. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/membership-for-woocommerce.json +161 -0
  389. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/mwb-bookings-for-woocommerce.json +180 -0
  390. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/no-migration-needed.json +286 -0
  391. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pricing-deals-for-woocommerce.json +79 -0
  392. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/product-extras-for-woocommerce.json +120 -0
  393. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/pw-woocommerce-gift-cards.json +128 -0
  394. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/requires-development.json +88 -0
  395. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seo-by-rank-math.json +80 -0
  396. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/seriously-simple-podcasting.json +46 -0
  397. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/strong-testimonials.json +55 -0
  398. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/subscriptions-for-woocommerce.json +99 -0
  399. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/tablepress.json +55 -0
  400. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/the-events-calendar.json +160 -0
  401. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/ti-woocommerce-wishlist.json +81 -0
  402. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-checkout-field-editor-pro.json +60 -0
  403. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-discount-rules.json +74 -0
  404. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woo-product-bundle.json +75 -0
  405. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-gift-cards.json +60 -0
  406. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-other-payment-gateway.json +44 -0
  407. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-pdf-invoices-packing-slips.json +57 -0
  408. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-product-addon.json +141 -0
  409. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/woocommerce-progressive-discounts.json +54 -0
  410. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wordpress-seo.json +158 -0
  411. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpc-composite-products.json +97 -0
  412. package/skills/wix-replatform/resources/rp-source-wordpress/plugins/wpforms-lite.json +86 -0
  413. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/capture-store-settings.js +90 -0
  414. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge-validate.js +51 -0
  415. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/plugin-knowledge.js +103 -0
  416. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-discovery.js +1668 -33
  417. package/skills/wix-replatform/resources/rp-source-wordpress/scripts/wp-plugin-inventory.js +273 -0
  418. package/skills/wix-replatform/resources/rp-source-wordpress/secret-value-patterns.v1.json +11 -0
  419. package/skills/wix-replatform/resources/rp-source-wordpress/sub-parts.json +13 -0
  420. package/skills/wix-replatform/resources/rp-target-wix/SKILL.md +446 -32
  421. package/skills/wix-replatform/resources/rp-target-wix/app-page-routing-capabilities.json +36 -0
  422. package/skills/wix-replatform/resources/rp-target-wix/domains/README.md +38 -2
  423. package/skills/wix-replatform/resources/rp-target-wix/domains/SERVICE-PLUGINS.md +322 -0
  424. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/docs-survey.json +70 -0
  425. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/domain.json +19 -9
  426. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/category.json +42 -8
  427. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/comment.json +54 -8
  428. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/post.json +66 -10
  429. package/skills/wix-replatform/resources/rp-target-wix/domains/blog/entities/tag.json +38 -7
  430. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/docs-survey.json +332 -0
  431. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/domain.json +75 -0
  432. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/booking.json +181 -0
  433. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service-add-on.json +130 -0
  434. package/skills/wix-replatform/resources/rp-target-wix/domains/bookings/entities/service.json +180 -0
  435. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/docs-survey.json +161 -0
  436. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/domain.json +15 -4
  437. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/collection.json +39 -7
  438. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/data-item.json +47 -8
  439. package/skills/wix-replatform/resources/rp-target-wix/domains/cms/entities/reference.json +7 -1
  440. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/docs-survey.json +204 -0
  441. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/domain.json +17 -4
  442. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/contact.json +159 -15
  443. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/email-subscription.json +80 -12
  444. package/skills/wix-replatform/resources/rp-target-wix/domains/crm/entities/label.json +48 -8
  445. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/docs-survey.json +306 -0
  446. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/domain.json +30 -7
  447. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/delivery-profile.json +171 -0
  448. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/discount-rule.json +182 -0
  449. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-applied-discount.json +129 -0
  450. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-invoice-document.json +192 -0
  451. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item-options.json +123 -0
  452. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-line-item.json +60 -12
  453. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-merchant-note.json +112 -0
  454. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-payment.json +191 -0
  455. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order-refund.json +140 -13
  456. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/order.json +383 -18
  457. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/pickup-location.json +122 -0
  458. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/shipping-option.json +98 -0
  459. package/skills/wix-replatform/resources/rp-target-wix/domains/ecom/entities/wishlist-item.json +86 -0
  460. package/skills/wix-replatform/resources/rp-target-wix/domains/events/docs-survey.json +128 -0
  461. package/skills/wix-replatform/resources/rp-target-wix/domains/events/domain.json +38 -0
  462. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/category.json +73 -0
  463. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/event.json +107 -0
  464. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/registration-form.json +89 -0
  465. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/rsvp.json +118 -0
  466. package/skills/wix-replatform/resources/rp-target-wix/domains/events/entities/ticket-definition.json +93 -0
  467. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/docs-survey.json +86 -0
  468. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/domain.json +56 -0
  469. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form-submission.json +121 -0
  470. package/skills/wix-replatform/resources/rp-target-wix/domains/forms/entities/form.json +134 -0
  471. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/docs-survey.json +41 -0
  472. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/domain.json +50 -0
  473. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card-product.json +87 -0
  474. package/skills/wix-replatform/resources/rp-target-wix/domains/gift-cards/entities/gift-card.json +130 -0
  475. package/skills/wix-replatform/resources/rp-target-wix/domains/index.json +914 -24
  476. package/skills/wix-replatform/resources/rp-target-wix/domains/media/docs-survey.json +41 -0
  477. package/skills/wix-replatform/resources/rp-target-wix/domains/media/domain.json +14 -4
  478. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-file.json +45 -8
  479. package/skills/wix-replatform/resources/rp-target-wix/domains/media/entities/media-folder.json +41 -7
  480. package/skills/wix-replatform/resources/rp-target-wix/domains/members/docs-survey.json +119 -0
  481. package/skills/wix-replatform/resources/rp-target-wix/domains/members/domain.json +13 -3
  482. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/badge.json +41 -7
  483. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/custom-field.json +82 -0
  484. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member-about.json +77 -0
  485. package/skills/wix-replatform/resources/rp-target-wix/domains/members/entities/member.json +165 -13
  486. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/docs-survey.json +41 -0
  487. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/domain.json +46 -0
  488. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan-order.json +137 -0
  489. package/skills/wix-replatform/resources/rp-target-wix/domains/pricing-plans/entities/plan.json +133 -0
  490. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/docs-survey.json +7 -0
  491. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/domain.json +36 -0
  492. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/amazon-shop-connect.json +71 -0
  493. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/ebay-shop-connect.json +67 -0
  494. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/google-youtube-connect.json +72 -0
  495. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/meta-catalog-connect.json +74 -0
  496. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/pinterest-catalog-connect.json +69 -0
  497. package/skills/wix-replatform/resources/rp-target-wix/domains/sales-channels/entities/tiktok-catalog-connect.json +61 -0
  498. package/skills/wix-replatform/resources/rp-target-wix/domains/schema.json +28 -1
  499. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/docs-survey.json +109 -0
  500. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/domain.json +73 -0
  501. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/item-seo-tags.json +148 -0
  502. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/seo-pattern.json +91 -0
  503. package/skills/wix-replatform/resources/rp-target-wix/domains/seo/entities/site-seo-tags.json +79 -0
  504. package/skills/wix-replatform/resources/rp-target-wix/domains/site/docs-survey.json +32 -0
  505. package/skills/wix-replatform/resources/rp-target-wix/domains/site/domain.json +49 -0
  506. package/skills/wix-replatform/resources/rp-target-wix/domains/site/entities/business-contact.json +103 -0
  507. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/docs-survey.json +203 -0
  508. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/domain.json +27 -10
  509. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/back-in-stock-request.json +119 -0
  510. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/brand.json +111 -0
  511. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/category.json +52 -8
  512. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/coupon.json +214 -10
  513. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/inventory-item.json +93 -13
  514. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-customization.json +111 -0
  515. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product-tag.json +52 -10
  516. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/product.json +145 -14
  517. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/promotion.json +84 -0
  518. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/review.json +178 -0
  519. package/skills/wix-replatform/resources/rp-target-wix/domains/stores/entities/subscription-product.json +87 -0
  520. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/docs-survey.json +106 -0
  521. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/domain.json +64 -0
  522. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/manual-tax-mapping.json +119 -0
  523. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-group.json +94 -0
  524. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-region.json +113 -0
  525. package/skills/wix-replatform/resources/rp-target-wix/domains/tax/entities/tax-settings.json +65 -0
  526. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-author-attribution.js +165 -0
  527. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-client.js +60 -0
  528. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-duplicates.js +201 -0
  529. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-ledger.js +306 -0
  530. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-report.js +229 -0
  531. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build-stage.js +55 -0
  532. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-build.js +715 -0
  533. package/skills/wix-replatform/resources/rp-target-wix/lib/blog-post-media.js +567 -0
  534. package/skills/wix-replatform/resources/rp-target-wix/lib/bulk-results.js +73 -0
  535. package/skills/wix-replatform/resources/rp-target-wix/lib/contact-extended-fields.js +194 -0
  536. package/skills/wix-replatform/resources/rp-target-wix/lib/contact-import.js +133 -0
  537. package/skills/wix-replatform/resources/rp-target-wix/lib/contract-ledger.js +13 -4
  538. package/skills/wix-replatform/resources/rp-target-wix/lib/coupon-redemption-drift.js +491 -0
  539. package/skills/wix-replatform/resources/rp-target-wix/lib/customer-member.js +104 -0
  540. package/skills/wix-replatform/resources/rp-target-wix/lib/data-extension-schema.js +673 -0
  541. package/skills/wix-replatform/resources/rp-target-wix/lib/domain-knowledge.js +453 -2
  542. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-balance-reconciliation.js +119 -0
  543. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-build.js +566 -0
  544. package/skills/wix-replatform/resources/rp-target-wix/lib/gift-card-redemption-build.js +249 -0
  545. package/skills/wix-replatform/resources/rp-target-wix/lib/order-applied-discount-build.js +63 -0
  546. package/skills/wix-replatform/resources/rp-target-wix/lib/order-buyer.js +187 -0
  547. package/skills/wix-replatform/resources/rp-target-wix/lib/order-channel-build.js +64 -0
  548. package/skills/wix-replatform/resources/rp-target-wix/lib/order-history.js +1027 -0
  549. package/skills/wix-replatform/resources/rp-target-wix/lib/order-invoice-contract.js +90 -0
  550. package/skills/wix-replatform/resources/rp-target-wix/lib/order-payment-mapping.js +420 -0
  551. package/skills/wix-replatform/resources/rp-target-wix/lib/pricing-plan-definition.js +177 -0
  552. package/skills/wix-replatform/resources/rp-target-wix/lib/product-import.js +151 -0
  553. package/skills/wix-replatform/resources/rp-target-wix/lib/product-seo.js +723 -0
  554. package/skills/wix-replatform/resources/rp-target-wix/lib/seo-redirects.js +353 -0
  555. package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-build.js +1437 -0
  556. package/skills/wix-replatform/resources/rp-target-wix/lib/shipping-option-stage.js +354 -0
  557. package/skills/wix-replatform/resources/rp-target-wix/lib/store-config-build.js +134 -0
  558. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-category-attach.js +221 -0
  559. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-inventory.js +248 -0
  560. package/skills/wix-replatform/resources/rp-target-wix/lib/stores-verification.js +86 -3
  561. package/skills/wix-replatform/resources/rp-target-wix/lib/tax-build.js +231 -0
  562. package/skills/wix-replatform/resources/rp-target-wix/lib/url-mapper-transport.js +28 -0
  563. package/skills/wix-replatform/resources/rp-target-wix/lib/value-utils.js +14 -0
  564. package/skills/wix-replatform/resources/rp-target-wix/lib/variant-correlation.js +34 -0
  565. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-build.js +166 -15
  566. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-target-spec.js +19 -2
  567. package/skills/wix-replatform/resources/rp-target-wix/lib/wix-writers.js +4079 -257
  568. package/skills/wix-replatform/resources/rp-target-wix/lib/write-verification.js +3 -0
  569. package/skills/wix-replatform/resources/rp-target-wix/scripts/docs-survey-sync.js +150 -0
  570. package/skills/wix-replatform/resources/rp-target-wix/scripts/domain-knowledge-validate.js +40 -2
  571. package/skills/wix-replatform/resources/rp-target-wix/scripts/verify-stores.js +6 -1
  572. package/skills/wix-replatform/resources/rp-telemetry/SKILL.md +148 -8
  573. package/skills/wix-replatform/resources/rp-telemetry/lib/bi-sink.js +83 -15
  574. package/skills/wix-replatform/resources/rp-telemetry/lib/telemetry-recorder.js +1119 -215
  575. package/skills/wix-replatform/resources/rp-telemetry/lib/transcript-digest.js +318 -0
  576. package/skills/wix-replatform/resources/rp-telemetry/scripts/meter-from-audit.js +2 -2
  577. package/skills/wix-replatform/resources/rp-telemetry/scripts/rp-telemetry.js +106 -4
  578. package/skills/wix-replatform/resources/rp-website-continuation/SKILL.md +94 -0
  579. package/skills/wix-replatform/scripts/census-reconciliation.js +25 -0
  580. package/skills/wix-replatform/scripts/cms-fallback.js +89 -0
  581. package/skills/wix-replatform/scripts/discovered-census.js +26 -0
  582. package/skills/wix-replatform/scripts/discovered-set-status.js +33 -0
  583. package/skills/wix-replatform/scripts/entity-qa-attestation.js +130 -0
  584. package/skills/wix-replatform/scripts/execution-health-check.js +415 -0
  585. package/skills/wix-replatform/scripts/execution-manifest-validate.js +1 -1
  586. package/skills/wix-replatform/scripts/execution-state-prepare.js +1 -1
  587. package/skills/wix-replatform/scripts/expectations-ledger.js +44 -0
  588. package/skills/wix-replatform/scripts/import-contract-verify.js +183 -0
  589. package/skills/wix-replatform/scripts/internal-link-audit.js +90 -0
  590. package/skills/wix-replatform/scripts/item-seo-tags-verify.js +205 -0
  591. package/skills/wix-replatform/scripts/migration-completion.js +76 -0
  592. package/skills/wix-replatform/scripts/orchestration-preflight.js +21 -2
  593. package/skills/wix-replatform/scripts/orchestration-route.js +32 -1
  594. package/skills/wix-replatform/scripts/orchestration-state.js +146 -10
  595. package/skills/wix-replatform/scripts/read-back-gate.js +175 -0
  596. package/skills/wix-replatform/scripts/replatform-console.js +146 -0
  597. package/skills/wix-replatform/scripts/replatform.js +436 -0
  598. package/skills/wix-replatform/scripts/source-secrets.js +77 -0
  599. package/skills/wix-replatform/scripts/url-redirects.js +286 -0
  600. package/skills/wix-replatform/scripts/user-action.js +58 -0
  601. package/skills/wix-replatform/scripts/website-handoff-generate.js +64 -0
  602. package/skills/wix-replatform/scripts/wix-dashboard-url.js +54 -0
  603. package/skills/wix-replatform/scripts/wix-destination.js +143 -0
  604. package/skills/wix-replatform/scripts/wix-headless-scaffold.js +762 -0
  605. package/skills/wix-vibe-headless/SKILL.md +7 -3
  606. package/skills/wix-vibe-headless/install/deploy.cjs +61 -34
  607. package/skills/wix-vibe-headless/platforms/base44.md +1 -0
  608. package/skills/wix-vibe-headless/references/donations/INSTRUCTIONS.md +88 -0
  609. package/skills/wix-vibe-headless/references/donations/app/rest/wix-donations.js +112 -0
  610. package/skills/wix-vibe-headless/references/donations/seed/SEED.md +11 -0
  611. package/skills/wix-vibe-headless/references/donations/tests/donations.test.mjs +258 -0
  612. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +5 -2
  613. package/skills/wix-vibe-headless/references/restaurants/app/context/OrderCartContext.jsx +4 -1
  614. package/skills/wix-vibe-headless/references/restaurants/app/rest/wix-restaurants-ordering.js +67 -3
  615. package/skills/wix-vibe-headless/references/shared/app/rest/wix-client.js +4 -3
  616. package/skills/wix-replatform/resources/rp-target-wix/SPEC-product-reviews-and-blog-authorship.md +0 -330
@@ -25,12 +25,27 @@ This is not a stage in the migration flow — it is a reference consulted by two
25
25
  WordPress-specific knowledge is already baked into the generated reader code. Keeping the
26
26
  WordPress knowledge here is what lets the rest of the workflow stay platform-agnostic.
27
27
 
28
+ For app routing and internal-link reconciliation, the generated reader also enumerates the captured source roots, declared
29
+ source hosts, source permalink/alias evidence, and each record's containing source page.
30
+ It passes those facts to the shared `internal-link-audit.js plan` input after backend
31
+ import. Do not infer the containing page from a file basename or assume a default
32
+ WooCommerce path. The deterministic census records every captured file as scanned,
33
+ unreadable or unsupported; unsupported documents, dynamic URLs and missing captures
34
+ remain coverage items. Never scan `config/*.env` or execute captured source code.
35
+
28
36
  ## Platform identity
29
37
 
30
38
  - Source platform: WordPress (core REST `wp/v2`), optionally WooCommerce (`wc/v3`).
31
39
  - Detect by hitting `<base-url>/wp-json/` — the REST index lists advertised namespaces.
32
40
  - Set `"platform": "wordpress"` (and note WooCommerce presence in `sourceMeta`) in the
33
- emitted `source-schema.json`.
41
+ emitted `source-schema.json`. This describes the discovery artifact, not the orchestration
42
+ decision.
43
+ - Once discovery confirms WooCommerce, record `sourcePlatform.value=woocommerce` in
44
+ `orchestration/decisions.json` and use `config/source.woocommerce.env`. Otherwise record
45
+ `sourcePlatform.value=wordpress` there and use `config/source.wordpress.env`. Generated
46
+ importers read that exact nested value — never the decision wrapper object and never the
47
+ nullable `orchestration/run.json` `sourcePlatform` field. The adapter remains
48
+ `rp-source-wordpress` in both cases.
34
49
 
35
50
  ## Capture (discovery-time)
36
51
 
@@ -43,13 +58,16 @@ turn that hint into a target decision here. Keep frontend, theme, runtime, admin
43
58
  transient routes skipped by default even if a knowledge entry exists for a related
44
59
  setup/config target.
45
60
 
46
- Before capture, verify `migrations/<project>/config/source.wordpress.env`. Create it if
47
- missing, using empty values for the user to fill:
61
+ After identifying the platform, verify `migrations/<project>/config/source.<platform>.env`.
62
+ For a confirmed WooCommerce source, this is `source.woocommerce.env`; for WordPress without
63
+ WooCommerce evidence, it is `source.wordpress.env`. Create the selected file if missing with
64
+ empty values:
48
65
 
49
66
  ```bash
50
67
  WP_BASE_URL=
51
68
  WP_USERNAME=
52
69
  WP_APPLICATION_PASSWORD=
70
+ WMH2_MIGRATION_KEY=
53
71
  WP_MEDIA_URL_REWRITE_FROM=
54
72
  WP_MEDIA_URL_REWRITE_TO=
55
73
  WC_CONSUMER_KEY=
@@ -64,13 +82,52 @@ the user which data scope they want:
64
82
 
65
83
  This choice is mandatory and must be explicit. Do not ask for `WP_USERNAME`,
66
84
  `WP_APPLICATION_PASSWORD`, or WooCommerce keys until the user chooses `also include
67
- private/authenticated data`.
85
+ private/authenticated data`. Request `WMH2_MIGRATION_KEY` only when bridge-backed entities
86
+ are actually in scope and the detected source has no WooCommerce. Collect every sensitive
87
+ value through the secure Secrets Manager flow; do not ask the user to paste it into chat.
88
+
89
+ There are two independent scopes, and a complete run may need both credentials:
90
+
91
+ - **Private WordPress/WooCommerce REST** (`wp/v2`, `wc/v3`) requires a WordPress
92
+ Application Password (`WP_USERNAME` + `WP_APPLICATION_PASSWORD`).
93
+ - **`wix-wp-plugin-v2` bridge reads** use the Application Password path only when
94
+ WooCommerce is present, because that route requires `manage_woocommerce`. When the
95
+ source has no WooCommerce, collect `WMH2_MIGRATION_KEY` for bridge reads instead. The
96
+ key is generated and shown once on the plugin's wp-admin page at activation or rotation.
97
+ It never authenticates ordinary `wp/v2` or `wc/v3` reads.
98
+
99
+ Key sensitivity is predefined and does not change at runtime. Requiredness is separate
100
+ and may depend on the selected acquisition mode or source behavior.
101
+
102
+ | Key | Sensitivity | Requiredness |
103
+ |---|---|---|
104
+ | `WP_BASE_URL` | non-sensitive | Required for URL-based capture |
105
+ | `WP_USERNAME` | sensitive | Required for private/authenticated WordPress capture via Application Password |
106
+ | `WP_APPLICATION_PASSWORD` | sensitive | Required for private/authenticated WordPress capture via Application Password |
107
+ | `WMH2_MIGRATION_KEY` | sensitive | Required for `wix-wp-plugin-v2` bridge reads when the source has no WooCommerce; optional otherwise |
108
+ | `WP_MEDIA_URL_REWRITE_FROM` | non-sensitive | Optional |
109
+ | `WP_MEDIA_URL_REWRITE_TO` | non-sensitive | Optional |
110
+ | `WC_CONSUMER_KEY` | sensitive | Optional unless WooCommerce rejects the WordPress Application Password for `wc/v3` reads |
111
+ | `WC_CONSUMER_SECRET` | sensitive | Optional unless WooCommerce rejects the WordPress Application Password for `wc/v3` reads |
68
112
 
69
113
  Required for a complete WordPress/WooCommerce capture:
70
114
 
71
115
  - `WP_BASE_URL`
72
- - `WP_USERNAME`
73
- - `WP_APPLICATION_PASSWORD`
116
+ - `WP_USERNAME` + `WP_APPLICATION_PASSWORD` when private REST-visible data is in scope
117
+ - `WMH2_MIGRATION_KEY` additionally when bridge-backed entities are in scope and the
118
+ source has no WooCommerce
119
+
120
+ ⚠️ **`WMH2_MIGRATION_KEY` only unblocks the plugin's own `/structure` and `/query` routes**
121
+ (`wix-wp-plugin-v2`'s `structure-bridge-plugin` entities — custom plugin tables with no REST
122
+ route) — `wp-discovery.js`/`wix-wp-plugin-v2-client.js` sign requests with it
123
+ automatically when the signed bridge path is selected. It does **not** authenticate
124
+ standard WordPress/WooCommerce REST (`wp/v2`, `wc/v3`) at all — reading posts, pages,
125
+ products, orders, customers, and every other REST-visible entity still requires the
126
+ Application Password. On a source site with no WooCommerce, the Application Password routes
127
+ are `manage_woocommerce`-gated and unusable by any user regardless, so the migration key is
128
+ the only credential that reaches the bridge. If no separate WordPress REST credential is
129
+ available, ordinary private content still cannot be captured through this adapter and needs
130
+ another source API or export.
74
131
 
75
132
  `WC_CONSUMER_KEY` and `WC_CONSUMER_SECRET` are optional when WooCommerce accepts the
76
133
  WordPress Application Password for `wc/v3` reads; ask for them only if WooCommerce routes
@@ -81,7 +138,7 @@ WordPress API is reached through a public tunnel but media/file URLs inside reco
81
138
  point at `localhost` or another private origin. If they are blank, generated readers may
82
139
  rewrite localhost/private origins to `WP_BASE_URL` when `WP_BASE_URL` is public.
83
140
 
84
- `config/source.wordpress.env` is a secret-bearing file once it may contain real values.
141
+ `config/source.<platform>.env` is a secret-bearing file once it may contain real values.
85
142
  Do not read it with whole-file commands that print its contents into tool output. Check
86
143
  only whether the file exists and whether each required key is present/blank/missing; when
87
144
  describing status, name keys only and never echo values.
@@ -90,12 +147,14 @@ describing status, name keys only and never echo values.
90
147
  containing this `SKILL.md`; see `CONVENTIONS.md`):
91
148
 
92
149
  ```
93
- node scripts/wp-discovery.js --base-url <url> --out-dir <migrations-root>/<project>/data/wp-discovery
150
+ node scripts/wp-discovery.js --env-file <migrations-root>/<project>/config/source.<platform>.env --out-dir <migrations-root>/<project>/data/wp-discovery
94
151
  ```
95
152
 
96
153
  It walks the REST index, runs one `OPTIONS` + a small `GET` sample per entity, and
97
154
  writes per-entity markdown (routes, schemas, sample records, record counts,
98
- relationships). Pass auth options (see Read contract → Auth) for a complete capture.
155
+ relationships). `--env-file` is the preferred launch path for project-local
156
+ credential-bearing config because it avoids shell sourcing of secret-bearing files.
157
+ Pass direct auth options only when there is no project-local config yet.
99
158
  For long runs, pass `--progress-log <path>` and poll it per
100
159
  `CONVENTIONS.md#progress-log-polling`.
101
160
 
@@ -120,7 +179,7 @@ describing status, name keys only and never echo values.
120
179
  authenticated → per-author mapping: administrators → owner user-member,
121
180
  content roles → per-author members; subscribers/customers → **Wix contacts by
122
181
  default** — a contact is the no-login CRM person entity and Contacts V5 upserts in
123
- bulk — promote to full site members (MEM-01: 1/sec serial, re-registration) only when
182
+ bulk — promote to full site members (1/sec serial, re-registration) only when
124
183
  member-gated features are in scope. Every Wix member auto-creates its contact, so
125
184
  member-first ordering: never pre-create a contact for someone who will become a
126
185
  member. Note contacts cannot author blog posts or comments — bylines need members).
@@ -135,6 +194,12 @@ describing status, name keys only and never echo values.
135
194
  setup, and integration/helper APIs. It writes the skipped-route audit trail to
136
195
  `skipped-routes.json`.
137
196
 
197
+ It also always writes `source-coverage.json` (`rows: []` when clean): one row per entity
198
+ whose data request returned 401/403 (`auth-gated`) or failed with no records (an
199
+ OPTIONS-probe failure alone is not one), per backend data route skipped by rule, per
200
+ credential-bearing route never read (`not-recoverable`), and per unsupported route
201
+ namespace. Frontend, theme, admin and transient skips get no row. `rp-discovery` promotes it; never edit it by hand.
202
+
138
203
  For WooCommerce, prefer canonical durable data APIs such as `wc/v3` over duplicated
139
204
  `wc/v2`/`wc/v1` routes for authenticated/private commerce reads. For `public content only`
140
205
  discovery, probe public Store API catalog routes before declaring commerce deferred:
@@ -142,8 +207,51 @@ describing status, name keys only and never echo values.
142
207
  commerce data when present. Cart and checkout Store API routes remain out of scope as
143
208
  runtime session state.
144
209
 
145
- 6. Map known plugins to entity types where relevant (e.g. WooCommerce → store,
146
- Seriously Simple Podcasting `ssp/v1` → podcasts, Yoast → SEO, ACF → custom fields).
210
+ **Images: never read a field, always resolve.** An image reference in a WordPress-family
211
+ payload is a *set* of candidate URLs for one picture — the merchant's original upload plus
212
+ the crops WordPress generated from it. Which field holds the original varies by route, and
213
+ the two WooCommerce APIs disagree on the **same site**: on `wc/store/v1` `images[].src` is
214
+ the original, while on `wc/v3` it is resolved through WordPress's registered image sizes
215
+ and can be the *smallest* crop, because any theme or image-optimizer plugin may redefine
216
+ those sizes. Pass the whole reference to `resolveImageUrl` / `resolveImageUrls` in
217
+ `lib/wp-image-url.js` and use what it returns. It handles `src`, `thumbnail`,
218
+ `source_url`, `srcset`, `thumbnail_srcset`, and `media_details.sizes.*`, prefers the
219
+ original, falls back to the widest crop, percent-encodes the result, and retains the
220
+ attachment id as `sourceId` for the media-record check below.
221
+
222
+ **Product pictures require source-record confirmation in generated imports.** For the
223
+ exact orchestration identities `woocommerce` and `wordpress`, resolving the product
224
+ reference is only the first step. Vendor/use the shared root `lib/confirmed-pictures.js`:
225
+ collect the selected products' distinct positive `sourceId` values, call
226
+ `readSourceMediaRecords({ ids, source: { baseUrl, request: authenticatedWpRequest } })`,
227
+ where that wrapper delegates to `fetchJson` with the run's existing auth headers, then pass each
228
+ whole resolved gallery to `confirmPictures`. The read requests authenticated
229
+ `GET /wp/v2/media?include=<ids>&per_page=100` chunks in sorted groups of at most 100; each
230
+ chunk has one total 60-second transport deadline. The media record's `source_url` is the
231
+ evidence. Never infer an original from the filename and never construct a lookalike read or
232
+ confirmed list: provenance is private to the shared module.
233
+
234
+ A picture with no positive attachment id, no returned media record, or a media record in an
235
+ unreadable chunk is unconfirmed (`no-source-id`, `no-source-record`, or
236
+ `source-unreadable`). One unconfirmed picture defers the entire product gallery; continue
237
+ with non-media product fields and follow `rp-import-codegen`'s ledger/recovery contract.
238
+ Quick mode is unchanged and does not use this boundary.
239
+
240
+ ⚠️ This is not optional on the public path just because `src` happens to be right there
241
+ today. Same call, everywhere — a contract test pins the Store API no-op, so a change in
242
+ that behaviour surfaces as a test failure rather than as a merchant's blurry storefront.
243
+
244
+ ⚠️ The destination gives one attempt. Wix copies the single URL you send and generates
245
+ every derivative from it, with no way to raise quality afterwards, so a crop shipped here
246
+ is that product's permanent ceiling. When `resolvedIsCrop()` is true the source genuinely
247
+ had nothing better — report those images in the run's own record rather than shipping them
248
+ silently.
249
+
250
+ 6. **Plugin coverage runs automatically.** Plugin handling is a deterministic pre-pass plus
251
+ declarative profiles, not prose guesswork — see "Plugin coverage" below. The capture
252
+ script writes `plugin-inventory.json` and `plugin-coverage.json` alongside the per-entity
253
+ files. Never hand-map a plugin from memory; consult the profiles through
254
+ `scripts/plugin-knowledge.js`.
147
255
 
148
256
  7. **Localhost sources and media URLs.** A source at `localhost`, `127.0.0.1`, or another
149
257
  private-only host is valid for discovery and source reads from the user's machine.
@@ -168,6 +276,477 @@ The raw capture is evidence, not a hand-off artifact. `rp-discovery` synthesizes
168
276
  the canonical artifacts and records traceability pointers (`rawDiscovery`, per-entity
169
277
  `rawFile`).
170
278
 
279
+ ## Payment gateways: read the summary, never the route
280
+
281
+ `GET /wc/v3/payment_gateways` and `/wc/v3/payment_gateways/{id}` return each gateway's **entire
282
+ settings object**. On a site with a real gateway connected that includes the live API key and
283
+ secret. Anything a tool returns is in the model's context, the transcript and potentially a
284
+ durable run artifact, so there is no careful-handling mitigation available after the fact — the
285
+ value must never be requested.
286
+
287
+ **`fetchGatewaySummary()` in `lib/wp-http.js` is the only way to read gateway configuration.**
288
+ It returns, per gateway, `id` / `title` / `enabled` / `order` and `fields: [{ name, state }]`,
289
+ where `state` is `present`, `blank` or `absent` — plus a `value` for the short allowlist of
290
+ display and behaviour fields, each of which passes the shared secret-value guard first. That is
291
+ everything discovery legitimately needs: which gateways are registered, which are enabled, and
292
+ whether a credential is configured. Request-level outcomes (`ok`, `unauthorized`, `unreachable`,
293
+ `malformed`) are a separate axis, and a failed request returns **zero** field entries rather
294
+ than a page of `absent`, so "this gateway has no API key" can never be confused with "we could
295
+ not ask".
296
+
297
+ Everything else about the route is closed, on purpose:
298
+
299
+ - `fetchJson()` **refuses** either gateway route before issuing the request, below discovery's
300
+ `--include-route` override. There is no flag, option, capability token or environment variable
301
+ that lifts it; adding one is the exposure with extra steps.
302
+ - The route classifier categorizes both shapes as `excluded_credential_bearing`, and neither
303
+ `--include-route` nor `--include-excluded-category` can promote them to `sample`.
304
+ - A plugin profile may not declare a gateway route as a readable entity route, under any flag.
305
+ - Never write a gateway field value into `source-profile.md`, `source-schema.json`, a sample
306
+ record, a progress log, a completion report, a CMS field, telemetry, or `.local.env`. Never a
307
+ prefix, a length or a hash of one either — each of those is a usable clue on its own.
308
+
309
+ Report what was read. A run that states "N gateways, M enabled, K credential fields present, 0
310
+ values retained" is auditable; one that silently read the settings blob is not.
311
+ `describeGatewaySummary()` produces that line for the approval gate.
312
+
313
+ ⚠️ The value guard is a pattern list, not a guarantee — a credential in an unrecognized format
314
+ passes it. It is the backstop for credentials nobody can name in advance; the controls above are
315
+ the actual protection.
316
+
317
+ ## Reading payment, refund and invoice history
318
+
319
+ Three deterministic registries carry this knowledge. Use them; do not re-derive which source keys
320
+ mean what, and do not hand-author the reads.
321
+
322
+ | What you need | Use |
323
+ | --- | --- |
324
+ | Which source keys the payments import may read at all | `paymentReadKeys()` in `lib/gateway-source-keys.js` |
325
+ | Which value may become a payment's provider transaction reference | `resolveTransactionReference()` in the same module |
326
+ | Whether a method is online or offline | `resolveOfflinePayment()` — omits the flag when unknown |
327
+ | Whether a refund actually happened | `qualifyRefund()` in `lib/refund-corroboration.js` |
328
+ | Whether the store has invoices, and where their bytes live | `lib/invoice-discovery.js` |
329
+
330
+ **The read manifest is closed.** `paymentReadKeys()` returns the complete set of keys this import
331
+ touches — the amount, both paid-date fields, the method, the currency, the canonical reference,
332
+ and each registry family's candidate fields. Nothing else is read. In particular a raw provider
333
+ response blob is declared **unreadable**, not "to be pattern-matched later": on a live store its
334
+ sibling fields carried customer name, phone and email, and a contract that requires parsing
335
+ unstructured personal data is not one that can be honoured safely.
336
+ `assertNoNeverReadKeys()` refuses a read plan naming it *before* the plan executes — filtering
337
+ after the response arrives is too late, the blob is already in the run's working data.
338
+
339
+ **Two surfaces, and neither alone covers payments.** Row-shaped tables come through the plugin
340
+ `/query` surface; meta-backed fields come through WooCommerce REST. Run the generated
341
+ `…--<table>.eav-read-plan.json` verbatim and reconcile it afterwards — a hand-authored scoped read
342
+ cannot be reconciled against anything, and a plan edited after discovery wrote it is caught by its
343
+ own hash.
344
+
345
+ **A gateway-shaped key is not attribution.** The payment-method slug names the checkout plugin,
346
+ not necessarily the processor: orders whose method is one gateway can carry
347
+ another gateway's reference key, while every order whose method *was* that second gateway carried
348
+ none. So a candidate is written only when its semantics are verified AND the registry recognizes
349
+ the order's method — directly, or through a separately verified layered integration. Everything
350
+ else is reported with a reason and never guessed at by precedence. Report candidate coverage and
351
+ defensible coverage as separate numbers; collapsing them overstates fidelity.
352
+
353
+ **A refund needs a refund record.** A canonical WooCommerce refund entity with a stable id and a
354
+ positive amount is authoritative on its own. An order-level refunded status corroborates but
355
+ cannot supply a missing amount. Gateway flags, statuses and refundable-balance fields never create
356
+ a refund, however many agree — fields written by one callback are one signal. When only such flags and
357
+ statuses exist, this yields zero importable refunds, which is the correct output.
358
+
359
+ **Invoices are resolved per run.** Discovery matches key *families*, not authored names, and
360
+ patterns found in a sample are then applied across the full order population. A discovered
361
+ document URL is untrusted input that something is about to fetch: classify it before touching it,
362
+ and never log or store it — the query string can be a bearer token and the filename can carry the
363
+ customer's name. Nothing found means nothing is created, and the report says "no invoices found",
364
+ never "not supported".
365
+
366
+ Redaction counts are findings, not plumbing: a non-zero `redactedValueCount` means a credential
367
+ was sitting somewhere unexpected in the source, and it belongs in the run report. Telemetry
368
+ carries counts only — never a discovered key name and never a value.
369
+
370
+ ## Plugin coverage
371
+
372
+ A real WooCommerce site is mostly plugins. Handling them is deterministic and has its own
373
+ knowledge base: `plugins/` (profiles + `no-migration-needed.json` + `README.md`), read through
374
+ `scripts/plugin-knowledge.js`, detected by `scripts/wp-plugin-inventory.js`.
375
+
376
+ **Every installed plugin lands in exactly one of the four statuses, and every status
377
+ produces a row the user sees. Nothing is silently ignored.**
378
+
379
+ | Population | Evidence | Result |
380
+ | --- | --- | --- |
381
+ | recognized | a profile matched | named entities, declared read channels, Wix target refs; mapping `confirmed` |
382
+ | derived | no profile, data is REST-visible | derived from `wp/v2/types` + `wp/v2/taxonomies`, mapping `proposed` (CMS, or native on a domain-KB match) |
383
+ | named only | detected, no read channel | *Pending* with its reason, resolved by the no-migration-needed list or at the review gate |
384
+
385
+ A plugin therefore does not need a profile to migrate — it needs one to migrate *natively*.
386
+
387
+ ### Detection
388
+
389
+ Runs before route classification so profile-declared routes are already in scope. Signals,
390
+ strongest first:
391
+
392
+ 1. `GET /wp/v2/plugins` — the authoritative installed list (`plugin`, `status`, `name`,
393
+ `version`). **Administrator credential required.** On 401/403 the run continues and
394
+ records `pluginListAvailable: false`; say so plainly rather than implying full coverage.
395
+ Without it, installed-but-unprofiled plugins cannot be enumerated at all — this is the
396
+ concrete argument for the authenticated acquisition mode.
397
+ 2. Declared routes present in the REST index.
398
+ 3. `GET /wp/v2/types` / `GET /wp/v2/taxonomies` entries matching a profile's `restBases`.
399
+ 4. **Record payload keys from the samples** — a second pass after sampling. This is not
400
+ optional polish: several major WooCommerce extensions (Product Bundles, Composite
401
+ Products, Product Add-Ons, ACF, Yoast) add **no REST route at all** and only widen
402
+ `wc/v3/products` / `wc/v3/orders` payloads. Route-based discovery cannot see them.
403
+ 5. REST namespaces and public `/wp-content/plugins/<slug>/` asset paths (low confidence,
404
+ but they work with no credentials).
405
+
406
+ A plugin version below a profile's `detect.minVersion` is reported as
407
+ `api-below-min-version`, not read as if the documented API were present.
408
+
409
+ ### Project-local plugin entity plans
410
+
411
+ For an unfamiliar plugin, prefer a complete supported REST reader. If it is unavailable,
412
+ investigate verified storage and use [PLUGIN-READ-PLANS.md](PLUGIN-READ-PLANS.md)'s shared
413
+ validator/writer, including stable entity references, scoped related records and a bounded
414
+ sample. A permanent profile or new transport handler is not required. Preserve the plan
415
+ binding in canonical source metadata; only reconciled full extraction certifies readiness.
416
+ Never return an ineffective “rerun discovery” task when a producer cannot emit its input.
417
+
418
+ ## Read channels
419
+
420
+ `plugin-rest`, `core-cpt`, `core-embedded`, `core-meta`, `plugin-rest-child`, `export-file`,
421
+ `db-only` — see `plugins/README.md`. Two consequences worth stating to the user:
422
+
423
+ - **Unregistered post meta is invisible to the REST API.** WordPress exposes meta only when
424
+ the plugin registered it with `show_in_rest` (and, for a CPT, declared `custom-fields`
425
+ support). A plugin can keep its entire per-record state in `postmeta` and expose none of
426
+ it. Three ways to reach it, in preference order: the **structure bridge** (see "Reading
427
+ key/value tables through the bridge" below — this is the one that needs no profile and no
428
+ user action), a WXR export the user produces, or a database dump.
429
+ - **WXR cannot be fetched.** Application Passwords authenticate the REST API and XML-RPC
430
+ only, never `wp-admin`, so an export file is something the **user produces and supplies**.
431
+ Treat `export-file` as a request to the user, never as a download.
432
+ ### Source redirect maps
433
+
434
+ Old addresses kept alive only by redirect rules must be inventoried; an empty REST answer or
435
+ the route classifier's `/redirection/*` exclusion is never evidence that no rules exist.
436
+ `lib/wp-redirect-sources.js` normalizes them from the plugins' own storage:
437
+
438
+ - **Redirection** — `redirection_items` + `redirection_groups` tables; **Rank Math** —
439
+ `rank_math_redirections` (PHP-serialized `sources`, one rule per pattern). Read them through
440
+ the structure bridge by **bare** table name; `resolveTable` matches the site's real prefix
441
+ from the bridge's table listing and reports several matches (multisite) as ambiguous.
442
+ - **Yoast Premium** keeps redirects in `options`, which stays denied: coverage is
443
+ `unsupported` until the operator supplies the plugin's CSV export.
444
+ - Server/CDN rules (`.htaccess`, nginx, edge) live outside WordPress: record them as
445
+ `unsupported` and ask the host for an export.
446
+ - Logs, 404 records and hit counts are not rules. Disabled rules and rules of an inactive
447
+ plugin are kept as `enabled: false` exclusions, never reactivated.
448
+ - A failed, partial or channel-less read is `unreadable`, never `confirmed_empty`.
449
+
450
+ Feed the reads to `node skills/wix-replatform/scripts/url-redirects.js sources --project
451
+ <dir> --input <file.json>`; it appends `state/url-preservation/source-redirects.ndjson` and
452
+ writes `source-redirect-coverage.json`. When declared source domains are present, it also
453
+ plans the captured destinations into the redirect audit and URL rewrite report. If domains
454
+ are declared later, run `url-redirects.js import-targets --project <dir>` after that
455
+ declaration. Capability evidence for a specific rule can be supplied in the source input's
456
+ `targetCapabilities[ruleKey]` with `productSupported`, optional `automationSupported`, and
457
+ an evidence reference; absent evidence remains unverified where product support is not
458
+ established for that rule shape. A complete capture records a disabled tombstone for a
459
+ previously captured rule that disappeared, so the cutover report keeps its old address
460
+ open for explicit redirect reconciliation. Partial or unreadable captures never retire
461
+ missing rules.
462
+
463
+ ### Reading key/value tables through the bridge
464
+
465
+ When `wix-wp-plugin-v2` is installed on the source site, `postmeta` and every other
466
+ key/value-shaped table is readable — key-scoped. This is the channel to reach for when a
467
+ capability would otherwise be a `user-file` blocker, and it is the ONLY one that works for a
468
+ plugin nobody has profiled.
469
+
470
+ **Always run key discovery, even for a profiled plugin.** It is tempting to treat a profile's
471
+ `detect.recordProperties` as the key list and skip the round-trip — `subscriptions-for-woocommerce.json`
472
+ names `_wps_sfw_product` there — but those are *detection markers*, one or two keys that prove
473
+ the plugin wrote the record. They are not the record's field list. Reading only them produces a
474
+ short extraction that looks complete, the same failure as a truncated key list.
475
+
476
+ Profiles earn their place elsewhere: they say which table to read, which entity it maps to, and
477
+ what the pitfalls are. The key list comes from the site. A single order on a gateway-backed store
478
+ can carry dozens of gateway-specific meta keys under a prefix no profile anywhere has seen, which
479
+ is why discovery is the mechanism and not the fallback.
480
+
481
+ **The rule: if you have not named the keys you want, the value column does not exist.** Not
482
+ in `select`, not in `where`, not in `orderBy`, not in `groupBy`. Violating it returns
483
+ `wmh2_invalid_structure_request` (400) — the same error as naming a nonexistent column, so
484
+ the refusal will not tell you which column is the value column. Two steps:
485
+
486
+ 1. **`GET /structure?table=postmeta`** → the column list plus `eavPair: {keyColumn,
487
+ valueColumn}`. This tells you the table is key-scoped *before* you write a query. No rows,
488
+ no key names.
489
+ 2. **Discovery** — `select` the key column plus a count, `groupBy` the key column. Returns the
490
+ real key list with row counts, `eavAccess.tier: "key-discovery"`.
491
+ 3. **Scoped read** — `where` the key column `in` the keys you want (**max 50 per request**),
492
+ then `select` the value column freely. `eavAccess.tier: "key-scoped"`.
493
+
494
+ ```jsonc
495
+ // 2. discovery // 3. scoped read
496
+ {"structureRequest": { {"structureRequest": {
497
+ "table": "postmeta", "table": "postmeta",
498
+ "select": [{"column": "meta_key"}, "select": [{"column": "post_id"},
499
+ {"column": "meta_key", {"column": "meta_key"},
500
+ "aggregate": "count", "as": "n"}], {"column": "meta_value"}],
501
+ "groupBy": ["meta_key"], "where": [{"column": "meta_key", "op": "in",
502
+ "orderBy": [{"column": "meta_key", "value": ["<key from step 2>",
503
+ "direction": "asc"}] "<key from step 2>"]}],
504
+ }} "orderBy": [{"column": "post_id", "direction": "asc"}]
505
+ }}
506
+ ```
507
+
508
+ ⚠️ **Count the KEY column, not some other column.** `{"column": "<keyColumn>", "aggregate":
509
+ "count"}` works on any key/value table; counting `meta_id` only works where that column happens
510
+ to exist, and a `name`/`value` table has no `meta_id` at all. The generic client counts the
511
+ discovered key column for exactly this reason.
512
+
513
+ Things that will bite:
514
+
515
+ - **Page discovery to exhaustion.** `limit` maxes at 200 and a large `postmeta` has more
516
+ distinct keys than one page. Follow `pagingMetadata.cursors.next` until it is null before
517
+ concluding a key is absent — a truncated key list reads as "these are all the keys" and
518
+ will send the mapping down a wrong path. Budget `ceil(N/200)` discovery pages plus at least
519
+ `ceil(N/50)` scoped **batches** — and a batch is not a request: each pages at 200 rows, so a
520
+ batch whose 50 keys hold 4,000 rows costs 20 requests. Because discovery returns each key's
521
+ row count, the read plan states the real figure (`expectedPages` per batch,
522
+ `expectedScopedRequests` overall) — read it there rather than estimating.
523
+ - **`options` and `usermeta` are still refused outright**, and pinning `option_name` does not
524
+ unlock `options`. Gateway configuration held in `options` is a different mechanism
525
+ entirely — do not try to route around this.
526
+ - **Check `redactionMetadata.redactedValueCount` on every response.** Non-zero means a value
527
+ matched a credential shape and came back as `[REDACTED:secret-shaped]`. That is not a bug
528
+ to work around: it means the source site keeps a credential somewhere unexpected, and it
529
+ belongs in the run's notes.
530
+ - **A table named `*meta` whose pair is not one of the six recognized ones is refused (403),
531
+ not readable.** There is no key column to pin, so there is nothing to scope. Reach for a WXR
532
+ export or a database dump for that table, and say so rather than reporting it as empty.
533
+ - **A table can carry MORE than one pair**, and each is guarded separately: pinning `meta_key`
534
+ does not make a second pair's `value` column readable. `eavPair.additionalPairs` and
535
+ `eavAccess.additionalPairs` list them; `guardedValueColumns()` in the client returns all of
536
+ them at once, which is what you want when building a select list.
537
+ - **Every value comes back as a STRING — including flags, ids and counts.** A column the
538
+ plugin stores as an integer flag arrives as `"0"` or `"1"`, and `Boolean("0")` is **true**
539
+ in JavaScript, so the natural-looking `if (row.is_spam)` classifies *every* row as spam.
540
+ A form-submission import that reads a plugin's `is_spam` column straight into a truthiness
541
+ test mismarks the entire set, and nothing errors: the run simply reports every row as spam. Compare
542
+ explicitly (`row.is_spam === '1'`, or `Number(row.is_spam) === 1`) on every flag, id and
543
+ numeric column you branch on, and never feed a bridge value to a bare truthiness test. The
544
+ same holds for text-valued status columns: `"active"` is a string the plugin chose, not a
545
+ normalized enum — and note that a source's own spam/status flags being uniformly clean is
546
+ not evidence the data is clean, only that the source never flagged it.
547
+ - **A submissions table shared by every form needs the source form id on BOTH sides.** Form
548
+ plugins that keep one entries table (Forminator's is the shape seen live; it has no profile
549
+ in `plugins/` yet) key each row by the source form id, while the target form ids are minted
550
+ per form by the forms import. That is two lookups, not one: filter the source rows to the
551
+ form being imported, **and** resolve that same form's crosswalked target id. Getting either
552
+ side wrong writes real submissions onto the wrong target form and the create still succeeds
553
+ — there is no error to notice. Assert the source-form-id →
554
+ target-form-id pairing from the crosswalk before the first write, and read one created
555
+ submission back on the form you expected it to land on.
556
+
557
+ #### Yoast product SEO evidence
558
+
559
+ WooCommerce product `yoast_head_json` is **rendered** output, never proof of an override (a store
560
+ can render a title on every product while holding no raw title override). Decide each product's SEO title,
561
+ description and focus keyphrase per field with `lib/yoast-product-seo-evidence.js`:
562
+
563
+ 1. **REST `meta`** on the product post (`/wp/v2/product/{id}?context=edit`): a key counts only
564
+ when `meta` contains it (`restEvidence`). Missing key means unresolved, not "no override".
565
+ 2. **Bridge key-scoped `postmeta` read** for keys still unresolved (`readBridgeEvidence`, with
566
+ `queryPage` bound to `queryStructure`). It pins exactly `_yoast_wpseo_title`,
567
+ `_yoast_wpseo_metadesc`, `_yoast_wpseo_focuskw` plus a batch of product ids, and pages to
568
+ `hasNext: false`. This is the one deliberate exception to "always run key discovery": the key
569
+ set is a fixed allow-list, never widened, and a complete scoped read with **no row** is itself
570
+ the evidence (inherit). A failed, truncated or non-key-scoped batch makes only that batch's
571
+ unresolved fields `unknown`.
572
+ 3. Otherwise `unknown`. A user-supplied export can later provide evidence through the
573
+ blocked-data request flow.
574
+
575
+ `buildProductSeoReceipts` returns the audit receipt per product (stored with the extraction,
576
+ never hashed whole); `seoEvidenceFromReceipt` gives the canonical `product.seoEvidence`. Never map
577
+ `yoast_head_json.title/description`, `og_*` or the excerpt into `seoTitle`/`seoDescription`.
578
+ Non-Yoast SEO plugins have no evidence contract: their product SEO is `unknown`.
579
+
580
+ - **Parent-scoped sub-resources (`plugin-rest-child`, fixed 2026-08-11).** WooCommerce order
581
+ *notes* (`/wc/v3/orders/{id}/notes`) and similar per-parent sub-collections cannot be listed
582
+ on their own — the base classifier only samples flat collection routes (`page`/`per_page`
583
+ shape). This is why a plugin that writes real data via `$order->add_order_note(...)` (e.g.
584
+ Custom Payment Gateway for WooCommerce's payment-note field) used to report as
585
+ `pending · cannot-tell` even though the data is real and REST-reachable in principle.
586
+ `plugin-rest-child` fixes this: an entity declares `route` as a `{parentId}`-templated path
587
+ (`/wc/v3/orders/{parentId}/notes`) plus `parentRoute` (the already-sampled collection that
588
+ supplies real parent ids, e.g. `/wc/v3/orders`). Availability is a two-part **presence**
589
+ check against the site's REST index — `parentRoute` is in scope, and the index advertises a
590
+ route shaped like the template (WordPress's `(?P<id>[\d]+)`-style regex segments are
591
+ normalized to `{parentId}` for the comparison, so the parameter's name never has to match).
592
+ Presence alone does not confirm real data exists there: `wp-discovery.js`'s
593
+ `sampleChildEntities` runs a **live, representative check** afterward — it substitutes up to
594
+ 3 already-sampled parent ids into the template and fetches each, purely to say "N of 3
595
+ sampled parents actually returned something," not to claim a full count. That result surfaces
596
+ as a plain-language run note, never as a channel-status change: a small sample coming back
597
+ empty does not mean the capability is absent, only that this sample did not confirm it.
598
+ The infrastructure is
599
+ built and tested (`plugin-knowledge.js`, `wp-route-classifier.js`'s `childRouteAdvertised`,
600
+ `wp-plugin-detect.js`'s `describeProfiledEntity`, `wp-discovery.js`'s `sampleChildEntities`),
601
+ and `plugins/woocommerce-other-payment-gateway.json` is authored and live-verified
602
+ — do not guess a plugin's installed-directory id when authoring a similar
603
+ profile; confirm `detect.pluginFileIds` against a live `GET /wp/v2/plugins` response first, it
604
+ is exactly the kind of fact a stale or invented guess silently gets wrong.
605
+ - **Non-standard response envelope (`responseEnvelope`, fixed 2026-08-11).** Sampling and
606
+ counting assume a flat array (`X-WP-Total`/`X-WP-TotalPages` headers, or the array's own
607
+ length) or a single object. MailPoet's REST API (`/mailpoet/v1/subscribers`,
608
+ `/segments`, `/newsletters`, ...) broke that assumption: it is a real, paginated, GET-only
609
+ REST route (VERIFIED LIVE 2026-08-11: readable with the
610
+ standard Application Password), but the payload is wrapped as
611
+ `{ data: { items: [...], meta: { count, pages } } }` with no `X-WP-Total` header — the count
612
+ lives inside the body instead. Before this fix the whole envelope would have sampled as one
613
+ opaque record. A profile entity (route-bearing channels only) now declares
614
+ `responseEnvelope: { itemsPath, countPath }` — a dot-path to the array of records, and an
615
+ optional dot-path to the true total when there's no header. `wp-discovery.js`'s
616
+ `inspectEntity` unwraps to that path and otherwise treats it exactly like a flat array; a
617
+ path that stops resolving (stale profile, plugin version drift) falls back to the raw payload
618
+ shape and records a discovery note rather than failing the run.
619
+ `plugins/mailpoet.json` is authored and live-verified: `crm.email-subscription`
620
+ now resolves to migration-planned via the existing `crm/email-subscription` target, with
621
+ `recordCount` read via the declared `countPath`. A namespace-wide `unprofiledRoutes`
622
+ entry is still a sign a route this shape (or any other) has never been profiled — check there
623
+ before concluding a plugin has "no REST surface."
624
+ - **Route default query (`ROUTE_DEFAULT_QUERY_RULES`, fixed 2026-08-16).** Sampling sends
625
+ `per_page` and nothing else, so any collection route that applies a **default filter to an
626
+ unparameterized request** is read as its filtered subset and the plan under-counts — the
627
+ hidden records are invisible to the mapper, not merely misreported. Observed on
628
+ `/wc/v3/products/reviews`, which defaults to `status=approved`: a store holding both approved and on-hold
629
+ reviews had only the approved ones counted. This is a class of bug, so the fix is a
630
+ table rather than a special case: `wp-route-classifier.js` carries
631
+ `ROUTE_DEFAULT_QUERY_RULES` — `[pattern, params, reason]`, `*`-suffix matching, longest
632
+ pattern wins — and `defaultQueryFor(routePath)` / `defaultQueryReasonFor(routePath)` resolve
633
+ it. `wp-discovery.js` merges the result under its paging parameters and records a discovery
634
+ note naming the parameters and the reason, so generated readers know to send them too.
635
+ Adding a route is one row in the table plus a fixture; a route with no row is unchanged.
636
+
637
+ ### Coverage statuses
638
+
639
+ The four statuses, and only the four: `migration-planned` (via `api` or `cms`,
640
+ confidence `confirmed` or `proposed`), `no-need-to-migrate`, `pending`,
641
+ `requires-development`. A failed or unavailable read is never a status — it attaches to the
642
+ row as `blocked[]` (`user-file` / `surface-changed`, with a `declined` flag).
643
+
644
+ The split is deliberate: `pending` (we do not know how to migrate this yet — our open item)
645
+ and `requires-development` (a human established Wix has no surface) call for different
646
+ conversations. Only the second is a closed door, and only a human may open it — the
647
+ human-signed register `plugins/requires-development.json` is its single source.
648
+
649
+ ### Interaction rules
650
+
651
+ - **The admin Application Password is the source REST credential; `wix-wp-plugin-v2`'s
652
+ migration key is scoped to that bridge.** A plugin REST namespace that honours
653
+ WordPress authentication works with the Application Password, and that is the normal
654
+ case — plugins with their own key systems offer them as an *alternative* for external
655
+ callers, not a requirement (verified for Gravity Forms, which accepts Application
656
+ Passwords and runs requests under the caller's plugin capabilities). Do **not** treat
657
+ "this plugin has API keys" as "we need another credential"; that reasoning has been
658
+ wrong every time it was applied. If a plugin genuinely rejects the platform credential,
659
+ that is a config-gate requirement like the WooCommerce consumer keys — not a
660
+ per-capability runtime state. For the structure bridge specifically, select the
661
+ Application Password path only when WooCommerce is present; otherwise use
662
+ `WMH2_MIGRATION_KEY`. Never describe that bridge-only key as private REST authentication.
663
+ - `blocked[]` entries are collected and asked **once**, batched, each individually
664
+ skippable. A `user-file` blocker means no *WordPress API* can reach it: unregistered post
665
+ meta and plugin tables are invisible to REST, and Application Passwords cannot reach
666
+ `wp-admin`, so absent anything else a WXR or CSV export must be produced by the user.
667
+ **Check the structure bridge first** — if `wix-wp-plugin-v2` is installed on the source
668
+ site, both of those are readable through it (see "Reading key/value tables through the
669
+ bridge" below), and asking the user for an export they did not need to produce is a real
670
+ cost to avoid. Record each
671
+ answer in `orchestration/decisions.json` under `pluginBlocker:<capability>:<kind>`
672
+ (`provided` / `declined`); `wp-discovery.js` reads it back (`--decisions`, defaulting to
673
+ `<out-dir>/../../orchestration/decisions.json`) so a declined ask renders as declined.
674
+ - `pluginCmsFallback:<capability>` sits beside it in the same file: the merchant's
675
+ approve/decline answer to archiving a recognized capability's records in CMS, bound to the
676
+ exact proposal shown. `wp-discovery.js` reads it with the eligibility register
677
+ (`plugins/cms-fallback-eligible.json`) and classifies the row (`pending` with a
678
+ `cms-fallback-*` reason, or `migration-planned · via cms · basis decision` with
679
+ `cmsFallback.functionalityRestored: false`). It is written only at the mapping review by
680
+ `orchestration-state.js decide-cms-fallback`. A malformed entry is never honored and never
681
+ fails the run; with no row to carry it, it appears in `plugin-coverage.json`
682
+ `cmsFallbackDecisionIssues[]`.
683
+ - `plugin-coverage.json` also carries `entityStats` (per-route record counts and sample
684
+ evidence) and `inputs.cmsFallbackDigest`; `--reclassify-coverage` rebuilds coverage from
685
+ them without any source request (see `rp-discovery`).
686
+ - `migration-planned · proposed` rows never block; the proposed capability and target are
687
+ reviewed at the normal mapping-review checkpoint.
688
+ - `requires-development` and `pending` rows **must** reach the user. A detected but
689
+ undeliverable capability the user was never told about is the failure this machinery
690
+ exists to prevent. Every `pending` row is decided at the mapping review — the only exit.
691
+
692
+ **A recognized plugin always ends in Migration planned** (J2 property 1). If a profile
693
+ matched we know what the plugin holds, so:
694
+ - no readable channel (plugin tables only) → still classified by its target, with a
695
+ `user-file` blocker on the row — **not** pending, and never a "cannot" verdict. A database
696
+ export or a source-side bridge would reach it, and automation may not foreclose that any
697
+ more than it may declare Wix incapable.
698
+ - `pending · cannot-tell` is reserved for an **unrecognized** plugin where we genuinely
699
+ found nothing; the no-migration-needed list or the agent's own reading can
700
+ still resolve it to *No need to migrate* with a recorded rationale.
701
+ - The one deliberate exception is a **CMS fallback**: when nothing native fits, a
702
+ recognized capability with a signed `cms-fallback-eligible.json` entry stays *Pending*
703
+ (`cms-fallback-*` reason) until this migration's merchant approves archiving its records in
704
+ CMS. A profile never names a `cms/*` target itself.
705
+
706
+ **A CMS destination is not turnkey.** "Kept as data" means the records land in a Wix CMS
707
+ collection with their original IDs — genuinely migrated, not documented. But Wix Data must be
708
+ installed first (item writes fail with `WDE0110` otherwise) and collection creation has no
709
+ verified writer yet, so it is a setup step. Say both on the row; a customer reading "Kept as
710
+ data" should not discover the setup work later.
711
+
712
+ ### Guardrails
713
+
714
+ - **Never a silent native mapping.** A derived entity may target a native Wix entity
715
+ only when the Wix domain knowledge confirms its proposed capability; otherwise it goes to
716
+ CMS. Either way the row is `confidence: proposed` and is confirmed at the mapping review.
717
+ - **Never a scope reopening.** The generic path operates only on routes the route classifier
718
+ already accepted. It does not resurrect frontend, editor, admin, runtime, or integration
719
+ families because a plugin registered them.
720
+ - Profile data routes outrank exclusion families (that is the per-capability opt-in), but a
721
+ wildcard pattern must keep two concrete leading segments, so `/yoast/*` can never be
722
+ opted in wholesale.
723
+
724
+ ### Commands
725
+
726
+ ```bash
727
+ node scripts/plugin-knowledge.js list-plugins
728
+ node scripts/plugin-knowledge.js read-plugin --slug the-events-calendar
729
+ node scripts/plugin-knowledge.js resolve-route --route /tribe/events/v1/events
730
+ node scripts/plugin-knowledge.js resolve-property --property bundled_items
731
+ node scripts/plugin-knowledge.js list-capabilities
732
+ node scripts/wp-plugin-inventory.js --base-url <url> --out-dir <dir> [auth options]
733
+ node scripts/plugin-knowledge-validate.js --write-index
734
+ ```
735
+
736
+ To add or correct a plugin, edit `plugins/<slug>.json` and add a fixture — no skill logic
737
+ changes. See `plugins/README.md`.
738
+
739
+ ## Product and variation stock
740
+
741
+ Extract `stock_status`, `manage_stock`, `stock_quantity`, `backorders`,
742
+ `backorders_allowed`, and `backordered` from products and every variation. Use
743
+ `lib/woo-inventory.js` → `mapWooInventory(record, {parent})` during canonical mapping.
744
+ Persist its `fields` on the canonical variant and its disposition beside the source ID.
745
+ An untracked product can still be explicitly in stock; `manage_stock:false` is not missing
746
+ inventory. Use variation records for variable products; never replicate a parent's shared
747
+ tracked quantity onto each child. Preserve `decision-needed` outcomes for missing signals,
748
+ backorders, invalid quantities, and shared stock; do not turn them into an empty success.
749
+
171
750
  ## Read contract (codegen-time)
172
751
 
173
752
  What a generated WordPress reader must get right. Capture the operational facts below into
@@ -182,7 +761,9 @@ disk later without re-fetching the source.
182
761
  throttling, and `Retry-After`-aware 429/503 backoff a reader needs already exist as a
183
762
  dependency-free module at `lib/wp-http.js` in **this skill directory** (the same module
184
763
  the capture script imports). It
185
- exports `fetchJson`, `buildHeaders`, `configureRateLimit`, and `parseTotalHeader`. Any
764
+ exports `fetchJson`, `buildHeaders`, `configureRateLimit`, and `parseTotalHeader`. It does
765
+ **not** export an undenied transport — `fetchJson` refuses payment-gateway routes, and a
766
+ generated reader must not reintroduce a raw one. Any
186
767
  generated WordPress reader **must reuse this module rather than reimplementing transport**,
187
768
  so the reader contains only per-project orchestration: which entities to pull, the
188
769
  pagination loop, `_embed`/`_links` resolution, and transform glue. One tested transport
@@ -191,6 +772,11 @@ carried into a runnable migration project is `rp-import-codegen`'s concern (its
191
772
  targets), not this adapter's. The notes below describe what the reader does *on top of*
192
773
  that shared core:
193
774
 
775
+ - **Bounded media reads:** `fetchJson` accepts optional `deadlineMs`. One absolute deadline
776
+ includes the rate-limit wait, every fetch attempt, each attempt timeout, response handling,
777
+ and every retry delay. A wait that would finish after the deadline is not started. Omit
778
+ `deadlineMs` for all existing calls; their behavior stays unchanged.
779
+
194
780
  - **Namespaces & auth differ per namespace:**
195
781
  - `wp/v2` (core): HTTP Basic auth with a WordPress **Application Password**
196
782
  (`--username` + `--application-password`).
@@ -224,29 +810,88 @@ that shared core:
224
810
  name, blog title, tag label, CMS text columns) must be entity-decoded by the generated
225
811
  transforms or the encoded form appears verbatim in the Wix dashboard. HTML passed to
226
812
  rich-content conversion does NOT need pre-decoding — the converter handles entities.
813
+ - **`slug` / `post_name` is percent-encoded for non-Latin values, and REST returns it
814
+ undecoded (trap, confirmed live on a non-Latin source 2026-09-27):** WordPress stores a
815
+ post/product slug whose original text contains non-Latin characters (e.g. Hebrew) as raw
816
+ UTF-8 percent-encoded escapes (`%d7%90%d7%99...`), and both `wp/v2` and `wc/v3` return this
817
+ field exactly as stored — no decoding happens server-side. The target's slug coercion
818
+ (`rp-target-wix`'s `toWixSlug`) turns the literal `%` characters into dashes rather than
819
+ erroring, so passing this value straight through silently produces a garbled
820
+ hex-with-dashes URL instead of the real slug. Generated transforms must decode `slug` /
821
+ `post_name` with `decodeURIComponent` before using it as a Wix slug or any other field —
822
+ guarded in a try/catch, since a slug that was never percent-encoded (or one containing a
823
+ literal `%` that isn't part of a valid escape) must pass through unchanged rather than throw
824
+ or get mangled. The decoded Unicode slug is then kept as-is (the coercion preserves letters
825
+ and digits in any script), so do not transliterate it. See the codegen-side rule in
826
+ `rp-import-codegen/SKILL.md`'s "Never hand-write slug sanitizing" section and the domain-knowledge pitfalls on `rp-target-wix`'s
827
+ `stores/entities/product.json` and `blog/entities/post.json`.
227
828
  - **Custom fields:** ACF / meta often appear in sample records but are absent from the
228
829
  `OPTIONS` schema — surface them as `unknowns` in discovery so the mapper can decide.
229
830
  - **WooCommerce variation attribute values are URL-encoded slugs, not display names (trap, hit live 2026-07-21):**
230
831
  `variations[].attributes[].value` from `wc/v3/products/{id}/variations` is a URL-encoded
231
- taxonomy-term slug (e.g. `1-%d7%a7%d7%92` for `1 ק"ג`), not the human-readable display
832
+ taxonomy-term slug (e.g. `caf%c3%a9` for `café`), not the human-readable display
232
833
  name that the product's `attributes[].options[]` array contains. Generated readers must
233
834
  build a per-option slug→displayName map from the parent product's `attributes[].options[]`
234
835
  (which does carry display names) and resolve slug values to display names before writing to
235
836
  Wix. Passing slugs directly causes `MISSING_VARIANT_OPTION_CHOICE` because Wix compares
236
837
  variant choice names against option choice names and finds no match.
237
- - **WooCommerce "Any"-style variations have null attribute values (trap, hit live 2026-07-21):**
238
- When a WooCommerce variation is set to "Any" for an option (the variation applies to any
239
- value of that option), `variations[].attributes[].value` is an empty string or null, not a
240
- specific term. Generated transforms that filter variants by attribute value will silently
241
- drop "Any" variants, leaving options with no variants — Wix then auto-generates priceless
242
- placeholder variants, causing price-empty errors. Fix: when ALL attributes for a variation
243
- are null/"Any", treat the variation as a single cartesian entry covering all option choices
244
- at the variation's price, rather than filtering it out.
838
+ - **WooCommerce "Any"-style variations omit the attribute entirely, and fail silently (trap,
839
+ hit live 2026-07-21):**
840
+ `wc/v3` returns the per-variation field as `attributes[].option`, not `.value`. When a
841
+ WooCommerce variation is set to "Any" for an option (the variation applies to any value of
842
+ that option), that attribute is typically omitted from the variation's `attributes` array
843
+ entirely rather than present with a null/empty `option` — but treat a present-and-empty
844
+ `option` the same way if seen. Generated transforms that require every declared parent
845
+ attribute to appear on the variation will resolve an empty option set for it. **This does
846
+ not fail loudly** — Wix accepts the write — it surfaces only as a product that goes live
847
+ with zero purchasable options, found by a human browsing the storefront, not by any
848
+ automated check. Do not "fix" this by auto-expanding into a cartesian combination of all
849
+ option choices: live-refuted (the combination count explodes for some products,
850
+ because the attribute slots are ordinal pick-N-from-a-shared-list pickers,
851
+ not independent size/color axes). The correct handling is to record the loss for human
852
+ review rather than emit a default variant silently.
853
+ - **The store's own configuration must be read DIRECTLY, and with the WordPress credential
854
+ (trap, hit live 2026-09-01):** `/wc/v3/settings/general` holds the business address
855
+ (`woocommerce_store_address`, `_address_2`, `_city`, `_postcode`, `woocommerce_default_country`),
856
+ the currency, which countries the shop sells to, and whether tax is calculated. Two reasons it
857
+ was never captured on any run:
858
+ 1. **The REST index advertises only the parameterized form** `/wc/v3/settings/(?P<group>...)`,
859
+ never the literal group route, so nothing in the generic sweep ever fetches it. Read
860
+ `/wc/v3/settings/general` directly, the same way a shipping zone's `/locations` and
861
+ `/methods` sub-resources are — a parent-scoped sub-resource the caller resolves itself.
862
+ 2. **The WooCommerce consumer key gets `401 woocommerce_rest_cannot_view` here; the WordPress
863
+ application password gets `200`.** Both are already in every run's source config. A reader
864
+ that only tries the store key gets a plausible "no permission" and moves on — which is
865
+ exactly how this stayed invisible while migrated sites published with an empty business
866
+ address.
867
+
868
+ `woocommerce_default_country` is `COUNTRY` **or** `COUNTRY:STATE` (e.g. `US:CA`) — the same
869
+ packing a zone's `state`-type location uses. `rp-target-wix/lib/store-config-build.js` maps the
870
+ whole group; do not hand-roll it.
245
871
  - **Route paths passed to `fetchJson` / `buildApiUrl` must NOT include `/wp-json` (trap, hit live 2026-07-21):**
246
872
  `buildApiUrl` in `lib/wp-http.js` already prepends `/wp-json` to the `routePath` argument.
247
873
  Generated route paths must start with the namespace directly (e.g. `/wc/store/v1/products`,
248
874
  `/wp/v2/posts`) — never with `/wp-json/...`. Prefixing with `/wp-json` doubles the prefix
249
875
  and produces 404s.
876
+ - **WooCommerce consumer key/secret can 401 on some `wc/v3` routes even when it reads others
877
+ fine (trap, hit live 2026-08-12):** `WC_CONSUMER_KEY`/`WC_CONSUMER_SECRET` are scoped at
878
+ key-creation time (Read / Write / Read-Write) and can be narrower than the WordPress
879
+ Application Password's access. The consumer key/secret can 401
880
+ `woocommerce_rest_cannot_view` on `wc/v3/taxes`, `wc/v3/taxes/classes`, and
881
+ `wc/v3/settings/tax` while the very same reads succeeded immediately with the
882
+ `WP_USERNAME`/`WP_APPLICATION_PASSWORD` Basic auth pair. Generated readers must not treat a
883
+ 401/403 on one `wc/v3` route as proof the whole consumer key is broken (which would wrongly
884
+ block products/orders/coupons too) — retry that specific route with the Application Password
885
+ credential before failing, and prefer the Application Password outright for admin-ish
886
+ `wc/v3` routes (taxes, settings) that a narrowly-scoped key is more likely to be denied on.
887
+ - **Core WooCommerce settings/tax routes are core, not plugin data:** `wc/v3/taxes`,
888
+ `wc/v3/taxes/classes`, and `wc/v3/settings/tax` are ordinary WooCommerce core collections
889
+ (same auth/pagination rules as `wc/v3/products` etc.) — they are not profiled as a "plugin"
890
+ because tax is core WooCommerce, not an extension. Per-product `tax_status`/`tax_class`
891
+ fields ride along on the ordinary `wc/v3/products` read; no separate per-product route is
892
+ needed to get them. See `rp-target-wix` domains/tax/ for the corresponding Wix target
893
+ entities (Tax Group / Tax Region / Manual Tax Mapping / Tax Settings) and mapping-plan.json's
894
+ `product` and `tax-class` entities.
250
895
 
251
896
  ## Schema shape
252
897
 
@@ -254,3 +899,72 @@ that shared core:
254
899
  when emitting `migrations/<project>/source-schema.json`. It is a shape to follow, not a
255
900
  strict schema to validate against. Keep the platform-agnostic core stable; push WordPress
256
901
  quirks (`restNamespace`, statuses, etc.) into each entity's open `sourceMeta` blob.
902
+
903
+ For a profiled plugin entity, `sourceMeta` MUST also carry `requestMethod`, `requestBody`,
904
+ and `responseFragmentGroupSize` verbatim whenever the plugin profile declares them — copy them from the profile entity (surfaced structurally in plugin detection's
905
+ per-entity output, `wp-plugin-detect.js`'s `describeProfiledEntity`) rather than re-deriving
906
+ them from discovery's prose notes. `rp-import-codegen`'s read contract depends on finding
907
+ these under exactly those `sourceMeta` keys; omitting them silently degrades a declared
908
+ non-GET/fragmented read into an unreadable one downstream.
909
+
910
+ ## Coupons, coupon usage and the currency gate
911
+
912
+ Three source-side decisions, each in a library so a generated importer supplies facts rather than
913
+ rules. Read `lib/coupon-discovery.js` and `lib/currency-gate.js`; both are pure.
914
+
915
+ - **Coupons are counted from one surface and imported from the same one.** `wc/v3/coupons`
916
+ (published) is the discovered set and the denominator; sweep it to exhaustion with
917
+ `sweepCoupons(fetchPage)`, which refuses a total that disagrees with `X-WP-Total`. The `posts`
918
+ count by `post_status` is a reconciliation check only — `summarizeCoupons({ restCoupons,
919
+ postsByStatus })` reports the non-published rows as deliberately excluded, never as a shortfall
920
+ (the difference being drafts and auto-drafts).
921
+ - **Redeemability, not expiry, classifies a coupon.** `classifyCoupon` — redeemable iff not expired
922
+ and not usage-exhausted; **`usage_limit` 0 or empty means UNLIMITED**. A `usage_limit` or
923
+ `usage_count` that is not an integer (`"1.5"`, `"abc"`) is `unresolved`: neither redeemable nor
924
+ exhausted, not written (`coupon-usage-limit-unresolved`), its own partition line. Import all
925
+ published coupons; the report carries the breakdown, never "imported N coupons".
926
+ - **Mapping reports rather than approximates.** `mapCouponToWix(coupon, { resolveProduct,
927
+ resolveCategory })` returns a Wix specification or a named refusal: `fgf_free_gift` and any
928
+ unknown `discount_type` are `coupon-type-unmappable`, and so is an **unrestricted
929
+ `fixed_product`** (a per-unit amount on every product; Wix's money-off takes it off the order
930
+ once — a product-scoped one maps); a scope Wix cannot hold in one group is
931
+ `coupon-scope-unmappable`. An exhausted single-use code is imported `active: false`, because
932
+ Wix's usage count starts at zero. An eligibility constraint Wix cannot hold — excluded products
933
+ or categories, a sale-item exclusion, a maximum discount amount, an email restriction, or a
934
+ minimum spend on a product/collection-scoped coupon — is a **refusal** (`coupon-scope-unmappable`,
935
+ reason `scope: constraints`): dropped, each would make the coupon more generous than the
936
+ merchant published. Only a dropped amount on a free-shipping coupon stays a per-coupon finding.
937
+ - **Coupon usage rides on the order.** On legacy post storage the surface is the order's REST
938
+ `coupon_lines[]` (each carrying
939
+ `discount` per code); HPOS stores have `wc_order_coupon_lookup`. Name whichever was found as
940
+ `coupon-usage-surface`. The target side (`rp-target-wix/lib/order-applied-discount-build.js`)
941
+ turns the lines into `appliedDiscounts` through the coupon crosswalk; an unresolved code writes
942
+ nothing and is reported.
943
+ - **Currency halts before any order write.** `currency-gate.js` `evaluateCurrencyGate({
944
+ settingsCurrency, orderCurrencies, targetSiteCurrency })` — settings from
945
+ `wc/v3/settings/general` through `store-currency.js`, order currencies through
946
+ `resolveOrderCurrency` (which decodes `&#8362;` and refuses a contradicting symbol). One
947
+ currency matching the site proceeds with `currency-validated`; a mismatch or a history spanning
948
+ two currencies is a **halt**, and unresolved orders halt too. Two configured currencies with a
949
+ single-currency history proceeds, with the configuration reported.
950
+ - **Plan-shaped sources are gated by row counts** (`B1` subscription products, `B2` membership
951
+ CPT, `B3` recurring-plan meta), never by plugin detection. Zero rows on all three reports "no pricing plans found". The gate and the plan body live target-side
952
+ in `rp-target-wix/lib/pricing-plan-definition.js`.
953
+
954
+ ## Currency is a store setting, not an order field
955
+
956
+ `order.currency` is documented as an ISO 4217 code. Do not trust it. A store can return
957
+ `currency: "&#8362;"` (an HTML-encoded currency SYMBOL) on every order, because a plugin or
958
+ theme filter has overridden the field. Passed through, the payment
959
+ existence gate refuses every payment as a currency mismatch; passed through without a gate it
960
+ labels amounts with a symbol.
961
+
962
+ `lib/store-currency.js` resolves it: `readStoreCurrency()` reads `woocommerce_currency` from
963
+ `/wc/v3/settings/general` (one request per run, and the store's own authoritative setting), and
964
+ `resolveOrderCurrency(order, storeCurrency)` trusts the order's own value when it is a plausible
965
+ ISO code — a multi-currency store legitimately holds several — and falls back to the store setting
966
+ when it is not. It reports `order-currency-not-iso` when it falls back, because a store that
967
+ mangles this field is a store whose other currency-bearing fields are suspect too.
968
+
969
+ It never infers a code from a symbol. `$` is at least a dozen different currencies, and guessing
970
+ one silently mislabels money.