From ef151059945793b7f9c01e3c257e7bfa9603083b Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 11 Sep 2026 14:44:56 +0000 Subject: [PATCH 1/3] fix(exporter): remove unused HTML DataJSON field buildHTMLData marshaled iterations into DataJSON but the template never referenced it. Drop the field, the marshal, and the unused encoding/json import. Add regression coverage so it is not reintroduced. Fixes #14 --- internal/exporter/html.go | 7 +-- internal/exporter/html_test.go | 82 ++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 6 deletions(-) create mode 100644 internal/exporter/html_test.go diff --git a/internal/exporter/html.go b/internal/exporter/html.go index 752aee9..6e975b7 100644 --- a/internal/exporter/html.go +++ b/internal/exporter/html.go @@ -1,7 +1,6 @@ package exporter import ( - "encoding/json" "fmt" "html" "html/template" @@ -18,7 +17,6 @@ type htmlData struct { Format string Iterations []htmlIteration Summary htmlSummary - DataJSON template.JS } type htmlIteration struct { @@ -117,9 +115,6 @@ func buildHTMLData(t *trace.Trace) htmlData { } } - jsonBytes, _ := json.Marshal(d.Iterations) - d.DataJSON = template.JS(string(jsonBytes)) - return d } @@ -306,4 +301,4 @@ document.addEventListener('keydown', function(e) { show(0); -` +` \ No newline at end of file diff --git a/internal/exporter/html_test.go b/internal/exporter/html_test.go new file mode 100644 index 0000000..20e0305 --- /dev/null +++ b/internal/exporter/html_test.go @@ -0,0 +1,82 @@ +package exporter + +import ( + "os" + "path/filepath" + "strings" + "testing" + + "github.com/loop-eng/looprelay/internal/trace" +) + +func TestExportHTML_NoDeadDataJSON(t *testing.T) { + tr := trace.New([]trace.Iteration{ + { + Number: 1, + Phase: trace.PhaseAct, + Action: trace.Action{Type: "Edit", Target: "main.go", Diff: "+package main\n"}, + Tokens: trace.TokenUsage{InputTokens: 10, OutputTokens: 5}, + Context: trace.ContextState{FillPct: 42, Compactions: 1}, + CostUSD: 0.01, + }, + { + Number: 2, + Phase: trace.PhaseVerify, + Verification: &trace.Verification{Status: "success", Output: "ok"}, + Tokens: trace.TokenUsage{InputTokens: 8, OutputTokens: 2}, + CostUSD: 0.005, + }, + }) + tr.SourceFile = "demo.ltf" + tr.SourceFormat = "ltf" + tr.SessionID = "sess-1" + tr.Agent = "claude" + + out := filepath.Join(t.TempDir(), "replay.html") + if err := ExportHTML(tr, out); err != nil { + t.Fatalf("ExportHTML: %v", err) + } + + raw, err := os.ReadFile(out) + if err != nil { + t.Fatalf("read output: %v", err) + } + html := string(raw) + + if !strings.Contains(html, "Iteration 1") { + t.Error("expected iteration 1 in HTML") + } + if !strings.Contains(html, "Iteration 2") { + t.Error("expected iteration 2 in HTML") + } + if !strings.Contains(html, `const total = 2`) { + t.Error("expected JS total from summary") + } + // Regression: unused DataJSON must not be reintroduced into the page. + if strings.Contains(html, "DataJSON") { + t.Error("HTML must not reference unused DataJSON") + } + // Marshal of iterations used to inject a raw JSON blob into the page; ensure gone. + if strings.Contains(html, `"ActionType"`) || strings.Contains(html, `"PhaseClass"`) { + t.Error("HTML must not embed marshaled htmlIteration JSON (former DataJSON)") + } +} + +func TestBuildHTMLData_OmitsDataJSONComputation(t *testing.T) { + tr := trace.New([]trace.Iteration{ + {Number: 1, Phase: trace.PhasePlan, CostUSD: 0.02}, + }) + tr.SourceFile = "x.ltf" + tr.SourceFormat = "ltf" + + d := buildHTMLData(tr) + if len(d.Iterations) != 1 { + t.Fatalf("expected 1 iteration, got %d", len(d.Iterations)) + } + if d.Iterations[0].Phase != "plan" { + t.Errorf("expected plan phase, got %q", d.Iterations[0].Phase) + } + if d.Summary.TotalIterations != 1 { + t.Errorf("expected summary total 1, got %d", d.Summary.TotalIterations) + } +} \ No newline at end of file From b9014785f865b38afa695a514004f825c75b1989 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 11 Sep 2026 14:46:52 +0000 Subject: [PATCH 2/3] style(exporter): restore trailing newline in html.go --- internal/exporter/html.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/exporter/html.go b/internal/exporter/html.go index 6e975b7..292ed2e 100644 --- a/internal/exporter/html.go +++ b/internal/exporter/html.go @@ -301,4 +301,4 @@ document.addEventListener('keydown', function(e) { show(0); -` \ No newline at end of file +` From 731111db515501a9db7046e78a7a1df6427de3be Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 11 Sep 2026 14:46:54 +0000 Subject: [PATCH 3/3] style(exporter): restore trailing newline in html_test.go --- internal/exporter/html_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/exporter/html_test.go b/internal/exporter/html_test.go index 20e0305..ac6f2d3 100644 --- a/internal/exporter/html_test.go +++ b/internal/exporter/html_test.go @@ -12,10 +12,10 @@ import ( func TestExportHTML_NoDeadDataJSON(t *testing.T) { tr := trace.New([]trace.Iteration{ { - Number: 1, - Phase: trace.PhaseAct, - Action: trace.Action{Type: "Edit", Target: "main.go", Diff: "+package main\n"}, - Tokens: trace.TokenUsage{InputTokens: 10, OutputTokens: 5}, + Number: 1, + Phase: trace.PhaseAct, + Action: trace.Action{Type: "Edit", Target: "main.go", Diff: "+package main\n"}, + Tokens: trace.TokenUsage{InputTokens: 10, OutputTokens: 5}, Context: trace.ContextState{FillPct: 42, Compactions: 1}, CostUSD: 0.01, }, @@ -79,4 +79,4 @@ func TestBuildHTMLData_OmitsDataJSONComputation(t *testing.T) { if d.Summary.TotalIterations != 1 { t.Errorf("expected summary total 1, got %d", d.Summary.TotalIterations) } -} \ No newline at end of file +}