Skip to content

fix: update titan-grind's dead-symbol script for current roles --json object shape#1880

Closed
carlos-alm wants to merge 2 commits into
fix/issue-1761-parsediff-hunk-scoped-headersfrom
fix/issue-1762-titan-grind-dead-symbol-shape
Closed

fix: update titan-grind's dead-symbol script for current roles --json object shape#1880
carlos-alm wants to merge 2 commits into
fix/issue-1761-parsediff-hunk-scoped-headersfrom
fix/issue-1762-titan-grind-dead-symbol-shape

Conversation

@carlos-alm

Copy link
Copy Markdown
Contributor

Summary

  • Confirmed codegraph roles --role dead --json's { count, summary, symbols } shape is original, intentional design present since the very first commit that introduced the roles command — not a regression from codegraph roles --role dead flags used function parameters and interface members as dead #1723 (which only changed which nodes get classified dead, not the output shape). Traced via git log --follow -p and pickaxe search.
  • Fixed 3 spots in .claude/skills/titan-grind/SKILL.md that assumed a bare array: Step 0.12 (baseline) and Step 4 (delta gate), both updated to read .count/.summary; Step 2c (duplicate scan) updated to guard .symbols. Step 2c was worse than the reported crash — its TypeError was silently swallowed by a try/catch falling back to console.log('[]'), so that "mandatory" scan has always been a silent no-op.
  • No docs/examples/claude-code-skills/titan-grind/ mirror exists to sync (titan-grind is the only titan-* skill without one — filed separately).

Closes #1762

Test plan

  • npm test — full suite green (3521 passed, 0 failed) — no src/ changes, this is a skill-doc-only fix
  • npm run lint — clean
  • Extracted and ran each corrected one-liner standalone against real CLI output: old scripts reproduced the exact reported crash + the silent Step 2c failure; new scripts produce correct output

Filed #1879 for an out-of-scope finding: titan-grind is the only titan-* skill with no docs mirror and no README table entry.

Stacked on #1878 (base branch fix/issue-1761-parsediff-hunk-scoped-headers) — only the SKILL.md diff is this issue's change.

… object shape

codegraph roles --role dead --json has always returned { count, summary,
symbols } (never a bare array, confirmed back to the command's original
commit) but three Node one-liners in titan-grind's SKILL.md assumed a bare
array, calling .length/.reduce()/.filter() directly on the parsed JSON.
Steps 0.12 and 4 threw "items.reduce is not a function"; the Step 2c
symbol-level duplicate scan silently swallowed the same TypeError in a
try/catch and always emitted an empty candidate list.

Read .count and .summary directly (summary is already the per-role
breakdown) and read .symbols for the duplicate-scan candidate list.

Fixes #1762
@greptile-apps

greptile-apps Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes three places in .claude/skills/titan-grind/SKILL.md that incorrectly assumed codegraph roles --role dead --json returns a bare array, when it has always returned { count, summary, symbols }. It also addresses the follow-up thread concern by adding try/catch with process.exit(1) to the Step 0.12 and Step 4 one-liners.

  • Step 0.12 (baseline) and Step 4 (delta gate): switched from items.length/items.reduce(...) to data.count ?? 0 / data.summary ?? {}, wrapped in try/catch that exits non-zero on parse failure.
  • Step 2c (duplicate scan): changed items.filter(...) to (data.symbols || []).filter(...), fixing a TypeError that was previously silently swallowed by the existing catch { console.log('[]') }, causing the scan to always silently no-op.

Confidence Score: 5/5

Documentation-only fix to a skill file; no source code touched, and the corrected one-liners directly map to the actual CLI output shape confirmed via git archaeology.

All three broken spots are correctly updated to match the { count, summary, symbols } object shape. The follow-up thread concern (missing error guards on Steps 0.12 and 4) was addressed in this revision with try/catch + process.exit(1). No source code is changed; test suite passes unaffected.

No files require special attention.

Important Files Changed

