@topogram/cli 0.3.50 → 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 +501 -409
  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,31 +2371,72 @@ 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);
2397
+ }
2398
+ }
2399
+ }
2400
+
2401
+ const SHARED_UI_SEMANTIC_BLOCKS = [
2402
+ "screens",
2403
+ "collection_views",
2404
+ "screen_actions",
2405
+ "visibility_rules",
2406
+ "field_lookups",
2407
+ "app_shell",
2408
+ "navigation",
2409
+ "screen_regions"
2410
+ ];
2411
+
2412
+ function validateProjectionUiOwnership(errors, statement, fieldMap) {
2413
+ if (statement.kind !== "projection") {
2414
+ return;
2415
+ }
2416
+
2417
+ const platform = symbolValue(getFieldValue(statement, "type"));
2418
+ for (const key of SHARED_UI_SEMANTIC_BLOCKS) {
2419
+ const field = fieldMap.get(key)?.[0];
2420
+ if (!field || field.value.type !== "block") {
2421
+ continue;
2422
+ }
2423
+ if (platform !== "ui_contract") {
2424
+ pushError(
2425
+ errors,
2426
+ `Projection ${statement.id} ${key} belongs on shared UI projections; concrete UI projections may define screen_routes and platform surface hints only`,
2427
+ field.loc
2428
+ );
2351
2429
  }
2352
2430
  }
2431
+
2432
+ const routesField = fieldMap.get("screen_routes")?.[0];
2433
+ if (routesField?.value.type === "block" && !["web_surface", "ios_surface"].includes(platform || "")) {
2434
+ pushError(
2435
+ errors,
2436
+ `Projection ${statement.id} screen_routes belongs on concrete UI projections; shared UI projections own semantic screens and regions`,
2437
+ routesField.loc
2438
+ );
2439
+ }
2353
2440
  }
2354
2441
 
2355
2442
  function validateProjectionUiDesign(errors, statement, fieldMap) {
@@ -2357,13 +2444,13 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2357
2444
  return;
2358
2445
  }
2359
2446
 
2360
- const designField = fieldMap.get("ui_design")?.[0];
2447
+ const designField = fieldMap.get("design_tokens")?.[0];
2361
2448
  if (!designField || designField.value.type !== "block") {
2362
2449
  return;
2363
2450
  }
2364
2451
 
2365
- if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2366
- 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);
2367
2454
  }
2368
2455
 
2369
2456
  for (const entry of designField.value.entries) {
@@ -2372,60 +2459,60 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2372
2459
 
2373
2460
  if (key === "density") {
2374
2461
  if (!UI_DESIGN_DENSITIES.has(value || "")) {
2375
- 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);
2376
2463
  }
2377
2464
  if (tokens.length !== 2) {
2378
- 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);
2379
2466
  }
2380
2467
  continue;
2381
2468
  }
2382
2469
 
2383
2470
  if (key === "tone") {
2384
2471
  if (!UI_DESIGN_TONES.has(value || "")) {
2385
- 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);
2386
2473
  }
2387
2474
  if (tokens.length !== 2) {
2388
- 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);
2389
2476
  }
2390
2477
  continue;
2391
2478
  }
2392
2479
 
2393
2480
  if (key === "radius_scale") {
2394
2481
  if (!UI_DESIGN_RADIUS_SCALES.has(value || "")) {
2395
- 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);
2396
2483
  }
2397
2484
  if (tokens.length !== 2) {
2398
- 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);
2399
2486
  }
2400
2487
  continue;
2401
2488
  }
2402
2489
 
2403
2490
  if (key === "color_role") {
2404
2491
  if (!UI_DESIGN_COLOR_ROLES.has(value || "")) {
2405
- 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);
2406
2493
  }
2407
2494
  if (tokens.length !== 3) {
2408
- 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);
2409
2496
  }
2410
2497
  continue;
2411
2498
  }
2412
2499
 
2413
2500
  if (key === "typography_role") {
2414
2501
  if (!UI_DESIGN_TYPOGRAPHY_ROLES.has(value || "")) {
2415
- 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);
2416
2503
  }
2417
2504
  if (tokens.length !== 3) {
2418
- 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);
2419
2506
  }
2420
2507
  continue;
2421
2508
  }
2422
2509
 
2423
2510
  if (key === "action_role") {
2424
2511
  if (!UI_DESIGN_ACTION_ROLES.has(value || "")) {
2425
- 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);
2426
2513
  }
2427
2514
  if (tokens.length !== 3) {
2428
- 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);
2429
2516
  }
2430
2517
  continue;
2431
2518
  }
@@ -2433,17 +2520,17 @@ function validateProjectionUiDesign(errors, statement, fieldMap) {
2433
2520
  if (key === "accessibility") {
2434
2521
  const values = UI_DESIGN_ACCESSIBILITY_VALUES[value];
2435
2522
  if (tokens.length !== 3) {
2436
- 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);
2437
2524
  }
2438
2525
  if (!values) {
2439
- 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);
2440
2527
  } else if (!values.has(extra || "")) {
2441
- 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);
2442
2529
  }
2443
2530
  continue;
2444
2531
  }
2445
2532
 
2446
- 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);
2447
2534
  }
2448
2535
  }
2449
2536
 
@@ -2452,7 +2539,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2452
2539
  return;
2453
2540
  }
2454
2541
 
2455
- const navigationField = fieldMap.get("ui_navigation")?.[0];
2542
+ const navigationField = fieldMap.get("navigation")?.[0];
2456
2543
  if (!navigationField || navigationField.value.type !== "block") {
2457
2544
  return;
2458
2545
  }
@@ -2466,58 +2553,58 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2466
2553
 
2467
2554
  if (targetKind === "group") {
2468
2555
  if (!targetId || !IDENTIFIER_PATTERN.test(targetId)) {
2469
- 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);
2470
2557
  continue;
2471
2558
  }
2472
2559
  groups.add(targetId);
2473
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation group '${targetId}'`);
2560
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation group '${targetId}'`);
2474
2561
  for (const key of directives.keys()) {
2475
2562
  if (!["label", "placement", "icon", "order", "pattern"].includes(key)) {
2476
- 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);
2477
2564
  }
2478
2565
  }
2479
2566
  if (directives.has("placement") && !["primary", "secondary", "utility"].includes(directives.get("placement"))) {
2480
- 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);
2481
2568
  }
2482
2569
  if (directives.has("pattern") && !UI_NAVIGATION_PATTERNS.has(directives.get("pattern"))) {
2483
- 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);
2484
2571
  }
2485
2572
  continue;
2486
2573
  }
2487
2574
 
2488
2575
  if (targetKind === "screen") {
2489
2576
  if (!availableScreens.has(targetId)) {
2490
- 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);
2491
2578
  }
2492
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `ui_navigation screen '${targetId}'`);
2579
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `navigation screen '${targetId}'`);
2493
2580
  for (const key of directives.keys()) {
2494
2581
  if (!["group", "label", "order", "visible", "default", "breadcrumb", "sitemap", "placement", "pattern"].includes(key)) {
2495
- 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);
2496
2583
  }
2497
2584
  }
2498
2585
  if (directives.has("visible") && !["true", "false"].includes(directives.get("visible"))) {
2499
- 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);
2500
2587
  }
2501
2588
  if (directives.has("default") && !["true", "false"].includes(directives.get("default"))) {
2502
- 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);
2503
2590
  }
2504
2591
  if (directives.has("placement") && !["primary", "secondary", "utility"].includes(directives.get("placement"))) {
2505
- 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);
2506
2593
  }
2507
2594
  if (directives.has("sitemap") && !["include", "exclude"].includes(directives.get("sitemap"))) {
2508
- 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);
2509
2596
  }
2510
2597
  if (directives.has("pattern") && !UI_NAVIGATION_PATTERNS.has(directives.get("pattern"))) {
2511
- 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);
2512
2599
  }
2513
2600
  const breadcrumb = directives.get("breadcrumb");
2514
2601
  if (breadcrumb && breadcrumb !== "none" && !availableScreens.has(breadcrumb)) {
2515
- 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);
2516
2603
  }
2517
2604
  continue;
2518
2605
  }
2519
2606
 
2520
- 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);
2521
2608
  }
2522
2609
 
2523
2610
  for (const entry of navigationField.value.entries) {
@@ -2527,7 +2614,7 @@ function validateProjectionUiNavigation(errors, statement, fieldMap, registry) {
2527
2614
  }
2528
2615
  const directives = parseUiDirectiveMap(tokens, 2, [], statement, entry, "");
2529
2616
  if (directives.has("group") && !groups.has(directives.get("group"))) {
2530
- 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);
2531
2618
  }
2532
2619
  }
2533
2620
  }
@@ -2537,7 +2624,7 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2537
2624
  return;
2538
2625
  }
2539
2626
 
2540
- const regionField = fieldMap.get("ui_screen_regions")?.[0];
2627
+ const regionField = fieldMap.get("screen_regions")?.[0];
2541
2628
  if (!regionField || regionField.value.type !== "block") {
2542
2629
  return;
2543
2630
  }
@@ -2548,33 +2635,33 @@ function validateProjectionUiScreenRegions(errors, statement, fieldMap, registry
2548
2635
  const [keyword, screenId, regionKeyword, regionName] = tokens;
2549
2636
 
2550
2637
  if (keyword !== "screen") {
2551
- 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);
2552
2639
  continue;
2553
2640
  }
2554
2641
  if (!availableScreens.has(screenId)) {
2555
- 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);
2556
2643
  }
2557
2644
  if (regionKeyword !== "region") {
2558
- 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);
2559
2646
  }
2560
2647
  if (!UI_REGION_KINDS.has(regionName || "")) {
2561
- 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);
2562
2649
  }
2563
2650
 
2564
- 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}'`);
2565
2652
  for (const key of directives.keys()) {
2566
2653
  if (!["pattern", "placement", "title", "state", "variant"].includes(key)) {
2567
- 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);
2568
2655
  }
2569
2656
  }
2570
2657
  if (directives.has("pattern") && !UI_PATTERN_KINDS.has(directives.get("pattern"))) {
2571
- 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);
2572
2659
  }
2573
2660
  if (directives.has("placement") && !["primary", "secondary", "supporting"].includes(directives.get("placement"))) {
2574
- 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);
2575
2662
  }
2576
2663
  if (directives.has("state") && !UI_STATE_KINDS.has(directives.get("state"))) {
2577
- 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);
2578
2665
  }
2579
2666
  }
2580
2667
  }
@@ -2584,13 +2671,13 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2584
2671
  return;
2585
2672
  }
2586
2673
 
2587
- const componentsField = fieldMap.get("ui_components")?.[0];
2674
+ const componentsField = fieldMap.get("widget_bindings")?.[0];
2588
2675
  if (!componentsField || componentsField.value.type !== "block") {
2589
2676
  return;
2590
2677
  }
2591
2678
 
2592
- if (symbolValue(getFieldValue(statement, "platform")) !== "ui_shared") {
2593
- 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);
2594
2681
  }
2595
2682
 
2596
2683
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
@@ -2602,48 +2689,48 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2602
2689
  const [screenKeyword, screenId, regionKeyword, regionName, componentKeyword, componentId] = tokens;
2603
2690
 
2604
2691
  if (screenKeyword !== "screen") {
2605
- 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);
2606
2693
  continue;
2607
2694
  }
2608
2695
  if (!availableScreens.has(screenId)) {
2609
- 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);
2610
2697
  }
2611
2698
  if (regionKeyword !== "region") {
2612
- 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);
2613
2700
  }
2614
2701
  if (!UI_REGION_KINDS.has(regionName || "")) {
2615
- 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);
2616
2703
  } else if (!availableRegions.has(`${screenId}:${regionName}`)) {
2617
- 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);
2618
2705
  }
2619
- if (componentKeyword !== "component") {
2620
- 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);
2621
2708
  }
2622
2709
 
2623
- const component = registry.get(componentId);
2624
- if (!component) {
2625
- 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);
2626
2713
  continue;
2627
2714
  }
2628
- if (component.kind !== "component") {
2629
- 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);
2630
2717
  continue;
2631
2718
  }
2632
2719
 
2633
- const propNames = new Set(blockEntries(getFieldValue(component, "props"))
2720
+ const propNames = new Set(blockEntries(getFieldValue(widget, "props"))
2634
2721
  .map((propEntry) => propEntry.items[0])
2635
2722
  .filter((item) => item?.type === "symbol")
2636
2723
  .map((item) => item.value));
2637
- const eventNames = new Set(blockEntries(getFieldValue(component, "events"))
2724
+ const eventNames = new Set(blockEntries(getFieldValue(widget, "events"))
2638
2725
  .map((eventEntry) => eventEntry.items[0])
2639
2726
  .filter((item) => item?.type === "symbol")
2640
2727
  .map((item) => item.value));
2641
- const componentRegions = symbolValues(getFieldValue(component, "regions"));
2642
- const componentPatterns = symbolValues(getFieldValue(component, "patterns"));
2728
+ const componentRegions = symbolValues(getFieldValue(widget, "regions"));
2729
+ const componentPatterns = symbolValues(getFieldValue(widget, "patterns"));
2643
2730
  if (componentRegions.length > 0 && !componentRegions.includes(regionName)) {
2644
2731
  pushError(
2645
2732
  errors,
2646
- `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(", ")}]`,
2647
2734
  entry.loc
2648
2735
  );
2649
2736
  }
@@ -2651,7 +2738,7 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2651
2738
  if (regionPattern && componentPatterns.length > 0 && !componentPatterns.includes(regionPattern)) {
2652
2739
  pushError(
2653
2740
  errors,
2654
- `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(", ")}]`,
2655
2742
  entry.loc
2656
2743
  );
2657
2744
  }
@@ -2663,15 +2750,15 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2663
2750
  const fromKeyword = tokens[i + 2];
2664
2751
  const sourceId = tokens[i + 3];
2665
2752
  if (!propName || fromKeyword !== "from" || !sourceId) {
2666
- 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);
2667
2754
  break;
2668
2755
  }
2669
2756
  if (!propNames.has(propName)) {
2670
- 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);
2671
2758
  }
2672
2759
  const source = registry.get(sourceId);
2673
2760
  if (!source || !["capability", "projection", "shape", "entity"].includes(source.kind)) {
2674
- 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);
2675
2762
  }
2676
2763
  i += 4;
2677
2764
  continue;
@@ -2682,29 +2769,29 @@ function validateProjectionUiComponents(errors, statement, fieldMap, registry) {
2682
2769
  const action = tokens[i + 2];
2683
2770
  const targetId = tokens[i + 3];
2684
2771
  if (!eventName || !action || !targetId) {
2685
- 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);
2686
2773
  break;
2687
2774
  }
2688
2775
  if (!eventNames.has(eventName)) {
2689
- 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);
2690
2777
  }
2691
2778
  if (action === "navigate") {
2692
2779
  if (!availableScreens.has(targetId)) {
2693
- 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);
2694
2781
  }
2695
2782
  } else if (action === "action") {
2696
2783
  const target = registry.get(targetId);
2697
2784
  if (!target || target.kind !== "capability") {
2698
- 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);
2699
2786
  }
2700
2787
  } else {
2701
- 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);
2702
2789
  }
2703
2790
  i += 4;
2704
2791
  continue;
2705
2792
  }
2706
2793
 
2707
- 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);
2708
2795
  break;
2709
2796
  }
2710
2797
  }
@@ -2715,7 +2802,7 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2715
2802
  return;
2716
2803
  }
2717
2804
 
2718
- const visibilityField = fieldMap.get("ui_visibility")?.[0];
2805
+ const visibilityField = fieldMap.get("visibility_rules")?.[0];
2719
2806
  if (!visibilityField || visibilityField.value.type !== "block") {
2720
2807
  return;
2721
2808
  }
@@ -2726,30 +2813,30 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2726
2813
  const [keyword, capabilityId, predicateKeyword, predicateType, predicateValue] = tokens;
2727
2814
 
2728
2815
  if (keyword !== "action") {
2729
- 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);
2730
2817
  continue;
2731
2818
  }
2732
2819
 
2733
2820
  const capability = registry.get(capabilityId);
2734
2821
  if (!capability) {
2735
- 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);
2736
2823
  } else if (capability.kind !== "capability") {
2737
- 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);
2738
2825
  } else if (!realized.has(capabilityId)) {
2739
- 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);
2740
2827
  }
2741
2828
 
2742
2829
  if (predicateKeyword !== "visible_if") {
2743
- 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);
2744
2831
  }
2745
2832
  if (!["permission", "ownership", "claim"].includes(predicateType || "")) {
2746
- 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);
2747
2834
  }
2748
2835
  if (!predicateValue) {
2749
- 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);
2750
2837
  }
2751
2838
  if (predicateType === "ownership" && !["owner", "owner_or_admin", "project_member", "none"].includes(predicateValue || "")) {
2752
- 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);
2753
2840
  }
2754
2841
  const directiveTokens = blockSymbolItems(entry).map((item) => item.value);
2755
2842
  const directives = new Map();
@@ -2757,18 +2844,18 @@ function validateProjectionUiVisibility(errors, statement, fieldMap, registry) {
2757
2844
  const key = directiveTokens[i];
2758
2845
  const value = directiveTokens[i + 1];
2759
2846
  if (!value) {
2760
- 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);
2761
2848
  continue;
2762
2849
  }
2763
2850
  directives.set(key, value);
2764
2851
  }
2765
2852
  for (const key of directives.keys()) {
2766
2853
  if (!["claim_value"].includes(key)) {
2767
- 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);
2768
2855
  }
2769
2856
  }
2770
2857
  if (directives.get("claim_value") && predicateType !== "claim") {
2771
- 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);
2772
2859
  }
2773
2860
  }
2774
2861
  }
@@ -2778,7 +2865,7 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2778
2865
  return;
2779
2866
  }
2780
2867
 
2781
- const lookupsField = fieldMap.get("ui_lookups")?.[0];
2868
+ const lookupsField = fieldMap.get("field_lookups")?.[0];
2782
2869
  if (!lookupsField || lookupsField.value.type !== "block") {
2783
2870
  return;
2784
2871
  }
@@ -2790,56 +2877,56 @@ function validateProjectionUiLookups(errors, statement, fieldMap, registry) {
2790
2877
  const [keyword, screenId, fieldKeyword, fieldName, entityKeyword, entityId, labelKeyword, labelField, maybeEmptyKeyword, maybeEmptyLabel] = tokens;
2791
2878
 
2792
2879
  if (keyword !== "screen") {
2793
- 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);
2794
2881
  continue;
2795
2882
  }
2796
2883
 
2797
2884
  const screenEntry = screens.get(screenId);
2798
2885
  if (!screenEntry) {
2799
- 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);
2800
2887
  continue;
2801
2888
  }
2802
2889
 
2803
2890
  if (fieldKeyword !== "field") {
2804
- 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);
2805
2892
  }
2806
2893
  if (!fieldName) {
2807
- 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);
2808
2895
  }
2809
2896
 
2810
2897
  if (entityKeyword !== "entity") {
2811
- 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);
2812
2899
  }
2813
2900
  const entity = entityId ? registry.get(entityId) : null;
2814
2901
  if (!entity) {
2815
- 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);
2816
2903
  } else if (entity.kind !== "entity") {
2817
- 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);
2818
2905
  }
2819
2906
 
2820
2907
  if (labelKeyword !== "label_field") {
2821
- 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);
2822
2909
  }
2823
2910
  if (!labelField) {
2824
- 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);
2825
2912
  }
2826
2913
 
2827
2914
  if (maybeEmptyKeyword && maybeEmptyKeyword !== "empty_label") {
2828
- 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);
2829
2916
  }
2830
2917
  if (maybeEmptyKeyword === "empty_label" && !maybeEmptyLabel) {
2831
- 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);
2832
2919
  }
2833
2920
 
2834
2921
  const availableFields = resolveProjectionUiScreenFieldNames(registry, screenEntry, statement);
2835
2922
  if (fieldName && availableFields.size > 0 && !availableFields.has(fieldName)) {
2836
- 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);
2837
2924
  }
2838
2925
 
2839
2926
  if (entity?.kind === "entity") {
2840
2927
  const entityFieldNames = new Set(statementFieldNames(entity));
2841
2928
  if (labelField && !entityFieldNames.has(labelField)) {
2842
- 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);
2843
2930
  }
2844
2931
  }
2845
2932
  }
@@ -2850,38 +2937,38 @@ function validateProjectionUiRoutes(errors, statement, fieldMap, registry) {
2850
2937
  return;
2851
2938
  }
2852
2939
 
2853
- const routesField = fieldMap.get("ui_routes")?.[0];
2940
+ const routesField = fieldMap.get("screen_routes")?.[0];
2854
2941
  if (!routesField || routesField.value.type !== "block") {
2855
2942
  return;
2856
2943
  }
2857
2944
 
2858
2945
  const availableScreens = collectAvailableUiScreenIds(statement, fieldMap, registry);
2859
2946
  const seenPaths = new Set();
2860
- const platform = symbolValue(getFieldValue(statement, "platform"));
2947
+ const platform = symbolValue(getFieldValue(statement, "type"));
2861
2948
 
2862
2949
  for (const entry of routesField.value.entries) {
2863
2950
  const tokens = blockSymbolItems(entry).map((item) => item.value);
2864
2951
  const [keyword, screenId, pathKeyword, routePath] = tokens;
2865
2952
 
2866
2953
  if (keyword !== "screen") {
2867
- 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);
2868
2955
  continue;
2869
2956
  }
2870
2957
  if (!availableScreens.has(screenId)) {
2871
- 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);
2872
2959
  }
2873
2960
  if (pathKeyword !== "path") {
2874
- 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);
2875
2962
  }
2876
2963
  if (!routePath) {
2877
- 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);
2878
2965
  continue;
2879
2966
  }
2880
- if ((platform === "ui_web" || platform === "ui_ios") && !routePath.startsWith("/")) {
2881
- 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);
2882
2969
  }
2883
2970
  if (seenPaths.has(routePath)) {
2884
- 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);
2885
2972
  }
2886
2973
  seenPaths.add(routePath);
2887
2974
  }
@@ -2897,7 +2984,7 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
2897
2984
  return;
2898
2985
  }
2899
2986
 
2900
- const platform = symbolValue(getFieldValue(statement, "platform"));
2987
+ const platform = symbolValue(getFieldValue(statement, "type"));
2901
2988
  if (platform !== expectedPlatform) {
2902
2989
  pushError(errors, `Projection ${statement.id} may only use '${surfaceBlockKey}' when platform is '${expectedPlatform}'`, surfaceField.loc);
2903
2990
  return;
@@ -2969,11 +3056,11 @@ function validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry,
2969
3056
  }
2970
3057
 
2971
3058
  function validateProjectionUiWeb(errors, statement, fieldMap, registry) {
2972
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_web", "ui_web");
3059
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "web_hints", "web_surface");
2973
3060
  }
2974
3061
 
2975
3062
  function validateProjectionUiIos(errors, statement, fieldMap, registry) {
2976
- validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ui_ios", "ui_ios");
3063
+ validateProjectionUiSurfaceHints(errors, statement, fieldMap, registry, "ios_hints", "ios_surface");
2977
3064
  }
2978
3065
 
2979
3066
  function validateProjectionGeneratorDefaults(errors, statement, fieldMap) {
@@ -3014,7 +3101,7 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3014
3101
  return;
3015
3102
  }
3016
3103
 
3017
- const dbTablesField = fieldMap.get("db_tables")?.[0];
3104
+ const dbTablesField = fieldMap.get("tables")?.[0];
3018
3105
  if (!dbTablesField || dbTablesField.value.type !== "block") {
3019
3106
  return;
3020
3107
  }
@@ -3027,22 +3114,22 @@ function validateProjectionDbTables(errors, statement, fieldMap, registry) {
3027
3114
  const entity = registry.get(entityId);
3028
3115
 
3029
3116
  if (!entity) {
3030
- 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);
3031
3118
  continue;
3032
3119
  }
3033
3120
  if (entity.kind !== "entity") {
3034
- 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);
3035
3122
  }
3036
3123
  if (!realized.has(entityId)) {
3037
- 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);
3038
3125
  }
3039
3126
  if (tableKeyword !== "table") {
3040
- 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);
3041
3128
  }
3042
3129
  if (!tableName) {
3043
- 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);
3044
3131
  } else if (seenTables.has(tableName)) {
3045
- 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);
3046
3133
  }
3047
3134
  seenTables.add(tableName);
3048
3135
  }
@@ -3053,7 +3140,7 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3053
3140
  return;
3054
3141
  }
3055
3142
 
3056
- const dbColumnsField = fieldMap.get("db_columns")?.[0];
3143
+ const dbColumnsField = fieldMap.get("columns")?.[0];
3057
3144
  if (!dbColumnsField || dbColumnsField.value.type !== "block") {
3058
3145
  return;
3059
3146
  }
@@ -3065,27 +3152,27 @@ function validateProjectionDbColumns(errors, statement, fieldMap, registry) {
3065
3152
  const entity = registry.get(entityId);
3066
3153
 
3067
3154
  if (!entity) {
3068
- 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);
3069
3156
  continue;
3070
3157
  }
3071
3158
  if (entity.kind !== "entity") {
3072
- 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);
3073
3160
  }
3074
3161
  if (!realized.has(entityId)) {
3075
- 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);
3076
3163
  }
3077
3164
  if (fieldKeyword !== "field") {
3078
- 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);
3079
3166
  }
3080
3167
  if (columnKeyword !== "column") {
3081
- 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);
3082
3169
  }
3083
3170
  const entityFieldNames = new Set(statementFieldNames(entity));
3084
3171
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3085
- 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);
3086
3173
  }
3087
3174
  if (!columnName) {
3088
- 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);
3089
3176
  }
3090
3177
  }
3091
3178
  }
@@ -3095,7 +3182,7 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3095
3182
  return;
3096
3183
  }
3097
3184
 
3098
- const dbKeysField = fieldMap.get("db_keys")?.[0];
3185
+ const dbKeysField = fieldMap.get("keys")?.[0];
3099
3186
  if (!dbKeysField || dbKeysField.value.type !== "block") {
3100
3187
  return;
3101
3188
  }
@@ -3107,27 +3194,27 @@ function validateProjectionDbKeys(errors, statement, fieldMap, registry) {
3107
3194
  const entity = registry.get(entityId);
3108
3195
 
3109
3196
  if (!entity) {
3110
- 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);
3111
3198
  continue;
3112
3199
  }
3113
3200
  if (entity.kind !== "entity") {
3114
- 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);
3115
3202
  }
3116
3203
  if (!realized.has(entityId)) {
3117
- 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);
3118
3205
  }
3119
3206
  if (!["primary", "unique"].includes(keyType || "")) {
3120
- 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);
3121
3208
  }
3122
3209
  const fieldList = entry.items[2];
3123
3210
  if (!fieldList || fieldList.type !== "list" || fieldList.items.length === 0) {
3124
- 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);
3125
3212
  continue;
3126
3213
  }
3127
3214
  const entityFieldNames = new Set(statementFieldNames(entity));
3128
3215
  for (const item of fieldList.items) {
3129
3216
  if (item.type === "symbol" && entityFieldNames.size > 0 && !entityFieldNames.has(item.value)) {
3130
- 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);
3131
3218
  }
3132
3219
  }
3133
3220
  }
@@ -3138,7 +3225,7 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3138
3225
  return;
3139
3226
  }
3140
3227
 
3141
- const dbIndexesField = fieldMap.get("db_indexes")?.[0];
3228
+ const dbIndexesField = fieldMap.get("indexes")?.[0];
3142
3229
  if (!dbIndexesField || dbIndexesField.value.type !== "block") {
3143
3230
  return;
3144
3231
  }
@@ -3150,27 +3237,27 @@ function validateProjectionDbIndexes(errors, statement, fieldMap, registry) {
3150
3237
  const entity = registry.get(entityId);
3151
3238
 
3152
3239
  if (!entity) {
3153
- 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);
3154
3241
  continue;
3155
3242
  }
3156
3243
  if (entity.kind !== "entity") {
3157
- 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);
3158
3245
  }
3159
3246
  if (!realized.has(entityId)) {
3160
- 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);
3161
3248
  }
3162
3249
  if (!["index", "unique"].includes(indexType || "")) {
3163
- 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);
3164
3251
  }
3165
3252
  const fieldList = entry.items[2];
3166
3253
  if (!fieldList || fieldList.type !== "list" || fieldList.items.length === 0) {
3167
- 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);
3168
3255
  continue;
3169
3256
  }
3170
3257
  const entityFieldNames = new Set(statementFieldNames(entity));
3171
3258
  for (const item of fieldList.items) {
3172
3259
  if (item.type === "symbol" && entityFieldNames.size > 0 && !entityFieldNames.has(item.value)) {
3173
- 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);
3174
3261
  }
3175
3262
  }
3176
3263
  }
@@ -3181,7 +3268,7 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3181
3268
  return;
3182
3269
  }
3183
3270
 
3184
- const dbRelationsField = fieldMap.get("db_relations")?.[0];
3271
+ const dbRelationsField = fieldMap.get("relations")?.[0];
3185
3272
  if (!dbRelationsField || dbRelationsField.value.type !== "block") {
3186
3273
  return;
3187
3274
  }
@@ -3193,43 +3280,43 @@ function validateProjectionDbRelations(errors, statement, fieldMap, registry) {
3193
3280
  const entity = registry.get(entityId);
3194
3281
 
3195
3282
  if (!entity) {
3196
- 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);
3197
3284
  continue;
3198
3285
  }
3199
3286
  if (entity.kind !== "entity") {
3200
- 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);
3201
3288
  }
3202
3289
  if (!realized.has(entityId)) {
3203
- 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);
3204
3291
  }
3205
3292
  if (relationType !== "foreign_key") {
3206
- 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);
3207
3294
  }
3208
3295
  if (referencesKeyword !== "references") {
3209
- 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);
3210
3297
  }
3211
3298
  if (onDeleteKeyword && onDeleteKeyword !== "on_delete") {
3212
- 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);
3213
3300
  }
3214
3301
  if (onDeleteValue && !["cascade", "restrict", "set_null", "no_action"].includes(onDeleteValue)) {
3215
- 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);
3216
3303
  }
3217
3304
  const entityFieldNames = new Set(statementFieldNames(entity));
3218
3305
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3219
- 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);
3220
3307
  }
3221
3308
  const [targetEntityId, targetFieldName] = (targetRef || "").split(".");
3222
3309
  const targetEntity = registry.get(targetEntityId);
3223
3310
  if (!targetEntity) {
3224
- 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);
3225
3312
  continue;
3226
3313
  }
3227
3314
  if (targetEntity.kind !== "entity") {
3228
- 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);
3229
3316
  }
3230
3317
  const targetFieldNames = new Set(statementFieldNames(targetEntity));
3231
3318
  if (targetFieldName && targetFieldNames.size > 0 && !targetFieldNames.has(targetFieldName)) {
3232
- 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);
3233
3320
  }
3234
3321
  }
3235
3322
  }
@@ -3239,7 +3326,7 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3239
3326
  return;
3240
3327
  }
3241
3328
 
3242
- const dbLifecycleField = fieldMap.get("db_lifecycle")?.[0];
3329
+ const dbLifecycleField = fieldMap.get("lifecycle")?.[0];
3243
3330
  if (!dbLifecycleField || dbLifecycleField.value.type !== "block") {
3244
3331
  return;
3245
3332
  }
@@ -3251,19 +3338,19 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3251
3338
  const entity = registry.get(entityId);
3252
3339
 
3253
3340
  if (!entity) {
3254
- 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);
3255
3342
  continue;
3256
3343
  }
3257
3344
  if (entity.kind !== "entity") {
3258
- 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);
3259
3346
  }
3260
3347
  if (!realized.has(entityId)) {
3261
- 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);
3262
3349
  }
3263
3350
 
3264
- const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `db_lifecycle for '${entityId}'`);
3351
+ const directives = parseUiDirectiveMap(tokens, 2, errors, statement, entry, `lifecycle for '${entityId}'`);
3265
3352
  if (!["soft_delete", "timestamps"].includes(lifecycleType || "")) {
3266
- 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);
3267
3354
  continue;
3268
3355
  }
3269
3356
 
@@ -3271,23 +3358,23 @@ function validateProjectionDbLifecycle(errors, statement, fieldMap, registry) {
3271
3358
  if (lifecycleType === "soft_delete") {
3272
3359
  for (const requiredKey of ["field", "value"]) {
3273
3360
  if (!directives.has(requiredKey)) {
3274
- 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);
3275
3362
  }
3276
3363
  }
3277
3364
  const fieldName = directives.get("field");
3278
3365
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3279
- 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);
3280
3367
  }
3281
3368
  }
3282
3369
 
3283
3370
  if (lifecycleType === "timestamps") {
3284
3371
  for (const requiredKey of ["created_at", "updated_at"]) {
3285
3372
  if (!directives.has(requiredKey)) {
3286
- 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);
3287
3374
  }
3288
3375
  const fieldName = directives.get(requiredKey);
3289
3376
  if (fieldName && entityFieldNames.size > 0 && !entityFieldNames.has(fieldName)) {
3290
- 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);
3291
3378
  }
3292
3379
  }
3293
3380
  }
@@ -3300,7 +3387,11 @@ export function buildRegistry(workspaceAst, errors) {
3300
3387
  for (const file of workspaceAst.files) {
3301
3388
  for (const statement of file.statements) {
3302
3389
  if (!STATEMENT_KINDS.has(statement.kind)) {
3303
- 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
+ }
3304
3395
  }
3305
3396
 
3306
3397
  if (!IDENTIFIER_PATTERN.test(statement.id)) {
@@ -3499,6 +3590,7 @@ export function validateWorkspace(workspaceAst) {
3499
3590
  validateProjectionHttpDownload(errors, statement, fieldMap, registry);
3500
3591
  validateProjectionHttpAuthz(errors, statement, fieldMap, registry);
3501
3592
  validateProjectionHttpCallbacks(errors, statement, fieldMap, registry);
3593
+ validateProjectionUiOwnership(errors, statement, fieldMap);
3502
3594
  validateProjectionUiScreens(errors, statement, fieldMap, registry);
3503
3595
  validateProjectionUiCollections(errors, statement, fieldMap, registry);
3504
3596
  validateProjectionUiActions(errors, statement, fieldMap, registry);
@@ -3519,7 +3611,7 @@ export function validateWorkspace(workspaceAst) {
3519
3611
  validateProjectionDbRelations(errors, statement, fieldMap, registry);
3520
3612
  validateProjectionDbLifecycle(errors, statement, fieldMap, registry);
3521
3613
  validateProjectionGeneratorDefaults(errors, statement, fieldMap);
3522
- validateComponent(errors, statement, fieldMap, registry);
3614
+ validateWidget(errors, statement, fieldMap, registry);
3523
3615
  validateDomain(errors, statement, fieldMap, registry);
3524
3616
  validateDomainTag(errors, statement, fieldMap, registry);
3525
3617
  validatePitch(errors, statement, fieldMap, registry);