@topogram/cli 0.3.51 → 0.3.52

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 +61 -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 +92 -42
  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 +473 -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";
@@ -225,7 +225,47 @@ function validateFieldPresence(errors, statement, fieldMap) {
225
225
  return;
226
226
  }
227
227
 
228
+ const renamedFields = new Map([
229
+ ["platform", "type"],
230
+ ["ui_components", "widget_bindings"],
231
+ ["ui_design", "design_tokens"],
232
+ ["ui_routes", "screen_routes"],
233
+ ["ui_screens", "screens"],
234
+ ["ui_screen_regions", "screen_regions"],
235
+ ["ui_navigation", "navigation"],
236
+ ["ui_app_shell", "app_shell"],
237
+ ["ui_collections", "collection_views"],
238
+ ["ui_actions", "screen_actions"],
239
+ ["ui_visibility", "visibility_rules"],
240
+ ["ui_lookups", "field_lookups"],
241
+ ["web_surface", "web_hints"],
242
+ ["ios_surface", "ios_hints"],
243
+ ["http", "endpoints"],
244
+ ["http_errors", "error_responses"],
245
+ ["http_fields", "wire_fields"],
246
+ ["http_responses", "responses"],
247
+ ["http_preconditions", "preconditions"],
248
+ ["http_idempotency", "idempotency"],
249
+ ["http_cache", "cache"],
250
+ ["http_delete", "delete_semantics"],
251
+ ["http_async", "async_jobs"],
252
+ ["http_status", "async_status"],
253
+ ["http_download", "downloads"],
254
+ ["http_authz", "authorization"],
255
+ ["http_callbacks", "callbacks"],
256
+ ["db_tables", "tables"],
257
+ ["db_columns", "columns"],
258
+ ["db_keys", "keys"],
259
+ ["db_indexes", "indexes"],
260
+ ["db_relations", "relations"],
261
+ ["db_lifecycle", "lifecycle"]
262
+ ]);
263
+
228
264
  for (const key of fieldMap.keys()) {
265
+ if (renamedFields.has(key)) {
266
+ pushError(errors, `Field '${key}' was renamed to '${renamedFields.get(key)}' on ${statement.kind} ${statement.id}`, fieldMap.get(key)[0].loc);
267
+ continue;
268
+ }
229
269
  if (!spec.allowed.includes(key)) {
230
270
  pushError(errors, `Field '${key}' is not allowed on ${statement.kind} ${statement.id}`, fieldMap.get(key)[0].loc);
231
271
  }
@@ -255,7 +295,7 @@ function validateFieldShapes(errors, statement, fieldMap) {
255
295
  ensureSingleValueField(errors, statement, fieldMap, "name", ["string"]);
256
296
  ensureSingleValueField(errors, statement, fieldMap, "description", ["string"]);
257
297
  ensureSingleValueField(errors, statement, fieldMap, "status", ["symbol"]);
258
- ensureSingleValueField(errors, statement, fieldMap, "platform", ["symbol"]);
298
+ ensureSingleValueField(errors, statement, fieldMap, "type", ["symbol"]);
259
299
  ensureSingleValueField(errors, statement, fieldMap, "method", ["symbol"]);
260
300
  ensureSingleValueField(errors, statement, fieldMap, "severity", ["symbol"]);
261
301
  ensureSingleValueField(errors, statement, fieldMap, "category", ["symbol"]);
@@ -299,7 +339,7 @@ function validateFieldShapes(errors, statement, fieldMap) {
299
339
  ensureSingleValueField(errors, statement, fieldMap, key, ["list"]);
300
340
  }
301
341
 
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"]) {
342
+ 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
343
  ensureSingleValueField(errors, statement, fieldMap, key, ["block"]);
304
344
  }
305
345
 
@@ -307,40 +347,46 @@ function validateFieldShapes(errors, statement, fieldMap) {
307
347
  validateBlockEntryLengths(errors, statement, fieldMap, "props", 3);
308
348
  validateBlockEntryLengths(errors, statement, fieldMap, "events", 2);
309
349
  validateBlockEntryLengths(errors, statement, fieldMap, "slots", 2);
310
- validateBlockEntryLengths(errors, statement, fieldMap, "keys", 2);
311
- validateBlockEntryLengths(errors, statement, fieldMap, "relations", 3);
312
350
  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
351
  validateBlockEntryLengths(errors, statement, fieldMap, "generator_defaults", 2);
352
+
353
+ if (statement.kind === "entity") {
354
+ validateBlockEntryLengths(errors, statement, fieldMap, "keys", 2);
355
+ validateBlockEntryLengths(errors, statement, fieldMap, "relations", 3);
356
+ }
357
+
358
+ if (statement.kind === "projection") {
359
+ validateBlockEntryLengths(errors, statement, fieldMap, "endpoints", 7);
360
+ validateBlockEntryLengths(errors, statement, fieldMap, "error_responses", 3);
361
+ validateBlockEntryLengths(errors, statement, fieldMap, "wire_fields", 5);
362
+ validateBlockEntryLengths(errors, statement, fieldMap, "responses", 3);
363
+ validateBlockEntryLengths(errors, statement, fieldMap, "preconditions", 9);
364
+ validateBlockEntryLengths(errors, statement, fieldMap, "idempotency", 7);
365
+ validateBlockEntryLengths(errors, statement, fieldMap, "cache", 11);
366
+ validateBlockEntryLengths(errors, statement, fieldMap, "delete_semantics", 7);
367
+ validateBlockEntryLengths(errors, statement, fieldMap, "async_jobs", 11);
368
+ validateBlockEntryLengths(errors, statement, fieldMap, "async_status", 11);
369
+ validateBlockEntryLengths(errors, statement, fieldMap, "downloads", 7);
370
+ validateBlockEntryLengths(errors, statement, fieldMap, "authorization", 3);
371
+ validateBlockEntryLengths(errors, statement, fieldMap, "callbacks", 11);
372
+ validateBlockEntryLengths(errors, statement, fieldMap, "screens", 4);
373
+ validateBlockEntryLengths(errors, statement, fieldMap, "collection_views", 4);
374
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_actions", 6);
375
+ validateBlockEntryLengths(errors, statement, fieldMap, "visibility_rules", 5);
376
+ validateBlockEntryLengths(errors, statement, fieldMap, "field_lookups", 8);
377
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_routes", 4);
378
+ validateBlockEntryLengths(errors, statement, fieldMap, "web_hints", 4);
379
+ validateBlockEntryLengths(errors, statement, fieldMap, "ios_hints", 4);
380
+ validateBlockEntryLengths(errors, statement, fieldMap, "app_shell", 2);
381
+ validateBlockEntryLengths(errors, statement, fieldMap, "navigation", 2);
382
+ validateBlockEntryLengths(errors, statement, fieldMap, "screen_regions", 4);
383
+ validateBlockEntryLengths(errors, statement, fieldMap, "tables", 3);
384
+ validateBlockEntryLengths(errors, statement, fieldMap, "columns", 5);
385
+ validateBlockEntryLengths(errors, statement, fieldMap, "keys", 3);
386
+ validateBlockEntryLengths(errors, statement, fieldMap, "indexes", 3);
387
+ validateBlockEntryLengths(errors, statement, fieldMap, "relations", 6);
388
+ validateBlockEntryLengths(errors, statement, fieldMap, "lifecycle", 3);
389
+ }
344
390
  }
345
391
 
346
392
  function validateStatus(errors, statement, fieldMap) {
@@ -456,7 +502,7 @@ function validateReferenceKinds(errors, statement, fieldMap, registry) {
456
502
  pitch: ["pitch"],
457
503
  requirement: null,
458
504
  from_requirement: ["requirement"],
459
- affects: ["capability", "entity", "rule", "projection", "component", "orchestration", "operation"],
505
+ affects: ["capability", "entity", "rule", "projection", "widget", "orchestration", "operation"],
460
506
  introduces_rules: ["rule"],
461
507
  respects_rules: ["rule"],
462
508
  decisions: ["decision"],
@@ -798,7 +844,7 @@ function validateProjectionHttp(errors, statement, fieldMap, registry) {
798
844
  return;
799
845
  }
800
846
 
801
- const httpField = fieldMap.get("http")?.[0];
847
+ const httpField = fieldMap.get("endpoints")?.[0];
802
848
  if (!httpField || httpField.value.type !== "block") {
803
849
  return;
804
850
  }
@@ -879,7 +925,7 @@ function validateProjectionHttpErrors(errors, statement, fieldMap, registry) {
879
925
  return;
880
926
  }
881
927
 
882
- const httpErrorsField = fieldMap.get("http_errors")?.[0];
928
+ const httpErrorsField = fieldMap.get("error_responses")?.[0];
883
929
  if (!httpErrorsField || httpErrorsField.value.type !== "block") {
884
930
  return;
885
931
  }
@@ -891,20 +937,20 @@ function validateProjectionHttpErrors(errors, statement, fieldMap, registry) {
891
937
 
892
938
  const target = registry.get(capabilityId);
893
939
  if (!target) {
894
- pushError(errors, `Projection ${statement.id} http_errors references missing capability '${capabilityId}'`, entry.loc);
940
+ pushError(errors, `Projection ${statement.id} error_responses references missing capability '${capabilityId}'`, entry.loc);
895
941
  continue;
896
942
  }
897
943
  if (target.kind !== "capability") {
898
- pushError(errors, `Projection ${statement.id} http_errors must target a capability, found ${target.kind} '${target.id}'`, entry.loc);
944
+ pushError(errors, `Projection ${statement.id} error_responses must target a capability, found ${target.kind} '${target.id}'`, entry.loc);
899
945
  }
900
946
  if (!realized.has(capabilityId)) {
901
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
947
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
902
948
  }
903
949
  if (!/^\d{3}$/.test(status || "")) {
904
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must use a 3-digit status`, entry.loc);
950
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must use a 3-digit status`, entry.loc);
905
951
  }
906
952
  if (!errorCode) {
907
- pushError(errors, `Projection ${statement.id} http_errors for '${capabilityId}' must include an error code`, entry.loc);
953
+ pushError(errors, `Projection ${statement.id} error_responses for '${capabilityId}' must include an error code`, entry.loc);
908
954
  }
909
955
  }
910
956
  }
@@ -939,7 +985,7 @@ function validateProjectionHttpFields(errors, statement, fieldMap, registry) {
939
985
  return;
940
986
  }
941
987
 
942
- const httpFieldsField = fieldMap.get("http_fields")?.[0];
988
+ const httpFieldsField = fieldMap.get("wire_fields")?.[0];
943
989
  if (!httpFieldsField || httpFieldsField.value.type !== "block") {
944
990
  return;
945
991
  }
@@ -951,34 +997,34 @@ function validateProjectionHttpFields(errors, statement, fieldMap, registry) {
951
997
 
952
998
  const capability = registry.get(capabilityId);
953
999
  if (!capability) {
954
- pushError(errors, `Projection ${statement.id} http_fields references missing capability '${capabilityId}'`, entry.loc);
1000
+ pushError(errors, `Projection ${statement.id} wire_fields references missing capability '${capabilityId}'`, entry.loc);
955
1001
  continue;
956
1002
  }
957
1003
  if (capability.kind !== "capability") {
958
- pushError(errors, `Projection ${statement.id} http_fields must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1004
+ pushError(errors, `Projection ${statement.id} wire_fields must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
959
1005
  }
960
1006
  if (!realized.has(capabilityId)) {
961
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1007
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
962
1008
  }
963
1009
  if (!["input", "output"].includes(direction)) {
964
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has invalid direction '${direction}'`, entry.loc);
1010
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has invalid direction '${direction}'`, entry.loc);
965
1011
  }
966
1012
  if (keywordIn !== "in") {
967
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must use 'in' before the location`, entry.loc);
1013
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must use 'in' before the location`, entry.loc);
968
1014
  }
969
1015
  if (!["path", "query", "header", "body"].includes(location)) {
970
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has invalid location '${location}'`, entry.loc);
1016
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has invalid location '${location}'`, entry.loc);
971
1017
  }
972
1018
  if (maybeAs && maybeAs !== "as") {
973
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' has unexpected token '${maybeAs}'`, entry.loc);
1019
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' has unexpected token '${maybeAs}'`, entry.loc);
974
1020
  }
975
1021
  if (maybeAs === "as" && !maybeWireName) {
976
- pushError(errors, `Projection ${statement.id} http_fields for '${capabilityId}' must provide a wire name after 'as'`, entry.loc);
1022
+ pushError(errors, `Projection ${statement.id} wire_fields for '${capabilityId}' must provide a wire name after 'as'`, entry.loc);
977
1023
  }
978
1024
 
979
1025
  const availableFields = resolveCapabilityContractFields(registry, capabilityId, direction);
980
1026
  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);
1027
+ pushError(errors, `Projection ${statement.id} wire_fields references unknown ${direction} field '${fieldName}' on ${capabilityId}`, entry.loc);
982
1028
  }
983
1029
  }
984
1030
  }
@@ -988,7 +1034,7 @@ function validateProjectionHttpResponses(errors, statement, fieldMap, registry)
988
1034
  return;
989
1035
  }
990
1036
 
991
- const httpResponsesField = fieldMap.get("http_responses")?.[0];
1037
+ const httpResponsesField = fieldMap.get("responses")?.[0];
992
1038
  if (!httpResponsesField || httpResponsesField.value.type !== "block") {
993
1039
  return;
994
1040
  }
@@ -1000,86 +1046,86 @@ function validateProjectionHttpResponses(errors, statement, fieldMap, registry)
1000
1046
  const capability = registry.get(capabilityId);
1001
1047
 
1002
1048
  if (!capability) {
1003
- pushError(errors, `Projection ${statement.id} http_responses references missing capability '${capabilityId}'`, entry.loc);
1049
+ pushError(errors, `Projection ${statement.id} responses references missing capability '${capabilityId}'`, entry.loc);
1004
1050
  continue;
1005
1051
  }
1006
1052
  if (capability.kind !== "capability") {
1007
- pushError(errors, `Projection ${statement.id} http_responses must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1053
+ pushError(errors, `Projection ${statement.id} responses must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1008
1054
  }
1009
1055
  if (!realized.has(capabilityId)) {
1010
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1056
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1011
1057
  }
1012
1058
 
1013
1059
  const directives = parseProjectionHttpResponsesDirectives(tokens.slice(1));
1014
1060
  for (const message of directives.errors) {
1015
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' ${message}`, entry.loc);
1061
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' ${message}`, entry.loc);
1016
1062
  }
1017
1063
 
1018
1064
  if (!directives.mode) {
1019
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'mode'`, entry.loc);
1065
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'mode'`, entry.loc);
1020
1066
  }
1021
1067
 
1022
1068
  const mode = directives.mode;
1023
1069
  if (mode && !["item", "collection", "paged", "cursor"].includes(mode)) {
1024
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1070
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1025
1071
  }
1026
1072
 
1027
1073
  const itemShapeId = directives.item;
1028
1074
  if (mode && mode !== "item" && !itemShapeId) {
1029
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'item' for mode '${mode}'`, entry.loc);
1075
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'item' for mode '${mode}'`, entry.loc);
1030
1076
  }
1031
1077
  if (itemShapeId) {
1032
1078
  const itemShape = registry.get(itemShapeId);
1033
1079
  if (!itemShape) {
1034
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' references missing shape '${itemShapeId}'`, entry.loc);
1080
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' references missing shape '${itemShapeId}'`, entry.loc);
1035
1081
  } 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);
1082
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must reference a shape for 'item', found ${itemShape.kind} '${itemShape.id}'`, entry.loc);
1037
1083
  }
1038
1084
  }
1039
1085
 
1040
1086
  if (mode === "cursor") {
1041
1087
  if (!directives.cursor?.requestAfter) {
1042
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'cursor request_after <field>'`, entry.loc);
1088
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'cursor request_after <field>'`, entry.loc);
1043
1089
  }
1044
1090
  if (!directives.cursor?.responseNext) {
1045
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'cursor response_next <wire_name>'`, entry.loc);
1091
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'cursor response_next <wire_name>'`, entry.loc);
1046
1092
  }
1047
1093
  if (!directives.limit) {
1048
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'limit field <field> default <n> max <n>'`, entry.loc);
1094
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'limit field <field> default <n> max <n>'`, entry.loc);
1049
1095
  }
1050
1096
  if (!directives.sort) {
1051
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must include 'sort by <field> direction <asc|desc>'`, entry.loc);
1097
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must include 'sort by <field> direction <asc|desc>'`, entry.loc);
1052
1098
  }
1053
1099
  }
1054
1100
 
1055
1101
  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);
1102
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid sort direction '${directives.sort.direction}'`, entry.loc);
1057
1103
  }
1058
1104
 
1059
1105
  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);
1106
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' has invalid total included value '${directives.total.included}'`, entry.loc);
1061
1107
  }
1062
1108
 
1063
1109
  if (directives.limit) {
1064
1110
  const defaultValue = Number.parseInt(directives.limit.defaultValue || "", 10);
1065
1111
  const maxValue = Number.parseInt(directives.limit.maxValue || "", 10);
1066
1112
  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);
1113
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must use integer default/max values for 'limit'`, entry.loc);
1068
1114
  } else if (defaultValue > maxValue) {
1069
- pushError(errors, `Projection ${statement.id} http_responses for '${capabilityId}' must use default <= max for 'limit'`, entry.loc);
1115
+ pushError(errors, `Projection ${statement.id} responses for '${capabilityId}' must use default <= max for 'limit'`, entry.loc);
1070
1116
  }
1071
1117
  }
1072
1118
 
1073
1119
  const inputFields = resolveCapabilityContractFields(registry, capabilityId, "input");
1074
1120
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1075
1121
  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);
1122
+ pushError(errors, `Projection ${statement.id} responses references unknown input field '${directives.cursor.requestAfter}' for cursor request_after on ${capabilityId}`, entry.loc);
1077
1123
  }
1078
1124
  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);
1125
+ pushError(errors, `Projection ${statement.id} responses references unknown input field '${directives.limit.field}' for limit on ${capabilityId}`, entry.loc);
1080
1126
  }
1081
1127
  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);
1128
+ pushError(errors, `Projection ${statement.id} responses references unknown output field '${directives.sort.field}' for sort on ${capabilityId}`, entry.loc);
1083
1129
  }
1084
1130
  }
1085
1131
  }
@@ -1089,7 +1135,7 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1089
1135
  return;
1090
1136
  }
1091
1137
 
1092
- const httpPreconditionsField = fieldMap.get("http_preconditions")?.[0];
1138
+ const httpPreconditionsField = fieldMap.get("preconditions")?.[0];
1093
1139
  if (!httpPreconditionsField || httpPreconditionsField.value.type !== "block") {
1094
1140
  return;
1095
1141
  }
@@ -1101,14 +1147,14 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1101
1147
  const capability = registry.get(capabilityId);
1102
1148
 
1103
1149
  if (!capability) {
1104
- pushError(errors, `Projection ${statement.id} http_preconditions references missing capability '${capabilityId}'`, entry.loc);
1150
+ pushError(errors, `Projection ${statement.id} preconditions references missing capability '${capabilityId}'`, entry.loc);
1105
1151
  continue;
1106
1152
  }
1107
1153
  if (capability.kind !== "capability") {
1108
- pushError(errors, `Projection ${statement.id} http_preconditions must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1154
+ pushError(errors, `Projection ${statement.id} preconditions must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1109
1155
  }
1110
1156
  if (!realized.has(capabilityId)) {
1111
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1157
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1112
1158
  }
1113
1159
 
1114
1160
  const directives = new Map();
@@ -1116,7 +1162,7 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1116
1162
  const key = tokens[i];
1117
1163
  const value = tokens[i + 1];
1118
1164
  if (!value) {
1119
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1165
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1120
1166
  continue;
1121
1167
  }
1122
1168
  directives.set(key, value);
@@ -1124,30 +1170,30 @@ function validateProjectionHttpPreconditions(errors, statement, fieldMap, regist
1124
1170
 
1125
1171
  for (const requiredKey of ["header", "required", "error", "source", "code"]) {
1126
1172
  if (!directives.has(requiredKey)) {
1127
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1173
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1128
1174
  }
1129
1175
  }
1130
1176
 
1131
1177
  for (const key of directives.keys()) {
1132
1178
  if (!["header", "required", "error", "source", "code"].includes(key)) {
1133
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1179
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1134
1180
  }
1135
1181
  }
1136
1182
 
1137
1183
  const required = directives.get("required");
1138
1184
  if (required && !["true", "false"].includes(required)) {
1139
- pushError(errors, `Projection ${statement.id} http_preconditions for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1185
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1140
1186
  }
1141
1187
 
1142
1188
  const errorStatus = directives.get("error");
1143
1189
  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);
1190
+ pushError(errors, `Projection ${statement.id} preconditions for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1145
1191
  }
1146
1192
 
1147
1193
  const sourceField = directives.get("source");
1148
1194
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1149
1195
  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);
1196
+ pushError(errors, `Projection ${statement.id} preconditions references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1151
1197
  }
1152
1198
  }
1153
1199
  }
@@ -1157,7 +1203,7 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1157
1203
  return;
1158
1204
  }
1159
1205
 
1160
- const httpIdempotencyField = fieldMap.get("http_idempotency")?.[0];
1206
+ const httpIdempotencyField = fieldMap.get("idempotency")?.[0];
1161
1207
  if (!httpIdempotencyField || httpIdempotencyField.value.type !== "block") {
1162
1208
  return;
1163
1209
  }
@@ -1169,14 +1215,14 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1169
1215
  const capability = registry.get(capabilityId);
1170
1216
 
1171
1217
  if (!capability) {
1172
- pushError(errors, `Projection ${statement.id} http_idempotency references missing capability '${capabilityId}'`, entry.loc);
1218
+ pushError(errors, `Projection ${statement.id} idempotency references missing capability '${capabilityId}'`, entry.loc);
1173
1219
  continue;
1174
1220
  }
1175
1221
  if (capability.kind !== "capability") {
1176
- pushError(errors, `Projection ${statement.id} http_idempotency must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1222
+ pushError(errors, `Projection ${statement.id} idempotency must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1177
1223
  }
1178
1224
  if (!realized.has(capabilityId)) {
1179
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1225
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1180
1226
  }
1181
1227
 
1182
1228
  const directives = new Map();
@@ -1184,7 +1230,7 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1184
1230
  const key = tokens[i];
1185
1231
  const value = tokens[i + 1];
1186
1232
  if (!value) {
1187
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1233
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1188
1234
  continue;
1189
1235
  }
1190
1236
  directives.set(key, value);
@@ -1192,24 +1238,24 @@ function validateProjectionHttpIdempotency(errors, statement, fieldMap, registry
1192
1238
 
1193
1239
  for (const requiredKey of ["header", "required", "error", "code"]) {
1194
1240
  if (!directives.has(requiredKey)) {
1195
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1241
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1196
1242
  }
1197
1243
  }
1198
1244
 
1199
1245
  for (const key of directives.keys()) {
1200
1246
  if (!["header", "required", "error", "code"].includes(key)) {
1201
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1247
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1202
1248
  }
1203
1249
  }
1204
1250
 
1205
1251
  const required = directives.get("required");
1206
1252
  if (required && !["true", "false"].includes(required)) {
1207
- pushError(errors, `Projection ${statement.id} http_idempotency for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1253
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1208
1254
  }
1209
1255
 
1210
1256
  const errorStatus = directives.get("error");
1211
1257
  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);
1258
+ pushError(errors, `Projection ${statement.id} idempotency for '${capabilityId}' must use a 3-digit error status`, entry.loc);
1213
1259
  }
1214
1260
  }
1215
1261
  }
@@ -1219,13 +1265,13 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1219
1265
  return;
1220
1266
  }
1221
1267
 
1222
- const httpCacheField = fieldMap.get("http_cache")?.[0];
1268
+ const httpCacheField = fieldMap.get("cache")?.[0];
1223
1269
  if (!httpCacheField || httpCacheField.value.type !== "block") {
1224
1270
  return;
1225
1271
  }
1226
1272
 
1227
1273
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1228
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1274
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1229
1275
  const httpMethodsByCapability = new Map();
1230
1276
 
1231
1277
  for (const entry of httpEntries) {
@@ -1245,14 +1291,14 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1245
1291
  const capability = registry.get(capabilityId);
1246
1292
 
1247
1293
  if (!capability) {
1248
- pushError(errors, `Projection ${statement.id} http_cache references missing capability '${capabilityId}'`, entry.loc);
1294
+ pushError(errors, `Projection ${statement.id} cache references missing capability '${capabilityId}'`, entry.loc);
1249
1295
  continue;
1250
1296
  }
1251
1297
  if (capability.kind !== "capability") {
1252
- pushError(errors, `Projection ${statement.id} http_cache must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1298
+ pushError(errors, `Projection ${statement.id} cache must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1253
1299
  }
1254
1300
  if (!realized.has(capabilityId)) {
1255
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1301
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1256
1302
  }
1257
1303
 
1258
1304
  const directives = new Map();
@@ -1260,7 +1306,7 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1260
1306
  const key = tokens[i];
1261
1307
  const value = tokens[i + 1];
1262
1308
  if (!value) {
1263
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1309
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1264
1310
  continue;
1265
1311
  }
1266
1312
  directives.set(key, value);
@@ -1268,35 +1314,35 @@ function validateProjectionHttpCache(errors, statement, fieldMap, registry) {
1268
1314
 
1269
1315
  for (const requiredKey of ["response_header", "request_header", "required", "not_modified", "source", "code"]) {
1270
1316
  if (!directives.has(requiredKey)) {
1271
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1317
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1272
1318
  }
1273
1319
  }
1274
1320
 
1275
1321
  for (const key of directives.keys()) {
1276
1322
  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);
1323
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1278
1324
  }
1279
1325
  }
1280
1326
 
1281
1327
  const required = directives.get("required");
1282
1328
  if (required && !["true", "false"].includes(required)) {
1283
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1329
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' has invalid required value '${required}'`, entry.loc);
1284
1330
  }
1285
1331
 
1286
1332
  const notModifiedStatus = directives.get("not_modified");
1287
1333
  if (notModifiedStatus && notModifiedStatus !== "304") {
1288
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' must use 304 for 'not_modified'`, entry.loc);
1334
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' must use 304 for 'not_modified'`, entry.loc);
1289
1335
  }
1290
1336
 
1291
1337
  const sourceField = directives.get("source");
1292
1338
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1293
1339
  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);
1340
+ pushError(errors, `Projection ${statement.id} cache references unknown output field '${sourceField}' on ${capabilityId}`, entry.loc);
1295
1341
  }
1296
1342
 
1297
1343
  const method = httpMethodsByCapability.get(capabilityId);
1298
1344
  if (method && method !== "GET") {
1299
- pushError(errors, `Projection ${statement.id} http_cache for '${capabilityId}' requires an HTTP GET realization, found '${method}'`, entry.loc);
1345
+ pushError(errors, `Projection ${statement.id} cache for '${capabilityId}' requires an HTTP GET realization, found '${method}'`, entry.loc);
1300
1346
  }
1301
1347
  }
1302
1348
  }
@@ -1306,7 +1352,7 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1306
1352
  return;
1307
1353
  }
1308
1354
 
1309
- const httpDeleteField = fieldMap.get("http_delete")?.[0];
1355
+ const httpDeleteField = fieldMap.get("delete_semantics")?.[0];
1310
1356
  if (!httpDeleteField || httpDeleteField.value.type !== "block") {
1311
1357
  return;
1312
1358
  }
@@ -1318,14 +1364,14 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1318
1364
  const capability = registry.get(capabilityId);
1319
1365
 
1320
1366
  if (!capability) {
1321
- pushError(errors, `Projection ${statement.id} http_delete references missing capability '${capabilityId}'`, entry.loc);
1367
+ pushError(errors, `Projection ${statement.id} delete_semantics references missing capability '${capabilityId}'`, entry.loc);
1322
1368
  continue;
1323
1369
  }
1324
1370
  if (capability.kind !== "capability") {
1325
- pushError(errors, `Projection ${statement.id} http_delete must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1371
+ pushError(errors, `Projection ${statement.id} delete_semantics must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1326
1372
  }
1327
1373
  if (!realized.has(capabilityId)) {
1328
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1374
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1329
1375
  }
1330
1376
 
1331
1377
  const directives = new Map();
@@ -1333,7 +1379,7 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1333
1379
  const key = tokens[i];
1334
1380
  const value = tokens[i + 1];
1335
1381
  if (!value) {
1336
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1382
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1337
1383
  continue;
1338
1384
  }
1339
1385
  directives.set(key, value);
@@ -1341,34 +1387,34 @@ function validateProjectionHttpDelete(errors, statement, fieldMap, registry) {
1341
1387
 
1342
1388
  for (const requiredKey of ["mode", "response"]) {
1343
1389
  if (!directives.has(requiredKey)) {
1344
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1390
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1345
1391
  }
1346
1392
  }
1347
1393
 
1348
1394
  for (const key of directives.keys()) {
1349
1395
  if (!["mode", "field", "value", "response"].includes(key)) {
1350
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1396
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1351
1397
  }
1352
1398
  }
1353
1399
 
1354
1400
  const mode = directives.get("mode");
1355
1401
  if (mode && !["soft", "hard"].includes(mode)) {
1356
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1402
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1357
1403
  }
1358
1404
 
1359
1405
  const response = directives.get("response");
1360
1406
  if (response && !["none", "body"].includes(response)) {
1361
- pushError(errors, `Projection ${statement.id} http_delete for '${capabilityId}' has invalid response '${response}'`, entry.loc);
1407
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' has invalid response '${response}'`, entry.loc);
1362
1408
  }
1363
1409
 
1364
1410
  if (mode === "soft") {
1365
1411
  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);
1412
+ pushError(errors, `Projection ${statement.id} delete_semantics for '${capabilityId}' must include 'field' and 'value' for soft deletes`, entry.loc);
1367
1413
  }
1368
1414
  const outputFields = resolveCapabilityContractFields(registry, capabilityId, "output");
1369
1415
  const fieldName = directives.get("field");
1370
1416
  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);
