parseReports returns the culprit twice in frames — once because the culprit is pushed into the list, and once because it is also the top entry of the stack. Both formats do it, and both have since before #13; the JSON path introduced in that PR simply matches the text path.
Reproduce with a file the library repo generates:
const { parseReports } = require("./out/stParser.js");
const fs = require("fs");
const text = fs.readFileSync("<stacktale>/stacktale-core/src/test/resources/golden/repro-report.txt", "utf8");
const r = parseReports(text)[0];
console.log(r.frames.length); // 2
r.frames.forEach(f => console.log(f.file + ":" + f.line));
// PaymentService.java:118
// PaymentService.java:118
One distinct location, listed twice. repro-report.txt has a one-frame stack, so the duplicate is the whole list; on a real report it's the first two entries that are identical.
Why it matters
frames drives the navigable list in the panel. A user clicking the first two entries lands in the same place both times and has no way to tell why, which reads as the extension being confused about the stack rather than as a duplicate.
What to change
Drop the frame from the stack when it is the same location the culprit already occupies. Compare on file and line rather than on text — the culprit's text carries a ← YOUR CODE suffix that the stack copy doesn't, so a string comparison silently never matches, which is the trap here.
Keep the culprit as the first entry and drop the stack's copy, not the other way round: the culprit is the one with the marker on it.
How to check it
repro-report.txt above goes from 2 frames to 1.
- The same report in
st-json/1 gives the same count as the text form. That equivalence is the actual invariant and is worth an assertion of its own — the two paths are independent code and only a test comparing them will keep them honest.
- A report whose culprit genuinely isn't the top stack frame (a distilled stack where the culprit sits further down) keeps every frame. Deduping blindly on "first two are equal" would be wrong; the check is culprit-vs-frame, not frame-vs-frame.
parseReportsreturns the culprit twice inframes— once because the culprit is pushed into the list, and once because it is also the top entry of the stack. Both formats do it, and both have since before #13; the JSON path introduced in that PR simply matches the text path.Reproduce with a file the library repo generates:
One distinct location, listed twice.
repro-report.txthas a one-frame stack, so the duplicate is the whole list; on a real report it's the first two entries that are identical.Why it matters
framesdrives the navigable list in the panel. A user clicking the first two entries lands in the same place both times and has no way to tell why, which reads as the extension being confused about the stack rather than as a duplicate.What to change
Drop the frame from the stack when it is the same location the culprit already occupies. Compare on
fileandlinerather than ontext— the culprit'stextcarries a← YOUR CODEsuffix that the stack copy doesn't, so a string comparison silently never matches, which is the trap here.Keep the culprit as the first entry and drop the stack's copy, not the other way round: the culprit is the one with the marker on it.
How to check it
repro-report.txtabove goes from 2 frames to 1.st-json/1gives the same count as the text form. That equivalence is the actual invariant and is worth an assertion of its own — the two paths are independent code and only a test comparing them will keep them honest.