@topogram/cli 0.3.51 → 0.3.53

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 (77) hide show
  1. package/ARCHITECTURE.md +4 -4
  2. package/CHANGELOG.md +11 -11
  3. package/package.json +1 -1
  4. package/src/adoption/plan.js +2 -2
  5. package/src/agent-ops/query-builders.js +42 -33
  6. package/src/cli.js +174 -129
  7. package/src/generator/adapters.d.ts +1 -0
  8. package/src/generator/adapters.js +64 -39
  9. package/src/generator/check.js +19 -12
  10. package/src/generator/context/diff.js +9 -9
  11. package/src/generator/context/domain-coverage.js +11 -10
  12. package/src/generator/context/domain-page.js +6 -6
  13. package/src/generator/context/shared.js +37 -21
  14. package/src/generator/context/slice.js +70 -65
  15. package/src/generator/index.js +12 -12
  16. package/src/generator/output.js +21 -20
  17. package/src/generator/registry.js +71 -49
  18. package/src/generator/runtime/app-bundle.js +15 -15
  19. package/src/generator/runtime/compile-check.js +7 -7
  20. package/src/generator/runtime/deployment.js +9 -9
  21. package/src/generator/runtime/environment.js +39 -39
  22. package/src/generator/runtime/runtime-check.js +5 -5
  23. package/src/generator/runtime/shared.js +40 -38
  24. package/src/generator/runtime/smoke.js +5 -5
  25. package/src/generator/surfaces/databases/contract.js +1 -1
  26. package/src/generator/surfaces/databases/lifecycle-shared.js +6 -5
  27. package/src/generator/surfaces/databases/postgres/drizzle.js +3 -2
  28. package/src/generator/surfaces/databases/postgres/prisma.js +3 -2
  29. package/src/generator/surfaces/databases/shared.js +3 -2
  30. package/src/generator/surfaces/databases/snapshot.js +1 -1
  31. package/src/generator/surfaces/databases/sqlite/prisma.js +3 -2
  32. package/src/generator/surfaces/native/swiftui-app.js +3 -3
  33. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +1 -1
  34. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +3 -3
  35. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +3 -3
  36. package/src/generator/surfaces/services/persistence-wiring.js +3 -2
  37. package/src/generator/surfaces/services/server-contract.js +4 -4
  38. package/src/generator/surfaces/shared.js +2 -2
  39. package/src/generator/surfaces/web/design-intent.js +1 -1
  40. package/src/generator/surfaces/web/index.js +7 -7
  41. package/src/generator/surfaces/web/{react-components.js → react-widgets.js} +53 -53
  42. package/src/generator/surfaces/web/react.js +36 -36
  43. package/src/generator/surfaces/web/{sveltekit-components.js → sveltekit-widgets.js} +53 -53
  44. package/src/generator/surfaces/web/sveltekit.js +34 -34
  45. package/src/generator/surfaces/web/{ui-web-contract.js → ui-surface-contract.js} +8 -8
  46. package/src/generator/surfaces/web/vanilla.js +6 -6
  47. package/src/generator/{component-conformance.js → widget-conformance.js} +129 -128
  48. package/src/generator/widgets.js +40 -0
  49. package/src/generator-policy.js +10 -12
  50. package/src/import/core/runner.js +34 -34
  51. package/src/import/core/shared.js +1 -1
  52. package/src/import/extractors/ui/android-compose.js +1 -1
  53. package/src/import/extractors/ui/blazor.js +1 -1
  54. package/src/import/extractors/ui/razor-pages.js +1 -1
  55. package/src/import/extractors/ui/react-router.js +4 -4
  56. package/src/import/extractors/ui/sveltekit.js +4 -4
  57. package/src/import/extractors/ui/swiftui.js +1 -1
  58. package/src/import/extractors/ui/uikit.js +1 -1
  59. package/src/new-project.js +19 -18
  60. package/src/project-config.js +104 -44
  61. package/src/proofs/contract-audit.js +1 -1
  62. package/src/proofs/ios-parity.js +1 -1
  63. package/src/proofs/issues-parity.js +1 -1
  64. package/src/realization/backend/build-backend-runtime-realization.js +2 -2
  65. package/src/realization/ui/build-ui-shared-realization.js +33 -33
  66. package/src/realization/ui/build-web-realization.js +23 -20
  67. package/src/reconcile/journeys.js +1 -1
  68. package/src/resolver/index.js +148 -65
  69. package/src/validator/index.js +509 -423
  70. package/src/validator/kinds.js +36 -36
  71. package/src/validator/per-kind/{component.js → widget.js} +47 -47
  72. package/src/{component-behavior.js → widget-behavior.js} +3 -3
  73. package/src/workflows.js +39 -38
  74. package/template-helpers/react.js +4 -4
  75. package/template-helpers/sveltekit.js +4 -4
  76. package/src/generator/components.js +0 -39
  77. /package/src/resolver/enrich/{component.js → widget.js} +0 -0
@@ -49,7 +49,7 @@ import {
49
49
  UI_DESIGN_ACCESSIBILITY_VALUES,
50
50
  FIELD_SPECS
51
51
  } from "./kinds.js";
52
- import { validateComponent } from "./per-kind/component.js";
52
+ import { validateWidget } from "./per-kind/widget.js";
53
53
  import { validateDomain, validateDomainTag } from "./per-kind/domain.js";
54
54
  import { validatePitch } from "./per-kind/pitch.js";
55
55
  import { validateRequirement } from "./per-kind/requirement.js";
@@ -108,6 +108,10 @@ export function pushError(errors, message, loc) {
108
108
  });
109
109
  }
110
110
 
111
+ function renameDiagnostic(oldName, newName, example) {
112
+ return `${oldName} was renamed to ${newName}. Example fix: ${example}`;
113
+ }
114
+
111
115
  export function formatLoc(loc) {
112
116
  const line = loc?.start?.line ?? 1;
113
117
  const column = loc?.start?.column ?? 1;
@@ -225,7 +229,48 @@ function validateFieldPresence(errors, statement, fieldMap) {
225
229
  return;
226
230
  }
227
231
 
232
+ const renamedFields = new Map([
233
+ ["platform", ["type", "type web_surface"]],
234
+ ["ui_components", ["widget_bindings", "widget_bindings { screen item_list region results widget widget_data_grid }"]],
235
+ ["ui_design", ["design_tokens", "design_tokens { density comfortable tone operational }"]],
236
+ ["ui_routes", ["screen_routes", "screen_routes { screen item_list path /items }"]],
237
+ ["ui_screens", ["screens", "screens { item_list list title \"Items\" }"]],
238
+ ["ui_screen_regions", ["screen_regions", "screen_regions { screen item_list region results pattern resource_table }"]],
239
+ ["ui_navigation", ["navigation", "navigation { primary item_list label \"Items\" }"]],
240
+ ["ui_app_shell", ["app_shell", "app_shell { shell sidebar }"]],
241
+ ["ui_collections", ["collection_views", "collection_views { item_list presentation table }"]],
242
+ ["ui_actions", ["screen_actions", "screen_actions { screen item_list action create target cap_create_item }"]],
243
+ ["ui_visibility", ["visibility_rules", "visibility_rules { screen item_list when cap_list_items }"]],
244
+ ["ui_lookups", ["field_lookups", "field_lookups { field owner_id source cap_list_users }"]],
245
+ ["web_surface", ["web_hints", "web_hints { router file_based }"]],
246
+ ["ios_surface", ["ios_hints", "ios_hints { navigation stack }"]],
247
+ ["http", ["endpoints", "endpoints { cap_list_items method GET path /items success 200 }"]],
248
+ ["http_errors", ["error_responses", "error_responses { cap_list_items 404 shape_error }"]],
249
+ ["http_fields", ["wire_fields", "wire_fields { shape_item title title }"]],
250
+ ["http_responses", ["responses", "responses { cap_list_items 200 shape_item_list }"]],
251
+ ["http_preconditions", ["preconditions", "preconditions { cap_update_item rule_item_exists }"]],
252
+ ["http_idempotency", ["idempotency", "idempotency { cap_create_item key request_id }"]],
253
+ ["http_cache", ["cache", "cache { cap_list_items max_age 60 }"]],
254
+ ["http_delete", ["delete_semantics", "delete_semantics { cap_delete_item mode soft_delete }"]],
255
+ ["http_async", ["async_jobs", "async_jobs { cap_export_items job task_export }"]],
256
+ ["http_status", ["async_status", "async_status { cap_export_items path /exports/{job_id} }"]],
257
+ ["http_download", ["downloads", "downloads { cap_download_export content_type text/csv }"]],
258
+ ["http_authz", ["authorization", "authorization { cap_update_item role editor }"]],
259
+ ["http_callbacks", ["callbacks", "callbacks { cap_export_items event completed }"]],
260
+ ["db_tables", ["tables", "tables { entity_item table items }"]],
261
+ ["db_columns", ["columns", "columns { entity_item field title column title }"]],
262
+ ["db_keys", ["keys", "keys { entity_item primary [id] }"]],
263
+ ["db_indexes", ["indexes", "indexes { entity_item index [title] }"]],
264
+ ["db_relations", ["relations", "relations { entity_item foreign_key owner_id references entity_user.id }"]],
265
+ ["db_lifecycle", ["lifecycle", "lifecycle { entity_item timestamps created_at created_at updated_at updated_at }"]]
266
+ ]);
267
+
228
268
  for (const key of fieldMap.keys()) {
269
+ if (renamedFields.has(key)) {
270
+ const [newName, example] = renamedFields.get(key);
271
+ pushError(errors, `Field '${key}' on ${statement.kind} ${statement.id} ${renameDiagnostic(`'${key}'`, `'${newName}'`, example)}`, fieldMap.get(key)[0].loc);
272
+ continue;
273
+ }
229
274
  if (!spec.allowed.includes(key)) {
230
275
  pushError(errors, `Field '${key}' is not allowed on ${statement.kind} ${statement.id}`, fieldMap.get(key)[0].loc);
231
276
  }
@@ -238,6 +283,36 @@ function validateFieldPresence(errors, statement, fieldMap) {
238
283
  }
239
284
  }
240
285
 
286
+ function validateProjectionTypeRenames(errors, statement, fieldMap) {
287
+ if (statement.kind !== "projection") {
288
+ return;
289
+ }
290
+
291
+ const typeField = fieldMap.get("type")?.[0];
292
+ const typeValue = symbolValue(typeField?.value);
293
+ const renamedTypes = new Map([
294
+ ["ui_shared", "ui_contract"],
295
+ ["ui_web", "web_surface"],
296
+ ["ui_ios", "ios_surface"],
297
+ ["ui_android", "android_surface"],
298
+ ["dotnet", "api_contract"],
299
+ ["api", "api_contract"],
300
+ ["backend", "api_contract"],
301
+ ["db_postgres", "db_contract"],
302
+ ["db_sqlite", "db_contract"]
303
+ ]);
304
+ if (!typeField || !renamedTypes.has(typeValue)) {
305
+ return;
306
+ }
307
+
308
+ const nextType = renamedTypes.get(typeValue);
309
+ pushError(
310
+ errors,
311
+ `Projection ${statement.id} ${renameDiagnostic(`type value '${typeValue}'`, `'${nextType}'`, `type ${nextType}`)}`,
312
+ typeField.value.loc
313
+ );
314
+ }
315
+
241
316
  function validateBlockEntryLengths(errors, statement, fieldMap, key, minimumWidth) {
242
317
  const field = fieldMap.get(key)?.[0];
243
318
  if (!field || field.value.type !== "block") {
@@ -255,7 +330,7 @@ function validateFieldShapes(errors, statement, fieldMap) {
255
330
  ensureSingleValueField(errors, statement, fieldMap, "name", ["string"]);
256
331
  ensureSingleValueField(errors, statement, fieldMap, "description", ["string"]);
257
332
  ensureSingleValueField(errors, statement, fieldMap, "status", ["symbol"]);
258
- ensureSingleValueField(errors, statement, fieldMap, "platform", ["symbol"]);
333
+ ensureSingleValueField(errors, statement, fieldMap, "type", ["symbol"]);
259
334
  ensureSingleValueField(errors, statement, fieldMap, "method", ["symbol"]);
260
335
  ensureSingleValueField(errors, statement, fieldMap, "severity", ["symbol"]);
261
336
  ensureSingleValueField(errors, statement, fieldMap, "category", ["symbol"]);
@@ -299,7 +374,7 @@ function validateFieldShapes(errors, statement, fieldMap) {
299
374
  ensureSingleValueField(errors, statement, fieldMap, key, ["list"]);
300
375
  }
301
376
 
302
- for (const key of ["fields", "props", "events", "slots", "behaviors", "keys", "relations", "invariants", "rename", "overrides", "http", "http_errors", "http_fields", "http_responses", "http_preconditions", "http_idempotency", "http_cache", "http_delete", "http_async", "http_status", "http_download", "http_authz", "http_callbacks", "ui_screens", "ui_collections", "ui_actions", "ui_visibility", "ui_lookups", "ui_routes", "ui_web", "ui_ios", "ui_app_shell", "ui_navigation", "ui_screen_regions", "ui_components", "ui_design", "db_tables", "db_columns", "db_keys", "db_indexes", "db_relations", "db_lifecycle", "generator_defaults"]) {
377
+ for (const key of ["fields", "props", "events", "slots", "behaviors", "keys", "relations", "invariants", "rename", "overrides", "endpoints", "error_responses", "wire_fields", "responses", "preconditions", "idempotency", "cache", "delete_semantics", "async_jobs", "async_status", "downloads", "authorization", "callbacks", "screens", "collection_views", "screen_actions", "visibility_rules", "field_lookups", "screen_routes", "web_hints", "ios_hints", "app_shell", "navigation", "screen_regions", "widget_bindings", "design_tokens", "tables", "columns", "keys", "indexes", "relations", "lifecycle", "generator_defaults"]) {
303
378
  ensureSingleValueField(errors, statement, fieldMap, key, ["block"]);
304
379
  }
305
380
 
@@ -307,40 +382,46 @@ function validateFieldShapes(errors, statement, fieldMap) {
307
382
  validateBlockEntryLengths(errors, statement, fieldMap, "props", 3);
308
383
  validateBlockEntryLengths(errors, statement, fieldMap, "events", 2);
309
384
  validateBlockEntryLengths(errors, statement, fieldMap, "slots", 2);
310
- validateBlockEntryLengths(errors, statement, fieldMap, "keys", 2);
311
- validateBlockEntryLengths(errors, statement, fieldMap, "relations", 3);
312
385
  validateBlockEntryLengths(errors, statement, fieldMap, "invariants", 2);
313
- validateBlockEntryLengths(errors, statement, fieldMap, "http", 7);
314
- validateBlockEntryLengths(errors, statement, fieldMap, "http_errors", 3);
315
- validateBlockEntryLengths(errors, statement, fieldMap, "http_fields", 5);
316
- validateBlockEntryLengths(errors, statement, fieldMap, "http_responses", 3);
317
- validateBlockEntryLengths(errors, statement, fieldMap, "http_preconditions", 9);
318
- validateBlockEntryLengths(errors, statement, fieldMap, "http_idempotency", 7);
319
- validateBlockEntryLengths(errors, statement, fieldMap, "http_cache", 11);
320
- validateBlockEntryLengths(errors, statement, fieldMap, "http_delete", 7);
321
- validateBlockEntryLengths(errors, statement, fieldMap, "http_async", 11);
322
- validateBlockEntryLengths(errors, statement, fieldMap, "http_status", 11);
323
- validateBlockEntryLengths(errors, statement, fieldMap, "http_download", 7);
324
- validateBlockEntryLengths(errors, statement, fieldMap, "http_authz", 3);
325
- validateBlockEntryLengths(errors, statement, fieldMap, "http_callbacks", 11);
326
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_screens", 4);
327
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_collections", 4);
328
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_actions", 6);
329
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_visibility", 5);
330
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_lookups", 8);
331
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_routes", 4);
332
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_web", 4);
333
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_ios", 4);
334
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_app_shell", 2);
335
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_navigation", 2);
336
- validateBlockEntryLengths(errors, statement, fieldMap, "ui_screen_regions", 4);
337
- validateBlockEntryLengths(errors, statement, fieldMap, "db_tables", 3);
338
- validateBlockEntryLengths(errors, statement, fieldMap, "db_columns", 5);
339
- validateBlockEntryLengths(errors, statement, fieldMap, "db_keys", 3);
340
- validateBlockEntryLengths(errors, statement, fieldMap, "db_indexes", 3);
341
- validateBlockEntryLengths(errors, statement, fieldMap, "db_relations", 6);
342
- validateBlockEntryLengths(errors, statement, fieldMap, "db_lifecycle", 3);
343
386
  validateBlockEntryLengths(errors, statement, fieldMap, "generator_defaults", 2);
387
+
388
+ if (statement.kind === "entity") {
389
+ validateBlockEntryLengths(errors, statement, fieldMap, "keys", 2);
390
+ validateBlockEntryLengths(errors, statement, fieldMap, "relations", 3);
391
+ }
392
+
393
+ if (statement.kind === "projection") {
394
+ validateBlockEntryLengths(errors, statement, fieldMap, "endpoints", 7);
395
+ validateBlockEntryLengths(errors, statement, fieldMap, "error_responses", 3);
396
+ validateBlockEntryLengths(errors, statement, fieldMap, "wire_fields", 5);
397
+ validateBlockEntryLengths(errors, statement, fieldMap, "responses", 3);
398
+ validateBlockEntryLengths(errors, statement, fieldMap, "preconditions", 9);
399
+ validateBlockEntryLengths(errors, statement, fieldMap, "idempotency", 7);
400
+ validateBlockEntryLengths(errors, statement, fieldMap, "cache", 11);
401
+ validateBlockEntryLengths(errors, statement, fieldMap, "delete_semantics", 7);
402
+ validateBlockEntryLengths(errors, statement, fieldMap, "async_jobs", 11);
403
+ validateBlockEntryLengths(errors, statement, fieldMap, "async_status", 11);
404
+ validateBlockEntryLengths(errors, statement, fieldMap, "downloads", 7);
405
+ validateBlockEntryLengths(errors, statement, fieldMap, "authorization", 3);
406
+ validateBlockEntryLengths(errors, statement, fieldMap, "callbacks", 11);
407
+ validateBlockEntryLengths(errors, statement, fieldMap, "screens", 4);
408
+ validateBlockEntryLengths(errors, statement, fieldMap, "collection_views", 4);
409
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_actions", 6);
410
+ validateBlockEntryLengths(errors, statement, fieldMap, "visibility_rules", 5);
411
+ validateBlockEntryLengths(errors, statement, fieldMap, "field_lookups", 8);
412
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_routes", 4);
413
+ validateBlockEntryLengths(errors, statement, fieldMap, "web_hints", 4);
414
+ validateBlockEntryLengths(errors, statement, fieldMap, "ios_hints", 4);
415
+ validateBlockEntryLengths(errors, statement, fieldMap, "app_shell", 2);
416
+ validateBlockEntryLengths(errors, statement, fieldMap, "navigation", 2);
417
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_regions", 4);
418
+ validateBlockEntryLengths(errors, statement, fieldMap, "tables", 3);
419
+ validateBlockEntryLengths(errors, statement, fieldMap, "columns", 5);
420
+ validateBlockEntryLengths(errors, statement, fieldMap, "keys", 3);
421
+ validateBlockEntryLengths(errors, statement, fieldMap, "indexes", 3);
422
+ validateBlockEntryLengths(errors, statement, fieldMap, "relations", 6);
423
+ validateBlockEntryLengths(errors, statement, fieldMap, "lifecycle", 3);
424
+ }
344
425
  }
345
426
 
346
427
  function validateStatus(errors, statement, fieldMap) {
@@ -456,7 +537,7 @@ function validateReferenceKinds(errors, statement, fieldMap, registry) {
456
537
  pitch: ["pitch"],
457
538
  requirement: null,
458
539
  from_requirement: ["requirement"],
459
- affects: ["capability", "entity", "rule", "projection", "component", "orchestration", "operation"],
540
+ affects: ["capability", "entity", "rule", "projection", "widget", "orchestration", "operation"],
460
541
  introduces_rules: ["rule"],
461
542
  respects_rules: ["rule"],
462
543
  decisions: ["decision"],
@@ -798,7 +879,7 @@ function validateProjectionHttp(errors, statement, fieldMap, registry) {
798
879
  return;
799
880
  }
800
881
 
801
- const httpField = fieldMap.get("http")?.[0];
882
+ const httpField = fieldMap.get("endpoints")?.[0];
802
883
  if (!httpField || httpField.value.type !== "block") {
803
884
  return;
804
885
  }
@@ -879,7 +960,7 @@ function validateProjectionHttpErrors(errors, statement, fieldMap, registry) {
879
960
  return;
880
961
  }
881
962
 
882
- const httpErrorsField = fieldMap.get("http_errors")?.[0];
963
+ const httpErrorsField = fieldMap.get("error_responses")?.[0];
883
964
  if (!httpErrorsField || httpErrorsField.value.type !== "block") {
884
965
  return;
885
966
  }
@@ -891,20 +972,20 @@ function validateProjectionHttpErrors(errors, statement, fieldMap, registry) {
891
972
 
892
973
  const target = registry.get(capabilityId);
893
974
  if (!target) {
894
- pushError(errors, `Projection ${statement.id} http_errors references missing capability '${capabilityId}'`, entry.loc);
975
+ pushError(errors, `Projection ${statement.id} error_responses references missing capability '${capabilityId}'`, entry.loc);
895
976
  continue;
896
977
  }
897
978
  if (target.kind !== "capability") {
898
- pushError(errors, `Projection ${statement.id} http_errors must target a capability, found ${target.kind} '${target.id}'`, entry.loc);
979
+ pushError(errors, `Projection ${statement.id} error_responses must target a capability, found ${target.kind} '${target.id}'`, entry.loc);
899
980
  }
900
981
  if (!realized.has(capabilityId)) {
901
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
982
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
902
983
  }
903
984
  if (!/^\d{3}$/.test(status || "")) {
904
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must use a 3-digit status`, entry.loc);
985
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must use a 3-digit status`, entry.loc);
905
986
  }
906
987
  if (!errorCode) {
907
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must include an error code`, entry.loc);
988
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must include an error code`, entry.loc);
908
989
  }
909
990
  }
910
991
  }
@@ -939,7 +1020,7 @@ function validateProjectionHttpFields(errors, statement, fieldMap, registry) {
939
1020
  return;
940
1021
  }
941
1022
 
942
- const httpFieldsField = fieldMap.get("http_fields")?.[0];
1023
+ const httpFieldsField = fieldMap.get("wire_fields")?.[0];
943
1024
  if (!httpFieldsField || httpFieldsField.value.type !== "block") {
944
1025
  return;
945
1026
  }
@@ -951,34 +1032,34 @@ function validateProjectionHttpFields(errors, statement, fieldMap, registry) {
951
1032
 
952
1033
  const capability = registry.get(capabilityId);
953
1034
  if (!capability) {
954
- pushError(errors, `Projection ${statement.id} http_fields references missing capability '${capabilityId}'`, entry.loc);
1035
+ pushError(errors, `Projection ${statement.id} wire_fields references missing capability '${capabilityId}'`, entry.loc);
955
1036
  continue;
956
1037
  }
957
1038
  if (capability.kind !== "capability") {
958
- pushError(errors, `Projection ${statement.id} http_fields must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1039
+ pushError(errors, `Projection ${statement.id} wire_fields must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
959
1040
  }
960
1041
  if (!realized.has(capabilityId)) {
961
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1042
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
962
1043
  }
963
1044
  if (!["input", "output"].includes(direction)) {
964
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has invalid direction '${direction}'`, entry.loc);
1045
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has invalid direction '${direction}'`, entry.loc);
965
1046
  }
966
1047
  if (keywordIn !== "in") {
967
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must use 'in' before the location`, entry.loc);
1048
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must use 'in' before the location`, entry.loc);
968
1049
  }
969
1050
  if (!["path", "query", "header", "body"].includes(location)) {
970
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has invalid location '${location}'`, entry.loc);
1051
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has invalid location '${location}'`, entry.loc);
971
1052
  }
972
1053
  if (maybeAs && maybeAs !== "as") {
973
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has unexpected token '${maybeAs}'`, entry.loc);
1054
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has unexpected token '${maybeAs}'`, entry.loc);
974
1055
  }
975
1056
  if (maybeAs === "as" && !maybeWireName) {
976
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must provide a wire name after 'as'`, entry.loc);
1057
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must provide a wire name after 'as'`, entry.loc);
977
1058
  }
978
1059
 
979
1060
  const availableFields = resolveCapabilityContractFields(registry, capabilityId, direction);
980
1061
  if (fieldName && availableFields.size > 0 && !availableFields.has(fieldName)) {
981
- pushError(errors, `Projection ${statement.id} http_fields references unknown ${direction} field '${fieldName}' on ${capabilityId}`, entry.loc);
1062
+ pushError(errors, `Projection ${statement.id} wire_fields references unknown ${direction} field '${fieldName}' on ${capabilityId}`, entry.loc);
982
1063
  }
983
1064
  }
