Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions IMPLEMENTATION_PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ priorities.
delivered for ordinary, adjacent, quoted, here-string, redirect, standalone,
call-operator, dynamic-identity, and host-wrapper positions, with
current-scope state propagation and bounded expression rejection pinned
by the 372-entry executable corpus.
by the 383-entry executable corpus.
- [ ] Deliver typed PowerShell script-block execution regions before calling
tasks 7.5-7.7 complete. The corrected contract adds an execution-region
syntax node with independent origin, phase, timing, and cardinality rather than a
Expand Down Expand Up @@ -425,8 +425,27 @@ priorities.
proved child-process isolation while their body analysis stays fail
closed; an explicit alternate `-PSVersion` remains visible but incomplete
because it falls outside the pinned PowerShell 7 runtime model.
Child runspace jobs and parallel
blocks; deferred breakpoint/event/completion actions; then unknown
`ForEach-Object -Parallel` now publishes a concurrent per-input child-
runspace region, inherits the captured caller location, starts without
caller bindings or alias mutations, and isolates runspace-local child
exit from the host continuation, including `-AsJob`. Pooled-runspace command-
resolution mutation is joined across later activations; `-UseNewRunspace`
keeps activations independent. Live PowerShell probes pin both behaviors,
including the process-wide environment-provider exception: a possibly
escaping child mutation invalidates later host binding, command-
resolution, and location facts and also poisons later child activations
under `-UseNewRunspace`, which resets runspace-local but not process-wide
state. Runspace-global variables, functions, aliases, and location remain
isolated. The analyzer retains a bounded, case-insensitive set of exact
alias/function names whose identity changed and treats only matching later
invocations as possible process escapes; an ambiguous mutation or set
overflow fails closed to every unproved command name. This includes a
mutator such as `Set-Alias` being rebound to `Set-Item` before a later
authored `Set-Alias Env:...` invocation.
The generator-owned executable corpus now supports per-entry initial-
state mode and includes the promoted Parallel design case. Pinned child
runspace jobs and remote/session invocation remain before deferred
breakpoint/event/completion actions; then unknown
receiver and nested/adversarial matrices. Preserve script blocks proved
to be data as opaque values, expose ambiguous bodies with incomplete
facts, and fail atomically when any potentially executable interior is
Expand Down
18 changes: 17 additions & 1 deletion SPEC.POWERSHELL.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ The version-pinned PowerShell 7 catalog covers:
| `ForEach-Object -Begin` | Begin | Synchronous | Once | current runspace |
| `ForEach-Object -Process` / `-RemainingScripts` | Process, with binder-assigned Begin/End where applicable | Synchronous | OncePerInputObject | current runspace |
| `ForEach-Object -End` | End | Synchronous | Once | current runspace |
| `ForEach-Object -Parallel` | Process | Concurrent | OncePerInputObject | child runspace; exit isolated |
| `ForEach-Object -Parallel` | Process | Concurrent | OncePerInputObject | child runspace; runspace-local exit isolated; process-wide effects conservative |
| `Where-Object -FilterScript` | Filter | Synchronous | OncePerInputObject | current runspace |
| in-process `Invoke-Command -ScriptBlock` | Main | Synchronous | Once | child scope unless `-NoNewScope`; shared location |
| remote/session/SSH/VM/container `Invoke-Command` | Main | proved from complete parameter set | Unknown unless targets are proved | remote/child state; exit isolated |
Expand All @@ -540,6 +540,22 @@ The version-pinned PowerShell 7 catalog covers:
| `Set-PSBreakpoint -Action`, event `-Action` | Action | Deferred | ZeroOrMore | trigger-time state Unknown without proof |
| `Register-ArgumentCompleter -ScriptBlock` | Completion | Deferred | ZeroOrMore | completion-time state Unknown without proof |

