feat(mcp): report descendant processes in inspect_port - #69
Merged
Merged
Conversation
Answers what else stops when you stop a port: a dev server's workers are its children, not separate port owners. Entries are flat and carry ppid and depth, which holds what a nested structure would and is simpler to consume. The walk is breadth-first and bounded, so a truncated result keeps the shallowest part of the tree, and says that it truncated.
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.
Last of Tier 3.
inspect_portreported achildrencount but not what they were, so an agent could not answer the question that actually matters before stopping something: what else goes down with it? A dev server's workers are its children, not separate port owners, so they were invisible.Flat, not nested
Each entry carries
pid,ppid,process, anddepth:That holds exactly what a nested structure would —
ppidgives the edges — and is far simpler to emit and to consume. A client that wants a tree can nest byppid.children(count of direct children) andchild_processes(all descendants) are deliberately separate keys: 2 and 4 above. Reusing the name would have silently changed the type of an existing field.Bounded, and honest about it
Depth ≤ 3, ≤ 64 nodes, breadth-first — so when the cap bites, what survives is the shallowest and most relevant part of the tree, and
child_processes_truncatedsays so rather than returning a partial tree that looks complete. The node cap matters most on Windows, which pays a process snapshot per node walked.There is also a cycle guard. A PID cycle should be impossible, but a reparenting race could in principle produce one, and the alternative is an unbounded walk for no benefit.
Per-platform
New
get_child_processes(pid) -> Vec<(u32, String)>on each platform. PID and name come back together on purpose: Windows already has both inPROCESSENTRY32W.szExeFile, and resolving names separately would mean opening a handle per child — which fails for anything privileged./proc/<pid>/task/<pid>/childrenproc_listchildpidswith a real buffer. The existingcount_childrenalready called it and threw the PIDs away. Sized with headroom over what the sizing call reports, since children can be forked between the two calls and a full buffer truncates silently.CreateToolhelp32Snapshotfiltered by parentVerification
Against
psas ground truth, with a listener that forks workers which fork grandchildren:Same PIDs, same parent links, correct depths.
The walker takes its child lookup as a parameter, so the bounds and the cycle guard are unit-tested directly rather than needing a real process tree: depth cap, node cap with the truncation flag, a cycle, and the empty case.
196 tests, fmt/clippy clean, macOS + Windows type-check — the last matters more than usual here, since two thirds of this is FFI I cannot run locally.