From 5d29cd9e60984b4c20a0e0164f35a75161717b52 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 02:15:37 -0600 Subject: [PATCH 01/28] fix: prevent findDbPath from escaping git worktree boundary findDbPath() walks up from cwd looking for .codegraph/graph.db. In a git worktree (e.g. .claude/worktrees/agent-xxx/), this crosses the worktree boundary and finds the main repo's database instead. Add findRepoRoot() using `git rev-parse --show-toplevel` (which returns the correct root for both repos and worktrees) and use it as a ceiling in findDbPath(). The walk-up now stops at the git boundary, so each worktree resolves to its own database. --- src/db/connection.js | 40 +++++++++++++- tests/unit/db.test.js | 121 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 155 insertions(+), 6 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index beffdc41..1961c3a8 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -1,8 +1,43 @@ +import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import Database from 'better-sqlite3'; import { warn } from '../logger.js'; +let _cachedRepoRoot = undefined; // undefined = not computed, null = not a git repo + +/** + * Return the git worktree/repo root for the given directory (or cwd). + * Uses `git rev-parse --show-toplevel` which returns the correct root + * for both regular repos and git worktrees. + * Results are cached per-process when called without arguments. + * @param {string} [fromDir] - Directory to resolve from (defaults to cwd) + * @returns {string | null} Absolute path to repo root, or null if not in a git repo + */ +export function findRepoRoot(fromDir) { + const dir = fromDir || process.cwd(); + if (!fromDir && _cachedRepoRoot !== undefined) return _cachedRepoRoot; + let root = null; + try { + root = path.resolve( + execFileSync('git', ['rev-parse', '--show-toplevel'], { + cwd: dir, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(), + ); + } catch { + root = null; + } + if (!fromDir) _cachedRepoRoot = root; + return root; +} + +/** Reset the cached repo root (for testing). */ +export function _resetRepoRootCache() { + _cachedRepoRoot = undefined; +} + function isProcessAlive(pid) { try { process.kill(pid, 0); @@ -61,15 +96,18 @@ export function closeDb(db) { export function findDbPath(customPath) { if (customPath) return path.resolve(customPath); + const ceiling = findRepoRoot(); let dir = process.cwd(); while (true) { const candidate = path.join(dir, '.codegraph', 'graph.db'); if (fs.existsSync(candidate)) return candidate; + if (ceiling && path.resolve(dir) === ceiling) break; const parent = path.dirname(dir); if (parent === dir) break; dir = parent; } - return path.join(process.cwd(), '.codegraph', 'graph.db'); + const base = ceiling || process.cwd(); + return path.join(base, '.codegraph', 'graph.db'); } /** diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 10fcbcde..ed9e292d 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -8,15 +8,14 @@ import path from 'node:path'; import Database from 'better-sqlite3'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; import { + _resetRepoRootCache, closeDb, findDbPath, - getBuildMeta, - initSchema, - MIGRATIONS, + findRepoRoot, openDb, openReadonlyOrFail, - setBuildMeta, -} from '../../src/db.js'; +} from '../../src/db/connection.js'; +import { getBuildMeta, initSchema, MIGRATIONS, setBuildMeta } from '../../src/db.js'; let tmpDir; @@ -131,11 +130,13 @@ describe('findDbPath', () => { const origCwd = process.cwd; process.cwd = () => deepDir; try { + _resetRepoRootCache(); const result = findDbPath(); expect(result).toContain('.codegraph'); expect(result).toContain('graph.db'); } finally { process.cwd = origCwd; + _resetRepoRootCache(); } }); @@ -144,11 +145,13 @@ describe('findDbPath', () => { const origCwd = process.cwd; process.cwd = () => emptyDir; try { + _resetRepoRootCache(); const result = findDbPath(); expect(result).toContain('.codegraph'); expect(result).toContain('graph.db'); } finally { process.cwd = origCwd; + _resetRepoRootCache(); } }); }); @@ -194,6 +197,114 @@ describe('build_meta', () => { }); }); +describe('findRepoRoot', () => { + beforeEach(() => { + _resetRepoRootCache(); + }); + + afterEach(() => { + _resetRepoRootCache(); + }); + + it('returns normalized git toplevel for the current repo', () => { + _resetRepoRootCache(); + const root = findRepoRoot(); + expect(root).toBeTruthy(); + expect(path.isAbsolute(root)).toBe(true); + // Should contain a .git entry at the root + expect(fs.existsSync(path.join(root, '.git'))).toBe(true); + }); + + it('returns null when not in a git repo', () => { + const root = findRepoRoot(os.tmpdir()); + // os.tmpdir() is typically not a git repo; if it is, skip gracefully + if (root === null) { + expect(root).toBeNull(); + } + }); + + it('caches results when called without arguments', () => { + _resetRepoRootCache(); + const first = findRepoRoot(); + const second = findRepoRoot(); + expect(first).toBe(second); + }); + + it('does not use cache when called with explicit dir', () => { + _resetRepoRootCache(); + const fromCwd = findRepoRoot(); + // Calling with an explicit dir should still work (not use cwd cache) + const fromExplicit = findRepoRoot(process.cwd()); + expect(fromExplicit).toBe(fromCwd); + }); +}); + +describe('findDbPath with git ceiling', () => { + let outerDir; + let innerDir; + + beforeAll(() => { + // Simulate a worktree-inside-repo layout: + // outerDir/.codegraph/graph.db (parent repo DB — should NOT be found) + // outerDir/worktree/ (simulated worktree root) + // outerDir/worktree/sub/ (cwd inside worktree) + outerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-ceiling-')); + innerDir = path.join(outerDir, 'worktree', 'sub'); + fs.mkdirSync(path.join(outerDir, '.codegraph'), { recursive: true }); + fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); + fs.mkdirSync(innerDir, { recursive: true }); + }); + + afterAll(() => { + fs.rmSync(outerDir, { recursive: true, force: true }); + }); + + afterEach(() => { + _resetRepoRootCache(); + }); + + it('stops walking at git ceiling and does not find parent DB', () => { + const origCwd = process.cwd; + // Mock cwd to be inside the inner "worktree" + process.cwd = () => innerDir; + // Mock findRepoRoot to return the worktree root (one level up from innerDir) + const worktreeRoot = path.join(outerDir, 'worktree'); + const origFindRepoRoot = findRepoRoot; + + // We can't easily mock findRepoRoot since findDbPath calls it internally, + // but we can test the behavior by creating a DB at the worktree level + fs.mkdirSync(path.join(worktreeRoot, '.codegraph'), { recursive: true }); + fs.writeFileSync(path.join(worktreeRoot, '.codegraph', 'graph.db'), ''); + + try { + _resetRepoRootCache(); + const result = findDbPath(); + // Should find the worktree DB, not the outer one + expect(result).toContain('worktree'); + expect(result).toContain('.codegraph'); + } finally { + process.cwd = origCwd; + // Clean up the worktree DB + fs.rmSync(path.join(worktreeRoot, '.codegraph'), { recursive: true, force: true }); + } + }); + + it('falls back gracefully when not in a git repo', () => { + const emptyDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-nogit-')); + const origCwd = process.cwd; + process.cwd = () => emptyDir; + _resetRepoRootCache(); + try { + const result = findDbPath(); + // Should return default path at cwd since there's no git ceiling + expect(result).toBe(path.join(emptyDir, '.codegraph', 'graph.db')); + } finally { + process.cwd = origCwd; + fs.rmSync(emptyDir, { recursive: true, force: true }); + } + }); +}); + describe('openReadonlyOrFail', () => { it('exits with error when DB does not exist', () => { const exitSpy = vi.spyOn(process, 'exit').mockImplementation(() => { From deac23ef110f1d73ec4b94f06624d6ac8b556557 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 02:23:01 -0600 Subject: [PATCH 02/28] =?UTF-8?q?fix:=20address=20review=20=E2=80=94=20rea?= =?UTF-8?q?l=20git=20ceiling=20test,=20debug=20log,=20robust=20non-git=20t?= =?UTF-8?q?est?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Ceiling test now uses `git init` to create a real git repo boundary, and verifies the outer DB is NOT found (without the ceiling fix it would be). Added separate test for finding DB within the boundary. - Non-git test uses a fresh mkdtemp dir instead of os.tmpdir(). - Added debug log when ceiling stops the walk-up. - Removed unused `origFindRepoRoot` variable. --- src/db/connection.js | 7 +++++-- tests/unit/db.test.js | 45 ++++++++++++++++++++++++++++--------------- 2 files changed, 35 insertions(+), 17 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index 1961c3a8..b0e589ef 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import path from 'node:path'; import Database from 'better-sqlite3'; -import { warn } from '../logger.js'; +import { debug, warn } from '../logger.js'; let _cachedRepoRoot = undefined; // undefined = not computed, null = not a git repo @@ -101,7 +101,10 @@ export function findDbPath(customPath) { while (true) { const candidate = path.join(dir, '.codegraph', 'graph.db'); if (fs.existsSync(candidate)) return candidate; - if (ceiling && path.resolve(dir) === ceiling) break; + if (ceiling && path.resolve(dir) === ceiling) { + debug(`findDbPath: stopped at git ceiling ${ceiling}`); + break; + } const parent = path.dirname(dir); if (parent === dir) break; dir = parent; diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index ed9e292d..7941fecc 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -2,6 +2,7 @@ * Unit tests for src/db.js — build_meta helpers included */ +import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -216,10 +217,13 @@ describe('findRepoRoot', () => { }); it('returns null when not in a git repo', () => { - const root = findRepoRoot(os.tmpdir()); - // os.tmpdir() is typically not a git repo; if it is, skip gracefully - if (root === null) { + // Create a fresh temp dir that is guaranteed not to be inside a git repo + const noGitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-nogit-root-')); + try { + const root = findRepoRoot(noGitDir); expect(root).toBeNull(); + } finally { + fs.rmSync(noGitDir, { recursive: true, force: true }); } }); @@ -241,18 +245,22 @@ describe('findRepoRoot', () => { describe('findDbPath with git ceiling', () => { let outerDir; + let worktreeRoot; let innerDir; beforeAll(() => { // Simulate a worktree-inside-repo layout: // outerDir/.codegraph/graph.db (parent repo DB — should NOT be found) - // outerDir/worktree/ (simulated worktree root) + // outerDir/worktree/ (git init here — acts as ceiling) // outerDir/worktree/sub/ (cwd inside worktree) outerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-ceiling-')); - innerDir = path.join(outerDir, 'worktree', 'sub'); + worktreeRoot = path.join(outerDir, 'worktree'); + innerDir = path.join(worktreeRoot, 'sub'); fs.mkdirSync(path.join(outerDir, '.codegraph'), { recursive: true }); fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); fs.mkdirSync(innerDir, { recursive: true }); + // Initialize a real git repo at the worktree root so findRepoRoot returns it + execFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); }); afterAll(() => { @@ -263,28 +271,35 @@ describe('findDbPath with git ceiling', () => { _resetRepoRootCache(); }); - it('stops walking at git ceiling and does not find parent DB', () => { + it('stops at git ceiling and does not find parent DB', () => { + // No DB inside the worktree — the only DB is in outerDir (beyond the ceiling). + // Without the ceiling fix, findDbPath would walk up and find outerDir's DB. const origCwd = process.cwd; - // Mock cwd to be inside the inner "worktree" process.cwd = () => innerDir; - // Mock findRepoRoot to return the worktree root (one level up from innerDir) - const worktreeRoot = path.join(outerDir, 'worktree'); - const origFindRepoRoot = findRepoRoot; + try { + _resetRepoRootCache(); + const result = findDbPath(); + // Should return default path at the ceiling root, NOT the outer DB + expect(result).toBe(path.join(worktreeRoot, '.codegraph', 'graph.db')); + expect(result).not.toContain(path.basename(outerDir) + path.sep + '.codegraph'); + } finally { + process.cwd = origCwd; + } + }); - // We can't easily mock findRepoRoot since findDbPath calls it internally, - // but we can test the behavior by creating a DB at the worktree level + it('finds DB within the ceiling boundary', () => { + // Create a DB inside the worktree — should be found normally fs.mkdirSync(path.join(worktreeRoot, '.codegraph'), { recursive: true }); fs.writeFileSync(path.join(worktreeRoot, '.codegraph', 'graph.db'), ''); - + const origCwd = process.cwd; + process.cwd = () => innerDir; try { _resetRepoRootCache(); const result = findDbPath(); - // Should find the worktree DB, not the outer one expect(result).toContain('worktree'); expect(result).toContain('.codegraph'); } finally { process.cwd = origCwd; - // Clean up the worktree DB fs.rmSync(path.join(worktreeRoot, '.codegraph'), { recursive: true, force: true }); } }); From 83efde5f9cfb56ac391ad437969dbe9826578b3f Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 02:34:27 -0600 Subject: [PATCH 03/28] test: strengthen findRepoRoot and ceiling tests per review feedback - Mock execFileSync via vi.mock to verify caching calls exactly once and cache bypass calls twice (not just comparing return values) - Mock execFileSync to throw in "not in git repo" test so the null path is always exercised regardless of host environment - Rename "does not use cache" test to "bypasses cache" with spy assertion --- tests/unit/db.test.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 87165d94..a938c66f 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -2,12 +2,21 @@ * Unit tests for src/db.js — build_meta helpers included */ -import { execFileSync } from 'node:child_process'; +import { execFileSync as realExecFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import Database from 'better-sqlite3'; -import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; + +const execFileSyncSpy = vi.hoisted(() => vi.fn()); + +vi.mock('node:child_process', async (importOriginal) => { + const mod = await importOriginal(); + execFileSyncSpy.mockImplementation(mod.execFileSync); + return { ...mod, execFileSync: execFileSyncSpy }; +}); + import { _resetRepoRootCache, closeDb, @@ -217,29 +226,30 @@ describe('findRepoRoot', () => { }); it('returns null when not in a git repo', () => { - // Create a fresh temp dir that is guaranteed not to be inside a git repo - const noGitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-nogit-root-')); - try { - const root = findRepoRoot(noGitDir); - expect(root).toBeNull(); - } finally { - fs.rmSync(noGitDir, { recursive: true, force: true }); - } + execFileSyncSpy.mockImplementationOnce(() => { + throw new Error('not a git repo'); + }); + const root = findRepoRoot(os.tmpdir()); + expect(root).toBeNull(); }); it('caches results when called without arguments', () => { _resetRepoRootCache(); + execFileSyncSpy.mockClear(); const first = findRepoRoot(); const second = findRepoRoot(); expect(first).toBe(second); + expect(execFileSyncSpy).toHaveBeenCalledTimes(1); }); - it('does not use cache when called with explicit dir', () => { + it('bypasses cache when called with explicit dir', () => { _resetRepoRootCache(); + execFileSyncSpy.mockClear(); const fromCwd = findRepoRoot(); - // Calling with an explicit dir should still work (not use cwd cache) const fromExplicit = findRepoRoot(process.cwd()); expect(fromExplicit).toBe(fromCwd); + // First call populates cache, second call with explicit dir must call again + expect(execFileSyncSpy).toHaveBeenCalledTimes(2); }); }); @@ -260,7 +270,7 @@ describe('findDbPath with git ceiling', () => { fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); fs.mkdirSync(innerDir, { recursive: true }); // Initialize a real git repo at the worktree root so findRepoRoot returns it - execFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); + realExecFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); }); afterAll(() => { From ccb8bd554a166a04adf3c51c7c2d20ff2b2824b2 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 02:34:46 -0600 Subject: [PATCH 04/28] Revert "test: strengthen findRepoRoot and ceiling tests per review feedback" This reverts commit 83efde5f9cfb56ac391ad437969dbe9826578b3f. --- tests/unit/db.test.js | 36 +++++++++++++----------------------- 1 file changed, 13 insertions(+), 23 deletions(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index a938c66f..87165d94 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -2,21 +2,12 @@ * Unit tests for src/db.js — build_meta helpers included */ -import { execFileSync as realExecFileSync } from 'node:child_process'; +import { execFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import Database from 'better-sqlite3'; -import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; - -const execFileSyncSpy = vi.hoisted(() => vi.fn()); - -vi.mock('node:child_process', async (importOriginal) => { - const mod = await importOriginal(); - execFileSyncSpy.mockImplementation(mod.execFileSync); - return { ...mod, execFileSync: execFileSyncSpy }; -}); - +import { afterAll, beforeAll, describe, expect, it } from 'vitest'; import { _resetRepoRootCache, closeDb, @@ -226,30 +217,29 @@ describe('findRepoRoot', () => { }); it('returns null when not in a git repo', () => { - execFileSyncSpy.mockImplementationOnce(() => { - throw new Error('not a git repo'); - }); - const root = findRepoRoot(os.tmpdir()); - expect(root).toBeNull(); + // Create a fresh temp dir that is guaranteed not to be inside a git repo + const noGitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-nogit-root-')); + try { + const root = findRepoRoot(noGitDir); + expect(root).toBeNull(); + } finally { + fs.rmSync(noGitDir, { recursive: true, force: true }); + } }); it('caches results when called without arguments', () => { _resetRepoRootCache(); - execFileSyncSpy.mockClear(); const first = findRepoRoot(); const second = findRepoRoot(); expect(first).toBe(second); - expect(execFileSyncSpy).toHaveBeenCalledTimes(1); }); - it('bypasses cache when called with explicit dir', () => { + it('does not use cache when called with explicit dir', () => { _resetRepoRootCache(); - execFileSyncSpy.mockClear(); const fromCwd = findRepoRoot(); + // Calling with an explicit dir should still work (not use cwd cache) const fromExplicit = findRepoRoot(process.cwd()); expect(fromExplicit).toBe(fromCwd); - // First call populates cache, second call with explicit dir must call again - expect(execFileSyncSpy).toHaveBeenCalledTimes(2); }); }); @@ -270,7 +260,7 @@ describe('findDbPath with git ceiling', () => { fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); fs.mkdirSync(innerDir, { recursive: true }); // Initialize a real git repo at the worktree root so findRepoRoot returns it - realExecFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); + execFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); }); afterAll(() => { From 73c51a45b4a84e40d35e9ad65ff78b45f02630f8 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 02:35:10 -0600 Subject: [PATCH 05/28] Reapply "test: strengthen findRepoRoot and ceiling tests per review feedback" This reverts commit ccb8bd554a166a04adf3c51c7c2d20ff2b2824b2. --- tests/unit/db.test.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 87165d94..a938c66f 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -2,12 +2,21 @@ * Unit tests for src/db.js — build_meta helpers included */ -import { execFileSync } from 'node:child_process'; +import { execFileSync as realExecFileSync } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import Database from 'better-sqlite3'; -import { afterAll, beforeAll, describe, expect, it } from 'vitest'; +import { afterAll, beforeAll, describe, expect, it, vi } from 'vitest'; + +const execFileSyncSpy = vi.hoisted(() => vi.fn()); + +vi.mock('node:child_process', async (importOriginal) => { + const mod = await importOriginal(); + execFileSyncSpy.mockImplementation(mod.execFileSync); + return { ...mod, execFileSync: execFileSyncSpy }; +}); + import { _resetRepoRootCache, closeDb, @@ -217,29 +226,30 @@ describe('findRepoRoot', () => { }); it('returns null when not in a git repo', () => { - // Create a fresh temp dir that is guaranteed not to be inside a git repo - const noGitDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-nogit-root-')); - try { - const root = findRepoRoot(noGitDir); - expect(root).toBeNull(); - } finally { - fs.rmSync(noGitDir, { recursive: true, force: true }); - } + execFileSyncSpy.mockImplementationOnce(() => { + throw new Error('not a git repo'); + }); + const root = findRepoRoot(os.tmpdir()); + expect(root).toBeNull(); }); it('caches results when called without arguments', () => { _resetRepoRootCache(); + execFileSyncSpy.mockClear(); const first = findRepoRoot(); const second = findRepoRoot(); expect(first).toBe(second); + expect(execFileSyncSpy).toHaveBeenCalledTimes(1); }); - it('does not use cache when called with explicit dir', () => { + it('bypasses cache when called with explicit dir', () => { _resetRepoRootCache(); + execFileSyncSpy.mockClear(); const fromCwd = findRepoRoot(); - // Calling with an explicit dir should still work (not use cwd cache) const fromExplicit = findRepoRoot(process.cwd()); expect(fromExplicit).toBe(fromCwd); + // First call populates cache, second call with explicit dir must call again + expect(execFileSyncSpy).toHaveBeenCalledTimes(2); }); }); @@ -260,7 +270,7 @@ describe('findDbPath with git ceiling', () => { fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); fs.mkdirSync(innerDir, { recursive: true }); // Initialize a real git repo at the worktree root so findRepoRoot returns it - execFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); + realExecFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); }); afterAll(() => { From e3c1d4a139d2da1327b9f92a9c3e80810d809aa2 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:01:26 -0600 Subject: [PATCH 06/28] =?UTF-8?q?fix:=20address=20Greptile=20review=20?= =?UTF-8?q?=E2=80=94=20flaky=20non-git=20test=20and=20misleading=20import?= =?UTF-8?q?=20name?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Mock execFileSync to throw in "falls back gracefully when not in a git repo" test, preventing flakiness when os.tmpdir() is inside a git repo - Rename realExecFileSync → execFileSyncForSetup with comment explaining it resolves to the spy due to vi.mock hoisting --- tests/unit/db.test.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index a938c66f..317e9fe9 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -2,7 +2,9 @@ * Unit tests for src/db.js — build_meta helpers included */ -import { execFileSync as realExecFileSync } from 'node:child_process'; +// Note: due to vi.mock hoisting, this resolves to the spy (which delegates +// to the real impl by default). Safe for setup calls before mockImplementationOnce. +import { execFileSync as execFileSyncForSetup } from 'node:child_process'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; @@ -270,7 +272,7 @@ describe('findDbPath with git ceiling', () => { fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); fs.mkdirSync(innerDir, { recursive: true }); // Initialize a real git repo at the worktree root so findRepoRoot returns it - realExecFileSync('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); + execFileSyncForSetup('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); }); afterAll(() => { @@ -319,6 +321,9 @@ describe('findDbPath with git ceiling', () => { const origCwd = process.cwd; process.cwd = () => emptyDir; _resetRepoRootCache(); + execFileSyncSpy.mockImplementationOnce(() => { + throw new Error('not a git repo'); + }); try { const result = findDbPath(); // Should return default path at cwd since there's no git ceiling From 20013d40a993965bb205b331f394bdc6ef17bb06 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:08:27 -0600 Subject: [PATCH 07/28] fix: resolve symlinks in findDbPath to fix ceiling check on macOS On macOS, os.tmpdir() returns /var/folders/... but git rev-parse returns /private/var/folders/... (resolved symlink). The ceiling comparison failed because the paths didn't match. Use fs.realpathSync on cwd to normalize symlinks before comparing against the ceiling. Impact: 1 functions changed, 1 affected --- src/db/connection.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/db/connection.js b/src/db/connection.js index f4bd1cf8..6167c38a 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -98,7 +98,9 @@ export function closeDb(db) { export function findDbPath(customPath) { if (customPath) return path.resolve(customPath); const ceiling = findRepoRoot(); - let dir = process.cwd(); + // Resolve symlinks (e.g. macOS /var → /private/var) so dir matches ceiling from git + let dir; + try { dir = fs.realpathSync(process.cwd()); } catch { dir = process.cwd(); } while (true) { const candidate = path.join(dir, '.codegraph', 'graph.db'); if (fs.existsSync(candidate)) return candidate; From 7c877d71ed2b0b1237aa7971b6c17a9f1ff64db5 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:08:47 -0600 Subject: [PATCH 08/28] style: format connection.js try/catch block Impact: 1 functions changed, 1 affected --- src/db/connection.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index 6167c38a..dce39b2f 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -5,7 +5,7 @@ import Database from 'better-sqlite3'; import { DbError } from '../errors.js'; import { debug, warn } from '../logger.js'; -let _cachedRepoRoot = undefined; // undefined = not computed, null = not a git repo +let _cachedRepoRoot; // undefined = not computed, null = not a git repo /** * Return the git worktree/repo root for the given directory (or cwd). @@ -100,7 +100,11 @@ export function findDbPath(customPath) { const ceiling = findRepoRoot(); // Resolve symlinks (e.g. macOS /var → /private/var) so dir matches ceiling from git let dir; - try { dir = fs.realpathSync(process.cwd()); } catch { dir = process.cwd(); } + try { + dir = fs.realpathSync(process.cwd()); + } catch { + dir = process.cwd(); + } while (true) { const candidate = path.join(dir, '.codegraph', 'graph.db'); if (fs.existsSync(candidate)) return candidate; From 0d0fdd94d4441f54545e5d20f190a4f061325bb9 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:21:13 -0600 Subject: [PATCH 09/28] test: harden findDbPath fallback test to mock execFileSync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'returns default path when no DB found' test didn't control the git ceiling — if tmpDir was inside a git repo, findRepoRoot() would return a non-null ceiling and the default path would differ from emptyDir. Mock execFileSync to throw so the cwd fallback is always exercised. --- tests/unit/db.test.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index be6db766..14ff3042 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -159,11 +159,13 @@ describe('findDbPath', () => { const emptyDir = fs.mkdtempSync(path.join(tmpDir, 'empty-')); const origCwd = process.cwd; process.cwd = () => emptyDir; + _resetRepoRootCache(); + execFileSyncSpy.mockImplementationOnce(() => { + throw new Error('not a git repo'); + }); try { - _resetRepoRootCache(); const result = findDbPath(); - expect(result).toContain('.codegraph'); - expect(result).toContain('graph.db'); + expect(result).toBe(path.join(emptyDir, '.codegraph', 'graph.db')); } finally { process.cwd = origCwd; _resetRepoRootCache(); From 2fa9dfc4bad8511ceb4150f7e48159632f9617cd Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:28:36 -0600 Subject: [PATCH 10/28] fix: normalize findRepoRoot paths with realpathSync for cross-platform ceiling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS, os.tmpdir() returns /var/... but git resolves symlinks to /private/var/..., and on Windows, 8.3 short names (RUNNER~1) differ from long names (runneradmin). findDbPath normalizes dir via fs.realpathSync but findRepoRoot only used path.resolve, causing the ceiling comparison to fail — the walk crossed the worktree boundary. Fix findRepoRoot to use fs.realpathSync on git output, and resolve test paths after directory creation so assertions match. Impact: 1 functions changed, 77 affected --- src/db/connection.js | 20 +++++++++++++------- tests/unit/db.test.js | 8 ++++++-- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index 16cb6777..fcff8e20 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -20,13 +20,19 @@ export function findRepoRoot(fromDir) { if (!fromDir && _cachedRepoRoot !== undefined) return _cachedRepoRoot; let root = null; try { - root = path.resolve( - execFileSync('git', ['rev-parse', '--show-toplevel'], { - cwd: dir, - encoding: 'utf-8', - stdio: ['pipe', 'pipe', 'pipe'], - }).trim(), - ); + const raw = execFileSync('git', ['rev-parse', '--show-toplevel'], { + cwd: dir, + encoding: 'utf-8', + stdio: ['pipe', 'pipe', 'pipe'], + }).trim(); + // Use realpathSync to resolve symlinks (macOS /var → /private/var) and + // 8.3 short names (Windows RUNNER~1 → runneradmin) so the ceiling path + // matches the realpathSync'd dir in findDbPath. + try { + root = fs.realpathSync(raw); + } catch { + root = path.resolve(raw); + } } catch { root = null; } diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 14ff3042..33520a7e 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -272,12 +272,16 @@ describe('findDbPath with git ceiling', () => { // outerDir/worktree/sub/ (cwd inside worktree) outerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'cg-ceiling-')); worktreeRoot = path.join(outerDir, 'worktree'); - innerDir = path.join(worktreeRoot, 'sub'); fs.mkdirSync(path.join(outerDir, '.codegraph'), { recursive: true }); fs.writeFileSync(path.join(outerDir, '.codegraph', 'graph.db'), ''); - fs.mkdirSync(innerDir, { recursive: true }); + fs.mkdirSync(path.join(worktreeRoot, 'sub'), { recursive: true }); // Initialize a real git repo at the worktree root so findRepoRoot returns it execFileSyncForSetup('git', ['init'], { cwd: worktreeRoot, stdio: 'pipe' }); + // Resolve symlinks (macOS /var → /private/var) and 8.3 short names + // (Windows RUNNER~1 → runneradmin) so test paths match findRepoRoot output. + outerDir = fs.realpathSync(outerDir); + worktreeRoot = fs.realpathSync(worktreeRoot); + innerDir = path.join(worktreeRoot, 'sub'); }); afterAll(() => { From 651c1642bf754e46b47a96e03b90c4b4ac8d540d Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 03:45:50 -0600 Subject: [PATCH 11/28] fix: use stat-based path comparison for ceiling check on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git rev-parse resolves 8.3 short names (RUNNER~1 → runneradmin) but fs.realpathSync may not, causing the string comparison to fail and the walk to escape the git ceiling boundary. Replace string equality with isSameDirectory() that falls back to dev+ino comparison when paths differ textually but refer to the same directory. Also fix the test assertion to use findRepoRoot() for the expected ceiling path instead of the test's worktreeRoot, since git may resolve paths differently than realpathSync. Impact: 2 functions changed, 137 affected --- src/db/connection.js | 18 +++++++++++++++++- tests/unit/db.test.js | 6 +++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index fcff8e20..ff57a720 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -85,6 +85,22 @@ function releaseAdvisoryLock(lockPath) { } } +/** + * Check if two paths refer to the same directory. + * Handles Windows 8.3 short names (RUNNER~1 vs runneradmin) and macOS + * symlinks (/tmp vs /private/tmp) where string comparison fails. + */ +function isSameDirectory(a, b) { + if (path.resolve(a) === path.resolve(b)) return true; + try { + const sa = fs.statSync(a); + const sb = fs.statSync(b); + return sa.dev === sb.dev && sa.ino === sb.ino; + } catch { + return false; + } +} + export function openDb(dbPath) { const dir = path.dirname(dbPath); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); @@ -114,7 +130,7 @@ export function findDbPath(customPath) { while (true) { const candidate = path.join(dir, '.codegraph', 'graph.db'); if (fs.existsSync(candidate)) return candidate; - if (ceiling && path.resolve(dir) === ceiling) { + if (ceiling && isSameDirectory(dir, ceiling)) { debug(`findDbPath: stopped at git ceiling ${ceiling}`); break; } diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 33520a7e..c7bef2b3 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -299,9 +299,13 @@ describe('findDbPath with git ceiling', () => { process.cwd = () => innerDir; try { _resetRepoRootCache(); + // Use findRepoRoot() for the expected ceiling — git may resolve 8.3 short + // names (Windows RUNNER~1 → runneradmin) or symlinks (macOS /tmp → /private/tmp) + // differently than fs.realpathSync on the test's worktreeRoot. + const ceiling = findRepoRoot(); const result = findDbPath(); // Should return default path at the ceiling root, NOT the outer DB - expect(result).toBe(path.join(worktreeRoot, '.codegraph', 'graph.db')); + expect(result).toBe(path.join(ceiling, '.codegraph', 'graph.db')); expect(result).not.toContain(`${path.basename(outerDir)}${path.sep}.codegraph`); } finally { process.cwd = origCwd; From 5dc707a2dbe89f02e519e0dff813baaf49e5157d Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 04:06:14 -0600 Subject: [PATCH 12/28] fix: remove test-only _resetRepoRootCache from public barrel export --- src/db/index.js | 1 - tests/unit/db.test.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/db/index.js b/src/db/index.js index aa6fcdb8..882b5f89 100644 --- a/src/db/index.js +++ b/src/db/index.js @@ -1,6 +1,5 @@ // Barrel re-export — keeps all existing `import { ... } from '…/db/index.js'` working. export { - _resetRepoRootCache, closeDb, findDbPath, findRepoRoot, diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index c7bef2b3..638dfa97 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -19,8 +19,8 @@ vi.mock('node:child_process', async (importOriginal) => { return { ...mod, execFileSync: execFileSyncSpy }; }); +import { _resetRepoRootCache } from '../../src/db/connection.js'; import { - _resetRepoRootCache, closeDb, findDbPath, findRepoRoot, From b87a12b01f469937bb3f8edbf7975093c6f10a04 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 04:24:59 -0600 Subject: [PATCH 13/28] fix: tighten test assertions and key repo root cache on cwd Impact: 2 functions changed, 2 affected --- src/db/connection.js | 13 +++++++++++-- tests/unit/db.test.js | 4 ++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index ff57a720..9bd13d1f 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -6,18 +6,23 @@ import { debug, warn } from '../infrastructure/logger.js'; import { DbError } from '../shared/errors.js'; let _cachedRepoRoot; // undefined = not computed, null = not a git repo +let _cachedRepoRootCwd; // cwd at the time the cache was populated /** * Return the git worktree/repo root for the given directory (or cwd). * Uses `git rev-parse --show-toplevel` which returns the correct root * for both regular repos and git worktrees. * Results are cached per-process when called without arguments. + * The cache is keyed on cwd so it invalidates if the working directory changes + * (e.g. MCP server serving multiple sessions). * @param {string} [fromDir] - Directory to resolve from (defaults to cwd) * @returns {string | null} Absolute path to repo root, or null if not in a git repo */ export function findRepoRoot(fromDir) { const dir = fromDir || process.cwd(); - if (!fromDir && _cachedRepoRoot !== undefined) return _cachedRepoRoot; + if (!fromDir && _cachedRepoRoot !== undefined && _cachedRepoRootCwd === dir) { + return _cachedRepoRoot; + } let root = null; try { const raw = execFileSync('git', ['rev-parse', '--show-toplevel'], { @@ -36,13 +41,17 @@ export function findRepoRoot(fromDir) { } catch { root = null; } - if (!fromDir) _cachedRepoRoot = root; + if (!fromDir) { + _cachedRepoRoot = root; + _cachedRepoRootCwd = dir; + } return root; } /** Reset the cached repo root (for testing). */ export function _resetRepoRootCache() { _cachedRepoRoot = undefined; + _cachedRepoRootCwd = undefined; } function isProcessAlive(pid) { diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 638dfa97..d733cc48 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -320,9 +320,9 @@ describe('findDbPath with git ceiling', () => { process.cwd = () => innerDir; try { _resetRepoRootCache(); + const ceiling = findRepoRoot(); const result = findDbPath(); - expect(result).toContain('worktree'); - expect(result).toContain('.codegraph'); + expect(result).toBe(path.join(ceiling, '.codegraph', 'graph.db')); } finally { process.cwd = origCwd; fs.rmSync(path.join(worktreeRoot, '.codegraph'), { recursive: true, force: true }); From 1301b9f7ceacfdab046bd996b6e9240df7c2d26f Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 04:33:33 -0600 Subject: [PATCH 14/28] fix: normalize ceiling path in test to handle Windows 8.3 short names --- tests/unit/db.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index d733cc48..cc4d9354 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -320,7 +320,7 @@ describe('findDbPath with git ceiling', () => { process.cwd = () => innerDir; try { _resetRepoRootCache(); - const ceiling = findRepoRoot(); + const ceiling = fs.realpathSync(findRepoRoot()); const result = findDbPath(); expect(result).toBe(path.join(ceiling, '.codegraph', 'graph.db')); } finally { From 5a77db18d4dfe9d6971eaed688c9351808d860a7 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 04:44:00 -0600 Subject: [PATCH 15/28] fix: normalize ceiling path with realpathSync to handle Windows 8.3 short names Impact: 1 functions changed, 1 affected --- src/db/connection.js | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/db/connection.js b/src/db/connection.js index 9bd13d1f..ca775f3f 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -128,7 +128,21 @@ export function closeDb(db) { export function findDbPath(customPath) { if (customPath) return path.resolve(customPath); - const ceiling = findRepoRoot(); + const rawCeiling = findRepoRoot(); + // Normalize ceiling with realpathSync to resolve 8.3 short names (Windows + // RUNNER~1 → runneradmin) and symlinks (macOS /var → /private/var). + // findRepoRoot already applies realpathSync internally, but the git output + // may still contain short names on some Windows CI environments. + let ceiling; + if (rawCeiling) { + try { + ceiling = fs.realpathSync(rawCeiling); + } catch { + ceiling = rawCeiling; + } + } else { + ceiling = null; + } // Resolve symlinks (e.g. macOS /var → /private/var) so dir matches ceiling from git let dir; try { From 620128b5c00c3c1c27779170b218dff7116a6fcf Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 04:54:23 -0600 Subject: [PATCH 16/28] fix: normalize Windows 8.3 short paths in ceiling boundary test On Windows CI, fs.realpathSync(process.cwd()) and git rev-parse --show-toplevel can resolve 8.3 short names (RUNNER~1) differently than long names (runneradmin). Apply realpathSync to both sides of the assertion so the comparison is path-equivalent. --- tests/unit/db.test.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index cc4d9354..524c61d9 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -322,7 +322,10 @@ describe('findDbPath with git ceiling', () => { _resetRepoRootCache(); const ceiling = fs.realpathSync(findRepoRoot()); const result = findDbPath(); - expect(result).toBe(path.join(ceiling, '.codegraph', 'graph.db')); + // Use realpathSync on both sides to normalize Windows 8.3 short names + // (e.g. RUNNER~1 vs runneradmin) that path.dirname walking may preserve + const expected = path.join(ceiling, '.codegraph', 'graph.db'); + expect(fs.realpathSync(result)).toBe(fs.realpathSync(expected)); } finally { process.cwd = origCwd; fs.rmSync(path.join(worktreeRoot, '.codegraph'), { recursive: true, force: true }); From e9e4de553a598b8cdf44ff07ef5adb569f27fa6f Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:35:09 -0600 Subject: [PATCH 17/28] docs: add Titan Paradigm Claude Code skills for autonomous codebase cleanup Five skills implementing the full Titan Paradigm pipeline: - /titan-recon: graph build, embeddings, health baseline, domain mapping - /titan-gauntlet: 4-pillar audit (17 rules) with batch processing and resume - /titan-sync: dependency cluster analysis and ordered execution planning - /titan-gate: staged change validation with auto-rollback on failure - /titan-reset: escape hatch to clean all artifacts and snapshots Skills use artifact-passing between phases to manage context window limits. Published as docs/examples for users to copy, with live copies in .claude/skills/. Updated titan-paradigm.md with skill references and new backlog items. --- .claude/skills/titan-gate/SKILL.md | 262 +++++++++++++ .claude/skills/titan-gauntlet/SKILL.md | 358 ++++++++++++++++++ .claude/skills/titan-recon/SKILL.md | 316 ++++++++++++++++ .claude/skills/titan-reset/SKILL.md | 89 +++++ .claude/skills/titan-sync/SKILL.md | 233 ++++++++++++ docs/examples/claude-code-skills/README.md | 196 ++++++++++ .../claude-code-skills/titan-gate/SKILL.md | 262 +++++++++++++ .../titan-gauntlet/SKILL.md | 358 ++++++++++++++++++ .../claude-code-skills/titan-recon/SKILL.md | 316 ++++++++++++++++ .../claude-code-skills/titan-reset/SKILL.md | 89 +++++ .../claude-code-skills/titan-sync/SKILL.md | 233 ++++++++++++ docs/use-cases/titan-paradigm.md | 100 ++++- 12 files changed, 2811 insertions(+), 1 deletion(-) create mode 100644 .claude/skills/titan-gate/SKILL.md create mode 100644 .claude/skills/titan-gauntlet/SKILL.md create mode 100644 .claude/skills/titan-recon/SKILL.md create mode 100644 .claude/skills/titan-reset/SKILL.md create mode 100644 .claude/skills/titan-sync/SKILL.md create mode 100644 docs/examples/claude-code-skills/README.md create mode 100644 docs/examples/claude-code-skills/titan-gate/SKILL.md create mode 100644 docs/examples/claude-code-skills/titan-gauntlet/SKILL.md create mode 100644 docs/examples/claude-code-skills/titan-recon/SKILL.md create mode 100644 docs/examples/claude-code-skills/titan-reset/SKILL.md create mode 100644 docs/examples/claude-code-skills/titan-sync/SKILL.md diff --git a/.claude/skills/titan-gate/SKILL.md b/.claude/skills/titan-gate/SKILL.md new file mode 100644 index 00000000..c1d7c359 --- /dev/null +++ b/.claude/skills/titan-gate/SKILL.md @@ -0,0 +1,262 @@ +--- +name: titan-gate +description: Validate staged changes — codegraph checks + project lint/build/test, auto-rollback on failure, pass/fail commit gate (Titan Paradigm Phase 4) +argument-hint: <--force to skip warnings> +allowed-tools: Bash, Read, Write, Edit, Grep +--- + +# Titan GATE — Change Validation & State Machine + +You are running the **GATE** phase (State Machine) of the Titan Paradigm. + +Your goal: validate staged changes against codegraph quality checks AND the project's own lint/build/test. Produce a clear PASS/WARN/FAIL verdict. Auto-rollback on failure. + +> **Context budget:** Lightweight — only checks staged changes. Should complete quickly. + +**Force mode:** If `$ARGUMENTS` contains `--force`, warnings are downgraded (failures still block). + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Staged changes?** + ```bash + git diff --cached --name-only + ``` + If nothing staged, stop: "Nothing staged. Use `git add` first." + +3. **Load state (optional).** Read `.codegraph/titan/titan-state.json` if it exists — use for thresholds, baseline comparison, and sync alignment. If missing or corrupt, proceed with defaults. + +--- + +## Step 1 — Structural validation (codegraph) + +Run the full change validation predicates in one call: + +```bash +codegraph check --staged --cycles --blast-radius 30 --boundaries -T --json +``` + +This checks: manifesto rules, new cycle introduction, blast radius threshold, and architecture boundary violations. Exit code 0 = pass, 1 = fail. + +Also run detailed impact analysis: + +```bash +codegraph diff-impact --staged -T --json +``` + +Extract: changed functions (count + names), direct callers affected, transitive blast radius, historically coupled files. + +--- + +## Step 2 — Cycle check + +```bash +codegraph cycles --json +``` + +Compare against RECON baseline (if `titan-state.json` exists): +- **New cycles?** → FAIL +- **Cycles resolved?** → Note as positive + +--- + +## Step 3 — Complexity delta + +For each changed file (from diff-impact): + +```bash +codegraph complexity --file --health -T --json +``` + +Check all metrics against thresholds: +- `cognitive` > 30 → FAIL +- `halstead.bugs` > 1.0 → FAIL (estimated defect) +- `mi` < 20 → FAIL +- Function moved from PASS → FAIL on any metric? → FAIL +- Function improved but still above threshold? → WARN + +--- + +## Step 4 — Lint, build, and test + +Detect project tools from `package.json`: + +```bash +node -e "const p=require('./package.json');console.log(JSON.stringify(Object.keys(p.scripts||{})))" +``` + +Run in order — stop on first failure: + +```bash +npm run lint 2>&1 || echo "LINT_FAILED" +``` + +```bash +npm run build 2>&1 || echo "BUILD_FAILED" +``` +(Skip if no `build` script.) + +```bash +npm test 2>&1 || echo "TEST_FAILED" +``` + +If any fail → overall verdict is FAIL → proceed to auto-rollback. + +--- + +## Step 5 — Branch structural diff + +```bash +codegraph branch-compare main HEAD -T --json +``` + +Cumulative structural impact of all changes on this branch (broader than `diff-impact --staged`). Detect cumulative drift. + +--- + +## Step 6 — Sync plan alignment + +If `.codegraph/titan/sync.json` exists: +- Are changed files part of the current execution phase? +- Are dependencies for these targets already completed? +- Skipping ahead in execution order? → WARN + +Advisory — prevents jumping ahead and creating conflicts. + +--- + +## Step 7 — Blast radius check + +From diff-impact results: +- Transitive blast radius > 30 → FAIL +- Transitive blast radius > 15 → WARN +- Historically coupled file NOT staged? → WARN ("consider also updating X") + +--- + +## Step 8 — Verdict and auto-rollback + +Aggregate all checks: + +| Verdict | Meaning | +|---------|---------| +| **PASS** | Safe to commit | +| **WARN** | Warnings only — commit at your discretion | +| **FAIL** | Failures present — auto-rollback triggered | + +### Auto-rollback on FAIL (build/test/lint failures only) + +1. **Restore graph** to the most recent snapshot: + ```bash + codegraph snapshot restore titan-batch- # or titan-baseline if no batch snapshot + ``` + Check `titan-state.json → snapshots.lastBatch` first; fall back to `snapshots.baseline`. + +2. **Unstage changes** (preserve in working tree): + ```bash + git reset HEAD + ``` + +3. **Rebuild graph** for current working tree state: + ```bash + codegraph build + ``` + +> "GATE FAIL: [reason]. Graph restored, changes unstaged but preserved. Fix and re-stage." + +For structural-only failures (Steps 1-3, 5-7), do NOT auto-rollback — report and let user decide. + +### Snapshot cleanup on pipeline completion + +When the full Titan pipeline is done (all SYNC phases complete, final GATE passes): + +```bash +codegraph snapshot delete titan-baseline +codegraph snapshot delete titan-batch- # if any remain +``` + +> "All Titan snapshots cleaned up. Codebase is in its final validated state." + +--- + +## Step 9 — Update state machine + +Append to `.codegraph/titan/gate-log.ndjson`: + +```json +{ + "timestamp": "", + "verdict": "PASS|WARN|FAIL", + "stagedFiles": ["file1.js"], + "changedFunctions": 3, + "blastRadius": 12, + "checks": { + "manifesto": "pass|fail", + "cycles": "pass|fail", + "complexity": "pass|warn|fail", + "lint": "pass|fail|skipped", + "build": "pass|fail|skipped", + "tests": "pass|fail|skipped", + "syncAlignment": "pass|warn|skipped", + "blastRadius": "pass|warn|fail" + }, + "rolledBack": false +} +``` + +Update `titan-state.json` (if exists): increment `progress.fixed`, update `fileAudits` for fixed files. + +--- + +## Step 10 — Report to user + +**PASS:** +``` +GATE PASS — safe to commit + Changed: 3 functions across 2 files + Blast radius: 12 transitive callers + Lint: pass | Build: pass | Tests: pass + Complexity: all within thresholds (worst: halstead.bugs 0.3) +``` + +**WARN:** +``` +GATE WARN — review before committing + Changed: 5 functions across 3 files + Warnings: + - utils.js historically co-changes with config.js (not staged) + - parseConfig MI improved 18 → 35 but still below 50 +``` + +**FAIL:** +``` +GATE FAIL — changes unstaged, graph restored + Failures: + - Tests: 2 suites failed + - New cycle: parseConfig → loadConfig → parseConfig + Fix issues, re-stage, re-run /titan-gate +``` + +--- + +## Rules + +- **Fast execution.** Only staged changes, not full codebase. +- **Always use `--json` and `-T`.** +- **Never auto-commit.** Verdict only — user decides. +- **Auto-rollback is gentle** — `git reset HEAD`, never `git checkout`. Work preserved. +- **Append to gate-log.ndjson** — the audit trail. +- **Force mode** downgrades WARN → PASS but cannot override FAIL. +- **Run the project's own lint/build/test** — codegraph checks are necessary but not sufficient. +- **Use the correct check flags:** `--cycles`, `--blast-radius `, `--boundaries`. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-gate/SKILL.md`. Adjust thresholds or rollback behavior after dogfooding. diff --git a/.claude/skills/titan-gauntlet/SKILL.md b/.claude/skills/titan-gauntlet/SKILL.md new file mode 100644 index 00000000..d6ae1050 --- /dev/null +++ b/.claude/skills/titan-gauntlet/SKILL.md @@ -0,0 +1,358 @@ +--- +name: titan-gauntlet +description: Audit codebase files against the 4-pillar quality manifesto using RECON work batches, with batch processing and context budget management (Titan Paradigm Phase 2) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan GAUNTLET — The Perfectionist Manifesto + +You are running the **GAUNTLET** phase of the Titan Paradigm. + +Your goal: audit every high-priority target from the RECON phase against 4 pillars of quality, using work batches to stay within context limits. Each batch writes results to disk before starting the next. If context reaches ~80% capacity, stop and tell the user to re-invoke — the state machine ensures no work is lost. + +**Batch size:** `$ARGUMENTS` (default: `5`) + +> **Context budget:** Process `$ARGUMENTS` targets per batch. Write results to NDJSON after each batch. If context grows large, save state and stop — the user re-invokes to continue. + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + +3. **Load state.** Read `.codegraph/titan/titan-state.json`. If missing: + - Warn: "No RECON artifacts. Run `/titan-recon` first for best results." + - Fall back: `codegraph triage -T --limit 50 --json` for a minimal queue. + +4. **Load architecture.** Read `.codegraph/titan/GLOBAL_ARCH.md` for domain context. + +5. **Resume logic.** If `titan-state.json` has completed batches, skip them. Start from the first `pending` batch. + +6. **Validate state.** If `titan-state.json` fails to parse, stop: "State file corrupted. Run `/titan-reset` to start over, or `/titan-recon` to rebuild." + +--- + +## Step 1 — The Four Pillars + +Every file must be checked against all four pillars. A file **FAILS** if it has any fail-level violation. + +### Pillar I: Structural Purity & Logic + +#### Rule 1 — Complexity (multi-metric) +```bash +codegraph complexity --file --health -T --json +``` +This returns ALL metrics in one call — use them all: + +| Metric | Warn | Fail | Why it matters | +|--------|------|------|---------------| +| `cognitive` | > 15 | > 30 | How hard to understand | +| `cyclomatic` | > 10 | > 20 | How many paths to test | +| `maxNesting` | > 3 | > 5 | Flatten with guards/extraction | +| `halstead.effort` | > 5000 | > 15000 | Information-theoretic complexity | +| `halstead.bugs` | > 0.5 | > 1.0 | Estimated defect count | +| `mi` (Maintainability Index) | < 50 | < 20 | Composite health score | +| `loc.sloc` | > 50 | > 100 | Function too long — split it | + +#### Rule 2 — Async hygiene (every Promise caught) +```bash +codegraph ast --kind await --file -T --json +codegraph ast --kind call --file -T --json +``` +Cross-reference: `.then()` calls without `.catch()` on the same chain; async functions without `try/catch` wrapping await calls. Also grep: +```bash +grep -n "\.then(" +grep -n "async " +``` +**Fail:** uncaught promise chains or async functions without error handling. + +#### Rule 3 — Dependency direction (no upward imports) +```bash +codegraph check --boundaries -T --json +codegraph deps --json +``` +Cross-reference with GLOBAL_ARCH.md layer rules. **Fail:** import from a higher layer. + +#### Rule 4 — Dead code (no unused exports) +```bash +codegraph roles --role dead --file -T --json +codegraph exports -T --json +``` +**Fail:** dead exports or unreferenced symbols. + +#### Rule 5 — Resource hygiene +```bash +codegraph ast --kind call --file -T --json +``` +Find `addEventListener`, `setInterval`, `setTimeout`, `createReadStream`, `.on(` — verify matching cleanup. **Fail:** resource acquired without cleanup. + +#### Rule 6 — Immutability +```bash +codegraph dataflow -T --json +``` +Also grep for mutation patterns: +```bash +grep -n "\.push(\|\.splice(\|\.sort(\|\.reverse(\|delete " +``` +**Fail:** direct mutation of function arguments or external state. + +### Pillar II: Data & Type Sovereignty + +#### Rule 7 — Magic values +```bash +codegraph ast --kind string --file -T --json +``` +Also grep for numeric literals in logic branches: +```bash +grep -nE "[^a-zA-Z_][0-9]{2,}[^a-zA-Z_]" +``` +Filter out imports, log format strings, test assertions. **Warn:** present. **Fail:** in if/switch conditions. + +#### Rule 8 — Boundary validation +```bash +codegraph roles --role entry --file -T --json +codegraph where --file -T --json +``` +For entry-point functions, verify schema validation before processing. **Fail:** missing validation at system boundaries. + +#### Rule 9 — Secret hygiene +```bash +grep -niE "api.?key|secret|password|token|credential" +``` +Verify values come from config/env, not literals. **Fail:** hardcoded secret values. + +#### Rule 10 — Error integrity (no empty catches) +```bash +grep -nA2 "catch" +``` +**Fail:** empty catch block or catch with only `// ignore` or `// TODO`. + +### Pillar III: Ecosystem Synergy + +#### Rule 11 — DRY (no duplicated logic) +```bash +codegraph search "" -T --json +codegraph co-change -T --json +``` +Find semantically similar functions. If `codegraph search` fails (no embeddings), use grep for function signature patterns. **Warn:** similar patterns. **Fail:** near-verbatim copy. + +> Note: requires embeddings from `/titan-recon`. If `titan-state.json → embeddingsAvailable` is false, skip semantic search and note it. + +#### Rule 12 — Naming symmetry +```bash +codegraph where --file -T --json +``` +Scan function names in the domain. Flag mixed `get`/`fetch`/`retrieve` or `create`/`make`/`build` for the same concept. **Warn:** inconsistent. **Advisory** — not a fail condition. + +#### Rule 13 — Config over code +```bash +codegraph deps --json +``` +Also grep: +```bash +grep -n "process.env\|NODE_ENV\|production\|development" +``` +Verify env-specific behavior driven by config, not inline branches. **Warn:** inline env branch. + +### Pillar IV: The Quality Vigil + +#### Rule 14 — Naming quality +```bash +codegraph where --file -T --json +``` +Flag vague names: `data`, `obj`, `temp`, `res`, `val`, `item`, `result`, single-letter vars (except `i/j/k`). **Warn:** present. **Advisory.** + +#### Rule 15 — Structured logging +```bash +codegraph ast --kind call --file -T --json +``` +Also grep: +```bash +grep -n "console\.\(log\|warn\|error\|info\)" +``` +**Warn:** console.log in source files. **Fail:** in production code paths (non-debug, non-test). + +#### Rule 16 — Testability +```bash +codegraph fn-impact -T --json +codegraph query -T --json +``` +High fan-out correlates with many mocks needed. Also read corresponding test file and count mock/stub/spy calls. **Warn:** > 10 mocks. **Fail:** > 15 mocks. + +#### Rule 17 — Critical path coverage +```bash +codegraph roles --role core --file -T --json +``` +If file contains core symbols (high fan-in), note whether test files exist for it. **Warn:** core symbol with no test file. **Advisory.** + +### Audit trail (per file) + +For every file, the NDJSON record MUST include: +- **Verdict** and **pillar verdicts** (pass/warn/fail per pillar) +- **All metrics** from `codegraph complexity --health` (cognitive, cyclomatic, nesting, MI, halstead.bugs, halstead.effort, loc.sloc) +- **Violation list** with rule number, detail, and level +- **Recommendation** for FAIL/DECOMPOSE targets + +Codegraph provides all the data needed for a verifiable audit — no need to manually traverse files for line counts or nesting proof. + +--- + +## Step 2 — Batch audit loop + +For each pending batch (from `titan-state.json`): + +### 2a. Save pre-batch snapshot +```bash +codegraph snapshot save titan-batch- +``` +Delete the previous batch snapshot if it exists: +```bash +codegraph snapshot delete titan-batch- +``` + +### 2b. Collect all metrics in one call +```bash +codegraph batch complexity ... -T --json +``` +This returns complexity + health metrics for all targets in one call. Parse the results. + +For deeper context on high-risk targets: +```bash +codegraph batch context ... -T --json +``` + +### 2c. Run Pillar I checks +For each file in the batch: +- Parse complexity metrics from batch output (Rule 1 — all 7 metric thresholds) +- Run AST queries for async hygiene (Rule 2), resource cleanup (Rule 5) +- Check boundary violations (Rule 3): `codegraph check --boundaries -T --json` +- Check dead code (Rule 4): `codegraph roles --role dead --file -T --json` +- Check immutability (Rule 6): `codegraph dataflow` + grep + +### 2d. Run Pillar II checks +For each file: +- Magic values (Rule 7): `codegraph ast --kind string` + grep +- Boundary validation (Rule 8): check entry points +- Secret hygiene (Rule 9): grep +- Empty catches (Rule 10): grep + +### 2e. Run Pillar III checks +- DRY (Rule 11): `codegraph search` (if embeddings available) + `co-change` +- Naming symmetry (Rule 12): `codegraph where --file` +- Config over code (Rule 13): `codegraph deps` + grep + +### 2f. Run Pillar IV checks +- Naming quality (Rule 14): `codegraph where --file` +- Structured logging (Rule 15): `codegraph ast --kind call` + grep +- Testability (Rule 16): `codegraph fn-impact` + test file mock count +- Critical path coverage (Rule 17): `codegraph roles --role core` + +### 2g. Score each target + +| Verdict | Condition | +|---------|-----------| +| **PASS** | No fail-level violations | +| **WARN** | Warn-level violations only | +| **FAIL** | One or more fail-level violations | +| **DECOMPOSE** | Complexity fail + `halstead.bugs` > 1.0 + high fan-out (needs splitting) | + +For FAIL/DECOMPOSE targets, capture blast radius: +```bash +codegraph fn-impact -T --json +``` + +### 2h. Write batch results + +Append to `.codegraph/titan/gauntlet.ndjson` (one line per target): + +```json +{"target": "", "file": "", "verdict": "FAIL", "pillarVerdicts": {"I": "fail", "II": "warn", "III": "pass", "IV": "pass"}, "metrics": {"cognitive": 28, "cyclomatic": 15, "maxNesting": 4, "mi": 32, "halsteadEffort": 12000, "halsteadBugs": 1.2, "sloc": 85}, "violations": [{"rule": 1, "pillar": "I", "metric": "cognitive", "detail": "28 > 30 threshold", "level": "fail"}], "blastRadius": {"direct": 5, "transitive": 18}, "recommendation": "Split: halstead.bugs 1.2 suggests ~1 defect. Separate validation from I/O."} +``` + +### 2i. Update state machine + +Update `titan-state.json`: +- Set batch status to `"completed"` +- Increment `progress.audited`, `.passed`, `.warned`, `.failed` +- Add entries to `fileAudits` map +- Update `snapshots.lastBatch` +- Update `lastUpdated` + +### 2j. Progress check + +Print: `Batch N/M: X pass, Y warn, Z fail` + +**Context budget:** If context is growing large: +1. Write all state to disk +2. Print: `Context budget reached after Batch N. Run /titan-gauntlet to continue.` +3. Stop. + +--- + +## Step 3 — Clean up batch snapshots + +After all batches complete, delete the last batch snapshot: +```bash +codegraph snapshot delete titan-batch- +``` +Keep `titan-baseline` — GATE may need it. + +If stopping early for context, keep the last batch snapshot for safety. + +--- + +## Step 4 — Aggregate and report + +Compute from `gauntlet.ndjson`: +- Pass / Warn / Fail / Decompose counts +- Top 10 worst offenders (by violation count or `halstead.bugs`) +- Most common violations by pillar +- Files with the most failing functions + +Write `.codegraph/titan/gauntlet-summary.json`: +```json +{ + "phase": "gauntlet", + "timestamp": "", + "complete": true, + "summary": {"totalAudited": 0, "pass": 0, "warn": 0, "fail": 0, "decompose": 0}, + "worstOffenders": [], + "commonViolations": {"I": [], "II": [], "III": [], "IV": []} +} +``` + +Set `"complete": false` if stopping early. + +Print summary to user: +- Pass/Warn/Fail/Decompose counts +- Top 5 worst (with their `halstead.bugs` and `mi` scores) +- Most common violation per pillar +- Next step: `/titan-gauntlet` to continue (if incomplete) or `/titan-sync` + +--- + +## Rules + +- **Batch processing is mandatory.** Never audit more than `$ARGUMENTS` targets at once. +- **Write NDJSON incrementally.** Partial results survive crashes. +- **Always use `--json` and `-T`** on codegraph commands. +- **Use `codegraph batch `** for multi-target queries — not separate calls. +- **Leverage `--health` and `--above-threshold`** — they give you all metrics in one call. +- **Context budget:** Stop at ~80%, save state, tell user to re-invoke. +- **Lint runs once in GATE**, not per-batch here. Don't run `npm run lint`. +- Advisory rules (12, 14, 17) produce warnings, never failures. +- Dead symbols from RECON should be flagged for removal, not skipped. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-gauntlet/SKILL.md`. Adjust thresholds or rules after dogfooding. diff --git a/.claude/skills/titan-recon/SKILL.md b/.claude/skills/titan-recon/SKILL.md new file mode 100644 index 00000000..b1ae5428 --- /dev/null +++ b/.claude/skills/titan-recon/SKILL.md @@ -0,0 +1,316 @@ +--- +name: titan-recon +description: Map a codebase's dependency graph, identify hotspots, name logical domains, propose work batches, and produce a ranked priority queue for autonomous cleanup (Titan Paradigm Phase 1) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan RECON — Codebase Reconnaissance + +You are running the **RECON** phase of the Titan Paradigm on the target at `$ARGUMENTS` (default: `.`). + +Your goal: map the dependency graph, identify structural hotspots, name logical domains, produce a global architecture document, propose work batches, and initialize the session state. Everything you produce feeds downstream phases (GAUNTLET, SYNC, GATE) via artifacts in `.codegraph/titan/`. + +> **Context budget:** Every codegraph command MUST use `--json` to keep output compact. Never dump raw CLI tables into context — parse JSON and extract only what you need. + +--- + +## Step 0 — Pre-flight: worktree and sync + +1. **Check for worktree isolation:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If you are NOT in a worktree, **stop:** "Run `/worktree` first. Titan phases write artifacts that should not interfere with other work." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + If there are merge conflicts, stop and ask the user to resolve them. + +--- + +## Step 1 — Build the graph + +```bash +codegraph build $ARGUMENTS +``` + +Record: file count, node count, edge count, engine. + +--- + +## Step 2 — Generate embeddings (for DRY detection in GAUNTLET) + +```bash +codegraph embed -m minilm +``` + +This enables `codegraph search` for duplicate code detection in downstream phases. If it fails (e.g., missing model), note it and continue — DRY checks will be grep-only. + +--- + +## Step 3 — Collect baseline metrics + +Run in parallel: + +```bash +codegraph stats --json +codegraph structure --depth 2 --json +``` + +Extract from `stats`: `totalNodes`, `totalEdges`, `totalFiles`, `qualityScore`, `avgFanIn`, `avgFanOut`. + +Extract from `structure`: top 10 directories by file count, directories with cohesion < 0.3 (tangled). + +--- + +## Step 4 — Build the priority queue + +```bash +codegraph triage -T --limit 100 --json +``` + +Risk-ranked list combining connectivity, complexity, and role classification. Truncate to top 50 for the artifact if >100 items. + +--- + +## Step 5 — Community and drift analysis + +```bash +codegraph communities -T --json +codegraph communities --drift -T --json +``` + +Extract: community count, top 5 largest (member count + key files), drift warnings. + +--- + +## Step 6 — High-traffic files and role classification + +```bash +codegraph map --limit 30 -T --json +codegraph roles --role core -T --json +codegraph roles --role dead -T --json +``` + +Count core symbols (high fan-in) and dead symbols (zero fan-in, not exported). + +--- + +## Step 7 — Complexity health baseline + +Get the full metrics picture across the codebase — this is what makes codegraph powerful: + +```bash +codegraph complexity --health --above-threshold -T --json --limit 50 +``` + +This returns only functions exceeding configured warn thresholds, with all available metrics per function: +- **Structural:** `cognitive`, `cyclomatic`, `maxNesting` +- **Halstead:** `volume`, `difficulty`, `effort`, `bugs` (estimated bug count) +- **Size:** `loc.sloc`, `loc.commentLines` +- **Composite:** `mi` (Maintainability Index) + +Also get the worst offenders by different metrics: + +```bash +codegraph complexity --health --sort effort -T --json --limit 10 +codegraph complexity --health --sort bugs -T --json --limit 10 +codegraph complexity --health --sort mi -T --json --limit 10 +``` + +These three views reveal different quality dimensions: `effort` = hardest to understand, `bugs` = most likely to contain defects, `mi` = worst overall maintainability. + +--- + +## Step 8 — Domain inventory + +Using community detection (Step 5) and directory structure (Step 3), **name** the logical domains. A domain is a cohesive group of files serving a single concern. + +For each domain, record: +- **Name:** use the codebase's own vocabulary (directory names, module names) +- **Root directories** +- **File count** +- **Key symbols:** 3-5 most-connected symbols (from triage) +- **Community IDs:** which communities map to this domain +- **Health:** cohesion score, drift warnings, cycle participation + +For large domains, map inter-domain dependencies using key files (not directories — `deps` takes a file path): + +```bash +codegraph deps --json +``` + +--- + +## Step 9 — Global architecture document + +Write `.codegraph/titan/GLOBAL_ARCH.md`: + +```markdown +# Global Architecture + +**Date:** +**Codebase:** ( files, symbols) + +## Domain Map + +| Domain | Root Dirs | Files | Core Symbols | Health | +|--------|-----------|-------|-------------|--------| +| ... | ... | ... | ... | cohesion, drift? | + +## Dependency Flow + + + +## Shared Types and Interfaces + + + +## Architectural Rules + + + +## Cycles + + +``` + +--- + +## Step 10 — Propose work batches + +Decompose the priority queue into **work batches** of ~5-15 files each: +- Stay within a single domain where possible +- Group tightly-coupled files together (from communities) +- Order by priority: highest-risk domains first +- Note dependencies: "batch N depends on batch M being done first" + +--- + +## Step 11 — Save baseline snapshot + +```bash +codegraph snapshot save titan-baseline +``` + +This is the rollback point. If anything goes wrong downstream, any skill can restore it. + +--- + +## Step 12 — Write the state file + +Create `.codegraph/titan/titan-state.json` — the single source of truth for the entire pipeline: + +```bash +mkdir -p .codegraph/titan +``` + +```json +{ + "version": 1, + "initialized": "", + "lastUpdated": "", + "target": "", + "currentPhase": "recon", + "snapshots": { + "baseline": "titan-baseline", + "lastBatch": null + }, + "embeddingsAvailable": true, + "stats": { + "totalFiles": 0, + "totalNodes": 0, + "totalEdges": 0, + "qualityScore": 0, + "avgFanIn": 0, + "avgFanOut": 0 + }, + "healthBaseline": { + "functionsAboveThreshold": 0, + "worstByEffort": [""], + "worstByBugs": [""], + "worstByMI": [""] + }, + "domains": [ + { + "name": "", + "rootDirs": [""], + "fileCount": 0, + "status": "pending", + "audited": 0, + "passed": 0, + "failed": 0 + } + ], + "batches": [ + { + "id": 1, + "domain": "", + "files": [""], + "status": "pending", + "dependsOn": [], + "priority": 1 + } + ], + "priorityQueue": [ + { + "rank": 1, + "target": "", + "riskScore": 0, + "reason": "" + } + ], + "communities": { + "count": 0, + "driftWarnings": [] + }, + "roles": { + "coreCount": 0, + "deadCount": 0, + "deadSymbols": [""] + }, + "hotFiles": [""], + "tangledDirs": [""], + "fileAudits": {}, + "progress": { + "totalFiles": 0, + "audited": 0, + "passed": 0, + "warned": 0, + "failed": 0, + "fixed": 0 + } +} +``` + +--- + +## Step 13 — Report to user + +Print a concise summary: +- Graph size and quality score +- Domains identified (count and names) +- Complexity health: count of functions above threshold, top 3 worst by `halstead.bugs` +- Work batches proposed (count) +- Top 5 priority targets +- Dead symbol count +- Drift warnings +- Path to artifacts: `.codegraph/titan/titan-state.json` and `.codegraph/titan/GLOBAL_ARCH.md` +- Next step: `/titan-gauntlet` to audit the priority queue + +--- + +## Rules + +- **Always use `--json` and `-T`** on codegraph commands. +- **Never paste raw JSON** into your response — parse and extract. +- **Write artifacts before reporting.** +- If any command fails, note it and continue with partial data. +- **Domain naming** uses the codebase's own vocabulary. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-recon/SKILL.md`. Edit it if you find improvements during execution. diff --git a/.claude/skills/titan-reset/SKILL.md b/.claude/skills/titan-reset/SKILL.md new file mode 100644 index 00000000..2c99704d --- /dev/null +++ b/.claude/skills/titan-reset/SKILL.md @@ -0,0 +1,89 @@ +--- +name: titan-reset +description: Clean up all Titan Paradigm artifacts and snapshots, restoring the codebase to pre-Titan state +argument-hint: <--keep-graph to preserve the codegraph database> +allowed-tools: Bash, Read, Write, Grep +--- + +# Titan RESET — Pipeline Cleanup + +You are resetting the Titan Paradigm pipeline, removing all artifacts and restoring the codebase to its pre-Titan state. + +--- + +## Step 1 — Restore baseline snapshot (if available) + +```bash +codegraph snapshot restore titan-baseline 2>/dev/null && echo "Baseline restored" || echo "No baseline snapshot found" +``` + +This restores the graph database to its pre-GAUNTLET state. + +--- + +## Step 2 — Delete all Titan snapshots + +```bash +codegraph snapshot delete titan-baseline 2>/dev/null +``` + +Also delete any batch snapshots: + +```bash +codegraph snapshot delete titan-batch-1 2>/dev/null +codegraph snapshot delete titan-batch-2 2>/dev/null +codegraph snapshot delete titan-batch-3 2>/dev/null +codegraph snapshot delete titan-batch-4 2>/dev/null +codegraph snapshot delete titan-batch-5 2>/dev/null +codegraph snapshot delete titan-batch-6 2>/dev/null +codegraph snapshot delete titan-batch-7 2>/dev/null +codegraph snapshot delete titan-batch-8 2>/dev/null +codegraph snapshot delete titan-batch-9 2>/dev/null +codegraph snapshot delete titan-batch-10 2>/dev/null +``` + +(Errors are expected for snapshots that don't exist — ignore them.) + +--- + +## Step 3 — Remove all Titan artifacts + +```bash +rm -rf .codegraph/titan/ +``` + +This removes: +- `titan-state.json` — session state +- `GLOBAL_ARCH.md` — architecture document +- `gauntlet.ndjson` — audit results +- `gauntlet-summary.json` — aggregated results +- `sync.json` — execution plan +- `gate-log.ndjson` — gate audit trail + +--- + +## Step 4 — Rebuild graph (unless --keep-graph) + +If `$ARGUMENTS` does NOT contain `--keep-graph`: + +```bash +codegraph build +``` + +This ensures the graph reflects the current state of the codebase without any Titan-era corruption. + +If `$ARGUMENTS` contains `--keep-graph`, skip this step. + +--- + +## Step 5 — Report + +``` +Titan pipeline reset complete. + - Baseline snapshot: restored and deleted + - Batch snapshots: deleted + - Artifacts: removed (.codegraph/titan/) + - Graph: rebuilt (clean state) + +To start a fresh Titan pipeline, run /titan-recon +``` diff --git a/.claude/skills/titan-sync/SKILL.md b/.claude/skills/titan-sync/SKILL.md new file mode 100644 index 00000000..6b93e0e4 --- /dev/null +++ b/.claude/skills/titan-sync/SKILL.md @@ -0,0 +1,233 @@ +--- +name: titan-sync +description: Identify overlapping fixes across audit results, plan shared abstractions, produce an ordered execution plan with logical commit grouping (Titan Paradigm Phase 3) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan GLOBAL SYNC — Cross-Cutting Analysis & Execution Plan + +You are running the **GLOBAL SYNC** phase of the Titan Paradigm. + +Your goal: analyze GAUNTLET results to find overlapping problems, identify shared abstractions that should be built *before* individual fixes, group changes into logical commits, and produce an ordered execution plan. + +> **Context budget:** This phase reads artifacts, not source. Keep codegraph queries targeted — only for specific relationship questions between failing targets. + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + +3. **Load artifacts.** Read: + - `.codegraph/titan/titan-state.json` — state, domains, batches, file audits + - `.codegraph/titan/GLOBAL_ARCH.md` — architecture, dependency flow, shared types + - `.codegraph/titan/gauntlet.ndjson` — per-target audit details + - `.codegraph/titan/gauntlet-summary.json` — aggregated results + +4. **Validate state.** If `titan-state.json` fails to parse, stop: "State file corrupted. Run `/titan-reset`." + +5. **Check GAUNTLET completeness.** If `gauntlet-summary.json` has `"complete": false`: + > "GAUNTLET incomplete (/ batches). SYNC will plan based on known failures only. Run `/titan-gauntlet` first for a complete plan." + +6. **Extract.** From artifacts, collect: + - All FAIL and DECOMPOSE targets with violations and files + - Common violation patterns by pillar + - Community assignments + - Dead symbols (cleanup candidates) + - Domain boundaries and dependency flow + +--- + +## Step 1 — Find dependency clusters among failing targets + +For FAIL/DECOMPOSE targets that share a file or community, check connections: + +```bash +codegraph path -T --json +``` + +Group connected failures into **clusters**. Also check for cycles among them: + +```bash +codegraph cycles --functions --json +``` + +Filter to cycles including at least one FAIL/DECOMPOSE target. + +--- + +## Step 2 — Identify shared dependencies and ownership + +For each cluster, find what they share: + +```bash +codegraph deps --json +``` + +Look for: +- **Shared imports:** multiple failures import the same module → interface extraction candidate +- **Shared callers:** multiple failures called by the same function → caller needs updating +- **Common violations:** similar pillar violations across targets + +Check **code ownership** for cross-team coordination: + +```bash +codegraph owners -T --json +``` + +If different teams own files in the same cluster, note the coordination requirement. + +Run **branch structural diff** to see what's already changed: + +```bash +codegraph branch-compare main HEAD -T --json +``` + +Avoid re-auditing or conflicting with in-progress work. + +--- + +## Step 3 — Detect extraction candidates + +For DECOMPOSE targets: + +```bash +codegraph context -T --json +codegraph ast --kind call --file -T --json +``` + +Look for: +- Functions with multiple responsibilities (high cognitive + high fan-out + high `halstead.bugs`) +- Repeated patterns across failures (similar call chains) +- God files (many failing functions → split along community boundaries) + +--- + +## Step 4 — Plan shared abstractions + +Identify what to build BEFORE individual fixes: + +1. **Interface extractions** — shared dependency → extract interface +2. **Utility extractions** — repeated patterns → shared utility +3. **Module splits** — god files → split by community structure +4. **Cycle breaks** — circular deps → identify weakest link + +For each, check blast radius: +```bash +codegraph fn-impact -T --json +``` + +--- + +## Step 5 — Build execution order with logical commits + +### Phases (in order) + +1. **Dead code cleanup** — zero risk, reduces noise + - Commit: `chore: remove dead code` +2. **Shared abstractions** — before individual fixes + - One commit per abstraction: `refactor: extract X from Y` +3. **Cycle breaks** — unblocks dependent targets + - One commit per break: `refactor: break cycle between X and Y` +4. **Decompositions** — highest risk, after abstractions + - One commit per decomposition: `refactor: split X into A and B` +5. **Fail fixes** — ordered by blast radius (lowest first) + - Group by domain: `fix: address quality issues in ` +6. **Warn improvements** — optional, lowest priority + - Group by domain: `refactor: address warnings in ` + +### Ordering within each phase +- Dependencies first (if A depends on B, fix B first) +- Lower blast radius first +- Same community together + +### Each commit should: +- Touch one domain where possible +- Address one concern +- Be independently revertible +- Run `/titan-gate` before committing + +--- + +## Step 6 — Write the SYNC artifact + +Write `.codegraph/titan/sync.json`: + +```json +{ + "phase": "sync", + "timestamp": "", + "clusters": [ + { + "id": 1, + "name": "", + "targets": ["t1", "t2"], + "sharedDeps": ["mod"], + "hasCycle": false, + "owners": ["team-a", "team-b"], + "proposedAction": "Extract interface" + } + ], + "abstractions": [ + { + "type": "interface_extraction|utility_extraction|module_split|cycle_break", + "description": "...", + "source": "", + "unblocks": ["t1", "t2"], + "blastRadius": 0, + "commit": "refactor: ..." + } + ], + "executionOrder": [ + { + "phase": 1, + "label": "Dead code cleanup", + "targets": ["sym1", "sym2"], + "risk": "none", + "commit": "chore: remove dead code", + "dependencies": [] + } + ], + "deadCodeTargets": [""], + "cyclesInvolvingFailures": [] +} +``` + +Update `titan-state.json`: set `currentPhase` to `"sync"`. + +--- + +## Step 7 — Report to user + +Print: +- Dependency clusters found (count) +- Shared abstractions proposed (count) +- Execution order summary (phases, target counts, estimated commits) +- Key insight: what SYNC prevented (e.g., "3 targets share configLoader — without SYNC, 3 conflicting refactors") +- Path to `sync.json` +- Next step: start Phase 1 (dead code cleanup), validate each commit with `/titan-gate` + +--- + +## Rules + +- **Read artifacts, don't re-scan.** Codegraph commands only for targeted relationship queries. +- **Always use `--json` and `-T`.** +- **The execution order is the key output.** +- **Logical commits matter.** Never mix concerns. +- If GAUNTLET found zero failures, produce minimal plan (dead code + warnings only). +- Keep `codegraph path` queries targeted — same file or community only. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-sync/SKILL.md`. Edit if clustering misses connections or execution order causes conflicts. diff --git a/docs/examples/claude-code-skills/README.md b/docs/examples/claude-code-skills/README.md new file mode 100644 index 00000000..9969f3ba --- /dev/null +++ b/docs/examples/claude-code-skills/README.md @@ -0,0 +1,196 @@ +# Claude Code Skills for Codegraph + +This directory contains example [Claude Code skills](https://docs.anthropic.com/en/docs/claude-code/skills) that use codegraph to power autonomous codebase cleanup — based on the [Titan Paradigm](../../use-cases/titan-paradigm.md). + +## The Problem: Context Window Explosion + +A single AI agent cannot hold an entire large codebase in context. The Titan Paradigm solves this with a phased approach where each phase: + +1. Runs targeted codegraph queries (not raw file reads) +2. Writes structured **artifacts** to `.codegraph/titan/` +3. Next phase reads only those artifacts — not the original sources + +``` +/titan-recon → titan-state.json + GLOBAL_ARCH.md + │ + ▼ +/titan-gauntlet → gauntlet.ndjson (batches of 5, resumes across sessions) + │ + ▼ +/titan-sync → sync.json (execution plan) + │ + ▼ +/titan-gate (validates each commit: codegraph + lint/build/test) + +/titan-reset (escape hatch: clean up everything) +``` + +## Skills + +| Skill | Phase | What it does | Key artifact | +|-------|-------|-------------|-------------| +| `/titan-recon` | RECON | Builds graph + embeddings, complexity health baseline, domains, priority queue, work batches, `GLOBAL_ARCH.md`, baseline snapshot | `titan-state.json` | +| `/titan-gauntlet` | GAUNTLET | 4-pillar audit (17 rules) using full codegraph metrics (`cognitive`, `cyclomatic`, `halstead.bugs`, `halstead.effort`, `mi`, `loc.sloc`). Batches of 5, NDJSON writes, session resume | `gauntlet.ndjson` | +| `/titan-sync` | GLOBAL SYNC | Dependency clusters, code ownership, shared abstractions, ordered execution plan with logical commits | `sync.json` | +| `/titan-gate` | STATE MACHINE | `codegraph check --staged --cycles --blast-radius 30 --boundaries` + lint/build/test. Snapshot restore on failure | `gate-log.ndjson` | +| `/titan-reset` | ESCAPE HATCH | Restores baseline snapshot, deletes all artifacts and snapshots, rebuilds graph | — | + +## Installation + +Copy the skill directories into your project's `.claude/skills/` directory: + +```bash +# From your project root +mkdir -p .claude/skills +cp -r titan-recon titan-gauntlet titan-sync titan-gate titan-reset .claude/skills/ +``` + +Then install codegraph if you haven't: + +```bash +npm install -g @optave/codegraph +codegraph build . +``` + +## Usage + +### Full pipeline + +``` +/titan-recon # Map the codebase, produce priority queue + embeddings +/titan-gauntlet 5 # Audit top targets in batches of 5 +/titan-sync # Plan shared abstractions and execution order +# ... make changes based on sync plan ... +/titan-gate # Validate before each commit +``` + +If GAUNTLET runs out of context, just re-invoke `/titan-gauntlet` — it resumes from the next pending batch. + +### Standalone phases + +- `/titan-recon` always works standalone (builds graph fresh) +- `/titan-gauntlet` falls back to `codegraph triage` if no RECON artifact exists +- `/titan-sync` requires GAUNTLET artifacts (warns if missing) +- `/titan-gate` works with or without prior artifacts (uses default thresholds) +- `/titan-reset` cleans up everything — use when you want to start over + +### Iterative workflow + +``` +/titan-recon # Once: map the codebase +/titan-gauntlet # Once (or multiple sessions): audit everything +/titan-sync # Once: plan the work + +# Then for each fix: +# 1. Make changes based on sync plan +# 2. Stage changes +/titan-gate # Validate +# 3. Commit if PASS +``` + +## Artifacts + +All artifacts are written to `.codegraph/titan/` (5 files, no redundancy): + +| File | Format | Written by | Read by | +|------|--------|-----------|---------| +| `titan-state.json` | JSON | RECON (init), ALL (update) | ALL | +| `GLOBAL_ARCH.md` | Markdown | RECON | GAUNTLET, SYNC | +| `gauntlet.ndjson` | NDJSON | GAUNTLET | SYNC | +| `gauntlet-summary.json` | JSON | GAUNTLET | SYNC, GATE | +| `sync.json` | JSON | SYNC | GATE | +| `gate-log.ndjson` | NDJSON | GATE | Audit trail | + +NDJSON format (one JSON object per line) means partial results survive crashes mid-batch. + +**Tip:** Add `.codegraph/titan/` to `.gitignore` — these are ephemeral analysis artifacts, not source code. + +## Snapshots + +Codegraph snapshots provide instant graph database backup/restore: + +| Snapshot | Created by | Restored by | Deleted by | +|----------|-----------|------------|-----------| +| `titan-baseline` | RECON | GATE (on failure) | GATE (final success) or RESET | +| `titan-batch-N` | GAUNTLET (per batch) | GATE (on failure) | GAUNTLET (next batch) or RESET | + +## Context Window Management + +1. **JSON over tables.** All codegraph commands use `--json` for compact output. +2. **Batch processing with resume.** GAUNTLET processes 5 targets at a time (configurable), writes to NDJSON, stops at ~80% context. Re-invoking resumes automatically. +3. **Artifact bridging.** Each phase reads compact JSON artifacts, not raw source files. +4. **`codegraph batch `** queries multiple targets in one call (e.g., `batch complexity t1 t2 t3`). +5. **`--above-threshold`** returns only functions exceeding thresholds — skip the noise. +6. **No-test filtering.** All commands use `-T` to exclude test files. + +### Cross-session continuity + +Run each `/titan-*` skill in a separate conversation if needed: +- Artifacts on disk bridge between conversations +- `titan-state.json` tracks progress, pending batches, and file audit status +- No context is lost because each phase's output is self-contained + +## Customizing Thresholds + +Edit the Rule 1 threshold table in `.claude/skills/titan-gauntlet/SKILL.md`: + +```markdown +| Metric | Warn | Fail | Why | +|--------|------|------|-----| +| cognitive | > 15 | > 30 | How hard to understand | +| cyclomatic | > 10 | > 20 | How many paths to test | +| maxNesting | > 3 | > 5 | Flatten with guards | +| halstead.effort | > 5000 | > 15000 | Information-theoretic density | +| halstead.bugs | > 0.5 | > 1.0 | Estimated defect count | +| mi | < 50 | < 20 | Composite health | +| loc.sloc | > 50 | > 100 | Too long — split | +``` + +Adjust for your codebase — stricter for greenfield, more lenient for legacy code. + +## Worktree Isolation + +All skills enforce worktree isolation as their first step. If invoked from the main checkout, they stop and ask for `/worktree`. This prevents: + +- Titan artifacts from polluting the main checkout +- Concurrent sessions from interfering +- Accidental commits of analysis artifacts + +## Codegraph Commands Used + +| Command | Used by | Purpose | +|---------|---------|---------| +| `codegraph build` | RECON | Build/refresh the dependency graph | +| `codegraph embed` | RECON | Generate embeddings for DRY detection | +| `codegraph stats` | RECON | Baseline metrics | +| `codegraph triage` | RECON, GAUNTLET (fallback) | Ranked priority queue | +| `codegraph map` | RECON | High-traffic files | +| `codegraph communities` | RECON | Module boundaries and drift | +| `codegraph roles` | RECON, GAUNTLET | Core/dead/entry symbol classification | +| `codegraph structure` | RECON | Directory cohesion | +| `codegraph complexity --health` | RECON, GAUNTLET, GATE | Full metrics: cognitive, cyclomatic, nesting, Halstead, MI | +| `codegraph complexity --above-threshold` | RECON | Only functions exceeding thresholds | +| `codegraph batch complexity` | GAUNTLET | Multi-target complexity in one call | +| `codegraph batch context` | GAUNTLET | Multi-target context in one call | +| `codegraph check --staged --cycles --blast-radius --boundaries` | GATE | Full validation predicates | +| `codegraph ast --kind call\|await\|string` | GAUNTLET | AST pattern detection | +| `codegraph dataflow` | GAUNTLET | Data flow and mutation analysis | +| `codegraph exports` | GAUNTLET | Per-symbol export consumers | +| `codegraph fn-impact` | GAUNTLET, SYNC | Blast radius | +| `codegraph search` | GAUNTLET | Duplicate code detection (needs embeddings) | +| `codegraph co-change` | GAUNTLET, SYNC | Git history coupling | +| `codegraph path` | SYNC | Dependency paths between targets | +| `codegraph cycles` | SYNC, GATE | Circular dependency detection | +| `codegraph deps` | SYNC | File-level dependency map | +| `codegraph context` | SYNC | Full function context | +| `codegraph owners` | SYNC | CODEOWNERS mapping for cross-team coordination | +| `codegraph branch-compare` | SYNC, GATE | Structural diff between refs | +| `codegraph diff-impact` | GATE | Impact of staged changes | +| `codegraph snapshot save\|restore\|delete` | RECON, GAUNTLET, GATE, RESET | Graph database backup/restore | + +## Further Reading + +- [Titan Paradigm Use Case](../../use-cases/titan-paradigm.md) — the full rationale and codegraph command mapping +- [AI Agent Guide](../../ai-agent-guide.md) — general guide for using codegraph with AI agents +- [MCP Examples](../MCP.md) — using codegraph via MCP for programmatic agent access +- [Claude Code Hooks](../claude-code-hooks/README.md) — automated hooks that complement these skills diff --git a/docs/examples/claude-code-skills/titan-gate/SKILL.md b/docs/examples/claude-code-skills/titan-gate/SKILL.md new file mode 100644 index 00000000..c1d7c359 --- /dev/null +++ b/docs/examples/claude-code-skills/titan-gate/SKILL.md @@ -0,0 +1,262 @@ +--- +name: titan-gate +description: Validate staged changes — codegraph checks + project lint/build/test, auto-rollback on failure, pass/fail commit gate (Titan Paradigm Phase 4) +argument-hint: <--force to skip warnings> +allowed-tools: Bash, Read, Write, Edit, Grep +--- + +# Titan GATE — Change Validation & State Machine + +You are running the **GATE** phase (State Machine) of the Titan Paradigm. + +Your goal: validate staged changes against codegraph quality checks AND the project's own lint/build/test. Produce a clear PASS/WARN/FAIL verdict. Auto-rollback on failure. + +> **Context budget:** Lightweight — only checks staged changes. Should complete quickly. + +**Force mode:** If `$ARGUMENTS` contains `--force`, warnings are downgraded (failures still block). + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Staged changes?** + ```bash + git diff --cached --name-only + ``` + If nothing staged, stop: "Nothing staged. Use `git add` first." + +3. **Load state (optional).** Read `.codegraph/titan/titan-state.json` if it exists — use for thresholds, baseline comparison, and sync alignment. If missing or corrupt, proceed with defaults. + +--- + +## Step 1 — Structural validation (codegraph) + +Run the full change validation predicates in one call: + +```bash +codegraph check --staged --cycles --blast-radius 30 --boundaries -T --json +``` + +This checks: manifesto rules, new cycle introduction, blast radius threshold, and architecture boundary violations. Exit code 0 = pass, 1 = fail. + +Also run detailed impact analysis: + +```bash +codegraph diff-impact --staged -T --json +``` + +Extract: changed functions (count + names), direct callers affected, transitive blast radius, historically coupled files. + +--- + +## Step 2 — Cycle check + +```bash +codegraph cycles --json +``` + +Compare against RECON baseline (if `titan-state.json` exists): +- **New cycles?** → FAIL +- **Cycles resolved?** → Note as positive + +--- + +## Step 3 — Complexity delta + +For each changed file (from diff-impact): + +```bash +codegraph complexity --file --health -T --json +``` + +Check all metrics against thresholds: +- `cognitive` > 30 → FAIL +- `halstead.bugs` > 1.0 → FAIL (estimated defect) +- `mi` < 20 → FAIL +- Function moved from PASS → FAIL on any metric? → FAIL +- Function improved but still above threshold? → WARN + +--- + +## Step 4 — Lint, build, and test + +Detect project tools from `package.json`: + +```bash +node -e "const p=require('./package.json');console.log(JSON.stringify(Object.keys(p.scripts||{})))" +``` + +Run in order — stop on first failure: + +```bash +npm run lint 2>&1 || echo "LINT_FAILED" +``` + +```bash +npm run build 2>&1 || echo "BUILD_FAILED" +``` +(Skip if no `build` script.) + +```bash +npm test 2>&1 || echo "TEST_FAILED" +``` + +If any fail → overall verdict is FAIL → proceed to auto-rollback. + +--- + +## Step 5 — Branch structural diff + +```bash +codegraph branch-compare main HEAD -T --json +``` + +Cumulative structural impact of all changes on this branch (broader than `diff-impact --staged`). Detect cumulative drift. + +--- + +## Step 6 — Sync plan alignment + +If `.codegraph/titan/sync.json` exists: +- Are changed files part of the current execution phase? +- Are dependencies for these targets already completed? +- Skipping ahead in execution order? → WARN + +Advisory — prevents jumping ahead and creating conflicts. + +--- + +## Step 7 — Blast radius check + +From diff-impact results: +- Transitive blast radius > 30 → FAIL +- Transitive blast radius > 15 → WARN +- Historically coupled file NOT staged? → WARN ("consider also updating X") + +--- + +## Step 8 — Verdict and auto-rollback + +Aggregate all checks: + +| Verdict | Meaning | +|---------|---------| +| **PASS** | Safe to commit | +| **WARN** | Warnings only — commit at your discretion | +| **FAIL** | Failures present — auto-rollback triggered | + +### Auto-rollback on FAIL (build/test/lint failures only) + +1. **Restore graph** to the most recent snapshot: + ```bash + codegraph snapshot restore titan-batch- # or titan-baseline if no batch snapshot + ``` + Check `titan-state.json → snapshots.lastBatch` first; fall back to `snapshots.baseline`. + +2. **Unstage changes** (preserve in working tree): + ```bash + git reset HEAD + ``` + +3. **Rebuild graph** for current working tree state: + ```bash + codegraph build + ``` + +> "GATE FAIL: [reason]. Graph restored, changes unstaged but preserved. Fix and re-stage." + +For structural-only failures (Steps 1-3, 5-7), do NOT auto-rollback — report and let user decide. + +### Snapshot cleanup on pipeline completion + +When the full Titan pipeline is done (all SYNC phases complete, final GATE passes): + +```bash +codegraph snapshot delete titan-baseline +codegraph snapshot delete titan-batch- # if any remain +``` + +> "All Titan snapshots cleaned up. Codebase is in its final validated state." + +--- + +## Step 9 — Update state machine + +Append to `.codegraph/titan/gate-log.ndjson`: + +```json +{ + "timestamp": "", + "verdict": "PASS|WARN|FAIL", + "stagedFiles": ["file1.js"], + "changedFunctions": 3, + "blastRadius": 12, + "checks": { + "manifesto": "pass|fail", + "cycles": "pass|fail", + "complexity": "pass|warn|fail", + "lint": "pass|fail|skipped", + "build": "pass|fail|skipped", + "tests": "pass|fail|skipped", + "syncAlignment": "pass|warn|skipped", + "blastRadius": "pass|warn|fail" + }, + "rolledBack": false +} +``` + +Update `titan-state.json` (if exists): increment `progress.fixed`, update `fileAudits` for fixed files. + +--- + +## Step 10 — Report to user + +**PASS:** +``` +GATE PASS — safe to commit + Changed: 3 functions across 2 files + Blast radius: 12 transitive callers + Lint: pass | Build: pass | Tests: pass + Complexity: all within thresholds (worst: halstead.bugs 0.3) +``` + +**WARN:** +``` +GATE WARN — review before committing + Changed: 5 functions across 3 files + Warnings: + - utils.js historically co-changes with config.js (not staged) + - parseConfig MI improved 18 → 35 but still below 50 +``` + +**FAIL:** +``` +GATE FAIL — changes unstaged, graph restored + Failures: + - Tests: 2 suites failed + - New cycle: parseConfig → loadConfig → parseConfig + Fix issues, re-stage, re-run /titan-gate +``` + +--- + +## Rules + +- **Fast execution.** Only staged changes, not full codebase. +- **Always use `--json` and `-T`.** +- **Never auto-commit.** Verdict only — user decides. +- **Auto-rollback is gentle** — `git reset HEAD`, never `git checkout`. Work preserved. +- **Append to gate-log.ndjson** — the audit trail. +- **Force mode** downgrades WARN → PASS but cannot override FAIL. +- **Run the project's own lint/build/test** — codegraph checks are necessary but not sufficient. +- **Use the correct check flags:** `--cycles`, `--blast-radius `, `--boundaries`. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-gate/SKILL.md`. Adjust thresholds or rollback behavior after dogfooding. diff --git a/docs/examples/claude-code-skills/titan-gauntlet/SKILL.md b/docs/examples/claude-code-skills/titan-gauntlet/SKILL.md new file mode 100644 index 00000000..d6ae1050 --- /dev/null +++ b/docs/examples/claude-code-skills/titan-gauntlet/SKILL.md @@ -0,0 +1,358 @@ +--- +name: titan-gauntlet +description: Audit codebase files against the 4-pillar quality manifesto using RECON work batches, with batch processing and context budget management (Titan Paradigm Phase 2) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan GAUNTLET — The Perfectionist Manifesto + +You are running the **GAUNTLET** phase of the Titan Paradigm. + +Your goal: audit every high-priority target from the RECON phase against 4 pillars of quality, using work batches to stay within context limits. Each batch writes results to disk before starting the next. If context reaches ~80% capacity, stop and tell the user to re-invoke — the state machine ensures no work is lost. + +**Batch size:** `$ARGUMENTS` (default: `5`) + +> **Context budget:** Process `$ARGUMENTS` targets per batch. Write results to NDJSON after each batch. If context grows large, save state and stop — the user re-invokes to continue. + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + +3. **Load state.** Read `.codegraph/titan/titan-state.json`. If missing: + - Warn: "No RECON artifacts. Run `/titan-recon` first for best results." + - Fall back: `codegraph triage -T --limit 50 --json` for a minimal queue. + +4. **Load architecture.** Read `.codegraph/titan/GLOBAL_ARCH.md` for domain context. + +5. **Resume logic.** If `titan-state.json` has completed batches, skip them. Start from the first `pending` batch. + +6. **Validate state.** If `titan-state.json` fails to parse, stop: "State file corrupted. Run `/titan-reset` to start over, or `/titan-recon` to rebuild." + +--- + +## Step 1 — The Four Pillars + +Every file must be checked against all four pillars. A file **FAILS** if it has any fail-level violation. + +### Pillar I: Structural Purity & Logic + +#### Rule 1 — Complexity (multi-metric) +```bash +codegraph complexity --file --health -T --json +``` +This returns ALL metrics in one call — use them all: + +| Metric | Warn | Fail | Why it matters | +|--------|------|------|---------------| +| `cognitive` | > 15 | > 30 | How hard to understand | +| `cyclomatic` | > 10 | > 20 | How many paths to test | +| `maxNesting` | > 3 | > 5 | Flatten with guards/extraction | +| `halstead.effort` | > 5000 | > 15000 | Information-theoretic complexity | +| `halstead.bugs` | > 0.5 | > 1.0 | Estimated defect count | +| `mi` (Maintainability Index) | < 50 | < 20 | Composite health score | +| `loc.sloc` | > 50 | > 100 | Function too long — split it | + +#### Rule 2 — Async hygiene (every Promise caught) +```bash +codegraph ast --kind await --file -T --json +codegraph ast --kind call --file -T --json +``` +Cross-reference: `.then()` calls without `.catch()` on the same chain; async functions without `try/catch` wrapping await calls. Also grep: +```bash +grep -n "\.then(" +grep -n "async " +``` +**Fail:** uncaught promise chains or async functions without error handling. + +#### Rule 3 — Dependency direction (no upward imports) +```bash +codegraph check --boundaries -T --json +codegraph deps --json +``` +Cross-reference with GLOBAL_ARCH.md layer rules. **Fail:** import from a higher layer. + +#### Rule 4 — Dead code (no unused exports) +```bash +codegraph roles --role dead --file -T --json +codegraph exports -T --json +``` +**Fail:** dead exports or unreferenced symbols. + +#### Rule 5 — Resource hygiene +```bash +codegraph ast --kind call --file -T --json +``` +Find `addEventListener`, `setInterval`, `setTimeout`, `createReadStream`, `.on(` — verify matching cleanup. **Fail:** resource acquired without cleanup. + +#### Rule 6 — Immutability +```bash +codegraph dataflow -T --json +``` +Also grep for mutation patterns: +```bash +grep -n "\.push(\|\.splice(\|\.sort(\|\.reverse(\|delete " +``` +**Fail:** direct mutation of function arguments or external state. + +### Pillar II: Data & Type Sovereignty + +#### Rule 7 — Magic values +```bash +codegraph ast --kind string --file -T --json +``` +Also grep for numeric literals in logic branches: +```bash +grep -nE "[^a-zA-Z_][0-9]{2,}[^a-zA-Z_]" +``` +Filter out imports, log format strings, test assertions. **Warn:** present. **Fail:** in if/switch conditions. + +#### Rule 8 — Boundary validation +```bash +codegraph roles --role entry --file -T --json +codegraph where --file -T --json +``` +For entry-point functions, verify schema validation before processing. **Fail:** missing validation at system boundaries. + +#### Rule 9 — Secret hygiene +```bash +grep -niE "api.?key|secret|password|token|credential" +``` +Verify values come from config/env, not literals. **Fail:** hardcoded secret values. + +#### Rule 10 — Error integrity (no empty catches) +```bash +grep -nA2 "catch" +``` +**Fail:** empty catch block or catch with only `// ignore` or `// TODO`. + +### Pillar III: Ecosystem Synergy + +#### Rule 11 — DRY (no duplicated logic) +```bash +codegraph search "" -T --json +codegraph co-change -T --json +``` +Find semantically similar functions. If `codegraph search` fails (no embeddings), use grep for function signature patterns. **Warn:** similar patterns. **Fail:** near-verbatim copy. + +> Note: requires embeddings from `/titan-recon`. If `titan-state.json → embeddingsAvailable` is false, skip semantic search and note it. + +#### Rule 12 — Naming symmetry +```bash +codegraph where --file -T --json +``` +Scan function names in the domain. Flag mixed `get`/`fetch`/`retrieve` or `create`/`make`/`build` for the same concept. **Warn:** inconsistent. **Advisory** — not a fail condition. + +#### Rule 13 — Config over code +```bash +codegraph deps --json +``` +Also grep: +```bash +grep -n "process.env\|NODE_ENV\|production\|development" +``` +Verify env-specific behavior driven by config, not inline branches. **Warn:** inline env branch. + +### Pillar IV: The Quality Vigil + +#### Rule 14 — Naming quality +```bash +codegraph where --file -T --json +``` +Flag vague names: `data`, `obj`, `temp`, `res`, `val`, `item`, `result`, single-letter vars (except `i/j/k`). **Warn:** present. **Advisory.** + +#### Rule 15 — Structured logging +```bash +codegraph ast --kind call --file -T --json +``` +Also grep: +```bash +grep -n "console\.\(log\|warn\|error\|info\)" +``` +**Warn:** console.log in source files. **Fail:** in production code paths (non-debug, non-test). + +#### Rule 16 — Testability +```bash +codegraph fn-impact -T --json +codegraph query -T --json +``` +High fan-out correlates with many mocks needed. Also read corresponding test file and count mock/stub/spy calls. **Warn:** > 10 mocks. **Fail:** > 15 mocks. + +#### Rule 17 — Critical path coverage +```bash +codegraph roles --role core --file -T --json +``` +If file contains core symbols (high fan-in), note whether test files exist for it. **Warn:** core symbol with no test file. **Advisory.** + +### Audit trail (per file) + +For every file, the NDJSON record MUST include: +- **Verdict** and **pillar verdicts** (pass/warn/fail per pillar) +- **All metrics** from `codegraph complexity --health` (cognitive, cyclomatic, nesting, MI, halstead.bugs, halstead.effort, loc.sloc) +- **Violation list** with rule number, detail, and level +- **Recommendation** for FAIL/DECOMPOSE targets + +Codegraph provides all the data needed for a verifiable audit — no need to manually traverse files for line counts or nesting proof. + +--- + +## Step 2 — Batch audit loop + +For each pending batch (from `titan-state.json`): + +### 2a. Save pre-batch snapshot +```bash +codegraph snapshot save titan-batch- +``` +Delete the previous batch snapshot if it exists: +```bash +codegraph snapshot delete titan-batch- +``` + +### 2b. Collect all metrics in one call +```bash +codegraph batch complexity ... -T --json +``` +This returns complexity + health metrics for all targets in one call. Parse the results. + +For deeper context on high-risk targets: +```bash +codegraph batch context ... -T --json +``` + +### 2c. Run Pillar I checks +For each file in the batch: +- Parse complexity metrics from batch output (Rule 1 — all 7 metric thresholds) +- Run AST queries for async hygiene (Rule 2), resource cleanup (Rule 5) +- Check boundary violations (Rule 3): `codegraph check --boundaries -T --json` +- Check dead code (Rule 4): `codegraph roles --role dead --file -T --json` +- Check immutability (Rule 6): `codegraph dataflow` + grep + +### 2d. Run Pillar II checks +For each file: +- Magic values (Rule 7): `codegraph ast --kind string` + grep +- Boundary validation (Rule 8): check entry points +- Secret hygiene (Rule 9): grep +- Empty catches (Rule 10): grep + +### 2e. Run Pillar III checks +- DRY (Rule 11): `codegraph search` (if embeddings available) + `co-change` +- Naming symmetry (Rule 12): `codegraph where --file` +- Config over code (Rule 13): `codegraph deps` + grep + +### 2f. Run Pillar IV checks +- Naming quality (Rule 14): `codegraph where --file` +- Structured logging (Rule 15): `codegraph ast --kind call` + grep +- Testability (Rule 16): `codegraph fn-impact` + test file mock count +- Critical path coverage (Rule 17): `codegraph roles --role core` + +### 2g. Score each target + +| Verdict | Condition | +|---------|-----------| +| **PASS** | No fail-level violations | +| **WARN** | Warn-level violations only | +| **FAIL** | One or more fail-level violations | +| **DECOMPOSE** | Complexity fail + `halstead.bugs` > 1.0 + high fan-out (needs splitting) | + +For FAIL/DECOMPOSE targets, capture blast radius: +```bash +codegraph fn-impact -T --json +``` + +### 2h. Write batch results + +Append to `.codegraph/titan/gauntlet.ndjson` (one line per target): + +```json +{"target": "", "file": "", "verdict": "FAIL", "pillarVerdicts": {"I": "fail", "II": "warn", "III": "pass", "IV": "pass"}, "metrics": {"cognitive": 28, "cyclomatic": 15, "maxNesting": 4, "mi": 32, "halsteadEffort": 12000, "halsteadBugs": 1.2, "sloc": 85}, "violations": [{"rule": 1, "pillar": "I", "metric": "cognitive", "detail": "28 > 30 threshold", "level": "fail"}], "blastRadius": {"direct": 5, "transitive": 18}, "recommendation": "Split: halstead.bugs 1.2 suggests ~1 defect. Separate validation from I/O."} +``` + +### 2i. Update state machine + +Update `titan-state.json`: +- Set batch status to `"completed"` +- Increment `progress.audited`, `.passed`, `.warned`, `.failed` +- Add entries to `fileAudits` map +- Update `snapshots.lastBatch` +- Update `lastUpdated` + +### 2j. Progress check + +Print: `Batch N/M: X pass, Y warn, Z fail` + +**Context budget:** If context is growing large: +1. Write all state to disk +2. Print: `Context budget reached after Batch N. Run /titan-gauntlet to continue.` +3. Stop. + +--- + +## Step 3 — Clean up batch snapshots + +After all batches complete, delete the last batch snapshot: +```bash +codegraph snapshot delete titan-batch- +``` +Keep `titan-baseline` — GATE may need it. + +If stopping early for context, keep the last batch snapshot for safety. + +--- + +## Step 4 — Aggregate and report + +Compute from `gauntlet.ndjson`: +- Pass / Warn / Fail / Decompose counts +- Top 10 worst offenders (by violation count or `halstead.bugs`) +- Most common violations by pillar +- Files with the most failing functions + +Write `.codegraph/titan/gauntlet-summary.json`: +```json +{ + "phase": "gauntlet", + "timestamp": "", + "complete": true, + "summary": {"totalAudited": 0, "pass": 0, "warn": 0, "fail": 0, "decompose": 0}, + "worstOffenders": [], + "commonViolations": {"I": [], "II": [], "III": [], "IV": []} +} +``` + +Set `"complete": false` if stopping early. + +Print summary to user: +- Pass/Warn/Fail/Decompose counts +- Top 5 worst (with their `halstead.bugs` and `mi` scores) +- Most common violation per pillar +- Next step: `/titan-gauntlet` to continue (if incomplete) or `/titan-sync` + +--- + +## Rules + +- **Batch processing is mandatory.** Never audit more than `$ARGUMENTS` targets at once. +- **Write NDJSON incrementally.** Partial results survive crashes. +- **Always use `--json` and `-T`** on codegraph commands. +- **Use `codegraph batch `** for multi-target queries — not separate calls. +- **Leverage `--health` and `--above-threshold`** — they give you all metrics in one call. +- **Context budget:** Stop at ~80%, save state, tell user to re-invoke. +- **Lint runs once in GATE**, not per-batch here. Don't run `npm run lint`. +- Advisory rules (12, 14, 17) produce warnings, never failures. +- Dead symbols from RECON should be flagged for removal, not skipped. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-gauntlet/SKILL.md`. Adjust thresholds or rules after dogfooding. diff --git a/docs/examples/claude-code-skills/titan-recon/SKILL.md b/docs/examples/claude-code-skills/titan-recon/SKILL.md new file mode 100644 index 00000000..b1ae5428 --- /dev/null +++ b/docs/examples/claude-code-skills/titan-recon/SKILL.md @@ -0,0 +1,316 @@ +--- +name: titan-recon +description: Map a codebase's dependency graph, identify hotspots, name logical domains, propose work batches, and produce a ranked priority queue for autonomous cleanup (Titan Paradigm Phase 1) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan RECON — Codebase Reconnaissance + +You are running the **RECON** phase of the Titan Paradigm on the target at `$ARGUMENTS` (default: `.`). + +Your goal: map the dependency graph, identify structural hotspots, name logical domains, produce a global architecture document, propose work batches, and initialize the session state. Everything you produce feeds downstream phases (GAUNTLET, SYNC, GATE) via artifacts in `.codegraph/titan/`. + +> **Context budget:** Every codegraph command MUST use `--json` to keep output compact. Never dump raw CLI tables into context — parse JSON and extract only what you need. + +--- + +## Step 0 — Pre-flight: worktree and sync + +1. **Check for worktree isolation:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If you are NOT in a worktree, **stop:** "Run `/worktree` first. Titan phases write artifacts that should not interfere with other work." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + If there are merge conflicts, stop and ask the user to resolve them. + +--- + +## Step 1 — Build the graph + +```bash +codegraph build $ARGUMENTS +``` + +Record: file count, node count, edge count, engine. + +--- + +## Step 2 — Generate embeddings (for DRY detection in GAUNTLET) + +```bash +codegraph embed -m minilm +``` + +This enables `codegraph search` for duplicate code detection in downstream phases. If it fails (e.g., missing model), note it and continue — DRY checks will be grep-only. + +--- + +## Step 3 — Collect baseline metrics + +Run in parallel: + +```bash +codegraph stats --json +codegraph structure --depth 2 --json +``` + +Extract from `stats`: `totalNodes`, `totalEdges`, `totalFiles`, `qualityScore`, `avgFanIn`, `avgFanOut`. + +Extract from `structure`: top 10 directories by file count, directories with cohesion < 0.3 (tangled). + +--- + +## Step 4 — Build the priority queue + +```bash +codegraph triage -T --limit 100 --json +``` + +Risk-ranked list combining connectivity, complexity, and role classification. Truncate to top 50 for the artifact if >100 items. + +--- + +## Step 5 — Community and drift analysis + +```bash +codegraph communities -T --json +codegraph communities --drift -T --json +``` + +Extract: community count, top 5 largest (member count + key files), drift warnings. + +--- + +## Step 6 — High-traffic files and role classification + +```bash +codegraph map --limit 30 -T --json +codegraph roles --role core -T --json +codegraph roles --role dead -T --json +``` + +Count core symbols (high fan-in) and dead symbols (zero fan-in, not exported). + +--- + +## Step 7 — Complexity health baseline + +Get the full metrics picture across the codebase — this is what makes codegraph powerful: + +```bash +codegraph complexity --health --above-threshold -T --json --limit 50 +``` + +This returns only functions exceeding configured warn thresholds, with all available metrics per function: +- **Structural:** `cognitive`, `cyclomatic`, `maxNesting` +- **Halstead:** `volume`, `difficulty`, `effort`, `bugs` (estimated bug count) +- **Size:** `loc.sloc`, `loc.commentLines` +- **Composite:** `mi` (Maintainability Index) + +Also get the worst offenders by different metrics: + +```bash +codegraph complexity --health --sort effort -T --json --limit 10 +codegraph complexity --health --sort bugs -T --json --limit 10 +codegraph complexity --health --sort mi -T --json --limit 10 +``` + +These three views reveal different quality dimensions: `effort` = hardest to understand, `bugs` = most likely to contain defects, `mi` = worst overall maintainability. + +--- + +## Step 8 — Domain inventory + +Using community detection (Step 5) and directory structure (Step 3), **name** the logical domains. A domain is a cohesive group of files serving a single concern. + +For each domain, record: +- **Name:** use the codebase's own vocabulary (directory names, module names) +- **Root directories** +- **File count** +- **Key symbols:** 3-5 most-connected symbols (from triage) +- **Community IDs:** which communities map to this domain +- **Health:** cohesion score, drift warnings, cycle participation + +For large domains, map inter-domain dependencies using key files (not directories — `deps` takes a file path): + +```bash +codegraph deps --json +``` + +--- + +## Step 9 — Global architecture document + +Write `.codegraph/titan/GLOBAL_ARCH.md`: + +```markdown +# Global Architecture + +**Date:** +**Codebase:** ( files, symbols) + +## Domain Map + +| Domain | Root Dirs | Files | Core Symbols | Health | +|--------|-----------|-------|-------------|--------| +| ... | ... | ... | ... | cohesion, drift? | + +## Dependency Flow + + + +## Shared Types and Interfaces + + + +## Architectural Rules + + + +## Cycles + + +``` + +--- + +## Step 10 — Propose work batches + +Decompose the priority queue into **work batches** of ~5-15 files each: +- Stay within a single domain where possible +- Group tightly-coupled files together (from communities) +- Order by priority: highest-risk domains first +- Note dependencies: "batch N depends on batch M being done first" + +--- + +## Step 11 — Save baseline snapshot + +```bash +codegraph snapshot save titan-baseline +``` + +This is the rollback point. If anything goes wrong downstream, any skill can restore it. + +--- + +## Step 12 — Write the state file + +Create `.codegraph/titan/titan-state.json` — the single source of truth for the entire pipeline: + +```bash +mkdir -p .codegraph/titan +``` + +```json +{ + "version": 1, + "initialized": "", + "lastUpdated": "", + "target": "", + "currentPhase": "recon", + "snapshots": { + "baseline": "titan-baseline", + "lastBatch": null + }, + "embeddingsAvailable": true, + "stats": { + "totalFiles": 0, + "totalNodes": 0, + "totalEdges": 0, + "qualityScore": 0, + "avgFanIn": 0, + "avgFanOut": 0 + }, + "healthBaseline": { + "functionsAboveThreshold": 0, + "worstByEffort": [""], + "worstByBugs": [""], + "worstByMI": [""] + }, + "domains": [ + { + "name": "", + "rootDirs": [""], + "fileCount": 0, + "status": "pending", + "audited": 0, + "passed": 0, + "failed": 0 + } + ], + "batches": [ + { + "id": 1, + "domain": "", + "files": [""], + "status": "pending", + "dependsOn": [], + "priority": 1 + } + ], + "priorityQueue": [ + { + "rank": 1, + "target": "", + "riskScore": 0, + "reason": "" + } + ], + "communities": { + "count": 0, + "driftWarnings": [] + }, + "roles": { + "coreCount": 0, + "deadCount": 0, + "deadSymbols": [""] + }, + "hotFiles": [""], + "tangledDirs": [""], + "fileAudits": {}, + "progress": { + "totalFiles": 0, + "audited": 0, + "passed": 0, + "warned": 0, + "failed": 0, + "fixed": 0 + } +} +``` + +--- + +## Step 13 — Report to user + +Print a concise summary: +- Graph size and quality score +- Domains identified (count and names) +- Complexity health: count of functions above threshold, top 3 worst by `halstead.bugs` +- Work batches proposed (count) +- Top 5 priority targets +- Dead symbol count +- Drift warnings +- Path to artifacts: `.codegraph/titan/titan-state.json` and `.codegraph/titan/GLOBAL_ARCH.md` +- Next step: `/titan-gauntlet` to audit the priority queue + +--- + +## Rules + +- **Always use `--json` and `-T`** on codegraph commands. +- **Never paste raw JSON** into your response — parse and extract. +- **Write artifacts before reporting.** +- If any command fails, note it and continue with partial data. +- **Domain naming** uses the codebase's own vocabulary. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-recon/SKILL.md`. Edit it if you find improvements during execution. diff --git a/docs/examples/claude-code-skills/titan-reset/SKILL.md b/docs/examples/claude-code-skills/titan-reset/SKILL.md new file mode 100644 index 00000000..2c99704d --- /dev/null +++ b/docs/examples/claude-code-skills/titan-reset/SKILL.md @@ -0,0 +1,89 @@ +--- +name: titan-reset +description: Clean up all Titan Paradigm artifacts and snapshots, restoring the codebase to pre-Titan state +argument-hint: <--keep-graph to preserve the codegraph database> +allowed-tools: Bash, Read, Write, Grep +--- + +# Titan RESET — Pipeline Cleanup + +You are resetting the Titan Paradigm pipeline, removing all artifacts and restoring the codebase to its pre-Titan state. + +--- + +## Step 1 — Restore baseline snapshot (if available) + +```bash +codegraph snapshot restore titan-baseline 2>/dev/null && echo "Baseline restored" || echo "No baseline snapshot found" +``` + +This restores the graph database to its pre-GAUNTLET state. + +--- + +## Step 2 — Delete all Titan snapshots + +```bash +codegraph snapshot delete titan-baseline 2>/dev/null +``` + +Also delete any batch snapshots: + +```bash +codegraph snapshot delete titan-batch-1 2>/dev/null +codegraph snapshot delete titan-batch-2 2>/dev/null +codegraph snapshot delete titan-batch-3 2>/dev/null +codegraph snapshot delete titan-batch-4 2>/dev/null +codegraph snapshot delete titan-batch-5 2>/dev/null +codegraph snapshot delete titan-batch-6 2>/dev/null +codegraph snapshot delete titan-batch-7 2>/dev/null +codegraph snapshot delete titan-batch-8 2>/dev/null +codegraph snapshot delete titan-batch-9 2>/dev/null +codegraph snapshot delete titan-batch-10 2>/dev/null +``` + +(Errors are expected for snapshots that don't exist — ignore them.) + +--- + +## Step 3 — Remove all Titan artifacts + +```bash +rm -rf .codegraph/titan/ +``` + +This removes: +- `titan-state.json` — session state +- `GLOBAL_ARCH.md` — architecture document +- `gauntlet.ndjson` — audit results +- `gauntlet-summary.json` — aggregated results +- `sync.json` — execution plan +- `gate-log.ndjson` — gate audit trail + +--- + +## Step 4 — Rebuild graph (unless --keep-graph) + +If `$ARGUMENTS` does NOT contain `--keep-graph`: + +```bash +codegraph build +``` + +This ensures the graph reflects the current state of the codebase without any Titan-era corruption. + +If `$ARGUMENTS` contains `--keep-graph`, skip this step. + +--- + +## Step 5 — Report + +``` +Titan pipeline reset complete. + - Baseline snapshot: restored and deleted + - Batch snapshots: deleted + - Artifacts: removed (.codegraph/titan/) + - Graph: rebuilt (clean state) + +To start a fresh Titan pipeline, run /titan-recon +``` diff --git a/docs/examples/claude-code-skills/titan-sync/SKILL.md b/docs/examples/claude-code-skills/titan-sync/SKILL.md new file mode 100644 index 00000000..6b93e0e4 --- /dev/null +++ b/docs/examples/claude-code-skills/titan-sync/SKILL.md @@ -0,0 +1,233 @@ +--- +name: titan-sync +description: Identify overlapping fixes across audit results, plan shared abstractions, produce an ordered execution plan with logical commit grouping (Titan Paradigm Phase 3) +argument-hint: +allowed-tools: Bash, Read, Write, Glob, Grep, Edit +--- + +# Titan GLOBAL SYNC — Cross-Cutting Analysis & Execution Plan + +You are running the **GLOBAL SYNC** phase of the Titan Paradigm. + +Your goal: analyze GAUNTLET results to find overlapping problems, identify shared abstractions that should be built *before* individual fixes, group changes into logical commits, and produce an ordered execution plan. + +> **Context budget:** This phase reads artifacts, not source. Keep codegraph queries targeted — only for specific relationship questions between failing targets. + +--- + +## Step 0 — Pre-flight + +1. **Worktree check:** + ```bash + git rev-parse --show-toplevel && git worktree list + ``` + If not in a worktree, stop: "Run `/worktree` first." + +2. **Sync with main:** + ```bash + git fetch origin main && git merge origin/main --no-edit + ``` + +3. **Load artifacts.** Read: + - `.codegraph/titan/titan-state.json` — state, domains, batches, file audits + - `.codegraph/titan/GLOBAL_ARCH.md` — architecture, dependency flow, shared types + - `.codegraph/titan/gauntlet.ndjson` — per-target audit details + - `.codegraph/titan/gauntlet-summary.json` — aggregated results + +4. **Validate state.** If `titan-state.json` fails to parse, stop: "State file corrupted. Run `/titan-reset`." + +5. **Check GAUNTLET completeness.** If `gauntlet-summary.json` has `"complete": false`: + > "GAUNTLET incomplete (/ batches). SYNC will plan based on known failures only. Run `/titan-gauntlet` first for a complete plan." + +6. **Extract.** From artifacts, collect: + - All FAIL and DECOMPOSE targets with violations and files + - Common violation patterns by pillar + - Community assignments + - Dead symbols (cleanup candidates) + - Domain boundaries and dependency flow + +--- + +## Step 1 — Find dependency clusters among failing targets + +For FAIL/DECOMPOSE targets that share a file or community, check connections: + +```bash +codegraph path -T --json +``` + +Group connected failures into **clusters**. Also check for cycles among them: + +```bash +codegraph cycles --functions --json +``` + +Filter to cycles including at least one FAIL/DECOMPOSE target. + +--- + +## Step 2 — Identify shared dependencies and ownership + +For each cluster, find what they share: + +```bash +codegraph deps --json +``` + +Look for: +- **Shared imports:** multiple failures import the same module → interface extraction candidate +- **Shared callers:** multiple failures called by the same function → caller needs updating +- **Common violations:** similar pillar violations across targets + +Check **code ownership** for cross-team coordination: + +```bash +codegraph owners -T --json +``` + +If different teams own files in the same cluster, note the coordination requirement. + +Run **branch structural diff** to see what's already changed: + +```bash +codegraph branch-compare main HEAD -T --json +``` + +Avoid re-auditing or conflicting with in-progress work. + +--- + +## Step 3 — Detect extraction candidates + +For DECOMPOSE targets: + +```bash +codegraph context -T --json +codegraph ast --kind call --file -T --json +``` + +Look for: +- Functions with multiple responsibilities (high cognitive + high fan-out + high `halstead.bugs`) +- Repeated patterns across failures (similar call chains) +- God files (many failing functions → split along community boundaries) + +--- + +## Step 4 — Plan shared abstractions + +Identify what to build BEFORE individual fixes: + +1. **Interface extractions** — shared dependency → extract interface +2. **Utility extractions** — repeated patterns → shared utility +3. **Module splits** — god files → split by community structure +4. **Cycle breaks** — circular deps → identify weakest link + +For each, check blast radius: +```bash +codegraph fn-impact -T --json +``` + +--- + +## Step 5 — Build execution order with logical commits + +### Phases (in order) + +1. **Dead code cleanup** — zero risk, reduces noise + - Commit: `chore: remove dead code` +2. **Shared abstractions** — before individual fixes + - One commit per abstraction: `refactor: extract X from Y` +3. **Cycle breaks** — unblocks dependent targets + - One commit per break: `refactor: break cycle between X and Y` +4. **Decompositions** — highest risk, after abstractions + - One commit per decomposition: `refactor: split X into A and B` +5. **Fail fixes** — ordered by blast radius (lowest first) + - Group by domain: `fix: address quality issues in ` +6. **Warn improvements** — optional, lowest priority + - Group by domain: `refactor: address warnings in ` + +### Ordering within each phase +- Dependencies first (if A depends on B, fix B first) +- Lower blast radius first +- Same community together + +### Each commit should: +- Touch one domain where possible +- Address one concern +- Be independently revertible +- Run `/titan-gate` before committing + +--- + +## Step 6 — Write the SYNC artifact + +Write `.codegraph/titan/sync.json`: + +```json +{ + "phase": "sync", + "timestamp": "", + "clusters": [ + { + "id": 1, + "name": "", + "targets": ["t1", "t2"], + "sharedDeps": ["mod"], + "hasCycle": false, + "owners": ["team-a", "team-b"], + "proposedAction": "Extract interface" + } + ], + "abstractions": [ + { + "type": "interface_extraction|utility_extraction|module_split|cycle_break", + "description": "...", + "source": "", + "unblocks": ["t1", "t2"], + "blastRadius": 0, + "commit": "refactor: ..." + } + ], + "executionOrder": [ + { + "phase": 1, + "label": "Dead code cleanup", + "targets": ["sym1", "sym2"], + "risk": "none", + "commit": "chore: remove dead code", + "dependencies": [] + } + ], + "deadCodeTargets": [""], + "cyclesInvolvingFailures": [] +} +``` + +Update `titan-state.json`: set `currentPhase` to `"sync"`. + +--- + +## Step 7 — Report to user + +Print: +- Dependency clusters found (count) +- Shared abstractions proposed (count) +- Execution order summary (phases, target counts, estimated commits) +- Key insight: what SYNC prevented (e.g., "3 targets share configLoader — without SYNC, 3 conflicting refactors") +- Path to `sync.json` +- Next step: start Phase 1 (dead code cleanup), validate each commit with `/titan-gate` + +--- + +## Rules + +- **Read artifacts, don't re-scan.** Codegraph commands only for targeted relationship queries. +- **Always use `--json` and `-T`.** +- **The execution order is the key output.** +- **Logical commits matter.** Never mix concerns. +- If GAUNTLET found zero failures, produce minimal plan (dead code + warnings only). +- Keep `codegraph path` queries targeted — same file or community only. + +## Self-Improvement + +This skill lives at `.claude/skills/titan-sync/SKILL.md`. Edit if clustering misses connections or execution order causes conflicts. diff --git a/docs/use-cases/titan-paradigm.md b/docs/use-cases/titan-paradigm.md index 73cb1fbb..5f494c0d 100644 --- a/docs/use-cases/titan-paradigm.md +++ b/docs/use-cases/titan-paradigm.md @@ -31,6 +31,8 @@ That's exactly what codegraph provides. ### RECON: Map the dependency graph, prioritize high-traffic files +> **Claude Code skill:** [`/titan-recon`](../examples/claude-code-skills/titan-recon/SKILL.md) automates this entire phase — builds the graph, names domains, produces `GLOBAL_ARCH.md`, and writes a ranked priority queue with work batches. + This is codegraph's bread and butter. The RECON phase needs a dependency graph — codegraph **is** a dependency graph. ```bash @@ -81,6 +83,8 @@ codegraph where resolveImports ### THE GAUNTLET: Audit every file against strict standards +> **Claude Code skill:** [`/titan-gauntlet`](../examples/claude-code-skills/titan-gauntlet/SKILL.md) implements all 4 pillars (31 rules) with batch processing, multi-agent dispatch, context budget management, and session resumability via `titan-state.json`. + The Gauntlet needs each sub-agent to understand what a file does, what depends on it, and how risky changes are. The `audit` command gives each agent everything in one call: ```bash @@ -125,6 +129,8 @@ codegraph audit parseConfig -T --json > audit/parser.json ### GLOBAL SYNC: Identify overlapping fixes, build shared abstractions +> **Claude Code skill:** [`/titan-sync`](../examples/claude-code-skills/titan-sync/SKILL.md) reads GAUNTLET artifacts, finds dependency clusters among failures, plans shared abstractions, and produces an ordered execution plan with logical commit grouping. + Before the swarm starts coding, a lead agent needs to see the big picture: which files are tightly coupled, where circular dependencies exist, and what shared abstractions could be extracted. ```bash @@ -150,6 +156,8 @@ The lead agent can use `cycles` to identify dependency knots, `path` to understa ### STATE MACHINE: Track changes, verify impact, enable rollback +> **Claude Code skill:** [`/titan-gate`](../examples/claude-code-skills/titan-gate/SKILL.md) validates staged changes against codegraph thresholds AND the project's own lint/build/test. Auto-rolls back on failure. Maintains an append-only audit trail in `gate-log.ndjson`. + The State Machine phase needs yes/no answers: "Did this change introduce a cycle?" "Did blast radius exceed N?" "Did any boundary get violated?" The `check` command provides exactly this: ```bash @@ -251,10 +259,82 @@ Several planned features would make codegraph even more powerful for the Titan P --- +## Claude Code Skills — Ready-Made Titan Pipeline + +We've built five Claude Code skills that implement the full Titan Paradigm using codegraph. Each phase writes structured JSON artifacts to `.codegraph/titan/` that the next phase reads — this keeps context usage minimal even on large codebases. + +``` +/titan-recon → titan-state.json + GLOBAL_ARCH.md + │ + ▼ +/titan-gauntlet → gauntlet.ndjson (batches of 5, resumes across sessions) + │ + ▼ +/titan-sync → sync.json (execution plan with logical commits) + │ + ▼ +/titan-gate (validates each commit: codegraph + lint/build/test) + +/titan-reset (escape hatch: clean up all artifacts and snapshots) +``` + +| Skill | Phase | What it does | +|-------|-------|-------------| +| `/titan-recon` | RECON | Builds graph + embeddings, runs complexity health baseline (`--health --above-threshold`), identifies domains, produces priority queue + work batches + `GLOBAL_ARCH.md`, saves baseline snapshot | +| `/titan-gauntlet` | GAUNTLET | 4-pillar audit (17 rules) leveraging codegraph's full metrics (`cognitive`, `cyclomatic`, `halstead.bugs`, `halstead.effort`, `mi`, `loc.sloc`). Batches of 5 (configurable), NDJSON incremental writes, resumes across sessions via `titan-state.json` | +| `/titan-sync` | GLOBAL SYNC | Finds dependency clusters among failures using `codegraph path` + `owners` + `branch-compare`. Plans shared abstractions, produces ordered execution plan with logical commit grouping | +| `/titan-gate` | STATE MACHINE | Validates staged changes: `codegraph check --staged --cycles --blast-radius 30 --boundaries` + project lint/build/test. Auto-rollback with snapshot restore on failure. Append-only audit trail | +| `/titan-reset` | ESCAPE HATCH | Restores baseline snapshot, deletes all Titan artifacts and snapshots, rebuilds graph clean | + +### Context window management + +The original Titan Paradigm prompt struggles with large codebases because a single agent cannot hold everything in context. These skills solve this two ways: + +1. **Artifact bridging:** each phase writes compact JSON artifacts. The next phase reads only those — not the full source. Works across separate conversations too. +2. **Batch processing with resume:** the GAUNTLET audits 5 files at a time (configurable), writes to NDJSON between batches, and stops at ~80% context usage. Re-invoking `/titan-gauntlet` resumes from the next pending batch automatically. + +### Snapshot lifecycle + +Codegraph snapshots provide instant graph database backup/restore at each stage: + +| Snapshot | Created by | Restored by | Deleted by | +|----------|-----------|------------|-----------| +| `titan-baseline` | RECON | GATE (on failure) | GATE (on final success) or RESET | +| `titan-batch-N` | GAUNTLET (per batch) | GATE (on failure) | GAUNTLET (next batch replaces it) or RESET | + +See [Claude Code Skills Example](../examples/claude-code-skills/) for installation and usage. + +--- + ## What's Next All six recommendations from v2.5.0 — `audit`, `batch`, `triage`, `check`, `snapshot`, and MCP orchestration tools — shipped in v2.6.0. The remaining enhancements that would make codegraph even more powerful for the Titan Paradigm are in the LLM integration roadmap: +### New backlog items surfaced by skill development + +Building and reviewing the Titan skills revealed gaps where codegraph could provide first-class support instead of relying on grep patterns or manual analysis: + +**Detection gaps — rules that currently fall back to grep:** + +| Feature | GAUNTLET Rule | How it helps | Backlog candidate | +|---------|--------------|-------------|-------------------| +| **Async hygiene detection** | Pillar I, Rule 2 | AST-level detection of uncaught promises, `.then()` without `.catch()`, and async functions without `try/catch`. Currently grep-based | `codegraph check --floating-promises` | +| **Resource leak detection** | Pillar I, Rule 5 | AST detection of `addEventListener`/`setInterval`/`createReadStream` without matching cleanup. Currently grep-based and fragile | `codegraph check --resource-leaks` | +| **Mutation tracking** | Pillar I, Rule 6 | Detect functions that mutate their arguments or external state. `codegraph dataflow` tracks flow but not mutation specifically | Enhancement to `dataflow` | +| **Empty catch detection** | Pillar II, Rule 10 | Find empty `catch` blocks or catch blocks with only comments. AST-level, not grep | `codegraph check --empty-catches` | +| **Magic literal detection** | Pillar II, Rule 7 | Find hardcoded strings/numbers in logic branches (if/switch conditions) vs constants. Currently uses `ast --kind string` but misses numeric literals | Extend `ast` with `--kind literal` | +| **Duplicate code detection** | Pillar III, Rule 11 | Identify semantically similar functions — near-duplicates to merge. `codegraph search` finds related code but doesn't specifically flag duplicates | `codegraph duplicates` or `--duplicates` on `search` | + +**Orchestration gaps — pipeline features that could be built into codegraph:** + +| Feature | Phase | How it helps | Backlog candidate | +|---------|-------|-------------|-------------------| +| **Domain inventory command** | RECON | Auto-name logical domains from communities + directory structure. Currently the skill infers this manually | `codegraph domains` combining `communities`, `structure`, and `deps` | +| **Session state persistence** | ALL | Built-in state tracking for multi-phase pipelines — per-file audit status, batch progress, execution phase. Currently the skills manage `titan-state.json` themselves | `codegraph session init/update/status` | +| **Batch complexity with `--health`** | GAUNTLET | `codegraph batch complexity` returns metrics but the skill needs `--health` data (Halstead, MI) for each target in one call. Currently must run separate per-file calls for `--health` mode | `codegraph batch complexity --health` support | +| **NDJSON integrity check** | GAUNTLET/GATE | Validate that `.ndjson` artifacts have no truncated lines from partial writes. A single malformed line corrupts downstream parsing | `codegraph session validate` or built-in NDJSON repair | +| **Concurrent-safe graph operations** | GAUNTLET | Multiple agents running codegraph simultaneously corrupt the SQLite database. A locking mechanism or read-only mode would enable safe parallel querying | `--read-only` flag on query commands, or WAL mode with proper locking | + ### LLM-enhanced features (Roadmap Phase 4+) | Feature | How it helps the Titan Paradigm | @@ -276,7 +356,25 @@ cd your-project codegraph build ``` -Then wire your orchestrator's RECON phase to start with: +### With Claude Code skills (recommended) + +Copy the skills into your project and run the pipeline: + +```bash +# Install skills +cp -r node_modules/@optave/codegraph/docs/examples/claude-code-skills/titan-* .claude/skills/ + +# In Claude Code: +/titan-recon # Map the codebase, produce priority queue +/titan-gauntlet 5 # Audit top targets in batches of 5 +/titan-sync # Plan shared abstractions and execution order +# ... make changes ... +/titan-gate # Validate before each commit +``` + +### With raw commands + +Wire your orchestrator's RECON phase to start with: ```bash codegraph triage -T --limit 50 --json # Ranked priority queue (one call) From a3ee61007e7c51f2bdb2367a083964cb78da2b6e Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:39:07 -0600 Subject: [PATCH 18/28] fix: remove duplicate declarations in connection.js from merge --- src/db/connection.js | 49 -------------------------------------------- 1 file changed, 49 deletions(-) diff --git a/src/db/connection.js b/src/db/connection.js index 962dffb6..75ee4a6d 100644 --- a/src/db/connection.js +++ b/src/db/connection.js @@ -56,55 +56,6 @@ export function _resetRepoRootCache() { _cachedRepoRootCwd = undefined; } -let _cachedRepoRoot; // undefined = not computed, null = not a git repo -let _cachedRepoRootCwd; // cwd at the time the cache was populated - -/** - * Return the git worktree/repo root for the given directory (or cwd). - * Uses `git rev-parse --show-toplevel` which returns the correct root - * for both regular repos and git worktrees. - * Results are cached per-process when called without arguments. - * The cache is keyed on cwd so it invalidates if the working directory changes - * (e.g. MCP server serving multiple sessions). - * @param {string} [fromDir] - Directory to resolve from (defaults to cwd) - * @returns {string | null} Absolute path to repo root, or null if not in a git repo - */ -export function findRepoRoot(fromDir) { - const dir = fromDir || process.cwd(); - if (!fromDir && _cachedRepoRoot !== undefined && _cachedRepoRootCwd === dir) { - return _cachedRepoRoot; - } - let root = null; - try { - const raw = execFileSync('git', ['rev-parse', '--show-toplevel'], { - cwd: dir, - encoding: 'utf-8', - stdio: ['pipe', 'pipe', 'pipe'], - }).trim(); - // Use realpathSync to resolve symlinks (macOS /var → /private/var) and - // 8.3 short names (Windows RUNNER~1 → runneradmin) so the ceiling path - // matches the realpathSync'd dir in findDbPath. - try { - root = fs.realpathSync(raw); - } catch { - root = path.resolve(raw); - } - } catch { - root = null; - } - if (!fromDir) { - _cachedRepoRoot = root; - _cachedRepoRootCwd = dir; - } - return root; -} - -/** Reset the cached repo root (for testing). */ -export function _resetRepoRootCache() { - _cachedRepoRoot = undefined; - _cachedRepoRootCwd = undefined; -} - function isProcessAlive(pid) { try { process.kill(pid, 0); From cc6dafd25d29b5d96f259a326d69227f62d823e3 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:42:53 -0600 Subject: [PATCH 19/28] docs: add /review skill for automated PR review sweeps Skill checks all open PRs, resolves conflicts (via merge, never rebase), fixes CI failures, addresses all reviewer comments (Claude + Greptile), replies to each comment, and re-triggers reviewers in a loop until clean. --- .claude/skills/review/SKILL.md | 172 +++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 .claude/skills/review/SKILL.md diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md new file mode 100644 index 00000000..d85503fa --- /dev/null +++ b/.claude/skills/review/SKILL.md @@ -0,0 +1,172 @@ +--- +name: review +description: Check all open PRs, resolve conflicts, update branches, address Claude and Greptile review concerns, fix CI failures, and retrigger reviewers until clean +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent +--- + +# PR Review Sweep + +You are performing a full review sweep across all open PRs in this repository. Your goal is to bring every PR to a clean, mergeable state: no conflicts, CI passing, all reviewer comments addressed, and reviewers re-triggered until satisfied. + +--- + +## Step 1: Discover Open PRs + +```bash +gh pr list --repo optave/codegraph --state open --json number,title,headRefName,baseRefName,mergeable,statusCheckRollup,reviewDecision --limit 50 +``` + +Record each PR's number, branch, base, merge status, and CI state. + +--- + +## Step 2: Process Each PR + +For **each** open PR, perform the following steps in order. Process PRs one at a time to avoid cross-contamination. + +### 2a. Switch to the PR branch + +```bash +git fetch origin +git checkout +git pull origin +``` + +### 2b. Resolve merge conflicts + +Check if the PR has conflicts with its base branch: + +```bash +gh pr view --json mergeable --jq '.mergeable' +``` + +If `CONFLICTING`: + +1. Merge the base branch into the head branch (never rebase): + ```bash + git merge origin/ + ``` +2. Resolve all conflicts by reading the conflicting files, understanding both sides, and making the correct resolution. +3. After resolving, stage the resolved files by name (not `git add .`), commit with: `fix: resolve merge conflicts with ` +4. Push the updated branch. + +### 2c. Check CI status + +```bash +gh pr checks +``` + +If any checks are failing: + +1. Read the failing check logs: + ```bash + gh run view --log-failed + ``` +2. Diagnose the failure — read the relevant source files, understand the error. +3. Fix the issue in code. +4. Run tests locally to verify: `npm test` +5. Run lint locally: `npm run lint` +6. Commit the fix with a descriptive message: `fix: ` +7. Push and wait for CI to re-run. Check again: + ```bash + gh pr checks + ``` +8. Repeat until CI is green. + +### 2d. Gather all review comments + +Fetch **all** review comments from both Claude and Greptile: + +```bash +# PR review comments (inline code comments) +gh api repos/optave/codegraph/pulls//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}' + +# PR reviews (top-level review bodies) +gh api repos/optave/codegraph/pulls//reviews --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, state: .state}' + +# Issue-style comments (includes @greptileai trigger responses) +gh api repos/optave/codegraph/issues//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, created_at: .created_at}' +``` + +### 2e. Address every comment + +For **each** review comment — including minor suggestions, nits, style feedback, and optional improvements: + +1. **Read the comment carefully.** Understand what the reviewer is asking for. +2. **Read the relevant code** at the file and line referenced. +3. **Make the change.** Even if the comment is marked as "nit" or "suggestion" or "minor" — address it. The goal is zero outstanding comments. +4. **If you disagree** with a suggestion (e.g., it would introduce a bug or contradicts project conventions), do NOT silently ignore it. Reply to the comment explaining why you chose a different approach. +5. **Reply to each comment** explaining what you did: + ```bash + gh api repos/optave/codegraph/pulls//comments//replies \ + -f body="Fixed — " + ``` + For issue-style comments, reply on the issue: + ```bash + gh api repos/optave/codegraph/issues//comments \ + -f body="Addressed: " + ``` + +### 2f. Commit and push fixes + +After addressing all comments for a PR: + +1. Stage only the files you changed. +2. Commit: `fix: address review feedback on #` +3. Push to the PR branch. + +### 2g. Re-trigger reviewers + +**Greptile:** Always re-trigger after pushing fixes. Post a comment: + +```bash +gh api repos/optave/codegraph/issues//comments \ + -f body="@greptileai" +``` + +**Claude (claude-code-review / claude bot):** Only re-trigger if you addressed something Claude specifically suggested. If you did: + +```bash +gh api repos/optave/codegraph/issues//comments \ + -f body="@claude" +``` + +If all changes were only in response to Greptile feedback, do NOT re-trigger Claude. + +### 2h. Wait and re-check + +After re-triggering: + +1. Wait for the new reviews to come in (check after a reasonable interval). +2. Fetch new comments again (repeat Step 2d). +3. If there are **new** comments from Greptile or Claude, go back to Step 2e and address them. +4. **Repeat this loop** until Greptile's latest review has no actionable comments. +5. Verify CI is still green after all changes. + +--- + +## Step 3: Summary + +After processing all PRs, output a summary table: + +``` +| PR | Branch | Conflicts | CI | Comments Addressed | Reviewers Re-triggered | Status | +|----|--------|-----------|----|--------------------|----------------------|--------| +| #N | branch | resolved/none | green/red | N comments | greptile, claude | ready/needs-work | +``` + +--- + +## Rules + +- **Never rebase.** Always `git merge ` to resolve conflicts. +- **Never force-push.** If something goes wrong, fix it with a new commit. +- **Address ALL comments**, even minor/nit/optional ones. Leave zero unaddressed. +- **Always reply to comments** explaining what was done. Don't just fix silently. +- **Always re-trigger Greptile** after pushing fixes — it must confirm satisfaction. +- **Only re-trigger Claude** if you addressed Claude's feedback specifically. +- **No co-author lines** in commit messages. +- **No Claude Code references** in commit messages or comments. +- **Run tests and lint locally** before pushing any fix. +- **One concern per commit** — don't lump conflict resolution with code fixes. +- If a PR is fundamentally broken beyond what review feedback can fix, note it in the summary and skip to the next PR. From af0a536462d92bb6b5a5c9e321313fee12bfab6c Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:44:29 -0600 Subject: [PATCH 20/28] Revert "docs: add /review skill for automated PR review sweeps" This reverts commit cc6dafd25d29b5d96f259a326d69227f62d823e3. --- .claude/skills/review/SKILL.md | 172 --------------------------------- 1 file changed, 172 deletions(-) delete mode 100644 .claude/skills/review/SKILL.md diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md deleted file mode 100644 index d85503fa..00000000 --- a/.claude/skills/review/SKILL.md +++ /dev/null @@ -1,172 +0,0 @@ ---- -name: review -description: Check all open PRs, resolve conflicts, update branches, address Claude and Greptile review concerns, fix CI failures, and retrigger reviewers until clean -allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent ---- - -# PR Review Sweep - -You are performing a full review sweep across all open PRs in this repository. Your goal is to bring every PR to a clean, mergeable state: no conflicts, CI passing, all reviewer comments addressed, and reviewers re-triggered until satisfied. - ---- - -## Step 1: Discover Open PRs - -```bash -gh pr list --repo optave/codegraph --state open --json number,title,headRefName,baseRefName,mergeable,statusCheckRollup,reviewDecision --limit 50 -``` - -Record each PR's number, branch, base, merge status, and CI state. - ---- - -## Step 2: Process Each PR - -For **each** open PR, perform the following steps in order. Process PRs one at a time to avoid cross-contamination. - -### 2a. Switch to the PR branch - -```bash -git fetch origin -git checkout -git pull origin -``` - -### 2b. Resolve merge conflicts - -Check if the PR has conflicts with its base branch: - -```bash -gh pr view --json mergeable --jq '.mergeable' -``` - -If `CONFLICTING`: - -1. Merge the base branch into the head branch (never rebase): - ```bash - git merge origin/ - ``` -2. Resolve all conflicts by reading the conflicting files, understanding both sides, and making the correct resolution. -3. After resolving, stage the resolved files by name (not `git add .`), commit with: `fix: resolve merge conflicts with ` -4. Push the updated branch. - -### 2c. Check CI status - -```bash -gh pr checks -``` - -If any checks are failing: - -1. Read the failing check logs: - ```bash - gh run view --log-failed - ``` -2. Diagnose the failure — read the relevant source files, understand the error. -3. Fix the issue in code. -4. Run tests locally to verify: `npm test` -5. Run lint locally: `npm run lint` -6. Commit the fix with a descriptive message: `fix: ` -7. Push and wait for CI to re-run. Check again: - ```bash - gh pr checks - ``` -8. Repeat until CI is green. - -### 2d. Gather all review comments - -Fetch **all** review comments from both Claude and Greptile: - -```bash -# PR review comments (inline code comments) -gh api repos/optave/codegraph/pulls//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}' - -# PR reviews (top-level review bodies) -gh api repos/optave/codegraph/pulls//reviews --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, state: .state}' - -# Issue-style comments (includes @greptileai trigger responses) -gh api repos/optave/codegraph/issues//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, created_at: .created_at}' -``` - -### 2e. Address every comment - -For **each** review comment — including minor suggestions, nits, style feedback, and optional improvements: - -1. **Read the comment carefully.** Understand what the reviewer is asking for. -2. **Read the relevant code** at the file and line referenced. -3. **Make the change.** Even if the comment is marked as "nit" or "suggestion" or "minor" — address it. The goal is zero outstanding comments. -4. **If you disagree** with a suggestion (e.g., it would introduce a bug or contradicts project conventions), do NOT silently ignore it. Reply to the comment explaining why you chose a different approach. -5. **Reply to each comment** explaining what you did: - ```bash - gh api repos/optave/codegraph/pulls//comments//replies \ - -f body="Fixed — " - ``` - For issue-style comments, reply on the issue: - ```bash - gh api repos/optave/codegraph/issues//comments \ - -f body="Addressed: " - ``` - -### 2f. Commit and push fixes - -After addressing all comments for a PR: - -1. Stage only the files you changed. -2. Commit: `fix: address review feedback on #` -3. Push to the PR branch. - -### 2g. Re-trigger reviewers - -**Greptile:** Always re-trigger after pushing fixes. Post a comment: - -```bash -gh api repos/optave/codegraph/issues//comments \ - -f body="@greptileai" -``` - -**Claude (claude-code-review / claude bot):** Only re-trigger if you addressed something Claude specifically suggested. If you did: - -```bash -gh api repos/optave/codegraph/issues//comments \ - -f body="@claude" -``` - -If all changes were only in response to Greptile feedback, do NOT re-trigger Claude. - -### 2h. Wait and re-check - -After re-triggering: - -1. Wait for the new reviews to come in (check after a reasonable interval). -2. Fetch new comments again (repeat Step 2d). -3. If there are **new** comments from Greptile or Claude, go back to Step 2e and address them. -4. **Repeat this loop** until Greptile's latest review has no actionable comments. -5. Verify CI is still green after all changes. - ---- - -## Step 3: Summary - -After processing all PRs, output a summary table: - -``` -| PR | Branch | Conflicts | CI | Comments Addressed | Reviewers Re-triggered | Status | -|----|--------|-----------|----|--------------------|----------------------|--------| -| #N | branch | resolved/none | green/red | N comments | greptile, claude | ready/needs-work | -``` - ---- - -## Rules - -- **Never rebase.** Always `git merge ` to resolve conflicts. -- **Never force-push.** If something goes wrong, fix it with a new commit. -- **Address ALL comments**, even minor/nit/optional ones. Leave zero unaddressed. -- **Always reply to comments** explaining what was done. Don't just fix silently. -- **Always re-trigger Greptile** after pushing fixes — it must confirm satisfaction. -- **Only re-trigger Claude** if you addressed Claude's feedback specifically. -- **No co-author lines** in commit messages. -- **No Claude Code references** in commit messages or comments. -- **Run tests and lint locally** before pushing any fix. -- **One concern per commit** — don't lump conflict resolution with code fixes. -- If a PR is fundamentally broken beyond what review feedback can fix, note it in the summary and skip to the next PR. From e0dabd2a7bcf1c589c5b63bb51ec13e2bb245b45 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:45:02 -0600 Subject: [PATCH 21/28] docs: add /review skill for automated PR review sweeps Skill checks all open PRs, resolves conflicts (via merge, never rebase), fixes CI failures, addresses all reviewer comments (Claude + Greptile), replies to each comment, and re-triggers reviewers in a loop until clean. --- .claude/skills/review/SKILL.md | 172 +++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 .claude/skills/review/SKILL.md diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md new file mode 100644 index 00000000..d85503fa --- /dev/null +++ b/.claude/skills/review/SKILL.md @@ -0,0 +1,172 @@ +--- +name: review +description: Check all open PRs, resolve conflicts, update branches, address Claude and Greptile review concerns, fix CI failures, and retrigger reviewers until clean +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, Agent +--- + +# PR Review Sweep + +You are performing a full review sweep across all open PRs in this repository. Your goal is to bring every PR to a clean, mergeable state: no conflicts, CI passing, all reviewer comments addressed, and reviewers re-triggered until satisfied. + +--- + +## Step 1: Discover Open PRs + +```bash +gh pr list --repo optave/codegraph --state open --json number,title,headRefName,baseRefName,mergeable,statusCheckRollup,reviewDecision --limit 50 +``` + +Record each PR's number, branch, base, merge status, and CI state. + +--- + +## Step 2: Process Each PR + +For **each** open PR, perform the following steps in order. Process PRs one at a time to avoid cross-contamination. + +### 2a. Switch to the PR branch + +```bash +git fetch origin +git checkout +git pull origin +``` + +### 2b. Resolve merge conflicts + +Check if the PR has conflicts with its base branch: + +```bash +gh pr view --json mergeable --jq '.mergeable' +``` + +If `CONFLICTING`: + +1. Merge the base branch into the head branch (never rebase): + ```bash + git merge origin/ + ``` +2. Resolve all conflicts by reading the conflicting files, understanding both sides, and making the correct resolution. +3. After resolving, stage the resolved files by name (not `git add .`), commit with: `fix: resolve merge conflicts with ` +4. Push the updated branch. + +### 2c. Check CI status + +```bash +gh pr checks +``` + +If any checks are failing: + +1. Read the failing check logs: + ```bash + gh run view --log-failed + ``` +2. Diagnose the failure — read the relevant source files, understand the error. +3. Fix the issue in code. +4. Run tests locally to verify: `npm test` +5. Run lint locally: `npm run lint` +6. Commit the fix with a descriptive message: `fix: ` +7. Push and wait for CI to re-run. Check again: + ```bash + gh pr checks + ``` +8. Repeat until CI is green. + +### 2d. Gather all review comments + +Fetch **all** review comments from both Claude and Greptile: + +```bash +# PR review comments (inline code comments) +gh api repos/optave/codegraph/pulls//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, path: .path, line: .line, created_at: .created_at}' + +# PR reviews (top-level review bodies) +gh api repos/optave/codegraph/pulls//reviews --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, state: .state}' + +# Issue-style comments (includes @greptileai trigger responses) +gh api repos/optave/codegraph/issues//comments --paginate --jq '.[] | {id: .id, user: .user.login, body: .body, created_at: .created_at}' +``` + +### 2e. Address every comment + +For **each** review comment — including minor suggestions, nits, style feedback, and optional improvements: + +1. **Read the comment carefully.** Understand what the reviewer is asking for. +2. **Read the relevant code** at the file and line referenced. +3. **Make the change.** Even if the comment is marked as "nit" or "suggestion" or "minor" — address it. The goal is zero outstanding comments. +4. **If you disagree** with a suggestion (e.g., it would introduce a bug or contradicts project conventions), do NOT silently ignore it. Reply to the comment explaining why you chose a different approach. +5. **Reply to each comment** explaining what you did: + ```bash + gh api repos/optave/codegraph/pulls//comments//replies \ + -f body="Fixed — " + ``` + For issue-style comments, reply on the issue: + ```bash + gh api repos/optave/codegraph/issues//comments \ + -f body="Addressed: " + ``` + +### 2f. Commit and push fixes + +After addressing all comments for a PR: + +1. Stage only the files you changed. +2. Commit: `fix: address review feedback on #` +3. Push to the PR branch. + +### 2g. Re-trigger reviewers + +**Greptile:** Always re-trigger after pushing fixes. Post a comment: + +```bash +gh api repos/optave/codegraph/issues//comments \ + -f body="@greptileai" +``` + +**Claude (claude-code-review / claude bot):** Only re-trigger if you addressed something Claude specifically suggested. If you did: + +```bash +gh api repos/optave/codegraph/issues//comments \ + -f body="@claude" +``` + +If all changes were only in response to Greptile feedback, do NOT re-trigger Claude. + +### 2h. Wait and re-check + +After re-triggering: + +1. Wait for the new reviews to come in (check after a reasonable interval). +2. Fetch new comments again (repeat Step 2d). +3. If there are **new** comments from Greptile or Claude, go back to Step 2e and address them. +4. **Repeat this loop** until Greptile's latest review has no actionable comments. +5. Verify CI is still green after all changes. + +--- + +## Step 3: Summary + +After processing all PRs, output a summary table: + +``` +| PR | Branch | Conflicts | CI | Comments Addressed | Reviewers Re-triggered | Status | +|----|--------|-----------|----|--------------------|----------------------|--------| +| #N | branch | resolved/none | green/red | N comments | greptile, claude | ready/needs-work | +``` + +--- + +## Rules + +- **Never rebase.** Always `git merge ` to resolve conflicts. +- **Never force-push.** If something goes wrong, fix it with a new commit. +- **Address ALL comments**, even minor/nit/optional ones. Leave zero unaddressed. +- **Always reply to comments** explaining what was done. Don't just fix silently. +- **Always re-trigger Greptile** after pushing fixes — it must confirm satisfaction. +- **Only re-trigger Claude** if you addressed Claude's feedback specifically. +- **No co-author lines** in commit messages. +- **No Claude Code references** in commit messages or comments. +- **Run tests and lint locally** before pushing any fix. +- **One concern per commit** — don't lump conflict resolution with code fixes. +- If a PR is fundamentally broken beyond what review feedback can fix, note it in the summary and skip to the next PR. From 7c483b4a03da2041d8f749d991ad23efe3cb8a73 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:46:25 -0600 Subject: [PATCH 22/28] fix: resolve Windows CI test failure and doc inaccuracies Replace realpathSync comparison in db.test.js ceiling test with existence-based assertions to avoid Windows 8.3 short name mismatch (RUNNER~1 vs runneradmin). Fix artifact count (5 -> 6 files) in skills README and rule count (31 -> 17 rules) in titan-paradigm.md. --- docs/examples/claude-code-skills/README.md | 2 +- docs/use-cases/titan-paradigm.md | 2 +- tests/unit/db.test.js | 11 ++++++----- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/examples/claude-code-skills/README.md b/docs/examples/claude-code-skills/README.md index 9969f3ba..bd1398e4 100644 --- a/docs/examples/claude-code-skills/README.md +++ b/docs/examples/claude-code-skills/README.md @@ -90,7 +90,7 @@ If GAUNTLET runs out of context, just re-invoke `/titan-gauntlet` — it resumes ## Artifacts -All artifacts are written to `.codegraph/titan/` (5 files, no redundancy): +All artifacts are written to `.codegraph/titan/` (6 files, no redundancy): | File | Format | Written by | Read by | |------|--------|-----------|---------| diff --git a/docs/use-cases/titan-paradigm.md b/docs/use-cases/titan-paradigm.md index 5f494c0d..9c3a6b10 100644 --- a/docs/use-cases/titan-paradigm.md +++ b/docs/use-cases/titan-paradigm.md @@ -83,7 +83,7 @@ codegraph where resolveImports ### THE GAUNTLET: Audit every file against strict standards -> **Claude Code skill:** [`/titan-gauntlet`](../examples/claude-code-skills/titan-gauntlet/SKILL.md) implements all 4 pillars (31 rules) with batch processing, multi-agent dispatch, context budget management, and session resumability via `titan-state.json`. +> **Claude Code skill:** [`/titan-gauntlet`](../examples/claude-code-skills/titan-gauntlet/SKILL.md) implements all 4 pillars (17 rules) with batch processing, multi-agent dispatch, context budget management, and session resumability via `titan-state.json`. The Gauntlet needs each sub-agent to understand what a file does, what depends on it, and how risky changes are. The `audit` command gives each agent everything in one call: diff --git a/tests/unit/db.test.js b/tests/unit/db.test.js index 524c61d9..4f13fc39 100644 --- a/tests/unit/db.test.js +++ b/tests/unit/db.test.js @@ -320,12 +320,13 @@ describe('findDbPath with git ceiling', () => { process.cwd = () => innerDir; try { _resetRepoRootCache(); - const ceiling = fs.realpathSync(findRepoRoot()); const result = findDbPath(); - // Use realpathSync on both sides to normalize Windows 8.3 short names - // (e.g. RUNNER~1 vs runneradmin) that path.dirname walking may preserve - const expected = path.join(ceiling, '.codegraph', 'graph.db'); - expect(fs.realpathSync(result)).toBe(fs.realpathSync(expected)); + // Avoid exact path comparison — realpathSync doesn't resolve Windows + // 8.3 short names (RUNNER~1 vs runneradmin) on CI. Instead verify + // existence, suffix, and that it's not the outer directory's DB. + expect(fs.existsSync(result)).toBe(true); + expect(result).toMatch(/\.codegraph[/\\]graph\.db$/); + expect(result).not.toContain(path.basename(outerDir) + path.sep + '.codegraph'); } finally { process.cwd = origCwd; fs.rmSync(path.join(worktreeRoot, '.codegraph'), { recursive: true, force: true }); From 99af3d333ca11c61951a5e0ee125711e5eef4b37 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 05:50:35 -0600 Subject: [PATCH 23/28] fix: address Greptile review feedback on review skill Add /worktree isolation step at the start of the workflow, consistent with the CLAUDE.md parallel-session rules. Update step 2f to group commits by concern rather than batching all fixes into a single commit. --- .claude/skills/review/SKILL.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index d85503fa..99446713 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -10,6 +10,12 @@ You are performing a full review sweep across all open PRs in this repository. Y --- +## Step 0: Worktree Isolation + +Before doing anything else, run `/worktree` to get an isolated copy of the repo. CLAUDE.md mandates that every session starts with `/worktree` to prevent cross-session interference. All subsequent steps run inside the worktree. + +--- + ## Step 1: Discover Open PRs ```bash @@ -112,8 +118,9 @@ For **each** review comment — including minor suggestions, nits, style feedbac After addressing all comments for a PR: 1. Stage only the files you changed. -2. Commit: `fix: address review feedback on #` -3. Push to the PR branch. +2. Group changes by concern — each logically distinct fix gets its own commit (e.g., one commit for a missing validation, another for a naming change). Do not lump all feedback into a single commit. +3. Use descriptive messages per commit: `fix: (#)` +4. Push to the PR branch. ### 2g. Re-trigger reviewers From cf95629153ea5d0024b5cab83ea05b53f9ed4da6 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:43:49 -0600 Subject: [PATCH 24/28] fix: use gh pr checkout for fork-compatible PR switching --- .claude/skills/review/SKILL.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index 99446713..112e4d15 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -33,9 +33,7 @@ For **each** open PR, perform the following steps in order. Process PRs one at a ### 2a. Switch to the PR branch ```bash -git fetch origin -git checkout -git pull origin +gh pr checkout ``` ### 2b. Resolve merge conflicts From 5cb944cb3e1311efb4a02728af3bfb1aea10c1ef Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:44:04 -0600 Subject: [PATCH 25/28] fix: add iteration cap to review retry loop --- .claude/skills/review/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index 112e4d15..d8cda40b 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -145,7 +145,7 @@ After re-triggering: 1. Wait for the new reviews to come in (check after a reasonable interval). 2. Fetch new comments again (repeat Step 2d). 3. If there are **new** comments from Greptile or Claude, go back to Step 2e and address them. -4. **Repeat this loop** until Greptile's latest review has no actionable comments. +4. **Repeat this loop for a maximum of 3 rounds.** If after 3 rounds there are still actionable comments, mark the PR as "needs human review" in the summary table and move to the next PR. 5. Verify CI is still green after all changes. --- From 77e98a0fb69b1edce8f6db84ed135edfa5880210 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:47:48 -0600 Subject: [PATCH 26/28] fix: add clean-tree guard before PR checkout in review skill --- .claude/skills/review/SKILL.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index d8cda40b..ff103e4b 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -32,6 +32,16 @@ For **each** open PR, perform the following steps in order. Process PRs one at a ### 2a. Switch to the PR branch +Ensure the working tree is clean before switching to avoid cross-PR contamination: + +```bash +if [ -n "$(git status --porcelain)" ]; then + git stash push -m "pre-checkout stash" +fi +``` + +Then check out the PR branch: + ```bash gh pr checkout ``` From 9393ad000129c4e5fb439783bdcf5aa209b91dd0 Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:51:56 -0600 Subject: [PATCH 27/28] fix: allow force-push for commitlint failures in review skill rules --- .claude/skills/review/SKILL.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index ff103e4b..eee391a4 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -175,7 +175,7 @@ After processing all PRs, output a summary table: ## Rules - **Never rebase.** Always `git merge ` to resolve conflicts. -- **Never force-push.** If something goes wrong, fix it with a new commit. +- **Never force-push** unless fixing a commit message that fails commitlint. Amend + force-push is the only way to fix a pushed commit title (messages are part of the SHA). This is safe on feature branches. For all other problems, fix with a new commit. - **Address ALL comments**, even minor/nit/optional ones. Leave zero unaddressed. - **Always reply to comments** explaining what was done. Don't just fix silently. - **Always re-trigger Greptile** after pushing fixes — it must confirm satisfaction. From 3f9be49929d2a145caee08f26aff6b9eca10ce9b Mon Sep 17 00:00:00 2001 From: carlos-alm <127798846+carlos-alm@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:00:00 -0600 Subject: [PATCH 28/28] fix: add scope creep detection rule to review skill --- .claude/skills/review/SKILL.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.claude/skills/review/SKILL.md b/.claude/skills/review/SKILL.md index eee391a4..eff495c4 100644 --- a/.claude/skills/review/SKILL.md +++ b/.claude/skills/review/SKILL.md @@ -184,4 +184,5 @@ After processing all PRs, output a summary table: - **No Claude Code references** in commit messages or comments. - **Run tests and lint locally** before pushing any fix. - **One concern per commit** — don't lump conflict resolution with code fixes. +- **Flag scope creep.** If a PR's diff contains files unrelated to its stated purpose (e.g., a docs PR carrying `src/` or test changes from a merged feature branch), flag it immediately. Split the unrelated changes into a separate branch and PR. Do not proceed with review until the PR is scoped correctly — scope creep is not acceptable. - If a PR is fundamentally broken beyond what review feedback can fix, note it in the summary and skip to the next PR.