Problem
Five separate hot paths share the same anti-pattern: a for...of loop awaiting one I/O operation (git clone, file read+hash) per iteration instead of batching with bounded concurrency.
Where
src/application/use-cases/plugin/plugin-update-use-case.ts:46-56,74-86 — N plugins = N sequential git clone calls
src/application/use-cases/marketplace/marketplace-refresh-use-case.ts:42-51 — sequential per-marketplace fetch (also hit from aidd update)
src/application/use-cases/status-use-case.ts:181-198,252-268 — sequential per-file fileExists+readFileHash; status-all-use-case.ts:44-58 runs this 3x sequentially for --all
src/application/use-cases/doctor/doctor-tracked-files-use-case.ts:37-77 — same drift-check pattern, duplicated instead of shared with status
Caution
marketplace-refresh-use-case.ts's target, marketplace-registry-adapter.ts:44-66, does read-modify-write on one shared marketplaces.json per scope — a naive Promise.all on the writes would race and drop updates. Parallelize the fetch step, keep registry writes serial (or batched into one combined read-modify-write) after all fetches resolve. Same caution applies to file-count concurrency for the drift checks — use bounded concurrency (chunked), not unbounded Promise.all, to avoid EMFILE on large trees.
Fix
- Parallelize
plugin-update-use-case.ts's fetch+compare phase, keep the manifest save serial after.
- Parallelize
marketplace-refresh-use-case.ts's fetch phase, batch registry writes serially after.
- Extract one shared, bounded-concurrency drift-check helper used by both
status-use-case.ts and doctor-tracked-files-use-case.ts.
Acceptance criteria
Found by a performance audit of cli/ — full report: cli/aidd_docs/tasks/2026_07/2026_07_22_audit/performance.md.
Problem
Five separate hot paths share the same anti-pattern: a
for...ofloopawaiting one I/O operation (git clone, file read+hash) per iteration instead of batching with bounded concurrency.Where
src/application/use-cases/plugin/plugin-update-use-case.ts:46-56,74-86— N plugins = N sequentialgit clonecallssrc/application/use-cases/marketplace/marketplace-refresh-use-case.ts:42-51— sequential per-marketplace fetch (also hit fromaidd update)src/application/use-cases/status-use-case.ts:181-198,252-268— sequential per-filefileExists+readFileHash;status-all-use-case.ts:44-58runs this 3x sequentially for--allsrc/application/use-cases/doctor/doctor-tracked-files-use-case.ts:37-77— same drift-check pattern, duplicated instead of shared withstatusCaution
marketplace-refresh-use-case.ts's target,marketplace-registry-adapter.ts:44-66, does read-modify-write on one sharedmarketplaces.jsonper scope — a naivePromise.allon the writes would race and drop updates. Parallelize the fetch step, keep registry writes serial (or batched into one combined read-modify-write) after all fetches resolve. Same caution applies to file-count concurrency for the drift checks — use bounded concurrency (chunked), not unboundedPromise.all, to avoidEMFILEon large trees.Fix
plugin-update-use-case.ts's fetch+compare phase, keep the manifest save serial after.marketplace-refresh-use-case.ts's fetch phase, batch registry writes serially after.status-use-case.tsanddoctor-tracked-files-use-case.ts.Acceptance criteria
aidd plugin update/aidd updateclone plugins concurrently (bounded), not one at a timeaidd marketplace refreshfetches marketplaces concurrently, registry writes stay correct (no lost updates under concurrent refresh)status/doctordrift checks use one shared, bounded-concurrency helper instead of two duplicated sequential loopsFound by a performance audit of
cli/— full report:cli/aidd_docs/tasks/2026_07/2026_07_22_audit/performance.md.