Skip to content

fix(onboard): clean up build context temp dir on sandbox creation failure - #375

Merged
cv merged 3 commits into
NVIDIA:mainfrom
futhgar:fix/onboard-build-context-cleanup
Apr 22, 2026
Merged

fix(onboard): clean up build context temp dir on sandbox creation failure#375
cv merged 3 commits into
NVIDIA:mainfrom
futhgar:fix/onboard-build-context-cleanup

Conversation

@futhgar

@futhgar futhgar commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Summary

The build context temp directory (/tmp/nemoclaw-build-*) contains the Dockerfile, NemoClaw source code, blueprint policies, and scripts. If openshell sandbox create fails during onboarding, run() calls process.exit() which bypasses try/finally blocks, leaving the temp directory on disk permanently.

On multi-user systems (e.g., DGX Spark), this leaks project files into the world-readable /tmp.

Fix

Register a process.on('exit') handler immediately after creating the temp directory. This handler fires even when process.exit() is called, guaranteeing cleanup regardless of how the function exits. On success, the handler is explicitly deregistered after cleanup.

Key insight: process.exit() skips try/finally blocks but does execute process.on('exit') handlers synchronously before termination.

Changes

File Change
bin/lib/onboard.js Register exit handler for build context cleanup; replace run("rm -rf ...") with fs.rmSync() + handler deregistration
test/onboard-build-cleanup.test.js 2 tests: verify cleanup on process.exit(1) (failure path) and on success with handler deregistration

Test plan

  • npm test — 20/20 core tests pass (no regressions)
  • New test: process.exit(1) mid-build → temp dir removed by exit handler
  • New test: success path → temp dir removed, exit handler deregistered, 0 leaked listeners
  • Manual verification: node -e simulation confirms /tmp/nemoclaw-build-* is cleaned up even on process.exit(1)

Summary by CodeRabbit

  • Bug Fixes

    • Ensures temporary build directories are reliably removed by adding a process-exit cleanup handler and removing it after successful completion, so cleanup runs on both normal and abrupt termination.
  • Tests

    • Added integration tests that simulate abrupt exits and normal completion to validate the cleanup handler runs and is properly deregistered when appropriate.

Signed-off-by: Josue Balandrano Coronel josuebc@pm.me

@coderabbitai

coderabbitai Bot commented Mar 19, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Replace unconditional post-staging deletion with a dedicated cleanupBuildCtx() registered on process.on("exit"); wrap staging and sandbox creation in try/finally to run cleanup and remove the listener on success; adjust failure paths to rely on the exit handler. (47 words)

Changes

Cohort / File(s) Summary
Build context cleanup
bin/lib/onboard.js
Add cleanupBuildCtx() using fs.rmSync(buildCtx, { recursive: true, force: true }); register it via process.on("exit", cleanupBuildCtx) before staging; wrap staging + sandbox creation/forwarding in try { ... } finally { cleanupBuildCtx(); process.removeListener("exit", cleanupBuildCtx) }; on sandbox creation failures retain process.exit(...) and rely on the exit handler for cleanup.
Integration tests (subprocess)
test/onboard-build-cleanup.test.js
Add Vitest subprocess tests (3 scenarios) that spawn node -e scripts to verify exit-handler cleanup: (1) exit with process.exit(1) and ensure temp dir removed, (2) explicit inline cleanup + removeListener then exit and assert removed and exit code, (3) failing inline cleanup that leaves the listener registered and ensures the exit handler removes the dir on normal completion.

Sequence Diagram(s)

sequenceDiagram
  participant Onboard as "onboard.js"
  participant Node as "Node Process"
  participant FS as "Filesystem (buildCtx)"
  participant Sandbox as "Sandbox"

  rect rgba(200,220,255,0.5)
  Onboard->>FS: create temporary buildCtx
  Onboard->>Node: register exit handler (cleanupBuildCtx)
  end

  rect rgba(200,255,200,0.5)
  Onboard->>Sandbox: stage files & create sandbox
  Sandbox-->>Onboard: ready / error
  end

  alt error -> process.exit called
    Node->>Node: emit exit
    Node->>FS: cleanupBuildCtx() removes buildCtx
  else success
    Onboard->>FS: call cleanupBuildCtx()
    Onboard->>Node: remove exit handler
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 I nibbled at temp crumbs in moonlit ticks,

