Repository navigation
lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741
Description
Feature: #833 — Renderings: the pipeline from result value to stdout
Epic: #740 — Command line interface
Converted by: the Phase 4 TableFormatter work in docs/plans/cli-output-conventions.md
lore search renders its results table with byte arithmetic, so a description containing any non-ASCII
character is truncated at the wrong place and can be cut mid-rune.
The code
cmd/lore/lore/commands.go:536-539:
desc := r.Description
if len(desc) > 50 {
desc = desc[:47] + "..."
}Two defects, neither a panic
len(desc) > 50 guards the slice against a range error, so this does not crash. It is wrong in two quieter
ways:
- The threshold measures bytes, not characters. A 20-character CJK description is 60 bytes, so it is
truncated as "too long" while a 50-character ASCII description is not. The user sees an arbitrary cut. desc[:47]can split a rune. Byte 47 may land inside a multi-byte character, and the output is then
invalid UTF-8 — rendered as a replacement character by most terminals.
The same byte-vs-width confusion affects alignment: the %-30s column padding counts bytes, so a row with
any wide or multi-byte character does not line up with its neighbours.
Scope
Not only this call site. Any fixed-width fmt.Printf table has the same problem, and runSearch is the one
real table in the tree today (lore list is a stub). The fix is the shared TableFormatter that #740's
Phase 4 introduces: measure in runes, truncate on a rune boundary, and pad by display width rather than byte
count.
Acceptance
- A description of non-ASCII characters is truncated by character count, not byte count
- No truncation produces invalid UTF-8
- Columns align when a row contains multi-byte characters
installedis a field the renderer decorates, not a*concatenated onto the name column
Retracted 2026-09-18: this was never what lore search truncates descriptions by byte count, cutting multi-byte characters mid-rune #741 fixed. §7 rules it directly --installedstays a
field that--output jsonemits, and no rendering folds a boolean into a name column -- so the
box closes on the ruling rather than on work. Ledger row 14.
Activity
feat(writ): the common set, and --json retires (#739, #740)
feat(result): one delimited formatter with three presets, and one tab…
David-Noble-at-work commentedon Aug 30, 2026
Half of this is fixed on
cli-output-conventions(PR #744), and half is not. Recording which, so the issue is not read as either done or untouched.Fixed — the alignment half.
pkg/result.TableFormatterrenders throughtext/tabwriter, which measures cell widths in runes. The hand-rolled%-30sthis issue describes counts bytes, so every row containing a multi-byte character misaligned.Not fixed — the truncation half.
lore'srunSearchstill slices descriptions by byte count, which is what cuts a multi-byte character mid-rune. That is the defect in this issue's title and it is untouched.The remaining work is one task on epic #740: convert
runSearchto the shared formatter. Doing so fixes the truncation as a consequence rather than as a separate patch — the conversion is a bug fix as much as a refactor, which is why the plan sequences it first amonglore's commands.One output convention for every app: result to stdout, documents to t…
writ reconcile: the command is renamed, and its renderings go through…