feat: implement high-performance atom cache and maintenance tools (Issue #29)#54
feat: implement high-performance atom cache and maintenance tools (Issue #29)#54c-ferrier wants to merge 6 commits into
Conversation
ba52aba to
47cd720
Compare
…intenance tools Optimizes repository scanning by caching git file lists in a sharded plain-text filesystem structure within .lore/cache/atoms. This eliminates the N+1 Git process bottleneck during metadata extraction. To provide a complete maintenance lifecycle, 'lore init' now automatically ensures '.lore/cache' is added to .gitignore, and a new 'lore cache --clean' command allows users to manually reset local storage. The implementation follows the Null Object pattern for safe bypass and is fully configurable via CLI flags and environment variables. Lore-id: f6cc4348 Constraint: Must only cache file lists to remain agnostic to config changes Constraint: Cache files must not have an extension to avoid redundancy Constraint: Storage directory MUST be .lore/cache/atoms Constraint: Service instantiation MUST happen inside main() for async config loading Constraint: Bypass logic must reside in src/util to avoid side effects during tests Constraint: Gitignore automation must be localized to the .lore initialization directory Constraint: Cache cleaning MUST use recursive force removal for robustness Rejected: Full JSON caching of trailers | Increased complexity and potential for stale data Rejected: Keeping shouldBypassCache in main.ts | Caused unexpected execution during unit tests Rejected: Automatic .gitignore update on every command | Too intrusive; belongs in 'init' Rejected: Adding --clean to 'init' | Better as a dedicated 'cache' command for future extensibility Tested: Verified all 444 unit tests pass (including 6 new tests for init/cache commands) Tested: Benchmark: ~52% speedup confirmed by eliminating N+1 git processes Tested: Verified bypass via --no-cache, LORE_NO_CACHE=true, and config flags Tested: Verified .gitignore creation and idempotency in 'lore init' Tested: Verified successful directory removal in 'lore cache --clean' Confidence: high Scope-risk: narrow Reversibility: clean Assisted-by: Gemini:CLI [lore-protocol]
47cd720 to
d1b8d48
Compare
|
Ready for feedback or merging. |
Ian-stetsenko
left a comment
There was a problem hiding this comment.
Nice work on this one. Architecture looks good — clean interface, Null Object done right, files-only as we discussed. Few things to address:
-
process.exit(1)incache.ts— Project convention isprocess.exitCode = 1; return;(see CLAUDE.md).process.exit()kills the process immediately and can lose pending I/O output. -
error: anyincache.tsandatom-cache.ts— Should beerror: unknownwith type narrowing (error instanceof Error ? error.message : String(error)for messages,(error as NodeJS.ErrnoException).codefor filesystem errors). Matches the pattern inconfig-loader.tsandhead-lore-id-reader.ts. -
Corrupted cache file returns garbage — If a cache file is partially written or contains bad data,
content.split('\n')returns garbage strings that silently corrupt query results.getFilesshould returnnull(treat as cache miss) when content looks wrong (e.g., contains NUL bytes or is empty when it shouldn't be). -
No test for corrupted cache — Add a test that writes bad content to a cache path and verifies
getFilesreturnsnullinstead of garbage.
… standards Refines the AtomCache implementation to handle potential data corruption and align with established project patterns for error handling and process execution. To prevent "garbage" results from partial writes or filesystem errors, getFiles now validates cache content for NUL bytes and emptiness before parsing. The write path has been upgraded to an atomic "write-then-rename" strategy using unique temporary files, minimizing the risk of corruption during concurrent access or process crashes. Error handling has been migrated from any to unknown with explicit narrowing, and the cache command now uses process.exitCode instead of process.exit() to ensure clean I/O termination. Lore-id: fbd91a65 Constraint: Must treat empty, whitespace-only, or NUL-containing files as cache misses Constraint: Atomic writes MUST use randomUUID to prevent collision between concurrent processes Constraint: Command errors MUST set process.exitCode and return to avoid abrupt termination Constraint: Error narrowing MUST follow the pattern in config-loader.ts Rejected: Using fs.writeSync | Prefer async atomic rename for better performance and consistency Rejected: Trimming all whitespace | Must preserve single newline for --allow-empty commits Tested: Added corruption tests covering NUL bytes, empty files, and whitespace Tested: Verified project-wide suite (445 tests) passing Depends-on: f6cc4348 Confidence: high Assisted-by: Gemini:CLI [lore-protocol]
|
Updates in a new commit in the pull request. Let me know what you think. |
Implemented robust Lore root resolution to ensure the atom cache is shared across the entire project, even when running commands from subdirectories. The resolution logic (now extracted to a testable utility) prioritizes the nearest '.lore' directory, then the git repository root, and finally falls back to the current working directory. This prevents the creation of redundant '.lore' directories in sub-folders and ensures cache hits are consistent throughout the workspace. Lore-id: 8b23be8b Constraint: Root resolution MUST prioritize nearest .lore directory for monorepo support Constraint: Git root is the primary fallback for standard repositories Constraint: Resolution MUST be non-throwing (fallback to CWD on error) Tested: Added 5 unit tests for resolveLoreRoot utility Tested: Manually verified cache reuse from src/ subdirectory after build Tested: Full suite (450 tests) passing Depends-on: fbd91a65 Confidence: high Assisted-by: Gemini:CLI [lore-protocol]
- Add hex hash validation in AtomCache to prevent path traversal - Remove concrete NullAtomCache import from AtomRepository (DIP) - Replace unsafe `as readonly string[]` cast with null guard - Add comment to shouldBypassCache explaining pre-parse argv access - Clarify ensureCacheIgnored uses cwd to match lore init convention - Remove unused CONFIG_DIR import from cache.ts - Fix duplicate step numbering in main.ts - Fix blank lines in atom-cache.ts and atom-repository.ts - Add test for invalid hash rejection Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Pushed fixes for all review findings: Security:
DIP compliance:
Type safety:
Convention fixes:
451 tests pass, typecheck clean. |
|
I've pushed two new commits to address the final feedback items: 1. Architectural Refactor: Root ResolverMoved 2. Robustness Verification: Self-Healing CacheAdded a dedicated unit test in Discussion: .gitignore and Monorepo StrategyRegarding the The current implementation follows a Monorepo Override pattern. The Lore configuration system is designed to walk up the directory tree and merge configurations, allowing sub-projects to have their own By creating the
However, I agree this could lead to "accidental fragmentation" in a standard single-project repository if run from a subdirectory. How would you like to handle this duality?
I'd love your take on the preferred architectural direction for monorepos before finalizing this part. |
Relocated root-resolver from util/ to services/ as requested during architectural review. This aligns with project standards for modules performing complex logic and I/O. Updated all internal and test references. Lore-id: ebe7b9c4 Constraint: Root resolution must be non-throwing (fallback to CWD on error) Tested: Successfully moved src/util/root-resolver.ts to src/services/ Tested: Updated imports in src/main.ts and tests/unit/root-resolver.test.ts Tested: Verified all 5 root-resolver unit tests pass Tested: Full project suite (452 tests) passing Confidence: high Assisted-by: Gemini:CLI [lore-protocol]
Added a unit test to demonstrate that the AtomCache correctly handles and recovers from corrupted files. When binary corruption or empty files are encountered, the system treats them as a cache miss, allowing the repository to fetch fresh data and overwrite the 'bad' file via an atomic rename. Lore-id: 61f54538 Constraint: Must treat empty, whitespace-only, or NUL-containing files as cache misses Tested: Added 'should recover from and overwrite corrupted cache files' test case Tested: Verified detection of NUL bytes and whitespace-only corruption Tested: Confirmed successful overwrite with valid data following a detected miss Confidence: high Assisted-by: Gemini:CLI [lore-protocol]
3e24008 to
7c87495
Compare
This PR implements Phase 1: The Immutable Atom Cache for #29, providing a foundational performance boost for all Lore queries.
Key Improvements:
.lore/cache/atoms.lore initnow automatically ensures.lore/cacheis ignored by your.gitignore.lore cache --cleanto safely manage or reset local storage.This unified implementation replaces the draft approach in the previously closed PR #47.