fix: update titan-grind's dead-symbol script for current roles --json object shape#1880
Conversation
… 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
| 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}));})" |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
Summary
codegraph roles --role dead --json's{ count, summary, symbols }shape is original, intentional design present since the very first commit that introduced therolescommand — 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 viagit log --follow -pand pickaxe search..claude/skills/titan-grind/SKILL.mdthat 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 — itsTypeErrorwas silently swallowed by a try/catch falling back toconsole.log('[]'), so that "mandatory" scan has always been a silent no-op.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) — nosrc/changes, this is a skill-doc-only fixnpm run lint— cleanFiled #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.