mit einem Klick
plug-leaking-promise-race
// Reference for the `Promise.race` cross-iteration handler-leak bug. Loads on demand when writing or reviewing concurrency code that uses `Promise.race`, `Promise.any`, or hand-rolled concurrency limiters.
// Reference for the `Promise.race` cross-iteration handler-leak bug. Loads on demand when writing or reviewing concurrency code that uses `Promise.race`, `Promise.any`, or hand-rolled concurrency limiters.
Run this repo's GitHub Actions workflows locally in Docker with Agent CI to validate changes before pushing. Use before opening or updating a PR, after editing a workflow YAML under .github/workflows, or whenever catching a CI failure locally beats waiting on a remote runner.
Scans the codebase for bugs, logic errors, cache races, workflow problems, insecure defaults, security regressions in the diff, and variant analysis on prior findings. Spawns specialized Task agents per scan type, deduplicates findings, and produces an A-F prioritized report. Use when preparing a release, investigating quality issues, running pre-merge checks, or whenever a recent diff touches security-sensitive code.
Umbrella update skill for a Socket fleet repo. Runs `pnpm run update` (npm), validates `lockstep.json` via `pnpm run lockstep` (if present), optionally bumps submodules, checks workflow SHA pins, resolves open Dependabot security alerts, refreshes the README coverage badge when applicable, and audits GitHub repo + Actions settings drift via `scripts/lint-github-settings.mts`. Use when asked to update dependencies, sync upstreams, fix security advisories, refresh coverage, or prepare for a release.
Reference for locking down programmatic Claude invocations (the `claude` CLI in workflows/scripts, the `@anthropic-ai/claude-agent-sdk` `query()` in code). Loads on demand when writing or reviewing any callsite that runs Claude programmatically. Source: https://code.claude.com/docs/en/agent-sdk/permissions.
Resolve open GitHub Dependabot security alerts on a fleet repo. Fetches alerts via `gh api`, applies fixes (direct dep bump, pnpm override for transitives, or principled dismissal for unfixable), validates with `pnpm run check`, commits per-alert, and reports remaining advisories. Sibling of `updating-lockstep` under the `updating` umbrella.
Compact the current conversation into a handoff doc so a fresh agent can pick up the work. Use when context is getting thin, a session is about to end, or the next stage of the work needs a different agent / human.
| name | plug-leaking-promise-race |
| description | Reference for the `Promise.race` cross-iteration handler-leak bug. Loads on demand when writing or reviewing concurrency code that uses `Promise.race`, `Promise.any`, or hand-rolled concurrency limiters. |
| user-invocable | false |
| allowed-tools | Read, Grep, Glob |
Never re-race the same pool of promises across loop iterations. Each call to Promise.race([A, B, …]) attaches fresh .then handlers to every arm. A promise that survives N iterations accumulates N handler sets. See nodejs/node#17469 and @watchable/unpromise.
Safe — both arms created per call:
const value = await Promise.race([
fetchSomething(),
new Promise((_, r) => setTimeout(() => r(new Error('timeout')), 5000)),
])
Leaky — pool survives across iterations, accumulating handlers:
while (queue.length) {
const winner = await Promise.race(pool) // ← N handlers per arm by iteration N
pool = pool.filter(p => p !== winner)
}
Same hazard for Promise.any and any long-lived arm such as an interrupt signal.
Use a single-waiter "slot available" signal. Each task's .then resolves a one-shot promiseWithResolvers that the loop awaits, then replaces. No persistent pool, nothing to stack.
let signal = Promise.withResolvers<Task>()
function startTask(task: Task) {
task.run().then(() => {
const prev = signal
signal = Promise.withResolvers<Task>()
prev.resolve(task)
})
}
while (queue.length) {
// launch up to N tasks
while (running < N && queue.length) startTask(queue.shift()!)
const finished = await signal.promise
running -= 1
}
The arm being awaited is always fresh; nothing accumulates handlers.
Before merging concurrency code, ask: does any arm of a Promise.race/Promise.any outlive the call? If yes, refactor to the single-waiter signal.