Skip to content

fix(repair): stop stalled GitHub CLI requests blocking workers - #1302

Merged
steipete merged 1 commit into
openclaw:mainfrom
SebTardif:fix/f005-gh-cli-timeout
Aug 31, 2026
Merged

fix(repair): stop stalled GitHub CLI requests blocking workers#1302
steipete merged 1 commit into
openclaw:mainfrom
SebTardif:fix/f005-gh-cli-timeout

Conversation

@SebTardif

@SebTardif SebTardif commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Fixes stalled GitHub CLI requests in the shared repair helpers. These calls previously had no application deadline, and the original proposal ignored timeout settings supplied through the existing per-call env option.

Why This Change Was Made

ghText, ghTextAsync, and ghSpawn now pass a native timeout to Node using the same merged environment supplied to their child process. A positive explicit timeoutMs wins; otherwise the GitHub-specific environment setting takes precedence over the network fallback, with a two-minute default and 30-second minimum for configured values. A fractional positive explicit value is clamped to one millisecond so rounding cannot disable the deadline.

This resolves the current review's options.env finding without adding another environment merge or process supervisor. Native error/result shapes and existing mutation-ledger handling remain in place. The synchronous shared helper was added without a timeout in 62dd779eb1, verified against its raw parent 8e77181d04da619f26005d8c7299e0b9b2aeb3c4.

User Impact

A stalled ordinary gh process is terminated at its selected deadline instead of holding the repair worker open. Per-call worker settings now take effect. Healthy commands preserve stdout, and ordinary command failures remain failures.

The deadline applies to each command attempt. Longer legitimate calls may require a larger configured budget. This does not bound the entire retry loop or add process-tree supervision; it retains Node's normal SIGTERM behavior.

OpenClaw Bay Impact

Unaffected: this changes worker-side process execution, not Bay's observer-only surface or any queue/status data shape.

Documentation Impact

Updated the active docs/repair/README.md reference with the shared-helper default, environment floor, and per-call precedence. Added a changelog entry with credit to @SebTardif.

Evidence

The exact candidate tree 979970625147762dd98f60191387a8ce51724f2f (commit acb5b36df1e964e024fbe43462e11fe974599530) passed pnpm run check: 4,151 tests passed, eight skipped, and all 13 static checks passed. The focused suite passed six tests. Codex pre-commit review found no actionable P0 findings.

The controlled proof uses the real /usr/bin/gh against a local HTTP fixture, through the compiled production helpers. It supplies only synthetic auth in an isolated home and config directory. It does not replace child-process execution with a mock.

Real Behavior Proof

  • Environment: secretless AWS Crabbox cbx_500a9817fb0b, Linux, Node 24.18.1, GitHub CLI 2.46.0 from the image, pinned pnpm 11.10.0. No instance role, Tailscale, hydration, or live GitHub credentials.
  • Original proposal: fbde9401b4148d95636ad895472f23b81677d49b stayed pending past 31.5 seconds with a per-call 30-second GH budget and an ambient 90-second value. The separate watchdog stopped that deliberately stuck driver.
  • Current-main baseline: 1b9086615d892ecc7c1fd4b681e8a1b1208dfa5c ignored a 250 ms explicit budget and was still pending at the 1.2-second watchdog.
  • Candidate: ghText, ghTextAsync, and ghSpawn terminated stalled real CLI requests in 254, 256, and 254 ms with an explicit 250 ms budget. Per-call GH and network settings terminated them in 30,012 and 30,011 ms despite ambient 90-second values. Every timed-out connection closed. Healthy requests after each helper returned synthetic-user, and a normal HTTP error retained native exit status 1.
  • Commands: pnpm run build:all; node /tmp/clawsweeper-github-cli-proof.mjs run "$PWD" original-env on the original PR; baseline on the pinned main; candidate after the patch; node --test test/repair/github-cli.test.ts; pnpm run check.
  • Limits: controlled local HTTP failures with a real CLI, not a live GitHub outage. No public mutations occurred. Windows and children that resist SIGTERM are not claimed. Node's native timeout semantics remain the contract.

The regression suite also checks merged-environment selection, the configured minimum, explicit precedence, invalid values, fractional rounding, and successful output. Environment-budget proof uses real 30-second timers without compression.