984
1065
  }
@@ -988,7 +1069,7 @@ function validateProjectionHttpResponses(errors, statement, fieldMap, registry)
988
1069
  return;
989
1070
  }
990
1071
 
991
- const httpResponsesField = fieldMap.get("http_responses")?.[0];
1072
+ const httpResponsesField = fieldMap.get("responses")?.[0];
992
1073
  if (!httpResponsesField || httpResponsesField.value.type !== "block") {
993
1074
  return;
994
1075
  }
@@ -1000,86 +1081,86 @@ function validateProjectionHttpResponses(errors, statement, fieldMap, registry)
1000
1081
  const capability = registry.get(capabilityId);
1001
1082
 
1002
1083
  if (!capability) {
1003
- pushError(errors, `Projection ${statement.id} http_responses references missing capability '${capabilityId}'`, entry.loc);
1084
+ pushError(errors, `Projection ${statement.id} responses references missing capability '${capabilityId}'`, entry.loc);
1004
1085
  continue;
1005
1086
  }
1006
1087
  if (capability.kind !== "capability") {
1007
- pushError(errors, `Projection ${statement.id} http_responses must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1088
+ pushError(errors, `Projection ${statement.id} responses must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1008
1089
  }
1009
1090
  if (!realized.has(capabilityId)) {
1010
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1091
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1011
1092
  }
1012
1093
 
1013
1094
  const directives = parseProjectionHttpResponsesDirectives(tokens.slice(1));
1014
1095
  for (const message of directives.errors) {
1015
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' ${message}`, entry.loc);
1096
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' ${message}`, entry.loc);
1016
1097
  }
1017
1098
 
1018
1099
  if (!directives.mode) {
1019
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'mode'`, entry.loc);
1100
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'mode'`, entry.loc);
1020
1101
  }
1021
1102
 
1022
1103
  const mode = directives.mode;
1023
1104
  if (mode && !["item", "collection", "paged", "cursor"].includes(mode)) {
1024
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1105
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1025
1106
  }
1026
1107
 
1027
1108
  const itemShapeId = directives.item;
1028
1109
  if (mode && mode !== "item" && !itemShapeId) {
1029
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'item' for mode '${mode}'`, entry.loc);
1110
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'item' for mode '${mode}'`, entry.loc);
1030
1111
  }
1031
1112
  if (itemShapeId) {
1032
1113
  const itemShape = registry.get(itemShapeId);
1033
1114
  if (!itemShape) {
1034
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' references missing shape '${itemShapeId}'`, entry.loc);
1115
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' references missing shape '${itemShapeId}'`, entry.loc);
1035
1116
  } else if (itemShape.kind !== "shape") {
1036
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must reference a shape for 'item', found ${itemShape.kind} '${itemShape.id}'`, entry.loc);
1117
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must reference a shape for 'item', found ${itemShape.kind} '${itemShape.id}'`, entry.loc);
1037
1118
  }
1038
1119
  }
1039
1120
 
1040
1121
  if (mode === "cursor") {
1041
1122
  if (!directives.cursor?.requestAfter) {
1042
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'cursor request_after <field>'`, entry.loc);
1123
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'cursor request_after <field>'`, entry.loc);
1043
1124
  }
1044
1125
  if (!directives.cursor?.responseNext) {
1045
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'cursor response_next <wire_name>'`, entry.loc);
1126
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'cursor response_next <wire_name>'`, entry.loc);
1046
1127
  }
1047
1128
  if (!directives.limit) {
1048
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'limit field <field> default <n> max <n>'`, entry.loc);
1129
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'limit field <field> default <n> max <n>'`, entry.loc);
1049
1130
  }
1050
1131
  if (!directives.sort) {
1051
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'sort by <field> direction <asc|desc>'`, entry.loc);
1132
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'sort by <field> direction <asc|desc>'`, entry.loc);
1052
1133
  }
1053
1134
  }
1054
1135
 
1055
1136
  if (directives.sort && !["asc", "desc"].includes(directives.sort.direction || "")) {
1056
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' has invalid sort direction '${directives.sort.direction}'`, entry.loc);
1137
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid sort direction '${directives.sort.direction}'`, entry.loc);
1057
1138
  }
1058
1139
 
1059
1140
  if (directives.total && !["true", "false"].includes(directives.total.included || "")) {
1060
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' has invalid total included value '${directives.total.included}'`, entry.loc);
1141
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid total included value '${directives.total.included}'`, entry.loc);
1061
1142
  }
1062
1143
 
1063
1144
  if (directives.limit) {
1064
1145
  const defaultValue = Number.parseInt(directives.limit.defaultValue || "", 10);
1065
1146
  const maxValue = Number.parseInt(directives.limit.maxValue || "", 10);
1066
1147
  if (!Number.isInteger(defaultValue) || !Number.isInteger(maxValue)) {
1067
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must use integer default/max values for 'limit'`, entry.loc);
1148
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must use integer default/max values for 'limit'`, entry.loc);
1068
1149
  } else if (defaultValue > maxValue) {
1069
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must use default <= max for 'limit'`, entry.loc);
1150
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must use default <= max for 'limit'`, entry.loc);
1070
1151
  }
1071
1152
  }
1072
1153
 
1073
1154
  const inputFields = resolveCapabilityContractFields(registry, capabilityId, "input");
1074
1155
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1075
1156
  if (directives.cursor?.requestAfter && inputFields.size > 0 && !inputFields.has(directives.cursor.requestAfter)) {
1076
- pushError(errors, `Projection ${statement.id} http_responses references unknown input field '${directives.cursor.requestAfter}' for cursor request_after on ${capabilityId}`, entry.loc);
1157
+ pushError(errors, `Projection ${statement.id} responses references unknown input field '${directives.cursor.requestAfter}' for cursor request_after on ${capabilityId}`, entry.loc);
1077
1158
  }
1078
1159
  if (directives.limit?.field && inputFields.size > 0 && !inputFields.has(directives.limit.field)) {
1079
- pushError(errors, `Projection ${statement.id} http_responses references unknown input field '${directives.limit.field}' for limit on ${capabilityId}`, entry.loc);
1160
+ pushError(errors, `Projection ${statement.id} responses references unknown input field '${directives.limit.field}' for limit on ${capabilityId}`, entry.loc);
1080
1161
  }
1081
1162
  if (directives.sort?.field && outputFields.size > 0 && !outputFields.has(directives.sort.field)) {
1082
- pushError(errors, `Projection ${statement.id} http_responses references unknown output field '${directives.sort.field}' for sort on ${capabilityId}`, entry.loc);
1163
+ pushError(errors, `Projection ${statement.id} responses references unknown output field '${directives.sort.field}' for sort on ${capabilityId}`, entry.loc);
1083
1164
  }
1084
1165
  }
1085
1166
  }
@@ -1089,7 +1170,7 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1089
1170
  return;
1090
1171
  }
1091
1172
 
1092
- const httpPreconditionsField = fieldMap.get("http_preconditions")?.[0];
1173
+ const httpPreconditionsField = fieldMap.get("preconditions")?.[0];
1093
1174
  if (!httpPreconditionsField || httpPreconditionsField.value.type !== "block") {
1094
1175
  return;
1095
1176
  }
@@ -1101,14 +1182,14 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1101
1182
  const capability = registry.get(capabilityId);
1102
1183
 
1103
1184
  if (!capability) {
1104
- pushError(errors, `Projection ${statement.id} http_preconditions references missing capability '${capabilityId}'`, entry.loc);
1185
+ pushError(errors, `Projection ${statement.id} preconditions references missing capability '${capabilityId}'`, entry.loc);
1105
1186
  continue;
1106
1187
  }
1107
1188
  if (capability.kind !== "capability") {
1108
- pushError(errors, `Projection ${statement.id} http_preconditions must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1189
+ pushError(errors, `Projection ${statement.id} preconditions must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1109
1190
  }
1110
1191
  if (!realized.has(capabilityId)) {
1111
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1192
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1112
1193
  }
1113
1194
 
1114
1195
  const directives = new Map();
@@ -1116,7 +1197,7 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1116
1197
  const key = tokens[i];
1117
1198
  const value = tokens[i + 1];
1118
1199
  if (!value) {
1119
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1200
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1120
1201
  continue;
1121
1202
  }
1122
1203
  directives.set(key, value);
@@ -1124,30 +1205,30 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1124
1205
 
1125
1206
  for (const requiredKey of ["header", "required", "error", "source", "code"]) {
1126
1207
  if (!directives.has(requiredKey)) {
1127
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1208
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1128
1209
  }
1129
1210
  }
1130
1211
 
1131
1212
  for (const key of directives.keys()) {
1132
1213
  if (!["header", "required", "error", "source", "code"].includes(key)) {
1133
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1214
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1134
1215
  }
1135
1216
  }
1136
1217
 
1137
1218
  const required = directives.get("required");
1138
1219
  if (required && !["true", "false"].includes(required)) {
1139
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1220
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1140
1221
  }
1141
1222
 
1142
1223
  const errorStatus = directives.get("error");
1143
1224
  if (errorStatus && !/^\d{3}$/.test(errorStatus)) {
1144
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1225
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1145
1226
  }
1146
1227
 
1147
1228
  const sourceField = directives.get("source");
1148
1229
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1149
1230
  if (sourceField && outputFields.size > 0 && !outputFields.has(sourceField)) {
1150
- pushError(errors, `Projection ${statement.id} http_preconditions references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1231
+ pushError(errors, `Projection ${statement.id} preconditions references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1151
1232
  }
1152
1233
  }
1153
1234
  }
@@ -1157,7 +1238,7 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1157
1238
  return;
1158
1239
  }
1159
1240
 
1160
- const httpIdempotencyField = fieldMap.get("http_idempotency")?.[0];
1241
+ const httpIdempotencyField = fieldMap.get("idempotency")?.[0];
1161
1242
  if (!httpIdempotencyField || httpIdempotencyField.value.type !== "block") {
1162
1243
  return;
1163
1244
  }
@@ -1169,14 +1250,14 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1169
1250
  const capability = registry.get(capabilityId);
1170
1251
 
1171
1252
  if (!capability) {
1172
- pushError(errors, `Projection ${statement.id} http_idempotency references missing capability '${capabilityId}'`, entry.loc);
1253
+ pushError(errors, `Projection ${statement.id} idempotency references missing capability '${capabilityId}'`, entry.loc);
1173
1254
  continue;
1174
1255
  }
1175
1256
  if (capability.kind !== "capability") {
1176
- pushError(errors, `Projection ${statement.id} http_idempotency must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1257
+ pushError(errors, `Projection ${statement.id} idempotency must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1177
1258
  }
1178
1259
  if (!realized.has(capabilityId)) {
1179
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1260
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1180
1261
  }
1181
1262
 
1182
1263
  const directives = new Map();
@@ -1184,7 +1265,7 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1184
1265
  const key = tokens[i];
1185
1266
  const value = tokens[i + 1];
1186
1267
  if (!value) {
1187
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1268
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1188
1269
  continue;
1189
1270
  }
1190
1271
  directives.set(key, value);
@@ -1192,24 +1273,24 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1192
1273
 
1193
1274
  for (const requiredKey of ["header", "required", "error", "code"]) {
1194
1275
  if (!directives.has(requiredKey)) {
1195
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1276
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1196
1277
  }
1197
1278
  }
1198
1279
 
1199
1280
  for (const key of directives.keys()) {
1200
1281
  if (!["header", "required", "error", "code"].includes(key)) {
1201
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1282
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1202
1283
  }
1203
1284
  }
1204
1285
 
1205
1286
  const required = directives.get("required");
1206
1287
  if (required && !["true", "false"].includes(required)) {
1207
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1288
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1208
1289
  }
1209
1290
 
1210
1291
  const errorStatus = directives.get("error");
1211
1292
  if (errorStatus && !/^\d{3}$/.test(errorStatus)) {
1212
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1293
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1213
1294
  }
1214
1295
  }
1215
1296
  }
@@ -1219,13 +1300,13 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1219
1300
  return;
1220
1301
  }
1221
1302
 
1222
- const httpCacheField = fieldMap.get("http_cache")?.[0];
1303
+ const httpCacheField = fieldMap.get("cache")?.[0];
1223
1304
  if (!httpCacheField || httpCacheField.value.type !== "block") {
1224
1305
  return;
1225
1306
  }
1226
1307
 
1227
1308
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1228
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1309
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1229
1310
  const httpMethodsByCapability = new Map();
1230
1311
 
1231
1312
  for (const entry of httpEntries) {
@@ -1245,14 +1326,14 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1245
1326
  const capability = registry.get(capabilityId);
1246
1327
 
1247
1328
  if (!capability) {
1248
- pushError(errors, `Projection ${statement.id} http_cache references missing capability '${capabilityId}'`, entry.loc);
1329
+ pushError(errors, `Projection ${statement.id} cache references missing capability '${capabilityId}'`, entry.loc);
1249
1330
  continue;
1250
1331
  }
1251
1332
  if (capability.kind !== "capability") {
1252
- pushError(errors, `Projection ${statement.id} http_cache must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1333
+ pushError(errors, `Projection ${statement.id} cache must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1253
1334
  }
1254
1335
  if (!realized.has(capabilityId)) {
1255
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1336
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1256
1337
  }
1257
1338
 
1258
1339
  const directives = new Map();
@@ -1260,7 +1341,7 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1260
1341
  const key = tokens[i];
1261
1342
  const value = tokens[i + 1];
1262
1343
  if (!value) {
1263
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1344
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1264
1345
  continue;
1265
1346
  }
1266
1347
  directives.set(key, value);
@@ -1268,35 +1349,35 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1268
1349
 
1269
1350
  for (const requiredKey of ["response_header", "request_header", "required", "not_modified", "source", "code"]) {
1270
1351
  if (!directives.has(requiredKey)) {
1271
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1352
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1272
1353
  }
1273
1354
  }
1274
1355
 
1275
1356
  for (const key of directives.keys()) {
1276
1357
  if (!["response_header", "request_header", "required", "not_modified", "source", "code"].includes(key)) {
1277
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1358
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1278
1359
  }
1279
1360
  }
1280
1361
 
1281
1362
  const required = directives.get("required");
1282
1363
  if (required && !["true", "false"].includes(required)) {
1283
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1364
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1284
1365
  }
1285
1366
 
1286
1367
  const notModifiedStatus = directives.get("not_modified");
1287
1368
  if (notModifiedStatus && notModifiedStatus !== "304") {
1288
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must use 304 for 'not_modified'`, entry.loc);
1369
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must use 304 for 'not_modified'`, entry.loc);
1289
1370
  }
1290
1371
 
1291
1372
  const sourceField = directives.get("source");
1292
1373
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1293
1374
  if (sourceField && outputFields.size > 0 && !outputFields.has(sourceField)) {
1294
- pushError(errors, `Projection ${statement.id} http_cache references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1375
+ pushError(errors, `Projection ${statement.id} cache references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1295
1376
  }
1296
1377
 
1297
1378
  const method = httpMethodsByCapability.get(capabilityId);
1298
1379
  if (method && method !== "GET") {
1299
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' requires an HTTP GET realization, found '${method}'`, entry.loc);
1380
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' requires an HTTP GET realization, found '${method}'`, entry.loc);
1300
1381
  }
1301
1382
  }
1302
1383
  }