Filename Overview
.claude/skills/titan-grind/SKILL.md Three one-liners updated to correctly read .count/.summary/.symbols from the { count, summary, symbols } object shape; Step 0.12 and Step 4 gain try/catch with process.exit(1); Step 2c fixes a silent TypeError. No new logic issues found.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["codegraph roles --role dead -T --json"] --> B["Node.js stdin collector"]
    B --> C{"JSON.parse"}
    C -- "parse error" --> D["console.error + process.exit(1)"]
    C -- "success" --> E["data = count + summary + symbols object"]
    E --> F1["Step 0.12 Baseline\ntotal = data.count ?? 0\nbyRole = data.summary ?? {}"]
    E --> F2["Step 2c Duplicate Scan\n(data.symbols || []).filter(tokens)"]
    E --> F3["Step 4 Delta Gate\ntotal = data.count ?? 0\nbyRole = data.summary ?? {}"]
    F1 --> G1["Store grind.deadSymbolBaseline"]
    F2 --> G2["Return candidate list"]
    F3 --> G3["Store grind.deadSymbolCurrent"]
    G1 --> DELTA["delta = current.total - baseline.total"]
    G3 --> DELTA
    DELTA --> R1{"delta <= 0"}
    R1 -- yes --> PASS["PASS"]
    R1 -- no --> R2{"delta <= 10"}
    R2 -- yes --> WARN["WARN"]
    R2 -- no --> FAIL["FAIL"]
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    A["codegraph roles --role dead -T --json"] --> B["Node.js stdin collector"]
    B --> C{"JSON.parse"}
    C -- "parse error" --> D["console.error + process.exit(1)"]
    C -- "success" --> E["data = count + summary + symbols object"]
    E --> F1["Step 0.12 Baseline\ntotal = data.count ?? 0\nbyRole = data.summary ?? {}"]
    E --> F2["Step 2c Duplicate Scan\n(data.symbols || []).filter(tokens)"]
    E --> F3["Step 4 Delta Gate\ntotal = data.count ?? 0\nbyRole = data.summary ?? {}"]
    F1 --> G1["Store grind.deadSymbolBaseline"]
    F2 --> G2["Return candidate list"]
    F3 --> G3["Store grind.deadSymbolCurrent"]
    G1 --> DELTA["delta = current.total - baseline.total"]
    G3 --> DELTA
    DELTA --> R1{"delta <= 0"}
    R1 -- yes --> PASS["PASS"]
    R1 -- no --> R2{"delta <= 10"}
    R2 -- yes --> WARN["WARN"]
    R2 -- no --> FAIL["FAIL"]
Loading

Reviews (3): Last reviewed commit: "fix: guard against parse failures in Ste..." | Re-trigger Greptile

Comment thread .claude/skills/titan-grind/SKILL.md Outdated
12. **Capture dead-symbol baseline** (only if `grind.deadSymbolBaseline` is null):
```bash
codegraph roles --role dead -T --json | node -e "const d=[];process.stdin.on('data',c=>d.push(c));process.stdin.on('end',()=>{const items=JSON.parse(Buffer.concat(d));console.log(JSON.stringify({total:items.length,byRole:items.reduce((a,i)=>{a[i.role]=(a[i.role]||0)+1;return a},{})}));})"
codegraph roles --role dead -T --json | node -e "const d=[];process.stdin.on('data',c=>d.push(c));process.stdin.on('end',()=>{const data=JSON.parse(Buffer.concat(d));console.log(JSON.stringify({total:data.count,byRole:data.summary}));})"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Missing error guard in Step 0.12 and Step 4 one-liners

Step 2c now has a try/catch that keeps the script from crashing on a parse error, but the Step 0.12 baseline and Step 4 delta pipelines have none. If data.count or data.summary is absent — e.g. a future CLI shape change, a non-zero exit code polluting stdout, or an empty response — JSON.stringify silently emits {"byRole":null} (since undefined is dropped), storing null as the baseline. The delta gate then computes null - null which is 0, always triggering PASS and masking any regression. Adding a try/catch with process.exit(1) and null-coalescing defaults (?? 0, ?? {}) would give the same defensive coverage Step 2c already has.

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — Step 0.12 and Step 4 one-liners now wrap the parse in try/catch with process.exit(1) on failure, plus ?? 0/?? {} defaults, matching Step 2c's existing defensive pattern.

@carlos-alm

Copy link
Copy Markdown
Contributor Author

@greptileai

@carlos-alm carlos-alm deleted the branch fix/issue-1761-parsediff-hunk-scoped-headers July 7, 2026 01:53
@carlos-alm carlos-alm closed this Jul 7, 2026
@github-actions github-actions Bot locked and limited conversation to collaborators Jul 7, 2026
@carlos-alm

Copy link
Copy Markdown
Contributor Author

Recreated as #1942 after this PR was closed by the squash+delete-branch merge of its stacked base #1940.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant