10 Fingerprint Store Abstraction and Batch File Record API
Plan Status: completed Last Reviewed: 2026-05-06 Source: user request for file change detection decoupling in nop-code Related: 09-nop-code-memory-graphql-fix.md
Purpose
将 nop-code-core 中增量文件变化检测与硬编码的磁盘存储 ManifestStore 解耦,引入 IFingerprintStore 抽象接口,使指纹持久化可切换(磁盘、内存、数据库)。在 ICodeIndexService 上增加批量 save/load/delete file record API,复用已有 NopCodeFile ORM 实体。全部新代码由自动化测试覆盖。
Current Baseline
- nop-code-core / incremental 包有
FileFingerprint(DTO)、ManifestStore(磁盘 JSON)、IncrementalDetector(SHA-256 两级检测)、ChangeSet。ManifestStore是具体类,无接口抽象。 - nop-code-dao 的
NopCodeFileORM 实体已有fileHash(VARCHAR 64)、lastModified(BIGINT)、fileSize(BIGINT) 列,唯一键(indexId, filePath)。但CodeIndexService.saveFileResultInSession()从未写入这些字段。 CodeIndexService.triggerIncrementalIndex()直接new ManifestStore()绑定磁盘。ICodeIndexService仅有单文件indexFile(),无批量操作。- 现有测试:
TestManifestStore(13 cases)、TestIncrementalDetector(12 cases)、TestProjectAnalyzerIncremental— 全部通过。
Goals
ProjectAnalyzer和CodeIndexService依赖IFingerprintStore(接口)而非ManifestStore(具体类)- 交付两种实现:
PathFingerprintStore(磁盘)和InMemoryFingerprintStore(内存/测试) ICodeIndexService暴露batchSaveFileRecords/batchLoadFileRecords/batchDeleteFileRecordssaveFileResultInSession正确写入fileHash/lastModified/fileSize- 全部新代码和改动有对应测试
Non-Goals
- 新建 ORM 实体或数据库表 —
NopCodeFile已有所有必需列 - 实现独立的
DbFingerprintStore类 — service 层 batch 方法直接承担此角色 - 修改
IncrementalDetector或ChangeSetAPI - 删除
ManifestStore— 保留为PathFingerprintStore的内部委托 - 暴露 GraphQL/REST 端点给 batch API
Scope
In Scope
IFingerprintStore接口 (nop-code-core)InMemoryFingerprintStore实现 (nop-code-core)PathFingerprintStore实现 (nop-code-core,委托 ManifestStore)ProjectAnalyzer新增接受IFingerprintStore的analyzeIncremental重载ICodeIndexService增加 3 个 batch 方法签名 +CodeIndexService实现saveFileResultInSession补充指纹字段写入triggerIncrementalIndex改用IFingerprintStore- 契约测试 + 实现测试 + 集成测试
Out Of Scope
DbFingerprintStore作为独立类- GraphQL/REST 端点暴露
- 语言适配器变更(java/typescript/python)
- 前端变更
Execution Plan
Phase 1 - Interface and Implementations
Status: completed
Targets: nop-code/nop-code-core/src/main/java/io/nop/code/core/incremental/
-
Item Types:
Proof -
创建
IFingerprintStore接口,包含saveFingerprints(String indexId, List<FileFingerprint>)、loadFingerprints(String indexId)、deleteByPaths(String indexId, List<String>)、deleteByIndex(String indexId)四个方法 -
创建
InMemoryFingerprintStore实现接口,内部ConcurrentHashMap<String, Map<String, FileFingerprint>>按 indexId → filePath 分区,所有方法 no-op safe -
创建
PathFingerprintStore实现接口,委托ManifestStore序列化,构造器PathFingerprintStore(Path baseDir),indexId 映射为baseDir / indexId / manifest.json
Exit Criteria:
- 三个新文件编译通过:
IFingerprintStore.java、InMemoryFingerprintStore.java、PathFingerprintStore.java -
mvn compile -pl nop-code/nop-code-coreexit code 0
Phase 2 - Contract Tests
Status: completed
Targets: nop-code/nop-code-core/src/test/java/io/nop/code/core/incremental/
-
Item Types:
Proof -
创建抽象类
TestIFingerprintStore,提供abstract IFingerprintStore createStore()工厂方法,包含 ≥10 个@Test方法覆盖:save/load empty、save/load single、save/load batch、save updates existing、load non-existent index、deleteByPaths、deleteByIndex、delete non-existent paths no-op、large batch 100+、special characters in path -
创建
TestInMemoryFingerprintStore extends TestIFingerprintStore,createStore()返回new InMemoryFingerprintStore() -
创建
TestPathFingerprintStore extends TestIFingerprintStore,createStore()返回new PathFingerprintStore(tempDir),额外增加磁盘特有测试:manifest file created on disk、multiple indexes isolated、overwrite existing manifest
Exit Criteria:
- 三个测试文件存在且编译通过
-
mvn test -pl nop-code/nop-code-coreexit code 0(新测试 +TestManifestStore+TestIncrementalDetector全通过)
Phase 3 - ProjectAnalyzer Refactor
Status: completed
Targets: nop-code/nop-code-core/src/main/java/io/nop/code/core/analyzer/ProjectAnalyzer.java, nop-code/nop-code-core/src/test/java/io/nop/code/core/analyzer/
-
Item Types:
Fix -
ProjectAnalyzer新增方法analyzeIncremental(Path projectRoot, String indexId, IFingerprintStore fingerprintStore):load fingerprints from store → detect changes → re-analyze → save fingerprints back to store -
保留旧方法
analyzeIncremental(Path, Path manifestPath),内部创建PathFingerprintStore委托到新方法 -
创建
TestProjectAnalyzerWithStore测试:incremental with in-memory store detects changes、no changes skips analysis、mixed add/modify/delete scenario
Exit Criteria:
- 新方法签名存在且编译通过,旧方法仍可用
-
TestProjectAnalyzerWithStore所有测试通过 -
TestProjectAnalyzerIncremental(现有)不回归 -
mvn test -pl nop-code/nop-code-coreexit code 0
Phase 4 - Service Batch API
Status: completed
Targets: nop-code/nop-code-service/src/main/java/io/nop/code/service/
-
Item Types:
Fix,Decision -
ICodeIndexService增加 3 个方法签名:batchSaveFileRecords(String indexId, List<FileFingerprint>)、batchLoadFileRecords(String indexId)、batchDeleteFileRecords(String indexId, List<String> filePaths) -
CodeIndexService实现batchSaveFileRecords:查询 NopCodeFile by (indexId, filePath),存在则更新 fileHash/lastModified/fileSize,不存在则新建 -
CodeIndexService实现batchLoadFileRecords:查询所有 NopCodeFile by indexId,转为FileFingerprint列表 -
CodeIndexService实现batchDeleteFileRecords:复用已有deleteFileRecordshelper 处理级联删除 -
saveFileResultInSession补充:计算 SHA-256 hash 写入fileHash,写入lastModified = System.currentTimeMillis(),写入fileSize = sourceCode.length() -
triggerIncrementalIndex改造:不再new ManifestStore(),改用fingerprintStore.loadFingerprints(indexId)获取旧指纹,分析后用fingerprintStore.saveFingerprints(indexId, newFingerprints)持久化 -
CodeIndexService增加IFingerprintStore字段,默认InMemoryFingerprintStore,可通过 setter 注入
Exit Criteria:
-
ICodeIndexService有 3 个新方法签名 -
CodeIndexService6 处改动全部实现 -
saveFileResultInSession对新建的NopCodeFile实体设置了 fileHash/lastModified/fileSize -
triggerIncrementalIndex不再包含new ManifestStore()调用 -
mvn compile -pl nop-code/nop-code-serviceexit code 0
Phase 5 - Integration Tests
Status: completed
Targets: nop-code/nop-code-service/src/test/java/io/nop/code/service/
-
Item Types:
Proof -
创建
TestBatchFileRecordOperations:使用 JUnit 5,测试:batch load returns empty when no database、batch save graceful when no database、batch delete graceful when no database、fingerprint store integration、batch save then load round-trip、empty/null input handling
Exit Criteria:
- 测试文件存在,≥7 个
@Test方法 - 测试文件编译通过(
mvn compile test -pl nop-code/nop-code-service编译成功;注意:nop-code-service 有 6 个既有测试文件存在 Path→String 类型不匹配的预存编译错误,与本计划变更无关)
Phase 6 - Build Verification
Status: completed
Targets: nop-code/
-
Item Types:
Proof -
运行
mvn clean install -Dmaven.test.skip=true -pl nop-code -am -T 1C
Exit Criteria:
- 全量构建 exit code 0(
BUILD SUCCESS) - 所有前序 Phase 的 Exit Criteria 已勾选
Closure Gates
关闭条件:只有本 section 所有条目以及每个 Phase 的 Exit Criteria 全部勾选为
[x]后,才能将Plan Status改为completed。
- 所有 in-scope confirmed live defects 已修复
-
IFingerprintStore接口 + 2 种实现编译通过且测试通过 -
ProjectAnalyzer新analyzeIncremental重载接受IFingerprintStore,旧方法保持兼容 -
ICodeIndexService3 个 batch 方法实现正确 -
saveFileResultInSession写入 fileHash/lastModified/fileSize -
triggerIncrementalIndex不再直接依赖ManifestStore - 不存在被静默降级到 deferred / follow-up 的 in-scope live defect 或 contract drift
- 受影响的 owner docs 已同步到 live baseline,或明确写明 No owner-doc update required
- 独立子 agent / 独立审阅者 closure-audit 已完成并记录证据
-
mvn compile通过(nop-code-core, nop-code-service) -
mvn test通过(nop-code-core: 122 tests, 0 failures) -
mvn clean install -Dmaven.test.skip=true通过(nop-code 全量 BUILD SUCCESS)
Deferred But Adjudicated
(无 deferred 项)
Non-Blocking Follow-ups
- 未来可考虑
DbFingerprintStore作为独立类放在 nop-code-dao,直接操作 ORM 而非通过 service 层 - 未来可将
ManifestStore的手写 JSON 序列化替换为IJsonSerializer - nop-code-service 有 6 个既有测试文件存在 Path→String 类型预存编译错误(与本次变更无关),需后续修复
Decisions
- [D1] 不新建 ORM 实体 — 复用
NopCodeFile.fileHash/lastModified/fileSize。列和唯一键已存在,新建表是冗余的。 - [D2] 保留
ManifestStore— 被现有测试直接依赖,作为PathFingerprintStore的内部委托,比删除更安全。 - [D3] 不做
DbFingerprintStore— service 层 batch 方法直接承担此角色,避免在 core 层引入 ORM 依赖。 - [D4]
analyzeIncremental(Path, Path)保留原始实现而非委托到新方法 — 委托会改变 manifest 文件存储路径(PathFingerprintStore 将 indexId 映射到子目录),破坏现有测试的向后兼容性。新analyzeIncremental(Path, String, IFingerprintStore)重载是新的推荐 API。
Closure
Status Note: All 6 phases completed. 122 nop-code-core tests pass (0 failures). Full nop-code build succeeds. No in-scope defects deferred.
Closure Audit Evidence:
- Phase 1+2+3: Subagent ses_2047adf81ffe9lGhNl5wafgQT0 — 6 new files + 1 modified file.
mvn test -pl nop-code/nop-code-core= 122 tests, 0 failures. - Phase 4+5: Subagent ses_20479a8b5ffe23LDQ4Dv1OQkNg — 1 new test file + 2 modified source files.
mvn compile -pl nop-code/nop-code-service -am= BUILD SUCCESS. - Phase 6:
./mvnw clean install -Dmaven.test.skip=true -pl nop-code -am -T 1C= BUILD SUCCESS (all nop-code modules).