@@ -1306,7 +1387,7 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1306
1387
  return;
1307
1388
  }
1308
1389
 
1309
- const httpDeleteField = fieldMap.get("http_delete")?.[0];
1390
+ const httpDeleteField = fieldMap.get("delete_semantics")?.[0];
1310
1391
  if (!httpDeleteField || httpDeleteField.value.type !== "block") {
1311
1392
  return;
1312
1393
  }
@@ -1318,14 +1399,14 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1318
1399
  const capability = registry.get(capabilityId);
1319
1400
 
1320
1401
  if (!capability) {
1321
- pushError(errors, `Projection ${statement.id} http_delete references missing capability '${capabilityId}'`, entry.loc);
1402
+ pushError(errors, `Projection ${statement.id} delete_semantics references missing capability '${capabilityId}'`, entry.loc);
1322
1403
  continue;
1323
1404
  }
1324
1405
  if (capability.kind !== "capability") {
1325
- pushError(errors, `Projection ${statement.id} http_delete must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1406
+ pushError(errors, `Projection ${statement.id} delete_semantics must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1326
1407
  }
1327
1408
  if (!realized.has(capabilityId)) {
1328
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1409
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1329
1410
  }
1330
1411
 
1331
1412
  const directives = new Map();
@@ -1333,7 +1414,7 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1333
1414
  const key = tokens[i];
1334
1415
  const value = tokens[i + 1];
1335
1416
  if (!value) {
1336
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1417
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1337
1418
  continue;
1338
1419
  }
1339
1420
  directives.set(key, value);
@@ -1341,34 +1422,34 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1341
1422
 
1342
1423
  for (const requiredKey of ["mode", "response"]) {
1343
1424
  if (!directives.has(requiredKey)) {
1344
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1425
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1345
1426
  }
1346
1427
  }
1347
1428
 
1348
1429
  for (const key of directives.keys()) {
1349
1430
  if (!["mode", "field", "value", "response"].includes(key)) {
1350
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1431
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1351
1432
  }
1352
1433
  }
1353
1434
 
1354
1435
  const mode = directives.get("mode");
1355
1436
  if (mode && !["soft", "hard"].includes(mode)) {
1356
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1437
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1357
1438
  }
1358
1439
 
1359
1440
  const response = directives.get("response");
1360
1441
  if (response && !["none", "body"].includes(response)) {
1361
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has invalid response '${response}'`, entry.loc);
1442
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has invalid response '${response}'`, entry.loc);
1362
1443
  }
1363
1444
 
1364
1445
  if (mode === "soft") {
1365
1446
  if (!directives.has("field") || !directives.has("value")) {
1366
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' must include 'field' and 'value' for soft deletes`, entry.loc);
1447
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must include 'field' and 'value' for soft deletes`, entry.loc);
1367
1448
  }
1368
1449
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1369
1450
  const fieldName = directives.get("field");
1370
1451
  if (fieldName && outputFields.size > 0 && !outputFields.has(fieldName)) {
1371
- pushError(errors, `Projection ${statement.id} http_delete references unknown output field '${fieldName}' on ${capabilityId}`, entry.loc);
1452
+ pushError(errors, `Projection ${statement.id} delete_semantics references unknown output field '${fieldName}' on ${capabilityId}`, entry.loc);
1372
1453
  }
1373
1454
  }
1374
1455
  }
@@ -1379,13 +1460,13 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1379
1460
  return;
1380
1461
  }
1381
1462
 
1382
- const httpAsyncField = fieldMap.get("http_async")?.[0];
1463
+ const httpAsyncField = fieldMap.get("async_jobs")?.[0];
1383
1464
  if (!httpAsyncField || httpAsyncField.value.type !== "block") {
1384
1465
  return;
1385
1466
  }
1386
1467
 
1387
1468
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1388
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1469
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1389
1470
  const httpDirectivesByCapability = new Map();
1390
1471
  for (const entry of httpEntries) {
1391
1472
  const tokens = blockSymbolItems(entry).map((item) => item.value);
@@ -1402,14 +1483,14 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1402
1483
  const capability = registry.get(capabilityId);
1403
1484
 
1404
1485
  if (!capability) {
1405
- pushError(errors, `Projection ${statement.id} http_async references missing capability '${capabilityId}'`, entry.loc);
1486
+ pushError(errors, `Projection ${statement.id} async_jobs references missing capability '${capabilityId}'`, entry.loc);
1406
1487
  continue;
1407
1488
  }
1408
1489
  if (capability.kind !== "capability") {
1409
- pushError(errors, `Projection ${statement.id} http_async must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1490
+ pushError(errors, `Projection ${statement.id} async_jobs must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1410
1491
  }
1411
1492
  if (!realized.has(capabilityId)) {
1412
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1493
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1413
1494
  }
1414
1495
 
1415
1496
  const directives = new Map();
@@ -1417,7 +1498,7 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1417
1498
  const key = tokens[i];
1418
1499
  const value = tokens[i + 1];
1419
1500
  if (!value) {
1420
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1501
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1421
1502
  continue;
1422
1503
  }
1423
1504
  directives.set(key, value);
@@ -1425,33 +1506,33 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1425
1506
 
1426
1507
  for (const requiredKey of ["mode", "accepted", "location_header", "retry_after_header", "status_path", "status_capability", "job"]) {
1427
1508
  if (!directives.has(requiredKey)) {
1428
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1509
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1429
1510
  }
1430
1511
  }
1431
1512
 
1432
1513
  for (const key of directives.keys()) {
1433
1514
  if (!["mode", "accepted", "location_header", "retry_after_header", "status_path", "status_capability", "job"].includes(key)) {
1434
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1515
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1435
1516
  }
1436
1517
  }
1437
1518
 
1438
1519
  const mode = directives.get("mode");
1439
1520
  if (mode && mode !== "job") {
1440
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1521
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1441
1522
  }
1442
1523
 
1443
1524
  const accepted = directives.get("accepted");
1444
1525
  if (accepted && accepted !== "202") {
1445
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must use 202 for 'accepted'`, entry.loc);
1526
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must use 202 for 'accepted'`, entry.loc);
1446
1527
  }
1447
1528
 
1448
1529
  const jobShapeId = directives.get("job");
1449
1530
  if (jobShapeId) {
1450
1531
  const jobShape = registry.get(jobShapeId);
1451
1532
  if (!jobShape) {
1452
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' references missing shape '${jobShapeId}'`, entry.loc);
1533
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' references missing shape '${jobShapeId}'`, entry.loc);
1453
1534
  } else if (jobShape.kind !== "shape") {
1454
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must reference a shape for 'job', found ${jobShape.kind} '${jobShape.id}'`, entry.loc);
1535
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must reference a shape for 'job', found ${jobShape.kind} '${jobShape.id}'`, entry.loc);
1455
1536
  }
1456
1537
  }
1457
1538
 
@@ -1459,25 +1540,25 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1459
1540
  if (statusCapabilityId) {
1460
1541
  const statusCapability = registry.get(statusCapabilityId);
1461
1542
  if (!statusCapability) {
1462
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' references missing status capability '${statusCapabilityId}'`, entry.loc);
1543
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' references missing status capability '${statusCapabilityId}'`, entry.loc);
1463
1544
  } else if (statusCapability.kind !== "capability") {
1464
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must reference a capability for 'status_capability', found ${statusCapability.kind} '${statusCapability.id}'`, entry.loc);
1545
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must reference a capability for 'status_capability', found ${statusCapability.kind} '${statusCapability.id}'`, entry.loc);
1465
1546
  } else if (!realized.has(statusCapabilityId)) {
1466
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' status capability '${statusCapabilityId}' must also appear in 'realizes'`, entry.loc);
1547
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status capability '${statusCapabilityId}' must also appear in 'realizes'`, entry.loc);
1467
1548
  }
1468
1549
 
1469
1550
  const statusHttp = httpDirectivesByCapability.get(statusCapabilityId);
1470
1551
  if (statusHttp?.get("method") && statusHttp.get("method") !== "GET") {
1471
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' status capability '${statusCapabilityId}' must use HTTP GET`, entry.loc);
1552
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status capability '${statusCapabilityId}' must use HTTP GET`, entry.loc);
1472
1553
  }
1473
1554
  if (statusHttp?.get("path") && directives.get("status_path") && statusHttp.get("path") !== directives.get("status_path")) {
1474
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' status_path must match the path for '${statusCapabilityId}'`, entry.loc);
1555
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status_path must match the path for '${statusCapabilityId}'`, entry.loc);
1475
1556
  }
1476
1557
  }
1477
1558
 
1478
1559
  const statusPath = directives.get("status_path");
1479
1560
  if (statusPath && !statusPath.startsWith("/")) {
1480
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must use an absolute path for 'status_path'`, entry.loc);
1561
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must use an absolute path for 'status_path'`, entry.loc);
1481
1562
  }
1482
1563
  }
1483
1564
  }
@@ -1487,13 +1568,13 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1487
1568
  return;
1488
1569
  }
1489
1570
 
1490
- const httpStatusField = fieldMap.get("http_status")?.[0];
1571
+ const httpStatusField = fieldMap.get("async_status")?.[0];
1491
1572
  if (!httpStatusField || httpStatusField.value.type !== "block") {
1492
1573
  return;
1493
1574
  }
1494
1575
 
1495
1576
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1496
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1577
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1497
1578
  const httpMethodsByCapability = new Map();
1498
1579
  for (const entry of httpEntries) {
1499
1580
  const tokens = blockSymbolItems(entry).map((item) => item.value);
@@ -1511,14 +1592,14 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1511
1592
  const capability = registry.get(capabilityId);
1512
1593
 
1513
1594
  if (!capability) {
1514
- pushError(errors, `Projection ${statement.id} http_status references missing capability '${capabilityId}'`, entry.loc);
1595
+ pushError(errors, `Projection ${statement.id} async_status references missing capability '${capabilityId}'`, entry.loc);
1515
1596
  continue;
1516
1597
  }
1517
1598
  if (capability.kind !== "capability") {
1518
- pushError(errors, `Projection ${statement.id} http_status must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1599
+ pushError(errors, `Projection ${statement.id} async_status must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1519
1600
  }
1520
1601
  if (!realized.has(capabilityId)) {
1521
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1602
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1522
1603
  }
1523
1604
 
1524
1605
  const directives = new Map();
@@ -1526,7 +1607,7 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1526
1607
  const key = tokens[i];
1527
1608
  const value = tokens[i + 1];
1528
1609
  if (!value) {
1529
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1610
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1530
1611
  continue;
1531
1612
  }
1532
1613
  directives.set(key, value);
@@ -1534,13 +1615,13 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1534
1615
 
1535
1616
  for (const requiredKey of ["async_for", "state_field", "completed", "failed"]) {
1536
1617
  if (!directives.has(requiredKey)) {
1537
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1618
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1538
1619
  }
1539
1620
  }
1540
1621
 
1541
1622
  for (const key of directives.keys()) {
1542
1623
  if (!["async_for", "state_field", "completed", "failed", "expired", "download_capability", "download_field", "error_field"].includes(key)) {
1543
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1624
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1544
1625
  }
1545
1626
  }
1546
1627
 
@@ -1548,11 +1629,11 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1548
1629
  if (asyncCapabilityId) {
1549
1630
  const asyncCapability = registry.get(asyncCapabilityId);
1550
1631
  if (!asyncCapability) {
1551
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1632
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1552
1633
  } else if (asyncCapability.kind !== "capability") {
1553
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1634
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1554
1635
  } else if (!realized.has(asyncCapabilityId)) {
1555
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1636
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1556
1637
  }
1557
1638
  }
1558
1639
 
@@ -1563,7 +1644,7 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1563
1644
  ["error_field", directives.get("error_field")]
1564
1645
  ]) {
1565
1646
  if (fieldName && outputFields.size > 0 && !outputFields.has(fieldName)) {
1566
- pushError(errors, `Projection ${statement.id} http_status references unknown output field '${fieldName}' for '${directive}' on ${capabilityId}`, entry.loc);
1647
+ pushError(errors, `Projection ${statement.id} async_status references unknown output field '${fieldName}' for '${directive}' on ${capabilityId}`, entry.loc);
1567
1648
  }
1568
1649
  }
1569
1650
 
@@ -1571,16 +1652,16 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1571
1652
  if (downloadCapabilityId) {
1572
1653
  const downloadCapability = registry.get(downloadCapabilityId);
1573
1654
  if (!downloadCapability) {
1574
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' references missing download capability '${downloadCapabilityId}'`, entry.loc);
1655
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' references missing download capability '${downloadCapabilityId}'`, entry.loc);
1575
1656
  } else if (downloadCapability.kind !== "capability") {
1576
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must reference a capability for 'download_capability', found ${downloadCapability.kind} '${downloadCapability.id}'`, entry.loc);
1657
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must reference a capability for 'download_capability', found ${downloadCapability.kind} '${downloadCapability.id}'`, entry.loc);
1577
1658
  } else if (!realized.has(downloadCapabilityId)) {
1578
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' download capability '${downloadCapabilityId}' must also appear in 'realizes'`, entry.loc);
1659
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' download capability '${downloadCapabilityId}' must also appear in 'realizes'`, entry.loc);
1579
1660
  }
