Skip to content

fix(miner): register worktree-allocator in purge-cli's right-to-be-forgotten sweep (#8320) - #8563

Closed
philluiz2323 wants to merge 1 commit into
JSONbored:mainfrom
philluiz2323:fix-worktree-allocator-purge-8320
Closed

fix(miner): register worktree-allocator in purge-cli's right-to-be-forgotten sweep (#8320)#8563
philluiz2323 wants to merge 1 commit into
JSONbored:mainfrom
philluiz2323:fix-worktree-allocator-purge-8320

Conversation

@philluiz2323

Copy link
Copy Markdown
Contributor

Summary

packages/loopover-miner/lib/worktree-allocator.ts's worktree_slots table has a repo_full_name column, just like every other repo-scoped local store, but it was absent from purge-cli.ts's right-to-be-forgotten sweep (REAL_PURGE_TARGETS) — the same recurring gap class already fixed for other stores (#7091, #6599, #8009).

worktree_slots is a fixed pool of maxConcurrency pre-allocated rows (slot_index is the primary key and every slot index must always exist), not an append-only ledger, so the generic purgeStoreByRepo helper (a hard DELETE) is the wrong shape here — it would shrink the pool below maxConcurrency and break ensureSlots'/selectFreeSlot's invariant. This PR follows governor-state.ts's own precedent for a non-uniform store: a hand-written purgeByRepo method directly on the WorktreeAllocator object.

  • Added WorktreeAllocator.purgeByRepo(repoFullName): number — an UPDATE, never a DELETE, that only clears a row where status = 'free' AND repo_full_name = ?. An active slot's row (a live, currently-running attempt's real worktree checkout) is never touched or counted — force-clearing it would desync the allocator from the live checkout on disk.
  • Registered worktree-allocator in purge-cli.ts's REAL_PURGE_TARGETS with no spec/specs field (its purge logic lives on the store object itself), mirroring how governor-state's entry carries specs instead of a single spec for its own non-uniform case.
  • Added a countDryRun extension point to PurgeTarget + runPurgeDryRun for stores whose dry-run preview can't be expressed as a generic LedgerPurgeSpec; worktree-allocator's own dry-run query matches the real purge's status = 'free' AND repo_full_name = ? condition exactly, so --dry-run never reports an active slot as purgeable.
  • Every existing miner-purge-cli.test.ts fixture referencing the "twelve real stores" / toHaveLength(12) / a full REAL_PURGE_TARGETS-shaped options object is updated to the new thirteenth store.

