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)
Symptom
The Pulse assistant dashboard
/assistant/diaryendpoint 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.tsdiaryResponse()(lines 174-186) hardcodes the three experiential fields instead of computing them:And
LIFEOS/PULSE/Assistant/checks/diary.ts(the0 23 * * *cron writer) only writes task-state fields -openTasks,highPriorityTasks,topTasks,summary- never the experiential ones. So even ifdiaryResponse()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'srating=avg_rating;moodderived 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 viaSessionEndSentiment.hook.ts(explicit + implicit ratings flow intoratings.jsonlagain). 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.jsonlwith three fields computed from the two sources, scoped to the samedate:interaction_count: count ofprompt-processing.jsonllines whosetimestampdate (Europe/Berlin or ISO date slice - match whatevertodayISO()yields) equals today.avg_rating: mean ofratings.jsonlratingvalues whosetimestampdate equals today (0 if none).mood: derive fromavg_ratingband (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
readJsonlhelper the module already uses, or a local equivalent. Entry shape after fix:B)
module.tsdiaryResponse()- read from entry, fall back gracefullyReplace the three hardcoded zeros with reads from the stored entry, keeping the current values as fallbacks for old entries written before part A landed:
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.tsand... module.tsclean.prompt-processing.jsonl+ratings.jsonlentries; confirm the new diary entry indiary.jsonlcarries non-zerointeraction_countand a realavg_rating/mood.curl -s localhost:31337/assistant/diary | jqshows today's entry with the real values, not 0/neutral/0.checks/diary.ts(LifeOS test convention: tests co-located, notest/tree,bun:testself-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
readJsonlhelper andDIARY_PATHpattern already exist inmodule.ts- reuse, do not duplicate.prompt-processing.jsonltimestamps are ISO-Z (2026-07-29T08:13:35.852Z),ratings.jsonlare+02:00local. Pick one normalization (slice first 10 chars works for both) and state it in a comment.response_preview/prompt_excerpt/commentstay out ofdiary.jsonl.created by Klaus (LifeOs 7.1.1)