I tied an exit ribbon to sweep up the mix,
If chaos struck, I'd tidy every trail,
If all went well, I'd loosen the tail,
Happy burrow — no stray bits to fix.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: implementing cleanup of the build context temp directory when sandbox creation fails, which is the primary security and stability fix in this PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
bin/lib/onboard.js (1)

423-458: Use try/finally in addition to the exit hook for non-exit exceptions.

At Line 423 onward, a thrown exception (e.g., synchronous FS errors) can skip immediate cleanup and leave sensitive temp contents until process exit. Keep the exit hook, but also scope the staging/create block with try/finally for immediate teardown.

♻️ Suggested structure
   const buildCtx = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-build-"));
   const cleanupBuildCtx = () => {
     try { fs.rmSync(buildCtx, { recursive: true, force: true }); } catch {}
   };
   process.on("exit", cleanupBuildCtx);
-
-  fs.copyFileSync(path.join(ROOT, "Dockerfile"), path.join(buildCtx, "Dockerfile"));
-  run(`cp -r "${path.join(ROOT, "nemoclaw")}" "${buildCtx}/nemoclaw"`);
-  run(`cp -r "${path.join(ROOT, "nemoclaw-blueprint")}" "${buildCtx}/nemoclaw-blueprint"`);
-  run(`cp -r "${path.join(ROOT, "scripts")}" "${buildCtx}/scripts"`);
-  run(`rm -rf "${buildCtx}/nemoclaw/node_modules" "${buildCtx}/nemoclaw/src"`, { ignoreError: true });
+  try {
+    fs.copyFileSync(path.join(ROOT, "Dockerfile"), path.join(buildCtx, "Dockerfile"));
+    run(`cp -r "${path.join(ROOT, "nemoclaw")}" "${buildCtx}/nemoclaw"`);
+    run(`cp -r "${path.join(ROOT, "nemoclaw-blueprint")}" "${buildCtx}/nemoclaw-blueprint"`);
+    run(`cp -r "${path.join(ROOT, "scripts")}" "${buildCtx}/scripts"`);
+    run(`rm -rf "${buildCtx}/nemoclaw/node_modules" "${buildCtx}/nemoclaw/src"`, { ignoreError: true });
+
+    // ... sandbox create + forwarding steps ...
+  } finally {
+    cleanupBuildCtx();
+    process.removeListener("exit", cleanupBuildCtx);
+  }
-
-  // Clean up build context and deregister the exit handler
-  cleanupBuildCtx();
-  process.removeListener("exit", cleanupBuildCtx);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 423 - 458, The staging and sandbox creation
sequence (the block using run(...), the openshell sandbox create/forward calls
and related temp work that references sandboxName and uses run) must be wrapped
in a try/finally so cleanupBuildCtx() and process.removeListener("exit",
cleanupBuildCtx) run immediately on any thrown exception (not only at process
exit); keep the existing process.on("exit", cleanupBuildCtx) hook but surround
the code that copies files, builds createArgs/envArgs, calls run(`openshell
sandbox create ...`) and the forward start/stop calls with try { /* existing
code */ } finally { cleanupBuildCtx(); process.removeListener("exit",
cleanupBuildCtx); } so temporary files are removed deterministically even for
synchronous errors.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/onboard-build-cleanup.test.js`:
- Around line 64-66: Replace the fragile listener count check with a direct
containment check: call process.listeners("exit") and assert it does not include
the cleanup handler (the cleanup function referenced in the test), i.e., verify
!process.listeners("exit").includes(cleanup) instead of comparing
process.listenerCount("exit") > 0; update the test assertion logic around the
existing cleanup reference to ensure it specifically asserts the cleanup
function was deregistered.

---

Nitpick comments:
In `@bin/lib/onboard.js`:
- Around line 423-458: The staging and sandbox creation sequence (the block
using run(...), the openshell sandbox create/forward calls and related temp work
that references sandboxName and uses run) must be wrapped in a try/finally so
cleanupBuildCtx() and process.removeListener("exit", cleanupBuildCtx) run
immediately on any thrown exception (not only at process exit); keep the
existing process.on("exit", cleanupBuildCtx) hook but surround the code that
copies files, builds createArgs/envArgs, calls run(`openshell sandbox create
...`) and the forward start/stop calls with try { /* existing code */ } finally
{ cleanupBuildCtx(); process.removeListener("exit", cleanupBuildCtx); } so
temporary files are removed deterministically even for synchronous errors.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2113ebd5-afa0-40d1-bb6b-f5997b7e6982

📥 Commits

Reviewing files that changed from the base of the PR and between 9513eca and bbc2d91.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard-build-cleanup.test.js

Comment thread test/onboard-build-cleanup.test.js Outdated

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem is real — run() calling process.exit() bypasses try/finally, leaving build context with source code and potentially credentials in /tmp. The process.on('exit') approach is the correct fix for this.

Stale against main

The createSandbox function has changed since this PR was written. Main now uses streamSandboxCreate() instead of run() with the awk pipe, and the env args section has been restructured (shellQuote added, Discord/Slack tokens added). The PR will conflict on merge.

The fix itself is sound

process.on('exit', cleanupBuildCtx) fires even on process.exit() calls — this is the right pattern. The finally block provides cleanup on normal flow, and the exit handler catches the process.exit() path. Deregistering the handler after successful cleanup prevents double-cleanup.

Test uses node:test instead of vitest

The test file (test/onboard-build-cleanup.test.js) uses require("node:test") and require("node:assert/strict"). The repo has migrated to vitest — this will fail in CI with "No test suite found" (same issue we hit on other PRs). Needs conversion to vitest with import { describe, it, expect } from "vitest".

The behavioral tests are good

Spawning a child process that registers the exit handler and then process.exit(1) is the right way to test this — it validates actual process.exit() behavior rather than matching source patterns.

Rebase onto current main and convert the test to vitest, and this is ready to merge.

@futhgar
futhgar force-pushed the fix/onboard-build-context-cleanup branch from 10b5a2a to 0aff7d6 Compare March 24, 2026 11:22
@futhgar

futhgar commented Mar 24, 2026

Copy link
Copy Markdown
Contributor Author

Thanks @cv — rebased onto current main and addressed both points:

  1. Rebased against main: Adapted to streamSandboxCreate(), shellQuote(), Discord/Slack tokens, and the ready-wait logic. The try/finally + process.on('exit') pattern wraps the full staging+creation block.

  2. Converted test to vitest: test/onboard-build-cleanup.test.js now uses import { describe, it, expect } from "vitest" — both tests pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
bin/lib/onboard.js (1)

548-636: Clean the build context immediately after a successful create.

After streamSandboxCreate() returns 0, the readiness poll and port-forward setup no longer use buildCtx. Keeping the temp tree around until the outer finally extends the on-disk exposure window by up to ~60s on the success path.

♻️ Suggested reshape
-  try {
+  try {
     fs.copyFileSync(path.join(ROOT, "Dockerfile"), path.join(buildCtx, "Dockerfile"));
     run(`cp -r "${path.join(ROOT, "nemoclaw")}" "${buildCtx}/nemoclaw"`);
     run(`cp -r "${path.join(ROOT, "nemoclaw-blueprint")}" "${buildCtx}/nemoclaw-blueprint"`);
     run(`cp -r "${path.join(ROOT, "scripts")}" "${buildCtx}/scripts"`);
     run(`rm -rf "${buildCtx}/nemoclaw/node_modules"`, { ignoreError: true });
@@
     if (createResult.status !== 0) {
       console.error("");
       console.error(`  Sandbox creation failed (exit ${createResult.status}).`);
       if (createResult.output) {
         console.error("");
         console.error(createResult.output);
       }
       console.error("  Try:  openshell sandbox list        # check gateway state");
       console.error("  Try:  nemoclaw onboard              # retry from scratch");
       process.exit(createResult.status || 1);
     }
+  } finally {
+    cleanupBuildCtx();
+    process.removeListener("exit", cleanupBuildCtx);
+  }
 
-    // Wait for sandbox to reach Ready state in k3s before registering.
+  // Wait for sandbox to reach Ready state in k3s before registering.
     console.log("  Waiting for sandbox to become ready...");
     let ready = false;
     for (let i = 0; i < 30; i++) {
       const list = runCapture("openshell sandbox list 2>&1", { ignoreError: true });
       if (isSandboxReady(list, sandboxName)) {
         ready = true;
         break;
       }
       require("child_process").spawnSync("sleep", ["2"]);
     }
@@
-    run(`openshell forward start --background 18789 "${sandboxName}"`, { ignoreError: true });
-  } finally {
-    cleanupBuildCtx();
-    process.removeListener("exit", cleanupBuildCtx);
-  }
+  run(`openshell forward start --background 18789 "${sandboxName}"`, { ignoreError: true });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@bin/lib/onboard.js` around lines 548 - 636, The build context (buildCtx) is
only cleaned in the outer finally, leaving temporary files on disk until
readiness polling finishes; after streamSandboxCreate() returns success you
should immediately call cleanupBuildCtx() and remove the exit listener so the
temp tree is removed early — modify the block after checking createResult.status
=== 0 (i.e., after the const createResult = await streamSandboxCreate(...) and
the failure branch) to invoke cleanupBuildCtx() and
process.removeListener("exit", cleanupBuildCtx) before proceeding to
isSandboxReady polling and port-forward setup so buildCtx is removed on the
successful path.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@bin/lib/onboard.js`:
- Around line 548-636: The build context (buildCtx) is only cleaned in the outer
finally, leaving temporary files on disk until readiness polling finishes; after
streamSandboxCreate() returns success you should immediately call
cleanupBuildCtx() and remove the exit listener so the temp tree is removed early
— modify the block after checking createResult.status === 0 (i.e., after the
const createResult = await streamSandboxCreate(...) and the failure branch) to
invoke cleanupBuildCtx() and process.removeListener("exit", cleanupBuildCtx)
before proceeding to isSandboxReady polling and port-forward setup so buildCtx
is removed on the successful path.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55f158a3-99c5-442d-9844-9f2298e22dab

📥 Commits

Reviewing files that changed from the base of the PR and between 10b5a2a and 0aff7d6.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard-build-cleanup.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/onboard-build-cleanup.test.js

@drobison00 drobison00 self-assigned this Mar 24, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bin/lib/onboard.js`:
- Around line 1769-1772: The inline cleanup function cleanupBuildCtx currently
swallows fs.rmSync errors and the exit handler is unconditionally removed later;
change cleanupBuildCtx to return a boolean indicating success (true when
fs.rmSync deletes the buildCtx, false when it fails) and log the caught error
instead of silencing it, then only call process.removeListener/removeHandler for
the "exit" event when cleanupBuildCtx() returns true so the fallback exit
handler remains registered after an inline failure; update all places where the
listener is deregistered (the code that calls process.off/process.removeListener
with cleanupBuildCtx) to first invoke cleanupBuildCtx and conditionally remove
the listener on success.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 55da4595-1ec3-42f8-a850-c4486e0921c3