Scope

  • packages/loopover-miner/** only (plus its test files) — not Codecov-gated per this issue's own text.
  • Narrow, single-purpose change; no unrelated refactors.

Validation

  • npm --workspace @loopover/miner run build:tsc — clean
  • npm run typecheck — clean
  • npx vitest run test/unit/miner-worktree-allocator.test.ts test/unit/miner-purge-cli.test.ts test/unit/miner-worktree-allocator-collisions.test.ts test/unit/miner-worktree-allocator-lease-expiry.test.ts test/unit/miner-attempt-cli.test.ts — all green except 4 pre-existing, unrelated Windows-local-only failures (path-separator string assertions expecting /, a chmod-permission-bit assertion Windows doesn't enforce, and two EBUSY: resource busy or locked temp-dir cleanup races) — reproduced identically on a git stash'd clean baseline before this change, so confirmed environment noise, not a regression.
  • git diff --check upstream/main HEAD — clean, no trailing whitespace.
  • ui:* checks — skipped (backend/CLI-only change, no apps/gittensory-ui files touched).

New coverage

  • purgeByRepo clears a free slot carrying a stale repo_full_name and counts it (seeded directly, bypassing the normal release path since normal operation never leaves one stale).
  • purgeByRepo never touches an active slot for the target repo — 0 purged, row unchanged.
  • purgeByRepo returns 0 when no slot matches the repo.
  • purge-cli's dry-run + real purge both exercise the new store end-to-end (free-stale-row counted, active-row excluded, file-doesn't-exist-yet, corrupted-file error path, real on-disk default-path open/close).

Safety

  • No secrets, tokens, wallets, hotkeys/coldkeys, trust scores, or reward/payout values in code, comments, tests, or this PR text.
  • No auth/session/CORS surface touched.
  • Destructive-by-default risk addressed directly: the whole point of this change is that the new purge path can only update a free slot, never delete a row or touch a live attempt's active checkout — covered by the two invariant tests above.

Notes

Closes #8320

@philluiz2323
philluiz2323 requested a review from JSONbored as a code owner July 24, 2026 21:03
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 24, 2026

Copy link
Copy Markdown

❌ 31 Tests Failed:

Tests completed Failed Passed Skipped
1428 31 1397 0
View the top 3 failed test(s) by shortest run time
test/unit/miner-claim-ledger.test.ts > loopover-miner claim ledger (#2314) > documents that miner_claims is local bookkeeping only, not duplicate adjudication (#3355)
Stack Traces | 0.0106s run time
Error: ENOENT: no such file or directory, open '.../loopover-miner/lib/claim-ledger.js'
 ❯ test/unit/miner-claim-ledger.test.ts:155:20

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { errno: -2, code: 'ENOENT', syscall: 'open', path: '.../loopover-miner/lib/claim-ledger.js' }
test/unit/miner-benchmark-script.test.ts > loopover-miner benchmark script (#4845) > runs end-to-end as a CLI script and prints both benchmark lines
Stack Traces | 0.0701s run time
AssertionError: expected 1 to be +0 // Object.is equality

- Expected
+ Received

- 0
+ 1

 ❯ test/unit/miner-benchmark-script.test.ts:79:27
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > regression: never schema-initializes an existing-but-empty SQLite file (genuinely read-only, not just by convention)
Stack Traces | 0.0892s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > returns a raise verdict when found is explicitly false
Stack Traces | 0.0918s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/miner-concurrent-store-races.test.ts > portfolio-queue cross-process races (#4867) > rejects the claim-child helper when required args are missing
Stack Traces | 0.0973s run time
AssertionError: expected 1 to be 2 // Object.is equality

- Expected
+ Received

- 2
+ 1

 ❯ test/unit/miner-concurrent-store-races.test.ts:212:22
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > returns a go verdict for a clean, unclaimed, low-risk issue — with no network call
Stack Traces | 0.0982s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/miner-concurrent-store-races.test.ts > portfolio-queue cross-process races (#4867) > rejects the dequeue-child helper when required args are missing
Stack Traces | 0.1s run time
AssertionError: expected 1 to be 2 // Object.is equality

- Expected
+ Received

- 2
+ 1

 ❯ test/unit/miner-concurrent-store-races.test.ts:203:22
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > never leaks private financial terminology in the response
Stack Traces | 0.103s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > rejects an invalid duplicateClusterRisk enum value
Stack Traces | 0.105s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > tool description documents the ledger-sourcing behavior and advisory-only guarantee
Stack Traces | 0.105s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > returns an avoid verdict when the issue is already solved
Stack Traces | 0.105s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/miner-worktree-allocator-collisions.test.ts > loopover-miner worktree allocator collisions (#4298) > rejects the acquire-child helper when required args are missing
Stack Traces | 0.106s run time
AssertionError: expected 1 to be 2 // Object.is equality

- Expected
+ Received

- 2
+ 1

 ❯ test/unit/miner-worktree-allocator-collisions.test.ts:182:22
test/unit/miner-portfolio-queue-crash-recovery.test.ts > portfolio-queue crash recovery (#4868) > rejects the claim-and-hold-child helper when required args are missing
Stack Traces | 0.11s run time
AssertionError: expected 1 to be 2 // Object.is equality

- Expected
+ Received

- 2
+ 1

 ❯ test/unit/miner-portfolio-queue-crash-recovery.test.ts:123:22
test/unit/miner-concurrent-store-races.test.ts > claim-ledger cross-process races (#4867) > two processes claiming DIFFERENT issues concurrently both succeed with distinct rows
Stack Traces | 0.112s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-concurrent-store-races.test.ts:51:47
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > falls back to the caller-supplied claimStatus unchanged when repoFullName/issueNumber are omitted
Stack Traces | 0.112s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate stdio tool (#4270) > registers the tool in the stdio server tool list
Stack Traces | 0.113s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > falls back to the caller-supplied claimStatus unchanged when the ledger DB file does not exist (no local install detected)
Stack Traces | 0.114s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > sources claimStatus: unclaimed from the ledger when no active claim matches the issue, overriding a contradicting caller-supplied value
Stack Traces | 0.115s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > regression: reports claimStatus 'unknown' (never silently trusts the caller-supplied value) when the ledger DB file exists but is unreadable/corrupt
Stack Traces | 0.118s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > invariant: never writes to the claim ledger and never gains blocking/override authority (advisory-only output shape unchanged)
Stack Traces | 0.121s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/miner-concurrent-store-races.test.ts > claim-ledger cross-process races (#4867) > two processes claiming the SAME issue simultaneously produce exactly one active row, no duplication
Stack Traces | 0.131s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-concurrent-store-races.test.ts:51:47
test/unit/mcp-feasibility-gate.test.ts > loopover_feasibility_gate: local claim-ledger sourcing (#5157) > regression: prefers ledger-backed truth (claimed) over a contradicting caller-supplied claimStatus
Stack Traces | 0.134s run time
McpError: MCP error -32000: Connection closed
 ❯ Function.fromError node_modules/@.../sdk/src/types.ts:2330:15
 ❯ Client._onclose node_modules/@.../src/shared/protocol.ts:662:31
 ❯ StdioClientTransport._transport.onclose node_modules/@.../src/shared/protocol.ts:618:17
 ❯ ChildProcess.<anonymous> node_modules/@.../src/client/stdio.ts:143:30

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
Serialized Error: { code: -32000, data: undefined }
test/unit/miner-worktree-allocator-collisions.test.ts > loopover-miner worktree allocator collisions (#4298) > returns the same allocation when two processes race on one attempt id
Stack Traces | 0.14s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-worktree-allocator-collisions.test.ts:92:47
test/unit/miner-portfolio-queue-crash-recovery.test.ts > portfolio-queue crash recovery (#4868) > a process killed mid-claim leaves the item stuck in_progress until the lease expires, then it is swept back to queued and re-claimable
Stack Traces | 0.143s run time
Error: child exited before claiming (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-portfolio-queue-crash-recovery.test.ts:63:47
test/unit/miner-concurrent-store-races.test.ts > portfolio-queue cross-process races (#4867) > N processes racing dequeueNext() over N queued items claim exactly N distinct items, none duplicated
Stack Traces | 0.16s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-concurrent-store-races.test.ts:51:47
test/unit/miner-worktree-allocator-collisions.test.ts > loopover-miner worktree allocator collisions (#4298) > rejects excess simultaneous cross-process acquire calls at the concurrency cap
Stack Traces | 0.17s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-worktree-allocator-collisions.test.ts:92:47
test/unit/miner-concurrent-store-races.test.ts > portfolio-queue cross-process races (#4867) > two processes racing dequeueNext() on a single queued item: exactly one wins, the other gets null
Stack Traces | 0.173s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-concurrent-store-races.test.ts:51:47
test/unit/miner-worktree-allocator-collisions.test.ts > loopover-miner worktree allocator collisions (#4298) > returns distinct worktree paths when multiple processes acquire simultaneously
Stack Traces | 0.229s run time
Error: child exited before READY (1)
 ❯ ChildProcess.<anonymous> test/unit/miner-worktree-allocator-collisions.test.ts:92:47
test/unit/miner-discover-cli.test.ts > loopover-miner discover CLI entrypoint (#4247) > exits 2 with a usage error when neither repo targets nor --search are given
Stack Traces | 0.232s run time
AssertionError: expected 'node:.../modules/esm/resolve:275…' to contain 'Usage: loopover-miner discover'

- Expected
+ Received

- Usage: loopover-miner discover
+ node:.../modules/esm/resolve:275
+     throw new ERR_MODULE_NOT_FOUND(
+           ^
+
+ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../loopover-miner/lib/attempt-cli.js' imported from .../loopover-miner/bin/loopover-miner.ts
+     at finalizeResolution (node:.../modules/esm/resolve:275:11)
+     at moduleResolve (node:.../modules/esm/resolve:861:10)
+     at defaultResolve (node:.../modules/esm/resolve:985:11)
+     at #cachedDefaultResolve (node:.../modules/esm/loader:747:20)
+     at ModuleLoader.resolve (node:.../modules/esm/loader:724:38)
+     at ModuleLoader.getModuleJobForImport (node:.../modules/esm/loader:320:38)
+     at ModuleJob._link (node:.../modules/esm/module_job:182:49) {
+   code: 'ERR_MODULE_NOT_FOUND',
+   url: 'file://.../loopover-miner/lib/attempt-cli.js'
+ }
+
+ Node.js v22.23.1
+

 ❯ test/unit/miner-discover-cli.test.ts:1489:20
test/unit/miner-discover-cli.test.ts > loopover-miner discover CLI entrypoint (#4247) > lists the discover command in --help
Stack Traces | 0.266s run time
AssertionError: expected 'node:.../modules/esm/resolve:275…' to contain 'loopover-miner discover'

- Expected
+ Received

- loopover-miner discover
+ node:.../modules/esm/resolve:275
+     throw new ERR_MODULE_NOT_FOUND(
+           ^
+
+ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../loopover-miner/lib/attempt-cli.js' imported from .../loopover-miner/bin/loopover-miner.ts
+     at finalizeResolution (node:.../modules/esm/resolve:275:11)
+     at moduleResolve (node:.../modules/esm/resolve:861:10)
+     at defaultResolve (node:.../modules/esm/resolve:985:11)
+     at #cachedDefaultResolve (node:.../modules/esm/loader:747:20)
+     at ModuleLoader.resolve (node:.../modules/esm/loader:724:38)
+     at ModuleLoader.getModuleJobForImport (node:.../modules/esm/loader:320:38)
+     at ModuleJob._link (node:.../modules/esm/module_job:182:49) {
+   code: 'ERR_MODULE_NOT_FOUND',
+   url: 'file://.../loopover-miner/lib/attempt-cli.js'
+ }
+
+ Node.js v22.23.1
+

 ❯ test/unit/miner-discover-cli.test.ts:1484:20
test/unit/miner-init-wizard.test.ts > loopover-miner init --interactive wizard (#5176) > e2e: `loopover-miner init --interactive` dispatches to the wizard, not the non-interactive path
Stack Traces | 0.316s run time
AssertionError: expected 'node:.../modules/esm/resolve:275…' to contain 'requires a real terminal'

- Expected
+ Received

- requires a real terminal
+ node:.../modules/esm/resolve:275
+     throw new ERR_MODULE_NOT_FOUND(
+           ^
+
+ Error [ERR_MODULE_NOT_FOUND]: Cannot find module '.../loopover-miner/lib/attempt-cli.js' imported from .../loopover-miner/bin/loopover-miner.ts
+     at finalizeResolution (node:.../modules/esm/resolve:275:11)
+     at moduleResolve (node:.../modules/esm/resolve:861:10)
+     at defaultResolve (node:.../modules/esm/resolve:985:11)
+     at #cachedDefaultResolve (node:.../modules/esm/loader:747:20)
+     at ModuleLoader.resolve (node:.../modules/esm/loader:724:38)
+     at ModuleLoader.getModuleJobForImport (node:.../modules/esm/loader:320:38)
+     at ModuleJob._link (node:.../modules/esm/module_job:182:49) {
+   code: 'ERR_MODULE_NOT_FOUND',
+   url: 'file://.../loopover-miner/lib/attempt-cli.js'
+ }
+
+ Node.js v22.23.1
+

 ❯ test/unit/miner-init-wizard.test.ts:360:27

To view more test analytics, go to the Test Analytics Dashboard
📋 Got 3 mins? Take this short survey to help us improve Test Analytics.

…rgotten sweep (JSONbored#8320)

worktree_slots carries repo_full_name like every other repo-scoped store, but was absent from purge-cli's REAL_PURGE_TARGETS. Adds WorktreeAllocator.purgeByRepo, a hand-written UPDATE (not the generic purgeStoreByRepo DELETE, which would shrink the fixed slot pool) that only ever clears a free slot's stale repo_full_name -- an active slot's live worktree checkout is never touched. Registers the store in REAL_PURGE_TARGETS with its own dry-run counting query matching the same free-only match condition.
@JSONbored
JSONbored force-pushed the fix-worktree-allocator-purge-8320 branch from 593e42e to 2d3c71b Compare July 24, 2026 21:11
@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 24, 2026
@loopover-orb

loopover-orb Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Caution

🛑 LoopOver review result - fixes required

Review updated: 2026-07-24 21:16:24 UTC

5 files · 1 AI reviewer · no blockers · CI failing · blocked

🛑 Suggested Action - Fix Blockers

Review summary
This PR adds a fixed-pool-aware `purgeByRepo` (UPDATE, never DELETE) to `worktree-allocator.ts`, wires it into `purge-cli.ts`'s `REAL_PURGE_TARGETS` via a new `countDryRun` extension point on `PurgeTarget`, and updates every affected test fixture across four test files. The UPDATE correctly guards on `status = 'free' AND repo_full_name = ?` so an active slot's live-checkout row is never touched, matching the stated invariant that `slot_index` count must stay fixed at `maxConcurrency`, and the dry-run counting query mirrors the real purge condition exactly so `--dry-run` never over- or under-reports. Test coverage for the new `purgeByRepo` method exercises all three real branches (stale free slot cleared, active slot untouched, no match), and the purge-cli fixture correctly seeds a stale `repo_full_name` on a free slot by bypassing acquire/release, matching how the real backstop path would be reached.

Nits — 3 non-blocking
  • `countWorktreeAllocatorFreeByRepo` (purge-cli.ts) does `Number(row?.count)` where `row` can never actually be `undefined` for a `COUNT(*)` query — harmless but mirrors an existing loose pattern in `countStoreByRepo`, worth tightening at the source rather than propagating.
  • The new `purgeByRepo` comment block in worktree-allocator.ts is fairly long (7 lines) for a single prepared statement; consider trimming to the load-bearing parts (the `status='free'` guard rationale) now that the PR description already documents the rest.
  • Consider exporting `countWorktreeAllocatorFreeByRepo` as `PURGE_FREE_SLOT_QUERY` or similar constant string shared between the UPDATE in worktree-allocator.ts and the SELECT COUNT in purge-cli.ts, so the two `status = 'free' AND repo_full_name = ?` conditions can't silently drift apart in a future edit — right now they're two independently-typed literals that happen to match.

CI checks failing

  • validate
  • validate-tests

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #8320
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 1037 registered-repo PR(s), 610 merged, 126 issue(s).
Contributor context ✅ Confirmed Gittensor contributor philluiz2323; Gittensor profile; 1037 PR(s), 126 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: significant
Linked issue satisfaction

Addressed
The PR adds a purgeByRepo UPDATE-only method to WorktreeAllocator that guards on status='free' and never touches active rows, registers worktree-allocator in REAL_PURGE_TARGETS without a spec/specs field via a new countDryRun extension mirroring the real purge condition, and adds targeted tests for stale-free, active-untouched, no-match, and dry-run consistency cases as required.

Review context
  • Author: philluiz2323
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, JavaScript, MDX, TypeScript, CSS, Cuda, HTML, Kotlin
  • Official Gittensor activity: 1037 PR(s), 126 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb

loopover-orb Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

LoopOver is closing this pull request on the maintainer's behalf (CI is failing (validate, validate-tests)). This is an automated maintenance action — to pursue this change, please open a new pull request with the issues resolved. Closed PRs may be analyzed later to improve review accuracy, but they are not automatically reopened or re-reviewed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(miner): worktree-allocator.ts's worktree_slots (has repo_full_name) is absent from purge-cli.ts's right-to-be-forgotten sweep

1 participant