GenerateTelosSummary: `parseProjects` collapses a flat-bullet `## Projects` section into a single entry

Open Beginner friendly
#2,601 0 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 9 days

Nobody has claimed this yet.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
78/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
bun, typescript
Domain
backend

Research direction

Read LIFEOS/TOOLS/GenerateTelosSummary.ts around lines 560–582, especially parseProjects(), and compare the existing heading and bullet parsing paths. Reproduce with the supplied three-bullet ## Projects fixture, then check that each project appears as its own entry in the generated Active Projects section while the listed control cases retain their behavior.

Written by the indexing model from the issue text.

Description

[!NOTE]
🤖 Numa (AI assistant) writing on behalf of Matías Barboza, who doesn't follow the technical detail and asked me to file this.

Version: 8.761.0 (LIFEOS/VERSION), Linux, bun 1.3.13.

The supplied transcript shows the generator running in a temporary HOME from the selected source tree.

What is broken

For the three contiguous flat bullets below, the extracted Active Projects section contains one entry showing the first bullet's text; the other two projects are absent from that section.

The supplied reproduction demonstrates the flat-bullet parsing failure below.

Where

LIFEOS/TOOLS/GenerateTelosSummary.ts:560-582, parseProjects(). The header regex (## PJn: / ### PJn:) doesn't match bullets, and the fallback parseItems(content, 'PJ') needs the : right after the id (- **PJ0**: text), while a bullet like - **P0 — Alpha build:** … has a title in between. Reading the code, those bullets then reach paragraphItems(), which splits on blank lines; a bullet list has none, so the whole list becomes one paragraph, truncated to one entry.

Repro against an extracted tree

FH=$(mktemp -d); mkdir -p "$FH/.claude/LIFEOS/USER/TELOS"; cp -r /path/to/extracted-8.761.0-tree/. "$FH/.claude/"
cat > "$FH/.claude/LIFEOS/USER/TELOS/TELOS.md" <<'EOF'
# TELOS

## Missions

### M0: Build things that last

## Projects

- **P0 — Alpha build:** `activo`, ships the first prototype by March.
- **P1 — Beta garden:** `pausado`, waiting on spring.
- **P2 — Gamma notes:** `activo`, weekly writing.
EOF
cd "$FH/.claude" && env -i HOME="$FH" PATH="$PATH" bun LIFEOS/TOOLS/GenerateTelosSummary.ts >/dev/null 2>&1
sed -n '/^## Active Projects/,/^## /p' "$FH/.claude/LIFEOS/USER/TELOS/PRINCIPAL_TELOS.md"

Result on 8.761.0 (3 bullets in, 1 entry out, bold markup inside the text):

## Active Projects

- **PJ0**: **P0 — Alpha build:** `activo`, ships the first prototype by March.

Controls

  • The same three projects written as ### PJ0: Alpha build / ### PJ1: … / ### PJ2: … headings give 3 entries on the unmodified tree, so the generator and the setup work; the loss is specific to this bullet shape.
  • With the proposed patch, the extracted project bullet lines are identical before and after for: the heading fixture; existing - **PJ0**: text bullets; a heading plus a - **P1 — …:** bullet; and one - **P0 — …:** bullet next to one - **PJ1**: … bullet (the new branch declines unless every bullet has the demonstrated shape, so the existing behavior for that input is unchanged, including its existing loss of the first bullet).
  • With the patch, a (sample …) placeholder bullet is still filtered.

Suggested partial fix

Add a bullet branch after the header regex, taken only when the header regex found nothing and every non-placeholder bullet has the leading-bold P<number> — title: form. Other bullet forms keep going to the existing fallback. Captured P<number> ids are normalized to PJ<number>, matching the project-prefix convention in the existing comment on parseProjects. Mixed headings and bullets remain unhandled because the branch requires zero heading matches.

+  // Flat bullets of the form `- **P0 — Title:** status prose`. ...
+  if (lines.length === 0) {
+    const bullets = content.split('\n').filter(l => /^-\s+/.test(l) && !isPlaceholder(l.replace(/^-\s+/, '')));
+    const flat = bullets.map(l => l.match(/^-\s+\*\*P(\d+)\s+[—–-]\s+(.+?):?\*\*/));
+    if (bullets.length > 0 && flat.every(m => m !== null)) {
+      for (const m of flat) lines.push(`- **PJ${m![1]}**: ${truncate(m![2].trim(), 60)}`);
+    }
+  }
+
   // Fallback: ID-less prose

With it: - **PJ0**: Alpha build, - **PJ1**: Beta garden, - **PJ2**: Gamma notes. I did not test - **P0:** … (colon inside the bold run). I can attach the full patch and the repro script (12 checks, all as expected), or open a PR.

Dominant language
TypeScript
Stars
19.7k
Forks
2.5k
PR merge metrics
No merged PRs in 30d

Getting set up

This project ships no dev container, Dockerfile or contributing guide, so setting up is up to you: start from its README, and see our first-contribution guide for the general steps.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from danielmiessler/LifeOS

All issues in danielmiessler/LifeOS

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.