Files
deepseek-harness/examples/desktop/docs/trace-signals-shoot/signals-01-timeline-loop.html
ZiyaZhang 47949244c8 feat(desktop): trace signal detection + tri-view badges
Adds a small signal-detection layer over trace payloads and surfaces
three kinds of semantic signals inline in the trace tri-view (Timeline
/ Graph / turn footer chips) so a researcher can spot repetitive
tool-call loops, redundant identical calls, and plan-completion beats
without hand-scanning the raw log:

- loop-detected  — three or more repeats of the same tool.method
                   signature inside a window, badge in Timeline gutter
                   + colored ring on the Graph node
- redundant-call — identical (tool, arg-hash) call twice without any
                   state change in between, badge + subdued styling
                   so it stays advisory not alarmist
- plan-* chips   — plan/subplan step-complete beats surfaced above the
                   assistant body in the turn footer, wired through
                   the finishTurnContainer tail

Pure rendering; no wire-format changes needed on the runtime side —
signal detection is fixture-driven off the same payload the tri-view
already consumes. Companion RFC L-2 in docs/upstream-ledger.md asks
the runtime to emit these signals natively rather than deriving them
in the shell, so once L-2 lands the shell will consume upstream signals
and this detector becomes a fallback.

Files:
  src/renderer/trace-signal-detect.js       new  (loop/redundant/plan)
  src/renderer/trace-tri-view.js            +24  (wire detector output)
  src/renderer/trace-timeline.js            +39  (badge in gutter)
  src/renderer/trace-graph.js               +30  (colored ring)
  src/renderer/renderer.js                  +81  (finishTurnContainer chips)
  src/renderer/index.html                    +1  (one script tag)
  src/renderer/style.css                    +58  (badge/ring/chip rules)
  docs/upstream-ledger.md                  +121  (append L-2 RFC)
  scripts/qa-trace-signals-fixture.mjs     new  (headless SVG proof)
  scripts/qa-trace-signals-shoot.mjs       new  (CDP live shoot)
  docs/trace-signals-shoot/*.html          new  (3 fixture-driven shots)
  test/trace-signal-detect.test.js         new  (detector unit tests)
  test/trace-signal-overlay.test.js        new  (renderer overlay tests)

Test suite: 1668/1668 pass (17 new). Fixture-driven signals-01-timeline-
loop.html regenerated byte-identical from the merged HEAD.
2026-07-19 00:57:19 -07:00

50 lines
6.2 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html><head><meta charset="utf-8"><title>Signals 01 — Timeline: loop-detected badge</title>
<style>
body { font: 13px system-ui, sans-serif; margin: 24px; background: #f9fafb; color: #1d1d1f; }
h1 { font-size: 15px; margin: 0 0 8px 0; }
.note { color: #6b6b70; font-size: 12px; margin: 0 0 16px 0; max-width: 720px; line-height: 1.4; }
.stage { background: #fff; padding: 16px; border: 1px solid #e5e5ea; border-radius: 8px; overflow-x: auto; }
/* Palette matches src/renderer/style.css tail block for signal badges/rings/chips. */
.trace-timeline-svg { font: 11px system-ui, sans-serif; }
.trace-timeline-tick { stroke: #d0d0d5; stroke-width: 0.5; stroke-dasharray: 2 3; }
.trace-timeline-tick-label { fill: #6b6b70; font-size: 10px; }
.trace-timeline-label { fill: #1d1d1f; font-size: 11px; }
.trace-timeline-bar.family-step { fill: #b0b4bd; }
.trace-timeline-bar.family-llm { fill: #7c3aed; }
.trace-timeline-bar.family-tool { fill: #b45309; }
.trace-timeline-bar.family-input { fill: #6b6b70; }
.trace-timeline-signal-badge { stroke: rgba(0,0,0,0.15); stroke-width: 0.75; }
.trace-timeline-signal-badge.sig-error { fill: #dc2626; }
.trace-timeline-signal-badge.sig-loop { fill: #ef4444; }
.trace-timeline-signal-badge.sig-redundant{ fill: #f59e0b; }
.trace-timeline-signal-badge.sig-plan { fill: #2563eb; }
.trace-graph-svg { background: #fff; }
.trace-graph-node-body { fill: #f3f4f6; stroke: #9ca3af; stroke-width: 1; }
.trace-graph-node-body.family-tool { fill: #fde68a; stroke: #b45309; }
.trace-graph-node-body.family-step { fill: #e5e7eb; stroke: #6b7280; }
.trace-graph-node-body.family-llm { fill: #ddd6fe; stroke: #7c3aed; }
.trace-graph-node-glyph { font: 12px system-ui, sans-serif; fill: #1d1d1f; }
.trace-graph-node-label { font: 10px system-ui, sans-serif; fill: #1d1d1f; }
.trace-graph-edge { stroke: #9ca3af; stroke-width: 1.2; }
.trace-graph-signal-ring { stroke-width: 2.5; fill: none; }
.trace-graph-signal-ring.sig-error { stroke: #dc2626; }
.trace-graph-signal-ring.sig-loop { stroke: #ef4444; }
.trace-graph-signal-ring.sig-plan { stroke: #2563eb; }
.assistant-turn { border-left: 3px solid #d0d0d5; padding: 12px; margin: 12px 0; background: #fff; border-radius: 6px; }
.turn-body { display: flex; flex-direction: column; gap: 6px; }
.text-block { font-size: 13px; color: #1d1d1f; }
.tool-row { font-family: ui-monospace, monospace; font-size: 12px; color: #4b5563; }
.turn-glyph { display: inline-block; width: 14px; }
.turn-signal-chip-row { display: flex; flex-wrap: wrap; gap: 6px; padding: 4px 0 6px 0; align-items: center; }
.turn-signal-chip { font-size: 11px; line-height: 1; padding: 3px 8px; border-radius: 10px; border: 1px solid transparent; background: rgba(0,0,0,0.03); color: #1d1d1f; cursor: pointer; font-family: inherit; }
.turn-signal-chip::before { content: ''; display: inline-block; width: 6px; height: 6px; border-radius: 50%; margin-right: 6px; vertical-align: 1px; background: currentColor; }
.turn-signal-chip.sig-error, .turn-signal-chip.sig-loop { color: #b91c1c; border-color: rgba(239,68,68,0.35); background: rgba(239,68,68,0.06); }
.turn-signal-chip.sig-redundant { color: #92400e; border-color: rgba(245,158,11,0.4); background: rgba(245,158,11,0.08); }
.turn-signal-chip.sig-plan { color: #1d4ed8; border-color: rgba(37,99,235,0.35); background: rgba(37,99,235,0.06); }
</style>
</head><body>
<h1>Signals 01 — Timeline: loop-detected badge</h1>
<p class="note">Three consecutive <code>fs.read</code> calls with identical args. The detector fires <code>loop-detected</code> on the third call; its badge sits in the left gutter, colored red. Signals detected: <code>loop-detected</code>. See docs/upstream-ledger.md L-2.</p>
<div class="stage"><div data-total-ms="500" class="trace-timeline"><svg viewBox="0 0 820 110" width="820" height="110" role="img" aria-label="Timeline of 4 rows over 500ms" class="trace-timeline-svg"><g class="trace-timeline-axis"><line x1="220" y1="22" x2="220" y2="106" class="trace-timeline-tick"></line><text x="223" y="12" class="trace-timeline-tick-label">0ms</text><line x1="367" y1="22" x2="367" y2="106" class="trace-timeline-tick"></line><text x="370" y="12" class="trace-timeline-tick-label">125ms</text><line x1="514" y1="22" x2="514" y2="106" class="trace-timeline-tick"></line><text x="517" y="12" class="trace-timeline-tick-label">250ms</text><line x1="661" y1="22" x2="661" y2="106" class="trace-timeline-tick"></line><text x="664" y="12" class="trace-timeline-tick-label">375ms</text><line x1="808" y1="22" x2="808" y2="106" class="trace-timeline-tick"></line><text x="811" y="12" class="trace-timeline-tick-label">500ms</text></g><g data-seq="10" data-kind="step" class="trace-timeline-row family-step depth-0"><text x="8" y="38.96" class="trace-timeline-label"><title>step 1.0 — read main.ts · 500ms</title></text><rect x="220" y="30" width="588" height="10" rx="2" ry="2" class="trace-timeline-bar family-step kind-step"><title>step 1.0 — read main.ts · 500ms · 500ms</title></rect></g><g data-seq="11" data-kind="event" class="trace-timeline-row family-tool depth-1"><text x="20" y="56.96" class="trace-timeline-label"><title>tool: fs.read</title></text><rect x="278.8" y="48" width="35.27999999999997" height="10" rx="2" ry="2" class="trace-timeline-bar family-tool kind-event"><title>tool: fs.read · 30ms</title></rect></g><g data-seq="12" data-kind="event" class="trace-timeline-row family-tool depth-1"><text x="20" y="74.96" class="trace-timeline-label"><title>tool: fs.read</title></text><rect x="396.4" y="66" width="35.27999999999997" height="10" rx="2" ry="2" class="trace-timeline-bar family-tool kind-event"><title>tool: fs.read · 30ms</title></rect></g><g data-seq="13" data-kind="event" class="trace-timeline-row family-tool depth-1"><text x="20" y="92.96" class="trace-timeline-label"><title>tool: fs.read</title></text><rect x="514" y="84" width="35.27999999999997" height="10" rx="2" ry="2" class="trace-timeline-bar family-tool kind-event"><title>tool: fs.read · 30ms</title></rect></g><g class="trace-timeline-badges"><circle cx="210" cy="89" r="4" class="trace-timeline-signal-badge sig-loop"><title>Loop detected · 3 × fs.read · prior seq 11, 12 · (heuristic)</title></circle></g></svg></div></div>
</body></html>