CLAUDE_CONFIG_DIR + PAI_DIR two-root split (v4.0.3+)#103
Closed
virtualian wants to merge 8 commits intomainfrom
Closed
CLAUDE_CONFIG_DIR + PAI_DIR two-root split (v4.0.3+)#103virtualian wants to merge 8 commits intomainfrom
virtualian wants to merge 8 commits intomainfrom
Conversation
…template Split single PAI_DIR root into CONFIG (hooks, MEMORY, settings) and CODE (PAI Tools, Algorithm, skills, agents) roots. Updates paths.ts, settings.json, 20 hooks, and 170+ docs/skills with new path scheme.
MEMORY is PAI-specific state, not CC config. Reclassify all
configPath('MEMORY') to codePath('MEMORY') and update docs
from ~/.claude/MEMORY to ~/.pai/MEMORY.
VoiceNotification, tab-setter, and prd-utils only imported configPath — add codePath to their import statements.
Phase 4 of issue #100. Updates 749 path references across 185 files to reflect the CLAUDE_CONFIG_DIR + PAI_DIR two-root separation. CC-native paths (history.jsonl, commands/, projects/) preserved.
After ebc8130 (two-root separation) and 5cc0656 (MEMORY to PAI root), eight hook files still bypassed paths.ts, using ad-hoc fallbacks like "process.env.PAI_DIR || join(HOME, '.claude')" or hardcoded "$HOME/.claude/settings.json". With defaults they read/wrote MEMORY under ~/.claude while migrated hooks used ~/.pai, producing a silent split-brain where neither half saw the other's data. All eight now route through codePath/getSettingsPath: - LastResponseCache.hook.ts: last-response.txt via codePath - RatingCapture.hook.ts: SIGNALS/RATINGS/LAST_RESPONSE and learningsDir - SessionCleanup.hook.ts: STATE_DIR, WORK_DIR - WorkCompletionLearning.hook.ts: STATE_DIR, WORK_DIR, LEARNING_DIR - lib/notifications.ts: loadNtfyConfig reads getSettingsPath() - lib/identity.ts: SETTINGS_PATH = getSettingsPath() - KittyEnvPersist.hook.ts: kitty-env stateDir via codePath - LoadContext.hook.ts: MEMORY/RELATIONSHIP lookup, loadLearningDigest, loadWisdomFrames, loadFailurePatterns, loadSignalTrends, and checkActiveProgress now all receive paiDir (not configDir); dropped now-dead configDir param from loadRelationshipContext
Path-separation fixes: - LastResponseCache: mkdirSync before writeFileSync (ENOENT on fresh install) - prd-utils: swap broken configPath() replace prefix to codePath() - paths.ts: fix stale docstring; deprecate paiPath() (semantic flip to ~/.pai) - settings.json _env: correct stale MEMORY-under-CONFIG claim - UpdateCounts: route MEMORY paths to PAI root; simplify refreshUsageCache (was reading/writing ~/.claude/MEMORY/* after 5cc0656 moved MEMORY) - VoiceCompletion: replace hardcoded settings path with getSettingsPath() - LoadContext: five raw join(paiDir, ...) swapped to codePath(); rename misleading loadSettings parameter; extract sessionProgressPath - VoiceNotification, tab-setter: drop dead configPath imports - .pai-protected.json: fix stale Kai workflow comment Revert of unannounced deletions from ebc8130: - RatingCapture: restore 446 lines (CorrectionMode fast-path, BehavioralSignal, BEHAVIORAL_FEEDBACK_STATE subsystem); keep only the load-bearing dynamic import swap for inference/captureFailure. - learning-readback: restore 160 lines (loadLatestSynthesis). Identical to main - ebc8130 made no legitimate path-sep changes to this file. Scoped branch delta: -538 -> +59 net.
ebc8130 rewrote 25 hook command paths in settings.json from ${PAI_DIR}/hooks/* to ${CLAUDE_CONFIG_DIR}/hooks/*, and rewired paths.ts getHooksDir() and several path constructions in hooks to use configPath('hooks'). This was the wrong model: CLAUDE_CONFIG_DIR is Claude Code's own config root (sessions, projects, CC's own settings.json), while PAI's runtime (hooks, skills, agents, MEMORY, PAI code) lives under PAI_DIR. Runtime install at ~/.pai/hooks/ confirms the correct placement. 42f70b4 inherited the wrong model in two doc strings; corrected here. settings.json: - 25 hook command paths: ${CLAUDE_CONFIG_DIR}/hooks/* -> ${PAI_DIR}/hooks/* - _env.CLAUDE_CONFIG_DIR description corrected to show CC's own files in CONFIG root and PAI install in PAI root - statusLine line 250 left alone (physical file at ~/.claude/; ambiguous whether to move it, separate decision) paths.ts: - getHooksDir(): configPath('hooks') -> codePath('hooks') - Module docstring: hooks moved to PAI root listing DocCrossRefIntegrity.ts: - HOOKS_DIR: configPath('hooks') -> codePath('hooks') UpdateCounts.ts: - countHooks(configDir) -> countHooks(paiDir) - Detached spawn scriptPath: join(configDir, 'hooks', ...) -> join(paiDir, 'hooks', ...) RelationshipMemory.hook.ts: - ensureRelationshipDir(configDir) -> ensureRelationshipDir(getPaiDir()) (was writing daily notes to ~/.claude/MEMORY/RELATIONSHIP/ which doesn't exist after 5cc0656 moved MEMORY to PAI root) change-detection.ts: - STATE_FILE: join(CONFIG_DIR, 'MEMORY', 'STATE', ...) -> join(PAI_DIR, 'MEMORY', 'STATE', ...) - Remove dead getConfigDir import and CONFIG_DIR constant .pai-protected.json: - Remove stale allow-list entries ~/.claude/skills and ~/.claude/hooks (both moved to PAI root in the corrected model)
…pers After 42f70b4 modernized LoadContext.hook.ts to use codePath() for MEMORY path construction, the paiDir variable in main() existed only to pass through to helper functions that still took it as a param for raw join(paiDir, 'MEMORY', ...) calls. Rewriting those helpers to call codePath() directly eliminates the whole chain - the param, the variable, and the getPaiDir import. LoadContext.hook.ts: - loadStartupFiles(paiDir, settings) -> loadStartupFiles(settings); body: join(paiDir, relPath) -> codePath(relPath) - Delete const paiDir = getPaiDir(); from main() - Drop paiDir arg from 5 call sites (loadStartupFiles + loadLearningDigest + loadWisdomFrames + loadFailurePatterns + loadSignalTrends) - Remove getPaiDir from paths import learning-readback.ts: - Add codePath import - 6 exported functions (loadLearningDigest, loadWisdomFrames, loadFailurePatterns, loadSignalTrends, loadLatestSynthesis, loadBehavioralTrends) lose their paiDir parameter - Internal helper getRecentLearnings(baseDir, subdir, count) -> getRecentLearnings(subdir, count); updated 2 internal call sites - 8 join(paiDir, 'MEMORY', ...) sites rewritten as codePath() Symmetric +33/-33. Pure pattern modernization - no behavior change.
8 tasks
virtualian
added a commit
that referenced
this pull request
Apr 13, 2026
Supersedes PR #103. Incorporates the 4 correction commits from branch 100-claude-config-dir-pai-dir-separation (b3e4e49, 42f70b4, 39ea6b1, c7f304c) as a single focused refactor on top of the CLAUDE_CONFIG_DIR + PAI_DIR split that landed via #101 and the CorrectionMode + loadLatestSynthesis restore that landed via #104. Path separation fixes --------------------- - settings.json: revert 13 hook commands from CLAUDE_CONFIG_DIR back to PAI_DIR (the hook-root inversion originally introduced by ebc8130) - paths.ts: getHooksDir() returns codePath('hooks') not configPath('hooks'), docstring rewrites for the two-root contract - LoadContext.hook.ts: eliminate paiDir parameter chain across 5 helpers, route 5 raw join(paiDir, ...) calls through codePath(), rename misleading loadSettings param, extract sessionProgressPath - UpdateCounts.ts: countHooks and refreshUsageCache now read MEMORY from the PAI root instead of the CONFIG root - RelationshipMemory.hook.ts: ensureRelationshipDir fixed for the split - LastResponseCache.hook.ts: mkdirSync before writeFileSync (silent ENOENT on fresh install) - prd-utils.ts: correct replace() prefix from configPath to codePath - VoiceCompletion.hook.ts: replace hardcoded ~/.claude/settings.json with getSettingsPath() - VoiceNotification.ts, tab-setter.ts: drop dead configPath imports - DocCrossRefIntegrity.ts, change-detection.ts, identity.ts, notifications.ts, SessionCleanup.hook.ts, KittyEnvPersist.hook.ts, WorkCompletionLearning.hook.ts: small codePath / configPath cleanups - .pai-protected.json: correct stale comment Deprecated alias deletion ------------------------- - paths.ts: remove the paiPath() function entirely. The semantic flip in v4.0.3 (pre-split ~/.claude/x -> post-split ~/.pai/x) is exactly the class of change where a silent alias is a landmine, not a safety net. The compiler now catches any revenant caller. Zero code callers existed when this commit was written. - SYSTEM_USER_EXTENDABILITY.md: update 4 code examples from paiPath() to codePath() so the documentation matches the public API. MEMORY migration release note (manual upgrade path) --------------------------------------------------- - Releases/v4.0.3+/README.md: add a "Breaking Changes" subsection explaining the two-root split and add the required MEMORY migration step (`mv ~/.claude/MEMORY ~/.pai/MEMORY`) to the "Upgrading from v4.0.x" recipe. An automated migrator is a follow-up - see the tracking issue. Deferred (intentional) ---------------------- - statusLine.command still points at ${CLAUDE_CONFIG_DIR}/statusline-command.sh because the physical file currently lives at ~/.claude/statusline-command.sh. Relocating the script is a separate decision that does not belong in this refactor. Verification ------------ - bun build --target=bun clean on all 17 touched hook TypeScript files - settings.json parses as valid JSON - env-var resolution tested under CLAUDE_CONFIG_DIR=/tmp/ctest PAI_DIR=/tmp/ptest: 10/10 path primitives resolve to the expected roots - settings.json hook-command audit: 0 references to CLAUDE_CONFIG_DIR/hooks, 24 references to PAI_DIR/hooks - MEMORY routing audit: 0 configPath('MEMORY'), 45 codePath('MEMORY'), 0 raw ~/.claude/MEMORY references in hook sources - CI symbol guard (merged in #105) passes all 13 required-symbol checks against this commit
Owner
Author
|
Superseded by #106. Summary of what happened to this PR:
Closing this PR in favour of #106. |
virtualian
added a commit
that referenced
this pull request
Apr 13, 2026
) Supersedes PR #103. Incorporates the 4 correction commits from branch 100-claude-config-dir-pai-dir-separation (b3e4e49, 42f70b4, 39ea6b1, c7f304c) as a single focused refactor on top of the CLAUDE_CONFIG_DIR + PAI_DIR split that landed via #101 and the CorrectionMode + loadLatestSynthesis restore that landed via #104. Path separation fixes --------------------- - settings.json: revert 13 hook commands from CLAUDE_CONFIG_DIR back to PAI_DIR (the hook-root inversion originally introduced by ebc8130) - paths.ts: getHooksDir() returns codePath('hooks') not configPath('hooks'), docstring rewrites for the two-root contract - LoadContext.hook.ts: eliminate paiDir parameter chain across 5 helpers, route 5 raw join(paiDir, ...) calls through codePath(), rename misleading loadSettings param, extract sessionProgressPath - UpdateCounts.ts: countHooks and refreshUsageCache now read MEMORY from the PAI root instead of the CONFIG root - RelationshipMemory.hook.ts: ensureRelationshipDir fixed for the split - LastResponseCache.hook.ts: mkdirSync before writeFileSync (silent ENOENT on fresh install) - prd-utils.ts: correct replace() prefix from configPath to codePath - VoiceCompletion.hook.ts: replace hardcoded ~/.claude/settings.json with getSettingsPath() - VoiceNotification.ts, tab-setter.ts: drop dead configPath imports - DocCrossRefIntegrity.ts, change-detection.ts, identity.ts, notifications.ts, SessionCleanup.hook.ts, KittyEnvPersist.hook.ts, WorkCompletionLearning.hook.ts: small codePath / configPath cleanups - .pai-protected.json: correct stale comment Deprecated alias deletion ------------------------- - paths.ts: remove the paiPath() function entirely. The semantic flip in v4.0.3 (pre-split ~/.claude/x -> post-split ~/.pai/x) is exactly the class of change where a silent alias is a landmine, not a safety net. The compiler now catches any revenant caller. Zero code callers existed when this commit was written. - SYSTEM_USER_EXTENDABILITY.md: update 4 code examples from paiPath() to codePath() so the documentation matches the public API. MEMORY migration release note (manual upgrade path) --------------------------------------------------- - Releases/v4.0.3+/README.md: add a "Breaking Changes" subsection explaining the two-root split and add the required MEMORY migration step (`mv ~/.claude/MEMORY ~/.pai/MEMORY`) to the "Upgrading from v4.0.x" recipe. An automated migrator is a follow-up - see the tracking issue. Deferred (intentional) ---------------------- - statusLine.command still points at ${CLAUDE_CONFIG_DIR}/statusline-command.sh because the physical file currently lives at ~/.claude/statusline-command.sh. Relocating the script is a separate decision that does not belong in this refactor. Verification ------------ - bun build --target=bun clean on all 17 touched hook TypeScript files - settings.json parses as valid JSON - env-var resolution tested under CLAUDE_CONFIG_DIR=/tmp/ctest PAI_DIR=/tmp/ptest: 10/10 path primitives resolve to the expected roots - settings.json hook-command audit: 0 references to CLAUDE_CONFIG_DIR/hooks, 24 references to PAI_DIR/hooks - MEMORY routing audit: 0 configPath('MEMORY'), 45 codePath('MEMORY'), 0 raw ~/.claude/MEMORY references in hook sources - CI symbol guard (merged in #105) passes all 13 required-symbol checks against this commit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Releases/v4.0.3+/release template~/.claude) holds Claude Code's own config — settings.json, sessions, projects, CC's CLAUDE.md~/.pai) holds the PAI install — hooks, skills, agents, PAI code, Algorithm, VoiceServer, MEMORY, USERpaths.tsprimitives:getConfigDir(),getPaiDir(),configPath(),codePath(); ~20 hooks migrated to use themcodePath('MEMORY', ...)across LoadContext, prd-utils, UpdateCounts, RelationshipMemory, change-detection, LastResponseCache, learning-readback, tab-setter, VoiceNotificationCorrections made during branch development
ebc8130initially misplaced PAI hooks under CLAUDE_CONFIG_DIR. Reverted in39ea6b1— PAI hooks belong under PAI_DIR (settings.json hook commands,getHooksDir(), UpdateCountscountHookscaller, DocCrossRefIntegrityHOOKS_DIR, .pai-protected.json allow-list).ebc8130also bundled unannounced deletions of ~606 lines: CorrectionMode fast-path + BehavioralSignal capture subsystem in RatingCapture.hook.ts (-446),loadLatestSynthesisin learning-readback.ts (-160). Restored in42f70b4.42f70b4: missing `mkdirSync` in LastResponseCache (silent ENOENT on fresh install); broken `.replace()` prefix in prd-utils after the split; MEMORY-wrong-root bugs in UpdateCounts (`countHooks`, `refreshUsageCache`) and RelationshipMemory (`ensureRelationshipDir`); hardcoded `~/.claude/settings.json` path in VoiceCompletion; stale docs in paths.ts and settings.json `_env`.c7f304celiminates the residual `paiDir` parameter chain in LoadContext and learning-readback — 7 helper functions now use `codePath()` directly, removing the last legacy pattern.Test plan
Known deferred
/.claude/MEMORY` → `/.pai/MEMORY` for anyone upgrading from a pre-split release