procdeck: debug from observability, not from code
My dev stack is six processes: two backends, a frontend, a couple of workers, a watcher. For years the routine was the same — six terminal tabs, hardcoded ports, and when something broke, the investigation started in the worst possible place: the code. Here's the thing about debugging a running system by reading source: the code describes every behavior the system _could_ have. The runtime had exactly _one_. Logs, stack traces, and the HTTP requests between processes describe that one behavior directly. Reading code to find a runtime bug is reconstructing a crime from the architect's blueprints instead of looking at the security footage. So I built [procdeck](https://github.com/kondaurovDev/procdeck) — a dev-process manager where the footage is always on. One command runs the whole stack, every process is a real terminal in a browser tab, and everything the processes do — output, errors, the HTTP and WebSocket traffic _between_ them — is queryable from the CLI. ## The debugging shift The practical change is the order of questions. Before, "checkout is broken" meant opening the checkout code. Now it means: ```sh procdeck status # anything crashed, blocked, alerting? procdeck errors api # deduplicated stack traces: "same TypeError, 41×, last 3s ago" procdeck http --digest # 4xx/5xx grouped by route, with counts ``` Three commands, a few seconds, and the bug usually has coordinates: which process, which route, which exception, how often. _Then_ I open the code — the right file, not the entry point. The traffic part deserves a word. Browser DevTools shows browser → server. But most of what happens in a service stack is server → server: web calls api, api calls a worker. procdeck assigns ports through `${port}` templates, so it sits on every assigned port and captures that half too: ``` 14:02:11.883 [web] GET /checkout → 200 34ms 12.4kB 14:02:11.902 [api] POST /orders → 422 8ms 231B ``` The `422` between web and api never appears in DevTools, was never printed to any log — and is exactly the bug. ## Why agents need this even more than I do A coding agent edits code, but the consequences land in processes it didn't start and cannot see. Without a harness it does one of two things, both bad: spawns its _own_ copy of the dev server (duplicate processes, port fights, testing against the wrong instance), or asks me to paste logs into the chat like it's 2023. procdeck already owns the processes, so the agent gets a verify loop instead: ```sh procdeck mark before-fix # marker at "now" # … agent edits code … procdeck restart api && procdeck wait-for api procdeck logs --since-mark before-fix # only what the change caused procdeck http --since-mark before-fix # only the requests it caused ``` `--since-mark` is the important part, and it's where the token economy lives. An agent that tails logs pulls in 200 lines of startup noise, old warnings, and unrelated chatter — thousands of tokens of context, most of it misleading. `--since-mark` answers a different question: not "what do the logs say" but "what happened _because of what I just did_". That answer is usually ten lines. Every command is bounded by default and takes `--json`, so the agent physically cannot slurp a megabyte of scrollback into its context window. The effect on my day: I stopped being the agent's eyes. It restarts the process, waits for it to listen, reads exactly the delta, and either confirms the fix or reads the actual stack trace — deduplicated, with a count. No copy-pasting, no "can you check the terminal?", and noticeably fewer tokens burned on log noise. The same verbs are served over MCP (`claude mcp add procdeck -- procdeck mcp`, once, globally), so an agent doesn't even need shell access. ## The boring parts that make it livable - **Ports never collide.** `${port}` is a free port assigned before spawn; `${port:api}` wires a dependent to it. No hardcoded numbers anywhere in the config. - **Every service gets a name.** `api.localhost:4820`, `web.localhost:4820` — browsers resolve `*.localhost` themselves, zero system setup, HMR WebSockets pass through. - **Dependencies mean something.** `needs: ["api"]` parks a proc until the API actually listens — detected from the OS, no cooperation needed from the process. - **It's ~2 MB with one dependency**, nothing compiles at install, and the server binds 127.0.0.1 only. ## Try it ```sh npx procdeck init # writes procdeck.config.json from your workspace's dev scripts npx procdeck # starts the deck in the background and opens the UI ``` Your terminal is free again, and the next time something breaks, start with `procdeck errors` — not with the code. Repo: [github.com/kondaurovDev/procdeck](https://github.com/kondaurovDev/procdeck). Feedback and issues welcome.My dev stack is six processes: two backends, a frontend, a couple of workers, a watcher. For years the routine was the same — six terminal tabs, hardcoded ports, and when something broke, the investigation started in the worst possible place: the code.
Here’s the thing about debugging a running system by reading source: the code describes every behavior the system could have. The runtime had exactly one. Logs, stack traces, and the HTTP requests between processes describe that one behavior directly. Reading code to find a runtime bug is reconstructing a crime from the architect’s blueprints instead of looking at the security footage.
So I built procdeck — a dev-process manager where the footage is always on. One command runs the whole stack, every process is a real terminal in a browser tab, and everything the processes do — output, errors, the HTTP and WebSocket traffic between them — is queryable from the CLI.
The debugging shift
The practical change is the order of questions. Before, “checkout is broken” meant opening the checkout code. Now it means:
procdeck status # anything crashed, blocked, alerting?
procdeck errors api # deduplicated stack traces: "same TypeError, 41×, last 3s ago"
procdeck http --digest # 4xx/5xx grouped by route, with counts
Three commands, a few seconds, and the bug usually has coordinates: which process, which route, which exception, how often. Then I open the code — the right file, not the entry point.
The traffic part deserves a word. Browser DevTools shows browser → server. But most of what happens in a service stack is server → server: web calls api, api calls a worker. procdeck assigns ports through ${port} templates, so it sits on every assigned port and captures that half too:
14:02:11.883 [web] GET /checkout → 200 34ms 12.4kB
14:02:11.902 [api] POST /orders → 422 8ms 231B
The 422 between web and api never appears in DevTools, was never printed to any log — and is exactly the bug.
Why agents need this even more than I do
A coding agent edits code, but the consequences land in processes it didn’t start and cannot see. Without a harness it does one of two things, both bad: spawns its own copy of the dev server (duplicate processes, port fights, testing against the wrong instance), or asks me to paste logs into the chat like it’s 2023.
procdeck already owns the processes, so the agent gets a verify loop instead:
procdeck mark before-fix # marker at "now"
# … agent edits code …
procdeck restart api && procdeck wait-for api
procdeck logs --since-mark before-fix # only what the change caused
procdeck http --since-mark before-fix # only the requests it caused
--since-mark is the important part, and it’s where the token economy lives. An agent that tails logs pulls in 200 lines of startup noise, old warnings, and unrelated chatter — thousands of tokens of context, most of it misleading. --since-mark answers a different question: not “what do the logs say” but “what happened because of what I just did”. That answer is usually ten lines. Every command is bounded by default and takes --json, so the agent physically cannot slurp a megabyte of scrollback into its context window.
The effect on my day: I stopped being the agent’s eyes. It restarts the process, waits for it to listen, reads exactly the delta, and either confirms the fix or reads the actual stack trace — deduplicated, with a count. No copy-pasting, no “can you check the terminal?”, and noticeably fewer tokens burned on log noise. The same verbs are served over MCP (claude mcp add procdeck -- procdeck mcp, once, globally), so an agent doesn’t even need shell access.
The boring parts that make it livable
- Ports never collide.
${port}is a free port assigned before spawn;${port:api}wires a dependent to it. No hardcoded numbers anywhere in the config. - Every service gets a name.
api.localhost:4820,web.localhost:4820— browsers resolve*.localhostthemselves, zero system setup, HMR WebSockets pass through. - Dependencies mean something.
needs: ["api"]parks a proc until the API actually listens — detected from the OS, no cooperation needed from the process. - It’s ~2 MB with one dependency, nothing compiles at install, and the server binds 127.0.0.1 only.
Try it
npx procdeck init # writes procdeck.config.json from your workspace's dev scripts
npx procdeck # starts the deck in the background and opens the UI
Your terminal is free again, and the next time something breaks, start with procdeck errors — not with the code.
Repo: github.com/kondaurovDev/procdeck. Feedback and issues welcome.