Profiler/debug + editor fixes (#61, #62, #65, #68) and a self-contained, test-verified VSIX bundle (#71)#72
Merged
Merged
Conversation
#68) The inlay-hints pass looped over `FunctionInfo::local_vars` and skipped any var with `has_annotation == true`. But `local_vars` is populated solely by `collect_local_annotated_vars` (only `Stmt::AnnAssign`), so every entry is annotated and the hint-building body was unreachable — plain `n = 0` locals were never collected at all. `local_vars` cannot be repurposed because E0014/E0036/E0047/E0117/E0149 rely on it being annotated-only. Add `FunctionInfo::local_unannotated_vars`, populated by a new `collect_local_unannotated_vars` collector (mirrors the annotated one, reusing `walk_function_stmts` + `assign_infos_from`). The inlay pass consumes it, and the two near-identical module/local hint loops collapse into one `push_variable_type_hints` helper. Adds an e2e regression test for `def f(): n = 0; s = "hi"` asserting the `: int` / `: str` hints. Spec clarified to note module-level and local.
…m read-only info Actionable rows (feature toggles, quick actions) in the basilisk.info panel were visually indistinguishable from read-only Server Info rows: no imperative tooltip and no inline button affordance. Per [EXTACT-INFO-AFFORDANCE]: - FeatureItem/ActionItem now carry imperative tooltips and an always-visible inline action button. A single basilisk.info.runAction dispatcher forwards the button to the row's own command, so button and whole-row click share one source of truth. - Quick Actions derive from a single ActionDef source-of-truth list (kills per-call-site drift). - Read-only Server Info rows keep no command and no inline button (unchanged). Adds affordance-partition tests and a panel-driven wiring test proving every actionable row resolves to a registered handler (EXTACT-INFO-ACTION-WIRING) — the check that would have caught the original "dead action" report. Adds a BSK_TEST_GREP focus filter for invoking the test runner directly.
Starting a CPU/memory profile no longer demands a hand-typed PID (and its non-existent "auto-detect"). The LSP now owns process discovery and the editor renders a picker: - LSP: new advertised `basilisk.profiler.processes` command backed by `profiler/processes.rs` (sysinfo) — enumerates attachable Python processes with pid/ppid, interpreter, version, cpu/mem/runtime, owner, elevation hint, and interpreter-vs-launcher kind. Enumeration only reads the process table, so it never needs elevation. Logs count only. - VS Code: new "Python Processes" activity-bar panel (process-explorer.ts) with sort/group/filter and one-click Profile CPU / Track Memory inline actions; package.json view/commands/menus/settings/welcome. profiler.ts drops the input box — `profileStart` focuses the panel; panel launches reuse the existing session/status-bar state via startProfilingForPid. - Spec: LSP-PROFILING-SPEC #PROFILE-PROCESSES* family; corrected #PROFILE-REQUESTS-START to remove the false auto-detect claim. - Tests: ws_test_processes e2e (spawns Python, asserts it is listed and non-Python is excluded) + process-explorer VSIX component test.
macOS profiling of any non-child process was 100% broken. Three defects: 1. The LSP never bound a UnixListener, so the elevated helper's connect() always failed with "No such file or directory (os error 2)" — and after "elevating", the LSP still tried an in-process vm_read that can never succeed for an external process. 2. `do shell script ... with administrator privileges` inherited an inaccessible cwd, failing with `getcwd: cannot access parent directories`. 3. The helper was spawned with `.output().await`, blocking on the exit of a process designed to stream samples. Fix: - New basilisk-profiler-protocol crate: single source of truth for the wire types + newline-JSON framing, shared by the LSP and the helper (removes the duplicated message structs). - profiler/helper_client: bind the UnixListener BEFORE spawning the helper, spawn it detached (osascript-elevated in prod, direct for tests), drive attach/samples/stop, and forward batches into the same SamplerHandle channel the in-process sampler uses. - build_elevation_script `cd /`s first so the elevated shell cannot fail on an inaccessible working directory. - privilege::create_sampler replaces the broken "elevate then also attach in-process" flow: child -> in-process py-spy; external macOS -> elevated helper socket; missing -> denied. - Real-binary e2e tests over a real socket: a cross-platform listener proof and cwd/spawn-error checks, plus Linux-gated full-protocol and end-to-end sample tests (py-spy can't attach non-elevated on macOS). Docs/spec corrected to distinguish child vs external same-user processes. Also lands the Cargo.lock entries for sysinfo so the lockfile matches the manifests already on the branch.
Make debugging/profiling actually work on a fresh install, and stop broken bundles from shipping green. Debug launch - effective_python: a blank interpreter override (empty `basilisk.python` / interpreterPath) is now treated as absent and falls back to auto-detection, instead of spawning `""` -> "No such file or directory (os error 2)". - The LSP runs the BUNDLED debugpy: the extension passes its vendored debugpy directory via BASILISK_DEBUGPY_PATH, and debug.rs prepends it to PYTHONPATH for the debugpy check and adapter spawn, so debugging works even when the target interpreter has no debugpy. Bundling integrity (shipwright) - Declare debugpy as a bundled `asset` component in shipwright.json and vendor it at build time (vendor-debugpy.mjs; version read from the manifest), wired into `_release_vsix` and the release workflow (with setup-python). - verify-shipwright `vsix` now asserts EVERY bundled component is present — binaries (required AND optional) and asset directories (debugpy) — so a declared-but-missing artifact fails the build instead of shipping green. - Run the VSIX content verification during local packaging too, not only in release.yml. - gitignore the vendored bundled/ build artifact. Tests: debug.rs effective_python + bundled_debugpy_pythonpath; clippy -D warnings clean; verify-shipwright now fails a helper-stripped or debugpy-less VSIX (verified). Follow-up: #71 (run e2e tests against a real-release VSIX so missing bundled artifacts fail the suite).
The e2e suite loaded the extension from the dev/source layout with only the `basilisk` binary staged — never the per-platform bundle the release ships. So debugging/profiling tests passed even though the deployed VSIX was missing the debugger (debugpy) and the profiler helper: tests validated a different bundle than what ships. - New regression test (bundle-integrity.test.ts): inside the e2e harness, assert the running extension contains EVERY shipwright-declared bundled component for the platform (binaries + asset dirs). Fails if packaging omits one (e.g. debugpy) — so a debugger-less bundle can no longer pass tests. - New stage-runtime.mjs: single source of truth that stages all manifest runtime binaries for the platform from a build dir, and hard-fails if a declared binary was not built. - _test_vsix now assembles the REAL bundle via stage-runtime + vendor-debugpy (the same vendoring the release uses), instead of staging only basilisk. - _release_vsix uses the same stage-runtime, collapsing its hand-rolled copy. - Removed stage-bundled-binary.mjs — the divergent single-binary path that let this gap exist. Red->green verified in the real VS Code e2e harness: the test reported the missing basilisk-profiler-helper + debugpy before the fix, and passes after. Closes #71.
Two fixes surfaced by running every CI job locally (ci-prep): - _test_vsix now builds BOTH bundled binaries (basilisk + profiler-helper) and stages from target/ci via stage-runtime, instead of reusing a found binary whose directory may lack the helper. The #71 stage-runtime requires every bundled binary declared for the platform; on macOS (where the helper is bundled) the old reuse path failed with "built binary ... not found". This also stops the e2e from running against the slow coverage-instrumented binary. - command-registration.test.ts: add basilisk.profiler.processes to SERVER_COMMANDS. #62 added this command to basilisk_common::commands::ALL (the server advertises it) but not to the extension's expected-commands list, so the "SERVER_COMMANDS matches server capabilities" compliance test failed. Verified locally: VS Code e2e 322 passing, coverage 86% >= 84%.
The #61 socket tests are #[cfg(target_os = "linux")], so they were never linted on macOS but failed CI's Linux clippy (pedantic, -D warnings): - unnested_or_patterns (x2): nest the match or-arms (`Some(Stopped) | None`, `Ok(None) | Err(_)`). - too_many_lines (108/100): extract the sample-collection and stop-drain loops into collect_sample_batches / drain_until_stopped helpers. Verified by compiling the Linux paths on macOS via a temporary cfg(unix) widening: clippy -D warnings is clean.
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.
TLDR
Fixes five profiler/debug/editor bugs (macOS external-process profiling #61, Python Processes panel #62, sidebar affordance #65, function-local inlay hints #68) and makes the VS Code bundle self-contained and test-verified — debugpy is now bundled and the e2e suite runs against the real release bundle (#71).
What Was Added?
basilisk-profiler-protocolcrate — shared wire types (Command/Message/TraceData/FrameData) + newline-delimited-JSON framing for the LSP↔helper Unix socket, so the two binaries can't drift.profiler/helper_client/{mod,wire}.rs— the LSP-sideUnixListener: binds the socket before spawning the elevated helper, drivesattach→samples→stop, converts samples into the existingSamplerHandlechannel (Showstopper: macOS profiling of any non-child process is 100% broken (no socket listener + getcwd elevation failure) #61).profiler/processes.rs+vscode-extension/src/process-explorer.ts— LSP enumeration of attachable Python processes and the "Python Processes" panel; new server commandbasilisk.profiler.processes(Profiler start UX: no UI button, raw PID input box, fake auto-detect, no process picker #62).debugpydeclared as a shipwrightassetinshipwright.json;scripts/vendor-debugpy.mjsvendors it at build time;lsp-client.tspasses the bundled dir to the server, which adds it toPYTHONPATHso debugging works without a user-installed debugpy.scripts/stage-runtime.mjs— single source of truth that stages every manifest-declared runtime binary for the platform (used by both test and release packaging).tests/profiler_helper_socket.rs,src/test/suite/bundle-integrity.test.ts,process-explorer.test.ts,tests/lsp/ws_test_processes.rs,tests/lsp/ws_test_inlay_hints.rs, plus info-panel/activity-panel suites; newdebug.rsandhelper_clientunit tests.What Was Changed or Deleted?
privilege.rs— replaced the broken "elevate, then also attach in-process" flow (which could never read an external macOS process) withcreate_sampler: in-process py-spy when permitted, elevated helper-over-socket when macOS requiresvm_read, denied otherwise. Removedelevate_if_needed,spawn_elevated_helper,create_socket_path.profiler-helper/src/main.rs— now uses the shared protocol crate (−135 lines of duplicated structs/framing).debug.rs—effective_pythontreats a blank interpreter override as absent (no moreCommand::new("")→os error 2); the debugpy check + adapter spawn use the bundled debugpy viaPYTHONPATH.commands.rs— debug-session start useseffective_python.inlay_hints.rs+ resolver (function_info.rs,function_types.rs) — produce inlay type hints for function-local unannotated (literal-assigned) variables; the local branch was previously dead ([LSP] Function-local variable inlay type hints never produced (dead loop: local_vars only holds annotated vars) #68).info-panel.ts/profiler.ts— actionable vs read-only sidebar rows are now distinguishable with working actions (CRITICAL: Basilisk sidebar — actions vs info indistinguishable, and several Quick Actions are dead (no handler) #65); the raw PID input box is replaced by the process panel (Profiler start UX: no UI button, raw PID input box, fake auto-detect, no process picker #62).Makefile(_test_vsix,_release_vsix) +verify-shipwright.mjs— both packaging paths usestage-runtime+ vendor debugpy;verify-shipwright vsixnow asserts every bundled component is present (binaries and asset dirs), so a debugger-less bundle fails the build. Deleted the divergentstage-bundled-binary.mjs.How Do The Automated Tests Prove It Works?
profiler_helper_socket.rs::helper_listener_is_bound_before_helper_connectsdrives the realbasilisk-profiler-helperbinary and asserts it connects (the old code died withos error 2because nothing listened).helper_socket_full_protocol_with_real_binary+start_helper_sampler_profiles_real_python(Linux) collect >50 real samples and attributehot_functionas the hotspot.helper_client::tests::elevation_script_guards_cwdproves thecd /fix.debug::tests::effective_python_ignores_blank_overrideandbundled_debugpy_pythonpath_resolves_only_for_existing_dir.bundle-integrity.test.tsreads the packagedshipwright.jsonand asserts the extension-under-test contains every declared bundled component (debugpy dir + profiler helper) — it fails if packaging omits the debugger.command-registration.test.tsassertsSERVER_COMMANDSmatches the server's advertised commands (incl.basilisk.profiler.processes).ws_test_processes.rs+process-explorer.test.tscover enumeration and the panel. [LSP] Function-local variable inlay type hints never produced (dead loop: local_vars only holds annotated vars) #68:ws_test_inlay_hints.rscovers function-local hints.cargo fmt/clippy -D warnings/eslint clean. (CI additionally runs the Neovim and mutation jobs.)Spec / Doc Changes
docs/specs/LSP-PROFILING-SPEC.md— added[PROFILE-HELPER-PROTOCOL]and[PROFILE-HELPER-SOCKET]sections; documented child-vs-external (same-user) macOS process handling.website/src/docs/profiler.md— corrected macOS messaging: only Basilisk-launched child processes profile without elevation; same-user external processes still require it.docs/specs/LSP-ARCHITECTURE-SPEC.md— minor.Breaking Changes
Closes #61, #62, #65, #68, #71.