Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
## 2026-05-16 - [Batch FeathersJS user fetches with $in operator to fix N+1 query]
**Learning:** FeathersJS allows passing `$in` clauses through the query parameter (e.g. `user_id: { $in: ownerIds }`). When writing custom Feathers service logic, you can easily parse this array and pass it to Drizzle's `inArray()` to perform a batched query, instead of looping over `service.get(id)` causing N+1 database roundtrips.
**Action:** When implementing or updating custom Feathers `find()` methods, extract and parse the `$in` parameters to support batched Drizzle `inArray()` lookups, and always replace `Promise.all(ids.map(id => service.get(id)))` with a single batched `find()` call.
## 2026-05-23 - [Prevent O(N) disk I/O when loading config in bulk operations]
**Learning:** `loadConfig()` reads and parses the YAML configuration file from disk synchronously during its operation. Calling it inside a loop or mapped operation over multiple items (e.g. `Promise.all(data.map(...))`) creates an O(N) disk I/O and parsing overhead that severely degrades performance for bulk actions.
**Action:** When a method needs to apply configuration to multiple items, pre-load the configuration once before the loop and pass it as an argument or context to the handlers, changing O(N) config loads to O(1).
16 changes: 12 additions & 4 deletions apps/agor-daemon/src/services/worktrees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,9 +226,14 @@ export class WorktreesService extends DrizzleService<Worktree, Partial<Worktree>
* so admins can set org-wide defaults in config.yaml. Explicit values on the
* input always win; defaults fill in only when the caller omits the field.
*/
private async applyWorktreeCreateDefaults(data: Partial<Worktree>): Promise<Partial<Worktree>> {
const config = await loadConfig();
const defaults = config.worktrees;
private async applyWorktreeCreateDefaults(
data: Partial<Worktree>,
config?: import('@agor/core/config').AgorConfig
): Promise<Partial<Worktree>> {
// ⚡ Bolt Performance Optimization:
// Pre-load configuration to avoid O(N) disk I/O when applying defaults to multiple worktrees.
const loadedConfig = config ?? (await loadConfig());
const defaults = loadedConfig.worktrees;
if (!defaults) return data;

const withDefaults: Partial<Worktree> = { ...data };
Expand All @@ -252,8 +257,11 @@ export class WorktreesService extends DrizzleService<Worktree, Partial<Worktree>
params?: WorktreeParams
): Promise<Worktree | Worktree[]> {
if (Array.isArray(data)) {
// ⚡ Bolt Performance Optimization:
// Load config once outside the loop instead of during each map iteration.
const preLoadedConfig = await loadConfig();
const withDefaults = await Promise.all(
data.map((item) => this.applyWorktreeCreateDefaults(item))
data.map((item) => this.applyWorktreeCreateDefaults(item, preLoadedConfig))
);
return super.create(withDefaults, params) as Promise<Worktree[]>;
}
Expand Down