Skip to content

Bug-hunt round 30: word-level invisible-text filtering, stale install.sh trap comment - #46

Merged
REPPL merged 3 commits into
mainfrom
bughunt-30
Aug 6, 2026
Merged

Bug-hunt round 30: word-level invisible-text filtering, stale install.sh trap comment#46
REPPL merged 3 commits into
mainfrom
bughunt-30

Conversation

@REPPL

@REPPL REPPL commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Round 30 of the automated bug-hunt loop (state: #24).

Confirmed findings (fixed)

1. internal/transcribe/transcribe.go:527 (mapSegments) — nitpick
The word-level emptiness check judged presence via strings.TrimSpace(w.W), the raw form, unlike the segment-level guard five lines above it (transcribe.go:508), which decides presence on session.SafeText's rendered form. A word that is entirely invisible-only Unicode (e.g. a zero-width space, U+200B) is non-empty raw and survives TrimSpace (which strips only Unicode whitespace, not Cf format characters), so it reached transcript.jsonl, timeline.jsonl, and — once SafeText was applied at marshal time in analyze.EmitRequest — an emitted analysis request as a timestamped word with no visible content ({"w":"","t":...}). Low severity: nothing in the codebase renders or otherwise consumes the words field (report.go never reads it), so the blast radius is one cosmetic empty entry.

Fix: the word-level check now mirrors the segment-level one, deciding presence on session.SafeText(word) while still storing the raw trimmed form. Covered by a new regression test, TestMapSegmentsDropsInvisibleOnlyWord, mirroring the existing TestMapSegmentsDropsInvisibleOnlyText; verified failing before the fix (the invisible word survived into utts[0].Words) and passing after.

2. install.sh:282-285 — nitpick
A comment in install_ffmpeg_local's error-handling block claimed an unguarded failure would leak $tmp2 because "the EXIT trap covers only install_binary's $tmp" — describing behaviour that predates the trap being widened. The trap set at install.sh:134 already sweeps ${tmp2:+"$tmp2"} alongside $gnupg/$uvd, directly contradicting the stale parenthetical a few lines below it (install.sh:128-130 correctly states the traps cover the dependency stage). No behavioural change — comment-only fix, so no new test.

Considered and rejected (adversarially refuted, or precedent duplicates)

  • internal/analyze/validate.go:66 (indexTimeline) writing idx.uttText[nid] without the empty-id guard its sibling idx.ids/idx.selectors/idx.routes writes apply — the read path (validate.go:200) is reached only after idx.ids[nid] already passed a gate that idx.ids[""] never satisfies, so the unguarded key is write-only dead data with no observable effect on validation output.
  • internal/session/session.go:299-307 (WriteFileAtomicNoFollow) refusing only symlinks at its target path, not other non-regular files (FIFO, device, socket), unlike its sibling openNoFollow's stricter check — the function never opens the pre-existing file; it writes to a fresh temp file and os.Renames over the target, and rename(2) neither opens, blocks on, nor writes through a FIFO, so the hazard the sibling guard exists to prevent (an open(2) hang) is absent by construction, not merely unexploited.
  • docs/reference/cli.md:61's -offset table cell compressing the "external audio vs the session's own audio.wav" branch as "with/without -audio" — the surrounding prose (lines 63, 71) states the exception twice within ten lines, and the table cell already compresses two other branches (a bad manifest t0, an unusable sidecar) the same way; a reader following the section is not misled.
  • .github/workflows/ci.yml:84-85's cross-compile-step comment ("linux/amd64 already covered by Build above") read as a claim that the Build step also uses CGO_ENABLED=0 — the comment's CGO_ENABLED=0 clause and its coverage clause are two independent statements joined by a semicolon; the coverage claim (the step's own stated purpose, catching a GOOS-conditional compile break) holds regardless, and the module has no cgo-conditional code paths.
  • docs/how-to/analyse-a-session.md:12's "four steps" intro read against its five numbered headings — the fifth, re-rendering the report, is grammatically set off by an em dash as a follow-on outside the enumerated list ("four steps: A, B, C, and D — then E"), and invokes a different, separately-documented pipeline command (report), not a fifth step of the analysis layer the intro scopes itself to.
  • docs/reference/session-directory.md:76,108's abbreviated utt-003 example text read against the fuller fixture sentence — the page's other examples are established as illustrative reductions too (the manifest example already drops three fixture fields), the words array is trimmed in step with the shortened text rather than left orphaned, and no findings.jsonl quote cites utt-003, so nothing depends on the byte-exact form.
  • Excluded before verification, as a precedent duplicate of a finding already discussed in an earlier round: AGENTS.md claiming CI runs plain go test ./... (round 13).

Verification

go build, gofmt -l ., go vet ./..., go test ./..., go test -race ./..., and the pipeline smoke (merge/report on examples/sample-session) all pass.

.abcd/work/DECISIONS.md gains the round's entry.

REPPL added 3 commits August 6, 2026 10:58
mapSegments judged a word's emptiness on the raw form (w.W after
TrimSpace), unlike the sibling segment-level guard five lines above,
which decides presence on session.SafeText's rendered form. A word
that is entirely invisible-only Unicode (e.g. ZWSP U+200B) is
non-empty raw and survives TrimSpace, so it reached transcript.jsonl,
timeline.jsonl, and the analysis request as a timestamped word with
no visible content.

Assisted-by: Claude:claude-sonnet-5
The comment at install_ffmpeg_local's error-handling block claimed an
unguarded failure would leak $tmp2 because "the EXIT trap covers only
install_binary's $tmp" — describing pre-fix behaviour. The trap set at
line 134 already sweeps $tmp2 (added alongside $gnupg/$uvd), directly
contradicting the stale parenthetical a few lines above it.

Assisted-by: Claude:claude-sonnet-5
Assisted-by: Claude:claude-sonnet-5
@REPPL
REPPL merged commit ad3106a into main Aug 6, 2026
6 checks passed
@REPPL
REPPL deleted the bughunt-30 branch August 6, 2026 12:32
@REPPL REPPL mentioned this pull request Aug 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant