@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.
Files changed (66) hide show
  1. package/AGENTS.md +23 -8
  2. package/README.md +13 -2
  3. package/package.json +35 -19
  4. package/skills/motel-debug/SKILL.md +203 -0
  5. package/skills/motel-debug/references/effect.md +38 -0
  6. package/src/App.tsx +12 -5
  7. package/src/StartupGate.tsx +289 -0
  8. package/src/cli.ts +15 -16
  9. package/src/config.ts +7 -1
  10. package/src/daemon.test.ts +332 -51
  11. package/src/daemon.ts +105 -153
  12. package/src/httpApi.ts +1 -0
  13. package/src/httpListPolicy.test.ts +76 -0
  14. package/src/httpListPolicy.ts +129 -0
  15. package/src/index.tsx +9 -2
  16. package/src/localServer.ts +194 -313
  17. package/src/mcp.ts +2 -1
  18. package/src/motel.ts +0 -2
  19. package/src/opentui-jsx.d.ts +11 -0
  20. package/src/otlp.test.ts +65 -0
  21. package/src/otlp.ts +20 -0
  22. package/src/otlpProtobuf.ts +35 -0
  23. package/src/registry.ts +37 -11
  24. package/src/runtime.ts +2 -6
  25. package/src/services/AsyncIngest.ts +22 -8
  26. package/src/services/LogQueryService.ts +13 -27
  27. package/src/services/TelemetryQuery.ts +62 -0
  28. package/src/services/TelemetryStore.ts +546 -231
  29. package/src/services/TraceQueryService.ts +22 -56
  30. package/src/services/ingestRpc.ts +2 -4
  31. package/src/services/queryRpc.ts +15 -0
  32. package/src/services/telemetryQueryWorker.ts +32 -0
  33. package/src/services/telemetryWorker.ts +5 -8
  34. package/src/startupBench.ts +19 -0
  35. package/src/storybook/aiChatStory.tsx +1 -1
  36. package/src/telemetry.test.ts +307 -41
  37. package/src/ui/AiChatView.tsx +1 -1
  38. package/src/ui/AttrFilterModal.tsx +1 -1
  39. package/src/ui/ServiceLogs.tsx +10 -7
  40. package/src/ui/SpanContentView.tsx +24 -21
  41. package/src/ui/TraceDetailsPane.tsx +1 -1
  42. package/src/ui/TraceList.tsx +1 -1
  43. package/src/ui/aiState.ts +10 -22
  44. package/src/ui/app/TraceWorkspace.tsx +2 -1
  45. package/src/ui/app/useAppLayout.ts +1 -1
  46. package/src/ui/app/useTraceScreenData.ts +35 -23
  47. package/src/ui/atoms.ts +1 -1
  48. package/src/ui/cachedLoader.test.ts +23 -0
  49. package/src/ui/cachedLoader.ts +60 -0
  50. package/src/ui/loaders.ts +34 -53
  51. package/src/ui/persistence.ts +3 -3
  52. package/src/ui/primitives.tsx +1 -1
  53. package/src/ui/state.ts +2 -0
  54. package/src/ui/theme.ts +7 -5
  55. package/src/ui/traceDetailsWidth.repro.test.ts +12 -1
  56. package/src/ui/traceSortNav.repro.seed.ts +1 -1
  57. package/src/ui/traceSortNav.repro.test.ts +12 -2
  58. package/src/ui/useAttrFilterPicker.ts +10 -8
  59. package/src/ui/useKeyboardNav.ts +28 -5
  60. package/src/ui/waterfallNav.repro.seed.ts +1 -1
  61. package/src/ui/waterfallNav.repro.test.ts +16 -8
  62. package/web/dist/assets/index-B01z9BaO.css +2 -0
  63. package/web/dist/assets/index-M86tcih5.js +22 -0
  64. package/web/dist/index.html +2 -2
  65. package/web/dist/assets/index-DnyVo03x.js +0 -27
  66. 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/TraceQueryService.ts` reads traces from the local store.
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; each has a sibling `*.repro.seed.ts` that
105
- seeds a deterministic trace into SQLite in a child process. These
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 `.motel-data/telemetry.sqlite`
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
- `.motel-data/telemetry.sqlite`. No Docker, no cloud account.
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.4",
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.1.0"
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.85.1",
76
- "@types/bun": "^1.3.12",
77
- "@types/react": "^19.2.14",
78
- "typescript": "^6.0.2"
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": "^4.0.0-beta.49",
82
- "@effect/opentelemetry": "^4.0.0-beta.49",
83
- "@effect/platform-bun": "^4.0.0-beta.50",
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.214.0",
86
- "@opentelemetry/exporter-trace-otlp-http": "^0.211.0",
87
- "@opentelemetry/sdk-logs": "^0.214.0",
88
- "@opentelemetry/sdk-node": "^0.214.0",
89
- "@opentelemetry/sdk-trace-base": "^2.5.0",
90
- "@opentelemetry/sdk-trace-node": "^2.6.1",
91
- "@opentui/core": "^0.1.99",
92
- "@opentui/react": "^0.1.99",
93
- "effect": "^4.0.0-beta.49",
94
- "react": "^19.2.5",
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/state.ts"
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)}>