Problem
A project with a goal that exceeds the detail panel width breaks the entire TUI layout — the right border, sidebar and DRIFT header all get pushed/clipped (see screenshot). The damage is global because lipgloss.JoinHorizontal/JoinVertical desync once one cell renders wider than its declared Width().
Reproduction
Any project whose .drift/project.json has a goal text longer than the detail panel width (~80 chars on a typical terminal). Open it in the TUI → instant breakage.
The issue is in internal/ui/view.go:432-446 — goal text is rendered as-is. Every other section in the same renderDetail truncates:
- description →
Truncate(*p.Description, w-6) (line 365)
- tags →
Truncate(...) (376)
- path →
Truncate(...) (382)
- notes →
Truncate(n.Text, w-16) (477)
- goals → no truncation ❌
Possible fixes (open question — UX call)
- Single-line truncate, symmetric with notes:
Truncate(g.Text, w-6). Minimal diff, but long goals lose their tail.
- Soft-wrap inside panel width via
lipgloss.NewStyle().Width(w-6).Render(...), optionally capped at N lines per goal so one verbose goal doesn't eat the whole panel. Preserves content but needs the height accounting in the notes section to count wrapped lines (currently len(lines)).
Happy to send a PR once direction is picked.
PS — notes schema drift
On the same screenshot the notes section looks empty even though the project has 3 notes. Cause: the JSON uses {"at": ..., "note": ...} while protocol.Note expects {"ts": ..., "text": ...}, so they deserialize to empty strings. The LLM that wrote those notes followed the ## drift section drift injects into CLAUDE.md (internal/protocol/io.go:414-426) — which tells the agent to "add a note" but doesn't specify the field names, so a more natural at/note shape gets invented. Worth documenting the exact schema in that CLAUDE.md snippet (or accepting aliases on read).

Problem
A project with a goal that exceeds the detail panel width breaks the entire TUI layout — the right border, sidebar and
DRIFTheader all get pushed/clipped (see screenshot). The damage is global becauselipgloss.JoinHorizontal/JoinVerticaldesync once one cell renders wider than its declaredWidth().Reproduction
Any project whose
.drift/project.jsonhas a goaltextlonger than the detail panel width (~80 chars on a typical terminal). Open it in the TUI → instant breakage.The issue is in
internal/ui/view.go:432-446— goal text is rendered as-is. Every other section in the samerenderDetailtruncates:Truncate(*p.Description, w-6)(line 365)Truncate(...)(376)Truncate(...)(382)Truncate(n.Text, w-16)(477)Possible fixes (open question — UX call)
Truncate(g.Text, w-6). Minimal diff, but long goals lose their tail.lipgloss.NewStyle().Width(w-6).Render(...), optionally capped at N lines per goal so one verbose goal doesn't eat the whole panel. Preserves content but needs the height accounting in thenotessection to count wrapped lines (currentlylen(lines)).Happy to send a PR once direction is picked.
PS — notes schema drift
On the same screenshot the
notessection looks empty even though the project has 3 notes. Cause: the JSON uses{"at": ..., "note": ...}whileprotocol.Noteexpects{"ts": ..., "text": ...}, so they deserialize to empty strings. The LLM that wrote those notes followed the## driftsection drift injects intoCLAUDE.md(internal/protocol/io.go:414-426) — which tells the agent to "add a note" but doesn't specify the field names, so a more naturalat/noteshape gets invented. Worth documenting the exact schema in that CLAUDE.md snippet (or accepting aliases on read).