fix(compile): use rune slice to truncate history summary — avoids UTF-8 boundary corruption#75
Conversation
yvonnedevlinrh
left a comment
There was a problem hiding this comment.
Good catch, the byte-slicing bug is a subtle one and this is exactly the right fix. The writeup in the PR description is really helpful too, appreciate the detail.
Approved.
I'll create a follow-up issue to add a regression test covering multibyte truncation, just so we have coverage if this area changes down the line.
jflowers
left a comment
There was a problem hiding this comment.
Good fix — the rune-slice approach is the correct solution for UTF-8 safe truncation.
Two notes for next time:
-
Use an OpenSpec for bug fixes. Even single-file fixes benefit from the lightweight
openspec/changes/workflow — it creates a traceable record (proposal + tasks), and the tasks artifact is where the regression test requirement (TC-006) gets captured before implementation begins. Running/opsx-proposetakes a couple of minutes and would have caught the missing test gap during planning rather than during review. -
Regression test (TC-006). Convention pack rule TC-006 [MUST]: bug fixes must include a regression test. @yvonnedevlinrh has committed to a follow-up issue for this — please ensure it's filed and linked.
This review was generated by /review-pr (AI-assisted).
|
Created #81 for the following on issue of adding in regression testing |
Byte-slicing a string at a fixed offset (summary[:80]) corrupts multibyte UTF-8 characters that span the boundary. For example, an em-dash (U+2014, encoded as 0xE2 0x80 0x94) sliced at byte 79 or 80 produces an invalid sequence 0xE2 0x80 followed by the first byte of '...' (0x2E), yielding 0xE2 0x80 0x2E — invalid UTF-8. Fix: convert to []rune before slicing, then convert back to string. This preserves character boundaries regardless of multibyte encoding. Reproducer: a learning whose information field contains an em-dash at or near byte offset 80 in UTF-8 encoding. Tested: go test ./tools/... passes (438 tests), recompile of process-git tag produces clean UTF-8 output.
fbb7aa4 to
570c2ad
Compare
Root cause
buildCompiledArticleintools/compile.gotruncates learning content for the history table using a byte slice:In Go,
string[i:j]slices by bytes, not characters. When a multibyte UTF-8 character (e.g. em-dash U+2014, encoded as0xE2 0x80 0x94) spans the byte-80 boundary, the slice cuts the character mid-encoding. Appending"..."then places0x2E(period) as the third byte of the incomplete sequence, producing0xE2 0x80 0x2E— invalid UTF-8.Fix
Convert to
[]runebefore slicing, then back tostring:Reproducer
Any learning whose
informationfield contains a multibyte character (em-dash, curly quote, arrows, CJK, etc.) where the UTF-8 encoding spans byte offset 80. Observed in production on theprocess-gitcompiled article — every compile produced0xE2 0x80 0x2EcausingUnicodeDecodeErrorin Python consumers of the article.Testing