Parallel child runspaces do not inherit ordinary caller variables or aliases,
and their runspace-local variable and location exit state does not flow into the
host continuation. They do share process-wide state such as the environment
provider. A supported or unknown child mutation that may affect such state
invalidates later host binding, command-resolution, and location facts. It also
invalidates later child-activation facts even with `-UseNewRunspace`, because a
fresh runspace is not a fresh process.
Runspace `global:` variable, function, alias, and location state remains local
to that runspace and does not by itself invalidate the host. Once child command
resolution is mutated, however, an exact changed alias or function name is
retained in bounded case-insensitive state. A later matching invocation whose
identity is no longer proved is a possible process-wide mutation and receives
the same conservative invalidation unless its identity is independently proved.
An ambiguous changed name, wildcard, or candidate-set overflow collapses to all
unproved command names rather than guessing.

The optional inbox `Microsoft.PowerShell.ThreadJob\Start-ThreadJob` follows
the initialization/main child-runspace model only when the caller's pinned
module baseline proves that identity. Otherwise it follows the unknown
Expand Down
18 changes: 17 additions & 1 deletion openspec/changes/v0-3-structured-shell-analysis/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ PowerShell 7.6.4 probes demonstrate that these are independent dimensions:
| local `Invoke-Command { ... }` | child scope unless `-NoNewScope` | shared | synchronous / once |
| `Measure-Command { ... }` / `Trace-Command -Expression { ... }` | shared current scope | shared | synchronous / once |
| `Start-Job { ... }` | child process state | inherited initial location; exit isolated | concurrent / once |
| `ForEach-Object -Parallel { ... }` | child runspace state | inherited initial location; exit isolated | concurrent / once per input |
| `ForEach-Object -Parallel { ... }` | child runspace state; process-wide effects may escape | inherited initial location; runspace-local exit isolated | concurrent / once per input |
| event, breakpoint, completion actions | trigger-time state | trigger-time state | deferred / zero or more |

The shell-specific analyzer owns inbound state, exit propagation, phase
Expand All @@ -383,6 +383,22 @@ Occurrence facts expose the effective result. This keeps the public structural
API narrow while avoiding the false claim that variables and location always
share one boundary.

An in-process child runspace is not a process boundary. Ordinary variables,
aliases, and PowerShell location are runspace-local, but environment-provider
mutation is process-wide and can affect the host and sibling runspaces. The
analyzer therefore invalidates later host binding, command-resolution, and
location facts after any possibly escaping child effect. The same invalidation
applies to later activations under `-UseNewRunspace`: the switch removes pooled
runspace-local state, but it does not create a separate process.
Runspace `global:` variables, functions, aliases, and location remain
runspace-local and do not themselves poison host facts. Exact alias/function
names changed inside a child are retained in a bounded, case-insensitive set;
only a matching subsequent invocation whose identity is not independently
proved is treated as a possible process-wide mutation. Ambiguous names,
wildcards, and set overflow collapse to all unproved command names. This
prevents both an alias to `Set-Item` and a rebound `Set-Alias` mutator from
hiding an environment-provider write without tainting unrelated names.

`SimpleCommandSyntax.ExecutionRegions` preserves authored script-block order.
The analyzer separately applies semantic phase order. PowerShell's binder can
assign Begin, Process, and End roles to multiple `ForEach-Object` blocks even
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,19 @@ from the complete parameter set.

`Start-Job` executes initialization before its main block in a child process;
`ForEach-Object -Parallel` and a proved `Start-ThreadJob` execute in child
runspaces. Their exit mutation does not flow into the containing continuation.
runspaces. Runspace-local variable and location exit mutation does not flow into
the containing continuation. In-process child runspaces share process-wide
state such as the environment provider, so a possibly escaping mutation SHALL
invalidate later host binding, command-resolution, and location facts. It SHALL
also invalidate later child-activation facts under `-UseNewRunspace`, which
creates a fresh runspace but not a fresh process.
Runspace `global:` variables, functions, aliases, and location SHALL remain
runspace-local and SHALL NOT by themselves invalidate host facts. After child
command resolution is mutated, each exact changed alias or function name SHALL
be retained in bounded case-insensitive state. A later matching invocation
whose identity is not independently proved SHALL be treated as a possible
process-wide mutation. An ambiguous name, wildcard, or candidate-set overflow
SHALL fail closed to every unproved command name.
Breakpoint actions, event actions, and argument completers are deferred and
may execute zero or more times; their trigger-time cwd and mutable state are
Unknown unless independently proved. Deferred commands remain in the
Expand Down
Loading