If we broaden our thinking to look at a computer process as a mathematical function, i.e., if we think about a computer process as
y = f(x)
Where y is the behavior of the function (what final state it results in / resolves to), f is the process itself, and x is the parameter or list of parameters that are passed to f, then if that process reads and subsequently alters its behavior in relation to any environment variable, then we should no longer think of that function as merely y = f(x), but as
y = f(x, z)
where Z is the set of environment variables that are consumed by, and subsequently alter the behavior of f, resulting in a different y.
Now not all processes read environment variables and alter their behaviors because of an environment variable being set.
That is true.
Those processes remain as y = f(x).
But if a process does read and does act on an environment variable (usually without the end-users' knowledge, because how many users keep track of which programs read/act on -- which environment variables?), then then this the same as passing extra parameters -- y = f(x, z) -- to the function!
(Sub-observation: A future OS would have an API call to granularly read a single named environment variable at a time (not the entire block of them at once!), and use of this call could be logged and sorted by program, and there would be a user-settable control to granularly determine which environment variables could be read by which programs/processes...)
>They're not globals, since they're copied into sub-processes; so mutation doesn't propagate upwards.
Mutation or non-mutation upwards -- is not the issue.
The issue is: If users run program f and they pass it various command line parameters x, and they want y, then if environment variables are present and if they alter the behavior of that program, then what the users are really getting is y = f(x, z), which may not be the behavior/result they want, because z influences the behavior, and is passed in a not-really-all-that-transparent manner (most people usually don't check, log or modify environment variables nor account for them in the determinism of their programs -- unless something is broken...)
Phrased another way -- a future OS would have some way of logging everything, all state information (including environment variables which includes registry settings) -- that go into any given program.
Now maybe environment variables aren't "globals" in the strictest definition...
But let's see...
In most modern operating systems as of 2025, environment variables can easily be read by most programs, functions/procedures/methods inside of those programs, sub-functions/sub-procedures/sub-methods of those programs, etc., etc., etc.
Once they can be read... they can become the state of one or more variables in that program its functions, sub-functions, etc.
And once it can become the state of those one or more internal variables, the program can alter its behavior / result -- based on them.
Global variables -- can do the same exact same thing to a program.
So I'll leave it as a linguistic/semantic debate to future readers, mathematicians, programmers and OS designers -- as to whether or not environment variables (and related globally readable objects such as the Windows Registry) are global variables...
(You are very much correct about environment variables -- if they are mutated (aka written to/overwritten) by by a sub-process, then that mutation doesn't typically propapagate upwards the parent/creator process chain -- but perhaps in the context of my discussion, I am interested/concerned about -- the global readability/accessibility of environment variables... But you are very much correct in your statement!)
I think the key point that I am trying to make is that more transparency/insight/logging could always be had as to where exactly programs get ALL of their inputs from (this includes environment variables, this includes API calls which may differ machine to machine, etc., etc.), and what average end users are made aware of...