4 Commits
Author SHA1 Message Date
cbd7870159 Address review: collision-resistant slugs, os.homedir() tilde expansion
- The per-project state dir key is now the readable separator-mapped
  slug plus an 8-hex sha256 of the resolved project path. The readable
  part alone is lossy (/x/my.app and /x/my-app both mapped to -x-my-app
  and shared hook state); the digest keeps distinct projects' cache and
  pending state apart while the dir name stays human-scannable.
- Tilde roots now expand via os.homedir() instead of HOME/USERPROFILE
  with a '.' fallback. When no home dir can be determined, expansion is
  rejected and state falls back to the project-local default rather
  than anchoring under the hook process's working directory.
- Tests updated to the digest-suffixed slug via a mirrored slugFor()
  helper, plus two new cases: colliding readable slugs get distinct
  state dirs, and the tilde form resolves identically to the explicit
  homedir-joined form.

Prepared with AI assistance (Claude Code) under direction of
0xDarkMatter, per the maintainer-approved issue #422.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 15:14:49 +05:00
30b3628f5b Expand a leading ~ in IMPECCABLE_CACHE_ROOT against the home dir
Env files and settings JSON hand '~/caches' to Node unexpanded; without
this it would resolve to a literal '~' directory under the process cwd.
Mirrors the exact treatment IMPECCABLE_HOOK_LOG already gets in
writeAuditLog (HOME || USERPROFILE fallback), plus the Windows '~\'
spelling.

Prepared with AI assistance (Claude Code) under direction of
0xDarkMatter, per the maintainer-approved issue #422.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 15:14:49 +05:00
5c82d58b7e Harden IMPECCABLE_CACHE_ROOT edges: normalization, opt-in gate, failure path
- hookStateDir now trims the env value (stray whitespace in env files)
  and path.resolve()s both the root and the cwd, so trailing separators
  and relative segments slug to the same per-project dir.
- The #344/#305 persist gate also treats an existing (possibly
  redirected) cache file as the opted-in marker. Without this, once
  state relocated, clean-edit editCount bumps stopped persisting because
  the project-local .impeccable/ dir never appears. No-op under stock
  paths, where the cache file lives inside .impeccable/.
- New tests: slug normalization equivalences, whitespace trim, graceful
  persistCache failure on an unusable root, and three runHook
  end-to-end cases (findings persist + dedup through the redirect,
  clean-edit editCount persistence, and the no-footprint no-op gate
  holding under redirect).

Prepared with AI assistance (Claude Code) under direction of
0xDarkMatter, per the maintainer-approved issue #422.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 15:14:49 +05:00
77a2eae861 Add IMPECCABLE_CACHE_ROOT to relocate hook state out of project roots (#422)
Honor an optional IMPECCABLE_CACHE_ROOT env var in getCachePath() /
getPendingPath(): when set, hook.cache.json and hook.pending.json land
under $IMPECCABLE_CACHE_ROOT/<project-slug>/ (slug = project path with
[:\/.] mapped to hyphens, mirroring Claude Code's ~/.claude/projects/
convention). Unset or blank env keeps stock project-local behavior.
User-authored config (config.json, config.local.json, design.json)
deliberately stays project-local - only disposable state relocates.

Also clears ambient IMPECCABLE_CACHE_ROOT at the top of hook.test.mjs so
a developer running the suite with the redirect active still gets
deterministic stock-path assertions; the new suite sets and restores the
var explicitly.

Prepared with AI assistance (Claude Code) under direction of
0xDarkMatter, per the maintainer-approved issue #422.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-28 15:14:49 +05:00