pi-apexlang 0.2.2 → 0.4.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 (239) hide show
  1. package/README.md +22 -5
  2. package/THIRD_PARTY_NOTICES.md +2 -2
  3. package/UPSTREAM.json +2 -2
  4. package/docs/assets/overview.en.png +0 -0
  5. package/docs/assets/overview.en.svg +89 -0
  6. package/docs/assets/overview.uk.png +0 -0
  7. package/docs/assets/overview.uk.svg +89 -0
  8. package/docs/en/README.md +41 -0
  9. package/docs/en/architecture.md +73 -0
  10. package/docs/en/getting-started.md +77 -0
  11. package/docs/en/maintenance.md +83 -0
  12. package/docs/en/tool-reference.md +85 -0
  13. package/docs/uk/README.md +41 -0
  14. package/docs/uk/architecture.md +73 -0
  15. package/docs/uk/getting-started.md +77 -0
  16. package/docs/uk/maintenance.md +83 -0
  17. package/docs/uk/tool-reference.md +85 -0
  18. package/extensions/apexlang/index.ts +19 -10
  19. package/extensions/lib/apexlang-cli.d.mts +1 -2
  20. package/extensions/lib/apexlang-cli.mjs +18 -13
  21. package/extensions/lib/apexlang-local-validate.mjs +83 -0
  22. package/extensions/lib/apexlang-local-validator.py +116 -0
  23. package/extensions/lib/apexlang-runtime-validate.mjs +76 -0
  24. package/package.json +2 -1
  25. package/skills/apexlang/APEXlang Skills and Workflows.md +325 -0
  26. package/skills/apexlang/README.md +6 -6
  27. package/skills/apexlang/RELEASE-NOTES.md +2 -2
  28. package/skills/apexlang/SKILL.md +24 -19
  29. package/skills/apexlang/agents/openai.yaml +4 -3
  30. package/skills/apexlang/assets/apex-generation/components.registry.json +1001 -16
  31. package/skills/apexlang/assets/apexlang/domains-catalog.json +6 -1
  32. package/skills/apexlang/assets/component-attributes.json +1924 -180
  33. package/skills/apexlang/assets/component-policies.json +19 -10
  34. package/skills/apexlang/assets/contracts/grammar-semantic-rules.json +134 -0
  35. package/skills/apexlang/assets/contracts/package-layers.json +56 -0
  36. package/skills/apexlang/assets/contracts/page-construction-packs.json +154 -0
  37. package/skills/apexlang/assets/contracts/page-patterns.json +119 -0
  38. package/skills/apexlang/assets/grammar/apexlang.ebnf +11743 -0
  39. package/skills/apexlang/assets/indexes/font-apex-icon-index.json +1699 -0
  40. package/skills/apexlang/assets/routing-catalog-main.json +11 -10
  41. package/skills/apexlang/assets/routing-load-policy.json +35 -9
  42. package/skills/apexlang/assets/rules-mapping.json +110 -2
  43. package/skills/apexlang/assets/rules.catalog.json +1298 -0
  44. package/skills/apexlang/assets/validator-fix-recipes.json +372 -17
  45. package/skills/apexlang/assets/workspace-intelligence.json +7 -10
  46. package/skills/apexlang/manifest.json +268 -12
  47. package/skills/apexlang/references/domains/README.md +1 -1
  48. package/skills/apexlang/references/domains/page-components/buttons.md +2 -2
  49. package/skills/apexlang/references/domains/page-components/page-items.md +3 -3
  50. package/skills/apexlang/references/domains/page-components/regions/region-display-selector/workflow-region-display-selector.md +69 -0
  51. package/skills/apexlang/references/domains/page-components/regions.md +1 -0
  52. package/skills/apexlang/references/domains/template-components/comments.md +61 -0
  53. package/skills/apexlang/references/domains/template-components/registry.md +2 -0
  54. package/skills/apexlang/references/domains/template-components/templates.md +47 -0
  55. package/skills/apexlang/references/domains/template-components/timeline.md +51 -0
  56. package/skills/apexlang/references/ops/one-message-router-contract.md +2 -2
  57. package/skills/apexlang/references/ops/runtime-gates/01-direct-sqlcl-import.md +1 -1
  58. package/skills/apexlang/references/ops/runtime-gates/02-direct-sqlcl-validate-gate.md +1 -1
  59. package/skills/apexlang/references/ops/runtime-gates.md +17 -16
  60. package/skills/apexlang/references/ops/sqlcl-agents/00-connection-gate.md +7 -7
  61. package/skills/apexlang/references/ops/sqlcl.md +7 -8
  62. package/skills/apexlang/references/policies/compiler-prop-map.md +6 -4
  63. package/skills/apexlang/references/policies/governance/00-governance.md +2 -2
  64. package/skills/apexlang/references/policies/memory-bank/00-guard/ai.guard.md +126 -922
  65. package/skills/apexlang/references/policies/memory-bank/20-data/apex.logic.md +3 -3
  66. package/skills/apexlang/references/policies/memory-bank/20-data/db.connection.md +9 -11
  67. package/skills/apexlang/references/policies/memory-bank/30-pages/apex.cards-page.md +95 -0
  68. package/skills/apexlang/references/policies/memory-bank/30-pages/apex.page.md +2 -2
  69. package/skills/apexlang/references/policies/memory-bank/30-pages/apex.smart-filter-search.md +145 -74
  70. package/skills/apexlang/references/policies/memory-bank/40-components/apex.region-interactions.md +9 -2
  71. package/skills/apexlang/references/policies/memory-bank/40-components/apex.region-media.md +6 -5
  72. package/skills/apexlang/references/policies/memory-bank/systemPatterns.md +9 -1
  73. package/skills/apexlang/references/workflows/apex-generation/agents/20-agent-draft.md +51 -232
  74. package/skills/apexlang/references/workflows/apex-generation/agents/30-agent-critique.md +49 -449
  75. package/skills/apexlang/references/workflows/apex-generation/agents/40-agent-revision.md +38 -238
  76. package/skills/apexlang/references/workflows/apex-generation/nlu/nlu-routing-test-matrix.md +8 -2
  77. package/skills/apexlang/references/workflows/apex-generation/nlu/nlu-routing-validation-notes.md +19 -3
  78. package/skills/apexlang/references/workflows/apex-generation/registry.md +15 -1
  79. package/skills/apexlang/references/workflows/apex-generation/templates.md +1 -1
  80. package/skills/apexlang/references/workflows/apex-generation.md +1 -1
  81. package/skills/apexlang/references/workflows/apexlang/apexlang-execution-model.md +5 -5
  82. package/skills/apexlang/references/workflows/apexlang/prompt-contracts.md +81 -10
  83. package/skills/apexlang/references/workflows/apexlang/workflow-create-app-from-fr-and-model.md +18 -7
  84. package/skills/apexlang/release-notes.json +156 -0
  85. package/skills/apexlang/runtime/grammar_contract.mjs +1166 -0
  86. package/skills/apexlang/runtime/internal/python/format_apexlang.py +210 -0
  87. package/skills/apexlang/runtime/internal/python/validate_apexlang.py +14142 -5843
  88. package/skills/apexlang/runtime/internal/python/validator_common.py +2 -0
  89. package/skills/apexlang/runtime/lib/catalogs.mjs +1026 -0
  90. package/skills/apexlang/runtime/lib/common.mjs +35 -0
  91. package/skills/apexlang/runtime/runtime.bundle.mjs +457 -57
  92. package/skills/apexlang/templates/items/combobox/combobox.explicit-defaults.md +1 -1
  93. package/skills/apexlang/templates/items/date-picker/date-picker._common.md +1 -1
  94. package/skills/apexlang/templates/items/date-picker/date-picker.explicit-defaults.md +1 -1
  95. package/skills/apexlang/templates/items/file-upload/file-upload._common.md +30 -24
  96. package/skills/apexlang/templates/items/file-upload/file-upload.compact-template.md +6 -0
  97. package/skills/apexlang/templates/items/file-upload/file-upload.explicit-defaults.md +23 -8
  98. package/skills/apexlang/templates/items/file-upload/file-upload.minimal.md +6 -0
  99. package/skills/apexlang/templates/items/hidden-item/hidden-item._common.md +1 -1
  100. package/skills/apexlang/templates/items/hidden-item/hidden-item.explicit-defaults.md +1 -1
  101. package/skills/apexlang/templates/items/items._common.md +3 -2
  102. package/skills/apexlang/templates/items/list-manager/list-manager.explicit-defaults.md +1 -1
  103. package/skills/apexlang/templates/items/markdown-editor/markdown-editor._common.md +1 -1
  104. package/skills/apexlang/templates/items/markdown-editor/markdown-editor.explicit-defaults.md +1 -1
  105. package/skills/apexlang/templates/items/number-field/number-field._common.md +8 -6
  106. package/skills/apexlang/templates/items/number-field/number-field.explicit-defaults.md +12 -2
  107. package/skills/apexlang/templates/items/popup-lov/popup-lov.explicit-defaults.md +1 -1
  108. package/skills/apexlang/templates/items/rich-text-editor/rich-text-editor._common.md +1 -1
  109. package/skills/apexlang/templates/items/rich-text-editor/rich-text-editor.explicit-defaults.md +1 -1
  110. package/skills/apexlang/templates/items/select-many/standard.md +1 -1
  111. package/skills/apexlang/templates/items/shuttle/shuttle.explicit-defaults.md +1 -1
  112. package/skills/apexlang/templates/items/switch/switch._common.md +8 -8
  113. package/skills/apexlang/templates/items/switch/switch.explicit-defaults.md +1 -1
  114. package/skills/apexlang/templates/items/text-area/text-area._common.md +2 -3
  115. package/skills/apexlang/templates/items/text-area/text-area.explicit-defaults.md +1 -1
  116. package/skills/apexlang/templates/items/text-area/text-area.required-length.md +1 -3
  117. package/skills/apexlang/templates/items/text-autocomplete/text-autocomplete.explicit-defaults.md +1 -1
  118. package/skills/apexlang/templates/items/text-autocomplete/text-autocomplete.required-length.md +1 -3
  119. package/skills/apexlang/templates/items/text-field/text-field._common.md +2 -3
  120. package/skills/apexlang/templates/items/text-field/text-field.explicit-defaults.md +1 -1
  121. package/skills/apexlang/templates/items/text-field/text-field.required-length.md +1 -3
  122. package/skills/apexlang/templates/page-examples/faceted-search/faceted-search.example.md +0 -5
  123. package/skills/apexlang/templates/page-examples/form-page/form-page.example.md +0 -9
  124. package/skills/apexlang/templates/page-examples/interactive-report-page/interactive-report-page.example.md +2 -2
  125. package/skills/apexlang/templates/page-examples/login-page/login-page.example.md +0 -8
  126. package/skills/apexlang/templates/page-examples/task-definition-initiated-tasks-page/task-definition-initiated-tasks-page.example.md +7 -17
  127. package/skills/apexlang/templates/page-examples/task-definition-my-tasks-page/task-definition-my-tasks-page.example.md +7 -65
  128. package/skills/apexlang/templates/page-examples/task-definition-task-details/task-definition-task-details.example.md +38 -201
  129. package/skills/apexlang/templates/region-components/calendar/calendar.custom-styling.md +3 -1
  130. package/skills/apexlang/templates/region-components/calendar/calendar.faceted-search.md +3 -2
  131. package/skills/apexlang/templates/region-components/cards/README.md +12 -4
  132. package/skills/apexlang/templates/region-components/cards/cards._common.md +176 -37
  133. package/skills/apexlang/templates/region-components/cards/cards._index.md +2 -1
  134. package/skills/apexlang/templates/region-components/cards/cards.refresh-after-dialog.md +46 -0
  135. package/skills/apexlang/templates/region-components/cards/cards.refresh-on-change.md +58 -0
  136. package/skills/apexlang/templates/region-components/cards/cards.rest-source.md +54 -18
  137. package/skills/apexlang/templates/region-components/cards/cards.standard.md +37 -21
  138. package/skills/apexlang/templates/region-components/chart/chart._series._common.md +1 -4
  139. package/skills/apexlang/templates/region-components/chart/chart.pie.md +6 -2
  140. package/skills/apexlang/templates/region-components/chart/chart.status-meter-gauge.md +1 -1
  141. package/skills/apexlang/templates/region-components/chart/chart.stock.md +2 -0
  142. package/skills/apexlang/templates/region-components/chart/features/chart.feature-chart-links.md +9 -7
  143. package/skills/apexlang/templates/region-components/faceted-search/faceted-search._common.md +1 -14
  144. package/skills/apexlang/templates/region-components/form/form._items._common.md +1 -5
  145. package/skills/apexlang/templates/region-components/interactive-report/interactive-report._columns._common.md +2 -6
  146. package/skills/apexlang/templates/region-components/interactive-report/interactive-report.page-header-actions.md +1 -7
  147. package/skills/apexlang/templates/region-components/list/README.md +1 -1
  148. package/skills/apexlang/templates/region-components/list/list._common.md +1 -1
  149. package/skills/apexlang/templates/region-components/map/map.backgrounds.md +12 -10
  150. package/skills/apexlang/templates/region-components/map/map.layer.extruded-polygons.md +16 -12
  151. package/skills/apexlang/templates/region-components/map/map.layer.heat-map.md +10 -7
  152. package/skills/apexlang/templates/region-components/map/map.layer.lines.md +10 -8
  153. package/skills/apexlang/templates/region-components/map/map.layer.polygons.md +21 -18
  154. package/skills/apexlang/templates/region-components/map/map.region.background-custom.md +25 -19
  155. package/skills/apexlang/templates/region-components/map/map.region.background-shared.md +19 -13
  156. package/skills/apexlang/templates/region-components/map/map.region.bbox-sql.md +13 -13
  157. package/skills/apexlang/templates/region-components/map/map.region.bbox-static.md +2 -2
  158. package/skills/apexlang/templates/region-components/map/map.region.browser-location.md +3 -7
  159. package/skills/apexlang/templates/region-components/map/map.region.init-position-sql.md +7 -7
  160. package/skills/apexlang/templates/region-components/region-display-selector/README.md +5 -2
  161. package/skills/apexlang/templates/region-components/region-display-selector/region-display-selector._common.md +60 -7
  162. package/skills/apexlang/templates/region-components/region-display-selector/region-display-selector._index.md +4 -1
  163. package/skills/apexlang/templates/region-components/region-display-selector/region-display-selector.permutations.md +55 -0
  164. package/skills/apexlang/templates/region-components/region-display-selector/region-display-selector.standard.md +91 -5
  165. package/skills/apexlang/templates/region-components/smart-filter-search/README.md +6 -2
  166. package/skills/apexlang/templates/region-components/smart-filter-search/smart-filter-search._common.md +107 -46
  167. package/skills/apexlang/templates/region-components/smart-filter-search/smart-filter-search._index.md +4 -3
  168. package/skills/apexlang/templates/region-components/smart-filter-search/smart-filter-search.standard.md +33 -17
  169. package/skills/apexlang/templates/shared-components/component-settings/component-settings._common.md +1 -0
  170. package/skills/apexlang/templates/shared-components/component-settings.example.md +1 -0
  171. package/skills/apexlang/templates/shared-components/email-templates.example.md +1 -1
  172. package/skills/apexlang/templates/shared-components/rest-data-sources/example_rest_data_source.example.md +40 -46
  173. package/skills/apexlang/templates/shared-components/task-definitions/action-task.example.md +2 -30
  174. package/skills/apexlang/templates/shared-components/task-definitions/approve-request.example.md +3 -31
  175. package/skills/apexlang/templates/template-components/README.md +27 -15
  176. package/skills/apexlang/templates/template-components/avatar/README.md +56 -0
  177. package/skills/apexlang/templates/template-components/avatar/avatar._common.md +149 -0
  178. package/skills/apexlang/templates/template-components/avatar/avatar._index.md +42 -0
  179. package/skills/apexlang/templates/template-components/{avatar._template_options.md → avatar/avatar._template_options.md} +1 -1
  180. package/skills/apexlang/templates/template-components/avatar/avatar.report-icon.md +48 -0
  181. package/skills/apexlang/templates/template-components/avatar/avatar.report-image-url-column.md +62 -0
  182. package/skills/apexlang/templates/template-components/avatar/avatar.report-initials.md +54 -0
  183. package/skills/apexlang/templates/template-components/badge/README.md +38 -0
  184. package/skills/apexlang/templates/template-components/badge/badge._common.md +183 -0
  185. package/skills/apexlang/templates/template-components/badge/badge._index.md +20 -0
  186. package/skills/apexlang/templates/template-components/badge/badge.report-link.md +66 -0
  187. package/skills/apexlang/templates/template-components/badge/badge.report-minimal.md +76 -0
  188. package/skills/apexlang/templates/template-components/comments/README.md +20 -0
  189. package/skills/apexlang/templates/template-components/comments/comments._common.md +43 -0
  190. package/skills/apexlang/templates/template-components/comments/comments._index.md +25 -0
  191. package/skills/apexlang/templates/template-components/{comments._template_options.md → comments/comments._template_options.md} +2 -1
  192. package/skills/apexlang/templates/template-components/comments/comments.apex-task-comments.md +77 -0
  193. package/skills/apexlang/templates/template-components/comments/comments.partial-column.md +120 -0
  194. package/skills/apexlang/templates/template-components/comments/comments.permutations.md +425 -0
  195. package/skills/apexlang/templates/template-components/content-row/README.md +1 -1
  196. package/skills/apexlang/templates/template-components/content-row/content-row._common.md +1 -1
  197. package/skills/apexlang/templates/template-components/content-row/content-row._index.md +1 -1
  198. package/skills/apexlang/templates/template-components/content-row/content-row._template_options.md +1 -1
  199. package/skills/apexlang/templates/template-components/flexbox-container/README.md +9 -0
  200. package/skills/apexlang/templates/template-components/flexbox-container/flexbox-container._index.md +13 -0
  201. package/skills/apexlang/templates/template-components/media-list/README.md +100 -0
  202. package/skills/apexlang/templates/template-components/media-list/media-list._common.md +174 -0
  203. package/skills/apexlang/templates/template-components/media-list/media-list._index.md +50 -0
  204. package/skills/apexlang/templates/template-components/{media-list._template_options.md → media-list/media-list._template_options.md} +8 -2
  205. package/skills/apexlang/templates/template-components/media-list/media-list.partial.md +50 -0
  206. package/skills/apexlang/templates/template-components/media-list/media-list.report-avatar-badge.md +76 -0
  207. package/skills/apexlang/templates/template-components/media-list/media-list.report-base.md +71 -0
  208. package/skills/apexlang/templates/template-components/media-list/media-list.report-grouped.md +40 -0
  209. package/skills/apexlang/templates/template-components/media-list/media-list.report-layouts.md +50 -0
  210. package/skills/apexlang/templates/template-components/media-list/media-list.report-link.md +53 -0
  211. package/skills/apexlang/templates/template-components/metric-card/README.md +19 -4
  212. package/skills/apexlang/templates/template-components/metric-card/metric-card._common.md +122 -29
  213. package/skills/apexlang/templates/template-components/metric-card/metric-card._index.md +6 -0
  214. package/skills/apexlang/templates/template-components/metric-card/metric-card._template_options.md +47 -2
  215. package/skills/apexlang/templates/template-components/metric-card/metric-card.partial-minimal.md +51 -0
  216. package/skills/apexlang/templates/template-components/metric-card/metric-card.permutations.md +56 -0
  217. package/skills/apexlang/templates/template-components/metric-card/metric-card.report-avatar-badge.md +98 -0
  218. package/skills/apexlang/templates/template-components/metric-card/metric-card.report-grouping-selection.md +131 -0
  219. package/skills/apexlang/templates/template-components/metric-card/metric-card.report-link.md +66 -0
  220. package/skills/apexlang/templates/template-components/metric-card/metric-card.report-minimal.md +97 -0
  221. package/skills/apexlang/templates/template-components/template-components.registry.json +90 -1
  222. package/skills/apexlang/templates/template-components/timeline/README.md +19 -0
  223. package/skills/apexlang/templates/template-components/timeline/timeline._common.md +43 -0
  224. package/skills/apexlang/templates/template-components/timeline/timeline._index.md +24 -0
  225. package/skills/apexlang/templates/template-components/{timeline._template_options.md → timeline/timeline._template_options.md} +2 -1
  226. package/skills/apexlang/templates/template-components/timeline/timeline.permutations.md +399 -0
  227. package/skills/apexlang/templates/template-components/timeline/timeline.report-sample-data.md +168 -0
  228. package/skills/apexlang/templates/workspace-components/credentials/credentials-for-open-ai.example.md +1 -1
  229. package/skills/apexlang/templates/workspace-components/generative-ai-services/open-ai.example.md +8 -6
  230. package/skills/apexlang/tools/apexctl.mjs +167 -4
  231. package/skills/apexlang/tools/compiler-truth-audit.mjs +95 -63
  232. package/skills/apexlang/tools/query-valid-props-context.mjs +30 -0
  233. package/skills/apexlang/tools/query-valid-props-normalize.mjs +1 -0
  234. package/skills/apexlang/tools/query-valid-props-runtime.mjs +13 -8
  235. package/skills/apexlang/tools/query-valid-props-semantics.mjs +391 -0
  236. package/skills/apexlang/tools/query-valid-props-template-components.mjs +15 -1
  237. package/skills/apexlang/tools/query-valid-props.mjs +198 -216
  238. /package/skills/apexlang/templates/template-components/{badge._template_options.md → badge/badge._template_options.md} +0 -0
  239. /package/skills/apexlang/templates/template-components/{flexbox-container._template_options.md → flexbox-container/flexbox-container._template_options.md} +0 -0
