Revision 16 said the scroll formatter could keep its signature and
change only what callers pass. It cannot, and the byte fallback in
revision 17 made that unavoidable rather than merely untidy.
Every branch of format_scroll_indicator derives from total_lines:
total_lines <= 1 -> All
visible >= total_lines -> All
view_top == 0 -> Top
view_top + visible >= total_lines -> Bot
pct = (cursor_row + 1) * 100 / total_lines
Revision 17 removed the total. Passing byte counts makes the Bot branch
compare rows against bytes --- it would return plausible strings that
mean nothing. Passing any stand-in small enough to satisfy the guards
restores the false All this section exists to remove. And "local
predicates" is not something that signature can evaluate, because it
has no parameter for them.
Resolved by not asking it to. truncate calls the existing formatter
UNTOUCHED, with the same arguments in the same units, so its output is
byte-identical by construction rather than by assertion, and every
existing formatter test stays valid --- including the GPU's
format_scroll_indicator(0, 10, 1, 0) == "All", which correctly pins
line-space behavior. wrap calls a new classifier:
classify(first_visible: bool, last_visible: bool,
byte_pos: u64, byte_len: u64) -> ScrollPosition
All = first && last; Top = first && !last; Bot = last && !first;
otherwise Percent from bytes. No count of rows enters it, so the unit
mixing is not avoided but unrepresentable.
The actual mistake was trying to serve two genuinely different
contracts from one four-count signature. It could only do that by
making units implicit, which is how the contradiction arose. This is
5b.5's identity-case strategy applied to the indicator itself: the old
mode keeps the old code, and the new mode gets code shaped to it.
Leaves one question open. pmacs-gpu depends on pmacs-protocol only,
never on the pmacs lib, so format_scroll_indicator is duplicated
STRUCTURALLY and the classifier faces the same fork. Duplicating it
preserves exactly the condition that produced the earlier defect, where
one copy was fixed and the other was not. Sharing it through
pmacs-protocol makes the agreement structural rather than maintained,
which is the principle that chose byte-anchoring, additive sub_row, and
a content-derived cache key --- but it widens that crate from wire
vocabulary toward presentation, which is a COHERENCE section 16
layering question and not this lane's to settle alone.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016bqGA6s9tTUFzYpbeW3tai