From 03ccb232eec7c53f38fc9f14276e52d9a0d4ff1b Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 19:50:48 +0000 Subject: [PATCH] Refactor lock backend constants and fix scheduler syntax error - Extracted hardcoded retry/health constants from `src/lock-ops.mjs` to `src/constants.mjs`. - Refactored `src/lock-ops.mjs` to use `LockPublisher` class pattern and shared constants. - Fixed `SyntaxError` in `src/lock-ops.mjs` (function inside function). - Fixed `ReferenceError` in `src/cmd-check.mjs` (`queryLocksFn` -> `queryLocks`). - Verified with unit tests (`test:unit:lock-backend`) and linting. - Completed daily scheduler cycle for `const-refactor-agent`. Co-authored-by: PR0M3TH3AN <53631862+PR0M3TH3AN@users.noreply.github.com> --- .scheduler-memory/memory-store.json | 53 + .../session-mlr0dhq6/retrieve.json | 18 + .../session-mlr0dhq6/retrieve.ok | 1 + .scheduler-memory/session-mlr0iplm/store.json | 20 + .scheduler-memory/session-mlr0iplm/store.ok | 1 + ...6-02-17T19-44-58Z__const-refactor-agent.md | 7 + perf/constants-refactor/candidates.json | 1667 +++++++++++++++++ src/cmd-check.mjs | 2 +- src/constants.mjs | 8 + src/context/CONTEXT_2026-02-17T19-41-31Z.md | 10 + .../DECISIONS_2026-02-17T19-43-02Z.md | 3 + src/lock-ops.mjs | 86 +- .../TEST_LOG_2026-02-17T19-43-26Z.md | 227 +++ .../TEST_LOG_2026-02-17T19-47-44Z.md | 175 ++ src/todo/TODO_2026-02-17T19-43-02Z.md | 1 + ...45-47Z__const-refactor-agent__completed.md | 15 + 16 files changed, 2266 insertions(+), 28 deletions(-) create mode 100644 .scheduler-memory/memory-store.json create mode 100644 .scheduler-memory/session-mlr0dhq6/retrieve.json create mode 100644 .scheduler-memory/session-mlr0dhq6/retrieve.ok create mode 100644 .scheduler-memory/session-mlr0iplm/store.json create mode 100644 .scheduler-memory/session-mlr0iplm/store.ok create mode 100644 memory-updates/2026-02-17T19-44-58Z__const-refactor-agent.md create mode 100644 perf/constants-refactor/candidates.json create mode 100644 src/context/CONTEXT_2026-02-17T19-41-31Z.md create mode 100644 src/decisions/DECISIONS_2026-02-17T19-43-02Z.md create mode 100644 src/test_logs/TEST_LOG_2026-02-17T19-43-26Z.md create mode 100644 src/test_logs/TEST_LOG_2026-02-17T19-47-44Z.md create mode 100644 src/todo/TODO_2026-02-17T19-43-02Z.md create mode 100644 task-logs/daily/2026-02-17T19-45-47Z__const-refactor-agent__completed.md diff --git a/.scheduler-memory/memory-store.json b/.scheduler-memory/memory-store.json new file mode 100644 index 0000000..fc448b2 --- /dev/null +++ b/.scheduler-memory/memory-store.json @@ -0,0 +1,53 @@ +[ + [ + "b8fc8eef-facd-4632-a404-aefc94de033a", + { + "schema_version": 1, + "id": "b8fc8eef-facd-4632-a404-aefc94de033a", + "agent_id": "const-refactor-agent", + "session_id": "session-mlr0dhq6", + "type": "batch_window", + "content": "Memory retrieval seed for daily :: Required startup + artifacts + memory + issue capture", + "summary": "Memory retrieval seed for daily :: Required startup + artifacts + memory + issue capture", + "tags": [ + "scheduler", + "daily", + "retrieve" + ], + "importance": 0.4, + "embedding_id": null, + "created_at": 1771357255423, + "last_seen": 1771357255430, + "source": "scheduler-retrieve", + "ttl_days": null, + "merged_into": null, + "pinned": false + } + ], + [ + "b6580c2a-2c32-4239-afd8-eb8cbca5449d", + { + "schema_version": 1, + "id": "b6580c2a-2c32-4239-afd8-eb8cbca5449d", + "agent_id": "const-refactor-agent", + "session_id": "session-mlr0iplm", + "type": "batch_window", + "content": "# Memory Update: Constants Refactor\n\n- Discovered hardcoded constants in `src/lock-ops.mjs` duplicating defaults.\n- Extracted `DEFAULT_RETRY_ATTEMPTS`, `DEFAULT_RETRY_BASE_DELAY_MS`, etc. to `src/constants.mjs`.\n- Refactored `src/lock-ops.mjs` to use these constants.\n- Fixed a bug in `src/cmd-check.mjs` where `queryLocksFn` was undefined.\n- Verified with unit tests and lint.", + "summary": "# Memory Update: Constants Refactor\n\n- Discovered hardcoded constants in `src/lock-ops.mjs` duplicating defaults.\n- Extracted `DEFAULT_RETRY_ATTEMPTS`, `DEFAULT_RETRY_BASE_DELAY_MS`, etc. to `src/constants.mjs`.\n- Refactored `src/lock-ops.mjs` to use these constants.\n- Fixed a b…", + "tags": [ + "scheduler", + "daily", + "store", + "insight" + ], + "importance": 0.8, + "embedding_id": null, + "created_at": 1771357498907, + "last_seen": 1771357498907, + "source": "agent-output", + "ttl_days": null, + "merged_into": null, + "pinned": false + } + ] +] \ No newline at end of file diff --git a/.scheduler-memory/session-mlr0dhq6/retrieve.json b/.scheduler-memory/session-mlr0dhq6/retrieve.json new file mode 100644 index 0000000..5105a48 --- /dev/null +++ b/.scheduler-memory/session-mlr0dhq6/retrieve.json @@ -0,0 +1,18 @@ +{ + "cadence": "daily", + "operation": "retrieve", + "runId": "session-mlr0dhq6", + "servicePath": "src/services/memory/index.js#getRelevantMemories", + "inputs": { + "agentId": "const-refactor-agent", + "promptPath": "src/prompts/daily/const-refactor-agent.md", + "promptIntent": "Required startup + artifacts + memory + issue capture", + "query": "scheduler | daily | agent | const-refactor-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/session-mlr0dhq6/retrieve.ok b/.scheduler-memory/session-mlr0dhq6/retrieve.ok new file mode 100644 index 0000000..84b1719 --- /dev/null +++ b/.scheduler-memory/session-mlr0dhq6/retrieve.ok @@ -0,0 +1 @@ +MEMORY_RETRIEVED diff --git a/.scheduler-memory/session-mlr0iplm/store.json b/.scheduler-memory/session-mlr0iplm/store.json new file mode 100644 index 0000000..395e0e7 --- /dev/null +++ b/.scheduler-memory/session-mlr0iplm/store.json @@ -0,0 +1,20 @@ +{ + "cadence": "daily", + "operation": "store", + "runId": "session-mlr0iplm", + "servicePath": "src/services/memory/index.js#ingestEvents", + "inputs": { + "agentId": "const-refactor-agent", + "promptPath": "src/prompts/daily/const-refactor-agent.md", + "promptIntent": "Required startup + artifacts + memory + issue capture", + "events": 1, + "sourceFile": "memory-updates/2026-02-17T19-44-58Z__const-refactor-agent.md" + }, + "outputs": { + "storedCount": 1, + "summaries": [ + "# Memory Update: Constants Refactor\n\n- Discovered hardcoded constants in `src/lock-ops.mjs` duplicating defaults.\n- Extracted `DEFAULT_RETRY_ATTEMPTS`, `DEFAULT_RETRY_BASE_DELAY_MS`, etc. to `src/constants.mjs`.\n- Refactored `src/lock-ops.mjs` to use these constants.\n- Fixed a b…" + ] + }, + "status": "ok" +} \ No newline at end of file diff --git a/.scheduler-memory/session-mlr0iplm/store.ok b/.scheduler-memory/session-mlr0iplm/store.ok new file mode 100644 index 0000000..1e40bb6 --- /dev/null +++ b/.scheduler-memory/session-mlr0iplm/store.ok @@ -0,0 +1 @@ +MEMORY_STORED diff --git a/memory-updates/2026-02-17T19-44-58Z__const-refactor-agent.md b/memory-updates/2026-02-17T19-44-58Z__const-refactor-agent.md new file mode 100644 index 0000000..3572664 --- /dev/null +++ b/memory-updates/2026-02-17T19-44-58Z__const-refactor-agent.md @@ -0,0 +1,7 @@ +# Memory Update: Constants Refactor + +- Discovered hardcoded constants in `src/lock-ops.mjs` duplicating defaults. +- Extracted `DEFAULT_RETRY_ATTEMPTS`, `DEFAULT_RETRY_BASE_DELAY_MS`, etc. to `src/constants.mjs`. +- Refactored `src/lock-ops.mjs` to use these constants. +- Fixed a bug in `src/cmd-check.mjs` where `queryLocksFn` was undefined. +- Verified with unit tests and lint. diff --git a/perf/constants-refactor/candidates.json b/perf/constants-refactor/candidates.json new file mode 100644 index 0000000..2cd25ba --- /dev/null +++ b/perf/constants-refactor/candidates.json @@ -0,0 +1,1667 @@ +[ + [ + "0", + [ + { + "file": "src/relay-health.mjs", + "line": 24, + "context": "ary?.totalRelays || 0), 0);\n const succe" + }, + { + "file": "src/relay-health.mjs", + "line": 24, + "context": ".totalRelays || 0), 0);\n const successCo" + }, + { + "file": "src/relay-health.mjs", + "line": 25, + "context": "y?.healthyRelays || 0), 0);\n const succe" + }, + { + "file": "src/relay-health.mjs", + "line": 25, + "context": "ealthyRelays || 0), 0);\n const successRa" + }, + { + "file": "src/relay-health.mjs", + "line": 26, + "context": "= relayProbeCount > 0 ? successCount / re" + }, + { + "file": "src/relay-health.mjs", + "line": 29, + "context": "es.length - 1; i >= 0; i -= 1) {\n if (" + }, + { + "file": "src/relay-health.mjs", + "line": 30, + "context": "y?.healthyRelays || 0) > 0) {\n lastH" + }, + { + "file": "src/relay-health.mjs", + "line": 30, + "context": "althyRelays || 0) > 0) {\n lastHealth" + }, + { + "file": "src/relay-health.mjs", + "line": 38, + "context": "null\n : Math.max(0, Math.floor((nowMs" + }, + { + "file": "src/relay-health.mjs", + "line": 61, + "context": "stats.sampleCount > 0 && stats.successRat" + }, + { + "file": "src/relay-health.mjs", + "line": 146, + "context": "onst type = message[0];\n\n if (type =" + }, + { + "file": "src/relay-health.mjs", + "line": 252, + "context": "y.healthyRelays === 0;\n\n const result =" + }, + { + "file": "src/roster.mjs", + "line": 75, + "context": "if (daily.length > 0 && weekly.length >" + }, + { + "file": "src/roster.mjs", + "line": 75, + "context": "&& weekly.length > 0) {\n cachedCano" + }, + { + "file": "src/cmd-check.mjs", + "line": 19, + "context": "usedAgents.length > 0) {\n console.erro" + }, + { + "file": "src/torch-config.mjs", + "line": 20, + "context": "(value) || value <= 0) return null;\n ret" + }, + { + "file": "src/torch-config.mjs", + "line": 27, + "context": "urn parsed.length > 0 ? parsed : null;\n}" + }, + { + "file": "src/torch-config.mjs", + "line": 51, + "context": "(value) || value <= 0) {\n throw new Er" + }, + { + "file": "src/torch-config.mjs", + "line": 94, + "context": "trLock.ttlSeconds > 0\n ? Math.floo" + }, + { + "file": "src/torch-config.mjs", + "line": 239, + "context": "parsed) && parsed > 0) return parsed;\n }" + }, + { + "file": "src/ops.mjs", + "line": 165, + "context": "f (entries.length > 0 && installDir !== '" + }, + { + "file": "src/ops.mjs", + "line": 424, + "context": ";\n let added = 0;\n let updated" + }, + { + "file": "src/ops.mjs", + "line": 425, + "context": "let updated = 0;\n let skipped" + }, + { + "file": "src/ops.mjs", + "line": 426, + "context": "let skipped = 0;\n\n for (const" + }, + { + "file": "src/cli-parser.mjs", + "line": 38, + "context": "ull,\n };\n let i = 0;\n\n if (argv.length" + }, + { + "file": "src/cli-parser.mjs", + "line": 40, + "context": "if (argv.length > 0 && !argv[0].startsW" + }, + { + "file": "src/cli-parser.mjs", + "line": 40, + "context": "length > 0 && !argv[0].startsWith('-')) {" + }, + { + "file": "src/cli-parser.mjs", + "line": 41, + "context": "args.command = argv[0];\n i = 1;\n }" + }, + { + "file": "src/constants.mjs", + "line": 56, + "context": "dex)\n\nExit codes:\n 0 Success\n 1 Usage" + }, + { + "file": "src/utils.mjs", + "line": 2, + "context": "toISOString().slice(0, 10);\n}\n\nexport fun" + }, + { + "file": "src/utils.mjs", + "line": 21, + "context": "the year\n // day: 0 (Sun) -> 6 (Sat). W" + }, + { + "file": "src/utils.mjs", + "line": 25, + "context": "e.getUTCFullYear(), 0, 1));\n const weekN" + }, + { + "file": "src/utils.mjs", + "line": 28, + "context": "eekNo).padStart(2, '0')}`;\n}\n\nexport func" + }, + { + "file": "src/lib.mjs", + "line": 87, + "context": "usedAgents.length > 0) {\n error(`Pau" + }, + { + "file": "src/lib.mjs", + "line": 95, + "context": "pletedAgents.size > 0) {\n error(`Com" + }, + { + "file": "src/lib.mjs", + "line": 207, + "context": "onflicting.length > 0) {\n const earlie" + }, + { + "file": "src/lib.mjs", + "line": 208, + "context": "edAt - b.createdAt)[0];\n\n // Check if" + }, + { + "file": "src/lib.mjs", + "line": 231, + "context": "pubkey: ${pk.slice(0, 16)}...`);\n\n erro" + }, + { + "file": "src/lib.mjs", + "line": 277, + "context": "> 1 && racingLocks[0].eventId !== event." + }, + { + "file": "src/lib.mjs", + "line": 278, + "context": "inner = racingLocks[0];\n error(" + }, + { + "file": "src/lib.mjs", + "line": 341, + "context": "f (locks.length === 0) {\n log(' (no" + }, + { + "file": "src/lib.mjs", + "line": 364, + "context": "lock.eventId?.slice(0, 12)}...`,\n );" + }, + { + "file": "src/lib.mjs", + "line": 374, + "context": "ckedAgents.length > 0) {\n log(` War" + }, + { + "file": "src/lib.mjs", + "line": 620, + "context": "emory.content.slice(0, 200) + '... (trunc" + }, + { + "file": "src/cmd-lock.mjs", + "line": 39, + "context": "onflicting.length > 0) {\n const earlie" + }, + { + "file": "src/cmd-lock.mjs", + "line": 40, + "context": "edAt - b.createdAt)[0];\n console.error" + }, + { + "file": "src/cmd-lock.mjs", + "line": 54, + "context": "pubkey: ${pk.slice(0, 16)}...`);\n\n cons" + }, + { + "file": "src/cmd-lock.mjs", + "line": 99, + "context": "> 1 && racingLocks[0].eventId !== event." + }, + { + "file": "src/cmd-lock.mjs", + "line": 100, + "context": "inner = racingLocks[0];\n console.err" + }, + { + "file": "src/lock-ops.mjs", + "line": 17, + "context": ".tags.find((t) => t[0] === 'd')?.[1] ?? '" + }, + { + "file": "src/lock-ops.mjs", + "line": 18, + "context": ".tags.find((t) => t[0] === 'expiration')?" + }, + { + "file": "src/lock-ops.mjs", + "line": 136, + "context": "return randomInt(0, MAX_RANDOM) / MAX_" + }, + { + "file": "src/lock-ops.mjs", + "line": 140, + "context": "Ms * (2 ** Math.max(0, attemptNumber - 1)" + }, + { + "file": "src/lock-ops.mjs", + "line": 150, + "context": "ue) => sum + value, 0) / latencyEntries.l" + }, + { + "file": "src/lock-ops.mjs", + "line": 152, + "context": "ccessRate = total > 0 ? successCount / to" + }, + { + "file": "src/lock-ops.mjs", + "line": 153, + "context": "meoutRate = total > 0 ? timeoutCount / to" + }, + { + "file": "src/lock-ops.mjs", + "line": 153, + "context": "eoutCount / total : 0;\n const quarantine" + }, + { + "file": "src/lock-ops.mjs", + "line": 154, + "context": "tineUntil - nowMs : 0;\n return {\n rel" + }, + { + "file": "src/lock-ops.mjs", + "line": 162, + "context": "antineRemainingMs > 0,\n quarantineRema" + }, + { + "file": "src/lock-ops.mjs", + "line": 173, + "context": "y.quarantined ? 1 : 0;\n return (summary." + }, + { + "file": "src/lock-ops.mjs", + "line": 180, + "context": "is.lastSnapshotAt = 0;\n }\n\n ensureMetri" + }, + { + "file": "src/lock-ops.mjs", + "line": 189, + "context": "failureStreak: 0,\n quarantine" + }, + { + "file": "src/lock-ops.mjs", + "line": 190, + "context": "quarantineUntil: 0,\n cooldownMs" + }, + { + "file": "src/lock-ops.mjs", + "line": 234, + "context": "..quarantined.slice(0, additionalNeeded))" + }, + { + "file": "src/lock-ops.mjs", + "line": 249, + "context": "centOutcomes.splice(0, metrics.recentOutc" + }, + { + "file": "src/lock-ops.mjs", + "line": 255, + "context": "ics.failureStreak = 0;\n if (metrics." + }, + { + "file": "src/lock-ops.mjs", + "line": 257, + "context": "s.quarantineUntil = 0;\n metrics.co" + }, + { + "file": "src/lock-ops.mjs", + "line": 292, + "context": "is.lastSnapshotAt = 0;\n }\n}\n\nexport cons" + }, + { + "file": "src/lock-ops.mjs", + "line": 335, + "context": "rioritized.length > 0) {\n errorLogge" + }, + { + "file": "src/lock-ops.mjs", + "line": 508, + "context": "backRelays.length > 0) {\n await this" + }, + { + "file": "src/lock-ops.mjs", + "line": 598, + "context": "result.category] || 0) + 1;\n this." + }, + { + "file": "src/cmd-list.mjs", + "line": 29, + "context": "f (locks.length === 0) {\n console.lo" + }, + { + "file": "src/cmd-list.mjs", + "line": 46, + "context": "lock.eventId?.slice(0, 12)}...`,\n );" + }, + { + "file": "src/cmd-list.mjs", + "line": 55, + "context": "ckedAgents.length > 0) {\n console.lo" + }, + { + "file": "src/dashboard.mjs", + "line": 38, + "context": "const type = parts[0];\n const crede" + }, + { + "file": "src/perf/benchmark-config-load.mjs", + "line": 22, + "context": "{\n for (let i = 0; i < ITERATIONS; i+" + } + ] + ], + [ + "1", + [ + { + "file": "src/relay-health.mjs", + "line": 26, + "context": "/ relayProbeCount : 1;\n\n let lastHealthy" + }, + { + "file": "src/relay-health.mjs", + "line": 29, + "context": "oryEntries.length - 1; i >= 0; i -= 1) {" + }, + { + "file": "src/relay-health.mjs", + "line": 29, + "context": "h - 1; i >= 0; i -= 1) {\n if ((history" + }, + { + "file": "src/relay-health.mjs", + "line": 133, + "context": "[event.id], limit: 1 }]));\n });" + }, + { + "file": "src/relay-health.mjs", + "line": 148, + "context": "=== 'OK' && message[1] === event.id) {" + }, + { + "file": "src/relay-health.mjs", + "line": 156, + "context": "'EVENT' && message[1] === subId && messa" + }, + { + "file": "src/ops.mjs", + "line": 126, + "context": "');\n\n try {\n // 1. Install Directory" + }, + { + "file": "src/ops.mjs", + "line": 174, + "context": "installDir) {\n // 1. Copy App Directori" + }, + { + "file": "src/ops.mjs", + "line": 369, + "context": "first.`);\n }\n\n // 1. Backup\n const bac" + }, + { + "file": "src/cli-parser.mjs", + "line": 42, + "context": "= argv[0];\n i = 1;\n }\n\n for (; i <" + }, + { + "file": "src/cli-parser.mjs", + "line": 56, + "context": "le = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 60, + "context": "nt = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 62, + "context": "ce = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 78, + "context": "id = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 82, + "context": "pe = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 86, + "context": "ring(arg.split('=')[1]).split(',').map((t" + }, + { + "file": "src/cli-parser.mjs", + "line": 91, + "context": "ring(arg.split('=')[1]).toLowerCase();" + }, + { + "file": "src/cli-parser.mjs", + "line": 96, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 100, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 104, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 108, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 112, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 116, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 120, + "context": "loat(arg.split('=')[1]);\n } else if (a" + }, + { + "file": "src/cli-parser.mjs", + "line": 124, + "context": "eInt(arg.split('=')[1], 10);\n } else i" + }, + { + "file": "src/cli-parser.mjs", + "line": 128, + "context": "rm = arg.split('=')[1];\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 132, + "context": "el = arg.split('=')[1];\n }\n }\n\n retu" + }, + { + "file": "src/constants.mjs", + "line": 12, + "context": "CESSFUL_PUBLISHES = 1;\nexport const DEFAU" + }, + { + "file": "src/constants.mjs", + "line": 13, + "context": "ACTIVE_RELAY_POOL = 1;\nexport const VALID" + }, + { + "file": "src/constants.mjs", + "line": 57, + "context": "des:\n 0 Success\n 1 Usage error\n 2 R" + }, + { + "file": "src/utils.mjs", + "line": 21, + "context": "-> 6 (Sat). We want 1 (Mon) -> 7 (Sun)" + }, + { + "file": "src/utils.mjs", + "line": 25, + "context": "etUTCFullYear(), 0, 1));\n const weekNo =" + }, + { + "file": "src/utils.mjs", + "line": 26, + "context": "tart) / 86400000) + 1) / 7);\n\n return `$" + }, + { + "file": "src/lib.mjs", + "line": 51, + "context": "m three sources:\n * 1. Configuration (pau" + }, + { + "file": "src/lib.mjs", + "line": 149, + "context": "*\n * Algorithm:\n * 1. Validate agent aga" + }, + { + "file": "src/lib.mjs", + "line": 200, + "context": "throw new ExitError(1, 'Agent not in rost" + }, + { + "file": "src/lib.mjs", + "line": 203, + "context": "}\n\n error('Step 1: Checking for exist" + }, + { + "file": "src/lib.mjs", + "line": 277, + "context": "acingLocks.length > 1 && racingLocks[0].e" + }, + { + "file": "src/lib.mjs", + "line": 388, + "context": "* This function:\n * 1. Verifies that the" + }, + { + "file": "src/lib.mjs", + "line": 425, + "context": "oin(', ')}`);\n\n // 1. Find existing lock" + }, + { + "file": "src/lib.mjs", + "line": 432, + "context": "throw new ExitError(1, 'No active lock fo" + }, + { + "file": "src/lib.mjs", + "line": 444, + "context": "Iso;\n\n error('Step 1: Generating complet" + }, + { + "file": "src/lib.mjs", + "line": 510, + "context": "throw new ExitError(1, 'No command specif" + }, + { + "file": "src/lib.mjs", + "line": 517, + "context": "throw new ExitError(1, 'Missing cadence')" + }, + { + "file": "src/lib.mjs", + "line": 532, + "context": "throw new ExitError(1, 'Missing agent');" + }, + { + "file": "src/lib.mjs", + "line": 536, + "context": "throw new ExitError(1, 'Missing cadence')" + }, + { + "file": "src/lib.mjs", + "line": 549, + "context": "throw new ExitError(1, 'Missing agent');" + }, + { + "file": "src/lib.mjs", + "line": 553, + "context": "throw new ExitError(1, 'Missing cadence')" + }, + { + "file": "src/lib.mjs", + "line": 566, + "context": "throw new ExitError(1, 'Invalid cadence')" + }, + { + "file": "src/lib.mjs", + "line": 575, + "context": "throw new ExitError(1, 'Missing cadence')" + }, + { + "file": "src/lib.mjs", + "line": 632, + "context": "throw new ExitError(1, 'Missing memory id" + }, + { + "file": "src/lib.mjs", + "line": 642, + "context": "throw new ExitError(1, 'Missing memory id" + }, + { + "file": "src/lib.mjs", + "line": 652, + "context": "throw new ExitError(1, 'Missing memory id" + }, + { + "file": "src/lib.mjs", + "line": 674, + "context": "throw new ExitError(1, 'Unknown command')" + }, + { + "file": "src/cmd-lock.mjs", + "line": 32, + "context": "throw new ExitError(1, 'Agent not in rost" + }, + { + "file": "src/cmd-lock.mjs", + "line": 35, + "context": "console.error('Step 1: Checking for exist" + }, + { + "file": "src/cmd-lock.mjs", + "line": 99, + "context": "acingLocks.length > 1 && racingLocks[0].e" + }, + { + "file": "src/lock-ops.mjs", + "line": 17, + "context": "=> t[0] === 'd')?.[1] ?? '';\n const exp" + }, + { + "file": "src/lock-ops.mjs", + "line": 18, + "context": "=== 'expiration')?.[1];\n const expiresAt" + }, + { + "file": "src/lock-ops.mjs", + "line": 133, + "context": "6710655; // 2**48 - 1\n\nfunction secureRan" + }, + { + "file": "src/lock-ops.mjs", + "line": 140, + "context": "(0, attemptNumber - 1)));\n return Math.f" + }, + { + "file": "src/lock-ops.mjs", + "line": 173, + "context": "mmary.quarantined ? 1 : 0;\n return (summ" + }, + { + "file": "src/lock-ops.mjs", + "line": 213, + "context": "mmary.quarantined ? 1 : -1;\n if (a.s" + }, + { + "file": "src/lock-ops.mjs", + "line": 213, + "context": ".quarantined ? 1 : -1;\n if (a.summar" + }, + { + "file": "src/lock-ops.mjs", + "line": 215, + "context": "Ms === null) return 1;\n if (b.summ" + }, + { + "file": "src/lock-ops.mjs", + "line": 216, + "context": "s === null) return -1;\n return a.s" + }, + { + "file": "src/lock-ops.mjs", + "line": 224, + "context": "inActive = Math.max(1, Math.min(config.mi" + }, + { + "file": "src/lock-ops.mjs", + "line": 224, + "context": "l, relays.length || 1));\n const ranked" + }, + { + "file": "src/lock-ops.mjs", + "line": 263, + "context": "cs.failureStreak += 1;\n if (metrics.fa" + }, + { + "file": "src/lock-ops.mjs", + "line": 305, + "context": "RelayPool: Math.max(1, deps.minActiveRela" + }, + { + "file": "src/lock-ops.mjs", + "line": 465, + "context": "let attemptNumber = 1; attemptNumber <= t" + }, + { + "file": "src/lock-ops.mjs", + "line": 465, + "context": "s; attemptNumber += 1) {\n const st" + }, + { + "file": "src/lock-ops.mjs", + "line": 598, + "context": "t.category] || 0) + 1;\n this.telem" + }, + { + "file": "src/dashboard.mjs", + "line": 39, + "context": "credentials = parts[1];\n let isValid" + } + ] + ], + [ + "2", + [ + { + "file": "src/relay-health.mjs", + "line": 101, + "context": ".toString(16).slice(2, 10)}`;\n const pro" + }, + { + "file": "src/relay-health.mjs", + "line": 145, + "context": "|| message.length < 2) return;\n cons" + }, + { + "file": "src/relay-health.mjs", + "line": 149, + "context": "k = Boolean(message[2]);\n if (!okA" + }, + { + "file": "src/relay-health.mjs", + "line": 156, + "context": "== subId && message[2]?.id === event.id)" + }, + { + "file": "src/cmd-check.mjs", + "line": 50, + "context": "ngify(result, null, 2));\n return result;" + }, + { + "file": "src/ops.mjs", + "line": 135, + "context": "defaultDir;\n\n // 2. Namespace\n cons" + }, + { + "file": "src/ops.mjs", + "line": 177, + "context": "r, 'Copied');\n\n // 2. Copy App Files\n s" + }, + { + "file": "src/ops.mjs", + "line": 268, + "context": "y(configData, null, 2), 'utf8');\n consol" + }, + { + "file": "src/ops.mjs", + "line": 330, + "context": "tringify(pkg, null, 2), 'utf8');" + }, + { + "file": "src/ops.mjs", + "line": 387, + "context": "true });\n }\n\n // 2. Update App Directo" + }, + { + "file": "src/cli-parser.mjs", + "line": 6, + "context": "process.argv.slice(2)\n * @returns {Objec" + }, + { + "file": "src/constants.mjs", + "line": 58, + "context": "1 Usage error\n 2 Relay/network erro" + }, + { + "file": "src/utils.mjs", + "line": 28, + "context": "ng(weekNo).padStart(2, '0')}`;\n}\n\nexport" + }, + { + "file": "src/lib.mjs", + "line": 52, + "context": "(paused agents)\n * 2. Local logs (comple" + }, + { + "file": "src/lib.mjs", + "line": 130, + "context": "ngify(result, null, 2);\n\n if (jsonFile)" + }, + { + "file": "src/lib.mjs", + "line": 150, + "context": "inst the roster.\n * 2. Query relays for e" + }, + { + "file": "src/lib.mjs", + "line": 228, + "context": "}\n\n error('Step 2: Generating ephemer" + }, + { + "file": "src/lib.mjs", + "line": 389, + "context": "ds a valid lock.\n * 2. Publishes a replac" + }, + { + "file": "src/lib.mjs", + "line": 441, + "context": "ventId };\n }\n\n // 2. Build completion e" + }, + { + "file": "src/lib.mjs", + "line": 477, + "context": ") {\n error('Step 2: [DRY RUN] Skipping" + }, + { + "file": "src/lib.mjs", + "line": 479, + "context": "e {\n error('Step 2: Publishing complet" + }, + { + "file": "src/lib.mjs", + "line": 502, + "context": "process.argv.slice(2)\n */\nexport async f" + }, + { + "file": "src/lib.mjs", + "line": 587, + "context": "ngify(result, null, 2));\n if (!res" + }, + { + "file": "src/lib.mjs", + "line": 588, + "context": "throw new ExitError(2, 'Relay health chec" + }, + { + "file": "src/lib.mjs", + "line": 625, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 635, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 645, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 655, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 661, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 667, + "context": "ngify(result, null, 2));\n break;" + }, + { + "file": "src/lib.mjs", + "line": 681, + "context": "process.exit(2);\n }\n }\n}" + }, + { + "file": "src/lock-utils.mjs", + "line": 26, + "context": "e.match(/^(\\d{4}-\\d{2}-\\d{2})T.*__([a-zA-" + }, + { + "file": "src/lock-utils.mjs", + "line": 26, + "context": "h(/^(\\d{4}-\\d{2}-\\d{2})T.*__([a-zA-Z0-9-_" + }, + { + "file": "src/cmd-lock.mjs", + "line": 51, + "context": "console.error('Step 2: Generating ephemer" + }, + { + "file": "src/lock-ops.mjs", + "line": 133, + "context": "281474976710655; // 2**48 - 1\n\nfunction s" + }, + { + "file": "src/lock-ops.mjs", + "line": 140, + "context": "in(capMs, baseMs * (2 ** Math.max(0, atte" + }, + { + "file": "src/dashboard.mjs", + "line": 115, + "context": "y(safeConfig, null, 2));\n return" + }, + { + "file": "src/perf/benchmark-config-load.mjs", + "line": 15, + "context": "s\"\n }\n }, null, 2));\n}\n\nconst ITERATI" + }, + { + "file": "src/perf/benchmark-config-load.mjs", + "line": 36, + "context": "${duration.toFixed(2)}ms`);\n console." + } + ] + ], + [ + "10", + [ + { + "file": "src/relay-health.mjs", + "line": 101, + "context": "String(16).slice(2, 10)}`;\n const probeTa" + }, + { + "file": "src/relay-health.mjs", + "line": 200, + "context": "RelaysDownMinutes = 10,\n minSuccessRate" + }, + { + "file": "src/torch-config.mjs", + "line": 185, + "context": "parseInt(envValue, 10);\n if (Number.isNa" + }, + { + "file": "src/torch-config.mjs", + "line": 238, + "context": "= parseInt(envTtl, 10);\n if (!Number.i" + }, + { + "file": "src/cli-parser.mjs", + "line": 68, + "context": "parseInt(argv[++i], 10) || DEFAULT_DASHBOA" + }, + { + "file": "src/cli-parser.mjs", + "line": 94, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 96, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 98, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 100, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 102, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 104, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 106, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 108, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 110, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 112, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 114, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 116, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 122, + "context": "parseInt(argv[++i], 10);\n } else if (ar" + }, + { + "file": "src/cli-parser.mjs", + "line": 124, + "context": "(arg.split('=')[1], 10);\n } else if (ar" + }, + { + "file": "src/utils.mjs", + "line": 2, + "context": "SOString().slice(0, 10);\n}\n\nexport functio" + }, + { + "file": "src/lock-ops.mjs", + "line": 19, + "context": "? parseInt(expTag, 10) : null;\n\n let con" + } + ] + ], + [ + "4", + [ + { + "file": "src/relay-health.mjs", + "line": 65, + "context": "successRate.toFixed(4)),\n windowMinu" + }, + { + "file": "src/ops.mjs", + "line": 136, + "context": "crypto.randomBytes(4).toString('hex');" + }, + { + "file": "src/ops.mjs", + "line": 189, + "context": "ster)}`);\n }\n\n // 4. Copy Static Files" + }, + { + "file": "src/ops.mjs", + "line": 393, + "context": ", 'Updated');\n\n // 4. Update Static File" + }, + { + "file": "src/utils.mjs", + "line": 23, + "context": "date.getUTCDate() + 4 - day);\n\n const ye" + }, + { + "file": "src/lib.mjs", + "line": 152, + "context": "nt (kind 30078).\n * 4. Publish the lock e" + }, + { + "file": "src/lib.mjs", + "line": 263, + "context": ") {\n error('Step 4: [DRY RUN] Skipping" + }, + { + "file": "src/lib.mjs", + "line": 266, + "context": "e {\n error('Step 4: Publishing to rela" + }, + { + "file": "src/lib.mjs", + "line": 361, + "context": "ng(ageMin).padStart(4)}m ` +\n `" + }, + { + "file": "src/lib.mjs", + "line": 362, + "context": "remainMin).padStart(4)} ` +\n `p" + }, + { + "file": "src/lock-utils.mjs", + "line": 26, + "context": "= file.match(/^(\\d{4}-\\d{2}-\\d{2})T.*__(" + }, + { + "file": "src/cmd-lock.mjs", + "line": 85, + "context": "console.error('Step 4: [DRY RUN] Skipping" + }, + { + "file": "src/cmd-lock.mjs", + "line": 88, + "context": "console.error('Step 4: Publishing to rela" + }, + { + "file": "src/lock-ops.mjs", + "line": 274, + "context": "entry.score.toFixed(4)),\n ...entry.s" + }, + { + "file": "src/lock-ops.mjs", + "line": 427, + "context": "ps.retryAttempts || 4;\n this.retryBase" + }, + { + "file": "src/cmd-list.mjs", + "line": 43, + "context": "ng(ageMin).padStart(4)}m ` +\n `" + }, + { + "file": "src/cmd-list.mjs", + "line": 44, + "context": "remainMin).padStart(4)}m ` +\n `" + }, + { + "file": "src/perf/benchmark-config-load.mjs", + "line": 37, + "context": "ITERATIONS).toFixed(4)}ms per load`);" + } + ] + ], + [ + "1000", + [ + { + "file": "src/relay-health.mjs", + "line": 100, + "context": ".floor(Date.now() / 1000);\n const nonce = `" + }, + { + "file": "src/utils.mjs", + "line": 6, + "context": ".floor(Date.now() / 1000);\n}\n\nexport functio" + }, + { + "file": "src/lib.mjs", + "line": 193, + "context": "ew Date(expiresAt * 1000).toISOString()}`);" + }, + { + "file": "src/lib.mjs", + "line": 253, + "context": "dAt: new Date(now * 1000).toISOString()," + }, + { + "file": "src/lib.mjs", + "line": 254, + "context": "ew Date(expiresAt * 1000).toISOString()," + }, + { + "file": "src/lib.mjs", + "line": 299, + "context": "ew Date(expiresAt * 1000).toISOString()}`);" + }, + { + "file": "src/lib.mjs", + "line": 468, + "context": "dAt: new Date(now * 1000).toISOString()," + }, + { + "file": "src/cmd-lock.mjs", + "line": 25, + "context": "ew Date(expiresAt * 1000).toISOString()}`);" + }, + { + "file": "src/cmd-lock.mjs", + "line": 75, + "context": "dAt: new Date(now * 1000).toISOString()," + }, + { + "file": "src/cmd-lock.mjs", + "line": 76, + "context": "ew Date(expiresAt * 1000).toISOString()," + }, + { + "file": "src/cmd-lock.mjs", + "line": 120, + "context": "ew Date(expiresAt * 1000).toISOString()}`);" + }, + { + "file": "src/lock-ops.mjs", + "line": 13, + "context": ".floor(Date.now() / 1000);\n}\n\nexport functio" + }, + { + "file": "src/lock-ops.mjs", + "line": 35, + "context": "(event.created_at * 1000).toISOString()," + }, + { + "file": "src/lock-ops.mjs", + "line": 37, + "context": "ew Date(expiresAt * 1000).toISOString() : nu" + }, + { + "file": "src/perf/benchmark-config-load.mjs", + "line": 18, + "context": "const ITERATIONS = 1000;\nconst start = proc" + } + ] + ], + [ + "3", + [ + { + "file": "src/relay-health.mjs", + "line": 151, + "context": "t: ${String(message[3] || 'unknown reason" + }, + { + "file": "src/ops.mjs", + "line": 141, + "context": "tNamespace;\n\n // 3. Relays\n console" + }, + { + "file": "src/ops.mjs", + "line": 182, + "context": "installDir) {\n // 3. Copy Roster\n cons" + }, + { + "file": "src/ops.mjs", + "line": 390, + "context": ", 'Updated');\n\n // 3. Update App Files (" + }, + { + "file": "src/constants.mjs", + "line": 59, + "context": "lay/network error\n 3 Lock denied (alrea" + }, + { + "file": "src/lib.mjs", + "line": 53, + "context": "ompleted agents)\n * 3. Nostr relays (curr" + }, + { + "file": "src/lib.mjs", + "line": 151, + "context": "for conflicts).\n * 3. Generate a new eph" + }, + { + "file": "src/lib.mjs", + "line": 215, + "context": "throw new ExitError(3, 'Task already comp" + }, + { + "file": "src/lib.mjs", + "line": 225, + "context": "throw new ExitError(3, 'Lock denied');" + }, + { + "file": "src/lib.mjs", + "line": 233, + "context": ".`);\n\n error('Step 3: Building lock even" + }, + { + "file": "src/lib.mjs", + "line": 285, + "context": "throw new ExitError(3, 'Race check lost')" + }, + { + "file": "src/cmd-lock.mjs", + "line": 48, + "context": "throw new ExitError(3, 'Lock denied');" + }, + { + "file": "src/cmd-lock.mjs", + "line": 56, + "context": "console.error('Step 3: Building lock even" + }, + { + "file": "src/cmd-lock.mjs", + "line": 107, + "context": "throw new ExitError(3, 'Race check lost')" + }, + { + "file": "src/lock-ops.mjs", + "line": 301, + "context": "failureThreshold ?? 3,\n quarantineCool" + } + ] + ], + [ + "60", + [ + { + "file": "src/relay-health.mjs", + "line": 21, + "context": "(), windowMinutes = 60 } = {}) {\n const w" + }, + { + "file": "src/relay-health.mjs", + "line": 202, + "context": "windowMinutes = 60,\n } = options;" + }, + { + "file": "src/lib.mjs", + "line": 349, + "context": "= Math.round(age / 60);\n\n let remain" + }, + { + "file": "src/lib.mjs", + "line": 356, + "context": "h.round(remaining / 60);\n }\n\n lo" + }, + { + "file": "src/cmd-list.mjs", + "line": 37, + "context": "= Math.round(age / 60);\n const remai" + }, + { + "file": "src/cmd-list.mjs", + "line": 39, + "context": "h.round(remaining / 60) : '?';\n\n cons" + } + ] + ], + [ + "5", + [ + { + "file": "src/ops.mjs", + "line": 199, + "context": "`);\n }\n }\n\n // 5. Copy Prompts\n for" + }, + { + "file": "src/ops.mjs", + "line": 404, + "context": "`);\n }\n }\n\n // 5. Update Roster (Pre" + }, + { + "file": "src/lib.mjs", + "line": 153, + "context": "event to relays.\n * 5. Wait for propagati" + }, + { + "file": "src/lib.mjs", + "line": 269, + "context": ");\n\n error('Step 5: Race check...');" + }, + { + "file": "src/cmd-lock.mjs", + "line": 91, + "context": "console.error('Step 5: Race check...');" + }, + { + "file": "src/lock-ops.mjs", + "line": 303, + "context": "antineCooldownMs ?? 5 * 60_000,\n snaps" + } + ] + ], + [ + "7", + [ + { + "file": "src/ops.mjs", + "line": 273, + "context": "installDir) {\n // 7. Inject Scripts int" + }, + { + "file": "src/utils.mjs", + "line": 21, + "context": "We want 1 (Mon) -> 7 (Sun)\n const day =" + }, + { + "file": "src/utils.mjs", + "line": 22, + "context": "date.getUTCDay() || 7;\n date.setUTCDate(" + }, + { + "file": "src/utils.mjs", + "line": 26, + "context": "/ 86400000) + 1) / 7);\n\n return `${date" + } + ] + ], + [ + "72", + [ + { + "file": "src/lib.mjs", + "line": 337, + "context": "log(`\\n${'='.repeat(72)}`);\n log(`Activ" + }, + { + "file": "src/lib.mjs", + "line": 339, + "context": "log('='.repeat(72));\n\n if (locks.l" + }, + { + "file": "src/cmd-list.mjs", + "line": 25, + "context": "log(`\\n${'='.repeat(72)}`);\n console.lo" + }, + { + "file": "src/cmd-list.mjs", + "line": 27, + "context": "sole.log('='.repeat(72));\n\n if (locks.l" + } + ] + ], + [ + "200", + [ + { + "file": "src/lib.mjs", + "line": 619, + "context": "ry.content.length > 200) {\n me" + }, + { + "file": "src/lib.mjs", + "line": 620, + "context": "ry.content.slice(0, 200) + '... (truncated," + }, + { + "file": "src/dashboard.mjs", + "line": 114, + "context": "res.writeHead(200, { 'Content-Type':" + }, + { + "file": "src/dashboard.mjs", + "line": 183, + "context": "res.writeHead(200, { 'Content-Type':" + } + ] + ], + [ + "16", + [ + { + "file": "src/relay-health.mjs", + "line": 101, + "context": "h.random().toString(16).slice(2, 10)}`;" + }, + { + "file": "src/lib.mjs", + "line": 231, + "context": "bkey: ${pk.slice(0, 16)}...`);\n\n error('S" + }, + { + "file": "src/cmd-lock.mjs", + "line": 54, + "context": "bkey: ${pk.slice(0, 16)}...`);\n\n console." + } + ] + ], + [ + "6", + [ + { + "file": "src/ops.mjs", + "line": 218, + "context": "ace, relays) {\n // 6. Create/Update torc" + }, + { + "file": "src/ops.mjs", + "line": 415, + "context": "');\n }\n }\n\n // 6. Update Prompts (Co" + }, + { + "file": "src/utils.mjs", + "line": 21, + "context": "// day: 0 (Sun) -> 6 (Sat). We want 1 (M" + } + ] + ], + [ + "500", + [ + { + "file": "src/lock-ops.mjs", + "line": 428, + "context": "retryBaseDelayMs || 500;\n this.retryCapD" + }, + { + "file": "src/dashboard.mjs", + "line": 119, + "context": "res.writeHead(500);\n res.end" + }, + { + "file": "src/dashboard.mjs", + "line": 188, + "context": "res.writeHead(500);\n res.end('" + } + ] + ], + [ + "7200", + [ + { + "file": "src/constants.mjs", + "line": 8, + "context": "const DEFAULT_TTL = 7200;\nexport const DEFAU" + }, + { + "file": "src/constants.mjs", + "line": 48, + "context": "n seconds (default: 7200)\n NOSTR_LOCK_QUERY" + } + ] + ], + [ + "30078", + [ + { + "file": "src/constants.mjs", + "line": 15, + "context": "nst KIND_APP_DATA = 30078;\n\nexport const USAG" + }, + { + "file": "src/lib.mjs", + "line": 151, + "context": "a lock event (kind 30078).\n * 4. Publish the" + } + ] + ], + [ + "30", + [ + { + "file": "src/lib.mjs", + "line": 360, + "context": "? 'unknown').padEnd(30)} ` +\n `ag" + }, + { + "file": "src/cmd-list.mjs", + "line": 42, + "context": "? 'unknown').padEnd(30)} ` +\n `ag" + } + ] + ], + [ + "12", + [ + { + "file": "src/lib.mjs", + "line": 364, + "context": "k.eventId?.slice(0, 12)}...`,\n );" + }, + { + "file": "src/cmd-list.mjs", + "line": 46, + "context": "k.eventId?.slice(0, 12)}...`,\n );" + } + ] + ], + [ + "0.1", + [ + { + "file": "src/lock-ops.mjs", + "line": 171, + "context": "cyMs === null\n ? 0.1\n : Math.min(0.35" + }, + { + "file": "src/dashboard.mjs", + "line": 21, + "context": "PORT, host = '127.0.0.1') {\n // Resolve pa" + } + ] + ], + [ + "25", + [ + { + "file": "src/lock-ops.mjs", + "line": 300, + "context": "ollingWindowSize ?? 25,\n failureThresho" + }, + { + "file": "src/lock-ops.mjs", + "line": 450, + "context": "rollingWindowSize: 25,\n minActiveRel" + } + ] + ], + [ + "401", + [ + { + "file": "src/dashboard.mjs", + "line": 33, + "context": "res.writeHead(401, { 'WWW-Authenticat" + }, + { + "file": "src/dashboard.mjs", + "line": 51, + "context": "res.writeHead(401, { 'WWW-Authenticat" + } + ] + ], + [ + "404", + [ + { + "file": "src/dashboard.mjs", + "line": 124, + "context": "res.writeHead(404);\n res.end('" + }, + { + "file": "src/dashboard.mjs", + "line": 162, + "context": "res.writeHead(404);\n res.end('No" + } + ] + ], + [ + "0.0", + [ + { + "file": "src/dashboard.mjs", + "line": 196, + "context": "`http://${host === '0.0.0.0' ? 'localhost'" + }, + { + "file": "src/dashboard.mjs", + "line": 196, + "context": "p://${host === '0.0.0.0' ? 'localhost' : ho" + } + ] + ] +] diff --git a/src/cmd-check.mjs b/src/cmd-check.mjs index 2ca8fe3..8d81a27 100644 --- a/src/cmd-check.mjs +++ b/src/cmd-check.mjs @@ -20,7 +20,7 @@ export async function cmdCheck(cadence) { console.error(`Paused agents: ${pausedAgents.join(', ')}`); } - const locks = await queryLocksFn(relays, cadence, dateStr, namespace); + const locks = await queryLocks(relays, cadence, dateStr, namespace); const lockedAgents = [...new Set(locks.map((l) => l.agent).filter(Boolean))]; const roster = await getRoster(cadence); diff --git a/src/constants.mjs b/src/constants.mjs index 0c0a7ec..8e5a653 100644 --- a/src/constants.mjs +++ b/src/constants.mjs @@ -11,6 +11,14 @@ export const DEFAULT_QUERY_TIMEOUT_MS = 30_000; export const DEFAULT_PUBLISH_TIMEOUT_MS = 15_000; export const DEFAULT_MIN_SUCCESSFUL_PUBLISHES = 1; export const DEFAULT_MIN_ACTIVE_RELAY_POOL = 1; +export const DEFAULT_RETRY_ATTEMPTS = 4; +export const DEFAULT_RETRY_BASE_DELAY_MS = 500; +export const DEFAULT_RETRY_CAP_DELAY_MS = 8000; +export const DEFAULT_ROLLING_WINDOW_SIZE = 25; +export const DEFAULT_FAILURE_THRESHOLD = 3; +export const DEFAULT_QUARANTINE_COOLDOWN_MS = 30_000; +export const DEFAULT_MAX_QUARANTINE_COOLDOWN_MS = 300_000; +export const DEFAULT_SNAPSHOT_INTERVAL_MS = 60_000; export const VALID_CADENCES = new Set(['daily', 'weekly']); export const KIND_APP_DATA = 30078; diff --git a/src/context/CONTEXT_2026-02-17T19-41-31Z.md b/src/context/CONTEXT_2026-02-17T19-41-31Z.md new file mode 100644 index 0000000..46d9ee8 --- /dev/null +++ b/src/context/CONTEXT_2026-02-17T19-41-31Z.md @@ -0,0 +1,10 @@ +# Context for Constants Refactor Run + +**Agent:** const-refactor-agent +**Date:** 2026-02-17T19-41-31Z +**Baseline:** +- `js/` directory does not exist. Scanning `src/` instead. +- `AGENTS.md` and `CLAUDE.md` not found. +- Target branch: default + +**Goal:** Find duplicated numeric constants in `src/` and extract them to `src/constants.mjs`. diff --git a/src/decisions/DECISIONS_2026-02-17T19-43-02Z.md b/src/decisions/DECISIONS_2026-02-17T19-43-02Z.md new file mode 100644 index 0000000..be96fc8 --- /dev/null +++ b/src/decisions/DECISIONS_2026-02-17T19-43-02Z.md @@ -0,0 +1,3 @@ +- Extracted retry/health constants from `src/lock-ops.mjs` to `src/constants.mjs`. + - Constants: `DEFAULT_RETRY_ATTEMPTS`, `DEFAULT_RETRY_BASE_DELAY_MS`, `DEFAULT_RETRY_CAP_DELAY_MS`, `DEFAULT_ROLLING_WINDOW_SIZE`, `DEFAULT_FAILURE_THRESHOLD`, `DEFAULT_QUARANTINE_COOLDOWN_MS`, `DEFAULT_MAX_QUARANTINE_COOLDOWN_MS`, `DEFAULT_SNAPSHOT_INTERVAL_MS`. + - Reason: These values were hardcoded in multiple places in `src/lock-ops.mjs` and represent system-wide defaults. diff --git a/src/lock-ops.mjs b/src/lock-ops.mjs index 50dbb9d..f6bab94 100644 --- a/src/lock-ops.mjs +++ b/src/lock-ops.mjs @@ -1,4 +1,4 @@ -import { randomInt } from 'node:crypto'; +import { randomInt, randomUUID } from 'node:crypto'; import { SimplePool } from 'nostr-tools/pool'; import { getQueryTimeoutMs, @@ -7,7 +7,17 @@ import { getRelayFallbacks, getMinActiveRelayPool, } from './torch-config.mjs'; -import { KIND_APP_DATA } from './constants.mjs'; +import { + KIND_APP_DATA, + DEFAULT_RETRY_ATTEMPTS, + DEFAULT_RETRY_BASE_DELAY_MS, + DEFAULT_RETRY_CAP_DELAY_MS, + DEFAULT_ROLLING_WINDOW_SIZE, + DEFAULT_FAILURE_THRESHOLD, + DEFAULT_QUARANTINE_COOLDOWN_MS, + DEFAULT_MAX_QUARANTINE_COOLDOWN_MS, + DEFAULT_SNAPSHOT_INTERVAL_MS, +} from './constants.mjs'; function nowUnix() { return Math.floor(Date.now() / 1000); @@ -297,11 +307,11 @@ export const defaultHealthManager = new RelayHealthManager(); function buildRelayHealthConfig(deps) { return { - rollingWindowSize: deps.rollingWindowSize ?? 25, - failureThreshold: deps.failureThreshold ?? 3, - quarantineCooldownMs: deps.quarantineCooldownMs ?? 30_000, - maxQuarantineCooldownMs: deps.maxQuarantineCooldownMs ?? 5 * 60_000, - snapshotIntervalMs: deps.snapshotIntervalMs ?? 60_000, + rollingWindowSize: deps.rollingWindowSize ?? DEFAULT_ROLLING_WINDOW_SIZE, + failureThreshold: deps.failureThreshold ?? DEFAULT_FAILURE_THRESHOLD, + quarantineCooldownMs: deps.quarantineCooldownMs ?? DEFAULT_QUARANTINE_COOLDOWN_MS, + maxQuarantineCooldownMs: deps.maxQuarantineCooldownMs ?? DEFAULT_MAX_QUARANTINE_COOLDOWN_MS, + snapshotIntervalMs: deps.snapshotIntervalMs ?? DEFAULT_SNAPSHOT_INTERVAL_MS, minActiveRelayPool: Math.max(1, deps.minActiveRelayPool), }; } @@ -415,26 +425,48 @@ async function publishToRelays(pool, relays, event, publishTimeoutMs, phase) { }); } -export async function publishLock(relays, event, deps = {}) { - const { - poolFactory = () => new SimplePool(), - getPublishTimeoutMsFn = getPublishTimeoutMs, - getMinSuccessfulRelayPublishesFn = getMinSuccessfulRelayPublishes, - getRelayFallbacksFn = getRelayFallbacks, - getMinActiveRelayPoolFn = getMinActiveRelayPool, - retryAttempts = 4, - retryBaseDelayMs = 500, - retryCapDelayMs = 8_000, - sleepFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms)), - randomFn = secureRandom, - telemetryLogger = console.error, - healthLogger = console.error, - diagnostics = {}, - } = deps; +class LockPublisher { + constructor(relays, event, deps = {}) { + this.relays = relays; + this.event = event; + this.poolFactory = deps.poolFactory || (() => new SimplePool()); + this.getPublishTimeoutMsFn = deps.getPublishTimeoutMsFn || getPublishTimeoutMs; + this.getMinSuccessfulRelayPublishesFn = deps.getMinSuccessfulRelayPublishesFn || getMinSuccessfulRelayPublishes; + this.getRelayFallbacksFn = deps.getRelayFallbacksFn || getRelayFallbacks; + this.getMinActiveRelayPoolFn = deps.getMinActiveRelayPoolFn || getMinActiveRelayPool; + this.retryAttempts = deps.retryAttempts || DEFAULT_RETRY_ATTEMPTS; + this.retryBaseDelayMs = deps.retryBaseDelayMs || DEFAULT_RETRY_BASE_DELAY_MS; + this.retryCapDelayMs = deps.retryCapDelayMs || DEFAULT_RETRY_CAP_DELAY_MS; + this.sleepFn = deps.sleepFn || ((ms) => new Promise((resolve) => setTimeout(resolve, ms))); + this.randomFn = deps.randomFn || secureRandom; + this.telemetryLogger = deps.telemetryLogger || console.error; + this.healthLogger = deps.healthLogger || console.error; + this.diagnostics = deps.diagnostics || {}; + this.healthManager = deps.healthManager || defaultHealthManager; + + // Derived values initialized later in async method + this.attemptId = deps.diagnostics?.attemptId || deps.attemptId || randomUUID(); + this.correlationId = deps.diagnostics?.correlationId || deps.correlationId || randomUUID(); + + this.pool = this.poolFactory(); + } async publish() { + this.publishTimeoutMs = await this.getPublishTimeoutMsFn(); + this.minSuccesses = await this.getMinSuccessfulRelayPublishesFn(); + this.fallbackRelays = (await this.getRelayFallbacksFn()).filter((relay) => !this.relays.includes(relay)); + const minActive = await this.getMinActiveRelayPoolFn(); + this.healthConfig = buildRelayHealthConfig({ + rollingWindowSize: DEFAULT_ROLLING_WINDOW_SIZE, + minActiveRelayPool: minActive, + ...this.diagnostics, // Assuming diagnostics might carry config overrides? Or better, just rely on defaults/config + }); + + this.allRelays = mergeRelayList(this.relays, this.fallbackRelays); + this.maxAttempts = this.retryAttempts; + try { - maybeLogHealthSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic'); + this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic'); let lastAttemptState = null; const retryTimeline = []; const overallStartedAt = Date.now(); @@ -500,7 +532,7 @@ export async function publishLock(relays, event, deps = {}) { async attemptPhase(phaseRelays, phaseName, attempted, publishResults) { if (!phaseRelays.length) return; - const { prioritized } = prioritizeRelays(phaseRelays, this.healthConfig); + const { prioritized } = this.healthManager.prioritizeRelays(phaseRelays, this.healthConfig); if (!prioritized.length) return; console.error(`[${phaseName}] Publishing to ${prioritized.length} relays (${prioritized.join(', ')})...`); @@ -513,7 +545,7 @@ export async function publishLock(relays, event, deps = {}) { const elapsedMs = Date.now() - startedAtMs; for (const result of phaseResults) { result.latencyMs = elapsedMs; - recordRelayOutcome(result.relay, result.success, result.message, elapsedMs, this.healthConfig); + this.healthManager.recordOutcome(result.relay, result.success, result.message, elapsedMs, this.healthConfig); } publishResults.push(...phaseResults); } @@ -601,7 +633,7 @@ export async function publishLock(relays, event, deps = {}) { totalElapsedMs: Date.now() - overallStartedAt, })); - maybeLogHealthSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:failure', true); + this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:failure', true); throw new Error( `Failed relay publish quorum in publish phase: ${lastAttemptState.successCount}/${lastAttemptState.attempted.size} successful ` + `(required=${this.minSuccesses}, timeout=${this.publishTimeoutMs}ms, attempts=${this.maxAttempts}, attempt_id=${this.attemptId}, correlation_id=${this.correlationId}, error_category=${terminalFailureCategory}, total_retry_timeline_ms=${Date.now() - overallStartedAt})\n` diff --git a/src/test_logs/TEST_LOG_2026-02-17T19-43-26Z.md b/src/test_logs/TEST_LOG_2026-02-17T19-43-26Z.md new file mode 100644 index 0000000..f6e63b9 --- /dev/null +++ b/src/test_logs/TEST_LOG_2026-02-17T19-43-26Z.md @@ -0,0 +1,227 @@ + +> 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:3973) 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: 6.654526 + 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.073858 + 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: 5.742571 + type: 'test' + ... + # Subtest: short-circuits non-retryable failures without extra attempts + ok 4 - short-circuits non-retryable failures without extra attempts + --- + duration_ms: 2.299061 + type: 'test' + ... + 1..4 +ok 1 - publishLock retry behavior + --- + duration_ms: 20.363862 + type: 'suite' + ... +# Subtest: lock-ops + # Subtest: parseLockEvent + # Subtest: parses a valid lock event correctly + ok 1 - parses a valid lock event correctly + --- + duration_ms: 1.874948 + type: 'test' + ... + 1..1 + ok 1 - parseLockEvent + --- + duration_ms: 2.822323 + 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":"7d70b395-2145-4470-9f33-9b602a34792d","attemptId":"c0035955-5f07-4d6b-a450-e07811f00e2f","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":"239da9db-92a7-44f8-b694-52a4a6ddf437","attemptId":"12267c26-1926-4f5b-835b-f92682e5ffc4","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":"54df96c1-356d-40a2-a300-0a5fce94d1f9","attemptId":"72e3fd52-0c75-4f3f-a43f-241ed8d973e1","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://healthy, wss://bad)... +# {"event":"lock_publish_quorum_met","correlationId":"35e05397-38ab-4fd5-b979-9f731dc5765a","attemptId":"c6410080-1244-4f4b-9139-dbb85187b441","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://healthy, wss://bad)... +# {"event":"lock_publish_quorum_met","correlationId":"e6f1916c-879a-4ed2-953e-7a519c11d857","attemptId":"1aa9c36c-8755-45ca-9fe6-6a89f883c152","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":"56c8195a-74eb-4b72-a495-3e1efcb4a3f0","attemptId":"d2e0b655-7115-4f61-91fc-139dc5ec9db4","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 2 relays (wss://primary-a, wss://primary-b)... +# [publish:fallback] Publishing to 1 relays (wss://fallback-healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"94251c93-ebf1-481d-8fb5-c8a73bdf0a20","attemptId":"533fb740-98ec-4358-9d32-78c8b0ac72a4","publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"elapsedMs":0}],"totalElapsedMs":0} +# Published to 1/3 relays (required=1, timeout=200ms) + # 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: 27.235965 + type: 'test' + ... + 1..1 + ok 2 - queryLocks + --- + duration_ms: 27.514651 + type: 'suite' + ... + # Subtest: publishLock + # Subtest: retries transient failures and succeeds on a later attempt + not ok 1 - retries transient failures and succeeds on a later attempt + --- + duration_ms: 6.53189 + type: 'test' + location: '/app/test/lock-ops.test.mjs:78:5' + failureType: 'testCodeFailure' + error: |- + Expected values to be strictly deep-equal: + + actual - expected + + { + + attemptId: '52c2baa6-8ff8-4f42-b6b3-ead606942315', + + correlationId: '6428aa2b-60f0-40a5-b385-525fc25c46b0', + - attemptId: 'attempt-1', + - correlationId: 'corr-1', + elapsedMs: 1, + errorCategory: 'connection_reset', + event: 'lock_publish_retry', + nextDelayMs: 250, + publishAttempt: 1, + + code: 'ERR_ASSERTION' + name: 'AssertionError' + expected: + event: 'lock_publish_retry' + correlationId: 'corr-1' + attemptId: 'attempt-1' + publishAttempt: 1 + relayUrl: 'wss://primary-a' + errorCategory: 'connection_reset' + elapsedMs: 1 + nextDelayMs: 250 + actual: + event: 'lock_publish_retry' + correlationId: '6428aa2b-60f0-40a5-b385-525fc25c46b0' + attemptId: '52c2baa6-8ff8-4f42-b6b3-ead606942315' + publishAttempt: 1 + relayUrl: 'wss://primary-a' + errorCategory: 'connection_reset' + elapsedMs: 1 + nextDelayMs: 250 + operator: 'deepStrictEqual' + stack: |- + TestContext. (file:///app/test/lock-ops.test.mjs:124:14) + async Test.run (node:internal/test_runner/test:1054:7) + async Promise.all (index 0) + async Suite.run (node:internal/test_runner/test:1442:7) + async Suite.processPendingSubtests (node:internal/test_runner/test:744:7) + ... + # 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: 2.875565 + type: 'test' + ... + # Subtest: fails without retry for persistent validation failures + ok 3 - fails without retry for persistent validation failures + --- + duration_ms: 1.360655 + 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: 2.145677 + 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.586006 + type: 'test' + ... + # Subtest: uses fallback relays to satisfy min success quorum + ok 6 - uses fallback relays to satisfy min success quorum + --- + duration_ms: 1.059862 + 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: 2.500909 + 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: 1.073546 + type: 'test' + ... + 1..8 + not ok 3 - publishLock + --- + duration_ms: 19.967883 + type: 'suite' + location: '/app/test/lock-ops.test.mjs:77:3' + failureType: 'subtestsFailed' + error: '1 subtest failed' + code: 'ERR_TEST_FAILURE' + ... + 1..3 +not ok 2 - lock-ops + --- + duration_ms: 52.981447 + type: 'suite' + location: '/app/test/lock-ops.test.mjs:10:1' + failureType: 'subtestsFailed' + error: '1 subtest failed' + code: 'ERR_TEST_FAILURE' + ... +1..2 +# tests 14 +# suites 5 +# pass 13 +# fail 1 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 196.37029 diff --git a/src/test_logs/TEST_LOG_2026-02-17T19-47-44Z.md b/src/test_logs/TEST_LOG_2026-02-17T19-47-44Z.md new file mode 100644 index 0000000..f7d19d7 --- /dev/null +++ b/src/test_logs/TEST_LOG_2026-02-17T19-47-44Z.md @@ -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:5876) 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: 6.413745 + 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: 3.062285 + 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.611267 + type: 'test' + ... + # Subtest: short-circuits non-retryable failures without extra attempts + ok 4 - short-circuits non-retryable failures without extra attempts + --- + duration_ms: 2.07494 + type: 'test' + ... + 1..4 +ok 1 - publishLock retry behavior + --- + duration_ms: 17.852297 + type: 'suite' + ... +# Subtest: lock-ops + # Subtest: parseLockEvent + # Subtest: parses a valid lock event correctly + ok 1 - parses a valid lock event correctly + --- + duration_ms: 1.583449 + type: 'test' + ... + 1..1 + ok 1 - parseLockEvent + --- + duration_ms: 2.601962 + 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":"1be06dfa-a31e-42b0-b789-327fd9e11131","attemptId":"10405dd5-2f84-4e23-9a5a-696abb34c1bc","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":"becb5a52-cef6-4bc6-84b1-ca4d14d63e04","attemptId":"25dca8be-61ee-42b8-a764-7ebdc46f234b","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://bad, wss://healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"67ecc056-eaa3-40c2-9042-15e033b7a252","attemptId":"07181123-946d-4823-bc4a-c79d13a84920","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":"6075ee96-8def-4111-84e2-d7b395950a84","attemptId":"bb03ae8b-ff47-41e2-ba91-b3031d38c497","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://healthy, wss://bad)... +# {"event":"lock_publish_quorum_met","correlationId":"38f8db94-b61b-4b02-9a33-05fd8da73e5e","attemptId":"e645bbac-64ec-4f61-a865-a91d0adc739c","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://healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"4c6a0906-bd5b-431b-9af1-1d4a87aa59d3","attemptId":"13836194-8337-49af-8f81-8038a7101d4b","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 2 relays (wss://primary-a, wss://primary-b)... +# [publish:fallback] Publishing to 1 relays (wss://fallback-healthy)... +# {"event":"lock_publish_quorum_met","correlationId":"2e7e8c9f-5b93-4cd3-8324-a63b981e9e4c","attemptId":"d53e657e-431a-4203-a017-256ba03a05db","publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"requiredSuccesses":1,"timeoutMs":200,"retryTimeline":[{"publishAttempt":1,"successCount":1,"relayAttemptedCount":3,"elapsedMs":1}],"totalElapsedMs":2} +# Published to 1/3 relays (required=1, timeout=200ms) + # 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: 27.905124 + type: 'test' + ... + 1..1 + ok 2 - queryLocks + --- + duration_ms: 28.20218 + 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: 4.944259 + 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: 3.551748 + type: 'test' + ... + # Subtest: fails without retry for persistent validation failures + ok 3 - fails without retry for persistent validation failures + --- + duration_ms: 1.406267 + 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: 2.101245 + 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.547853 + type: 'test' + ... + # Subtest: uses fallback relays to satisfy min success quorum + ok 6 - uses fallback relays to satisfy min success quorum + --- + duration_ms: 1.380374 + 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.040514 + 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.862114 + type: 'test' + ... + 1..8 + ok 3 - publishLock + --- + duration_ms: 21.884946 + type: 'suite' + ... + 1..3 +ok 2 - lock-ops + --- + duration_ms: 53.519877 + type: 'suite' + ... +1..2 +# tests 14 +# suites 5 +# pass 14 +# fail 0 +# cancelled 0 +# skipped 0 +# todo 0 +# duration_ms 212.244831 diff --git a/src/todo/TODO_2026-02-17T19-43-02Z.md b/src/todo/TODO_2026-02-17T19-43-02Z.md new file mode 100644 index 0000000..96de603 --- /dev/null +++ b/src/todo/TODO_2026-02-17T19-43-02Z.md @@ -0,0 +1 @@ +- [x] Extract lock-ops constants to src/constants.mjs diff --git a/task-logs/daily/2026-02-17T19-45-47Z__const-refactor-agent__completed.md b/task-logs/daily/2026-02-17T19-45-47Z__const-refactor-agent__completed.md new file mode 100644 index 0000000..f129e8c --- /dev/null +++ b/task-logs/daily/2026-02-17T19-45-47Z__const-refactor-agent__completed.md @@ -0,0 +1,15 @@ +--- +cadence: daily +agent: const-refactor-agent +status: completed +reason: 'Scheduler cycle completed successfully' +created_at: 2026-02-17T19-48-02Z +timestamp: 2026-02-17T19-48-02Z +platform: linux +--- + +# Scheduler completed + +- reason: Scheduler cycle completed successfully +- platform: linux +- details: Extracted lock backend constants to src/constants.mjs, refactored lock-ops.mjs, fixed cmd-check.mjs bug.