1580
1661
 
1581
1662
  const method = httpMethodsByCapability.get(downloadCapabilityId);
1582
1663
  if (method && method !== "GET") {
1583
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' download capability '${downloadCapabilityId}' must use HTTP GET`, entry.loc);
1664
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' download capability '${downloadCapabilityId}' must use HTTP GET`, entry.loc);
1584
1665
  }
1585
1666
  }
1586
1667
  }
@@ -1591,7 +1672,7 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1591
1672
  return;
1592
1673
  }
1593
1674
 
1594
- const httpDownloadField = fieldMap.get("http_download")?.[0];
1675
+ const httpDownloadField = fieldMap.get("downloads")?.[0];
1595
1676
  if (!httpDownloadField || httpDownloadField.value.type !== "block") {
1596
1677
  return;
1597
1678
  }
@@ -1603,14 +1684,14 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1603
1684
  const capability = registry.get(capabilityId);
1604
1685
 
1605
1686
  if (!capability) {
1606
- pushError(errors, `Projection ${statement.id} http_download references missing capability '${capabilityId}'`, entry.loc);
1687
+ pushError(errors, `Projection ${statement.id} downloads references missing capability '${capabilityId}'`, entry.loc);
1607
1688
  continue;
1608
1689
  }
1609
1690
  if (capability.kind !== "capability") {
1610
- pushError(errors, `Projection ${statement.id} http_download must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1691
+ pushError(errors, `Projection ${statement.id} downloads must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1611
1692
  }
1612
1693
  if (!realized.has(capabilityId)) {
1613
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1694
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1614
1695
  }
1615
1696
 
1616
1697
  const directives = new Map();
@@ -1618,7 +1699,7 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1618
1699
  const key = tokens[i];
1619
1700
  const value = tokens[i + 1];
1620
1701
  if (!value) {
1621
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1702
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1622
1703
  continue;
1623
1704
  }
1624
1705
  directives.set(key, value);
@@ -1626,13 +1707,13 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1626
1707
 
1627
1708
  for (const requiredKey of ["async_for", "media", "disposition"]) {
1628
1709
  if (!directives.has(requiredKey)) {
1629
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1710
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1630
1711
  }
1631
1712
  }
1632
1713
 
1633
1714
  for (const key of directives.keys()) {
1634
1715
  if (!["async_for", "media", "filename", "disposition"].includes(key)) {
1635
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1716
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1636
1717
  }
1637
1718
  }
1638
1719
 
@@ -1640,22 +1721,22 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1640
1721
  if (asyncCapabilityId) {
1641
1722
  const asyncCapability = registry.get(asyncCapabilityId);
1642
1723
  if (!asyncCapability) {
1643
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1724
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1644
1725
  } else if (asyncCapability.kind !== "capability") {
1645
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1726
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1646
1727
  } else if (!realized.has(asyncCapabilityId)) {
1647
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1728
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1648
1729
  }
1649
1730
  }
1650
1731
 
1651
1732
  const media = directives.get("media");
1652
1733
  if (media && !media.includes("/")) {
1653
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must use a valid media type`, entry.loc);
1734
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must use a valid media type`, entry.loc);
1654
1735
  }
1655
1736
 
1656
1737
  const disposition = directives.get("disposition");
1657
1738
  if (disposition && !["attachment", "inline"].includes(disposition)) {
1658
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' has invalid disposition '${disposition}'`, entry.loc);
1739
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' has invalid disposition '${disposition}'`, entry.loc);
1659
1740
  }
1660
1741
  }
1661
1742
  }
@@ -1665,7 +1746,7 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1665
1746
  return;
1666
1747
  }
1667
1748
 
1668
- const httpAuthzField = fieldMap.get("http_authz")?.[0];
1749
+ const httpAuthzField = fieldMap.get("authorization")?.[0];
1669
1750
  if (!httpAuthzField || httpAuthzField.value.type !== "block") {
1670
1751
  return;
1671
1752
  }
@@ -1677,14 +1758,14 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1677
1758
  const capability = registry.get(capabilityId);
1678
1759
 
1679
1760
  if (!capability) {
1680
- pushError(errors, `Projection ${statement.id} http_authz references missing capability '${capabilityId}'`, entry.loc);
1761
+ pushError(errors, `Projection ${statement.id} authorization references missing capability '${capabilityId}'`, entry.loc);
1681
1762
  continue;
1682
1763
  }
1683
1764
  if (capability.kind !== "capability") {
1684
- pushError(errors, `Projection ${statement.id} http_authz must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1765
+ pushError(errors, `Projection ${statement.id} authorization must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1685
1766
  }
1686
1767
  if (!realized.has(capabilityId)) {
1687
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1768
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1688
1769
  }
1689
1770
 
1690
1771
  const directives = new Map();
@@ -1692,7 +1773,7 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1692
1773
  const key = tokens[i];
1693
1774
  const value = tokens[i + 1];
1694
1775
  if (!value) {
1695
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1776
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1696
1777
  continue;
1697
1778
  }
1698
1779
  directives.set(key, value);
@@ -1700,27 +1781,27 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1700
1781
 
1701
1782
  for (const key of directives.keys()) {
1702
1783
  if (!["role", "permission", "claim", "claim_value", "ownership", "ownership_field"].includes(key)) {
1703
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1784
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1704
1785
  }
1705
1786
  }
1706
1787
 
1707
1788
  if (directives.size === 0) {
1708
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' must include at least one directive`, entry.loc);
1789
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' must include at least one directive`, entry.loc);
1709
1790
  }
1710
1791
 
1711
1792
  const ownership = directives.get("ownership");
1712
1793
  if (ownership && !["owner", "owner_or_admin", "project_member", "none"].includes(ownership)) {
1713
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' has invalid ownership '${ownership}'`, entry.loc);
1794
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' has invalid ownership '${ownership}'`, entry.loc);
1714
1795
  }
1715
1796
 
1716
1797
  const ownershipField = directives.get("ownership_field");
1717
1798
  if (ownershipField && (!ownership || ownership === "none")) {
1718
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' cannot declare ownership_field without ownership`, entry.loc);
1799
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' cannot declare ownership_field without ownership`, entry.loc);
1719
1800
  }
1720
1801
 
1721
1802
  const claimValue = directives.get("claim_value");
1722
1803
  if (claimValue && !directives.get("claim")) {
1723
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
1804
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
1724
1805
  }
1725
1806
  }
1726
1807
  }
@@ -1730,7 +1811,7 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1730
1811
  return;
1731
1812
  }
1732
1813
 
1733
- const httpCallbacksField = fieldMap.get("http_callbacks")?.[0];
1814
+ const httpCallbacksField = fieldMap.get("callbacks")?.[0];
1734
1815
  if (!httpCallbacksField || httpCallbacksField.value.type !== "block") {
1735
1816
  return;
1736
1817
  }
@@ -1742,14 +1823,14 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1742
1823
  const capability = registry.get(capabilityId);
1743
1824
 
1744
1825
  if (!capability) {
1745
- pushError(errors, `Projection ${statement.id} http_callbacks references missing capability '${capabilityId}'`, entry.loc);
1826
+ pushError(errors, `Projection ${statement.id} callbacks references missing capability '${capabilityId}'`, entry.loc);
1746
1827
  continue;
1747
1828
  }
1748
1829
  if (capability.kind !== "capability") {
1749
- pushError(errors, `Projection ${statement.id} http_callbacks must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1830
+ pushError(errors, `Projection ${statement.id} callbacks must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1750
1831
  }
1751
1832
  if (!realized.has(capabilityId)) {
1752
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1833
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1753
1834
  }
1754
1835
 
1755
1836
  const directives = new Map();
@@ -1757,7 +1838,7 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1757
1838
  const key = tokens[i];
1758
1839
  const value = tokens[i + 1];
1759
1840
  if (!value) {
1760
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1841
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1761
1842
  continue;
1762
1843
  }
1763
1844
  directives.set(key, value);
@@ -1765,40 +1846,40 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1765
1846
 
1766
1847
  for (const requiredKey of ["event", "target_field", "method", "payload", "success"]) {
1767
1848
  if (!directives.has(requiredKey)) {
1768
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1849
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1769
1850
  }
1770
1851
  }
1771
1852
 
1772
1853
  for (const key of directives.keys()) {
1773
1854
  if (!["event", "target_field", "method", "payload", "success"].includes(key)) {
1774
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1855
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1775
1856
  }
1776
1857
  }
1777
1858
 
1778
1859
  const method = directives.get("method");
1779
1860
  if (method && !["POST", "PUT", "PATCH"].includes(method)) {
1780
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' has invalid method '${method}'`, entry.loc);
1861
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' has invalid method '${method}'`, entry.loc);
1781
1862
  }
1782
1863
 
1783
1864
  const success = directives.get("success");
1784
1865
  if (success && !/^\d{3}$/.test(success)) {
1785
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must use a 3-digit success status`, entry.loc);
1866
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must use a 3-digit success status`, entry.loc);
1786
1867
  }
1787
1868
 
1788
1869
  const payloadShapeId = directives.get("payload");
1789
1870
  if (payloadShapeId) {
1790
1871
  const payloadShape = registry.get(payloadShapeId);
1791
1872
  if (!payloadShape) {
1792
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' references missing shape '${payloadShapeId}'`, entry.loc);
1873
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' references missing shape '${payloadShapeId}'`, entry.loc);
1793
1874
  } else if (payloadShape.kind !== "shape") {
1794
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must reference a shape for 'payload', found ${payloadShape.kind} '${payloadShape.id}'`, entry.loc);
1875
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must reference a shape for 'payload', found ${payloadShape.kind} '${payloadShape.id}'`, entry.loc);
1795
1876
  }
1796
1877
  }
1797
1878
 
1798
1879
  const targetField = directives.get("target_field");
1799
1880
  const inputFields = resolveCapabilityContractFields(registry, capabilityId, "input");
1800
1881
  if (targetField && inputFields.size > 0 && !inputFields.has(targetField)) {
1801
- pushError(errors, `Projection ${statement.id} http_callbacks references unknown input field '${targetField}' on ${capabilityId}`, entry.loc);
1882
+ pushError(errors, `Projection ${statement.id} callbacks references unknown input field '${targetField}' on ${capabilityId}`, entry.loc);
1802
1883
  }
1803
1884
  }
1804
1885
  }
@@ -1909,7 +1990,7 @@ function resolveCapabilityOutputShape(registry, capabilityId) {
1909
1990
  }
1910
1991
 
1911
1992
  function collectProjectionUiScreens(statement, fieldMap) {
1912
- const screensField = fieldMap.get("ui_screens")?.[0];
1993
+ const screensField = fieldMap.get("screens")?.[0];
1913
1994
  if (!screensField || screensField.value.type !== "block") {
1914
1995
  return new Map();
1915
1996
  }
@@ -1954,7 +2035,7 @@ function resolveProjectionUiScreenFieldNames(registry, screenEntry, statement) {
1954
2035
 
1955
2036
  function screenIdsFromProjectionStatement(statement) {
1956
2037
  const screens = new Set();
1957
- for (const entry of blockEntries(getFieldValue(statement, "ui_screens"))) {
2038
+ for (const entry of blockEntries(getFieldValue(statement, "screens"))) {
1958
2039
  const tokens = blockSymbolItems(entry).map((item) => item.value);
1959
2040
  if (tokens[0] === "screen" && tokens[1]) {
1960
2041
  screens.add(tokens[1]);
@@ -1978,7 +2059,7 @@ function collectAvailableUiScreenIds(statement, fieldMap, registry) {
1978
2059
 
1979
2060
  function collectProjectionUiRegionKeys(statement) {
1980
2061
  const keys = new Set();
1981
- for (const entry of blockEntries(getFieldValue(statement, "ui_screen_regions"))) {
2062
+ for (const entry of blockEntries(getFieldValue(statement, "screen_regions"))) {
1982
2063
  const tokens = blockSymbolItems(entry).map((item) => item.value);
1983
2064
  if (tokens[0] === "screen" && tokens[1] && tokens[2] === "region" && tokens[3]) {
1984
2065
  keys.add(`${tokens[1]}:${tokens[3]}`);
@@ -2002,7 +2083,7 @@ function collectAvailableUiRegionKeys(statement, registry) {
2002
2083
 
2003
2084
  function collectProjectionUiRegionPatterns(statement) {
2004
2085
  const patterns = new Map();
2005
- for (const entry of blockEntries(getFieldValue(statement, "ui_screen_regions"))) {
2086
+ for (const entry of blockEntries(getFieldValue(statement, "screen_regions"))) {
2006
2087
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2007
2088
  if (tokens[0] !== "screen" || !tokens[1] || tokens[2] !== "region" || !tokens[3]) {
2008
2089
  continue;
@@ -2056,7 +2137,7 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2056
2137
  return;
2057
2138
  }
2058
2139
 
2059
- const screensField = fieldMap.get("ui_screens")?.[0];
2140
+ const screensField = fieldMap.get("screens")?.[0];
2060
2141
  if (!screensField || screensField.value.type !== "block") {
2061
2142
  return;
2062
2143
  }
@@ -2069,33 +2150,33 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2069
2150
  const [keyword, screenId] = tokens;
2070
2151
 
2071
2152
  if (keyword !== "screen") {
2072
- pushError(errors, `Projection ${statement.id} ui_screens entries must start with 'screen'`, entry.loc);
2153
+ pushError(errors, `Projection ${statement.id} screens entries must start with 'screen'`, entry.loc);
2073
2154
  continue;
2074
2155
  }
2075
2156
  if (!screenId) {
2076
- pushError(errors, `Projection ${statement.id} ui_screens entries must include a screen id`, entry.loc);
2157
+ pushError(errors, `Projection ${statement.id} screens entries must include a screen id`, entry.loc);
2077
2158
  continue;
2078
2159
  }
2079
2160
  if (!IDENTIFIER_PATTERN.test(screenId)) {
2080
- pushError(errors, `Projection ${statement.id} ui_screens has invalid screen id '${screenId}'`, entry.loc);
2161
+ pushError(errors, `Projection ${statement.id} screens has invalid screen id '${screenId}'`, entry.loc);
2081
2162
  }
2082
2163
  if (seenScreens.has(screenId)) {
2083
- pushError(errors, `Projection ${statement.id} ui_screens has duplicate screen id '${screenId}'`, entry.loc);
2164
+ pushError(errors, `Projection ${statement.id} screens has duplicate screen id '${screenId}'`, entry.loc);
2084
2165
  }
2085
2166
  seenScreens.add(screenId);
2086
2167
 
2087
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_screens for '${screenId}'`);
2168
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `screens for '${screenId}'`);
2088
2169
  const kind = directives.get("kind");
2089
2170
  if (!kind) {
2090
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' must include 'kind'`, entry.loc);
2171
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must include 'kind'`, entry.loc);
2091
2172
  }
2092
2173
  if (kind && !UI_SCREEN_KINDS.has(kind)) {
2093
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' has invalid kind '${kind}'`, entry.loc);
2174
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid kind '${kind}'`, entry.loc);
2094
2175
  }
2095
2176
 
2096
2177
  for (const key of directives.keys()) {
2097
2178
  if (!["kind", "title", "load", "item_shape", "view_shape", "input_shape", "submit", "detail_capability", "primary_action", "secondary_action", "destructive_action", "success_navigate", "success_refresh", "empty_title", "empty_body", "terminal_action", "loading_state", "error_state", "unauthorized_state", "not_found_state", "success_state"].includes(key)) {
2098
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' has unknown directive '${key}'`, entry.loc);
2179
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has unknown directive '${key}'`, entry.loc);
2099
2180
  }
2100
2181
  }
2101
2182
 
@@ -2117,43 +2198,43 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2117
2198
  }
2118
2199
  const target = registry.get(targetId);
2119
2200
  if (!target) {
2120
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' references missing ${expectedKind} '${targetId}' for '${key}'`, entry.loc);
2201
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references missing ${expectedKind} '${targetId}' for '${key}'`, entry.loc);
2121
2202
  continue;
2122
2203
  }
2123
2204
  if (target.kind !== expectedKind) {
2124
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' must reference a ${expectedKind} for '${key}', found ${target.kind} '${target.id}'`, entry.loc);
2205
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must reference a ${expectedKind} for '${key}', found ${target.kind} '${target.id}'`, entry.loc);
2125
2206
  }
2126
2207
  if (expectedKind === "capability" && !realized.has(targetId)) {
2127
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' capability '${targetId}' for '${key}' must also appear in 'realizes'`, entry.loc);
2208
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' capability '${targetId}' for '${key}' must also appear in 'realizes'`, entry.loc);
2128
2209
  }
2129
2210
  }
2130
2211
 
2131
2212
  const successNavigate = directives.get("success_navigate");
2132
2213
  const successRefresh = directives.get("success_refresh");
2133
2214
  if (successNavigate && !IDENTIFIER_PATTERN.test(successNavigate)) {
2134
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' has invalid target '${successNavigate}' for 'success_navigate'`, entry.loc);
2215
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successNavigate}' for 'success_navigate'`, entry.loc);
2135
2216
  }
2136
2217
  if (successRefresh && !IDENTIFIER_PATTERN.test(successRefresh)) {
2137
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' has invalid target '${successRefresh}' for 'success_refresh'`, entry.loc);
2218
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successRefresh}' for 'success_refresh'`, entry.loc);
2138
2219
  }
2139
2220
 
2140
2221
  if (kind === "list" && !directives.get("load")) {
2141
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'list' requires 'load'`, entry.loc);
2222
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'list' requires 'load'`, entry.loc);
2142
2223
  }
2143
2224
  if (kind === "detail") {
2144
2225
  if (!directives.get("load")) {
2145
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'detail' requires 'load'`, entry.loc);
2226
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'load'`, entry.loc);
2146
2227
  }
2147
2228
  if (!directives.get("view_shape")) {
2148
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'detail' requires 'view_shape'`, entry.loc);
2229
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'view_shape'`, entry.loc);
2149
2230
  }
2150
2231
  }
2151
2232
  if (kind === "form") {
2152
2233
  if (!directives.get("input_shape")) {
2153
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'form' requires 'input_shape'`, entry.loc);
2234
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'input_shape'`, entry.loc);
2154
2235
  }
2155
2236
  if (!directives.get("submit")) {
2156
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'form' requires 'submit'`, entry.loc);
2237
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'submit'`, entry.loc);
2157
2238
  }
2158
2239
  }