Standalone proof driver

Save as /tmp/clawsweeper-github-cli-proof.mjs and run against a built checkout on Linux with /usr/bin/gh installed.

import assert from 'node:assert/strict';
import { fork } from 'node:child_process';
import fs from 'node:fs/promises';
import http from 'node:http';
import os from 'node:os';
import path from 'node:path';
import { fileURLToPath, pathToFileURL } from 'node:url';
import { once } from 'node:events';
import { setTimeout as delay } from 'node:timers/promises';
const self = fileURLToPath(import.meta.url);
const [mode, checkout, variant] = process.argv.slice(2);
if (mode === 'fixture') {
  const server = http.createServer((req,res) => {
    process.send({event:'request',path:req.url});
    res.on('close',()=>{if(!res.writableFinished)process.send({event:'aborted',path:req.url})});
    if(req.url === '/hang')return;
    res.writeHead(req.url === '/error' ? 503 : 200, {'content-type':'application/json'});
    res.end(JSON.stringify(req.url === '/error' ? {message:'synthetic failure'} : {login:'synthetic-user'}));
  });
  server.listen(0,'127.0.0.1',()=>process.send({event:'ready',port:server.address().port}));
} else if (mode === 'call') {
  const [method, optionsJson, endpoint] = process.argv.slice(4);
  const api = await import(pathToFileURL(path.join(checkout,'dist/repair/github-cli.js')));
  const options = {...JSON.parse(optionsJson),cwd:checkout};
  const started = performance.now();
  let result;
  try {
    const value = await api[method](['api',endpoint,'--jq','.login'],options);
    result = method === 'ghSpawn' ? {status:value.status,stdout:String(value.stdout).trim(),code:value.error?.code,signal:value.signal} : {stdout:value};
  } catch (error) {
    result = {code:error.code??null,status:error.status??null,killed:error.killed===true,signal:error.signal??null};
  }
  process.send({...result,elapsedMs:Math.round(performance.now()-started)},()=>process.exit(0));
} else {
  assert.equal(mode,'run');assert.ok(['baseline','original-env','candidate','merged'].includes(variant));
  const scratch = await fs.mkdtemp(path.join(os.tmpdir(),'gh-deadline-'));
  const env={PATH:process.env.PATH,HOME:scratch,GH_CONFIG_DIR:path.join(scratch,'gh'),GH_BIN:'/usr/bin/gh',GH_BIN_ARGS:'[]',GH_TOKEN:'synthetic-fixture-token',GH_PROMPT_DISABLED:'1',NO_COLOR:'1'};
  const fixture=fork(self,['fixture'],{env,stdio:['ignore','ignore','ignore','ipc']});
  let requests=0,aborted=0;
  fixture.on('message',m=>{if(m.event==='request')requests++;if(m.event==='aborted')aborted++});
  const [{port}]=await once(fixture,'message');
  const evidence={variant,node:process.version,gh:'/usr/bin/gh',cases:[]};
  const groups=[];
  async function call(method,options,route,ambient={},watchdog=5000,expectWatchdog=false) {
    const beforeRequests=requests,beforeAborted=aborted;
    const child=fork(self,['call',checkout,method,JSON.stringify(options),`http://127.0.0.1:${port}${route}`],{env:{...env,...ambient},detached:true,stdio:['ignore','ignore','pipe','ipc']});
    groups.push(child.pid);let stderr='';child.stderr.on('data',b=>stderr+=b);
    const result=await new Promise((resolve,reject)=>{
      const timer=setTimeout(()=>{
        try{process.kill(-child.pid,'SIGKILL')}catch{}
        expectWatchdog ? resolve({watchdog:true}) : reject(new Error(`external watchdog for ${method}: ${stderr}`));
      },watchdog);
      child.once('message',m=>{clearTimeout(timer);resolve(m)});
      child.once('error',e=>{clearTimeout(timer);reject(e)});
      child.once('exit',(code,signal)=>{if(code && signal!=='SIGKILL'){clearTimeout(timer);reject(new Error(`driver failed: ${stderr}`))}});
    });
    assert.equal(requests,beforeRequests+1,'real gh must reach the HTTP fixture exactly once');
    if(route==='/hang') {
      for(let i=0;i<80&&aborted===beforeAborted;i++)await delay(25);
      assert.equal(aborted,beforeAborted+1,'the upstream connection must close');
    }
    return {...result,upstreamClosed:route==='/hang'?true:undefined};
  }
  try {
    assert.equal((await call('ghText',{timeoutMs:2000},'/ok')).stdout,'synthetic-user');
    if(variant==='baseline') {
      const result=await call('ghText',{timeoutMs:250},'/hang',{},1200,true);
      assert.equal(result.watchdog,true);evidence.cases.push({method:'ghText',expectedMs:250,stillPendingAtMs:1200,...result});
    } else if(variant==='original-env') {
      const result=await call('ghTextAsync',{env:{CLAWSWEEPER_GH_COMMAND_TIMEOUT_MS:'30000'}},'/hang',{CLAWSWEEPER_GH_COMMAND_TIMEOUT_MS:'90000'},31500,true);
      assert.equal(result.watchdog,true);evidence.cases.push({case:'per-call-gh-env',expectedMs:30000,stillPendingAtMs:31500,...result});
    } else {
      for(const method of ['ghText','ghTextAsync','ghSpawn']) {
        const result=await call(method,{timeoutMs:250},'/hang');
        assert.ok(result.code==='ETIMEDOUT'||(result.killed&&result.signal==='SIGTERM'));
        assert.ok(result.elapsedMs>=200&&result.elapsedMs<2000);
        evidence.cases.push({method,...result});
        assert.equal((await call(method,{timeoutMs:2000},'/ok')).stdout,'synthetic-user');
      }
      for(const variable of ['CLAWSWEEPER_GH_COMMAND_TIMEOUT_MS','CLAWSWEEPER_NETWORK_COMMAND_TIMEOUT_MS']) {
        const result=await call('ghTextAsync',{env:{[variable]:'30000'}},'/hang',{[variable]:'90000'},35000);
        assert.equal(result.killed,true);assert.equal(result.signal,'SIGTERM');
        assert.ok(result.elapsedMs>=29500&&result.elapsedMs<34000);
        evidence.cases.push({case:variable,...result});
      }
      assert.equal((await call('ghText',{timeoutMs:2000},'/error')).status,1);
      evidence.cases.push({case:'http-error',status:1},{case:'healthy-after-each-helper',stdout:'synthetic-user'});
    }
    console.log(JSON.stringify(evidence));
  } finally {
    for(const pid of groups){try{process.kill(-pid,'SIGKILL')}catch{}}
    fixture.kill('SIGTERM');await once(fixture,'exit');
    await fs.rm(scratch,{recursive:true,force:true});
  }
}

