feat(skill): comprehensive grind rewrite and cross-skill integration#896
feat(skill): comprehensive grind rewrite and cross-skill integration#896carlos-alm merged 10 commits intomainfrom
Conversation
Wire up extracted helpers from Titan runs that existed but were never consumed, reducing boilerplate and improving error specificity. - Adopt named_child_text across 27 sites in 11 Rust extractors - Migrate cpp.rs from hand-rolled find_cpp_parent_class to find_enclosing_type_name - Add toSymbolRef helper in shared/normalize.ts, adopt at 15 mapping sites - Wire ParseError in parser.ts for structured PARSE_FAILED error codes - Wire BoundaryError in boundaries.ts to distinguish config/DB failures from clean results - Add --modules/--threshold flags to codegraph structure command - Wire batchQuery in CLI batch command, removing duplicated routing logic - Route detect-changes pending analysis through unified runAnalyses engine
- manifesto.ts: report 'warn' instead of 'pass' when boundary check throws - structure.ts: validate --threshold flag rejects non-numeric input - dependencies.ts: clarify intentional skip of toSymbolRef for callers
Forge extracts helpers but never completes the adoption loop — dead symbol count inflates with every run. Grind closes the gap by finding dead helpers from forge, classifying them (adopt/re-export/promote/ false-positive/remove), wiring them into consumers, and gating on a non-positive dead-symbol delta. Pipeline is now: recon → gauntlet → sync → forge → grind → close
- Track currentTarget, processedTargets, failedTargets in state for mid-run resume after interruption - Persist grind classifications to grind-targets.ndjson (append-only) so re-runs skip already-analyzed targets - Write titan-state.json after every target, not just at phase end - Add interrupted-mid-target recovery logic in edge cases - Use codegraph audit/context/fn-impact/where/query/ast before edits - Add codegraph diff-impact --staged before commits - Add codegraph build after edits to keep graph current - Add --target flag for retrying individual failures
Rewrite titan-grind with full resilience (state machine, .bak files, NDJSON persistence, snapshot management), codegraph integration (audit/context/fn-impact/diff-impact), diff review (DR1-DR3), drift detection, false positive tracking via issues.ndjson, and phase timestamps. Update titan-close: grind-targets.ndjson in artifact load list, adoption concern type in PR grouping, grind metrics in before/after comparison, GRIND row in Pipeline Timeline, Grind Results section in report template, grind block in close-summary.json. Update titan-reset: titan-grind-baseline snapshot deletion and grind-targets.ndjson in artifact listing.
Greptile SummaryThis PR is a comprehensive rewrite of Confidence Score: 5/5Safe to merge — prior P0/P1 concerns are resolved and the single remaining finding is a P2 documentation gap. All previously flagged issues (schema mismatch in titan-close grind metrics, null guard in batchQuery) are confirmed fixed. The only new finding is a missing explicit dry-run guard at Step 3 of titan-grind, which is a P2 style issue — the intent is documented in the Rules section, and LLM agents reading the full skill will still respect it. The batch.ts null guard restoration is clean and correct. .claude/skills/titan-grind/SKILL.md — Step 3 dry-run guard is implicit rather than structural.
|
| Filename | Overview |
|---|---|
| .claude/skills/titan-grind/SKILL.md | Full rewrite with state machine, NDJSON persistence, snapshot management, and codegraph integration; one minor gap: Step 3 lacks an explicit dry-run guard at its header. |
| .claude/skills/titan-close/SKILL.md | Grind artifact integration aligned: grind-targets.ndjson in artifact list, grind metrics reading corrected to use classification field and titan-state.json grind block, adoption concern type added to grouping strategy. |
| .claude/skills/titan-reset/SKILL.md | Correctly adds titan-grind-baseline snapshot deletion in Step 2 and grind-targets.ndjson to the artifact listing in Step 3. |
| .claude/skills/titan-run/SKILL.md | Adds grind row to Step 0.5 pre-validation table for --start-from grind; grind loop in Step 4.5 and post-loop validation are consistent with titan-grind's state schema. |
| src/presentation/batch.ts | Null guard restored before typeof check in isMulti detection; the fix is correct and prevents TypeError on null first elements. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[titan-run Step 4.5] --> B[Grind Loop]
B --> C{unground phases?}
C -- no --> D[Post-loop V18 validation]
C -- yes --> E[titan-grind --phase N]
E --> F[Step 0: Pre-flight + state init]
F --> G[Step 1: Identify forge symbols]
G --> H[Step 2: Classify dead helpers\nPersist to grind-targets.ndjson]
H --> I{dry-run?}
I -- yes --> J[Print plan, stop]
I -- no --> K[Step 3: Per-target adoption loop]
K --> L[DR1-DR3 Diff review]
L -- DIFF FAIL --> M[Rollback staged, add to failedTargets]
L -- pass --> N[codegraph diff-impact]
N --> O[Run tests]
O -- fail --> P[Snapshot restore + git rollback]
O -- pass --> Q[titan-gate]
Q -- FAIL --> P
Q -- PASS --> R[git commit]
R --> S[Update titan-state.json + .bak]
S --> K
M --> K
P --> K
K --> T[Step 4: Dead-symbol delta gate]
T --> U[Step 5: Phase completion\nUpdate grind.completedPhases]
U --> B
D --> V[titan-close reads\ngrind-targets.ndjson +\ntitan-state.json grind block]
Reviews (2): Last reviewed commit: "fix(skill): add --start-from grind pre-c..." | Re-trigger Greptile
.claude/skills/titan-close/SKILL.md
Outdated
| - `.codegraph/titan/issues.ndjson` — issue tracker from all phases | ||
| - `.codegraph/titan/arch-snapshot.json` — pre-forge architectural snapshot (communities, structure, drift). Use for before/after comparison in the Metrics section. May not exist if capture failed. | ||
| - `.codegraph/titan/drift-report.json` — cumulative drift reports from all phases. May not exist if no drift was detected. | ||
| - `.codegraph/titan/grind-targets.ndjson` — grind phase adoption targets and outcomes. Each line: `{target, status, deadBefore, deadAfter, ...}`. May not exist if grind wasn't run. |
There was a problem hiding this comment.
Schema mismatch between grind-targets.ndjson writer and reader
titan-close expects entries with status: "done"|"failed"|"false_positive" and per-entry deadBefore/deadAfter fields, but titan-grind Step 2f writes entries with classification: "adopt|re-export|promote|..." and no dead-count fields. The grind metrics block in titan-close (count of status: "done", status: "failed", status: "false_positive") will always return 0, and the dead-symbol delta table will have no data.
titan-grind Step 2f writes:
{"target":"...","file":"...","phase":N,"classification":"adopt|remove|...","reason":"...","consumers":[...],"pattern":"...","timestamp":"..."}titan-close grind metrics reads:
- count of lines with `status: "done"`
- count of lines with `status: "failed"`
- `deadBefore`/`deadAfter` per target
The fix is to align the field names — either titan-grind should write a status field (mapped from classification) plus deadBefore/deadAfter, or titan-close should read classification and compute the counts from titan-state.json's grind.processedTargets/grind.failedTargets/grind.falsePositives lists instead.
There was a problem hiding this comment.
Fixed in 9c3752e. Updated titan-close to read classification from grind-targets.ndjson and pull metrics (processedTargets, failedTargets, falsePositives, deadSymbolDelta) from titan-state.json → grind block instead of the non-existent status/deadBefore/deadAfter fields. Also updated the artifact description on line 68 to document the actual NDJSON schema.
|
|
||
| const batchOpts = { | ||
| batchQuery(targets as Array<string | { command: string; target: string }>, opts.db, { | ||
| command, | ||
| depth: opts.depth ? parseInt(opts.depth as string, 10) : undefined, | ||
| file: opts.file, | ||
| kind: opts.kind, | ||
| noTests: ctx.resolveNoTests(opts), | ||
| }; | ||
|
|
||
| if (isMultiBatch(targets)) { | ||
| const data = multiBatchData(targets as MultiBatchItem[], opts.db, batchOpts); | ||
| console.log(JSON.stringify(data, null, 2)); | ||
| } else { | ||
| batch(command!, targets as string[], opts.db, batchOpts); | ||
| } | ||
| }); | ||
| }, | ||
| }; |
There was a problem hiding this comment.
Missing null guard dropped from isMultiBatch
The old isMultiBatch guard checked targets[0] !== null before 'command' in targets[0]. The new batchQuery checks typeof targets[0] === 'object' which is true for null, then immediately accesses (targets[0] as MultiBatchTarget).command. If a caller ever passes an array with a null first element (valid at runtime even if the union type excludes it), this will throw TypeError: Cannot read properties of null.
| const batchOpts = { | |
| batchQuery(targets as Array<string | { command: string; target: string }>, opts.db, { | |
| command, | |
| depth: opts.depth ? parseInt(opts.depth as string, 10) : undefined, | |
| file: opts.file, | |
| kind: opts.kind, | |
| noTests: ctx.resolveNoTests(opts), | |
| }; | |
| if (isMultiBatch(targets)) { | |
| const data = multiBatchData(targets as MultiBatchItem[], opts.db, batchOpts); | |
| console.log(JSON.stringify(data, null, 2)); | |
| } else { | |
| batch(command!, targets as string[], opts.db, batchOpts); | |
| } | |
| }); | |
| }, | |
| }; | |
| const isMulti = | |
| targets.length > 0 && | |
| targets[0] !== null && | |
| typeof targets[0] === 'object' && | |
| !!(targets[0] as MultiBatchTarget).command; |
There was a problem hiding this comment.
Fixed in e77e1f8. Added the targets[0] !== null guard back before the typeof check, since typeof null === 'object' is true in JS and would cause a TypeError when accessing .command.
| rootDir, | ||
| analysisOpts, | ||
| ); | ||
| if (needsCfg) { | ||
| const { buildCFGData } = await import('../../../../features/cfg.js'); | ||
| await buildCFGData(db, analysisSymbols, rootDir, engineOpts); | ||
| } | ||
| if (needsDataflow) { | ||
| const { buildDataflowEdges } = await import('../../../../features/dataflow.js'); | ||
| await buildDataflowEdges(db, analysisSymbols, rootDir, engineOpts); | ||
| } | ||
| const { runAnalyses } = await import('../../../../ast-analysis/engine.js'); | ||
| await runAnalyses( | ||
| db, | ||
| analysisSymbols, | ||
| rootDir, | ||
| { ast: false, complexity: false, cfg: needsCfg, dataflow: needsDataflow }, | ||
| engineOpts, | ||
| ); | ||
| return true; | ||
| } | ||
|
|
There was a problem hiding this comment.
Unconditional engine import when neither analysis is needed
The old code only dynamically imported cfg.js / dataflow.js when the respective flag was set. The new code always imports ast-analysis/engine.js, even when both needsCfg and needsDataflow are false (the common incremental-build path). This adds a module-load round-trip on every incremental rebuild that requires no supplemental analysis.
Consider guarding the import:
if (needsCfg || needsDataflow) {
const { runAnalyses } = await import('../../../../ast-analysis/engine.js');
await runAnalyses(db, analysisSymbols, rootDir,
{ ast: false, complexity: false, cfg: needsCfg, dataflow: needsDataflow },
engineOpts,
);
}There was a problem hiding this comment.
This is actually already guarded. Line 268 has if (!needsCfg && !needsDataflow) return false; which early-returns before the import on line 280 ever executes. The import is only reached when at least one of needsCfg/needsDataflow is true, so there's no unnecessary module-load on the incremental-build path.
…chema (#896) titan-close was reading status/deadBefore/deadAfter fields that titan-grind never writes. Updated to read classification field from grind-targets.ndjson and pull metrics from titan-state.json grind block.
typeof null === 'object' is true in JS, so accessing .command on null would throw TypeError. Added explicit null check before the typeof.
Without a validation table row for grind, the orchestrator had no guidance on which pre-checks to run when starting directly from grind.
|
Re: P2 — Fixed in 0718ddf. Added a |
Codegraph Impact Analysis1 functions changed → 1 callers affected across 1 files
|
Summary
adoptionconcern type in PR grouping, grind metrics in before/after comparison, GRIND row in Pipeline Timeline, Grind Results section in report templateContext
Follow-up to #895 (dead helper adoption). The initial grind skill was minimal — this rewrite addresses 25 gaps found by comparing against all other titan skills (forge, gate, close, reset, sync, gauntlet, recon).
Test plan