@salesforce/afv-skills 1.41.0 → 1.43.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 (288) hide show
  1. package/package.json +2 -6
  2. package/skills/automation-sandbox-post-copy-config-generate/SKILL.md +3 -0
  3. package/skills/automation-sandbox-post-copy-configure/SKILL.md +4 -0
  4. package/skills/design-systems-slds-validate/SKILL.md +2 -2
  5. package/skills/dx-app-analytics-query/SKILL.md +1 -3
  6. package/skills/dx-devops-conflict-resolve/SKILL.md +205 -0
  7. package/skills/dx-devops-conflict-resolve/examples/conflict-workflows.md +165 -0
  8. package/skills/dx-devops-conflict-resolve/references/deploy-failure-resolution.md +75 -0
  9. package/skills/dx-devops-conflict-resolve/references/git-conflict-resolution.md +124 -0
  10. package/skills/dx-devops-conflict-resolve/scripts/detect-conflicts.sh +91 -0
  11. package/skills/dx-devops-conflict-resolve/scripts/diagnose-deploy-failure.sh +156 -0
  12. package/skills/dx-devops-request-status/SKILL.md +160 -0
  13. package/skills/dx-devops-request-status/examples/polling-workflows.md +101 -0
  14. package/skills/dx-devops-request-status/references/cli-commands.md +176 -0
  15. package/skills/dx-devops-request-status/scripts/poll-status.sh +134 -0
  16. package/skills/dx-devops-test-failures-analyze/SKILL.md +6 -6
  17. package/skills/dx-devops-test-pipeline-configure/SKILL.md +7 -7
  18. package/skills/dx-devops-test-suite-assignments-configure/SKILL.md +6 -6
  19. package/skills/dx-devops-test-suite-run/SKILL.md +6 -6
  20. package/skills/dx-devops-work-item-manage/SKILL.md +2 -0
  21. package/skills/dx-org-manage/references/creating-scratch-org.md +5 -2
  22. package/skills/dx-org-manage/references/creating-snapshot.md +1 -0
  23. package/skills/dx-org-shape-manage/SKILL.md +152 -0
  24. package/skills/dx-org-shape-manage/examples/create_error_output.json +9 -0
  25. package/skills/dx-org-shape-manage/examples/create_success_output.json +9 -0
  26. package/skills/dx-org-shape-manage/examples/delete_output.json +11 -0
  27. package/skills/dx-org-shape-manage/examples/list_inactive_output.json +32 -0
  28. package/skills/dx-org-shape-manage/examples/list_output.json +24 -0
  29. package/skills/dx-org-shape-manage/references/cli_flags.md +135 -0
  30. package/skills/dx-org-switch/SKILL.md +2 -2
  31. package/skills/dx-org-trial-expiration-check/SKILL.md +2 -0
  32. package/skills/dx-pkg-post-install-configure/SKILL.md +3 -0
  33. package/skills/education-cloud-multi-campus-configure/SKILL.md +211 -0
  34. package/skills/education-cloud-multi-campus-configure/examples/hierarchy_visualization.md +174 -0
  35. package/skills/education-cloud-multi-campus-configure/examples/output_examples.md +54 -0
  36. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input.csv +29 -0
  37. package/skills/education-cloud-multi-campus-configure/examples/sample_hierarchy_input_edgecases.csv +16 -0
  38. package/skills/education-cloud-multi-campus-configure/references/account_recordtype_prerequisite.md +44 -0
  39. package/skills/education-cloud-multi-campus-configure/references/delta_computation.md +33 -0
  40. package/skills/education-cloud-multi-campus-configure/references/error_handling.md +198 -0
  41. package/skills/education-cloud-multi-campus-configure/references/foundation_prerequisites.md +54 -0
  42. package/skills/education-cloud-multi-campus-configure/references/gotchas.md +16 -0
  43. package/skills/education-cloud-multi-campus-configure/references/hierarchy_parsing_rules.md +133 -0
  44. package/skills/education-cloud-multi-campus-configure/references/mcp-invocation.md +75 -0
  45. package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/SKILL.md +1 -1
  46. package/skills/experience-cms-brand-apply/SKILL.md +4 -1
  47. package/skills/experience-cms-brand-create/SKILL.md +197 -0
  48. package/skills/experience-cms-brand-create/assets/brand-template.json +329 -0
  49. package/skills/experience-cms-brand-create/references/brand-anatomy.md +120 -0
  50. package/skills/experience-cms-brand-create/references/disk-contract.md +84 -0
  51. package/skills/experience-cms-content-generate/SKILL.md +190 -0
  52. package/skills/experience-cms-content-generate/assets/display-formats.md +84 -0
  53. package/skills/experience-cms-content-generate/assets/payloads/create-content-bulk.json +11 -0
  54. package/skills/experience-cms-content-generate/assets/payloads/create-content-single.json +11 -0
  55. package/skills/experience-cms-content-generate/assets/payloads/publish-content.json +4 -0
  56. package/skills/experience-cms-content-generate/assets/payloads/update-content.json +9 -0
  57. package/skills/experience-cms-content-generate/assets/questions.md +241 -0
  58. package/skills/experience-cms-content-generate/examples/create-content-call.md +81 -0
  59. package/skills/experience-cms-content-generate/references/bulk-batching.md +56 -0
  60. package/skills/experience-cms-content-generate/references/content-type-classification.md +88 -0
  61. package/skills/experience-cms-content-generate/references/content-write-tool.md +95 -0
  62. package/skills/experience-cms-content-generate/references/delegation-protocol.md +37 -0
  63. package/skills/experience-cms-content-generate/references/edit-publish-workflow.md +63 -0
  64. package/skills/experience-cms-content-generate/references/error-recovery.md +72 -0
  65. package/skills/experience-cms-content-generate/references/identifier-resolution.md +26 -0
  66. package/skills/experience-cms-content-generate/references/intent-routing.md +45 -0
  67. package/skills/experience-cms-content-generate/references/principles.md +19 -0
  68. package/skills/experience-cms-content-generate/references/ux-rules.md +68 -0
  69. package/skills/experience-cms-content-generate/references/workspace-resolution.md +33 -0
  70. package/skills/experience-cms-content-type-generate/SKILL.md +449 -0
  71. package/skills/experience-cms-content-type-generate/assets/discovery-prompts.md +79 -0
  72. package/skills/experience-cms-content-type-generate/assets/schema-example.json +70 -0
  73. package/skills/experience-cms-content-type-generate/references/agent-checklist.md +50 -0
  74. package/skills/experience-cms-content-type-generate/references/anti-patterns.md +39 -0
  75. package/skills/experience-cms-content-type-generate/references/deployment-errors.md +27 -0
  76. package/skills/experience-cms-content-type-generate/references/discovery-details.md +106 -0
  77. package/skills/experience-cms-content-type-generate/references/discovery-query-rules.md +85 -0
  78. package/skills/experience-cms-content-type-generate/references/edit-fields-loop.md +48 -0
  79. package/skills/experience-cms-content-type-generate/references/pre-deploy-checklist.md +21 -0
  80. package/skills/experience-cms-content-type-generate/references/retrieve-and-reconcile.md +105 -0
  81. package/skills/experience-cms-content-type-generate/references/schema-rules.md +51 -0
  82. package/skills/experience-cms-content-type-generate/references/schema-summary-format.md +60 -0
  83. package/skills/experience-lds-graphql-generate/SKILL.md +4 -0
  84. package/skills/experience-lwc-accessibility-jest-run/SKILL.md +79 -0
  85. package/skills/experience-lwc-accessibility-jest-run/references/running-sa11y-jest-tests.md +205 -0
  86. package/skills/experience-lwc-design-generate/SKILL.md +5 -4
  87. package/skills/experience-lwc-generate/SKILL.md +10 -9
  88. package/skills/experience-ui-bundle-deploy/references/dev-preview.md +21 -0
  89. package/skills/experience-ui-bundle-deploy/references/social-login.md +1 -0
  90. package/skills/experience-ui-bundle-file-upload-generate/SKILL.md +6 -6
  91. package/skills/experience-ui-bundle-localize/SKILL.md +30 -37
  92. package/skills/experience-ui-bundle-localize/references/gotchas.md +23 -7
  93. package/skills/experience-ui-bundle-localize/references/i18n-setup.md +48 -4
  94. package/skills/experience-ui-bundle-localize/references/label-xml.md +22 -8
  95. package/skills/experience-ui-bundle-localize/references/verifying.md +39 -11
  96. package/skills/experience-ui-bundle-localize/scripts/detect-bundle-type.sh +118 -15
  97. package/skills/experience-ui-bundle-localize/scripts/tests/test-detect-bundle-type.sh +187 -0
  98. package/skills/experience-ui-bundle-mfa-configure/SKILL.md +13 -2
  99. package/skills/experience-ui-bundle-mfa-configure/references/social-login.md +1 -0
  100. package/skills/experience-ui-bundle-project-generate/SKILL.md +2 -2
  101. package/skills/integration-connectivity-generate/SKILL.md +11 -10
  102. package/skills/integration-eventing-cdc-configure/SKILL.md +6 -6
  103. package/skills/integration-eventing-subscription-configure/SKILL.md +6 -5
  104. package/skills/mobile-platform-native-capabilities-integrate/SKILL.md +2 -2
  105. package/skills/mobile-platform-offline-validate/scripts/package.json +1 -1
  106. package/skills/platform-agentexchange-partner-offers-configure/SKILL.md +8 -8
  107. package/skills/platform-custom-application-generate/SKILL.md +2 -0
  108. package/skills/platform-custom-field-generate/SKILL.md +8 -6
  109. package/skills/platform-custom-metadata-type-generate/SKILL.md +460 -0
  110. package/skills/platform-custom-metadata-type-generate/references/cmdt-records.md +251 -0
  111. package/skills/platform-custom-metadata-type-generate/scripts/sanitize-developer-name.sh +46 -0
  112. package/skills/platform-custom-setting-generate/SKILL.md +419 -0
  113. package/skills/platform-dataspace-access-configure/SKILL.md +3 -3
  114. package/skills/platform-sharing-owd-configure/SKILL.md +3 -0
  115. package/skills/platform-value-set-generate/SKILL.md +2 -0
  116. package/skills/service-agentforce-channel-configure/SKILL.md +29 -20
  117. package/skills/service-agentforce-channel-configure/assets/BotEmailDefinition.botEmailDefinition-meta.xml +36 -0
  118. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email +17 -0
  119. package/skills/service-agentforce-channel-configure/assets/email/unfiled$public/AgentforceForServiceEmailTemplate.email-meta.xml +29 -0
  120. package/skills/service-agentforce-channel-configure/assets/mdapi-package.xml +25 -0
  121. package/skills/service-agentforce-channel-configure/assets/settings-mdapi-package.xml +30 -0
  122. package/skills/service-agentforce-channel-configure/references/agent-wiring.md +20 -0
  123. package/skills/service-agentforce-channel-configure/references/botemaildefinition.md +107 -0
  124. package/skills/service-agentforce-channel-configure/references/channel-branch-email.md +203 -50
  125. package/skills/service-agentforce-channel-configure/references/channel-types.md +30 -3
  126. package/skills/service-agentforce-channel-configure/references/queue-resolution.md +17 -8
  127. package/skills/service-agentforce-channel-configure/references/routing-flow.md +14 -3
  128. package/skills/service-agentforce-channel-configure/scripts/validate-botemaildefinition.py +126 -0
  129. package/skills/service-agentforce-channel-configure/scripts/validate-emailtemplate.py +133 -0
  130. package/skills/service-de-channel-activate/SKILL.md +271 -0
  131. package/skills/service-de-channel-activate/references/gotchas.md +31 -0
  132. package/skills/service-de-channel-activate/references/phone-verification.md +96 -0
  133. package/skills/service-de-channel-activate/references/worked-examples.md +14 -0
  134. package/skills/service-de-channel-consent-configure/SKILL.md +230 -0
  135. package/skills/service-de-channel-consent-configure/references/gotchas.md +41 -0
  136. package/skills/service-de-channel-consent-configure/references/language-keywords.md +84 -0
  137. package/skills/service-de-channel-consent-configure/references/worked-examples.md +134 -0
  138. package/skills/service-de-channel-create/SKILL.md +369 -0
  139. package/skills/service-de-channel-create/references/apple.md +59 -0
  140. package/skills/service-de-channel-create/references/connect-insert.md +84 -0
  141. package/skills/service-de-channel-create/references/facebook.md +154 -0
  142. package/skills/service-de-channel-create/references/line.md +66 -0
  143. package/skills/service-de-channel-create/references/sms.md +98 -0
  144. package/skills/service-de-channel-create/references/whatsapp.md +98 -0
  145. package/skills/service-de-channel-create/references/worked-examples.md +70 -0
  146. package/skills/service-de-channel-routing-configure/SKILL.md +345 -0
  147. package/skills/service-de-channel-routing-configure/references/asa-routing.md +90 -0
  148. package/skills/service-de-channel-routing-configure/references/gotchas.md +27 -0
  149. package/skills/service-de-channel-routing-configure/references/queue-creation.md +123 -0
  150. package/skills/service-de-channel-routing-configure/references/target-locate.md +98 -0
  151. package/skills/service-de-channel-routing-configure/references/worked-examples.md +174 -0
  152. package/skills/service-de-headless-channel-configure/SKILL.md +305 -0
  153. package/skills/service-de-headless-channel-configure/references/gotchas.md +27 -0
  154. package/skills/service-de-headless-channel-configure/references/inputs.md +49 -0
  155. package/skills/service-de-headless-channel-configure/references/output-envelopes.md +53 -0
  156. package/skills/service-de-headless-channel-configure/references/partial-success.md +24 -0
  157. package/skills/service-de-headless-channel-configure/references/terms-and-conditions.md +54 -0
  158. package/skills/service-de-headless-channel-configure/references/worked-examples.md +108 -0
  159. package/skills/service-de-waba-integrate/SKILL.md +227 -0
  160. package/skills/service-digital-engagement-channel-configure/scripts/check-api-version.sh +29 -0
  161. package/skills/service-digital-engagement-messaging-site-integrate/SKILL.md +10 -9
  162. package/skills/service-digital-engagement-messaging-site-integrate/references/lwr_patch.md +39 -21
  163. package/skills/service-digital-engagement-messaging-site-integrate/scripts/patch_lwr_bundle.sh +157 -96
  164. package/skills/service-email-to-case-configure/SKILL.md +190 -0
  165. package/skills/service-email-to-case-configure/assets/CaseSettings.settings-meta.xml +45 -0
  166. package/skills/service-email-to-case-configure/examples/CaseSettings-two-addresses.settings-meta.xml +36 -0
  167. package/skills/service-email-to-case-configure/references/apply-mechanics.md +73 -0
  168. package/skills/service-email-to-case-configure/references/routing_address_reference.md +73 -0
  169. package/skills/service-email-to-case-configure/references/troubleshooting.md +25 -0
  170. package/skills/service-email-to-case-configure/scripts/apply-casesettings.py +1315 -0
  171. package/skills/service-email-to-case-configure/scripts/check-agent-email-capability.sh +26 -0
  172. package/skills/service-email-to-case-configure/scripts/tests/__init__.py +0 -0
  173. package/skills/service-email-to-case-configure/scripts/tests/_bootstrap.py +45 -0
  174. package/skills/service-email-to-case-configure/scripts/tests/_fakeorg.py +366 -0
  175. package/skills/service-email-to-case-configure/scripts/tests/_run.py +84 -0
  176. package/skills/service-email-to-case-configure/scripts/tests/test_apply_org_scenarios.py +655 -0
  177. package/skills/service-email-to-case-configure/scripts/tests/test_get_session.py +134 -0
  178. package/skills/service-email-to-case-configure/scripts/tests/test_new_capabilities.py +193 -0
  179. package/skills/service-email-to-case-configure/scripts/tests/test_validate_casesettings.py +170 -0
  180. package/skills/service-email-to-case-configure/scripts/validate-casesettings.py +229 -0
  181. package/skills/service-itsm-agentic-setup-agentforce-coordinate/SKILL.md +33 -15
  182. package/skills/service-itsm-agentic-setup-agentforce-coordinate/examples/output-templates.md +63 -47
  183. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/SKILL.md +17 -12
  184. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/references/cli-invocation.md +5 -3
  185. package/skills/service-itsm-agentic-setup-agentforce-studio-configure/scripts/classify-enable-plan.mjs +11 -3
  186. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/SKILL.md +8 -6
  187. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/references/cli-invocation.md +3 -3
  188. package/skills/service-itsm-agentic-setup-agentforce-studio-validate/scripts/classify-readiness.mjs +11 -3
  189. package/skills/service-itsm-agentic-setup-configure/SKILL.md +5 -5
  190. package/skills/service-itsm-agentic-setup-configure/examples/output-templates.md +5 -5
  191. package/skills/service-itsm-agentic-setup-employee-agent-configure/SKILL.md +2 -2
  192. package/skills/service-itsm-agentic-setup-employee-agent-configure/references/report-format.md +11 -7
  193. package/skills/service-itsm-agentic-setup-employee-agent-configure/scripts/render-report.mjs +20 -7
  194. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/SKILL.md +10 -6
  195. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/action-availability.md +7 -7
  196. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/references/report-format.md +11 -7
  197. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/build-create-body.mjs +24 -17
  198. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/classify-action-availability.mjs +98 -58
  199. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/render-report.mjs +20 -7
  200. package/skills/service-itsm-agentic-setup-fulfiller-agent-configure/scripts/strip-release-management.mjs +176 -0
  201. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/SKILL.md +19 -19
  202. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/cli-invocation.md +3 -4
  203. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/helper-contracts.md +6 -6
  204. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/references/permset-topology.md +3 -10
  205. package/skills/service-itsm-agentic-setup-itsm-agentforce-permset-assign/scripts/classify-permset-availability.mjs +7 -9
  206. package/skills/service-itsm-incident-mgmt-configure/SKILL.md +2 -0
  207. package/skills/service-itsm-incident-priority-configure/SKILL.md +15 -12
  208. package/skills/service-itsm-incident-priority-configure/examples/matrix-operations.md +27 -1
  209. package/skills/service-itsm-incident-priority-configure/references/sf-cli-invocation.md +37 -6
  210. package/skills/service-native-voice-recording-transcription-configure/SKILL.md +146 -0
  211. package/skills/service-native-voice-recording-transcription-configure/assets/confirmation-output.json +16 -0
  212. package/skills/service-native-voice-recording-transcription-configure/assets/package.xml +22 -0
  213. package/skills/service-native-voice-recording-transcription-configure/references/thunderbird-voice-settings.md +101 -0
  214. package/skills/service-native-voice-recording-transcription-configure/scripts/enable-recording-transcription.sh +181 -0
  215. package/skills/data360-activate/README.md +0 -38
  216. package/skills/data360-activate/SKILL.md +0 -127
  217. package/skills/data360-connect/README.md +0 -57
  218. package/skills/data360-connect/SKILL.md +0 -163
  219. package/skills/data360-connect/examples/connections/heroku-postgres.json +0 -15
  220. package/skills/data360-connect/examples/connections/ingest-api-connection.json +0 -5
  221. package/skills/data360-connect/examples/connections/ingest-api-schema.json +0 -31
  222. package/skills/data360-connect/examples/connections/redshift.json +0 -16
  223. package/skills/data360-connect/examples/connections/sharepoint-unstructured.json +0 -20
  224. package/skills/data360-connect/examples/connections/snowflake-connection.json +0 -42
  225. package/skills/data360-harmonize/README.md +0 -31
  226. package/skills/data360-harmonize/SKILL.md +0 -126
  227. package/skills/data360-orchestrate/README.md +0 -120
  228. package/skills/data360-orchestrate/SKILL.md +0 -263
  229. package/skills/data360-orchestrate/assets/definitions/activation-target.template.json +0 -5
  230. package/skills/data360-orchestrate/assets/definitions/activation.template.json +0 -7
  231. package/skills/data360-orchestrate/assets/definitions/calculated-insight.template.json +0 -7
  232. package/skills/data360-orchestrate/assets/definitions/data-action-target.template.json +0 -5
  233. package/skills/data360-orchestrate/assets/definitions/data-action.template.json +0 -5
  234. package/skills/data360-orchestrate/assets/definitions/data-graph.template.json +0 -21
  235. package/skills/data360-orchestrate/assets/definitions/data-stream.template.json +0 -55
  236. package/skills/data360-orchestrate/assets/definitions/dmo.template.json +0 -17
  237. package/skills/data360-orchestrate/assets/definitions/identity-resolution.template.json +0 -30
  238. package/skills/data360-orchestrate/assets/definitions/mapping.template.json +0 -14
  239. package/skills/data360-orchestrate/assets/definitions/relationship.template.json +0 -12
  240. package/skills/data360-orchestrate/assets/definitions/search-index.template.json +0 -9
  241. package/skills/data360-orchestrate/assets/definitions/segment.template.json +0 -16
  242. package/skills/data360-orchestrate/references/feature-readiness.md +0 -157
  243. package/skills/data360-orchestrate/references/plugin-setup.md +0 -138
  244. package/skills/data360-orchestrate/scripts/bootstrap-plugin.sh +0 -53
  245. package/skills/data360-orchestrate/scripts/diagnose-org.mjs +0 -511
  246. package/skills/data360-orchestrate/scripts/generate-manifest.mjs +0 -68
  247. package/skills/data360-orchestrate/scripts/verify-plugin.sh +0 -58
  248. package/skills/data360-prepare/README.md +0 -50
  249. package/skills/data360-prepare/SKILL.md +0 -203
  250. package/skills/data360-prepare/examples/ingestion-api/.env.example +0 -8
  251. package/skills/data360-prepare/examples/ingestion-api/README.md +0 -48
  252. package/skills/data360-prepare/examples/ingestion-api/send-data.py +0 -144
  253. package/skills/data360-query/README.md +0 -43
  254. package/skills/data360-query/SKILL.md +0 -128
  255. package/skills/data360-query/examples/search-indexes/hybrid-structured.json +0 -44
  256. package/skills/data360-query/examples/search-indexes/vector-knowledge.json +0 -43
  257. package/skills/data360-segment/README.md +0 -35
  258. package/skills/data360-segment/SKILL.md +0 -123
  259. package/skills/service-helpagent-coordinate/references/channel-help-portal.md +0 -22
  260. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-1-1-non-text-content.md +0 -0
  261. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-i-lists.md +0 -0
  262. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-ii-tables.md +0 -0
  263. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iii-form-labels.md +0 -0
  264. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-iv-regions.md +0 -0
  265. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-1-v-groups.md +0 -0
  266. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-3-5-identify-input.md +0 -0
  267. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-1-4-3-contrast.md +0 -0
  268. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-1-1-keyboard.md +0 -0
  269. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-4-link-purpose.md +0 -0
  270. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-4-6-headings-labels.md +0 -0
  271. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-1-pointer-gestures.md +0 -0
  272. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-2-pointer-cancellation.md +0 -0
  273. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-3-label-in-name.md +0 -0
  274. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-2-5-7-dragging-movement.md +0 -0
  275. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-1-on-focus.md +0 -0
  276. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-2-2-on-input.md +0 -0
  277. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-1-error-identification.md +0 -0
  278. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-2-labels-instructions.md +0 -0
  279. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-3-3-3-error-suggestion.md +0 -0
  280. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-i-name.md +0 -0
  281. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-ii-role.md +0 -0
  282. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/reviewers/sc-4-1-2-iii-value.md +0 -0
  283. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-1-1-non-text-content.md +0 -0
  284. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-1-use-of-color.md +0 -0
  285. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-10-resize-reflow.md +0 -0
  286. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-11-non-text-contrast.md +0 -0
  287. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/references/vision/sc-1-4-3-contrast.md +0 -0
  288. /package/skills/{experience-lwc-accessibility-validate → experience-accessibility-validate}/scripts/contrast-ratio.py +0 -0
