AI Agents SDE Task Viewer
      • Context
      • Plan
      • Prd
  1. Home
  2. AgentSDE
  3. meridian-backend
  4. gh-14
  5. plan
  6. context.json
context.json(4.0 KB)· Apr 2, 2026
{
  "meta": {
    "agent": "planner",
    "task_id": "14",
    "title": "Fix SearchIndexService OOM — rebuild guard + memory cap",
    "created_at": "2026-04-02T12:00:00Z"
  },
  "inputs": [
    {
      "name": "GitHub Issue #14",
      "type": "context",
      "ref": "AgentSDE/meridian-backend#14",
      "notes": "OOM crash from unbounded concurrent index rebuilds"
    },
    {
      "name": "SearchIndexService",
      "type": "file",
      "ref": "src/modules/search/search-index.service.ts",
      "notes": "buildIndex() has no concurrency guard; handleFileChange() triggers full rebuild on FILE_CHANGE_EVENT"
    },
    {
      "name": "FileWatcherService",
      "type": "file",
      "ref": "src/modules/file-watcher/file-watcher.service.ts",
      "notes": "Already has 500ms debounce on FILE_CHANGE_EVENT; FILE_CHANGE_DETAIL_EVENT fires immediately per file"
    },
    {
      "name": "start-backend.sh",
      "type": "file",
      "ref": "services/start-backend.sh",
      "notes": "No --max-old-space-size flag on node exec"
    },
    {
      "name": "AGENTS.md",
      "type": "context",
      "ref": "AGENTS.md",
      "notes": "PM2 runs as viewerv2-backend; no ecosystem.config.js exists"
    }
  ],
  "outputs": [
    {
      "name": "plan.md",
      "type": "plan",
      "format": "md",
      "content": "Implementation plan for rebuild guard + PM2 memory cap"
    }
  ],
  "files": [
    {
      "path": "src/modules/search/search-index.service.ts",
      "action": "modify",
      "reason": "Add AbortController-based rebuild guard with rebuildPending flag to prevent concurrent buildIndex() calls"
    },
    {
      "path": "ecosystem.config.js",
      "action": "create",
      "reason": "PM2 config with --max-old-space-size=512 and max_memory_restart"
    },
    {
      "path": "services/start-backend.sh",
      "action": "modify",
      "reason": "Add --max-old-space-size=512 to node exec line"
    },
    {
      "path": "src/modules/search/search-index.service.spec.ts",
      "action": "modify",
      "reason": "Add tests for rebuild guard, cancellation, and rebuildPending behavior"
    }
  ],
  "steps": [
    {
      "id": "S1",
      "summary": "Add rebuild guard with AbortController cancellation to SearchIndexService",
      "acceptance": [
        "Only one buildIndex() runs at a time — concurrent calls abort the in-flight rebuild",
        "AbortController signal is checked between task iterations in buildIndex()",
        "rebuildPending flag triggers a follow-up rebuild after the current one completes or aborts",
        "handleFileChangeDetail() skips incremental updates when rebuildPending is true"
      ],
      "depends_on": []
    },
    {
      "id": "S2",
      "summary": "Create ecosystem.config.js and update start-backend.sh with --max-old-space-size=512",
      "acceptance": [
        "ecosystem.config.js exists at repo root with app name viewerv2-backend and node_args --max-old-space-size=512",
        "ecosystem.config.js includes max_memory_restart: 512M",
        "start-backend.sh exec line includes --max-old-space-size=512"
      ],
      "depends_on": []
    },
    {
      "id": "S3",
      "summary": "Update SearchIndexService unit tests for rebuild guard and cancellation",
      "acceptance": [
        "Test: concurrent handleFileChange() calls result in single active rebuild",
        "Test: aborted rebuild does not swap index (old index preserved)",
        "Test: rebuildPending triggers follow-up rebuild after current completes",
        "All existing tests continue to pass"
      ],
      "depends_on": [
        "S1"
      ]
    }
  ],
  "risks": [
    {
      "risk": "AbortController per-task granularity may allow partial work before abort",
      "mitigation": "Acceptable — per-file checks add overhead for negligible memory savings with small markdown files"
    },
    {
      "risk": "PM2 max_memory_restart causes restart loops under sustained load",
      "mitigation": "Rebuild guard eliminates concurrent allocations; 512MB is well above single-rebuild peak"
    }
  ],
  "assumptions": [
    "The 500ms debounce in FileWatcherService is sufficient — no changes needed there",
    "Single buildIndex() peak memory is well under 512MB (OOM was caused by concurrent rebuilds, not a single rebuild)",
    "PM2 is used in production per AGENTS.md despite launchd plist existing for local macOS dev"
  ],
  "open_questions": []
}
Plan