1417
+ pushError(errors, `Projection ${statement.id} delete_semantics references unknown output field '${fieldName}' on ${capabilityId}`, entry.loc);
1372
1418
  }
1373
1419
  }
1374
1420
  }
@@ -1379,13 +1425,13 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1379
1425
  return;
1380
1426
  }
1381
1427
 
1382
- const httpAsyncField = fieldMap.get("http_async")?.[0];
1428
+ const httpAsyncField = fieldMap.get("async_jobs")?.[0];
1383
1429
  if (!httpAsyncField || httpAsyncField.value.type !== "block") {
1384
1430
  return;
1385
1431
  }
1386
1432
 
1387
1433
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1388
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1434
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1389
1435
  const httpDirectivesByCapability = new Map();
1390
1436
  for (const entry of httpEntries) {
1391
1437
  const tokens = blockSymbolItems(entry).map((item) => item.value);
@@ -1402,14 +1448,14 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1402
1448
  const capability = registry.get(capabilityId);
1403
1449
 
1404
1450
  if (!capability) {
1405
- pushError(errors, `Projection ${statement.id} http_async references missing capability '${capabilityId}'`, entry.loc);
1451
+ pushError(errors, `Projection ${statement.id} async_jobs references missing capability '${capabilityId}'`, entry.loc);
1406
1452
  continue;
1407
1453
  }
1408
1454
  if (capability.kind !== "capability") {
1409
- pushError(errors, `Projection ${statement.id} http_async must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1455
+ pushError(errors, `Projection ${statement.id} async_jobs must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1410
1456
  }
1411
1457
  if (!realized.has(capabilityId)) {
1412
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1458
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1413
1459
  }
1414
1460
 
1415
1461
  const directives = new Map();
@@ -1417,7 +1463,7 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1417
1463
  const key = tokens[i];
1418
1464
  const value = tokens[i + 1];
1419
1465
  if (!value) {
1420
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1466
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1421
1467
  continue;
1422
1468
  }
1423
1469
  directives.set(key, value);
@@ -1425,33 +1471,33 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1425
1471
 
1426
1472
  for (const requiredKey of ["mode", "accepted", "location_header", "retry_after_header", "status_path", "status_capability", "job"]) {
1427
1473
  if (!directives.has(requiredKey)) {
1428
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1474
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1429
1475
  }
1430
1476
  }
1431
1477
 
1432
1478
  for (const key of directives.keys()) {
1433
1479
  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);
1480
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1435
1481
  }
1436
1482
  }
1437
1483
 
1438
1484
  const mode = directives.get("mode");
1439
1485
  if (mode && mode !== "job") {
1440
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1486
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' has invalid mode '${mode}'`, entry.loc);
1441
1487
  }
1442
1488
 
1443
1489
  const accepted = directives.get("accepted");
1444
1490
  if (accepted && accepted !== "202") {
1445
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must use 202 for 'accepted'`, entry.loc);
1491
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must use 202 for 'accepted'`, entry.loc);
1446
1492
  }
1447
1493
 
1448
1494
  const jobShapeId = directives.get("job");
1449
1495
  if (jobShapeId) {
1450
1496
  const jobShape = registry.get(jobShapeId);
1451
1497
  if (!jobShape) {
1452
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' references missing shape '${jobShapeId}'`, entry.loc);
1498
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' references missing shape '${jobShapeId}'`, entry.loc);
1453
1499
  } 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);
1500
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must reference a shape for 'job', found ${jobShape.kind} '${jobShape.id}'`, entry.loc);
1455
1501
  }
1456
1502
  }
1457
1503
 
@@ -1459,25 +1505,25 @@ function validateProjectionHttpAsync(errors, statement, fieldMap, registry) {
1459
1505
  if (statusCapabilityId) {
1460
1506
  const statusCapability = registry.get(statusCapabilityId);
1461
1507
  if (!statusCapability) {
1462
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' references missing status capability '${statusCapabilityId}'`, entry.loc);
1508
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' references missing status capability '${statusCapabilityId}'`, entry.loc);
1463
1509
  } 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);
1510
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must reference a capability for 'status_capability', found ${statusCapability.kind} '${statusCapability.id}'`, entry.loc);
1465
1511
  } 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);
1512
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status capability '${statusCapabilityId}' must also appear in 'realizes'`, entry.loc);
1467
1513
  }
1468
1514
 
1469
1515
  const statusHttp = httpDirectivesByCapability.get(statusCapabilityId);
1470
1516
  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);
1517
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status capability '${statusCapabilityId}' must use HTTP GET`, entry.loc);
1472
1518
  }
1473
1519
  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);
1520
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' status_path must match the path for '${statusCapabilityId}'`, entry.loc);
1475
1521
  }
1476
1522
  }
1477
1523
 
1478
1524
  const statusPath = directives.get("status_path");
1479
1525
  if (statusPath && !statusPath.startsWith("/")) {
1480
- pushError(errors, `Projection ${statement.id} http_async for '${capabilityId}' must use an absolute path for 'status_path'`, entry.loc);
1526
+ pushError(errors, `Projection ${statement.id} async_jobs for '${capabilityId}' must use an absolute path for 'status_path'`, entry.loc);
1481
1527
  }
1482
1528
  }
1483
1529
  }
@@ -1487,13 +1533,13 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1487
1533
  return;
1488
1534
  }
1489
1535
 
1490
- const httpStatusField = fieldMap.get("http_status")?.[0];
1536
+ const httpStatusField = fieldMap.get("async_status")?.[0];
1491
1537
  if (!httpStatusField || httpStatusField.value.type !== "block") {
1492
1538
  return;
1493
1539
  }
1494
1540
 
1495
1541
  const realized = new Set(symbolValues(getFieldValue(statement, "realizes")));
1496
- const httpEntries = blockEntries(getFieldValue(statement, "http"));
1542
+ const httpEntries = blockEntries(getFieldValue(statement, "endpoints"));
1497
1543
  const httpMethodsByCapability = new Map();
1498
1544
  for (const entry of httpEntries) {
1499
1545
  const tokens = blockSymbolItems(entry).map((item) => item.value);
@@ -1511,14 +1557,14 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1511
1557
  const capability = registry.get(capabilityId);
1512
1558
 
1513
1559
  if (!capability) {
1514
- pushError(errors, `Projection ${statement.id} http_status references missing capability '${capabilityId}'`, entry.loc);
1560
+ pushError(errors, `Projection ${statement.id} async_status references missing capability '${capabilityId}'`, entry.loc);
1515
1561
  continue;
1516
1562
  }
1517
1563
  if (capability.kind !== "capability") {
1518
- pushError(errors, `Projection ${statement.id} http_status must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1564
+ pushError(errors, `Projection ${statement.id} async_status must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1519
1565
  }
1520
1566
  if (!realized.has(capabilityId)) {
1521
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1567
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1522
1568
  }
1523
1569
 
1524
1570
  const directives = new Map();
@@ -1526,7 +1572,7 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1526
1572
  const key = tokens[i];
1527
1573
  const value = tokens[i + 1];
1528
1574
  if (!value) {
1529
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1575
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1530
1576
  continue;
1531
1577
  }
1532
1578
  directives.set(key, value);
@@ -1534,13 +1580,13 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1534
1580
 
1535
1581
  for (const requiredKey of ["async_for", "state_field", "completed", "failed"]) {
1536
1582
  if (!directives.has(requiredKey)) {
1537
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1583
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1538
1584
  }
1539
1585
  }
1540
1586
 
1541
1587
  for (const key of directives.keys()) {
1542
1588
  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);
1589
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1544
1590
  }
1545
1591
  }
1546
1592
 
@@ -1548,11 +1594,11 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1548
1594
  if (asyncCapabilityId) {
1549
1595
  const asyncCapability = registry.get(asyncCapabilityId);
1550
1596
  if (!asyncCapability) {
1551
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1597
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1552
1598
  } 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);
1599
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1554
1600
  } 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);
1601
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1556
1602
  }
1557
1603
  }
1558
1604
 
@@ -1563,7 +1609,7 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1563
1609
  ["error_field", directives.get("error_field")]
1564
1610
  ]) {
1565
1611
  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);
1612
+ pushError(errors, `Projection ${statement.id} async_status references unknown output field '${fieldName}' for '${directive}' on ${capabilityId}`, entry.loc);
1567
1613
  }
1568
1614
  }
1569
1615
 
@@ -1571,16 +1617,16 @@ function validateProjectionHttpStatus(errors, statement, fieldMap, registry) {
1571
1617
  if (downloadCapabilityId) {
1572
1618
  const downloadCapability = registry.get(downloadCapabilityId);
1573
1619
  if (!downloadCapability) {
1574
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' references missing download capability '${downloadCapabilityId}'`, entry.loc);
1620
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' references missing download capability '${downloadCapabilityId}'`, entry.loc);
1575
1621
  } 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);
1622
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' must reference a capability for 'download_capability', found ${downloadCapability.kind} '${downloadCapability.id}'`, entry.loc);
1577
1623
  } 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);
1624
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' download capability '${downloadCapabilityId}' must also appear in 'realizes'`, entry.loc);
1579
1625
  }
1580
1626
 
1581
1627
  const method = httpMethodsByCapability.get(downloadCapabilityId);
1582
1628
  if (method && method !== "GET") {
1583
- pushError(errors, `Projection ${statement.id} http_status for '${capabilityId}' download capability '${downloadCapabilityId}' must use HTTP GET`, entry.loc);
1629
+ pushError(errors, `Projection ${statement.id} async_status for '${capabilityId}' download capability '${downloadCapabilityId}' must use HTTP GET`, entry.loc);
1584
1630
  }
1585
1631
  }
1586
1632
  }
@@ -1591,7 +1637,7 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1591
1637
  return;
1592
1638
  }
1593
1639
 
1594
- const httpDownloadField = fieldMap.get("http_download")?.[0];
1640
+ const httpDownloadField = fieldMap.get("downloads")?.[0];
1595
1641
  if (!httpDownloadField || httpDownloadField.value.type !== "block") {
1596
1642
  return;
1597
1643
  }
@@ -1603,14 +1649,14 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1603
1649
  const capability = registry.get(capabilityId);
1604
1650
 
1605
1651
  if (!capability) {
1606
- pushError(errors, `Projection ${statement.id} http_download references missing capability '${capabilityId}'`, entry.loc);
1652
+ pushError(errors, `Projection ${statement.id} downloads references missing capability '${capabilityId}'`, entry.loc);
1607
1653
  continue;
1608
1654
  }
1609
1655
  if (capability.kind !== "capability") {
1610
- pushError(errors, `Projection ${statement.id} http_download must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1656
+ pushError(errors, `Projection ${statement.id} downloads must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1611
1657
  }
1612
1658
  if (!realized.has(capabilityId)) {
1613
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1659
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1614
1660
  }
1615
1661
 
1616
1662
  const directives = new Map();
@@ -1618,7 +1664,7 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1618
1664
  const key = tokens[i];
1619
1665
  const value = tokens[i + 1];
1620
1666
  if (!value) {
1621
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1667
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1622
1668
  continue;
1623
1669
  }
1624
1670
  directives.set(key, value);
@@ -1626,13 +1672,13 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1626
1672
 
1627
1673
  for (const requiredKey of ["async_for", "media", "disposition"]) {
1628
1674
  if (!directives.has(requiredKey)) {
1629
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1675
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1630
1676
  }
1631
1677
  }
1632
1678
 
1633
1679
  for (const key of directives.keys()) {
1634
1680
  if (!["async_for", "media", "filename", "disposition"].includes(key)) {
1635
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1681
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1636
1682
  }
1637
1683
  }
1638
1684
 
@@ -1640,22 +1686,22 @@ function validateProjectionHttpDownload(errors, statement, fieldMap, registry) {
1640
1686
  if (asyncCapabilityId) {
1641
1687
  const asyncCapability = registry.get(asyncCapabilityId);
1642
1688
  if (!asyncCapability) {
1643
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1689
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' references missing async capability '${asyncCapabilityId}'`, entry.loc);
1644
1690
  } 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);
1691
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must reference a capability for 'async_for', found ${asyncCapability.kind} '${asyncCapability.id}'`, entry.loc);
1646
1692
  } 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);
1693
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' async capability '${asyncCapabilityId}' must also appear in 'realizes'`, entry.loc);
1648
1694
  }
1649
1695
  }
1650
1696
 
1651
1697
  const media = directives.get("media");
1652
1698
  if (media && !media.includes("/")) {
1653
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' must use a valid media type`, entry.loc);
1699
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' must use a valid media type`, entry.loc);
1654
1700
  }
1655
1701
 
1656
1702
  const disposition = directives.get("disposition");
1657
1703
  if (disposition && !["attachment", "inline"].includes(disposition)) {
1658
- pushError(errors, `Projection ${statement.id} http_download for '${capabilityId}' has invalid disposition '${disposition}'`, entry.loc);
1704
+ pushError(errors, `Projection ${statement.id} downloads for '${capabilityId}' has invalid disposition '${disposition}'`, entry.loc);
1659
1705
  }
1660
1706
  }
1661
1707
  }
@@ -1665,7 +1711,7 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1665
1711
  return;
1666
1712
  }
1667
1713
 
1668
- const httpAuthzField = fieldMap.get("http_authz")?.[0];
1714
+ const httpAuthzField = fieldMap.get("authorization")?.[0];
1669
1715
  if (!httpAuthzField || httpAuthzField.value.type !== "block") {
1670
1716
  return;
1671
1717
  }
@@ -1677,14 +1723,14 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1677
1723
  const capability = registry.get(capabilityId);
1678
1724
 
1679
1725
  if (!capability) {
1680
- pushError(errors, `Projection ${statement.id} http_authz references missing capability '${capabilityId}'`, entry.loc);
1726
+ pushError(errors, `Projection ${statement.id} authorization references missing capability '${capabilityId}'`, entry.loc);
1681
1727
  continue;
1682
1728
  }
1683
1729
  if (capability.kind !== "capability") {
1684
- pushError(errors, `Projection ${statement.id} http_authz must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1730
+ pushError(errors, `Projection ${statement.id} authorization must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1685
1731
  }
1686
1732
  if (!realized.has(capabilityId)) {
1687
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1733
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1688
1734
  }
1689
1735
 
1690
1736
  const directives = new Map();
@@ -1692,7 +1738,7 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1692
1738
  const key = tokens[i];
1693
1739
  const value = tokens[i + 1];
1694
1740
  if (!value) {
1695
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1741
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1696
1742
  continue;
1697
1743
  }
1698
1744
  directives.set(key, value);
@@ -1700,27 +1746,27 @@ function validateProjectionHttpAuthz(errors, statement, fieldMap, registry) {
1700
1746
 
1701
1747
  for (const key of directives.keys()) {
1702
1748
  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);
1749
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1704
1750
  }
1705
1751
  }
1706
1752
 
1707
1753
  if (directives.size === 0) {
1708
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' must include at least one directive`, entry.loc);
1754
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' must include at least one directive`, entry.loc);
1709
1755
  }
1710
1756
 
1711
1757
  const ownership = directives.get("ownership");
1712
1758
  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);