2159
2240
  }
@@ -2168,7 +2249,7 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2168
2249
  for (const key of ["success_navigate", "success_refresh"]) {
2169
2250
  const targetScreenId = directives.get(key);
2170
2251
  if (targetScreenId && !seenScreens.has(targetScreenId)) {
2171
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' references unknown screen '${targetScreenId}' for '${key}'`, entry.loc);
2252
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references unknown screen '${targetScreenId}' for '${key}'`, entry.loc);
2172
2253
  }
2173
2254
  }
2174
2255
  }
@@ -2179,7 +2260,7 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2179
2260
  return;
2180
2261
  }
2181
2262
 
2182
- const collectionsField = fieldMap.get("ui_collections")?.[0];
2263
+ const collectionsField = fieldMap.get("collection_views")?.[0];
2183
2264
  if (!collectionsField || collectionsField.value.type !== "block") {
2184
2265
  return;
2185
2266
  }
@@ -2190,23 +2271,23 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2190
2271
  const [keyword, screenId, operation, value, extra] = tokens;
2191
2272
 
2192
2273
  if (keyword !== "screen") {
2193
- pushError(errors, `Projection ${statement.id} ui_collections entries must start with 'screen'`, entry.loc);
2274
+ pushError(errors, `Projection ${statement.id} collection_views entries must start with 'screen'`, entry.loc);
2194
2275
  continue;
2195
2276
  }
2196
2277
  const screenEntry = screens.get(screenId);
2197
2278
  if (!screenEntry) {
2198
- pushError(errors, `Projection ${statement.id} ui_collections references unknown screen '${screenId}'`, entry.loc);
2279
+ pushError(errors, `Projection ${statement.id} collection_views references unknown screen '${screenId}'`, entry.loc);
2199
2280
  continue;
2200
2281
  }
2201
2282
 
2202
2283
  const screenTokens = blockSymbolItems(screenEntry).map((item) => item.value);
2203
2284
  const screenDirectives = parseUiDirectiveMap(screenTokens, 2, [], statement, screenEntry, "");
2204
2285
  if (screenDirectives.get("kind") !== "list") {
2205
- pushError(errors, `Projection ${statement.id} ui_collections may only target list screens, found '${screenId}'`, entry.loc);
2286
+ pushError(errors, `Projection ${statement.id} collection_views may only target list screens, found '${screenId}'`, entry.loc);
2206
2287
  }
2207
2288
 
2208
2289
  if (!["filter", "search", "pagination", "sort", "group", "view", "refresh"].includes(operation)) {
2209
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid operation '${operation}'`, entry.loc);
2290
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid operation '${operation}'`, entry.loc);
2210
2291
  continue;
2211
2292
  }
2212
2293
 
@@ -2219,43 +2300,43 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2219
2300
 
2220
2301
  if (operation === "filter" || operation === "search") {
2221
2302
  if (!value) {
2222
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must include a field for '${operation}'`, entry.loc);
2303
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must include a field for '${operation}'`, entry.loc);
2223
2304
  } else if (inputFields.size > 0 && !inputFields.has(value)) {
2224
- pushError(errors, `Projection ${statement.id} ui_collections references unknown input field '${value}' for '${operation}' on '${screenId}'`, entry.loc);
2305
+ pushError(errors, `Projection ${statement.id} collection_views references unknown input field '${value}' for '${operation}' on '${screenId}'`, entry.loc);
2225
2306
  }
2226
2307
  }
2227
2308
 
2228
2309
  if (operation === "pagination" && !["cursor", "paged", "none"].includes(value || "")) {
2229
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid pagination '${value}'`, entry.loc);
2310
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid pagination '${value}'`, entry.loc);
2230
2311
  }
2231
2312
 
2232
2313
  if (operation === "sort") {
2233
2314
  if (!value || !extra) {
2234
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must use 'sort <field> <asc|desc>'`, entry.loc);
2315
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must use 'sort <field> <asc|desc>'`, entry.loc);
2235
2316
  } else {
2236
2317
  if (!["asc", "desc"].includes(extra)) {
2237
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid sort direction '${extra}'`, entry.loc);
2318
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid sort direction '${extra}'`, entry.loc);
2238
2319
  }
2239
2320
  if (outputFields.size > 0 && !outputFields.has(value)) {
2240
- pushError(errors, `Projection ${statement.id} ui_collections references unknown output field '${value}' for sort on '${screenId}'`, entry.loc);
2321
+ pushError(errors, `Projection ${statement.id} collection_views references unknown output field '${value}' for sort on '${screenId}'`, entry.loc);
2241
2322
  }
2242
2323
  }
2243
2324
  }
2244
2325
 
2245
2326
  if (operation === "group") {
2246
2327
  if (!value) {
2247
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must include a field for 'group'`, entry.loc);
2328
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must include a field for 'group'`, entry.loc);
2248
2329
  } else if (outputFields.size > 0 && !outputFields.has(value)) {
2249
- pushError(errors, `Projection ${statement.id} ui_collections references unknown output field '${value}' for group on '${screenId}'`, entry.loc);
2330
+ pushError(errors, `Projection ${statement.id} collection_views references unknown output field '${value}' for group on '${screenId}'`, entry.loc);
2250
2331
  }
2251
2332
  }
2252
2333
 
2253
2334
  if (operation === "view" && !UI_COLLECTION_PRESENTATIONS.has(value || "")) {
2254
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid view '${value}'`, entry.loc);
2335
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid view '${value}'`, entry.loc);
2255
2336
  }
2256
2337
 
2257
2338
  if (operation === "refresh" && !["manual", "pull_to_refresh", "auto"].includes(value || "")) {
2258
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid refresh '${value}'`, entry.loc);
2339
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid refresh '${value}'`, entry.loc);
2259
2340
  }
2260
2341
  }
2261
2342
  }
@@ -2265,7 +2346,7 @@ function validateProjectionUiActions(errors, statement, fieldMap, registry) {
2265
2346
  return;
2266
2347
  }
2267
2348
 
2268
- const actionsField = fieldMap.get("ui_actions")?.[0];
2349
+ const actionsField = fieldMap.get("screen_actions")?.[0];
2269
2350
  if (!actionsField || actionsField.value.type !== "block") {
2270
2351
  return;
2271
2352
  }
@@ -2278,34 +2359,34 @@ function validateProjectionUiActions(errors, statement, fieldMap, registry) {
2278
2359
  const [keyword, screenId, actionKeyword, capabilityId, prominenceKeyword, prominence, placementKeyword, placement] = tokens;
2279
2360
 
2280
2361
  if (keyword !== "screen") {
2281
- pushError(errors, `Projection ${statement.id} ui_actions entries must start with 'screen'`, entry.loc);
2362
+ pushError(errors, `Projection ${statement.id} screen_actions entries must start with 'screen'`, entry.loc);
2282
2363
  continue;
2283
2364
  }
2284
2365
  if (!screens.has(screenId)) {
2285
- pushError(errors, `Projection ${statement.id} ui_actions references unknown screen '${screenId}'`, entry.loc);
2366
+ pushError(errors, `Projection ${statement.id} screen_actions references unknown screen '${screenId}'`, entry.loc);
2286
2367
  }
2287
2368
  if (actionKeyword !== "action") {
2288
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' must use 'action'`, entry.loc);
2369
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' must use 'action'`, entry.loc);
2289
2370
  }
2290
2371
  const capability = registry.get(capabilityId);
2291
2372
  if (!capability) {
2292
- pushError(errors, `Projection ${statement.id} ui_actions references missing capability '${capabilityId}'`, entry.loc);
2373
+ pushError(errors, `Projection ${statement.id} screen_actions references missing capability '${capabilityId}'`, entry.loc);
2293
2374
  } else if (capability.kind !== "capability") {
2294
- pushError(errors, `Projection ${statement.id} ui_actions must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2375
+ pushError(errors, `Projection ${statement.id} screen_actions must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2295
2376
  } else if (!realized.has(capabilityId)) {
2296
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' capability '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2377
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' capability '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2297
2378
  }
2298
2379
  if (prominenceKeyword !== "prominence") {
2299
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' must use 'prominence'`, entry.loc);
2380
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' must use 'prominence'`, entry.loc);
2300
2381
  }
2301
2382
  if (!["primary", "secondary", "destructive", "contextual"].includes(prominence || "")) {
2302
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' has invalid prominence '${prominence}'`, entry.loc);
2383
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has invalid prominence '${prominence}'`, entry.loc);
2303
2384
  }
2304
2385
  if (placementKeyword && placementKeyword !== "placement") {
2305
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' has unknown directive '${placementKeyword}'`, entry.loc);
2386
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has unknown directive '${placementKeyword}'`, entry.loc);
2306
2387
  }
2307
2388
  if (placementKeyword === "placement" && !["toolbar", "menu", "bulk", "inline", "footer"].includes(placement || "")) {
2308
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' has invalid placement '${placement}'`, entry.loc);
2389
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has invalid placement '${placement}'`, entry.loc);
2309
2390
  }
2310
2391
  }
2311
2392
  }
@@ -2315,7 +2396,7 @@ function validateProjectionUiAppShell(errors, statement, fieldMap) {
2315
2396
  return;
2316
2397
  }
2317
2398
 
2318
- const shellField = fieldMap.get("ui_app_shell")?.[0];
2399
+ const shellField = fieldMap.get("app_shell")?.[0];
2319
2400
  if (!shellField || shellField.value.type !== "block") {
2320
2401
  return;
2321
2402
  }
@@ -2325,42 +2406,42 @@ function validateProjectionUiAppShell(errors, statement, fieldMap) {
2325
2406
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2326
2407
  const [key, value, extra] = tokens;
2327
2408
  if (!["brand", "shell", "primary_nav", "secondary_nav", "utility_nav", "footer", "global_search", "notifications", "account_menu", "workspace_switcher", "windowing"].includes(key || "")) {
2328
- pushError(errors, `Projection ${statement.id} ui_app_shell has unknown key '${key}'`, entry.loc);
2409
+ pushError(errors, `Projection ${statement.id} app_shell has unknown key '${key}'`, entry.loc);
2329
2410
  continue;
2330
2411
  }
2331
2412
  if (!value) {
2332
- pushError(errors, `Projection ${statement.id} ui_app_shell is missing a value for '${key}'`, entry.loc);
2413
+ pushError(errors, `Projection ${statement.id} app_shell is missing a value for '${key}'`, entry.loc);
2333
2414
  continue;
2334
2415
  }
2335
2416
  if (extra) {
2336
- pushError(errors, `Projection ${statement.id} ui_app_shell '${key}' accepts exactly one value`, entry.loc);
2417
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' accepts exactly one value`, entry.loc);
2337
2418
  }
2338
2419
  if (seenKeys.has(key)) {
2339
- pushError(errors, `Projection ${statement.id} ui_app_shell has duplicate key '${key}'`, entry.loc);
2420
+ pushError(errors, `Projection ${statement.id} app_shell has duplicate key '${key}'`, entry.loc);
2340
2421
  }
2341
2422
  seenKeys.add(key);
2342
2423
 
2343
2424
  if (key === "shell" && !UI_APP_SHELL_KINDS.has(value)) {
2344
- pushError(errors, `Projection ${statement.id} ui_app_shell has invalid shell '${value}'`, entry.loc);
2425
+ pushError(errors, `Projection ${statement.id} app_shell has invalid shell '${value}'`, entry.loc);
2345
2426
  }
2346
2427
  if (["global_search", "notifications", "account_menu", "workspace_switcher"].includes(key) && !["true", "false"].includes(value)) {
2347
- pushError(errors, `Projection ${statement.id} ui_app_shell '${key}' must be true or false`, entry.loc);
2428
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' must be true or false`, entry.loc);
2348
2429
  }
2349
2430
  if (key === "windowing" && !UI_WINDOWING_MODES.has(value)) {
2350
- pushError(errors, `Projection ${statement.id} ui_app_shell has invalid windowing '${value}'`, entry.loc);
2431
+ pushError(errors, `Projection ${statement.id} app_shell has invalid windowing '${value}'`, entry.loc);
2351
2432
  }
2352
2433
  }
2353
2434
  }
2354
2435
 
2355
2436
  const SHARED_UI_SEMANTIC_BLOCKS = [
2356
- "ui_screens",
2357
- "ui_collections",
2358
- "ui_actions",
2359
- "ui_visibility",
2360
- "ui_lookups",
2361
- "ui_app_shell",
2362
- "ui_navigation",
2363
- "ui_screen_regions"
2437
+ "screens",
2438
+ "collection_views",
2439
+ "screen_actions",
2440
+ "visibility_rules",
2441
+ "field_lookups",
2442
+ "app_shell",
2443
+ "navigation",
2444
+ "screen_regions"
2364
2445
  ];
2365
2446
 
2366
2447
  function validateProjectionUiOwnership(errors, statement, fieldMap) {
@@ -2368,26 +2449,26 @@ function validateProjectionUiOwnership(errors, statement, fieldMap) {
2368
2449
  return;
2369
2450
  }
2370
2451
 
2371
- const platform = symbolValue(getFieldValue(statement, "platform"));
2452
+ const platform = symbolValue(getFieldValue(statement, "type"));
2372
2453
  for (const key of SHARED_UI_SEMANTIC_BLOCKS) {
2373
2454
  const field = fieldMap.get(key)?.[0];
2374
2455
  if (!field || field.value.type !== "block") {
2375
2456
  continue;
2376
2457
  }
2377
- if (platform !== "ui_shared") {
2458
+ if (platform !== "ui_contract") {
2378
2459
  pushError(
2379
2460
  errors,
2380
- `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define ui_routes and platform surface hints only`,
2461
+ `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define screen_routes and platform surface hints only`,
2381
2462
  field.loc
2382
2463
  );
2383
2464
  }
2384
2465
  }
2385
2466
 
2386
- const routesField = fieldMap.get("ui_routes")?.[0];
2387
- if (routesField?.value.type === "block" && !["ui_web", "ui_ios"].includes(platform || "")) {
2467
+ const routesField = fieldMap.get("screen_routes")?.[0];
2468
+ if (routesField?.value.type === "block" && !["web_surface", "ios_surface"].includes(platform || "")) {
2388
2469
  pushError(
2389
2470
  errors,
2390
- `Projection ${statement.id} ui_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
2471
+ `Projection ${statement.id} screen_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
2391
2472
  routesField.loc
2392
2473
  );
2393
2474
  }
@@ -2398,13 +2479,13 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2398
2479
  return;
2399
2480
  }
2400
2481
 
2401
- const designField = fieldMap.get("ui_design")?.[0];
2482
+ const designField = fieldMap.get("design_tokens")?.[0];
2402
2483
  if (!designField || designField.value.type !== "block") {
2403
2484
  return;
2404
2485
  }
2405
2486
 
2406
- if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2407
- pushError(errors, `Projection ${statement.id} ui_design belongs on shared UI projections; concrete UI projections inherit semantic design intent through 'realizes'`, designField.loc);
2487
+ if (symbolValue(getFieldValue(statement, "type")) !== "ui_contract") {
2488
+ pushError(errors, `Projection ${statement.id} design_tokens belongs on shared UI projections; concrete UI projections inherit semantic design intent through 'realizes'`, designField.loc);
2408
2489
  }
2409
2490
 
2410
2491
  for (const entry of designField.value.entries) {
@@ -2413,60 +2494,60 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2413
2494
 
2414
2495
  if (key === "density") {
2415
2496
  if (!UI_DESIGN_DENSITIES.has(value || "")) {
2416
- pushError(errors, `Projection ${statement.id} ui_design density has invalid value '${value}'`, entry.loc);
2497
+ pushError(errors, `Projection ${statement.id} design_tokens density has invalid value '${value}'`, entry.loc);
2417
2498
  }
2418
2499
  if (tokens.length !== 2) {
2419
- pushError(errors, `Projection ${statement.id} ui_design density accepts exactly one value`, entry.loc);
2500
+ pushError(errors, `Projection ${statement.id} design_tokens density accepts exactly one value`, entry.loc);
2420
2501
  }
2421
2502
  continue;
2422
2503
  }
2423
2504
 
2424
2505
  if (key === "tone") {
2425
2506
  if (!UI_DESIGN_TONES.has(value || "")) {
2426
- pushError(errors, `Projection ${statement.id} ui_design tone has invalid value '${value}'`, entry.loc);
2507
+ pushError(errors, `Projection ${statement.id} design_tokens tone has invalid value '${value}'`, entry.loc);
2427
2508
  }
2428
2509
  if (tokens.length !== 2) {
2429
- pushError(errors, `Projection ${statement.id} ui_design tone accepts exactly one value`, entry.loc);
2510
+ pushError(errors, `Projection ${statement.id} design_tokens tone accepts exactly one value`, entry.loc);
2430
2511
  }
2431
2512
  continue;
2432
2513
  }
2433
2514
 
2434
2515
  if (key === "radius_scale") {
2435
2516
  if (!UI_DESIGN_RADIUS_SCALES.has(value || "")) {
2436
- pushError(errors, `Projection ${statement.id} ui_design radius_scale has invalid value '${value}'`, entry.loc);
2517
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale has invalid value '${value}'`, entry.loc);
2437
2518
  }
2438
2519
  if (tokens.length !== 2) {
2439
- pushError(errors, `Projection ${statement.id} ui_design radius_scale accepts exactly one value`, entry.loc);
2520
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale accepts exactly one value`, entry.loc);
2440
2521
  }
2441
2522
  continue;
2442
2523
  }
2443
2524
 
2444
2525
  if (key === "color_role") {
2445
2526
  if (!UI_DESIGN_COLOR_ROLES.has(value || "")) {
2446
- pushError(errors, `Projection ${statement.id} ui_design color_role has invalid role '${value}'`, entry.loc);
2527
+ pushError(errors, `Projection ${statement.id} design_tokens color_role has invalid role '${value}'`, entry.loc);
2447
2528
  }
2448
2529
  if (tokens.length !== 3) {
2449
- pushError(errors, `Projection ${statement.id} ui_design color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
2530
+ pushError(errors, `Projection ${statement.id} design_tokens color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
2450
2531
  }
2451
2532
  continue;
2452
2533
  }
2453
2534
 
2454
2535
  if (key === "typography_role") {
2455
2536
  if (!UI_DESIGN_TYPOGRAPHY_ROLES.has(value || "")) {
2456
- pushError(errors, `Projection ${statement.id} ui_design typography_role has invalid role '${value}'`, entry.loc);
2537
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role has invalid role '${value}'`, entry.loc);
2457
2538
  }
2458
2539
  if (tokens.length !== 3) {
2459
- pushError(errors, `Projection ${statement.id} ui_design typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
2540
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
2460
2541
  }
2461
2542
  continue;
2462
2543
  }
2463
2544
 
2464
2545
  if (key === "action_role") {
2465
2546
  if (!UI_DESIGN_ACTION_ROLES.has(value || "")) {
2466
- pushError(errors, `Projection ${statement.id} ui_design action_role has invalid role '${value}'`, entry.loc);
2547
+ pushError(errors, `Projection ${statement.id} design_tokens action_role has invalid role '${value}'`, entry.loc);
2467
2548
  }
2468
2549
  if (tokens.length !== 3) {
2469
- pushError(errors, `Projection ${statement.id} ui_design action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
2550
+ pushError(errors, `Projection ${statement.id} design_tokens action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
2470
2551
  }
2471
2552
  continue;
2472
2553
  }
@@ -2474,17 +2555,17 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2474
2555
  if (key === "accessibility") {
2475
2556
  const values = UI_DESIGN_ACCESSIBILITY_VALUES[value];
2476
2557
  if (tokens.length !== 3) {
2477
- pushError(errors, `Projection ${statement.id} ui_design accessibility must use 'accessibility <setting> <value>'`, entry.loc);
2558
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility must use 'accessibility <setting> <value>'`, entry.loc);
2478
2559
  }
2479
2560
  if (!values) {
2480
- pushError(errors, `Projection ${statement.id} ui_design accessibility has invalid setting '${value}'`, entry.loc);
2561
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility has invalid setting '${value}'`, entry.loc);
2481
2562
  } else if (!values.has(extra || "")) {
2482
- pushError(errors, `Projection ${statement.id} ui_design accessibility '${value}' has invalid value '${extra}'`, entry.loc);
2563
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility '${value}' has invalid value '${extra}'`, entry.loc);
2483
2564
  }
2484
2565
  continue;
2485
2566
  }
2486
2567
 
2487
- pushError(errors, `Projection ${statement.id} ui_design has unknown key '${key}'`, entry.loc);
2568
+ pushError(errors, `Projection ${statement.id} design_tokens has unknown key '${key}'`, entry.loc);
2488
2569
  }
2489
2570
  }
2490
2571
 
@@ -2493,7 +2574,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2493
2574
  return;
2494
2575
  }
2495
2576
 
2496
- const navigationField = fieldMap.get("ui_navigation")?.[0];
2577
+ const navigationField = fieldMap.get("navigation")?.[0];
2497
2578
  if (!navigationField || navigationField.value.type !== "block") {
2498
2579
  return;
2499
2580
  }
@@ -2507,58 +2588,58 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2507
2588
 
2508
2589
  if (targetKind === "group") {
2509
2590
  if (!targetId || !IDENTIFIER_PATTERN.test(targetId)) {
2510
- pushError(errors, `Projection ${statement.id} ui_navigation group entries must include a valid group id`, entry.loc);
2591
+ pushError(errors, `Projection ${statement.id} navigation group entries must include a valid group id`, entry.loc);
2511
2592
  continue;
2512
2593
  }
2513
2594
  groups.add(targetId);
2514
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation group '${targetId}'`);
2595
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation group '${targetId}'`);
2515
2596
  for (const key of directives.keys()) {
2516
2597
  if (!["label", "placement", "icon", "order", "pattern"].includes(key)) {
2517
- pushError(errors, `Projection ${statement.id} ui_navigation group '${targetId}' has unknown directive '${key}'`, entry.loc);
2598
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has unknown directive '${key}'`, entry.loc);
2518
2599
  }
2519
2600
  }
2520
2601
  if (directives.has("placement") && !["primary", "secondary", "utility"].includes(directives.get("placement"))) {
2521
- pushError(errors, `Projection ${statement.id} ui_navigation group '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2602
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2522
2603
  }
2523
2604
  if (directives.has("pattern") && !UI_NAVIGATION_PATTERNS.has(directives.get("pattern"))) {
2524
- pushError(errors, `Projection ${statement.id} ui_navigation group '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2605
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2525
2606
  }
2526
2607
  continue;
2527
2608
  }
2528
2609
 
2529
2610
  if (targetKind === "screen") {
2530
2611
  if (!availableScreens.has(targetId)) {
2531
- pushError(errors, `Projection ${statement.id} ui_navigation references unknown screen '${targetId}'`, entry.loc);
2612
+ pushError(errors, `Projection ${statement.id} navigation references unknown screen '${targetId}'`, entry.loc);
2532
2613
  }
2533
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation screen '${targetId}'`);
2614
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation screen '${targetId}'`);
2534
2615
  for (const key of directives.keys()) {
2535
2616
  if (!["group", "label", "order", "visible", "default", "breadcrumb", "sitemap", "placement", "pattern"].includes(key)) {
2536
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has unknown directive '${key}'`, entry.loc);
2617
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has unknown directive '${key}'`, entry.loc);
2537
2618
  }
2538
2619
  }
2539
2620
  if (directives.has("visible") && !["true", "false"].includes(directives.get("visible"))) {
2540
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has invalid visible '${directives.get("visible")}'`, entry.loc);
2621
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid visible '${directives.get("visible")}'`, entry.loc);
2541
2622
  }
2542
2623
  if (directives.has("default") && !["true", "false"].includes(directives.get("default"))) {
2543
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has invalid default '${directives.get("default")}'`, entry.loc);
2624
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid default '${directives.get("default")}'`, entry.loc);
2544
2625
  }
2545
2626
  if (directives.has("placement") && !["primary", "secondary", "utility"].includes(directives.get("placement"))) {
2546
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2627
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2547
2628
  }
2548
2629
  if (directives.has("sitemap") && !["include", "exclude"].includes(directives.get("sitemap"))) {
2549
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has invalid sitemap '${directives.get("sitemap")}'`, entry.loc);
2630
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid sitemap '${directives.get("sitemap")}'`, entry.loc);
2550
2631
  }
2551
2632
  if (directives.has("pattern") && !UI_NAVIGATION_PATTERNS.has(directives.get("pattern"))) {
2552
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2633
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2553
2634
  }
2554
2635
  const breadcrumb = directives.get("breadcrumb");
2555
2636
  if (breadcrumb && breadcrumb !== "none" && !availableScreens.has(breadcrumb)) {
2556
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' references unknown breadcrumb screen '${breadcrumb}'`, entry.loc);
2637
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' references unknown breadcrumb screen '${breadcrumb}'`, entry.loc);
2557
2638
  }
2558
2639
  continue;
2559
2640
  }
2560
2641
 
2561
- pushError(errors, `Projection ${statement.id} ui_navigation entries must start with 'group' or 'screen'`, entry.loc);
2642
+ pushError(errors, `Projection ${statement.id} navigation entries must start with 'group' or 'screen'`, entry.loc);
2562
2643
  }
2563
2644
 
2564
2645
  for (const entry of navigationField.value.entries) {
@@ -2568,7 +2649,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2568
2649
  }
2569
2650
  const directives = parseUiDirectiveMap(tokens, 2, [], statement, entry, "");
2570
2651
  if (directives.has("group") && !groups.has(directives.get("group"))) {
2571
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${tokens[1]}' references unknown group '${directives.get("group")}'`, entry.loc);
2652
+ pushError(errors, `Projection ${statement.id} navigation screen '${tokens[1]}' references unknown group '${directives.get("group")}'`, entry.loc);
2572
2653
  }
2573
2654
  }
2574
2655
  }
@@ -2578,7 +2659,7 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2578
2659
  return;
2579
2660
  }
2580
2661
 
2581
- const regionField = fieldMap.get("ui_screen_regions")?.[0];
2662
+ const regionField = fieldMap.get("screen_regions")?.[0];
2582
2663
  if (!regionField || regionField.value.type !== "block") {
2583
2664
  return;
2584
2665
  }
@@ -2589,33 +2670,33 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2589
2670
  const [keyword, screenId, regionKeyword, regionName] = tokens;
2590
2671
 
2591
2672
  if (keyword !== "screen") {
2592
- pushError(errors, `Projection ${statement.id} ui_screen_regions entries must start with 'screen'`, entry.loc);
2673
+ pushError(errors, `Projection ${statement.id} screen_regions entries must start with 'screen'`, entry.loc);
2593
2674
  continue;
2594
2675
  }
2595
2676
  if (!availableScreens.has(screenId)) {
2596
- pushError(errors, `Projection ${statement.id} ui_screen_regions references unknown screen '${screenId}'`, entry.loc);
2677
+ pushError(errors, `Projection ${statement.id} screen_regions references unknown screen '${screenId}'`, entry.loc);
2597
2678
  }
2598
2679
  if (regionKeyword !== "region") {
2599
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' must use 'region'`, entry.loc);
2680
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' must use 'region'`, entry.loc);
2600
2681
  }
2601
2682
  if (!UI_REGION_KINDS.has(regionName || "")) {
2602
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2683
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2603
2684
  }
2604
2685
 
2605
- const directives = parseUiDirectiveMap(tokens, 4, errors, statement, entry, `ui_screen_regions for '${screenId}'`);
2686
+ const directives = parseUiDirectiveMap(tokens, 4, errors, statement, entry, `screen_regions for '${screenId}'`);
2606
2687
  for (const key of directives.keys()) {
2607
2688
  if (!["pattern", "placement", "title", "state", "variant"].includes(key)) {
2608
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has unknown directive '${key}'`, entry.loc);
2689
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has unknown directive '${key}'`, entry.loc);
2609
2690
  }
2610
2691
  }
2611
2692
  if (directives.has("pattern") && !UI_PATTERN_KINDS.has(directives.get("pattern"))) {
2612
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2693
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2613
2694
  }
2614
2695
  if (directives.has("placement") && !["primary", "secondary", "supporting"].includes(directives.get("placement"))) {
2615
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2696
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2616
2697
  }
2617
2698
  if (directives.has("state") && !UI_STATE_KINDS.has(directives.get("state"))) {
2618
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid state '${directives.get("state")}'`, entry.loc);
2699
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid state '${directives.get("state")}'`, entry.loc);
2619
2700
  }
2620
2701
  }
2621
2702
  }
@@ -2625,13 +2706,13 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2625
2706
  return;
2626
2707
  }
2627
2708
 
2628
- const componentsField = fieldMap.get("ui_components")?.[0];
2709
+ const componentsField = fieldMap.get("widget_bindings")?.[0];
2629
2710
  if (!componentsField || componentsField.value.type !== "block") {
2630
2711
  return;
2631
2712
  }
2632
2713
 
2633
- if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2634
- pushError(errors, `Projection ${statement.id} ui_components belongs on shared UI projections; concrete UI projections inherit component placement through 'realizes'`, componentsField.loc);
2714
+ if (symbolValue(getFieldValue(statement, "type")) !== "ui_contract") {
2715
+ pushError(errors, `Projection ${statement.id} widget_bindings belongs on shared UI projections; concrete UI projections inherit widget placement through 'realizes'`, componentsField.loc);
2635
2716
  }
2636
2717
 
2637
2718
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
@@ -2643,48 +2724,48 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2643
2724
  const [screenKeyword, screenId, regionKeyword, regionName, componentKeyword, componentId] = tokens;
2644
2725
 
2645
2726
  if (screenKeyword !== "screen") {
2646
- pushError(errors, `Projection ${statement.id} ui_components entries must start with 'screen'`, entry.loc);
2727
+ pushError(errors, `Projection ${statement.id} widget_bindings entries must start with 'screen'`, entry.loc);
2647
2728
  continue;
2648
2729
  }
2649
2730
  if (!availableScreens.has(screenId)) {
2650
- pushError(errors, `Projection ${statement.id} ui_components references unknown screen '${screenId}'`, entry.loc);
2731
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown screen '${screenId}'`, entry.loc);
2651
2732
  }
2652
2733
  if (regionKeyword !== "region") {
2653
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' must use 'region'`, entry.loc);
2734
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' must use 'region'`, entry.loc);
2654
2735
  }
2655
2736
  if (!UI_REGION_KINDS.has(regionName || "")) {
2656
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2737
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2657
2738
  } else if (!availableRegions.has(`${screenId}:${regionName}`)) {
2658
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' references undeclared region '${regionName}'`, entry.loc);
2739
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' references undeclared region '${regionName}'`, entry.loc);
2659
2740
  }
2660
- if (componentKeyword !== "component") {
2661
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' must use 'component'`, entry.loc);
2741
+ if (componentKeyword !== "widget") {
2742
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' must use 'widget'`, entry.loc);
2662
2743
  }
2663
2744
 
2664
- const component = registry.get(componentId);
2665
- if (!component) {
2666
- pushError(errors, `Projection ${statement.id} ui_components references missing component '${componentId}'`, entry.loc);
2745
+ const widget = registry.get(componentId);
2746
+ if (!widget) {
2747
+ pushError(errors, `Projection ${statement.id} widget_bindings references missing widget '${componentId}'`, entry.loc);
2667
2748
  continue;
2668
2749
  }
2669
- if (component.kind !== "component") {
2670
- pushError(errors, `Projection ${statement.id} ui_components must reference a component, found ${component.kind} '${component.id}'`, entry.loc);
2750
+ if (widget.kind !== "widget") {
2751
+ pushError(errors, `Projection ${statement.id} widget_bindings must reference a widget, found ${widget.kind} '${widget.id}'`, entry.loc);
2671
2752
  continue;
2672
2753
  }
2673
2754
 
2674
- const propNames = new Set(blockEntries(getFieldValue(component, "props"))
2755
+ const propNames = new Set(blockEntries(getFieldValue(widget, "props"))
2675
2756
  .map((propEntry) => propEntry.items[0])
2676
2757
  .filter((item) => item?.type === "symbol")
2677
2758
  .map((item) => item.value));
2678
- const eventNames = new Set(blockEntries(getFieldValue(component, "events"))
2759
+ const eventNames = new Set(blockEntries(getFieldValue(widget, "events"))
2679
2760
  .map((eventEntry) => eventEntry.items[0])
2680
2761
  .filter((item) => item?.type === "symbol")
2681
2762
  .map((item) => item.value));
2682
- const componentRegions = symbolValues(getFieldValue(component, "regions"));
2683
- const componentPatterns = symbolValues(getFieldValue(component, "patterns"));
2763
+ const componentRegions = symbolValues(getFieldValue(widget, "regions"));
2764
+ const componentPatterns = symbolValues(getFieldValue(widget, "patterns"));
2684
2765
  if (componentRegions.length > 0 && !componentRegions.includes(regionName)) {
2685
2766
  pushError(
2686
2767
  errors,
2687
- `Projection ${statement.id} ui_components uses component '${componentId}' in region '${regionName}', but the component supports regions [${componentRegions.join(", ")}]`,
2768
+ `Projection ${statement.id} widget_bindings uses widget '${componentId}' in region '${regionName}', but the widget supports regions [${componentRegions.join(", ")}]`,
2688
2769
  entry.loc
2689
2770
  );
2690
2771
  }
@@ -2692,7 +2773,7 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2692
2773
  if (regionPattern && componentPatterns.length > 0 && !componentPatterns.includes(regionPattern)) {
2693
2774
  pushError(
2694
2775
  errors,
2695
- `Projection ${statement.id} ui_components uses component '${componentId}' in '${screenId}:${regionName}' with pattern '${regionPattern}', but the component supports patterns [${componentPatterns.join(", ")}]`,
2776
+ `Projection ${statement.id} widget_bindings uses widget '${componentId}' in '${screenId}:${regionName}' with pattern '${regionPattern}', but the widget supports patterns [${componentPatterns.join(", ")}]`,
2696
2777
  entry.loc
2697
2778
  );
2698
2779
  }
@@ -2704,15 +2785,15 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2704
2785
  const fromKeyword = tokens[i + 2];
2705
2786
  const sourceId = tokens[i + 3];
2706
2787
  if (!propName || fromKeyword !== "from" || !sourceId) {
2707
- pushError(errors, `Projection ${statement.id} ui_components data bindings must use 'data <prop> from <source>'`, entry.loc);
2788
+ pushError(errors, `Projection ${statement.id} widget_bindings data bindings must use 'data <prop> from <source>'`, entry.loc);
2708
2789
  break;
2709
2790
  }
2710
2791
  if (!propNames.has(propName)) {
2711
- pushError(errors, `Projection ${statement.id} ui_components references unknown prop '${propName}' on component '${componentId}'`, entry.loc);
2792
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown prop '${propName}' on widget '${componentId}'`, entry.loc);
2712
2793
  }
2713
2794
  const source = registry.get(sourceId);
2714
2795
  if (!source || !["capability", "projection", "shape", "entity"].includes(source.kind)) {
2715
- pushError(errors, `Projection ${statement.id} ui_components data binding for '${propName}' references missing source '${sourceId}'`, entry.loc);
2796
+ pushError(errors, `Projection ${statement.id} widget_bindings data binding for '${propName}' references missing source '${sourceId}'`, entry.loc);
2716
2797
  }
2717
2798
  i += 4;
2718
2799
  continue;
@@ -2723,29 +2804,29 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2723
2804
  const action = tokens[i + 2];
2724
2805
  const targetId = tokens[i + 3];
2725
2806
  if (!eventName || !action || !targetId) {
2726
- pushError(errors, `Projection ${statement.id} ui_components event bindings must use 'event <event> <navigate|action> <target>'`, entry.loc);
2807
+ pushError(errors, `Projection ${statement.id} widget_bindings event bindings must use 'event <event> <navigate|action> <target>'`, entry.loc);
2727
2808
  break;
2728
2809
  }
2729
2810
  if (!eventNames.has(eventName)) {
2730
- pushError(errors, `Projection ${statement.id} ui_components references unknown event '${eventName}' on component '${componentId}'`, entry.loc);
2811
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown event '${eventName}' on widget '${componentId}'`, entry.loc);
2731
2812
  }
2732
2813
  if (action === "navigate") {
2733
2814
  if (!availableScreens.has(targetId)) {
2734
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' references unknown navigation target '${targetId}'`, entry.loc);
2815
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' references unknown navigation target '${targetId}'`, entry.loc);
2735
2816
  }
2736
2817
  } else if (action === "action") {
2737
2818
  const target = registry.get(targetId);
2738
2819
  if (!target || target.kind !== "capability") {
2739
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' references missing capability action '${targetId}'`, entry.loc);
2820
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' references missing capability action '${targetId}'`, entry.loc);
2740
2821
  }
2741
2822
  } else {
2742
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' has unsupported action '${action}'`, entry.loc);
2823
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' has unsupported action '${action}'`, entry.loc);
2743
2824
  }
2744
2825
  i += 4;
2745
2826
  continue;
2746
2827
  }
2747
2828
 
2748
- pushError(errors, `Projection ${statement.id} ui_components has unknown directive '${directive}'`, entry.loc);
2829
+ pushError(errors, `Projection ${statement.id} widget_bindings has unknown directive '${directive}'`, entry.loc);
2749
2830
  break;
2750
2831
  }
2751
2832
  }
@@ -2756,7 +2837,7 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2756
2837
  return;
2757
2838
  }
2758
2839
 
2759
- const visibilityField = fieldMap.get("ui_visibility")?.[0];
2840
+ const visibilityField = fieldMap.get("visibility_rules")?.[0];
2760
2841
  if (!visibilityField || visibilityField.value.type !== "block") {
2761
2842
  return;
2762
2843
  }
@@ -2767,30 +2848,30 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2767
2848
  const [keyword, capabilityId, predicateKeyword, predicateType, predicateValue] = tokens;
2768
2849
 
2769
2850
  if (keyword !== "action") {
2770
- pushError(errors, `Projection ${statement.id} ui_visibility entries must start with 'action'`, entry.loc);
2851
+ pushError(errors, `Projection ${statement.id} visibility_rules entries must start with 'action'`, entry.loc);
2771
2852
  continue;
2772
2853
  }
2773
2854
 
2774
2855
  const capability = registry.get(capabilityId);
2775
2856
  if (!capability) {
2776
- pushError(errors, `Projection ${statement.id} ui_visibility references missing capability '${capabilityId}'`, entry.loc);
2857
+ pushError(errors, `Projection ${statement.id} visibility_rules references missing capability '${capabilityId}'`, entry.loc);
2777
2858
  } else if (capability.kind !== "capability") {
2778
- pushError(errors, `Projection ${statement.id} ui_visibility must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2859
+ pushError(errors, `Projection ${statement.id} visibility_rules must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2779
2860
  } else if (!realized.has(capabilityId)) {
2780
- pushError(errors, `Projection ${statement.id} ui_visibility action '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2861
+ pushError(errors, `Projection ${statement.id} visibility_rules action '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2781
2862
  }
2782
2863
 
2783
2864
  if (predicateKeyword !== "visible_if") {
2784
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' must use 'visible_if'`, entry.loc);
2865
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' must use 'visible_if'`, entry.loc);
2785
2866
  }
2786
2867
  if (!["permission", "ownership", "claim"].includes(predicateType || "")) {
2787
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' has invalid predicate '${predicateType}'`, entry.loc);
2868
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has invalid predicate '${predicateType}'`, entry.loc);
2788
2869
  }
2789
2870
  if (!predicateValue) {
2790
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' must include a predicate value`, entry.loc);
2871
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' must include a predicate value`, entry.loc);
2791
2872
  }
2792
2873
  if (predicateType === "ownership" && !["owner", "owner_or_admin", "project_member", "none"].includes(predicateValue || "")) {
2793
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' has invalid ownership '${predicateValue}'`, entry.loc);
2874
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has invalid ownership '${predicateValue}'`, entry.loc);
2794
2875
  }
2795
2876
  const directiveTokens = blockSymbolItems(entry).map((item) => item.value);
2796
2877
  const directives = new Map();
@@ -2798,18 +2879,18 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2798
2879
  const key = directiveTokens[i];
2799
2880
  const value = directiveTokens[i + 1];
2800
2881
  if (!value) {
2801
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
2882
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
2802
2883
  continue;
2803
2884
  }
2804
2885
  directives.set(key, value);
2805
2886
  }
2806
2887
  for (const key of directives.keys()) {
2807
2888
  if (!["claim_value"].includes(key)) {
2808
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
2889
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
2809
2890
  }
2810
2891
  }
2811
2892
  if (directives.get("claim_value") && predicateType !== "claim") {
2812
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
2893
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
2813
2894
  }
2814
2895
  }
2815
2896
  }
@@ -2819,7 +2900,7 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2819
2900
  return;
2820
2901
  }
2821
2902
 
2822
- const lookupsField = fieldMap.get("ui_lookups")?.[0];
2903
+ const lookupsField = fieldMap.get("field_lookups")?.[0];
2823
2904
  if (!lookupsField || lookupsField.value.type !== "block") {
2824
2905
  return;
2825
2906
  }
@@ -2831,56 +2912,56 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2831
2912
  const [keyword, screenId, fieldKeyword, fieldName, entityKeyword, entityId, labelKeyword, labelField, maybeEmptyKeyword, maybeEmptyLabel] = tokens;
2832
2913
 
2833
2914
  if (keyword !== "screen") {
2834
- pushError(errors, `Projection ${statement.id} ui_lookups entries must start with 'screen'`, entry.loc);
2915
+ pushError(errors, `Projection ${statement.id} field_lookups entries must start with 'screen'`, entry.loc);
2835
2916
  continue;
2836
2917
  }
2837
2918
 
2838
2919
  const screenEntry = screens.get(screenId);
2839
2920
  if (!screenEntry) {
2840
- pushError(errors, `Projection ${statement.id} ui_lookups references unknown screen '${screenId}'`, entry.loc);
2921
+ pushError(errors, `Projection ${statement.id} field_lookups references unknown screen '${screenId}'`, entry.loc);
2841
2922
  continue;
2842
2923
  }
2843
2924
 
2844
2925
  if (fieldKeyword !== "field") {
2845
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'field'`, entry.loc);
2926
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'field'`, entry.loc);
2846
2927
  }
2847
2928
  if (!fieldName) {
2848
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a field name`, entry.loc);
2929
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a field name`, entry.loc);
2849
2930
  }
2850
2931
 
2851
2932
  if (entityKeyword !== "entity") {
2852
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'entity'`, entry.loc);
2933
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'entity'`, entry.loc);
2853
2934
  }
2854
2935
  const entity = entityId ? registry.get(entityId) : null;
2855
2936
  if (!entity) {
2856
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' references missing entity '${entityId}'`, entry.loc);
2937
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references missing entity '${entityId}'`, entry.loc);
2857
2938
  } else if (entity.kind !== "entity") {
2858
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must reference an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
2939
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must reference an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
2859
2940
  }
2860
2941
 
2861
2942
  if (labelKeyword !== "label_field") {
2862
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'label_field'`, entry.loc);
2943
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'label_field'`, entry.loc);
2863
2944
  }
2864
2945
  if (!labelField) {
2865
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a label_field`, entry.loc);
2946
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a label_field`, entry.loc);
2866
2947
  }
2867
2948
 
2868
2949
  if (maybeEmptyKeyword && maybeEmptyKeyword !== "empty_label") {
2869
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' has unknown directive '${maybeEmptyKeyword}'`, entry.loc);
2950
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' has unknown directive '${maybeEmptyKeyword}'`, entry.loc);
2870
2951
  }
2871
2952
  if (maybeEmptyKeyword === "empty_label" && !maybeEmptyLabel) {
2872
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a value for 'empty_label'`, entry.loc);
2953
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a value for 'empty_label'`, entry.loc);
2873
2954
  }
2874
2955
 
2875
2956
  const availableFields = resolveProjectionUiScreenFieldNames(registry, screenEntry, statement);
2876
2957
  if (fieldName && availableFields.size > 0 && !availableFields.has(fieldName)) {
2877
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' references unknown screen field '${fieldName}'`, entry.loc);
2958
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references unknown screen field '${fieldName}'`, entry.loc);
2878
2959
  }
2879
2960
 
2880
2961
  if (entity?.kind === "entity") {
2881
2962
  const entityFieldNames = new Set(statementFieldNames(entity));
2882
2963
  if (labelField && !entityFieldNames.has(labelField)) {
2883
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' references unknown entity field '${labelField}' on '${entity.id}'`, entry.loc);
2964
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references unknown entity field '${labelField}' on '${entity.id}'`, entry.loc);
2884
2965
  }
2885
2966
  }
2886
2967
  }
@@ -2891,38 +2972,38 @@ function validateProjectionUiRoutes(errors, statement, fieldMap, registry) {
2891
2972
  return;
2892
2973
  }
2893
2974
 
2894
- const routesField = fieldMap.get("ui_routes")?.[0];
2975
+ const routesField = fieldMap.get("screen_routes")?.[0];
2895
2976
  if (!routesField || routesField.value.type !== "block") {
2896
2977
  return;
2897
2978
  }
2898
2979
 
2899
2980
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
2900
2981
  const seenPaths = new Set();
2901
- const platform = symbolValue(getFieldValue(statement, "platform"));
2982
+ const platform = symbolValue(getFieldValue(statement, "type"));
2902
2983
 
2903
2984
  for (const entry of routesField.value.entries) {
2904
2985
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2905
2986
  const [keyword, screenId, pathKeyword, routePath] = tokens;
2906
2987
 
2907
2988
  if (keyword !== "screen") {
2908
- pushError(errors, `Projection ${statement.id} ui_routes entries must start with 'screen'`, entry.loc);
2989
+ pushError(errors, `Projection ${statement.id} screen_routes entries must start with 'screen'`, entry.loc);
2909
2990
  continue;
2910
2991
  }
2911
2992
  if (!availableScreens.has(screenId)) {
2912
- pushError(errors, `Projection ${statement.id} ui_routes references unknown screen '${screenId}'`, entry.loc);
2993
+ pushError(errors, `Projection ${statement.id} screen_routes references unknown screen '${screenId}'`, entry.loc);
2913
2994
  }
2914
2995
  if (pathKeyword !== "path") {
2915
- pushError(errors, `Projection ${statement.id} ui_routes for '${screenId}' must use 'path'`, entry.loc);
2996
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must use 'path'`, entry.loc);
2916
2997
  }
2917
2998
  if (!routePath) {
2918
- pushError(errors, `Projection ${statement.id} ui_routes for '${screenId}' must include a path`, entry.loc);
2999
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must include a path`, entry.loc);
2919
3000
  continue;
2920
3001
  }
2921
- if ((platform === "ui_web" || platform === "ui_ios") && !routePath.startsWith("/")) {
2922
- pushError(errors, `Projection ${statement.id} ui_routes for '${screenId}' must use an absolute path`, entry.loc);
3002
+ if ((platform === "web_surface" || platform === "ios_surface") && !routePath.startsWith("/")) {
3003
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must use an absolute path`, entry.loc);
2923
3004
  }
2924
3005
  if (seenPaths.has(routePath)) {
2925
- pushError(errors, `Projection ${statement.id} ui_routes has duplicate path '${routePath}'`, entry.loc);
3006
+ pushError(errors, `Projection ${statement.id} screen_routes has duplicate path '${routePath}'`, entry.loc);
2926
3007
  }
2927
3008
  seenPaths.add(routePath);
2928
3009
  }
@@ -2938,7 +3019,7 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
2938
3019
  return;
2939
3020
  }
2940
3021
 
2941
- const platform = symbolValue(getFieldValue(statement, "platform"));
3022
+ const platform = symbolValue(getFieldValue(statement, "type"));
2942
3023
  if (platform !== expectedPlatform) {
2943
3024
  pushError(errors, `Projection ${statement.id} may only use '${surfaceBlockKey}' when platform is '${expectedPlatform}'`, surfaceField.loc);
2944
3025
  return;
@@ -3010,11 +3091,11 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
3010
3091
  }
3011
3092
 
3012
3093
  function validateProjectionUiWeb(errors, statement, fieldMap, registry) {
3013
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_web", "ui_web");
3094
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "web_hints", "web_surface");
3014
3095
  }
3015
3096
 
3016
3097
  function validateProjectionUiIos(errors, statement, fieldMap, registry) {
3017
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_ios", "ui_ios");
3098
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ios_hints", "ios_surface");
3018
3099
  }
3019
3100
 
3020
3101
  function validateProjectionGeneratorDefaults(errors, statement, fieldMap) {
@@ -3055,7 +3136,7 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3055
3136
  return;
3056
3137
  }
3057
3138
 
3058
- const dbTablesField = fieldMap.get("db_tables")?.[0];
3139
+ const dbTablesField = fieldMap.get("tables")?.[0];
3059
3140
  if (!dbTablesField || dbTablesField.value.type !== "block") {
3060
3141
  return;
3061
3142
  }
@@ -3068,22 +3149,22 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3068
3149
  const entity = registry.get(entityId);
3069
3150
 
3070
3151
  if (!entity) {
3071
- pushError(errors, `Projection ${statement.id} db_tables references missing entity '${entityId}'`, entry.loc);
3152
+ pushError(errors, `Projection ${statement.id} tables references missing entity '${entityId}'`, entry.loc);
3072
3153
  continue;
3073
3154
  }