@@ -15,6 +15,7 @@ import {
15
15
  apexlangToolPath,
16
16
  collectFiles,
17
17
  ensureDir,
18
+ normalizeApxLineEndings,
18
19
  readJson,
19
20
  removeDir,
20
21
  runCommand,
@@ -154,12 +155,12 @@ async function cleanupDraftRun(stagingRunRoot) {
154
155
  }
155
156
 
156
157
  /**
157
- * Public workspace probe that discovers app, metadata, requirement, and explicit DB/workspace prompt context.
158
+ * Public workspace probe that discovers app, metadata, requirements, and DB/runtime prompt context.
158
159
  */
159
160
 
160
161
 
161
162
  /**
162
- * Default discovery policy for app roots, metadata files, bounded scans, and explicit DB/workspace prompts.
163
+ * Default discovery policy for app roots, metadata files, bounded scans, and discovery-first DB prompts.
163
164
  */
164
165
  const DEFAULT_CONFIG = {
165
166
  app_discovery: {
@@ -190,28 +191,26 @@ const DEFAULT_CONFIG = {
190
191
  allowed_extensions: [".json", ".yaml", ".yml", ".md", ".sql", ".xml"]
191
192
  },
192
193
  db_prompt_flow: {
193
- prompt_mode: "explicit_db_connection_and_workspace_flow",
194
+ prompt_mode: "discovery_first_connection_flow",
194
195
  interactive_only: true,
195
196
  discovery_steps: ["inspect_offline_schema_registry", "scan_saved_sqlcl_connections"],
196
197
  metadata_preference: "prefer_authoritative_offline_context",
197
- required_live_inputs: ["db_connection_name", "db_context.workspace.name"],
198
- auto_bind_single_saved_connection: false,
198
+ required_live_inputs: ["db_connection_name"],
199
+ auto_bind_single_saved_connection: true,
199
200
  manual_entry: {
200
201
  input_name: "db_connection_name",
201
- companion_input_name: "apex_workspace_name",
202
- workspace_context_field: "db_context.workspace.name",
203
- prompt: "Provide db_connection_name and the corresponding APEX workspace name for this workflow."
202
+ prompt: "Provide db_connection_name because no usable saved SQLcl connection could be resolved."
204
203
  },
205
204
  workspace_prompt: {
206
205
  input_name: "apex_workspace_name",
207
206
  context_field: "db_context.workspace.name",
208
- prompt: "Provide the APEX workspace name that corresponds to db_connection_name."
207
+ prompt: "Choose the workspace reported by the active runtime when it cannot resolve a unique workspace."
209
208
  },
210
209
  multiple_connection_prompt: {
211
210
  source: "saved_sqlcl_connections",
212
211
  prompt_mode: "select_from_list",
213
212
  empty_list_prompt:
214
- "No saved SQLcl connections were found. Provide db_connection_name and the corresponding APEX workspace name if live DB context is still required, or continue only with authoritative offline metadata."
213
+ "No saved SQLcl connections were found. Provide db_connection_name if live DB context is still required, or continue only with authoritative offline metadata."
215
214
  },
216
215
  offline_override: {
217
216
  prompt_mode: "explicit_opt_in",
@@ -223,7 +222,7 @@ const DEFAULT_CONFIG = {
223
222
  disables_apex_import: true
224
223
  },
225
224
  workspace_selection: {
226
- prompt_mode: "explicit_workspace_name_required",
225
+ prompt_mode: "runtime_ambiguity_or_new_app_only",
227
226
  persistence: "session_context_only",
228
227
  context_path: "APEXLANG_OUTPUT_ROOT/context-resolution.json",
229
228
  context_field: "db_context.workspace",
@@ -1740,20 +1739,20 @@ function normalizeRuntimeProvider(value = DEFAULT_RUNTIME_PROVIDER) {
1740
1739
  return DEFAULT_RUNTIME_PROVIDER;
1741
1740
  }
1742
1741
 
1743
- function decodeHtmlEntities(value = "") {
1742
+ export function decodeHtmlEntities(value = "") {
1744
1743
  return String(value)
1745
1744
  .replace(/ /gi, " ")
1746
- .replace(/&/gi, "&")
1747
1745
  .replace(/&lt;/gi, "<")
1748
1746
  .replace(/&gt;/gi, ">")
1749
1747
  .replace(/&quot;/gi, "\"")
1750
- .replace(/&#39;/gi, "'");
1748
+ .replace(/&#39;/gi, "'")
1749
+ .replace(/&amp;/gi, "&");
1751
1750
  }
1752
1751
 
1753
- function extractTextFromHtml(html = "") {
1752
+ export function extractTextFromHtml(html = "") {
1754
1753
  return decodeHtmlEntities(
1755
1754
  String(html)
1756
- .replace(/<script\b[^>]*>[\s\S]*?<\/script>/gi, " ")
1755
+ .replace(/<script\b[^>]*>[\s\S]*?<\/script\b[^>]*>/gi, " ")
1757
1756
  .replace(/<style\b[^>]*>[\s\S]*?<\/style>/gi, " ")
1758
1757
  .replace(RUNTIME_TEXT_TAG_STRIP_PATTERN, " ")
1759
1758
  .replace(/\s+/g, " ")
@@ -1802,22 +1801,22 @@ function safeArtifactName(prefix, detail, suffix) {
1802
1801
  return `${sanitizeArtifactFilePart(prefix)}-${sanitizeArtifactFilePart(detail)}.${suffix}`;
1803
1802
  }
1804
1803
 
1805
- async function requestRuntimeUrl(targetUrl, { maxRedirects = 5 } = {}) {
1804
+ export async function requestRuntimeUrl(targetUrl, { maxRedirects = 5, requestImpl } = {}) {
1806
1805
  const visited = [];
1807
1806
 
1808
1807
  async function execute(currentUrl, redirectsRemaining) {
1809
1808
  const parsedUrl = new URL(currentUrl);
1810
1809
  const client = parsedUrl.protocol === "https:" ? https : http;
1811
1810
  const result = await new Promise((resolve, reject) => {
1812
- const req = client.request(
1811
+ const request = requestImpl ?? client.request.bind(client);
1812
+ const req = request(
1813
1813
  parsedUrl,
1814
1814
  {
1815
1815
  method: "GET",
1816
1816
  headers: {
1817
1817
  "user-agent": "apexctl-runtime-verifier/1.0",
1818
1818
  accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
1819
- },
1820
- rejectUnauthorized: false
1819
+ }
1821
1820
  },
1822
1821
  (res) => {
1823
1822
  const chunks = [];
@@ -2082,7 +2081,8 @@ async function verifyRuntimeUiWithHttpFallback({
2082
2081
  runtimePageUrl = "",
2083
2082
  pageId = "",
2084
2083
  artifactDir = DEFAULT_RUNTIME_VERIFY_ARTIFACT_DIR,
2085
- runCommandImpl
2084
+ runCommandImpl,
2085
+ requestRuntimeUrlImpl = requestRuntimeUrl
2086
2086
  }) {
2087
2087
  const baseUrlResolution = runtimePageUrl
2088
2088
  ? { status: "pass", runtimeBaseUrl: "", checkedCandidates: [] }
@@ -2179,7 +2179,7 @@ async function verifyRuntimeUiWithHttpFallback({
2179
2179
  }
2180
2180
 
2181
2181
  try {
2182
- const response = await requestRuntimeUrl(target.runtimeUrl);
2182
+ const response = await requestRuntimeUrlImpl(target.runtimeUrl);
2183
2183
  const htmlArtifactPath = path.join(
2184
2184
  artifactDir,
2185
2185
  safeArtifactName(`runtime-page-${targetsResult.appIdentity.applicationId || "app"}`, String(target.pageId), "html")
@@ -2271,6 +2271,7 @@ async function verifyRuntimeUiWithHttpFallback({
2271
2271
  export async function verifyRuntimeUi(options = {}) {
2272
2272
  const deps = {
2273
2273
  runCommand,
2274
+ requestRuntimeUrl,
2274
2275
  verifyRuntimeWithChromeDevtools: null,
2275
2276
  ...options._deps
2276
2277
  };
@@ -2314,7 +2315,8 @@ export async function verifyRuntimeUi(options = {}) {
2314
2315
  runtimePageUrl: options.runtimePageUrl,
2315
2316
  pageId: options.pageId,
2316
2317
  artifactDir,
2317
- runCommandImpl: deps.runCommand
2318
+ runCommandImpl: deps.runCommand,
2319
+ requestRuntimeUrlImpl: deps.requestRuntimeUrl
2318
2320
  });
2319
2321
  result.payload.runtime_verification_provider_requested = requestedProvider;
2320
2322
  if (requestedProvider === RUNTIME_PROVIDER_CHROME) {
@@ -3287,6 +3289,9 @@ function buildRoundtripSummary(base) {
3287
3289
  runtime_verification_retry_required: false,
3288
3290
  capability_state: "",
3289
3291
  local_validation_policy: "advisory",
3292
+ line_endings_status: "not-run",
3293
+ line_endings_checked_files: 0,
3294
+ line_endings_normalized_files: [],
3290
3295
  local_validation_execution_status: "not-run",
3291
3296
  local_validation_status: "not-run",
3292
3297
  local_validation_entrypoint_requested: LOCAL_VALIDATION_REQUESTED_ENTRYPOINT,
@@ -3673,6 +3678,14 @@ function componentAttributesPath() {
3673
3678
  : path.resolve(apexlangToolPath("..", "..", "..", "ai-context", "memory-bank", "component-attributes.json"));
3674
3679
  }
3675
3680
 
3681
+ function grammarContractToolPath() {
3682
+ return apexlangToolPath("public", "grammar_contract.mjs");
3683
+ }
3684
+
3685
+ function apexlangValidatorPath() {
3686
+ return apexlangToolPath("internal", "python", "validate_apexlang.py");
3687
+ }
3688
+
3676
3689
  async function readJsonIfExists(filePath) {
3677
3690
  try {
3678
3691
  return await readJson(filePath);
@@ -3937,17 +3950,129 @@ async function loadVscodeProblemsEvidence({ vscodeProblemsPath = "", appPath = "
3937
3950
  };
3938
3951
  }
3939
3952
 
3940
- function buildComponentContracts(componentAttributes = {}, { build = "", compilerReport = {}, source = "component-attributes" } = {}) {
3953
+ function grammarPropertyContract(definition = "", requiredPaths = new Set(), pathPrefix = "") {
3954
+ const allowedProperties = [];
3955
+ const propertyEnums = {};
3956
+ for (const line of String(definition || "").split(/\r?\n/)) {
3957
+ const propertyMatch = line.match(/"([A-Za-z][A-Za-z0-9]*)"\s*":"\s*<ws>/);
3958
+ if (!propertyMatch) continue;
3959
+ const propertyName = propertyMatch[1];
3960
+ if (!allowedProperties.includes(propertyName)) allowedProperties.push(propertyName);
3961
+ const enumMatch = line.match(/<ws>\s*\(([^)]+)\)/);
3962
+ if (enumMatch) {
3963
+ const values = [...enumMatch[1].matchAll(/"([^"]+)"/g)].map((match) => match[1]);
3964
+ if (values.length) propertyEnums[propertyName] = [...new Set(values)];
3965
+ }
3966
+ }
3967
+ return {
3968
+ allowedProperties,
3969
+ requiredProperties: allowedProperties.filter((propertyName) => requiredPaths.has(`${pathPrefix}${propertyName}`)),
3970
+ propertyEnums
3971
+ };
3972
+ }
3973
+
3974
+ function grammarChildContract(grammarContract = {}, selector = "region.column") {
3975
+ const resolved = (grammarContract?.compilerContract?.resolvedChildren || [])
3976
+ .find(({ parent, child }) => `${parent}.${child}` === selector);
3977
+ if (!resolved) return null;
3978
+ const productions = new Map((grammarContract.productions || []).map(({ name, definition }) => [name, definition]));
3979
+ const root = resolved.grammarProduction;
3980
+ const requiredPaths = new Set(
3981
+ (grammarContract?.compilerContract?.requiredActiveProperties || [])
3982
+ // Compiler defaults are not optionality. Keep a property required unless
3983
+ // the target-build contract explicitly marks it otherwise.
3984
+ .filter(({ compilerRequired }) => compilerRequired !== false)
3985
+ .map(({ path: propertyPath }) => propertyPath)
3986
+ );
3987
+ const direct = grammarPropertyContract(
3988
+ productions.get(`${root}-direct-property`) || "",
3989
+ requiredPaths,
3990
+ `${selector}.`
3991
+ );
3992
+ const namedDeclaration = resolved.componentIdPolicy === "required-as-name" || direct.allowedProperties.includes("name");
3993
+ if (namedDeclaration) {
3994
+ direct.allowedProperties = direct.allowedProperties.filter((propertyName) => propertyName !== "name");
3995
+ direct.requiredProperties = direct.requiredProperties.filter((propertyName) => propertyName !== "name");
3996
+ delete direct.propertyEnums.name;
3997
+ }
3998
+
3999
+ const childContract = {
4000
+ ...direct,
4001
+ allowedBlocks: [],
4002
+ requiredBlocks: [],
4003
+ declarationStyle: namedDeclaration ? "named" : "unnamed",
4004
+ declaration: resolved.declaration,
4005
+ componentIdPolicy: resolved.componentIdPolicy,
4006
+ compilerComponentTypeId: resolved.compilerComponentTypeId,
4007
+ grammarProduction: resolved.grammarProduction,
4008
+ conformance: (grammarContract.compilerContract?.grammarConformance || [])
4009
+ .find((entry) => entry.selector === selector) || null
4010
+ };
4011
+ const groupDefinition = productions.get(`${root}-group-block`) || "";
4012
+ for (const referenceMatch of groupDefinition.matchAll(/<([^>]+)>/g)) {
4013
+ const productionName = referenceMatch[1];
4014
+ const blockDefinition = productions.get(productionName) || "";
4015
+ const blockName = blockDefinition.match(/::=.*?"([A-Za-z][A-Za-z0-9]*)"\s*<ws>\s*"\{"/)?.[1];
4016
+ if (!blockName) continue;
4017
+ const blockContract = grammarPropertyContract(
4018
+ productions.get(`${productionName}-property`) || "",
4019
+ requiredPaths,
4020
+ `${selector}.${blockName}.`
4021
+ );
4022
+ childContract.allowedBlocks.push(blockName);
4023
+ if (blockContract.requiredProperties.length) childContract.requiredBlocks.push(blockName);
4024
+ childContract[blockName] = blockContract;
4025
+ }
4026
+ return childContract;
4027
+ }
4028
+
4029
+ function copyContractNode(node = {}) {
4030
+ const copied = {
4031
+ allowedProperties: node.allowedProperties || [],
4032
+ requiredProperties: node.requiredProperties || [],
4033
+ allowedBlocks: node.allowedBlocks || [],
4034
+ requiredBlocks: node.requiredBlocks || [],
4035
+ propertyEnums: node.propertyEnums || {},
4036
+ childComponents: {}
4037
+ };
4038
+ for (const key of ["grammarRequiredProperties", "compilerRequiredProperties", "compilerDefaultedProperties", "conformance"]) {
4039
+ if (node[key] !== undefined) copied[key] = node[key];
4040
+ }
4041
+ for (const key of ["declarationStyle", "declaration", "componentIdPolicy", "compilerComponentTypeId", "grammarProduction"]) {
4042
+ if (node[key] !== undefined) copied[key] = node[key];
4043
+ }
4044
+ for (const [childName, childNode] of Object.entries(node || {})) {
4045
+ if (!childNode || typeof childNode !== "object" || Array.isArray(childNode)) continue;
4046
+ if (!childNode.allowedProperties && !childNode.requiredProperties && !childNode.allowedBlocks) continue;
4047
+ copied.childComponents[childName] = copyContractNode(childNode);
4048
+ }
4049
+ return copied;
4050
+ }
4051
+
4052
+ export function buildComponentContracts(
4053
+ componentAttributes = {},
4054
+ { build = "", compilerReport = {}, source = "component-attributes", mediaListGrammarContract = null } = {}
4055
+ ) {
4056
+ const resolvedAttributes = structuredClone(componentAttributes || {});
4057
+ const mediaListPolicy = resolvedAttributes?.components?.region?.mediaList || null;
4058
+ const policyBuild = componentAttributes?.compilerProvenance?.buildID || "unknown";
4059
+ const mediaListColumn = mediaListGrammarContract
4060
+ ? grammarChildContract(mediaListGrammarContract, "region.column")
4061
+ : null;
4062
+ const mediaListSourceLocations = mediaListGrammarContract?.compilerContract
4063
+ ? mediaListGrammarContract.compilerContract.mediaListSourceLocations
4064
+ || (mediaListGrammarContract.compilerContract.assumptions || [])
4065
+ .map((entry) => /^region\.source\.location=(.+)$/.exec(String(entry))?.[1] || "")
4066
+ .filter(Boolean)
4067
+ : [];
4068
+ if (mediaListColumn && resolvedAttributes?.components?.region?.mediaList) {
4069
+ resolvedAttributes.components.region.mediaList.column = mediaListColumn;
4070
+ }
3941
4071
  const components = {};
3942
- for (const [componentType, families] of Object.entries(componentAttributes.components || {})) {
4072
+ for (const [componentType, families] of Object.entries(resolvedAttributes.components || {})) {
3943
4073
  components[componentType] = {};
3944
4074
  for (const [familyName, contract] of Object.entries(families || {})) {
3945
- components[componentType][familyName] = {
3946
- allowedBlocks: contract.allowedBlocks || [],
3947
- requiredBlocks: contract.requiredBlocks || [],
3948
- propertyEnums: contract.propertyEnums || {},
3949
- childComponents: {}
3950
- };
4075
+ components[componentType][familyName] = copyContractNode(contract);
3951
4076
  for (const [childName, childContract] of Object.entries(contract || {})) {
3952
4077
  if (!childContract || typeof childContract !== "object" || Array.isArray(childContract)) {
3953
4078
  continue;
@@ -3955,30 +4080,210 @@ function buildComponentContracts(componentAttributes = {}, { build = "", compile
3955
4080
  if (!childContract.allowedProperties && !childContract.requiredProperties && !childContract.allowedBlocks) {
3956
4081
  continue;
3957
4082
  }
3958
- components[componentType][familyName].childComponents[childName] = {
3959
- allowedProperties: childContract.allowedProperties || [],
3960
- requiredProperties: childContract.requiredProperties || [],
3961
- allowedBlocks: childContract.allowedBlocks || [],
3962
- requiredBlocks: childContract.requiredBlocks || [],
3963
- propertyEnums: childContract.propertyEnums || {}
3964
- };
4083
+ components[componentType][familyName].childComponents[childName] = copyContractNode(childContract);
3965
4084
  }
3966
4085
  }
3967
4086
  }
3968
4087
  return {
3969
4088
  generated_at: new Date().toISOString(),
3970
- build: build || compilerReport?.compilerTruth?.buildID || componentAttributes?.compilerProvenance?.buildID || "unknown",
4089
+ build: mediaListGrammarContract?.compilerContract?.buildID
4090
+ || build
4091
+ || compilerReport?.compilerTruth?.buildID
4092
+ || componentAttributes?.compilerProvenance?.buildID
4093
+ || "unknown",
3971
4094
  source,
3972
4095
  warnings_as_errors: true,
3973
4096
  valid_component_types: Object.keys(components).sort(),
3974
4097
  deprecated_slots: componentAttributes?.deprecatedSlots || componentAttributes?.deprecated_slots || [],
3975
4098
  known_warning_as_error_cases: compilerReport?.warningAsErrorCases || componentAttributes?.knownWarningAsErrorCases || [],
3976
4099
  compiler_truth_status: compilerReport?.status || "unknown",
3977
- compiler_provenance: componentAttributes?.compilerProvenance || null,
4100
+ compiler_provenance: mediaListGrammarContract?.compilerContract
4101
+ ? {
4102
+ buildID: mediaListGrammarContract.compilerContract.buildID,
4103
+ metadataHash: mediaListGrammarContract.compilerContract.metadataHash,
4104
+ normalizerVersion: mediaListGrammarContract.compilerContract.normalizerVersion,
4105
+ source: mediaListGrammarContract.compilerContract.source
4106
+ }
4107
+ : componentAttributes?.compilerProvenance || null,
4108
+ compiler_resolutions: mediaListColumn
4109
+ ? {
4110
+ "region.mediaList.column": {
4111
+ compilerComponentTypeId: mediaListColumn.compilerComponentTypeId,
4112
+ grammarProduction: mediaListColumn.grammarProduction,
4113
+ declarationStyle: mediaListColumn.declarationStyle,
4114
+ declaration: mediaListColumn.declaration,
4115
+ componentIdPolicy: mediaListColumn.componentIdPolicy,
4116
+ assumptions: mediaListGrammarContract.compilerContract.assumptions,
4117
+ conformance: mediaListColumn.conformance || null
4118
+ }
4119
+ }
4120
+ : {},
4121
+ capability_provenance: mediaListPolicy
4122
+ ? {
4123
+ "region.mediaList.column": {
4124
+ source: mediaListColumn ? "compiler-runtime-metadata" : "curated-component-policy",
4125
+ compilerDerived: Boolean(mediaListColumn),
4126
+ build: mediaListColumn
4127
+ ? mediaListGrammarContract.compilerContract.buildID
4128
+ : policyBuild,
4129
+ status: mediaListColumn ? "resolved" : "policy_fallback",
4130
+ sourceLocations: mediaListColumn ? mediaListSourceLocations : []
4131
+ },
4132
+ "region.mediaList.settings": {
4133
+ source: "curated-component-policy",
4134
+ compilerDerived: false,
4135
+ build: policyBuild,
4136
+ status: mediaListPolicy.settings ? "available" : "unsupported"
4137
+ },
4138
+ "region.mediaList.plugin-avatar": {
4139
+ source: "curated-component-policy",
4140
+ compilerDerived: false,
4141
+ build: policyBuild,
4142
+ status: mediaListPolicy["plugin-avatar"] ? "available" : "unsupported"
4143
+ },
4144
+ "region.mediaList.plugin-badge": {
4145
+ source: "curated-component-policy",
4146
+ compilerDerived: false,
4147
+ build: policyBuild,
4148
+ status: mediaListPolicy["plugin-badge"] ? "available" : "unsupported"
4149
+ },
4150
+ "region.mediaList.action": {
4151
+ source: "curated-component-policy",
4152
+ compilerDerived: false,
4153
+ build: policyBuild,
4154
+ status: mediaListPolicy.action ? "available" : "unsupported"
4155
+ },
4156
+ "region.mediaList.plugin-grouping": {
4157
+ source: "curated-component-policy",
4158
+ compilerDerived: false,
4159
+ build: policyBuild,
4160
+ status: mediaListPolicy["plugin-grouping"] ? "available" : "unsupported"
4161
+ }
4162
+ }
4163
+ : {},
3978
4164
  components
3979
4165
  };
3980
4166
  }
3981
4167
 
4168
+ function apexlangRegionBlocks(text) {
4169
+ const regionPattern = /^([ \t]*)region\b[^\n]*\(\s*$([\s\S]*?)^\1\)\s*$/gm;
4170
+ return [...text.matchAll(regionPattern)].map((match) => match[0]);
4171
+ }
4172
+
4173
+ export function appTextUsesMediaListReport(text) {
4174
+ return appTextMediaListReportSourceLocations(text).length > 0
4175
+ || apexlangRegionBlocks(text).some((regionBlock) =>
4176
+ /\btype\s*:\s*themeTemplateComponent\/mediaList\b/.test(regionBlock)
4177
+ && /\bcomponentAppearance\s*\{[\s\S]*?\bdisplay\s*:\s*report\b/.test(regionBlock));
4178
+ }
4179
+
4180
+ export function appTextMediaListReportSourceLocations(text) {
4181
+ return apexlangRegionBlocks(text)
4182
+ .filter((regionBlock) =>
4183
+ /\btype\s*:\s*themeTemplateComponent\/mediaList\b/.test(regionBlock)
4184
+ && /\bcomponentAppearance\s*\{[\s\S]*?\bdisplay\s*:\s*report\b/.test(regionBlock))
4185
+ .map((regionBlock) => {
4186
+ const match = /\bsource\s*\{[\s\S]*?\blocation\s*:\s*([A-Za-z][A-Za-z0-9_/-]*)/.exec(regionBlock);
4187
+ return match?.[1] || "";
4188
+ });
4189
+ }
4190
+
4191
+ async function appMediaListReportSourceLocations(appPath) {
4192
+ const locations = [];
4193
+ const files = await collectFiles(appPath, (filePath) =>
4194
+ filePath.endsWith(".apx")
4195
+ && !filePath.split(path.sep).includes("apex-exports"));
4196
+ for (const filePath of files) {
4197
+ const text = await fs.readFile(filePath, "utf8");
4198
+ locations.push(...appTextMediaListReportSourceLocations(text));
4199
+ }
4200
+ return locations;
4201
+ }
4202
+
4203
+ export async function resolveMediaListGrammarContract({
4204
+ appPath = "",
4205
+ compilerOracleHome = "",
4206
+ buildGrammarContractFn = null
4207
+ } = {}) {
4208
+ if (!appPath) return { status: "not_applicable", contract: null };
4209
+ const sourceLocations = await appMediaListReportSourceLocations(appPath);
4210
+ if (sourceLocations.length === 0) return { status: "not_applicable", contract: null };
4211
+ if (sourceLocations.some((location) => !location)) {
4212
+ return {
4213
+ status: "unresolved",
4214
+ contract: null,
4215
+ reason: "Every Media List report region must declare source.location."
4216
+ };
4217
+ }
4218
+ try {
4219
+ const buildGrammarContract = buildGrammarContractFn
4220
+ || (await import(pathToFileURL(grammarContractToolPath()).href)).buildGrammarContract;
4221
+ const contracts = [];
4222
+ for (const sourceLocation of [...new Set(sourceLocations)].sort()) {
4223
+ const { contract } = await buildGrammarContract({
4224
+ components: ["region"],
4225
+ children: ["region.column"],
4226
+ groups: ["source", "layout", "appearance"],
4227
+ conditions: [
4228
+ "region.type=themeTemplateComponent/mediaList",
4229
+ `region.source.location=${sourceLocation}`
4230
+ ],
4231
+ useCache: false,
4232
+ compilerOracleHome
4233
+ });
4234
+ const column = contract?.compilerContract?.resolvedChildren?.find(({ parent, child }) =>
4235
+ parent === "region" && child === "column");
4236
+ if (contract?.compilerContract?.status !== "resolved" || !column) {
4237
+ return {
4238
+ status: "unresolved",
4239
+ contract: null,
4240
+ reason: `Media List region.column did not resolve uniquely for source.location=${sourceLocation}.`
4241
+ };
4242
+ }
4243
+ contracts.push({ sourceLocation, contract, column });
4244
+ }
4245
+ const baselineColumn = JSON.stringify(contracts[0].column);
4246
+ if (contracts.some(({ column }) => JSON.stringify(column) !== baselineColumn)) {
4247
+ return {
4248
+ status: "unresolved",
4249
+ contract: null,
4250
+ reason: "Media List report source locations resolve incompatible region.column contracts."
4251
+ };
4252
+ }
4253
+ const contract = structuredClone(contracts[0].contract);
4254
+ const conformance = contract.compilerContract?.grammarConformance || [];
4255
+ if (conformance.some((entry) => entry.blocking || entry.status === "mismatch")) {
4256
+ const mismatch = conformance.flatMap((entry) => entry.mismatches || [])
4257
+ .map((entry) => entry.path)
4258
+ .sort()
4259
+ .join(", ");
4260
+ return {
4261
+ status: "unresolved",
4262
+ contract: null,
4263
+ reason: `Media List grammar/compiler conformance mismatch${mismatch ? `: ${mismatch}` : "."}`
4264
+ };
4265
+ }
4266
+ contract.compilerContract.mediaListSourceLocations = contracts.map(({ sourceLocation }) => sourceLocation);
4267
+ contract.compilerContract.assumptions = [
4268
+ ...(contract.compilerContract.assumptions || []).filter((entry) =>
4269
+ !String(entry).startsWith("region.source.location=")),
4270
+ ...contracts.map(({ sourceLocation }) => `region.source.location=${sourceLocation}`)
4271
+ ];
4272
+ return { status: "resolved", contract };
4273
+ } catch (error) {
4274
+ return { status: "unavailable", contract: null, reason: error instanceof Error ? error.message : String(error) };
4275
+ }
4276
+ }
4277
+
4278
+ async function runTargetBuildLocalValidation({ appPath, componentContractPath }) {
4279
+ return runCommand("python3", [
4280
+ apexlangValidatorPath(),
4281
+ "--component-attributes",
4282
+ componentContractPath,
4283
+ appPath
4284
+ ], { allowFailure: true, passthrough: false });
4285
+ }
4286
+
3982
4287
  function validationArtifactPaths(options = {}) {
3983
4288
  const artifactDir = path.resolve(
3984
4289
  options.artifactDir ||
@@ -3997,8 +4302,7 @@ function validationArtifactPaths(options = {}) {
3997
4302
  }
3998
4303
 
3999
4304
  async function writeValidationArtifacts({ report, problemsPayload, componentContract, paths }) {
4000
- const contractBuild = sanitizeSegment(componentContract.build || "unknown") || "unknown";
4001
- const componentContractPath = path.join(paths.componentContractsDir, `${contractBuild}.json`);
4305
+ const componentContractPath = componentContractArtifactPath(componentContract, paths);
4002
4306
  report.artifacts.component_contract_path = componentContractPath;
4003
4307
  try {
4004
4308
  await fs.access(paths.transcriptPath);
@@ -4011,6 +4315,11 @@ async function writeValidationArtifacts({ report, problemsPayload, componentCont
4011
4315
  await writeJson(paths.reportPath, report);
4012
4316
  }
4013
4317
 
4318
+ function componentContractArtifactPath(componentContract, paths) {
4319
+ const contractBuild = sanitizeSegment(componentContract.build || "unknown") || "unknown";
4320
+ return path.join(paths.componentContractsDir, `${contractBuild}.json`);
4321
+ }
4322
+
4014
4323
  /**
4015
4324
  * Run the live validate-only gate and write validation reports for editor and agent feedback.
4016
4325
  */
@@ -4020,6 +4329,8 @@ export async function runRuntimeValidate(options = {}) {
4020
4329
  runCommand,
4021
4330
  loadVscodeProblemsEvidence,
4022
4331
  readJsonIfExists,
4332
+ resolveMediaListGrammarContract,
4333
+ runTargetBuildLocalValidation,
4023
4334
  writeValidationArtifacts,
4024
4335
  ...options._deps
4025
4336
  };
@@ -4048,11 +4359,13 @@ export async function runRuntimeValidate(options = {}) {
4048
4359
  validation_sources: {
4049
4360
  live_validator: { status: "blocked", source: "runtime validate-only roundtrip" },
4050
4361
  compiler_truth: { status: "not_run", report_path: paths.compilerTruthReportPath },
4362
+ target_build_contract: { status: "not_run", source: "compiler-runtime-metadata" },
4051
4363
  vscode_problems: { status: "not_provided", source: "not_provided", unresolved_count: null }
4052
4364
  },
4053
4365
  diagnostic_sources: {
4054
4366
  local_lint: { status: "not_run", source: "runtime validate-only roundtrip" },
4055
4367
  compiler_truth: { status: "not_run", report_path: paths.compilerTruthReportPath },
4368
+ target_build_contract: { status: "not_run", source: "compiler-runtime-metadata" },
4056
4369
  vscode_problems: { status: "not_provided", source: "not_provided", unresolved_count: null }
4057
4370
  },
4058
4371
  problem_count: 0,
@@ -4134,12 +4447,63 @@ export async function runRuntimeValidate(options = {}) {
4134
4447
  policy: "advisory_when_live_validation_passes"
4135
4448
  };
4136
4449
 
4450
+ const mediaListResolution = await deps.resolveMediaListGrammarContract({
4451
+ appPath,
4452
+ compilerOracleHome: options.compilerOracleHome || ""
4453
+ });
4454
+ const mediaListGrammarContract = mediaListResolution.contract || null;
4455
+ const grammarBuild = mediaListGrammarContract?.compilerContract?.buildID || "";
4456
+ const compilerBuild = compilerReport?.compilerTruth?.buildID || "";
4457
+ if (mediaListResolution.status !== "not_applicable" && mediaListResolution.status !== "resolved") {
4458
+ report.blocking_reasons.push(
4459
+ `Target-build Media List contract is ${mediaListResolution.status}: ${mediaListResolution.reason || "unknown reason"}`
4460
+ );
4461
+ }
4462
+ if (grammarBuild && compilerBuild && grammarBuild !== compilerBuild) {
4463
+ report.blocking_reasons.push(
4464
+ `Target-build Media List contract build ${grammarBuild} does not match compiler-truth build ${compilerBuild}.`
4465
+ );
4466
+ }
4467
+ if (grammarBuild && report.target_build && grammarBuild !== report.target_build) {
4468
+ report.blocking_reasons.push(
4469
+ `Target-build Media List contract build ${grammarBuild} does not match live runtime build ${report.target_build}.`
4470
+ );
4471
+ }
4472
+
4137
4473
  const componentAttributes = (await deps.readJsonIfExists(componentAttributesPath())) || {};
4138
4474
  const componentContract = buildComponentContracts(componentAttributes, {
4139
- build: report.target_build || compilerReport?.compilerTruth?.buildID || "",
4475
+ build: compilerReport?.compilerTruth?.buildID || componentAttributes?.compilerProvenance?.buildID || "",
4140
4476
  compilerReport,
4141
- source: report.target_build ? "target-build-component-contract" : "component-attributes-fallback"
4477
+ source: mediaListGrammarContract ? "compiler-runtime-metadata+curated-policy" : "curated-component-policy",
4478
+ mediaListGrammarContract
4142
4479
  });
4480
+ const componentContractPath = componentContractArtifactPath(componentContract, paths);
4481
+ report.artifacts.component_contract_path = componentContractPath;
4482
+ await writeJson(componentContractPath, componentContract);
4483
+
4484
+ if (mediaListGrammarContract && !report.blocking_reasons.some((reason) => /contract build .* does not match/.test(reason))) {
4485
+ const targetBuildLint = await deps.runTargetBuildLocalValidation({ appPath, componentContractPath });
4486
+ const targetBuildLintStatus = targetBuildLint.code === 0 ? "pass" : "fail";
4487
+ report.validation_sources.target_build_contract = {
4488
+ status: targetBuildLintStatus,
4489
+ source: componentContractPath,
4490
+ compiler_build: componentContract.build,
4491
+ output: cleanOutput(targetBuildLint).slice(0, 4000)
4492
+ };
4493
+ report.diagnostic_sources.target_build_contract = {
4494
+ ...report.validation_sources.target_build_contract,
4495
+ policy: "blocking_for_compiler_resolved_components"
4496
+ };
4497
+ if (targetBuildLintStatus !== "pass") {
4498
+ report.blocking_reasons.push("Target-build component contract validation did not pass.");
4499
+ }
4500
+ } else if (mediaListResolution.status === "not_applicable") {
4501
+ report.validation_sources.target_build_contract = {
4502
+ status: "not_applicable",
4503
+ source: "No Media List report component found in the target app."
4504
+ };
4505
+ report.diagnostic_sources.target_build_contract = report.validation_sources.target_build_contract;
4506
+ }
4143
4507
 
4144
4508
  const vscodeEvidence = await deps.loadVscodeProblemsEvidence({
4145
4509
  vscodeProblemsPath: options.vscodeProblemsPath || "",
@@ -4185,7 +4549,7 @@ export async function runRuntimeValidate(options = {}) {
4185
4549
  report.validation_status =
4186
4550
  report.validation_sources.live_validator.status === "pass" &&
4187
4551
  problemsPayload.unresolved_count === 0 &&
4188
- !report.blocking_reasons.some((reason) => /^Missing required /.test(reason))
4552
+ report.blocking_reasons.length === 0
4189
4553
  ? "pass"
4190
4554
  : "fail";
4191
4555
  report.import_eligibility = report.validation_status === "pass" ? "validate-only-passed" : "blocked";
@@ -4338,18 +4702,32 @@ function runPathSession({ dbConnectionName, input, labelPrefix = "sql" }) {
4338
4702
  };
4339
4703
  }
4340
4704
 
4341
- async function runBuildRootSession({ buildRoot, input, label = "apex_sql_build_root" }) {
4342
- const tempScriptPath = path.join(os.tmpdir(), `apexctl-roundtrip-${Date.now()}.sql`);
4343
- await fs.writeFile(tempScriptPath, `${input}\n`, "utf8");
4344
- const result = runInteractiveCommand("apex", ["sql", "-s", tempScriptPath], { cwd: buildRoot });
4345
- await fs.rm(tempScriptPath, { force: true });
4346
- return {
4347
- success: !hasWorkspaceAmbiguity(result) && !hasRuntimeFailure(result),
4348
- workspaceAmbiguity: hasWorkspaceAmbiguity(result),
4349
- entrypoint: label,
4350
- result,
4351
- transcript: `## ${label}\n${cleanOutput(result)}\n`
4352
- };
4705
+ export async function runBuildRootSession({
4706
+ buildRoot,
4707
+ input,
4708
+ label = "apex_sql_build_root",
4709
+ _deps = {}
4710
+ }) {
4711
+ const runInteractiveCommandImpl = _deps.runInteractiveCommand ?? runInteractiveCommand;
4712
+ const tempDirectory = await fs.mkdtemp(path.join(os.tmpdir(), "apexctl-roundtrip-"));
4713
+ const tempScriptPath = path.join(tempDirectory, "session.sql");
4714
+ try {
4715
+ await fs.writeFile(tempScriptPath, `${input}\n`, {
4716
+ encoding: "utf8",
4717
+ mode: 0o600,
4718
+ flag: "wx"
4719
+ });
4720
+ const result = runInteractiveCommandImpl("apex", ["sql", "-s", tempScriptPath], { cwd: buildRoot });
4721
+ return {
4722
+ success: !hasWorkspaceAmbiguity(result) && !hasRuntimeFailure(result),
4723
+ workspaceAmbiguity: hasWorkspaceAmbiguity(result),
4724
+ entrypoint: label,
4725
+ result,
4726
+ transcript: `## ${label}\n${cleanOutput(result)}\n`
4727
+ };
4728
+ } finally {
4729
+ await fs.rm(tempDirectory, { recursive: true, force: true });
4730
+ }
4353
4731
  }
4354
4732
 
4355
4733
  function runPathRoundtrip({ appPath, dbConnectionName, workspaceId, includeImport = true }) {
@@ -4583,6 +4961,7 @@ export async function runRuntimeRoundtrip(options = {}) {
4583
4961
  resolveRuntimeTargetApplication,
4584
4962
  executeSelectedRoundtrip,
4585
4963
  resolveWorkspaceIdForRuntime,
4964
+ normalizeApxLineEndings,
4586
4965
  verifyRuntimeUi,
4587
4966
  writeRoundtripArtifacts,
4588
4967
  ...options._deps
@@ -4749,6 +5128,27 @@ export async function runRuntimeRoundtrip(options = {}) {
4749
5128
  return buildRoundtripResult(0, summary);
4750
5129
  }
4751
5130
 
5131
+ try {
5132
+ const lineEndings = await deps.normalizeApxLineEndings(options.appPath);
5133
+ summary.line_endings_status = lineEndings.status;
5134
+ summary.line_endings_checked_files = lineEndings.checkedFiles;
5135
+ summary.line_endings_normalized_files = lineEndings.normalizedFiles;
5136
+ if (lineEndings.normalizedFiles.length > 0) {
5137
+ summary.notes.push(
5138
+ `Normalized ${lineEndings.normalizedFiles.length} APEXlang source file(s) from CRLF or CR to LF before live validation.`
5139
+ );
5140
+ }
5141
+ } catch (error) {
5142
+ summary.line_endings_status = "fail";
5143
+ summary.runtime_gate_status = "fail";
5144
+ summary.failure_class = "line_endings_normalization_failed";
5145
+ summary.blocking_reason = "APEXLANG_LF_LINE_ENDINGS_REQUIRED_001";
5146
+ summary.recommended_next_action = "Normalize every .apx file to LF before rerunning validation or import.";
5147
+ summary.notes.push(error instanceof Error ? error.message : String(error));
5148
+ await deps.writeRoundtripArtifacts(summary, transcriptParts.join("\n"));
5149
+ return buildRoundtripResult(1, summary);
5150
+ }
5151
+
4752
5152
  const localValidationStage = await runTimedStage(
4753
5153
  summary,
4754
5154
  "local_validate",