ML-161 (and subtasks): research and plan
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
---
|
||||
id: ML-161
|
||||
title: pi access to production errors
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-05-04 08:06'
|
||||
updated_date: '2026-05-04 08:10'
|
||||
labels:
|
||||
- pi
|
||||
- api
|
||||
- observability
|
||||
dependencies:
|
||||
- ML-162
|
||||
- ML-163
|
||||
- ML-164
|
||||
references:
|
||||
- 'backlog://document/doc-7'
|
||||
priority: medium
|
||||
ordinal: 4000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Errors in production are captured via the `error_tracker` Elixir dependency and accessed via a dedicated dashboard at `/dev/errors`. There is no built-in tooling or endpoint to pull production errors programmatically in a pi session. This task covers the investigation and implementation of the best approach to expose production error data to pi, broken into three subtasks:
|
||||
|
||||
1. **Expose production errors via a programmatic API (behind auth)** — Add a JSON API endpoint under `/api/v1/errors` that lists errors and shows individual error details from the error_tracker tables.
|
||||
2. **Create pi tools to pull errors** — Implement `fetch_production_errors` (list with filtering/pagination) and `fetch_production_error` (single error detail) as pi tools.
|
||||
3. **Create a pi extension to browse errors** — Build an interactive TUI browsing experience via a pi extension that uses the tools from subtask 2.
|
||||
|
||||
### Error data attributes
|
||||
|
||||
These are the attributes that subtask implementations must treat as the canonical data model:
|
||||
|
||||
**Error (from `error_tracker_errors` table):**
|
||||
- `id` — UUID, unique error identifier
|
||||
- `kind` — string, e.g. "error", "throw", "exit"
|
||||
- `reason` — string, the error message/reason
|
||||
- `source_line` — string, e.g. "lib/foo.ex:42"
|
||||
- `source_function` — string, e.g. "MusicLibrary.Foo.bar/2"
|
||||
- `status` — atom: `:resolved` | `:unresolved`
|
||||
- `fingerprint` — hex string, deterministic hash of (kind, source_line, source_function)
|
||||
- `last_occurrence_at` — UTC datetime (microsecond precision)
|
||||
- `muted` — boolean
|
||||
- `inserted_at` / `updated_at` — UTC datetime
|
||||
|
||||
**Occurrence (from `error_tracker_occurrences` table):**
|
||||
- `id` — UUID
|
||||
- `reason` — string, error reason at time of occurrence
|
||||
- `context` — map, includes `live_view.view`, `request.path`, etc.
|
||||
- `breadcrumbs` — array of strings
|
||||
- `stacktrace` — embedded struct with `lines` array (each having: `application`, `module`, `function`, `arity`, `file`, `line`)
|
||||
- `error_id` — FK to error
|
||||
- `inserted_at` — UTC datetime
|
||||
|
||||
**Aggregated/per-error metadata (computed, not stored):**
|
||||
- `occurrence_count` — total occurrences for this error
|
||||
- `first_occurrence_at` — earliest occurrence timestamp
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
@@ -0,0 +1,47 @@
|
||||
---
|
||||
id: ML-162
|
||||
title: Expose production errors via JSON API endpoint
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-05-04 08:08'
|
||||
updated_date: '2026-05-04 08:10'
|
||||
labels:
|
||||
- api
|
||||
- elixir
|
||||
- backend
|
||||
dependencies: []
|
||||
parent_task_id: DRAFT-1
|
||||
priority: medium
|
||||
ordinal: 7000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Add an API controller and routes under `/api/v1/errors` to expose ErrorTracker data as JSON, behind the existing Bearer token auth.
|
||||
|
||||
This subtask covers the server-side work only: controller, JSON serialization, context queries, and routes. The pi tooling and extensions are covered by separate subtasks.
|
||||
|
||||
### API design
|
||||
|
||||
**`GET /api/v1/errors`** — List errors
|
||||
- Query params: `status` (resolved/unresolved), `muted` (true/false), `search` (substring match on reason), `limit` (default 50), `offset` (default 0)
|
||||
- Returns: `{ errors: [...], total: n, limit: n, offset: n }`
|
||||
- Each error includes: id, kind, reason, source_line, source_function, status, fingerprint, last_occurrence_at, muted, inserted_at, updated_at, occurrence_count, first_occurrence_at
|
||||
|
||||
**`GET /api/v1/errors/:id`** — Single error detail
|
||||
- Returns the error with all its occurrences (with stacktraces), sorted by inserted_at desc
|
||||
- Each occurrence includes: id, reason, context, breadcrumbs, stacktrace (lines), inserted_at
|
||||
|
||||
### Data attributes (canonical — shared with all subtasks)
|
||||
|
||||
**Error fields:** id, kind, reason, source_line, source_function, status, fingerprint, last_occurrence_at, muted, inserted_at, updated_at
|
||||
**Occurrence fields:** id, reason, context, breadcrumbs, stacktrace (with lines), error_id, inserted_at
|
||||
**Computed:** occurrence_count, first_occurrence_at
|
||||
|
||||
### Dependencies
|
||||
|
||||
- Uses `MusicLibrary.TelemetryRepo` (already exists)
|
||||
- Uses the `error_tracker_errors` and `error_tracker_occurrences` tables (already exist)
|
||||
- Auth via existing `require_api_token` plug (already in use by `/api/v1` pipeline)
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
@@ -0,0 +1,51 @@
|
||||
---
|
||||
id: ML-163
|
||||
title: Create pi tools to fetch production errors
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-05-04 08:08'
|
||||
updated_date: '2026-05-04 08:15'
|
||||
labels:
|
||||
- pi
|
||||
- tool
|
||||
- typescript
|
||||
dependencies: []
|
||||
parent_task_id: DRAFT-1
|
||||
priority: medium
|
||||
ordinal: 5000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Create `fetch_production_errors` and `fetch_production_error` pi tools that call the `/api/v1/errors` JSON API endpoint.
|
||||
|
||||
These tools are registered via `pi.registerTool()` in a pi extension. They make authenticated HTTP requests to the production server using the same HURL variable pattern established by `fetch_production_logs` (ML-160).
|
||||
|
||||
### Tools
|
||||
|
||||
**`fetch_production_errors`** — List/filter production errors
|
||||
- Parameters: `status` (optional: "resolved" | "unresolved"), `muted` (optional: boolean), `search` (optional: string, substring match on reason), `limit` (optional: number, default 50), `offset` (optional: number, default 0)
|
||||
- Calls `GET /api/v1/errors` with query params
|
||||
- Returns: formatted list of errors with counts and timestamps
|
||||
- Truncates output with `truncateTail` (50KB / 2000 lines)
|
||||
|
||||
**`fetch_production_error`** — Get a single error with full details
|
||||
- Parameters: `id` (required: UUID string)
|
||||
- Calls `GET /api/v1/errors/:id`
|
||||
- Returns: error details with all occurrences, stacktraces, and context
|
||||
- Truncates output with `truncateTail`
|
||||
|
||||
### Tool guidelines (promptGuidelines)
|
||||
- Use fetch_production_errors when investigating what errors are occurring in production
|
||||
- Use fetch_production_error when you need full details on a specific error, including stacktraces and context
|
||||
- Start with a small limit (e.g., 20) and filter by status or search before fetching large result sets
|
||||
|
||||
### Auth
|
||||
|
||||
Uses the same HURL variable pattern as `fetch_production_logs`:
|
||||
- `PI_API_TOKEN` — Bearer token for API auth
|
||||
- `PI_SERVICE_FQDN_WEB` — Production domain
|
||||
|
||||
They need to be configured in the pi environment as local secrets.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
@@ -0,0 +1,79 @@
|
||||
---
|
||||
id: ML-164
|
||||
title: Create pi extension for interactive error browsing
|
||||
status: To Do
|
||||
assignee: []
|
||||
created_date: '2026-05-04 08:08'
|
||||
updated_date: '2026-05-04 08:10'
|
||||
labels:
|
||||
- pi
|
||||
- extension
|
||||
- typescript
|
||||
- tui
|
||||
dependencies: []
|
||||
parent_task_id: DRAFT-1
|
||||
priority: medium
|
||||
ordinal: 6000
|
||||
---
|
||||
|
||||
## Description
|
||||
|
||||
<!-- SECTION:DESCRIPTION:BEGIN -->
|
||||
Build a pi extension that provides an interactive TUI for browsing production errors, using the `fetch_production_errors` and `fetch_production_error` tools from the parent task.
|
||||
|
||||
This extension gives the user (and LLM) a browseable interface for production errors, accessible via a slash command like `/prod-errors`.
|
||||
|
||||
### Extension features
|
||||
|
||||
1. **`/prod-errors` command** — Opens an interactive TUI using `ctx.ui.custom()`:
|
||||
- Lists recent errors (unresolved first, then by last_occurrence_at desc)
|
||||
- Shows key metadata: reason (truncated), kind, source location, occurrence count, last seen, status badge, muted indicator
|
||||
- Keyboard navigation: up/down to select, Enter to view details, Escape to close
|
||||
- Filter toggle: show/hide resolved, show/hide muted (keyboard shortcuts)
|
||||
- Pagination: load more errors as user scrolls
|
||||
|
||||
2. **Error detail view** (Enter on an error):
|
||||
- Full reason text
|
||||
- Source location with link-like formatting
|
||||
- Status, muted, fingerprint
|
||||
- Timeline of occurrences with timestamps
|
||||
- Stacktrace display (collapsible per occurrence)
|
||||
- Context display (request path, LiveView, etc.)
|
||||
- Breadcrumbs if present
|
||||
|
||||
3. **UI patterns** (consistent with existing `/prod-logs` extension):
|
||||
- Uses `pi.exec("curl", ...)` to call the API
|
||||
- Reads credentials from `resolveVar()` (same pattern as prod-logs)
|
||||
- Keyboard shortcuts displayed as hints
|
||||
- Theme-aware rendering via `ctx.ui.theme`
|
||||
|
||||
### TUI component design
|
||||
|
||||
```
|
||||
╔══════════════════════════════════════════════════╗
|
||||
║ Production Errors [12 errors] ║
|
||||
╠══════════════════════════════════════════════════╣
|
||||
║ ▶ [UNRESOLVED] FunctionClauseError ║
|
||||
║ MusicLibrary.Foo.bar/2 lib/foo.ex:42 ║
|
||||
║ 23 occurrences · last seen 2h ago ║
|
||||
║ ──────────────────────────────────────────────── ║
|
||||
║ [RESOLVED] MatchError ║
|
||||
║ MusicLibrary.Baz.qux/1 lib/baz.ex:15 ║
|
||||
║ 5 occurrences · last seen 3d ago ║
|
||||
║ ──────────────────────────────────────────────── ║
|
||||
║ [MUTED] KeyError (key :foo not found) ║
|
||||
║ MusicLibrary.Other.func/3 lib/other.ex:99 ║
|
||||
║ 1 occurrence · last seen 7d ago ║
|
||||
║ ──────────────────────────────────────────────── ║
|
||||
║ ║
|
||||
║ ↑↓ navigate ↵ details r toggle resolved ║
|
||||
║ m toggle muted q quit ║
|
||||
╚══════════════════════════════════════════════════╝
|
||||
```
|
||||
|
||||
### File location
|
||||
|
||||
`.pi/extensions/prod-errors/index.ts` (new extension, separate from `prod-logs`)
|
||||
|
||||
The prod-logs extension already provides the `resolveVar` pattern and `fetchLogs` function. This extension follows the same conventions but for error_tracker data.
|
||||
<!-- SECTION:DESCRIPTION:END -->
|
||||
Reference in New Issue
Block a user