📥 Commits

Reviewing files that changed from the base of the PR and between 0aff7d6 and de0c943.

📒 Files selected for processing (2)
  • bin/lib/onboard.js
  • test/onboard-build-cleanup.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • test/onboard-build-cleanup.test.js

Comment thread src/lib/onboard.ts Outdated
mafueee pushed a commit to mafueee/NemoClaw that referenced this pull request Mar 28, 2026
chore: add vouch system for first-time contributors
@wscurran wscurran mentioned this pull request Mar 30, 2026
2 tasks
@futhgar
futhgar force-pushed the fix/onboard-build-context-cleanup branch from 6d575aa to 8608fc9 Compare April 1, 2026 02:11
@futhgar

futhgar commented Apr 1, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main and reworked the fix to integrate with the upstream changes.

Upstream already added run('rm -rf ...') after streamSandboxCreate, but run() calls process.exit() on failure which bypasses that cleanup line. This update:

  1. Registers a process.on("exit") handler immediately after mkdtempSync — guarantees cleanup even when process.exit() is called
  2. Replaces the shell rm -rf with fs.rmSync on the normal path (avoids spawning a process for cleanup)
  3. Deregisters the exit handler after successful cleanup

Tests updated to match.

@futhgar
futhgar force-pushed the fix/onboard-build-context-cleanup branch from ae3697d to a2e488c Compare April 14, 2026 01:45
@futhgar

futhgar commented Apr 14, 2026

Copy link
Copy Markdown
Contributor Author

Rebased against main and ported to TypeScript per review feedback. The test file was also renamed from .js to .ts for consistency with the migration.

@cv cv added v0.0.21 and removed v0.0.20 labels Apr 20, 2026
@wscurran

Copy link
Copy Markdown
Contributor

We really appreciate the rebase and TypeScript port on April 14 — that was a significant effort and we're sorry to ask again. New conflicts have developed against main since then, so one more rebase pass is needed before we can merge.

Note: this is from the same contributor as #380. A joint rebase on both at once would be ideal if you have time.

We're actively scaling our response time and will be watching for your update to turn this around quickly. Thanks for your patience and continued contribution to NemoClaw!

If we don't hear back within 7 days, we'll post a reminder; items with no response at 14 days are closed to keep the queue healthy.

The build context temp dir contains source code and potentially API keys
in env args. The existing rm -rf cleanup after streamSandboxCreate is
bypassed when run() calls process.exit() on command failure.

Register a process 'exit' handler immediately after creating the temp dir
to guarantee cleanup in all exit paths. On the normal path, clean up
explicitly with fs.rmSync (avoids spawning a shell) and deregister the
handler. The exit handler remains as a safety net for early process.exit()
calls.
@futhgar

