term_msg: carry the source event's time on every row
A terminal row published on `$SWARM.term.<hive>.<agent>` goes out bare, with no envelope around it and no server-side stamp, so a subscriber had nothing to place the row in time with beyond its own receipt clock — wrong by the queue's latency and meaningless for anything read later than live. `TermMsg` gains `ts`, ISO 8601 UTC. `classify` takes the event's own unix-seconds stamp and applies it to every row that event expands into, so a row replayed out of sqlite says when it happened rather than when it was read, and a row that sat in a lagging subscriber's buffer does not lie about its time. The oversize degrade keeps it; only the body is ever spent. `TermEnvelope` stops duplicating `ts` and keeps `seq`: the dedup counter is a real transport concern, the event's time is not, now that it rides on the row. Nothing in the frontend read `envelope.ts` — only the type declared it. Refs #4321
This commit is contained in:
parent
349f7096ba
commit
053340128b
5 changed files with 206 additions and 38 deletions
|
|
@ -6,6 +6,10 @@
|
|||
export type Level = "debug" | "info" | "warn" | "error";
|
||||
|
||||
export interface TermMsg {
|
||||
/** When the event this row came from happened — ISO 8601 UTC, e.g.
|
||||
* "2026-09-13T12:35:03Z". The source event's own time, so a row replayed
|
||||
* from history reads as when it happened rather than when it was fetched. */
|
||||
ts: string;
|
||||
icon?: string;
|
||||
level: Level;
|
||||
summary: string;
|
||||
|
|
@ -16,9 +20,10 @@ export interface TermMsg {
|
|||
|
||||
/** One SSE frame / history array entry — `hive-agent`'s `TermEnvelope`.
|
||||
* `seq` is the live per-event dedup counter (`BusEvent::seq`); absent on
|
||||
* history-replayed envelopes, see useLiveStream.ts's backfill dance. */
|
||||
* history-replayed envelopes, see useLiveStream.ts's backfill dance. It is
|
||||
* the only field left: the event's time rides on each row's own `ts`, where
|
||||
* a consumer reading a bare row (the swarm queue's) can also see it. */
|
||||
export interface TermEnvelope {
|
||||
ts: number;
|
||||
seq?: number;
|
||||
msgs: TermMsg[];
|
||||
}
|
||||
|
|
@ -27,7 +32,11 @@ export interface TermEnvelope {
|
|||
* stable identity for coalescing/keys, and whether it came from history
|
||||
* replay vs. the live tail. Not a separate model: everything content-wise
|
||||
* is still exactly the wire shape. */
|
||||
export interface TermRow extends TermMsg {
|
||||
export interface TermRow extends Omit<TermMsg, "ts"> {
|
||||
/** Absent on the rows the client invents for itself — the history/live
|
||||
* separators, the connection note, a locally echoed prompt — which answer
|
||||
* to no server event and so have no event time to carry. */
|
||||
ts?: string;
|
||||
key: string;
|
||||
fromHistory: boolean;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue