fix: relocate global Rules/Workflows/Hooks from ~/Documents to ~/.dirac (FU-9) - #174
Conversation
dirac-run
left a comment
There was a problem hiding this comment.
Requesting follow-up for three migration gaps:
- Rules and workflows are loaded recursively, but this migration copies only immediate
readdirentries. Nested directories hitEISDIRand are skipped, so nested global configuration stops loading after relocation. HookRegistry.isGlobalHooksDir()recognizes.../Dirac/Hooks, but not the new.../.dirac/Hookspath. Relocated global hooks are therefore classified as workspace hooks.- The migration currently catches every read error and swallows every non-
EEXISTcopy failure, although only expected legacy-path failures such asENOENT/EPERMshould be ignored.
Please add coverage for nested migration and global-hook classification at the new path.
86f1dcb to
3815d0c
Compare
|
Follow-up needed: One migration error-contract issue remains. Please ignore only |
…ac (FU-9)
~/Documents is TCC-protected on macOS since Catalina. Processes without
Full Disk Access get EPERM on readdir/watch, causing ruleLoadErrors on
every task run. The existing catch fallback re-resolved the same
protected path, so it could never recover.
Move ensureRules/Workflows/HooksDirectoryExists to ~/.dirac/{Rules,
Workflows,Hooks} (already used by settings/state/cache via
getDiracHomePath). Add best-effort migration from the legacy
~/Documents/Dirac/<subdir> location — copies files with COPYFILE_EXCL
(idempotent, no clobber) and swallows EPERM on the legacy readdir.
- recurse nested Rules/Workflows/Hooks dirs (no EISDIR skip) - classify ~/.dirac/Hooks as global hooks - swallow only ENOENT/EPERM/EACCES; log unexpected
log + rethrow non-ENOENT/EPERM/EACCES so incomplete migration fails loudly instead of fake success. test now expects rejection.
3815d0c to
ec49204
Compare
|
One issue remains: I’m merging this and will fix it forward before release by limiting expected-error suppression to legacy source reads, allowing destination failures to propagate, and adding regression coverage for destination permission failures. |
Summary
ensureRulesDirectoryExists,ensureWorkflowsDirectoryExists, andensureHooksDirectoryExistsfrom~/Documents/Dirac/{Rules,Workflows,Hooks}to~/.dirac/{Rules,Workflows,Hooks}viagetDiracHomePath().~/Documents/Dirac/<subdir>: copies files withCOPYFILE_EXCL(idempotent, no clobber), swallows onlyENOENT/EPERM/EACCESon the legacy path, logs and rethrows unexpected errors.Dirac/Hooksand new.dirac/Hooksas global hooks.catchfallback that re-resolved the same TCC-protected path onmkdirfailure.Why
~/Documentsis TCC-protected on macOS since Catalina (10.15). Any process without Full Disk Access getsEPERMonreaddir/fs.watchagainst paths under~/Documents, even when the directory exists and is readable interactively. This causedruleLoadErrorsto fire on every task run:{"ruleLoadErrors":["Failed to read .diracrules directory at /Users/.../Documents/Dirac/Rules: Error reading directory at ...: EPERM: operation not permitted, scandir ..."]}The existing
catchfallback re-resolved the same~/Documents/Dirac/Rulespath, so it could never recover from TCC denial. Telling users to grant Full Disk Access is the wrong fix — it's overbroad (gives access to all protected paths, not just the one Dirac needs) and masks the real issue: global config shouldn't live on a TCC-protected path in the first place.~/.diracis already used byensureSettingsDirectoryExists,ensureStateDirectoryExists, andensureCacheDirectoryExistsviagetGlobalStorageDir(). Rules/Workflows/Hooks were the only outliers under~/Documents.Review follow-ups
EISDIRskip).HookRegistry.isGlobalHooksDirrecognizes~/.dirac/Hooks(and legacyDirac/Hooks).ENOENT/EPERM/EACCES) are swallowed; unexpected errors (e.g.EIO,ENOSYS) are logged and rethrown soensureDiracSubdirfails loudly instead of reporting success on incomplete migration.Changes
src/core/storage/directoryEnsurers.tsensureRulesDirectoryExists/ensureWorkflowsDirectoryExists/ensureHooksDirectoryExistsresolve~/.dirac/{Rules,Workflows,Hooks}.migrateDir+migrateFile: recursive migration with per-file isolation; destination files never clobbered.migrateFromDocumentsDir: swallows only expected legacy-path errors; logs and rethrows unexpected ones.src/core/hooks/HookRegistry.tsisGlobalHooksDirmatches both.dirac/HooksandDirac/Hooks.Tests
directoryEnsurers.test.ts— path resolution, creation, migration, idempotency, EPERM/EACCES, nested dirs, per-file isolation, unexpected-error rejection.HookRegistry.test.ts— legacy path, new.diracpath, workspace rejection.Test plan
directoryEnsurers.test.ts— 30/30 passingHookRegistry.test.ts— 3/3 passingsrc/shared/storage/types.tsbaseline error