Summary
GET /api/life/home (Pulse Observability) reads four per-topic TELOS files directly, with no fallback to the unified TELOS.md. Three of those four files are not shipped by the installer, and the fourth (GOALS.md) is the legacy form that TELOS.md superseded on 2026-05-01. The result is a Life Home that renders entirely empty on a stock install, with no error anywhere.
Two sibling call sites in the same repo already implement the fallback this endpoint is missing, so this is an inconsistency inside the codebase rather than a missing feature.
Reproduction
On an install where TELOS lives in the unified USER/TELOS/TELOS.md (the documented single source of truth):
curl -s http://localhost:31337/api/life/home
Observed:
{"oneSentence":"Unknown, Unknown energy. Focused on: Unknown.",
"current":{},"topGoals":[],"nextActions":[],
"spark":null,"sparkCount":0,"timelineBlockCount":0,"topIntent":null}
Root cause
LIFEOS/PULSE/Observability/observability.ts, handleLifeHome() (around line 1666):
const current = readMd(join(TELOS_DIR, "CURRENT.md"))
const goalsRaw = readMd(join(TELOS_DIR, "GOALS.md"))
const sparksRaw = readMd(join(TELOS_DIR, "SPARKS.md"))
const timelineRaw = readMd(join(TELOS_DIR, "2036.md"))
Checked against the files the installer actually creates in LifeOS/install/USER/TELOS/:
| File read |
Shipped by the installer? |
Notes |
CURRENT.md |
no |
no template ships it |
GOALS.md |
yes |
legacy per-topic file, superseded by TELOS.md H2 sections |
SPARKS.md |
no |
no template ships it |
2036.md |
no |
no template ships it |
So on a fresh install three of the four reads are guaranteed empty, and the fourth goes empty as soon as the user follows the documented unified-TELOS.md layout. readMd returns empty rather than throwing, so every downstream parse yields []/Unknown and the endpoint reports success.
The same pattern appears again at handleLifeDashboard() (around lines 2188 and 2242), which reads CURRENT.md and 2036.md the same way.
Why this is inconsistent with the rest of the repo
Two other call sites already treat the per-topic files as legacy and fall back to the unified document:
skills/Daemon/Tools/DaemonAggregator.ts:100-132 — readTelosSection(), with the comment: "TELOS.md is the single source of truth; the legacy per-topic files (MISSION.md, GOALS.md, WISDOM.md) no longer exist on TELOS.md-only setups, so the daemon read them as empty."
LIFEOS/PULSE/checks/life-morning-brief.ts:24-31 — legacy GOALS.md first, then TELOS.md's ## GOALS section.
handleLifeHome() never received the same treatment.
Proposed fix
Reuse the established pattern: try the legacy per-topic file, then fall back to the matching H2 section of TELOS.md.
// Legacy per-topic files were superseded by the unified TELOS.md (H2 sections) on
// 2026-05-01, and CURRENT.md / SPARKS.md / 2036.md are not shipped by the installer
// at all. Falling back to the matching section keeps Life Home populated on a stock
// install instead of silently rendering "Unknown".
function readTelosSection(heading: string): string {
const telos = readMd(join(TELOS_DIR, "TELOS.md"))
if (!telos) return ""
const re = new RegExp(`^##\\s+${heading}\\s*$([\\s\\S]*?)(?=^##\\s|\\Z)`, "im")
return telos.match(re)?.[1]?.trim() ?? ""
}
const current = readMd(join(TELOS_DIR, "CURRENT.md")) || readTelosSection("Current State")
const goalsRaw = readMd(join(TELOS_DIR, "GOALS.md")) || readTelosSection("GOALS")
const sparksRaw = readMd(join(TELOS_DIR, "SPARKS.md")) || readTelosSection("Sparks")
const timelineRaw = readMd(join(TELOS_DIR, "2036.md")) || readTelosSection("Timeline")
Applying the same fallback at handleLifeDashboard() (lines ~2188, ~2242) would close the second occurrence.
Two smaller points worth considering alongside it:
- Fail loud rather than silently empty. An endpoint that finds no TELOS source at all currently returns
200 with "Unknown". Surfacing a sources: [] / degraded flag in the payload would make the empty state visible to the dashboard rather than indistinguishable from a user who genuinely has no goals.
- Spark parsing expects
### headings. A SPARKS.md written with ## headings yields sparkCount: 0 even when the file exists, so a heading-level-tolerant parse would help here too.
Happy to open a PR with the fallback if the approach looks right.
Summary
GET /api/life/home(Pulse Observability) reads four per-topic TELOS files directly, with no fallback to the unifiedTELOS.md. Three of those four files are not shipped by the installer, and the fourth (GOALS.md) is the legacy form thatTELOS.mdsuperseded on 2026-05-01. The result is a Life Home that renders entirely empty on a stock install, with no error anywhere.Two sibling call sites in the same repo already implement the fallback this endpoint is missing, so this is an inconsistency inside the codebase rather than a missing feature.
Reproduction
On an install where TELOS lives in the unified
USER/TELOS/TELOS.md(the documented single source of truth):Observed:
{"oneSentence":"Unknown, Unknown energy. Focused on: Unknown.", "current":{},"topGoals":[],"nextActions":[], "spark":null,"sparkCount":0,"timelineBlockCount":0,"topIntent":null}Root cause
LIFEOS/PULSE/Observability/observability.ts,handleLifeHome()(around line 1666):Checked against the files the installer actually creates in
LifeOS/install/USER/TELOS/:CURRENT.mdGOALS.mdTELOS.mdH2 sectionsSPARKS.md2036.mdSo on a fresh install three of the four reads are guaranteed empty, and the fourth goes empty as soon as the user follows the documented unified-
TELOS.mdlayout.readMdreturns empty rather than throwing, so every downstream parse yields[]/Unknownand the endpoint reports success.The same pattern appears again at
handleLifeDashboard()(around lines 2188 and 2242), which readsCURRENT.mdand2036.mdthe same way.Why this is inconsistent with the rest of the repo
Two other call sites already treat the per-topic files as legacy and fall back to the unified document:
skills/Daemon/Tools/DaemonAggregator.ts:100-132—readTelosSection(), with the comment: "TELOS.md is the single source of truth; the legacy per-topic files (MISSION.md, GOALS.md, WISDOM.md) no longer exist on TELOS.md-only setups, so the daemon read them as empty."LIFEOS/PULSE/checks/life-morning-brief.ts:24-31— legacyGOALS.mdfirst, thenTELOS.md's## GOALSsection.handleLifeHome()never received the same treatment.Proposed fix
Reuse the established pattern: try the legacy per-topic file, then fall back to the matching H2 section of
TELOS.md.Applying the same fallback at
handleLifeDashboard()(lines ~2188, ~2242) would close the second occurrence.Two smaller points worth considering alongside it:
200with"Unknown". Surfacing asources: []/ degraded flag in the payload would make the empty state visible to the dashboard rather than indistinguishable from a user who genuinely has no goals.###headings. ASPARKS.mdwritten with##headings yieldssparkCount: 0even when the file exists, so a heading-level-tolerant parse would help here too.Happy to open a PR with the fallback if the approach looks right.