@@ -0,0 +1,50 @@
1
+ # Agent execution checklist and tripwires
2
+
3
+ Copy this checklist agent-internally (do NOT print to chat). Tick each box only after the action is genuinely done.
4
+
5
+ ## Mandatory progress checklist
6
+
7
+ - [ ] Step 1a: read `sfdx-project.json`, resolved `<sfdx-source>`
8
+ - [ ] Step 1b: ran directory-listing tool against `<sfdx-source>/contentTypes/`
9
+ - [ ] Step 1c: dispatched `metadata-grounding.search_metadata` (NOT `salesforce-api-context`)
10
+ → `query` string is 3-5 English content-domain nouns (`news article announcement story headline`, not `sfdc_cms__news content type` or `bundle`). Confirmed no `sfdc_cms`, `c__`, `__` substring, `content type`, `bundle`, `metadata`, or `cms` in the query. See `discovery-query-rules.md` § Concrete tool calls.
11
+ → recorded `grounding=complete` OR `grounding=unavailable` from a REAL `metadata-grounding` attempt
12
+ → ALSO dispatched `content-readonly.get_content_types_for_workspace` per the mutual-exclusivity contract: `{ spaceId }` alone if the caller supplied `spaceId` — no `baseType` sent; `{ folderId }` alone if the caller supplied `folderId` — no `baseType` sent; `{ baseType }` if the caller supplied only that (no scope); `{ baseType: "CONTENT" }` if the caller supplied none of the three. If `grounding=complete`: took the intersection of the two responses' FQNs (Flow 1). If `grounding=unavailable`: this tool's semantically-matched rows ARE the org signal (Flow 2) — recorded `groundingFallback=workspaceTypes`. Only skip this dispatch if `get_content_types_for_workspace` itself is absent from the deferred-tool list this turn (record `workspaceTypes=unavailable`).
13
+ - [ ] Step 1d: presented findings to the user — has-matches variant emits exactly 4 options: ONE `Use existing: <row-1 FQN>` (row 1 = OOTB if any exists, else grounding's top rank), `Provide an FQN` (only when matches exist OR delegated invocation), `Create new`, and `Cancel`. Step 1d is always chat-visible and always waits for the user's pick.
14
+ - [ ] Step 1e (only when user picked `Use existing` or `Provide FQN`): retrieve-and-reconcile — namespace-gated `sf project retrieve` for custom FQNs, skipped for OOTB.
15
+ → **BEFORE retrieve**: snapshotted `localSchemaBefore` (schema.json only, if it exists locally) into memory. Did NOT snapshot meta.xml — retrieve writes it correctly on its own.
16
+ → **AFTER retrieve**: compared `localSchemaBefore` (pre-retrieve snapshot) against post-retrieve `schema.json` as parsed JSON trees. If they differ → dispatched drift prompt (`Deploy local` / `Overwrite local` / `Cancel`) and waited for user reply. Did NOT declare "reconciled" or "in sync" silently on schema drift.
17
+ → **On drift-prompt Cancel or Deploy-local**: restored `localSchemaBefore` to `schema.json` BEFORE emitting outcome. Left meta.xml alone (retrieve wrote correct XML).
18
+ On success returns `{fqn, schema}` and jumps to step 7.5.
19
+ - [ ] Step 2 (only on "Create new" path): SF CLI default org resolved (silent).
20
+ - [ ] Step 3b: presented proposed-fields markdown table, got user approval (this is the FIRST chat message — until now, only silent work).
21
+ - [ ] Step 4: wrote `schema.json` + `.contentTypeBundle-meta.xml`.
22
+ - [ ] Step 5: ran `sf project deploy start --dry-run --json` (mandatory — no exit before this except "use existing").
23
+ - [ ] Step 7a: asked the user yes/no to deploy via `ask_user_tool`.
24
+ - [ ] Step 7b/7e: deploy resolved.
25
+ - [ ] Step 7.5: printed the schema summary table (chat-visible) — always runs when `{fqn, schema}` was resolved, regardless of `suppressCreateContentPrompt`.
26
+ - [ ] Step 8 gate: read `suppressCreateContentPrompt`. If `true`, skip step 8 and emit `Task Completed` with `{fqn, schema}`. If `false`/unset AND `{fqn, schema}` was resolved, dispatch the trailing "create content?" prompt and route on the reply, THEN emit `Task Completed`.
27
+
28
+ ## Tripwires — stop and restart from step 1a
29
+
30
+ - About to dispatch a discovery tool to any server other than `metadata-grounding` or `content-readonly.get_content_types_for_workspace` — including `salesforce-api-context`, `salesforce-metadata-experts`, `execute_metadata_action`, `run_soql_query`, `run_tooling_query`, `sf data query`, `sf org list metadata`, or a SOQL `SELECT … FROM ContentTypeBundle` → **Rule 1 violation.** These two are the ONLY allowed discovery paths. `get_content_types_for_workspace` is a sanctioned complement to grounding (Flow 1 intersection) and its sanctioned Flow 2 replacement when grounding is down — it is NOT a loophole for reaching any other server. If `metadata-grounding` isn't in the deferred-tool list at turn start, accept `grounding=unavailable`, still dispatch `get_content_types_for_workspace` (Flow 2), and use the matching variant in step 1d — do NOT reach for any OTHER substitute.
31
+ - About to dispatch `metadata-grounding.search_metadata` with a `query` containing `sfdc_cms`, `c__`, any `__` substring, `content type`, `bundle`, `metadata`, or `cms` → **query-construction violation.** The `query` is 3-5 English content-domain nouns; the kind is already carried by the `metadataType` argument. Rebuild before the tool call — see `discovery-query-rules.md` § Concrete tool calls for the shape.
32
+ - About to run `sf project retrieve start --metadata ContentTypeBundle:<Name>` in step 1e WITHOUT first reading local `schema.json` into memory as `localSchemaBefore` → **Rule 5 violation (5a).** Retrieve overwrites `schema.json` in place; if you skip the snapshot, drift is undetectable and Cancel becomes a lie.
33
+ - Reading local meta.xml into a `localMetaBefore` variable, or restoring meta.xml from any snapshot on the Cancel / Deploy-local branches → **do not do this.** Retrieve returns each of the two bundle files with its own correct content — meta.xml is not user-authored and has no reconciliation role. Snapshot and restore `schema.json` only.
34
+ - After step 1e retrieve, `localSchemaBefore` differs from the post-retrieve `schema.json` (JSON tree compare) AND you are about to declare "reconciled" / "in sync" / "local now matches org" / "no drift" WITHOUT dispatching the drift prompt → **Rule 5 violation (5b/5c).** `schema.json` drift = mandatory prompt with `Deploy local to org` / `Overwrite local with org` / `Cancel`. The user picks, not you.
35
+ - About to compare the post-retrieve `schema.json` against a fresh read of the SAME file (or against the retrieved bytes) → **Rule 5 violation (5b).** That's diffing a file against itself and will always return "no drift." Left-hand side of the compare is `localSchemaBefore` (pre-retrieve snapshot), full stop.
36
+ - About to emit `Cancelled. No files written.` from the step 1e drift-prompt Cancel branch, or route the `Deploy local to org` branch to step 5, WITHOUT first writing `localSchemaBefore` back to `schema.json` → **Rule 5 violation (5d).** The message is a promise about disk state; a Deploy-local without restore deploys the org copy back to org (no-op) instead of the user's original local.
37
+ - About to write "Proposed fields for …" without step 1c having dispatched `metadata-grounding.search_metadata` → **Rule 2 violation.**
38
+ - About to write any step 1d header or pick line that claims a content type is "not in the org", "not deployed", "local only", "missing from org", or equivalent → **Rule 6 violation.** Grounding is a heuristic index, not the org — its silence proves nothing about org state. `get_content_types_for_workspace` confirms workspace support, but that's also not a deploy-shape confirmation. Only step 1e's `sf project retrieve start` can confirm absence. Copy the exact case header from `assets/discovery-prompts.md` verbatim.
39
+ - About to append `(org check skipped — grounding unavailable)` when `get_content_types_for_workspace` actually ran this turn (Flow 2), or omit any disclosure at all when BOTH tools were unavailable → **TRUTH GATE violation.** Three distinct wordings for three distinct states — see SKILL.md Step 1d and `discovery-details.md#1d`. Picking the wrong one either overstates or understates what was actually checked.
40
+ - About to offer a "deploy it now?" prompt at step 1d (before retrieve has run) → **Rule 6 violation.** Deploy offers belong AFTER step 1e's retrieve confirms org absence. At step 1d, you only present the pick list.
41
+ - Step 1d output doesn't match `assets/discovery-prompts.md` verbatim — preamble text above the table, wrong columns, bare-FQN cells, table packed into `question`, multiple `Use existing:` options, or ad-libbed question wording → **Rule 6 violation.** Re-open `assets/discovery-prompts.md` and copy the has-matches / zero-matches shape exactly.
42
+ - About to emit `Task Completed` after step 4 without step 5 run → **Rule 3 violation.**
43
+ - About to dispatch `open <deployUrl>`, `sf org open`, or any command AFTER emitting `Task Completed` → **Rule 4 violation.**
44
+ - About to dispatch the step 8 trailing "create content?" prompt WITHOUT first checking `suppressCreateContentPrompt` from invocation params → **invocation-contract violation.** If the flag is `true`, step 8 is a no-op — return `{fqn, schema}` and emit `Task Completed`.
45
+ - About to print `intent=… | skill_selection=complete`, `grounding=…`, `mcp=…`, or any status line to chat → **output-discipline violation.** Agent-internal only, never printed.
46
+ - If you call any tool before step 1c is checked off → you are not following this skill correctly. Stop and start at step 1a.
47
+ - If your first chat message to the user contains "Proposed fields", "I'll create the files", "I'll attempt salesforce-api-context", or "Per the global rule" → you have skipped step 1. Stop and start at step 1a.
48
+ - If you emit `Task Completed` and step 5 (dry-run) or step 7 (deploy ask) is unchecked → you have violated Rule 3. Stop, run the missing steps.
49
+
50
+ If `metadata-grounding` is genuinely unreachable (real server error, not a misrouted call): record `grounding=unavailable` agent-internally, dispatch `get_content_types_for_workspace` directly (Flow 2), and continue silently to step 1d with whatever it returns.
@@ -0,0 +1,39 @@
1
+ # Anti-patterns — the "bug thoughts" that precede a rule violation
2
+
3
+ The middle column is what the agent tells itself when it's about to violate the rule. If you catch yourself thinking the middle-column thought, **STOP. That thought is the bug.** Apply the right column.
4
+
5
+ | Violation | Rationalization the agent uses ("the bug thought") | Correct action |
6
+ |---|---|---|
7
+ | Skipping step 7 (deploy ask) after step 5 succeeded | "I already validated; the bundle is good; the task is done." / "User just asked to create, not deploy." / "Validation result is the final result — emit `Task Completed`." | **STRICTLY DO NOT.** Step 5's success is what UNLOCKS step 7; it does NOT replace it. After printing the one-line `Validated against <org>. Deploy result: Succeeded.`, your VERY NEXT action is `ask_user_tool` for step 7a. No `Task Completed` until 7 resolves. |
8
+ | Skipping step 5 (dry-run) after step 4 | "The user said create, not validate." / "I'll let the user run validation themselves." / "Grounding was unavailable, so org access seems off." | **STRICTLY DO NOT.** Step 5 is YOUR job. Different auth path from grounding. Run it. |
9
+ | Emitting `Task Completed` after step 5's success line | "Validated. Done. Nothing left to do." | Step 5's success line is NOT a terminal marker. The terminal marker is step 7b (no) or step 7e (yes), AFTER the user has answered the deploy ask. |
10
+ | Skipping `metadata-grounding.search_metadata` AND writing a step 1d header that implies the org was checked via grounding — OR appending the grounding-unavailable-skip suffix when `get_content_types_for_workspace` actually ran and checked the org | "I already did local discovery, the org check probably wouldn't add anything." / "I'll claim it ran to keep the user-facing message simple." / "Grounding was down so I'll say 'org check skipped' even though the workspace tool ran fine." | Fabrication in either direction. Three TRUTH GATE states, not two: (1) `search_metadata` dispatched → no suffix needed. (2) `search_metadata` down, `get_content_types_for_workspace` dispatched (Flow 2) → org WAS checked, use `(checked supported content types for this workspace — metadata-grounding unavailable)`. (3) both down → org genuinely not checked, use `(org check skipped — grounding unavailable)`. Picking case-3 wording for a case-2 turn understates real coverage just as badly as claiming a check that never ran. See SKILL.md Step 1d and `discovery-details.md#1d`. |
11
+ | Listing local matches in 1d AND proposing fields in the same turn (skipping the pick prompt) | "I found a local Company bundle, but the user said create, so they obviously want a new one — I'll just propose fields and move on." / "The local match is probably stale, the user wants a fresh one." | The user picks, not you. STRICTLY DO NOT print `"Found locally: [local] X"` and then `"Proposed fields for X..."` in the same message. After the findings list, dispatch `ask_user_tool` with `Use existing: <Name>` / `Create new: <Name>` / `Cancel` and END YOUR TURN. Step 3 may only run AFTER the user replies `Create new`. If user replies `Use existing`, exit — do not propose fields, do not validate, do not deploy. |
12
+ | Auto-proceeding to step 3 on gibberish intent — proposing a content-type name and fields for input like `aadl asdf sdaf` | "Zero matches means I should just create fresh." / "I'll guess the domain from context." / "One of my open tabs was about a Government content type, I'll use that name." | Direct-invocation zero-matches auto-proceed has an intent-sanity gate. If the user's message contains no recognizable content-domain noun (real English word, common named entity, or clearly domain-vocabulary compound), do NOT auto-proceed. Dispatch the sanity-check `ask_user_tool` (`Your request "<msg>" doesn't contain a clear content-type intent. What kind of content type would you like to create?` / `Cancel`) and wait. Do NOT invent a name from thin air, and STRICTLY DO NOT pull the name from an unrelated open tab or a prior session — that is context-leakage hallucination. |
13
+ | Proposing a content-type name whose tokens don't appear in the user's message (e.g. user typed `aadl asdf sdaf`, agent proposed `GovernmentGO`) | "The `title` should read professional — `AadlAsdfSdaf` looks wrong." / "I remember `Government` from an earlier tab; that's what the user probably means." / "I need to pick SOMETHING for the name." | Hallucination. The `<contentTypeName>` MUST be built from tokens actually in the user's original intent (normal PascalCase / word-order shaping is fine). If the recognizable-noun set is empty, the intent-sanity gate should have caught it BEFORE step 3 — go back and dispatch that prompt. If the recognizable-noun set is one word, use that word (`laptop` → `Laptop`). Never invent, never borrow from other contexts. |
14
+ | Filtering the step 1d matches table down to the top row (or 2-3 rows) when combined returned more | "The top match is what the user obviously wants; the others are noise." / "Showing all 6 will overwhelm the user, I'll pick the most relevant." / "The genre entries are semantically far — user wouldn't want them." | The rule is DETERMINISTIC, not judgment-based. `combined` is built in step 1c (local matches + the Flow 1 intersection or Flow 2 semantic-match rows), THEN capped at 5 in step 1d (top-5, rest discarded). The 1c-stage filtering (intersection / semantic match) is part of building `combined`, not a step-1d judgment call — once `combined` exists, step 1d itself does zero further filtering. Emit ALL rows in `combined`. If it has 5 rows, the table has 5. If it has 3, the table has 3. Never fewer than `combined.length`. |
15
+ | Ad-libbing the `ask_user_tool` question at step 1d | "`Found matches. Pick one:` reads clinical; `A content type matching X was found. How would you like to proceed?` reads more human." / "The user needs context in the question." | Exact strings only. Two variants: `Found matches. Pick one:` (has matches) / `No matches found. Pick one:` (zero matches). Context lives in the table above, not in the question field. |
16
+ | Treating "same bundle in local + org" as a naming conflict | "PressRelease is in local AND grounding returned c__PressRelease — that's a duplicate, I need a rename flow." / "Two hits with the same name means the deploy will fail." | It's the SAME BUNDLE, found in two places. Deduplicate: emit one row with location `local, org`. User picks `Use existing:` like any other row → step 1e retrieves + reconciles. No rename prompt, no collision variant. |
17
+ | Writing a preamble sentence before the step 1d `Top …` header | "`c__PressRelease` appears in both local and org — I should tell the user what happened before showing the table." / "`Presenting discovery findings.` gives the table context." / "`6 total results, c__PressRelease was the top match` is a helpful summary." | The header + table IS the summary. The FIRST character of Output 1 is `T` in `Top`. No lead-in. If you want to say something about the top match, you already are — it's row 1 of the table with Location `local, org`. Extra words are the bug. |
18
+ | Putting the 1d table (or its columns, or the `(checked supported… / org check skipped…)` suffix) INSIDE the `ask_user_tool` `question` field | "If I put the table in the question, the user sees the matches right next to the options." / "The suffix is context, it belongs with the question." / "One combined prompt is cleaner than a chat message plus a tool call." | The `question` field renders as ONE flat line with no markdown — a table pasted there becomes the reported broken wall of text. TWO separate outputs: Output 1 = the header + table as CHAT markdown; Output 2 = `ask_user_tool` with `question` EXACTLY `Found matches. Pick one:` and the 4 fixed options. The disclosure suffix goes on the chat header (Output 1), never in `question`. See SKILL.md § step 1d output shape. |
19
+ | Dispatching `ask_user_tool` at step 1d with a `Use existing:` option per table row (5+ options) | "The user wants to be able to pick any row, so I'll add a `Use existing:` option per row." / "Only offering the best match is limiting — the user might want row 2." | Wrong shape. Options are FIXED at 4 (has matches): (1) ONE `Use existing:` naming row 1's FQN (2) `Provide an FQN` (3) `Create new:` (4) `Cancel`. Users who want row 2+ pick `Provide an FQN` and type its FQN. This is the shape — do not "improve" it. |
20
+ | Adding a second `Use existing:` option for the top OOTB match when the top custom match already got one (or vice versa) | "OOTB is more authoritative than custom — I should give the user a shortcut to it alongside the top custom." / "`c__NewsArticle` and `sfdc_cms__news` are both strong matches, both deserve their own option." / "Two `Use existing:` options is still fine, it's not `per row`." | Same violation — options are FIXED at 4, ever. The way to promote OOTB is through the row-1 sort: if any OOTB row exists in `combined`, it moves to row 1 (see SKILL.md § step 1d "Row-1 sort"), which makes `Use existing:` name it automatically. Two `Use existing:` options is the wrong knob to reach for. If the sort didn't put OOTB in row 1, fix the sort — do not add a second option. |
21
+ | Column headers other than exactly `FQN | Description | Location` (adding `#`, using `Name` or `Label` or `API Name`) | "`#` makes rows scannable." / "`Name` is friendlier than `FQN`." / "Grounding's raw table used `API Name` and `Label`, I'll match it." | Three columns, fixed order, fixed names: `FQN | Description | Location`. That's the shape. Grounding's raw response is agent-internal; the user-visible table has ITS OWN column contract. Do not mirror grounding's shape. |
22
+ | Writing the bare DeveloperName in the FQN column (e.g. `PressRelease`) instead of the namespaced FQN (e.g. `c__PressRelease`) | "`PressRelease` is cleaner than `c__PressRelease` — the namespace is noise." / "The user knows their namespace, they don't need to see it." / "The `Use existing:` option shows the full FQN; the table doesn't have to." | The column is called `FQN`, not `Name`. Every FQN cell contains `<namespace>__<DeveloperName>`. `c__` for org-custom, `sfdc_cms__` for OOTB. The namespace disambiguates `c__Article` (custom) from `sfdc_cms__article` (OOTB) — it is NOT noise. |
23
+ | Modifying / refining / deploying an existing bundle when user picks "use existing" | "The user picked it, so they want to work on it." | Skill is create-only. Exit with `Using existing bundle '<Name>'. No files written.` |
24
+ | Writing `"type": "string"` / `"format": "date"` / `"default": …` on a field | "JSON Schema requires a type." | `lightning:type` is the only type marker. Validator rejects bare `type`/`format`/`default` under `unevaluatedProperties: false`. |
25
+ | Writing `"lightning:uiOptions": {}` (empty object) | "The skill says omit placeholderText, but uiOptions is fine." | Empty `uiOptions` ALWAYS fails. Omit it entirely OR include a real `placeholderText`. Nothing in between. |
26
+ | Auto-fix loop fixing one field per attempt | "I'll fix the field the validator named, then re-run." | Validator reports 1-2 errors per pass. Sweep the WHOLE schema for the same error class per attempt. See SKILL.md § step 6. |
27
+ | Adding `lightning:textIndexed`, `lightning:localizable`, etc. without the user asking | "Better to enable indexing/localization for them." | Default-minimal. Match the request only. |
28
+ | Dispatching any command after `Task Completed` (`open <deployUrl>`, `sf org open`, etc.) | "The deploy JSON has a `deployUrl` — let me open it for them." | RULE 4 violation. `Task Completed` is the LAST action. The user opens the URL themselves. |
29
+ | Dispatching step 8's "create content?" prompt when `suppressCreateContentPrompt=true` | "The user might still want to create content, I'll ask anyway." | Invocation-contract violation. `true` means the caller drives the content flow. Skip step 8 entirely and return `{fqn, schema}`. |
30
+ | Prompting for edits in step 3b without reprinting the current fields table | "The user just saw the table one turn ago." / "The prompt has example edit phrasings; that's enough." | The user can't scroll or recall across turns. Reprint the current proposed-fields table (chat-visible markdown) BEFORE every edit prompt so the user sees the field names, types, and required flags they're editing. |
31
+ | Stopping the edit-fields loop when `ask_user_tool` returns free text | "The tool result came back as a raw string, not one of my enumerated options — I'll treat it as a non-actionable response and ask again." / "This is ambiguous, better to clarify before proceeding." | Free-text tool results in the edit-fields loop ARE the user's edit instructions. Parse the string (add / drop / rename / require / type-change) and apply. Then reprint the updated table and re-ask `Approve / Edit fields / Cancel`. Do NOT stop, do NOT ask "did you mean edits?" — that's the workflow-halt regression. Only clarify if the instruction is genuinely ambiguous (e.g. "make X better"). |
32
+ | Hunting for a `metadata-grounding` substitute when the server is unavailable — including SOQL / Tooling API (`run_soql_query`, `run_tooling_query`, `sf data query`, `SELECT … FROM ContentTypeBundle`), sibling metadata MCPs (`salesforce-api-context`, `salesforce-metadata-experts`, `execute_metadata_action`), `sf org list metadata`, or any other org-side introspection | "The primary tool isn't loaded but there might be a sibling that does the same thing." / "Let me try one more variant query — maybe the ranker just missed it." / "`execute_metadata_action` mentions metadata, close enough." / "ContentTypeBundle is metadata, so I can just SOQL it — data queries are a different category from what the rule forbids." / "SOQL isn't a metadata MCP, so 'no substitute metadata MCP' doesn't apply." / "`get_content_types_for_workspace` is already a sanctioned exception, so other substitutes should be fine too." | RULE 1 violation. If it isn't `metadata-grounding.search_metadata` / `query_metadata` / `describe_metadata` / `content-readonly.get_content_types_for_workspace`, it doesn't count as discovery. `get_content_types_for_workspace` is the ONE sanctioned complement/replacement (Flow 1 intersection when grounding is up, Flow 2 sole signal when it's down) — it does NOT open the door to any OTHER substitute. **The deferred-tool list at turn start IS the probe** — if metadata-grounding isn't in it, go DIRECTLY to Flow 2 (dispatch `get_content_types_for_workspace`, use its matching variant in step 1d). Do not run ToolSearch to "look harder." Do not reach for SOQL, Tooling API, sibling MCPs, or CLI introspection. ContentTypeBundle isn't a queryable SObject; SOQL results (even if any come back) aren't the JSON Schema shape the deploy validator expects. Absence of BOTH sanctioned tools IS the answer, not a hint to retry. |
33
+ | After step 1e retrieve, silently declaring "reconciled" / "in sync" / "local now matches org" when `localSchemaBefore` differs from the post-retrieve `schema.json` — no prompt | "The retrieve pulled the org copy and it's a valid schema, so I'll just use it — no need to bother the user." / "The CLI already merged it; my job is done." / "I read the file, it's the org version, that IS reconciliation." | RULE 5 violation. When `schema.json` differs, the drift prompt (`Deploy local to org` / `Overwrite local with org` / `Cancel`) is MANDATORY. The user chose which side wins, not the agent. Silently accepting the org copy is a two-way data loss: (a) the user's local edits are gone with no consent, (b) the "success" message misrepresents what happened. Reading the post-retrieve file and diffing it against itself will always return "no drift" — that IS the bug. Use `localSchemaBefore` (the pre-retrieve snapshot) as the left-hand side, never the post-retrieve file. |
34
+ | Snapshotting local meta.xml into `localMetaBefore` before step 1e retrieve, or restoring meta.xml from a snapshot on the drift-prompt Cancel / Deploy-local branches | "meta.xml might get clobbered by retrieve, better back it up." / "Symmetry with `schema.json` — snapshot both, restore both." / "The old rule said to restore meta.xml on Cancel." | Do not do this. `sf project retrieve start` returns the two bundle files with their own correct content — `schema.json` gets JSON, `<Name>.contentTypeBundle-meta.xml` gets the minimal XML wrapper. meta.xml is not user-authored — it's a small mechanical file (folder name → `<masterLabel>`) — so there is nothing to preserve across a retrieve. Snapshot `schema.json` ONLY; restore `schema.json` ONLY. If a snapshot/restore step writes meta.xml, it can round-trip a pre-existing meta.xml corruption (e.g. schema JSON in the meta.xml file from a prior bad run) back onto disk. Let retrieve own meta.xml — always. |
35
+ | Prompting the user about meta.xml drift after step 1e retrieve | "The retrieve JSON says `state: Changed`, so I have to prompt." / "The meta.xml file was modified, that's drift, the rule says prompt on drift." / "I'll ask the user which meta.xml to keep." | Drift is `schema.json` content compare, full stop. meta.xml has no reconciliation role — whatever `sf project retrieve` wrote is correct by definition. Never prompt on meta.xml differences. `state: Changed` in the retrieve JSON is a hint, NOT the authoritative signal — `schema.json` content compare is. |
36
+ | Diffing the retrieved schema against the local file WITHOUT snapshotting local first — then concluding "no drift" | "The retrieve command says `Changed` but I just read local and retrieved and they match — must be a false positive." / "The `state: Changed` field is a CLI quirk; I'll trust my content comparison." | `sf project retrieve start` writes the org copy INTO the local folder — it overwrites in place. Diffing "local" against "retrieved" AFTER retrieve is diffing the same file against itself and will ALWAYS return "no drift." The CLI's `files[].state === "Changed"` IS the drift signal precisely because the CLI knows what was on disk before it wrote. STRICTLY: (1) snapshot `localSchemaBefore` into memory BEFORE running retrieve, (2) treat `state === "Changed"` as authoritative drift, (3) use the pre-retrieve snapshot as the left-hand side of any content compare. See `references/retrieve-and-reconcile.md` § Retrieve. |
37
+ | Emitting `Cancelled. No files written.` from the step 1e drift-prompt Cancel branch WITHOUT restoring the pre-retrieve schema.json snapshot | "The user cancelled, so I don't need to touch anything else." / "The message is a chat convention, not a promise about disk state." | The message IS a promise. `sf project retrieve` has already overwritten the local `schema.json` with the org copy by the time the drift prompt appears — walking away silently leaves the user's local schema mutated behind their back. Cancel MUST write `localSchemaBefore` back to `schema.json` before emitting the outcome. Same rule for the `Deploy local to org` branch: restore `schema.json` first, THEN dry-run + deploy, so what deploys is the user's original local — not the org copy the retrieve just dropped in. Leave meta.xml alone in both branches — retrieve wrote it correctly and there's no snapshot to restore. |
38
+ | Dispatching `search_metadata({ query: "sfdc_cms__news content type" })` (or `"sfdc_cms news content type"`, `"c__PressRelease bundle"`, `"news content type"`, `"CMS bundle"`) | "The user said `content type` in their message, I'll keep it." / "Prefixing `sfdc_cms` will help the ranker find CMS types." / "The user pasted `sfdc_cms__news`, I'll pass it through." | The `query` is 3-5 English content-domain nouns. The kind (ContentTypeBundle) is already carried by `metadataType`; the namespace goes in an FQN, not in a keyword search. Rebuild: `sfdc_cms__news content type` → `news article announcement story headline`; `c__PressRelease bundle` → `press release announcement news statement corporate`. Copy shapes from `discovery-query-rules.md` § Concrete tool calls, not from the user's message. |
39
+ | Step 1e OOTB FQN — `describe_metadata` / `query_metadata` returns error/empty/non-schema, and the agent proceeds to Step 3 with a schema fabricated from training data (e.g. `title, body, summary, bannerImage, publishedDate` for `sfdc_cms__news`) | "The describe call failed, but I know OOTB `sfdc_cms__news` has these fields — I'll just use what I remember and continue." / "The query failed but the schema is well-known; no need to block the user." / "Standard OOTB shape, safe to assume." | STRICTLY DO NOT. Failed `describe_metadata` / `query_metadata` for an OOTB FQN = hard exit with `error` outcome and message `Can't resolve OOTB FQN "<fqn>" without a real schema from metadata-grounding. Retry when grounding is back, or provide a custom FQN.` Training-data schemas are unreliable per-org / per-release / per-API-version, and using them silently poisons every downstream step — `create_content` will fail with `unknown key` or `missing required field` errors that LOOK like backend bugs but are schema hallucinations. Fail closed. See `retrieve-and-reconcile.md` § Namespace gate. |
@@ -0,0 +1,27 @@
1
+ # Common Deployment Errors
2
+
3
+ Full mapping of validator error strings to fixes for `ContentTypeBundle` deploys. The skill loads this during step 6 (fix validator failures) after step 5's dry-run fails.
4
+
5
+ **The `Kind` column governs step 6b's user-facing messaging.** Step 6a (silent auto-fix, `userEditedFields === false`) ignores it and just applies the fix. Step 6b (`userEditedFields === true`) uses it to phrase the planned-fix impact line the user sees before approving:
6
+
7
+ - **`safe`** — validator hygiene only. The fix removes something the user could not have meant to author (validator rejects it on every type, or the key is a runtime-only concern). No intent is lost. 6b's parenthetical: literal string `safe: validator hygiene, no intent change`.
8
+ - **`intent-changing`** — the fix drops or reshapes a user-authored constraint (`enum`, `const`, `lightning:localizable`, numeric bounds, type swap). Only the user can judge whether the trade-off is acceptable. 6b's parenthetical: a specific consequence (`loses numeric ordering`, `no longer restricted to enum values`, `text won't localize`, etc.), NOT the generic phrase.
9
+
10
+ | Error / Symptom | Likely Cause | Fix | Kind |
11
+ |---|---|---|---|
12
+ | `additionalProperties NOT expected: <key>` (in `lightning:uiOptions`) | Validation/behavior key placed inside `uiOptions` | **Move** the key (don't delete) to a sibling of `lightning:type` | safe |
13
+ | `additionalProperties NOT expected: <key>` (on a field) | Key not allowed on this type per the rules table | Remove the key, or change the field's `lightning:type` to one that supports it | intent-changing (removal drops user-authored constraint; type swap changes intent) |
14
+ | `You can't add the enum property … unevaluatedProperties … set to false` | `enum` used on a type that doesn't accept it. The validator accepts `enum` ONLY on `lightning__textType` | Either change the type to `lightning__textType` (the only type that accepts `enum`) or drop the constraint and surface to the user | intent-changing (type swap loses numeric/date semantics; dropping the enum loses value restriction) |
15
+ | `You can't add the const property … unevaluatedProperties … set to false` | `const` used on a type that doesn't accept it. The validator accepts `const` only on `lightning__textType`/`lightning__integerType`/`lightning__numberType` | Either change the type to one of `lightning__textType`/`lightning__integerType`/`lightning__numberType` or drop the constraint and surface to the user | intent-changing |
16
+ | `additionalProperties NOT expected: minimum` / `maximum` on non-numeric type | Numeric bounds only on `lightning__integerType`/`lightning__numberType` | Change type or drop the bound | intent-changing |
17
+ | `$.properties.<field>.lightning:localizable: must be a constant value false` | `lightning:localizable: true` on `lightning__integerType`, `lightning__numberType`, or `lightning__imageType` | **Remove** the key entirely; the validator hard-pins it to `false` on these types | intent-changing (field won't localize — user asked for it explicitly) |
18
+ | `You can't add the lightning:localizable property … unevaluatedProperties … set to false` | `lightning:localizable` on `lightning__booleanType` (the property isn't declared at all on boolean) | **Remove** the key entirely on boolean fields | intent-changing |
19
+ | `You can't add the default property defined at $.properties.<field>.default because the unevaluatedProperties keyword value is set to false` | `default` on any field | Remove the `default` key; bundle root's `unevaluatedProperties: false` rejects it | safe |
20
+ | `$.lightning:mixinTypes` missing or empty | No mixins declared | Add `"sfdc_cms:metadataContent": {}` at minimum | safe |
21
+ | `The value for lightning:mixinTypes can't be null when defined in the schema.` (often paired with `Required field is missing: masterLabel`) | An additional mixin (e.g. `sfdc_cms:localizable`, `sfdc_cms:deliveryApiEnabled`, `sfdc_cms:authoringSearch`, `sfdc_cms:deliverySearch`) was declared alongside `metadataContent` | The deploy validator currently accepts only `sfdc_cms:metadataContent`. Remove every other mixin entry. The `masterLabel` complaint is a downstream symptom and clears once the mixin error is fixed | safe |
22
+ | `additionalProperties NOT expected: typeClasses` at root | `typeClasses` was authored in `schema.json` | Remove it. `typeClasses` is a runtime/grounding-side property, not a schema.json key | safe |
23
+ | `lightning:type` is not recognized | Misspelled or unsupported type | Map to a supported type. Common substitutions: `lightning__fileType` → `lightning__imageType`; long text → `lightning__multilineTextType` or `lightning__richTextType`; whole numbers → `lightning__integerType` | intent-changing (type swap — flag the substitution to the user) |
24
+ | Missing `lightning:type` on a property | Field declared no type | Add `lightning:type` based on intent; default to `lightning__textType` only when there is no other signal | intent-changing (agent is picking the type on the user's behalf) |
25
+ | `lightning:allowedUrlSchemes` missing on URL field | URL metaschema requires it | Add `"lightning:allowedUrlSchemes": ["https"]` (or schemes the user specified) | safe |
26
+ | `minLength` greater than `maxLength` (or `minimum` > `maximum`) | Bounds inverted | Swap, or pick the user's stated bound and drop the other | intent-changing (which bound to keep is the user's call) |
27
+ | `status: Failed` with zero `componentFailures` | Deploy itself failed (auth, path, project layout) | Show raw response; verify `sf org list`, source path, and `sfdx-project.json`. Don't count toward 3-attempt cap | — (not a schema fix — CLI/auth failure surfaces to user regardless of `userEditedFields`) |
@@ -0,0 +1,106 @@
1
+ # Discovery internals — steps 1b–1d rationale
2
+
3
+ Load this only when you need the full rationale for a rule you're about to apply or a check you're about to skip. SKILL.md has the short version of each rule; this file has the "why" and the anti-patterns.
4
+
5
+ ## 1b. Local discovery — semantic intent match
6
+
7
+ Use whichever directory-listing capability your environment exposes — pick the first that exists and call it without narration:
8
+
9
+ - `list_files` (takes a directory path)
10
+ - `Glob` (pattern: `<sfdx-source>/contentTypes/*/schema.json`)
11
+ - a local `list_directory` tool wired into your IDE
12
+
13
+ **STRICTLY DO NOT use a content-search / regex / grep tool here** (e.g. `search_files`, IDE text-search-in-files). Those tools look for literal string matches inside file contents and will MISS a folder named `MarketPlace` when the literal string `"MarketPlace"` doesn't appear in the schema's contents. Use a directory-listing tool to enumerate the subfolders of `<sfdx-source>/contentTypes/`, then read each `schema.json`.
14
+
15
+ For each subfolder, read `schema.json` (`title` + `description` fields). **Match by intent semantically** — your job is to reason about content domains, not to text-match. The user request implies a domain (a "marketplace content type" → e-commerce/listings/products/sellers domain; a "press release" → news/announcements domain; a "customer story" → case-study/testimonial domain). Surface bundles whose folder name OR `title` OR `description` is in the SAME content domain as the user's request, even if no literal string matches. A folder named `MarketPlace` IS a match for a marketplace request — folder-name match alone is sufficient.
16
+
17
+ Return every semantically-matching local bundle — they all feed `combined`, which step 1d's 5-row table cap and Row-1 sort handle downstream. If zero bundles match the user's intent, return zero — step 1d's zero-matches variant handles the rest (auto-proceed on direct invocation; delegated invocation still gets a pick list). Listing every bundle as a candidate (e.g. surfacing Product, JobListing, Webinar when the user asked for an unrelated type) is one regression to prevent — filter by intent, not by count. Missing an obvious folder-name match (e.g. saying "no local matches" when a `MarketPlace` folder exists for a marketplace request) is the OTHER regression to prevent.
18
+
19
+ Last-resort fallback if no directory-listing tool exists: `ls -1 <sfdx-source>/contentTypes 2>/dev/null` — single command, no pipes.
20
+
21
+ ## 1c. Org discovery — dispatch gate
22
+
23
+ **Step 1c is a tool call, not a thought.** The primary way to satisfy 1c is to actually invoke `metadata-grounding.search_metadata` and receive its response. If `search_metadata` itself is unreachable, 1c is NOT automatically incomplete — dispatch `content-readonly.get_content_types_for_workspace` directly (Flow 2) as the fallback org signal before concluding no check happened. You are NOT permitted to write any step 1d header that implies a check happened when neither tool ran, nor to disclose a skip when one of them did (see 1d's Truth Gate). `search_metadata` dispatched → standard variant. `search_metadata` down, `get_content_types_for_workspace` dispatched → the Flow-2 variant (org WAS checked). Both down → the grounding-unavailable-skip variant.
24
+
25
+ **STRICTLY DO NOT skip 1c just because 1b found a local match.** A local hit does NOT mean the org is clean — the org may already have a bundle with the same name (the "Name already exists" deploy failure originates here, not in 1b). Step 1c is unconditional: it runs on zero local matches AND on N local matches. The `grounding=unavailable` outage path (real metadata-grounding server error) does NOT exempt 1c from running — it routes to `get_content_types_for_workspace` instead (Flow 2). Only when BOTH tools are unreachable must you record the outage agent-internally and disclose it with the `(org check skipped — grounding unavailable)` suffix from the grounding-unavailable variant in step 1d.
26
+
27
+ Rationalizations the agent uses to justify skipping — all violations:
28
+
29
+ - "Local found a match, no need to also check the org." → No. 1c MUST still run. Its purpose is the org-side check that local cannot do.
30
+ - "Grounding was unavailable last turn, I'll skip it this turn." → No. Re-attempt every run. Outages are per-turn.
31
+ - "User said 'use existing' before I dispatched 1c." → That can't happen — 1d (where the user picks) runs AFTER 1c. If you're already at "user said use existing," you skipped 1c. STOP and start over from 1c.
32
+
33
+ Just call it. Do NOT ask the user "should I search?" — the search is unconditional, and asking IS the violation:
34
+
35
+ ```javascript
36
+ search_metadata({ metadataType: "ContentTypeBundle", query: "<2-5 keywords from user request>", limit: 100 })
37
+ ```
38
+
39
+ Then for each top match:
40
+
41
+ ```javascript
42
+ query_metadata({ metadataType: "ContentTypeBundle", id: "<id>" })
43
+ ```
44
+
45
+ **Server target:** `metadata-grounding` (per RULE 1 and the registry table).
46
+
47
+ Apply the same intent matching. **Historically** (before `get_content_types_for_workspace` existed) every grounding result fed straight into the step 1d table, unfiltered — that behavior now only applies when the workspace-content-types check itself is unavailable (see below); when both tools returned, the intersection computed in "1c continued" is what feeds the table, capped at 5, with the remainder surfacing inline in the FQN option's parenthetical. Tag matches `[org]` — or `[org, OOTB]` when the namespace / grounding response identifies the type as OOTB (`sfdc_cms__*` or grounding's `isOOTB` flag).
48
+
49
+ **OOTB-first row ordering.** If any grounding result is OOTB (`sfdc_cms__*` or `isOOTB === true`), it goes in **row 1** of the step 1d table — and therefore becomes the single `Use existing:` option's FQN. Custom (`c__*`) rows follow in grounding's original rank order. If no OOTB result exists, custom rows use grounding's rank order as-is. This is a deterministic sort, not a judgment call — row 1's FQN is what `Use existing:` names, and only that ONE option is emitted regardless of how many rows the table has.
50
+
51
+ **If `metadata-grounding` is unavailable** (server error, denial, timeout, or empty result): do NOT stop at local-only. Dispatch `content-readonly.get_content_types_for_workspace` directly (Flow 2, below) as the org-discovery signal for this turn. Record `grounding=unavailable` agent-internally regardless — this drives step 1e's OOTB-schema fail-closed rule, which is unaffected by Flow 2. Do not ask the user. `metadata-grounding` unavailability does not block step 5 or step 7 — those use the SF CLI default org via a separate auth path.
52
+
53
+ ## 1c continued. `get_content_types_for_workspace` — why it exists and how it combines
54
+
55
+ Grounding is a search index over metadata descriptions — it can surface a type that sounds right without knowing whether that type is actually usable in the caller's target workspace (a workspace may be scoped to a subset of base types, e.g. `CONTENT` only, or to a specific space/folder). `get_content_types_for_workspace` answers the complementary question: "what can I actually create here?" Neither question alone is sufficient — grounding without workspace-scoping can propose a type the user can't use; workspace-scoping without grounding can't rank by relevance to the user's intent. Combining them is more precise than either alone.
56
+
57
+ **Call params — resolved from the caller's invocation params per the mutual-exclusivity contract (SKILL.md § Invocation contract):**
58
+
59
+ ```javascript
60
+ // caller passed spaceId
61
+ get_content_types_for_workspace({ spaceId })
62
+ // caller passed folderId
63
+ get_content_types_for_workspace({ folderId })
64
+ // caller passed baseType alone
65
+ get_content_types_for_workspace({ baseType })
66
+ // caller passed none of the three (direct/standalone default)
67
+ get_content_types_for_workspace({ baseType: "CONTENT" })
68
+ ```
69
+
70
+ Never send `baseType` alongside `spaceId`/`folderId`, not even as a default — the space/folder scope already determines the eligible types, and layering a base-type filter on top narrows for no reason the caller asked for.
71
+
72
+ **Flow 1 — grounding ran.** After `search_metadata` returns, ALSO dispatch `get_content_types_for_workspace`. Take the intersection of both responses' FQNs. Rationale for intersection over union: a row that grounding likes but the workspace doesn't support would fail deploy/create later (wrong scope); a row the workspace supports but grounding didn't surface is not relevant to what the user asked for. Only rows satisfying BOTH are genuinely "a good match AND usable here." An empty intersection is not a failure state — it means nothing in this workspace matches the user's intent, which step 1d already has a zero-matches variant for. Do not treat empty as a signal to retry or loosen the query. If `get_content_types_for_workspace` itself is unavailable this turn (grounding still fine): record `workspaceTypes=unavailable` agent-internally and fall back to the raw `search_metadata` results, unfiltered — the org check already happened truthfully via grounding, so no 1d header wording changes.
73
+
74
+ **Flow 2 — grounding unavailable.** Do NOT skip the org check — dispatch `get_content_types_for_workspace` directly (same params) as the sole org-discovery signal. Since there's no grounding rank to intersect against, apply the same semantic intent-match reasoning used in step 1b (domain reasoning, not literal string match) directly to the `name`/`description` of each row it returns, keeping only rows in the same content domain as the request. These become the org candidate rows for step 1d. Record `groundingFallback=workspaceTypes` agent-internally — step 1d's TRUTH GATE uses this to pick accurate wording (the org WAS checked, just not via grounding). If `get_content_types_for_workspace` is ALSO unavailable: record `workspaceTypes=unavailable` too — org discovery produced zero signal this turn, so use the grounding-unavailable-skip wording. This is the only case where the org genuinely was not checked.
75
+
76
+ **Local matches (1b) are never intersected or filtered by this tool.** The workspace-content-types check is scoped to org-side candidates only; a local `contentTypes/` folder match is assumed deployable as-is and always survives into `combined` untouched, per the same logic as any other local match.
77
+
78
+ **Neither tool substitutes for 1e's retrieve.** Grounding is a heuristic index; `get_content_types_for_workspace` confirms workspace support for this session — neither is authoritative on org deploy-shape. A `search_metadata` hit is high-signal, a miss is low-signal — NEVER conclude "the org does not have `<Name>`" from grounding alone. Only step 1e's `sf project retrieve start` confirms org presence/absence.
79
+
80
+ ## 1d. Truth gate
81
+
82
+ STRICTLY DO NOT print a step 1d header that implies a check ran when it didn't, and do NOT disclose a skip that didn't happen. Three distinct states, not two:
83
+
84
+ 1. **`search_metadata` dispatched this turn (Flow 1).** The org was checked via grounding — no disclosure suffix needed, regardless of whether `get_content_types_for_workspace` also ran.
85
+ 2. **`search_metadata` unavailable, `get_content_types_for_workspace` dispatched instead (Flow 2).** The org WAS checked — just through the workspace tool, not grounding. Saying "org check skipped" here would itself be fabrication in the other direction (claiming less happened than actually did). Append `(checked supported content types for this workspace — metadata-grounding unavailable)` per `assets/discovery-prompts.md`.
86
+ 3. **Both unavailable.** No org signal was obtained this turn. Append `(org check skipped — grounding unavailable)` per `assets/discovery-prompts.md`.
87
+
88
+ Implying a check happened without a real dispatch is fabrication — the user trusts the header as evidence a check ran, and writing it falsely is the worst kind of regression because it's silent. Equally, describing a real dispatch (case 2) with skip-wording understates the actual coverage and could make the user think a match was missed when it wasn't checked at all — pick the wording that matches what was ACTUALLY dispatched this turn, not just whether grounding specifically ran.
89
+
90
+ ## 1d. Intent-sanity gate (direct-invocation zero-matches)
91
+
92
+ Before auto-proceeding to step 2 on zero matches, extract at least one recognizable content-domain noun from the message:
93
+
94
+ - **Recognizable** = a real English word, a named entity (brand, sport, industry term), or compound domain vocabulary (`pet grooming`, `job listing`, `product review`).
95
+ - **Gibberish** = keyboard mash, no real words.
96
+ - **Filler-only** = the message contains ONLY mechanic nouns after stripping `content type(s)`, `bundle`, `CMS`, `schema`, `metadata`, `record`, `entity` — e.g. `create a content type bundle for our CMS` names the mechanic, not the domain.
97
+
98
+ **≥1 recognizable noun** → proceed to step 2 → 3. The step 3 proposed name MUST be built from user-message tokens only (`foo laptop bar` → `Laptop`) — do NOT introduce nouns absent from the message; `GovernmentGO`-out-of-nowhere is the hallucination anti-pattern. If the noun set is thin (one word), use that word as-is — do not embellish.
99
+
100
+ **Gibberish OR filler-only** → do NOT auto-proceed. Dispatch `ask_user_tool` with question `Your request "<original message>" doesn't name a content domain. What kind of content type would you like to create (e.g. news, blog, press release, product)?` and options `Cancel` (plus free-text). Free-text reply → treat as fresh `intent`, restart from 1b. `Cancel` → emit `cancelled`, print `Cancelled. No files written.`, `Task Completed`.
101
+
102
+ **Empty-query implication:** if 1c's rebuilt `query` is empty after stripping forbidden tokens (`content type`, `bundle`, `CMS`, `metadata`, `sfdc_cms`, `c__`, `__`), route directly to this gate — do NOT dispatch a blank `search_metadata`, do NOT record `grounding=unavailable`.
103
+
104
+ No verb-parsing on the message otherwise — results + invocation context determine `showFqnOption`. `Cancel` is always present; `Use existing:` is present when a semantically-relevant match exists.
105
+
106
+ Discovery is the user's first chat-visible signal that the skill is doing real work. **Always tell the user what was checked and what was found**, even when zero matches. This is the user's chance to redirect to an existing type before any files are written.
@@ -0,0 +1,85 @@
1
+ # Discovery Query Rules — `metadata-grounding.search_metadata` for ContentTypeBundle
2
+
3
+ **Single source of truth for how the ContentTypeBundle discovery query is constructed.** Owned by `experience-cms-content-type-generate` (referenced from its Step 1c). Also referenced from `experience-cms-content-generate`'s Step 2 — the parent skill should not construct a discovery query itself (delegation drift bug), but if drift happens, the query must still be built by these rules.
4
+
5
+ ## The call
6
+
7
+ ```javascript
8
+ search_metadata({ metadataType: "ContentTypeBundle", query: "<3-5 domain nouns>", limit: 5 })
9
+ ```
10
+
11
+ `search_metadata` alone returns everything discovery (Step 1c/1d) needs — FQN, description, OOTB flag — for all top matches. Do NOT dispatch `query_metadata` per match here: per-row fan-out is N wasted round-trips. `query_metadata` is load-bearing only in Step 1e, on-demand, for the single picked OOTB FQN. See `SKILL.md` § 1c/1e.
12
+
13
+ Server target: `metadata-grounding` — no other server, no SOQL, no `sf project retrieve`, no `sf org list metadata`.
14
+
15
+ ## The `query` value in one line
16
+
17
+ **3-5 English content-domain nouns.** That's it. Not an FQN, not a namespace, not a copy of the user's message.
18
+
19
+ - The `metadataType: "ContentTypeBundle"` argument already tells grounding the kind — the query only carries the domain.
20
+ - Words about the CMS machinery (`content type`, `bundle`, `metadata`, `cms`) belong in `metadataType`, not `query`.
21
+ - Namespaces (`sfdc_cms`, `sfdc_cms__`, `c__`, `<anything>__`) belong in an FQN, not `query`.
22
+ - Years (`2023`, `Q4`), one-off proper names (product SKUs, campaign codenames like `Project Nova`, `Dreamforce 2025 Keynote`), and meta-instructions (`please`, `help me`) don't describe a type shape — drop them.
23
+
24
+ ## The transform — 5 minute mental model
25
+
26
+ **User's message → 3-5 content-domain nouns.** Keep the shape noun (news, article, product, case study, whitepaper, event, webinar, announcement, story, page, blog, press release, FAQ, bio, profile, earnings report), then add 2-4 neighbors along the same real-world axis. Never send a one-word query.
27
+
28
+ If the user pasted an FQN, take ONLY the DeveloperName portion, word-split it, and expand:
29
+ - `sfdc_cms__news` → shape = `news` → query = `news article announcement story headline`
30
+ - `c__PressRelease` → shape = `press release` → query = `press release announcement news statement corporate`
31
+
32
+ ## Concrete tool calls — this is what dispatch should look like
33
+
34
+ Copy the shape, adapt to the domain. Read left-to-right: user message, then the exact `search_metadata` call.
35
+
36
+ | User message | `search_metadata` call |
37
+ |---|---|
38
+ | `create a content type for health insurance` | `search_metadata({ metadataType: "ContentTypeBundle", query: "health insurance policy claim benefit", limit: 5 })` |
39
+ | `set up a bundle for press releases` | `search_metadata({ metadataType: "ContentTypeBundle", query: "press release announcement news statement corporate", limit: 5 })` |
40
+ | `webinar content type with abstract and speakers` | `search_metadata({ metadataType: "ContentTypeBundle", query: "webinar event session speaker agenda", limit: 5 })` |
41
+ | `create content on Q4 2025 product launch` | `search_metadata({ metadataType: "ContentTypeBundle", query: "news article announcement launch product", limit: 5 })` |
42
+ | `create a news content type` | `search_metadata({ metadataType: "ContentTypeBundle", query: "news article announcement story headline", limit: 5 })` |
43
+ | `use sfdc_cms__news for this` | `search_metadata({ metadataType: "ContentTypeBundle", query: "news article announcement story headline", limit: 5 })` |
44
+ | `create content for our upcoming enterprise CRM announcement` | `search_metadata({ metadataType: "ContentTypeBundle", query: "announcement news product launch release", limit: 5 })` |
45
+ | `create content type for Q4 2025 earnings report` | `search_metadata({ metadataType: "ContentTypeBundle", query: "earnings report financial results quarterly revenue", limit: 5 })` |
46
+ | `pet grooming products in our store` | `search_metadata({ metadataType: "ContentTypeBundle", query: "product pet grooming catalog listing", limit: 5 })` |
47
+ | `add something for our upcoming launch` | `search_metadata({ metadataType: "ContentTypeBundle", query: "announcement launch news release event", limit: 5 })` |
48
+ | `blog posts about AI safety` | `search_metadata({ metadataType: "ContentTypeBundle", query: "blog post article author publication", limit: 5 })` |
49
+ | `FAQ page for our support site` | `search_metadata({ metadataType: "ContentTypeBundle", query: "faq question answer support help", limit: 5 })` |
50
+
51
+ Notice: no query contains `sfdc_cms`, `c__`, `__`, `content type`, `bundle`, `metadata`, `cms`, a year, or a one-off proper name. Every query is 3-5 English content-domain nouns.
52
+
53
+ ## Wrong queries — do not dispatch these
54
+
55
+ If your composed query looks like ANY of the following, rebuild it before the tool call.
56
+
57
+ | ❌ Wrong query | Why it's wrong |
58
+ |---|---|
59
+ | `sfdc_cms__news content type` | Namespace + filler. Both belong elsewhere. Correct: `news article announcement story headline`. |
60
+ | `sfdc_cms news content type` | Namespace fragment + filler. Correct: `news article announcement story headline`. |
61
+ | `c__PressRelease bundle` | Namespace + filler. Correct: `press release announcement news statement corporate`. |
62
+ | `news content type` | Filler `content type` — kind is already implied by `metadataType`. Correct: `news article announcement story headline`. |
63
+ | `CMS content type bundle` | All filler, no domain. Correct: fill in domain nouns from the user's message. |
64
+ | `dreamforce 2025 keynote` | One-off proper name + year + no shape noun. Correct: `event keynote announcement session agenda`. |
65
+ | `launch` | Single word, under-recalls. Correct: `announcement launch news release event`. |
66
+
67
+ ## Pre-dispatch check
68
+
69
+ Before calling `search_metadata`, read your composed `query` string. If it contains any of these tokens, rebuild:
70
+
71
+ - `content type`, `contenttype`, `bundle`, `metadata`, `cms`, `record`, `object` — filler; drop.
72
+ - `sfdc_cms`, `sfdc`, `c__`, or any `__` substring, or any `<prefix>__` token — namespace; drop.
73
+ - Years (`2023`, `Q4`, `November`), one-off proper names (SKUs, event codenames), meta-instructions (`please`, `can you`) — noise; drop.
74
+
75
+ Then dispatch. Do not re-dispatch a "corrected" query in the same turn — grounding rate-limits repeat queries; take what it returned and continue to step 1d.
76
+
77
+ ## Grounding is a heuristic, not the org
78
+
79
+ A grounding hit is high-signal (the org very likely has that type). A grounding miss is low-signal (grounding may lag org state). NEVER conclude "the org does not have `<Name>`" from grounding's response, and NEVER print that conclusion at Step 1d. Only Step 1e's `sf project retrieve start` can confirm org presence or absence.
80
+
81
+ ## Related
82
+
83
+ - Full Step 1c rationale and skip-rationalization anti-patterns → `discovery-details.md#1c`
84
+ - Step 1d table + pick-list prompt templates → `../assets/discovery-prompts.md`
85
+ - Step 1e retrieve-and-reconcile → `retrieve-and-reconcile.md`
@@ -0,0 +1,48 @@
1
+ # Step 3b — edit-fields loop
2
+
3
+ Reached when the user picks `Edit fields` at step 3b's initial approval prompt. Each round uses the same two-message pattern as the initial 3b: chat-visible table FIRST, `ask_user_tool` SECOND, in the same turn.
4
+
5
+ ## Message 1 — chat-visible plain text (NOT inside `ask_user_tool`)
6
+
7
+ Reprint the current proposed-fields table so the user sees the field names, types, required flags, AND constraints they are editing. The constraints column is load-bearing — an edit like "make playerName maxLength 257" needs to survive to the next reprint so the user can verify it stuck. Format matches step 3b's initial table and step 7.5's schema summary (see `references/schema-summary-format.md`):
8
+
9
+ ```text
10
+ Current fields for `<contentTypeName>`:
11
+
12
+ | # | API name | Type | Required | Constraints | Title |
13
+ |---|---|---|---|---|---|
14
+ | 1 | `title` | `lightning__textType` | yes | maxLength: 200 | Title |
15
+ | 2 | `body` | `lightning__richTextType` | yes | — | Body |
16
+ | … | … | … | … | … | … |
17
+
18
+ Type your edits below. Examples:
19
+ - Add: "add field matchResults as rich text"
20
+ - Remove: "drop numberOfTeams"
21
+ - Rename: "rename winner to champion"
22
+ - Change: "make finalVenue required"
23
+ - Constrain: "set playerName maxLength to 257"
24
+
25
+ Or pick a button below to approve as shown or cancel.
26
+ ```
27
+
28
+ STRICTLY DO NOT summarize edits as trailing prose ("`playerName` now has `maxLength: 257`") instead of reflecting them in the Constraints column. Trailing prose disappears on the next reprint; the column survives. The table IS the current state.
29
+
30
+ ## Message 2 — same turn, immediately after the table
31
+
32
+ Dispatch `ask_user_tool`:
33
+ - Question: `Type your edits, or pick an option below.`
34
+ - Options: `Approve as shown`, `Cancel`.
35
+
36
+ ## Routing on the tool result — critical, do NOT stop the workflow
37
+
38
+ The `ask_user_tool` tool result will be one of three shapes. Route deterministically:
39
+
40
+ | Tool result | What to do |
41
+ |---|---|
42
+ | Exact string `Approve as shown` (option pick) | Continue to step 4. |
43
+ | Exact string `Cancel` (option pick) | Emit `cancelled` outcome per SKILL.md § Invocation contract, print `Cancelled. No files written.`, stop. |
44
+ | Anything else (free text — the user typed edit instructions) | **Treat the string as edit instructions.** Parse it (add / drop / rename / require / type-change / localizable) and apply to the field list in memory. **Set `userEditedFields = true` agent-internally** — this flag governs step 6's silent-vs-ask branching (see SKILL.md § step 3a and § step 6). Then jump back to the top of this loop (reprint the updated table + the same `Approve as shown / Cancel` prompt). Do NOT ask the user to clarify unless the instruction is genuinely ambiguous (e.g. "make X better"). |
45
+
46
+ Free-text tool result is NOT a stop signal, NOT a "non-actionable response," and NOT a request for the agent to acknowledge before continuing. It IS the user's answer to "what edits would you like?" — apply it and loop.
47
+
48
+ Do NOT skip the table reprint after applying edits — the user must always see the current state before the next prompt.
@@ -0,0 +1,21 @@
1
+ # Pre-deploy schema checklist (agent-internal)
2
+
3
+ Run this check between step 4 (write files) and step 5 (dry-run). Do NOT print to chat. Fix in place and re-check before dispatching step 5a. Following this on the first write avoids spending auto-fix attempts in step 6.
4
+
5
+ Verify against `references/schema-rules.md`:
6
+
7
+ **Root of `schema.json`**
8
+ - Has `title`, `description`, `lightning:type: "lightning__objectType"`, `lightning:mixinTypes: { "sfdc_cms:metadataContent": {} }` (nothing else in mixins), `properties`, `required`, `unevaluatedProperties: false`.
9
+ - Does NOT have `$schema` or `default` at root.
10
+
11
+ **Per field**
12
+ - Every `required` entry matches a `properties` key.
13
+ - Every field has a supported `lightning:type`.
14
+ - No bare `type`, `format`, or `default` keys.
15
+ - Type-specific keys match the per-field rules table in `references/schema-rules.md`.
16
+ - `lightning:uiOptions` is either omitted or has a non-empty `placeholderText` (never `{}`).
17
+ - `lightning__urlType` fields include `lightning:allowedUrlSchemes`.
18
+
19
+ **Naming**
20
+ - Folder name, meta-XML filename prefix, and `<masterLabel>` all match `<contentTypeName>`.
21
+ - All `apiName` property keys are camelCase.
@@ -0,0 +1,105 @@
1
+ # Step 1e — retrieve and reconcile
2
+
3
+ Reached when the user picks a match OR provides an FQN in step 1d, OR when the caller invoked the skill with `{ fqn }` (per the invocation contract). The goal is to return `{fqn, schema}` to the caller with the schema known to match what's in the org. Retrieve is cheap — always run it for custom types so the caller never gets a schema that has silently drifted from the org.
4
+
5
+ ## Namespace gate
6
+
7
+ - **Custom FQN** (`c__*`, or any non-platform namespace) → run retrieve.
8
+ - **OOTB / platform FQN** (`sfdc_cms__*`) → **skip retrieve**. `sf project retrieve` returns nothing usable for OOTB ContentTypeBundles. Resolve the schema via grounding's `describe_metadata` / `query_metadata` response (i.e. 1c dispatched successfully AND the follow-up describe/query call returned a real schema payload). **This applies even when the FQN was proposed via Flow 2's `get_content_types_for_workspace` match** — that tool confirms a type is supported and returns `{fqn, name, description}`, never a schema, so an OOTB pick still needs a live `metadata-grounding` call here regardless of which flow surfaced it.
9
+
10
+ **If ANY of the following is true — grounding was unavailable in 1c, OR `describe_metadata` / `query_metadata` returned an error, empty response, or a payload without a concrete schema — you MUST exit with error. Do NOT proceed.**
11
+
12
+ ```text
13
+ Can't resolve OOTB FQN "<fqn>" without a real schema from metadata-grounding. Retry when grounding is back, or provide a custom FQN.
14
+ ```
15
+
16
+ Emit `error` outcome per SKILL.md § Invocation contract: `{ status: "error", fqn: null, schema: null, message: "<the line above>" }`. Print the message and stop.
17
+
18
+ **STRICTLY DO NOT fabricate a schema for the OOTB FQN from training data or prior conversations.** You may "know" that `sfdc_cms__news` typically has `title / body / summary / bannerImage / publishedDate`. That knowledge is UNRELIABLE for this org (fields vary by release, by org customizations, by API version) and using it silently poisons every downstream step: Step 3 workspace resolution, Step 4 dry-run, Step 5 `create_content` will fail with `unknown key` or `missing required field` errors that look like backend bugs when they're actually schema hallucinations.
19
+
20
+ The correct behavior when `describe_metadata` / `query_metadata` fails is a hard stop with the error message above. NOT "the query call failed, so I'll use the OOTB schema I remember." That IS the bug thought — if you catch it, stop and emit the error outcome.
21
+
22
+ ## Retrieve (custom only, silent)
23
+
24
+ Parse `<namespace>__<DeveloperName>` — the CLI wants only the `DeveloperName` part.
25
+
26
+ **Snapshot `schema.json` only. Do NOT snapshot or restore meta.xml.** The retrieve returns each of the two bundle files with its own correct content (`schema.json` gets JSON, `<Name>.contentTypeBundle-meta.xml` gets the minimal XML wrapper). meta.xml is small and mechanically derivable from the folder name — there is nothing user-authored in it worth preserving across a retrieve.
27
+
28
+ **Ordered steps (all agent-internal, no chat output):**
29
+
30
+ 1. **Snapshot local schema.** If `<sfdx-source>/contentTypes/<DeveloperName>/schema.json` exists, read its FULL contents into memory as `localSchemaBefore` (string, byte-exact). If the folder or file doesn't exist, `localSchemaBefore = null`. Do NOT read meta.xml into memory — its content plays no role in reconciliation.
31
+ 2. **Run retrieve:**
32
+
33
+ ```bash
34
+ sf project retrieve start \
35
+ --metadata ContentTypeBundle:<DeveloperName> \
36
+ --target-org <orgAlias> \
37
+ --json
38
+ ```
39
+
40
+ Runs against the SF CLI default org — different auth path from `metadata-grounding`, so it works even under grounding outage.
41
+
42
+ **Foreign-file retry (macOS):** if retrieve fails with an error mentioning a foreign file inside the bundle folder — most commonly `.DS_Store` (Finder metadata file), also `Thumbs.db` (Windows) or editor swap files — silently `rm -f <sfdx-source>/contentTypes/<DeveloperName>/.DS_Store` (or the offending file) and retry the retrieve ONCE. Do NOT ask the user; the file is OS clutter, not their work. If the retry still fails, surface the raw CLI error.
43
+ 3. **Determine `retrievedFromOrg`.** Parse the retrieve JSON's top-level `result`:
44
+ - `result.success === true` AND `result.files[]` includes an entry for `ContentTypeBundle` `<DeveloperName>` → **retrieved**. Read the post-retrieve `schema.json` into `retrievedSchema`.
45
+ - `result.success === true` with no `ContentTypeBundle` entry, OR an explicit `"cannot be found"` error in `messages[]` / `warnings[]` → **not retrieved** (org doesn't have this type).
46
+ - Any other failure → surface the raw CLI error and exit with `error` outcome.
47
+ 4. **Detect drift (only when both local and org copies exist).** **Compare `schema.json` ONLY.** Drift is present when `localSchemaBefore !== retrievedSchema` as parsed JSON trees (key order and whitespace don't count). Byte-inequality with structural equivalence = no drift.
48
+
49
+ STRICTLY DO NOT read the post-retrieve `schema.json` and diff it against itself — that's the "no drift" bug. `localSchemaBefore` (the pre-retrieve in-memory snapshot) is the only correct left-hand side.
50
+
51
+ ## Reconciliation table
52
+
53
+ Comparing `localSchemaBefore` (snapshot from step 1) against `retrievedSchema` (post-retrieve from step 2). meta.xml handling is trivial — whatever `sf project retrieve` wrote is correct; there is no snapshot to restore.
54
+
55
+ | Local `schema.json`? (before retrieve) | Retrieved from org? | Action |
56
+ |---|---|---|
57
+ | No (`localSchemaBefore === null`) | Yes | Retrieve wrote both files (`schema.json` + `<Name>.contentTypeBundle-meta.xml`) — accept as-is. Return `{fqn, schema=retrievedSchema}`. |
58
+ | Yes | Yes, and `localSchemaBefore` semantically equals `retrievedSchema` (JSON tree compare) | No drift. The retrieve rewrote meta.xml to the org's copy of the minimal XML wrapper; that's fine, leave it. Return `{fqn, schema=localSchemaBefore}`. |
59
+ | Yes | Yes, and `localSchemaBefore` differs from `retrievedSchema` in the parsed JSON tree | **Drift detected on `schema.json`.** Retrieve has already overwritten local `schema.json` — you MUST hold `localSchemaBefore` in memory through the drift prompt so `Deploy local` and `Cancel` can restore it. Go to "Drift prompt" below. |
60
+ | Yes | No (retrieve confirmed empty for this type) | Retrieve didn't touch the folder, so local is intact. **Retrieve is what confirmed org absence** — grounding's earlier response is NOT sufficient to conclude this; only a clean `sf project retrieve start` return justifies the "not in org" claim. **First print the step 7.5 schema summary of the LOCAL schema** (see `references/schema-summary-format.md`) so the user sees what they're about to deploy — a local-only bundle is the most likely case to be stale or hand-edited. Then offer to deploy: `The bundle "<Name>" exists locally but not in the org (confirmed via retrieve). Deploy it? Yes / Cancel`. On yes → skip to step 5 (dry-run) → step 7 (deploy). On cancel → emit `not_deployed` outcome per the return contract in SKILL.md § Invocation contract: `{ status: "not_deployed", fqn, schema: null, message: 'Content type "<fqn>" exists locally but is not deployed to <org>. Deploy it before creating content.' }`. Print the message and stop — do NOT run step 7.5 or step 8. |
61
+ | No | No | FQN doesn't resolve anywhere. Print `FQN "<fqn>" not found locally or in the org.` and re-dispatch the step 1d pick with an added `Try a different FQN` option, or `Cancel`. |
62
+
63
+ ## Drift prompt (local ≠ org)
64
+
65
+ **Precondition state at this point:** disk currently holds the org copy of `schema.json` (retrieve overwrote it). `localSchemaBefore` holds the pre-retrieve local `schema.json` bytes in memory. meta.xml is whatever retrieve wrote — it's not tracked and doesn't need to be. The three routing branches below rely on the `localSchemaBefore` snapshot.
66
+
67
+ **Two outputs in this order, same as step 1d — chat message first, then short tool call.** The diff details go in the chat message. The tool call is ONE short question and 3 options. Do NOT pack the field list into `ask_user_tool`'s `question` field — it collapses into an unreadable paragraph.
68
+
69
+ **Output 1 — chat message (plain markdown, NOT the tool call):**
70
+
71
+ ```text
72
+ Schema drift detected in "<fqn>". Differences (org vs local):
73
+
74
+ | Change | Field | Local | Org |
75
+ |---|---|---|---|
76
+ | renamed | `summary1` → `summary` | title "Summary1" | title "Summary" |
77
+ | required-list | — | ["headline", "summary1"] | ["headline", "summary"] |
78
+ ```
79
+
80
+ Use whatever concise diff shape fits the actual delta — added/removed rows, renamed rows, required-list changes, type changes. Keep it factual, no editorializing. If the diff is small (1–2 changes), a bulleted list is fine:
81
+
82
+ ```text
83
+ Schema drift detected in "<fqn>". Differences (org vs local):
84
+
85
+ • renamed: `summary1` (local) → `summary` (org)
86
+ • required list changed: local has "summary1", org has "summary"
87
+ ```
88
+
89
+ **Output 2 — `ask_user_tool` (short question, 3 options):**
90
+
91
+ - **question**: `Changes detected between local and org. What would you like to do?`
92
+ - **options** (fixed order):
93
+ 1. `Deploy local to org`
94
+ 2. `Overwrite local with org`
95
+ 3. `Cancel`
96
+
97
+ Routing:
98
+
99
+ - `Deploy local to org` → **restore the schema snapshot first**: write `localSchemaBefore` back to `<sfdx-source>/contentTypes/<DeveloperName>/schema.json`. Leave meta.xml alone (retrieve wrote the correct minimal XML). Then route to step 5 (dry-run) → step 7 (deploy). On deploy success, emit `success` outcome with `{fqn, schema=localSchemaBefore}`.
100
+ - `Overwrite local with org` → nothing to do on disk (retrieve already wrote both files correctly). Emit `success` outcome with `{fqn, schema=retrievedSchema}`.
101
+ - `Cancel` → **restore the schema snapshot**: write `localSchemaBefore` back to `<sfdx-source>/contentTypes/<DeveloperName>/schema.json`, restoring the pre-retrieve `schema.json`. Leave meta.xml alone. Then emit `cancelled` outcome per SKILL.md § Invocation contract: `{ status: "cancelled", fqn: null, schema: null, message: "Cancelled. No files written." }`. Print the message and stop. Do NOT tell the user "no files written" without actually restoring `schema.json` — that message is a promise about the disk state of the schema.
102
+
103
+ ## Terminal state
104
+
105
+ On any successful terminal state, return `{fqn, schema}` to the caller and continue to step 7.5 (schema summary), then step 8 (trailing prompt gate). No proposal-of-fields, no re-validation of existing types, no "approve as-is" prompt — the reconciled schema IS the source of truth. This skill is create-only and reconcile-only for existing types; do not modify field shapes.