agentleFS
optaveoptave/ops-codegraph-tool/.claude/skills/titan-grind/SKILL.md

titan-grind

Skill94 starsChanged 3 months agoApache-2.0View on GitHub

Adopt extracted helpers — find dead symbols from forge, wire them into consumers, replace duplicated inline patterns, and gate on dead-symbol delta (Titan Paradigm Phase 4.5)

  • Deletes or force-pushes

The file

Show its rules and text

We split this skill into the rules it gives an agent

62 rules. About 0 are common, 32 are distinctive, and 30 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈32

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 30

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  • could be automatic: a setting could enforce it
  1. Distinctive:

    No args → process the next unground forge phase

    line 19
  2. Very specific:

    If not in a worktree, stop: "Run /worktree first."

    line 33
  3. Distinctive:

    Session discovery: Check all worktrees for titan artifacts:

    line 35
  4. Very specific:

    Found artifacts in a different worktree: Read its titan-state.json → currentPhase and execution block.

    line 43
  5. Very specific:

    Found nothing: Stop: "No Titan session found.

    line 45
  6. Very specific:

    If merge conflicts → stop: "Merge conflict detected.

    line 51
  7. Very specific:

    If CORRUPT → check .codegraph/titan/titan-state.json.bak:

    line 57
  8. Very specific:

    If no execution block → stop: "No forge execution found.

    line 72
  9. Distinctive:

    Take a graph snapshot: before making changes:

    line 105
  10. Distinctive:

    If main has advanced, check if any forge-touched files were modified:

    line 121
  11. Very specific:

    Determine next phase: Use --phase N if provided, otherwise find the lowest forge phase number not in grind.completedPhases.

    line 127
  12. Very specific:

    If corrupt > 0: Print "WARNING: <corrupt> corrupt lines in grind-targets.ndjson (likely from a crashed write).

    line 166
  13. Distinctive:

    If existing valid entries cover this phase → load them and skip to Step 3.

    line 167
  14. Distinctive:

    Run dead-code detection scoped to the candidate files:

    line 187
  15. Distinctive:

    Read the helper's source file to understand its signature, parameters, and behavior.

    line 208
  16. Very specific:

    If any of these apply → classify as false-positive.

    line 223
  17. Distinctive:

    Read the helper's source to identify its core pattern (the 2–5 token signature that distinguishes it from generic code).

    line 232
  18. Distinctive:

    Check if the helper should be called by existing code that currently does the same work.

    line 328
  19. Distinctive:

    If the helper wraps an underlying operation, find every call site of that underlying operation across the entire codebase:

    line 340
  20. Distinctive:

    If the underlying operation is called in >10 files and fewer than half are in the consumer list, that is a red flag — re-run the grep and …

    line 348
  21. Distinctive:

    Before classifying as adopt, promote, or re-export, check whether a semantically equivalent helper already exists elsewhere in the codebase …

    line 352
  22. Very specific:

    If the helper is in a module with a barrel file (index.ts, mod.rs), check if it needs to be re-exported:

    line 371
  23. Distinctive:

    If zero actionable grind targets (only false-positives and intentionally-private) → print "Phase N: no dead helpers to adopt.

    line 399
  24. Very specific:

    Skip if done: Check if target is already in grind.processedTargets.

    line 407
  25. Distinctive:

    Understand before touching: Run codegraph commands to get current state (code may have changed since classification):

    line 416
  26. Distinctive:

    If the target is no longer dead → skip with note: "Target already adopted (by prior grind commit or main merge)."

    line 432
  27. Distinctive:

    Read source file(s): Read the helper source and each consumer site.

    line 434
  28. Distinctive:

    re-export: Add the symbol to the barrel file's export list.

    line 439
  29. Very specific:

    promote: Add export keyword (or pub visibility in Rust), add to barrel if applicable, then wire consumers as in adopt.

    line 440
  30. Distinctive:

    Never git add . or git add -A.

    line 449
  31. Distinctive:

    Test file for the target → OK

    line 463
  32. Distinctive:

    If the diff does something entirely different from the classification → DIFF FAIL

    line 470
  33. Distinctive:

    If lines removed > 10, verify deleted symbols have no active callers not updated in this diff:

    line 473
  34. Distinctive:

    If deleted symbol has callers not in the staged diff → DIFF FAIL: "Deleted <symbol> still has <N> callers not updated."

    line 477
  35. Very specific:

    Add to grind.failedTargets with reason "diff-review: <detail>".

    line 484
  36. Very specific:

    If testCmd == "NO_TEST_SCRIPT" → skip pre-gate test run.

    line 502
  37. Very specific:

    Use the Skill tool to invoke titan-gate.

    line 506
  38. Distinctive:

    If FAIL on cycle/test/lint/build (gate auto-rolls back staged changes) → go to rollback (step 15) to also revert working tree.

    line 507
  39. Distinctive:

    If FAIL on other checks (complexity, semantic, structural/arch) — gate does NOT auto-rollback → unstage AND revert:

    line 508
  40. Very specific:

    Add to grind.failedTargets with reason, continue to next target.

    line 513
  41. Very specific:

    Update state on success: Write titan-state.json immediately after each commit:

    line 528
  42. Very specific:

    Add target to grind.processedTargets

    line 529
  43. Very specific:

    Add to grind.failedTargets: { "target": "<name>", "reason": "<why>", "phase": N }

    line 556
  44. Very specific:

    Add target to grind.processedTargets (so it's not retried on resume)

    line 557
  45. Distinctive:

    Rebuild graph: after successful changes to keep it current for the next target:

    line 563
  46. Distinctive:

    After all targets in the phase are processed:

    line 579
  47. Very specific:

    Add phase number to grind.completedPhases

    line 603
  48. Very specific:

    Update grind.deadSymbolBaseline to grind.deadSymbolCurrent (rolling baseline for next phase)

    line 604
  49. Very specific:

    When ALL forge phases are ground (all entries in execution.completedPhases are in grind.completedPhases):

    line 613
  50. Distinctive:

    If all phases are complete:

    line 660
  51. Very specific:

    Helper was adopted by a previous target in this phase: Check codegraph roles --role dead before applying — if no longer dead, skip.

    line 685
  52. Distinctive:

    Merge conflict with main: Stop with conflicting files listed.

    line 687
  53. Very specific:

    --target <name>: Run single target only. Useful for retrying entries in grind.failedTargets.

    line 688
  54. Distinctive:

    Context budget exhausted: If context reaches ~80% capacity mid-phase, write all state to disk (including partially processed targets), …

    line 690
  55. Very specific:

    Sync plan alignment: Before modifying a file, check if it's scheduled for a later forge phase in sync.json.

    line 691
  56. Distinctive:

    One forge phase per invocation: Stop after the phase completes.

    line 697
  57. Very specific:

    Use codegraph to understand before editing: Run codegraph context, codegraph audit --quick, codegraph fn-impact, codegraph where, and …

    line 700
  58. Distinctive:

    Diff review before gate: Every change goes through DR1-DR3 checks.

    line 701
  59. Very specific:

    Test before gate: Run the project's test suite as fast-fail before invoking /titan-gate.

    line 702
  60. Very specific:

    Gate before commit: Every commit must pass /titan-gate.

    line 703
  61. Distinctive:

    Stage only specific files: Never git add . or git add -A.

    line 704
  62. Very specific:

    Persist state after every target: Write titan-state.json after each commit, failure, or classification.

    line 709

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Titan GRIND — Adopt Extracted Helpers

You are running the GRIND phase of the Titan Paradigm.

Forge shapes the metal. Grind smooths the rough edges. Your goal: find helpers that forge extracted but never wired into consumers, adopt them across the codebase, and gate on a non-positive dead-symbol delta.

Why this phase exists: Forge decomposes god-functions into smaller helpers, but those helpers are only called within their own file. The dead symbol count inflates with every forge phase because the adoption loop is never closed. Grind closes it.

Context budget: One forge phase per invocation. Process all targets from one forge phase's commits, then stop. If context reaches ~80% capacity mid-phase, write all state to disk, print a progress message, and stop — user re-runs for remainder.

Arguments (from $ARGUMENTS):

  • No args → process the next unground forge phase
  • --phase N → process a specific forge phase
  • --target <name> → run single target only (for retrying failures)
  • --dry-run → analyze and classify without making code changes (classifications ARE persisted to grind-targets.ndjson)
  • --yes → skip confirmation prompt (typically passed by /titan-run orchestrator)

Step 0 — Pre-flight

  1. Worktree check:
    git rev-parse --show-toplevel && git worktree list
    If not in a worktree, stop: "Run /worktree first."
  2. Session discovery. Check all worktrees for titan artifacts:
    git worktree list
    For each worktree, check:
    ls <worktree-path>/.codegraph/titan/titan-state.json 2>/dev/null
    • Found artifacts in a different worktree: Read its titan-state.json → currentPhase and execution block. If forge completed there but grind hasn't run, merge its branch: git merge <branch> --no-edit
    • Found artifacts in current worktree: Proceed normally.
    • Found nothing: Stop: "No Titan session found. Run /titan-forge first."
  3. Sync with main:
    git fetch origin main && git merge origin/main --no-edit
    If merge conflicts → stop: "Merge conflict detected. Resolve and re-run /titan-grind."
  4. Validate state file integrity:
    node -e "try { JSON.parse(require('fs').readFileSync('.codegraph/titan/titan-state.json','utf8')); console.log('OK'); } catch(e) { console.log('CORRUPT: '+e.message); process.exit(1); }"
    If CORRUPT → check .codegraph/titan/titan-state.json.bak:
    • Backup valid → restore: cp .codegraph/titan/titan-state.json.bak .codegraph/titan/titan-state.json
    • Backup also corrupt or missing → stop: "State file corrupted with no valid backup. Run /titan-reset and start over."
  5. Load artifacts. Read:
    • .codegraph/titan/titan-state.json — current state (required)
    • .codegraph/titan/sync.json — execution plan (required)
    • .codegraph/titan/gate-log.ndjson — gate verdicts (optional)
    • .codegraph/titan/grind-targets.ndjson — persisted grind analysis (optional, exists on resume)
    • .codegraph/titan/arch-snapshot.json — pre-forge architectural snapshot (optional — if missing, gate's A1/A3/A4 checks will be skipped; print: "NOTE: No arch-snapshot.json found. Gate architectural comparisons will be skipped.")
    • .codegraph/titan/issues.ndjson — cross-phase issue tracker (optional, for appending)
  6. Validate state. Grind runs after forge. Check:
    • titan-state.json → execution block exists
    • execution.completedPhases has at least one entry
    • If no execution block → stop: "No forge execution found. Run /titan-forge first."
  7. Initialize grind state (if grind block doesn't exist in titan-state.json). Merge into titan-state.json:
    {
      "grind": {
        "completedPhases": [],
        "currentPhase": null,
        "currentTarget": null,
        "processedTargets": [],
        "failedTargets": [],
        "adoptions": [],
        "removals": [],
        "falsePositives": [],
        "diffWarnings": [],
        "deadSymbolBaseline": null,
        "deadSymbolCurrent": null
      }
    }
  8. Update top-level state. Set currentPhase to "grind" in titan-state.json. Write immediately.
  9. Back up state:
    cp .codegraph/titan/titan-state.json .codegraph/titan/titan-state.json.bak
  10. Ensure graph is current. Rebuild if stale:
    codegraph build
  11. Take a graph snapshot before making changes:
    codegraph snapshot save titan-grind-baseline 2>/dev/null || true
  12. Capture dead-symbol baseline (only if grind.deadSymbolBaseline is null):
    codegraph roles --role dead -T --json | node -e "const d=[];process.stdin.on('data',c=>d.push(c));process.stdin.on('end',()=>{try{const data=JSON.parse(Buffer.concat(d));console.log(JSON.stringify({total:data.count??0,byRole:data.summary??{}}));}catch(e){console.error('Failed to parse roles --json output: '+e.message);process.exit(1);}})"
    codegraph roles --json returns { count, summary, symbols } (not a bare array) — summary is already the per-role breakdown (e.g. dead-leaf, dead-entry, dead-ffi, dead-unresolved), so no manual reduce is needed. Store the total in grind.deadSymbolBaseline. Write titan-state.json immediately.
  13. Drift detection. Compare titan-state.json → mainSHA against current origin/main:
    git rev-list --count <mainSHA>..origin/main
    If main has advanced, check if any forge-touched files were modified:
    git diff --name-only <mainSHA>..origin/main
    Cross-reference against files from forge's execution.commits. If >20% of grind candidate files changed on main → WARN: "Main has advanced <N> commits since forge. <M> grind candidate files were modified. Some helpers may already be adopted or removed on main. Consider re-running /titan-recon." Continue unless >50% affected, then stop.
  14. Determine next phase. Use --phase N if provided, otherwise find the lowest forge phase number not in grind.completedPhases.
  15. Update state. Set grind.currentPhase to the target phase number. Write titan-state.json.
  16. Record phase timestamp (only if not already set — may exist from a prior crashed run):
    node -e "const fs=require('fs');const s=JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));s.phaseTimestamps=s.phaseTimestamps||{};s.phaseTimestamps['grind']=s.phaseTimestamps['grind']||{};if(!s.phaseTimestamps['grind'].startedAt){s.phaseTimestamps['grind'].startedAt=new Date().toISOString();fs.writeFileSync('.codegraph/titan/titan-state.json',JSON.stringify(s,null,2));}"
  17. Print plan and ask for confirmation (unless --yes):
    GRIND — Phase N: <label>
    Forge made N commits for this phase.
    Dead symbol baseline: <N>
    Previously processed: <N> targets (<N> adopted, <N> failed)
    Drift: <fresh|N commits behind>
    
    Will: identify new dead symbols, find adoption opportunities, wire helpers into consumers.
    Proceed? [y/n]

Step 1 — Identify forge's new symbols

Skip if: .codegraph/titan/grind-targets.ndjson already has entries for this phase (resume case). Validate NDJSON integrity first:

node -e "
const fs = require('fs');
const path = '.codegraph/titan/grind-targets.ndjson';
if (!fs.existsSync(path)) { console.log(JSON.stringify({ valid: 0, corrupt: 0, total: 0, missing: true })); process.exit(0); }
const lines = fs.readFileSync(path,'utf8').trim().split('\n');
let valid = 0, corrupt = 0;
for (const line of lines) { try { JSON.parse(line); valid++; } catch { corrupt++; } }
console.log(JSON.stringify({ valid, corrupt, total: lines.length, missing: false }));
"
  • If corrupt > 0: Print "WARNING: <corrupt> corrupt lines in grind-targets.ndjson (likely from a crashed write). These targets will be re-classified."
  • If existing valid entries cover this phase → load them and skip to Step 3.

For the target forge phase, get the commits from titan-state.json → execution.commits that belong to this phase (cross-reference with sync.json → executionOrder[phase].targets).

For each commit, identify changed files:

git diff-tree --no-commit-id --name-only -r <commit-sha>

For each changed file, inventory the symbols:

codegraph where --file <changed-file> -T --json

Collect all symbols defined in files touched by this forge phase. These are the candidate symbols — forge created or modified them.


Step 2 — Find and classify dead helpers

Run dead-code detection scoped to the candidate files:

codegraph roles --role dead -T --file <changed-file> --json

For each file touched by forge in this phase, collect symbols flagged as dead. Filter to:

  • Functions and constants only (skip interfaces, parameters, type aliases — these are typically false positives from TypeScript type-level usage)
  • Symbols that are exported (file-local helpers called within their own file are not dead — they're just private)
  • Symbols NOT in the public API barrel (src/index.ts) unless they have zero external consumers

For each candidate dead symbol, run the full analysis:

2a. Understand the helper

codegraph where <helper-name>
codegraph context <helper-name> -T --json
codegraph audit --quick <helper-name> -T --json

Read the helper's source file to understand its signature, parameters, and behavior. This is critical — you cannot classify or adopt a helper you don't understand.

2b. Check if actually dead (false-positive detection)

codegraph fn-impact <helper-name> -T --json
codegraph ast --kind call <helper-name> -T --json

Check for:

  • Dynamic imports (await import(...)) that call this symbol — codegraph can't trace these
  • Re-export chains (export { X } from './foo.js') — codegraph may not count re-exports as references
  • Closure-local usage — the symbol is assigned to a variable and called within the same function
  • Template literal usage — the symbol is referenced inside a string template (e.g., HTML renderer)

If any of these apply → classify as false-positive. Append to .codegraph/titan/issues.ndjson:

{"phase":"grind","timestamp":"<ISO 8601>","severity":"bug","category":"codegraph","description":"False positive dead-code detection: <symbol> in <file> — <reason>","context":"<detection method that failed>"}

2c. Duplicate-logic scan (codebase-wide — mandatory)

This scan must cover the entire codebase, not just forge-touched files. Stopping after finding the first few matches or only checking files the forge phase touched is the primary cause of redundant helpers being created in later runs. You must prove the pattern is absent elsewhere before classifying.

Read the helper's source to identify its core pattern (the 2–5 token signature that distinguishes it from generic code). Then run all three of the following — not just one:

1. Semantic search (catches renamed or restructured duplicates):

codegraph search "<describe what the helper does in plain language>" --json

Retain these results in memory — Step 2e reuses them. Inline-pattern matches (anonymous code inside functions) belong to Step 2c; named-function results that describe a pre-existing helper belong to Step 2e. Evaluate both in a single pass rather than issuing a second identical query later.

2. Token-level grep across all source files (catches exact inline duplicates):

# Discover actual TypeScript source roots from repo layout (do not assume src/)
node -e "
const fs = require('fs');
const roots = [];
// Check tsconfig for rootDir (strip JSONC comments and trailing commas first)
if (fs.existsSync('tsconfig.json')) {
  try {
    const raw = fs.readFileSync('tsconfig.json','utf8')
      .replace(/\/\/[^\n]*/g,'')       // strip // line comments
      .replace(/,(\s*[}\]])/g,'\$1');  // strip trailing commas
    const tc = JSON.parse(raw);
    if (tc.compilerOptions?.rootDir) roots.push(tc.compilerOptions.rootDir);
  } catch {}
}
// Check package.json workspaces
if (fs.existsSync('package.json')) {
  try {
    const pkg = JSON.parse(fs.readFileSync('package.json','utf8'));
    const wsGlobs = Array.isArray(pkg.workspaces) ? pkg.workspaces : (pkg.workspaces?.packages ?? []);
    wsGlobs.forEach(w => roots.push(w.replace(/\/\*\*?.*$/, '')));
  } catch {}
}
// Fall back to any top-level dir that contains TS/JS source files
if (roots.length === 0) {
  const srcExts = ['.ts','.tsx','.mts','.cts','.js','.jsx'];
  fs.readdirSync('.').filter(d => {
    try { return fs.statSync(d).isDirectory() && fs.readdirSync(d).some(f => srcExts.some(e => f.endsWith(e))); } catch { return false; }
  }).forEach(d => roots.push(d));
}
console.log([...new Set(roots)].join(' ') || 'src');
"
# Use the discovered roots (e.g. src, packages/core, lib) — never hardcode
grep -rn "<key-token-1>" <discovered-ts-roots> --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.mts" --include="*.cts" --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git --exclude-dir=.cache -l
grep -rn "<key-token-2>" <discovered-ts-roots> --include="*.ts" --include="*.tsx" --include="*.js" --include="*.jsx" --include="*.mts" --include="*.cts" --exclude-dir=node_modules --exclude-dir=dist --exclude-dir=.git --exclude-dir=.cache -l
# Discover Rust workspace members if applicable
if [ -f Cargo.toml ]; then
  cargo metadata --no-deps --format-version 1 2>/dev/null | node -e "
    const d=[];process.stdin.on('data',c=>d.push(c));
    process.stdin.on('end',()=>{ try { const m=JSON.parse(Buffer.concat(d));
      // Use packages[].manifest_path to get actual source directories — workspace_members
      // only contains names, not paths. Print one path per line for xargs to iterate.
      const paths = (m.packages||[]).map(p => require('path').dirname(p.manifest_path));
      const unique = [...new Set(paths)];
      if (unique.length) { console.log(unique.join('\n')); } else { console.log('.'); }
    } catch{} });
  " | xargs -I{} grep -rn "<key-token>" {} --include="*.rs" --exclude-dir=target --exclude-dir=.git -l
fi

3. Symbol-level duplicate scan (catches helpers with different names but identical purpose):

# Extract 2-3 meaningful tokens from the helper name and filter dead symbols by those tokens.
# This keeps output focused on candidates actually worth reviewing — not the full dead-symbol list.
codegraph roles --role dead -T --json | node -e "
const helperName = '<helper-name>';
// Split camelCase/snake_case into tokens; take the 2-3 most distinctive (skip generic words)
const stopWords = new Set(['get','set','is','has','to','from','by','on','of','the','a','an','format','parse','build','make','create','handle','process','run','check','with','use']);
const tokens = helperName
  .replace(/([a-z])([A-Z])/g,'\$1 \$2')
  .replace(/[_-]+/g,' ')
  .toLowerCase()
  .split(' ')
  .filter(t => t.length > 2 && !stopWords.has(t))
  .slice(0, 3);
const d=[];process.stdin.on('data',c=>d.push(c));
process.stdin.on('end',()=>{ try {
  const data=JSON.parse(Buffer.concat(d));
  const candidates = (data.symbols || []).filter(i =>
    i.name !== helperName &&
    tokens.some(t => i.name.toLowerCase().includes(t))
  );
  console.log(JSON.stringify(candidates));
} catch { console.log('[]'); } });
"

For each file that produces a match in any of the three scans: read the matching region, confirm whether it duplicates the helper's logic, and add it to the consumer list if so. A match list of zero across all three scans is required before concluding "no duplicates found" — do not assume absence without running all three.

Look for:

  • Identical multi-line patterns (e.g., object literal mappings)
  • Equivalent .map() / .reduce() callbacks the helper could replace
  • Hand-rolled loops that duplicate the helper's logic
  • Similar function signatures doing the same work in a different module

2d. Consumer-wiring scan (all call sites — mandatory)

Check if the helper should be called by existing code that currently does the same work. This must scan the full codebase, not only files in the current forge phase.

codegraph fn-impact <helper-name> -T --json

For borderline cases where you need to confirm whether a path exists between the helper and a specific suspected consumer:

codegraph path <helper-name> <potential-consumer> -T --json

If the helper wraps an underlying operation, find every call site of that underlying operation across the entire codebase:

codegraph ast --kind call <underlying-function> -T --json

The result is a list of all call sites. For each site NOT already using the helper: read the surrounding code and determine if the helper is a drop-in replacement. Add every adoptable site to the consumer list — not just the ones in forge-touched files.

If the underlying operation is called in >10 files and fewer than half are in the consumer list, that is a red flag — re-run the grep and semantic search before classifying.

2e. Pre-existing duplicate helper check

Before classifying as adopt, promote, or re-export, check whether a semantically equivalent helper already exists elsewhere in the codebase (a different name, same purpose). This is how redundant helpers accumulate across Titan runs.

Re-use Scan 1 results from Step 2c if already completed — do not issue a second identical query. The semantic search from Step 2c covers the same space; here you are evaluating named-function results (not inline patterns) from that result set. Only rerun if the Step 2c results were not retained:

codegraph search "<describe helper purpose>" --json

Evaluate the named-function results against the current helper:

  • No semantically equivalent helper found → proceed with the original classification unchanged (adopt, promote, or re-export as determined in Steps 2d–2f).
  • Pre-existing helper found, more broadly used → classify the new helper as remove (redirect): wire consumers to the existing helper, then delete the new one. Use redirect_to to record the target name and redirect_to_file to record its file path (see persist schema below).
  • Pre-existing helper found, narrower scope → classify the new helper as adopt or promote (as applicable) but file an issue to consolidate later:
    gh issue create --title "Consolidate duplicate helpers: <new> and <existing>" --body "Both do <purpose>. Created by forge phase N. Consolidate in a follow-up." --label "follow-up" || gh issue create --title "Consolidate duplicate helpers: <new> and <existing>" --body "Both do <purpose>. Created by forge phase N. Consolidate in a follow-up."

2f. Re-export check

If the helper is in a module with a barrel file (index.ts, mod.rs), check if it needs to be re-exported:

codegraph exports <barrel-file> -T --json

2g. Classify and persist

For each grind target, assign one of:

ClassificationAction
adoptFound N sites where this helper replaces duplicated code. Wire it in.
re-exportHelper is consumed internally but missing from barrel. Add re-export.
promoteHelper is file-local but useful elsewhere. Export and wire consumers.
false-positiveNot actually dead (dynamic import, closure, re-export chain). Skip.
intentionally-privateHelper is file-local and only used within its file. Remove export or leave as-is.
removeHelper is genuinely unused and has no adoption opportunity. Delete it.

Persist each classification immediately to .codegraph/titan/grind-targets.ndjson (one JSON object per line, append-only):

{"target":"<name>","file":"<file>","phase":N,"classification":"adopt|re-export|promote|false-positive|intentionally-private|remove","reason":"<why>","consumers":["file1.ts"],"pattern":"<what to search for>","redirect_to":"<existing-helper-name-or-null>","redirect_to_file":"<path/to/file-or-null>","timestamp":"<ISO 8601>"}

redirect_to and redirect_to_file are only set when classification is remove and the removal reason is a pre-existing equivalent identified in Step 2e. redirect_to_file records the file path of the target helper so import rewiring is unambiguous on resume (two helpers may share a name across different modules). Leave both null (or omit) for ordinary removals.

This ensures resume works — if interrupted, re-running loads existing entries and skips already-classified targets.

If zero actionable grind targets (only false-positives and intentionally-private) → print "Phase N: no dead helpers to adopt. Forge wired everything correctly (or all dead symbols are false positives)." Mark phase complete, skip to Step 5.


Step 3 — Execute adoptions (per-target loop)

For each grind target classified as adopt, re-export, promote, or remove:

  1. Skip if done. Check if target is already in grind.processedTargets. If so, skip.
  2. Update state. Set grind.currentTarget in titan-state.json. Write immediately. Back up:
    cp .codegraph/titan/titan-state.json .codegraph/titan/titan-state.json.bak
  3. Reload grind-targets.ndjson entry for this target to get classification, consumers, and pattern.
  4. Understand before touching. Run codegraph commands to get current state (code may have changed since classification):

    codegraph context <target> -T --json
    codegraph fn-impact <target> -T --json

    For adopt targets, also understand each consumer site:

    codegraph audit --quick <consumer-file> -T --json
    codegraph where --file <consumer-file> -T --json
  5. Check if still dead. The target may have been adopted by a previous grind commit or by changes merged from main:
    codegraph roles --role dead -T --file <target-file> --json
    If the target is no longer dead → skip with note: "Target already adopted (by prior grind commit or main merge)."
  6. Read source file(s). Read the helper source and each consumer site. Understand the code before editing.
  7. Apply the change based on classification:

    • adopt: Replace inline duplications with calls to the helper. Add imports at each consumer. Verify semantic equivalence — the replacement must produce identical behavior.
    • re-export: Add the symbol to the barrel file's export list.
    • promote: Add export keyword (or pub visibility in Rust), add to barrel if applicable, then wire consumers as in adopt.
    • remove: Two sub-cases based on redirect_to in the grind-targets.ndjson entry:
      • redirect_to is set (Step 2e redirect case): Wire each consumer in the consumers list to call redirect_to instead of the current helper. Use redirect_to_file to resolve the correct import path unambiguously (update import paths and call sites). After wiring, run codegraph fn-impact <target> -T --json to verify no remaining consumers. If fn-impact still reports callsites that were absent from the original consumers list: wire those additional consumers to redirect_to as well and re-verify. If re-wiring is not safe (e.g. different semantics or cross-package boundary) → DIFF FAIL (do not delete). Only delete the helper when fn-impact reports zero remaining callsites.
      • redirect_to is null/absent (ordinary unused removal): Verify no consumers first with codegraph fn-impact <target> -T --json. If consumers exist → DIFF FAIL (do not delete). If no consumers → delete the symbol and clean up orphaned imports.
  8. Stage changed files:
    git add <specific changed files>
    Never git add . or git add -A.
  9. Diff review (intent verification):

    Collect the context:

    git diff --cached --stat
    git diff --cached

    DR1. Scope — only expected files touched: Compare staged file paths against the consumer list from grind-targets.ndjson. Flag any file NOT associated with the current target or its known consumers.

    • File in a completely different domain → DIFF FAIL
    • File is a direct dependency of the target (import chain) → OK (expected ripple)
    • Test file for the target → OK

    DR2. Intent match — diff aligns with classification:

    • adopt → diff should show new import statements and replaced inline patterns, not new logic
    • re-export → diff should only touch the barrel file
    • promote → diff should add export + barrel entry + consumer imports
    • remove → diff should show only deletions (no new functions added) If the diff does something entirely different from the classification → DIFF FAIL

    DR3. Deletion audit (for remove targets): If lines removed > 10, verify deleted symbols have no active callers not updated in this diff:

    codegraph fn-impact <deleted-symbol> -T --json 2>/dev/null

    If deleted symbol has callers not in the staged diff → DIFF FAIL: "Deleted <symbol> still has <N> callers not updated."

    On DIFF FAIL:

    git restore --staged $(git diff --cached --name-only)
    git checkout -- $(git diff --name-only)

    Add to grind.failedTargets with reason "diff-review: <detail>". Continue to next target.

    On DIFF WARN: Log to grind.diffWarnings:

    { "target": "<name>", "check": "<DR1|DR2|DR3>", "message": "<warning text>", "phase": N }

    Proceed to tests.

  10. Verify impact:
    codegraph diff-impact --staged -T --json
    Review the blast radius. If transitive callers > 30 for a simple adoption, something is wrong — review the change carefully.
  11. Run tests (fast-fail before gate):
    testCmd=$(node -e "const p=require('./package.json');const s=p.scripts||{};const script=s.test?'test':s['test:ci']?'test:ci':null;if(!script){console.log('NO_TEST_SCRIPT');process.exit(0);}const fs=require('fs');const runner=fs.existsSync('yarn.lock')?'yarn':fs.existsSync('pnpm-lock.yaml')?'pnpm':fs.existsSync('bun.lockb')?'bun':'npm';console.log(runner+(script==='test'?' test':' run '+script));")
    • If testCmd == "NO_TEST_SCRIPT" → skip pre-gate test run.
    • Otherwise run: $testCmd 2>&1. If tests fail → go to rollback (step 15).
  12. Run /titan-gate: Use the Skill tool to invoke titan-gate.
    • If FAIL on cycle/test/lint/build (gate auto-rolls back staged changes) → go to rollback (step 15) to also revert working tree.
    • If FAIL on other checks (complexity, semantic, structural/arch) — gate does NOT auto-rollback → unstage AND revert:
      git restore --staged $(git diff --cached --name-only) 2>/dev/null
      git checkout -- $(git diff --name-only) 2>/dev/null
      Add to grind.failedTargets with reason, continue to next target. Do NOT go to step 15.
  13. Commit on success:
    git commit -m "grind(<scope>): adopt <helper> across <N> consumers"
    For removals:
    git commit -m "grind(<scope>): remove unused <helper>"
    For re-exports:
    git commit -m "grind(<scope>): re-export <helper> from barrel"
  14. Update state on success. Write titan-state.json immediately after each commit:
    • Add target to grind.processedTargets
    • Record in grind.adoptions (or grind.removals):
      {
        "target": "<helper-name>",
        "classification": "adopt|re-export|promote|remove",
        "consumers": ["file1.ts", "file2.ts"],
        "commit": "<sha>",
        "phase": N
      }
    • Clear grind.currentTarget
    • Back up state:
      cp .codegraph/titan/titan-state.json .codegraph/titan/titan-state.json.bak
  15. On failure (test or gate cycle/test/lint/build rollback):
    # Restore graph snapshot
    codegraph snapshot restore titan-grind-baseline 2>/dev/null || true
    # Revert working tree
    git restore --staged $(git diff --cached --name-only) 2>/dev/null
    git checkout -- $(git diff --name-only) 2>/dev/null
    # Rebuild graph from clean state
    codegraph build
    • Add to grind.failedTargets: { "target": "<name>", "reason": "<why>", "phase": N }
    • Add target to grind.processedTargets (so it's not retried on resume)
    • Clear grind.currentTarget
    • Write titan-state.json
    • Back up state
    • Continue to next target — don't block the whole phase
  16. Rebuild graph after successful changes to keep it current for the next target:
    codegraph build
  17. Take a fresh snapshot for the next target's rollback safety:
    codegraph snapshot save titan-grind-baseline 2>/dev/null || true

For false-positive and intentionally-private targets: add to grind.processedTargets and grind.falsePositives, write state and backup, but make no code changes.


Step 4 — Dead-symbol delta gate

After all targets in the phase are processed:

codegraph build
codegraph roles --role dead -T --json | node -e "const d=[];process.stdin.on('data',c=>d.push(c));process.stdin.on('end',()=>{try{const data=JSON.parse(Buffer.concat(d));console.log(JSON.stringify({total:data.count??0,byRole:data.summary??{}}));}catch(e){console.error('Failed to parse roles --json output: '+e.message);process.exit(1);}})"

Store in grind.deadSymbolCurrent. Write titan-state.json.

Compute delta: current - baseline.

DeltaVerdict
delta < 0PASS — grind reduced dead symbols
delta == 0PASS — neutral (all new helpers were adopted or removed)
delta > 0, delta <= 10WARN — slight increase, likely false positives from type-level symbols
delta > 10FAIL — forge created helpers that grind couldn't adopt. Review grind.adoptions for missed opportunities

On FAIL: print the new dead symbols that were not addressed and their files. Do NOT block — log the warning and continue.


Step 5 — Phase completion

  1. Add phase number to grind.completedPhases
  2. Update grind.deadSymbolBaseline to grind.deadSymbolCurrent (rolling baseline for next phase)
  3. Clear grind.currentPhase
  4. Clear grind.currentTarget
  5. Write updated titan-state.json
  6. Back up state:
    cp .codegraph/titan/titan-state.json .codegraph/titan/titan-state.json.bak

When ALL forge phases are ground (all entries in execution.completedPhases are in grind.completedPhases): 7. Record phaseTimestamps.grind.completedAt:

node -e "const fs=require('fs');const s=JSON.parse(fs.readFileSync('.codegraph/titan/titan-state.json','utf8'));s.phaseTimestamps=s.phaseTimestamps||{};s.phaseTimestamps['grind']=s.phaseTimestamps['grind']||{};s.phaseTimestamps['grind'].completedAt=new Date().toISOString();fs.writeFileSync('.codegraph/titan/titan-state.json',JSON.stringify(s,null,2));"
  1. Clean up grind snapshot:
    codegraph snapshot delete titan-grind-baseline 2>/dev/null || true

Step 6 — Report

Print:

## Grind Phase N Complete: <label>

Dead symbols: <baseline> → <current> (delta: <+/-N>)
Targets: <processed>/<total> processed, <failed> failed
Adoptions: <N> helpers wired into <M> consumers
Removals: <N> unused helpers deleted
False positives: <N> (codegraph resolution bugs → logged to issues.ndjson)
Intentionally private: <N>

### Adoptions:
- <helper>: adopted by <N> consumers (<classification>)
  Commit: <sha>

### Removals:
- <helper>: removed (no adoption opportunity)
  Commit: <sha>

### Failed targets (if any):
- <target>: <reason>

### Diff review warnings (if any):
- <target>: <check> — <message>

### False positives (codegraph bugs — issues filed):
- <symbol>: <reason> (dynamic import / re-export chain / closure)

### Next: Phase M — <label>
Run /titan-grind to continue.

If all phases are complete:

## All grind phases complete

Dead symbols: <initial baseline> → <final> (total delta: <+/-N>)
Total adoptions: <N> across <M> consumers
Total removals: <N>
Total false positives: <N>
Total failed: <N>

Run /titan-close to finalize.

Edge Cases

  • Interrupted mid-target: On re-run, grind.currentTarget is set. Check if target has uncommitted changes (git status). If dirty → restore graph snapshot, rollback dirty files, then re-process the target. If clean → the commit succeeded but state wasn't updated; check git log -1 to see if the last commit matches, and update state accordingly.
  • Interrupted mid-classification (Step 2): On re-run, validate grind-targets.ndjson integrity. Load valid entries, skip already-classified targets, continue from the next unclassified candidate. Corrupt lines are re-classified.
  • State file corrupt on resume: G4-style recovery from .bak file (Step 0.4).
  • No dead helpers in a phase: Skip with note. Some forge phases may have wired everything correctly.
  • Helper is used via dynamic import: Classify as false-positive. Log to issues.ndjson as a codegraph bug.
  • Helper is in Rust, consumers are TypeScript (or vice versa): Cross-language helpers cannot be adopted across the FFI boundary. Classify as intentionally-private if used within their language, or false-positive if the dead flag is from FFI resolution limits.
  • Gate fails on adoption: Restore snapshot, rollback, record failure, continue. A failed adoption may indicate the helper's semantics don't match the inline pattern exactly.
  • Helper was adopted by a previous target in this phase: Check codegraph roles --role dead before applying — if no longer dead, skip.
  • Helper was adopted on main since forge ran: Drift detection (Step 0.13) warns about this. Per-target dead check (Step 3.5) catches it.
  • Merge conflict with main: Stop with conflicting files listed. User resolves and re-runs.
  • --target <name>: Run single target only. Useful for retrying entries in grind.failedTargets.
  • --dry-run: Walk through all targets, classify them, persist to grind-targets.ndjson, print the adoption plan. No code changes, no commits, no graph mutations. Note: dry-run DOES write to grind-targets.ndjson (classifications are useful even without code changes).
  • Context budget exhausted: If context reaches ~80% capacity mid-phase, write all state to disk (including partially processed targets), print progress, and stop. User re-runs to continue.
  • Sync plan alignment: Before modifying a file, check if it's scheduled for a later forge phase in sync.json. If so, WARN: "File <file> is scheduled for forge phase <N> — grind modifications may conflict." Proceed but log the warning.

Rules

  • One forge phase per invocation. Stop after the phase completes. User re-runs for next.
  • Resumable. State is written after every target. If interrupted, re-running picks up from grind.currentTarget and grind.processedTargets. Already-committed adoptions are skipped.
  • Always use --json and -T for codegraph commands.
  • Use codegraph to understand before editing. Run codegraph context, codegraph audit --quick, codegraph fn-impact, codegraph where, and codegraph query before touching any code. Run codegraph diff-impact --staged before committing.
  • Diff review before gate. Every change goes through DR1-DR3 checks. DIFF FAIL = rollback without running gate.
  • Test before gate. Run the project's test suite as fast-fail before invoking /titan-gate.
  • Gate before commit. Every commit must pass /titan-gate. No exceptions.
  • Stage only specific files. Never git add . or git add -A.
  • Never change control flow. Adoptions must be semantically identical to the code they replace. If the helper does something slightly different from the inline pattern, skip it.
  • Codebase-wide scan is mandatory. Steps 2c and 2d must cover the entire source tree, not just forge-touched files. "No duplicates found" requires zero matches across all three scan methods (semantic search, token grep, symbol scan). Stopping early is the primary cause of redundant helpers in later runs.
  • Check for pre-existing equivalents before adopting (Step 2e). If a helper with the same purpose already exists elsewhere, wire consumers to it and remove the new one — don't create two helpers that do the same thing.
  • Rollback restores graph snapshot — codegraph snapshot restore, then git restore --staged + git checkout --, then codegraph build. Never git reset --hard.
  • Persist state after every target. Write titan-state.json after each commit, failure, or classification. Back up after every write. The .ndjson files are append-only — never rewrite them.
  • Log false positives to issues.ndjson. These are codegraph bugs. Close phase compiles them into the report and opens GitHub issues.
  • Dead-symbol delta is advisory, not blocking. Some increase from type-level symbols is expected. The gate catches real problems.
  • Update top-level currentPhase. Set to "grind" at start. Close phase reads this to know grind ran.

Relationship to Other Skills

SkillRelationship
/titan-forgeGrind runs after forge — processes forge's output
/titan-gateCalled per-commit for validation (same as forge)
/titan-closeRuns after grind — reads grind state block, grind-targets.ndjson, and grind entries in issues.ndjson for the final report
/titan-syncGrind reads sync.json to map commits to phases and check alignment
/titan-reconGrind reads titan-state.json produced by recon
/titan-runOrchestrator that dispatches grind as Step 4.5 between forge and close
/titan-resetCleans grind artifacts (grind-targets.ndjson, titan-grind-baseline snapshot)
/titan-gauntletGauntlet recommendations inform what forge extracted, which is what grind processes

Self-Improvement

This skill lives at .claude/skills/titan-grind/SKILL.md. Edit if adoption strategies need refinement or the dead-symbol delta thresholds need adjustment after dogfooding.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.