Consolidate local work: skin depth, Live Ops density, WinPSCompat leak fix - #87
Merged
Conversation
The shell rendered its icons from appicon.png, which is the untinted orange source art rather than a skin, so the window, tray, exe and installer all shipped orange while the app itself was blue. Render the static set from appicon-dark.png instead, and let the window and tray icon track the color skin at runtime: the SPA already rewrites <link rel="icon"> to /appicon-<skin>.png on every skin switch, Chromium reports that as page-favicon-updated, and skins.ts maps it back onto the generated assets/skins/<id>.* pair. Reusing that signal keeps the production SPA window preload-less and IPC-free. The exe, installer and Start-Menu icon cannot follow a skin - Windows resolves those from the file - which is why the shipped default matters. Skin ids are not mirrored in the shell. The generator emits one variant per appicon-*.png the SPA ships and the shell resolves against the files on disk, so a skin added to the UI without a regenerated icon set keeps the current icon instead of picking a wrong one. Ids are held to a strict charset before they reach a path join, since they arrive from the renderer. Icon generation moves out of Build-DesktopInstaller.ps1 into scripts/generate-desktop-icons.ps1 so the from-source dev loop can populate the gitignored assets folder via `npm run icons`.
Three related tweaks to the same depth system. Azur (default dark) read as embossed rather than as plates lying on the page. Scale every depth cue down about a quarter - lit top rim, side and bottom rims, face gradient, sheen and the whole shadow ramp - keeping the geometry so the hard contact line survives. The press, primary-button and input values that do not flow through the tokens are scaled with them; leaving the press depth untouched against a softened plate would read as a larger travel than the button has. The default light skin had no depth layer at all: cards and buttons were a border on a white fill with no shadow, which is why the surfaces looked flat. Add a Lumen block mirroring Azur's ingredients and selector list. One divergence is forced by the physics - a white plate on a near-white ground has no headroom for a lit top rim, so the bevel runs monotonically darker downward and the lift comes from the shadow. Alphas are far lower for the same perceived depth. The button rule deliberately omits background-color and excludes the tinted variants, which would otherwise lose their fill and border colour to the bevel. The shared table column header sat only ~4% under the white card body in light mode and stopped reading as its own band. Move it one step down the ramp for light only; dark keeps its value, where a bigger step would bring back the harsh near-black edge.
Live Ops timeline: the raw-bar cap hid workflows that ran inside the window but whose runs all fell past it, so they read as idle — the exact misreading the timeline exists to prevent. The ops graph now carries per-workflow density buckets, and lane allocation reserves a lane for density-only workflows so their strip has somewhere to draw. AI chat: the starter prompts assumed the DB/text2sql knowledge source, which is off by default and privileged-only. Picking one without it just produced "source not available". Prompts now follow the resolved capabilities and fall back to a lite set, held back until caps resolve so the lite set does not flash first. REST, CLI, MCP, docs and the docs-ui corpus kept in sync.
Root cause of the 9-GB / 472-thread growth of a long-running dev API
(diagnosed via full dump + gcroot): scheduled workflows targeting the
localhost machine run engine-local in the PowerShell SDK runspace pool,
and the SDK ships only the eight core modules. A local Compress-Archive
call therefore resolved to the Windows PowerShell 5.1 copy of
Microsoft.PowerShell.Archive (edition Desktop), which PowerShell loaded
through implicit Windows compatibility: one `powershell.exe -Version 5.1
-s` child (~148 MB) per pool runspace, registered as WinPSCompatSession
in that runspace's session repository and never closed. Pool runspaces
live forever, so sessions, their transport pump threads and child
processes accumulated without bound (~1 MB parent heap per execution
plus a child process per fresh runspace).
Fix, two parts:
- Bundle Microsoft.PowerShell.Archive 1.2.5 (MIT, unmodified, the same
version full pwsh ships) and import it eagerly into every pool
runspace via the InitialSessionState, so Compress-/Expand-Archive run
natively in-process and the auto-loader never goes looking.
- Disable implicit WinCompat for the in-process SDK by placing
powershell.config.json (DisableImplicitWinCompat) next to
System.Management.Automation.dll via Directory.Build.targets (build
and publish layouts). Desktop-only modules now fail loudly ("disabled
in the settings file") instead of silently spawning compat sessions;
explicit `Import-Module -UseWindowsPowerShell` remains available, and
standalone powershell.exe/pwsh process engines are unaffected (own
PSHOME).
Guard tests pin both halves: the archive roundtrip must resolve from the
bundled PSModules copy, and importing the Desktop-only System32 module
must fail with the settings-file refusal without leaving a session.
Removing the config placement turns the second test red.
Docs: performance-improvements.md gains the full diagnosis session
(including the disproved shared-FormatTable attempt and the gcdump
10M-object sampling pitfall), claude-reference.md and the docs-ui
activities reference describe the engine-local module surface, CLAUDE.md
links both.
This was referenced Jul 31, 2026
Sev7eNup
added a commit
that referenced
this pull request
Jul 31, 2026
docs: sync after #87 (RunspacePool memory rule of thumb correction)
hshalab
pushed a commit
to hshalab/NodePilot
that referenced
this pull request
Aug 4, 2026
PR Sev7eNup#87 (WinPSCompat-Session-Leak fix, merged 2026-07-30) added measurement data showing in-process pool runspaces cost 1.2-1.4 MB each, not ~30 MB. The 30 MB figure came from the process-spawn path and does not apply to the RunspaceExecutionEngine's pool. The "Default-Tuning für 500-par.-WF-Topologie" section still carried the old rule of thumb, producing contradictory guidance within the same document. Corrected to match the 2026-07-30 measurement already present later in the file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Four commits consolidating all local work:
Compress-Archiveresolved to the Desktop-edition System32 module and implicit WinCompat spawned one never-closedpowershell.exe -ssession per pool runspace. Fix bundlesMicrosoft.PowerShell.Archive1.2.5 (imported eagerly into the pool) and disables implicit WinCompat for the in-process SDK viapowershell.config.jsonplaced next to the SDK assembly, so desktop-only modules fail loudly instead of silently leaking. Guard tests pin both halves; full diagnosis indocs/performance-improvements.md.