@clawsweeper

clawsweeper Bot commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. labels Aug 30, 2026
@clawsweeper

clawsweeper Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed August 31, 2026, 3:23 AM ET / 07:23 UTC.

ClawSweeper review

What this changes

The PR gives repair-lane GitHub CLI calls native per-command deadlines, honors per-call timeout environments, and adds focused regression coverage and documentation.

Regression provenance

Possible regression — probable (reviewed change; reproduction). No predecessor PR is attributed.

Merge readiness

⚠️ Ready for maintainer review - 2 items remain

The prior per-call environment finding is resolved: all three shared helpers now derive their deadline from the same merged environment passed to the child process. The patch is correct and has strong controlled real-process proof; it remains open for normal maintainer landing review.

Priority: P2
Reviewed head: acb5b36df1e964e024fbe43462e11fe974599530

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) Strong real-process proof and focused coverage support a correct, narrowly scoped reliability repair; the intentional new default timeout remains the primary operational tradeoff.
Proof confidence 🦞 diamond lobster (5/6) Sufficient (terminal): The changed production owner is the shared repair GitHub CLI helper; the supplied Crabbox evidence runs compiled helpers through real /usr/bin/gh against a controlled HTTP fixture, shows current-main failure to honor a 250 ms deadline, and reports candidate termination, closed connections, healthy output, and preserved HTTP-error behavior. The fixture is controlled rather than a live GitHub outage, as disclosed.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (terminal): The changed production owner is the shared repair GitHub CLI helper; the supplied Crabbox evidence runs compiled helpers through real /usr/bin/gh against a controlled HTTP fixture, shows current-main failure to honor a 250 ms deadline, and reports candidate termination, closed connections, healthy output, and preserved HTTP-error behavior. The fixture is controlled rather than a live GitHub outage, as disclosed.
Evidence reviewed 7 items Introduced timeout implementation: The PR passes the selected timeout into synchronous, asynchronous, and spawn-based GitHub CLI execution paths.
Per-call environment fix: The resolver reads the merged child environment, so an options.env override takes precedence over the ambient process value while preserving the documented fallback and floor.
Focused coverage: Tests exercise timeout termination and successful output for all three helpers, plus ambient, per-call, fallback, invalid, and fractional timeout selection.
Findings None None.
Security None None.

