@codeyam/codeyam-cli 0.1.0-staging.323686 → 0.1.0-staging.483fdc2

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 (171) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/ai/index.ts +6 -1
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
  6. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
  7. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
  8. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
  11. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  12. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
  17. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
  18. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
  19. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
  20. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  21. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
  22. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
  23. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  24. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  25. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
  26. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
  27. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  28. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  29. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  30. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  31. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  32. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  33. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  34. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  35. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
  36. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  37. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  38. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
  39. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  40. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  41. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
  42. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  43. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
  44. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
  45. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
  46. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  47. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  48. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  49. package/analyzer-template/packages/aws/package.json +1 -1
  50. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  51. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  52. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  53. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  54. package/analyzer-template/packages/github/package.json +1 -1
  55. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
  56. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  57. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  58. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  59. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  60. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  61. package/analyzer-template/project/constructMockCode.ts +54 -9
  62. package/analyzer-template/project/writeMockDataTsx.ts +73 -2
  63. package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
  64. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  65. package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
  66. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  67. package/codeyam-cli/scripts/apply-setup.js +146 -0
  68. package/codeyam-cli/scripts/apply-setup.js.map +1 -1
  69. package/codeyam-cli/src/commands/debug.js +7 -5
  70. package/codeyam-cli/src/commands/debug.js.map +1 -1
  71. package/codeyam-cli/src/utils/install-skills.js +22 -0
  72. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  73. package/codeyam-cli/src/utils/reviewedRules.js +92 -0
  74. package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
  75. package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
  76. package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
  78. package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
  79. package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
  80. package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
  81. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  82. package/codeyam-cli/src/webserver/build-info.json +5 -5
  83. package/codeyam-cli/templates/codeyam:memory.md +174 -233
  84. package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
  85. package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
  86. package/codeyam-cli/templates/rules-instructions.md +126 -0
  87. package/package.json +1 -1
  88. package/packages/ai/index.js +2 -1
  89. package/packages/ai/index.js.map +1 -1
  90. package/packages/ai/src/lib/analyzeScope.js +29 -12
  91. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  92. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
  93. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  94. package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
  95. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  96. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
  97. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  98. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  99. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  100. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  101. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  102. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
  103. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  104. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
  105. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  106. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  107. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  108. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  109. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  110. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
  111. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  112. package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
  113. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  114. package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
  115. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  116. package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
  117. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  118. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
  119. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  120. package/packages/ai/src/lib/mergeStatements.js +70 -51
  121. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  122. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  123. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  124. package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
  125. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  126. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  127. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  128. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  129. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  130. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  131. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  132. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  133. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  134. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  135. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  136. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  137. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  138. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  139. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  140. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  141. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  142. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  143. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
  144. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  145. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  146. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  147. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  148. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  149. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
  150. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  151. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  152. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  153. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  154. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  155. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
  156. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  157. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  158. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  159. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
  160. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  161. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
  162. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  163. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
  164. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  165. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  166. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  167. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  168. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
  170. package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
  171. package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
@@ -25,6 +25,111 @@ import {
25
25
  } from './conditionalEffectsExtractor';
26
26
  import { detectArrayDerivedPattern } from './arrayDerivationDetector';
27
27
 
28
+ /**
29
+ * Checks if a JSX element has props that reference variables from the parent scope.
30
+ * This is used to detect unconditionally-rendered children that should have their
31
+ * execution flows merged into the parent.
32
+ *
33
+ * We want to track children where the parent controls data that affects the child's
34
+ * conditional rendering. Static props (like title="Dashboard") don't need tracking
35
+ * because they don't create variable execution flows.
36
+ *
37
+ * Examples:
38
+ * - <WorkoutsView workouts={workouts} /> → true (workouts is a variable)
39
+ * - <ItemList items={items} count={count} /> → true (items, count are variables)
40
+ * - <Header title="Dashboard" /> → false (static string)
41
+ * - <Footer /> → false (no props)
42
+ * - <Button onClick={handleClick} /> → false (only callback, no data props)
43
+ *
44
+ * @returns true if the component has at least one prop that references a variable
45
+ * (excluding callbacks which typically start with 'on' or 'handle')
46
+ */
47
+ function hasDataPropsFromParent(
48
+ node: ts.JsxElement | ts.JsxSelfClosingElement,
49
+ componentName: string,
50
+ ): { hasDataProps: boolean; dataProps: string[] } {
51
+ const attributes = ts.isJsxElement(node)
52
+ ? node.openingElement.attributes.properties
53
+ : node.attributes.properties;
54
+
55
+ const dataProps: string[] = [];
56
+
57
+ for (const attr of attributes) {
58
+ // Spread attributes always reference parent data: {...props}
59
+ if (ts.isJsxSpreadAttribute(attr)) {
60
+ const spreadText = attr.expression?.getText() || '...spread';
61
+ dataProps.push(`{...${spreadText}}`);
62
+ console.log(
63
+ `[UnconditionalChild] ${componentName}: Found spread attribute {${spreadText}}`,
64
+ );
65
+ continue;
66
+ }
67
+
68
+ if (ts.isJsxAttribute(attr)) {
69
+ const propName = attr.name.getText();
70
+
71
+ // Skip callback props - they don't create data-driven execution flows
72
+ // Callbacks typically start with 'on' (onClick, onChange) or 'handle' (handleSubmit)
73
+ if (
74
+ propName.startsWith('on') ||
75
+ propName.startsWith('handle') ||
76
+ propName === 'ref'
77
+ ) {
78
+ console.log(
79
+ `[UnconditionalChild] ${componentName}: Skipping callback prop '${propName}'`,
80
+ );
81
+ continue;
82
+ }
83
+
84
+ // Check if the prop value is a JSX expression (references a variable)
85
+ // vs a string literal which is static
86
+ if (attr.initializer) {
87
+ if (ts.isJsxExpression(attr.initializer)) {
88
+ // JSX expression like prop={value} - this references a variable
89
+ // Could be a simple identifier, property access, or more complex expression
90
+ const expression = attr.initializer.expression;
91
+ if (expression) {
92
+ // Skip if it's just a function/arrow function (callback)
93
+ if (
94
+ ts.isArrowFunction(expression) ||
95
+ ts.isFunctionExpression(expression)
96
+ ) {
97
+ console.log(
98
+ `[UnconditionalChild] ${componentName}: Skipping inline callback prop '${propName}'`,
99
+ );
100
+ continue;
101
+ }
102
+ // This is a data prop that references parent state/props
103
+ const exprText = expression.getText();
104
+ dataProps.push(`${propName}={${exprText}}`);
105
+ console.log(
106
+ `[UnconditionalChild] ${componentName}: Found data prop '${propName}' = {${exprText}}`,
107
+ );
108
+ }
109
+ } else {
110
+ // String literals like prop="value" are static
111
+ console.log(
112
+ `[UnconditionalChild] ${componentName}: Skipping static prop '${propName}'`,
113
+ );
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ const hasDataProps = dataProps.length > 0;
120
+ if (hasDataProps) {
121
+ console.log(
122
+ `[UnconditionalChild] ${componentName}: Has ${dataProps.length} data props: [${dataProps.join(', ')}]`,
123
+ );
124
+ } else {
125
+ console.log(
126
+ `[UnconditionalChild] ${componentName}: No data props found, will NOT track`,
127
+ );
128
+ }
129
+
130
+ return { hasDataProps, dataProps };
131
+ }
132
+
28
133
  /**
29
134
  * Extracts the component name from a JSX element.
30
135
  * Returns null for intrinsic elements (div, span, etc.) since we only care about
@@ -921,18 +1026,50 @@ function extractConditionalsFromJsx(
921
1026
  // Recursively process nested JSX elements - Fix 32: pass parent conditions
922
1027
  else if (ts.isJsxElement(child)) {
923
1028
  // Check if this is a user-defined component (vs intrinsic element like div)
924
- // If it's a component AND there are parent conditions, record the gating conditions
925
1029
  const componentName = getComponentNameFromJsx(child);
926
- if (componentName && parentConditions.length > 0) {
927
- for (const condition of parentConditions) {
928
- context.addChildBoundaryGatingCondition(componentName, {
929
- path: condition.path,
930
- conditionType: 'truthiness',
931
- location: 'ternary',
932
- sourceLocation: condition.sourceLocation,
933
- controlsJsxRendering: true,
934
- isNegated: condition.isNegated,
935
- });
1030
+ if (componentName) {
1031
+ if (parentConditions.length > 0) {
1032
+ // If there are parent conditions, record them as gating conditions
1033
+ console.log(
1034
+ `[ChildBoundary] ${componentName}: Conditionally rendered with ${parentConditions.length} gating conditions`,
1035
+ );
1036
+ for (const condition of parentConditions) {
1037
+ console.log(
1038
+ `[ChildBoundary] ${componentName}: Adding gating condition path='${condition.path}' isNegated=${condition.isNegated}`,
1039
+ );
1040
+ context.addChildBoundaryGatingCondition(componentName, {
1041
+ path: condition.path,
1042
+ conditionType: 'truthiness',
1043
+ location: 'ternary',
1044
+ sourceLocation: condition.sourceLocation,
1045
+ controlsJsxRendering: true,
1046
+ isNegated: condition.isNegated,
1047
+ });
1048
+ }
1049
+ } else {
1050
+ // No parent conditions - check if it has data props for unconditional tracking
1051
+ console.log(
1052
+ `[ChildBoundary] ${componentName}: Checking for unconditional rendering with data props...`,
1053
+ );
1054
+ const { hasDataProps, dataProps } = hasDataPropsFromParent(
1055
+ child,
1056
+ componentName,
1057
+ );
1058
+ if (hasDataProps) {
1059
+ // Fix: Track unconditionally-rendered children that receive data props
1060
+ // These need to be tracked for flow merging even without gating conditions
1061
+ // Example: <WorkoutsView workouts={workouts} /> - parent controls workouts data
1062
+ console.log(
1063
+ `[ChildBoundary] ${componentName}: TRACKING as unconditionally-rendered with data props: [${dataProps.join(', ')}]`,
1064
+ );
1065
+ context.addChildBoundaryGatingCondition(componentName, {
1066
+ path: '__unconditional__',
1067
+ conditionType: 'truthiness',
1068
+ location: 'unconditional',
1069
+ controlsJsxRendering: true,
1070
+ isNegated: false,
1071
+ });
1072
+ }
936
1073
  }
937
1074
  }
938
1075
  extractConditionalsFromJsx(child, context, parentConditions);
@@ -940,18 +1077,48 @@ function extractConditionalsFromJsx(
940
1077
  // Handle self-closing JSX elements (e.g., <ScenarioViewer />)
941
1078
  else if (ts.isJsxSelfClosingElement(child)) {
942
1079
  // Check if this is a user-defined component (vs intrinsic element like div)
943
- // If it's a component AND there are parent conditions, record the gating conditions
944
1080
  const componentName = getComponentNameFromJsx(child);
945
- if (componentName && parentConditions.length > 0) {
946
- for (const condition of parentConditions) {
947
- context.addChildBoundaryGatingCondition(componentName, {
948
- path: condition.path,
949
- conditionType: 'truthiness',
950
- location: 'ternary',
951
- sourceLocation: condition.sourceLocation,
952
- controlsJsxRendering: true,
953
- isNegated: condition.isNegated,
954
- });
1081
+ if (componentName) {
1082
+ if (parentConditions.length > 0) {
1083
+ // If there are parent conditions, record them as gating conditions
1084
+ console.log(
1085
+ `[ChildBoundary] ${componentName}: Conditionally rendered (self-closing) with ${parentConditions.length} gating conditions`,
1086
+ );
1087
+ for (const condition of parentConditions) {
1088
+ console.log(
1089
+ `[ChildBoundary] ${componentName}: Adding gating condition path='${condition.path}' isNegated=${condition.isNegated}`,
1090
+ );
1091
+ context.addChildBoundaryGatingCondition(componentName, {
1092
+ path: condition.path,
1093
+ conditionType: 'truthiness',
1094
+ location: 'ternary',
1095
+ sourceLocation: condition.sourceLocation,
1096
+ controlsJsxRendering: true,
1097
+ isNegated: condition.isNegated,
1098
+ });
1099
+ }
1100
+ } else {
1101
+ // No parent conditions - check if it has data props for unconditional tracking
1102
+ console.log(
1103
+ `[ChildBoundary] ${componentName}: Checking for unconditional rendering (self-closing) with data props...`,
1104
+ );
1105
+ const { hasDataProps, dataProps } = hasDataPropsFromParent(
1106
+ child,
1107
+ componentName,
1108
+ );
1109
+ if (hasDataProps) {
1110
+ // Fix: Track unconditionally-rendered children that receive data props
1111
+ console.log(
1112
+ `[ChildBoundary] ${componentName}: TRACKING as unconditionally-rendered (self-closing) with data props: [${dataProps.join(', ')}]`,
1113
+ );
1114
+ context.addChildBoundaryGatingCondition(componentName, {
1115
+ path: '__unconditional__',
1116
+ conditionType: 'truthiness',
1117
+ location: 'unconditional',
1118
+ controlsJsxRendering: true,
1119
+ isNegated: false,
1120
+ });
1121
+ }
955
1122
  }
956
1123
  }
957
1124
  // Self-closing elements have no children, so no recursion needed
@@ -1449,7 +1616,7 @@ export function processExpression({
1449
1616
  const structure = context.getStructure();
1450
1617
 
1451
1618
  // Propagate existing equivalencies for sub-properties
1452
- for (const [key, value] of Object.entries(equivalentVariables)) {
1619
+ for (const [key, rawValue] of Object.entries(equivalentVariables)) {
1453
1620
  // Check if this equivalency is for a sub-property of the identifier
1454
1621
  // e.g., completeDataStructure['Function Arguments'] or completeDataStructure.foo
1455
1622
  if (
@@ -1460,8 +1627,14 @@ export function processExpression({
1460
1627
  const newTargetPath = StructuredPath.fromBase(
1461
1628
  targetPath.toString() + subPath,
1462
1629
  );
1463
- const valuePath = StructuredPath.fromBase(value);
1464
- context.addEquivalence(newTargetPath, valuePath);
1630
+ // Handle both string and string[] values
1631
+ const values = Array.isArray(rawValue) ? rawValue : [rawValue];
1632
+ for (const value of values) {
1633
+ if (typeof value === 'string') {
1634
+ const valuePath = StructuredPath.fromBase(value);
1635
+ context.addEquivalence(newTargetPath, valuePath);
1636
+ }
1637
+ }
1465
1638
  }
1466
1639
  }
1467
1640
 
@@ -1945,23 +2118,47 @@ export function processExpression({
1945
2118
  // - items[] (from the find result)
1946
2119
  // - null (from the fallback)
1947
2120
  if (targetPath) {
1948
- // Process left side recursively to capture its full equivalency chain
2121
+ // Get paths for both sides
2122
+ const leftPath = StructuredPath.fromNode(
2123
+ unwrappedNode.left,
2124
+ context.sourceFile,
2125
+ );
2126
+ const rightPath = StructuredPath.fromNode(
2127
+ unwrappedNode.right,
2128
+ context.sourceFile,
2129
+ );
2130
+
2131
+ // Collect all valid paths
2132
+ const allPaths: StructuredPath[] = [];
2133
+ if (leftPath) allPaths.push(leftPath);
2134
+ if (rightPath) allPaths.push(rightPath);
2135
+
2136
+ // Add multiple equivalencies to track both sources
2137
+ if (allPaths.length > 0) {
2138
+ context.addMultipleEquivalencies(targetPath, allPaths);
2139
+ }
2140
+
2141
+ // Process both sides to capture their internal structures
1949
2142
  processExpression({
1950
2143
  node: unwrappedNode.left,
1951
2144
  context,
1952
- targetPath,
1953
2145
  });
1954
- // Process right side recursively for completeness
1955
2146
  processExpression({
1956
2147
  node: unwrappedNode.right,
1957
2148
  context,
1958
- targetPath,
1959
2149
  });
1960
- // Set resultPath to left side for type inference
1961
- resultPath = StructuredPath.fromNode(
1962
- unwrappedNode.left,
1963
- context.sourceFile,
1964
- );
2150
+
2151
+ // Register the type for the target path
2152
+ const leftType = context.inferTypeFromNode(unwrappedNode.left);
2153
+ const rightType = context.inferTypeFromNode(unwrappedNode.right);
2154
+ const orResultType = isDefinedType(leftType)
2155
+ ? leftType
2156
+ : rightType || 'unknown';
2157
+ context.addType(targetPath, orResultType);
2158
+
2159
+ // Return early - we've already handled equivalencies with addMultipleEquivalencies
2160
+ // Don't fall through to the generic addEquivalence call below
2161
+ return true;
1965
2162
  }
1966
2163
  // Note: When there's no targetPath, we don't recursively process
1967
2164
  // because || is often used in boolean contexts where the full expression matters
@@ -2071,24 +2268,56 @@ export function processExpression({
2071
2268
 
2072
2269
  // Get the source expression path (e.g., the object for obj.method())
2073
2270
  const sourceExpr = unwrappedNode.expression.expression;
2074
- const sourcePath = StructuredPath.fromNode(
2075
- sourceExpr,
2076
- context.sourceFile,
2077
- );
2271
+ const unwrappedSourceExpr = unwrapExpression(sourceExpr);
2272
+
2273
+ // When the source is a ternary expression like (cond ? arr : arr.slice()),
2274
+ // apply method semantics to BOTH branches directly. The ternary itself isn't
2275
+ // a variable - it's just a choice between two paths that both flow to the result.
2276
+ if (ts.isConditionalExpression(unwrappedSourceExpr)) {
2277
+ const branches = [
2278
+ unwrappedSourceExpr.whenTrue,
2279
+ unwrappedSourceExpr.whenFalse,
2280
+ ];
2078
2281
 
2079
- if (sourcePath) {
2080
- // For array-specific semantics (like push), verify the source is actually an array
2081
- // This prevents router.push() from being mistakenly treated as Array.push()
2082
- const isArraySemantics = semantics instanceof ArrayPushSemantics;
2083
- const shouldApply =
2084
- !isArraySemantics ||
2085
- isLikelyArrayType(sourceExpr, context.typeChecker);
2282
+ for (const branch of branches) {
2283
+ const branchPath = StructuredPath.fromNode(
2284
+ branch,
2285
+ context.sourceFile,
2286
+ );
2287
+ if (branchPath) {
2288
+ const isArraySemantics = semantics instanceof ArrayPushSemantics;
2289
+ const shouldApply =
2290
+ !isArraySemantics ||
2291
+ isLikelyArrayType(branch, context.typeChecker);
2292
+
2293
+ if (shouldApply) {
2294
+ semantics.addEquivalences(callPath, branchPath, context);
2295
+ returnType = semantics.getReturnType();
2296
+ handledBySemantics = true;
2297
+ }
2298
+ }
2299
+ }
2300
+ } else {
2301
+ // Regular (non-ternary) source expression
2302
+ const sourcePath = StructuredPath.fromNode(
2303
+ sourceExpr,
2304
+ context.sourceFile,
2305
+ );
2086
2306
 
2087
- if (shouldApply) {
2088
- // Apply method semantics
2089
- semantics.addEquivalences(callPath, sourcePath, context);
2090
- returnType = semantics.getReturnType();
2091
- handledBySemantics = true;
2307
+ if (sourcePath) {
2308
+ // For array-specific semantics (like push), verify the source is actually an array
2309
+ // This prevents router.push() from being mistakenly treated as Array.push()
2310
+ const isArraySemantics = semantics instanceof ArrayPushSemantics;
2311
+ const shouldApply =
2312
+ !isArraySemantics ||
2313
+ isLikelyArrayType(sourceExpr, context.typeChecker);
2314
+
2315
+ if (shouldApply) {
2316
+ // Apply method semantics
2317
+ semantics.addEquivalences(callPath, sourcePath, context);
2318
+ returnType = semantics.getReturnType();
2319
+ handledBySemantics = true;
2320
+ }
2092
2321
  }
2093
2322
  }
2094
2323
  }
@@ -2646,6 +2875,35 @@ export function processExpression({
2646
2875
 
2647
2876
  // Handle Arrow Functions: (p) => p.prop, (a, b) => { ... }
2648
2877
  if (ts.isArrowFunction(unwrappedNode)) {
2878
+ // If this arrow function is a child boundary (e.g., a .map() callback),
2879
+ // don't process its parameters here - they will be processed when the
2880
+ // child scope is analyzed separately. This prevents parameter variables
2881
+ // from leaking into the parent scope's equivalencies.
2882
+ // Check if this arrow function is a child boundary (i.e., should be processed
2883
+ // as a separate child scope, not here in the parent scope).
2884
+ //
2885
+ // We use two checks because childBoundary positions can be unreliable:
2886
+ // 1. Position-based check (standard isChildBoundary)
2887
+ // 2. Text-based check: if the arrow function text doesn't appear in the
2888
+ // statement text, it was replaced with a cyScope placeholder
2889
+ const isChildBoundary = context.isChildBoundary(unwrappedNode);
2890
+
2891
+ // Text-based child scope detection for when positions are unreliable
2892
+ const arrowFnText = unwrappedNode.getText(context.sourceFile);
2893
+ const firstLine = arrowFnText.split('\n')[0].trim();
2894
+ const searchText = firstLine.substring(0, Math.min(20, firstLine.length));
2895
+ const isInStatementText = context.statementInfo.text.includes(searchText);
2896
+ const isChildScope = !isInStatementText && arrowFnText.length > 10;
2897
+
2898
+ if (isChildBoundary || isChildScope) {
2899
+ // The method semantics (e.g., ArrayMapSemantics) have already established
2900
+ // the necessary equivalences between the child scope placeholder and array elements
2901
+ if (targetPath) {
2902
+ context.addType(targetPath, 'function');
2903
+ }
2904
+ return true;
2905
+ }
2906
+
2649
2907
  // Create a path for the function
2650
2908
  const functionPath = StructuredPath.empty();
2651
2909
 
@@ -287,8 +287,9 @@ export interface AstScopeAnalysisResult {
287
287
  // Variable structure map (variable path -> type)
288
288
  structure: Record<string, string>;
289
289
 
290
- // Variable equivalence map (variable path -> equivalent variable path)
291
- equivalentVariables: Record<string, string>;
290
+ // Variable equivalence map (variable path -> equivalent variable path(s))
291
+ // Supports multiple equivalencies per key for OR expressions (e.g., x = a || b)
292
+ equivalentVariables: Record<string, string | string[]>;
292
293
 
293
294
  // Environment variables accessed in this scope
294
295
  environmentVariables: string[];
@@ -348,8 +349,9 @@ export interface ConditionalUsage {
348
349
 
349
350
  /**
350
351
  * Where this conditional usage occurs
352
+ * 'unconditional' is used for children that are always rendered but receive data props
351
353
  */
352
- location: 'if' | 'ternary' | 'logical-and' | 'switch';
354
+ location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
353
355
 
354
356
  /**
355
357
  * Source location information for this conditional usage
@@ -441,7 +443,7 @@ export interface CompoundConditional {
441
443
  orGroupId?: string;
442
444
  }[];
443
445
  /** Where this compound conditional occurs */
444
- location: 'if' | 'ternary' | 'logical-and' | 'switch';
446
+ location: 'if' | 'ternary' | 'logical-and' | 'switch' | 'unconditional';
445
447
  /** Source location for the entire compound expression */
446
448
  sourceLocation: {
447
449
  lineNumber: number;
@@ -509,6 +511,13 @@ export interface AnalysisContext {
509
511
  // Add an equivalence relationship between two variable paths
510
512
  addEquivalence(leftSide: StructuredPath, rightSide: StructuredPath): void;
511
513
 
514
+ // Add multiple equivalence relationships for a single variable path.
515
+ // Used for OR expressions like `x = a || b` where x is equivalent to both a and b.
516
+ addMultipleEquivalencies(
517
+ leftSide: StructuredPath,
518
+ rightSides: StructuredPath[],
519
+ ): void;
520
+
512
521
  // Add a type for a variable path
513
522
  addType(path: StructuredPath, type: string): void;
514
523
 
@@ -555,8 +564,8 @@ export interface AnalysisContext {
555
564
  // Get the built-up structure
556
565
  getStructure(): Record<string, string>;
557
566
 
558
- // Get the built-up equivalence map
559
- getEquivalentVariables(): Record<string, string>;
567
+ // Get the built-up equivalence map (supports multiple values for OR expressions)
568
+ getEquivalentVariables(): Record<string, string | string[]>;
560
569
 
561
570
  // Utilties for child boundaries (need to be skipped while capturing equivalencies)
562
571
  getChildBoundary(