diff --git a/.scheduler-memory/1771338346/retrieve.json b/.scheduler-memory/1771338346/retrieve.json new file mode 100644 index 0000000..a3998d3 --- /dev/null +++ b/.scheduler-memory/1771338346/retrieve.json @@ -0,0 +1,18 @@ +{ + "cadence": "weekly", + "operation": "retrieve", + "runId": "1771338346", + "servicePath": "src/services/memory/index.js#getRelevantMemories", + "inputs": { + "agentId": "dead-code-agent", + "promptPath": "src/prompts/weekly/dead-code-agent.md", + "promptIntent": "Required startup + artifacts + memory + issue capture", + "query": "scheduler | weekly | agent | dead-code-agent | intent | Required startup + artifacts + memory + issue capture", + "events": 1 + }, + "outputs": { + "ingestedCount": 1, + "retrievedCount": 1 + }, + "status": "ok" +} \ No newline at end of file diff --git a/.scheduler-memory/1771338346/retrieve.ok b/.scheduler-memory/1771338346/retrieve.ok new file mode 100644 index 0000000..84b1719 --- /dev/null +++ b/.scheduler-memory/1771338346/retrieve.ok @@ -0,0 +1 @@ +MEMORY_RETRIEVED diff --git a/.scheduler-memory/1771338501/store.json b/.scheduler-memory/1771338501/store.json new file mode 100644 index 0000000..870b086 --- /dev/null +++ b/.scheduler-memory/1771338501/store.json @@ -0,0 +1,20 @@ +{ + "cadence": "weekly", + "operation": "store", + "runId": "1771338501", + "servicePath": "src/services/memory/index.js#ingestEvents", + "inputs": { + "agentId": "dead-code-agent", + "promptPath": "src/prompts/weekly/dead-code-agent.md", + "promptIntent": "Required startup + artifacts + memory + issue capture", + "events": 1, + "sourceFile": "memory-update.md" + }, + "outputs": { + "storedCount": 1, + "summaries": [ + "# Weekly Dead Code Analysis - 2026-02-17\n\n**Agent:** dead-code-agent\n**Cadence:** weekly\n**Status:** Completed\n\n## Summary\nExecuted a safety-first dead-code sweep.\n\n## Findings\n- Identified `scripts/fix-duplication.mjs` as an unused standalone script.\n- Identified `src/services/…" + ] + }, + "status": "ok" +} \ No newline at end of file diff --git a/.scheduler-memory/1771338501/store.ok b/.scheduler-memory/1771338501/store.ok new file mode 100644 index 0000000..1e40bb6 --- /dev/null +++ b/.scheduler-memory/1771338501/store.ok @@ -0,0 +1 @@ +MEMORY_STORED diff --git a/.scheduler-memory/memory-store.json b/.scheduler-memory/memory-store.json index b3dab5c..16b92db 100644 --- a/.scheduler-memory/memory-store.json +++ b/.scheduler-memory/memory-store.json @@ -246,5 +246,56 @@ "merged_into": null, "pinned": false } + ], + [ + "40f6bf1a-9715-4906-82d1-8f68be78eb3f", + { + "schema_version": 1, + "id": "40f6bf1a-9715-4906-82d1-8f68be78eb3f", + "agent_id": "dead-code-agent", + "session_id": "1771338346", + "type": "batch_window", + "content": "Memory retrieval seed for weekly :: Required startup + artifacts + memory + issue capture", + "summary": "Memory retrieval seed for weekly :: Required startup + artifacts + memory + issue capture", + "tags": [ + "scheduler", + "weekly", + "retrieve" + ], + "importance": 0.4, + "embedding_id": null, + "created_at": 1771338346815, + "last_seen": 1771338346833, + "source": "scheduler-retrieve", + "ttl_days": null, + "merged_into": null, + "pinned": false + } + ], + [ + "267412f9-9704-4739-8481-8317a89e6769", + { + "schema_version": 1, + "id": "267412f9-9704-4739-8481-8317a89e6769", + "agent_id": "dead-code-agent", + "session_id": "1771338501", + "type": "batch_window", + "content": "# Weekly Dead Code Analysis - 2026-02-17\n\n**Agent:** dead-code-agent\n**Cadence:** weekly\n**Status:** Completed\n\n## Summary\nExecuted a safety-first dead-code sweep.\n\n## Findings\n- Identified `scripts/fix-duplication.mjs` as an unused standalone script.\n- Identified `src/services/memory/db-adapter.js` as an unused module (exports `createMemoryRepository` but not imported anywhere).\n\n## Actions\n- Verified zero usage for both files using `grep`.\n- Deleted both files in branch `ai/dead-code-20260217`.\n- Verified deletions with `ls` and ran `npm run lint` and `npm run test:unit:lock-backend`.\n- Artifacts generated in `artifacts/dead-code-20260217/`.", + "summary": "# Weekly Dead Code Analysis - 2026-02-17\n\n**Agent:** dead-code-agent\n**Cadence:** weekly\n**Status:** Completed\n\n## Summary\nExecuted a safety-first dead-code sweep.\n\n## Findings\n- Identified `scripts/fix-duplication.mjs` as an unused standalone script.\n- Identified `src/services/…", + "tags": [ + "scheduler", + "weekly", + "store", + "insight" + ], + "importance": 0.8, + "embedding_id": null, + "created_at": 1771338501891, + "last_seen": 1771338501891, + "source": "agent-output", + "ttl_days": null, + "merged_into": null, + "pinned": false + } ] ] \ No newline at end of file diff --git a/artifacts/candidates.txt b/artifacts/candidates.txt new file mode 100644 index 0000000..b8515ae --- /dev/null +++ b/artifacts/candidates.txt @@ -0,0 +1,2 @@ +./scripts/fix-duplication.mjs +./src/services/memory/db-adapter.js diff --git a/artifacts/dead-code-20260217/candidates.txt b/artifacts/dead-code-20260217/candidates.txt new file mode 100644 index 0000000..917772b --- /dev/null +++ b/artifacts/dead-code-20260217/candidates.txt @@ -0,0 +1,2 @@ +scripts/fix-duplication.mjs +src/services/memory/db-adapter.js diff --git a/artifacts/dead-code-20260217/evidence-db-adapter.txt b/artifacts/dead-code-20260217/evidence-db-adapter.txt new file mode 100644 index 0000000..a3b5e33 --- /dev/null +++ b/artifacts/dead-code-20260217/evidence-db-adapter.txt @@ -0,0 +1,3 @@ +Candidate: src/services/memory/db-adapter.js +Evidence: grep -r 'db-adapter' . returned only the file itself. 'createMemoryRepository' is exported but not imported anywhere. +Rationale: Unused export, file not imported. diff --git a/artifacts/dead-code-20260217/evidence-fix-duplication.txt b/artifacts/dead-code-20260217/evidence-fix-duplication.txt new file mode 100644 index 0000000..341e5ab --- /dev/null +++ b/artifacts/dead-code-20260217/evidence-fix-duplication.txt @@ -0,0 +1,3 @@ +Candidate: scripts/fix-duplication.mjs +Evidence: grep -r 'fix-duplication' . returned only the file itself. +Rationale: Standalone script, not imported, seems one-off. diff --git a/artifacts/dead-code-20260217/lint.log b/artifacts/dead-code-20260217/lint.log new file mode 100644 index 0000000..650e817 --- /dev/null +++ b/artifacts/dead-code-20260217/lint.log @@ -0,0 +1,36 @@ + +> torch-lock@0.1.0 lint +> eslint . + + +/app/scripts/agent/smoke-test.mjs + 23:7 warning 'CONFIRM_PUBLIC' is assigned a value but never used no-unused-vars + 48:14 warning 'e' is defined but never used no-unused-vars + +/app/scripts/benchmark-dashboard.mjs + 25:14 warning 'e' is defined but never used no-unused-vars + 62:22 warning 'e' is defined but never used no-unused-vars + +/app/src/lock-utils.mjs + 29:14 warning '_' is assigned a value but never used no-unused-vars + +/app/src/ops.mjs + 338:16 warning 'e' is defined but never used no-unused-vars + +/app/src/services/memory/embedder.js + 17:28 warning 'text' is defined but never used no-unused-vars + +/app/test-audit/run-flaky-check.mjs + 18:24 warning 'code' is defined but never used no-unused-vars + +/app/test-audit/run-static-analysis.mjs + 15:12 warning 'e' is defined but never used no-unused-vars + +/app/test/lib.test.mjs + 102:11 warning 'mockPublishLock' is assigned a value but never used no-unused-vars + 356:11 warning 'mockPublishLock' is assigned a value but never used no-unused-vars + +/app/test/nostr-lock.test.mjs + 1:10 warning 'test' is defined but never used no-unused-vars + +✖ 12 problems (0 errors, 12 warnings) diff --git a/artifacts/dead-code-20260217/test-unit.log b/artifacts/dead-code-20260217/test-unit.log new file mode 100644 index 0000000..177741c --- /dev/null +++ b/artifacts/dead-code-20260217/test-unit.log @@ -0,0 +1,175 @@ + +> torch-lock@0.1.0 test:unit:lock-backend +> node --test test/lock-ops.test.mjs test/lock-ops-publish-retry.test.mjs + +TAP version 13 +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# Published to 1/1 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# Published to 1/1 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# [publish:primary] Publishing to 1 relays (wss://relay-a)... +# (node:3760) ExperimentalWarning: The MockTimers API is an experimental feature and might change at any time +# (Use `node --trace-warnings ...` to show where the warning was created) +# Subtest: publishLock retry behavior + # Subtest: succeeds immediately on first attempt without retries + ok 1 - succeeds immediately on first attempt without retries + --- + duration_ms: 7.968017 + type: 'test' + ... + # Subtest: retries a transient failure then succeeds within retry budget with bounded jitter + ok 2 - retries a transient failure then succeeds within retry budget with bounded jitter + --- + duration_ms: 4.052192 + type: 'test' + ... + # Subtest: returns terminal relay_publish_quorum_failure after exhausting retry budget + ok 3 - returns terminal relay_publish_quorum_failure after exhausting retry budget + --- + duration_ms: 4.597509 + type: 'test' + ... + # Subtest: short-circuits non-retryable failures without extra attempts + ok 4 - short-circuits non-retryable failures without extra attempts + --- + duration_ms: 5.143082 + type: 'test' + ... + 1..4 +ok 1 - publishLock retry behavior + --- + duration_ms: 25.051826 + type: 'suite' + ... +# [publish:primary] Publishing to 2 relays (wss://primary-a, wss://primary-b)... +# [publish:primary] Publishing to 2 relays (wss://primary-a, wss://primary-b)... +# Published to 2/2 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://primary-down)... +# [publish:primary] Publishing to 1 relays (wss://primary-down)... +# [publish:primary] Publishing to 1 relays (wss://primary-down)... +# [publish:primary] Publishing to 1 relays (wss://primary-bad)... +# [publish:primary] Publishing to 5 relays (relay-timeout, wss://dns, wss://tcp, wss://tls, wss://ws)... +# [publish:primary] Publishing to 2 relays (wss://relay-bad, wss://relay-ok)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"elapsedMs":1}],"totalElapsedMs":1} +# Published to 1/2 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://primary-bad)... +# [publish:fallback] Publishing to 1 relays (wss://fallback-ok)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"elapsedMs":0}],"totalElapsedMs":0} +# Published to 1/2 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 2 relays (wss://bad, wss://healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"elapsedMs":1}],"totalElapsedMs":1} +# Published to 1/2 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 2 relays (wss://healthy, wss://bad)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":2,"elapsedMs":0}],"totalElapsedMs":0} +# Published to 1/2 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":1,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":1,"elapsedMs":0}],"totalElapsedMs":0} +# Published to 1/1 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 1 relays (wss://healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":1,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":1,"elapsedMs":1}],"totalElapsedMs":1} +# Published to 1/1 relays (required=1, timeout=200ms) +# [publish:primary] Publishing to 2 relays (wss://primary-a, wss://primary-b)... +# [publish:fallback] Publishing to 1 relays (wss://fallback-healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"none","attemptId":"1","publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"elapsedMs":1}],"totalElapsedMs":1} +# Published to 1/3 relays (required=1, timeout=200ms) +# Subtest: lock-ops + # Subtest: parseLockEvent + # Subtest: parses a valid lock event correctly + ok 1 - parses a valid lock event correctly + --- + duration_ms: 4.396514 + type: 'test' + ... + 1..1 + ok 1 - parseLockEvent + --- + duration_ms: 6.9509 + type: 'suite' + ... + # Subtest: queryLocks + # Subtest: falls back to fallback relays when primary query fails + ok 1 - falls back to fallback relays when primary query fails + --- + duration_ms: 19.306079 + type: 'test' + ... + 1..1 + ok 2 - queryLocks + --- + duration_ms: 19.534904 + type: 'suite' + ... + # Subtest: publishLock + # Subtest: retries transient failures and succeeds on a later attempt + ok 1 - retries transient failures and succeeds on a later attempt + --- + duration_ms: 11.113808 + type: 'test' + ... + # Subtest: fails after retry budget is exhausted for persistent transient failures + ok 2 - fails after retry budget is exhausted for persistent transient failures + --- + duration_ms: 5.744097 + type: 'test' + ... + # Subtest: fails without retry for persistent validation failures + ok 3 - fails without retry for persistent validation failures + --- + duration_ms: 3.423183 + type: 'test' + ... + # Subtest: includes per-relay reason categories and retry metadata in failure diagnostics + ok 4 - includes per-relay reason categories and retry metadata in failure diagnostics + --- + duration_ms: 3.009087 + type: 'test' + ... + # Subtest: returns success when mixed relay outcomes still satisfy quorum + ok 5 - returns success when mixed relay outcomes still satisfy quorum + --- + duration_ms: 1.594997 + type: 'test' + ... + # Subtest: uses fallback relays to satisfy min success quorum + ok 6 - uses fallback relays to satisfy min success quorum + --- + duration_ms: 1.382646 + type: 'test' + ... + # Subtest: quarantines repeatedly failing relays and still reaches quorum with one healthy relay + ok 7 - quarantines repeatedly failing relays and still reaches quorum with one healthy relay + --- + duration_ms: 3.866976 + type: 'test' + ... + # Subtest: uses fallback and min active pool to reintroduce quarantined relay when needed + ok 8 - uses fallback and min active pool to reintroduce quarantined relay when needed + --- + duration_ms: 2.56156 + type: 'test' + ... + 1..8 + ok 3 - publishLock + --- + duration_ms: 34.278329 + type: 'suite' + ... + 1..3 +ok 2 - lock-ops + --- + duration_ms: 62.352434 + type: 'suite' + ... +1..2 +# tests 14 +# suites 5 +# pass 14 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 245.037609 diff --git a/find_dead_code.py b/find_dead_code.py new file mode 100644 index 0000000..fc87bac --- /dev/null +++ b/find_dead_code.py @@ -0,0 +1,102 @@ +import os +import subprocess +import sys + +def get_all_files(extensions=['.mjs', '.js']): + files = [] + for root, dirs, filenames in os.walk('.'): + if 'node_modules' in root or '.git' in root or 'artifacts' in root or 'dist' in root: + continue + for filename in filenames: + if any(filename.endswith(ext) for ext in extensions): + files.append(os.path.join(root, filename)) + return files + +def grep_file(filepath, all_files): + filename = os.path.basename(filepath) + name_no_ext = os.path.splitext(filename)[0] + + # Heuristic: search for the filename or the name without extension + # We must exclude the file itself from the search + + # Construct grep command + # git grep -F "string" -- . + + # Check for full filename usage + try: + # We use git grep because it's fast and respects .gitignore + # We search for the filename string. + # We assume if the filename appears in another file, it MIGHT be used. + # This is conservative. + + # Search for filename (e.g. "foo.mjs") + result_full = subprocess.run( + ['git', 'grep', '-F', filename, '--', '.'], + capture_output=True, text=True + ) + + # Search for name without extension (e.g. "foo") if it's not "index" + result_no_ext = None + if name_no_ext != 'index': + result_no_ext = subprocess.run( + ['git', 'grep', '-F', name_no_ext, '--', '.'], + capture_output=True, text=True + ) + + output = result_full.stdout + if result_no_ext: + output += result_no_ext.stdout + + lines = output.splitlines() + + # Filter out self-references + count = 0 + for line in lines: + if line.startswith(filepath.lstrip('./')): + continue + count += 1 + + return count + except Exception as e: + print(f"Error grepping {filepath}: {e}") + return 1 # Assume used on error + +def main(): + files = get_all_files() + candidates = [] + + # Known entry points or special files to exclude from "dead" list immediately + # These might not be imported but are used by scripts or configuration + exclusions = [ + './bin/torch-lock.mjs', + './src/lib.mjs', # Main entry + './src/constants.mjs', # Likely used + './build.mjs', + './eslint.config.mjs', + ] + + print(f"Scanning {len(files)} files...") + + for f in files: + if f in exclusions: + continue + + # Skip test files + if '.test.' in f or '/test/' in f or '/tests/' in f: + continue + + count = grep_file(f, files) + if count == 0: + candidates.append(f) + print(f"Candidate: {f}") + + if not candidates: + print("No candidates found.") + else: + print(f"Found {len(candidates)} candidates.") + with open('artifacts/candidates.txt', 'w') as f_out: + for c in candidates: + f_out.write(c + '\n') + +if __name__ == "__main__": + main() diff --git a/memory-update.md b/memory-update.md index 93fdfb1..5feb185 100644 --- a/memory-update.md +++ b/memory-update.md @@ -1,7 +1,18 @@ -# Changelog Update - 2026-02-17 +# Weekly Dead Code Analysis - 2026-02-17 -Generated draft release notes covering 212 commits. -Key areas: -- Scheduler automation improvements -- Memory management features -- Codebase modularization +**Agent:** dead-code-agent +**Cadence:** weekly +**Status:** Completed + +## Summary +Executed a safety-first dead-code sweep. + +## Findings +- Identified `scripts/fix-duplication.mjs` as an unused standalone script. +- Identified `src/services/memory/db-adapter.js` as an unused module (exports `createMemoryRepository` but not imported anywhere). + +## Actions +- Verified zero usage for both files using `grep`. +- Deleted both files in branch `ai/dead-code-20260217`. +- Verified deletions with `ls` and ran `npm run lint` and `npm run test:unit:lock-backend`. +- Artifacts generated in `artifacts/dead-code-20260217/`. diff --git a/scripts/fix-duplication.mjs b/scripts/fix-duplication.mjs deleted file mode 100644 index ef52699..0000000 --- a/scripts/fix-duplication.mjs +++ /dev/null @@ -1,32 +0,0 @@ -import fs from 'fs'; - -const fix = (filePath) => { - let content = fs.readFileSync(filePath, 'utf8'); - const header = '| # | Agent Name | Prompt File |\n|---|------------|-------------|'; - - const parts = content.split(header); - if (parts.length < 2) return; // Should not happen - - const pre = parts[0]; - const post = parts[1]; - - const lines = post.split('\n'); - const tableLines = []; - let i = 0; - - // Skip initial empty lines if any (though usually generateTable doesn't add them at start) - while(i < lines.length && lines[i].trim() === '') i++; - - // Capture rows - while(i < lines.length && lines[i].trim().startsWith('|')) { - tableLines.push(lines[i]); - i++; - } - - const newContent = pre + header + '\n' + tableLines.join('\n') + '\n'; - fs.writeFileSync(filePath, newContent); - console.log(`Fixed ${filePath}`); -}; - -fix('src/prompts/daily-scheduler.md'); -fix('src/prompts/weekly-scheduler.md'); diff --git a/src/services/memory/db-adapter.js b/src/services/memory/db-adapter.js deleted file mode 100644 index 2913173..0000000 --- a/src/services/memory/db-adapter.js +++ /dev/null @@ -1,199 +0,0 @@ -/** - * @typedef {{ query: (sql: string, params?: unknown[]) => Promise<{ rows?: any[], rowCount?: number }> }} QueryableDb - */ - -/** - * @param {QueryableDb} db - */ -export function createMemoryRepository(db) { - return { - /** - * @param {import('./schema.js').MemoryRecord} memory - */ - async insertMemory(memory) { - const result = await db.query( - `INSERT INTO memories ( - id, schema_version, agent_id, session_id, type, summary, - content, tags, importance, embedding_id, source, - created_at, last_seen, ttl_days, merged_into, pinned - ) VALUES ( - $1, $2, $3, $4, $5, $6, - $7::jsonb, $8::text[], $9, $10, $11::jsonb, - $12, $13, $14, $15, $16 - ) - ON CONFLICT (id) DO UPDATE SET - summary = EXCLUDED.summary, - content = EXCLUDED.content, - tags = EXCLUDED.tags, - importance = EXCLUDED.importance, - embedding_id = EXCLUDED.embedding_id, - source = EXCLUDED.source, - last_seen = EXCLUDED.last_seen, - ttl_days = EXCLUDED.ttl_days, - merged_into = EXCLUDED.merged_into, - pinned = EXCLUDED.pinned, - updated_at = NOW() - RETURNING *`, - [ - memory.id, - memory.schema_version, - memory.agent_id, - memory.session_id, - memory.type, - memory.summary, - JSON.stringify({ text: memory.content }), - memory.tags, - memory.importance, - memory.embedding_id, - JSON.stringify({ value: memory.source }), - memory.created_at, - memory.last_seen, - memory.ttl_days, - memory.merged_into, - memory.pinned, - ] - ); - - return result.rows?.[0] ?? null; - }, - - /** - * @param {string} id - * @param {number} [lastSeen] - */ - async updateMemoryUsage(id, lastSeen = Date.now()) { - const result = await db.query( - `UPDATE memories - SET last_seen = $2, updated_at = NOW() - WHERE id = $1 - RETURNING *`, - [id, lastSeen] - ); - - return result.rows?.[0] ?? null; - }, - - /** - * @param {{ cutoff: number, limit?: number }} options - */ - async listPruneCandidates(options) { - const limit = Number.isFinite(options.limit) ? Number(options.limit) : 500; - const result = await db.query( - `SELECT * - FROM memories - WHERE pinned = FALSE - AND (merged_into IS NOT NULL OR last_seen < $1) - ORDER BY last_seen ASC - LIMIT $2`, - [options.cutoff, limit] - ); - - return result.rows ?? []; - }, - - /** - * @param {string} id - * @param {string} mergedInto - */ - async markMerged(id, mergedInto) { - const result = await db.query( - `WITH updated AS ( - UPDATE memories - SET merged_into = $2, updated_at = NOW() - WHERE id = $1 - RETURNING id - ) - INSERT INTO memory_links (from_memory_id, to_memory_id, relation_type) - SELECT id, $2, 'merged_into' FROM updated - ON CONFLICT (from_memory_id, to_memory_id, relation_type) DO NOTHING - RETURNING from_memory_id`, - [id, mergedInto] - ); - - return (result.rowCount ?? 0) > 0; - }, - - /** - * @param {string} id - * @param {boolean} pinned - */ - async setPinned(id, pinned) { - const result = await db.query( - `WITH updated AS ( - UPDATE memories - SET pinned = $2, updated_at = NOW(), last_seen = $3 - WHERE id = $1 - RETURNING id, pinned - ) - INSERT INTO memory_admin_flags (memory_id, pinned, pinned_at, updated_at) - SELECT id, pinned, CASE WHEN pinned THEN NOW() ELSE NULL END, NOW() FROM updated - ON CONFLICT (memory_id) DO UPDATE SET - pinned = EXCLUDED.pinned, - pinned_at = EXCLUDED.pinned_at, - updated_at = EXCLUDED.updated_at - RETURNING memory_id, pinned`, - [id, pinned, Date.now()] - ); - - return result.rows?.[0] ?? null; - }, - - /** - * @param {string} id - */ - async getMemoryById(id) { - const result = await db.query( - `SELECT * - FROM memories - WHERE id = $1 - LIMIT 1`, - [id] - ); - - return result.rows?.[0] ?? null; - }, - - /** - * @param {{ agent_id?: string, type?: string, pinned?: boolean, limit?: number, offset?: number }} [filters] - */ - async listMemories(filters = {}) { - const where = []; - const params = []; - - if (typeof filters.agent_id === 'string' && filters.agent_id.length > 0) { - params.push(filters.agent_id); - where.push(`agent_id = $${params.length}`); - } - - if (typeof filters.type === 'string' && filters.type.length > 0) { - params.push(filters.type); - where.push(`type = $${params.length}`); - } - - if (typeof filters.pinned === 'boolean') { - params.push(filters.pinned); - where.push(`pinned = $${params.length}`); - } - - const limit = Number.isFinite(filters.limit) ? Math.max(1, Number(filters.limit)) : 100; - const offset = Number.isFinite(filters.offset) ? Math.max(0, Number(filters.offset)) : 0; - params.push(limit); - const limitPos = params.length; - params.push(offset); - const offsetPos = params.length; - - const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : ''; - const result = await db.query( - `SELECT * - FROM memories - ${whereClause} - ORDER BY last_seen DESC - LIMIT $${limitPos} - OFFSET $${offsetPos}`, - params - ); - - return result.rows ?? []; - }, - }; -} diff --git a/task-logs/weekly/2026-02-17T14-29-07Z__dead-code-agent__completed.md b/task-logs/weekly/2026-02-17T14-29-07Z__dead-code-agent__completed.md new file mode 100644 index 0000000..3ae42ba --- /dev/null +++ b/task-logs/weekly/2026-02-17T14-29-07Z__dead-code-agent__completed.md @@ -0,0 +1,14 @@ +--- +agent: dead-code-agent +cadence: weekly +status: completed +timestamp: 2026-02-17T14:29:07Z +platform: jules +--- + +Successfully executed dead-code-agent. +Removed unused files: +- scripts/fix-duplication.mjs +- src/services/memory/db-adapter.js + +Verification passed.