1759
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' has invalid ownership '${ownership}'`, entry.loc);
1714
1760
  }
1715
1761
 
1716
1762
  const ownershipField = directives.get("ownership_field");
1717
1763
  if (ownershipField && (!ownership || ownership === "none")) {
1718
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' cannot declare ownership_field without ownership`, entry.loc);
1764
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' cannot declare ownership_field without ownership`, entry.loc);
1719
1765
  }
1720
1766
 
1721
1767
  const claimValue = directives.get("claim_value");
1722
1768
  if (claimValue && !directives.get("claim")) {
1723
- pushError(errors, `Projection ${statement.id} http_authz for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
1769
+ pushError(errors, `Projection ${statement.id} authorization for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
1724
1770
  }
1725
1771
  }
1726
1772
  }
@@ -1730,7 +1776,7 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1730
1776
  return;
1731
1777
  }
1732
1778
 
1733
- const httpCallbacksField = fieldMap.get("http_callbacks")?.[0];
1779
+ const httpCallbacksField = fieldMap.get("callbacks")?.[0];
1734
1780
  if (!httpCallbacksField || httpCallbacksField.value.type !== "block") {
1735
1781
  return;
1736
1782
  }
@@ -1742,14 +1788,14 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1742
1788
  const capability = registry.get(capabilityId);
1743
1789
 
1744
1790
  if (!capability) {
1745
- pushError(errors, `Projection ${statement.id} http_callbacks references missing capability '${capabilityId}'`, entry.loc);
1791
+ pushError(errors, `Projection ${statement.id} callbacks references missing capability '${capabilityId}'`, entry.loc);
1746
1792
  continue;
1747
1793
  }
1748
1794
  if (capability.kind !== "capability") {
1749
- pushError(errors, `Projection ${statement.id} http_callbacks must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1795
+ pushError(errors, `Projection ${statement.id} callbacks must target a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
1750
1796
  }
1751
1797
  if (!realized.has(capabilityId)) {
1752
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1798
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must also appear in 'realizes'`, entry.loc);
1753
1799
  }
1754
1800
 
1755
1801
  const directives = new Map();
@@ -1757,7 +1803,7 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1757
1803
  const key = tokens[i];
1758
1804
  const value = tokens[i + 1];
1759
1805
  if (!value) {
1760
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1806
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
1761
1807
  continue;
1762
1808
  }
1763
1809
  directives.set(key, value);
@@ -1765,40 +1811,40 @@ function validateProjectionHttpCallbacks(errors, statement, fieldMap, registry)
1765
1811
 
1766
1812
  for (const requiredKey of ["event", "target_field", "method", "payload", "success"]) {
1767
1813
  if (!directives.has(requiredKey)) {
1768
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1814
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must include '${requiredKey}'`, entry.loc);
1769
1815
  }
1770
1816
  }
1771
1817
 
1772
1818
  for (const key of directives.keys()) {
1773
1819
  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);
1820
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
1775
1821
  }
1776
1822
  }
1777
1823
 
1778
1824
  const method = directives.get("method");
1779
1825
  if (method && !["POST", "PUT", "PATCH"].includes(method)) {
1780
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' has invalid method '${method}'`, entry.loc);
1826
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' has invalid method '${method}'`, entry.loc);
1781
1827
  }
1782
1828
 
1783
1829
  const success = directives.get("success");
1784
1830
  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);
1831
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must use a 3-digit success status`, entry.loc);
1786
1832
  }
1787
1833
 
1788
1834
  const payloadShapeId = directives.get("payload");
1789
1835
  if (payloadShapeId) {
1790
1836
  const payloadShape = registry.get(payloadShapeId);
1791
1837
  if (!payloadShape) {
1792
- pushError(errors, `Projection ${statement.id} http_callbacks for '${capabilityId}' references missing shape '${payloadShapeId}'`, entry.loc);
1838
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' references missing shape '${payloadShapeId}'`, entry.loc);
1793
1839
  } 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);
1840
+ pushError(errors, `Projection ${statement.id} callbacks for '${capabilityId}' must reference a shape for 'payload', found ${payloadShape.kind} '${payloadShape.id}'`, entry.loc);
1795
1841
  }
1796
1842
  }
1797
1843
 
1798
1844
  const targetField = directives.get("target_field");
1799
1845
  const inputFields = resolveCapabilityContractFields(registry, capabilityId, "input");
1800
1846
  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);
1847
+ pushError(errors, `Projection ${statement.id} callbacks references unknown input field '${targetField}' on ${capabilityId}`, entry.loc);
1802
1848
  }
1803
1849
  }
1804
1850
  }
@@ -1909,7 +1955,7 @@ function resolveCapabilityOutputShape(registry, capabilityId) {
1909
1955
  }
1910
1956
 
1911
1957
  function collectProjectionUiScreens(statement, fieldMap) {
1912
- const screensField = fieldMap.get("ui_screens")?.[0];
1958
+ const screensField = fieldMap.get("screens")?.[0];
1913
1959
  if (!screensField || screensField.value.type !== "block") {
1914
1960
  return new Map();
1915
1961
  }
@@ -1954,7 +2000,7 @@ function resolveProjectionUiScreenFieldNames(registry, screenEntry, statement) {
1954
2000
 
1955
2001
  function screenIdsFromProjectionStatement(statement) {
1956
2002
  const screens = new Set();
1957
- for (const entry of blockEntries(getFieldValue(statement, "ui_screens"))) {
2003
+ for (const entry of blockEntries(getFieldValue(statement, "screens"))) {
1958
2004
  const tokens = blockSymbolItems(entry).map((item) => item.value);
1959
2005
  if (tokens[0] === "screen" && tokens[1]) {
1960
2006
  screens.add(tokens[1]);
@@ -1978,7 +2024,7 @@ function collectAvailableUiScreenIds(statement, fieldMap, registry) {
1978
2024
 
1979
2025
  function collectProjectionUiRegionKeys(statement) {
1980
2026
  const keys = new Set();
1981
- for (const entry of blockEntries(getFieldValue(statement, "ui_screen_regions"))) {
2027
+ for (const entry of blockEntries(getFieldValue(statement, "screen_regions"))) {
1982
2028
  const tokens = blockSymbolItems(entry).map((item) => item.value);
1983
2029
  if (tokens[0] === "screen" && tokens[1] && tokens[2] === "region" && tokens[3]) {
1984
2030
  keys.add(`${tokens[1]}:${tokens[3]}`);
@@ -2002,7 +2048,7 @@ function collectAvailableUiRegionKeys(statement, registry) {
2002
2048
 
2003
2049
  function collectProjectionUiRegionPatterns(statement) {
2004
2050
  const patterns = new Map();
2005
- for (const entry of blockEntries(getFieldValue(statement, "ui_screen_regions"))) {
2051
+ for (const entry of blockEntries(getFieldValue(statement, "screen_regions"))) {
2006
2052
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2007
2053
  if (tokens[0] !== "screen" || !tokens[1] || tokens[2] !== "region" || !tokens[3]) {
2008
2054
  continue;
@@ -2056,7 +2102,7 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2056
2102
  return;
2057
2103
  }
2058
2104
 
2059
- const screensField = fieldMap.get("ui_screens")?.[0];
2105
+ const screensField = fieldMap.get("screens")?.[0];
2060
2106
  if (!screensField || screensField.value.type !== "block") {
2061
2107
  return;
2062
2108
  }
@@ -2069,33 +2115,33 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2069
2115
  const [keyword, screenId] = tokens;
2070
2116
 
2071
2117
  if (keyword !== "screen") {
2072
- pushError(errors, `Projection ${statement.id} ui_screens entries must start with 'screen'`, entry.loc);
2118
+ pushError(errors, `Projection ${statement.id} screens entries must start with 'screen'`, entry.loc);
2073
2119
  continue;
2074
2120
  }
2075
2121
  if (!screenId) {
2076
- pushError(errors, `Projection ${statement.id} ui_screens entries must include a screen id`, entry.loc);
2122
+ pushError(errors, `Projection ${statement.id} screens entries must include a screen id`, entry.loc);
2077
2123
  continue;
2078
2124
  }
2079
2125
  if (!IDENTIFIER_PATTERN.test(screenId)) {
2080
- pushError(errors, `Projection ${statement.id} ui_screens has invalid screen id '${screenId}'`, entry.loc);
2126
+ pushError(errors, `Projection ${statement.id} screens has invalid screen id '${screenId}'`, entry.loc);
2081
2127
  }
2082
2128
  if (seenScreens.has(screenId)) {
2083
- pushError(errors, `Projection ${statement.id} ui_screens has duplicate screen id '${screenId}'`, entry.loc);
2129
+ pushError(errors, `Projection ${statement.id} screens has duplicate screen id '${screenId}'`, entry.loc);
2084
2130
  }
2085
2131
  seenScreens.add(screenId);
2086
2132
 
2087
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_screens for '${screenId}'`);
2133
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `screens for '${screenId}'`);
2088
2134
  const kind = directives.get("kind");
2089
2135
  if (!kind) {
2090
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' must include 'kind'`, entry.loc);
2136
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must include 'kind'`, entry.loc);
2091
2137
  }
2092
2138
  if (kind && !UI_SCREEN_KINDS.has(kind)) {
2093
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' has invalid kind '${kind}'`, entry.loc);
2139
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid kind '${kind}'`, entry.loc);
2094
2140
  }
2095
2141
 
2096
2142
  for (const key of directives.keys()) {
2097
2143
  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);
2144
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has unknown directive '${key}'`, entry.loc);
2099
2145
  }
2100
2146
  }
2101
2147
 
@@ -2117,43 +2163,43 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2117
2163
  }
2118
2164
  const target = registry.get(targetId);
2119
2165
  if (!target) {
2120
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' references missing ${expectedKind} '${targetId}' for '${key}'`, entry.loc);
2166
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references missing ${expectedKind} '${targetId}' for '${key}'`, entry.loc);
2121
2167
  continue;
2122
2168
  }
2123
2169
  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);
2170
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' must reference a ${expectedKind} for '${key}', found ${target.kind} '${target.id}'`, entry.loc);
2125
2171
  }
2126
2172
  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);
2173
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' capability '${targetId}' for '${key}' must also appear in 'realizes'`, entry.loc);
2128
2174
  }
2129
2175
  }
2130
2176
 
2131
2177
  const successNavigate = directives.get("success_navigate");
2132
2178
  const successRefresh = directives.get("success_refresh");
2133
2179
  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);
2180
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successNavigate}' for 'success_navigate'`, entry.loc);
2135
2181
  }
2136
2182
  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);
2183
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' has invalid target '${successRefresh}' for 'success_refresh'`, entry.loc);
2138
2184
  }
2139
2185
 
2140
2186
  if (kind === "list" && !directives.get("load")) {
2141
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'list' requires 'load'`, entry.loc);
2187
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'list' requires 'load'`, entry.loc);
2142
2188
  }
2143
2189
  if (kind === "detail") {
2144
2190
  if (!directives.get("load")) {
2145
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'detail' requires 'load'`, entry.loc);
2191
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'load'`, entry.loc);
2146
2192
  }
2147
2193
  if (!directives.get("view_shape")) {
2148
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'detail' requires 'view_shape'`, entry.loc);
2194
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'detail' requires 'view_shape'`, entry.loc);
2149
2195
  }
2150
2196
  }
2151
2197
  if (kind === "form") {
2152
2198
  if (!directives.get("input_shape")) {
2153
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'form' requires 'input_shape'`, entry.loc);
2199
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'input_shape'`, entry.loc);
2154
2200
  }
2155
2201
  if (!directives.get("submit")) {
2156
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' kind 'form' requires 'submit'`, entry.loc);
2202
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' kind 'form' requires 'submit'`, entry.loc);
2157
2203
  }
2158
2204
  }
2159
2205
  }
@@ -2168,7 +2214,7 @@ function validateProjectionUiScreens(errors, statement, fieldMap, registry) {
2168
2214
  for (const key of ["success_navigate", "success_refresh"]) {
2169
2215
  const targetScreenId = directives.get(key);
2170
2216
  if (targetScreenId && !seenScreens.has(targetScreenId)) {
2171
- pushError(errors, `Projection ${statement.id} ui_screens for '${screenId}' references unknown screen '${targetScreenId}' for '${key}'`, entry.loc);
2217
+ pushError(errors, `Projection ${statement.id} screens for '${screenId}' references unknown screen '${targetScreenId}' for '${key}'`, entry.loc);
2172
2218
  }
2173
2219
  }
2174
2220
  }
@@ -2179,7 +2225,7 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2179
2225
  return;
2180
2226
  }
2181
2227
 
2182
- const collectionsField = fieldMap.get("ui_collections")?.[0];
2228
+ const collectionsField = fieldMap.get("collection_views")?.[0];
2183
2229
  if (!collectionsField || collectionsField.value.type !== "block") {
2184
2230
  return;
2185
2231
  }
@@ -2190,23 +2236,23 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2190
2236
  const [keyword, screenId, operation, value, extra] = tokens;
2191
2237
 
2192
2238
  if (keyword !== "screen") {
2193
- pushError(errors, `Projection ${statement.id} ui_collections entries must start with 'screen'`, entry.loc);
2239
+ pushError(errors, `Projection ${statement.id} collection_views entries must start with 'screen'`, entry.loc);
2194
2240
  continue;
2195
2241
  }
2196
2242
  const screenEntry = screens.get(screenId);
2197
2243
  if (!screenEntry) {
2198
- pushError(errors, `Projection ${statement.id} ui_collections references unknown screen '${screenId}'`, entry.loc);
2244
+ pushError(errors, `Projection ${statement.id} collection_views references unknown screen '${screenId}'`, entry.loc);
2199
2245
  continue;
2200
2246
  }
2201
2247
 
2202
2248
  const screenTokens = blockSymbolItems(screenEntry).map((item) => item.value);
2203
2249
  const screenDirectives = parseUiDirectiveMap(screenTokens, 2, [], statement, screenEntry, "");
2204
2250
  if (screenDirectives.get("kind") !== "list") {
2205
- pushError(errors, `Projection ${statement.id} ui_collections may only target list screens, found '${screenId}'`, entry.loc);
2251
+ pushError(errors, `Projection ${statement.id} collection_views may only target list screens, found '${screenId}'`, entry.loc);
2206
2252
  }
2207
2253
 
2208
2254
  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);