3074
3155
  if (entity.kind !== "entity") {
3075
- pushError(errors, `Projection ${statement.id} db_tables must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3156
+ pushError(errors, `Projection ${statement.id} tables must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3076
3157
  }
3077
3158
  if (!realized.has(entityId)) {
3078
- pushError(errors, `Projection ${statement.id} db_tables entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3159
+ pushError(errors, `Projection ${statement.id} tables entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3079
3160
  }
3080
3161
  if (tableKeyword !== "table") {
3081
- pushError(errors, `Projection ${statement.id} db_tables for '${entityId}' must use 'table'`, entry.loc);
3162
+ pushError(errors, `Projection ${statement.id} tables for '${entityId}' must use 'table'`, entry.loc);
3082
3163
  }
3083
3164
  if (!tableName) {
3084
- pushError(errors, `Projection ${statement.id} db_tables for '${entityId}' must include a table name`, entry.loc);
3165
+ pushError(errors, `Projection ${statement.id} tables for '${entityId}' must include a table name`, entry.loc);
3085
3166
  } else if (seenTables.has(tableName)) {
3086
- pushError(errors, `Projection ${statement.id} db_tables has duplicate table name '${tableName}'`, entry.loc);
3167
+ pushError(errors, `Projection ${statement.id} tables has duplicate table name '${tableName}'`, entry.loc);
3087
3168
  }
3088
3169
  seenTables.add(tableName);
3089
3170
  }
@@ -3094,7 +3175,7 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3094
3175
  return;
3095
3176
  }
3096
3177
 
3097
- const dbColumnsField = fieldMap.get("db_columns")?.[0];
3178
+ const dbColumnsField = fieldMap.get("columns")?.[0];
3098
3179
  if (!dbColumnsField || dbColumnsField.value.type !== "block") {
3099
3180
  return;
3100
3181
  }
@@ -3106,27 +3187,27 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3106
3187
  const entity = registry.get(entityId);
3107
3188
 
3108
3189
  if (!entity) {
3109
- pushError(errors, `Projection ${statement.id} db_columns references missing entity '${entityId}'`, entry.loc);
3190
+ pushError(errors, `Projection ${statement.id} columns references missing entity '${entityId}'`, entry.loc);
3110
3191
  continue;
3111
3192
  }
3112
3193
  if (entity.kind !== "entity") {
3113
- pushError(errors, `Projection ${statement.id} db_columns must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3194
+ pushError(errors, `Projection ${statement.id} columns must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3114
3195
  }
3115
3196
  if (!realized.has(entityId)) {
3116
- pushError(errors, `Projection ${statement.id} db_columns entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3197
+ pushError(errors, `Projection ${statement.id} columns entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3117
3198
  }
3118
3199
  if (fieldKeyword !== "field") {
3119
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}' must use 'field'`, entry.loc);
3200
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}' must use 'field'`, entry.loc);
3120
3201
  }
3121
3202
  if (columnKeyword !== "column") {
3122
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}' must use 'column'`, entry.loc);
3203
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}' must use 'column'`, entry.loc);
3123
3204
  }
3124
3205
  const entityFieldNames = new Set(statementFieldNames(entity));
3125
3206
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3126
- pushError(errors, `Projection ${statement.id} db_columns references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3207
+ pushError(errors, `Projection ${statement.id} columns references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3127
3208
  }
3128
3209
  if (!columnName) {
3129
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}.${fieldName}' must include a column name`, entry.loc);
3210
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}.${fieldName}' must include a column name`, entry.loc);
3130
3211
  }
3131
3212
  }
3132
3213
  }
@@ -3136,7 +3217,7 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3136
3217
  return;
3137
3218
  }
3138
3219
 
3139
- const dbKeysField = fieldMap.get("db_keys")?.[0];
3220
+ const dbKeysField = fieldMap.get("keys")?.[0];
3140
3221
  if (!dbKeysField || dbKeysField.value.type !== "block") {
3141
3222
  return;
3142
3223
  }
@@ -3148,27 +3229,27 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3148
3229
  const entity = registry.get(entityId);
3149
3230
 
3150
3231
  if (!entity) {
3151
- pushError(errors, `Projection ${statement.id} db_keys references missing entity '${entityId}'`, entry.loc);
3232
+ pushError(errors, `Projection ${statement.id} keys references missing entity '${entityId}'`, entry.loc);
3152
3233
  continue;
3153
3234
  }
3154
3235
  if (entity.kind !== "entity") {
3155
- pushError(errors, `Projection ${statement.id} db_keys must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3236
+ pushError(errors, `Projection ${statement.id} keys must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3156
3237
  }
3157
3238
  if (!realized.has(entityId)) {
3158
- pushError(errors, `Projection ${statement.id} db_keys entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3239
+ pushError(errors, `Projection ${statement.id} keys entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3159
3240
  }
3160
3241
  if (!["primary", "unique"].includes(keyType || "")) {
3161
- pushError(errors, `Projection ${statement.id} db_keys for '${entityId}' has invalid key type '${keyType}'`, entry.loc);
3242
+ pushError(errors, `Projection ${statement.id} keys for '${entityId}' has invalid key type '${keyType}'`, entry.loc);
3162
3243
  }
3163
3244
  const fieldList = entry.items[2];
3164
3245
  if (!fieldList || fieldList.type !== "list" || fieldList.items.length === 0) {
3165
- pushError(errors, `Projection ${statement.id} db_keys for '${entityId}' must include a non-empty field list`, entry.loc);
3246
+ pushError(errors, `Projection ${statement.id} keys for '${entityId}' must include a non-empty field list`, entry.loc);
3166
3247
  continue;
3167
3248
  }
3168
3249
  const entityFieldNames = new Set(statementFieldNames(entity));
3169
3250
  for (const item of fieldList.items) {
3170
3251
  if (item.type === "symbol" && entityFieldNames.size > 0 && !entityFieldNames.has(item.value)) {
3171
- pushError(errors, `Projection ${statement.id} db_keys references unknown field '${item.value}' on ${entityId}`, item.loc);
3252
+ pushError(errors, `Projection ${statement.id} keys references unknown field '${item.value}' on ${entityId}`, item.loc);
3172
3253
  }
3173
3254
  }
3174
3255
  }
@@ -3179,7 +3260,7 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3179
3260
  return;
3180
3261
  }
3181
3262
 
3182
- const dbIndexesField = fieldMap.get("db_indexes")?.[0];
3263
+ const dbIndexesField = fieldMap.get("indexes")?.[0];
3183
3264
  if (!dbIndexesField || dbIndexesField.value.type !== "block") {
3184
3265
  return;
3185
3266
  }
@@ -3191,27 +3272,27 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3191
3272
  const entity = registry.get(entityId);
3192
3273
 
3193
3274
  if (!entity) {
3194
- pushError(errors, `Projection ${statement.id} db_indexes references missing entity '${entityId}'`, entry.loc);
3275
+ pushError(errors, `Projection ${statement.id} indexes references missing entity '${entityId}'`, entry.loc);
3195
3276
  continue;
3196
3277
  }
3197
3278
  if (entity.kind !== "entity") {
3198
- pushError(errors, `Projection ${statement.id} db_indexes must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3279
+ pushError(errors, `Projection ${statement.id} indexes must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3199
3280
  }
3200
3281
  if (!realized.has(entityId)) {
3201
- pushError(errors, `Projection ${statement.id} db_indexes entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3282
+ pushError(errors, `Projection ${statement.id} indexes entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3202
3283
  }
3203
3284
  if (!["index", "unique"].includes(indexType || "")) {
3204
- pushError(errors, `Projection ${statement.id} db_indexes for '${entityId}' has invalid index type '${indexType}'`, entry.loc);
3285
+ pushError(errors, `Projection ${statement.id} indexes for '${entityId}' has invalid index type '${indexType}'`, entry.loc);
3205
3286
  }
3206
3287
  const fieldList = entry.items[2];
3207
3288
  if (!fieldList || fieldList.type !== "list" || fieldList.items.length === 0) {
3208
- pushError(errors, `Projection ${statement.id} db_indexes for '${entityId}' must include a non-empty field list`, entry.loc);
3289
+ pushError(errors, `Projection ${statement.id} indexes for '${entityId}' must include a non-empty field list`, entry.loc);
3209
3290
  continue;
3210
3291
  }
3211
3292
  const entityFieldNames = new Set(statementFieldNames(entity));
3212
3293
  for (const item of fieldList.items) {
3213
3294
  if (item.type === "symbol" && entityFieldNames.size > 0 && !entityFieldNames.has(item.value)) {
3214
- pushError(errors, `Projection ${statement.id} db_indexes references unknown field '${item.value}' on ${entityId}`, item.loc);
3295
+ pushError(errors, `Projection ${statement.id} indexes references unknown field '${item.value}' on ${entityId}`, item.loc);
3215
3296
  }
3216
3297
  }
3217
3298
  }
@@ -3222,7 +3303,7 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3222
3303
  return;
3223
3304
  }
3224
3305
 
3225
- const dbRelationsField = fieldMap.get("db_relations")?.[0];
3306
+ const dbRelationsField = fieldMap.get("relations")?.[0];
3226
3307
  if (!dbRelationsField || dbRelationsField.value.type !== "block") {
3227
3308
  return;
3228
3309
  }
@@ -3234,43 +3315,43 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3234
3315
  const entity = registry.get(entityId);
3235
3316
 
3236
3317
  if (!entity) {
3237
- pushError(errors, `Projection ${statement.id} db_relations references missing entity '${entityId}'`, entry.loc);
3318
+ pushError(errors, `Projection ${statement.id} relations references missing entity '${entityId}'`, entry.loc);
3238
3319
  continue;
3239
3320
  }
3240
3321
  if (entity.kind !== "entity") {
3241
- pushError(errors, `Projection ${statement.id} db_relations must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3322
+ pushError(errors, `Projection ${statement.id} relations must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3242
3323
  }
3243
3324
  if (!realized.has(entityId)) {
3244
- pushError(errors, `Projection ${statement.id} db_relations entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3325
+ pushError(errors, `Projection ${statement.id} relations entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3245
3326
  }
3246
3327
  if (relationType !== "foreign_key") {
3247
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' must use 'foreign_key'`, entry.loc);
3328
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' must use 'foreign_key'`, entry.loc);
3248
3329
  }
3249
3330
  if (referencesKeyword !== "references") {
3250
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' must use 'references'`, entry.loc);
3331
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' must use 'references'`, entry.loc);
3251
3332
  }
3252
3333
  if (onDeleteKeyword && onDeleteKeyword !== "on_delete") {
3253
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' has unexpected token '${onDeleteKeyword}'`, entry.loc);
3334
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' has unexpected token '${onDeleteKeyword}'`, entry.loc);
3254
3335
  }
3255
3336
  if (onDeleteValue && !["cascade", "restrict", "set_null", "no_action"].includes(onDeleteValue)) {
3256
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' has invalid on_delete '${onDeleteValue}'`, entry.loc);
3337
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' has invalid on_delete '${onDeleteValue}'`, entry.loc);
3257
3338
  }
3258
3339
  const entityFieldNames = new Set(statementFieldNames(entity));
3259
3340
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3260
- pushError(errors, `Projection ${statement.id} db_relations references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3341
+ pushError(errors, `Projection ${statement.id} relations references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3261
3342
  }
3262
3343
  const [targetEntityId, targetFieldName] = (targetRef || "").split(".");
3263
3344
  const targetEntity = registry.get(targetEntityId);
3264
3345
  if (!targetEntity) {
3265
- pushError(errors, `Projection ${statement.id} db_relations references missing target entity '${targetEntityId}'`, entry.loc);
3346
+ pushError(errors, `Projection ${statement.id} relations references missing target entity '${targetEntityId}'`, entry.loc);
3266
3347
  continue;
3267
3348
  }
3268
3349
  if (targetEntity.kind !== "entity") {
3269
- pushError(errors, `Projection ${statement.id} db_relations must reference an entity target, found ${targetEntity.kind} '${targetEntity.id}'`, entry.loc);
3350
+ pushError(errors, `Projection ${statement.id} relations must reference an entity target, found ${targetEntity.kind} '${targetEntity.id}'`, entry.loc);
3270
3351
  }
3271
3352
  const targetFieldNames = new Set(statementFieldNames(targetEntity));
3272
3353
  if (targetFieldName && targetFieldNames.size > 0 && !targetFieldNames.has(targetFieldName)) {
3273
- pushError(errors, `Projection ${statement.id} db_relations references unknown target field '${targetFieldName}' on ${targetEntityId}`, entry.loc);
3354
+ pushError(errors, `Projection ${statement.id} relations references unknown target field '${targetFieldName}' on ${targetEntityId}`, entry.loc);
3274
3355
  }
3275
3356
  }
3276
3357
  }
@@ -3280,7 +3361,7 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3280
3361
  return;
3281
3362
  }
3282
3363
 
3283
- const dbLifecycleField = fieldMap.get("db_lifecycle")?.[0];
3364
+ const dbLifecycleField = fieldMap.get("lifecycle")?.[0];
3284
3365
  if (!dbLifecycleField || dbLifecycleField.value.type !== "block") {
3285
3366
  return;
3286
3367
  }
@@ -3292,19 +3373,19 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3292
3373
  const entity = registry.get(entityId);
3293
3374
 
3294
3375
  if (!entity) {
3295
- pushError(errors, `Projection ${statement.id} db_lifecycle references missing entity '${entityId}'`, entry.loc);
3376
+ pushError(errors, `Projection ${statement.id} lifecycle references missing entity '${entityId}'`, entry.loc);
3296
3377
  continue;
3297
3378
  }
3298
3379
  if (entity.kind !== "entity") {
3299
- pushError(errors, `Projection ${statement.id} db_lifecycle must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3380
+ pushError(errors, `Projection ${statement.id} lifecycle must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3300
3381
  }
3301
3382
  if (!realized.has(entityId)) {
3302
- pushError(errors, `Projection ${statement.id} db_lifecycle entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3383
+ pushError(errors, `Projection ${statement.id} lifecycle entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3303
3384
  }
3304
3385
 
3305
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `db_lifecycle for '${entityId}'`);
3386
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `lifecycle for '${entityId}'`);
3306
3387
  if (!["soft_delete", "timestamps"].includes(lifecycleType || "")) {
3307
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' has invalid lifecycle '${lifecycleType}'`, entry.loc);
3388
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' has invalid lifecycle '${lifecycleType}'`, entry.loc);
3308
3389
  continue;
3309
3390
  }
3310
3391
 
@@ -3312,23 +3393,23 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3312
3393
  if (lifecycleType === "soft_delete") {
3313
3394
  for (const requiredKey of ["field", "value"]) {
3314
3395
  if (!directives.has(requiredKey)) {
3315
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' must include '${requiredKey}' for soft_delete`, entry.loc);
3396
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' must include '${requiredKey}' for soft_delete`, entry.loc);
3316
3397
  }
3317
3398
  }
3318
3399
  const fieldName = directives.get("field");
3319
3400
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3320
- pushError(errors, `Projection ${statement.id} db_lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3401
+ pushError(errors, `Projection ${statement.id} lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3321
3402
  }
3322
3403
  }
3323
3404
 
3324
3405
  if (lifecycleType === "timestamps") {
3325
3406
  for (const requiredKey of ["created_at", "updated_at"]) {
3326
3407
  if (!directives.has(requiredKey)) {
3327
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' must include '${requiredKey}' for timestamps`, entry.loc);
3408
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' must include '${requiredKey}' for timestamps`, entry.loc);
3328
3409
  }
3329
3410
  const fieldName = directives.get(requiredKey);
3330
3411
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3331
- pushError(errors, `Projection ${statement.id} db_lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3412
+ pushError(errors, `Projection ${statement.id} lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3332
3413
  }
3333
3414
  }
3334
3415
  }
@@ -3341,7 +3422,11 @@ export function buildRegistry(workspaceAst, errors) {
3341
3422
  for (const file of workspaceAst.files) {
3342
3423
  for (const statement of file.statements) {
3343
3424
  if (!STATEMENT_KINDS.has(statement.kind)) {
3344
- pushError(errors, `Unknown statement kind '${statement.kind}'`, statement.loc);
3425
+ if (statement.kind === "component") {
3426
+ pushError(errors, `Statement kind ${renameDiagnostic("'component'", "'widget'", "widget widget_data_grid { ... }")}`, statement.loc);
3427
+ } else {
3428
+ pushError(errors, `Unknown statement kind '${statement.kind}'`, statement.loc);
3429
+ }
3345
3430
  }
3346
3431
 
3347
3432
  if (!IDENTIFIER_PATTERN.test(statement.id)) {
@@ -3519,6 +3604,7 @@ export function validateWorkspace(workspaceAst) {
3519
3604
  for (const statement of file.statements) {
3520
3605
  const fieldMap = collectFieldMap(statement);
3521
3606
  validateFieldPresence(errors, statement, fieldMap);
3607
+ validateProjectionTypeRenames(errors, statement, fieldMap);
3522
3608
  validateFieldShapes(errors, statement, fieldMap);
3523
3609
  validateStatus(errors, statement, fieldMap);
3524
3610
  validateRuleSeverity(errors, statement, fieldMap);
@@ -3561,7 +3647,7 @@ export function validateWorkspace(workspaceAst) {
3561
3647
  validateProjectionDbRelations(errors, statement, fieldMap, registry);
3562
3648
  validateProjectionDbLifecycle(errors, statement, fieldMap, registry);
3563
3649
  validateProjectionGeneratorDefaults(errors, statement, fieldMap);
3564
- validateComponent(errors, statement, fieldMap, registry);
3650
+ validateWidget(errors, statement, fieldMap, registry);
3565
3651
  validateDomain(errors, statement, fieldMap, registry);
3566
3652
  validateDomainTag(errors, statement, fieldMap, registry);
3567
3653
  validatePitch(errors, statement, fieldMap, registry);