diff --git a/docs/user/chat.md b/docs/user/chat.md index 6f907f2..30908d4 100644 --- a/docs/user/chat.md +++ b/docs/user/chat.md @@ -308,18 +308,19 @@ stroke buttons under each assistant message. Click it to expand a panel anchored to that turn. The panel shows: - A header pill marking the cohort as **confirmed**, - **disconfirmed**, **waiting**, or **aged out**, depending on what - the reaction classifier did with it on the following turn. -- The **predictions that fired** for that turn, grouped by theme - (paraphrase clusters collapse into a representative with a - "+N similar" chevron). "Show all" bypasses the clustering and + **disconfirmed**, or **pending**, depending on what the reaction + classifier did with it on the following turn. +- The **substrate row** for the same round, lifted to the top of the + panel with an accent stripe because it's the worker's after-the- + fact summary of what actually happened on this turn ("user asked + X about Y, expressing Z" / "the assistant did W and it landed P"). + Includes its lifecycle state (pending assimilation, assimilated + but unembedded, or fully baked) and the round's valence reading. +- Below that, the **predictions that fired** for that turn, grouped + by theme (paraphrase clusters collapse into a representative with + a "+N similar" chevron). "Show all" bypasses the clustering and lists every fire individually. Each entry shows its tier, ranking score, valence, confidence, and health. -- The **substrate row** for the same round - the worker's structured - summary of what happened ("user asked X about Y, expressing Z" - / "the assistant did W and it landed P"), with its lifecycle - state (pending assimilation, assimilated but unembedded, or fully - baked) and the round's valence reading. The icon only appears on messages that produced at least one fire or substrate row, so cold-start messages and any turn the worker diff --git a/src/components/CohortPanel.svelte b/src/components/CohortPanel.svelte index 76b134d..df5d578 100644 --- a/src/components/CohortPanel.svelte +++ b/src/components/CohortPanel.svelte @@ -123,20 +123,18 @@ return `${sign}${v.toFixed(2)}`; } - // Three-state resolution label. Null means the reaction classifier - // hasn't scored this cohort yet - distinguish in-flight from aged- - // out by the 10-minute resolution window the classifier uses. - function resolutionLabel( - confirmed: boolean | null, - firedAtIso: string | null - ): string { + // Three-state resolution. The old four-way label (in-flight / + // window-open / aged-out) earned its keep in the diagnostics + // modal where cohorts were a flat list with no message context; + // inline under the user message that fired them, the transcript + // already encodes "this turn fired N exchanges ago" - if a later + // user turn is visible the classifier had its shot, and if it + // didn't, the cohort is the latest one. "pending" covers every + // unresolved state. + function resolutionLabel(confirmed: boolean | null): string { if (confirmed === true) return 'confirmed'; if (confirmed === false) return 'disconfirmed'; - if (!firedAtIso) return 'pending'; - const ageMs = Date.now() - new Date(firedAtIso).getTime(); - if (ageMs < 60 * 1000) return 'waiting (in-flight)'; - if (ageMs < 10 * 60 * 1000) return 'waiting (resolution window open)'; - return 'aged out (no reaction)'; + return 'pending'; } function resolutionStatusClass(confirmed: boolean | null): string { @@ -164,7 +162,7 @@ - {resolutionLabel(wasConfirmed, firedAt)} + {resolutionLabel(wasConfirmed)} {#if collapsed && !raw} @@ -188,6 +186,45 @@ {/if} + {#if substrate} + +
+
+ substrate + + {assimilationStatus(substrate)} + + {#if substrate.valence !== null} + + valence {formatValence(substrate.valence)} + + {/if} +
+ {#if substrate.situation} +

{substrate.situation}

+ {/if} + {#if substrate.outcome} +

+ Outcome: + {substrate.outcome} +

+ {/if} +
+ {/if} + {#if raw} {/if} - - {#if substrate} - -
-
- substrate - - {assimilationStatus(substrate)} - - {#if substrate.valence !== null} - - valence {formatValence(substrate.valence)} - - {/if} -
- {#if substrate.situation} -

{substrate.situation}

- {/if} - {#if substrate.outcome} -

- Outcome: - {substrate.outcome} -

- {/if} -
- {/if} {#snippet fireRow(fire: SamskaraFireDiagnosticRow)} @@ -434,12 +438,18 @@ font-weight: 700; } + /* Substrate emphasised as the headline read of the turn: left- + edge accent stripe + faintly tinted background lift it visually + above the fire list that follows. Padded inside the stripe so + the text doesn't crowd against the accent. */ .substrate-block { display: flex; flex-direction: column; gap: 0.25rem; - padding-top: 0.5rem; - border-top: 1px dashed var(--border); + padding: 0.45rem 0.6rem 0.5rem; + border-left: 3px solid var(--accent); + border-radius: 0 4px 4px 0; + background: color-mix(in srgb, var(--accent) 8%, transparent); } .substrate-head { display: flex; @@ -451,8 +461,8 @@ font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em; - color: var(--muted); - font-weight: 600; + color: var(--accent); + font-weight: 700; } .substrate-meta { font-size: 0.72rem;