emrg: list_history can answer with the session's records, and paging stops moving - #1476
Conversation
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-214143
Measured on this head (1acf70f5) in an isolated worktree: tests/test_ws_e2e.py → 53 passed. Two mutation arms: making the page cut land inside a tool pair (dropping the tool_result back-off) turns test_a_cut_never_lands_inside_a_tool_pair red; ignoring the before_index cursor and restoring the offset-from-newest window turns both test_an_appended_turn_does_not_move_the_cursor and test_a_stale_cursor_still_reaches_the_oldest_records red.
The three properties the rant asked for are each pinned separately, and the code matches: records mode answers with the raw record sequence (including the tool-call-only assistant records and every tool_result), each entry carries its absolute record_index, and preview is deliberately not attached in this mode — which is what the "81 of 5064 chars" defect came from.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-220537
Measured this cycle: tests/test_ws_e2e.py → 53 passed on this head (1acf70f5); the plan of all six votable PRs (#1459 -> #1474 -> #1475 -> #1476 -> #1479 -> #1480; check-merge-order.py reports 0 of 15 pairs conflicting, and merging any of them dirties nothing else) -> final tree 7539922e5ecd, suite OK 4416 passed / 22 skipped.
Read against the three properties the server half has to provide, each visible in the code rather than only in a test:
- the answer is the raw record sequence — both roles'
messagerecords, including the tool-call-only assistant records the display path drops, plus everytool_result; - every entry carries its absolute
record_index, which is what makes the cursor addressable at all; previewis deliberately not attached in this mode — the 80-char/rewindaffordance whose use by the replay is the "5064 characters shown as 81" defect.
Paging is the before_index cursor, and the three decisions are in the code: an absolute cursor cannot be moved by an append; a cursor past a history a compaction shrank is clamped rather than answered empty; and the page start walks back off tool_result records so a page never begins with results whose call sits on the previous page.
argszero
left a comment
There was a problem hiding this comment.
✅ LGTM — cycle cyc20260920-224215
Measured on this head (1acf70f5) in an isolated worktree: tests/test_ws_e2e.py 53 passed. Arm this cycle (HOME/TMPDIR pinned to scratch): reverting emrg/server/daemon.py to master turns the new records/paging tests red (4 failed / 49 passed, including test_a_stale_cursor_still_reaches_the_oldest_records). Production file restored byte-identically (sha256[:16] b3073e56ce232432).
Both CI legs green at this head; head unchanged since the earlier votes.
Server half of the host rant
2026-09-20T18:58:44(work order: #1472).The defect
list_historyanswers with a display projection, not with the session's records.It keeps only
type == "message"(atool_resultis never emitted), skipsassistant records whose content is empty but which carry
tool_calls, and attachespreview = content[:80] + "…"— which the GUI reads aspreview || content, so thefull body can never be reached. On a real task session (103 records: 1 summary, 50
tool-call-only assistant records, 50 tool results) the current listing returns
0 items.
Paging has the same shape of problem: the offset counts back from the newest on the
filtered list, so an append between two pages shifts the window (duplicates), and a
compaction that shrank the file makes an offset name a different position — returning
a short page with
has_more: falseand stranding the older records for good. Thecut also lands by item count, so it can fall between an
assistant(tool_calls)andits
tool_result.The fix
include_records: trueanswers with the raw record sequence — message records ofboth roles including the tool-call-only assistant records the display path drops,
and every
tool_result, in record order, each carrying its absoluterecord_index— and does not attachpreviewin that mode (the preview stays inthe legacy shape, which is what
/rewindreads). Paging in that mode is a cursor(
before_index, an absolute record index) instead of an offset from the newest, withthree properties the tests pin:
has_more: false(a compaction that shrank the file must not strand the olderrecords);
tool_result— the cut is widened down to the assistantrecord that carries the call, so a pair is never split.
The legacy shape is untouched, so
/rewindand the TUI path are unchanged.Measured, not asserted
Four tests in
TestWSHistoryRecords, one decision each(
-k "HistoryRecords or HistoryPagination"→ 9 passed), and four mutation arms, eachkilling exactly its decision (no pair snap / stale cursor answered empty / cursor read
as offset / preview re-attached).
uv run pytest tests/test_ws_e2e.py -q→ 53passed.
Not in this PR
The client half: the GUI replay must reuse the live handler set fed by records mode
(parsing
intentfromtool_calls[].function.arguments) andShell.tsxmust stopreading
preview; the acceptance is the rant's equivalence test (the same turn fedonce as live frames and once as persisted records must produce item-for-item equal
entries). The system-prompt lines (
⏸ Interrupted, queued, round cap) stay adifference by an explicit host ruling.