futhgar commented Apr 21, 2026

Copy link
Copy Markdown
Contributor Author

@wscurran — rebased against current main. The only real conflict was the cleanup-path around the sandbox create call, which I resolved in favor of the fs.rmSync helper. While at it, I also addressed the outstanding CodeRabbit P1 from 2026-03-28 ("Don't drop the fallback cleanup after a failed rmSync"): cleanupBuildCtx now returns a boolean, and we only deregister the process exit safety-net when inline cleanup succeeded. Added a regression test for that path. Ready for another look.

@prekshivyas

Copy link
Copy Markdown
Collaborator

@futhgar can you pls resolve conflicts !

The "uses the custom Dockerfile parent directory as build context when
--from is given" test asserted that extra.txt existed in the staged
dir after createSandbox returned. That held only because the old
cleanup path spawned `rm -rf` via runner.run, which this test mocks
into a no-op.

The preceding commit replaces that cleanup with an inline fs.rmSync
(unmocked), so the staged dir is gone by the time the outer async
block inspects it. Move the existence check into the mocked spawn
so it captures the dir state while sandbox create is still in flight,
which is the window the test was actually trying to verify.

No runtime behavior change; test-only.
@futhgar
futhgar force-pushed the fix/onboard-build-context-cleanup branch from a2e488c to a604231 Compare April 22, 2026 01:03
@futhgar

futhgar commented Apr 22, 2026

Copy link
Copy Markdown
Contributor Author

@prekshivyas rebased onto latest main, conflicts resolved.

One note on the update: the existing test/onboard.test.ts case "uses the custom Dockerfile parent directory as build context when --from is given" (landed after this PR was opened) asserted that extra.txt existed in the staged build-context dir after createSandbox returned. That only held because the old cleanup path spawned rm -rf through runner.run, which the test mocks into a no-op.

This PR replaces that cleanup with an inline fs.rmSync (not mocked), so the staged dir is gone by the time the outer assertion runs. Added a second commit (test(onboard): observe staged build context before inline cleanup) that moves the existence check into the mocked childProcess.spawn, so it observes the staged dir while the sandbox create is in flight — which is the window that test was actually trying to verify. No runtime behavior change; test-only adaptation to the new cleanup ordering.

Verified locally:

  • test/onboard-build-cleanup.test.ts (this PR's tests): 3/3 pass
  • test/onboard.test.ts, test/onboard-readiness.test.ts, test/onboard-selection.test.ts: 195/195 pass
  • tsc -p tsconfig.src.json: clean

@cv cv added v0.0.23 and removed v0.0.22 labels Apr 22, 2026

@cv cv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the rebase and for following through on the cleanup hardening.

I re-checked the risky bits in src/lib/onboard.ts: the build context now stays protected by an exit handler when inline fs.rmSync() cleanup fails, and the regression tests cover both the process.exit() failure path and the updated custom-Dockerfile staging timing. CI is green, and the remaining CodeRabbit concern is satisfied by the current boolean-return cleanup path.

Approved for merge once the normal branch protections are satisfied.

@cv cv removed the status: rebase label Apr 22, 2026
@cv
cv merged commit d24bf3b into NVIDIA:main Apr 22, 2026
15 checks passed
@wscurran wscurran added area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression and removed priority: high labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: cli Command line interface, flags, terminal UX, or output bug-fix PR fixes a bug or regression

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants