I think I understand what is going on here. The behavior you are seeing is from scope searching when accessing variables in order to set them. When you set a variable without scoping it, ColdFusion will search the scopes to see if that variable exists anywhere first, and if it does, it will set it there.
In your first example:
<cfset qryChain = queryNew("id,next")>
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfset Next.id = qryChain.next>
</cfloop>
When you are creating your "Next" variable it is actually placing that variable into the VARIABLES scope, you can prove that if you dump the variables scope at any time during the looping process. You will see a "Next variable" with an empty struct.
The problem is in the next line. When you try to ACCESS the Next variable to set a new key into it, ColdFusion is finding the Next variable that exists in the Query result first, because while looping a query the Query scope (not really a scope, but it works like one in this case) has a higher precedence than the Variables scope. That variable does not contain a struct, so you will get an error about how you are referencing it.
Scope searching is happening, but it is not really while setting, it is while accessing in order to set.
Here is a working example to demonstrate this.
<cfset qryChain = queryNew("id,next")>
<cfset queryAddRow(qryChain, 3) />
<cfdump var="#qryChain#">
<!--- add some data so the cfloop kicks in --->
<cfloop query="qryChain">
<cfset Next = StructNew()>
<cfdump var="#variables#">
<cfset Next.id = qryChain.next>
<cfdump var="#qryChain#">
</cfloop>
in this example, I show that after you create the next variable it does exist in the variables scope, but when you immediately try to set a key into it without scoping that access you will instead get the NEXT variable from the current record in the query. This is only happening because the query happens to have record with a column name that happens to match the variable you are trying to use.
So why doesn't ColdFusion try to set the StructNew() into the Query scope (pseudo-scope)? The query cannot be manipulated using dot notation. Again, it is not really a scope. So in this sense it is read-only and is being skipped over
To manipulate a query resultset in CF you must use query functionsVARIABLES scope. Because unscoped variables are always placed in the VARIABLES scope. In the line where you try to set the id however, there is another phase going on in the background where it needs to access the variable in a read capacity first, and then try to perform the set. In this case, it DOES find the NEXT variable in the query because scope searching will occur first to determine in NEXT exists to set that key into and then when you try to set something to it, it fails.
As for your second set of examples, this is expected behavior and is easily explained.
In the first example, you are varing your variable (which places it in the local scope). You are then setting the value of that variable. When you set a value to a variable (without scoping it) ColdFusion checks to see if that variable already exists anywhere (so it does a scope search, again this is accessing at this point, not setting) it will find it in the local scope and then set the value there. After that you set the value again, this time properly scoping it, so no searching is done.
In the second example, you are not varing the variable when it is initially set, it does not exist anywhere, and so it is set to the variables scope. If it had already existed in the local scope, then ColdFusion would have found it and set it there (as in your first example), but since it did not exist, and it was not var'd it was set to the variables scope.
Finally, in your last example, you explicitly scope your variable and so it will be set in the local scope. You then set it again without scoping it. ColdFusion will find it in the local scope and overwrite it.
The moral of the story is, scope your variables. I tis important to get expected behavior. Scope-searching was never a good idea, but unfortunately, it is here to stay. I don't see anything here that I would call a bug, or even unpredictable behavior if you understand the way scope searching works.