@kitlangton/motel 0.2.4 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +23 -8
- package/README.md +13 -2
- package/package.json +35 -19
- package/skills/motel-debug/SKILL.md +203 -0
- package/skills/motel-debug/references/effect.md +38 -0
- package/src/App.tsx +12 -5
- package/src/StartupGate.tsx +289 -0
- package/src/cli.ts +15 -16
- package/src/config.ts +7 -1
- package/src/daemon.test.ts +332 -51
- package/src/daemon.ts +105 -153
- package/src/httpApi.ts +1 -0
- package/src/httpListPolicy.test.ts +76 -0
- package/src/httpListPolicy.ts +129 -0
- package/src/index.tsx +9 -2
- package/src/localServer.ts +194 -313
- package/src/mcp.ts +2 -1
- package/src/motel.ts +0 -2
- package/src/opentui-jsx.d.ts +11 -0
- package/src/otlp.test.ts +65 -0
- package/src/otlp.ts +20 -0
- package/src/otlpProtobuf.ts +35 -0
- package/src/registry.ts +37 -11
- package/src/runtime.ts +2 -6
- package/src/services/AsyncIngest.ts +22 -8
- package/src/services/LogQueryService.ts +13 -27
- package/src/services/TelemetryQuery.ts +62 -0
- package/src/services/TelemetryStore.ts +546 -231
- package/src/services/TraceQueryService.ts +22 -56
- package/src/services/ingestRpc.ts +2 -4
- package/src/services/queryRpc.ts +15 -0
- package/src/services/telemetryQueryWorker.ts +32 -0
- package/src/services/telemetryWorker.ts +5 -8
- package/src/startupBench.ts +19 -0
- package/src/storybook/aiChatStory.tsx +1 -1
- package/src/telemetry.test.ts +307 -41
- package/src/ui/AiChatView.tsx +1 -1
- package/src/ui/AttrFilterModal.tsx +1 -1
- package/src/ui/ServiceLogs.tsx +10 -7
- package/src/ui/SpanContentView.tsx +24 -21
- package/src/ui/TraceDetailsPane.tsx +1 -1
- package/src/ui/TraceList.tsx +1 -1
- package/src/ui/aiState.ts +10 -22
- package/src/ui/app/TraceWorkspace.tsx +2 -1
- package/src/ui/app/useAppLayout.ts +1 -1
- package/src/ui/app/useTraceScreenData.ts +35 -23
- package/src/ui/atoms.ts +1 -1
- package/src/ui/cachedLoader.test.ts +23 -0
- package/src/ui/cachedLoader.ts +60 -0
- package/src/ui/loaders.ts +34 -53
- package/src/ui/persistence.ts +3 -3
- package/src/ui/primitives.tsx +1 -1
- package/src/ui/state.ts +2 -0
- package/src/ui/theme.ts +7 -5
- package/src/ui/traceDetailsWidth.repro.test.ts +12 -1
- package/src/ui/traceSortNav.repro.seed.ts +1 -1
- package/src/ui/traceSortNav.repro.test.ts +12 -2
- package/src/ui/useAttrFilterPicker.ts +10 -8
- package/src/ui/useKeyboardNav.ts +28 -5
- package/src/ui/waterfallNav.repro.seed.ts +1 -1
- package/src/ui/waterfallNav.repro.test.ts +16 -8
- package/web/dist/assets/index-B01z9BaO.css +2 -0
- package/web/dist/assets/index-M86tcih5.js +22 -0
- package/web/dist/index.html +2 -2
- package/web/dist/assets/index-DnyVo03x.js +0 -27
- package/web/dist/assets/index-DzuHNBGV.css +0 -2
package/AGENTS.md
CHANGED
|
@@ -31,6 +31,18 @@
|
|
|
31
31
|
- Effect LSP diagnostics over the whole project: `bunx effect-language-service diagnostics --project tsconfig.json --format text`
|
|
32
32
|
- Effect LSP interactive setup wizard: `bunx effect-language-service setup`
|
|
33
33
|
|
|
34
|
+
## Release Strategy
|
|
35
|
+
- npm package: `@kitlangton/motel`
|
|
36
|
+
- Current published npm `latest`: `0.2.4` (`npm view @kitlangton/motel dist-tags --json`)
|
|
37
|
+
- Tags are versioned as `vX.Y.Z` (`git tag --sort=-version:refname` shows `v0.2.4`, `v0.2.3`, ...)
|
|
38
|
+
- Publishing is handled by GitHub Actions in `.github/workflows/publish.yml`, not by local manual `npm publish`
|
|
39
|
+
- The publish workflow triggers on `git push` of tags matching `v*` or via manual `workflow_dispatch`
|
|
40
|
+
- The workflow runs `bun install --frozen-lockfile`, `bun run typecheck`, `bun run test`, then `npm publish --provenance`
|
|
41
|
+
- `npm publish` runs `prepublishOnly`, which builds the web UI via `bun run web:build`
|
|
42
|
+
- Before tagging a release, make sure the committed `package.json` version matches the intended git tag exactly
|
|
43
|
+
- Preferred release flow: update `package.json` version, commit the release changes, create tag `v<package.json version>`, push the commit and tag, then verify the GitHub Actions publish and npm dist-tags
|
|
44
|
+
- Do not create or push release tags from a dirty worktree with unrelated uncommitted changes; ask before including unrelated edits in a release
|
|
45
|
+
|
|
34
46
|
## Effect LSP
|
|
35
47
|
The repo is wired up with `@effect/language-service` as a `tsconfig.json` `plugins` entry. Editors that pick up the TypeScript workspace plugin (Zed, VSCode, Cursor, NVim via vtsls) will surface Effect-specific diagnostics, quick fixes, and refactors inline. In Zed this requires selecting the workspace TypeScript version — it does so automatically when `node_modules/typescript` is present.
|
|
36
48
|
|
|
@@ -82,11 +94,11 @@ The repo is wired up with `@effect/language-service` as a `tsconfig.json` `plugi
|
|
|
82
94
|
- `src/runtime.ts` wires the Effect beta runtime and OTEL trace + log exporters.
|
|
83
95
|
- `src/localServer.ts` starts the local Bun OTLP/query server.
|
|
84
96
|
- `src/httpApi.ts` defines the typed Effect HttpApi surface and OpenAPI spec for the local server.
|
|
97
|
+
- `src/httpListPolicy.ts` owns pure HTTP list/search parameter decoding, bounds, cursors, and pagination metadata shaping.
|
|
85
98
|
- `src/server.ts` runs the local server without the TUI.
|
|
86
99
|
- `src/instructions.ts` contains the copied setup instructions for other Effect apps.
|
|
87
|
-
- `src/services/TelemetryStore.ts` persists traces and logs in SQLite and exposes indexed queries.
|
|
88
|
-
- `src/services/
|
|
89
|
-
- `src/services/LogQueryService.ts` reads logs from the local store.
|
|
100
|
+
- `src/services/TelemetryStore.ts` persists traces and logs in SQLite and exposes indexed queries through writer and read-only service identifiers.
|
|
101
|
+
- `src/services/TelemetryQuery.ts` proxies read-only store calls to `src/services/telemetryQueryWorker.ts`, keeping synchronous Bun SQLite queries off the HTTP event loop.
|
|
90
102
|
- `src/config.ts` is the source of truth for ports and env-driven OTEL settings.
|
|
91
103
|
- `web/` is a Vite + React SPA for the browser-based UI (Tailwind CSS, `@effect/atom-react`, `AtomHttpApi`).
|
|
92
104
|
- `web/src/api.ts` creates the typed `AtomHttpApi.Service` client from `src/httpApi.ts`.
|
|
@@ -95,15 +107,14 @@ The repo is wired up with `@effect/language-service` as a `tsconfig.json` `plugi
|
|
|
95
107
|
- The server in `src/localServer.ts` serves `web/dist/` as static files with SPA fallback for non-API routes.
|
|
96
108
|
|
|
97
109
|
## Tests
|
|
98
|
-
- `bun test` runs the suite. Three kinds of tests live in the repo:
|
|
110
|
+
- `bun run test` runs the suite. Three kinds of tests live in the repo:
|
|
99
111
|
- `src/telemetry.test.ts` exercises the SQLite TelemetryStore with
|
|
100
112
|
OTLP payloads end-to-end.
|
|
101
113
|
- `src/ui/waterfallNav.test.ts` unit-tests the pure collapse/expand
|
|
102
114
|
resolver (no UI).
|
|
103
115
|
- `src/ui/*.repro.test.ts` drive the real TUI under `tuistory` to
|
|
104
|
-
reproduce regressions
|
|
105
|
-
|
|
106
|
-
are auto-skipped when `tuistory` isn't installed.
|
|
116
|
+
reproduce regressions, using deterministic traces seeded into SQLite
|
|
117
|
+
by child processes. They become no-op passes when `tuistory` is absent.
|
|
107
118
|
|
|
108
119
|
## Effect Observability Guidance
|
|
109
120
|
- Inspect the target repo’s existing Effect runtime and observability wiring before adding anything new.
|
|
@@ -128,12 +139,16 @@ The repo is wired up with `@effect/language-service` as a `tsconfig.json` `plugi
|
|
|
128
139
|
- `MOTEL_OTEL_EXPORTER_URL`: defaults to `http://127.0.0.1:27686/v1/traces`
|
|
129
140
|
- `MOTEL_OTEL_LOGS_EXPORTER_URL`: defaults to `http://127.0.0.1:27686/v1/logs`
|
|
130
141
|
- `MOTEL_OTEL_QUERY_URL`: defaults to `http://127.0.0.1:27686`
|
|
131
|
-
- `MOTEL_OTEL_DB_PATH`: defaults to
|
|
142
|
+
- `MOTEL_OTEL_DB_PATH`: defaults to `${XDG_STATE_HOME:-~/.local/state}/motel/telemetry.sqlite` (one shared DB per machine; daemon log + lock + instance registry live in the same directory)
|
|
143
|
+
- `MOTEL_RUNTIME_DIR`: overrides the daemon log, lock, and instance-registry directory (primarily for isolated tests and custom managed instances)
|
|
132
144
|
- `MOTEL_OTEL_TRACE_LOOKBACK_MINUTES`: defaults to `1440` (24h)
|
|
133
145
|
- `MOTEL_OTEL_TRACE_LIMIT`: defaults to `100`
|
|
134
146
|
- `MOTEL_OTEL_LOG_LIMIT`: defaults to `80`
|
|
135
147
|
- `MOTEL_OTEL_RETENTION_HOURS`: defaults to `168` (7d)
|
|
136
148
|
- `MOTEL_OTEL_MAX_DB_SIZE_MB`: defaults to `1024` (size-based retention cap)
|
|
149
|
+
- `MOTEL_OTEL_RETENTION_TRACE_BATCH`: defaults to `100` completed traces per cleanup pass
|
|
150
|
+
- `MOTEL_OTEL_RETENTION_LOG_BATCH`: defaults to `5000` logs per cleanup pass
|
|
151
|
+
- `MOTEL_OTEL_RETENTION_INTERVAL_SECONDS`: defaults to `10`
|
|
137
152
|
|
|
138
153
|
## TUI Keys
|
|
139
154
|
- `?`: toggle shortcut help
|
package/README.md
CHANGED
|
@@ -62,8 +62,19 @@ http://127.0.0.1:27686/v1/traces
|
|
|
62
62
|
http://127.0.0.1:27686/v1/logs
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
Motel keeps everything in a local SQLite database at
|
|
66
|
-
|
|
65
|
+
Motel keeps everything in a machine-global local SQLite database at
|
|
66
|
+
`${XDG_STATE_HOME:-~/.local/state}/motel/telemetry.sqlite`. One managed
|
|
67
|
+
daemon is shared across local projects. No Docker, no cloud account.
|
|
68
|
+
|
|
69
|
+
The store retains seven days of telemetry by default and targets a 1 GB
|
|
70
|
+
active-data ceiling using bounded background batches. Recent data is preserved while
|
|
71
|
+
the oldest completed traces and logs are removed first. Configure the policy
|
|
72
|
+
with `MOTEL_OTEL_RETENTION_HOURS`, `MOTEL_OTEL_MAX_DB_SIZE_MB`,
|
|
73
|
+
`MOTEL_OTEL_RETENTION_TRACE_BATCH`, `MOTEL_OTEL_RETENTION_LOG_BATCH`, and
|
|
74
|
+
`MOTEL_OTEL_RETENTION_INTERVAL_SECONDS`. Existing databases created without
|
|
75
|
+
incremental auto-vacuum are never silently rewritten at startup; deleted pages
|
|
76
|
+
are reused, but shrinking such a historical file requires an explicit offline
|
|
77
|
+
SQLite `VACUUM` chosen by the user.
|
|
67
78
|
|
|
68
79
|
## How agents connect
|
|
69
80
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitlangton/motel",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "A local OpenTelemetry ingest + TUI viewer for development, backed by SQLite.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -27,14 +27,22 @@
|
|
|
27
27
|
"web"
|
|
28
28
|
],
|
|
29
29
|
"engines": {
|
|
30
|
-
"bun": ">=1.
|
|
30
|
+
"bun": ">=1.3.0"
|
|
31
31
|
},
|
|
32
32
|
"publishConfig": {
|
|
33
33
|
"access": "public"
|
|
34
34
|
},
|
|
35
|
+
"overrides": {
|
|
36
|
+
"@grpc/grpc-js": "1.14.4",
|
|
37
|
+
"protobufjs": "7.6.4",
|
|
38
|
+
"shell-quote": "1.9.0",
|
|
39
|
+
"ws": "8.21.0"
|
|
40
|
+
},
|
|
35
41
|
"files": [
|
|
36
42
|
"src",
|
|
37
43
|
"web/dist",
|
|
44
|
+
"skills/motel-debug/SKILL.md",
|
|
45
|
+
"skills/motel-debug/references/effect.md",
|
|
38
46
|
"LICENSE",
|
|
39
47
|
"README.md",
|
|
40
48
|
"AGENTS.md"
|
|
@@ -68,30 +76,38 @@
|
|
|
68
76
|
"web:dev": "bun run --cwd web dev",
|
|
69
77
|
"web:build": "bun run --cwd web build",
|
|
70
78
|
"story:chat": "bun run src/storybook/aiChatStory.tsx",
|
|
79
|
+
"bench:motel-cold-start": "bun run scripts/bench-motel-cold-start.ts",
|
|
80
|
+
"bench:ingest-logs": "bun run scripts/bench-ingest-logs.ts",
|
|
81
|
+
"bench:ingest-traces": "bun run scripts/bench-ingest-traces.ts",
|
|
82
|
+
"bench:search-spans": "bun run scripts/bench-search-spans.ts",
|
|
83
|
+
"release:validate": "bun run scripts/validate-release.ts",
|
|
71
84
|
"typecheck": "tsc --noEmit",
|
|
85
|
+
"web:typecheck": "tsc --noEmit -p web/tsconfig.json",
|
|
72
86
|
"prepublishOnly": "bun run web:build"
|
|
73
87
|
},
|
|
74
88
|
"devDependencies": {
|
|
75
|
-
"@effect/language-service": "^0.
|
|
76
|
-
"@types/bun": "^1.3.
|
|
77
|
-
"@types/react": "^19.2.
|
|
78
|
-
"typescript": "^6.0.
|
|
89
|
+
"@effect/language-service": "^0.86.2",
|
|
90
|
+
"@types/bun": "^1.3.14",
|
|
91
|
+
"@types/react": "^19.2.17",
|
|
92
|
+
"typescript": "^6.0.3"
|
|
79
93
|
},
|
|
80
94
|
"dependencies": {
|
|
81
|
-
"@effect/atom-react": "
|
|
82
|
-
"@effect/opentelemetry": "
|
|
83
|
-
"@effect/platform-bun": "
|
|
95
|
+
"@effect/atom-react": "4.0.0-beta.90",
|
|
96
|
+
"@effect/opentelemetry": "4.0.0-beta.90",
|
|
97
|
+
"@effect/platform-bun": "4.0.0-beta.90",
|
|
84
98
|
"@opentelemetry/api": "^1.9.0",
|
|
85
|
-
"@opentelemetry/exporter-logs-otlp-http": "^0.
|
|
86
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
87
|
-
"@opentelemetry/
|
|
88
|
-
"@opentelemetry/sdk-
|
|
89
|
-
"@opentelemetry/sdk-
|
|
90
|
-
"@opentelemetry/sdk-trace-
|
|
91
|
-
"@
|
|
92
|
-
"@opentui/
|
|
93
|
-
"
|
|
94
|
-
"
|
|
99
|
+
"@opentelemetry/exporter-logs-otlp-http": "^0.219.0",
|
|
100
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.219.0",
|
|
101
|
+
"@opentelemetry/otlp-transformer": "0.214.0",
|
|
102
|
+
"@opentelemetry/sdk-logs": "^0.219.0",
|
|
103
|
+
"@opentelemetry/sdk-node": "^0.219.0",
|
|
104
|
+
"@opentelemetry/sdk-trace-base": "^2.8.0",
|
|
105
|
+
"@opentelemetry/sdk-trace-node": "^2.8.0",
|
|
106
|
+
"@opentui/core": "0.4.2",
|
|
107
|
+
"@opentui/react": "0.4.2",
|
|
108
|
+
"effect": "4.0.0-beta.90",
|
|
109
|
+
"protobufjs": "7.6.4",
|
|
110
|
+
"react": "^19.2.7",
|
|
95
111
|
"scheduler": "^0.27.0"
|
|
96
112
|
}
|
|
97
113
|
}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: motel-debug
|
|
3
|
+
description: Debug applications with motel, a local OpenTelemetry ingest and query server. Use when the user wants runtime-evidence debugging with traces or logs, wants temporary debug instrumentation that can be removed later, or needs a repo wired to send OTLP/HTTP telemetry to a local motel server. If the target repo uses Effect or @effect/*, also read references/effect.md.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Motel Debug
|
|
7
|
+
|
|
8
|
+
You are in **debug mode**. Debug with runtime evidence, not guesswork.
|
|
9
|
+
|
|
10
|
+
Agents guess based on code alone. You need actual runtime data. Motel is the local OpenTelemetry server that collects traces and logs — use it as your evidence loop.
|
|
11
|
+
|
|
12
|
+
Default local server details:
|
|
13
|
+
|
|
14
|
+
- Base URL: `http://127.0.0.1:27686`
|
|
15
|
+
- OTLP traces: `POST /v1/traces`
|
|
16
|
+
- OTLP logs: `POST /v1/logs`
|
|
17
|
+
- Query API: `GET /api/*`
|
|
18
|
+
- OpenAPI: `GET /openapi.json`
|
|
19
|
+
- Header: `Content-Type: application/json`
|
|
20
|
+
- Auth: none by default
|
|
21
|
+
|
|
22
|
+
If the user provides a different motel URL, use that instead of the default.
|
|
23
|
+
|
|
24
|
+
## Workflow
|
|
25
|
+
|
|
26
|
+
### 1. Verify motel is running — and start it if not
|
|
27
|
+
|
|
28
|
+
Check `GET /api/health`. If it returns 200, continue.
|
|
29
|
+
|
|
30
|
+
If it fails (connection refused, timeout, non-200), motel isn't running.
|
|
31
|
+
Start it as a background daemon — **do not** launch the TUI, which is
|
|
32
|
+
interactive and will block your shell:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
motel start
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`motel start` ensures the machine-global managed daemon is running, writes
|
|
39
|
+
runtime files under `${XDG_STATE_HOME:-~/.local/state}/motel/`, and returns a
|
|
40
|
+
JSON status blob. It is idempotent and shared across local projects. If motel isn't on `PATH`, fall
|
|
41
|
+
back to `bunx @kitlangton/motel start`.
|
|
42
|
+
|
|
43
|
+
After starting, re-check `GET /api/health` (may take 1–2s to become
|
|
44
|
+
ready). If it still fails, read `${XDG_STATE_HOME:-~/.local/state}/motel/daemon.log` for the error
|
|
45
|
+
and surface it to the user.
|
|
46
|
+
|
|
47
|
+
Other lifecycle commands, for reference:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
motel status # JSON status (running? pid? originating workdir?)
|
|
51
|
+
motel stop # stop the shared managed daemon for all local projects
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Discover reporting services with `GET /api/services` when needed.
|
|
55
|
+
|
|
56
|
+
### 2. Generate hypotheses
|
|
57
|
+
|
|
58
|
+
Before touching any code, generate **3-5 specific hypotheses** about why the bug occurs. Be precise — "the cache key doesn't include the user ID" is better than "something is wrong with caching."
|
|
59
|
+
|
|
60
|
+
### 3. Instrument with tagged debug blocks
|
|
61
|
+
|
|
62
|
+
Add the minimum instrumentation needed to confirm or reject **all** hypotheses in parallel. Every debug block must:
|
|
63
|
+
|
|
64
|
+
- Be wrapped in `#region motel debug` / `#endregion motel debug` markers
|
|
65
|
+
- Include a `debug.hypothesis` attribute linking it to a specific hypothesis
|
|
66
|
+
- Use whatever tracing/logging mechanism the codebase already has (spans, structured logs, annotations — not raw `fetch` calls)
|
|
67
|
+
|
|
68
|
+
Tag every piece of debug instrumentation with structured attributes so you can query it later. Reuse these keys:
|
|
69
|
+
|
|
70
|
+
| Key | Purpose |
|
|
71
|
+
|-----|---------|
|
|
72
|
+
| `debug.session` | Groups all instrumentation for this debug session |
|
|
73
|
+
| `debug.hypothesis` | Links to a specific hypothesis (e.g. `"cache-miss"`, `"A"`) |
|
|
74
|
+
| `debug.step` | Position in the flow (e.g. `"entry"`, `"before-write"`, `"after-read"`) |
|
|
75
|
+
| `debug.label` | Human-readable description of what this point captures |
|
|
76
|
+
|
|
77
|
+
Choose log placements based on your hypotheses:
|
|
78
|
+
|
|
79
|
+
- Function entry with parameters
|
|
80
|
+
- Function exit with return values
|
|
81
|
+
- Values before and after critical operations
|
|
82
|
+
- Branch execution paths (which if/else ran)
|
|
83
|
+
- State mutations and intermediate values
|
|
84
|
+
- Suspected error or edge-case values
|
|
85
|
+
|
|
86
|
+
Guidelines:
|
|
87
|
+
|
|
88
|
+
- At least 1 instrumentation point is required; never skip instrumentation
|
|
89
|
+
- Do not exceed 10 — if you think you need more, narrow your hypotheses
|
|
90
|
+
- Typical range is 2-6
|
|
91
|
+
|
|
92
|
+
### 4. Reproduce the issue
|
|
93
|
+
|
|
94
|
+
- If a failing test exists, run it directly
|
|
95
|
+
- If reproduction is straightforward (CLI command, curl, simple script), write and run it yourself
|
|
96
|
+
- Otherwise, ask the user to reproduce — provide clear numbered steps and remind them to restart if needed
|
|
97
|
+
- Once a reproduction pathway is established, reuse it for all subsequent iterations
|
|
98
|
+
|
|
99
|
+
### 5. Analyze evidence
|
|
100
|
+
|
|
101
|
+
Query motel for the debug instrumentation:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
curl "http://127.0.0.1:27686/api/spans/search?service=<service>&attr.debug.hypothesis=<id>"
|
|
105
|
+
curl "http://127.0.0.1:27686/api/logs/search?service=<service>&attr.debug.session=<session>"
|
|
106
|
+
curl "http://127.0.0.1:27686/api/traces/search?service=<service>&attr.debug.hypothesis=<id>"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
For each hypothesis, evaluate: **CONFIRMED**, **REJECTED**, or **INCONCLUSIVE** — cite specific spans, logs, or attribute values as evidence.
|
|
110
|
+
|
|
111
|
+
### 6. Fix only with evidence
|
|
112
|
+
|
|
113
|
+
Do **not** fix without runtime evidence. When you fix:
|
|
114
|
+
|
|
115
|
+
- Keep all debug instrumentation in place — do not remove it yet
|
|
116
|
+
- Make the fix as small and targeted as possible
|
|
117
|
+
- Reuse existing architecture and patterns; do not overengineer
|
|
118
|
+
|
|
119
|
+
### 7. Verify the fix
|
|
120
|
+
|
|
121
|
+
Reproduce the issue again with instrumentation still active. Compare before/after evidence:
|
|
122
|
+
|
|
123
|
+
- Cite specific log lines or span attributes that prove the fix works
|
|
124
|
+
- If the fix failed: **revert code changes from rejected hypotheses** (do not let speculative fixes accumulate), generate new hypotheses from different subsystems, add more instrumentation, and iterate
|
|
125
|
+
- Iteration is expected. Taking longer with more data yields better fixes.
|
|
126
|
+
|
|
127
|
+
### 8. Clean up
|
|
128
|
+
|
|
129
|
+
Only after the fix is verified **and** the user confirms there are no remaining issues:
|
|
130
|
+
|
|
131
|
+
- Run the cleanup script or remove blocks manually (see Cleanup section below)
|
|
132
|
+
- Run `git diff` to confirm only the intentional fix remains
|
|
133
|
+
|
|
134
|
+
## Instrumentation Rules
|
|
135
|
+
|
|
136
|
+
Wrap every temporary debug block in these exact markers:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
// #region motel debug
|
|
140
|
+
// temporary debug instrumentation
|
|
141
|
+
// #endregion motel debug
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Use whatever the codebase already provides for tracing and logging. The markers are language-comment wrappers — adapt the comment syntax for non-JS/TS files (e.g. `# #region motel debug` for Python).
|
|
145
|
+
|
|
146
|
+
**Do not:**
|
|
147
|
+
- Log secrets, tokens, passwords, or raw PII
|
|
148
|
+
- Remove instrumentation before post-fix verification succeeds
|
|
149
|
+
- Use `setTimeout`, `sleep`, or artificial delays as a "fix"
|
|
150
|
+
- Let code changes from rejected hypotheses accumulate — revert them
|
|
151
|
+
|
|
152
|
+
## Query Patterns
|
|
153
|
+
|
|
154
|
+
Two filter prefixes for attribute search:
|
|
155
|
+
|
|
156
|
+
| Prefix | Match type | Example |
|
|
157
|
+
|--------|-----------|---------|
|
|
158
|
+
| `attr.<key>=<value>` | Exact match | `attr.debug.hypothesis=cache-miss` |
|
|
159
|
+
| `attrContains.<key>=<substring>` | Case-insensitive substring | `attrContains.ai.prompt.messages=hello world` |
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
curl http://127.0.0.1:27686/api/health
|
|
163
|
+
curl http://127.0.0.1:27686/api/services
|
|
164
|
+
|
|
165
|
+
# Trace search
|
|
166
|
+
curl "http://127.0.0.1:27686/api/traces/search?service=<service>&operation=<text>&attr.debug.session=<session>"
|
|
167
|
+
|
|
168
|
+
# Span search (supports traceId to scope to one trace)
|
|
169
|
+
curl "http://127.0.0.1:27686/api/spans/search?service=<service>&traceId=<trace-id>&attr.debug.hypothesis=<id>"
|
|
170
|
+
curl "http://127.0.0.1:27686/api/spans/search?service=<service>&attrContains.ai.prompt.messages=<phrase>"
|
|
171
|
+
|
|
172
|
+
# Log search (supports severity filter, case-insensitive body search)
|
|
173
|
+
curl "http://127.0.0.1:27686/api/logs/search?service=<service>&severity=ERROR&body=<text>"
|
|
174
|
+
curl "http://127.0.0.1:27686/api/logs/search?service=<service>&attrContains.debug.label=<substring>"
|
|
175
|
+
|
|
176
|
+
# AI call search (compact summaries with previews)
|
|
177
|
+
curl "http://127.0.0.1:27686/api/ai/calls?model=gpt-5.4&sessionId=<session>"
|
|
178
|
+
curl "http://127.0.0.1:27686/api/ai/calls?text=<phrase>&status=error"
|
|
179
|
+
|
|
180
|
+
# AI call detail (full prompt/response payloads)
|
|
181
|
+
curl "http://127.0.0.1:27686/api/ai/calls/<span-id>"
|
|
182
|
+
|
|
183
|
+
# AI stats
|
|
184
|
+
curl "http://127.0.0.1:27686/api/ai/stats?groupBy=model&agg=total_input_tokens"
|
|
185
|
+
|
|
186
|
+
curl http://127.0.0.1:27686/openapi.json
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
List and search responses include `meta.nextCursor` when more data is available.
|
|
190
|
+
|
|
191
|
+
Motel gives you trace-correlated data — you can see which span a debug log belongs to, the parent operation, timing, and the full trace tree. Use `GET /api/traces/<trace-id>/spans` and `GET /api/spans/<span-id>/logs` to navigate the correlation.
|
|
192
|
+
|
|
193
|
+
For AI/LLM calls, use `/api/ai/calls` for compact searchable summaries (with prompt/response previews and token usage), and `/api/ai/calls/<span-id>` for full payloads.
|
|
194
|
+
|
|
195
|
+
## Effect
|
|
196
|
+
|
|
197
|
+
If the target repo uses Effect, read `references/effect.md` before changing runtime wiring or adding instrumentation.
|
|
198
|
+
|
|
199
|
+
## Cleanup
|
|
200
|
+
|
|
201
|
+
Use the bundled script at `scripts/clear-motel-debug.ts` when you want deterministic cleanup. It removes every block between `#region motel debug` and `#endregion motel debug` in JS/TS files and fails on unmatched markers.
|
|
202
|
+
|
|
203
|
+
If you cannot run the script, delete every marked block manually and then grep for `#region motel debug` to confirm none remain.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Effect Notes
|
|
2
|
+
|
|
3
|
+
Apply these only when the target repo already uses Effect or `@effect/*`.
|
|
4
|
+
|
|
5
|
+
## Runtime
|
|
6
|
+
|
|
7
|
+
- Inspect the existing runtime and observability wiring before adding anything new.
|
|
8
|
+
- Prefer the repo's existing Effect-native observability APIs if they already exist.
|
|
9
|
+
- If `effect/unstable/observability` is already the best fit, prefer it over adding new OTEL packages.
|
|
10
|
+
- Merge telemetry into the main runtime once, not per feature or per request path.
|
|
11
|
+
|
|
12
|
+
## Instrumentation
|
|
13
|
+
|
|
14
|
+
- Prefer `Effect.fn("...")` for meaningful workflow spans.
|
|
15
|
+
- Add a few child spans around boundaries that are likely to fail or add latency.
|
|
16
|
+
- Emit `Effect.logInfo`, `Effect.logWarning`, and `Effect.logError` with structured fields.
|
|
17
|
+
- Put searchable values in annotations/attributes, not only in the free-form log body.
|
|
18
|
+
- Reuse stable debug keys such as `debug.session`, `debug.hypothesis`, `debug.step`, and `debug.label`.
|
|
19
|
+
|
|
20
|
+
## Debug Blocks
|
|
21
|
+
|
|
22
|
+
Wrap temporary debug-only Effect instrumentation in removable markers.
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
// #region motel debug
|
|
26
|
+
const program = Effect.fn("feature/doThing")(function*() {
|
|
27
|
+
yield* Effect.logInfo("entered doThing", {
|
|
28
|
+
debug: {
|
|
29
|
+
session: "abc123",
|
|
30
|
+
hypothesis: "cache-miss",
|
|
31
|
+
step: "entry",
|
|
32
|
+
},
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
// #endregion motel debug
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Keep those blocks until the fix is verified, then remove them with the cleanup script.
|
package/src/App.tsx
CHANGED
|
@@ -7,22 +7,21 @@ import { Divider, FooterHints, HelpModal, PlainLine, SplitDivider, TextLine } fr
|
|
|
7
7
|
import { useAppLayout } from "./ui/app/useAppLayout.ts"
|
|
8
8
|
import { useTraceScreenData } from "./ui/app/useTraceScreenData.ts"
|
|
9
9
|
import { TraceWorkspace } from "./ui/app/TraceWorkspace.tsx"
|
|
10
|
+
import { startupBenchMark } from "./startupBench.js"
|
|
10
11
|
import {
|
|
11
12
|
type AttrFacetState,
|
|
12
13
|
attrPickerIndexAtom,
|
|
13
14
|
attrPickerInputAtom,
|
|
14
15
|
attrPickerModeAtom,
|
|
15
16
|
attrFacetStateAtom,
|
|
16
|
-
chatDetailChunkIdAtom,
|
|
17
|
-
chatDetailScrollOffsetAtom,
|
|
18
17
|
noticeAtom,
|
|
19
|
-
persistSelectedTheme,
|
|
20
18
|
selectedAttrIndexAtom,
|
|
21
|
-
selectedChatChunkIdAtom,
|
|
22
19
|
selectedThemeAtom,
|
|
23
20
|
waterfallFilterModeAtom,
|
|
24
21
|
waterfallFilterTextAtom,
|
|
25
|
-
} from "./ui/
|
|
22
|
+
} from "./ui/atoms.ts"
|
|
23
|
+
import { chatDetailChunkIdAtom, chatDetailScrollOffsetAtom, selectedChatChunkIdAtom } from "./ui/aiState.ts"
|
|
24
|
+
import { persistSelectedTheme } from "./ui/persistence.ts"
|
|
26
25
|
import type { ThemeName } from "./ui/theme.ts"
|
|
27
26
|
import { applyTheme, colors, SEPARATOR, themeLabel } from "./ui/theme.ts"
|
|
28
27
|
import { useKeyboardNav } from "./ui/useKeyboardNav.ts"
|
|
@@ -32,6 +31,8 @@ import { getVisibleSpans } from "./ui/waterfallModel.ts"
|
|
|
32
31
|
|
|
33
32
|
const NOTICE_TIMEOUT_MS = 2500
|
|
34
33
|
|
|
34
|
+
startupBenchMark("app_module_loaded")
|
|
35
|
+
|
|
35
36
|
const buildHeaderModel = ({
|
|
36
37
|
headerFooterWidth,
|
|
37
38
|
selectedTraceService,
|
|
@@ -177,6 +178,7 @@ const AppOverlays = ({
|
|
|
177
178
|
)
|
|
178
179
|
|
|
179
180
|
export const App = () => {
|
|
181
|
+
startupBenchMark("app_render_started")
|
|
180
182
|
const { width, height } = useTerminalDimensions()
|
|
181
183
|
const [notice, setNotice] = useAtom(noticeAtom)
|
|
182
184
|
const [selectedTheme] = useAtom(selectedThemeAtom)
|
|
@@ -261,6 +263,10 @@ export const App = () => {
|
|
|
261
263
|
persistSelectedTheme(selectedTheme)
|
|
262
264
|
}, [selectedTheme])
|
|
263
265
|
|
|
266
|
+
useEffect(() => {
|
|
267
|
+
startupBenchMark("app_effects_committed")
|
|
268
|
+
}, [])
|
|
269
|
+
|
|
264
270
|
const { spanNavActive } = useKeyboardNav({
|
|
265
271
|
selectedTrace,
|
|
266
272
|
filteredTraces,
|
|
@@ -337,6 +343,7 @@ export const App = () => {
|
|
|
337
343
|
// above an empty column and leaves a visible stale sliver when
|
|
338
344
|
// toggling tab back and forth with the trace view.
|
|
339
345
|
const showSplit = isWideLayout && detailView !== "service-logs"
|
|
346
|
+
startupBenchMark("app_render_ready")
|
|
340
347
|
|
|
341
348
|
return (
|
|
342
349
|
<box width={width ?? 100} height={height ?? 24} flexGrow={1} flexDirection="column" backgroundColor={RGBA.fromHex(colors.screenBg)}>
|