diff --git a/claims.json b/claims.json index 8052821..00cd42b 100644 --- a/claims.json +++ b/claims.json @@ -102,16 +102,16 @@ }, { "id": "test-count", - "claim": "214 tests pass on Node's built-in test runner, the count docs/ROADMAP.md records for `npm test`.", - "value": "214", - "metric": "passing node:test cases (# tests 214 / # pass 214 / # fail 0)", - "method": "npm test → node --test test/*.test.js, run in the claims job after npm ci, and the TAP summary is asserted. The roadmap sentence ('214 tests on Node's built-in runner (`npm test`, 2026-09-23)') is verified by the run, not read back from the prose.", + "claim": "216 tests pass on Node's built-in test runner, the count docs/ROADMAP.md records for `npm test`.", + "value": "216", + "metric": "passing node:test cases (# tests 216 / # pass 216 / # fail 0)", + "method": "npm test → node --test test/*.test.js, run in the claims job after npm ci, and the TAP summary is asserted. The roadmap sentence ('216 tests on Node's built-in runner (`npm test`, 2026-09-23)') is verified by the run, not read back from the prose.", "repro": "npm test 2>&1 | grep -E '^# (tests|pass|fail)'", "evidence": "docs/ROADMAP.md", "as_of": "2026-09-13", "check": { "cmd": "npm test 2>&1 | grep -E '^# (tests|pass|fail)'", - "expect": { "contains": ["# tests 214", "# pass 214", "# fail 0"] }, + "expect": { "contains": ["# tests 216", "# pass 216", "# fail 0"] }, "timeout": 120 } }, diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index f3c6d11..e9ad4e7 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -8,15 +8,28 @@ - **Session browser** with tool-call inspection, trace/waterfall view, spawn tracking and message timeline - **Prompt tooling** — extraction (noise filtered), template clustering with outcome attribution, Claude-powered rewrites, and a prompt library that installs entries as native slash commands - **Global search** across all platforms, insights dashboard, incremental session backup -- **React + Vite frontend** served by an Express backend; 214 tests on Node's built-in runner (`npm test`, 2026-09-23), CI on Node 22 +- **React + Vite frontend** served by an Express backend; 216 tests on Node's built-in runner (`npm test`, 2026-09-23), CI on Node 22 - **Evidence-backed failure events and local review** with full-result invalidation, evidence navigation and narrow-screen session layout -## Roadmap +## Current priorities + +The product direction is **review the coding-agent sessions you already have, with evidence**. First prove that a useful review can be completed locally; then make it easy to try and share. Stars, downloads and screenshots alone do not establish usefulness. This order replaces the earlier feature-first ordering below. + +| Priority | Outcome | Acceptance, not a promise | +| --- | --- | --- | +| P0 | Make the new workflow immediately testable | A demo-only entry opens a clearly synthetic case: 7 pending records in 2 events, all evidence accessible, local review does not rewrite automatic results. Preserve the existing default demo and samples. | +| P1 | Make review work portable without exposing logs | Design explicit user-controlled review export/import, validate schema and evidence fingerprints, show a preview of exactly what leaves the browser, and reject stale/mismatched records. No automatic upload or raw-log export by default. | +| P2 | Validate daily usefulness with the maintainer's own sessions | Record reviewed/follow-up/expected/alternative-verification counts and timed review tasks using a fixed rubric. Keep measurements local, separate unknowns and stale labels, and publish only consented aggregate evidence. Do not infer precision or time saved from event compression. | +| P3 | Make releases reproducible for contributors | Keep clean-install tests, generated fixtures, documentation claims and release/package verification aligned. Add browser regression automation when it can run deterministically without personal logs. | + +No launch dates or star-count targets are promised. Progress is gated on these observable outcomes. Physical-device/keyboard coverage and complex Trace/analytics layouts remain separate work, not implied by the session-screen checks. + +## Existing backlog - [x] **Publish to npm** so `npx agent-xray` works without cloning — automated from GitHub releases (v1.14.0+) - [x] **Hosted live demo** on GitHub Pages with clearly-labeled synthetic session data, so people can try the UI before installing - [x] **Gemini CLI adapter** (`~/.gemini/tmp`) — shipped (#5) -- [ ] **More platform adapters** — opencode and Aider are the most-requested log formats; the per-platform module layout in `lib/platforms/` is designed for this +- [ ] **More platform adapters** — opencode and Aider are candidates, not a measured demand ranking. Require representative logs and parser acceptance tests before prioritizing them over the review workflow. - [x] **Session export & sharing** — render a session (with tool calls) to a standalone Markdown/HTML file you can attach to a bug report or blog post (#6) - [ ] **Cost & token analytics** — per-session and per-day token spend, broken down by model, building on the token counts we already parse for the summary panel - [ ] **Watch mode ergonomics** — highlight the currently-active session and surface "agent is waiting for input" state in the sidebar diff --git a/docs/diagnostics-verification.md b/docs/diagnostics-verification.md index aaed906..63ba24d 100644 --- a/docs/diagnostics-verification.md +++ b/docs/diagnostics-verification.md @@ -57,3 +57,13 @@ The preceding private, already-inspected regression set had 30 sessions and 9,07 - Existing aggregate statistics retain their original error-field semantics and may differ from diagnostic counts. - No physical iOS/Android device, Safari or software-keyboard certification is claimed. Other complex pages are not fully covered by the narrow-screen checks. - Existing lint findings and the Vite large-chunk warning remain; no claim of zero technical debt is made. + +## Release and hosted walkthrough follow-up + +The feature release is available as [v1.18.0](https://github.com/alloevil/AgentXRay/releases/tag/v1.18.0). Its [Publish run](https://github.com/alloevil/AgentXRay/actions/runs/35880348489) succeeded with a package tarball and provenance attached. An isolated installation from `https://registry.npmjs.org` returned CLI version `1.18.0`; the installed server served a synthetic OMP failure with normalized status and the bundled review UI. The initial registry query preceded npm processing and returned 404; success was recorded only after the registry and installation became available. + +A separate hosted-demo increment adds the explicitly synthetic `0199demo-diagnostics` session and a **Try diagnostics** entry, leaving the default Claude demo and previous samples intact. Raw parser output and bundled messages are asserted equal by tests; 216 total Node tests now pass (two additional hosted-fixture tests). The existing 214-test count above records the release acceptance, not the updated suite size. + +The hosted sample has 8 historical failures: one has a matching successful retry, leaving 7 pending records grouped into 2 events (6 edit failures and 1 nested search failure). A background start stays running. Six individual edit results, evidence jumps and browser-local review were verified; no backend API requests were made during the static walkthrough. Fixture regeneration produced identical bytes twice. + +The demo-only guide is height-limited on short screens. Expanded guide checks leave about 102px for messages at 360×640 and 224px at 360×800; at 740×360 landscape only 35px remains, so portrait use is recommended. This does not change the normal local application's layout or imply physical-device certification. diff --git a/docs/diagnostics.md b/docs/diagnostics.md index 226f9a5..f5ad26f 100644 --- a/docs/diagnostics.md +++ b/docs/diagnostics.md @@ -6,6 +6,8 @@ This guide covers the React UI. The synthetic terminal walkthrough uses a source ## Try it without sharing your logs +For a no-install tour, open the [hosted demo](https://alloevil.github.io/AgentXRay/) and click **体验失败复核 / Try diagnostics**. Its synthetic sample has 7 pending failure records in 2 events (6 repeated edits and 1 search failure), plus an earlier automatically recovered test failure. Open the first/last/all evidence and record a browser-local review note. This is a static sample: no live results are appended and no actual tool commands run. The terminal walkthrough below is a separate, larger fixture for live updates. + From the repository root, after installing the root and frontend dependencies: ```sh @@ -108,6 +110,8 @@ The local frozen regression set contained 30 sessions and 9,076 tool results. Gr **目标:先找到值得复查的重复操作,再追溯证据,而不是把几百条失败强行解释成几个根因。** +免安装体验:[在线 Demo](https://alloevil.github.io/AgentXRay/),点击“体验失败复核 / Try diagnostics”。这个独立合成案例将 7 条待复查记录聚为 2 个事件(6 次同参 edit 失败、1 次搜索失败),并展示一次早先测试失败的自动恢复。可查看全部证据、填写本浏览器的人工复核;它是静态示例,不追加真实结果。下面的本机终端演示则使用更大的合成日志来验证实时变化。 + 从源码仓库运行 `npm run build:ui`,再运行 `node scripts/demo-diagnostics.cjs`。打开终端打印的地址,选择 OMP 的 `[Synthetic]` 会话: - 初始为 **72 条待复查记录 → 7 个事件**,66 条同参 edit 失败集中在第一张卡片;默认展示 5 个事件,可以继续加载。 diff --git a/frontend/demo/sample-logs/omp/-demo-diagnostics/2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl b/frontend/demo/sample-logs/omp/-demo-diagnostics/2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl new file mode 100644 index 0000000..94a1105 --- /dev/null +++ b/frontend/demo/sample-logs/omp/-demo-diagnostics/2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl @@ -0,0 +1,24 @@ +{"type":"session","id":"0199demo-diagnostics","timestamp":"2026-09-23T08:00:00.000Z","cwd":"/demo/diagnostics"} +{"type":"title","title":"[Synthetic] Failure review: 7 records → 2 events"} +{"type":"message","id":"demo-review-user","timestamp":"2026-09-23T08:00:01.000Z","message":{"role":"user","content":[{"type":"text","text":"[Synthetic demo] Review repeated edit failures and a search failure. Inspect the evidence before recording a human review. These logs are fictional; no tool commands were executed."}]}} +{"type":"message","id":"demo-test-first-call","timestamp":"2026-09-23T08:00:02.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-test-first","name":"bash","arguments":{"command":"npm test","cwd":"/demo/diagnostics"}}]}} +{"type":"message","id":"demo-test-first-result","timestamp":"2026-09-23T08:00:03.000Z","message":{"role":"toolResult","toolCallId":"demo-test-first","toolName":"bash","isError":true,"details":{"exitCode":1,"wallTimeMs":100},"content":[{"type":"text","text":"Synthetic test failure: expected 2, got 1."}]}} +{"type":"message","id":"demo-test-retry-call","timestamp":"2026-09-23T08:00:04.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-test-retry","name":"bash","arguments":{"command":"npm test","cwd":"/demo/diagnostics"}}]}} +{"type":"message","id":"demo-test-retry-result","timestamp":"2026-09-23T08:00:05.000Z","message":{"role":"toolResult","toolCallId":"demo-test-retry","toolName":"bash","isError":false,"details":{"wallTimeMs":100},"content":[{"type":"text","text":"Synthetic test retry passed. This does not verify the later edits."}]}} +{"type":"message","id":"demo-edit-1-call","timestamp":"2026-09-23T08:00:10.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-1","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-1-result","timestamp":"2026-09-23T08:00:11.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-1","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 1: exact old text not found. Inspect the file before repeating this operation."}]}} +{"type":"message","id":"demo-edit-2-call","timestamp":"2026-09-23T08:00:20.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-2","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-2-result","timestamp":"2026-09-23T08:00:21.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-2","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 2: exact old text still not found."}]}} +{"type":"message","id":"demo-edit-3-call","timestamp":"2026-09-23T08:00:30.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-3","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-3-result","timestamp":"2026-09-23T08:00:31.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-3","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 3: no matching text; arguments have not changed."}]}} +{"type":"message","id":"demo-edit-4-call","timestamp":"2026-09-23T08:00:40.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-4","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-4-result","timestamp":"2026-09-23T08:00:41.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-4","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 4: exact match missing; no edit was applied."}]}} +{"type":"message","id":"demo-edit-5-call","timestamp":"2026-09-23T08:00:50.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-5","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-5-result","timestamp":"2026-09-23T08:00:51.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-5","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 5: expected old text absent."}]}} +{"type":"message","id":"demo-edit-6-call","timestamp":"2026-09-23T08:01:00.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-edit-6","name":"edit","arguments":{"path":"/demo/diagnostics/config.ts","oldText":"timeout: 10","newText":"timeout: 30"}}]}} +{"type":"message","id":"demo-edit-6-result","timestamp":"2026-09-23T08:01:01.000Z","message":{"role":"toolResult","toolCallId":"demo-edit-6","toolName":"edit","isError":true,"content":[{"type":"text","text":"Synthetic edit failure 6: unchanged operation failed again. Grouping retains all six results; it does not prove a shared root cause."}]}} +{"type":"message","id":"demo-search-call","timestamp":"2026-09-23T08:01:05.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-search","name":"web_search","arguments":{"query":"synthetic timeout configuration documentation"}}]}} +{"type":"message","id":"demo-search-result","timestamp":"2026-09-23T08:01:06.000Z","message":{"role":"toolResult","toolCallId":"demo-search","toolName":"web_search","isError":false,"details":{"error":"Synthetic provider failure: no search result available."},"content":[{"type":"text","text":"Synthetic search failed. OMP details.error records the failure even though the outer isError is false."}]}} +{"type":"message","id":"demo-background-call","timestamp":"2026-09-23T08:01:10.000Z","message":{"role":"assistant","content":[{"type":"toolCall","id":"demo-background","name":"bash","arguments":{"command":"npm run verify-config","cwd":"/demo/diagnostics","async":true}}]}} +{"type":"message","id":"demo-background-result","timestamp":"2026-09-23T08:01:11.000Z","message":{"role":"toolResult","toolCallId":"demo-background","toolName":"bash","isError":false,"details":{"async":{"state":"running","jobId":"synthetic-background"}},"content":[{"type":"text","text":"Synthetic verification was backgrounded. There is no completion result in this sample."}]}} +{"type":"message","id":"demo-review-summary","timestamp":"2026-09-23T08:01:12.000Z","message":{"role":"assistant","content":[{"type":"text","text":"Synthetic review exercise: inspect the six edit results and the search error, then record a follow-up note with evidence. The earlier test success does not validate the edits; a running background job is not a success. This static sample does not append live results."}]}} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b8b6c2e..709a063 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -26,6 +26,8 @@ export default function App() { useVersionPoller(); const view = useAppStore((s) => s.view); const setView = useAppStore((s) => s.setView); + const platform = useAppStore((state) => state.platform); + const selectedSession = useAppStore((state) => state.selectedSessionId); const [navigationOpen, setNavigationOpen] = useState(false); const navigationToggle = useRef(null); const navigationPanel = useRef(null); @@ -69,8 +71,20 @@ export default function App() {
{DEMO ? ( -
- 🧪 Demo mode — synthetic sample data (not real user sessions). Inspect your own agent logs: +
+ 🧪 Demo — synthetic data, not real sessions. +
) : null} diff --git a/frontend/src/demo/fixtures.json b/frontend/src/demo/fixtures.json index 532632c..a13e65e 100644 --- a/frontend/src/demo/fixtures.json +++ b/frontend/src/demo/fixtures.json @@ -92,6 +92,34 @@ } ], "omp": [ + { + "id": "0199demo-diagnostics", + "timestamp": "2026-09-23T08:00:00.000Z", + "lastActivity": "2026-09-23T08:01:12.000Z", + "messageCount": 22, + "userCount": 1, + "assistantCount": 11, + "toolCallCount": 10, + "toolResultCount": 10, + "topTools": [ + { + "name": "edit", + "count": 6 + }, + { + "name": "bash", + "count": 3 + }, + { + "name": "web_search", + "count": 1 + } + ], + "firstUserMessage": "[Synthetic demo] Review repeated edit failures and a search failure. Inspect the evidence before recording a human revie", + "title": "[Synthetic] Failure review: 7 records → 2 events", + "cwd": "/demo/diagnostics", + "file": "2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl" + }, { "id": "0199demo-omp-spawn", "timestamp": "2026-08-15T10:00:00.000Z", @@ -1027,23 +1055,23 @@ } ] }, - "omp/0199demo-omp-spawn": { + "omp/0199demo-diagnostics": { "session": { - "id": "0199demo-omp-spawn", - "cwd": "/demo/api-service", - "timestamp": "2026-08-15T10:00:00.000Z", - "version": "0.9.2", - "model": "claude-sonnet-4-5" + "id": "0199demo-diagnostics", + "cwd": "/demo/diagnostics", + "timestamp": "2026-09-23T08:00:00.000Z", + "version": null, + "model": null }, "messages": [ { - "id": "omp-u1", - "timestamp": "2026-08-15T10:00:02.000Z", + "id": "demo-review-user", + "timestamp": "2026-09-23T08:00:01.000Z", "role": "user", "content": [ { "type": "text", - "text": "[Synthetic demo] Add rate limiting to the public /v1/search endpoint: 60 req/min per API key, 429 with Retry-After when exceeded. Spawn a subagent to survey how our other services do it before touching code." + "text": "[Synthetic demo] Review repeated edit failures and a search failure. Inspect the evidence before recording a human review. These logs are fictional; no tool commands were executed." } ], "usage": null, @@ -1055,15 +1083,10 @@ "isError": false }, { - "id": null, - "timestamp": "2026-08-15T10:00:10.000Z", - "role": "reasoning", - "content": [ - { - "type": "text", - "text": "Two-phase plan: delegate the survey of existing rate-limit implementations to a subagent, meanwhile read the search route myself. Then implement a sliding-window limiter keyed by API key." - } - ], + "id": "demo-test-first-call", + "timestamp": "2026-09-23T08:00:02.000Z", + "role": "assistant", + "content": [], "usage": null, "model": null, "provider": null, @@ -1073,20 +1096,57 @@ "isError": false }, { - "id": "omp-a1", - "timestamp": "2026-08-15T10:00:10.000Z", - "role": "assistant", + "id": "demo-test-first", + "timestamp": "2026-09-23T08:00:02.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-test-first", + "toolName": "bash", + "details": { + "command": "npm test", + "cwd": "/demo/diagnostics" + }, + "isError": false + }, + { + "id": "demo-test-first-result", + "timestamp": "2026-09-23T08:00:03.000Z", + "role": "toolResult", "content": [ { "type": "text", - "text": "I'll spawn a DocsWriter subagent to survey the rate-limiting patterns in our other services while I read the search route." + "text": "Synthetic test failure: expected 2, got 1." } ], - "usage": { - "input": 2200, - "output": 170 + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-test-first", + "toolName": "bash", + "details": { + "exitCode": 1, + "wallTimeMs": 100 }, - "model": "claude-sonnet-4-5", + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true", + "details.exitCode=1" + ], + "warnings": [] + } + }, + { + "id": "demo-test-retry-call", + "timestamp": "2026-09-23T08:00:04.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, "provider": null, "toolCallId": null, "toolName": null, @@ -1094,87 +1154,166 @@ "isError": false }, { - "id": "omp-t1", - "timestamp": "2026-08-15T10:00:10.000Z", + "id": "demo-test-retry", + "timestamp": "2026-09-23T08:00:04.000Z", "role": "toolCall", "content": [], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t1", - "toolName": "task", + "toolCallId": "demo-test-retry", + "toolName": "bash", "details": { - "agent": "DocsWriter", - "task": "Survey how billing-service and auth-service implement per-key rate limiting: algorithm, storage, headers returned. Summarize in a table." + "command": "npm test", + "cwd": "/demo/diagnostics" }, "isError": false }, { - "id": "omp-t2", - "timestamp": "2026-08-15T10:00:10.000Z", + "id": "demo-test-retry-result", + "timestamp": "2026-09-23T08:00:05.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Synthetic test retry passed. This does not verify the later edits." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-test-retry", + "toolName": "bash", + "details": { + "wallTimeMs": 100 + }, + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "details.wallTimeMs (OMP synchronous completion)", + "isError=false" + ], + "warnings": [] + } + }, + { + "id": "demo-edit-1-call", + "timestamp": "2026-09-23T08:00:10.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "demo-edit-1", + "timestamp": "2026-09-23T08:00:10.000Z", "role": "toolCall", "content": [], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t2", - "toolName": "read", + "toolCallId": "demo-edit-1", + "toolName": "edit", "details": { - "path": "src/routes/search.ts" + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" }, "isError": false }, { - "id": "omp-r2", - "timestamp": "2026-08-15T10:00:12.000Z", + "id": "demo-edit-1-result", + "timestamp": "2026-09-23T08:00:11.000Z", "role": "toolResult", "content": [ { "type": "text", - "text": "import { Router } from 'express';\nexport const search = Router();\nsearch.get('/v1/search', async (req, res) => {\n const key = req.get('X-Api-Key');\n const hits = await index.query(req.query.q);\n res.json({ hits });\n});" + "text": "Synthetic edit failure 1: exact old text not found. Inspect the file before repeating this operation." } ], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t2", - "toolName": "read", + "toolCallId": "demo-edit-1", + "toolName": "edit", + "details": null, + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } + }, + { + "id": "demo-edit-2-call", + "timestamp": "2026-09-23T08:00:20.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, "details": null, "isError": false }, { - "id": "omp-r1", - "timestamp": "2026-08-15T10:01:45.000Z", + "id": "demo-edit-2", + "timestamp": "2026-09-23T08:00:20.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-edit-2", + "toolName": "edit", + "details": { + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" + }, + "isError": false + }, + { + "id": "demo-edit-2-result", + "timestamp": "2026-09-23T08:00:21.000Z", "role": "toolResult", "content": [ { "type": "text", - "text": "DocsWriter finished. Summary:\n\n| service | algorithm | storage | headers |\n|---|---|---|---|\n| billing-service | fixed window 100/min | Redis INCR + EXPIRE | X-RateLimit-Remaining |\n| auth-service | sliding window log | in-memory LRU | Retry-After on 429 |\n\nRecommendation: sliding window like auth-service, but keep counters in Redis so multiple replicas share state." + "text": "Synthetic edit failure 2: exact old text still not found." } ], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t1", - "toolName": "task", + "toolCallId": "demo-edit-2", + "toolName": "edit", "details": null, - "isError": false + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } }, { - "id": "omp-a2", - "timestamp": "2026-08-15T10:02:00.000Z", + "id": "demo-edit-3-call", + "timestamp": "2026-09-23T08:00:30.000Z", "role": "assistant", - "content": [ - { - "type": "text", - "text": "Survey is in — going with a Redis-backed sliding window. Implementing the middleware now." - } - ], - "usage": { - "input": 3100, - "output": 140 - }, - "model": "claude-sonnet-4-5", + "content": [], + "usage": null, + "model": null, "provider": null, "toolCallId": null, "toolName": null, @@ -1182,54 +1321,54 @@ "isError": false }, { - "id": "omp-t3", - "timestamp": "2026-08-15T10:02:00.000Z", + "id": "demo-edit-3", + "timestamp": "2026-09-23T08:00:30.000Z", "role": "toolCall", "content": [], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t3", + "toolCallId": "demo-edit-3", "toolName": "edit", "details": { - "path": "src/middleware/rate-limit.ts", - "content": "export function rateLimit({ limit = 60, windowMs = 60_000 } = {}) { /* sliding-window ZADD/ZCOUNT per api key */ }" + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" }, "isError": false }, { - "id": "omp-r3", - "timestamp": "2026-08-15T10:02:05.000Z", + "id": "demo-edit-3-result", + "timestamp": "2026-09-23T08:00:31.000Z", "role": "toolResult", "content": [ { "type": "text", - "text": "Created src/middleware/rate-limit.ts" + "text": "Synthetic edit failure 3: no matching text; arguments have not changed." } ], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t3", + "toolCallId": "demo-edit-3", "toolName": "edit", "details": null, - "isError": false + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } }, { - "id": "omp-a3", - "timestamp": "2026-08-15T10:02:20.000Z", + "id": "demo-edit-4-call", + "timestamp": "2026-09-23T08:00:40.000Z", "role": "assistant", - "content": [ - { - "type": "text", - "text": "Wiring it into the search route and adding a test for the 429 path." - } - ], - "usage": { - "input": 3600, - "output": 120 - }, - "model": "claude-sonnet-4-5", + "content": [], + "usage": null, + "model": null, "provider": null, "toolCallId": null, "toolName": null, @@ -1237,72 +1376,607 @@ "isError": false }, { - "id": "omp-t4", - "timestamp": "2026-08-15T10:02:20.000Z", + "id": "demo-edit-4", + "timestamp": "2026-09-23T08:00:40.000Z", "role": "toolCall", "content": [], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t4", + "toolCallId": "demo-edit-4", "toolName": "edit", "details": { - "path": "src/routes/search.ts", - "old": "search.get('/v1/search', async (req, res) => {", - "new": "search.get('/v1/search', rateLimit({ limit: 60 }), async (req, res) => {" + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" }, "isError": false }, { - "id": "omp-t5", - "timestamp": "2026-08-15T10:02:20.000Z", + "id": "demo-edit-4-result", + "timestamp": "2026-09-23T08:00:41.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Synthetic edit failure 4: exact match missing; no edit was applied." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-edit-4", + "toolName": "edit", + "details": null, + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } + }, + { + "id": "demo-edit-5-call", + "timestamp": "2026-09-23T08:00:50.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "demo-edit-5", + "timestamp": "2026-09-23T08:00:50.000Z", "role": "toolCall", "content": [], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t5", - "toolName": "bash", + "toolCallId": "demo-edit-5", + "toolName": "edit", "details": { - "command": "npm test -- rate-limit" + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" }, "isError": false }, { - "id": "omp-r4", - "timestamp": "2026-08-15T10:02:24.000Z", + "id": "demo-edit-5-result", + "timestamp": "2026-09-23T08:00:51.000Z", "role": "toolResult", "content": [ { "type": "text", - "text": "Updated src/routes/search.ts" + "text": "Synthetic edit failure 5: expected old text absent." } ], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t4", + "toolCallId": "demo-edit-5", "toolName": "edit", "details": null, + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } + }, + { + "id": "demo-edit-6-call", + "timestamp": "2026-09-23T08:01:00.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, "isError": false }, { - "id": "omp-r5", - "timestamp": "2026-08-15T10:03:10.000Z", + "id": "demo-edit-6", + "timestamp": "2026-09-23T08:01:00.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-edit-6", + "toolName": "edit", + "details": { + "path": "/demo/diagnostics/config.ts", + "oldText": "timeout: 10", + "newText": "timeout: 30" + }, + "isError": false + }, + { + "id": "demo-edit-6-result", + "timestamp": "2026-09-23T08:01:01.000Z", "role": "toolResult", "content": [ { "type": "text", - "text": "PASS test/rate-limit.test.ts\n ✓ allows 60 requests inside the window (12 ms)\n ✓ 61st request gets 429 with Retry-After (9 ms)\n\nTests: 2 passed, 2 total" + "text": "Synthetic edit failure 6: unchanged operation failed again. Grouping retains all six results; it does not prove a shared root cause." } ], "usage": null, "model": null, "provider": null, - "toolCallId": "omp-t5", - "toolName": "bash", + "toolCallId": "demo-edit-6", + "toolName": "edit", "details": null, - "isError": false + "isError": true, + "ompOutcome": { + "state": "failure", + "evidence": [ + "isError=true" + ], + "warnings": [] + } + }, + { + "id": "demo-search-call", + "timestamp": "2026-09-23T08:01:05.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "demo-search", + "timestamp": "2026-09-23T08:01:05.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-search", + "toolName": "web_search", + "details": { + "query": "synthetic timeout configuration documentation" + }, + "isError": false + }, + { + "id": "demo-search-result", + "timestamp": "2026-09-23T08:01:06.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Synthetic search failed. OMP details.error records the failure even though the outer isError is false." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-search", + "toolName": "web_search", + "details": { + "error": "Synthetic provider failure: no search result available." + }, + "isError": false, + "ompOutcome": { + "state": "failure", + "evidence": [ + "details.error (non-empty)" + ], + "warnings": [] + } + }, + { + "id": "demo-background-call", + "timestamp": "2026-09-23T08:01:10.000Z", + "role": "assistant", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "demo-background", + "timestamp": "2026-09-23T08:01:10.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-background", + "toolName": "bash", + "details": { + "command": "npm run verify-config", + "cwd": "/demo/diagnostics", + "async": true + }, + "isError": false + }, + { + "id": "demo-background-result", + "timestamp": "2026-09-23T08:01:11.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Synthetic verification was backgrounded. There is no completion result in this sample." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "demo-background", + "toolName": "bash", + "details": { + "async": { + "state": "running", + "jobId": "synthetic-background" + } + }, + "isError": false, + "ompOutcome": { + "state": "running", + "evidence": [ + "details.async.state=running" + ], + "warnings": [] + } + }, + { + "id": "demo-review-summary", + "timestamp": "2026-09-23T08:01:12.000Z", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Synthetic review exercise: inspect the six edit results and the search error, then record a follow-up note with evidence. The earlier test success does not validate the edits; a running background job is not a success. This static sample does not append live results." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + } + ] + }, + "omp/0199demo-omp-spawn": { + "session": { + "id": "0199demo-omp-spawn", + "cwd": "/demo/api-service", + "timestamp": "2026-08-15T10:00:00.000Z", + "version": "0.9.2", + "model": "claude-sonnet-4-5" + }, + "messages": [ + { + "id": "omp-u1", + "timestamp": "2026-08-15T10:00:02.000Z", + "role": "user", + "content": [ + { + "type": "text", + "text": "[Synthetic demo] Add rate limiting to the public /v1/search endpoint: 60 req/min per API key, 429 with Retry-After when exceeded. Spawn a subagent to survey how our other services do it before touching code." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": null, + "timestamp": "2026-08-15T10:00:10.000Z", + "role": "reasoning", + "content": [ + { + "type": "text", + "text": "Two-phase plan: delegate the survey of existing rate-limit implementations to a subagent, meanwhile read the search route myself. Then implement a sliding-window limiter keyed by API key." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "omp-a1", + "timestamp": "2026-08-15T10:00:10.000Z", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "I'll spawn a DocsWriter subagent to survey the rate-limiting patterns in our other services while I read the search route." + } + ], + "usage": { + "input": 2200, + "output": 170 + }, + "model": "claude-sonnet-4-5", + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "omp-t1", + "timestamp": "2026-08-15T10:00:10.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t1", + "toolName": "task", + "details": { + "agent": "DocsWriter", + "task": "Survey how billing-service and auth-service implement per-key rate limiting: algorithm, storage, headers returned. Summarize in a table." + }, + "isError": false + }, + { + "id": "omp-t2", + "timestamp": "2026-08-15T10:00:10.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t2", + "toolName": "read", + "details": { + "path": "src/routes/search.ts" + }, + "isError": false + }, + { + "id": "omp-r2", + "timestamp": "2026-08-15T10:00:12.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "import { Router } from 'express';\nexport const search = Router();\nsearch.get('/v1/search', async (req, res) => {\n const key = req.get('X-Api-Key');\n const hits = await index.query(req.query.q);\n res.json({ hits });\n});" + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t2", + "toolName": "read", + "details": null, + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } + }, + { + "id": "omp-r1", + "timestamp": "2026-08-15T10:01:45.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "DocsWriter finished. Summary:\n\n| service | algorithm | storage | headers |\n|---|---|---|---|\n| billing-service | fixed window 100/min | Redis INCR + EXPIRE | X-RateLimit-Remaining |\n| auth-service | sliding window log | in-memory LRU | Retry-After on 429 |\n\nRecommendation: sliding window like auth-service, but keep counters in Redis so multiple replicas share state." + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t1", + "toolName": "task", + "details": null, + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } + }, + { + "id": "omp-a2", + "timestamp": "2026-08-15T10:02:00.000Z", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Survey is in — going with a Redis-backed sliding window. Implementing the middleware now." + } + ], + "usage": { + "input": 3100, + "output": 140 + }, + "model": "claude-sonnet-4-5", + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "omp-t3", + "timestamp": "2026-08-15T10:02:00.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t3", + "toolName": "edit", + "details": { + "path": "src/middleware/rate-limit.ts", + "content": "export function rateLimit({ limit = 60, windowMs = 60_000 } = {}) { /* sliding-window ZADD/ZCOUNT per api key */ }" + }, + "isError": false + }, + { + "id": "omp-r3", + "timestamp": "2026-08-15T10:02:05.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Created src/middleware/rate-limit.ts" + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t3", + "toolName": "edit", + "details": null, + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } + }, + { + "id": "omp-a3", + "timestamp": "2026-08-15T10:02:20.000Z", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "Wiring it into the search route and adding a test for the 429 path." + } + ], + "usage": { + "input": 3600, + "output": 120 + }, + "model": "claude-sonnet-4-5", + "provider": null, + "toolCallId": null, + "toolName": null, + "details": null, + "isError": false + }, + { + "id": "omp-t4", + "timestamp": "2026-08-15T10:02:20.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t4", + "toolName": "edit", + "details": { + "path": "src/routes/search.ts", + "old": "search.get('/v1/search', async (req, res) => {", + "new": "search.get('/v1/search', rateLimit({ limit: 60 }), async (req, res) => {" + }, + "isError": false + }, + { + "id": "omp-t5", + "timestamp": "2026-08-15T10:02:20.000Z", + "role": "toolCall", + "content": [], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t5", + "toolName": "bash", + "details": { + "command": "npm test -- rate-limit" + }, + "isError": false + }, + { + "id": "omp-r4", + "timestamp": "2026-08-15T10:02:24.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "Updated src/routes/search.ts" + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t4", + "toolName": "edit", + "details": null, + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } + }, + { + "id": "omp-r5", + "timestamp": "2026-08-15T10:03:10.000Z", + "role": "toolResult", + "content": [ + { + "type": "text", + "text": "PASS test/rate-limit.test.ts\n ✓ allows 60 requests inside the window (12 ms)\n ✓ 61st request gets 429 with Retry-After (9 ms)\n\nTests: 2 passed, 2 total" + } + ], + "usage": null, + "model": null, + "provider": null, + "toolCallId": "omp-t5", + "toolName": "bash", + "details": null, + "isError": false, + "ompOutcome": { + "state": "unknown", + "evidence": [ + "details (missing bash completion evidence)" + ], + "warnings": [] + } }, { "id": "omp-a4", @@ -1438,7 +2112,15 @@ "toolCallId": "dw-t1", "toolName": "grep", "details": null, - "isError": false + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } }, { "id": "dw-r2", @@ -1456,7 +2138,15 @@ "toolCallId": "dw-t2", "toolName": "grep", "details": null, - "isError": false + "isError": false, + "ompOutcome": { + "state": "success", + "evidence": [ + "isError=false", + "content (tool result, not task acceptance)" + ], + "warnings": [] + } }, { "id": "dw-a2", @@ -1608,10 +2298,10 @@ "trend": [] }, "omp": { - "totalSessions": 1, - "totalMessages": 10, - "totalToolCalls": 5, - "errorRate": 0, + "totalSessions": 2, + "totalMessages": 32, + "totalToolCalls": 15, + "errorRate": 0.4667, "totalCost": 0, "tokenUsage": { "input": 13000, @@ -1621,11 +2311,18 @@ "toolStats": [ { "name": "edit", - "calls": 2, - "errors": 0, - "errorRate": 0, + "calls": 8, + "errors": 6, + "errorRate": 0.75, "avgDurationMs": 0 }, + { + "name": "bash", + "calls": 4, + "errors": 1, + "errorRate": 0.25, + "avgDurationMs": 50 + }, { "name": "task", "calls": 1, @@ -1641,14 +2338,106 @@ "avgDurationMs": 0 }, { - "name": "bash", + "name": "web_search", "calls": 1, "errors": 0, "errorRate": 0, "avgDurationMs": 0 } ], - "errorClusters": [], + "errorClusters": [ + { + "pattern": "Synthetic test failure: expected 2, got 1.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "bash", + "snippet": "Synthetic test failure: expected 2, got 1.", + "messageId": "demo-test-first-result", + "timestamp": "2026-09-23T08:00:03.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 1: exact old text not found. Inspect the file before repeating this operation.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 1: exact old text not found. Inspect the file before repeating this operation.", + "messageId": "demo-edit-1-result", + "timestamp": "2026-09-23T08:00:11.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 2: exact old text still not found.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 2: exact old text still not found.", + "messageId": "demo-edit-2-result", + "timestamp": "2026-09-23T08:00:21.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 3: no matching text; arguments have not changed.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 3: no matching text; arguments have not changed.", + "messageId": "demo-edit-3-result", + "timestamp": "2026-09-23T08:00:31.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 4: exact match missing; no edit was applied.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 4: exact match missing; no edit was applied.", + "messageId": "demo-edit-4-result", + "timestamp": "2026-09-23T08:00:41.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 5: expected old text absent.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 5: expected old text absent.", + "messageId": "demo-edit-5-result", + "timestamp": "2026-09-23T08:00:51.000Z" + } + ] + }, + { + "pattern": "Synthetic edit failure 6: unchanged operation failed again. Grouping retains all six results; it does not prove a shared root cause.", + "count": 1, + "examples": [ + { + "sessionId": "0199demo-diagnostics", + "toolName": "edit", + "snippet": "Synthetic edit failure 6: unchanged operation failed again. Grouping retains all six results; it does not prove a shared root cause.", + "messageId": "demo-edit-6-result", + "timestamp": "2026-09-23T08:01:01.000Z" + } + ] + } + ], "trend": [ { "date": "2026-08-15", @@ -1656,6 +2445,13 @@ "errors": 0, "toolCalls": 5, "cost": 0 + }, + { + "date": "2026-09-23", + "sessions": 1, + "errors": 7, + "toolCalls": 10, + "cost": 0 } ] } @@ -1743,8 +2539,8 @@ }, "omp": { "platform": "omp", - "totalSessions": 1, - "totalPrompts": 1, + "totalSessions": 2, + "totalPrompts": 2, "groups": [ { "directory": "/demo/api-service", @@ -1767,6 +2563,28 @@ ], "sessionCount": 1, "promptCount": 1 + }, + { + "directory": "/demo/diagnostics", + "sessions": [ + { + "id": "0199demo-diagnostics", + "file": "2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl", + "timestamp": "2026-09-23T08:00:00.000Z", + "lastActivity": "2026-09-23T08:01:12.000Z", + "slug": null, + "title": "[Synthetic] Failure review: 7 records → 2 events", + "promptCount": 1, + "prompts": [ + { + "text": "[Synthetic demo] Review repeated edit failures and a search failure. Inspect the evidence before recording a human review. These logs are fictional; no tool commands were executed.", + "timestamp": "2026-09-23T08:00:01.000Z" + } + ] + } + ], + "sessionCount": 1, + "promptCount": 1 } ] } diff --git a/intent.md b/intent.md index 178d0b3..66a88a7 100644 --- a/intent.md +++ b/intent.md @@ -31,6 +31,20 @@ The user explicitly requested commit, publish, then continued improvement. This The active `protect-default` ruleset requires a pull request and the `test` check. The user subsequently authorized autonomous branch/PR creation, publication and roadmap changes. Follow the protected-branch workflow; do not bypass the ruleset. +## Hosted diagnostics walkthrough (next increment) + +- Add one hand-written synthetic OMP session to the existing sample-log directory and regenerate bundled fixtures with the real backend. +- Keep the existing default demo session and all prior samples. Add a demo-only entry action that opens the diagnostic sample from any current tab or child transcript. +- The sample must produce 7 pending failure records in 2 events: 6 same-argument edit failures and 1 nested search failure; one earlier bash failure has a matching successful retry. A background bash start is not successful completion. +- Display a compact, clearly synthetic walkthrough with the actual counts, evidence/review instructions and a link to local usage. It does not upload notes, add model calls, or simulate live recovery in the static demo. +- Test the raw sample through the real parser and diagnostic rules, and assert the generated fixture matches it. Verify the hosted entry, evidence jump and browser-local review on desktop and narrow screens. +- Update the roadmap around measurable acceptance: public walkthrough first, safe review portability next, then owner dogfooding with explicit review outcomes. No star-count/adoption promise, no fabricated demand rankings, and no date promises. +- Publish through a separate protected PR/Pages deployment after the v1.18.0 release; do not mutate or republish the existing npm version. + +Release receipt: PR #48 merged as `c882564`; v1.18.0 Publish run `35880348489` succeeded. The official npm registry reports latest=1.18.0, and a fresh temporary installation returned that CLI version and served the bundled review UI plus a synthetic OMP result. Registry processing delay was observed before successful installation; no global npm registry configuration was changed. + +Hosted walkthrough acceptance: 216 tests pass, including raw-to-bundled fixture parity; regeneration is deterministic. Desktop and 360px browser checks cover the entry from another view/navigation, all six edit evidence links and local review with zero API requests. A height cap fixes the guide hiding the transcript on short screens; 740×360 remains cramped and is documented rather than called full mobile support. + ## Non-goals - No new platform, dependency, model call, account, telemetry, cloud log storage or automatic command execution. diff --git a/test/diagnostic-events.test.js b/test/diagnostic-events.test.js index 8868bcd..aaab659 100644 --- a/test/diagnostic-events.test.js +++ b/test/diagnostic-events.test.js @@ -224,3 +224,38 @@ test('embedded calls and separate child transcripts preserve event boundaries', assert.equal(parent.events[0].userTurn, 0); assert.equal(child.events.length, 0); }); + +test('hosted synthetic walkthrough yields two events and preserves seven failure results', async () => { + const { parseOmpSessionFile } = require('../lib/platforms/omp'); + const sample = path.join( + __dirname, + '../frontend/demo/sample-logs/omp/-demo-diagnostics/2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl' + ); + const detail = await parseOmpSessionFile(sample); + const report = diagnoseSession(detail.messages); + assert.equal(report.failureCount, 8); + assert.equal(report.recoveredCount, 1); + assert.equal(report.failures.length, 7); + assert.deepEqual( + report.events.map((event) => event.failures.length), + [6, 1] + ); + assert.equal(report.events[0].spanMs, 50000); + assert.equal(report.events[1].toolName, 'web_search'); + assert.equal(report.events[1].failures[0].message.isError, false); + assert.equal(detail.messages.find((message) => message.id === 'demo-background-result').ompOutcome.state, 'running'); + coverage(report); +}); + +test('bundled hosted walkthrough matches its real parser output', async () => { + const { parseOmpSessionFile } = require('../lib/platforms/omp'); + const sample = path.join( + __dirname, + '../frontend/demo/sample-logs/omp/-demo-diagnostics/2026-09-23T08-00-00-000Z_0199demo-diagnostics.jsonl' + ); + const detail = await parseOmpSessionFile(sample); + const fixtures = JSON.parse(readFileSync(path.join(__dirname, '../frontend/src/demo/fixtures.json'), 'utf8')); + assert.deepEqual(fixtures.details['omp/0199demo-diagnostics']?.messages, detail.messages); + assert.ok(fixtures.sessions.omp.some((session) => session.id === '0199demo-diagnostics')); + assert.ok(Object.keys(fixtures.details).some((key) => key.includes('synthetic-feature-dark-mode'))); +});