diff --git a/internal/exporter/html.go b/internal/exporter/html.go
index 752aee9..292ed2e 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
}
diff --git a/internal/exporter/html_test.go b/internal/exporter/html_test.go
new file mode 100644
index 0000000..ac6f2d3
--- /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)
+ }
+}