@topogram/cli 0.3.34

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 (257) hide show
  1. package/ARCHITECTURE.md +67 -0
  2. package/CHANGELOG.md +240 -0
  3. package/README.md +223 -0
  4. package/package.json +51 -0
  5. package/src/adoption/index.js +3 -0
  6. package/src/adoption/plan.js +702 -0
  7. package/src/adoption/reporting.js +464 -0
  8. package/src/adoption/review-groups.js +313 -0
  9. package/src/agent-ops/query-builders.js +5012 -0
  10. package/src/archive/archive.js +141 -0
  11. package/src/archive/compact.js +26 -0
  12. package/src/archive/jsonl.js +70 -0
  13. package/src/archive/resolver-bridge.js +82 -0
  14. package/src/archive/schema.js +87 -0
  15. package/src/archive/unarchive.js +108 -0
  16. package/src/catalog.js +752 -0
  17. package/src/cli/catalog-alias.js +166 -0
  18. package/src/cli.js +9738 -0
  19. package/src/component-behavior.js +173 -0
  20. package/src/example-implementation.js +91 -0
  21. package/src/format.js +19 -0
  22. package/src/generator/adapters.d.ts +4 -0
  23. package/src/generator/adapters.js +325 -0
  24. package/src/generator/api.d.ts +1 -0
  25. package/src/generator/api.js +1196 -0
  26. package/src/generator/check.js +355 -0
  27. package/src/generator/component-conformance.js +767 -0
  28. package/src/generator/components.js +39 -0
  29. package/src/generator/context/bundle.js +291 -0
  30. package/src/generator/context/diff.js +256 -0
  31. package/src/generator/context/digest.js +182 -0
  32. package/src/generator/context/domain-coverage.js +94 -0
  33. package/src/generator/context/domain-page.js +137 -0
  34. package/src/generator/context/index.js +42 -0
  35. package/src/generator/context/report.js +121 -0
  36. package/src/generator/context/shared.js +1397 -0
  37. package/src/generator/context/slice.js +703 -0
  38. package/src/generator/context/task-mode.js +466 -0
  39. package/src/generator/docs.js +327 -0
  40. package/src/generator/index.js +161 -0
  41. package/src/generator/native/parity-bundle.js +311 -0
  42. package/src/generator/output.js +300 -0
  43. package/src/generator/registry.js +482 -0
  44. package/src/generator/runtime/app-bundle.js +456 -0
  45. package/src/generator/runtime/bundle-shared.js +166 -0
  46. package/src/generator/runtime/compile-check.js +163 -0
  47. package/src/generator/runtime/deployment.js +287 -0
  48. package/src/generator/runtime/environment.js +635 -0
  49. package/src/generator/runtime/index.js +32 -0
  50. package/src/generator/runtime/runtime-check.js +554 -0
  51. package/src/generator/runtime/shared.js +515 -0
  52. package/src/generator/runtime/smoke.js +219 -0
  53. package/src/generator/schema.js +204 -0
  54. package/src/generator/sdlc/board.js +66 -0
  55. package/src/generator/sdlc/doc-page.js +53 -0
  56. package/src/generator/sdlc/index.js +23 -0
  57. package/src/generator/sdlc/release-notes.js +62 -0
  58. package/src/generator/sdlc/traceability-matrix.js +65 -0
  59. package/src/generator/shared.js +29 -0
  60. package/src/generator/surfaces/contracts.js +146 -0
  61. package/src/generator/surfaces/databases/contract.js +40 -0
  62. package/src/generator/surfaces/databases/index.js +84 -0
  63. package/src/generator/surfaces/databases/lifecycle-shared.d.ts +1 -0
  64. package/src/generator/surfaces/databases/lifecycle-shared.js +612 -0
  65. package/src/generator/surfaces/databases/migration-plan.js +281 -0
  66. package/src/generator/surfaces/databases/postgres/capabilities.js +14 -0
  67. package/src/generator/surfaces/databases/postgres/drizzle.js +99 -0
  68. package/src/generator/surfaces/databases/postgres/index.js +9 -0
  69. package/src/generator/surfaces/databases/postgres/lifecycle.js +16 -0
  70. package/src/generator/surfaces/databases/postgres/prisma.js +159 -0
  71. package/src/generator/surfaces/databases/postgres/sql-migration.js +102 -0
  72. package/src/generator/surfaces/databases/postgres/sql-schema.js +34 -0
  73. package/src/generator/surfaces/databases/shared.d.ts +1 -0
  74. package/src/generator/surfaces/databases/shared.js +350 -0
  75. package/src/generator/surfaces/databases/snapshot.js +96 -0
  76. package/src/generator/surfaces/databases/sqlite/capabilities.js +14 -0
  77. package/src/generator/surfaces/databases/sqlite/index.js +8 -0
  78. package/src/generator/surfaces/databases/sqlite/lifecycle.js +16 -0
  79. package/src/generator/surfaces/databases/sqlite/prisma.js +143 -0
  80. package/src/generator/surfaces/databases/sqlite/sql-migration.js +65 -0
  81. package/src/generator/surfaces/databases/sqlite/sql-schema.js +27 -0
  82. package/src/generator/surfaces/index.js +25 -0
  83. package/src/generator/surfaces/native/swiftui-app.js +38 -0
  84. package/src/generator/surfaces/native/swiftui-templates/Package.swift.txt +20 -0
  85. package/src/generator/surfaces/native/swiftui-templates/README.generated.md +26 -0
  86. package/src/generator/surfaces/native/swiftui-templates/runtime/DynamicScreens.swift +682 -0
  87. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoAPIClient.swift +156 -0
  88. package/src/generator/surfaces/native/swiftui-templates/runtime/TodoSwiftUIApp.swift +44 -0
  89. package/src/generator/surfaces/native/swiftui-templates/runtime/Visibility.swift +183 -0
  90. package/src/generator/surfaces/services/express.d.ts +1 -0
  91. package/src/generator/surfaces/services/express.js +766 -0
  92. package/src/generator/surfaces/services/hono.d.ts +1 -0
  93. package/src/generator/surfaces/services/hono.js +204 -0
  94. package/src/generator/surfaces/services/index.js +42 -0
  95. package/src/generator/surfaces/services/persistence-wiring.js +240 -0
  96. package/src/generator/surfaces/services/runtime-helpers.js +631 -0
  97. package/src/generator/surfaces/services/server-contract.js +80 -0
  98. package/src/generator/surfaces/services/stateless.d.ts +1 -0
  99. package/src/generator/surfaces/services/stateless.js +97 -0
  100. package/src/generator/surfaces/shared.js +64 -0
  101. package/src/generator/surfaces/web/api-client.js +1 -0
  102. package/src/generator/surfaces/web/forms.js +1 -0
  103. package/src/generator/surfaces/web/index.d.ts +2 -0
  104. package/src/generator/surfaces/web/index.js +53 -0
  105. package/src/generator/surfaces/web/react-components.js +248 -0
  106. package/src/generator/surfaces/web/react.js +538 -0
  107. package/src/generator/surfaces/web/routes.js +1 -0
  108. package/src/generator/surfaces/web/screens.js +1 -0
  109. package/src/generator/surfaces/web/shared.js +369 -0
  110. package/src/generator/surfaces/web/sveltekit-actions.js +28 -0
  111. package/src/generator/surfaces/web/sveltekit-components.js +234 -0
  112. package/src/generator/surfaces/web/sveltekit.js +426 -0
  113. package/src/generator/surfaces/web/ui-web-contract.js +65 -0
  114. package/src/generator/surfaces/web/vanilla.js +239 -0
  115. package/src/generator/verification.js +84 -0
  116. package/src/generator.js +1 -0
  117. package/src/import/core/context.js +52 -0
  118. package/src/import/core/contracts.js +23 -0
  119. package/src/import/core/registry.js +81 -0
  120. package/src/import/core/runner.js +646 -0
  121. package/src/import/core/shared.js +910 -0
  122. package/src/import/enrichers/auth-session.js +18 -0
  123. package/src/import/enrichers/django-rest.js +226 -0
  124. package/src/import/enrichers/doc-linking.js +20 -0
  125. package/src/import/enrichers/rails-controllers.js +246 -0
  126. package/src/import/enrichers/rails-models.js +130 -0
  127. package/src/import/enrichers/workflow-target-state.js +10 -0
  128. package/src/import/extractors/api/aspnet-core.js +304 -0
  129. package/src/import/extractors/api/django-routes.js +318 -0
  130. package/src/import/extractors/api/express.js +154 -0
  131. package/src/import/extractors/api/fastify.js +371 -0
  132. package/src/import/extractors/api/flutter-dio.js +135 -0
  133. package/src/import/extractors/api/generic-route-fallback.js +90 -0
  134. package/src/import/extractors/api/graphql-code-first.js +565 -0
  135. package/src/import/extractors/api/graphql-sdl.js +309 -0
  136. package/src/import/extractors/api/jaxrs.js +303 -0
  137. package/src/import/extractors/api/micronaut.js +213 -0
  138. package/src/import/extractors/api/next-route.js +50 -0
  139. package/src/import/extractors/api/next-server-action.js +51 -0
  140. package/src/import/extractors/api/nextauth.js +52 -0
  141. package/src/import/extractors/api/openapi-code.js +242 -0
  142. package/src/import/extractors/api/openapi.js +232 -0
  143. package/src/import/extractors/api/rails-routes.js +230 -0
  144. package/src/import/extractors/api/react-native-repository.js +128 -0
  145. package/src/import/extractors/api/retrofit.js +103 -0
  146. package/src/import/extractors/api/spring-web.js +372 -0
  147. package/src/import/extractors/api/swift-webapi.js +116 -0
  148. package/src/import/extractors/api/trpc.js +212 -0
  149. package/src/import/extractors/db/django-models.js +232 -0
  150. package/src/import/extractors/db/dotnet-models.js +93 -0
  151. package/src/import/extractors/db/drizzle.js +242 -0
  152. package/src/import/extractors/db/ef-core.js +221 -0
  153. package/src/import/extractors/db/flutter-entities.js +120 -0
  154. package/src/import/extractors/db/jpa.js +120 -0
  155. package/src/import/extractors/db/liquibase.js +180 -0
  156. package/src/import/extractors/db/mybatis-xml.js +145 -0
  157. package/src/import/extractors/db/prisma.js +185 -0
  158. package/src/import/extractors/db/rails-schema.js +175 -0
  159. package/src/import/extractors/db/react-native-entities.js +95 -0
  160. package/src/import/extractors/db/room.js +193 -0
  161. package/src/import/extractors/db/snapshot.js +112 -0
  162. package/src/import/extractors/db/sql.js +180 -0
  163. package/src/import/extractors/db/swiftdata.js +137 -0
  164. package/src/import/extractors/ui/android-compose.js +230 -0
  165. package/src/import/extractors/ui/backend-only.js +70 -0
  166. package/src/import/extractors/ui/blazor.js +227 -0
  167. package/src/import/extractors/ui/flutter-screens.js +152 -0
  168. package/src/import/extractors/ui/maui-xaml.js +135 -0
  169. package/src/import/extractors/ui/next-app-router.js +83 -0
  170. package/src/import/extractors/ui/next-pages-router.js +141 -0
  171. package/src/import/extractors/ui/razor-pages.js +181 -0
  172. package/src/import/extractors/ui/react-native-screens.js +166 -0
  173. package/src/import/extractors/ui/react-router.js +139 -0
  174. package/src/import/extractors/ui/sveltekit.js +123 -0
  175. package/src/import/extractors/ui/swiftui.js +193 -0
  176. package/src/import/extractors/ui/uikit.js +175 -0
  177. package/src/import/extractors/verification/generic.js +290 -0
  178. package/src/import/extractors/workflows/generic.js +137 -0
  179. package/src/import/index.js +7 -0
  180. package/src/import/provenance.js +158 -0
  181. package/src/new-project.js +2107 -0
  182. package/src/parser.js +439 -0
  183. package/src/policy/review-boundaries.js +165 -0
  184. package/src/project-config.js +535 -0
  185. package/src/proofs/backend-parity.js +19 -0
  186. package/src/proofs/contract-audit.js +220 -0
  187. package/src/proofs/ios-parity.js +7 -0
  188. package/src/proofs/issues-parity.js +10 -0
  189. package/src/proofs/web-parity.js +50 -0
  190. package/src/realization/api/build-api-realization.js +5 -0
  191. package/src/realization/api/index.js +1 -0
  192. package/src/realization/backend/build-backend-runtime-realization.js +82 -0
  193. package/src/realization/backend/index.d.ts +1 -0
  194. package/src/realization/backend/index.js +4 -0
  195. package/src/realization/db/build-db-realization.js +17 -0
  196. package/src/realization/db/index.js +3 -0
  197. package/src/realization/db/migration-plan.js +5 -0
  198. package/src/realization/db/snapshot.js +5 -0
  199. package/src/realization/ui/build-ui-shared-realization.js +305 -0
  200. package/src/realization/ui/build-web-realization.js +189 -0
  201. package/src/realization/ui/index.js +2 -0
  202. package/src/reconcile/docs.js +280 -0
  203. package/src/reconcile/index.js +3 -0
  204. package/src/reconcile/journeys.js +441 -0
  205. package/src/resolver/docs.js +1 -0
  206. package/src/resolver/enrich/acceptance-criterion.js +14 -0
  207. package/src/resolver/enrich/bug.js +12 -0
  208. package/src/resolver/enrich/component.js +2 -0
  209. package/src/resolver/enrich/index.js +1 -0
  210. package/src/resolver/enrich/pitch.js +18 -0
  211. package/src/resolver/enrich/requirement.js +20 -0
  212. package/src/resolver/enrich/task.js +16 -0
  213. package/src/resolver/expressions.js +1 -0
  214. package/src/resolver/index.js +2422 -0
  215. package/src/resolver/normalize.js +1 -0
  216. package/src/resolver.js +1 -0
  217. package/src/sdlc/adopt.js +65 -0
  218. package/src/sdlc/check.js +86 -0
  219. package/src/sdlc/dod/acceptance-criterion.js +22 -0
  220. package/src/sdlc/dod/bug.js +26 -0
  221. package/src/sdlc/dod/document.js +23 -0
  222. package/src/sdlc/dod/index.js +25 -0
  223. package/src/sdlc/dod/pitch.js +23 -0
  224. package/src/sdlc/dod/requirement.js +34 -0
  225. package/src/sdlc/dod/task.js +39 -0
  226. package/src/sdlc/explain.js +116 -0
  227. package/src/sdlc/history.js +80 -0
  228. package/src/sdlc/paths.js +11 -0
  229. package/src/sdlc/release.js +106 -0
  230. package/src/sdlc/scaffold.js +89 -0
  231. package/src/sdlc/status-filter.js +54 -0
  232. package/src/sdlc/transition.js +112 -0
  233. package/src/sdlc/transitions/acceptance-criterion.js +28 -0
  234. package/src/sdlc/transitions/bug.js +31 -0
  235. package/src/sdlc/transitions/document.js +29 -0
  236. package/src/sdlc/transitions/index.js +56 -0
  237. package/src/sdlc/transitions/pitch.js +34 -0
  238. package/src/sdlc/transitions/requirement.js +31 -0
  239. package/src/sdlc/transitions/task.js +34 -0
  240. package/src/template-trust.js +597 -0
  241. package/src/validator/expressions.js +1 -0
  242. package/src/validator/index.js +3424 -0
  243. package/src/validator/kinds.js +346 -0
  244. package/src/validator/per-kind/acceptance-criterion.js +91 -0
  245. package/src/validator/per-kind/bug.js +77 -0
  246. package/src/validator/per-kind/component.js +274 -0
  247. package/src/validator/per-kind/domain.js +205 -0
  248. package/src/validator/per-kind/pitch.js +101 -0
  249. package/src/validator/per-kind/requirement.js +75 -0
  250. package/src/validator/per-kind/task.js +96 -0
  251. package/src/validator/registry.js +1 -0
  252. package/src/validator/utils.js +12 -0
  253. package/src/validator.js +1 -0
  254. package/src/workflows.js +7597 -0
  255. package/src/workspace-docs.js +265 -0
  256. package/template-helpers/react.js +5 -0
  257. package/template-helpers/sveltekit.js +5 -0
@@ -0,0 +1,2422 @@
1
+ import {
2
+ blockEntries,
3
+ buildRegistry,
4
+ collectFieldMap,
5
+ getFieldValue,
6
+ stringValue,
7
+ symbolValue,
8
+ symbolValues,
9
+ validateWorkspace,
10
+ valueAsArray
11
+ } from "../validator.js";
12
+ import { enrichPitch } from "./enrich/pitch.js";
13
+ import { enrichRequirement } from "./enrich/requirement.js";
14
+ import { enrichAcceptanceCriterion } from "./enrich/acceptance-criterion.js";
15
+ import { enrichTask } from "./enrich/task.js";
16
+ import { enrichBug } from "./enrich/bug.js";
17
+ import { loadArchive, mergeArchivedIntoGraph } from "../archive/resolver-bridge.js";
18
+
19
+ function groupBy(items, keyFn) {
20
+ const grouped = {};
21
+ for (const item of items) {
22
+ const key = keyFn(item);
23
+ if (!Object.hasOwn(grouped, key)) {
24
+ grouped[key] = [];
25
+ }
26
+ grouped[key].push(item);
27
+ }
28
+ return grouped;
29
+ }
30
+
31
+ function resolveReference(registry, id) {
32
+ return registry.get(id) || null;
33
+ }
34
+
35
+ function toRef(target) {
36
+ if (!target) {
37
+ return null;
38
+ }
39
+
40
+ return {
41
+ id: target.id,
42
+ kind: target.kind
43
+ };
44
+ }
45
+
46
+ function resolveReferenceList(registry, value) {
47
+ return symbolValues(value).map((id) => ({
48
+ id,
49
+ target: toRef(resolveReference(registry, id))
50
+ }));
51
+ }
52
+
53
+ function resolveDomainTag(statement, registry) {
54
+ const domainField = statement.fields.find((field) => field.key === "domain");
55
+ if (!domainField || domainField.value.type !== "symbol") {
56
+ return null;
57
+ }
58
+ const id = domainField.value.value;
59
+ return {
60
+ id,
61
+ target: toRef(resolveReference(registry, id))
62
+ };
63
+ }
64
+
65
+ function normalizeDomainScopeList(statement, key) {
66
+ const field = statement.fields.find((f) => f.key === key);
67
+ if (!field) return [];
68
+ const items = field.value.type === "list" || field.value.type === "sequence"
69
+ ? field.value.items
70
+ : [field.value];
71
+ return items
72
+ .map((item) => (item.type === "string" || item.type === "symbol" ? item.value : null))
73
+ .filter((value) => value !== null);
74
+ }
75
+
76
+ function normalizeSequence(items) {
77
+ return items.map((item) => {
78
+ if (item.type === "symbol" || item.type === "string") {
79
+ return item.value;
80
+ }
81
+ if (item.type === "list") {
82
+ return item.items.map((nested) => nested.value);
83
+ }
84
+ return item.type;
85
+ });
86
+ }
87
+
88
+ function normalizeFieldsBlock(statement, key = "fields") {
89
+ return blockEntries(getFieldValue(statement, key)).map((entry) => {
90
+ const [name, type, requiredness, ...rest] = entry.items;
91
+ let defaultValue = null;
92
+
93
+ for (let i = 0; i < rest.length - 1; i += 1) {
94
+ if (rest[i].type === "symbol" && rest[i].value === "default") {
95
+ defaultValue = rest[i + 1]?.value ?? null;
96
+ }
97
+ }
98
+
99
+ return {
100
+ name: name?.value ?? null,
101
+ fieldType: type?.value ?? null,
102
+ requiredness: requiredness?.value ?? null,
103
+ defaultValue,
104
+ raw: normalizeSequence(entry.items),
105
+ loc: entry.loc
106
+ };
107
+ });
108
+ }
109
+
110
+ function parseDefaultLiteral(token) {
111
+ if (!token) return null;
112
+ if (token.type === "list") {
113
+ return (token.items || []).map((item) => parseDefaultLiteral(item));
114
+ }
115
+ if (token.type === "string") {
116
+ return token.value ?? null;
117
+ }
118
+ if (token.type === "symbol") {
119
+ if (token.value === "true") return true;
120
+ if (token.value === "false") return false;
121
+ if (token.value === "null") return null;
122
+ if (/^-?\d+$/.test(token.value)) return Number.parseInt(token.value, 10);
123
+ if (/^-?\d+\.\d+$/.test(token.value)) return Number.parseFloat(token.value);
124
+ return token.value;
125
+ }
126
+ return token.value ?? null;
127
+ }
128
+
129
+ function normalizeComponentProps(statement) {
130
+ return blockEntries(getFieldValue(statement, "props")).map((entry) => {
131
+ const [name, type, requiredness, ...rest] = entry.items;
132
+ let defaultValue = null;
133
+
134
+ for (let i = 0; i < rest.length - 1; i += 1) {
135
+ if (rest[i].type === "symbol" && rest[i].value === "default") {
136
+ defaultValue = parseDefaultLiteral(rest[i + 1]);
137
+ }
138
+ }
139
+
140
+ return {
141
+ name: name?.value ?? null,
142
+ fieldType: type?.value ?? null,
143
+ requiredness: requiredness?.value ?? null,
144
+ defaultValue,
145
+ raw: normalizeSequence(entry.items),
146
+ loc: entry.loc
147
+ };
148
+ });
149
+ }
150
+
151
+ function normalizeComponentEvents(statement, registry) {
152
+ return blockEntries(getFieldValue(statement, "events")).map((entry) => {
153
+ const [eventName, shapeRef] = entry.items;
154
+ const shapeId = tokenValue(shapeRef);
155
+ return {
156
+ id: tokenValue(eventName),
157
+ shape: shapeId
158
+ ? {
159
+ id: shapeId,
160
+ kind: registry.get(shapeId)?.kind || null
161
+ }
162
+ : null,
163
+ raw: normalizeSequence(entry.items),
164
+ loc: entry.loc
165
+ };
166
+ });
167
+ }
168
+
169
+ function normalizeComponentSlots(statement) {
170
+ return blockEntries(getFieldValue(statement, "slots")).map((entry) => ({
171
+ id: tokenValue(entry.items[0]),
172
+ description: tokenValue(entry.items[1]),
173
+ raw: normalizeSequence(entry.items),
174
+ loc: entry.loc
175
+ }));
176
+ }
177
+
178
+ function normalizeBehaviorValue(token) {
179
+ if (!token) {
180
+ return null;
181
+ }
182
+ if (token.type === "list") {
183
+ return token.items.map((item) => normalizeBehaviorValue(item));
184
+ }
185
+ return parseDefaultLiteral(token);
186
+ }
187
+
188
+ function normalizeComponentBehaviors(statement) {
189
+ const structured = blockEntries(getFieldValue(statement, "behaviors")).map((entry) => {
190
+ const kind = tokenValue(entry.items[0]);
191
+ const directives = {};
192
+ for (let i = 1; i < entry.items.length; i += 2) {
193
+ const key = tokenValue(entry.items[i]);
194
+ if (!key) {
195
+ continue;
196
+ }
197
+ directives[key] = normalizeBehaviorValue(entry.items[i + 1]);
198
+ }
199
+ return {
200
+ kind,
201
+ directives,
202
+ source: "structured",
203
+ raw: normalizeSequence(entry.items),
204
+ loc: entry.loc
205
+ };
206
+ });
207
+
208
+ const structuredKinds = new Set(structured.map((entry) => entry.kind));
209
+ const shorthand = symbolValues(getFieldValue(statement, "behavior"))
210
+ .filter((kind) => !structuredKinds.has(kind))
211
+ .map((kind) => ({
212
+ kind,
213
+ directives: {},
214
+ source: "shorthand",
215
+ raw: [kind],
216
+ loc: null
217
+ }));
218
+
219
+ return [...structured, ...shorthand];
220
+ }
221
+
222
+ function normalizeGenericBlock(statement, key) {
223
+ return blockEntries(getFieldValue(statement, key)).map((entry) => ({
224
+ raw: normalizeSequence(entry.items),
225
+ loc: entry.loc
226
+ }));
227
+ }
228
+
229
+ function tokenValue(token) {
230
+ return token?.value ?? null;
231
+ }
232
+
233
+ function parseLiteralToken(token) {
234
+ const value = tokenValue(token);
235
+ if (value == null) {
236
+ return null;
237
+ }
238
+
239
+ if (value === "true") {
240
+ return { kind: "boolean", value: true };
241
+ }
242
+ if (value === "false") {
243
+ return { kind: "boolean", value: false };
244
+ }
245
+ if (value === "null") {
246
+ return { kind: "null", value: null };
247
+ }
248
+ if (/^-?\d+$/.test(value)) {
249
+ return { kind: "integer", value: Number.parseInt(value, 10) };
250
+ }
251
+ if (/^-?\d+\.\d+$/.test(value)) {
252
+ return { kind: "number", value: Number.parseFloat(value) };
253
+ }
254
+
255
+ return { kind: "symbol", value };
256
+ }
257
+
258
+ function parseComparison(left, operator, right) {
259
+ if (!left || !operator || !right) {
260
+ return null;
261
+ }
262
+
263
+ return {
264
+ type: "comparison",
265
+ left: tokenValue(left),
266
+ operator,
267
+ right: parseLiteralToken(right)
268
+ };
269
+ }
270
+
271
+ function parseInvariantEntry(entry) {
272
+ const tokens = entry.items;
273
+ const values = tokens.map((item) => item.value);
274
+ const [a, b, c, d, e, f, g] = tokens;
275
+
276
+ if (tokenValue(b) === "requires") {
277
+ return {
278
+ type: "requires",
279
+ field: tokenValue(a),
280
+ predicate: parseComparison(c, tokenValue(d), e),
281
+ raw: values,
282
+ loc: entry.loc
283
+ };
284
+ }
285
+
286
+ if (tokenValue(b) === "length") {
287
+ return {
288
+ type: "length_check",
289
+ field: tokenValue(a),
290
+ operator: tokenValue(c),
291
+ value: parseLiteralToken(d),
292
+ raw: values,
293
+ loc: entry.loc
294
+ };
295
+ }
296
+
297
+ if (tokenValue(b) === "format") {
298
+ return {
299
+ type: "format_check",
300
+ field: tokenValue(a),
301
+ operator: tokenValue(c),
302
+ format: tokenValue(d),
303
+ raw: values,
304
+ loc: entry.loc
305
+ };
306
+ }
307
+
308
+ if (tokenValue(d) === "implies") {
309
+ return {
310
+ type: "implication",
311
+ when: parseComparison(a, tokenValue(b), c),
312
+ then:
313
+ tokenValue(f) === "is"
314
+ ? {
315
+ type: "state_check",
316
+ field: tokenValue(e),
317
+ operator: "is",
318
+ value: parseLiteralToken(g)
319
+ }
320
+ : parseComparison(e, tokenValue(f), g),
321
+ raw: values,
322
+ loc: entry.loc
323
+ };
324
+ }
325
+
326
+ if (["==", "!=", "<", "<=", ">", ">="].includes(tokenValue(b))) {
327
+ return {
328
+ type: "comparison",
329
+ left: tokenValue(a),
330
+ operator: tokenValue(b),
331
+ right: parseLiteralToken(c),
332
+ raw: values,
333
+ loc: entry.loc
334
+ };
335
+ }
336
+
337
+ return {
338
+ type: "unknown",
339
+ raw: values,
340
+ loc: entry.loc
341
+ };
342
+ }
343
+
344
+ function parseRuleExpression(value) {
345
+ const text = valueAsArray(value).map((item) => item.value).join(" ").trim();
346
+ const match = text.match(/^(.+?)\s*(==|!=|<=|>=|<|>)\s*(.+)$/);
347
+ if (!match) {
348
+ return {
349
+ type: "unknown",
350
+ raw: text
351
+ };
352
+ }
353
+
354
+ return {
355
+ type: "comparison",
356
+ left: match[1].trim(),
357
+ operator: match[2],
358
+ right: {
359
+ kind: "symbol",
360
+ value: match[3].trim()
361
+ },
362
+ raw: text
363
+ };
364
+ }
365
+
366
+ function parseSymbolNode(value) {
367
+ return {
368
+ type: "symbol",
369
+ id: value
370
+ };
371
+ }
372
+
373
+ function parseSymbolNodes(values) {
374
+ return values.map((value, index) => ({
375
+ ...parseSymbolNode(value),
376
+ order: index
377
+ }));
378
+ }
379
+
380
+ function parseReferenceNodes(values) {
381
+ return values.map((entry, index) => ({
382
+ type: "reference",
383
+ id: entry.id,
384
+ target: entry.target || { id: entry.id, kind: null },
385
+ order: index
386
+ }));
387
+ }
388
+
389
+ function buildComponentContract(statement) {
390
+ return {
391
+ type: "component_contract",
392
+ id: statement.id,
393
+ name: statement.name || statement.id,
394
+ description: statement.description || null,
395
+ category: statement.category || null,
396
+ version: statement.version || null,
397
+ status: statement.status || null,
398
+ props: (statement.props || []).map((prop) => ({
399
+ name: prop.name,
400
+ type: prop.fieldType,
401
+ requiredness: prop.requiredness,
402
+ defaultValue: prop.defaultValue ?? null
403
+ })),
404
+ events: (statement.events || []).map((event) => ({
405
+ id: event.id,
406
+ shape: event.shape || null
407
+ })),
408
+ slots: (statement.slots || []).map((slot) => ({
409
+ id: slot.id,
410
+ description: slot.description || null
411
+ })),
412
+ behavior: [...(statement.behavior || [])],
413
+ behaviors: (statement.behaviors || []).map((behavior) => ({
414
+ kind: behavior.kind,
415
+ directives: { ...(behavior.directives || {}) },
416
+ source: behavior.source || null
417
+ })),
418
+ patterns: [...(statement.patterns || [])],
419
+ regions: [...(statement.regions || [])],
420
+ approvals: [...(statement.approvals || [])],
421
+ lookups: parseReferenceNodes(statement.lookups || []),
422
+ dependencies: parseReferenceNodes(statement.dependencies || [])
423
+ };
424
+ }
425
+
426
+ function buildCapabilityFlow(statement) {
427
+ const effects = [];
428
+
429
+ for (const [kind, refs] of [
430
+ ["read", statement.reads],
431
+ ["create", statement.creates],
432
+ ["update", statement.updates],
433
+ ["delete", statement.deletes]
434
+ ]) {
435
+ for (const ref of refs) {
436
+ effects.push({
437
+ type: "effect",
438
+ action: kind,
439
+ target: ref.target || { id: ref.id, kind: null }
440
+ });
441
+ }
442
+ }
443
+
444
+ return {
445
+ type: "capability_flow",
446
+ actors: parseReferenceNodes(statement.actors),
447
+ roles: parseReferenceNodes(statement.roles),
448
+ effects,
449
+ contracts: {
450
+ input: statement.input.map((ref) => ref.target || { id: ref.id, kind: null }),
451
+ output: statement.output.map((ref) => ref.target || { id: ref.id, kind: null })
452
+ }
453
+ };
454
+ }
455
+
456
+ function buildRulePolicy(statement) {
457
+ return {
458
+ type: "policy",
459
+ appliesTo: statement.appliesTo.map((ref) => ref.target || { id: ref.id, kind: null }),
460
+ actors: statement.actors.map((ref) => ref.target || { id: ref.id, kind: null }),
461
+ roles: statement.roles.map((ref) => ref.target || { id: ref.id, kind: null }),
462
+ condition: statement.conditionNode,
463
+ requirement: statement.requirementNode,
464
+ severity: statement.severity,
465
+ sourceOfTruth: statement.sourceOfTruth.map((ref) => ref.target || { id: ref.id, kind: null })
466
+ };
467
+ }
468
+
469
+ function buildDecisionRecord(statement) {
470
+ return {
471
+ type: "decision_record",
472
+ context: parseSymbolNodes(statement.context),
473
+ consequences: parseSymbolNodes(statement.consequences),
474
+ status: statement.status
475
+ };
476
+ }
477
+
478
+ function buildProjectionPlan(statement) {
479
+ return {
480
+ type: "projection_plan",
481
+ platform: statement.platform,
482
+ realizes: statement.realizes.map((ref, index) => ({
483
+ order: index,
484
+ target: ref.target || { id: ref.id, kind: null }
485
+ })),
486
+ outputs: parseSymbolNodes(statement.outputs),
487
+ http: statement.http,
488
+ httpErrors: statement.httpErrors,
489
+ httpFields: statement.httpFields,
490
+ httpResponses: statement.httpResponses,
491
+ httpPreconditions: statement.httpPreconditions,
492
+ httpIdempotency: statement.httpIdempotency,
493
+ httpCache: statement.httpCache,
494
+ httpDelete: statement.httpDelete,
495
+ httpAsync: statement.httpAsync,
496
+ httpStatus: statement.httpStatus,
497
+ httpDownload: statement.httpDownload,
498
+ httpAuthz: statement.httpAuthz,
499
+ httpCallbacks: statement.httpCallbacks,
500
+ uiScreens: statement.uiScreens,
501
+ uiCollections: statement.uiCollections,
502
+ uiActions: statement.uiActions,
503
+ uiVisibility: statement.uiVisibility,
504
+ uiRoutes: statement.uiRoutes,
505
+ uiWeb: statement.uiWeb,
506
+ uiComponents: statement.uiComponents,
507
+ dbTables: statement.dbTables,
508
+ dbColumns: statement.dbColumns,
509
+ dbKeys: statement.dbKeys,
510
+ dbIndexes: statement.dbIndexes,
511
+ dbRelations: statement.dbRelations,
512
+ dbLifecycle: statement.dbLifecycle,
513
+ generatorDefaults: statement.generatorDefaults
514
+ };
515
+ }
516
+
517
+ function buildVerificationPlan(statement) {
518
+ return {
519
+ type: "verification_plan",
520
+ method: statement.method,
521
+ validates: statement.validates.map((ref, index) => ({
522
+ order: index,
523
+ target: ref.target || { id: ref.id, kind: null }
524
+ })),
525
+ scenarios: parseSymbolNodes(statement.scenarios)
526
+ };
527
+ }
528
+
529
+ function buildOperationMonitoring(statement) {
530
+ return {
531
+ type: "operation_monitoring",
532
+ observes: statement.observes.map((ref, index) => ({
533
+ order: index,
534
+ target: ref.target || { id: ref.id, kind: null }
535
+ })),
536
+ metrics: parseSymbolNodes(statement.metrics),
537
+ alerts: parseSymbolNodes(statement.alerts)
538
+ };
539
+ }
540
+
541
+ function buildOrchestrationPlan(statement) {
542
+ return {
543
+ type: "orchestration_plan",
544
+ inputs: statement.inputs.map((ref, index) => ({
545
+ order: index,
546
+ target: ref.target || { id: ref.id, kind: null }
547
+ })),
548
+ steps: parseSymbolNodes(statement.steps),
549
+ outputs: parseSymbolNodes(statement.outputs)
550
+ };
551
+ }
552
+
553
+ function buildTermVocabulary(statement) {
554
+ return {
555
+ type: "term_vocabulary",
556
+ aliases: parseSymbolNodes(statement.aliases),
557
+ excludes: parseSymbolNodes(statement.excludes)
558
+ };
559
+ }
560
+
561
+ function parseKeyBlock(statement) {
562
+ return blockEntries(getFieldValue(statement, "keys")).map((entry) => ({
563
+ type: tokenValue(entry.items[0]),
564
+ fields:
565
+ entry.items[1]?.type === "list"
566
+ ? entry.items[1].items.map((item) => item.value)
567
+ : [],
568
+ raw: normalizeSequence(entry.items),
569
+ loc: entry.loc
570
+ }));
571
+ }
572
+
573
+ function parseRelationBlock(statement, registry) {
574
+ return blockEntries(getFieldValue(statement, "relations")).map((entry) => {
575
+ const sourceField = tokenValue(entry.items[0]);
576
+ const targetRef = tokenValue(entry.items[2]);
577
+ const [entityId, fieldName] = (targetRef || "").split(".");
578
+ const targetStatement = entityId ? registry.get(entityId) : null;
579
+
580
+ return {
581
+ type: "reference",
582
+ sourceField,
583
+ target: entityId
584
+ ? {
585
+ id: entityId,
586
+ kind: targetStatement?.kind || null,
587
+ field: fieldName || null
588
+ }
589
+ : null,
590
+ raw: normalizeSequence(entry.items),
591
+ loc: entry.loc
592
+ };
593
+ });
594
+ }
595
+
596
+ function parseInvariantBlock(statement) {
597
+ return blockEntries(getFieldValue(statement, "invariants")).map((entry) => parseInvariantEntry(entry));
598
+ }
599
+
600
+ function parseRenameBlock(statement) {
601
+ return blockEntries(getFieldValue(statement, "rename")).map((entry) => ({
602
+ from: entry.items[0]?.value ?? null,
603
+ to: entry.items[1]?.value ?? null,
604
+ loc: entry.loc
605
+ }));
606
+ }
607
+
608
+ function parseOverridesBlock(statement) {
609
+ return blockEntries(getFieldValue(statement, "overrides")).map((entry) => {
610
+ const [fieldName, ...rest] = entry.items;
611
+ const override = {
612
+ field: fieldName?.value ?? null,
613
+ requiredness: null,
614
+ fieldType: null,
615
+ defaultValue: undefined,
616
+ loc: entry.loc
617
+ };
618
+
619
+ for (let i = 0; i < rest.length; i += 1) {
620
+ const token = rest[i];
621
+ if (!token || token.type !== "symbol") {
622
+ continue;
623
+ }
624
+
625
+ if (token.value === "required" || token.value === "optional") {
626
+ override.requiredness = token.value;
627
+ continue;
628
+ }
629
+
630
+ if (token.value === "type") {
631
+ override.fieldType = rest[i + 1]?.value ?? null;
632
+ i += 1;
633
+ continue;
634
+ }
635
+
636
+ if (token.value === "default") {
637
+ override.defaultValue = rest[i + 1]?.value ?? null;
638
+ i += 1;
639
+ }
640
+ }
641
+
642
+ return override;
643
+ });
644
+ }
645
+
646
+ function parseProjectionHttpBlock(statement, registry) {
647
+ return blockEntries(getFieldValue(statement, "http")).map((entry) => {
648
+ const capabilityId = tokenValue(entry.items[0]);
649
+ const directives = {};
650
+
651
+ for (let i = 1; i < entry.items.length; i += 2) {
652
+ const key = tokenValue(entry.items[i]);
653
+ const value = tokenValue(entry.items[i + 1]);
654
+ if (key && value != null) {
655
+ directives[key] = value;
656
+ }
657
+ }
658
+
659
+ return {
660
+ type: "http_realization",
661
+ capability: capabilityId
662
+ ? {
663
+ id: capabilityId,
664
+ kind: registry.get(capabilityId)?.kind || null
665
+ }
666
+ : null,
667
+ method: directives.method || null,
668
+ path: directives.path || null,
669
+ success: directives.success ? Number.parseInt(directives.success, 10) : null,
670
+ auth: directives.auth || null,
671
+ request: directives.request || null,
672
+ raw: normalizeSequence(entry.items),
673
+ loc: entry.loc
674
+ };
675
+ });
676
+ }
677
+
678
+ function parseProjectionHttpErrorsBlock(statement, registry) {
679
+ return blockEntries(getFieldValue(statement, "http_errors")).map((entry) => ({
680
+ type: "http_error_mapping",
681
+ capability: tokenValue(entry.items[0])
682
+ ? {
683
+ id: tokenValue(entry.items[0]),
684
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
685
+ }
686
+ : null,
687
+ code: tokenValue(entry.items[1]),
688
+ status: tokenValue(entry.items[2]) ? Number.parseInt(tokenValue(entry.items[2]), 10) : null,
689
+ raw: normalizeSequence(entry.items),
690
+ loc: entry.loc
691
+ }));
692
+ }
693
+
694
+ function parseProjectionHttpFieldsBlock(statement, registry) {
695
+ return blockEntries(getFieldValue(statement, "http_fields")).map((entry) => ({
696
+ type: "http_field_binding",
697
+ capability: tokenValue(entry.items[0])
698
+ ? {
699
+ id: tokenValue(entry.items[0]),
700
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
701
+ }
702
+ : null,
703
+ direction: tokenValue(entry.items[1]),
704
+ field: tokenValue(entry.items[2]),
705
+ location: tokenValue(entry.items[4]),
706
+ wireName: tokenValue(entry.items[5]) === "as" ? tokenValue(entry.items[6]) : null,
707
+ raw: normalizeSequence(entry.items),
708
+ loc: entry.loc
709
+ }));
710
+ }
711
+
712
+ function parseProjectionHttpResponsesBlock(statement, registry) {
713
+ return blockEntries(getFieldValue(statement, "http_responses")).map((entry) => {
714
+ const capabilityId = tokenValue(entry.items[0]);
715
+ const directives = parseProjectionHttpResponseDirectives(entry.items.slice(1));
716
+
717
+ return {
718
+ type: "http_response_realization",
719
+ capability: capabilityId
720
+ ? {
721
+ id: capabilityId,
722
+ kind: registry.get(capabilityId)?.kind || null
723
+ }
724
+ : null,
725
+ mode: directives.mode || null,
726
+ item: directives.item
727
+ ? {
728
+ id: directives.item,
729
+ kind: registry.get(directives.item)?.kind || null
730
+ }
731
+ : null,
732
+ cursor: directives.cursor || null,
733
+ limit: directives.limit
734
+ ? {
735
+ field: directives.limit.field,
736
+ defaultValue: directives.limit.defaultValue ? Number.parseInt(directives.limit.defaultValue, 10) : null,
737
+ maxValue: directives.limit.maxValue ? Number.parseInt(directives.limit.maxValue, 10) : null
738
+ }
739
+ : null,
740
+ sort: directives.sort
741
+ ? {
742
+ field: directives.sort.field,
743
+ direction: directives.sort.direction
744
+ }
745
+ : null,
746
+ total: directives.total
747
+ ? {
748
+ included: directives.total.included === "true"
749
+ }
750
+ : null,
751
+ raw: normalizeSequence(entry.items),
752
+ loc: entry.loc
753
+ };
754
+ });
755
+ }
756
+
757
+ function parseProjectionHttpPreconditionsBlock(statement, registry) {
758
+ return blockEntries(getFieldValue(statement, "http_preconditions")).map((entry) => {
759
+ const capabilityId = tokenValue(entry.items[0]);
760
+ const directives = {};
761
+
762
+ for (let i = 1; i < entry.items.length; i += 2) {
763
+ const key = tokenValue(entry.items[i]);
764
+ const value = tokenValue(entry.items[i + 1]);
765
+ if (key && value != null) {
766
+ directives[key] = value;
767
+ }
768
+ }
769
+
770
+ return {
771
+ type: "http_precondition",
772
+ capability: capabilityId
773
+ ? {
774
+ id: capabilityId,
775
+ kind: registry.get(capabilityId)?.kind || null
776
+ }
777
+ : null,
778
+ header: directives.header || null,
779
+ required: directives.required === "true",
780
+ error: directives.error ? Number.parseInt(directives.error, 10) : null,
781
+ source: directives.source || null,
782
+ code: directives.code || null,
783
+ raw: normalizeSequence(entry.items),
784
+ loc: entry.loc
785
+ };
786
+ });
787
+ }
788
+
789
+ function parseProjectionHttpIdempotencyBlock(statement, registry) {
790
+ return blockEntries(getFieldValue(statement, "http_idempotency")).map((entry) => {
791
+ const capabilityId = tokenValue(entry.items[0]);
792
+ const directives = {};
793
+
794
+ for (let i = 1; i < entry.items.length; i += 2) {
795
+ const key = tokenValue(entry.items[i]);
796
+ const value = tokenValue(entry.items[i + 1]);
797
+ if (key && value != null) {
798
+ directives[key] = value;
799
+ }
800
+ }
801
+
802
+ return {
803
+ type: "http_idempotency",
804
+ capability: capabilityId
805
+ ? {
806
+ id: capabilityId,
807
+ kind: registry.get(capabilityId)?.kind || null
808
+ }
809
+ : null,
810
+ header: directives.header || null,
811
+ required: directives.required === "true",
812
+ error: directives.error ? Number.parseInt(directives.error, 10) : null,
813
+ code: directives.code || null,
814
+ raw: normalizeSequence(entry.items),
815
+ loc: entry.loc
816
+ };
817
+ });
818
+ }
819
+
820
+ function parseProjectionHttpCacheBlock(statement, registry) {
821
+ return blockEntries(getFieldValue(statement, "http_cache")).map((entry) => {
822
+ const capabilityId = tokenValue(entry.items[0]);
823
+ const directives = {};
824
+
825
+ for (let i = 1; i < entry.items.length; i += 2) {
826
+ const key = tokenValue(entry.items[i]);
827
+ const value = tokenValue(entry.items[i + 1]);
828
+ if (key && value != null) {
829
+ directives[key] = value;
830
+ }
831
+ }
832
+
833
+ return {
834
+ type: "http_cache",
835
+ capability: capabilityId
836
+ ? {
837
+ id: capabilityId,
838
+ kind: registry.get(capabilityId)?.kind || null
839
+ }
840
+ : null,
841
+ responseHeader: directives.response_header || null,
842
+ requestHeader: directives.request_header || null,
843
+ required: directives.required === "true",
844
+ notModified: directives.not_modified ? Number.parseInt(directives.not_modified, 10) : null,
845
+ source: directives.source || null,
846
+ code: directives.code || null,
847
+ raw: normalizeSequence(entry.items),
848
+ loc: entry.loc
849
+ };
850
+ });
851
+ }
852
+
853
+ function parseProjectionHttpDeleteBlock(statement, registry) {
854
+ return blockEntries(getFieldValue(statement, "http_delete")).map((entry) => {
855
+ const capabilityId = tokenValue(entry.items[0]);
856
+ const directives = {};
857
+
858
+ for (let i = 1; i < entry.items.length; i += 2) {
859
+ const key = tokenValue(entry.items[i]);
860
+ const value = tokenValue(entry.items[i + 1]);
861
+ if (key && value != null) {
862
+ directives[key] = value;
863
+ }
864
+ }
865
+
866
+ return {
867
+ type: "http_delete",
868
+ capability: capabilityId
869
+ ? {
870
+ id: capabilityId,
871
+ kind: registry.get(capabilityId)?.kind || null
872
+ }
873
+ : null,
874
+ mode: directives.mode || null,
875
+ field: directives.field || null,
876
+ value: directives.value || null,
877
+ response: directives.response || null,
878
+ raw: normalizeSequence(entry.items),
879
+ loc: entry.loc
880
+ };
881
+ });
882
+ }
883
+
884
+ function parseProjectionHttpAsyncBlock(statement, registry) {
885
+ return blockEntries(getFieldValue(statement, "http_async")).map((entry) => {
886
+ const capabilityId = tokenValue(entry.items[0]);
887
+ const directives = {};
888
+
889
+ for (let i = 1; i < entry.items.length; i += 2) {
890
+ const key = tokenValue(entry.items[i]);
891
+ const value = tokenValue(entry.items[i + 1]);
892
+ if (key && value != null) {
893
+ directives[key] = value;
894
+ }
895
+ }
896
+
897
+ return {
898
+ type: "http_async",
899
+ capability: capabilityId
900
+ ? {
901
+ id: capabilityId,
902
+ kind: registry.get(capabilityId)?.kind || null
903
+ }
904
+ : null,
905
+ mode: directives.mode || null,
906
+ accepted: directives.accepted ? Number.parseInt(directives.accepted, 10) : null,
907
+ locationHeader: directives.location_header || null,
908
+ retryAfterHeader: directives.retry_after_header || null,
909
+ statusPath: directives.status_path || null,
910
+ statusCapability: directives.status_capability
911
+ ? {
912
+ id: directives.status_capability,
913
+ kind: registry.get(directives.status_capability)?.kind || null
914
+ }
915
+ : null,
916
+ job: directives.job
917
+ ? {
918
+ id: directives.job,
919
+ kind: registry.get(directives.job)?.kind || null
920
+ }
921
+ : null,
922
+ raw: normalizeSequence(entry.items),
923
+ loc: entry.loc
924
+ };
925
+ });
926
+ }
927
+
928
+ function parseProjectionHttpStatusBlock(statement, registry) {
929
+ return blockEntries(getFieldValue(statement, "http_status")).map((entry) => {
930
+ const capabilityId = tokenValue(entry.items[0]);
931
+ const directives = {};
932
+
933
+ for (let i = 1; i < entry.items.length; i += 2) {
934
+ const key = tokenValue(entry.items[i]);
935
+ const value = tokenValue(entry.items[i + 1]);
936
+ if (key && value != null) {
937
+ directives[key] = value;
938
+ }
939
+ }
940
+
941
+ return {
942
+ type: "http_status",
943
+ capability: capabilityId
944
+ ? {
945
+ id: capabilityId,
946
+ kind: registry.get(capabilityId)?.kind || null
947
+ }
948
+ : null,
949
+ asyncFor: directives.async_for
950
+ ? {
951
+ id: directives.async_for,
952
+ kind: registry.get(directives.async_for)?.kind || null
953
+ }
954
+ : null,
955
+ stateField: directives.state_field || null,
956
+ completed: directives.completed || null,
957
+ failed: directives.failed || null,
958
+ expired: directives.expired || null,
959
+ downloadCapability: directives.download_capability
960
+ ? {
961
+ id: directives.download_capability,
962
+ kind: registry.get(directives.download_capability)?.kind || null
963
+ }
964
+ : null,
965
+ downloadField: directives.download_field || null,
966
+ errorField: directives.error_field || null,
967
+ raw: normalizeSequence(entry.items),
968
+ loc: entry.loc
969
+ };
970
+ });
971
+ }
972
+
973
+ function parseProjectionHttpDownloadBlock(statement, registry) {
974
+ return blockEntries(getFieldValue(statement, "http_download")).map((entry) => {
975
+ const capabilityId = tokenValue(entry.items[0]);
976
+ const directives = {};
977
+
978
+ for (let i = 1; i < entry.items.length; i += 2) {
979
+ const key = tokenValue(entry.items[i]);
980
+ const value = tokenValue(entry.items[i + 1]);
981
+ if (key && value != null) {
982
+ directives[key] = value;
983
+ }
984
+ }
985
+
986
+ return {
987
+ type: "http_download",
988
+ capability: capabilityId
989
+ ? {
990
+ id: capabilityId,
991
+ kind: registry.get(capabilityId)?.kind || null
992
+ }
993
+ : null,
994
+ asyncFor: directives.async_for
995
+ ? {
996
+ id: directives.async_for,
997
+ kind: registry.get(directives.async_for)?.kind || null
998
+ }
999
+ : null,
1000
+ media: directives.media || null,
1001
+ filename: directives.filename || null,
1002
+ disposition: directives.disposition || null,
1003
+ raw: normalizeSequence(entry.items),
1004
+ loc: entry.loc
1005
+ };
1006
+ });
1007
+ }
1008
+
1009
+ function parseProjectionHttpAuthzBlock(statement, registry) {
1010
+ return blockEntries(getFieldValue(statement, "http_authz")).map((entry) => {
1011
+ const capabilityId = tokenValue(entry.items[0]);
1012
+ const directives = {};
1013
+
1014
+ for (let i = 1; i < entry.items.length; i += 2) {
1015
+ const key = tokenValue(entry.items[i]);
1016
+ const value = tokenValue(entry.items[i + 1]);
1017
+ if (key && value != null) {
1018
+ directives[key] = value;
1019
+ }
1020
+ }
1021
+
1022
+ return {
1023
+ type: "http_authz",
1024
+ capability: capabilityId
1025
+ ? {
1026
+ id: capabilityId,
1027
+ kind: registry.get(capabilityId)?.kind || null
1028
+ }
1029
+ : null,
1030
+ role: directives.role || null,
1031
+ permission: directives.permission || null,
1032
+ claim: directives.claim || null,
1033
+ claimValue: directives.claim_value || null,
1034
+ ownership: directives.ownership || null,
1035
+ ownershipField: directives.ownership_field || null,
1036
+ raw: normalizeSequence(entry.items),
1037
+ loc: entry.loc
1038
+ };
1039
+ });
1040
+ }
1041
+
1042
+ function parseProjectionHttpCallbacksBlock(statement, registry) {
1043
+ return blockEntries(getFieldValue(statement, "http_callbacks")).map((entry) => {
1044
+ const capabilityId = tokenValue(entry.items[0]);
1045
+ const directives = {};
1046
+
1047
+ for (let i = 1; i < entry.items.length; i += 2) {
1048
+ const key = tokenValue(entry.items[i]);
1049
+ const value = tokenValue(entry.items[i + 1]);
1050
+ if (key && value != null) {
1051
+ directives[key] = value;
1052
+ }
1053
+ }
1054
+
1055
+ return {
1056
+ type: "http_callback",
1057
+ capability: capabilityId
1058
+ ? {
1059
+ id: capabilityId,
1060
+ kind: registry.get(capabilityId)?.kind || null
1061
+ }
1062
+ : null,
1063
+ event: directives.event || null,
1064
+ targetField: directives.target_field || null,
1065
+ method: directives.method || null,
1066
+ payload: directives.payload
1067
+ ? {
1068
+ id: directives.payload,
1069
+ kind: registry.get(directives.payload)?.kind || null
1070
+ }
1071
+ : null,
1072
+ success: directives.success ? Number.parseInt(directives.success, 10) : null,
1073
+ raw: normalizeSequence(entry.items),
1074
+ loc: entry.loc
1075
+ };
1076
+ });
1077
+ }
1078
+
1079
+ function parseProjectionUiScreensBlock(statement, registry) {
1080
+ return blockEntries(getFieldValue(statement, "ui_screens")).map((entry) => {
1081
+ const directives = {};
1082
+
1083
+ for (let i = 2; i < entry.items.length; i += 2) {
1084
+ const key = tokenValue(entry.items[i]);
1085
+ const value = tokenValue(entry.items[i + 1]);
1086
+ if (key && value != null) {
1087
+ directives[key] = value;
1088
+ }
1089
+ }
1090
+
1091
+ return {
1092
+ type: "ui_screen",
1093
+ id: tokenValue(entry.items[1]),
1094
+ kind: directives.kind || null,
1095
+ title: directives.title || null,
1096
+ load: directives.load ? { id: directives.load, kind: registry.get(directives.load)?.kind || null } : null,
1097
+ itemShape: directives.item_shape ? { id: directives.item_shape, kind: registry.get(directives.item_shape)?.kind || null } : null,
1098
+ viewShape: directives.view_shape ? { id: directives.view_shape, kind: registry.get(directives.view_shape)?.kind || null } : null,
1099
+ inputShape: directives.input_shape ? { id: directives.input_shape, kind: registry.get(directives.input_shape)?.kind || null } : null,
1100
+ submit: directives.submit ? { id: directives.submit, kind: registry.get(directives.submit)?.kind || null } : null,
1101
+ detailCapability: directives.detail_capability
1102
+ ? { id: directives.detail_capability, kind: registry.get(directives.detail_capability)?.kind || null }
1103
+ : null,
1104
+ primaryAction: directives.primary_action
1105
+ ? { id: directives.primary_action, kind: registry.get(directives.primary_action)?.kind || null }
1106
+ : null,
1107
+ secondaryAction: directives.secondary_action
1108
+ ? { id: directives.secondary_action, kind: registry.get(directives.secondary_action)?.kind || null }
1109
+ : null,
1110
+ destructiveAction: directives.destructive_action
1111
+ ? { id: directives.destructive_action, kind: registry.get(directives.destructive_action)?.kind || null }
1112
+ : null,
1113
+ terminalAction: directives.terminal_action
1114
+ ? { id: directives.terminal_action, kind: registry.get(directives.terminal_action)?.kind || null }
1115
+ : null,
1116
+ successNavigate: directives.success_navigate || null,
1117
+ successRefresh: directives.success_refresh || null,
1118
+ emptyTitle: directives.empty_title || null,
1119
+ emptyBody: directives.empty_body || null,
1120
+ loadingState: directives.loading_state || null,
1121
+ errorState: directives.error_state || null,
1122
+ unauthorizedState: directives.unauthorized_state || null,
1123
+ notFoundState: directives.not_found_state || null,
1124
+ successState: directives.success_state || null,
1125
+ raw: normalizeSequence(entry.items),
1126
+ loc: entry.loc
1127
+ };
1128
+ });
1129
+ }
1130
+
1131
+ function parseProjectionUiCollectionsBlock(statement) {
1132
+ return blockEntries(getFieldValue(statement, "ui_collections")).map((entry) => {
1133
+ const operation = tokenValue(entry.items[2]);
1134
+ const primaryValue = tokenValue(entry.items[3]) || null;
1135
+ const secondaryValue = tokenValue(entry.items[4]) || null;
1136
+
1137
+ return {
1138
+ type: "ui_collection_binding",
1139
+ screenId: tokenValue(entry.items[1]),
1140
+ operation,
1141
+ field: ["filter", "search", "sort", "group"].includes(operation) ? primaryValue : null,
1142
+ direction: operation === "sort" ? secondaryValue : null,
1143
+ value: primaryValue,
1144
+ extra: secondaryValue,
1145
+ raw: normalizeSequence(entry.items),
1146
+ loc: entry.loc
1147
+ };
1148
+ });
1149
+ }
1150
+
1151
+ function parseProjectionUiActionsBlock(statement, registry) {
1152
+ return blockEntries(getFieldValue(statement, "ui_actions")).map((entry) => ({
1153
+ type: "ui_action_binding",
1154
+ screenId: tokenValue(entry.items[1]),
1155
+ capability: tokenValue(entry.items[3])
1156
+ ? {
1157
+ id: tokenValue(entry.items[3]),
1158
+ kind: registry.get(tokenValue(entry.items[3]))?.kind || null
1159
+ }
1160
+ : null,
1161
+ prominence: tokenValue(entry.items[5]) || null,
1162
+ placement: tokenValue(entry.items[6]) === "placement" ? tokenValue(entry.items[7]) || null : null,
1163
+ raw: normalizeSequence(entry.items),
1164
+ loc: entry.loc
1165
+ }));
1166
+ }
1167
+
1168
+ function parseProjectionUiVisibilityBlock(statement, registry) {
1169
+ return blockEntries(getFieldValue(statement, "ui_visibility")).map((entry) => {
1170
+ const directives = {};
1171
+ for (let i = 5; i < entry.items.length; i += 2) {
1172
+ const key = tokenValue(entry.items[i]);
1173
+ const value = tokenValue(entry.items[i + 1]);
1174
+ if (key && value != null) {
1175
+ directives[key] = value;
1176
+ }
1177
+ }
1178
+
1179
+ return {
1180
+ type: "ui_visibility_rule",
1181
+ capability: tokenValue(entry.items[1])
1182
+ ? {
1183
+ id: tokenValue(entry.items[1]),
1184
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
1185
+ }
1186
+ : null,
1187
+ predicate: tokenValue(entry.items[3]) || null,
1188
+ value: tokenValue(entry.items[4]) || null,
1189
+ claimValue: directives.claim_value || null,
1190
+ raw: normalizeSequence(entry.items),
1191
+ loc: entry.loc
1192
+ };
1193
+ });
1194
+ }
1195
+
1196
+ function parseProjectionUiLookupsBlock(statement, registry) {
1197
+ return blockEntries(getFieldValue(statement, "ui_lookups")).map((entry) => ({
1198
+ type: "ui_lookup_binding",
1199
+ screenId: tokenValue(entry.items[1]),
1200
+ field: tokenValue(entry.items[3]) || null,
1201
+ entity: tokenValue(entry.items[5])
1202
+ ? {
1203
+ id: tokenValue(entry.items[5]),
1204
+ kind: registry.get(tokenValue(entry.items[5]))?.kind || null
1205
+ }
1206
+ : null,
1207
+ labelField: tokenValue(entry.items[7]) || null,
1208
+ emptyLabel: tokenValue(entry.items[9]) || null,
1209
+ raw: normalizeSequence(entry.items),
1210
+ loc: entry.loc
1211
+ }));
1212
+ }
1213
+
1214
+ function parseProjectionUiRoutesBlock(statement) {
1215
+ return blockEntries(getFieldValue(statement, "ui_routes")).map((entry) => ({
1216
+ type: "ui_route",
1217
+ screenId: tokenValue(entry.items[1]),
1218
+ path: tokenValue(entry.items[3]) || null,
1219
+ raw: normalizeSequence(entry.items),
1220
+ loc: entry.loc
1221
+ }));
1222
+ }
1223
+
1224
+ function parseProjectionUiIosBlock(statement, registry) {
1225
+ return blockEntries(getFieldValue(statement, "ui_ios")).map((entry) => ({
1226
+ type: "ui_ios_binding",
1227
+ targetKind: tokenValue(entry.items[0]),
1228
+ targetId: tokenValue(entry.items[1]),
1229
+ capability:
1230
+ tokenValue(entry.items[0]) === "action" && tokenValue(entry.items[1])
1231
+ ? {
1232
+ id: tokenValue(entry.items[1]),
1233
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
1234
+ }
1235
+ : null,
1236
+ directive: tokenValue(entry.items[2]) || null,
1237
+ value: tokenValue(entry.items[3]) || null,
1238
+ raw: normalizeSequence(entry.items),
1239
+ loc: entry.loc
1240
+ }));
1241
+ }
1242
+
1243
+ function parseProjectionUiWebBlock(statement, registry) {
1244
+ return blockEntries(getFieldValue(statement, "ui_web")).map((entry) => ({
1245
+ type: "ui_web_binding",
1246
+ targetKind: tokenValue(entry.items[0]),
1247
+ targetId: tokenValue(entry.items[1]),
1248
+ capability:
1249
+ tokenValue(entry.items[0]) === "action" && tokenValue(entry.items[1])
1250
+ ? {
1251
+ id: tokenValue(entry.items[1]),
1252
+ kind: registry.get(tokenValue(entry.items[1]))?.kind || null
1253
+ }
1254
+ : null,
1255
+ directive: tokenValue(entry.items[2]) || null,
1256
+ value: tokenValue(entry.items[3]) || null,
1257
+ raw: normalizeSequence(entry.items),
1258
+ loc: entry.loc
1259
+ }));
1260
+ }
1261
+
1262
+ function parseProjectionUiAppShellBlock(statement) {
1263
+ return blockEntries(getFieldValue(statement, "ui_app_shell")).map((entry) => ({
1264
+ type: "ui_app_shell_binding",
1265
+ key: tokenValue(entry.items[0]) || null,
1266
+ value: tokenValue(entry.items[1]) || null,
1267
+ raw: normalizeSequence(entry.items),
1268
+ loc: entry.loc
1269
+ }));
1270
+ }
1271
+
1272
+ function parseProjectionUiNavigationBlock(statement) {
1273
+ return blockEntries(getFieldValue(statement, "ui_navigation")).map((entry) => {
1274
+ const directives = {};
1275
+ for (let i = 2; i < entry.items.length; i += 2) {
1276
+ const key = tokenValue(entry.items[i]);
1277
+ const value = tokenValue(entry.items[i + 1]);
1278
+ if (key && value != null) {
1279
+ directives[key] = value;
1280
+ }
1281
+ }
1282
+
1283
+ return {
1284
+ type: "ui_navigation_binding",
1285
+ targetKind: tokenValue(entry.items[0]) || null,
1286
+ targetId: tokenValue(entry.items[1]) || null,
1287
+ directives,
1288
+ raw: normalizeSequence(entry.items),
1289
+ loc: entry.loc
1290
+ };
1291
+ });
1292
+ }
1293
+
1294
+ function parseProjectionUiScreenRegionsBlock(statement) {
1295
+ return blockEntries(getFieldValue(statement, "ui_screen_regions")).map((entry) => {
1296
+ const directives = {};
1297
+ for (let i = 4; i < entry.items.length; i += 2) {
1298
+ const key = tokenValue(entry.items[i]);
1299
+ const value = tokenValue(entry.items[i + 1]);
1300
+ if (key && value != null) {
1301
+ directives[key] = value;
1302
+ }
1303
+ }
1304
+
1305
+ return {
1306
+ type: "ui_screen_region_binding",
1307
+ screenId: tokenValue(entry.items[1]) || null,
1308
+ region: tokenValue(entry.items[3]) || null,
1309
+ pattern: directives.pattern || null,
1310
+ placement: directives.placement || null,
1311
+ title: directives.title || null,
1312
+ state: directives.state || null,
1313
+ variant: directives.variant || null,
1314
+ raw: normalizeSequence(entry.items),
1315
+ loc: entry.loc
1316
+ };
1317
+ });
1318
+ }
1319
+
1320
+ function parseProjectionUiComponentsBlock(statement, registry) {
1321
+ return blockEntries(getFieldValue(statement, "ui_components")).map((entry) => {
1322
+ const dataBindings = [];
1323
+ const eventBindings = [];
1324
+
1325
+ for (let i = 6; i < entry.items.length;) {
1326
+ const directive = tokenValue(entry.items[i]);
1327
+ if (directive === "data") {
1328
+ const prop = tokenValue(entry.items[i + 1]);
1329
+ const sourceId = tokenValue(entry.items[i + 3]);
1330
+ dataBindings.push({
1331
+ prop,
1332
+ source: sourceId
1333
+ ? {
1334
+ id: sourceId,
1335
+ kind: registry.get(sourceId)?.kind || null
1336
+ }
1337
+ : null
1338
+ });
1339
+ i += 4;
1340
+ continue;
1341
+ }
1342
+ if (directive === "event") {
1343
+ const event = tokenValue(entry.items[i + 1]);
1344
+ const action = tokenValue(entry.items[i + 2]);
1345
+ const targetId = tokenValue(entry.items[i + 3]);
1346
+ eventBindings.push({
1347
+ event,
1348
+ action,
1349
+ target: targetId
1350
+ ? {
1351
+ id: targetId,
1352
+ kind: action === "navigate" ? "screen" : registry.get(targetId)?.kind || null
1353
+ }
1354
+ : null
1355
+ });
1356
+ i += 4;
1357
+ continue;
1358
+ }
1359
+ i += 1;
1360
+ }
1361
+
1362
+ const componentId = tokenValue(entry.items[5]);
1363
+ return {
1364
+ type: "ui_component_binding",
1365
+ screenId: tokenValue(entry.items[1]) || null,
1366
+ region: tokenValue(entry.items[3]) || null,
1367
+ component: componentId
1368
+ ? {
1369
+ id: componentId,
1370
+ kind: registry.get(componentId)?.kind || null
1371
+ }
1372
+ : null,
1373
+ dataBindings,
1374
+ eventBindings,
1375
+ raw: normalizeSequence(entry.items),
1376
+ loc: entry.loc
1377
+ };
1378
+ });
1379
+ }
1380
+
1381
+ function parseProjectionGeneratorDefaultsBlock(statement) {
1382
+ return blockEntries(getFieldValue(statement, "generator_defaults")).map((entry) => ({
1383
+ type: "generator_default",
1384
+ key: tokenValue(entry.items[0]),
1385
+ value: tokenValue(entry.items[1]) || null,
1386
+ raw: normalizeSequence(entry.items),
1387
+ loc: entry.loc
1388
+ }));
1389
+ }
1390
+
1391
+ function parseProjectionDbTablesBlock(statement, registry) {
1392
+ return blockEntries(getFieldValue(statement, "db_tables")).map((entry) => ({
1393
+ type: "db_table_mapping",
1394
+ entity: tokenValue(entry.items[0])
1395
+ ? {
1396
+ id: tokenValue(entry.items[0]),
1397
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1398
+ }
1399
+ : null,
1400
+ table: tokenValue(entry.items[2]) || null,
1401
+ raw: normalizeSequence(entry.items),
1402
+ loc: entry.loc
1403
+ }));
1404
+ }
1405
+
1406
+ function parseProjectionDbColumnsBlock(statement, registry) {
1407
+ return blockEntries(getFieldValue(statement, "db_columns")).map((entry) => ({
1408
+ type: "db_column_mapping",
1409
+ entity: tokenValue(entry.items[0])
1410
+ ? {
1411
+ id: tokenValue(entry.items[0]),
1412
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1413
+ }
1414
+ : null,
1415
+ field: tokenValue(entry.items[2]) || null,
1416
+ column: tokenValue(entry.items[4]) || null,
1417
+ raw: normalizeSequence(entry.items),
1418
+ loc: entry.loc
1419
+ }));
1420
+ }
1421
+
1422
+ function parseProjectionDbKeysBlock(statement, registry) {
1423
+ return blockEntries(getFieldValue(statement, "db_keys")).map((entry) => ({
1424
+ type: "db_key",
1425
+ entity: tokenValue(entry.items[0])
1426
+ ? {
1427
+ id: tokenValue(entry.items[0]),
1428
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1429
+ }
1430
+ : null,
1431
+ keyType: tokenValue(entry.items[1]) || null,
1432
+ fields: entry.items[2]?.type === "list" ? entry.items[2].items.map((item) => item.value) : [],
1433
+ raw: normalizeSequence(entry.items),
1434
+ loc: entry.loc
1435
+ }));
1436
+ }
1437
+
1438
+ function parseProjectionDbIndexesBlock(statement, registry) {
1439
+ return blockEntries(getFieldValue(statement, "db_indexes")).map((entry) => ({
1440
+ type: "db_index",
1441
+ entity: tokenValue(entry.items[0])
1442
+ ? {
1443
+ id: tokenValue(entry.items[0]),
1444
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1445
+ }
1446
+ : null,
1447
+ indexType: tokenValue(entry.items[1]) || null,
1448
+ fields: entry.items[2]?.type === "list" ? entry.items[2].items.map((item) => item.value) : [],
1449
+ raw: normalizeSequence(entry.items),
1450
+ loc: entry.loc
1451
+ }));
1452
+ }
1453
+
1454
+ function parseProjectionDbRelationsBlock(statement, registry) {
1455
+ return blockEntries(getFieldValue(statement, "db_relations")).map((entry) => {
1456
+ const targetRef = tokenValue(entry.items[4]) || null;
1457
+ const [targetEntityId, targetField] = (targetRef || "").split(".");
1458
+ return {
1459
+ type: "db_relation",
1460
+ entity: tokenValue(entry.items[0])
1461
+ ? {
1462
+ id: tokenValue(entry.items[0]),
1463
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1464
+ }
1465
+ : null,
1466
+ relationType: tokenValue(entry.items[1]) || null,
1467
+ field: tokenValue(entry.items[2]) || null,
1468
+ target: targetEntityId
1469
+ ? {
1470
+ id: targetEntityId,
1471
+ kind: registry.get(targetEntityId)?.kind || null,
1472
+ field: targetField || null
1473
+ }
1474
+ : null,
1475
+ onDelete: tokenValue(entry.items[6]) || null,
1476
+ raw: normalizeSequence(entry.items),
1477
+ loc: entry.loc
1478
+ };
1479
+ });
1480
+ }
1481
+
1482
+ function parseProjectionDbLifecycleBlock(statement, registry) {
1483
+ return blockEntries(getFieldValue(statement, "db_lifecycle")).map((entry) => {
1484
+ const directives = {};
1485
+ for (let i = 2; i < entry.items.length; i += 2) {
1486
+ const key = tokenValue(entry.items[i]);
1487
+ const value = tokenValue(entry.items[i + 1]);
1488
+ if (key && value != null) {
1489
+ directives[key] = value;
1490
+ }
1491
+ }
1492
+
1493
+ return {
1494
+ type: "db_lifecycle",
1495
+ entity: tokenValue(entry.items[0])
1496
+ ? {
1497
+ id: tokenValue(entry.items[0]),
1498
+ kind: registry.get(tokenValue(entry.items[0]))?.kind || null
1499
+ }
1500
+ : null,
1501
+ lifecycleType: tokenValue(entry.items[1]) || null,
1502
+ field: directives.field || null,
1503
+ value: directives.value || null,
1504
+ createdAt: directives.created_at || null,
1505
+ updatedAt: directives.updated_at || null,
1506
+ raw: normalizeSequence(entry.items),
1507
+ loc: entry.loc
1508
+ };
1509
+ });
1510
+ }
1511
+
1512
+ function parseProjectionHttpResponseDirectives(tokens) {
1513
+ const directives = {
1514
+ mode: null,
1515
+ item: null,
1516
+ cursor: null,
1517
+ limit: null,
1518
+ sort: null,
1519
+ total: null
1520
+ };
1521
+
1522
+ for (let i = 0; i < tokens.length; i += 1) {
1523
+ const token = tokenValue(tokens[i]);
1524
+ if (token === "mode") {
1525
+ directives.mode = tokenValue(tokens[i + 1]);
1526
+ i += 1;
1527
+ continue;
1528
+ }
1529
+ if (token === "item") {
1530
+ directives.item = tokenValue(tokens[i + 1]);
1531
+ i += 1;
1532
+ continue;
1533
+ }
1534
+ if (token === "cursor") {
1535
+ directives.cursor = {
1536
+ requestAfter: tokenValue(tokens[i + 1]) === "request_after" ? tokenValue(tokens[i + 2]) : null,
1537
+ responseNext: tokenValue(tokens[i + 3]) === "response_next" ? tokenValue(tokens[i + 4]) : null,
1538
+ responsePrev: tokenValue(tokens[i + 5]) === "response_prev" ? tokenValue(tokens[i + 6]) : null
1539
+ };
1540
+ i += directives.cursor.responsePrev ? 6 : 4;
1541
+ continue;
1542
+ }
1543
+ if (token === "limit") {
1544
+ directives.limit = {
1545
+ field: tokenValue(tokens[i + 1]) === "field" ? tokenValue(tokens[i + 2]) : null,
1546
+ defaultValue: tokenValue(tokens[i + 3]) === "default" ? tokenValue(tokens[i + 4]) : null,
1547
+ maxValue: tokenValue(tokens[i + 5]) === "max" ? tokenValue(tokens[i + 6]) : null
1548
+ };
1549
+ i += 6;
1550
+ continue;
1551
+ }
1552
+ if (token === "sort") {
1553
+ directives.sort = {
1554
+ field: tokenValue(tokens[i + 1]) === "by" ? tokenValue(tokens[i + 2]) : null,
1555
+ direction: tokenValue(tokens[i + 3]) === "direction" ? tokenValue(tokens[i + 4]) : null
1556
+ };
1557
+ i += 4;
1558
+ continue;
1559
+ }
1560
+ if (token === "total") {
1561
+ directives.total = {
1562
+ included: tokenValue(tokens[i + 1]) === "included" ? tokenValue(tokens[i + 2]) : null
1563
+ };
1564
+ i += 2;
1565
+ }
1566
+ }
1567
+
1568
+ return directives;
1569
+ }
1570
+
1571
+ function cloneField(field) {
1572
+ return {
1573
+ ...field,
1574
+ sourceName: field.sourceName ?? field.name
1575
+ };
1576
+ }
1577
+
1578
+ function fieldRef(field) {
1579
+ return {
1580
+ name: field.name,
1581
+ sourceName: field.sourceName ?? field.name,
1582
+ fieldType: field.fieldType,
1583
+ requiredness: field.requiredness,
1584
+ defaultValue: field.defaultValue ?? null
1585
+ };
1586
+ }
1587
+
1588
+ function applyRename(fields, renameRules) {
1589
+ const renameBySource = new Map(renameRules.map((rule) => [rule.from, rule.to]));
1590
+ return fields.map((field) => {
1591
+ const renamed = cloneField(field);
1592
+ const nextName = renameBySource.get(field.name);
1593
+ if (nextName) {
1594
+ renamed.name = nextName;
1595
+ }
1596
+ return renamed;
1597
+ });
1598
+ }
1599
+
1600
+ function applyOverrides(fields, overrideRules) {
1601
+ const byCurrentName = new Map(fields.map((field) => [field.name, field]));
1602
+ const bySourceName = new Map(fields.map((field) => [field.sourceName, field]));
1603
+
1604
+ for (const rule of overrideRules) {
1605
+ const target = byCurrentName.get(rule.field) || bySourceName.get(rule.field);
1606
+ if (!target) {
1607
+ continue;
1608
+ }
1609
+
1610
+ if (rule.requiredness) {
1611
+ target.requiredness = rule.requiredness;
1612
+ }
1613
+ if (rule.fieldType) {
1614
+ target.fieldType = rule.fieldType;
1615
+ }
1616
+ if (rule.defaultValue !== undefined) {
1617
+ target.defaultValue = rule.defaultValue;
1618
+ }
1619
+ }
1620
+
1621
+ return fields;
1622
+ }
1623
+
1624
+ function buildShapeSelection(shape, byId) {
1625
+ const explicitFields = shape.fields.length > 0;
1626
+ const selectedFields =
1627
+ explicitFields
1628
+ ? shape.fields.map((field) => ({
1629
+ ...cloneField(field),
1630
+ sourceName: field.name
1631
+ }))
1632
+ : deriveShapeFields(shape, byId).map((field) => ({
1633
+ ...cloneField(field),
1634
+ sourceName: field.name
1635
+ }));
1636
+
1637
+ return {
1638
+ type: "shape_selection",
1639
+ mode: explicitFields ? "explicit_fields" : shape.from?.id ? "derived_from_entity" : "empty",
1640
+ source: shape.from?.target || null,
1641
+ include: parseSymbolNodes(shape.include),
1642
+ exclude: parseSymbolNodes(shape.exclude),
1643
+ selectedFields: selectedFields.map((field) => fieldRef(field))
1644
+ };
1645
+ }
1646
+
1647
+ function buildRenameTransforms(renameRules) {
1648
+ return renameRules.map((rule, index) => ({
1649
+ type: "rename_field",
1650
+ order: index,
1651
+ from: rule.from,
1652
+ to: rule.to,
1653
+ loc: rule.loc
1654
+ }));
1655
+ }
1656
+
1657
+ function buildOverrideTransforms(overrideRules) {
1658
+ return overrideRules.map((rule, index) => ({
1659
+ type: "override_field",
1660
+ order: index,
1661
+ field: rule.field,
1662
+ changes: {
1663
+ requiredness: rule.requiredness,
1664
+ fieldType: rule.fieldType,
1665
+ defaultValue: rule.defaultValue ?? null
1666
+ },
1667
+ loc: rule.loc
1668
+ }));
1669
+ }
1670
+
1671
+ function buildShapeTransformGraph(shape, byId) {
1672
+ const selection = buildShapeSelection(shape, byId);
1673
+ return {
1674
+ type: "shape_transform_graph",
1675
+ selection,
1676
+ transforms: [
1677
+ ...buildRenameTransforms(shape.rename),
1678
+ ...buildOverrideTransforms(shape.overrides)
1679
+ ],
1680
+ resultFields: (shape.projectedFields || []).map((field) => fieldRef(field))
1681
+ };
1682
+ }
1683
+
1684
+ function projectShapeFields(shape, byId) {
1685
+ const baseFields =
1686
+ shape.fields.length > 0
1687
+ ? shape.fields.map((field) => ({
1688
+ ...cloneField(field),
1689
+ sourceName: field.name
1690
+ }))
1691
+ : deriveShapeFields(shape, byId).map((field) => ({
1692
+ ...cloneField(field),
1693
+ sourceName: field.name
1694
+ }));
1695
+
1696
+ const renamedFields = applyRename(baseFields, shape.rename);
1697
+ const overriddenFields = applyOverrides(renamedFields, shape.overrides);
1698
+
1699
+ return overriddenFields.map((field) => ({
1700
+ name: field.name,
1701
+ sourceName: field.sourceName,
1702
+ fieldType: field.fieldType,
1703
+ requiredness: field.requiredness,
1704
+ defaultValue: field.defaultValue ?? null,
1705
+ raw: field.raw,
1706
+ loc: field.loc
1707
+ }));
1708
+ }
1709
+
1710
+ function deriveShapeFields(shape, byId) {
1711
+ if (shape.fields.length > 0) {
1712
+ return shape.fields;
1713
+ }
1714
+
1715
+ if (!shape.from?.target?.id) {
1716
+ return [];
1717
+ }
1718
+
1719
+ const entity = byId.get(shape.from.target.id);
1720
+ if (!entity || entity.kind !== "entity") {
1721
+ return [];
1722
+ }
1723
+
1724
+ const sourceFields = new Map(entity.fields.map((field) => [field.name, field]));
1725
+ const includes = shape.include.length > 0 ? shape.include : [...sourceFields.keys()];
1726
+ const excludes = new Set(shape.exclude);
1727
+
1728
+ return includes
1729
+ .filter((fieldName) => !excludes.has(fieldName))
1730
+ .map((fieldName) => sourceFields.get(fieldName))
1731
+ .filter(Boolean)
1732
+ .map((field) => cloneField(field));
1733
+ }
1734
+
1735
+ export function normalizeStatement(statement, registry) {
1736
+ const fieldMap = collectFieldMap(statement);
1737
+ const base = {
1738
+ kind: statement.kind,
1739
+ id: statement.id,
1740
+ name: stringValue(getFieldValue(statement, "name")),
1741
+ description: stringValue(getFieldValue(statement, "description")),
1742
+ status: symbolValue(getFieldValue(statement, "status")),
1743
+ from: statement.from
1744
+ ? {
1745
+ id: statement.from.value,
1746
+ target: toRef(resolveReference(registry, statement.from.value))
1747
+ }
1748
+ : null,
1749
+ loc: statement.loc
1750
+ };
1751
+
1752
+ switch (statement.kind) {
1753
+ case "enum":
1754
+ return {
1755
+ ...base,
1756
+ values: symbolValues(getFieldValue(statement, "values"))
1757
+ };
1758
+ case "actor":
1759
+ case "role":
1760
+ return base;
1761
+ case "entity":
1762
+ return {
1763
+ ...base,
1764
+ usesTerms: resolveReferenceList(registry, getFieldValue(statement, "uses_terms")),
1765
+ fields: normalizeFieldsBlock(statement),
1766
+ keys: parseKeyBlock(statement),
1767
+ relations: parseRelationBlock(statement, registry),
1768
+ invariants: parseInvariantBlock(statement),
1769
+ resolvedDomain: resolveDomainTag(statement, registry)
1770
+ };
1771
+ case "shape":
1772
+ return {
1773
+ ...base,
1774
+ include: symbolValues(getFieldValue(statement, "include")),
1775
+ exclude: symbolValues(getFieldValue(statement, "exclude")),
1776
+ derivedFrom: resolveReferenceList(registry, getFieldValue(statement, "derived_from")),
1777
+ fields: normalizeFieldsBlock(statement),
1778
+ rename: parseRenameBlock(statement),
1779
+ overrides: parseOverridesBlock(statement)
1780
+ };
1781
+ case "capability":
1782
+ return {
1783
+ ...base,
1784
+ actors: resolveReferenceList(registry, getFieldValue(statement, "actors")),
1785
+ roles: resolveReferenceList(registry, getFieldValue(statement, "roles")),
1786
+ reads: resolveReferenceList(registry, getFieldValue(statement, "reads")),
1787
+ creates: resolveReferenceList(registry, getFieldValue(statement, "creates")),
1788
+ updates: resolveReferenceList(registry, getFieldValue(statement, "updates")),
1789
+ deletes: resolveReferenceList(registry, getFieldValue(statement, "deletes")),
1790
+ input: resolveReferenceList(registry, getFieldValue(statement, "input")),
1791
+ output: resolveReferenceList(registry, getFieldValue(statement, "output")),
1792
+ resolvedDomain: resolveDomainTag(statement, registry)
1793
+ };
1794
+ case "component":
1795
+ return {
1796
+ ...base,
1797
+ category: symbolValue(getFieldValue(statement, "category")),
1798
+ props: normalizeComponentProps(statement),
1799
+ events: normalizeComponentEvents(statement, registry),
1800
+ slots: normalizeComponentSlots(statement),
1801
+ behavior: symbolValues(getFieldValue(statement, "behavior")),
1802
+ behaviors: normalizeComponentBehaviors(statement),
1803
+ patterns: symbolValues(getFieldValue(statement, "patterns")),
1804
+ regions: symbolValues(getFieldValue(statement, "regions")),
1805
+ approvals: symbolValues(getFieldValue(statement, "approvals")),
1806
+ lookups: resolveReferenceList(registry, getFieldValue(statement, "lookups")),
1807
+ dependencies: resolveReferenceList(registry, getFieldValue(statement, "dependencies")),
1808
+ version: stringValue(getFieldValue(statement, "version"))
1809
+ };
1810
+ case "rule":
1811
+ return {
1812
+ ...base,
1813
+ appliesTo: resolveReferenceList(registry, getFieldValue(statement, "applies_to")),
1814
+ actors: resolveReferenceList(registry, getFieldValue(statement, "actors")),
1815
+ roles: resolveReferenceList(registry, getFieldValue(statement, "roles")),
1816
+ condition: valueAsArray(getFieldValue(statement, "condition")).map((item) => item.value),
1817
+ conditionNode: getFieldValue(statement, "condition") ? parseRuleExpression(getFieldValue(statement, "condition")) : null,
1818
+ requirement: valueAsArray(getFieldValue(statement, "requirement")).map((item) => item.value),
1819
+ requirementNode: getFieldValue(statement, "requirement") ? parseRuleExpression(getFieldValue(statement, "requirement")) : null,
1820
+ fromRequirement: getFieldValue(statement, "from_requirement")
1821
+ ? {
1822
+ id: symbolValue(getFieldValue(statement, "from_requirement")),
1823
+ target: toRef(resolveReference(registry, symbolValue(getFieldValue(statement, "from_requirement"))))
1824
+ }
1825
+ : null,
1826
+ severity: symbolValue(getFieldValue(statement, "severity")),
1827
+ sourceOfTruth: resolveReferenceList(registry, getFieldValue(statement, "source_of_truth")),
1828
+ resolvedDomain: resolveDomainTag(statement, registry)
1829
+ };
1830
+ case "decision":
1831
+ return {
1832
+ ...base,
1833
+ context: symbolValues(getFieldValue(statement, "context")),
1834
+ consequences: symbolValues(getFieldValue(statement, "consequences")),
1835
+ pitch: getFieldValue(statement, "pitch")
1836
+ ? {
1837
+ id: symbolValue(getFieldValue(statement, "pitch")),
1838
+ target: toRef(resolveReference(registry, symbolValue(getFieldValue(statement, "pitch"))))
1839
+ }
1840
+ : null,
1841
+ supersedes: resolveReferenceList(registry, getFieldValue(statement, "supersedes")),
1842
+ resolvedDomain: resolveDomainTag(statement, registry)
1843
+ };
1844
+ case "projection":
1845
+ return {
1846
+ ...base,
1847
+ platform: symbolValue(getFieldValue(statement, "platform")),
1848
+ realizes: resolveReferenceList(registry, getFieldValue(statement, "realizes")),
1849
+ outputs: symbolValues(getFieldValue(statement, "outputs")),
1850
+ http: parseProjectionHttpBlock(statement, registry),
1851
+ httpErrors: parseProjectionHttpErrorsBlock(statement, registry),
1852
+ httpFields: parseProjectionHttpFieldsBlock(statement, registry),
1853
+ httpResponses: parseProjectionHttpResponsesBlock(statement, registry),
1854
+ httpPreconditions: parseProjectionHttpPreconditionsBlock(statement, registry),
1855
+ httpIdempotency: parseProjectionHttpIdempotencyBlock(statement, registry),
1856
+ httpCache: parseProjectionHttpCacheBlock(statement, registry),
1857
+ httpDelete: parseProjectionHttpDeleteBlock(statement, registry),
1858
+ httpAsync: parseProjectionHttpAsyncBlock(statement, registry),
1859
+ httpStatus: parseProjectionHttpStatusBlock(statement, registry),
1860
+ httpDownload: parseProjectionHttpDownloadBlock(statement, registry),
1861
+ httpAuthz: parseProjectionHttpAuthzBlock(statement, registry),
1862
+ httpCallbacks: parseProjectionHttpCallbacksBlock(statement, registry),
1863
+ uiScreens: parseProjectionUiScreensBlock(statement, registry),
1864
+ uiCollections: parseProjectionUiCollectionsBlock(statement),
1865
+ uiActions: parseProjectionUiActionsBlock(statement, registry),
1866
+ uiVisibility: parseProjectionUiVisibilityBlock(statement, registry),
1867
+ uiLookups: parseProjectionUiLookupsBlock(statement, registry),
1868
+ uiRoutes: parseProjectionUiRoutesBlock(statement),
1869
+ uiWeb: parseProjectionUiWebBlock(statement, registry),
1870
+ uiIos: parseProjectionUiIosBlock(statement, registry),
1871
+ uiAppShell: parseProjectionUiAppShellBlock(statement),
1872
+ uiNavigation: parseProjectionUiNavigationBlock(statement),
1873
+ uiScreenRegions: parseProjectionUiScreenRegionsBlock(statement),
1874
+ uiComponents: parseProjectionUiComponentsBlock(statement, registry),
1875
+ dbTables: parseProjectionDbTablesBlock(statement, registry),
1876
+ dbColumns: parseProjectionDbColumnsBlock(statement, registry),
1877
+ dbKeys: parseProjectionDbKeysBlock(statement, registry),
1878
+ dbIndexes: parseProjectionDbIndexesBlock(statement, registry),
1879
+ dbRelations: parseProjectionDbRelationsBlock(statement, registry),
1880
+ dbLifecycle: parseProjectionDbLifecycleBlock(statement, registry),
1881
+ generatorDefaults: parseProjectionGeneratorDefaultsBlock(statement)
1882
+ };
1883
+ case "orchestration":
1884
+ return {
1885
+ ...base,
1886
+ inputs: resolveReferenceList(registry, getFieldValue(statement, "inputs")),
1887
+ steps: symbolValues(getFieldValue(statement, "steps")),
1888
+ outputs: symbolValues(getFieldValue(statement, "outputs")),
1889
+ resolvedDomain: resolveDomainTag(statement, registry)
1890
+ };
1891
+ case "verification":
1892
+ return {
1893
+ ...base,
1894
+ validates: resolveReferenceList(registry, getFieldValue(statement, "validates")),
1895
+ method: symbolValue(getFieldValue(statement, "method")),
1896
+ scenarios: symbolValues(getFieldValue(statement, "scenarios")),
1897
+ requirementRefs: resolveReferenceList(registry, getFieldValue(statement, "requirement_refs")),
1898
+ acceptanceRefs: resolveReferenceList(registry, getFieldValue(statement, "acceptance_refs")),
1899
+ fixesBugs: resolveReferenceList(registry, getFieldValue(statement, "fixes_bugs")),
1900
+ resolvedDomain: resolveDomainTag(statement, registry)
1901
+ };
1902
+ case "operation":
1903
+ return {
1904
+ ...base,
1905
+ observes: resolveReferenceList(registry, getFieldValue(statement, "observes")),
1906
+ metrics: symbolValues(getFieldValue(statement, "metrics")),
1907
+ alerts: symbolValues(getFieldValue(statement, "alerts")),
1908
+ resolvedDomain: resolveDomainTag(statement, registry)
1909
+ };
1910
+ case "term":
1911
+ return {
1912
+ ...base,
1913
+ aliases: symbolValues(getFieldValue(statement, "aliases")),
1914
+ excludes: symbolValues(getFieldValue(statement, "excludes"))
1915
+ };
1916
+ case "domain":
1917
+ return {
1918
+ ...base,
1919
+ inScope: normalizeDomainScopeList(statement, "in_scope"),
1920
+ outOfScope: normalizeDomainScopeList(statement, "out_of_scope"),
1921
+ owners: resolveReferenceList(registry, getFieldValue(statement, "owners")),
1922
+ parentDomain: getFieldValue(statement, "parent_domain")
1923
+ ? {
1924
+ id: symbolValue(getFieldValue(statement, "parent_domain")),
1925
+ target: toRef(resolveReference(registry, symbolValue(getFieldValue(statement, "parent_domain"))))
1926
+ }
1927
+ : null,
1928
+ aliases: normalizeDomainScopeList(statement, "aliases")
1929
+ };
1930
+ case "pitch":
1931
+ return {
1932
+ ...base,
1933
+ priority: symbolValue(getFieldValue(statement, "priority")),
1934
+ appetite: stringValue(getFieldValue(statement, "appetite")) || symbolValue(getFieldValue(statement, "appetite")),
1935
+ problem: stringValue(getFieldValue(statement, "problem")),
1936
+ solutionSketch: stringValue(getFieldValue(statement, "solution_sketch")),
1937
+ rabbitHoles: stringValue(getFieldValue(statement, "rabbit_holes")) || symbolValues(getFieldValue(statement, "rabbit_holes")),
1938
+ noGoAreas: stringValue(getFieldValue(statement, "no_go_areas")) || symbolValues(getFieldValue(statement, "no_go_areas")),
1939
+ affects: resolveReferenceList(registry, getFieldValue(statement, "affects")),
1940
+ decisions: resolveReferenceList(registry, getFieldValue(statement, "decisions")),
1941
+ updated: stringValue(getFieldValue(statement, "updated")),
1942
+ resolvedDomain: resolveDomainTag(statement, registry)
1943
+ };
1944
+ case "requirement":
1945
+ return {
1946
+ ...base,
1947
+ priority: symbolValue(getFieldValue(statement, "priority")),
1948
+ pitch: getFieldValue(statement, "pitch")
1949
+ ? {
1950
+ id: symbolValue(getFieldValue(statement, "pitch")),
1951
+ target: toRef(resolveReference(registry, symbolValue(getFieldValue(statement, "pitch"))))
1952
+ }
1953
+ : null,
1954
+ affects: resolveReferenceList(registry, getFieldValue(statement, "affects")),
1955
+ introducesRules: resolveReferenceList(registry, getFieldValue(statement, "introduces_rules")),
1956
+ respectsRules: resolveReferenceList(registry, getFieldValue(statement, "respects_rules")),
1957
+ supersedes: resolveReferenceList(registry, getFieldValue(statement, "supersedes")),
1958
+ updated: stringValue(getFieldValue(statement, "updated")),
1959
+ resolvedDomain: resolveDomainTag(statement, registry)
1960
+ };
1961
+ case "acceptance_criterion":
1962
+ return {
1963
+ ...base,
1964
+ requirement: getFieldValue(statement, "requirement")
1965
+ ? {
1966
+ id: symbolValue(getFieldValue(statement, "requirement")),
1967
+ target: toRef(resolveReference(registry, symbolValue(getFieldValue(statement, "requirement"))))
1968
+ }
1969
+ : null,
1970
+ supersedes: resolveReferenceList(registry, getFieldValue(statement, "supersedes")),
1971
+ updated: stringValue(getFieldValue(statement, "updated"))
1972
+ };
1973
+ case "task":
1974
+ return {
1975
+ ...base,
1976
+ priority: symbolValue(getFieldValue(statement, "priority")),
1977
+ workType: symbolValue(getFieldValue(statement, "work_type")),
1978
+ affects: resolveReferenceList(registry, getFieldValue(statement, "affects")),
1979
+ satisfies: resolveReferenceList(registry, getFieldValue(statement, "satisfies")),
1980
+ acceptanceRefs: resolveReferenceList(registry, getFieldValue(statement, "acceptance_refs")),
1981
+ blocks: resolveReferenceList(registry, getFieldValue(statement, "blocks")),
1982
+ blockedBy: resolveReferenceList(registry, getFieldValue(statement, "blocked_by")),
1983
+ claimedBy: resolveReferenceList(registry, getFieldValue(statement, "claimed_by")),
1984
+ introducesDecisions: resolveReferenceList(registry, getFieldValue(statement, "introduces_decisions")),
1985
+ modifies: resolveReferenceList(registry, getFieldValue(statement, "modifies")),
1986
+ introduces: resolveReferenceList(registry, getFieldValue(statement, "introduces")),
1987
+ removes: resolveReferenceList(registry, getFieldValue(statement, "removes")),
1988
+ updated: stringValue(getFieldValue(statement, "updated")),
1989
+ resolvedDomain: resolveDomainTag(statement, registry)
1990
+ };
1991
+ case "bug":
1992
+ return {
1993
+ ...base,
1994
+ priority: symbolValue(getFieldValue(statement, "priority")),
1995
+ severity: symbolValue(getFieldValue(statement, "severity")),
1996
+ affects: resolveReferenceList(registry, getFieldValue(statement, "affects")),
1997
+ violates: resolveReferenceList(registry, getFieldValue(statement, "violates")),
1998
+ surfacesRule: resolveReferenceList(registry, getFieldValue(statement, "surfaces_rule")),
1999
+ introducedIn: resolveReferenceList(registry, getFieldValue(statement, "introduced_in")),
2000
+ fixedIn: resolveReferenceList(registry, getFieldValue(statement, "fixed_in")),
2001
+ fixedInRelease: stringValue(getFieldValue(statement, "fixed_in_release")) || symbolValue(getFieldValue(statement, "fixed_in_release")),
2002
+ fixedInVerification: resolveReferenceList(registry, getFieldValue(statement, "fixed_in_verification")),
2003
+ reproduction: stringValue(getFieldValue(statement, "reproduction")),
2004
+ modifies: resolveReferenceList(registry, getFieldValue(statement, "modifies")),
2005
+ introduces: resolveReferenceList(registry, getFieldValue(statement, "introduces")),
2006
+ removes: resolveReferenceList(registry, getFieldValue(statement, "removes")),
2007
+ updated: stringValue(getFieldValue(statement, "updated")),
2008
+ resolvedDomain: resolveDomainTag(statement, registry)
2009
+ };
2010
+ default:
2011
+ return {
2012
+ ...base,
2013
+ fields: [...fieldMap.keys()]
2014
+ };
2015
+ }
2016
+ }
2017
+
2018
+ function normalizeDoc(doc) {
2019
+ return {
2020
+ id: doc.metadata.id,
2021
+ kind: doc.metadata.kind,
2022
+ title: doc.metadata.title,
2023
+ status: doc.metadata.status,
2024
+ summary: doc.metadata.summary || null,
2025
+ successOutcome: doc.metadata.success_outcome || null,
2026
+ aliases: [...(doc.metadata.aliases || [])],
2027
+ actors: [...(doc.metadata.actors || [])],
2028
+ relatedEntities: [...(doc.metadata.related_entities || [])],
2029
+ relatedCapabilities: [...(doc.metadata.related_capabilities || [])],
2030
+ relatedActors: [...(doc.metadata.related_actors || [])],
2031
+ relatedRoles: [...(doc.metadata.related_roles || [])],
2032
+ relatedRules: [...(doc.metadata.related_rules || [])],
2033
+ relatedWorkflows: [...(doc.metadata.related_workflows || [])],
2034
+ relatedShapes: [...(doc.metadata.related_shapes || [])],
2035
+ relatedProjections: [...(doc.metadata.related_projections || [])],
2036
+ relatedDocs: [...(doc.metadata.related_docs || [])],
2037
+ sourceOfTruth: doc.metadata.source_of_truth || null,
2038
+ confidence: doc.metadata.confidence || null,
2039
+ reviewRequired: doc.metadata.review_required ?? false,
2040
+ provenance: [...(doc.metadata.provenance || [])],
2041
+ tags: [...(doc.metadata.tags || [])],
2042
+ domain: doc.metadata.domain || null,
2043
+ appVersion: doc.metadata.app_version || null,
2044
+ audience: doc.metadata.audience || null,
2045
+ priority: doc.metadata.priority || null,
2046
+ version: doc.metadata.version || null,
2047
+ affects: [...(doc.metadata.affects || [])],
2048
+ satisfies: [...(doc.metadata.satisfies || [])],
2049
+ approvals: [...(doc.metadata.approvals || [])],
2050
+ file: doc.file,
2051
+ relativePath: doc.relativePath,
2052
+ body: doc.body
2053
+ };
2054
+ }
2055
+
2056
+ export function resolveWorkspace(workspaceAst) {
2057
+ const validation = validateWorkspace(workspaceAst);
2058
+ if (!validation.ok) {
2059
+ return {
2060
+ ok: false,
2061
+ validation
2062
+ };
2063
+ }
2064
+
2065
+ const archive = loadArchive(workspaceAst.root);
2066
+ if (archive.errors.length > 0) {
2067
+ const archiveErrors = archive.errors.map((message) => ({
2068
+ message: `Invalid SDLC archive: ${message}`,
2069
+ loc: {
2070
+ file: workspaceAst.root,
2071
+ start: { line: 1, column: 1, offset: 0 },
2072
+ end: { line: 1, column: 1, offset: 0 }
2073
+ }
2074
+ }));
2075
+ return {
2076
+ ok: false,
2077
+ validation: {
2078
+ ...validation,
2079
+ ok: false,
2080
+ errorCount: validation.errorCount + archiveErrors.length,
2081
+ errors: [...validation.errors, ...archiveErrors]
2082
+ }
2083
+ };
2084
+ }
2085
+
2086
+ const errors = [];
2087
+ const registry = buildRegistry(workspaceAst, errors);
2088
+ const statements = workspaceAst.files.flatMap((file) => file.statements);
2089
+ const resolvedStatements = statements.map((statement) => normalizeStatement(statement, registry));
2090
+ const byId = new Map(resolvedStatements.map((statement) => [statement.id, statement]));
2091
+
2092
+ // Build domain.members back-links by reverse-indexing tagged statements.
2093
+ // Members are grouped per kind so consumers can ask for `domain.members.capabilities`
2094
+ // without re-walking the registry. Phase 2 extends with SDLC kinds; documents
2095
+ // are folded in below from workspaceAst.docs[].metadata.domain.
2096
+ const domainMembersById = new Map();
2097
+ for (const statement of resolvedStatements) {
2098
+ if (statement.kind === "domain") {
2099
+ domainMembersById.set(statement.id, {
2100
+ capabilities: [],
2101
+ entities: [],
2102
+ rules: [],
2103
+ verifications: [],
2104
+ orchestrations: [],
2105
+ operations: [],
2106
+ decisions: [],
2107
+ pitches: [],
2108
+ requirements: [],
2109
+ tasks: [],
2110
+ bugs: [],
2111
+ documents: []
2112
+ });
2113
+ }
2114
+ }
2115
+ const memberKindToBucket = {
2116
+ capability: "capabilities",
2117
+ entity: "entities",
2118
+ rule: "rules",
2119
+ verification: "verifications",
2120
+ orchestration: "orchestrations",
2121
+ operation: "operations",
2122
+ decision: "decisions",
2123
+ pitch: "pitches",
2124
+ requirement: "requirements",
2125
+ task: "tasks",
2126
+ bug: "bugs"
2127
+ };
2128
+ for (const statement of resolvedStatements) {
2129
+ const bucketKey = memberKindToBucket[statement.kind];
2130
+ if (!bucketKey || !statement.resolvedDomain) {
2131
+ continue;
2132
+ }
2133
+ const members = domainMembersById.get(statement.resolvedDomain.id);
2134
+ if (members) {
2135
+ members[bucketKey].push(statement.id);
2136
+ }
2137
+ }
2138
+ // Fold tagged documents into domain.members.documents.
2139
+ for (const doc of workspaceAst.docs || []) {
2140
+ if (doc.parseError) continue;
2141
+ const domainId = doc.metadata?.domain;
2142
+ if (!domainId) continue;
2143
+ const members = domainMembersById.get(domainId);
2144
+ if (members && doc.metadata.id) {
2145
+ members.documents.push(doc.metadata.id);
2146
+ }
2147
+ }
2148
+ for (const members of domainMembersById.values()) {
2149
+ for (const bucket of Object.values(members)) {
2150
+ bucket.sort();
2151
+ }
2152
+ }
2153
+
2154
+ // Phase 2: build SDLC back-link indices in a single pass over the resolved
2155
+ // statements. Each index maps `targetId -> [sourceId,...]`.
2156
+ const sdlcIndex = {
2157
+ requirementsByPitch: new Map(),
2158
+ decisionsByPitch: new Map(),
2159
+ acsByRequirement: new Map(),
2160
+ tasksBySatisfiedRequirement: new Map(),
2161
+ tasksByAcceptanceRef: new Map(),
2162
+ verificationsByRequirementRef: new Map(),
2163
+ verificationsByAcceptanceRef: new Map(),
2164
+ verificationsFixingBug: new Map(),
2165
+ supersededByRequirements: new Map(),
2166
+ supersededByAcs: new Map(),
2167
+ documentsBySatisfies: new Map(),
2168
+ rulesByFromRequirement: new Map(),
2169
+ tasksThatBlockTarget: new Map(),
2170
+ tasksBlockedByTarget: new Map(),
2171
+ affectedByPitches: new Map(),
2172
+ affectedByRequirements: new Map(),
2173
+ affectedByTasks: new Map(),
2174
+ affectedByBugs: new Map(),
2175
+ introducedRulesByRequirement: new Map(),
2176
+ respectedRulesByRequirement: new Map(),
2177
+ rulesViolatedByBug: new Map(),
2178
+ rulesSurfacedByBug: new Map(),
2179
+ decisionsIntroducedByTask: new Map()
2180
+ };
2181
+ function pushIndex(map, key, value) {
2182
+ if (!key || !value) return;
2183
+ if (!map.has(key)) map.set(key, []);
2184
+ map.get(key).push(value);
2185
+ }
2186
+ function pushIndexFromList(map, list, value) {
2187
+ if (!Array.isArray(list)) return;
2188
+ for (const ref of list) {
2189
+ const key = typeof ref === "string" ? ref : ref?.id;
2190
+ pushIndex(map, key, value);
2191
+ }
2192
+ }
2193
+ for (const statement of resolvedStatements) {
2194
+ switch (statement.kind) {
2195
+ case "requirement":
2196
+ pushIndex(sdlcIndex.requirementsByPitch, statement.pitch?.id, statement.id);
2197
+ pushIndexFromList(sdlcIndex.affectedByRequirements, statement.affects, statement.id);
2198
+ pushIndexFromList(sdlcIndex.introducedRulesByRequirement, statement.introducesRules, statement.id);
2199
+ pushIndexFromList(sdlcIndex.respectedRulesByRequirement, statement.respectsRules, statement.id);
2200
+ pushIndexFromList(sdlcIndex.supersededByRequirements, statement.supersedes, statement.id);
2201
+ break;
2202
+ case "acceptance_criterion":
2203
+ pushIndex(sdlcIndex.acsByRequirement, statement.requirement?.id, statement.id);
2204
+ pushIndexFromList(sdlcIndex.supersededByAcs, statement.supersedes, statement.id);
2205
+ break;
2206
+ case "decision":
2207
+ pushIndex(sdlcIndex.decisionsByPitch, statement.pitch?.id, statement.id);
2208
+ break;
2209
+ case "rule":
2210
+ pushIndex(sdlcIndex.rulesByFromRequirement, statement.fromRequirement?.id, statement.id);
2211
+ break;
2212
+ case "pitch":
2213
+ pushIndexFromList(sdlcIndex.affectedByPitches, statement.affects, statement.id);
2214
+ break;
2215
+ case "task":
2216
+ pushIndexFromList(sdlcIndex.affectedByTasks, statement.affects, statement.id);
2217
+ pushIndexFromList(sdlcIndex.tasksBySatisfiedRequirement, statement.satisfies, statement.id);
2218
+ pushIndexFromList(sdlcIndex.tasksByAcceptanceRef, statement.acceptanceRefs, statement.id);
2219
+ pushIndexFromList(sdlcIndex.decisionsIntroducedByTask, statement.introducesDecisions, statement.id);
2220
+ pushIndexFromList(sdlcIndex.tasksThatBlockTarget, statement.blocks, statement.id);
2221
+ pushIndexFromList(sdlcIndex.tasksBlockedByTarget, statement.blockedBy, statement.id);
2222
+ break;
2223
+ case "bug":
2224
+ pushIndexFromList(sdlcIndex.affectedByBugs, statement.affects, statement.id);
2225
+ pushIndexFromList(sdlcIndex.rulesViolatedByBug, statement.violates, statement.id);
2226
+ pushIndexFromList(sdlcIndex.rulesSurfacedByBug, statement.surfacesRule, statement.id);
2227
+ break;
2228
+ case "verification":
2229
+ pushIndexFromList(sdlcIndex.verificationsByRequirementRef, statement.requirementRefs, statement.id);
2230
+ pushIndexFromList(sdlcIndex.verificationsByAcceptanceRef, statement.acceptanceRefs, statement.id);
2231
+ pushIndexFromList(sdlcIndex.verificationsFixingBug, statement.fixesBugs, statement.id);
2232
+ break;
2233
+ default:
2234
+ break;
2235
+ }
2236
+ }
2237
+ // Documents `satisfies` frontmatter is folded in from workspaceAst.docs.
2238
+ for (const doc of workspaceAst.docs || []) {
2239
+ if (doc.parseError) continue;
2240
+ const satisfies = doc.metadata?.satisfies;
2241
+ if (!satisfies || !doc.metadata.id) continue;
2242
+ const ids = Array.isArray(satisfies) ? satisfies : [satisfies];
2243
+ for (const id of ids) {
2244
+ pushIndex(sdlcIndex.documentsBySatisfies, id, doc.metadata.id);
2245
+ }
2246
+ }
2247
+
2248
+ const enrichedStatements = resolvedStatements.map((statement) => {
2249
+ switch (statement.kind) {
2250
+ case "shape":
2251
+ return {
2252
+ ...statement,
2253
+ projectedFields: projectShapeFields(statement, byId)
2254
+ };
2255
+ case "capability":
2256
+ return {
2257
+ ...statement,
2258
+ flow: buildCapabilityFlow(statement)
2259
+ };
2260
+ case "component":
2261
+ return {
2262
+ ...statement,
2263
+ componentContract: buildComponentContract(statement)
2264
+ };
2265
+ case "rule":
2266
+ return {
2267
+ ...statement,
2268
+ policy: buildRulePolicy(statement)
2269
+ };
2270
+ case "decision":
2271
+ return {
2272
+ ...statement,
2273
+ record: buildDecisionRecord(statement)
2274
+ };
2275
+ case "projection":
2276
+ return {
2277
+ ...statement,
2278
+ plan: buildProjectionPlan(statement)
2279
+ };
2280
+ case "orchestration":
2281
+ return {
2282
+ ...statement,
2283
+ plan: buildOrchestrationPlan(statement)
2284
+ };
2285
+ case "verification":
2286
+ return {
2287
+ ...statement,
2288
+ plan: buildVerificationPlan(statement)
2289
+ };
2290
+ case "operation":
2291
+ return {
2292
+ ...statement,
2293
+ monitoring: buildOperationMonitoring(statement)
2294
+ };
2295
+ case "term":
2296
+ return {
2297
+ ...statement,
2298
+ vocabulary: buildTermVocabulary(statement)
2299
+ };
2300
+ case "domain":
2301
+ return {
2302
+ ...statement,
2303
+ members: domainMembersById.get(statement.id) || {
2304
+ capabilities: [],
2305
+ entities: [],
2306
+ rules: [],
2307
+ verifications: [],
2308
+ orchestrations: [],
2309
+ operations: [],
2310
+ decisions: [],
2311
+ pitches: [],
2312
+ requirements: [],
2313
+ tasks: [],
2314
+ bugs: [],
2315
+ documents: []
2316
+ }
2317
+ };
2318
+ case "pitch":
2319
+ return {
2320
+ ...statement,
2321
+ ...enrichPitch(statement, sdlcIndex)
2322
+ };
2323
+ case "requirement":
2324
+ return {
2325
+ ...statement,
2326
+ ...enrichRequirement(statement, sdlcIndex)
2327
+ };
2328
+ case "acceptance_criterion":
2329
+ return {
2330
+ ...statement,
2331
+ ...enrichAcceptanceCriterion(statement, sdlcIndex)
2332
+ };
2333
+ case "task":
2334
+ return {
2335
+ ...statement,
2336
+ ...enrichTask(statement, sdlcIndex)
2337
+ };
2338
+ case "bug":
2339
+ return {
2340
+ ...statement,
2341
+ ...enrichBug(statement, sdlcIndex)
2342
+ };
2343
+ default:
2344
+ return statement;
2345
+ }
2346
+ });
2347
+
2348
+ // After per-kind enrichment, add `affectedBy*` lists onto the targets
2349
+ // (capability/entity/rule/projection/component/orchestration/operation) and
2350
+ // the change-tracking lists onto the carrier kinds (rule, decision).
2351
+ const affectedByPitches = sdlcIndex.affectedByPitches;
2352
+ const affectedByRequirements = sdlcIndex.affectedByRequirements;
2353
+ const affectedByTasks = sdlcIndex.affectedByTasks;
2354
+ const affectedByBugs = sdlcIndex.affectedByBugs;
2355
+ const introducedRulesByRequirement = sdlcIndex.introducedRulesByRequirement;
2356
+ const respectedRulesByRequirement = sdlcIndex.respectedRulesByRequirement;
2357
+ const rulesViolatedByBug = sdlcIndex.rulesViolatedByBug;
2358
+ const rulesSurfacedByBug = sdlcIndex.rulesSurfacedByBug;
2359
+ const decisionsIntroducedByTask = sdlcIndex.decisionsIntroducedByTask;
2360
+ const sortedOr = (map, key) => (map.get(key) || []).slice().sort();
2361
+ const enrichedWithAffected = enrichedStatements.map((statement) => {
2362
+ switch (statement.kind) {
2363
+ case "capability":
2364
+ case "entity":
2365
+ case "projection":
2366
+ case "component":
2367
+ case "orchestration":
2368
+ case "operation":
2369
+ return {
2370
+ ...statement,
2371
+ affectedByPitches: sortedOr(affectedByPitches, statement.id),
2372
+ affectedByRequirements: sortedOr(affectedByRequirements, statement.id),
2373
+ affectedByTasks: sortedOr(affectedByTasks, statement.id),
2374
+ affectedByBugs: sortedOr(affectedByBugs, statement.id)
2375
+ };
2376
+ case "rule":
2377
+ return {
2378
+ ...statement,
2379
+ affectedByPitches: sortedOr(affectedByPitches, statement.id),
2380
+ affectedByRequirements: sortedOr(affectedByRequirements, statement.id),
2381
+ affectedByTasks: sortedOr(affectedByTasks, statement.id),
2382
+ affectedByBugs: sortedOr(affectedByBugs, statement.id),
2383
+ introducedByRequirements: sortedOr(introducedRulesByRequirement, statement.id),
2384
+ respectedByRequirements: sortedOr(respectedRulesByRequirement, statement.id),
2385
+ violatedByBugs: sortedOr(rulesViolatedByBug, statement.id),
2386
+ surfacedByBugs: sortedOr(rulesSurfacedByBug, statement.id)
2387
+ };
2388
+ case "decision":
2389
+ return {
2390
+ ...statement,
2391
+ introducedByTasks: sortedOr(decisionsIntroducedByTask, statement.id)
2392
+ };
2393
+ default:
2394
+ return statement;
2395
+ }
2396
+ });
2397
+ const byKind = groupBy(enrichedWithAffected, (statement) => statement.kind);
2398
+ const finalStatements = enrichedWithAffected.map((statement) => {
2399
+ if (statement.kind !== "shape") {
2400
+ return statement;
2401
+ }
2402
+
2403
+ return {
2404
+ ...statement,
2405
+ transformGraph: buildShapeTransformGraph(statement, byId)
2406
+ };
2407
+ });
2408
+ const finalByKind = groupBy(finalStatements, (statement) => statement.kind);
2409
+
2410
+ const graph = mergeArchivedIntoGraph({
2411
+ root: workspaceAst.root,
2412
+ statements: finalStatements,
2413
+ byKind: finalByKind,
2414
+ docs: (workspaceAst.docs || []).filter((doc) => !doc.parseError).map(normalizeDoc)
2415
+ }, archive);
2416
+
2417
+ return {
2418
+ ok: true,
2419
+ validation,
2420
+ graph
2421
+ };
2422
+ }