{"slug": "a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck", "title": "A local dashboard that tells you when your Claude Code session is stuck", "summary": "Mohammed Nihal03 released AI Agent Observatory, an open-source local dashboard that analyzes Claude Code sessions to score agent health from 0 to 100 and flag degrading behavior, with the tool catching its own author editing a component 10 times during development. The Node.js 20.11+ tool runs entirely on the user's machine with no account or API key, and it provides a weighted score based on recovery, tool efficiency, repetition avoidance, goal adherence, and context headroom.", "body_md": "**See how your AI coding agent is actually behaving.**\n\nYou just spent two hours with Claude Code. Did it work steadily toward the goal, or did it spend forty minutes re-editing the same file and re-running the same failing test? You can scroll back through the transcript to find out. Or you can run one command.\n\n```\nobservatory import\n```\n\nThe Observatory reads the session your agent already recorded on disk and answers one question:\n\n**Is my agent behaving well — and if not, why?**\n\nEverything runs on your machine. No account, no API key, nothing uploaded.\n\nA local dashboard that shows what your agent did, how healthy the session was, and why.\n\nAnd the same answer in your terminal, if you would rather not leave it:\n\n```\n  AGENT HEALTH          65 / 100   stable (3/5 components measured)\n  BEHAVIORAL LEARNING   34 / 100   ▼ DEGRADING\n  DEGRADATION           27 / 100\n\n  Tokens 148M   Actions 396   Errors 3   Recovery 33%   Repetition 23%\n\n  WINDOW      actions   errors   recovery   repetition\n  early          132       1%       100%          16%\n  middle         132       1%         0%          25%\n  recent         132       1%         0%          22%\n\n  WHY THE AGENT IS DEGRADING\n    ✗ Recovery rate decreased 100%\n    ✗ Repetition increased 39%\n    ⚠ 1 retry with no change in between\n\n  SIGNALS\n    ⚠ apps/web/src/components/performance-chart.tsx ran 10 times\n    ⚠ apps/server/src/app.ts ran 8 times\n```\n\nThat is a real session — the one that built this tool. It caught the author editing the same component ten times while fighting a rendering bug, and called the session degrading for it. Every number comes with the reason behind it; you should never see a score without an explanation.\n\n**You need Node.js 20.11 or newer.** Nothing else.\n\n```\nnpm install -g ai-agent-observatory\n```\n\n**Prefer to build it yourself?** Same result, one extra minute:\n\n```\ngit clone https://github.com/MohammedNihal03/AI-observibility.git\ncd AI-observibility\nnpm install && npm run package\nnpm install -g ./dist-package\n```\n\nThe `./` matters: without it npm looks for a package by that name in the registry.\n\nStart it:\n\n```\nobservatory start\nObservatory running on http://127.0.0.1:4000\nDatabase C:\\Users\\you\\.observatory\\observatory.db\n\n  Dashboard   http://127.0.0.1:4000\n```\n\nOpen **[http://127.0.0.1:4000](http://127.0.0.1:4000)**. It will be empty — nothing has been recorded yet.\n\nIn a second terminal, bring in a session you have already run:\n\n```\nobservatory import\n```\n\nRefresh the dashboard. That is the whole loop.\n\n**No Claude Code sessions yet?** See it work with simulated data instead:\n\n```\nobservatory demo --scenario improving --stream\n```\n\nWatch the dashboard while that runs — the numbers, the chart and the timeline fill in live.\n\nThree scores, and they answer different questions. A session can be at 82 and going nowhere, or at 55 and climbing fast.\n\n**0–100.** A weighted average of five things, each measured, each shown to you:\n\n| Component | Weight | What it measures | \n|---|---|---|\n| Recovery | 30% | When something failed, did the agent fix it? | \n| Tool efficiency | 20% | How many tool calls succeeded | \n| Repetition avoidance | 20% | How much of the work was doing something already done | \n| Goal adherence | 15% | How much of the work related to what you asked for | \n| Context headroom | 15% | How much of the context window is still free | \n\n| Score | Band | \n|---|---|\n| 80–100 | healthy | \n| 60–79 | stable | \n| 40–59 | warning | \n| 0–39 | degrading | \n\nA component that cannot be measured is **excluded**, not counted as zero — a session with no\nfailures has no recovery rate, and scoring that as 0 would rank \"never failed\" below \"failed and\nrecovered\". The dashboard shows you how many of the five were actually measured.\n\nThe session is split into three windows by action count — early, middle, recent — and compared.\n**Improving** means errors fell, recovery rose, repetition dropped. **Degrading** means the reverse.\n**Stable** means no meaningful trend either way.\n\nThis is **not** model learning. There are no weights, gradients or loss values here. Claude Code\ndoes not expose them, and this tool does not pretend to. \"Learning\" means the agent's *observable\nbehavior* got better during this session — nothing more.\n\n**0–100**, from seven specific signals: the same action failing repeatedly, rising error rate,\nfalling recovery, correction loops that keep failing, drifting off the goal, and context pressure.\nEach one is listed with the measurement behind it.\n\n| You see | It usually means | \n|---|---|\n| **Repetition high, recovery low** | The agent is stuck. Give it new information, not another retry. | \n| **\"failed N times in a row\"** | It is retrying without changing anything. Intervene. | \n| **\"tried N times, never worked\"** | The whole approach is wrong, not the details. Redirect it. | \n| **Context utilization above 90%** | It is running out of room. Start a fresh session. | \n| **Goal adherence falling** | It has wandered. Restate the goal. | \n| **Health high, learning stable** | Nothing is wrong. Steady competent work looks exactly like this. | \n\n| Command | What it does | \n|---|---|\n| `observatory start` | Start everything — API, live updates and dashboard, on one port | \n| `observatory import` | Read a real Claude Code session and analyze it | \n| `observatory sessions` | List what has been recorded | \n| `observatory status` | What the Observatory is doing right now | \n| `observatory compare` | Compare two sessions, or group them by model or prompt | \n| `observatory demo` | Generate a simulated session (no agent required) | \n| `observatory dashboard` | Open the dashboard in a browser | \n| `observatory doctor` | Check that everything is set up correctly | \n\nEvery command takes `--help`.\n\n```\nobservatory import --list           # what is on this machine\nobservatory import                  # the newest session\nobservatory import --session 5f80   # a specific one (a prefix is enough)\nobservatory import --watch          # follow a session as it runs, live\nobservatory import --project myapp  # only sessions from one project\n```\n\nRe-running is safe. The Observatory asks how much it already has and sends only what is new — which\nis also how `--watch` keeps up with a running agent.\n\n```\nobservatory compare <session-a> <session-b>   # side by side\nobservatory compare --by model                # grouped by model\nobservatory compare --by goal                 # grouped by prompt\n```\n\nThe same thing in the terminal:\n\n```\n  left    demo_degrading_BA7E   (degrading)\n  right   demo_improving_9E21   (improving)\n\n                         LEFT    RIGHT   CHANGE\n  Health                   35       74   +39  better\n  Recovery rate           33%     100%   +67 pts  better\n  Error rate              71%      35%   -35 pts  better\n\n  Only on the left:\n    - npm test → read docs — tried 2 times, never worked\n    - npm test -- session failed 7 times in a row\n```\n\nGrouped comparison uses **medians, not averages** — one disastrous session should not define a\nmodel — and shows the session count on every row, because a comparison built from one session each\nis a data point, not a finding. It shows you differences; it does not claim causes. Two sessions\ndiffer in the model *and* the task *and* the day.\n\n```\nobservatory demo --scenario improving --stream    # watch it arrive live\nobservatory demo --scenario degrading             # or just print the report\n```\n\nThree scenarios, each with a known verdict:\n\n| Scenario | What the simulated agent does | Verdict | \n|---|---|---|\n| `improving` | thrashes, investigates, then recovers from everything | ▲ IMPROVING | \n| `stable` | one failure per stretch, corrected each time, no trend | ● STABLE | \n| `degrading` | the same test fails seven times; edits stop helping; drifts off task | ▼ DEGRADING | \n\nSimulated sessions are **always labelled as simulated** — in the session id, on every event, and in\nthe dashboard. They are never presented as something your agent did.\n\nThis is the part worth reading carefully, because the Observatory reads your agent's transcripts.\n\n**Nothing leaves your machine.** There is no cloud, no telemetry, no phone-home. The server binds to\n`127.0.0.1` only, so nothing on your network can reach it either.\n\n**And most of the transcript is never even read into the tool.** The import takes the *shape* of the\nwork, not its content:\n\n| Recorded | Never recorded | \n|---|---|\n| Which tool ran | Your prompts and the agent's replies | \n| The file path that was read or edited | File contents and diffs | \n| The command line (capped at 500 characters) | Command output, stdout, stderr | \n| Whether it succeeded or failed | Error messages | \n| Token counts | — | \n\nAnything that does get stored passes through secret redaction first — API keys, tokens, passwords\nand private keys in twelve known formats — and redaction runs *before* the database write, so an\nunredacted value never touches disk.\n\nYour data lives in one place:\n\n```\n~/.observatory/observatory.db\n```\n\nDelete that file and everything is gone. Point `OBSERVATORY_DB` somewhere else to move it.\n\nBeing straight about the limits, because a number presented confidently is worse than no number.\n\n**Fully measured with Claude Code:**\n\n- Failures and recoveries — Claude Code marks failed tool calls explicitly, so this is a fact, not a guess\n- Files read and modified, counted as distinct paths\n- Token usage, deduplicated per API response\n- Repetition, correction loops, and every score built on them\n- Lines added and removed, counted from the patch — the count, never the code\n- Thinking tokens, cache hit rate, and commands killed at their time limit\n\n**Not available:**\n\n- **Context utilization.** Claude Code records the model but never its context limit, so the\nObservatory shows \"no window reported\" rather than inventing a percentage. Codex*does* report it.\n- **Cost.** Shown when the agent recorded it, never estimated from a pricing table that would go\nstale.\n- **Codex sessions.** Codex writes readable logs and support is planned, but nothing reads them yet.`observatory doctor` says so plainly.\n\n**Deliberately approximate:**\n\nGoal adherence is word matching — it connects a goal that says \"authentication\" to a file called\n`auth.ts`, but it will not connect \"login\" to `session-store.ts`. That is why the signal is called\n*possible* goal drift and carries little weight. Same for repeated-strategy detection: it compares\ngeneralized actions, not meanings.\n\nThe scores themselves are **product judgements, not science**. The weights above are one reasonable\nopinion about what good agent behavior looks like. [docs/scoring.md](/MohammedNihal03/AI-observibility/blob/main/docs/scoring.md) explains every\none and how to change it.\n\n**`observatory` is not recognised**\nnpm's global install directory is not on your `PATH`. Find it with `npm prefix -g` and add it (the\nexecutables are in that folder on Windows, and in its `bin` subfolder on macOS and Linux). Or skip\nthe `PATH` entirely and use `npx ai-agent-observatory <command>`.\n\n**The dashboard says \"The Observatory API is not answering\"**\n`observatory start` is not running, or it is on a different port. Run `observatory doctor`.\n\n**`observatory import` says no transcripts found**\nClaude Code writes them the first time you run a session. Confirm with `observatory import --list`.\nIf that is empty, no sessions exist on this machine yet.\n\n**Port 4000 is already in use**\n\n```\nobservatory start --port 4100\n```\n\n**The dashboard is empty after importing**\nCheck that the import reported events, and that `observatory sessions` lists the session. If it does,\nrefresh the page.\n\n**Start over**\n\n```\nrm ~/.observatory/observatory.db      # deletes every recorded session\n```\n\n**Uninstall**\n\n```\nnpm uninstall -g ai-agent-observatory\nrm -rf ~/.observatory\ngit clone <this repo> && cd ai-agent-observatory\nnpm install\nnpm run dev        # API on :4000, dashboard on :4001, packages in watch mode\nnpm test           # 595 tests, no build required\n```\n\nIn development the dashboard runs on its own port under `next dev`, which is why it is `:4001` there\nand `:4000` once installed. To build the installable package:\n\n``` php\nnpm run package                # -> dist-package/\nnpm install -g ./dist-package  # install that build\n```\n\n| Command | What it does | \n|---|---|\n| `npm run dev` | Everything, in watch mode | \n| `npm test` | Vitest against package source | \n| `npm run typecheck` | Type-check every workspace, dashboard included | \n| `npm run lint` | ESLint over the repo | \n| `npm run package` | Build the single publishable package | \n| `npm run db:studio` | Browse the local database | \n\n```\napps/web/          Next.js dashboard (exported to static files when packaged)\napps/server/       Fastify API + WebSocket hub\npackages/shared/   Contracts (Zod) and the scoring configuration\npackages/telemetry/  Validation, normalization, secret redaction\npackages/metrics/  Metric computation — pure functions\npackages/behavior/ Repetition, recovery, health, learning, degradation\npackages/collectors/ Agent adapters and the demo generator\ncli/               The `observatory` command\n```\n\nThe analytics packages are pure and synchronous: events in, scores and reasons out. No database, no network, no LLM. That is what makes results reproducible, and it means adding an agent adapter never touches the scoring engine.\n\nThe dashboard is an ordinary client of the local server and computes nothing itself.\n\n| Endpoint | What it does | \n|---|---|\n| `POST /api/sessions` | Create a session | \n| `GET /api/sessions` | List sessions with their headline scores | \n| `GET /api/sessions/:id` | The full snapshot the dashboard renders | \n| `POST /api/sessions/:id/events` | Ingest one event or a batch | \n| `GET /api/sessions/:id/metrics` | Metrics, windows and the progress series | \n| `GET /api/sessions/:id/health` | Scores — with the reasons behind them | \n| `GET /api/sessions/:id/timeline` | The activity rows | \n| `GET /api/compare` | Compare two sessions, or group them | \n| `WS /api/sessions/:id/stream` | Live updates while a session runs | \n\nAny tool can be observed without an adapter by posting events to that API.\n\n- [docs/architecture.md](/MohammedNihal03/AI-observibility/blob/main/docs/architecture.md) — how a raw event becomes a score\n- [docs/scoring.md](/MohammedNihal03/AI-observibility/blob/main/docs/scoring.md) — what every score means and does not mean\n- [docs/integrations.md](/MohammedNihal03/AI-observibility/blob/main/docs/integrations.md) — what Claude Code and Codex actually expose\n\nMIT", "url": "https://wpnews.pro/news/a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck", "canonical_source": "https://github.com/MohammedNihal03/AI-observibility", "published_at": "2026-09-07 08:30:00+00:00", "updated_at": "2026-09-07 08:56:53.136543+00:00", "lang": "en", "topics": ["ai-tools", "ai-agents", "developer-tools", "mlops"], "entities": ["Mohammed Nihal03", "Claude Code", "AI Agent Observatory", "Node.js"], "alternates": {"html": "https://wpnews.pro/news/a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck", "markdown": "https://wpnews.pro/news/a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck.md", "text": "https://wpnews.pro/news/a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck.txt", "jsonld": "https://wpnews.pro/news/a-local-dashboard-that-tells-you-when-your-claude-code-session-is-stuck.jsonld"}}