2255
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid operation '${operation}'`, entry.loc);
2210
2256
  continue;
2211
2257
  }
2212
2258
 
@@ -2219,43 +2265,43 @@ function validateProjectionUiCollections(errors, statement, fieldMap, registry)
2219
2265
 
2220
2266
  if (operation === "filter" || operation === "search") {
2221
2267
  if (!value) {
2222
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must include a field for '${operation}'`, entry.loc);
2268
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must include a field for '${operation}'`, entry.loc);
2223
2269
  } 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);
2270
+ pushError(errors, `Projection ${statement.id} collection_views references unknown input field '${value}' for '${operation}' on '${screenId}'`, entry.loc);
2225
2271
  }
2226
2272
  }
2227
2273
 
2228
2274
  if (operation === "pagination" && !["cursor", "paged", "none"].includes(value || "")) {
2229
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid pagination '${value}'`, entry.loc);
2275
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid pagination '${value}'`, entry.loc);
2230
2276
  }
2231
2277
 
2232
2278
  if (operation === "sort") {
2233
2279
  if (!value || !extra) {
2234
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must use 'sort <field> <asc|desc>'`, entry.loc);
2280
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must use 'sort <field> <asc|desc>'`, entry.loc);
2235
2281
  } else {
2236
2282
  if (!["asc", "desc"].includes(extra)) {
2237
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid sort direction '${extra}'`, entry.loc);
2283
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid sort direction '${extra}'`, entry.loc);
2238
2284
  }
2239
2285
  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);
2286
+ pushError(errors, `Projection ${statement.id} collection_views references unknown output field '${value}' for sort on '${screenId}'`, entry.loc);
2241
2287
  }
2242
2288
  }
2243
2289
  }
2244
2290
 
2245
2291
  if (operation === "group") {
2246
2292
  if (!value) {
2247
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' must include a field for 'group'`, entry.loc);
2293
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' must include a field for 'group'`, entry.loc);
2248
2294
  } 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);
2295
+ pushError(errors, `Projection ${statement.id} collection_views references unknown output field '${value}' for group on '${screenId}'`, entry.loc);
2250
2296
  }
2251
2297
  }
2252
2298
 
2253
2299
  if (operation === "view" && !UI_COLLECTION_PRESENTATIONS.has(value || "")) {
2254
- pushError(errors, `Projection ${statement.id} ui_collections for '${screenId}' has invalid view '${value}'`, entry.loc);
2300
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid view '${value}'`, entry.loc);
2255
2301
  }
2256
2302
 
2257
2303
  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);
2304
+ pushError(errors, `Projection ${statement.id} collection_views for '${screenId}' has invalid refresh '${value}'`, entry.loc);
2259
2305
  }
2260
2306
  }
2261
2307
  }
@@ -2265,7 +2311,7 @@ function validateProjectionUiActions(errors, statement, fieldMap, registry) {
2265
2311
  return;
2266
2312
  }
2267
2313
 
2268
- const actionsField = fieldMap.get("ui_actions")?.[0];
2314
+ const actionsField = fieldMap.get("screen_actions")?.[0];
2269
2315
  if (!actionsField || actionsField.value.type !== "block") {
2270
2316
  return;
2271
2317
  }
@@ -2278,34 +2324,34 @@ function validateProjectionUiActions(errors, statement, fieldMap, registry) {
2278
2324
  const [keyword, screenId, actionKeyword, capabilityId, prominenceKeyword, prominence, placementKeyword, placement] = tokens;
2279
2325
 
2280
2326
  if (keyword !== "screen") {
2281
- pushError(errors, `Projection ${statement.id} ui_actions entries must start with 'screen'`, entry.loc);
2327
+ pushError(errors, `Projection ${statement.id} screen_actions entries must start with 'screen'`, entry.loc);
2282
2328
  continue;
2283
2329
  }
2284
2330
  if (!screens.has(screenId)) {
2285
- pushError(errors, `Projection ${statement.id} ui_actions references unknown screen '${screenId}'`, entry.loc);
2331
+ pushError(errors, `Projection ${statement.id} screen_actions references unknown screen '${screenId}'`, entry.loc);
2286
2332
  }
2287
2333
  if (actionKeyword !== "action") {
2288
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' must use 'action'`, entry.loc);
2334
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' must use 'action'`, entry.loc);
2289
2335
  }
2290
2336
  const capability = registry.get(capabilityId);
2291
2337
  if (!capability) {
2292
- pushError(errors, `Projection ${statement.id} ui_actions references missing capability '${capabilityId}'`, entry.loc);
2338
+ pushError(errors, `Projection ${statement.id} screen_actions references missing capability '${capabilityId}'`, entry.loc);
2293
2339
  } else if (capability.kind !== "capability") {
2294
- pushError(errors, `Projection ${statement.id} ui_actions must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2340
+ pushError(errors, `Projection ${statement.id} screen_actions must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2295
2341
  } else if (!realized.has(capabilityId)) {
2296
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' capability '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2342
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' capability '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2297
2343
  }
2298
2344
  if (prominenceKeyword !== "prominence") {
2299
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' must use 'prominence'`, entry.loc);
2345
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' must use 'prominence'`, entry.loc);
2300
2346
  }
2301
2347
  if (!["primary", "secondary", "destructive", "contextual"].includes(prominence || "")) {
2302
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' has invalid prominence '${prominence}'`, entry.loc);
2348
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has invalid prominence '${prominence}'`, entry.loc);
2303
2349
  }
2304
2350
  if (placementKeyword && placementKeyword !== "placement") {
2305
- pushError(errors, `Projection ${statement.id} ui_actions for '${screenId}' has unknown directive '${placementKeyword}'`, entry.loc);
2351
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has unknown directive '${placementKeyword}'`, entry.loc);
2306
2352
  }
2307
2353
  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);
2354
+ pushError(errors, `Projection ${statement.id} screen_actions for '${screenId}' has invalid placement '${placement}'`, entry.loc);
2309
2355
  }
2310
2356
  }
2311
2357
  }
@@ -2315,7 +2361,7 @@ function validateProjectionUiAppShell(errors, statement, fieldMap) {
2315
2361
  return;
2316
2362
  }
2317
2363
 
2318
- const shellField = fieldMap.get("ui_app_shell")?.[0];
2364
+ const shellField = fieldMap.get("app_shell")?.[0];
2319
2365
  if (!shellField || shellField.value.type !== "block") {
2320
2366
  return;
2321
2367
  }
@@ -2325,42 +2371,42 @@ function validateProjectionUiAppShell(errors, statement, fieldMap) {
2325
2371
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2326
2372
  const [key, value, extra] = tokens;
2327
2373
  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);
2374
+ pushError(errors, `Projection ${statement.id} app_shell has unknown key '${key}'`, entry.loc);
2329
2375
  continue;
2330
2376
  }
2331
2377
  if (!value) {
2332
- pushError(errors, `Projection ${statement.id} ui_app_shell is missing a value for '${key}'`, entry.loc);
2378
+ pushError(errors, `Projection ${statement.id} app_shell is missing a value for '${key}'`, entry.loc);
2333
2379
  continue;
2334
2380
  }
2335
2381
  if (extra) {
2336
- pushError(errors, `Projection ${statement.id} ui_app_shell '${key}' accepts exactly one value`, entry.loc);
2382
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' accepts exactly one value`, entry.loc);
2337
2383
  }
2338
2384
  if (seenKeys.has(key)) {
2339
- pushError(errors, `Projection ${statement.id} ui_app_shell has duplicate key '${key}'`, entry.loc);
2385
+ pushError(errors, `Projection ${statement.id} app_shell has duplicate key '${key}'`, entry.loc);
2340
2386
  }
2341
2387
  seenKeys.add(key);
2342
2388
 
2343
2389
  if (key === "shell" && !UI_APP_SHELL_KINDS.has(value)) {
2344
- pushError(errors, `Projection ${statement.id} ui_app_shell has invalid shell '${value}'`, entry.loc);
2390
+ pushError(errors, `Projection ${statement.id} app_shell has invalid shell '${value}'`, entry.loc);
2345
2391
  }
2346
2392
  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);
2393
+ pushError(errors, `Projection ${statement.id} app_shell '${key}' must be true or false`, entry.loc);
2348
2394
  }
2349
2395
  if (key === "windowing" && !UI_WINDOWING_MODES.has(value)) {
2350
- pushError(errors, `Projection ${statement.id} ui_app_shell has invalid windowing '${value}'`, entry.loc);
2396
+ pushError(errors, `Projection ${statement.id} app_shell has invalid windowing '${value}'`, entry.loc);
2351
2397
  }
2352
2398
  }
2353
2399
  }
2354
2400
 
2355
2401
  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"
2402
+ "screens",
2403
+ "collection_views",
2404
+ "screen_actions",
2405
+ "visibility_rules",
2406
+ "field_lookups",
2407
+ "app_shell",
2408
+ "navigation",
2409
+ "screen_regions"
2364
2410
  ];
2365
2411
 
2366
2412
  function validateProjectionUiOwnership(errors, statement, fieldMap) {
@@ -2368,26 +2414,26 @@ function validateProjectionUiOwnership(errors, statement, fieldMap) {
2368
2414
  return;
2369
2415
  }
2370
2416
 
2371
- const platform = symbolValue(getFieldValue(statement, "platform"));
2417
+ const platform = symbolValue(getFieldValue(statement, "type"));
2372
2418
  for (const key of SHARED_UI_SEMANTIC_BLOCKS) {
2373
2419
  const field = fieldMap.get(key)?.[0];
2374
2420
  if (!field || field.value.type !== "block") {
2375
2421
  continue;
2376
2422
  }
2377
- if (platform !== "ui_shared") {
2423
+ if (platform !== "ui_contract") {
2378
2424
  pushError(
2379
2425
  errors,
2380
- `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define ui_routes and platform surface hints only`,
2426
+ `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define screen_routes and platform surface hints only`,
2381
2427
  field.loc
2382
2428
  );
2383
2429
  }
2384
2430
  }
2385
2431
 
2386
- const routesField = fieldMap.get("ui_routes")?.[0];
2387
- if (routesField?.value.type === "block" && !["ui_web", "ui_ios"].includes(platform || "")) {
2432
+ const routesField = fieldMap.get("screen_routes")?.[0];
2433
+ if (routesField?.value.type === "block" && !["web_surface", "ios_surface"].includes(platform || "")) {
2388
2434
  pushError(
2389
2435
  errors,
2390
- `Projection ${statement.id} ui_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
2436
+ `Projection ${statement.id} screen_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
2391
2437
  routesField.loc
2392
2438
  );
2393
2439
  }
@@ -2398,13 +2444,13 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2398
2444
  return;
2399
2445
  }
2400
2446
 
2401
- const designField = fieldMap.get("ui_design")?.[0];
2447
+ const designField = fieldMap.get("design_tokens")?.[0];
2402
2448
  if (!designField || designField.value.type !== "block") {
2403
2449
  return;
2404
2450
  }
2405
2451
 
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);
2452
+ if (symbolValue(getFieldValue(statement, "type")) !== "ui_contract") {
2453
+ pushError(errors, `Projection ${statement.id} design_tokens belongs on shared UI projections; concrete UI projections inherit semantic design intent through 'realizes'`, designField.loc);
2408
2454
  }
2409
2455
 
2410
2456
  for (const entry of designField.value.entries) {
@@ -2413,60 +2459,60 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2413
2459
 
2414
2460
  if (key === "density") {
2415
2461
  if (!UI_DESIGN_DENSITIES.has(value || "")) {
2416
- pushError(errors, `Projection ${statement.id} ui_design density has invalid value '${value}'`, entry.loc);
2462
+ pushError(errors, `Projection ${statement.id} design_tokens density has invalid value '${value}'`, entry.loc);
2417
2463
  }
2418
2464
  if (tokens.length !== 2) {
2419
- pushError(errors, `Projection ${statement.id} ui_design density accepts exactly one value`, entry.loc);
2465
+ pushError(errors, `Projection ${statement.id} design_tokens density accepts exactly one value`, entry.loc);
2420
2466
  }
2421
2467
  continue;
2422
2468
  }
2423
2469
 
2424
2470
  if (key === "tone") {
2425
2471
  if (!UI_DESIGN_TONES.has(value || "")) {
2426
- pushError(errors, `Projection ${statement.id} ui_design tone has invalid value '${value}'`, entry.loc);
2472
+ pushError(errors, `Projection ${statement.id} design_tokens tone has invalid value '${value}'`, entry.loc);
2427
2473
  }
2428
2474
  if (tokens.length !== 2) {
2429
- pushError(errors, `Projection ${statement.id} ui_design tone accepts exactly one value`, entry.loc);
2475
+ pushError(errors, `Projection ${statement.id} design_tokens tone accepts exactly one value`, entry.loc);
2430
2476
  }
2431
2477
  continue;
2432
2478
  }
2433
2479
 
2434
2480
  if (key === "radius_scale") {
2435
2481
  if (!UI_DESIGN_RADIUS_SCALES.has(value || "")) {
2436
- pushError(errors, `Projection ${statement.id} ui_design radius_scale has invalid value '${value}'`, entry.loc);
2482
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale has invalid value '${value}'`, entry.loc);
2437
2483
  }
2438
2484
  if (tokens.length !== 2) {
2439
- pushError(errors, `Projection ${statement.id} ui_design radius_scale accepts exactly one value`, entry.loc);
2485
+ pushError(errors, `Projection ${statement.id} design_tokens radius_scale accepts exactly one value`, entry.loc);
2440
2486
  }
2441
2487
  continue;
2442
2488
  }
2443
2489
 
2444
2490
  if (key === "color_role") {
2445
2491
  if (!UI_DESIGN_COLOR_ROLES.has(value || "")) {
2446
- pushError(errors, `Projection ${statement.id} ui_design color_role has invalid role '${value}'`, entry.loc);
2492
+ pushError(errors, `Projection ${statement.id} design_tokens color_role has invalid role '${value}'`, entry.loc);
2447
2493
  }
2448
2494
  if (tokens.length !== 3) {
2449
- pushError(errors, `Projection ${statement.id} ui_design color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
2495
+ pushError(errors, `Projection ${statement.id} design_tokens color_role must use 'color_role <role> <semantic-token>'`, entry.loc);
2450
2496
  }
2451
2497
  continue;
2452
2498
  }
2453
2499
 
2454
2500
  if (key === "typography_role") {
2455
2501
  if (!UI_DESIGN_TYPOGRAPHY_ROLES.has(value || "")) {
2456
- pushError(errors, `Projection ${statement.id} ui_design typography_role has invalid role '${value}'`, entry.loc);
2502
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role has invalid role '${value}'`, entry.loc);
2457
2503
  }
2458
2504
  if (tokens.length !== 3) {
2459
- pushError(errors, `Projection ${statement.id} ui_design typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
2505
+ pushError(errors, `Projection ${statement.id} design_tokens typography_role must use 'typography_role <role> <semantic-token>'`, entry.loc);
2460
2506
  }
2461
2507
  continue;
2462
2508
  }
2463
2509
 
2464
2510
  if (key === "action_role") {
2465
2511
  if (!UI_DESIGN_ACTION_ROLES.has(value || "")) {
2466
- pushError(errors, `Projection ${statement.id} ui_design action_role has invalid role '${value}'`, entry.loc);
2512
+ pushError(errors, `Projection ${statement.id} design_tokens action_role has invalid role '${value}'`, entry.loc);
2467
2513
  }
2468
2514
  if (tokens.length !== 3) {
2469
- pushError(errors, `Projection ${statement.id} ui_design action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
2515
+ pushError(errors, `Projection ${statement.id} design_tokens action_role must use 'action_role <role> <semantic-token>'`, entry.loc);
2470
2516
  }
2471
2517
  continue;
2472
2518
  }
@@ -2474,17 +2520,17 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2474
2520
  if (key === "accessibility") {
2475
2521
  const values = UI_DESIGN_ACCESSIBILITY_VALUES[value];
2476
2522
  if (tokens.length !== 3) {
2477
- pushError(errors, `Projection ${statement.id} ui_design accessibility must use 'accessibility <setting> <value>'`, entry.loc);
2523
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility must use 'accessibility <setting> <value>'`, entry.loc);
2478
2524
  }
2479
2525
  if (!values) {
2480
- pushError(errors, `Projection ${statement.id} ui_design accessibility has invalid setting '${value}'`, entry.loc);
2526
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility has invalid setting '${value}'`, entry.loc);
2481
2527
  } else if (!values.has(extra || "")) {
2482
- pushError(errors, `Projection ${statement.id} ui_design accessibility '${value}' has invalid value '${extra}'`, entry.loc);
2528
+ pushError(errors, `Projection ${statement.id} design_tokens accessibility '${value}' has invalid value '${extra}'`, entry.loc);
2483
2529
  }
2484
2530
  continue;
2485
2531
  }
2486
2532
 
2487
- pushError(errors, `Projection ${statement.id} ui_design has unknown key '${key}'`, entry.loc);
2533
+ pushError(errors, `Projection ${statement.id} design_tokens has unknown key '${key}'`, entry.loc);
2488
2534
  }
2489
2535
  }
2490
2536
 
@@ -2493,7 +2539,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2493
2539
  return;
2494
2540
  }
2495
2541
 
2496
- const navigationField = fieldMap.get("ui_navigation")?.[0];
2542
+ const navigationField = fieldMap.get("navigation")?.[0];
2497
2543
  if (!navigationField || navigationField.value.type !== "block") {
2498
2544
  return;
2499
2545
  }
@@ -2507,58 +2553,58 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2507
2553
 
2508
2554
  if (targetKind === "group") {
2509
2555
  if (!targetId || !IDENTIFIER_PATTERN.test(targetId)) {
2510
- pushError(errors, `Projection ${statement.id} ui_navigation group entries must include a valid group id`, entry.loc);
2556
+ pushError(errors, `Projection ${statement.id} navigation group entries must include a valid group id`, entry.loc);
2511
2557
  continue;
2512
2558
  }
2513
2559
  groups.add(targetId);
2514
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation group '${targetId}'`);
2560
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation group '${targetId}'`);
2515
2561
  for (const key of directives.keys()) {
2516
2562
  if (!["label", "placement", "icon", "order", "pattern"].includes(key)) {
2517
- pushError(errors, `Projection ${statement.id} ui_navigation group '${targetId}' has unknown directive '${key}'`, entry.loc);
2563
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has unknown directive '${key}'`, entry.loc);
2518
2564
  }
2519
2565
  }
2520
2566
  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);
2567
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2522
2568
  }
2523
2569
  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);
2570
+ pushError(errors, `Projection ${statement.id} navigation group '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2525
2571
  }
2526
2572
  continue;
2527
2573
  }
2528
2574
 
2529
2575
  if (targetKind === "screen") {
2530
2576
  if (!availableScreens.has(targetId)) {
2531
- pushError(errors, `Projection ${statement.id} ui_navigation references unknown screen '${targetId}'`, entry.loc);
2577
+ pushError(errors, `Projection ${statement.id} navigation references unknown screen '${targetId}'`, entry.loc);
2532
2578
  }
2533
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation screen '${targetId}'`);
2579
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation screen '${targetId}'`);
2534
2580
  for (const key of directives.keys()) {
2535
2581
  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);
2582
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has unknown directive '${key}'`, entry.loc);
2537
2583
  }
2538
2584
  }
2539
2585
  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);
2586
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid visible '${directives.get("visible")}'`, entry.loc);
2541
2587
  }
2542
2588
  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);
2589
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid default '${directives.get("default")}'`, entry.loc);
2544
2590
  }
2545
2591
  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);
2592
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2547
2593
  }
2548
2594
  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);
2595
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid sitemap '${directives.get("sitemap")}'`, entry.loc);
2550
2596
  }
2551
2597
  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);
2598
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2553
2599
  }
2554
2600
  const breadcrumb = directives.get("breadcrumb");
2555
2601
  if (breadcrumb && breadcrumb !== "none" && !availableScreens.has(breadcrumb)) {
2556
- pushError(errors, `Projection ${statement.id} ui_navigation screen '${targetId}' references unknown breadcrumb screen '${breadcrumb}'`, entry.loc);
2602
+ pushError(errors, `Projection ${statement.id} navigation screen '${targetId}' references unknown breadcrumb screen '${breadcrumb}'`, entry.loc);
2557
2603
  }
2558
2604
  continue;
2559
2605
  }
2560
2606
 
2561
- pushError(errors, `Projection ${statement.id} ui_navigation entries must start with 'group' or 'screen'`, entry.loc);
2607
+ pushError(errors, `Projection ${statement.id} navigation entries must start with 'group' or 'screen'`, entry.loc);
2562
2608
  }
2563
2609
 
2564
2610
  for (const entry of navigationField.value.entries) {
@@ -2568,7 +2614,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2568
2614
  }
2569
2615
  const directives = parseUiDirectiveMap(tokens, 2, [], statement, entry, "");
2570
2616
  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);
2617
+ pushError(errors, `Projection ${statement.id} navigation screen '${tokens[1]}' references unknown group '${directives.get("group")}'`, entry.loc);
2572
2618
  }
2573
2619
  }
2574
2620
  }
@@ -2578,7 +2624,7 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2578
2624
  return;
2579
2625
  }
2580
2626
 
2581
- const regionField = fieldMap.get("ui_screen_regions")?.[0];
2627
+ const regionField = fieldMap.get("screen_regions")?.[0];
2582
2628
  if (!regionField || regionField.value.type !== "block") {
2583
2629
  return;
2584
2630
  }
@@ -2589,33 +2635,33 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2589
2635
  const [keyword, screenId, regionKeyword, regionName] = tokens;
2590
2636
 
2591
2637
  if (keyword !== "screen") {
2592
- pushError(errors, `Projection ${statement.id} ui_screen_regions entries must start with 'screen'`, entry.loc);
2638
+ pushError(errors, `Projection ${statement.id} screen_regions entries must start with 'screen'`, entry.loc);
2593
2639
  continue;
2594
2640
  }
2595
2641
  if (!availableScreens.has(screenId)) {
2596
- pushError(errors, `Projection ${statement.id} ui_screen_regions references unknown screen '${screenId}'`, entry.loc);
2642
+ pushError(errors, `Projection ${statement.id} screen_regions references unknown screen '${screenId}'`, entry.loc);
2597
2643
  }
2598
2644
  if (regionKeyword !== "region") {
2599
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' must use 'region'`, entry.loc);
2645
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' must use 'region'`, entry.loc);
2600
2646
  }
2601
2647
  if (!UI_REGION_KINDS.has(regionName || "")) {
2602
- pushError(errors, `Projection ${statement.id} ui_screen_regions for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2648
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2603
2649
  }
2604
2650
 
2605
- const directives = parseUiDirectiveMap(tokens, 4, errors, statement, entry, `ui_screen_regions for '${screenId}'`);
2651
+ const directives = parseUiDirectiveMap(tokens, 4, errors, statement, entry, `screen_regions for '${screenId}'`);
2606
2652
  for (const key of directives.keys()) {
2607
2653
  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);
2654
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has unknown directive '${key}'`, entry.loc);
2609
2655
  }
2610
2656
  }
2611
2657
  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);
2658
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid pattern '${directives.get("pattern")}'`, entry.loc);
2613
2659
  }
2614
2660
  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);
2661
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid placement '${directives.get("placement")}'`, entry.loc);
2616
2662
  }
2617
2663
  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);
2664
+ pushError(errors, `Projection ${statement.id} screen_regions for '${screenId}' has invalid state '${directives.get("state")}'`, entry.loc);
2619
2665
  }
2620
2666
  }
2621
2667
  }
@@ -2625,13 +2671,13 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2625
2671
  return;
2626
2672
  }
2627
2673
 
2628
- const componentsField = fieldMap.get("ui_components")?.[0];
2674
+ const componentsField = fieldMap.get("widget_bindings")?.[0];
2629
2675
  if (!componentsField || componentsField.value.type !== "block") {
2630
2676
  return;
2631
2677
  }
2632
2678
 
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);
2679
+ if (symbolValue(getFieldValue(statement, "type")) !== "ui_contract") {
2680
+ pushError(errors, `Projection ${statement.id} widget_bindings belongs on shared UI projections; concrete UI projections inherit widget placement through 'realizes'`, componentsField.loc);
2635
2681
  }
2636
2682
 
2637
2683
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
@@ -2643,48 +2689,48 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2643
2689
  const [screenKeyword, screenId, regionKeyword, regionName, componentKeyword, componentId] = tokens;
2644
2690
 
2645
2691
  if (screenKeyword !== "screen") {
2646
- pushError(errors, `Projection ${statement.id} ui_components entries must start with 'screen'`, entry.loc);
2692
+ pushError(errors, `Projection ${statement.id} widget_bindings entries must start with 'screen'`, entry.loc);
2647
2693
  continue;
2648
2694
  }
2649
2695
  if (!availableScreens.has(screenId)) {
2650
- pushError(errors, `Projection ${statement.id} ui_components references unknown screen '${screenId}'`, entry.loc);
2696
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown screen '${screenId}'`, entry.loc);
2651
2697
  }
2652
2698
  if (regionKeyword !== "region") {
2653
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' must use 'region'`, entry.loc);
2699
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' must use 'region'`, entry.loc);
2654
2700
  }
2655
2701
  if (!UI_REGION_KINDS.has(regionName || "")) {
2656
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2702
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' has invalid region '${regionName}'`, entry.loc);
2657
2703
  } else if (!availableRegions.has(`${screenId}:${regionName}`)) {
2658
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' references undeclared region '${regionName}'`, entry.loc);
2704
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' references undeclared region '${regionName}'`, entry.loc);
2659
2705
  }
2660
- if (componentKeyword !== "component") {
2661
- pushError(errors, `Projection ${statement.id} ui_components for '${screenId}' must use 'component'`, entry.loc);
2706
+ if (componentKeyword !== "widget") {
2707
+ pushError(errors, `Projection ${statement.id} widget_bindings for '${screenId}' must use 'widget'`, entry.loc);
2662
2708
  }
2663
2709
 
2664
- const component = registry.get(componentId);
2665
- if (!component) {
2666
- pushError(errors, `Projection ${statement.id} ui_components references missing component '${componentId}'`, entry.loc);
2710
+ const widget = registry.get(componentId);
2711
+ if (!widget) {
2712
+ pushError(errors, `Projection ${statement.id} widget_bindings references missing widget '${componentId}'`, entry.loc);
2667
2713
  continue;
2668
2714
  }
2669
- if (component.kind !== "component") {
2670
- pushError(errors, `Projection ${statement.id} ui_components must reference a component, found ${component.kind} '${component.id}'`, entry.loc);
2715
+ if (widget.kind !== "widget") {
2716
+ pushError(errors, `Projection ${statement.id} widget_bindings must reference a widget, found ${widget.kind} '${widget.id}'`, entry.loc);
2671
2717
  continue;
2672
2718
  }
2673
2719
 
2674
- const propNames = new Set(blockEntries(getFieldValue(component, "props"))
2720
+ const propNames = new Set(blockEntries(getFieldValue(widget, "props"))
2675
2721
  .map((propEntry) => propEntry.items[0])
2676
2722
  .filter((item) => item?.type === "symbol")
2677
2723
  .map((item) => item.value));
2678
- const eventNames = new Set(blockEntries(getFieldValue(component, "events"))
2724
+ const eventNames = new Set(blockEntries(getFieldValue(widget, "events"))
2679
2725
  .map((eventEntry) => eventEntry.items[0])
2680
2726
  .filter((item) => item?.type === "symbol")
2681
2727
  .map((item) => item.value));
2682
- const componentRegions = symbolValues(getFieldValue(component, "regions"));
2683
- const componentPatterns = symbolValues(getFieldValue(component, "patterns"));
2728
+ const componentRegions = symbolValues(getFieldValue(widget, "regions"));
2729
+ const componentPatterns = symbolValues(getFieldValue(widget, "patterns"));
2684
2730
  if (componentRegions.length > 0 && !componentRegions.includes(regionName)) {
2685
2731
  pushError(
2686
2732
  errors,
2687
- `Projection ${statement.id} ui_components uses component '${componentId}' in region '${regionName}', but the component supports regions [${componentRegions.join(", ")}]`,
2733
+ `Projection ${statement.id} widget_bindings uses widget '${componentId}' in region '${regionName}', but the widget supports regions [${componentRegions.join(", ")}]`,
2688
2734
  entry.loc
2689
2735
  );
2690
2736
  }
@@ -2692,7 +2738,7 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2692
2738
  if (regionPattern && componentPatterns.length > 0 && !componentPatterns.includes(regionPattern)) {
2693
2739
  pushError(
2694
2740
  errors,
2695
- `Projection ${statement.id} ui_components uses component '${componentId}' in '${screenId}:${regionName}' with pattern '${regionPattern}', but the component supports patterns [${componentPatterns.join(", ")}]`,
2741
+ `Projection ${statement.id} widget_bindings uses widget '${componentId}' in '${screenId}:${regionName}' with pattern '${regionPattern}', but the widget supports patterns [${componentPatterns.join(", ")}]`,
2696
2742
  entry.loc
2697
2743
  );
2698
2744
  }
@@ -2704,15 +2750,15 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2704
2750
  const fromKeyword = tokens[i + 2];
2705
2751
  const sourceId = tokens[i + 3];
2706
2752
  if (!propName || fromKeyword !== "from" || !sourceId) {
2707
- pushError(errors, `Projection ${statement.id} ui_components data bindings must use 'data <prop> from <source>'`, entry.loc);
2753
+ pushError(errors, `Projection ${statement.id} widget_bindings data bindings must use 'data <prop> from <source>'`, entry.loc);
2708
2754
  break;
2709
2755
  }
2710
2756
  if (!propNames.has(propName)) {
2711
- pushError(errors, `Projection ${statement.id} ui_components references unknown prop '${propName}' on component '${componentId}'`, entry.loc);
2757
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown prop '${propName}' on widget '${componentId}'`, entry.loc);
2712
2758
  }
2713
2759
  const source = registry.get(sourceId);
2714
2760
  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);
2761
+ pushError(errors, `Projection ${statement.id} widget_bindings data binding for '${propName}' references missing source '${sourceId}'`, entry.loc);
2716
2762
  }
2717
2763
  i += 4;
2718
2764
  continue;
@@ -2723,29 +2769,29 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2723
2769
  const action = tokens[i + 2];
2724
2770
  const targetId = tokens[i + 3];
2725
2771
  if (!eventName || !action || !targetId) {
2726
- pushError(errors, `Projection ${statement.id} ui_components event bindings must use 'event <event> <navigate|action> <target>'`, entry.loc);
2772
+ pushError(errors, `Projection ${statement.id} widget_bindings event bindings must use 'event <event> <navigate|action> <target>'`, entry.loc);
2727
2773
  break;
2728
2774
  }
2729
2775
  if (!eventNames.has(eventName)) {
2730
- pushError(errors, `Projection ${statement.id} ui_components references unknown event '${eventName}' on component '${componentId}'`, entry.loc);
2776
+ pushError(errors, `Projection ${statement.id} widget_bindings references unknown event '${eventName}' on widget '${componentId}'`, entry.loc);
2731
2777
  }
2732
2778
  if (action === "navigate") {
2733
2779
  if (!availableScreens.has(targetId)) {
2734
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' references unknown navigation target '${targetId}'`, entry.loc);
2780
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' references unknown navigation target '${targetId}'`, entry.loc);
2735
2781
  }
2736
2782
  } else if (action === "action") {
2737
2783
  const target = registry.get(targetId);
2738
2784
  if (!target || target.kind !== "capability") {
2739
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' references missing capability action '${targetId}'`, entry.loc);
2785
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' references missing capability action '${targetId}'`, entry.loc);
2740
2786
  }
2741
2787
  } else {
2742
- pushError(errors, `Projection ${statement.id} ui_components event '${eventName}' has unsupported action '${action}'`, entry.loc);
2788
+ pushError(errors, `Projection ${statement.id} widget_bindings event '${eventName}' has unsupported action '${action}'`, entry.loc);
2743
2789
  }
2744
2790
  i += 4;
2745
2791
  continue;
2746
2792
  }
2747
2793
 
2748
- pushError(errors, `Projection ${statement.id} ui_components has unknown directive '${directive}'`, entry.loc);
2794
+ pushError(errors, `Projection ${statement.id} widget_bindings has unknown directive '${directive}'`, entry.loc);
2749
2795
  break;
2750
2796
  }
2751
2797
  }
@@ -2756,7 +2802,7 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2756
2802
  return;
2757
2803
  }
2758
2804
 
2759
- const visibilityField = fieldMap.get("ui_visibility")?.[0];
2805
+ const visibilityField = fieldMap.get("visibility_rules")?.[0];
2760
2806
  if (!visibilityField || visibilityField.value.type !== "block") {
2761
2807
  return;
2762
2808
  }
@@ -2767,30 +2813,30 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2767
2813
  const [keyword, capabilityId, predicateKeyword, predicateType, predicateValue] = tokens;
2768
2814
 
2769
2815
  if (keyword !== "action") {
2770
- pushError(errors, `Projection ${statement.id} ui_visibility entries must start with 'action'`, entry.loc);
2816
+ pushError(errors, `Projection ${statement.id} visibility_rules entries must start with 'action'`, entry.loc);
2771
2817
  continue;
2772
2818
  }
2773
2819
 
2774
2820
  const capability = registry.get(capabilityId);
2775
2821
  if (!capability) {
2776
- pushError(errors, `Projection ${statement.id} ui_visibility references missing capability '${capabilityId}'`, entry.loc);
2822
+ pushError(errors, `Projection ${statement.id} visibility_rules references missing capability '${capabilityId}'`, entry.loc);
2777
2823
  } else if (capability.kind !== "capability") {
2778
- pushError(errors, `Projection ${statement.id} ui_visibility must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2824
+ pushError(errors, `Projection ${statement.id} visibility_rules must reference a capability, found ${capability.kind} '${capability.id}'`, entry.loc);
2779
2825
  } else if (!realized.has(capabilityId)) {
2780
- pushError(errors, `Projection ${statement.id} ui_visibility action '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2826
+ pushError(errors, `Projection ${statement.id} visibility_rules action '${capabilityId}' must also appear in 'realizes'`, entry.loc);
2781
2827
  }
2782
2828
 
2783
2829
  if (predicateKeyword !== "visible_if") {
2784
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' must use 'visible_if'`, entry.loc);
2830
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' must use 'visible_if'`, entry.loc);
2785
2831
  }
2786
2832
  if (!["permission", "ownership", "claim"].includes(predicateType || "")) {
2787
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' has invalid predicate '${predicateType}'`, entry.loc);
2833
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has invalid predicate '${predicateType}'`, entry.loc);
2788
2834
  }
2789
2835
  if (!predicateValue) {
2790
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' must include a predicate value`, entry.loc);
2836
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' must include a predicate value`, entry.loc);
2791
2837
  }
2792
2838
  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);
2839
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has invalid ownership '${predicateValue}'`, entry.loc);
2794
2840
  }
2795
2841
  const directiveTokens = blockSymbolItems(entry).map((item) => item.value);
2796
2842
  const directives = new Map();
@@ -2798,18 +2844,18 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2798
2844
  const key = directiveTokens[i];
2799
2845
  const value = directiveTokens[i + 1];
2800
2846
  if (!value) {
2801
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
2847
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' is missing a value for '${key}'`, entry.loc);
2802
2848
  continue;
2803
2849
  }
2804
2850
  directives.set(key, value);
2805
2851
  }
2806
2852
  for (const key of directives.keys()) {
2807
2853
  if (!["claim_value"].includes(key)) {
2808
- pushError(errors, `Projection ${statement.id} ui_visibility for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
2854
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' has unknown directive '${key}'`, entry.loc);
2809
2855
  }
2810
2856
  }
2811
2857
  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);
2858
+ pushError(errors, `Projection ${statement.id} visibility_rules for '${capabilityId}' cannot declare claim_value without claim`, entry.loc);
2813
2859
  }
2814
2860
  }
2815
2861
  }
@@ -2819,7 +2865,7 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2819
2865
  return;
2820
2866
  }
2821
2867
 
2822
- const lookupsField = fieldMap.get("ui_lookups")?.[0];
2868
+ const lookupsField = fieldMap.get("field_lookups")?.[0];
2823
2869
  if (!lookupsField || lookupsField.value.type !== "block") {
2824
2870
  return;
2825
2871
  }
@@ -2831,56 +2877,56 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2831
2877
  const [keyword, screenId, fieldKeyword, fieldName, entityKeyword, entityId, labelKeyword, labelField, maybeEmptyKeyword, maybeEmptyLabel] = tokens;
2832
2878
 
2833
2879
  if (keyword !== "screen") {
2834
- pushError(errors, `Projection ${statement.id} ui_lookups entries must start with 'screen'`, entry.loc);
2880
+ pushError(errors, `Projection ${statement.id} field_lookups entries must start with 'screen'`, entry.loc);
2835
2881
  continue;
2836
2882
  }
2837
2883
 
2838
2884
  const screenEntry = screens.get(screenId);
2839
2885
  if (!screenEntry) {
2840
- pushError(errors, `Projection ${statement.id} ui_lookups references unknown screen '${screenId}'`, entry.loc);
2886
+ pushError(errors, `Projection ${statement.id} field_lookups references unknown screen '${screenId}'`, entry.loc);
2841
2887
  continue;
2842
2888
  }
2843
2889
 
2844
2890
  if (fieldKeyword !== "field") {
2845
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'field'`, entry.loc);
2891
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'field'`, entry.loc);
2846
2892
  }
2847
2893
  if (!fieldName) {
2848
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a field name`, entry.loc);
2894
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a field name`, entry.loc);
2849
2895
  }
2850
2896
 
2851
2897
  if (entityKeyword !== "entity") {
2852
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'entity'`, entry.loc);
2898
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'entity'`, entry.loc);
2853
2899
  }
2854
2900
  const entity = entityId ? registry.get(entityId) : null;
2855
2901
  if (!entity) {
2856
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' references missing entity '${entityId}'`, entry.loc);
2902
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references missing entity '${entityId}'`, entry.loc);
2857
2903
  } 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);
2904
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must reference an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
2859
2905
  }
2860
2906
 
2861
2907
  if (labelKeyword !== "label_field") {
2862
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must use 'label_field'`, entry.loc);
2908
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must use 'label_field'`, entry.loc);
2863
2909
  }
2864
2910
  if (!labelField) {
2865
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a label_field`, entry.loc);
2911
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a label_field`, entry.loc);
2866
2912
  }
2867
2913
 
2868
2914
  if (maybeEmptyKeyword && maybeEmptyKeyword !== "empty_label") {
2869
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' has unknown directive '${maybeEmptyKeyword}'`, entry.loc);
2915
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' has unknown directive '${maybeEmptyKeyword}'`, entry.loc);
2870
2916
  }
2871
2917
  if (maybeEmptyKeyword === "empty_label" && !maybeEmptyLabel) {
2872
- pushError(errors, `Projection ${statement.id} ui_lookups for '${screenId}' must include a value for 'empty_label'`, entry.loc);
2918
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' must include a value for 'empty_label'`, entry.loc);
2873
2919
  }
2874
2920
 
2875
2921
  const availableFields = resolveProjectionUiScreenFieldNames(registry, screenEntry, statement);
2876
2922
  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);
2923
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references unknown screen field '${fieldName}'`, entry.loc);
2878
2924
  }
2879
2925
 
2880
2926
  if (entity?.kind === "entity") {
2881
2927
  const entityFieldNames = new Set(statementFieldNames(entity));
2882
2928
  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);
2929
+ pushError(errors, `Projection ${statement.id} field_lookups for '${screenId}' references unknown entity field '${labelField}' on '${entity.id}'`, entry.loc);
2884
2930
  }
2885
2931
  }
2886
2932
  }
@@ -2891,38 +2937,38 @@ function validateProjectionUiRoutes(errors, statement, fieldMap, registry) {
2891
2937
  return;
2892
2938
  }
2893
2939
 
2894
- const routesField = fieldMap.get("ui_routes")?.[0];
2940
+ const routesField = fieldMap.get("screen_routes")?.[0];
2895
2941
  if (!routesField || routesField.value.type !== "block") {
2896
2942
  return;
2897
2943
  }
2898
2944
 
2899
2945
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
2900
2946
  const seenPaths = new Set();
2901
- const platform = symbolValue(getFieldValue(statement, "platform"));
2947
+ const platform = symbolValue(getFieldValue(statement, "type"));
2902
2948
 
2903
2949
  for (const entry of routesField.value.entries) {
2904
2950
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2905
2951
  const [keyword, screenId, pathKeyword, routePath] = tokens;
2906
2952
 
2907
2953
  if (keyword !== "screen") {
2908
- pushError(errors, `Projection ${statement.id} ui_routes entries must start with 'screen'`, entry.loc);
2954
+ pushError(errors, `Projection ${statement.id} screen_routes entries must start with 'screen'`, entry.loc);
2909
2955
  continue;
2910
2956
  }
2911
2957
  if (!availableScreens.has(screenId)) {
2912
- pushError(errors, `Projection ${statement.id} ui_routes references unknown screen '${screenId}'`, entry.loc);
2958
+ pushError(errors, `Projection ${statement.id} screen_routes references unknown screen '${screenId}'`, entry.loc);
2913
2959
  }
2914
2960
  if (pathKeyword !== "path") {
2915
- pushError(errors, `Projection ${statement.id} ui_routes for '${screenId}' must use 'path'`, entry.loc);
2961
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must use 'path'`, entry.loc);
2916
2962
  }
2917
2963
  if (!routePath) {
2918
- pushError(errors, `Projection ${statement.id} ui_routes for '${screenId}' must include a path`, entry.loc);
2964
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must include a path`, entry.loc);
2919
2965
  continue;
2920
2966
  }
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);
2967
+ if ((platform === "web_surface" || platform === "ios_surface") && !routePath.startsWith("/")) {
2968
+ pushError(errors, `Projection ${statement.id} screen_routes for '${screenId}' must use an absolute path`, entry.loc);
2923
2969
  }
2924
2970
  if (seenPaths.has(routePath)) {
2925
- pushError(errors, `Projection ${statement.id} ui_routes has duplicate path '${routePath}'`, entry.loc);
2971
+ pushError(errors, `Projection ${statement.id} screen_routes has duplicate path '${routePath}'`, entry.loc);
2926
2972
  }
2927
2973
  seenPaths.add(routePath);
2928
2974
  }
@@ -2938,7 +2984,7 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
2938
2984
  return;
2939
2985
  }
2940
2986
 
2941
- const platform = symbolValue(getFieldValue(statement, "platform"));
2987
+ const platform = symbolValue(getFieldValue(statement, "type"));
2942
2988
  if (platform !== expectedPlatform) {
2943
2989
  pushError(errors, `Projection ${statement.id} may only use '${surfaceBlockKey}' when platform is '${expectedPlatform}'`, surfaceField.loc);
2944
2990
  return;
@@ -3010,11 +3056,11 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
3010
3056
  }
3011
3057
 
3012
3058
  function validateProjectionUiWeb(errors, statement, fieldMap, registry) {
3013
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_web", "ui_web");
3059
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "web_hints", "web_surface");
3014
3060
  }
3015
3061
 
3016
3062
  function validateProjectionUiIos(errors, statement, fieldMap, registry) {
3017
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_ios", "ui_ios");
3063
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ios_hints", "ios_surface");
3018
3064
  }
3019
3065
 
3020
3066
  function validateProjectionGeneratorDefaults(errors, statement, fieldMap) {
@@ -3055,7 +3101,7 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3055
3101
  return;
3056
3102
  }
3057
3103
 
3058
- const dbTablesField = fieldMap.get("db_tables")?.[0];
3104
+ const dbTablesField = fieldMap.get("tables")?.[0];
3059
3105
  if (!dbTablesField || dbTablesField.value.type !== "block") {
3060
3106
  return;
3061
3107
  }
@@ -3068,22 +3114,22 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3068
3114
  const entity = registry.get(entityId);
3069
3115
 
3070
3116
  if (!entity) {
3071
- pushError(errors, `Projection ${statement.id} db_tables references missing entity '${entityId}'`, entry.loc);
3117
+ pushError(errors, `Projection ${statement.id} tables references missing entity '${entityId}'`, entry.loc);
3072
3118
  continue;
3073
3119
  }
3074
3120
  if (entity.kind !== "entity") {
3075
- pushError(errors, `Projection ${statement.id} db_tables must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3121
+ pushError(errors, `Projection ${statement.id} tables must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3076
3122
  }
3077
3123
  if (!realized.has(entityId)) {
3078
- pushError(errors, `Projection ${statement.id} db_tables entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3124
+ pushError(errors, `Projection ${statement.id} tables entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3079
3125
  }
3080
3126
  if (tableKeyword !== "table") {
3081
- pushError(errors, `Projection ${statement.id} db_tables for '${entityId}' must use 'table'`, entry.loc);
3127
+ pushError(errors, `Projection ${statement.id} tables for '${entityId}' must use 'table'`, entry.loc);
3082
3128
  }
3083
3129
  if (!tableName) {
3084
- pushError(errors, `Projection ${statement.id} db_tables for '${entityId}' must include a table name`, entry.loc);
3130
+ pushError(errors, `Projection ${statement.id} tables for '${entityId}' must include a table name`, entry.loc);
3085
3131
  } else if (seenTables.has(tableName)) {
3086
- pushError(errors, `Projection ${statement.id} db_tables has duplicate table name '${tableName}'`, entry.loc);
3132
+ pushError(errors, `Projection ${statement.id} tables has duplicate table name '${tableName}'`, entry.loc);
3087
3133
  }
3088
3134
  seenTables.add(tableName);
3089
3135
  }
@@ -3094,7 +3140,7 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3094
3140
  return;
3095
3141
  }
3096
3142
 
3097
- const dbColumnsField = fieldMap.get("db_columns")?.[0];
3143
+ const dbColumnsField = fieldMap.get("columns")?.[0];
3098
3144
  if (!dbColumnsField || dbColumnsField.value.type !== "block") {
3099
3145
  return;
3100
3146
  }
@@ -3106,27 +3152,27 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3106
3152
  const entity = registry.get(entityId);
3107
3153
 
3108
3154
  if (!entity) {
3109
- pushError(errors, `Projection ${statement.id} db_columns references missing entity '${entityId}'`, entry.loc);
3155
+ pushError(errors, `Projection ${statement.id} columns references missing entity '${entityId}'`, entry.loc);
3110
3156
  continue;
3111
3157
  }
3112
3158
  if (entity.kind !== "entity") {
3113
- pushError(errors, `Projection ${statement.id} db_columns must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3159
+ pushError(errors, `Projection ${statement.id} columns must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3114
3160
  }
3115
3161
  if (!realized.has(entityId)) {
3116
- pushError(errors, `Projection ${statement.id} db_columns entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3162
+ pushError(errors, `Projection ${statement.id} columns entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3117
3163
  }
3118
3164
  if (fieldKeyword !== "field") {
3119
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}' must use 'field'`, entry.loc);
3165
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}' must use 'field'`, entry.loc);
3120
3166
  }
3121
3167
  if (columnKeyword !== "column") {
3122
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}' must use 'column'`, entry.loc);
3168
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}' must use 'column'`, entry.loc);
3123
3169
  }
3124
3170
  const entityFieldNames = new Set(statementFieldNames(entity));
3125
3171
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3126
- pushError(errors, `Projection ${statement.id} db_columns references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3172
+ pushError(errors, `Projection ${statement.id} columns references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3127
3173
  }
3128
3174
  if (!columnName) {
3129
- pushError(errors, `Projection ${statement.id} db_columns for '${entityId}.${fieldName}' must include a column name`, entry.loc);
3175
+ pushError(errors, `Projection ${statement.id} columns for '${entityId}.${fieldName}' must include a column name`, entry.loc);
3130
3176
  }
3131
3177
  }
3132
3178
  }
@@ -3136,7 +3182,7 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3136
3182
  return;
3137
3183
  }
3138
3184
 
3139
- const dbKeysField = fieldMap.get("db_keys")?.[0];
3185
+ const dbKeysField = fieldMap.get("keys")?.[0];
3140
3186
  if (!dbKeysField || dbKeysField.value.type !== "block") {
3141
3187
  return;
3142
3188
  }
@@ -3148,27 +3194,27 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3148
3194
  const entity = registry.get(entityId);
3149
3195
 
3150
3196
  if (!entity) {
3151
- pushError(errors, `Projection ${statement.id} db_keys references missing entity '${entityId}'`, entry.loc);
3197
+ pushError(errors, `Projection ${statement.id} keys references missing entity '${entityId}'`, entry.loc);
3152
3198
  continue;
3153
3199
  }
3154
3200
  if (entity.kind !== "entity") {
3155
- pushError(errors, `Projection ${statement.id} db_keys must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3201
+ pushError(errors, `Projection ${statement.id} keys must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3156
3202
  }
3157
3203
  if (!realized.has(entityId)) {
3158
- pushError(errors, `Projection ${statement.id} db_keys entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3204
+ pushError(errors, `Projection ${statement.id} keys entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3159
3205
  }
3160
3206
  if (!["primary", "unique"].includes(keyType || "")) {
3161
- pushError(errors, `Projection ${statement.id} db_keys for '${entityId}' has invalid key type '${keyType}'`, entry.loc);
3207
+ pushError(errors, `Projection ${statement.id} keys for '${entityId}' has invalid key type '${keyType}'`, entry.loc);
3162
3208
  }
3163
3209
  const fieldList = entry.items[2];
3164
3210
  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);
3211
+ pushError(errors, `Projection ${statement.id} keys for '${entityId}' must include a non-empty field list`, entry.loc);
3166
3212
  continue;
3167
3213
  }
3168
3214
  const entityFieldNames = new Set(statementFieldNames(entity));
3169
3215
  for (const item of fieldList.items) {
3170
3216
  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);
3217
+ pushError(errors, `Projection ${statement.id} keys references unknown field '${item.value}' on ${entityId}`, item.loc);
3172
3218
  }
3173
3219
  }
3174
3220
  }
@@ -3179,7 +3225,7 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3179
3225
  return;
3180
3226
  }
3181
3227
 
3182
- const dbIndexesField = fieldMap.get("db_indexes")?.[0];
3228
+ const dbIndexesField = fieldMap.get("indexes")?.[0];
3183
3229
  if (!dbIndexesField || dbIndexesField.value.type !== "block") {
3184
3230
  return;
3185
3231
  }
@@ -3191,27 +3237,27 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3191
3237
  const entity = registry.get(entityId);
3192
3238
 
3193
3239
  if (!entity) {
3194
- pushError(errors, `Projection ${statement.id} db_indexes references missing entity '${entityId}'`, entry.loc);
3240
+ pushError(errors, `Projection ${statement.id} indexes references missing entity '${entityId}'`, entry.loc);
3195
3241
  continue;
3196
3242
  }
3197
3243
  if (entity.kind !== "entity") {
3198
- pushError(errors, `Projection ${statement.id} db_indexes must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3244
+ pushError(errors, `Projection ${statement.id} indexes must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3199
3245
  }
3200
3246
  if (!realized.has(entityId)) {
3201
- pushError(errors, `Projection ${statement.id} db_indexes entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3247
+ pushError(errors, `Projection ${statement.id} indexes entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3202
3248
  }
3203
3249
  if (!["index", "unique"].includes(indexType || "")) {
3204
- pushError(errors, `Projection ${statement.id} db_indexes for '${entityId}' has invalid index type '${indexType}'`, entry.loc);
3250
+ pushError(errors, `Projection ${statement.id} indexes for '${entityId}' has invalid index type '${indexType}'`, entry.loc);
3205
3251
  }
3206
3252
  const fieldList = entry.items[2];
3207
3253
  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);
3254
+ pushError(errors, `Projection ${statement.id} indexes for '${entityId}' must include a non-empty field list`, entry.loc);
3209
3255
  continue;
3210
3256
  }
3211
3257
  const entityFieldNames = new Set(statementFieldNames(entity));
3212
3258
  for (const item of fieldList.items) {
3213
3259
  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);
3260
+ pushError(errors, `Projection ${statement.id} indexes references unknown field '${item.value}' on ${entityId}`, item.loc);
3215
3261
  }
3216
3262
  }
3217
3263
  }
@@ -3222,7 +3268,7 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3222
3268
  return;
3223
3269
  }
3224
3270
 
3225
- const dbRelationsField = fieldMap.get("db_relations")?.[0];
3271
+ const dbRelationsField = fieldMap.get("relations")?.[0];
3226
3272
  if (!dbRelationsField || dbRelationsField.value.type !== "block") {
3227
3273
  return;
3228
3274
  }
@@ -3234,43 +3280,43 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3234
3280
  const entity = registry.get(entityId);
3235
3281
 
3236
3282
  if (!entity) {
3237
- pushError(errors, `Projection ${statement.id} db_relations references missing entity '${entityId}'`, entry.loc);
3283
+ pushError(errors, `Projection ${statement.id} relations references missing entity '${entityId}'`, entry.loc);
3238
3284
  continue;
3239
3285
  }
3240
3286
  if (entity.kind !== "entity") {
3241
- pushError(errors, `Projection ${statement.id} db_relations must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3287
+ pushError(errors, `Projection ${statement.id} relations must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3242
3288
  }
3243
3289
  if (!realized.has(entityId)) {
3244
- pushError(errors, `Projection ${statement.id} db_relations entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3290
+ pushError(errors, `Projection ${statement.id} relations entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3245
3291
  }
3246
3292
  if (relationType !== "foreign_key") {
3247
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' must use 'foreign_key'`, entry.loc);
3293
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' must use 'foreign_key'`, entry.loc);
3248
3294
  }
3249
3295
  if (referencesKeyword !== "references") {
3250
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' must use 'references'`, entry.loc);
3296
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' must use 'references'`, entry.loc);
3251
3297
  }
3252
3298
  if (onDeleteKeyword && onDeleteKeyword !== "on_delete") {
3253
- pushError(errors, `Projection ${statement.id} db_relations for '${entityId}' has unexpected token '${onDeleteKeyword}'`, entry.loc);
3299
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' has unexpected token '${onDeleteKeyword}'`, entry.loc);
3254
3300
  }
3255
3301
  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);
3302
+ pushError(errors, `Projection ${statement.id} relations for '${entityId}' has invalid on_delete '${onDeleteValue}'`, entry.loc);
3257
3303
  }
3258
3304
  const entityFieldNames = new Set(statementFieldNames(entity));
3259
3305
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3260
- pushError(errors, `Projection ${statement.id} db_relations references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3306
+ pushError(errors, `Projection ${statement.id} relations references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3261
3307
  }
3262
3308
  const [targetEntityId, targetFieldName] = (targetRef || "").split(".");
3263
3309
  const targetEntity = registry.get(targetEntityId);
3264
3310
  if (!targetEntity) {
3265
- pushError(errors, `Projection ${statement.id} db_relations references missing target entity '${targetEntityId}'`, entry.loc);
3311
+ pushError(errors, `Projection ${statement.id} relations references missing target entity '${targetEntityId}'`, entry.loc);
3266
3312
  continue;
3267
3313
  }
3268
3314
  if (targetEntity.kind !== "entity") {
3269
- pushError(errors, `Projection ${statement.id} db_relations must reference an entity target, found ${targetEntity.kind} '${targetEntity.id}'`, entry.loc);
3315
+ pushError(errors, `Projection ${statement.id} relations must reference an entity target, found ${targetEntity.kind} '${targetEntity.id}'`, entry.loc);
3270
3316
  }
3271
3317
  const targetFieldNames = new Set(statementFieldNames(targetEntity));
3272
3318
  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);
3319
+ pushError(errors, `Projection ${statement.id} relations references unknown target field '${targetFieldName}' on ${targetEntityId}`, entry.loc);
3274
3320
  }
3275
3321
  }
3276
3322
  }
@@ -3280,7 +3326,7 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3280
3326
  return;
3281
3327
  }
3282
3328
 
3283
- const dbLifecycleField = fieldMap.get("db_lifecycle")?.[0];
3329
+ const dbLifecycleField = fieldMap.get("lifecycle")?.[0];
3284
3330
  if (!dbLifecycleField || dbLifecycleField.value.type !== "block") {
3285
3331
  return;
3286
3332
  }
@@ -3292,19 +3338,19 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3292
3338
  const entity = registry.get(entityId);
3293
3339
 
3294
3340
  if (!entity) {
3295
- pushError(errors, `Projection ${statement.id} db_lifecycle references missing entity '${entityId}'`, entry.loc);
3341
+ pushError(errors, `Projection ${statement.id} lifecycle references missing entity '${entityId}'`, entry.loc);
3296
3342
  continue;
3297
3343
  }
3298
3344
  if (entity.kind !== "entity") {
3299
- pushError(errors, `Projection ${statement.id} db_lifecycle must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3345
+ pushError(errors, `Projection ${statement.id} lifecycle must target an entity, found ${entity.kind} '${entity.id}'`, entry.loc);
3300
3346
  }
3301
3347
  if (!realized.has(entityId)) {
3302
- pushError(errors, `Projection ${statement.id} db_lifecycle entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3348
+ pushError(errors, `Projection ${statement.id} lifecycle entity '${entityId}' must also appear in 'realizes'`, entry.loc);
3303
3349
  }
3304
3350
 
3305
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `db_lifecycle for '${entityId}'`);
3351
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `lifecycle for '${entityId}'`);
3306
3352
  if (!["soft_delete", "timestamps"].includes(lifecycleType || "")) {
3307
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' has invalid lifecycle '${lifecycleType}'`, entry.loc);
3353
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' has invalid lifecycle '${lifecycleType}'`, entry.loc);
3308
3354
  continue;
3309
3355
  }
3310
3356
 
@@ -3312,23 +3358,23 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3312
3358
  if (lifecycleType === "soft_delete") {
3313
3359
  for (const requiredKey of ["field", "value"]) {
3314
3360
  if (!directives.has(requiredKey)) {
3315
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' must include '${requiredKey}' for soft_delete`, entry.loc);
3361
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' must include '${requiredKey}' for soft_delete`, entry.loc);
3316
3362
  }
3317
3363
  }
3318
3364
  const fieldName = directives.get("field");
3319
3365
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3320
- pushError(errors, `Projection ${statement.id} db_lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3366
+ pushError(errors, `Projection ${statement.id} lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3321
3367
  }
3322
3368
  }
3323
3369
 
3324
3370
  if (lifecycleType === "timestamps") {
3325
3371
  for (const requiredKey of ["created_at", "updated_at"]) {
3326
3372
  if (!directives.has(requiredKey)) {
3327
- pushError(errors, `Projection ${statement.id} db_lifecycle for '${entityId}' must include '${requiredKey}' for timestamps`, entry.loc);
3373
+ pushError(errors, `Projection ${statement.id} lifecycle for '${entityId}' must include '${requiredKey}' for timestamps`, entry.loc);
3328
3374
  }
3329
3375
  const fieldName = directives.get(requiredKey);
3330
3376
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3331
- pushError(errors, `Projection ${statement.id} db_lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3377
+ pushError(errors, `Projection ${statement.id} lifecycle references unknown field '${fieldName}' on ${entityId}`, entry.loc);
3332
3378
  }
3333
3379
  }
3334
3380
  }
@@ -3341,7 +3387,11 @@ export function buildRegistry(workspaceAst, errors) {
3341
3387
  for (const file of workspaceAst.files) {
3342
3388
  for (const statement of file.statements) {
3343
3389
  if (!STATEMENT_KINDS.has(statement.kind)) {
3344
- pushError(errors, `Unknown statement kind '${statement.kind}'`, statement.loc);
3390
+ if (statement.kind === "component") {
3391
+ pushError(errors, `Statement kind 'component' was renamed to 'widget'`, statement.loc);
3392
+ } else {
3393
+ pushError(errors, `Unknown statement kind '${statement.kind}'`, statement.loc);
3394
+ }
3345
3395
  }
3346
3396
 
3347
3397
  if (!IDENTIFIER_PATTERN.test(statement.id)) {
@@ -3561,7 +3611,7 @@ export function validateWorkspace(workspaceAst) {
3561
3611
  validateProjectionDbRelations(errors, statement, fieldMap, registry);
3562
3612
  validateProjectionDbLifecycle(errors, statement, fieldMap, registry);
3563
3613
  validateProjectionGeneratorDefaults(errors, statement, fieldMap);
3564
- validateComponent(errors, statement, fieldMap, registry);
3614
+ validateWidget(errors, statement, fieldMap, registry);
3565
3615
  validateDomain(errors, statement, fieldMap, registry);
3566
3616
  validateDomainTag(errors, statement, fieldMap, registry);
3567
3617
  validatePitch(errors, statement, fieldMap, registry);