Skip to content

emrg: the GUI shows no time on any message, though the daemon sends the moment #2340

Closed

Description

@argszero

Origin: rant 2026-10-17T10:03:08.155490+08:00

No message row in the GUI shows a time: not the user's own line, not the assistant reply. The TUI half of rant 2026-10-09T09:25:00 landed in #1978 — the daemon stamps a moment once and sends it on the frame that carries the message — but the GUI throws that value away on both paths, so a GUI user cannot tell when anything was said.

What is broken, and where

The daemon side is already correct and is not part of this change:

  • done carries timestamp — emrg/server/daemon.py:4623 (and the parked-injection path at :4833).
  • user_message carries timestamp and request_id — daemon.py:4115.
  • steer_committed carries timestamp — daemon.py:3738.
  • The persisted record carries the same string (daemon.py:4113, :4600).

The GUI drops it at three seams:

  1. The frame never arrives. emrg/gui/daemon_client.js has no branch for user_message, so it falls into the unknown-frame catch-all and is logged, not forwarded (test/daemon_client.test.js even lists it as deliberately not forwarded). The user row is drawn optimistically by the Composer, and nothing ever tells it the daemon's moment.
  2. The model has no field for it. lib/transcript.ts — DoneData is {request_id, timeout, content}, UserEntry is {kind, text}, AssistantEntry is {kind, rid, segments}. Even the frames that do arrive cannot be stored.
  3. The view has no element for it. components/TranscriptView.tsx renders no time on a message row; only tool rows show · Ns, which is a duration, not a moment.

Replay drops it a fourth time: lib/historyReplay.ts types timestamp?: string on the record but the sink it feeds (addUserMessage(text, sid), handleDone({request_id})) has nowhere to put it, so a reopened session would show nothing even once the live path is fixed.

What this changes

  • One formatter, formatMessageTime in lib/utils.ts, mirroring the TUI's format_message_time (emrg/client/python_tui/widgets/chat_row.py:34): HH:MM from the record's own clock, "" for an absent or unreadable value — never an empty pair of brackets, never Invalid Date, never NaN. The full stamp is the element's title.
  • The moment travels from the daemon only. DoneData gains timestamp and the store stamps the reply's last assistant entry from it; user_message is forwarded by the daemon client and the bridge stamps the user row with the request_id it names; steer_committed stamps a queued row the same way. The client never stamps its own clock.
  • Replay carries it too, so the live row and the reopened record show the same value (the invariant emrg: each message shows the moment it was written #1978 established for the TUI).
  • The scope is the GUI only (the rant's explicit boundary): emrg/client/** is not touched.

Acceptance

  1. formatMessageTime answers HH:MM for a stored moment and "" for absent/unreadable input, with a test over both directions.
  2. A GUI test reads the rendered output: a user row and an assistant row each carry the time text; a row whose moment is missing renders no time element at all.
  3. Mutation-verified: deleting the timestamp from the done frame, from the user row's stamp, or from the replayed record turns those tests red, and changing the value changes what is rendered.
  4. Live and reopened agree: the same message replayed from its record renders the same HH:MM as the live row.

Activity

  1. argszero commented on Oct 17, 2026

    @argszero
    OwnerAuthor

    Handled by #2341

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions