User-reported bug: a variable marked internal in an upstream Context
node could not be read by downstream Combine / Derivation modules —
they saw it as missing. The previous design treated the
PIPELINE_CONTEXT socket as the public boundary and stripped both
engine `__` keys AND user-flagged internal vars at socket emit.
Correct semantic: internal = "don't render as standalone $var in the
final prompt." It does NOT mean "don't propagate through context."
Downstream nodes still need to read internal vars to compose them
into other vars (e.g. a Combine builds `$subject` from an internal
`$style` + a public `$pose`). Only the final PromptAssembler must
skip internal vars at template substitution time.
Implementation:
- engine/context.py: split the filter into two helpers.
`strip_engine_internals` drops only `__`-prefixed engine keys —
used at the socket boundary. `strip_internals` keeps the original
dual-layer behavior (engine + user-flagged) — used at the prompt-
render boundary.
- wp_nodes/types.py: socket emit now uses `strip_engine_internals`,
so user-flagged internal vars cross node boundaries as regular
context keys. `__wp_internal_flags__` is added to
`_CROSS_NODE_INTERNAL_KEYS` so downstream PromptAssemblers can
still know which vars to filter at render.
- wp_nodes/assembler_node.py: before resolving variables, merges
the cross-node `__wp_internal_flags__` back into a copy of the
socket context and calls `strip_internals` — drops both engine
keys and user-flagged internal vars. Net effect on the rendered
prompt: unchanged from the user's perspective (internal vars
still don't appear as standalone $vars).
- tests/test_context.py: regression tests for the new helper +
propagation behavior.