Complete two-root path separation (supersedes #103)#106
Merged
virtualian merged 1 commit intomainfrom Apr 13, 2026
Merged
Conversation
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 was referenced Apr 13, 2026
This was referenced Apr 13, 2026
Closed
virtualian
added a commit
that referenced
this pull request
Apr 14, 2026
Two files under Releases/v4.0.3+/.claude/PAI/ still referenced the pre-separation `.claude/PAI` path in executable code. Both now resolve via `process.env.PAI_DIR || ~/.pai` matching the repo convention used in Tools/algorithm.ts, Tools/FailureCapture.ts, and hooks/lib/paths.ts. - RebuildPAI.ts:16 — introduce PAI_ROOT const; PAI_DIR now = PAI_ROOT/PAI - runner.v2.ts:33 — resolve PAI_ROOT inside createLocalLLM() Out of scope and intentionally untouched: - `~/.claude/settings.json` reference in RebuildPAI.ts:21 (correct per two-root architecture — settings live in CLAUDE_CONFIG_DIR) - The 102-line Algorithm-spec gap in v3.7.0.md (separate problem) - 4 residual hits in `.claude/skills/` tree (filed as separate issue) Per Plans/2026-04-14-two-root-separation-followups.md the plan listed 10 files but 8 were already clean on this branch between #101 and #106. Refs #108 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4 tasks
virtualian
added a commit
that referenced
this pull request
Apr 14, 2026
Two files under Releases/v4.0.3+/.claude/PAI/ still referenced the pre-separation `.claude/PAI` path in executable code. Both now resolve via `process.env.PAI_DIR || ~/.pai` matching the repo convention used in Tools/algorithm.ts, Tools/FailureCapture.ts, and hooks/lib/paths.ts. - RebuildPAI.ts:16 — introduce PAI_ROOT const; PAI_DIR now = PAI_ROOT/PAI - runner.v2.ts:33 — resolve PAI_ROOT inside createLocalLLM() Out of scope and intentionally untouched: - `~/.claude/settings.json` reference in RebuildPAI.ts:21 (correct per two-root architecture — settings live in CLAUDE_CONFIG_DIR) - The 102-line Algorithm-spec gap in v3.7.0.md (separate problem) - 4 residual hits in `.claude/skills/` tree (filed as separate issue) Per Plans/2026-04-14-two-root-separation-followups.md the plan listed 10 files but 8 were already clean on this branch between #101 and #106. Refs #108 Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This was referenced Apr 15, 2026
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
Focused refactor completing the
CLAUDE_CONFIG_DIR+PAI_DIRtwo-root separation. This PR supersedes #103 and is intentionally narrow: 20 files, +136/-128, all in the hooks layer plus the release README and one SYSTEM doc.Background — why this replaces #103
Issue #100's two-root split landed on
mainin two pieces:100-claude-config-dir-pai-dir-separation. That squash silently bundledebc8130, which also deleted ~606 lines of unrelated functionality:CorrectionModefast-path,BehavioralSignalcapture, andloadLatestSynthesis. No one noticed during review because the deletions were hidden inside a 400-file "refactor" commit.After #101, #104, and #105, PR #103's semantic delta collapsed from 403 files / +1712/-1658 (merge-base relative) to the 18-file correction set that genuinely differs from post-hotfix main. GitHub's PR UI couldn't show this because the merge base hadn't moved. #103 is also
CONFLICTING— a naive rebase produces phantom conflicts because git's patch-ID matching cannot resolve theebc8130hunks against #101's squash.This PR is the rebased outcome, produced via file-copy reconstruction + the 3 cleanup decisions documented below.
What this PR does
1. Path-separation fixes (18 files from #103's correction commits)
All of these are corrections to bugs introduced or left behind by
ebc8130:settings.json— revert 13 hook command paths from${CLAUDE_CONFIG_DIR}/hooks/*back to${PAI_DIR}/hooks/*. Hooks physically live underPAI_DIRafter the split.paths.ts—getHooksDir()now returnscodePath('hooks')(notconfigPath('hooks')). Docstring rewrites for the two-root contract.LoadContext.hook.ts— eliminate thepaiDirparameter chain across 5 helpers. 5 rawjoin(paiDir, ...)calls routed throughcodePath(). Rename misleadingloadSettingsparameter. ExtractsessionProgressPath.UpdateCounts.ts—countHooksandrefreshUsageCachenow read MEMORY from the PAI root. After Implement CLAUDE_CONFIG_DIR + PAI_DIR two-root separation #101's MEMORY move, the CONFIG-root reads silently returned empty data.RelationshipMemory.hook.ts—ensureRelationshipDirfixed for the split.LastResponseCache.hook.ts—mkdirSyncbeforewriteFileSync(silentENOENTon fresh install).prd-utils.ts— correct.replace()prefix fromconfigPathtocodePath.VoiceCompletion.hook.ts— replace hardcoded~/.claude/settings.jsonwithgetSettingsPath().VoiceNotification.ts,tab-setter.ts— drop deadconfigPathimports.DocCrossRefIntegrity.ts,change-detection.ts,identity.ts,notifications.ts,SessionCleanup.hook.ts,KittyEnvPersist.hook.ts,WorkCompletionLearning.hook.ts— smallcodePath/configPathcleanups..pai-protected.json— correct stale comment.2. Delete
paiPath()alias outrightpaths.tspreviously keptpaiPath()as a deprecated alias forcodePath()to preserve backward compatibility. This PR removes it entirely.Rationale. The semantic flip in v4.0.3 (pre-split
paiPath(x)→~/.claude/x, post-splitpaiPath(x)→~/.pai/x) is exactly the class of change where a silent alias is a landmine, not a safety net. A caller that survives the grep would silently write to a different directory, and the failure mode is "files appear in an unexpected location" — one of the worst bugs to diagnose. Deleting the function forces the compiler to catch any revenant caller. Zero code callers existed when this commit was written.SYSTEM_USER_EXTENDABILITY.mdhad 4 documentation examples usingpaiPath()— those are updated tocodePath()in the same commit so the documentation matches the public API.3. MEMORY migration release note
Releases/v4.0.3+/README.mdgains a "Breaking Changes" subsection explaining the two-root split and — crucially — a required manual migration step for v4.0.2 upgraders:This is a deliberate decision to ship the migration as documentation + manual command rather than as automated installer logic. Reasoning:
What this PR does not do (deliberately deferred)
statusLine.commandstill points at${CLAUDE_CONFIG_DIR}/statusline-command.sh. The physical script lives at~/.claude/statusline-command.sh. Relocating it underPAI_DIRis a one-file move + one settings line + one installer template update — a valid change, but it doesn't belong inside this refactor because it's a separate decision about which root owns the statusline script. Tracked as a follow-up.Verification
Run against this commit locally before pushing:
bun build --target=bunclean on all 17 touched hook TypeScript files (paths.ts,prd-utils.ts,LoadContext.hook.ts,LastResponseCache.hook.ts,VoiceCompletion.hook.ts,RelationshipMemory.hook.ts,WorkCompletionLearning.hook.ts,KittyEnvPersist.hook.ts,SessionCleanup.hook.ts,learning-readback.ts,tab-setter.ts,change-detection.ts,identity.ts,notifications.ts,UpdateCounts.ts,VoiceNotification.ts,DocCrossRefIntegrity.ts)settings.jsonparses as valid JSONCLAUDE_CONFIG_DIR=/tmp/ctest PAI_DIR=/tmp/ptest): 10/10 assertions pass —getConfigDir(),getPaiDir(),configPath('hooks'),codePath('MEMORY'),codePath('PAI','Tools'),getSettingsPath(),getHooksDir(),getMemoryDir(),getSkillsDir()all resolve to the expected rootssettings.jsonhook-command audit: 0 references to${CLAUDE_CONFIG_DIR}/hooks, 24 references to${PAI_DIR}/hooksconfigPath('MEMORY', 45codePath('MEMORY', 0 raw~/.claude/MEMORYreferences in hook sourcespaiPathsearch inReleases/v4.0.3+/: zero matches after deletion + doc substitutionNext for the #100 series
statusLine.commandrelocation and theebc8130scope-creep post-mortem for separate follow-ups — neither belongs in this refactor.