Skip to content

diary entry carries no interaction_count/avg_rating/mood - dashboard shows 0 / neutral / 0 #1693

Description

@schmetti-dev

Symptom

The Pulse assistant dashboard /assistant/diary endpoint renders every day as 0 interactions / mood: neutral / avg_rating: 0, regardless of how much actually happened that day. The "0 sessions neutral 0/10" reading is constant.

Root cause

LIFEOS/PULSE/Assistant/module.ts diaryResponse() (lines 174-186) hardcodes the three experiential fields instead of computing them:

function diaryResponse(): { entries: any[] } {
  const raw = readJsonl(DIARY_PATH)
  const entries = raw.slice(-30).map((e) => ({
    date: e.date ?? e.ts?.slice(0, 10) ?? "",
    interaction_count: 0,   // hardcoded
    topics: Array.isArray(e.topTasks) ? e.topTasks.map((t: string) => t) : [],
    mood: "neutral",         // hardcoded
    avg_rating: 0,           // hardcoded
    notable_moments: e.summary ? [e.summary] : [],
    learning: null as string | null,
  }))
  return { entries }
}

And LIFEOS/PULSE/Assistant/checks/diary.ts (the 0 23 * * * cron writer) only writes task-state fields - openTasks, highPriorityTasks, topTasks, summary - never the experiential ones. So even if diaryResponse() read them from the entry, they would not be there.

The experiential data does exist in two observability jsonl files, it just never flows into the diary:

  • LIFEOS/MEMORY/OBSERVABILITY/prompt-processing.jsonl - one line per processed prompt: { timestamp, session_id, prompt_excerpt, tab_title, source, latency_ms }. Count of today's lines by date = interaction_count.
  • LIFEOS/MEMORY/LEARNING/SIGNALS/ratings.jsonl - one line per session rating: { timestamp, rating (1-10), session_id, source, sentiment_summary, confidence, response_preview, comment? }. Mean of today's rating = avg_rating; mood derived from the rating band.

Context (why now)

LifeOS v7 (2026-07-17) removed implicit-sentiment inference from SatisfactionCapture.hook.ts. The ratings pipeline was restored 2026-07-29 via SessionEndSentiment.hook.ts (explicit + implicit ratings flow into ratings.jsonl again). So the source data is live again - the diary just never consumes it. This issue is the consumer side of that restore.

Fix (two parts)

A) checks/diary.ts - compute + store at write time (23:00 cron)

Extend the diary entry written to LIFEOS/USER/DA/diary.jsonl with three fields computed from the two sources, scoped to the same date:

  • interaction_count: count of prompt-processing.jsonl lines whose timestamp date (Europe/Berlin or ISO date slice - match whatever todayISO() yields) equals today.
  • avg_rating: mean of ratings.jsonl rating values whose timestamp date equals today (0 if none).
  • mood: derive from avg_rating band (e.g. <=4 low, 5-6 neutral, >=7 good - pick a sensible 3-band mapping and document it inline).

Keep the cron deterministic - no LLM call (the file header explicitly promises this; do not break it). Read both jsonl files with the same readJsonl helper the module already uses, or a local equivalent. Entry shape after fix:

const entry = {
  date, ts, weekday, openTasks, highPriorityTasks, topTasks, summary,
  interaction_count, avg_rating, mood,
}

B) module.ts diaryResponse() - read from entry, fall back gracefully

Replace the three hardcoded zeros with reads from the stored entry, keeping the current values as fallbacks for old entries written before part A landed:

interaction_count: e.interaction_count ?? 0,
mood: e.mood ?? "neutral",
avg_rating: e.avg_rating ?? 0,

This keeps historical entries rendering (as 0/neutral/0, which is honest - we did not track it then) while new entries show real data.

Verification

  • bun build --target=bun LIFEOS/PULSE/Assistant/checks/diary.ts and ... module.ts clean.
  • Run the diary cron once against a day with known prompt-processing.jsonl + ratings.jsonl entries; confirm the new diary entry in diary.jsonl carries non-zero interaction_count and a real avg_rating/mood.
  • curl -s localhost:31337/assistant/diary | jq shows today's entry with the real values, not 0/neutral/0.
  • Co-located test next to checks/diary.ts (LifeOS test convention: tests co-located, no test/ tree, bun:test self-contained harness) covering: empty-sources day → 0/neutral/0; a day with 3 prompts + ratings {7,8,9} → interaction_count 3, avg_rating 8, mood good.

Notes

  • The readJsonl helper and DIARY_PATH pattern already exist in module.ts - reuse, do not duplicate.
  • Date matching: prompt-processing.jsonl timestamps are ISO-Z (2026-07-29T08:13:35.852Z), ratings.jsonl are +02:00 local. Pick one normalization (slice first 10 chars works for both) and state it in a comment.
  • No PII in the diary entry - counts and a mean rating only. response_preview/prompt_excerpt/comment stay out of diary.jsonl.

created by Klaus (LifeOs 7.1.1)

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