@codeyam/codeyam-cli 0.1.0-staging.1669d45 → 0.1.0-staging.323686

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 (362) hide show
  1. package/analyzer-template/.build-info.json +8 -8
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +3 -3
  4. package/analyzer-template/packages/ai/index.ts +9 -1
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +48 -34
  6. package/analyzer-template/packages/ai/src/lib/astScopes/arrayDerivationDetector.ts +199 -0
  7. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +31 -0
  8. package/analyzer-template/packages/ai/src/lib/astScopes/methodSemantics.ts +139 -23
  9. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.ts +6 -126
  10. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +277 -8
  11. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +73 -1
  12. package/analyzer-template/packages/ai/src/lib/completionCall.ts +198 -34
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +108 -1
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.ts +205 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.ts +10 -2
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +23 -0
  17. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +87 -2
  18. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +32 -7
  19. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.ts +129 -0
  20. package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +156 -0
  21. package/analyzer-template/packages/ai/src/lib/e2eDataTracking.ts +334 -0
  22. package/analyzer-template/packages/ai/src/lib/extractCriticalDataKeys.ts +120 -0
  23. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +626 -6
  24. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +26 -1
  25. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +375 -6
  26. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +1003 -45
  27. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +239 -0
  28. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChunkPrompt.ts +82 -0
  29. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateCriticalKeysPrompt.ts +103 -0
  30. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.ts +23 -6
  31. package/analyzer-template/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.ts +391 -0
  32. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +154 -32
  33. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +22 -1
  34. package/analyzer-template/packages/ai/src/lib/worker/analyzeScopeWorker.ts +114 -2
  35. package/analyzer-template/packages/analyze/src/lib/analysisContext.ts +44 -4
  36. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +10 -13
  37. package/analyzer-template/packages/analyze/src/lib/files/analyze/dependencyResolver.ts +6 -0
  38. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +33 -7
  39. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +142 -73
  40. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +42 -5
  41. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +1 -1
  42. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +77 -0
  43. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.ts +56 -11
  44. package/analyzer-template/packages/aws/package.json +1 -1
  45. package/analyzer-template/packages/database/src/lib/kysely/db.ts +8 -1
  46. package/analyzer-template/packages/database/src/lib/kysely/tables/commitsTable.ts +6 -0
  47. package/analyzer-template/packages/database/src/lib/loadAnalyses.ts +58 -1
  48. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +13 -0
  49. package/analyzer-template/packages/database/src/lib/loadBranch.ts +16 -1
  50. package/analyzer-template/packages/database/src/lib/loadCommit.ts +11 -0
  51. package/analyzer-template/packages/database/src/lib/loadCommits.ts +28 -0
  52. package/analyzer-template/packages/database/src/lib/loadEntities.ts +26 -3
  53. package/analyzer-template/packages/database/src/lib/loadEntityBranches.ts +12 -0
  54. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +7 -14
  55. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.d.ts.map +1 -1
  56. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js +8 -1
  57. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js.map +1 -1
  58. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/analysesTable.d.ts.map +1 -1
  59. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts +1 -0
  60. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts.map +1 -1
  61. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js +3 -0
  62. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  63. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts +2 -0
  64. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts.map +1 -1
  65. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js +45 -2
  66. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js.map +1 -1
  67. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  68. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +8 -0
  69. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  70. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js +11 -1
  71. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js.map +1 -1
  72. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.d.ts.map +1 -1
  73. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js +7 -0
  74. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js.map +1 -1
  75. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts +3 -1
  76. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts.map +1 -1
  77. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js +22 -1
  78. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js.map +1 -1
  79. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts +3 -1
  80. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  81. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +23 -4
  82. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  83. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.d.ts.map +1 -1
  84. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js +9 -0
  85. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js.map +1 -1
  86. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts +2 -2
  87. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  88. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +5 -4
  89. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  90. package/analyzer-template/packages/github/dist/types/index.d.ts +1 -1
  91. package/analyzer-template/packages/github/dist/types/index.d.ts.map +1 -1
  92. package/analyzer-template/packages/github/dist/types/index.js.map +1 -1
  93. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts +25 -1
  94. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts.map +1 -1
  95. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts +2 -0
  96. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts.map +1 -1
  97. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +51 -1
  98. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  99. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts +9 -1
  100. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  101. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js +29 -3
  102. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js.map +1 -1
  103. package/analyzer-template/packages/types/index.ts +1 -0
  104. package/analyzer-template/packages/types/src/types/Analysis.ts +25 -0
  105. package/analyzer-template/packages/types/src/types/Commit.ts +2 -0
  106. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +64 -1
  107. package/analyzer-template/packages/utils/dist/types/index.d.ts +1 -1
  108. package/analyzer-template/packages/utils/dist/types/index.d.ts.map +1 -1
  109. package/analyzer-template/packages/utils/dist/types/index.js.map +1 -1
  110. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts +25 -1
  111. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts.map +1 -1
  112. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts +2 -0
  113. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts.map +1 -1
  114. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +51 -1
  115. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  116. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts +9 -1
  117. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  118. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js +29 -3
  119. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js.map +1 -1
  120. package/analyzer-template/packages/utils/src/lib/safeFileName.ts +48 -3
  121. package/analyzer-template/playwright/capture.ts +20 -8
  122. package/analyzer-template/playwright/captureStatic.ts +1 -1
  123. package/analyzer-template/project/analyzeBaselineCommit.ts +5 -0
  124. package/analyzer-template/project/analyzeRegularCommit.ts +5 -0
  125. package/analyzer-template/project/captureLibraryFunctionDirect.ts +29 -26
  126. package/analyzer-template/project/constructMockCode.ts +314 -29
  127. package/analyzer-template/project/createEntitiesAndSortFiles.ts +83 -0
  128. package/analyzer-template/project/loadReadyToBeCaptured.ts +65 -41
  129. package/analyzer-template/project/orchestrateCapture/AwsCaptureTaskRunner.ts +12 -4
  130. package/analyzer-template/project/orchestrateCapture/SequentialCaptureTaskRunner.ts +18 -7
  131. package/analyzer-template/project/orchestrateCapture/taskRunner.ts +4 -2
  132. package/analyzer-template/project/orchestrateCapture.ts +71 -6
  133. package/analyzer-template/project/reconcileMockDataKeys.ts +152 -9
  134. package/analyzer-template/project/runAnalysis.ts +4 -0
  135. package/analyzer-template/project/start.ts +35 -11
  136. package/analyzer-template/project/writeMockDataTsx.ts +114 -2
  137. package/analyzer-template/project/writeScenarioComponents.ts +101 -8
  138. package/analyzer-template/scripts/comboWorkerLoop.cjs +98 -50
  139. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js +5 -0
  140. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js.map +1 -1
  141. package/background/src/lib/virtualized/project/analyzeRegularCommit.js +5 -0
  142. package/background/src/lib/virtualized/project/analyzeRegularCommit.js.map +1 -1
  143. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js +3 -3
  144. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js.map +1 -1
  145. package/background/src/lib/virtualized/project/constructMockCode.js +255 -4
  146. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  147. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js +73 -1
  148. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js.map +1 -1
  149. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js +19 -8
  150. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js.map +1 -1
  151. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js +2 -2
  152. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js.map +1 -1
  153. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js +7 -5
  154. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js.map +1 -1
  155. package/background/src/lib/virtualized/project/orchestrateCapture.js +58 -6
  156. package/background/src/lib/virtualized/project/orchestrateCapture.js.map +1 -1
  157. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js +126 -9
  158. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js.map +1 -1
  159. package/background/src/lib/virtualized/project/runAnalysis.js +3 -0
  160. package/background/src/lib/virtualized/project/runAnalysis.js.map +1 -1
  161. package/background/src/lib/virtualized/project/start.js +32 -11
  162. package/background/src/lib/virtualized/project/start.js.map +1 -1
  163. package/background/src/lib/virtualized/project/writeMockDataTsx.js +89 -2
  164. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  165. package/background/src/lib/virtualized/project/writeScenarioComponents.js +57 -8
  166. package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
  167. package/codeyam-cli/src/cli.js +2 -0
  168. package/codeyam-cli/src/cli.js.map +1 -1
  169. package/codeyam-cli/src/commands/memory.js +273 -0
  170. package/codeyam-cli/src/commands/memory.js.map +1 -0
  171. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +4 -0
  172. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  173. package/codeyam-cli/src/utils/analysisRunner.js +21 -2
  174. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  175. package/codeyam-cli/src/utils/install-skills.js +20 -6
  176. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  177. package/codeyam-cli/src/utils/queue/job.js +1 -0
  178. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  179. package/codeyam-cli/src/utils/queue/manager.js +6 -0
  180. package/codeyam-cli/src/utils/queue/manager.js.map +1 -1
  181. package/codeyam-cli/src/utils/rules/index.js +5 -0
  182. package/codeyam-cli/src/utils/rules/index.js.map +1 -0
  183. package/codeyam-cli/src/utils/rules/parser.js +106 -0
  184. package/codeyam-cli/src/utils/rules/parser.js.map +1 -0
  185. package/codeyam-cli/src/utils/rules/pathMatcher.js +18 -0
  186. package/codeyam-cli/src/utils/rules/pathMatcher.js.map +1 -0
  187. package/codeyam-cli/src/utils/rules/staleness.js +132 -0
  188. package/codeyam-cli/src/utils/rules/staleness.js.map +1 -0
  189. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +2 -0
  190. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  191. package/codeyam-cli/src/webserver/app/lib/database.js +7 -3
  192. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  193. package/codeyam-cli/src/webserver/bootstrap.js +40 -0
  194. package/codeyam-cli/src/webserver/bootstrap.js.map +1 -1
  195. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-DsN1wKrm.js +11 -0
  196. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-COi5OvsN.js → EntityTypeBadge-DLqD3qNt.js} +1 -1
  197. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-BwdQv49w.js → EntityTypeIcon-Ba2JVPzP.js} +1 -1
  198. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CEleMv_j.js → InlineSpinner-C8lyxW9k.js} +1 -1
  199. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-D68KarMg.js → InteractivePreview-aht4aafF.js} +2 -2
  200. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-L75Wvqgw.js → LibraryFunctionPreview-CVtiBnY5.js} +1 -1
  201. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-C53WM8qn.js → LoadingDots-B0GLXMsr.js} +1 -1
  202. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CrNkmy4i.js → LogViewer-xgeCVgSM.js} +1 -1
  203. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-OApQuNyq.js +16 -0
  204. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CQifa1n-.js → SafeScreenshot-DuDvi0jm.js} +1 -1
  205. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CyaBFX7l.js → ScenarioViewer-DzccYyI8.js} +3 -13
  206. package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-D36O1rzU.js → TruncatedFilePath-DyFZkK0l.js} +1 -1
  207. package/codeyam-cli/src/webserver/build/client/assets/_index-BwqWJOgH.js +11 -0
  208. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BwavGCpm.js +32 -0
  209. package/codeyam-cli/src/webserver/build/client/assets/api.health-l0sNRNKZ.js +1 -0
  210. package/codeyam-cli/src/webserver/build/client/assets/api.memory-profile-l0sNRNKZ.js +1 -0
  211. package/codeyam-cli/src/webserver/build/client/assets/api.restart-server-l0sNRNKZ.js +1 -0
  212. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-DgTPh8H-.js → chevron-down-Cx24_aWc.js} +1 -1
  213. package/codeyam-cli/src/webserver/build/client/assets/{chunk-EPOLDU6W-DdQKK6on.js → chunk-EPOLDU6W-CXRTFQ3F.js} +1 -1
  214. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-Dmr2bb1R.js → circle-check-BOARzkeR.js} +1 -1
  215. package/codeyam-cli/src/webserver/build/client/assets/copy-Bb-80kDT.js +6 -0
  216. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-Do4ZLUYa.js → createLucideIcon-BdhJEx6B.js} +1 -1
  217. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BBnGWYga.js +1 -0
  218. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CbdFyxZh.js → entity._sha._-BJUiQqZF.js} +12 -12
  219. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-B4iCfs5M.js → entity._sha.scenarios._scenarioId.fullscreen-DavjRmOY.js} +1 -1
  220. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-wDWZZO1W.js → entity._sha_.create-scenario-D1T4TGjf.js} +1 -1
  221. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMbl7MeQ.js → entity._sha_.edit._scenarioId-CTBG2mmz.js} +1 -1
  222. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-5wRKRIH9.js → entry.client-CS2cb_eZ.js} +1 -1
  223. package/codeyam-cli/src/webserver/build/client/assets/file-code-Dhef1kWN.js +6 -0
  224. package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DD3SDH7t.js → fileTableUtils-DMJ7zii9.js} +1 -1
  225. package/codeyam-cli/src/webserver/build/client/assets/files-CJ6lTdTA.js +1 -0
  226. package/codeyam-cli/src/webserver/build/client/assets/{git-zXjT7J0G.js → git-CPTZZ-JZ.js} +8 -8
  227. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +1 -0
  228. package/codeyam-cli/src/webserver/build/client/assets/{index-DLbXwndH.js → index-B1h680n5.js} +1 -1
  229. package/codeyam-cli/src/webserver/build/client/assets/{index-gPZ-lad1.js → index-lzqtyFU8.js} +1 -1
  230. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BsPXJ81F.js → loader-circle-B7B9V-bu.js} +1 -1
  231. package/codeyam-cli/src/webserver/build/client/assets/manifest-7522edd4.js +1 -0
  232. package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +92 -0
  233. package/codeyam-cli/src/webserver/build/client/assets/root-eVAaavTS.js +62 -0
  234. package/codeyam-cli/src/webserver/build/client/assets/{search-P2FKIUql.js → search-CxXUmBSd.js} +1 -1
  235. package/codeyam-cli/src/webserver/build/client/assets/{settings-B2eDuBj8.js → settings-CS5f3WzT.js} +1 -1
  236. package/codeyam-cli/src/webserver/build/client/assets/{simulations-L18M6-kN.js → simulations-DwFIBT09.js} +1 -1
  237. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BDz7kbVA.js → triangle-alert-B6LgvRJg.js} +1 -1
  238. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-29dDmbH8.js → useCustomSizes-C1v1PQzo.js} +1 -1
  239. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BUm0UVJm.js → useLastLogLine-aSv48UbS.js} +1 -1
  240. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-CkIOKTrZ.js → useReportContext-DYxHZQuP.js} +1 -1
  241. package/codeyam-cli/src/webserver/build/client/assets/{useToast-KKw5kTn-.js → useToast-mBRpZPiu.js} +1 -1
  242. package/codeyam-cli/src/webserver/build/server/assets/index-DVzYx8PN.js +1 -0
  243. package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +257 -0
  244. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  245. package/codeyam-cli/src/webserver/build-info.json +5 -5
  246. package/codeyam-cli/templates/codeyam-memory-hook.sh +200 -0
  247. package/codeyam-cli/templates/codeyam:debug.md +47 -3
  248. package/codeyam-cli/templates/codeyam:diagnose.md +203 -25
  249. package/codeyam-cli/templates/codeyam:memory.md +462 -0
  250. package/codeyam-cli/templates/codeyam:new-rule.md +13 -0
  251. package/package.json +8 -5
  252. package/packages/ai/index.js +5 -2
  253. package/packages/ai/index.js.map +1 -1
  254. package/packages/ai/src/lib/analyzeScope.js +41 -17
  255. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  256. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js +150 -0
  257. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js.map +1 -0
  258. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +24 -0
  259. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  260. package/packages/ai/src/lib/astScopes/methodSemantics.js +109 -23
  261. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  262. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js +1 -102
  263. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js.map +1 -1
  264. package/packages/ai/src/lib/astScopes/processExpression.js +228 -11
  265. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  266. package/packages/ai/src/lib/completionCall.js +161 -30
  267. package/packages/ai/src/lib/completionCall.js.map +1 -1
  268. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +86 -1
  269. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  270. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js +179 -0
  271. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js.map +1 -1
  272. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js +7 -1
  273. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js.map +1 -1
  274. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +19 -0
  275. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  276. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +70 -2
  277. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  278. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +29 -7
  279. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  280. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js +107 -0
  281. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js.map +1 -0
  282. package/packages/ai/src/lib/dataStructureChunking.js +111 -0
  283. package/packages/ai/src/lib/dataStructureChunking.js.map +1 -0
  284. package/packages/ai/src/lib/e2eDataTracking.js +241 -0
  285. package/packages/ai/src/lib/e2eDataTracking.js.map +1 -0
  286. package/packages/ai/src/lib/extractCriticalDataKeys.js +96 -0
  287. package/packages/ai/src/lib/extractCriticalDataKeys.js.map +1 -0
  288. package/packages/ai/src/lib/generateEntityScenarioData.js +510 -7
  289. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  290. package/packages/ai/src/lib/generateEntityScenarios.js +19 -1
  291. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  292. package/packages/ai/src/lib/generateExecutionFlows.js +273 -4
  293. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  294. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +738 -40
  295. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  296. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js +194 -0
  297. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js.map +1 -0
  298. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js +54 -0
  299. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js.map +1 -0
  300. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js +15 -7
  301. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js.map +1 -1
  302. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js +335 -0
  303. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js.map +1 -0
  304. package/packages/ai/src/lib/resolvePathToControllable.js +131 -27
  305. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  306. package/packages/ai/src/lib/worker/SerializableDataStructure.js +7 -0
  307. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  308. package/packages/ai/src/lib/worker/analyzeScopeWorker.js +94 -1
  309. package/packages/ai/src/lib/worker/analyzeScopeWorker.js.map +1 -1
  310. package/packages/analyze/src/lib/analysisContext.js +30 -5
  311. package/packages/analyze/src/lib/analysisContext.js.map +1 -1
  312. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +8 -4
  313. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  314. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js +5 -0
  315. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js.map +1 -1
  316. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +31 -7
  317. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  318. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +116 -66
  319. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  320. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +37 -5
  321. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  322. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +1 -1
  323. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  324. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +65 -0
  325. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  326. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js +46 -9
  327. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js.map +1 -1
  328. package/packages/database/src/lib/kysely/db.js +8 -1
  329. package/packages/database/src/lib/kysely/db.js.map +1 -1
  330. package/packages/database/src/lib/kysely/tables/commitsTable.js +3 -0
  331. package/packages/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  332. package/packages/database/src/lib/loadAnalyses.js +45 -2
  333. package/packages/database/src/lib/loadAnalyses.js.map +1 -1
  334. package/packages/database/src/lib/loadAnalysis.js +8 -0
  335. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  336. package/packages/database/src/lib/loadBranch.js +11 -1
  337. package/packages/database/src/lib/loadBranch.js.map +1 -1
  338. package/packages/database/src/lib/loadCommit.js +7 -0
  339. package/packages/database/src/lib/loadCommit.js.map +1 -1
  340. package/packages/database/src/lib/loadCommits.js +22 -1
  341. package/packages/database/src/lib/loadCommits.js.map +1 -1
  342. package/packages/database/src/lib/loadEntities.js +23 -4
  343. package/packages/database/src/lib/loadEntities.js.map +1 -1
  344. package/packages/database/src/lib/loadEntityBranches.js +9 -0
  345. package/packages/database/src/lib/loadEntityBranches.js.map +1 -1
  346. package/packages/database/src/lib/updateCommitMetadata.js +5 -4
  347. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  348. package/packages/types/index.js.map +1 -1
  349. package/packages/utils/src/lib/safeFileName.js +29 -3
  350. package/packages/utils/src/lib/safeFileName.js.map +1 -1
  351. package/scripts/finalize-analyzer.cjs +3 -3
  352. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-vauWK972.js +0 -1
  353. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-DzJRkCkr.js +0 -11
  354. package/codeyam-cli/src/webserver/build/client/assets/_index-Be83mo_j.js +0 -11
  355. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BN6wu6Y-.js +0 -37
  356. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Bn6aCAy_.js +0 -1
  357. package/codeyam-cli/src/webserver/build/client/assets/files-DKyMFI90.js +0 -1
  358. package/codeyam-cli/src/webserver/build/client/assets/globals-DTTQ3gY7.css +0 -1
  359. package/codeyam-cli/src/webserver/build/client/assets/manifest-22590fcf.js +0 -1
  360. package/codeyam-cli/src/webserver/build/client/assets/root-BsAarjAM.js +0 -57
  361. package/codeyam-cli/src/webserver/build/server/assets/index-BND5I5fv.js +0 -1
  362. package/codeyam-cli/src/webserver/build/server/assets/server-build-CFXnd7MG.js +0 -228
@@ -13,9 +13,15 @@
13
13
  */
14
14
 
15
15
  import type { ExecutionFlow } from '~codeyam/types';
16
- import type { ConditionalUsage, CompoundConditional } from './astScopes/types';
16
+ import type {
17
+ ConditionalUsage,
18
+ CompoundConditional,
19
+ DerivedVariableOperation,
20
+ DerivedVariableInfo,
21
+ } from './astScopes/types';
17
22
  import type { EnrichedConditionalUsage } from './worker/SerializableDataStructure';
18
23
  import resolvePathToControllable from './resolvePathToControllable';
24
+ import cleanPathOfNonTransformingFunctions from './dataStructure/helpers/cleanPathOfNonTransformingFunctions';
19
25
 
20
26
  /** Extended conditional usage type that may include sourceDataPath from enrichment */
21
27
  type ExtendedConditionalUsage = ConditionalUsage &
@@ -54,6 +60,158 @@ export interface GenerateFlowsFromConditionalsArgs {
54
60
  * Used to merge child execution flows into parent.
55
61
  */
56
62
  childComponentData?: Record<string, ChildComponentConditionalData>;
63
+ /**
64
+ * Optional map of derived variables to their derivation info.
65
+ * Used to trace intermediate derived variables that aren't directly
66
+ * used in conditionals but are sources for other derived variables.
67
+ *
68
+ * Example:
69
+ * - `isInCurrentRun = currentRun.entityShas.includes(sha)` → { sourcePath: 'currentRun.entityShas', operation: 'arrayIncludes' }
70
+ * - `isAnalyzing = isInCurrentRun || isInQueue` → { sourcePaths: ['isInCurrentRun', 'isInQueue'], operation: 'or' }
71
+ *
72
+ * Without this map, we can't trace from `isAnalyzing` through `isInCurrentRun`
73
+ * to `currentRun.entityShas` when `isInCurrentRun` isn't in conditionalUsages.
74
+ */
75
+ derivedVariables?: Record<string, DerivedVariableInfo>;
76
+ }
77
+
78
+ /**
79
+ * Resolved source info from expanding a derived variable.
80
+ * Contains the path and the operation that led to it.
81
+ */
82
+ interface ResolvedSourceInfo {
83
+ path: string;
84
+ operation?:
85
+ | 'notNull'
86
+ | 'isNull'
87
+ | 'equals'
88
+ | 'notEquals'
89
+ | 'or'
90
+ | 'and'
91
+ | 'arrayIncludes'
92
+ | 'arraySome'
93
+ | 'arrayEvery'
94
+ | 'arrayLength';
95
+ }
96
+
97
+ /**
98
+ * Recursively expands a derived variable to its leaf data sources.
99
+ *
100
+ * For OR expressions like `isAnalyzing = a || b || c`:
101
+ * - Returns all source paths [a, b, c] so they can all be set appropriately
102
+ *
103
+ * For nested derivations like `isAnalyzing = isInCurrentRun || isInQueue`:
104
+ * - Where `isInCurrentRun` is derived from `currentRun.entityShas.includes(x)`
105
+ * - Returns the final data sources: [currentRun.entityShas, queueState.jobs]
106
+ *
107
+ * @param path The variable path to expand
108
+ * @param conditionalUsages All conditional usages (to look up derivedFrom info)
109
+ * @param attributesMap Map of controllable paths
110
+ * @param equivalentSignatureVariables Variable-to-path mappings
111
+ * @param fullToShortPathMap Full-to-short path mappings
112
+ * @param visited Set of already-visited paths (prevents infinite recursion)
113
+ * @param derivedVariables Optional map of all derived variables (for intermediate tracing)
114
+ * @returns Array of resolved source paths that are controllable
115
+ */
116
+ function expandDerivedVariableToSources(
117
+ path: string,
118
+ conditionalUsages: Record<string, ConditionalUsage[]>,
119
+ attributesMap: Record<string, string>,
120
+ equivalentSignatureVariables: Record<string, string>,
121
+ fullToShortPathMap: Record<string, string>,
122
+ visited: Set<string> = new Set(),
123
+ derivedVariables?: Record<string, DerivedVariableInfo>,
124
+ ): ResolvedSourceInfo[] {
125
+ // Prevent infinite recursion
126
+ if (visited.has(path)) {
127
+ return [];
128
+ }
129
+ visited.add(path);
130
+
131
+ // First, check if this path is directly controllable
132
+ const directResolution = resolvePathToControllable(
133
+ path,
134
+ attributesMap,
135
+ equivalentSignatureVariables,
136
+ fullToShortPathMap,
137
+ );
138
+
139
+ if (directResolution.isControllable && directResolution.resolvedPath) {
140
+ return [{ path: directResolution.resolvedPath }];
141
+ }
142
+
143
+ // Look up derivedFrom info for this path
144
+ // First check conditionalUsages, then fall back to derivedVariables
145
+ const usage = conditionalUsages[path]?.[0];
146
+ let derivedFrom = usage?.derivedFrom;
147
+
148
+ // CRITICAL: If not found in conditionalUsages, check derivedVariables
149
+ // This handles intermediate derived variables like `isInCurrentRun` that aren't
150
+ // directly used in conditionals but ARE derived from data sources
151
+ if (!derivedFrom && derivedVariables?.[path]) {
152
+ derivedFrom = derivedVariables[path];
153
+ }
154
+
155
+ if (!derivedFrom) {
156
+ return [];
157
+ }
158
+
159
+ const { operation, sourcePath, sourcePaths } = derivedFrom;
160
+
161
+ // For OR/AND operations, recursively expand all source paths
162
+ if ((operation === 'or' || operation === 'and') && sourcePaths) {
163
+ const allSources: ResolvedSourceInfo[] = [];
164
+
165
+ for (const sp of sourcePaths) {
166
+ const expanded = expandDerivedVariableToSources(
167
+ sp,
168
+ conditionalUsages,
169
+ attributesMap,
170
+ equivalentSignatureVariables,
171
+ fullToShortPathMap,
172
+ visited,
173
+ derivedVariables,
174
+ );
175
+
176
+ // Add all expanded sources
177
+ for (const source of expanded) {
178
+ // Avoid duplicates
179
+ if (!allSources.some((s) => s.path === source.path)) {
180
+ allSources.push(source);
181
+ }
182
+ }
183
+ }
184
+
185
+ return allSources;
186
+ }
187
+
188
+ // For single-source operations (arrayIncludes, arraySome, notNull, etc.)
189
+ if (sourcePath) {
190
+ // Try to resolve the source path directly
191
+ const sourceResolution = resolvePathToControllable(
192
+ sourcePath,
193
+ attributesMap,
194
+ equivalentSignatureVariables,
195
+ fullToShortPathMap,
196
+ );
197
+
198
+ if (sourceResolution.isControllable && sourceResolution.resolvedPath) {
199
+ return [{ path: sourceResolution.resolvedPath, operation }];
200
+ }
201
+
202
+ // If not directly resolvable, recursively expand
203
+ return expandDerivedVariableToSources(
204
+ sourcePath,
205
+ conditionalUsages,
206
+ attributesMap,
207
+ equivalentSignatureVariables,
208
+ fullToShortPathMap,
209
+ visited,
210
+ derivedVariables,
211
+ );
212
+ }
213
+
214
+ return [];
57
215
  }
58
216
 
59
217
  /**
@@ -113,6 +271,52 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
113
271
  return actualPath;
114
272
  }
115
273
 
274
+ /**
275
+ * Strip .length suffix from a path if present.
276
+ *
277
+ * When we have a path like "items.length", the controllable attribute is "items"
278
+ * (the array), not "items.length". The length is derived from the array contents.
279
+ *
280
+ * This ensures that execution flows reference the actual controllable attribute
281
+ * rather than the derived .length property.
282
+ */
283
+ function stripLengthSuffix(path: string): string {
284
+ if (path.endsWith('.length')) {
285
+ return path.slice(0, -7); // Remove ".length" (7 characters)
286
+ }
287
+ return path;
288
+ }
289
+
290
+ /**
291
+ * Extract the controllable base path from a path that may contain method calls.
292
+ *
293
+ * This handles complex expressions like:
294
+ * - `scenarios.filter((s) => s.active).length` → `scenarios`
295
+ * - `users.some((u) => u.role === 'admin')` → `users`
296
+ * - `items.map(x => x.name).join(', ')` → `items`
297
+ *
298
+ * The controllable base is the path that can be mocked - we can control
299
+ * what `scenarios` contains, but we can't control what `.filter()` returns.
300
+ *
301
+ * @param path - The path that may contain method calls
302
+ * @returns The controllable base path with method calls stripped
303
+ */
304
+ function extractControllableBase(path: string): string {
305
+ // First strip .length suffix if present
306
+ const pathWithoutLength = stripLengthSuffix(path);
307
+
308
+ // Use cleanPathOfNonTransformingFunctions to strip method calls like .filter(), .some()
309
+ const cleanedPath = cleanPathOfNonTransformingFunctions(pathWithoutLength);
310
+
311
+ // If the cleaned path is different, return it
312
+ if (cleanedPath !== pathWithoutLength) {
313
+ return cleanedPath;
314
+ }
315
+
316
+ // Otherwise, return the path with just .length stripped
317
+ return pathWithoutLength;
318
+ }
319
+
116
320
  /**
117
321
  * Find a path in attributesMap, using fullToShortPathMap to verify the path is controllable.
118
322
  *
@@ -244,6 +448,12 @@ function inferValueType(
244
448
  /**
245
449
  * Generate flows from a single conditional usage.
246
450
  * Sets impact to 'high' if the conditional controls JSX rendering.
451
+ *
452
+ * When the usage has a `constraintExpression`, it represents a complex expression
453
+ * that can't be simply resolved (e.g., `scenarios.filter(x => x.active).length > 1`).
454
+ * In this case:
455
+ * - `attributePath` is set to the controllable base (e.g., `scenarios`)
456
+ * - `constraint` is set to the full expression for LLM reasoning
247
457
  */
248
458
  function generateFlowsFromUsage(
249
459
  usage: ConditionalUsage,
@@ -258,6 +468,14 @@ function generateFlowsFromUsage(
258
468
  ? 'high'
259
469
  : 'medium';
260
470
 
471
+ // When there's a constraintExpression, use the controllable base for attributePath
472
+ // and pass through the constraint for LLM reasoning
473
+ const hasConstraint = !!usage.constraintExpression;
474
+ const attributePath = hasConstraint
475
+ ? extractControllableBase(resolvedPath)
476
+ : stripLengthSuffix(resolvedPath);
477
+ const constraint = usage.constraintExpression;
478
+
261
479
  if (usage.conditionType === 'truthiness') {
262
480
  // Generate both truthy and falsy flows
263
481
  const isNegated = usage.isNegated ?? false;
@@ -269,10 +487,11 @@ function generateFlowsFromUsage(
269
487
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'falsy' : 'truthy'}`,
270
488
  requiredValues: [
271
489
  {
272
- attributePath: resolvedPath,
490
+ attributePath,
273
491
  value: isNegated ? 'falsy' : 'truthy',
274
492
  comparison: isNegated ? 'falsy' : 'truthy',
275
493
  valueType: 'boolean',
494
+ constraint,
276
495
  },
277
496
  ],
278
497
  impact,
@@ -292,10 +511,11 @@ function generateFlowsFromUsage(
292
511
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'truthy' : 'falsy'}`,
293
512
  requiredValues: [
294
513
  {
295
- attributePath: resolvedPath,
514
+ attributePath,
296
515
  value: isNegated ? 'truthy' : 'falsy',
297
516
  comparison: isNegated ? 'truthy' : 'falsy',
298
517
  valueType: 'boolean',
518
+ constraint,
299
519
  },
300
520
  ],
301
521
  impact,
@@ -321,10 +541,11 @@ function generateFlowsFromUsage(
321
541
  description: `When ${baseName.toLowerCase()} equals "${value}"`,
322
542
  requiredValues: [
323
543
  {
324
- attributePath: resolvedPath,
544
+ attributePath,
325
545
  value: value,
326
546
  comparison: 'equals',
327
547
  valueType: inferValueType(value),
548
+ constraint,
328
549
  },
329
550
  ],
330
551
  impact,
@@ -381,7 +602,7 @@ function generateFlowFromCompound(
381
602
  }
382
603
 
383
604
  requiredValues.push({
384
- attributePath: resolvedPath,
605
+ attributePath: stripLengthSuffix(resolvedPath),
385
606
  value,
386
607
  comparison,
387
608
  valueType: inferValueType(value),
@@ -412,6 +633,60 @@ function generateFlowFromCompound(
412
633
  };
413
634
  }
414
635
 
636
+ /**
637
+ * Expand a compound conditional with OR groups into multiple condition sets.
638
+ *
639
+ * For a compound like `A && (B || C)`:
640
+ * - Conditions: [{ path: 'A' }, { path: 'B', orGroupId: 'or_xxx' }, { path: 'C', orGroupId: 'or_xxx' }]
641
+ * - Returns: [[A, B], [A, C]] - two sets of conditions
642
+ *
643
+ * For multiple OR groups like `A && (B || C) && (D || E)`:
644
+ * - Returns: [[A, B, D], [A, B, E], [A, C, D], [A, C, E]]
645
+ */
646
+ function expandOrGroups(
647
+ conditions: CompoundConditional['conditions'],
648
+ ): CompoundConditional['conditions'][] {
649
+ // Separate conditions into mandatory (no orGroupId) and OR groups
650
+ const mandatory = conditions.filter((c) => !c.orGroupId);
651
+ const orGroups = new Map<string, typeof conditions>();
652
+
653
+ for (const condition of conditions) {
654
+ if (condition.orGroupId) {
655
+ const group = orGroups.get(condition.orGroupId) ?? [];
656
+ group.push(condition);
657
+ orGroups.set(condition.orGroupId, group);
658
+ }
659
+ }
660
+
661
+ // If no OR groups, return the original conditions
662
+ if (orGroups.size === 0) {
663
+ return [conditions];
664
+ }
665
+
666
+ // Generate all combinations by picking one condition from each OR group
667
+ const groupArrays = Array.from(orGroups.values());
668
+ const combinations: CompoundConditional['conditions'][] = [];
669
+
670
+ function generateCombinations(
671
+ index: number,
672
+ current: typeof conditions,
673
+ ): void {
674
+ if (index === groupArrays.length) {
675
+ // We've picked one from each OR group - combine with mandatory conditions
676
+ combinations.push([...mandatory, ...current]);
677
+ return;
678
+ }
679
+
680
+ // Pick each option from the current OR group
681
+ for (const option of groupArrays[index]) {
682
+ generateCombinations(index + 1, [...current, option]);
683
+ }
684
+ }
685
+
686
+ generateCombinations(0, []);
687
+ return combinations;
688
+ }
689
+
415
690
  /**
416
691
  * Generate execution flows from conditional usages using pure static analysis.
417
692
  *
@@ -524,6 +799,7 @@ export default function generateExecutionFlowsFromConditionals(
524
799
  equivalentSignatureVariables,
525
800
  fullToShortPathMap,
526
801
  childComponentData,
802
+ derivedVariables,
527
803
  } = args;
528
804
 
529
805
  const flows: ExecutionFlow[] = [];
@@ -591,7 +867,8 @@ export default function generateExecutionFlowsFromConditionals(
591
867
  // This handles cases like: const hasAnalysis = analysis !== null
592
868
  // where hasAnalysis is not in attributesMap but analysis is
593
869
  if (!resolvedPath && usage.derivedFrom) {
594
- const { sourcePath, sourcePaths } = usage.derivedFrom;
870
+ const { operation, sourcePath, sourcePaths, comparedValue } =
871
+ usage.derivedFrom;
595
872
 
596
873
  // For single-source derivations (notNull, equals, etc.)
597
874
  if (sourcePath) {
@@ -607,8 +884,277 @@ export default function generateExecutionFlowsFromConditionals(
607
884
  }
608
885
  }
609
886
 
610
- // For multi-source derivations (or, and), try the first resolvable path
611
- // This is a simplification - ideally we'd generate flows for each source
887
+ // For equals/notEquals derivations with comparedValue, generate comparison flows
888
+ // e.g., canEdit derived from user.role === 'admin'
889
+ // When canEdit is used in truthiness check, we need:
890
+ // - Truthy flow: user.role = 'admin' (comparison: 'equals')
891
+ // - Falsy flow: user.role != 'admin' (comparison: 'notEquals')
892
+ if (
893
+ (operation === 'equals' || operation === 'notEquals') &&
894
+ comparedValue !== undefined &&
895
+ resolvedPath &&
896
+ usage.conditionType === 'truthiness'
897
+ ) {
898
+ const baseName = generateNameFromPath(usage.path);
899
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
900
+ ? 'high'
901
+ : 'medium';
902
+
903
+ const isNegated = usage.isNegated ?? false;
904
+
905
+ // For equals derivation:
906
+ // - Truthy check (!negated): needs value = comparedValue (equals)
907
+ // - Falsy check (negated): needs value != comparedValue (notEquals)
908
+ // For notEquals derivation: inverse of above
909
+ const isEqualsDerivation = operation === 'equals';
910
+ const truthyNeedsEquals = isEqualsDerivation !== isNegated;
911
+
912
+ // Generate truthy flow
913
+ const truthyFlow: ExecutionFlow = {
914
+ id: generateFlowId(usage.path, 'truthy'),
915
+ name: `${baseName} True`,
916
+ description: `When ${baseName.toLowerCase()} is truthy (${resolvedPath} ${truthyNeedsEquals ? '=' : '!='} ${comparedValue})`,
917
+ requiredValues: [
918
+ {
919
+ attributePath: stripLengthSuffix(resolvedPath),
920
+ value: comparedValue,
921
+ comparison: truthyNeedsEquals ? 'equals' : 'notEquals',
922
+ valueType: inferValueType(comparedValue),
923
+ },
924
+ ],
925
+ impact,
926
+ sourceLocation: usage.sourceLocation
927
+ ? {
928
+ lineNumber: usage.sourceLocation.lineNumber,
929
+ column: usage.sourceLocation.column,
930
+ }
931
+ : undefined,
932
+ codeSnippet: usage.sourceLocation?.codeSnippet,
933
+ };
934
+
935
+ // Generate falsy flow
936
+ const falsyFlow: ExecutionFlow = {
937
+ id: generateFlowId(usage.path, 'falsy'),
938
+ name: `${baseName} False`,
939
+ description: `When ${baseName.toLowerCase()} is falsy (${resolvedPath} ${truthyNeedsEquals ? '!=' : '='} ${comparedValue})`,
940
+ requiredValues: [
941
+ {
942
+ attributePath: stripLengthSuffix(resolvedPath),
943
+ value: comparedValue,
944
+ comparison: truthyNeedsEquals ? 'notEquals' : 'equals',
945
+ valueType: inferValueType(comparedValue),
946
+ },
947
+ ],
948
+ impact,
949
+ sourceLocation: usage.sourceLocation
950
+ ? {
951
+ lineNumber: usage.sourceLocation.lineNumber,
952
+ column: usage.sourceLocation.column,
953
+ }
954
+ : undefined,
955
+ codeSnippet: usage.sourceLocation?.codeSnippet,
956
+ };
957
+
958
+ // Add flows and skip normal flow generation
959
+ if (!seenFlowIds.has(truthyFlow.id)) {
960
+ seenFlowIds.add(truthyFlow.id);
961
+ flows.push(truthyFlow);
962
+ }
963
+ if (!seenFlowIds.has(falsyFlow.id)) {
964
+ seenFlowIds.add(falsyFlow.id);
965
+ flows.push(falsyFlow);
966
+ }
967
+
968
+ continue;
969
+ }
970
+
971
+ // For OR derivations with negation, we need ALL sources to be falsy
972
+ // e.g., !isBusy where isBusy = isRunning || isQueued || isPending
973
+ // For the falsy flow, ALL sources must be falsy
974
+ if (
975
+ operation === 'or' &&
976
+ usage.conditionType === 'truthiness' &&
977
+ usage.isNegated === true &&
978
+ sourcePaths &&
979
+ sourcePaths.length > 0
980
+ ) {
981
+ // Use expandDerivedVariableToSources to recursively resolve all sources
982
+ const allSources = expandDerivedVariableToSources(
983
+ usage.path,
984
+ conditionalUsages,
985
+ attributesMap,
986
+ equivalentSignatureVariables,
987
+ fullToShortPathMap,
988
+ new Set(),
989
+ derivedVariables,
990
+ );
991
+
992
+ if (allSources.length > 0) {
993
+ // Generate a compound-style flow with all sources set to falsy
994
+ const baseName = generateNameFromPath(usage.path);
995
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
996
+ ? 'high'
997
+ : 'medium';
998
+
999
+ const requiredValues: ExecutionFlow['requiredValues'] =
1000
+ allSources.map((source) => ({
1001
+ attributePath: source.path,
1002
+ value: 'falsy',
1003
+ comparison: 'falsy' as const,
1004
+ valueType: 'boolean' as const,
1005
+ }));
1006
+
1007
+ // Create a single falsy flow with all sources
1008
+ const falsyFlow: ExecutionFlow = {
1009
+ id: generateFlowId(usage.path, 'falsy'),
1010
+ name: `${baseName} False`,
1011
+ description: `When ${baseName.toLowerCase()} is falsy (all sources are falsy)`,
1012
+ requiredValues,
1013
+ impact,
1014
+ sourceLocation: usage.sourceLocation
1015
+ ? {
1016
+ lineNumber: usage.sourceLocation.lineNumber,
1017
+ column: usage.sourceLocation.column,
1018
+ }
1019
+ : undefined,
1020
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1021
+ };
1022
+
1023
+ // Create a truthy flow - for OR, ANY source being truthy is sufficient
1024
+ // We use the first resolvable source for the truthy flow
1025
+ const firstSource = allSources[0];
1026
+ const truthyFlow: ExecutionFlow = {
1027
+ id: generateFlowId(usage.path, 'truthy'),
1028
+ name: `${baseName} True`,
1029
+ description: `When ${baseName.toLowerCase()} is truthy`,
1030
+ requiredValues: [
1031
+ {
1032
+ attributePath: firstSource.path,
1033
+ value: 'truthy',
1034
+ comparison: 'truthy',
1035
+ valueType: 'boolean',
1036
+ },
1037
+ ],
1038
+ impact,
1039
+ sourceLocation: usage.sourceLocation
1040
+ ? {
1041
+ lineNumber: usage.sourceLocation.lineNumber,
1042
+ column: usage.sourceLocation.column,
1043
+ }
1044
+ : undefined,
1045
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1046
+ };
1047
+
1048
+ // Add both flows (falsy needs all sources, truthy needs one)
1049
+ if (!seenFlowIds.has(falsyFlow.id)) {
1050
+ seenFlowIds.add(falsyFlow.id);
1051
+ flows.push(falsyFlow);
1052
+ }
1053
+ if (!seenFlowIds.has(truthyFlow.id)) {
1054
+ seenFlowIds.add(truthyFlow.id);
1055
+ flows.push(truthyFlow);
1056
+ }
1057
+
1058
+ // Skip the normal flow generation for this usage
1059
+ continue;
1060
+ }
1061
+ }
1062
+
1063
+ // For AND derivations without negation, we need ALL sources to be truthy
1064
+ // e.g., isReady where isReady = hasData && isLoaded && isValid
1065
+ // For the truthy flow, ALL sources must be truthy
1066
+ // For negated AND (!isReady), ANY source being falsy is sufficient (fallback behavior)
1067
+ if (
1068
+ operation === 'and' &&
1069
+ usage.conditionType === 'truthiness' &&
1070
+ usage.isNegated !== true &&
1071
+ sourcePaths &&
1072
+ sourcePaths.length > 0
1073
+ ) {
1074
+ // Use expandDerivedVariableToSources to recursively resolve all sources
1075
+ const allSources = expandDerivedVariableToSources(
1076
+ usage.path,
1077
+ conditionalUsages,
1078
+ attributesMap,
1079
+ equivalentSignatureVariables,
1080
+ fullToShortPathMap,
1081
+ new Set(),
1082
+ derivedVariables,
1083
+ );
1084
+
1085
+ if (allSources.length > 0) {
1086
+ // Generate a compound-style flow with all sources set to truthy
1087
+ const baseName = generateNameFromPath(usage.path);
1088
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
1089
+ ? 'high'
1090
+ : 'medium';
1091
+
1092
+ const requiredValues: ExecutionFlow['requiredValues'] =
1093
+ allSources.map((source) => ({
1094
+ attributePath: source.path,
1095
+ value: 'truthy',
1096
+ comparison: 'truthy' as const,
1097
+ valueType: 'boolean' as const,
1098
+ }));
1099
+
1100
+ // Create a truthy flow with all sources
1101
+ const truthyFlow: ExecutionFlow = {
1102
+ id: generateFlowId(usage.path, 'truthy'),
1103
+ name: `${baseName} True`,
1104
+ description: `When ${baseName.toLowerCase()} is truthy (all sources are truthy)`,
1105
+ requiredValues,
1106
+ impact,
1107
+ sourceLocation: usage.sourceLocation
1108
+ ? {
1109
+ lineNumber: usage.sourceLocation.lineNumber,
1110
+ column: usage.sourceLocation.column,
1111
+ }
1112
+ : undefined,
1113
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1114
+ };
1115
+
1116
+ // Create a falsy flow - for AND, ANY source being falsy is sufficient
1117
+ // We use the first resolvable source for the falsy flow
1118
+ const firstSource = allSources[0];
1119
+ const falsyFlow: ExecutionFlow = {
1120
+ id: generateFlowId(usage.path, 'falsy'),
1121
+ name: `${baseName} False`,
1122
+ description: `When ${baseName.toLowerCase()} is falsy`,
1123
+ requiredValues: [
1124
+ {
1125
+ attributePath: firstSource.path,
1126
+ value: 'falsy',
1127
+ comparison: 'falsy',
1128
+ valueType: 'boolean',
1129
+ },
1130
+ ],
1131
+ impact,
1132
+ sourceLocation: usage.sourceLocation
1133
+ ? {
1134
+ lineNumber: usage.sourceLocation.lineNumber,
1135
+ column: usage.sourceLocation.column,
1136
+ }
1137
+ : undefined,
1138
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1139
+ };
1140
+
1141
+ // Add both flows (truthy needs all sources, falsy needs one)
1142
+ if (!seenFlowIds.has(truthyFlow.id)) {
1143
+ seenFlowIds.add(truthyFlow.id);
1144
+ flows.push(truthyFlow);
1145
+ }
1146
+ if (!seenFlowIds.has(falsyFlow.id)) {
1147
+ seenFlowIds.add(falsyFlow.id);
1148
+ flows.push(falsyFlow);
1149
+ }
1150
+
1151
+ // Skip the normal flow generation for this usage
1152
+ continue;
1153
+ }
1154
+ }
1155
+
1156
+ // For multi-source derivations (or, and) without special handling,
1157
+ // try the first resolvable path as a fallback
612
1158
  if (!resolvedPath && sourcePaths && sourcePaths.length > 0) {
613
1159
  for (const sp of sourcePaths) {
614
1160
  const resolution = resolvePathToControllable(
@@ -661,32 +1207,203 @@ export default function generateExecutionFlowsFromConditionals(
661
1207
 
662
1208
  // Process compound conditionals
663
1209
  for (const compound of compoundConditionals) {
664
- // First, check if ALL paths in this compound are controllable
665
- const resolvedPaths = new Map<string, string>();
666
- let allControllable = true;
1210
+ // Expand OR groups into separate condition sets
1211
+ // For example, `A && (B || C)` becomes [[A, B], [A, C]]
1212
+ const expandedConditionSets = expandOrGroups(compound.conditions);
1213
+
1214
+ // Process each expanded condition set as a separate potential flow
1215
+ for (const conditionSet of expandedConditionSets) {
1216
+ // First, check if ALL paths in this condition set are controllable (or can be expanded to controllable sources)
1217
+ const resolvedPaths = new Map<string, string>();
1218
+ // Track expanded sources for derived variables (path -> array of expanded sources)
1219
+ const expandedSources = new Map<
1220
+ string,
1221
+ Array<{ path: string; operation?: DerivedVariableOperation }>
1222
+ >();
1223
+ let allControllable = true;
1224
+
1225
+ for (const condition of conditionSet) {
1226
+ // Check if this condition path has derivation info
1227
+ // First check conditionalUsages, then fall back to derivedVariables
1228
+ const usagesForPath = conditionalUsages[condition.path];
1229
+ let derivedFromInfo = usagesForPath?.find(
1230
+ (u) => u.derivedFrom?.operation,
1231
+ )?.derivedFrom;
1232
+
1233
+ // CRITICAL: Also check derivedVariables for intermediate derived variables
1234
+ if (!derivedFromInfo && derivedVariables?.[condition.path]) {
1235
+ derivedFromInfo = derivedVariables[condition.path];
1236
+ }
667
1237
 
668
- for (const condition of compound.conditions) {
669
- const resolution = resolvePathToControllable(
670
- condition.path,
671
- attributesMap,
672
- equivalentSignatureVariables,
673
- fullToShortPathMap,
674
- );
1238
+ if (derivedFromInfo) {
1239
+ // This is a derived variable - expand to its sources
1240
+ const sources = expandDerivedVariableToSources(
1241
+ condition.path,
1242
+ conditionalUsages,
1243
+ attributesMap,
1244
+ equivalentSignatureVariables,
1245
+ fullToShortPathMap,
1246
+ new Set(),
1247
+ derivedVariables,
1248
+ );
1249
+
1250
+ if (sources.length > 0) {
1251
+ // Store the expanded sources for this condition
1252
+ expandedSources.set(condition.path, sources);
1253
+ // Use the first source's path for the resolvedPaths map (for ID generation)
1254
+ resolvedPaths.set(condition.path, sources[0].path);
1255
+ } else {
1256
+ // Derived variable but no controllable sources found
1257
+ allControllable = false;
1258
+ break;
1259
+ }
1260
+ } else {
1261
+ // Not a derived variable - resolve directly
1262
+ const resolution = resolvePathToControllable(
1263
+ condition.path,
1264
+ attributesMap,
1265
+ equivalentSignatureVariables,
1266
+ fullToShortPathMap,
1267
+ );
1268
+
1269
+ if (!resolution.isControllable || !resolution.resolvedPath) {
1270
+ allControllable = false;
1271
+ break;
1272
+ }
675
1273
 
676
- if (!resolution.isControllable || !resolution.resolvedPath) {
677
- allControllable = false;
678
- break;
1274
+ resolvedPaths.set(condition.path, resolution.resolvedPath);
1275
+ }
679
1276
  }
680
1277
 
681
- resolvedPaths.set(condition.path, resolution.resolvedPath);
682
- }
1278
+ // Only create a flow if ALL paths are controllable
1279
+ if (allControllable && resolvedPaths.size > 0) {
1280
+ // If any conditions were expanded from derived variables, we need to build a custom flow
1281
+ if (expandedSources.size > 0) {
1282
+ const requiredValues: ExecutionFlow['requiredValues'] = [];
1283
+
1284
+ for (const condition of conditionSet) {
1285
+ const sources = expandedSources.get(condition.path);
1286
+
1287
+ if (sources) {
1288
+ // This condition was expanded - add all its sources
1289
+ // Determine the required value based on condition type and derivation operation
1290
+ const usagesForPath = conditionalUsages[condition.path];
1291
+ let expandedDerivedFrom = usagesForPath?.find(
1292
+ (u) => u.derivedFrom?.operation,
1293
+ )?.derivedFrom;
1294
+
1295
+ // Also check derivedVariables for intermediate derived variables
1296
+ if (!expandedDerivedFrom && derivedVariables?.[condition.path]) {
1297
+ expandedDerivedFrom = derivedVariables[condition.path];
1298
+ }
1299
+ const operation = expandedDerivedFrom?.operation;
1300
+
1301
+ for (const source of sources) {
1302
+ // For OR-derived truthy: ANY source truthy
1303
+ // For AND-derived truthy: ALL sources truthy
1304
+ // For negated: inverse
1305
+ let value: string;
1306
+ let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
1307
+
1308
+ if (condition.conditionType === 'truthiness') {
1309
+ const isNegated = condition.isNegated === true;
1310
+ // For OR: truthy needs ANY source truthy, falsy needs ALL sources falsy
1311
+ // For AND: truthy needs ALL sources truthy, falsy needs ANY source falsy
1312
+ // In compound conditionals, we generate the truthy path by default
1313
+ // (the compound expression must be truthy)
1314
+ if (operation === 'or') {
1315
+ // For OR-derived, truthy means we need at least one source truthy
1316
+ // We'll use the first source as truthy (simplification)
1317
+ value = isNegated ? 'falsy' : 'truthy';
1318
+ } else if (operation === 'and') {
1319
+ // For AND-derived, truthy means ALL sources truthy
1320
+ value = isNegated ? 'falsy' : 'truthy';
1321
+ } else {
1322
+ value = isNegated ? 'falsy' : 'truthy';
1323
+ }
1324
+ comparison = isNegated ? 'falsy' : 'truthy';
1325
+ } else {
1326
+ value = 'truthy';
1327
+ comparison = 'truthy';
1328
+ }
1329
+
1330
+ requiredValues.push({
1331
+ attributePath: source.path,
1332
+ value,
1333
+ comparison,
1334
+ valueType: 'boolean' as const,
1335
+ });
1336
+ }
1337
+ } else {
1338
+ // This condition was resolved directly
1339
+ const resolvedPath = resolvedPaths.get(condition.path);
1340
+ if (resolvedPath) {
1341
+ let value: string;
1342
+ let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
1343
+
1344
+ if (condition.conditionType === 'truthiness') {
1345
+ value = condition.isNegated ? 'falsy' : 'truthy';
1346
+ comparison = condition.isNegated ? 'falsy' : 'truthy';
1347
+ } else {
1348
+ value =
1349
+ condition.requiredValue?.toString() ??
1350
+ condition.comparedValues?.[0] ??
1351
+ 'truthy';
1352
+ comparison = 'equals';
1353
+ }
1354
+
1355
+ requiredValues.push({
1356
+ attributePath: stripLengthSuffix(resolvedPath),
1357
+ value,
1358
+ comparison,
1359
+ valueType: inferValueType(value),
1360
+ });
1361
+ }
1362
+ }
1363
+ }
683
1364
 
684
- // Only create a flow if ALL paths are controllable
685
- if (allControllable && resolvedPaths.size > 0) {
686
- const compoundFlow = generateFlowFromCompound(compound, resolvedPaths);
687
- if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
688
- seenFlowIds.add(compoundFlow.id);
689
- flows.push(compoundFlow);
1365
+ if (requiredValues.length > 0) {
1366
+ const impact: ExecutionFlow['impact'] =
1367
+ compound.controlsJsxRendering ? 'high' : 'medium';
1368
+
1369
+ // Generate a combined ID from all paths
1370
+ const pathParts = requiredValues
1371
+ .map((rv) => {
1372
+ const name = generateNameFromPath(rv.attributePath);
1373
+ return name.toLowerCase().replace(/\s+/g, '-');
1374
+ })
1375
+ .join('-and-');
1376
+
1377
+ const compoundFlow: ExecutionFlow = {
1378
+ id: `${pathParts}-${requiredValues.map((rv) => rv.value).join('-')}`,
1379
+ name: generateNameFromPath(requiredValues[0].attributePath),
1380
+ description: `When ${requiredValues.map((rv) => `${generateNameFromPath(rv.attributePath).toLowerCase()} is ${rv.value}`).join(' and ')}`,
1381
+ impact,
1382
+ requiredValues,
1383
+ sourceLocation: compound.sourceLocation,
1384
+ };
1385
+
1386
+ if (!seenFlowIds.has(compoundFlow.id)) {
1387
+ seenFlowIds.add(compoundFlow.id);
1388
+ flows.push(compoundFlow);
1389
+ }
1390
+ }
1391
+ } else {
1392
+ // No derived variables - use the original generateFlowFromCompound
1393
+ // Create a modified compound with just this condition set
1394
+ const modifiedCompound = {
1395
+ ...compound,
1396
+ conditions: conditionSet,
1397
+ };
1398
+ const compoundFlow = generateFlowFromCompound(
1399
+ modifiedCompound,
1400
+ resolvedPaths,
1401
+ );
1402
+ if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
1403
+ seenFlowIds.add(compoundFlow.id);
1404
+ flows.push(compoundFlow);
1405
+ }
1406
+ }
690
1407
  }
691
1408
  }
692
1409
  }
@@ -733,7 +1450,18 @@ export default function generateExecutionFlowsFromConditionals(
733
1450
  fullToShortPathMap,
734
1451
  );
735
1452
 
1453
+ // Only use controllable paths for gating conditions (whitelist approach).
1454
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1455
+ // uncontrollable paths like useState that cannot be mocked.
1456
+ let resolvedVarPath: string | null = null;
736
1457
  if (varResolution.isControllable && varResolution.resolvedPath) {
1458
+ resolvedVarPath = varResolution.resolvedPath;
1459
+ }
1460
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1461
+ // because that would allow uncontrollable paths (like useState) to be added
1462
+ // as gating conditions.
1463
+
1464
+ if (resolvedVarPath) {
737
1465
  const isNegated = (gatingCondition as any).isNegated === true;
738
1466
  const isNotEquals = operator === '!=' || operator === '!==';
739
1467
  // Determine the effective value for this gating condition
@@ -744,7 +1472,7 @@ export default function generateExecutionFlowsFromConditionals(
744
1472
  const needsExactValue = isNegated !== isNotEquals;
745
1473
 
746
1474
  gatingRequiredValues.push({
747
- attributePath: varResolution.resolvedPath,
1475
+ attributePath: resolvedVarPath,
748
1476
  value: needsExactValue ? 'falsy' : comparedValue,
749
1477
  comparison: needsExactValue ? 'falsy' : 'equals',
750
1478
  });
@@ -810,7 +1538,9 @@ export default function generateExecutionFlowsFromConditionals(
810
1538
  // For non-negated &&: all parts must be truthy
811
1539
  // For negated ||: all parts must be falsy (DeMorgan: !(A || B) = !A && !B)
812
1540
  gatingRequiredValues.push({
813
- attributePath: partResolution.resolvedPath,
1541
+ attributePath: stripLengthSuffix(
1542
+ partResolution.resolvedPath,
1543
+ ),
814
1544
  value: isNegated ? 'falsy' : 'truthy',
815
1545
  comparison: isNegated ? 'falsy' : 'truthy',
816
1546
  });
@@ -827,16 +1557,27 @@ export default function generateExecutionFlowsFromConditionals(
827
1557
  fullToShortPathMap,
828
1558
  );
829
1559
 
1560
+ // Only use controllable paths for gating conditions (whitelist approach).
1561
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1562
+ // uncontrollable paths like useState that cannot be mocked.
1563
+ let resolvedGatingPath: string | null = null;
830
1564
  if (
831
1565
  gatingResolution.isControllable &&
832
1566
  gatingResolution.resolvedPath
833
1567
  ) {
1568
+ resolvedGatingPath = gatingResolution.resolvedPath;
1569
+ }
1570
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1571
+ // because that would allow uncontrollable paths (like useState) to be added
1572
+ // as gating conditions.
1573
+
1574
+ if (resolvedGatingPath) {
834
1575
  // For truthiness conditions on gating, check if the condition is negated
835
1576
  // e.g., ternary else branch: isError ? <ErrorView /> : <SuccessView />
836
1577
  // SuccessView has isNegated: true, meaning it renders when isError is falsy
837
1578
  const isNegated = (gatingCondition as any).isNegated === true;
838
1579
  gatingRequiredValues.push({
839
- attributePath: gatingResolution.resolvedPath,
1580
+ attributePath: resolvedGatingPath,
840
1581
  value: isNegated ? 'falsy' : 'truthy',
841
1582
  comparison: isNegated ? 'falsy' : 'truthy',
842
1583
  });
@@ -855,6 +1596,9 @@ export default function generateExecutionFlowsFromConditionals(
855
1596
  childData.conditionalUsages,
856
1597
  )) {
857
1598
  for (const usage of usages) {
1599
+ // Debug logging (disabled - set to true for troubleshooting child flow resolution)
1600
+ const shouldDebugChild = false;
1601
+
858
1602
  // Skip usages that are part of compound conditionals (handled separately)
859
1603
  // Fix 33: Only skip if the chainId is in the child's compound conditionals
860
1604
  if (usage.chainId && childCompoundChainIds.has(usage.chainId)) {
@@ -869,6 +1613,18 @@ export default function generateExecutionFlowsFromConditionals(
869
1613
  childPath = usage.derivedFrom.sourcePath;
870
1614
  }
871
1615
 
1616
+ if (shouldDebugChild) {
1617
+ console.log(
1618
+ `[DEBUG CHILD ${childName}] Processing usage path: ${usage.path}`,
1619
+ );
1620
+ console.log(
1621
+ `[DEBUG CHILD ${childName}] childPath (after derivedFrom): ${childPath}`,
1622
+ );
1623
+ console.log(
1624
+ `[DEBUG CHILD ${childName}] sourceDataPath: ${usage.sourceDataPath}`,
1625
+ );
1626
+ }
1627
+
872
1628
  // Translate the child path to a parent path
873
1629
  let translatedPath = translateChildPathToParent(
874
1630
  childPath,
@@ -877,6 +1633,12 @@ export default function generateExecutionFlowsFromConditionals(
877
1633
  childName,
878
1634
  );
879
1635
 
1636
+ if (shouldDebugChild) {
1637
+ console.log(
1638
+ `[DEBUG CHILD ${childName}] translatedPath (from translateChildPathToParent): ${translatedPath}`,
1639
+ );
1640
+ }
1641
+
880
1642
  // If translation failed but we have sourceDataPath, try to extract the prop path from it
881
1643
  // sourceDataPath format: "ChildName.signature[n].propPath.rest" → extract "propPath.rest"
882
1644
  if (!translatedPath && usage.sourceDataPath) {
@@ -885,15 +1647,26 @@ export default function generateExecutionFlowsFromConditionals(
885
1647
  );
886
1648
  if (signatureMatch) {
887
1649
  translatedPath = signatureMatch[1];
1650
+ if (shouldDebugChild) {
1651
+ console.log(
1652
+ `[DEBUG CHILD ${childName}] translatedPath (from sourceDataPath regex): ${translatedPath}`,
1653
+ );
1654
+ }
888
1655
  }
889
1656
  }
890
1657
 
891
1658
  if (!translatedPath) {
892
1659
  // Could not translate - skip this usage
1660
+ if (shouldDebugChild) {
1661
+ console.log(
1662
+ `[DEBUG CHILD ${childName}] SKIPPED - no translation found`,
1663
+ );
1664
+ }
893
1665
  continue;
894
1666
  }
895
1667
 
896
1668
  // Now resolve the translated path in the parent context
1669
+ // First, try standard resolution
897
1670
  const resolution = resolvePathToControllable(
898
1671
  translatedPath,
899
1672
  attributesMap,
@@ -901,12 +1674,113 @@ export default function generateExecutionFlowsFromConditionals(
901
1674
  fullToShortPathMap,
902
1675
  );
903
1676
 
904
- if (!resolution.isControllable || !resolution.resolvedPath) {
905
- // Path is not controllable in parent context
906
- continue;
1677
+ if (shouldDebugChild) {
1678
+ console.log(
1679
+ `[DEBUG CHILD ${childName}] resolvePathToControllable result:`,
1680
+ );
1681
+ console.log(
1682
+ `[DEBUG CHILD ${childName}] isControllable: ${resolution.isControllable}`,
1683
+ );
1684
+ console.log(
1685
+ `[DEBUG CHILD ${childName}] resolvedPath: ${resolution.resolvedPath}`,
1686
+ );
1687
+ console.log(
1688
+ `[DEBUG CHILD ${childName}] chain: ${resolution.resolutionChain.join(' -> ')}`,
1689
+ );
907
1690
  }
908
1691
 
909
- const resolvedPath = resolution.resolvedPath;
1692
+ // Only create flows for controllable paths (whitelist approach).
1693
+ // If the path doesn't resolve to something in attributesMap, skip it.
1694
+ // This prevents creating flows for useState values which are not
1695
+ // controllable via mock data injection.
1696
+ let resolvedPath = resolution.resolvedPath;
1697
+
1698
+ if (!resolution.isControllable || !resolvedPath) {
1699
+ // Path is not controllable via standard resolution.
1700
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
1701
+ // look for a related URL parameter like "varNameFromUrl" that might
1702
+ // control the initial state.
1703
+ //
1704
+ // Example: viewMode → cyScope20().functionCallReturnValue (useState value)
1705
+ // Fallback: viewModeFromUrl → segments[2] (URL param that initializes the useState)
1706
+ const useStateMatch = translatedPath.match(
1707
+ /^cyScope\d+\(\)\.functionCallReturnValue$/,
1708
+ );
1709
+
1710
+ if (useStateMatch) {
1711
+ // Find what variable this useState value corresponds to by looking
1712
+ // for entries like "varName": "cyScope20()" in equivalentSignatureVariables
1713
+ const useStatePattern = translatedPath.replace(
1714
+ /\.functionCallReturnValue$/,
1715
+ '',
1716
+ ); // e.g., "cyScope20()"
1717
+
1718
+ // Find the variable name that maps to this useState
1719
+ let useStateVarName: string | null = null;
1720
+ for (const [varName, varPath] of Object.entries(
1721
+ equivalentSignatureVariables,
1722
+ )) {
1723
+ if (varPath === useStatePattern) {
1724
+ useStateVarName = varName;
1725
+ break;
1726
+ }
1727
+ }
1728
+
1729
+ if (shouldDebugChild) {
1730
+ console.log(
1731
+ `[DEBUG CHILD ${childName}] useState fallback: looking for URL param`,
1732
+ );
1733
+ console.log(
1734
+ `[DEBUG CHILD ${childName}] useStatePattern: ${useStatePattern}`,
1735
+ );
1736
+ console.log(
1737
+ `[DEBUG CHILD ${childName}] useStateVarName: ${useStateVarName}`,
1738
+ );
1739
+ }
1740
+
1741
+ if (useStateVarName) {
1742
+ // Look for a related URL param like "varNameFromUrl"
1743
+ const urlParamName = `${useStateVarName}FromUrl`;
1744
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
1745
+
1746
+ if (shouldDebugChild) {
1747
+ console.log(
1748
+ `[DEBUG CHILD ${childName}] urlParamName: ${urlParamName}`,
1749
+ );
1750
+ console.log(
1751
+ `[DEBUG CHILD ${childName}] urlParamPath: ${urlParamPath}`,
1752
+ );
1753
+ }
1754
+
1755
+ if (urlParamPath) {
1756
+ // For useState values initialized from URL params, use the
1757
+ // URL param variable name directly (e.g., "viewModeFromUrl")
1758
+ // rather than fully resolving it. This keeps the path meaningful
1759
+ // for scenario generation and avoids overly generic paths like
1760
+ // "useParams().functionCallReturnValue.*".
1761
+ //
1762
+ // The flow will use the URL param name as the attributePath,
1763
+ // which gets properly resolved when generating mock data.
1764
+ resolvedPath = urlParamName;
1765
+ if (shouldDebugChild) {
1766
+ console.log(
1767
+ `[DEBUG CHILD ${childName}] useState fallback SUCCESS: using URL param name ${resolvedPath}`,
1768
+ );
1769
+ }
1770
+ }
1771
+ }
1772
+ }
1773
+
1774
+ // If still not resolved after fallback, skip
1775
+ if (!resolvedPath) {
1776
+ if (shouldDebugChild) {
1777
+ console.log(
1778
+ `[DEBUG CHILD ${childName}] SKIPPED - path not controllable`,
1779
+ );
1780
+ }
1781
+ continue;
1782
+ }
1783
+ }
910
1784
 
911
1785
  // Check for duplicates
912
1786
  const normalizedPath = normalizePathForDeduplication(
@@ -915,6 +1789,11 @@ export default function generateExecutionFlowsFromConditionals(
915
1789
  );
916
1790
 
917
1791
  if (seenNormalizedPaths.has(normalizedPath)) {
1792
+ if (shouldDebugChild) {
1793
+ console.log(
1794
+ `[DEBUG CHILD ${childName}] SKIPPED - duplicate normalized path: ${normalizedPath}`,
1795
+ );
1796
+ }
918
1797
  continue;
919
1798
  }
920
1799
  seenNormalizedPaths.add(normalizedPath);
@@ -931,6 +1810,17 @@ export default function generateExecutionFlowsFromConditionals(
931
1810
  resolvedPath,
932
1811
  );
933
1812
 
1813
+ if (shouldDebugChild) {
1814
+ console.log(
1815
+ `[DEBUG CHILD ${childName}] GENERATING ${usageFlows.length} flows with resolvedPath: ${resolvedPath}`,
1816
+ );
1817
+ for (const f of usageFlows) {
1818
+ console.log(
1819
+ `[DEBUG CHILD ${childName}] - Flow ID: ${f.id}, path: ${f.requiredValues[0]?.attributePath}`,
1820
+ );
1821
+ }
1822
+ }
1823
+
934
1824
  // Add gating conditions to each flow
935
1825
  for (const flow of usageFlows) {
936
1826
  // Add gating required values to the flow
@@ -967,7 +1857,7 @@ export default function generateExecutionFlowsFromConditionals(
967
1857
  // Process child's compound conditionals
968
1858
  for (const compound of childData.compoundConditionals) {
969
1859
  const resolvedPaths = new Map<string, string>();
970
- let allControllable = true;
1860
+ let allResolvable = true;
971
1861
 
972
1862
  for (const condition of compound.conditions) {
973
1863
  // Determine the child path to translate
@@ -982,7 +1872,7 @@ export default function generateExecutionFlowsFromConditionals(
982
1872
  );
983
1873
 
984
1874
  if (!translatedPath) {
985
- allControllable = false;
1875
+ allResolvable = false;
986
1876
  break;
987
1877
  }
988
1878
 
@@ -993,22 +1883,90 @@ export default function generateExecutionFlowsFromConditionals(
993
1883
  fullToShortPathMap,
994
1884
  );
995
1885
 
996
- if (!resolution.isControllable || !resolution.resolvedPath) {
997
- allControllable = false;
1886
+ // Only create flows for controllable paths (whitelist approach).
1887
+ // If the path doesn't resolve to something in attributesMap, skip it.
1888
+ // This prevents creating flows for useState values which are not
1889
+ // controllable via mock data injection.
1890
+ let resolvedPath = resolution.resolvedPath;
1891
+
1892
+ if (!resolution.isControllable || !resolvedPath) {
1893
+ // Path is not controllable via standard resolution.
1894
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
1895
+ // look for a related URL parameter like "varNameFromUrl" that might
1896
+ // control the initial state.
1897
+ const useStateMatch = translatedPath.match(
1898
+ /^cyScope\d+\(\)\.functionCallReturnValue$/,
1899
+ );
1900
+
1901
+ if (useStateMatch) {
1902
+ const useStatePattern = translatedPath.replace(
1903
+ /\.functionCallReturnValue$/,
1904
+ '',
1905
+ );
1906
+
1907
+ // Find the variable name that maps to this useState
1908
+ let useStateVarName: string | null = null;
1909
+ for (const [varName, varPath] of Object.entries(
1910
+ equivalentSignatureVariables,
1911
+ )) {
1912
+ if (varPath === useStatePattern) {
1913
+ useStateVarName = varName;
1914
+ break;
1915
+ }
1916
+ }
1917
+
1918
+ if (useStateVarName) {
1919
+ const urlParamName = `${useStateVarName}FromUrl`;
1920
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
1921
+
1922
+ if (urlParamPath) {
1923
+ resolvedPath = urlParamName;
1924
+ }
1925
+ }
1926
+ }
1927
+ }
1928
+
1929
+ if (!resolvedPath) {
1930
+ allResolvable = false;
998
1931
  break;
999
1932
  }
1000
1933
 
1001
- resolvedPaths.set(condition.path, resolution.resolvedPath);
1934
+ resolvedPaths.set(condition.path, resolvedPath);
1002
1935
  }
1003
1936
 
1004
- if (allControllable && resolvedPaths.size > 0) {
1937
+ if (allResolvable && resolvedPaths.size > 0) {
1005
1938
  const compoundFlow = generateFlowFromCompound(
1006
1939
  compound,
1007
1940
  resolvedPaths,
1008
1941
  );
1009
- if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
1010
- seenFlowIds.add(compoundFlow.id);
1011
- flows.push(compoundFlow);
1942
+ if (compoundFlow) {
1943
+ // Add gating conditions to compound flow (same as regular usage flows)
1944
+ if (gatingRequiredValues.length > 0) {
1945
+ // Filter out any gating values that are already in the flow
1946
+ const existingPaths = new Set(
1947
+ compoundFlow.requiredValues.map((rv) => rv.attributePath),
1948
+ );
1949
+ const newGatingValues = gatingRequiredValues.filter(
1950
+ (gv) => !existingPaths.has(gv.attributePath),
1951
+ );
1952
+ compoundFlow.requiredValues = [
1953
+ ...compoundFlow.requiredValues,
1954
+ ...newGatingValues,
1955
+ ];
1956
+
1957
+ // Update the flow ID to include gating conditions
1958
+ if (newGatingValues.length > 0) {
1959
+ const gatingIdPart = newGatingValues
1960
+ .map((gv) => `${gv.attributePath}-${gv.value}`)
1961
+ .join('-');
1962
+ compoundFlow.id = `${compoundFlow.id}-gated-${gatingIdPart}`;
1963
+ }
1964
+ }
1965
+
1966
+ if (!seenFlowIds.has(compoundFlow.id)) {
1967
+ seenFlowIds.add(compoundFlow.id);
1968
+ flows.push(compoundFlow);
1969
+ }
1012
1970
  }
1013
1971
  }
1014
1972
  }