How this fits together

Repair workers use shared GitHub CLI helpers to query and update GitHub during bounded repair jobs. These helpers combine worker-provided environment settings, invoke the CLI, and return either output or native process failure details to the calling worker.

flowchart LR
  A[Repair worker] --> B[Per-call settings]
  B --> C[GitHub CLI helpers]
  C --> D[Deadline selection]
  D --> E[GitHub CLI process]
  E --> F[Output or timeout error]
Loading

Before merge

  • Resolve merge risk (P1) - A healthy GitHub CLI operation exceeding the new two-minute default will now terminate unless its worker supplies a larger environment budget; explicit per-call budgets remain available for exceptional operations.
  • Complete next step (P2) - No discrete automated repair remains; proceed through ordinary exact-head maintainer and check review.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Production versus test delta production +20, tests +72, docs/release notes +5 The small shared-helper change is accompanied by materially broader focused coverage of timeout behavior and selection precedence.

Merge-risk options

Maintainer options:

  1. Retain the bounded default (recommended)
    Accept the documented two-minute default and use the existing per-call or environment budget for known long-running repair operations.

Technical review

Best possible solution:

Land the bounded-helper behavior with the documented environment override retained for legitimately long GitHub CLI operations.

Do we have a high-confidence way to reproduce the issue?

Yes. The supplied controlled proof exercises the compiled helpers with real GitHub CLI requests against a local HTTP fixture, and current source confirms the timeout path used by each helper.

Is this the best way to solve the issue?

Yes. Passing Node's native timeout through the existing helper boundary is the narrowest solution, and deriving it from the merged child environment resolves the prior override bug without adding a separate process supervisor.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 1b9086615d89.

Labels

Label changes:

  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The changed production owner is the shared repair GitHub CLI helper; the supplied Crabbox evidence runs compiled helpers through real /usr/bin/gh against a controlled HTTP fixture, shows current-main failure to honor a 250 ms deadline, and reports candidate termination, closed connections, healthy output, and preserved HTTP-error behavior. The fixture is controlled rather than a live GitHub outage, as disclosed.
  • remove status: ⏳ waiting on author: Current PR status label is status: 👀 ready for maintainer look.
  • remove rating: 🦐 gold shrimp: Current PR rating is rating: 🐚 platinum hermit, so this older rating label is no longer current.

Label justifications:

  • P2: This is a bounded repair-worker reliability fix without evidence of an active user-facing outage.
  • merge-risk: 🚨 availability: The new default intentionally converts unusually long GitHub CLI calls from unbounded waits into timeout failures.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🦞 diamond lobster and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The changed production owner is the shared repair GitHub CLI helper; the supplied Crabbox evidence runs compiled helpers through real /usr/bin/gh against a controlled HTTP fixture, shows current-main failure to honor a 250 ms deadline, and reports candidate termination, closed connections, healthy output, and preserved HTTP-error behavior. The fixture is controlled rather than a live GitHub outage, as disclosed.
  • proof: sufficient: Contributor real behavior proof is sufficient. The changed production owner is the shared repair GitHub CLI helper; the supplied Crabbox evidence runs compiled helpers through real /usr/bin/gh against a controlled HTTP fixture, shows current-main failure to honor a 250 ms deadline, and reports candidate termination, closed connections, healthy output, and preserved HTTP-error behavior. The fixture is controlled rather than a live GitHub outage, as disclosed.

Evidence

What I checked:

  • Introduced timeout implementation: The PR passes the selected timeout into synchronous, asynchronous, and spawn-based GitHub CLI execution paths. (src/repair/github-cli.ts:168, acb5b36df1e9)
  • Per-call environment fix: The resolver reads the merged child environment, so an options.env override takes precedence over the ambient process value while preserving the documented fallback and floor. (src/repair/github-cli.ts:383, acb5b36df1e9)
  • Focused coverage: Tests exercise timeout termination and successful output for all three helpers, plus ambient, per-call, fallback, invalid, and fractional timeout selection. (test/repair/github-cli.test.ts:191, acb5b36df1e9)
  • Prior finding addressed: The previous review required timeout variables supplied through options.env to be honored; the new merged-environment resolver directly covers that path. (src/repair/github-cli.ts:392, acb5b36df1e9)
  • Feature history: The repair GitHub CLI helper was introduced in the repair-lane unification commit; the current timeout resolver was added by the current commit, whose recorded parent is the reviewed main SHA. (src/repair/github-cli.ts:383, 62dd779eb155)
  • Real behavior evidence: The PR body reports a controlled Crabbox run through compiled production helpers and real /usr/bin/gh: current main remained pending beyond a 1.2-second watchdog at a 250 ms budget, while the candidate terminated all three helpers in roughly 254–256 ms, closed each fixture connection, and retained healthy-output and HTTP-error behavior. (acb5b36df1e9)

Likely related people:

  • unknown: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-08-30T00:01:15.597Z sha fbde940 :: needs changes before merge. :: [P2] Honor timeout variables supplied through options.env

Use the child environment for timeout selection and preserve native process failure semantics. Keep fractional explicit budgets positive after rounding.

Co-authored-by: Sebastien Tardif <sebtardif@ncf.ca>
@steipete
steipete force-pushed the fix/f005-gh-cli-timeout branch from fbde940 to acb5b36 Compare August 31, 2026 07:19
@steipete steipete changed the title fix: timeout GitHub CLI calls in repair github-cli fix(repair): stop stalled GitHub CLI requests blocking workers Aug 31, 2026
@steipete

Copy link
Copy Markdown
Contributor

@clawsweeper re-review

The per-call environment finding is fixed: timeout selection now uses the same merged environment as the child. The updated PR body includes a real GitHub CLI before/after proof for that exact failure and both environment settings, plus normal-output and HTTP-error controls. The exact candidate passed the full check (4,151 passed, eight skipped) and both required Codex reviews were clean at the P0 scope. Hosted CI is running on the updated head.

@clawsweeper

clawsweeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. and removed status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Aug 31, 2026
@steipete
steipete merged commit 6224135 into openclaw:main Aug 31, 2026
22 checks passed
@steipete

Copy link
Copy Markdown
Contributor

Landed as 6224135. Thanks @SebTardif; contributor authorship is preserved. The per-call environment finding is resolved, and the documented two-minute default with per-call/environment overrides is accepted.

Validation on secretless AWS Crabbox cbx_500a9817fb0b (Linux, Node 24.18.1, GitHub CLI 2.46.0, pnpm 11.10.0): the focused suite passed six tests; pnpm run check passed 4,151 tests with eight skips and all 13 static checks. Both required Codex reviews found no actionable P0 findings. Hosted CI passed, CodeQL passed, and the current hosted review accepts the proof with no actionable findings.

After merging, I pulled clean main, rebuilt, verified the merged tree exactly matched the tested candidate, and ran node /tmp/clawsweeper-github-cli-proof.mjs run "$PWD" merged using the driver in the PR body. All three helpers terminated stalled real /usr/bin/gh requests in 254–256 ms with a 250 ms explicit budget. Both per-call environment settings took effect at 30,011 ms despite ambient 90-second values. Connections closed, subsequent healthy requests preserved stdout, and an ordinary HTTP error retained exit status 1.

The original proposal's environment case stayed pending at the 31.5-second watchdog; unmodified main also ignored a short explicit budget. The first candidate proof attempt used the wrong Node error field for its HTTP-error assertion; the driver was corrected to read status, then the full proof passed. Production code did not need a change for that assertion.

This is controlled HTTP failure injection using the real CLI and synthetic auth, not a live GitHub outage. It retains Node's native SIGTERM behavior and does not claim Windows process-tree coverage or a total retry-loop deadline. The isolated checkout ended clean on main.

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

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. P2 Normal priority bug or improvement with limited blast radius. proof: sufficient Contributor real behavior proof is sufficient. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants