Skip to content

GenerateTelosSummary: per-section fail-loud guard resolves keys through the same lookup it guards, so heading drift stays silent #1517

Description

@christauff

Summary

The per-section fail-loud guard in GenerateTelosSummary.ts (L405–L422) resolves section keys through the same lookup as the code it guards. When a TELOS.md heading drifts from LEGACY_FILE_TO_SECTION, both miss it — and the guard reads "the source lookup returned nothing" as "the source has no content," so it stays silent and the section renders empty.

The guard is doing exactly the right thing conceptually; its comment names the failure precisely ("the total-items guard in main() can't see a partial drop"). It just can't observe the one condition it exists to detect.

The trace

loadTelosSections() keys sections by lowercased heading (L74):

sections[title.toLowerCase()] = body.join('\n').trim();

coreChecks looks up the legacy singular key (L410):

['mission', 'Missions', missions.length],

With a TELOS.md that heads the section ## MISSIONS:

keys present:                       ["missions"]
sections['mission']              -> undefined
(sections['mission'] ?? '')      -> ''
condition (…trim().length > 0)   -> false     // guard does not fire

readTelosFile() misses identically at L98 (return sections[sectionKey] ?? '';), so the section parses to zero items, and main()'s itemLines === 0 check passes because the other sections parsed fine. Exit 0, summary written, one section silently empty.

Why it's worth a look

PRINCIPAL_TELOS.md is @-imported at every session start, so a silently-empty section is absent from the model's boot context until someone opens the file and reads it. On my install a plural heading dropped the Missions section for ~2 weeks before I noticed; the artifact was 1,646 bytes and looked entirely plausible.

Notably it also passed a migration gate I'd written that checked the @-import chain was "non-empty" — 1,646 > 0. Which is really the same mistake as this one, and the reason I think the guard is worth fixing rather than the parser: an existence check can't see a partial drop either.

On whose fault this is

Mine, mostly — the shipped template uses ## Mission (singular) and maps correctly. My TELOS.md says ## MISSIONS, and also combines ## NARRATIVES / PROBLEMS / MODELS under one heading. That's divergence I introduced.

I'd still gently suggest the guard is the right place to catch it: TELOS.md is a hand-authored life document, and the whole point is that people make it theirs. Heading drift from the template seems like the expected steady state rather than an edge case — which is presumably why the guard was written. It just needs to be able to see the drift.

Possible fix

The narrowest version is to have the guard notice source content that no core section claimed, rather than asking about keys it already believes in:

// after coreChecks
const claimed = new Set(coreChecks.map(([key]) => key));
for (const [heading, body] of Object.entries(sections)) {
  if (!body.trim()) continue;
  const singular = heading.endsWith('s') ? heading.slice(0, -1) : heading;
  if (claimed.has(heading) || claimed.has(singular)) continue;
  console.error(`⚠️  TELOS heading "${heading}" has content but matches no known section — it will be dropped from the summary.`);
}

That's orphan detection rather than key lookup, so it also surfaces headings the map was never going to know about (mine has a ## FOUNDING INTENT that's silently dropped today — entirely my own doing, but I'd rather have been told).

A plural/singular fallback in readTelosFile() would fix my specific case, but wouldn't help the next person whose headings drift a different way, so the orphan check seems like better value for the same handful of lines.

Happy to open a PR if useful, or to leave it — you may have a cleaner direction in mind for that file, and it's a small enough thing that I don't want to spend your review time on it if it isn't where the project needs attention.

Environment

Linux, main, verified against the contents API today (L98, L405–422, L514–517 as quoted). Came through the PAI → LifeOS 6 upgrade with an existing corpus, which is a reliable way to find this genre of thing.

Thanks as always — and genuinely, the per-section guard being there at all is why this was a 20-minute diagnosis instead of a long one. The intent was already right.

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