@codeyam/codeyam-cli 0.1.0-staging.1669d45 → 0.1.0-staging.2a88920

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 (437) 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 +5 -5
  4. package/analyzer-template/packages/ai/index.ts +15 -2
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +87 -51
  6. package/analyzer-template/packages/ai/src/lib/astScopes/arrayDerivationDetector.ts +199 -0
  7. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +98 -9
  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 +555 -28
  11. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +88 -7
  12. package/analyzer-template/packages/ai/src/lib/completionCall.ts +198 -34
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +772 -243
  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/BatchSchemaProcessor.ts +16 -3
  17. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  18. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +43 -1
  19. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +122 -15
  20. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  21. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  22. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +319 -88
  23. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.ts +129 -0
  24. package/analyzer-template/packages/ai/src/lib/dataStructureChunking.ts +156 -0
  25. package/analyzer-template/packages/ai/src/lib/e2eDataTracking.ts +334 -0
  26. package/analyzer-template/packages/ai/src/lib/extractCriticalDataKeys.ts +120 -0
  27. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +642 -7
  28. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +35 -6
  29. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +383 -6
  30. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  31. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +1299 -51
  32. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +239 -0
  33. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  34. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  35. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateChunkPrompt.ts +82 -0
  36. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateCriticalKeysPrompt.ts +103 -0
  37. package/analyzer-template/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.ts +23 -6
  38. package/analyzer-template/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.ts +391 -0
  39. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +179 -45
  40. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +26 -4
  41. package/analyzer-template/packages/ai/src/lib/worker/analyzeScopeWorker.ts +114 -2
  42. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  43. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  44. package/analyzer-template/packages/analyze/src/lib/analysisContext.ts +44 -4
  45. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  46. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  47. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  48. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  49. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  50. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  51. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +30 -19
  52. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  53. package/analyzer-template/packages/analyze/src/lib/files/analyze/dependencyResolver.ts +6 -0
  54. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  55. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +33 -10
  56. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  57. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  58. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +189 -76
  59. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  60. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +77 -9
  61. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +118 -10
  62. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +276 -17
  63. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.ts +56 -11
  64. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  65. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  66. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  67. package/analyzer-template/packages/aws/package.json +2 -2
  68. package/analyzer-template/packages/database/src/lib/kysely/db.ts +8 -1
  69. package/analyzer-template/packages/database/src/lib/kysely/tables/commitsTable.ts +6 -0
  70. package/analyzer-template/packages/database/src/lib/loadAnalyses.ts +58 -1
  71. package/analyzer-template/packages/database/src/lib/loadAnalysis.ts +13 -0
  72. package/analyzer-template/packages/database/src/lib/loadBranch.ts +16 -1
  73. package/analyzer-template/packages/database/src/lib/loadCommit.ts +11 -0
  74. package/analyzer-template/packages/database/src/lib/loadCommits.ts +28 -0
  75. package/analyzer-template/packages/database/src/lib/loadEntities.ts +26 -3
  76. package/analyzer-template/packages/database/src/lib/loadEntityBranches.ts +12 -0
  77. package/analyzer-template/packages/database/src/lib/updateCommitMetadata.ts +7 -14
  78. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.d.ts.map +1 -1
  79. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js +8 -1
  80. package/analyzer-template/packages/github/dist/database/src/lib/kysely/db.js.map +1 -1
  81. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/analysesTable.d.ts.map +1 -1
  82. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts +1 -0
  83. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.d.ts.map +1 -1
  84. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js +3 -0
  85. package/analyzer-template/packages/github/dist/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  86. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts +2 -0
  87. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.d.ts.map +1 -1
  88. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js +45 -2
  89. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalyses.js.map +1 -1
  90. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.d.ts.map +1 -1
  91. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js +8 -0
  92. package/analyzer-template/packages/github/dist/database/src/lib/loadAnalysis.js.map +1 -1
  93. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js +11 -1
  94. package/analyzer-template/packages/github/dist/database/src/lib/loadBranch.js.map +1 -1
  95. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.d.ts.map +1 -1
  96. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js +7 -0
  97. package/analyzer-template/packages/github/dist/database/src/lib/loadCommit.js.map +1 -1
  98. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts +3 -1
  99. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.d.ts.map +1 -1
  100. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js +22 -1
  101. package/analyzer-template/packages/github/dist/database/src/lib/loadCommits.js.map +1 -1
  102. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts +3 -1
  103. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.d.ts.map +1 -1
  104. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js +23 -4
  105. package/analyzer-template/packages/github/dist/database/src/lib/loadEntities.js.map +1 -1
  106. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.d.ts.map +1 -1
  107. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js +9 -0
  108. package/analyzer-template/packages/github/dist/database/src/lib/loadEntityBranches.js.map +1 -1
  109. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts +2 -2
  110. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.d.ts.map +1 -1
  111. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js +5 -4
  112. package/analyzer-template/packages/github/dist/database/src/lib/updateCommitMetadata.js.map +1 -1
  113. package/analyzer-template/packages/github/dist/types/index.d.ts +1 -1
  114. package/analyzer-template/packages/github/dist/types/index.d.ts.map +1 -1
  115. package/analyzer-template/packages/github/dist/types/index.js.map +1 -1
  116. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts +25 -1
  117. package/analyzer-template/packages/github/dist/types/src/types/Analysis.d.ts.map +1 -1
  118. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts +2 -0
  119. package/analyzer-template/packages/github/dist/types/src/types/Commit.d.ts.map +1 -1
  120. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +56 -6
  121. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  122. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  123. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  124. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts +9 -1
  125. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  126. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js +29 -3
  127. package/analyzer-template/packages/github/dist/utils/src/lib/safeFileName.js.map +1 -1
  128. package/analyzer-template/packages/github/package.json +1 -1
  129. package/analyzer-template/packages/types/index.ts +1 -0
  130. package/analyzer-template/packages/types/src/types/Analysis.ts +25 -0
  131. package/analyzer-template/packages/types/src/types/Commit.ts +2 -0
  132. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +70 -6
  133. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  134. package/analyzer-template/packages/utils/dist/types/index.d.ts +1 -1
  135. package/analyzer-template/packages/utils/dist/types/index.d.ts.map +1 -1
  136. package/analyzer-template/packages/utils/dist/types/index.js.map +1 -1
  137. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts +25 -1
  138. package/analyzer-template/packages/utils/dist/types/src/types/Analysis.d.ts.map +1 -1
  139. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts +2 -0
  140. package/analyzer-template/packages/utils/dist/types/src/types/Commit.d.ts.map +1 -1
  141. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +56 -6
  142. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  143. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  144. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  145. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts +9 -1
  146. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.d.ts.map +1 -1
  147. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js +29 -3
  148. package/analyzer-template/packages/utils/dist/utils/src/lib/safeFileName.js.map +1 -1
  149. package/analyzer-template/packages/utils/src/lib/safeFileName.ts +48 -3
  150. package/analyzer-template/playwright/capture.ts +20 -8
  151. package/analyzer-template/playwright/captureStatic.ts +1 -1
  152. package/analyzer-template/project/analyzeBaselineCommit.ts +5 -0
  153. package/analyzer-template/project/analyzeRegularCommit.ts +5 -0
  154. package/analyzer-template/project/captureLibraryFunctionDirect.ts +29 -26
  155. package/analyzer-template/project/constructMockCode.ts +367 -37
  156. package/analyzer-template/project/createEntitiesAndSortFiles.ts +83 -0
  157. package/analyzer-template/project/loadReadyToBeCaptured.ts +65 -41
  158. package/analyzer-template/project/orchestrateCapture/AwsCaptureTaskRunner.ts +12 -4
  159. package/analyzer-template/project/orchestrateCapture/SequentialCaptureTaskRunner.ts +18 -7
  160. package/analyzer-template/project/orchestrateCapture/taskRunner.ts +4 -2
  161. package/analyzer-template/project/orchestrateCapture.ts +71 -6
  162. package/analyzer-template/project/reconcileMockDataKeys.ts +152 -9
  163. package/analyzer-template/project/runAnalysis.ts +4 -0
  164. package/analyzer-template/project/start.ts +35 -11
  165. package/analyzer-template/project/writeMockDataTsx.ts +127 -4
  166. package/analyzer-template/project/writeScenarioComponents.ts +101 -8
  167. package/analyzer-template/scripts/comboWorkerLoop.cjs +98 -50
  168. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js +5 -0
  169. package/background/src/lib/virtualized/project/analyzeBaselineCommit.js.map +1 -1
  170. package/background/src/lib/virtualized/project/analyzeRegularCommit.js +5 -0
  171. package/background/src/lib/virtualized/project/analyzeRegularCommit.js.map +1 -1
  172. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js +3 -3
  173. package/background/src/lib/virtualized/project/captureLibraryFunctionDirect.js.map +1 -1
  174. package/background/src/lib/virtualized/project/constructMockCode.js +300 -7
  175. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  176. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js +73 -1
  177. package/background/src/lib/virtualized/project/createEntitiesAndSortFiles.js.map +1 -1
  178. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js +19 -8
  179. package/background/src/lib/virtualized/project/loadReadyToBeCaptured.js.map +1 -1
  180. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js +2 -2
  181. package/background/src/lib/virtualized/project/orchestrateCapture/AwsCaptureTaskRunner.js.map +1 -1
  182. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js +7 -5
  183. package/background/src/lib/virtualized/project/orchestrateCapture/SequentialCaptureTaskRunner.js.map +1 -1
  184. package/background/src/lib/virtualized/project/orchestrateCapture.js +58 -6
  185. package/background/src/lib/virtualized/project/orchestrateCapture.js.map +1 -1
  186. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js +126 -9
  187. package/background/src/lib/virtualized/project/reconcileMockDataKeys.js.map +1 -1
  188. package/background/src/lib/virtualized/project/runAnalysis.js +3 -0
  189. package/background/src/lib/virtualized/project/runAnalysis.js.map +1 -1
  190. package/background/src/lib/virtualized/project/start.js +32 -11
  191. package/background/src/lib/virtualized/project/start.js.map +1 -1
  192. package/background/src/lib/virtualized/project/writeMockDataTsx.js +101 -4
  193. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  194. package/background/src/lib/virtualized/project/writeScenarioComponents.js +57 -8
  195. package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
  196. package/codeyam-cli/src/cli.js +2 -0
  197. package/codeyam-cli/src/cli.js.map +1 -1
  198. package/codeyam-cli/src/commands/debug.js +7 -5
  199. package/codeyam-cli/src/commands/debug.js.map +1 -1
  200. package/codeyam-cli/src/commands/memory.js +273 -0
  201. package/codeyam-cli/src/commands/memory.js.map +1 -0
  202. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js +4 -0
  203. package/codeyam-cli/src/utils/__tests__/setupClaudeCodeSettings.test.js.map +1 -1
  204. package/codeyam-cli/src/utils/analysisRunner.js +21 -2
  205. package/codeyam-cli/src/utils/analysisRunner.js.map +1 -1
  206. package/codeyam-cli/src/utils/install-skills.js +42 -6
  207. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  208. package/codeyam-cli/src/utils/queue/job.js +1 -0
  209. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  210. package/codeyam-cli/src/utils/queue/manager.js +6 -0
  211. package/codeyam-cli/src/utils/queue/manager.js.map +1 -1
  212. package/codeyam-cli/src/utils/rules/index.js +5 -0
  213. package/codeyam-cli/src/utils/rules/index.js.map +1 -0
  214. package/codeyam-cli/src/utils/rules/parser.js +106 -0
  215. package/codeyam-cli/src/utils/rules/parser.js.map +1 -0
  216. package/codeyam-cli/src/utils/rules/pathMatcher.js +18 -0
  217. package/codeyam-cli/src/utils/rules/pathMatcher.js.map +1 -0
  218. package/codeyam-cli/src/utils/rules/staleness.js +132 -0
  219. package/codeyam-cli/src/utils/rules/staleness.js.map +1 -0
  220. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js +2 -0
  221. package/codeyam-cli/src/utils/setupClaudeCodeSettings.js.map +1 -1
  222. package/codeyam-cli/src/webserver/app/lib/database.js +7 -3
  223. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  224. package/codeyam-cli/src/webserver/bootstrap.js +40 -0
  225. package/codeyam-cli/src/webserver/bootstrap.js.map +1 -1
  226. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-DsN1wKrm.js +11 -0
  227. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-COi5OvsN.js → EntityTypeBadge-DLqD3qNt.js} +1 -1
  228. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-BwdQv49w.js → EntityTypeIcon-Ba2JVPzP.js} +1 -1
  229. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-CEleMv_j.js → InlineSpinner-C8lyxW9k.js} +1 -1
  230. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-D68KarMg.js → InteractivePreview-aht4aafF.js} +2 -2
  231. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-L75Wvqgw.js → LibraryFunctionPreview-CVtiBnY5.js} +1 -1
  232. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-C53WM8qn.js → LoadingDots-B0GLXMsr.js} +1 -1
  233. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-CrNkmy4i.js → LogViewer-xgeCVgSM.js} +1 -1
  234. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-OApQuNyq.js +16 -0
  235. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-CQifa1n-.js → SafeScreenshot-DuDvi0jm.js} +1 -1
  236. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-CyaBFX7l.js → ScenarioViewer-DzccYyI8.js} +3 -13
  237. package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-D36O1rzU.js → TruncatedFilePath-DyFZkK0l.js} +1 -1
  238. package/codeyam-cli/src/webserver/build/client/assets/_index-BwqWJOgH.js +11 -0
  239. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BwavGCpm.js +32 -0
  240. package/codeyam-cli/src/webserver/build/client/assets/api.health-l0sNRNKZ.js +1 -0
  241. package/codeyam-cli/src/webserver/build/client/assets/api.memory-profile-l0sNRNKZ.js +1 -0
  242. package/codeyam-cli/src/webserver/build/client/assets/api.restart-server-l0sNRNKZ.js +1 -0
  243. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-DgTPh8H-.js → chevron-down-Cx24_aWc.js} +1 -1
  244. package/codeyam-cli/src/webserver/build/client/assets/{chunk-EPOLDU6W-DdQKK6on.js → chunk-EPOLDU6W-CXRTFQ3F.js} +1 -1
  245. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-Dmr2bb1R.js → circle-check-BOARzkeR.js} +1 -1
  246. package/codeyam-cli/src/webserver/build/client/assets/copy-Bb-80kDT.js +6 -0
  247. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-Do4ZLUYa.js → createLucideIcon-BdhJEx6B.js} +1 -1
  248. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-BBnGWYga.js +1 -0
  249. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-CbdFyxZh.js → entity._sha._-BJUiQqZF.js} +12 -12
  250. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-B4iCfs5M.js → entity._sha.scenarios._scenarioId.fullscreen-DavjRmOY.js} +1 -1
  251. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-wDWZZO1W.js → entity._sha_.create-scenario-D1T4TGjf.js} +1 -1
  252. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-BMbl7MeQ.js → entity._sha_.edit._scenarioId-CTBG2mmz.js} +1 -1
  253. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-5wRKRIH9.js → entry.client-CS2cb_eZ.js} +1 -1
  254. package/codeyam-cli/src/webserver/build/client/assets/file-code-Dhef1kWN.js +6 -0
  255. package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DD3SDH7t.js → fileTableUtils-DMJ7zii9.js} +1 -1
  256. package/codeyam-cli/src/webserver/build/client/assets/files-CJ6lTdTA.js +1 -0
  257. package/codeyam-cli/src/webserver/build/client/assets/{git-zXjT7J0G.js → git-CPTZZ-JZ.js} +8 -8
  258. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +1 -0
  259. package/codeyam-cli/src/webserver/build/client/assets/{index-DLbXwndH.js → index-B1h680n5.js} +1 -1
  260. package/codeyam-cli/src/webserver/build/client/assets/{index-gPZ-lad1.js → index-lzqtyFU8.js} +1 -1
  261. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-BsPXJ81F.js → loader-circle-B7B9V-bu.js} +1 -1
  262. package/codeyam-cli/src/webserver/build/client/assets/manifest-a78b90a2.js +1 -0
  263. package/codeyam-cli/src/webserver/build/client/assets/memory--GCbFsBE.js +92 -0
  264. package/codeyam-cli/src/webserver/build/client/assets/root-eVAaavTS.js +62 -0
  265. package/codeyam-cli/src/webserver/build/client/assets/{search-P2FKIUql.js → search-CxXUmBSd.js} +1 -1
  266. package/codeyam-cli/src/webserver/build/client/assets/{settings-B2eDuBj8.js → settings-CS5f3WzT.js} +1 -1
  267. package/codeyam-cli/src/webserver/build/client/assets/{simulations-L18M6-kN.js → simulations-DwFIBT09.js} +1 -1
  268. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-BDz7kbVA.js → triangle-alert-B6LgvRJg.js} +1 -1
  269. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-29dDmbH8.js → useCustomSizes-C1v1PQzo.js} +1 -1
  270. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-BUm0UVJm.js → useLastLogLine-aSv48UbS.js} +1 -1
  271. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-CkIOKTrZ.js → useReportContext-DYxHZQuP.js} +1 -1
  272. package/codeyam-cli/src/webserver/build/client/assets/{useToast-KKw5kTn-.js → useToast-mBRpZPiu.js} +1 -1
  273. package/codeyam-cli/src/webserver/build/server/assets/index-BM6TDT1Y.js +1 -0
  274. package/codeyam-cli/src/webserver/build/server/assets/server-build-dYC34MHw.js +257 -0
  275. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  276. package/codeyam-cli/src/webserver/build-info.json +5 -5
  277. package/codeyam-cli/templates/codeyam-memory-hook.sh +200 -0
  278. package/codeyam-cli/templates/codeyam:debug.md +47 -3
  279. package/codeyam-cli/templates/codeyam:diagnose.md +203 -25
  280. package/codeyam-cli/templates/codeyam:memory.md +341 -0
  281. package/codeyam-cli/templates/codeyam:new-rule.md +13 -0
  282. package/codeyam-cli/templates/rule-reflection-hook.py +160 -0
  283. package/codeyam-cli/templates/rules-instructions.md +93 -0
  284. package/package.json +8 -5
  285. package/packages/ai/index.js +7 -3
  286. package/packages/ai/index.js.map +1 -1
  287. package/packages/ai/src/lib/analyzeScope.js +70 -29
  288. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  289. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js +150 -0
  290. package/packages/ai/src/lib/astScopes/arrayDerivationDetector.js.map +1 -0
  291. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +78 -8
  292. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  293. package/packages/ai/src/lib/astScopes/methodSemantics.js +109 -23
  294. package/packages/ai/src/lib/astScopes/methodSemantics.js.map +1 -1
  295. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js +1 -102
  296. package/packages/ai/src/lib/astScopes/patterns/variableDeclarationHandler.js.map +1 -1
  297. package/packages/ai/src/lib/astScopes/processExpression.js +440 -27
  298. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  299. package/packages/ai/src/lib/completionCall.js +161 -30
  300. package/packages/ai/src/lib/completionCall.js.map +1 -1
  301. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +589 -166
  302. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  303. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js +179 -0
  304. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/MuiManager.js.map +1 -1
  305. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js +7 -1
  306. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/ReactFrameworkManager.js.map +1 -1
  307. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  308. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  309. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  310. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  311. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +41 -1
  312. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  313. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +104 -11
  314. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  315. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  316. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  317. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  318. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  319. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +265 -79
  320. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  321. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js +107 -0
  322. package/packages/ai/src/lib/dataStructure/helpers/fixNullIdsBySchema.js.map +1 -0
  323. package/packages/ai/src/lib/dataStructureChunking.js +111 -0
  324. package/packages/ai/src/lib/dataStructureChunking.js.map +1 -0
  325. package/packages/ai/src/lib/e2eDataTracking.js +241 -0
  326. package/packages/ai/src/lib/e2eDataTracking.js.map +1 -0
  327. package/packages/ai/src/lib/extractCriticalDataKeys.js +96 -0
  328. package/packages/ai/src/lib/extractCriticalDataKeys.js.map +1 -0
  329. package/packages/ai/src/lib/generateEntityScenarioData.js +525 -8
  330. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  331. package/packages/ai/src/lib/generateEntityScenarios.js +26 -2
  332. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  333. package/packages/ai/src/lib/generateExecutionFlows.js +281 -4
  334. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  335. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +946 -42
  336. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  337. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js +194 -0
  338. package/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.js.map +1 -0
  339. package/packages/ai/src/lib/mergeStatements.js +70 -51
  340. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  341. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  342. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  343. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js +54 -0
  344. package/packages/ai/src/lib/promptGenerators/generateChunkPrompt.js.map +1 -0
  345. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js +15 -7
  346. package/packages/ai/src/lib/promptGenerators/generateEntityScenarioDataGenerator.js.map +1 -1
  347. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js +335 -0
  348. package/packages/ai/src/lib/promptGenerators/simplifyKeysForLLM.js.map +1 -0
  349. package/packages/ai/src/lib/resolvePathToControllable.js +155 -41
  350. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  351. package/packages/ai/src/lib/worker/SerializableDataStructure.js +7 -0
  352. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  353. package/packages/ai/src/lib/worker/analyzeScopeWorker.js +94 -1
  354. package/packages/ai/src/lib/worker/analyzeScopeWorker.js.map +1 -1
  355. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  356. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  357. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  358. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  359. package/packages/analyze/src/lib/analysisContext.js +30 -5
  360. package/packages/analyze/src/lib/analysisContext.js.map +1 -1
  361. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  362. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  363. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  364. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  365. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  366. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  367. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  368. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  369. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  370. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  371. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  372. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  373. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +21 -9
  374. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  375. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  376. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  377. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js +5 -0
  378. package/packages/analyze/src/lib/files/analyze/dependencyResolver.js.map +1 -1
  379. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  380. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  381. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +31 -10
  382. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  383. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  384. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  385. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  386. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  387. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +160 -68
  388. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  389. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  390. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  391. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +71 -9
  392. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  393. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +57 -9
  394. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  395. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +233 -9
  396. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  397. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js +46 -9
  398. package/packages/analyze/src/lib/files/scenarios/mergeValidatedDataStructures.js.map +1 -1
  399. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  400. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  401. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  402. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  403. package/packages/database/src/lib/kysely/db.js +8 -1
  404. package/packages/database/src/lib/kysely/db.js.map +1 -1
  405. package/packages/database/src/lib/kysely/tables/commitsTable.js +3 -0
  406. package/packages/database/src/lib/kysely/tables/commitsTable.js.map +1 -1
  407. package/packages/database/src/lib/loadAnalyses.js +45 -2
  408. package/packages/database/src/lib/loadAnalyses.js.map +1 -1
  409. package/packages/database/src/lib/loadAnalysis.js +8 -0
  410. package/packages/database/src/lib/loadAnalysis.js.map +1 -1
  411. package/packages/database/src/lib/loadBranch.js +11 -1
  412. package/packages/database/src/lib/loadBranch.js.map +1 -1
  413. package/packages/database/src/lib/loadCommit.js +7 -0
  414. package/packages/database/src/lib/loadCommit.js.map +1 -1
  415. package/packages/database/src/lib/loadCommits.js +22 -1
  416. package/packages/database/src/lib/loadCommits.js.map +1 -1
  417. package/packages/database/src/lib/loadEntities.js +23 -4
  418. package/packages/database/src/lib/loadEntities.js.map +1 -1
  419. package/packages/database/src/lib/loadEntityBranches.js +9 -0
  420. package/packages/database/src/lib/loadEntityBranches.js.map +1 -1
  421. package/packages/database/src/lib/updateCommitMetadata.js +5 -4
  422. package/packages/database/src/lib/updateCommitMetadata.js.map +1 -1
  423. package/packages/types/index.js.map +1 -1
  424. package/packages/utils/src/lib/safeFileName.js +29 -3
  425. package/packages/utils/src/lib/safeFileName.js.map +1 -1
  426. package/scripts/finalize-analyzer.cjs +3 -3
  427. package/codeyam-cli/src/webserver/build/client/assets/EntityItem-vauWK972.js +0 -1
  428. package/codeyam-cli/src/webserver/build/client/assets/ReportIssueModal-DzJRkCkr.js +0 -11
  429. package/codeyam-cli/src/webserver/build/client/assets/_index-Be83mo_j.js +0 -11
  430. package/codeyam-cli/src/webserver/build/client/assets/activity.(_tab)-BN6wu6Y-.js +0 -37
  431. package/codeyam-cli/src/webserver/build/client/assets/dev.empty-Bn6aCAy_.js +0 -1
  432. package/codeyam-cli/src/webserver/build/client/assets/files-DKyMFI90.js +0 -1
  433. package/codeyam-cli/src/webserver/build/client/assets/globals-DTTQ3gY7.css +0 -1
  434. package/codeyam-cli/src/webserver/build/client/assets/manifest-22590fcf.js +0 -1
  435. package/codeyam-cli/src/webserver/build/client/assets/root-BsAarjAM.js +0 -57
  436. package/codeyam-cli/src/webserver/build/server/assets/index-BND5I5fv.js +0 -1
  437. package/codeyam-cli/src/webserver/build/server/assets/server-build-CFXnd7MG.js +0 -228
@@ -13,9 +13,16 @@
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
+ JsxRenderingUsage,
22
+ } from './astScopes/types';
17
23
  import type { EnrichedConditionalUsage } from './worker/SerializableDataStructure';
18
24
  import resolvePathToControllable from './resolvePathToControllable';
25
+ import cleanPathOfNonTransformingFunctions from './dataStructure/helpers/cleanPathOfNonTransformingFunctions';
19
26
 
20
27
  /** Extended conditional usage type that may include sourceDataPath from enrichment */
21
28
  type ExtendedConditionalUsage = ConditionalUsage &
@@ -26,7 +33,7 @@ export interface ChildComponentConditionalData {
26
33
  /** Child's conditional usages keyed by variable name (may include sourceDataPath from enrichment) */
27
34
  conditionalUsages: Record<string, ExtendedConditionalUsage[]>;
28
35
  /** Child's equivalent signature variables (maps internal paths to prop paths) */
29
- equivalentSignatureVariables: Record<string, string>;
36
+ equivalentSignatureVariables: Record<string, string | string[]>;
30
37
  /** Child's compound conditionals */
31
38
  compoundConditionals: CompoundConditional[];
32
39
  /**
@@ -35,6 +42,11 @@ export interface ChildComponentConditionalData {
35
42
  * then `hasAnalysis` is a gating condition for all of ChildComponent's flows.
36
43
  */
37
44
  gatingConditions?: ConditionalUsage[];
45
+ /**
46
+ * Child's JSX rendering usages (arrays rendered via .map(), text interpolation).
47
+ * These generate "variation flows" for different array lengths.
48
+ */
49
+ jsxRenderingUsages?: JsxRenderingUsage[];
38
50
  }
39
51
 
40
52
  export interface GenerateFlowsFromConditionalsArgs {
@@ -45,7 +57,7 @@ export interface GenerateFlowsFromConditionalsArgs {
45
57
  /** Map of controllable paths to their types */
46
58
  attributesMap: Record<string, string>;
47
59
  /** Map from local variable names to data sources */
48
- equivalentSignatureVariables: Record<string, string>;
60
+ equivalentSignatureVariables: Record<string, string | string[]>;
49
61
  /** Map from full paths to short paths */
50
62
  fullToShortPathMap: Record<string, string>;
51
63
  /**
@@ -54,6 +66,173 @@ export interface GenerateFlowsFromConditionalsArgs {
54
66
  * Used to merge child execution flows into parent.
55
67
  */
56
68
  childComponentData?: Record<string, ChildComponentConditionalData>;
69
+ /**
70
+ * Optional map of derived variables to their derivation info.
71
+ * Used to trace intermediate derived variables that aren't directly
72
+ * used in conditionals but are sources for other derived variables.
73
+ *
74
+ * Example:
75
+ * - `isInCurrentRun = currentRun.entityShas.includes(sha)` → { sourcePath: 'currentRun.entityShas', operation: 'arrayIncludes' }
76
+ * - `isAnalyzing = isInCurrentRun || isInQueue` → { sourcePaths: ['isInCurrentRun', 'isInQueue'], operation: 'or' }
77
+ *
78
+ * Without this map, we can't trace from `isAnalyzing` through `isInCurrentRun`
79
+ * to `currentRun.entityShas` when `isInCurrentRun` isn't in conditionalUsages.
80
+ */
81
+ derivedVariables?: Record<string, DerivedVariableInfo>;
82
+ /**
83
+ * Optional map of child prop paths to their actual data sources.
84
+ * Used when child props flow through useState but ultimately come from
85
+ * mockable data sources (e.g., API calls, fetchers).
86
+ *
87
+ * Example:
88
+ * - "WorkoutsView().signature[0].workouts" → "createClient()...functionCallReturnValue.data"
89
+ *
90
+ * When a child path translates to a useState value, we check this map
91
+ * to find the real data source that can be mocked.
92
+ */
93
+ sourceEquivalencies?: Record<
94
+ string,
95
+ Array<{ scopeNodeName: string; schemaPath: string }>
96
+ >;
97
+ }
98
+
99
+ /**
100
+ * Resolved source info from expanding a derived variable.
101
+ * Contains the path and the operation that led to it.
102
+ */
103
+ interface ResolvedSourceInfo {
104
+ path: string;
105
+ operation?:
106
+ | 'notNull'
107
+ | 'isNull'
108
+ | 'equals'
109
+ | 'notEquals'
110
+ | 'or'
111
+ | 'and'
112
+ | 'arrayIncludes'
113
+ | 'arraySome'
114
+ | 'arrayEvery'
115
+ | 'arrayLength';
116
+ }
117
+
118
+ /**
119
+ * Recursively expands a derived variable to its leaf data sources.
120
+ *
121
+ * For OR expressions like `isAnalyzing = a || b || c`:
122
+ * - Returns all source paths [a, b, c] so they can all be set appropriately
123
+ *
124
+ * For nested derivations like `isAnalyzing = isInCurrentRun || isInQueue`:
125
+ * - Where `isInCurrentRun` is derived from `currentRun.entityShas.includes(x)`
126
+ * - Returns the final data sources: [currentRun.entityShas, queueState.jobs]
127
+ *
128
+ * @param path The variable path to expand
129
+ * @param conditionalUsages All conditional usages (to look up derivedFrom info)
130
+ * @param attributesMap Map of controllable paths
131
+ * @param equivalentSignatureVariables Variable-to-path mappings
132
+ * @param fullToShortPathMap Full-to-short path mappings
133
+ * @param visited Set of already-visited paths (prevents infinite recursion)
134
+ * @param derivedVariables Optional map of all derived variables (for intermediate tracing)
135
+ * @returns Array of resolved source paths that are controllable
136
+ */
137
+ function expandDerivedVariableToSources(
138
+ path: string,
139
+ conditionalUsages: Record<string, ConditionalUsage[]>,
140
+ attributesMap: Record<string, string>,
141
+ equivalentSignatureVariables: Record<string, string | string[]>,
142
+ fullToShortPathMap: Record<string, string>,
143
+ visited: Set<string> = new Set(),
144
+ derivedVariables?: Record<string, DerivedVariableInfo>,
145
+ ): ResolvedSourceInfo[] {
146
+ // Prevent infinite recursion
147
+ if (visited.has(path)) {
148
+ return [];
149
+ }
150
+ visited.add(path);
151
+
152
+ // First, check if this path is directly controllable
153
+ const directResolution = resolvePathToControllable(
154
+ path,
155
+ attributesMap,
156
+ equivalentSignatureVariables,
157
+ fullToShortPathMap,
158
+ );
159
+
160
+ if (directResolution.isControllable && directResolution.resolvedPath) {
161
+ return [{ path: directResolution.resolvedPath }];
162
+ }
163
+
164
+ // Look up derivedFrom info for this path
165
+ // First check conditionalUsages, then fall back to derivedVariables
166
+ const usage = conditionalUsages[path]?.[0];
167
+ let derivedFrom = usage?.derivedFrom;
168
+
169
+ // CRITICAL: If not found in conditionalUsages, check derivedVariables
170
+ // This handles intermediate derived variables like `isInCurrentRun` that aren't
171
+ // directly used in conditionals but ARE derived from data sources
172
+ if (!derivedFrom && derivedVariables?.[path]) {
173
+ derivedFrom = derivedVariables[path];
174
+ }
175
+
176
+ if (!derivedFrom) {
177
+ return [];
178
+ }
179
+
180
+ const { operation, sourcePath, sourcePaths } = derivedFrom;
181
+
182
+ // For OR/AND operations, recursively expand all source paths
183
+ if ((operation === 'or' || operation === 'and') && sourcePaths) {
184
+ const allSources: ResolvedSourceInfo[] = [];
185
+
186
+ for (const sp of sourcePaths) {
187
+ const expanded = expandDerivedVariableToSources(
188
+ sp,
189
+ conditionalUsages,
190
+ attributesMap,
191
+ equivalentSignatureVariables,
192
+ fullToShortPathMap,
193
+ visited,
194
+ derivedVariables,
195
+ );
196
+
197
+ // Add all expanded sources
198
+ for (const source of expanded) {
199
+ // Avoid duplicates
200
+ if (!allSources.some((s) => s.path === source.path)) {
201
+ allSources.push(source);
202
+ }
203
+ }
204
+ }
205
+
206
+ return allSources;
207
+ }
208
+
209
+ // For single-source operations (arrayIncludes, arraySome, notNull, etc.)
210
+ if (sourcePath) {
211
+ // Try to resolve the source path directly
212
+ const sourceResolution = resolvePathToControllable(
213
+ sourcePath,
214
+ attributesMap,
215
+ equivalentSignatureVariables,
216
+ fullToShortPathMap,
217
+ );
218
+
219
+ if (sourceResolution.isControllable && sourceResolution.resolvedPath) {
220
+ return [{ path: sourceResolution.resolvedPath, operation }];
221
+ }
222
+
223
+ // If not directly resolvable, recursively expand
224
+ return expandDerivedVariableToSources(
225
+ sourcePath,
226
+ conditionalUsages,
227
+ attributesMap,
228
+ equivalentSignatureVariables,
229
+ fullToShortPathMap,
230
+ visited,
231
+ derivedVariables,
232
+ );
233
+ }
234
+
235
+ return [];
57
236
  }
58
237
 
59
238
  /**
@@ -113,6 +292,52 @@ function cleanSourceDataPath(sourceDataPath: string): string | null {
113
292
  return actualPath;
114
293
  }
115
294
 
295
+ /**
296
+ * Strip .length suffix from a path if present.
297
+ *
298
+ * When we have a path like "items.length", the controllable attribute is "items"
299
+ * (the array), not "items.length". The length is derived from the array contents.
300
+ *
301
+ * This ensures that execution flows reference the actual controllable attribute
302
+ * rather than the derived .length property.
303
+ */
304
+ function stripLengthSuffix(path: string): string {
305
+ if (path.endsWith('.length')) {
306
+ return path.slice(0, -7); // Remove ".length" (7 characters)
307
+ }
308
+ return path;
309
+ }
310
+
311
+ /**
312
+ * Extract the controllable base path from a path that may contain method calls.
313
+ *
314
+ * This handles complex expressions like:
315
+ * - `scenarios.filter((s) => s.active).length` → `scenarios`
316
+ * - `users.some((u) => u.role === 'admin')` → `users`
317
+ * - `items.map(x => x.name).join(', ')` → `items`
318
+ *
319
+ * The controllable base is the path that can be mocked - we can control
320
+ * what `scenarios` contains, but we can't control what `.filter()` returns.
321
+ *
322
+ * @param path - The path that may contain method calls
323
+ * @returns The controllable base path with method calls stripped
324
+ */
325
+ function extractControllableBase(path: string): string {
326
+ // First strip .length suffix if present
327
+ const pathWithoutLength = stripLengthSuffix(path);
328
+
329
+ // Use cleanPathOfNonTransformingFunctions to strip method calls like .filter(), .some()
330
+ const cleanedPath = cleanPathOfNonTransformingFunctions(pathWithoutLength);
331
+
332
+ // If the cleaned path is different, return it
333
+ if (cleanedPath !== pathWithoutLength) {
334
+ return cleanedPath;
335
+ }
336
+
337
+ // Otherwise, return the path with just .length stripped
338
+ return pathWithoutLength;
339
+ }
340
+
116
341
  /**
117
342
  * Find a path in attributesMap, using fullToShortPathMap to verify the path is controllable.
118
343
  *
@@ -183,6 +408,20 @@ function findInAttributesMapForPath(
183
408
  return null;
184
409
  }
185
410
 
411
+ /**
412
+ * Generate a slug from a path for use in flow IDs and exclusive groups.
413
+ */
414
+ function pathToSlug(path: string): string {
415
+ return path
416
+ .replace(/\[\d+\]/g, '')
417
+ .replace(/\[\]/g, '')
418
+ .replace(/\(\)/g, '')
419
+ .replace(/\.functionCallReturnValue/g, '')
420
+ .replace(/[<>]/g, '')
421
+ .replace(/\./g, '-')
422
+ .toLowerCase();
423
+ }
424
+
186
425
  /**
187
426
  * Generate a human-readable name from a path.
188
427
  * Extracts the last meaningful part of the path.
@@ -244,6 +483,12 @@ function inferValueType(
244
483
  /**
245
484
  * Generate flows from a single conditional usage.
246
485
  * Sets impact to 'high' if the conditional controls JSX rendering.
486
+ *
487
+ * When the usage has a `constraintExpression`, it represents a complex expression
488
+ * that can't be simply resolved (e.g., `scenarios.filter(x => x.active).length > 1`).
489
+ * In this case:
490
+ * - `attributePath` is set to the controllable base (e.g., `scenarios`)
491
+ * - `constraint` is set to the full expression for LLM reasoning
247
492
  */
248
493
  function generateFlowsFromUsage(
249
494
  usage: ConditionalUsage,
@@ -258,6 +503,14 @@ function generateFlowsFromUsage(
258
503
  ? 'high'
259
504
  : 'medium';
260
505
 
506
+ // When there's a constraintExpression, use the controllable base for attributePath
507
+ // and pass through the constraint for LLM reasoning
508
+ const hasConstraint = !!usage.constraintExpression;
509
+ const attributePath = hasConstraint
510
+ ? extractControllableBase(resolvedPath)
511
+ : stripLengthSuffix(resolvedPath);
512
+ const constraint = usage.constraintExpression;
513
+
261
514
  if (usage.conditionType === 'truthiness') {
262
515
  // Generate both truthy and falsy flows
263
516
  const isNegated = usage.isNegated ?? false;
@@ -269,10 +522,11 @@ function generateFlowsFromUsage(
269
522
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'falsy' : 'truthy'}`,
270
523
  requiredValues: [
271
524
  {
272
- attributePath: resolvedPath,
525
+ attributePath,
273
526
  value: isNegated ? 'falsy' : 'truthy',
274
527
  comparison: isNegated ? 'falsy' : 'truthy',
275
528
  valueType: 'boolean',
529
+ constraint,
276
530
  },
277
531
  ],
278
532
  impact,
@@ -292,10 +546,11 @@ function generateFlowsFromUsage(
292
546
  description: `When ${baseName.toLowerCase()} is ${isNegated ? 'truthy' : 'falsy'}`,
293
547
  requiredValues: [
294
548
  {
295
- attributePath: resolvedPath,
549
+ attributePath,
296
550
  value: isNegated ? 'truthy' : 'falsy',
297
551
  comparison: isNegated ? 'truthy' : 'falsy',
298
552
  valueType: 'boolean',
553
+ constraint,
299
554
  },
300
555
  ],
301
556
  impact,
@@ -321,10 +576,11 @@ function generateFlowsFromUsage(
321
576
  description: `When ${baseName.toLowerCase()} equals "${value}"`,
322
577
  requiredValues: [
323
578
  {
324
- attributePath: resolvedPath,
579
+ attributePath,
325
580
  value: value,
326
581
  comparison: 'equals',
327
582
  valueType: inferValueType(value),
583
+ constraint,
328
584
  },
329
585
  ],
330
586
  impact,
@@ -381,7 +637,7 @@ function generateFlowFromCompound(
381
637
  }
382
638
 
383
639
  requiredValues.push({
384
- attributePath: resolvedPath,
640
+ attributePath: stripLengthSuffix(resolvedPath),
385
641
  value,
386
642
  comparison,
387
643
  valueType: inferValueType(value),
@@ -412,6 +668,60 @@ function generateFlowFromCompound(
412
668
  };
413
669
  }
414
670
 
671
+ /**
672
+ * Expand a compound conditional with OR groups into multiple condition sets.
673
+ *
674
+ * For a compound like `A && (B || C)`:
675
+ * - Conditions: [{ path: 'A' }, { path: 'B', orGroupId: 'or_xxx' }, { path: 'C', orGroupId: 'or_xxx' }]
676
+ * - Returns: [[A, B], [A, C]] - two sets of conditions
677
+ *
678
+ * For multiple OR groups like `A && (B || C) && (D || E)`:
679
+ * - Returns: [[A, B, D], [A, B, E], [A, C, D], [A, C, E]]
680
+ */
681
+ function expandOrGroups(
682
+ conditions: CompoundConditional['conditions'],
683
+ ): CompoundConditional['conditions'][] {
684
+ // Separate conditions into mandatory (no orGroupId) and OR groups
685
+ const mandatory = conditions.filter((c) => !c.orGroupId);
686
+ const orGroups = new Map<string, typeof conditions>();
687
+
688
+ for (const condition of conditions) {
689
+ if (condition.orGroupId) {
690
+ const group = orGroups.get(condition.orGroupId) ?? [];
691
+ group.push(condition);
692
+ orGroups.set(condition.orGroupId, group);
693
+ }
694
+ }
695
+
696
+ // If no OR groups, return the original conditions
697
+ if (orGroups.size === 0) {
698
+ return [conditions];
699
+ }
700
+
701
+ // Generate all combinations by picking one condition from each OR group
702
+ const groupArrays = Array.from(orGroups.values());
703
+ const combinations: CompoundConditional['conditions'][] = [];
704
+
705
+ function generateCombinations(
706
+ index: number,
707
+ current: typeof conditions,
708
+ ): void {
709
+ if (index === groupArrays.length) {
710
+ // We've picked one from each OR group - combine with mandatory conditions
711
+ combinations.push([...mandatory, ...current]);
712
+ return;
713
+ }
714
+
715
+ // Pick each option from the current OR group
716
+ for (const option of groupArrays[index]) {
717
+ generateCombinations(index + 1, [...current, option]);
718
+ }
719
+ }
720
+
721
+ generateCombinations(0, []);
722
+ return combinations;
723
+ }
724
+
415
725
  /**
416
726
  * Generate execution flows from conditional usages using pure static analysis.
417
727
  *
@@ -457,8 +767,8 @@ function normalizePathForDeduplication(
457
767
  */
458
768
  function translateChildPathToParent(
459
769
  childPath: string,
460
- childEquivalentSignatureVariables: Record<string, string>,
461
- parentEquivalentSignatureVariables: Record<string, string>,
770
+ childEquivalentSignatureVariables: Record<string, string | string[]>,
771
+ parentEquivalentSignatureVariables: Record<string, string | string[]>,
462
772
  childName: string,
463
773
  ): string | null {
464
774
  // Extract the root variable from the child path
@@ -485,7 +795,11 @@ function translateChildPathToParent(
485
795
 
486
796
  // Look up the child's equivalence for this root variable
487
797
  // e.g., childEquiv[selectedScenario] = "signature[0].selectedScenario"
488
- const childPropPath = childEquivalentSignatureVariables[rootVar];
798
+ // Handle array case (OR expressions) - use first element if array
799
+ const rawChildPropPath = childEquivalentSignatureVariables[rootVar];
800
+ const childPropPath = Array.isArray(rawChildPropPath)
801
+ ? rawChildPropPath[0]
802
+ : rawChildPropPath;
489
803
 
490
804
  if (!childPropPath) {
491
805
  // No mapping found - this might be internal state, not a prop
@@ -498,7 +812,11 @@ function translateChildPathToParent(
498
812
 
499
813
  // Look up parent's equivalence to find what value was passed to this prop
500
814
  // e.g., parentEquiv["ChildName().signature[0].selectedScenario"] = "selectedScenario"
501
- const parentValue = parentEquivalentSignatureVariables[fullChildPropPath];
815
+ // Handle array case (OR expressions) - use first element if array
816
+ const rawParentValue = parentEquivalentSignatureVariables[fullChildPropPath];
817
+ const parentValue = Array.isArray(rawParentValue)
818
+ ? rawParentValue[0]
819
+ : rawParentValue;
502
820
 
503
821
  if (!parentValue) {
504
822
  // No parent mapping found - log ALL parent keys that contain the childName
@@ -524,6 +842,8 @@ export default function generateExecutionFlowsFromConditionals(
524
842
  equivalentSignatureVariables,
525
843
  fullToShortPathMap,
526
844
  childComponentData,
845
+ derivedVariables,
846
+ sourceEquivalencies,
527
847
  } = args;
528
848
 
529
849
  const flows: ExecutionFlow[] = [];
@@ -591,7 +911,8 @@ export default function generateExecutionFlowsFromConditionals(
591
911
  // This handles cases like: const hasAnalysis = analysis !== null
592
912
  // where hasAnalysis is not in attributesMap but analysis is
593
913
  if (!resolvedPath && usage.derivedFrom) {
594
- const { sourcePath, sourcePaths } = usage.derivedFrom;
914
+ const { operation, sourcePath, sourcePaths, comparedValue } =
915
+ usage.derivedFrom;
595
916
 
596
917
  // For single-source derivations (notNull, equals, etc.)
597
918
  if (sourcePath) {
@@ -607,8 +928,277 @@ export default function generateExecutionFlowsFromConditionals(
607
928
  }
608
929
  }
609
930
 
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
931
+ // For equals/notEquals derivations with comparedValue, generate comparison flows
932
+ // e.g., canEdit derived from user.role === 'admin'
933
+ // When canEdit is used in truthiness check, we need:
934
+ // - Truthy flow: user.role = 'admin' (comparison: 'equals')
935
+ // - Falsy flow: user.role != 'admin' (comparison: 'notEquals')
936
+ if (
937
+ (operation === 'equals' || operation === 'notEquals') &&
938
+ comparedValue !== undefined &&
939
+ resolvedPath &&
940
+ usage.conditionType === 'truthiness'
941
+ ) {
942
+ const baseName = generateNameFromPath(usage.path);
943
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
944
+ ? 'high'
945
+ : 'medium';
946
+
947
+ const isNegated = usage.isNegated ?? false;
948
+
949
+ // For equals derivation:
950
+ // - Truthy check (!negated): needs value = comparedValue (equals)
951
+ // - Falsy check (negated): needs value != comparedValue (notEquals)
952
+ // For notEquals derivation: inverse of above
953
+ const isEqualsDerivation = operation === 'equals';
954
+ const truthyNeedsEquals = isEqualsDerivation !== isNegated;
955
+
956
+ // Generate truthy flow
957
+ const truthyFlow: ExecutionFlow = {
958
+ id: generateFlowId(usage.path, 'truthy'),
959
+ name: `${baseName} True`,
960
+ description: `When ${baseName.toLowerCase()} is truthy (${resolvedPath} ${truthyNeedsEquals ? '=' : '!='} ${comparedValue})`,
961
+ requiredValues: [
962
+ {
963
+ attributePath: stripLengthSuffix(resolvedPath),
964
+ value: comparedValue,
965
+ comparison: truthyNeedsEquals ? 'equals' : 'notEquals',
966
+ valueType: inferValueType(comparedValue),
967
+ },
968
+ ],
969
+ impact,
970
+ sourceLocation: usage.sourceLocation
971
+ ? {
972
+ lineNumber: usage.sourceLocation.lineNumber,
973
+ column: usage.sourceLocation.column,
974
+ }
975
+ : undefined,
976
+ codeSnippet: usage.sourceLocation?.codeSnippet,
977
+ };
978
+
979
+ // Generate falsy flow
980
+ const falsyFlow: ExecutionFlow = {
981
+ id: generateFlowId(usage.path, 'falsy'),
982
+ name: `${baseName} False`,
983
+ description: `When ${baseName.toLowerCase()} is falsy (${resolvedPath} ${truthyNeedsEquals ? '!=' : '='} ${comparedValue})`,
984
+ requiredValues: [
985
+ {
986
+ attributePath: stripLengthSuffix(resolvedPath),
987
+ value: comparedValue,
988
+ comparison: truthyNeedsEquals ? 'notEquals' : 'equals',
989
+ valueType: inferValueType(comparedValue),
990
+ },
991
+ ],
992
+ impact,
993
+ sourceLocation: usage.sourceLocation
994
+ ? {
995
+ lineNumber: usage.sourceLocation.lineNumber,
996
+ column: usage.sourceLocation.column,
997
+ }
998
+ : undefined,
999
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1000
+ };
1001
+
1002
+ // Add flows and skip normal flow generation
1003
+ if (!seenFlowIds.has(truthyFlow.id)) {
1004
+ seenFlowIds.add(truthyFlow.id);
1005
+ flows.push(truthyFlow);
1006
+ }
1007
+ if (!seenFlowIds.has(falsyFlow.id)) {
1008
+ seenFlowIds.add(falsyFlow.id);
1009
+ flows.push(falsyFlow);
1010
+ }
1011
+
1012
+ continue;
1013
+ }
1014
+
1015
+ // For OR derivations with negation, we need ALL sources to be falsy
1016
+ // e.g., !isBusy where isBusy = isRunning || isQueued || isPending
1017
+ // For the falsy flow, ALL sources must be falsy
1018
+ if (
1019
+ operation === 'or' &&
1020
+ usage.conditionType === 'truthiness' &&
1021
+ usage.isNegated === true &&
1022
+ sourcePaths &&
1023
+ sourcePaths.length > 0
1024
+ ) {
1025
+ // Use expandDerivedVariableToSources to recursively resolve all sources
1026
+ const allSources = expandDerivedVariableToSources(
1027
+ usage.path,
1028
+ conditionalUsages,
1029
+ attributesMap,
1030
+ equivalentSignatureVariables,
1031
+ fullToShortPathMap,
1032
+ new Set(),
1033
+ derivedVariables,
1034
+ );
1035
+
1036
+ if (allSources.length > 0) {
1037
+ // Generate a compound-style flow with all sources set to falsy
1038
+ const baseName = generateNameFromPath(usage.path);
1039
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
1040
+ ? 'high'
1041
+ : 'medium';
1042
+
1043
+ const requiredValues: ExecutionFlow['requiredValues'] =
1044
+ allSources.map((source) => ({
1045
+ attributePath: source.path,
1046
+ value: 'falsy',
1047
+ comparison: 'falsy' as const,
1048
+ valueType: 'boolean' as const,
1049
+ }));
1050
+
1051
+ // Create a single falsy flow with all sources
1052
+ const falsyFlow: ExecutionFlow = {
1053
+ id: generateFlowId(usage.path, 'falsy'),
1054
+ name: `${baseName} False`,
1055
+ description: `When ${baseName.toLowerCase()} is falsy (all sources are falsy)`,
1056
+ requiredValues,
1057
+ impact,
1058
+ sourceLocation: usage.sourceLocation
1059
+ ? {
1060
+ lineNumber: usage.sourceLocation.lineNumber,
1061
+ column: usage.sourceLocation.column,
1062
+ }
1063
+ : undefined,
1064
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1065
+ };
1066
+
1067
+ // Create a truthy flow - for OR, ANY source being truthy is sufficient
1068
+ // We use the first resolvable source for the truthy flow
1069
+ const firstSource = allSources[0];
1070
+ const truthyFlow: ExecutionFlow = {
1071
+ id: generateFlowId(usage.path, 'truthy'),
1072
+ name: `${baseName} True`,
1073
+ description: `When ${baseName.toLowerCase()} is truthy`,
1074
+ requiredValues: [
1075
+ {
1076
+ attributePath: firstSource.path,
1077
+ value: 'truthy',
1078
+ comparison: 'truthy',
1079
+ valueType: 'boolean',
1080
+ },
1081
+ ],
1082
+ impact,
1083
+ sourceLocation: usage.sourceLocation
1084
+ ? {
1085
+ lineNumber: usage.sourceLocation.lineNumber,
1086
+ column: usage.sourceLocation.column,
1087
+ }
1088
+ : undefined,
1089
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1090
+ };
1091
+
1092
+ // Add both flows (falsy needs all sources, truthy needs one)
1093
+ if (!seenFlowIds.has(falsyFlow.id)) {
1094
+ seenFlowIds.add(falsyFlow.id);
1095
+ flows.push(falsyFlow);
1096
+ }
1097
+ if (!seenFlowIds.has(truthyFlow.id)) {
1098
+ seenFlowIds.add(truthyFlow.id);
1099
+ flows.push(truthyFlow);
1100
+ }
1101
+
1102
+ // Skip the normal flow generation for this usage
1103
+ continue;
1104
+ }
1105
+ }
1106
+
1107
+ // For AND derivations without negation, we need ALL sources to be truthy
1108
+ // e.g., isReady where isReady = hasData && isLoaded && isValid
1109
+ // For the truthy flow, ALL sources must be truthy
1110
+ // For negated AND (!isReady), ANY source being falsy is sufficient (fallback behavior)
1111
+ if (
1112
+ operation === 'and' &&
1113
+ usage.conditionType === 'truthiness' &&
1114
+ usage.isNegated !== true &&
1115
+ sourcePaths &&
1116
+ sourcePaths.length > 0
1117
+ ) {
1118
+ // Use expandDerivedVariableToSources to recursively resolve all sources
1119
+ const allSources = expandDerivedVariableToSources(
1120
+ usage.path,
1121
+ conditionalUsages,
1122
+ attributesMap,
1123
+ equivalentSignatureVariables,
1124
+ fullToShortPathMap,
1125
+ new Set(),
1126
+ derivedVariables,
1127
+ );
1128
+
1129
+ if (allSources.length > 0) {
1130
+ // Generate a compound-style flow with all sources set to truthy
1131
+ const baseName = generateNameFromPath(usage.path);
1132
+ const impact: ExecutionFlow['impact'] = usage.controlsJsxRendering
1133
+ ? 'high'
1134
+ : 'medium';
1135
+
1136
+ const requiredValues: ExecutionFlow['requiredValues'] =
1137
+ allSources.map((source) => ({
1138
+ attributePath: source.path,
1139
+ value: 'truthy',
1140
+ comparison: 'truthy' as const,
1141
+ valueType: 'boolean' as const,
1142
+ }));
1143
+
1144
+ // Create a truthy flow with all sources
1145
+ const truthyFlow: ExecutionFlow = {
1146
+ id: generateFlowId(usage.path, 'truthy'),
1147
+ name: `${baseName} True`,
1148
+ description: `When ${baseName.toLowerCase()} is truthy (all sources are truthy)`,
1149
+ requiredValues,
1150
+ impact,
1151
+ sourceLocation: usage.sourceLocation
1152
+ ? {
1153
+ lineNumber: usage.sourceLocation.lineNumber,
1154
+ column: usage.sourceLocation.column,
1155
+ }
1156
+ : undefined,
1157
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1158
+ };
1159
+
1160
+ // Create a falsy flow - for AND, ANY source being falsy is sufficient
1161
+ // We use the first resolvable source for the falsy flow
1162
+ const firstSource = allSources[0];
1163
+ const falsyFlow: ExecutionFlow = {
1164
+ id: generateFlowId(usage.path, 'falsy'),
1165
+ name: `${baseName} False`,
1166
+ description: `When ${baseName.toLowerCase()} is falsy`,
1167
+ requiredValues: [
1168
+ {
1169
+ attributePath: firstSource.path,
1170
+ value: 'falsy',
1171
+ comparison: 'falsy',
1172
+ valueType: 'boolean',
1173
+ },
1174
+ ],
1175
+ impact,
1176
+ sourceLocation: usage.sourceLocation
1177
+ ? {
1178
+ lineNumber: usage.sourceLocation.lineNumber,
1179
+ column: usage.sourceLocation.column,
1180
+ }
1181
+ : undefined,
1182
+ codeSnippet: usage.sourceLocation?.codeSnippet,
1183
+ };
1184
+
1185
+ // Add both flows (truthy needs all sources, falsy needs one)
1186
+ if (!seenFlowIds.has(truthyFlow.id)) {
1187
+ seenFlowIds.add(truthyFlow.id);
1188
+ flows.push(truthyFlow);
1189
+ }
1190
+ if (!seenFlowIds.has(falsyFlow.id)) {
1191
+ seenFlowIds.add(falsyFlow.id);
1192
+ flows.push(falsyFlow);
1193
+ }
1194
+
1195
+ // Skip the normal flow generation for this usage
1196
+ continue;
1197
+ }
1198
+ }
1199
+
1200
+ // For multi-source derivations (or, and) without special handling,
1201
+ // try the first resolvable path as a fallback
612
1202
  if (!resolvedPath && sourcePaths && sourcePaths.length > 0) {
613
1203
  for (const sp of sourcePaths) {
614
1204
  const resolution = resolvePathToControllable(
@@ -661,32 +1251,203 @@ export default function generateExecutionFlowsFromConditionals(
661
1251
 
662
1252
  // Process compound conditionals
663
1253
  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;
1254
+ // Expand OR groups into separate condition sets
1255
+ // For example, `A && (B || C)` becomes [[A, B], [A, C]]
1256
+ const expandedConditionSets = expandOrGroups(compound.conditions);
1257
+
1258
+ // Process each expanded condition set as a separate potential flow
1259
+ for (const conditionSet of expandedConditionSets) {
1260
+ // First, check if ALL paths in this condition set are controllable (or can be expanded to controllable sources)
1261
+ const resolvedPaths = new Map<string, string>();
1262
+ // Track expanded sources for derived variables (path -> array of expanded sources)
1263
+ const expandedSources = new Map<
1264
+ string,
1265
+ Array<{ path: string; operation?: DerivedVariableOperation }>
1266
+ >();
1267
+ let allControllable = true;
1268
+
1269
+ for (const condition of conditionSet) {
1270
+ // Check if this condition path has derivation info
1271
+ // First check conditionalUsages, then fall back to derivedVariables
1272
+ const usagesForPath = conditionalUsages[condition.path];
1273
+ let derivedFromInfo = usagesForPath?.find(
1274
+ (u) => u.derivedFrom?.operation,
1275
+ )?.derivedFrom;
1276
+
1277
+ // CRITICAL: Also check derivedVariables for intermediate derived variables
1278
+ if (!derivedFromInfo && derivedVariables?.[condition.path]) {
1279
+ derivedFromInfo = derivedVariables[condition.path];
1280
+ }
667
1281
 
668
- for (const condition of compound.conditions) {
669
- const resolution = resolvePathToControllable(
670
- condition.path,
671
- attributesMap,
672
- equivalentSignatureVariables,
673
- fullToShortPathMap,
674
- );
1282
+ if (derivedFromInfo) {
1283
+ // This is a derived variable - expand to its sources
1284
+ const sources = expandDerivedVariableToSources(
1285
+ condition.path,
1286
+ conditionalUsages,
1287
+ attributesMap,
1288
+ equivalentSignatureVariables,
1289
+ fullToShortPathMap,
1290
+ new Set(),
1291
+ derivedVariables,
1292
+ );
1293
+
1294
+ if (sources.length > 0) {
1295
+ // Store the expanded sources for this condition
1296
+ expandedSources.set(condition.path, sources);
1297
+ // Use the first source's path for the resolvedPaths map (for ID generation)
1298
+ resolvedPaths.set(condition.path, sources[0].path);
1299
+ } else {
1300
+ // Derived variable but no controllable sources found
1301
+ allControllable = false;
1302
+ break;
1303
+ }
1304
+ } else {
1305
+ // Not a derived variable - resolve directly
1306
+ const resolution = resolvePathToControllable(
1307
+ condition.path,
1308
+ attributesMap,
1309
+ equivalentSignatureVariables,
1310
+ fullToShortPathMap,
1311
+ );
1312
+
1313
+ if (!resolution.isControllable || !resolution.resolvedPath) {
1314
+ allControllable = false;
1315
+ break;
1316
+ }
675
1317
 
676
- if (!resolution.isControllable || !resolution.resolvedPath) {
677
- allControllable = false;
678
- break;
1318
+ resolvedPaths.set(condition.path, resolution.resolvedPath);
1319
+ }
679
1320
  }
680
1321
 
681
- resolvedPaths.set(condition.path, resolution.resolvedPath);
682
- }
1322
+ // Only create a flow if ALL paths are controllable
1323
+ if (allControllable && resolvedPaths.size > 0) {
1324
+ // If any conditions were expanded from derived variables, we need to build a custom flow
1325
+ if (expandedSources.size > 0) {
1326
+ const requiredValues: ExecutionFlow['requiredValues'] = [];
1327
+
1328
+ for (const condition of conditionSet) {
1329
+ const sources = expandedSources.get(condition.path);
1330
+
1331
+ if (sources) {
1332
+ // This condition was expanded - add all its sources
1333
+ // Determine the required value based on condition type and derivation operation
1334
+ const usagesForPath = conditionalUsages[condition.path];
1335
+ let expandedDerivedFrom = usagesForPath?.find(
1336
+ (u) => u.derivedFrom?.operation,
1337
+ )?.derivedFrom;
1338
+
1339
+ // Also check derivedVariables for intermediate derived variables
1340
+ if (!expandedDerivedFrom && derivedVariables?.[condition.path]) {
1341
+ expandedDerivedFrom = derivedVariables[condition.path];
1342
+ }
1343
+ const operation = expandedDerivedFrom?.operation;
1344
+
1345
+ for (const source of sources) {
1346
+ // For OR-derived truthy: ANY source truthy
1347
+ // For AND-derived truthy: ALL sources truthy
1348
+ // For negated: inverse
1349
+ let value: string;
1350
+ let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
1351
+
1352
+ if (condition.conditionType === 'truthiness') {
1353
+ const isNegated = condition.isNegated === true;
1354
+ // For OR: truthy needs ANY source truthy, falsy needs ALL sources falsy
1355
+ // For AND: truthy needs ALL sources truthy, falsy needs ANY source falsy
1356
+ // In compound conditionals, we generate the truthy path by default
1357
+ // (the compound expression must be truthy)
1358
+ if (operation === 'or') {
1359
+ // For OR-derived, truthy means we need at least one source truthy
1360
+ // We'll use the first source as truthy (simplification)
1361
+ value = isNegated ? 'falsy' : 'truthy';
1362
+ } else if (operation === 'and') {
1363
+ // For AND-derived, truthy means ALL sources truthy
1364
+ value = isNegated ? 'falsy' : 'truthy';
1365
+ } else {
1366
+ value = isNegated ? 'falsy' : 'truthy';
1367
+ }
1368
+ comparison = isNegated ? 'falsy' : 'truthy';
1369
+ } else {
1370
+ value = 'truthy';
1371
+ comparison = 'truthy';
1372
+ }
1373
+
1374
+ requiredValues.push({
1375
+ attributePath: source.path,
1376
+ value,
1377
+ comparison,
1378
+ valueType: 'boolean' as const,
1379
+ });
1380
+ }
1381
+ } else {
1382
+ // This condition was resolved directly
1383
+ const resolvedPath = resolvedPaths.get(condition.path);
1384
+ if (resolvedPath) {
1385
+ let value: string;
1386
+ let comparison: ExecutionFlow['requiredValues'][0]['comparison'];
1387
+
1388
+ if (condition.conditionType === 'truthiness') {
1389
+ value = condition.isNegated ? 'falsy' : 'truthy';
1390
+ comparison = condition.isNegated ? 'falsy' : 'truthy';
1391
+ } else {
1392
+ value =
1393
+ condition.requiredValue?.toString() ??
1394
+ condition.comparedValues?.[0] ??
1395
+ 'truthy';
1396
+ comparison = 'equals';
1397
+ }
683
1398
 
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);
1399
+ requiredValues.push({
1400
+ attributePath: stripLengthSuffix(resolvedPath),
1401
+ value,
1402
+ comparison,
1403
+ valueType: inferValueType(value),
1404
+ });
1405
+ }
1406
+ }
1407
+ }
1408
+
1409
+ if (requiredValues.length > 0) {
1410
+ const impact: ExecutionFlow['impact'] =
1411
+ compound.controlsJsxRendering ? 'high' : 'medium';
1412
+
1413
+ // Generate a combined ID from all paths
1414
+ const pathParts = requiredValues
1415
+ .map((rv) => {
1416
+ const name = generateNameFromPath(rv.attributePath);
1417
+ return name.toLowerCase().replace(/\s+/g, '-');
1418
+ })
1419
+ .join('-and-');
1420
+
1421
+ const compoundFlow: ExecutionFlow = {
1422
+ id: `${pathParts}-${requiredValues.map((rv) => rv.value).join('-')}`,
1423
+ name: generateNameFromPath(requiredValues[0].attributePath),
1424
+ description: `When ${requiredValues.map((rv) => `${generateNameFromPath(rv.attributePath).toLowerCase()} is ${rv.value}`).join(' and ')}`,
1425
+ impact,
1426
+ requiredValues,
1427
+ sourceLocation: compound.sourceLocation,
1428
+ };
1429
+
1430
+ if (!seenFlowIds.has(compoundFlow.id)) {
1431
+ seenFlowIds.add(compoundFlow.id);
1432
+ flows.push(compoundFlow);
1433
+ }
1434
+ }
1435
+ } else {
1436
+ // No derived variables - use the original generateFlowFromCompound
1437
+ // Create a modified compound with just this condition set
1438
+ const modifiedCompound = {
1439
+ ...compound,
1440
+ conditions: conditionSet,
1441
+ };
1442
+ const compoundFlow = generateFlowFromCompound(
1443
+ modifiedCompound,
1444
+ resolvedPaths,
1445
+ );
1446
+ if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
1447
+ seenFlowIds.add(compoundFlow.id);
1448
+ flows.push(compoundFlow);
1449
+ }
1450
+ }
690
1451
  }
691
1452
  }
692
1453
  }
@@ -733,7 +1494,18 @@ export default function generateExecutionFlowsFromConditionals(
733
1494
  fullToShortPathMap,
734
1495
  );
735
1496
 
1497
+ // Only use controllable paths for gating conditions (whitelist approach).
1498
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1499
+ // uncontrollable paths like useState that cannot be mocked.
1500
+ let resolvedVarPath: string | null = null;
736
1501
  if (varResolution.isControllable && varResolution.resolvedPath) {
1502
+ resolvedVarPath = varResolution.resolvedPath;
1503
+ }
1504
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1505
+ // because that would allow uncontrollable paths (like useState) to be added
1506
+ // as gating conditions.
1507
+
1508
+ if (resolvedVarPath) {
737
1509
  const isNegated = (gatingCondition as any).isNegated === true;
738
1510
  const isNotEquals = operator === '!=' || operator === '!==';
739
1511
  // Determine the effective value for this gating condition
@@ -744,7 +1516,7 @@ export default function generateExecutionFlowsFromConditionals(
744
1516
  const needsExactValue = isNegated !== isNotEquals;
745
1517
 
746
1518
  gatingRequiredValues.push({
747
- attributePath: varResolution.resolvedPath,
1519
+ attributePath: resolvedVarPath,
748
1520
  value: needsExactValue ? 'falsy' : comparedValue,
749
1521
  comparison: needsExactValue ? 'falsy' : 'equals',
750
1522
  });
@@ -810,7 +1582,9 @@ export default function generateExecutionFlowsFromConditionals(
810
1582
  // For non-negated &&: all parts must be truthy
811
1583
  // For negated ||: all parts must be falsy (DeMorgan: !(A || B) = !A && !B)
812
1584
  gatingRequiredValues.push({
813
- attributePath: partResolution.resolvedPath,
1585
+ attributePath: stripLengthSuffix(
1586
+ partResolution.resolvedPath,
1587
+ ),
814
1588
  value: isNegated ? 'falsy' : 'truthy',
815
1589
  comparison: isNegated ? 'falsy' : 'truthy',
816
1590
  });
@@ -827,16 +1601,27 @@ export default function generateExecutionFlowsFromConditionals(
827
1601
  fullToShortPathMap,
828
1602
  );
829
1603
 
1604
+ // Only use controllable paths for gating conditions (whitelist approach).
1605
+ // Do NOT fall back to equivalentSignatureVariables because those may contain
1606
+ // uncontrollable paths like useState that cannot be mocked.
1607
+ let resolvedGatingPath: string | null = null;
830
1608
  if (
831
1609
  gatingResolution.isControllable &&
832
1610
  gatingResolution.resolvedPath
833
1611
  ) {
1612
+ resolvedGatingPath = gatingResolution.resolvedPath;
1613
+ }
1614
+ // Note: We intentionally do NOT fall back to equivalentSignatureVariables here
1615
+ // because that would allow uncontrollable paths (like useState) to be added
1616
+ // as gating conditions.
1617
+
1618
+ if (resolvedGatingPath) {
834
1619
  // For truthiness conditions on gating, check if the condition is negated
835
1620
  // e.g., ternary else branch: isError ? <ErrorView /> : <SuccessView />
836
1621
  // SuccessView has isNegated: true, meaning it renders when isError is falsy
837
1622
  const isNegated = (gatingCondition as any).isNegated === true;
838
1623
  gatingRequiredValues.push({
839
- attributePath: gatingResolution.resolvedPath,
1624
+ attributePath: resolvedGatingPath,
840
1625
  value: isNegated ? 'falsy' : 'truthy',
841
1626
  comparison: isNegated ? 'falsy' : 'truthy',
842
1627
  });
@@ -855,6 +1640,9 @@ export default function generateExecutionFlowsFromConditionals(
855
1640
  childData.conditionalUsages,
856
1641
  )) {
857
1642
  for (const usage of usages) {
1643
+ // Debug logging (disabled - set to true for troubleshooting child flow resolution)
1644
+ const shouldDebugChild = false;
1645
+
858
1646
  // Skip usages that are part of compound conditionals (handled separately)
859
1647
  // Fix 33: Only skip if the chainId is in the child's compound conditionals
860
1648
  if (usage.chainId && childCompoundChainIds.has(usage.chainId)) {
@@ -869,6 +1657,18 @@ export default function generateExecutionFlowsFromConditionals(
869
1657
  childPath = usage.derivedFrom.sourcePath;
870
1658
  }
871
1659
 
1660
+ if (shouldDebugChild) {
1661
+ console.log(
1662
+ `[DEBUG CHILD ${childName}] Processing usage path: ${usage.path}`,
1663
+ );
1664
+ console.log(
1665
+ `[DEBUG CHILD ${childName}] childPath (after derivedFrom): ${childPath}`,
1666
+ );
1667
+ console.log(
1668
+ `[DEBUG CHILD ${childName}] sourceDataPath: ${usage.sourceDataPath}`,
1669
+ );
1670
+ }
1671
+
872
1672
  // Translate the child path to a parent path
873
1673
  let translatedPath = translateChildPathToParent(
874
1674
  childPath,
@@ -877,6 +1677,12 @@ export default function generateExecutionFlowsFromConditionals(
877
1677
  childName,
878
1678
  );
879
1679
 
1680
+ if (shouldDebugChild) {
1681
+ console.log(
1682
+ `[DEBUG CHILD ${childName}] translatedPath (from translateChildPathToParent): ${translatedPath}`,
1683
+ );
1684
+ }
1685
+
880
1686
  // If translation failed but we have sourceDataPath, try to extract the prop path from it
881
1687
  // sourceDataPath format: "ChildName.signature[n].propPath.rest" → extract "propPath.rest"
882
1688
  if (!translatedPath && usage.sourceDataPath) {
@@ -885,15 +1691,26 @@ export default function generateExecutionFlowsFromConditionals(
885
1691
  );
886
1692
  if (signatureMatch) {
887
1693
  translatedPath = signatureMatch[1];
1694
+ if (shouldDebugChild) {
1695
+ console.log(
1696
+ `[DEBUG CHILD ${childName}] translatedPath (from sourceDataPath regex): ${translatedPath}`,
1697
+ );
1698
+ }
888
1699
  }
889
1700
  }
890
1701
 
891
1702
  if (!translatedPath) {
892
1703
  // Could not translate - skip this usage
1704
+ if (shouldDebugChild) {
1705
+ console.log(
1706
+ `[DEBUG CHILD ${childName}] SKIPPED - no translation found`,
1707
+ );
1708
+ }
893
1709
  continue;
894
1710
  }
895
1711
 
896
1712
  // Now resolve the translated path in the parent context
1713
+ // First, try standard resolution
897
1714
  const resolution = resolvePathToControllable(
898
1715
  translatedPath,
899
1716
  attributesMap,
@@ -901,12 +1718,176 @@ export default function generateExecutionFlowsFromConditionals(
901
1718
  fullToShortPathMap,
902
1719
  );
903
1720
 
904
- if (!resolution.isControllable || !resolution.resolvedPath) {
905
- // Path is not controllable in parent context
906
- continue;
1721
+ if (shouldDebugChild) {
1722
+ console.log(
1723
+ `[DEBUG CHILD ${childName}] resolvePathToControllable result:`,
1724
+ );
1725
+ console.log(
1726
+ `[DEBUG CHILD ${childName}] isControllable: ${resolution.isControllable}`,
1727
+ );
1728
+ console.log(
1729
+ `[DEBUG CHILD ${childName}] resolvedPath: ${resolution.resolvedPath}`,
1730
+ );
1731
+ console.log(
1732
+ `[DEBUG CHILD ${childName}] chain: ${resolution.resolutionChain.join(' -> ')}`,
1733
+ );
907
1734
  }
908
1735
 
909
- const resolvedPath = resolution.resolvedPath;
1736
+ // Only create flows for controllable paths (whitelist approach).
1737
+ // If the path doesn't resolve to something in attributesMap, skip it.
1738
+ // This prevents creating flows for useState values which are not
1739
+ // controllable via mock data injection.
1740
+ let resolvedPath = resolution.resolvedPath;
1741
+
1742
+ if (!resolution.isControllable || !resolvedPath) {
1743
+ // Path is not controllable via standard resolution.
1744
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
1745
+ // look for a related URL parameter like "varNameFromUrl" that might
1746
+ // control the initial state.
1747
+ //
1748
+ // Example: viewMode → cyScope20().functionCallReturnValue (useState value)
1749
+ // Fallback: viewModeFromUrl → segments[2] (URL param that initializes the useState)
1750
+ const useStateMatch = translatedPath.match(
1751
+ /^cyScope\d+\(\)\.functionCallReturnValue$/,
1752
+ );
1753
+
1754
+ if (useStateMatch) {
1755
+ // Find what variable this useState value corresponds to by looking
1756
+ // for entries like "varName": "cyScope20()" in equivalentSignatureVariables
1757
+ const useStatePattern = translatedPath.replace(
1758
+ /\.functionCallReturnValue$/,
1759
+ '',
1760
+ ); // e.g., "cyScope20()"
1761
+
1762
+ // Find the variable name that maps to this useState
1763
+ let useStateVarName: string | null = null;
1764
+ for (const [varName, varPath] of Object.entries(
1765
+ equivalentSignatureVariables,
1766
+ )) {
1767
+ if (varPath === useStatePattern) {
1768
+ useStateVarName = varName;
1769
+ break;
1770
+ }
1771
+ }
1772
+
1773
+ if (shouldDebugChild) {
1774
+ console.log(
1775
+ `[DEBUG CHILD ${childName}] useState fallback: looking for URL param`,
1776
+ );
1777
+ console.log(
1778
+ `[DEBUG CHILD ${childName}] useStatePattern: ${useStatePattern}`,
1779
+ );
1780
+ console.log(
1781
+ `[DEBUG CHILD ${childName}] useStateVarName: ${useStateVarName}`,
1782
+ );
1783
+ }
1784
+
1785
+ if (useStateVarName) {
1786
+ // Look for a related URL param like "varNameFromUrl"
1787
+ const urlParamName = `${useStateVarName}FromUrl`;
1788
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
1789
+
1790
+ if (shouldDebugChild) {
1791
+ console.log(
1792
+ `[DEBUG CHILD ${childName}] urlParamName: ${urlParamName}`,
1793
+ );
1794
+ console.log(
1795
+ `[DEBUG CHILD ${childName}] urlParamPath: ${urlParamPath}`,
1796
+ );
1797
+ }
1798
+
1799
+ if (urlParamPath) {
1800
+ // For useState values initialized from URL params, use the
1801
+ // URL param variable name directly (e.g., "viewModeFromUrl")
1802
+ // rather than fully resolving it. This keeps the path meaningful
1803
+ // for scenario generation and avoids overly generic paths like
1804
+ // "useParams().functionCallReturnValue.*".
1805
+ //
1806
+ // The flow will use the URL param name as the attributePath,
1807
+ // which gets properly resolved when generating mock data.
1808
+ resolvedPath = urlParamName;
1809
+ if (shouldDebugChild) {
1810
+ console.log(
1811
+ `[DEBUG CHILD ${childName}] useState fallback SUCCESS: using URL param name ${resolvedPath}`,
1812
+ );
1813
+ }
1814
+ }
1815
+ }
1816
+ }
1817
+
1818
+ // Fallback 2: Try sourceEquivalencies to find the actual data source
1819
+ // This handles the case where props flow through useState but originate
1820
+ // from a mockable data source (e.g., API call, fetcher).
1821
+ //
1822
+ // Example: WorkoutsView receives `workouts` prop which in parent is stored
1823
+ // in useState, but ultimately comes from a Supabase query.
1824
+ // sourceEquivalencies tells us: "WorkoutsView().signature[0].workouts" → "createClient()...data"
1825
+ if (!resolvedPath && sourceEquivalencies) {
1826
+ // Build the child prop path to look up in sourceEquivalencies
1827
+ // Format: "ChildName().signature[0].propName"
1828
+ // First, find what prop this child path maps to
1829
+ let childPropName: string | null = null;
1830
+ for (const [varName, varPath] of Object.entries(
1831
+ childData.equivalentSignatureVariables,
1832
+ )) {
1833
+ // Check if childPath starts with this variable name
1834
+ // e.g., childPath = "workouts.length", varName = "workouts", varPath = "signature[0].workouts"
1835
+ if (
1836
+ childPath === varName ||
1837
+ childPath.startsWith(`${varName}.`)
1838
+ ) {
1839
+ childPropName = varName;
1840
+ break;
1841
+ }
1842
+ }
1843
+
1844
+ if (childPropName) {
1845
+ // Build the full sourceEquivalencies key
1846
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
1847
+
1848
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
1849
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
1850
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
1851
+
1852
+ // Check if this data source path is controllable
1853
+ const dataSourceResolution = resolvePathToControllable(
1854
+ dataSourcePath,
1855
+ attributesMap,
1856
+ equivalentSignatureVariables,
1857
+ fullToShortPathMap,
1858
+ );
1859
+
1860
+ if (
1861
+ dataSourceResolution.isControllable &&
1862
+ dataSourceResolution.resolvedPath
1863
+ ) {
1864
+ // Preserve any suffix from the child path
1865
+ // e.g., childPath = "workouts.length" → suffix = ".length"
1866
+ const suffix = childPath.startsWith(`${childPropName}.`)
1867
+ ? childPath.slice(childPropName.length)
1868
+ : '';
1869
+ resolvedPath = dataSourceResolution.resolvedPath + suffix;
1870
+
1871
+ if (shouldDebugChild) {
1872
+ console.log(
1873
+ `[DEBUG CHILD ${childName}] sourceEquivalencies fallback SUCCESS: using data source ${resolvedPath}`,
1874
+ );
1875
+ }
1876
+ }
1877
+ }
1878
+ }
1879
+ }
1880
+
1881
+ // If still not resolved after fallback, skip
1882
+ if (!resolvedPath) {
1883
+ if (shouldDebugChild) {
1884
+ console.log(
1885
+ `[DEBUG CHILD ${childName}] SKIPPED - path not controllable`,
1886
+ );
1887
+ }
1888
+ continue;
1889
+ }
1890
+ }
910
1891
 
911
1892
  // Check for duplicates
912
1893
  const normalizedPath = normalizePathForDeduplication(
@@ -915,6 +1896,11 @@ export default function generateExecutionFlowsFromConditionals(
915
1896
  );
916
1897
 
917
1898
  if (seenNormalizedPaths.has(normalizedPath)) {
1899
+ if (shouldDebugChild) {
1900
+ console.log(
1901
+ `[DEBUG CHILD ${childName}] SKIPPED - duplicate normalized path: ${normalizedPath}`,
1902
+ );
1903
+ }
918
1904
  continue;
919
1905
  }
920
1906
  seenNormalizedPaths.add(normalizedPath);
@@ -931,6 +1917,17 @@ export default function generateExecutionFlowsFromConditionals(
931
1917
  resolvedPath,
932
1918
  );
933
1919
 
1920
+ if (shouldDebugChild) {
1921
+ console.log(
1922
+ `[DEBUG CHILD ${childName}] GENERATING ${usageFlows.length} flows with resolvedPath: ${resolvedPath}`,
1923
+ );
1924
+ for (const f of usageFlows) {
1925
+ console.log(
1926
+ `[DEBUG CHILD ${childName}] - Flow ID: ${f.id}, path: ${f.requiredValues[0]?.attributePath}`,
1927
+ );
1928
+ }
1929
+ }
1930
+
934
1931
  // Add gating conditions to each flow
935
1932
  for (const flow of usageFlows) {
936
1933
  // Add gating required values to the flow
@@ -967,7 +1964,7 @@ export default function generateExecutionFlowsFromConditionals(
967
1964
  // Process child's compound conditionals
968
1965
  for (const compound of childData.compoundConditionals) {
969
1966
  const resolvedPaths = new Map<string, string>();
970
- let allControllable = true;
1967
+ let allResolvable = true;
971
1968
 
972
1969
  for (const condition of compound.conditions) {
973
1970
  // Determine the child path to translate
@@ -982,7 +1979,7 @@ export default function generateExecutionFlowsFromConditionals(
982
1979
  );
983
1980
 
984
1981
  if (!translatedPath) {
985
- allControllable = false;
1982
+ allResolvable = false;
986
1983
  break;
987
1984
  }
988
1985
 
@@ -993,22 +1990,273 @@ export default function generateExecutionFlowsFromConditionals(
993
1990
  fullToShortPathMap,
994
1991
  );
995
1992
 
996
- if (!resolution.isControllable || !resolution.resolvedPath) {
997
- allControllable = false;
1993
+ // Only create flows for controllable paths (whitelist approach).
1994
+ // If the path doesn't resolve to something in attributesMap, skip it.
1995
+ // This prevents creating flows for useState values which are not
1996
+ // controllable via mock data injection.
1997
+ let resolvedPath = resolution.resolvedPath;
1998
+
1999
+ if (!resolution.isControllable || !resolvedPath) {
2000
+ // Path is not controllable via standard resolution.
2001
+ // Try fallback: For useState values (cyScope*().functionCallReturnValue),
2002
+ // look for a related URL parameter like "varNameFromUrl" that might
2003
+ // control the initial state.
2004
+ const useStateMatch = translatedPath.match(
2005
+ /^cyScope\d+\(\)\.functionCallReturnValue$/,
2006
+ );
2007
+
2008
+ if (useStateMatch) {
2009
+ const useStatePattern = translatedPath.replace(
2010
+ /\.functionCallReturnValue$/,
2011
+ '',
2012
+ );
2013
+
2014
+ // Find the variable name that maps to this useState
2015
+ let useStateVarName: string | null = null;
2016
+ for (const [varName, varPath] of Object.entries(
2017
+ equivalentSignatureVariables,
2018
+ )) {
2019
+ if (varPath === useStatePattern) {
2020
+ useStateVarName = varName;
2021
+ break;
2022
+ }
2023
+ }
2024
+
2025
+ if (useStateVarName) {
2026
+ const urlParamName = `${useStateVarName}FromUrl`;
2027
+ const urlParamPath = equivalentSignatureVariables[urlParamName];
2028
+
2029
+ if (urlParamPath) {
2030
+ resolvedPath = urlParamName;
2031
+ }
2032
+ }
2033
+ }
2034
+ }
2035
+
2036
+ if (!resolvedPath) {
2037
+ allResolvable = false;
998
2038
  break;
999
2039
  }
1000
2040
 
1001
- resolvedPaths.set(condition.path, resolution.resolvedPath);
2041
+ resolvedPaths.set(condition.path, resolvedPath);
1002
2042
  }
1003
2043
 
1004
- if (allControllable && resolvedPaths.size > 0) {
2044
+ if (allResolvable && resolvedPaths.size > 0) {
1005
2045
  const compoundFlow = generateFlowFromCompound(
1006
2046
  compound,
1007
2047
  resolvedPaths,
1008
2048
  );
1009
- if (compoundFlow && !seenFlowIds.has(compoundFlow.id)) {
1010
- seenFlowIds.add(compoundFlow.id);
1011
- flows.push(compoundFlow);
2049
+ if (compoundFlow) {
2050
+ // Add gating conditions to compound flow (same as regular usage flows)
2051
+ if (gatingRequiredValues.length > 0) {
2052
+ // Filter out any gating values that are already in the flow
2053
+ const existingPaths = new Set(
2054
+ compoundFlow.requiredValues.map((rv) => rv.attributePath),
2055
+ );
2056
+ const newGatingValues = gatingRequiredValues.filter(
2057
+ (gv) => !existingPaths.has(gv.attributePath),
2058
+ );
2059
+ compoundFlow.requiredValues = [
2060
+ ...compoundFlow.requiredValues,
2061
+ ...newGatingValues,
2062
+ ];
2063
+
2064
+ // Update the flow ID to include gating conditions
2065
+ if (newGatingValues.length > 0) {
2066
+ const gatingIdPart = newGatingValues
2067
+ .map((gv) => `${gv.attributePath}-${gv.value}`)
2068
+ .join('-');
2069
+ compoundFlow.id = `${compoundFlow.id}-gated-${gatingIdPart}`;
2070
+ }
2071
+ }
2072
+
2073
+ if (!seenFlowIds.has(compoundFlow.id)) {
2074
+ seenFlowIds.add(compoundFlow.id);
2075
+ flows.push(compoundFlow);
2076
+ }
2077
+ }
2078
+ }
2079
+ }
2080
+
2081
+ // Process child's jsxRenderingUsages (array.map flows)
2082
+ // This generates array variation flows (empty, few, many) for arrays rendered in child
2083
+ if (childData.jsxRenderingUsages) {
2084
+ for (const jsxUsage of childData.jsxRenderingUsages) {
2085
+ // Translate the child path to a parent path
2086
+ const translatedPath = translateChildPathToParent(
2087
+ jsxUsage.path,
2088
+ childData.equivalentSignatureVariables,
2089
+ equivalentSignatureVariables,
2090
+ childName,
2091
+ );
2092
+
2093
+ if (!translatedPath) {
2094
+ continue;
2095
+ }
2096
+
2097
+ // Resolve to controllable path in parent context
2098
+ const resolution = resolvePathToControllable(
2099
+ translatedPath,
2100
+ attributesMap,
2101
+ equivalentSignatureVariables,
2102
+ fullToShortPathMap,
2103
+ );
2104
+
2105
+ let resolvedPath = resolution.resolvedPath;
2106
+
2107
+ // Try sourceEquivalencies fallback if not controllable
2108
+ if (!resolution.isControllable || !resolvedPath) {
2109
+ if (sourceEquivalencies) {
2110
+ // Build the sourceEquivalencies key
2111
+ // The child path (e.g., "workouts") maps to a prop path (e.g., "signature[0].workouts")
2112
+ let childPropName: string | null = null;
2113
+ for (const [varName, varPath] of Object.entries(
2114
+ childData.equivalentSignatureVariables,
2115
+ )) {
2116
+ if (
2117
+ jsxUsage.path === varName ||
2118
+ jsxUsage.path.startsWith(`${varName}.`)
2119
+ ) {
2120
+ childPropName = varName;
2121
+ break;
2122
+ }
2123
+ }
2124
+
2125
+ if (childPropName) {
2126
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
2127
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
2128
+
2129
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
2130
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
2131
+
2132
+ const dataSourceResolution = resolvePathToControllable(
2133
+ dataSourcePath,
2134
+ attributesMap,
2135
+ equivalentSignatureVariables,
2136
+ fullToShortPathMap,
2137
+ );
2138
+
2139
+ if (
2140
+ dataSourceResolution.isControllable &&
2141
+ dataSourceResolution.resolvedPath
2142
+ ) {
2143
+ resolvedPath = dataSourceResolution.resolvedPath;
2144
+ }
2145
+ }
2146
+ }
2147
+ }
2148
+ }
2149
+
2150
+ if (!resolvedPath) {
2151
+ continue;
2152
+ }
2153
+
2154
+ // Check for duplicates
2155
+ const normalizedPath = normalizePathForDeduplication(
2156
+ resolvedPath,
2157
+ fullToShortPathMap,
2158
+ );
2159
+ const dedupeKey = `${normalizedPath}:${jsxUsage.renderingType}`;
2160
+ if (seenNormalizedPaths.has(dedupeKey)) {
2161
+ continue;
2162
+ }
2163
+ seenNormalizedPaths.add(dedupeKey);
2164
+
2165
+ // Generate array variation flows for array-map rendering
2166
+ if (jsxUsage.renderingType === 'array-map') {
2167
+ const baseName = generateNameFromPath(resolvedPath);
2168
+ const pathSlug = pathToSlug(resolvedPath);
2169
+ const exclusiveGroup = `array-length-${pathSlug}`;
2170
+
2171
+ // Empty array flow
2172
+ const emptyFlow: ExecutionFlow = {
2173
+ id: `${pathSlug}-empty-array`,
2174
+ name: `${baseName} Empty`,
2175
+ description: `When ${baseName.toLowerCase()} array is empty`,
2176
+ requiredValues: [
2177
+ {
2178
+ attributePath: resolvedPath,
2179
+ value: '0',
2180
+ comparison: 'length<',
2181
+ valueType: 'array',
2182
+ },
2183
+ ...gatingRequiredValues,
2184
+ ],
2185
+ impact: 'medium',
2186
+ exclusiveGroup,
2187
+ sourceLocation: jsxUsage.sourceLocation
2188
+ ? {
2189
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2190
+ column: jsxUsage.sourceLocation.column,
2191
+ }
2192
+ : undefined,
2193
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2194
+ };
2195
+
2196
+ if (!seenFlowIds.has(emptyFlow.id)) {
2197
+ seenFlowIds.add(emptyFlow.id);
2198
+ flows.push(emptyFlow);
2199
+ }
2200
+
2201
+ // Few items flow (1-3)
2202
+ const fewFlow: ExecutionFlow = {
2203
+ id: `${pathSlug}-few-items`,
2204
+ name: `${baseName} Few Items`,
2205
+ description: `When ${baseName.toLowerCase()} array has 1-3 items`,
2206
+ requiredValues: [
2207
+ {
2208
+ attributePath: resolvedPath,
2209
+ value: '3',
2210
+ comparison: 'length<',
2211
+ valueType: 'array',
2212
+ },
2213
+ ...gatingRequiredValues,
2214
+ ],
2215
+ impact: 'low',
2216
+ exclusiveGroup,
2217
+ sourceLocation: jsxUsage.sourceLocation
2218
+ ? {
2219
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2220
+ column: jsxUsage.sourceLocation.column,
2221
+ }
2222
+ : undefined,
2223
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2224
+ };
2225
+
2226
+ if (!seenFlowIds.has(fewFlow.id)) {
2227
+ seenFlowIds.add(fewFlow.id);
2228
+ flows.push(fewFlow);
2229
+ }
2230
+
2231
+ // Many items flow (10+)
2232
+ const manyFlow: ExecutionFlow = {
2233
+ id: `${pathSlug}-many-items`,
2234
+ name: `${baseName} Many Items`,
2235
+ description: `When ${baseName.toLowerCase()} array has many items`,
2236
+ requiredValues: [
2237
+ {
2238
+ attributePath: resolvedPath,
2239
+ value: '10',
2240
+ comparison: 'length>',
2241
+ valueType: 'array',
2242
+ },
2243
+ ...gatingRequiredValues,
2244
+ ],
2245
+ impact: 'low',
2246
+ exclusiveGroup,
2247
+ sourceLocation: jsxUsage.sourceLocation
2248
+ ? {
2249
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2250
+ column: jsxUsage.sourceLocation.column,
2251
+ }
2252
+ : undefined,
2253
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2254
+ };
2255
+
2256
+ if (!seenFlowIds.has(manyFlow.id)) {
2257
+ seenFlowIds.add(manyFlow.id);
2258
+ flows.push(manyFlow);
2259
+ }
1012
2260
  }
1013
2261
  }
1014
2262
  }