diff --git a/AGENTS.md b/AGENTS.md index ae23985b4b8..86c7c0a749b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -45,6 +45,7 @@ Package-specific guides: | Build plugin | `cd nemoclaw && npm run build` | | Watch mode | `cd nemoclaw && npm run dev` | | Run all tests | `npm test` | +| Render behavior-oriented test tree | `npm run test:spec` | | Run fast source tests | `npm run test:fast` | | Run integration tests | `npm run test:integration` | | Run package contracts | `npm run test:package` | @@ -87,6 +88,7 @@ When writing tests: - Plugin tests use TypeScript and are co-located with their source files - Import CLI source from ordinary tests. Put genuine compiled-artifact assertions under `test/package-contract/`. - Keep project globs disjoint; `npm run test:projects:check` derives membership from Vitest and rejects overlap. +- Write behavior-oriented titles, put local issue references in a final `(#1234)` suffix, and use `npm run test:spec` for the hierarchical specification view. - Mock external dependencies; don't call real NVIDIA APIs in unit tests - E2E tests run on ephemeral Brev cloud instances diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 66d15f587f2..1af00b86b46 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -121,6 +121,7 @@ These are the primary `make` and `npm` targets for day-to-day development: | `make format` | Auto-format TypeScript and Python source | | `npm run typecheck:cli` | Type-check CLI TypeScript using `tsconfig.cli.json` (`bin/`, `scripts/`, `src/`, `test/`, `nemoclaw-blueprint/scripts/`) | | `npm test` | Build package artifacts and run every non-live Vitest project | +| `npm run test:spec` | Run every non-live test with hierarchical behavior-oriented output | | `npm run test:fast` | Clean `dist/` and run source CLI, plugin, and E2E-support tests | | `npm run test:integration` | Clean-build the CLI and run root integration and installer tests | | `npm run test:package` | Clean-build CLI/plugin artifacts and run compiled-package contracts | @@ -132,6 +133,18 @@ These are the primary `make` and `npm` targets for day-to-day development: | `npm run docs:deps` | Print the pinned Fern CLI version used by docs commands | | `npx prek run --all-files` | Run all hooks from `.pre-commit-config.yaml` — see below | +### Test Titles as Behavioral Documentation + +Write `describe` and `it` titles so the Vitest tree reads as behavioral documentation. Start test +titles with behavior or context rather than issue numbers, flags, or scenario labels, and put local +issue references in a final suffix such as `(#1234)`. Prefer +`it("reticulates splines correctly (#1234)")` over +`it("#1234 fixes spline reticulation")`. + +Run `npm run test:spec` to render the suite with Vitest's hierarchical tree reporter. Run +`npm run test:titles:check` to enforce the objective title-shape conventions without attempting to +lint subjective English grammar. + ### Git hooks (prek) All git hooks are managed by [prek](https://prek.j178.dev/), a fast, single-binary pre-commit hook runner installed as a devDependency (`@j178/prek`). The `npm install` step runs `prek install` automatically via the `prepare` script, which wires up the following hooks from [`.pre-commit-config.yaml`](.pre-commit-config.yaml): diff --git a/nemoclaw/src/blueprint/ssrf.test.ts b/nemoclaw/src/blueprint/ssrf.test.ts index 5d52b3dd26c..b1db007870e 100644 --- a/nemoclaw/src/blueprint/ssrf.test.ts +++ b/nemoclaw/src/blueprint/ssrf.test.ts @@ -258,7 +258,7 @@ describe("isPrivateIp – CIDR boundary precision", () => { ["128.0.0.0", false], // just above 127.0.0.0/8 ["192.167.255.255", false], // just below 192.168.0.0/16 ["192.169.0.0", false], // just above 192.168.0.0/16 - ])("boundary %s → private=%s", (ip, expected) => { + ])("classifies boundary address %s as private=%s", (ip, expected) => { expect(isPrivateIp(ip)).toBe(expected); }); }); @@ -277,7 +277,7 @@ describe("isPrivateIp – IPv6 edge cases", () => { ["fd00::0", true], // first address in fd00::/8 (within fc00::/7) ["fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", true], // last address in fc00::/7 ULA range ["fe00::1", false], // just above fc00::/7 (link-local starts at fe80::) - ])("IPv6 %s → private=%s", (ip, expected) => { + ])("classifies IPv6 address %s as private=%s", (ip, expected) => { expect(isPrivateIp(ip)).toBe(expected); }); diff --git a/nemoclaw/src/security/secret-scanner.test.ts b/nemoclaw/src/security/secret-scanner.test.ts index f87eef71333..a962049a448 100644 --- a/nemoclaw/src/security/secret-scanner.test.ts +++ b/nemoclaw/src/security/secret-scanner.test.ts @@ -29,132 +29,132 @@ const FAKE = { }; describe("scanForSecrets", () => { - describe("detects known secret patterns", () => { - it("NVIDIA API key", () => { + describe("known secret patterns", () => { + it("detects an NVIDIA API key", () => { const matches = scanForSecrets(`my key is ${FAKE.nvidia}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("NVIDIA API key"); }); - it("OpenAI API key", () => { + it("detects an OpenAI API key", () => { const matches = scanForSecrets(`export OPENAI_API_KEY=${FAKE.openai}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("OpenAI API key"); }); - it("OpenAI project API key", () => { + it("detects an OpenAI project API key", () => { const matches = scanForSecrets(`export OPENAI_API_KEY=${FAKE.openaiProject}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("OpenAI API key"); }); - it("GitHub personal access token", () => { + it("detects a GitHub personal access token", () => { const matches = scanForSecrets(`token: ${FAKE.github}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("GitHub token"); }); - it("AWS access key", () => { + it("detects an AWS access key", () => { const matches = scanForSecrets(`aws_access_key_id = ${FAKE.aws}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("AWS access key"); }); - it("Slack bot token", () => { + it("detects a Slack bot token", () => { const matches = scanForSecrets(`SLACK_TOKEN=${FAKE.slack}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Slack token"); }); - it("Slack app token", () => { + it("detects a Slack app token", () => { const matches = scanForSecrets(`SLACK_APP_TOKEN=${FAKE.slackApp}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Slack token"); }); - it("npm token", () => { + it("detects an npm token", () => { const matches = scanForSecrets(`//registry.npmjs.org/:_authToken=${FAKE.npm}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("npm token"); }); - it("private key (PEM RSA)", () => { + it("detects a PEM RSA private key", () => { const matches = scanForSecrets(FAKE.pemRsa); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Private key"); }); - it("private key (OpenSSH)", () => { + it("detects an OpenSSH private key", () => { const matches = scanForSecrets(FAKE.pemOpenssh); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Private key"); }); - it("Telegram bot token", () => { + it("detects a Telegram bot token", () => { const matches = scanForSecrets(`bot token: ${FAKE.telegram}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Telegram bot token"); }); - it("Google API key", () => { + it("detects a Google API key", () => { const matches = scanForSecrets(`GOOGLE_API_KEY=${FAKE.google}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Google API key"); }); - it("Anthropic API key", () => { + it("detects an Anthropic API key", () => { const matches = scanForSecrets(`key: ${FAKE.anthropic}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Anthropic API key"); }); - it("HuggingFace token", () => { + it("detects a HuggingFace token", () => { const matches = scanForSecrets(`HF_TOKEN=${FAKE.huggingface}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("HuggingFace token"); }); - it("Discord bot token", () => { + it("detects a Discord bot token", () => { const matches = scanForSecrets(`DISCORD_TOKEN=${FAKE.discord}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Discord bot token"); }); - it("AWS secret key", () => { + it("detects an AWS secret key", () => { const matches = scanForSecrets(`aws_secret_access_key = ${FAKE.awsSecret}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("AWS secret key"); }); - it("Authorization header", () => { + it("detects an Authorization header", () => { const matches = scanForSecrets(`Authorization: Bearer ${FAKE.authHeader}`); expect(matches).toHaveLength(1); expect(matches[0].pattern).toBe("Authorization header"); }); }); - describe("does not false-positive on safe content", () => { - it("normal markdown text", () => { + describe("safe content", () => { + it("allows normal markdown text", () => { expect(scanForSecrets("# My Project\n\nThis is a regular markdown file.")).toHaveLength(0); }); - it("code blocks without secrets", () => { + it("allows code blocks without secrets", () => { expect(scanForSecrets("```python\nprint('hello world')\n```")).toHaveLength(0); }); - it("short tokens that don't meet minimum length", () => { + it("ignores short tokens below the minimum length", () => { expect(scanForSecrets("sk-short")).toHaveLength(0); }); - it("URLs with path segments", () => { + it("allows URLs with path segments", () => { expect(scanForSecrets("https://github.com/NVIDIA/NemoClaw/pull/1121")).toHaveLength(0); }); - it("UUIDs", () => { + it("allows UUIDs", () => { expect(scanForSecrets("id: 550e8400-e29b-41d4-a716-446655440000")).toHaveLength(0); }); - it("git commit hashes", () => { + it("allows git commit hashes", () => { expect(scanForSecrets("commit 24a8b5a3f1e2d3c4b5a6f7e8d9c0b1a2")).toHaveLength(0); }); }); diff --git a/package.json b/package.json index e2864b9ace0..6861a9f1bf6 100644 --- a/package.json +++ b/package.json @@ -21,12 +21,14 @@ "scripts": { "preinstall": "node scripts/check-node-version.js", "test": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project cli --project integration --project installer-integration --project package-contract --project plugin --project e2e-vitest-support", + "test:spec": "npm test -- --reporter=tree", "test:fast": "npm run clean:cli && vitest run --project cli --project plugin --project e2e-vitest-support", "test:integration": "npm run clean:cli && npm run build:cli && vitest run --project integration --project installer-integration", "test:package": "npm run clean:cli && npm --prefix nemoclaw run clean && npm run build:cli && npm --prefix nemoclaw run build && vitest run --project package-contract", "test:live-e2e": "NEMOCLAW_RUN_E2E_SCENARIOS=1 vitest run --project e2e-scenarios-live", "test:imports:check": "tsx scripts/checks/no-test-dist-imports.ts", "test:projects:check": "tsx scripts/checks/vitest-project-overlap.ts", + "test:titles:check": "tsx scripts/checks/test-title-style.ts", "check": "npx prek run --all-files", "checks": "tsx scripts/checks/run.ts", "lint": "npx @biomejs/biome lint . && npm run checks", diff --git a/scripts/checks/run.ts b/scripts/checks/run.ts index 67f0eea7873..a7272ad31c4 100644 --- a/scripts/checks/run.ts +++ b/scripts/checks/run.ts @@ -46,6 +46,11 @@ const CHECKS: readonly CheckCommand[] = [ command: TSX, args: ["scripts/checks/vitest-project-overlap.ts"], }, + { + name: "test-title-style", + command: TSX, + args: ["scripts/checks/test-title-style.ts"], + }, ]; function main(): void { diff --git a/scripts/checks/test-title-style.ts b/scripts/checks/test-title-style.ts new file mode 100755 index 00000000000..6bae0dbf086 --- /dev/null +++ b/scripts/checks/test-title-style.ts @@ -0,0 +1,187 @@ +#!/usr/bin/env -S npx tsx +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { existsSync, readdirSync, readFileSync, statSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath, pathToFileURL } from "node:url"; + +import ts from "typescript"; + +export type TestTitleRule = + | "issue-reference-suffix" + | "leading-metadata" + | "placeholder-only" + | "result-arrow"; + +export type TestTitleViolation = { + readonly file: string; + readonly line: number; + readonly column: number; + readonly call: "describe" | "it" | "test"; + readonly title: string; + readonly rule: TestTitleRule; + readonly message: string; +}; + +const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); +const DEFAULT_SCAN_ROOTS = Object.freeze(["src", "test", "nemoclaw/src"]); +const TEST_FILE_PATTERN = /\.(?:test|spec)\.(?:[cm]?[jt]sx?)$/; +const TEST_CALL_NAMES = new Set(["describe", "it", "test"]); +const SKIP_DIRS = new Set([".git", ".venv", "coverage", "dist", "node_modules"]); +const LEADING_METADATA_PATTERN = + /^(?:#\d+\b|issue\s+#?\d+\b|regression\s+#?\d+\b|--\S+|-\w\b|\[[^\]]+\]|scenario\b)/i; +const LOCAL_ISSUE_REFERENCE_PATTERN = /(? `\${…}${span.literal.text}`) + .join("")}`; +} + +function titleRules(title: string): readonly { rule: TestTitleRule; message: string }[] { + const trimmed = title.trim(); + const violations: { rule: TestTitleRule; message: string }[] = []; + + if (LOCAL_ISSUE_REFERENCE_PATTERN.test(trimmed) && !ISSUE_SUFFIX_PATTERN.test(trimmed)) { + violations.push({ + rule: "issue-reference-suffix", + message: "move local issue references to a final '(#1234)' suffix", + }); + } + if (LEADING_METADATA_PATTERN.test(trimmed)) { + violations.push({ + rule: "leading-metadata", + message: "start with behavior or context instead of metadata, flags, or scenario labels", + }); + } + if (PLACEHOLDER_ONLY_PATTERN.test(trimmed)) { + violations.push({ + rule: "placeholder-only", + message: "add behavior around the parameter placeholder", + }); + } + if (/\s→\s/.test(trimmed)) { + violations.push({ + rule: "result-arrow", + message: "describe the expected result as a sentence instead of an input-to-output label", + }); + } + + return violations; +} + +export function scanTestTitleStyle(file: string, source: string): readonly TestTitleViolation[] { + const sourceFile = ts.createSourceFile( + file, + source, + ts.ScriptTarget.Latest, + true, + scriptKindFor(file), + ); + const violations: TestTitleViolation[] = []; + + function visit(node: ts.Node): void { + if (ts.isCallExpression(node)) { + const call = rootCallName(node.expression); + const title = literalTitle(node.arguments[0]); + if (call !== null && TEST_CALL_NAMES.has(call) && title !== null) { + const location = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)); + for (const violation of titleRules(title)) { + violations.push({ + file, + line: location.line + 1, + column: location.character + 1, + call: call as TestTitleViolation["call"], + title, + ...violation, + }); + } + } + } + ts.forEachChild(node, visit); + } + + visit(sourceFile); + return violations; +} + +function isSkipped(absolutePath: string): boolean { + const segments = path.relative(REPO_ROOT, absolutePath).split(path.sep); + return segments.some((segment) => SKIP_DIRS.has(segment)); +} + +function* walkTestFiles(directory: string): Generator { + if (!existsSync(directory) || isSkipped(directory)) return; + + for (const entry of readdirSync(directory)) { + const absolutePath = path.join(directory, entry); + if (isSkipped(absolutePath)) continue; + const stats = statSync(absolutePath); + if (stats.isDirectory()) { + yield* walkTestFiles(absolutePath); + } else if (stats.isFile() && TEST_FILE_PATTERN.test(entry)) { + yield absolutePath; + } + } +} + +export function findTestTitleStyleViolations( + roots: readonly string[] = DEFAULT_SCAN_ROOTS, +): readonly TestTitleViolation[] { + const violations: TestTitleViolation[] = []; + for (const root of roots) { + const absoluteRoot = path.resolve(REPO_ROOT, root); + for (const absolutePath of walkTestFiles(absoluteRoot)) { + const file = path.relative(REPO_ROOT, absolutePath).split(path.sep).join("/"); + violations.push(...scanTestTitleStyle(file, readFileSync(absolutePath, "utf8"))); + } + } + return violations; +} + +function main(): void { + const violations = findTestTitleStyleViolations(); + if (violations.length === 0) { + console.log("Test title style check passed."); + return; + } + + for (const violation of violations) { + console.error( + `${violation.file}:${violation.line}:${violation.column} [${violation.rule}] ${violation.message}: ${JSON.stringify(violation.title)}`, + ); + } + console.error(`Found ${violations.length} test title style violation(s).`); + process.exitCode = 1; +} + +const invokedPath = process.argv[1]; +if ( + invokedPath !== undefined && + import.meta.url === pathToFileURL(path.resolve(invokedPath)).href +) { + main(); +} diff --git a/src/lib/actions/sandbox/agent/passthrough.test.ts b/src/lib/actions/sandbox/agent/passthrough.test.ts index 62e171cda1b..792bf71da96 100644 --- a/src/lib/actions/sandbox/agent/passthrough.test.ts +++ b/src/lib/actions/sandbox/agent/passthrough.test.ts @@ -402,7 +402,7 @@ describe("runAgentPassthrough", () => { expect(exit).toHaveBeenCalledWith(2); }); - it("prints recovery hints with exit 1 before selector rejection when the sandbox phase is non-Ready (covers the literal #5655 stopped-sandbox repro `agent -m ping`)", async () => { + it("prints recovery hints with exit 1 before selector rejection for the literal stopped-sandbox repro `agent -m ping` (#5655)", async () => { ensureLiveMock.mockResolvedValueOnce({ output: "Phase: Error" }); getSandboxMock.mockReturnValueOnce({ agent: "openclaw" }); const { writes, exit, proc } = makeProcMock(); diff --git a/src/lib/actions/sandbox/auto-pair-approval.test.ts b/src/lib/actions/sandbox/auto-pair-approval.test.ts index 83fa1b1eb1d..21e2c17a4c8 100644 --- a/src/lib/actions/sandbox/auto-pair-approval.test.ts +++ b/src/lib/actions/sandbox/auto-pair-approval.test.ts @@ -268,7 +268,7 @@ process.exit(2); } }); - it("does not recover approval failures without the #4462 compatibility signature", () => { + it("does not recover approval failures without the compatibility signature (#4462)", () => { if (spawnSync("sh", ["-c", "command -v python3"], { stdio: "ignore" }).status !== 0) { return; } diff --git a/src/lib/actions/sandbox/auto-pair-warmup.test.ts b/src/lib/actions/sandbox/auto-pair-warmup.test.ts index d3d7c8f5384..d0c8e4c2a76 100644 --- a/src/lib/actions/sandbox/auto-pair-warmup.test.ts +++ b/src/lib/actions/sandbox/auto-pair-warmup.test.ts @@ -22,7 +22,7 @@ import { WARMUP_SESSION_ID_PREFIX } from "./warmup-session"; // the OpenShell-exec wrapping the leaf depends on — and the // finalization.test.ts ordering tests pin the provoke→approve wiring. -describe("scope-upgrade warm-up timeout bound (#4504-v2)", () => { +describe("scope-upgrade warm-up timeout bound v2 (#4504)", () => { it("uses a fixed 30s outer cap so a wedged warm-up can never block onboard", () => { // The `-m "ping"` one-shot returns fast even when it falls back to embedded // mode; 30s covers gateway-connect + the scope-upgrade request plus @@ -42,7 +42,7 @@ describe("scope-upgrade warm-up timeout bound (#4504-v2)", () => { }); }); -describe("warm-up payload survives OpenShell exec (#4504-v2)", () => { +describe("warm-up payload survives OpenShell exec in v2 (#4504)", () => { // The leaf wraps its in-sandbox script with the shared `wrapSandboxShellScript` // (OpenShell exec rejects newline-bearing args). These cases pin that wrapper // contract — the exact mechanism the warm-up exec relies on — without needing @@ -83,7 +83,7 @@ describe("warm-up tags its throwaway session for user-facing filters (#5511)", ( expect(WARMUP_SCRIPT).toContain(`--session-id "${WARMUP_SESSION_ID_PREFIX}$$-$(date +%s)"`); }); - it("keeps the #4504-v2 provoke run foreground and within the original budget", () => { + it("keeps the v2 provoke run foreground and within the original budget (#4504)", () => { expect(WARMUP_SCRIPT).toContain('openclaw agent --agent main -m "ping" \\'); expect(WARMUP_SCRIPT).toContain(">/dev/null 2>&1 || true"); expect(WARMUP_SCRIPT).not.toContain("setsid"); diff --git a/src/lib/actions/sandbox/gateway-wedge-diagnostics.test.ts b/src/lib/actions/sandbox/gateway-wedge-diagnostics.test.ts index f322798f932..77d43cd8e68 100644 --- a/src/lib/actions/sandbox/gateway-wedge-diagnostics.test.ts +++ b/src/lib/actions/sandbox/gateway-wedge-diagnostics.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from "vitest"; // Import from compiled dist for parity with the other CLI tests in this project. import { collectGatewayWedgeDiagnostics, sanitizeWedgeLogLine } from "./gateway-wedge-diagnostics"; -describe("collectGatewayWedgeDiagnostics — #4710 wedge signature", () => { +describe("collectGatewayWedgeDiagnostics wedge signature (#4710)", () => { it("returns the matching gateway.log lines, trimmed", () => { const lines = collectGatewayWedgeDiagnostics("my-sandbox", () => ({ status: 0, diff --git a/src/lib/actions/sandbox/policy-channel-conflict.test.ts b/src/lib/actions/sandbox/policy-channel-conflict.test.ts index 48754d95177..df3c0a11960 100644 --- a/src/lib/actions/sandbox/policy-channel-conflict.test.ts +++ b/src/lib/actions/sandbox/policy-channel-conflict.test.ts @@ -480,7 +480,7 @@ describe("addSandboxChannel cross-sandbox conflict check (#4305)", () => { }); // Scenario 4 - it("--force bypasses the conflict even in non-interactive mode", async () => { + it("bypasses the conflict with --force even in non-interactive mode", async () => { arrangeRegistry({ current: makeEmptyEntry("alpha"), others: [ @@ -566,7 +566,7 @@ describe("addSandboxChannel cross-sandbox conflict check (#4305)", () => { }); // Scenario 7 - it("--dry-run never runs the conflict check or touches credentials", async () => { + it("avoids the conflict check and credentials with --dry-run", async () => { arrangeRegistry({ current: makeEmptyEntry("alpha"), others: [ @@ -706,7 +706,7 @@ describe("addSandboxChannel cross-sandbox conflict check (#4305)", () => { expect(upsertMock).not.toHaveBeenCalled(); }); - it("--force proceeds when the conflict check throws", async () => { + it("proceeds with --force when the conflict check throws", async () => { arrangeRegistry({ current: makeEmptyEntry("alpha"), others: [] }); getCredentialMock.mockReturnValue(TELEGRAM_TOKEN); listSandboxesMock.mockImplementation(() => { diff --git a/src/lib/actions/sandbox/process-recovery.test.ts b/src/lib/actions/sandbox/process-recovery.test.ts index e7ba277bd15..cbfe901000a 100644 --- a/src/lib/actions/sandbox/process-recovery.test.ts +++ b/src/lib/actions/sandbox/process-recovery.test.ts @@ -9,7 +9,7 @@ import { waitForRecoveredSandboxGateway, } from "./process-recovery"; -describe("probeSandboxInferenceGatewayHealth — #3265 gateway-chain subprobe", () => { +describe("probeSandboxInferenceGatewayHealth gateway-chain subprobe (#3265)", () => { const makeExec = (stdout: string, status = 0) => async () => ({ status, stdout, stderr: "" }); @@ -58,7 +58,7 @@ describe("probeSandboxInferenceGatewayHealth — #3265 gateway-chain subprobe", }); }); -describe("waitForRecoveredSandboxGateway — #4710 settle-window confirm", () => { +describe("waitForRecoveredSandboxGateway settle-window confirmation (#4710)", () => { const ENV_KEYS = [ "NEMOCLAW_GATEWAY_RECOVERY_WAIT_SECONDS", "NEMOCLAW_GATEWAY_RECOVERY_POLL_INTERVAL_SECONDS", diff --git a/src/lib/actions/sandbox/rebuild-env-isolation.test.ts b/src/lib/actions/sandbox/rebuild-env-isolation.test.ts index 0b27a76cb51..24956edb006 100644 --- a/src/lib/actions/sandbox/rebuild-env-isolation.test.ts +++ b/src/lib/actions/sandbox/rebuild-env-isolation.test.ts @@ -10,7 +10,7 @@ import { sanitizeEnvValueForDisplay, } from "./rebuild-env-isolation.js"; -describe("sanitizeEnvValueForDisplay (#5735 PRA-7)", () => { +describe("sanitizeEnvValueForDisplay PRA-7 (#5735)", () => { it("collapses a multi-line / ANSI value into a single safe line", () => { // Untrusted NEMOCLAW_AGENT with a newline + CR + ANSI escape that tries to // paint a fake "Installation complete" status line. @@ -34,7 +34,7 @@ describe("sanitizeEnvValueForDisplay (#5735 PRA-7)", () => { }); }); -describe("AMBIENT_RECREATE_ENV_VARS contract (#5735 PRA-4)", () => { +describe("AMBIENT_RECREATE_ENV_VARS contract PRA-4 (#5735)", () => { it("pins the exact onboard-selection env set the recreate must isolate", () => { // Mirrors the ambient selection env vars `onboard --resume` reads at its // source boundary. Adding a new onboard-selection env var must be a conscious diff --git a/src/lib/actions/sandbox/rebuild-flow.test.ts b/src/lib/actions/sandbox/rebuild-flow.test.ts index 695b25e6f4e..60f3ab9b3d2 100644 --- a/src/lib/actions/sandbox/rebuild-flow.test.ts +++ b/src/lib/actions/sandbox/rebuild-flow.test.ts @@ -554,7 +554,7 @@ describe("rebuildSandbox flow", () => { } }); - it("recreates a matching-session custom-endpoint sandbox from a validated session endpoint, ignoring hostile ambient endpoint/provider/model (#5735 PRA-4)", async () => { + it("recreates a matching-session custom-endpoint sandbox from a validated session endpoint while ignoring hostile ambient values for PRA-4 (#5735)", async () => { // Matching session (sandboxName === target) with a custom endpoint recorded // in that session. Hostile ambient NEMOCLAW_ENDPOINT_URL/PROVIDER/MODEL must // be absent during recreate so onboard --resume uses the validated session diff --git a/src/lib/actions/uninstall/run-plan.test.ts b/src/lib/actions/uninstall/run-plan.test.ts index 153cf502ff9..50dc57e74aa 100644 --- a/src/lib/actions/uninstall/run-plan.test.ts +++ b/src/lib/actions/uninstall/run-plan.test.ts @@ -848,7 +848,7 @@ describe("uninstall run plan", () => { expect(logs).not.toContain("Swap file removed"); }); - it("#3456 sub-bug #4: gateway destroy no-op uses the 'already removed' wording, not 'Destroyed ... skipped'", () => { + it("uses the 'already removed' wording instead of 'Destroyed ... skipped' for gateway destroy no-ops, sub-bug 4 (#3456)", () => { // When `openshell gateway destroy -g nemoclaw` returns non-zero (gateway // already gone), the previous code printed `Destroyed gateway 'nemoclaw' // skipped` — self-contradictory. The fix routes this branch to an onSkip diff --git a/src/lib/actions/update.test.ts b/src/lib/actions/update.test.ts index 4befc314d46..28a4fa3d0c5 100644 --- a/src/lib/actions/update.test.ts +++ b/src/lib/actions/update.test.ts @@ -15,7 +15,7 @@ import { } from "./update"; describe("runUpdateAction", () => { - it("--check reports update availability without running the installer", async () => { + it("reports update availability without running the installer for --check", async () => { const spawnSyncImpl = vi.fn(); const log = vi.fn(); @@ -42,7 +42,7 @@ describe("runUpdateAction", () => { expect(log).toHaveBeenCalledWith(expect.stringContaining("Latest maintained version: 0.2.0")); }); - it("--check renders NemoHermes branding and installer guidance when the Hermes alias is active", async () => { + it("renders NemoHermes branding and installer guidance for --check when the Hermes alias is active", async () => { const log = vi.fn(); const result = await runUpdateAction( @@ -66,7 +66,7 @@ describe("runUpdateAction", () => { ); }); - it("--check renders NemoDeepAgents branding and installer guidance when the Deep Agents alias is active", async () => { + it("renders NemoDeepAgents branding and installer guidance for --check when the Deep Agents alias is active", async () => { const log = vi.fn(); const result = await runUpdateAction( @@ -173,7 +173,7 @@ describe("runUpdateAction", () => { ); }); - it("--yes runs the maintained installer without prompting", async () => { + it("runs the maintained installer without prompting for --yes", async () => { const prompt = vi.fn(async () => "no"); const spawnSyncImpl = vi.fn( () => ({ status: 0, stdout: "", stderr: "", signal: null }) as never, diff --git a/src/lib/adapters/openshell/resolve.test.ts b/src/lib/adapters/openshell/resolve.test.ts index 5a35838e9f5..03b11e21561 100644 --- a/src/lib/adapters/openshell/resolve.test.ts +++ b/src/lib/adapters/openshell/resolve.test.ts @@ -64,7 +64,7 @@ describe("lib/resolve-openshell", () => { ).toBe("/usr/local/bin/openshell"); }); - it("falls back to /opt/homebrew/bin (Apple Silicon Homebrew prefix, #5334)", () => { + it("falls back to the Apple Silicon Homebrew prefix at /opt/homebrew/bin (#5334)", () => { expect( resolveOpenshell({ commandVResult: null, diff --git a/src/lib/adapters/openshell/timeouts.test.ts b/src/lib/adapters/openshell/timeouts.test.ts index 65ea905188e..36850657ccd 100644 --- a/src/lib/adapters/openshell/timeouts.test.ts +++ b/src/lib/adapters/openshell/timeouts.test.ts @@ -32,7 +32,7 @@ describe("openshell-timeouts", () => { expect(OPENSHELL_DOWNLOAD_TIMEOUT_MS).toBeLessThan(OPENSHELL_HEAVY_TIMEOUT_MS); }); - it("uses the same probe constant name as PR #2454 for forward compatibility", () => { + it("uses the same probe constant name for forward compatibility (#2454)", () => { // PR #2454 introduces OPENSHELL_PROBE_TIMEOUT_MS = 15_000 locally. // This ensures the shared module stays aligned so #2454 can import it after rebase. expect(OPENSHELL_PROBE_TIMEOUT_MS).toBe(15_000); diff --git a/src/lib/agent/onboard.test.ts b/src/lib/agent/onboard.test.ts index 5e6feb8861c..55f3c0f1a20 100644 --- a/src/lib/agent/onboard.test.ts +++ b/src/lib/agent/onboard.test.ts @@ -95,7 +95,7 @@ const buildUrlsLoopback = (token: string | null, port: number): string[] => { return [`http://127.0.0.1:${port}/${hash}`]; }; -describe("printDashboardUi — regression for #2078 (port 8642 is not a chat UI)", () => { +describe("printDashboardUi with port 8642 outside the chat UI (#2078)", () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => {}); const noteSpy = vi.fn(); diff --git a/src/lib/agent/runtime.test.ts b/src/lib/agent/runtime.test.ts index 08ac29eb294..6c98edc1881 100644 --- a/src/lib/agent/runtime.test.ts +++ b/src/lib/agent/runtime.test.ts @@ -215,7 +215,7 @@ describe("buildRecoveryScript", () => { // swallowed sourcing errors via `2>/dev/null`, leaving respawned gateways // guard-less and crash-looping on the next library error from ciao, // model-pricing, or anything else hitting a sandboxed syscall. - describe("#2478 hardened library-guard preload chain", () => { + describe("hardened library-guard preload chain (#2478)", () => { it("sources the generated recovery env after validating the gateway env file", () => { const script = buildRecoveryScript(minimalAgent, 19000); expect(script).toContain("_nemoclaw_validate_recovery_proxy_env /tmp/nemoclaw-proxy-env.sh"); diff --git a/src/lib/build-context.test.ts b/src/lib/build-context.test.ts index 19bf57ac5b0..41516f5c9e3 100644 --- a/src/lib/build-context.test.ts +++ b/src/lib/build-context.test.ts @@ -98,7 +98,7 @@ describe("printSandboxCreateRecoveryHints", () => { // This recovery path runs ONLY after the OpenShell upload failure is // classified; ordinary x86_64 happy-path onboards never reach it. The tests // below assert that branch deterministically by injecting platform/arch. - it("prints the local-registry workaround with the preserved built image tag for the #3266 upload 404", () => { + it("prints the local-registry workaround with the preserved built image tag for an upload 404 (#3266)", () => { printSandboxCreateRecoveryHints( [ " Built image openshell/sandbox-from-nemoclaw:abcd1234", @@ -128,7 +128,7 @@ describe("printSandboxCreateRecoveryHints", () => { expect(out).toContain("onboard --resume"); }); - it("adds the Linux ARM64 (aarch64) note for the #3266 upload 404 only on Linux arm64", () => { + it("adds the Linux ARM64 note for an upload 404 only on Linux arm64 (#3266)", () => { printSandboxCreateRecoveryHints("failed to upload image tar into container", { platform: "linux", arch: "arm64", @@ -136,7 +136,7 @@ describe("printSandboxCreateRecoveryHints", () => { expect(stderr()).toContain("known limitation on Linux ARM64 (aarch64)"); }); - it("omits the ARM64 note for the #3266 upload 404 on x86_64 hosts", () => { + it("omits the ARM64 note for an upload 404 on x86_64 hosts (#3266)", () => { printSandboxCreateRecoveryHints("failed to upload image tar into container", { platform: "linux", arch: "x64", diff --git a/src/lib/channel-runtime-status.test.ts b/src/lib/channel-runtime-status.test.ts index 0027d4be7b1..93fa024a56d 100644 --- a/src/lib/channel-runtime-status.test.ts +++ b/src/lib/channel-runtime-status.test.ts @@ -311,7 +311,7 @@ describe("probeChannelRuntimeStatus", () => { expect(result.configuredButNotRunning).toEqual([]); }); - it("flags a configured channel as not-running when the gateway log never mentions it (#4156 reporter case)", () => { + it("flags a configured channel as not-running when the gateway log never mentions it in the reporter case (#4156)", () => { // Reporter symptom: openclaw.json had the telegram block but the // dashboard rendered "No channels found." This is the failure mode — // configured but the OpenClaw runtime never logged anything for it. diff --git a/src/lib/cli/oclif-runner.test.ts b/src/lib/cli/oclif-runner.test.ts index f14e9b06d7c..10f6b7118c2 100644 --- a/src/lib/cli/oclif-runner.test.ts +++ b/src/lib/cli/oclif-runner.test.ts @@ -195,7 +195,7 @@ describe("runOclifCommandById", () => { expect(errorLine).not.toHaveBeenCalled(); }); - it("#2666: surfaces errors that happen to carry oclif.exit === 0 instead of swallowing them", async () => { + it("surfaces errors that happen to carry oclif.exit === 0 instead of swallowing them (#2666)", async () => { // Before #2666 this branch silently set exit 0 and produced no output. // The bug was an arbitrary error riding the same `oclif.exit === 0` // channel, e.g. propagated from inside a command's run(). Surface the @@ -216,7 +216,7 @@ describe("runOclifCommandById", () => { ); }); - it("#2666: falls back to a generic line when the error message is empty", async () => { + it("falls back to a generic line when the error message is empty (#2666)", async () => { // Closes the residual silent path: if a non-ExitError(0) carries an // empty message (or one that trims to empty), still emit *something* // so the user is never left looking at exit 0 + blank stdout/stderr. diff --git a/src/lib/core/stdin.test.ts b/src/lib/core/stdin.test.ts index 7583f8bc0b5..a434e53455a 100644 --- a/src/lib/core/stdin.test.ts +++ b/src/lib/core/stdin.test.ts @@ -48,7 +48,7 @@ describe("readLineFromStdin", () => { ["returns buffered bytes when EOF arrives before a newline", ["y", 0], "y"], ["returns null on a hard error with no buffered bytes", ["EBADF"], null], ["returns buffered bytes when a hard error interrupts mid-line", ["y", "e", "EBADF"], "ye"], - ] as const)("%s", (_label, events, expected) => { + ] as const)("handles the table case: %s", (_label, events, expected) => { expect(readLineFromStdin({ readSync: makeReadSync([...events]), sleep: vi.fn() })).toBe( expected, ); diff --git a/src/lib/core/version.test.ts b/src/lib/core/version.test.ts index 60200f856af..b9e7ae65fd6 100644 --- a/src/lib/core/version.test.ts +++ b/src/lib/core/version.test.ts @@ -66,7 +66,7 @@ describe("lib/version", () => { rmSync(join(testDir, ".version")); }); - it("regression #1239: returns .version even when package.json is stale", () => { + it("returns .version even when package.json is stale (#1239)", () => { // npm-published tarballs ship with a stale package.json version (0.1.0) // and a .version file stamped from the git tag at publish time. The // installed CLI must report the .version contents, not the package.json diff --git a/src/lib/domain/sandbox/logs.test.ts b/src/lib/domain/sandbox/logs.test.ts index f7f9f1aea4d..234b761a2aa 100644 --- a/src/lib/domain/sandbox/logs.test.ts +++ b/src/lib/domain/sandbox/logs.test.ts @@ -125,7 +125,7 @@ describe("mergeTailLogLines", () => { expect(mergeTailLogLines(["[1] a\n"], 0)).toBe("[1] a\n"); }); - it("caps the merged output at maxLines (closes #4100)", () => { + it("caps the merged output at maxLines (#4100)", () => { const gateway = ["[1] g1", "[3] g2", "[5] g3"].join("\n") + "\n"; const openshell = ["[2] o1", "[4] o2", "[6] o3"].join("\n") + "\n"; const merged = mergeTailLogLines([gateway, openshell], 3); diff --git a/src/lib/domain/uninstall/paths.test.ts b/src/lib/domain/uninstall/paths.test.ts index e85641554a6..77b371e026b 100644 --- a/src/lib/domain/uninstall/paths.test.ts +++ b/src/lib/domain/uninstall/paths.test.ts @@ -53,7 +53,7 @@ describe("uninstall paths", () => { path.join("/home/test", ".config", "nemoclaw"), ]); }); - it("#3456: exposes the Linux Docker-driver gateway state dir so uninstall can clean it", () => { + it("exposes the Linux Docker-driver gateway state dir so uninstall can clean it (#3456)", () => { // ~/.local/state/nemoclaw/ holds the openshell-gateway PID file, SQLite // database, audit log, and vm-driver/ state. Documented as // NEMOCLAW_OPENSHELL_GATEWAY_STATE_DIR in docs/reference/commands.mdx. diff --git a/src/lib/inference/config.test.ts b/src/lib/inference/config.test.ts index 5acf9c49f7e..63d56ea1930 100644 --- a/src/lib/inference/config.test.ts +++ b/src/lib/inference/config.test.ts @@ -399,7 +399,7 @@ describe("planInferenceRouteReconcile", () => { expect(planInferenceRouteReconcile(null, recorded)).toEqual({ kind: "repair" }); }); - it("flags divergence when the gateway model differs (the #3726 case)", () => { + it("flags divergence when the gateway model differs (#3726)", () => { const live = { provider: "nvidia-prod", model: "nvidia/nemotron-3-nano-omni-30b-a3b-reasoning", diff --git a/src/lib/inference/nim.test.ts b/src/lib/inference/nim.test.ts index afaa8809819..ee2cc913786 100644 --- a/src/lib/inference/nim.test.ts +++ b/src/lib/inference/nim.test.ts @@ -632,7 +632,7 @@ describe("nim", () => { // hosts, so mixed-GPU machines (RTX PRO 6000 + GB300 on the QA verification // host) dropped the model info entirely. We keep `name` undefined to avoid // misattribution but now surface the per-GPU breakdown via `gpus`. - it("drops name and populates gpus breakdown on mixed-model hosts (regression #2669)", () => { + it("drops name and populates the gpus breakdown on mixed-model hosts (#2669)", () => { const runCapture = vi.fn((cmd: string | string[]) => { if (!Array.isArray(cmd)) throw new Error("expected argv array"); if (cmd[0] === "nvidia-smi" && cmd.some((a: string) => a.includes("name,memory.total"))) { diff --git a/src/lib/inference/onboard-probes.test.ts b/src/lib/inference/onboard-probes.test.ts index a8e4868fcc0..24ddad5fc9b 100644 --- a/src/lib/inference/onboard-probes.test.ts +++ b/src/lib/inference/onboard-probes.test.ts @@ -445,7 +445,7 @@ describe("OpenAI-compatible inference probes", () => { }); }); - describe("retriable HTTP statuses (issues #2980, #3033)", () => { + describe("retriable HTTP statuses (#2980, #3033)", () => { it("retries 429 (rate limit)", () => { expect(RETRIABLE_HTTP_PROBE_STATUSES.has(429)).toBe(true); }); @@ -465,7 +465,7 @@ describe("OpenAI-compatible inference probes", () => { expect(RETRIABLE_HTTP_PROBE_STATUSES.has(200)).toBe(false); }); - it("recovers when an upstream 502 clears on retry (regression #2980)", () => { + it("recovers when an upstream 502 clears on retry (#2980)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-502-probe-")); const fakeBin = path.join(tmpDir, "bin"); const counter = path.join(tmpDir, "counter"); diff --git a/src/lib/inventory/index.test.ts b/src/lib/inventory/index.test.ts index 8a7c4116caa..ba2d70b8339 100644 --- a/src/lib/inventory/index.test.ts +++ b/src/lib/inventory/index.test.ts @@ -135,7 +135,7 @@ describe("inventory commands", () => { expect(getLiveInference).not.toHaveBeenCalled(); }); - it("shows agent as 'unknown' for a gateway-recovered sandbox (#5714), not the OpenClaw default", async () => { + it("shows agent as 'unknown' instead of the OpenClaw default for a gateway-recovered sandbox (#5714)", async () => { const inventory = await getSandboxInventory({ recoverRegistryEntries: async () => ({ sandboxes: [ @@ -269,7 +269,7 @@ describe("inventory commands", () => { ); }); - it("#2753: suppresses last-onboarded hint when sandbox step never completed", async () => { + it("suppresses the last-onboarded hint when the sandbox step never completed (#2753)", async () => { // The session retains a sandbox name from an interrupted onboard // (pre-fix sessions on disk, or any in-progress write between steps). // Surfacing it as the "last onboarded sandbox" would resurrect the @@ -763,7 +763,7 @@ describe("inventory commands", () => { expect(showServiceStatus).toHaveBeenCalledWith({ sandboxName: "alpha" }); }); - describe("#1077 — env-resolved default sandbox", () => { + describe("env-resolved default sandbox (#1077)", () => { const savedSandboxName = process.env.SANDBOX_NAME; const savedNemoclawSandboxName = process.env.NEMOCLAW_SANDBOX_NAME; const savedNemoclawSandbox = process.env.NEMOCLAW_SANDBOX; diff --git a/src/lib/onboard/bridge-dns-preflight.test.ts b/src/lib/onboard/bridge-dns-preflight.test.ts index a12f7489f78..ee82544190f 100644 --- a/src/lib/onboard/bridge-dns-preflight.test.ts +++ b/src/lib/onboard/bridge-dns-preflight.test.ts @@ -26,7 +26,7 @@ describe("printDockerBridgeContainerStartFailure", () => { vi.restoreAllMocks(); }); - it("uses the active CLI branding in the verify-outside hint (#3630 CodeRabbit)", () => { + it("uses the active CLI branding in the verify-outside hint per CodeRabbit review (#3630)", () => { setOnboardBrandingAgent("hermes"); process.env.NEMOCLAW_AGENT = "hermes"; process.env.NEMOCLAW_INVOKED_AS = "nemohermes"; @@ -49,7 +49,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(verifyLine).not.toContain("Verify outside NemoClaw:"); }); - it("renders Linux daemon.json remediation without the bare-echo clobber fallback (#3630 CodeRabbit)", () => { + it("renders Linux daemon.json remediation without the bare-echo clobber fallback per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); @@ -79,7 +79,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(blob).toMatch(/\{"dns":\["[^"]+"\]\}/); }); - it("renders WSL-without-systemd remediation without using systemctl steps (#3630 CodeRabbit)", () => { + it("renders WSL-without-systemd remediation without using systemctl steps per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); @@ -103,7 +103,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(blob).toContain("jq -n"); }); - it("renders WSL-with-systemd remediation with the Linux systemd path (#3630 CodeRabbit)", () => { + it("renders WSL-with-systemd remediation with the Linux systemd path per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); @@ -120,7 +120,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(blob).toContain("sudo systemctl restart docker"); }); - it("uses the pinned BusyBox digest in the manual verify-fix commands (#3630 CodeRabbit)", () => { + it("uses the pinned BusyBox digest in manual verify-fix commands per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); @@ -139,7 +139,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(blob).not.toMatch(/docker run --rm busybox\s+nslookup/); }); - it("uses the pinned BusyBox digest in the verify-outside hint after a bridge failure (#3630 CodeRabbit)", () => { + it("uses the pinned BusyBox digest in the post-failure verify-outside hint per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); @@ -158,7 +158,7 @@ describe("printDockerBridgeContainerStartFailure", () => { expect(blob).not.toMatch(/busybox:latest true/); }); - it("renders macOS Docker Desktop daemon.json remediation without bare-echo clobber (#3630 CodeRabbit)", () => { + it("renders macOS Docker Desktop daemon.json remediation without bare-echo clobber per CodeRabbit review (#3630)", () => { const messages: string[] = []; const errSpy = vi.spyOn(console, "error").mockImplementation((arg?: unknown) => { messages.push(String(arg ?? "")); diff --git a/src/lib/onboard/docker-gpu-local-inference.test.ts b/src/lib/onboard/docker-gpu-local-inference.test.ts index 319a15a98a2..59ee505a1e8 100644 --- a/src/lib/onboard/docker-gpu-local-inference.test.ts +++ b/src/lib/onboard/docker-gpu-local-inference.test.ts @@ -173,7 +173,7 @@ describe("verifyDockerGpuSandboxLocalInference", () => { expect(script).not.toContain("docker exec"); }); - it("fails on a 4xx — route reached but not usable (auth/route misconfig), not the #4509 proof", () => { + it("fails on a 4xx because the route is reached but unusable instead of satisfying the proof (#4509)", () => { const result = verifyDockerGpuSandboxLocalInference( GPU_CONFIG, "ollama-local", @@ -186,7 +186,7 @@ describe("verifyDockerGpuSandboxLocalInference", () => { } }); - it("fails (unreachable) and retries on HTTP 000 — the #4509 regression", () => { + it("fails as unreachable and retries on HTTP 000 (#4509)", () => { const execInSandbox = execEmitting("HTTP_000"); const sleep = vi.fn(); const result = verifyDockerGpuSandboxLocalInference( diff --git a/src/lib/onboard/docker-gpu-patch.test.ts b/src/lib/onboard/docker-gpu-patch.test.ts index 2a11000f24a..2c67062fbbe 100644 --- a/src/lib/onboard/docker-gpu-patch.test.ts +++ b/src/lib/onboard/docker-gpu-patch.test.ts @@ -764,7 +764,7 @@ describe("docker-gpu-patch sandbox DNS fallback (#3579)", () => { ); }); - it("regression manifest: host.openshell.internal + google.com + gateway.discord.gg + integrate.api.nvidia.com (#3579 manager spec)", () => { + it("includes every hostname from the manager-provided regression manifest (#3579)", () => { // The four hostnames called out in #3579's manager-provided spec: // host.openshell.internal → resolved via --add-host (mount namespace) // google.com → public DNS via embedded Docker resolver diff --git a/src/lib/onboard/dockerfile-patch.test.ts b/src/lib/onboard/dockerfile-patch.test.ts index c0b1f6a8503..2d860d17b97 100644 --- a/src/lib/onboard/dockerfile-patch.test.ts +++ b/src/lib/onboard/dockerfile-patch.test.ts @@ -689,7 +689,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #1904: BASE_IMAGE must reference sandbox-base, not openshell-community", () => { + it("requires BASE_IMAGE to reference sandbox-base instead of openshell-community (#1904)", () => { // This is the exact bug that broke all e2e tests in PR #1937: // the code read a digest from blueprint.yaml (openshell-community registry) // and applied it to nemoclaw/sandbox-base (different registry). @@ -823,7 +823,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #1409: bakes NEMOCLAW_PROXY_HOST/PORT env into the staged Dockerfile", () => { + it("bakes NEMOCLAW_PROXY_HOST/PORT env into the staged Dockerfile (#1409)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-proxy-")); const dockerfilePath = path.join(tmpDir, "Dockerfile"); fs.writeFileSync( @@ -873,7 +873,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #1409: leaves Dockerfile defaults when proxy env is unset", () => { + it("leaves Dockerfile defaults when proxy env is unset (#1409)", () => { const tmpDir = fs.mkdtempSync( path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-proxy-default-"), ); @@ -918,7 +918,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #2421: bakes NEMOCLAW_INFERENCE_INPUTS into the staged Dockerfile when env is set", () => { + it("bakes NEMOCLAW_INFERENCE_INPUTS into the staged Dockerfile when env is set (#2421)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-inputs-")); const dockerfilePath = path.join(tmpDir, "Dockerfile"); fs.writeFileSync( @@ -959,7 +959,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #2421: rejects malformed NEMOCLAW_INFERENCE_INPUTS and keeps default", () => { + it("rejects malformed NEMOCLAW_INFERENCE_INPUTS and keeps the default (#2421)", () => { const tmpDir = fs.mkdtempSync( path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-inputs-bad-"), ); @@ -1018,7 +1018,7 @@ describe("dockerfile patch helpers", () => { } }); - it("regression #1409: rejects malformed NEMOCLAW_PROXY_HOST/PORT and keeps defaults", () => { + it("rejects malformed NEMOCLAW_PROXY_HOST/PORT and keeps defaults (#1409)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-proxy-bad-")); const dockerfilePath = path.join(tmpDir, "Dockerfile"); fs.writeFileSync( @@ -1070,7 +1070,7 @@ describe("dockerfile patch helpers", () => { } }); - it("#2281: bakes NEMOCLAW_AGENT_TIMEOUT env into the staged Dockerfile", () => { + it("bakes NEMOCLAW_AGENT_TIMEOUT env into the staged Dockerfile (#2281)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-timeout-")); const dockerfilePath = path.join(tmpDir, "Dockerfile"); fs.writeFileSync( @@ -1111,7 +1111,7 @@ describe("dockerfile patch helpers", () => { } }); - it("#2880: bakes NEMOCLAW_AGENT_HEARTBEAT_EVERY env into the staged Dockerfile", () => { + it("bakes NEMOCLAW_AGENT_HEARTBEAT_EVERY env into the staged Dockerfile (#2880)", () => { const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-onboard-dockerfile-heartbeat-")); const dockerfilePath = path.join(tmpDir, "Dockerfile"); const baseDockerfile = [ diff --git a/src/lib/onboard/gateway-sandbox-reachability.test.ts b/src/lib/onboard/gateway-sandbox-reachability.test.ts index bdc99b516d6..066279004d0 100644 --- a/src/lib/onboard/gateway-sandbox-reachability.test.ts +++ b/src/lib/onboard/gateway-sandbox-reachability.test.ts @@ -125,7 +125,7 @@ describe("isSandboxBridgeGatewayReachable", () => { expect(result.detail).toContain("operation not supported"); }); - it("does not misclassify unrelated 'veth' or 'operation not supported' output as veth_unsupported (#3630 CodeRabbit)", async () => { + it("does not misclassify unrelated 'veth' or 'operation not supported' output as veth_unsupported per CodeRabbit review (#3630)", async () => { // Generic veth status lines, or `operation not supported` from // other syscalls (mount, ioctl, etc.) must fall through to the // existing inconclusive path, not be reported as fatal Jetson veth. @@ -215,7 +215,7 @@ describe("isSandboxBridgeGatewayReachable", () => { expect(result.reason).toBe("tcp_failed"); }); - it("downgrades a slow-registry pre-pull timeout to probe_unavailable (not fatal probe_timeout) (#3630 codex review)", async () => { + it("downgrades a slow-registry pre-pull timeout to probe_unavailable instead of fatal probe_timeout per Codex review (#3630)", async () => { const result = await isSandboxBridgeGatewayReachable({ inspectNetworkImpl: () => ({ subnet: "172.19.0.0/16", gatewayIp: "172.19.0.1" }), usesHostGatewayRouteImpl: () => false, @@ -231,7 +231,7 @@ describe("isSandboxBridgeGatewayReachable", () => { expect(result.detail).toContain("timed out"); }); - it("classifies docker-daemon-connect failures from the probe run as fatal docker_daemon_unreachable (#3630 CodeRabbit)", async () => { + it("classifies docker-daemon-connect failures from the probe run as fatal docker_daemon_unreachable per CodeRabbit review (#3630)", async () => { // The image-cache pre-pull succeeded (or was bypassed), but the // actual `docker run` probe failed with the daemon-down signature. // This must surface as docker_daemon_unreachable (fatal), not slip @@ -278,7 +278,7 @@ describe("isSandboxBridgeGatewayReachable", () => { expect(result.reason).toBe("docker_daemon_unreachable"); }); - it("escalates inspect_unavailable to fatal docker_daemon_unreachable (#3630 codex review)", async () => { + it("escalates inspect_unavailable to fatal docker_daemon_unreachable per Codex review (#3630)", async () => { const result = await isSandboxBridgeGatewayReachable({ inspectNetworkImpl: () => ({ subnet: "172.19.0.0/16", gatewayIp: "172.19.0.1" }), usesHostGatewayRouteImpl: () => false, @@ -294,7 +294,7 @@ describe("isSandboxBridgeGatewayReachable", () => { expect(result.detail).toContain("Cannot connect to the Docker daemon"); }); - it("uses inspect-specific fallback detail when inspect_unavailable has no details (#3630 CodeRabbit)", async () => { + it("uses inspect-specific fallback detail when inspect_unavailable has no details per CodeRabbit review (#3630)", async () => { const result = await isSandboxBridgeGatewayReachable({ inspectNetworkImpl: () => ({ subnet: "172.19.0.0/16", gatewayIp: "172.19.0.1" }), usesHostGatewayRouteImpl: () => false, @@ -408,7 +408,7 @@ describe("formatSandboxBridgeUnreachableMessage", () => { expect(msg).toContain("enable integration for this distro"); }); - it("uses cliDisplayName() and cliName() in fatal messages instead of hardcoded NemoClaw branding (#3630 CodeRabbit)", () => { + it("uses cliDisplayName() and cliName() in fatal messages instead of hardcoded branding per CodeRabbit review (#3630)", () => { const savedAgent = process.env.NEMOCLAW_AGENT; const savedInvoked = process.env.NEMOCLAW_INVOKED_AS; process.env.NEMOCLAW_AGENT = "hermes"; diff --git a/src/lib/onboard/gateway-start-failure-integration.test.ts b/src/lib/onboard/gateway-start-failure-integration.test.ts index d28e6678532..a9c3cfec56d 100644 --- a/src/lib/onboard/gateway-start-failure-integration.test.ts +++ b/src/lib/onboard/gateway-start-failure-integration.test.ts @@ -186,7 +186,7 @@ describe("startGatewayWithOptions docker-unreachable abort (#2347)", () => { // confirms the chain bottoms out at exitProcess(1) with the recovery // message printed. - describe("composition: classify → handleFinal → exit 1", () => { + describe("composition classifies, handles the final state, and exits 1", () => { let capturedClassifyLog: string[]; let capturedPrintError: string[]; diff --git a/src/lib/onboard/hermes-dashboard.test.ts b/src/lib/onboard/hermes-dashboard.test.ts index 400a198a8ab..1075eb73f35 100644 --- a/src/lib/onboard/hermes-dashboard.test.ts +++ b/src/lib/onboard/hermes-dashboard.test.ts @@ -71,7 +71,7 @@ describe("onboard Hermes dashboard helpers", () => { ); }); - it("routes the #4984 rejection through fail() so onboarding exits non-zero", () => { + it("routes the reserved-port rejection through fail() so onboarding exits non-zero (#4984)", () => { const fail = vi.fn((message: string): never => { throw new Error(message); }); diff --git a/src/lib/onboard/install-ollama-linux.test.ts b/src/lib/onboard/install-ollama-linux.test.ts index 90a17e7436f..fad151e61ed 100644 --- a/src/lib/onboard/install-ollama-linux.test.ts +++ b/src/lib/onboard/install-ollama-linux.test.ts @@ -99,7 +99,7 @@ describe("decideInstallOllamaLinuxMode", () => { expect(decideInstallOllamaLinuxMode(opts)).toBe("system"); }); - it("returns user-local when non-interactive without passwordless sudo (issue #4114 repro)", () => { + it("returns user-local when non-interactive without passwordless sudo (#4114)", () => { const opts = makeOpts({ canSudoNonInteractive: () => false, isNonInteractive: () => true, diff --git a/src/lib/onboard/machine/handlers/finalization.test.ts b/src/lib/onboard/machine/handlers/finalization.test.ts index 6dff275a6be..9fea41cd87c 100644 --- a/src/lib/onboard/machine/handlers/finalization.test.ts +++ b/src/lib/onboard/machine/handlers/finalization.test.ts @@ -293,7 +293,7 @@ describe("handleFinalizationState", () => { // (provoke / create pending) → autoPairScopeApproval (approve / clear // pending). Reversing warmup and approval makes the approval pass a no-op and // the user's first real run falls back — exactly the bug v2 fixes. - it("provokes the scope upgrade after recovery and before the approval pass (#4504-v2)", async () => { + it("provokes the scope upgrade after recovery and before the approval pass in v2 (#4504)", async () => { const { deps, calls } = createDeps(); await handleFinalizationState(baseOptions(deps)); @@ -318,7 +318,7 @@ describe("handleFinalizationState", () => { // proceeds straight to the approval pass, verification, and the dashboard. // The dep returning nothing useful (no pending provoked, gateway slow) does // not change the downstream flow: behavior degrades to v1, never blocks. - it("does not depend on the warm-up succeeding; finalization still completes (#4504-v2)", async () => { + it("completes v2 finalization without depending on the warm-up succeeding (#4504)", async () => { // The default warm-up mock returns undefined (e.g. gateway not up → the // production leaf swallowed and provoked nothing). Finalization must be // unaffected. @@ -338,7 +338,7 @@ describe("handleFinalizationState", () => { // upgrade is provoked regardless of which agent the sandbox runs (the // contract says run it unconditionally; idempotent once operator.write is // paired). - it("provokes the scope upgrade regardless of agent type (#4504-v2)", async () => { + it("provokes the v2 scope upgrade regardless of agent type (#4504)", async () => { const { deps: depsHermes, calls: callsHermes } = createDeps(); await handleFinalizationState({ ...baseOptions(depsHermes), agent: { name: "hermes" } }); expect(callsHermes.warmupScopeUpgrade).toHaveBeenCalledWith("my-assistant"); diff --git a/src/lib/onboard/machine/transitions.test.ts b/src/lib/onboard/machine/transitions.test.ts index ac1bd22cbbd..92698f55aec 100644 --- a/src/lib/onboard/machine/transitions.test.ts +++ b/src/lib/onboard/machine/transitions.test.ts @@ -37,7 +37,7 @@ const canonicalDirectTransitions = [ ] as const; describe("onboard machine vocabulary", () => { - it("defines the initial coarse state vocabulary from issue #3802", () => { + it("defines the initial coarse state vocabulary (#3802)", () => { expect(ONBOARD_MACHINE_STATES).toEqual([ "init", "preflight", @@ -55,7 +55,7 @@ describe("onboard machine vocabulary", () => { ]); }); - it("defines the initial observe-only event vocabulary from issue #3802", () => { + it("defines the initial observe-only event vocabulary (#3802)", () => { expect(ONBOARD_MACHINE_EVENT_TYPES).toEqual([ "onboard.started", "onboard.resumed", diff --git a/src/lib/onboard/messaging-channel-setup.test.ts b/src/lib/onboard/messaging-channel-setup.test.ts index ea749a1e2c0..e8025633ff9 100644 --- a/src/lib/onboard/messaging-channel-setup.test.ts +++ b/src/lib/onboard/messaging-channel-setup.test.ts @@ -75,7 +75,7 @@ describe("setupSelectedMessagingChannels", () => { vi.restoreAllMocks(); }); - it("#4068 prints Telegram group privacy-mode setup guidance during onboarding", async () => { + it("prints Telegram group privacy-mode setup guidance during onboarding (#4068)", async () => { process.env.TELEGRAM_BOT_TOKEN = "123456:ABC-test-token"; process.env.TELEGRAM_REQUIRE_MENTION = "1"; process.env.TELEGRAM_ALLOWED_IDS = "123456789"; @@ -573,7 +573,7 @@ describe("setupMessagingChannels", () => { expect(output).toContain("slack — already configured"); }); - it("#5696 exits with code 1 when TELEGRAM_GROUP_POLICY is set to an unrecognised value", async () => { + it("exits with code 1 when TELEGRAM_GROUP_POLICY is set to an unrecognised value (#5696)", async () => { process.env.TELEGRAM_BOT_TOKEN = "123456:ABC-test-token"; process.env.TELEGRAM_GROUP_POLICY = "lockdown"; const errors: string[] = []; diff --git a/src/lib/onboard/model-router-process.test.ts b/src/lib/onboard/model-router-process.test.ts index 054dac3fe61..2ece137e4d5 100644 --- a/src/lib/onboard/model-router-process.test.ts +++ b/src/lib/onboard/model-router-process.test.ts @@ -6,7 +6,7 @@ import { describe, expect, it } from "vitest"; import { findModelRouterPidForPort } from "./model-router-process"; describe("findModelRouterPidForPort", () => { - it("returns the PID when a model-router proxy is found via proc scan (direct, #5169)", () => { + it("returns the PID when a model-router proxy is found via direct proc scan (#5169)", () => { const pid = findModelRouterPidForPort(4000, { readProcCommandLine: (p) => p === 12345 @@ -17,7 +17,7 @@ describe("findModelRouterPidForPort", () => { expect(pid).toBe(12345); }); - it("returns the PID when model-router is Python-interpreted (args[1], #5169)", () => { + it("returns the PID when model-router is Python-interpreted through args[1] (#5169)", () => { const pid = findModelRouterPidForPort(4000, { readProcCommandLine: (p) => p === 12345 diff --git a/src/lib/onboard/model-router-python.test.ts b/src/lib/onboard/model-router-python.test.ts index ec0a56c0a3d..ea168ff63bc 100644 --- a/src/lib/onboard/model-router-python.test.ts +++ b/src/lib/onboard/model-router-python.test.ts @@ -85,7 +85,7 @@ describe("pickHostPython", () => { assert.equal(result.overrideRequested, false); }); - it("returns every healthy candidate in priority order so the caller can fall back on venv failure (#3786 Codex P2)", () => { + it("returns every healthy candidate in priority order so the caller can fall back on venv failure per Codex P2 review (#3786)", () => { const which = (cmd: string) => ({ "python3.13": "/usr/bin/python3.13", @@ -176,7 +176,7 @@ describe("pickHostPython", () => { assert.equal(probeCount, 1); }); - it("treats NEMOCLAW_MODEL_ROUTER_PYTHON as a strict pin and does not fall back to PATH (#3786 Codex P3)", () => { + it("treats NEMOCLAW_MODEL_ROUTER_PYTHON as a strict pin without falling back to PATH per Codex P3 review (#3786)", () => { const which = (cmd: string) => (cmd === "python3.12" ? "/usr/bin/python3.12" : null); const probe = (executable: string) => { if (executable === "/opt/custom/python3.10") { @@ -299,7 +299,7 @@ describe("supported version window", () => { assert.deepEqual([...MIN_PYTHON_VERSION], [3, 10]); }); - it("excludes 3.14 to dodge the macOS Homebrew pyexpat regression in #3781", () => { + it("excludes 3.14 to avoid the macOS Homebrew pyexpat regression (#3781)", () => { assert.deepEqual([...MAX_PYTHON_EXCLUSIVE], [3, 14]); }); }); diff --git a/src/lib/onboard/ollama-startup.test.ts b/src/lib/onboard/ollama-startup.test.ts index 1b3ae8b0d89..a745a36e997 100644 --- a/src/lib/onboard/ollama-startup.test.ts +++ b/src/lib/onboard/ollama-startup.test.ts @@ -16,7 +16,7 @@ import { const wait = require("../core/wait"); const runner = require("../runner"); -describe("runOllamaStartupOrGate (#4365 steer hint)", () => { +describe("runOllamaStartupOrGate steer hint (#4365)", () => { let originalWaitForHttp: typeof wait.waitForHttp; let originalRunShell: typeof runner.runShell; let originalProviderEnv: string | undefined; diff --git a/src/lib/onboard/preflight-cdi.test.ts b/src/lib/onboard/preflight-cdi.test.ts index 7f66830e0b8..5930a94b24b 100644 --- a/src/lib/onboard/preflight-cdi.test.ts +++ b/src/lib/onboard/preflight-cdi.test.ts @@ -429,7 +429,7 @@ describe("planHostRemediation — CDI", () => { }); }); -describe("shouldEnforceCdiNvidiaGpuSpec (#5489 enforcement gate)", () => { +describe("shouldEnforceCdiNvidiaGpuSpec enforcement gate (#5489)", () => { it("enforces when the spec is missing and the operator did not explicitly opt out", () => { // The #5489 scenario: GPU hardware present (so cdiNvidiaGpuSpecMissing is // true) with sandbox GPU AUTO-disabled (nvidia-smi unavailable). Auto-disable diff --git a/src/lib/onboard/preflight.test.ts b/src/lib/onboard/preflight.test.ts index 925c7243c48..9cea07f9e4d 100644 --- a/src/lib/onboard/preflight.test.ts +++ b/src/lib/onboard/preflight.test.ts @@ -1040,7 +1040,7 @@ describe("probeContainerDns", () => { expect(isFatalContainerDnsProbeFailure(result)).toBe(true); }); - it("downgrades unrelated docker output (no resolver evidence) from fatal resolution_failed to inconclusive error (#3630 CodeRabbit)", () => { + it("downgrades unrelated docker output without resolver evidence from fatal resolution_failed to an inconclusive error per CodeRabbit review (#3630)", () => { // No "Server:" header — nslookup never produced a resolver response. // The output is some docker-side message unrelated to DNS, so we // must not abort onboarding with the systemd-resolved remediation. @@ -1183,7 +1183,7 @@ describe("probeContainerDns", () => { expect(isFatalContainerDnsProbeFailure(result)).toBe(true); }); - it("classifies a wedged Docker daemon (inspect_unavailable) as fatal docker_daemon_unreachable (#3630 codex review)", () => { + it("classifies a wedged Docker daemon with inspect_unavailable as fatal docker_daemon_unreachable per Codex review (#3630)", () => { const result = probeContainerDns({ ensureImageCachedOverride: { ok: false, @@ -1196,7 +1196,7 @@ describe("probeContainerDns", () => { expect(isFatalContainerDnsProbeFailure(result)).toBe(true); }); - it("does not treat a registry TCP timeout (i/o timeout on :443) as a fatal DNS failure (#3630 codex review)", () => { + it("does not treat a registry TCP timeout on port 443 as a fatal DNS failure per Codex review (#3630)", () => { // dial tcp :443 errors are TCP connectivity, NOT DNS — must not // be routed to UDP:53/systemd-resolved remediation. const result = probeContainerDns({ @@ -1303,7 +1303,7 @@ describe("probeContainerDns", () => { expect(seenScript).toContain("nslookup pinned-test.invalid"); }); - it("rejects shell metacharacters in probeName to prevent sh -c injection (#3630 CodeRabbit)", () => { + it("rejects shell metacharacters in probeName to prevent sh -c injection per CodeRabbit review (#3630)", () => { const injections = [ "x; touch /tmp/pwned", "x && touch /tmp/pwned", @@ -1415,7 +1415,7 @@ describe("probeDockerBridgeContainerStart", () => { expect(result.exitCode).toBe(125); }); - it("does not misclassify unrelated 'veth' mentions as fatal veth_unsupported (#3630 CodeRabbit)", () => { + it("does not misclassify unrelated 'veth' mentions as fatal veth_unsupported per CodeRabbit review (#3630)", () => { // Output references "veth" in passing — without the bridge-create // signature, it must stay on the generic-error path, not the fatal // Jetson remediation path. @@ -1432,7 +1432,7 @@ describe("probeDockerBridgeContainerStart", () => { expect(result.reason).not.toBe("veth_unsupported"); }); - it("does not misclassify generic 'operation not supported' errors as veth_unsupported (#3630 CodeRabbit)", () => { + it("does not misclassify generic 'operation not supported' errors as veth_unsupported per CodeRabbit review (#3630)", () => { // Generic OS-level "operation not supported" (e.g., from a cgroup // mount or unrelated syscall) must not be promoted to fatal veth. const result = probeDockerBridgeContainerStart({ @@ -1448,7 +1448,7 @@ describe("probeDockerBridgeContainerStart", () => { expect(result.reason).not.toBe("veth_unsupported"); }); - it("flags bridge container kill-by-signal (no timeout) as reason 'killed' (#3630 CodeRabbit)", () => { + it("flags a bridge container killed by signal without a timeout as reason 'killed' per CodeRabbit review (#3630)", () => { const result = probeDockerBridgeContainerStart({ executionOverride: { stdout: "", @@ -1507,7 +1507,7 @@ describe("probeDockerBridgeContainerStart", () => { expect(seenOpts?.timeout).toBe(20_000); }); - it("reports image_pull_failed (not bridge timeout) when the busybox pre-pull times out (#3630 codex review)", () => { + it("reports image_pull_failed instead of bridge timeout when the busybox pre-pull times out per Codex review (#3630)", () => { const result = probeDockerBridgeContainerStart({ ensureImageCachedOverride: { ok: false, @@ -1533,7 +1533,7 @@ describe("probeDockerBridgeContainerStart", () => { expect(result.ok).toBe(true); }); - it("reports a wedged Docker daemon (inspect_unavailable) as fatal docker_daemon_unreachable (#3630 codex review)", () => { + it("reports a wedged Docker daemon with inspect_unavailable as fatal docker_daemon_unreachable per Codex review (#3630)", () => { const result = probeDockerBridgeContainerStart({ ensureImageCachedOverride: { ok: false, @@ -1565,7 +1565,7 @@ describe("ensureProbeImageCached", () => { expect(result.alreadyCached).toBe(true); }); - it("classifies an inspect spawn timeout (ETIMEDOUT) as inspect_unavailable without falling through to pull (#3630 CodeRabbit)", () => { + it("classifies an ETIMEDOUT inspect spawn as inspect_unavailable without falling through to pull per CodeRabbit review (#3630)", () => { const result = ensureProbeImageCached("busybox:latest", { inspectProbeImpl: () => ({ stdout: "", @@ -1584,7 +1584,7 @@ describe("ensureProbeImageCached", () => { expect(result.reason).toBe("inspect_unavailable"); }); - it("classifies 'Cannot connect to the Docker daemon' inspect stderr as inspect_unavailable (#3630 codex review)", () => { + it("classifies 'Cannot connect to the Docker daemon' inspect stderr as inspect_unavailable per Codex review (#3630)", () => { const result = ensureProbeImageCached("busybox:latest", { inspectProbeImpl: () => ({ stdout: "", @@ -1799,7 +1799,7 @@ describe("isDockerUnderProvisioned", () => { }); }); -describe("assessHost — container runtime resource detection (regression #2514)", () => { +describe("assessHost container runtime resource detection (#2514)", () => { it("flags default Colima (2 CPU / 2 GiB) as under-provisioned", () => { const result = assessHost({ platform: "darwin", diff --git a/src/lib/registry-recovery-action.test.ts b/src/lib/registry-recovery-action.test.ts index fd82378cb98..e23e6fab92d 100644 --- a/src/lib/registry-recovery-action.test.ts +++ b/src/lib/registry-recovery-action.test.ts @@ -73,7 +73,7 @@ import { recoverRegistryEntries } from "./registry-recovery-action.js"; import { parseLiveSandboxEntries } from "./runtime-recovery.js"; import { loadSession } from "./state/onboard-session.js"; -describe("recoverRegistryEntries (#2753 seed-time guard)", () => { +describe("recoverRegistryEntries seed-time guard (#2753)", () => { beforeEach(() => { mockRegistryState.sandboxes = {}; mockRegistryState.defaultSandbox = null; @@ -226,7 +226,7 @@ describe("recoverRegistryEntries (#2753 seed-time guard)", () => { }); }); -describe("recoverRegistryEntries (#5714 empty-registry live gateway recovery)", () => { +describe("recoverRegistryEntries empty-registry live gateway recovery (#5714)", () => { beforeEach(() => { vi.clearAllMocks(); mockRegistryState.sandboxes = {}; @@ -347,7 +347,7 @@ describe("recoverRegistryEntries (#5714 empty-registry live gateway recovery)", expect(result.recoveredFromGateway).toBe(0); }); - it("does NOT persist unseeded gateway recoveries to the on-disk registry (#5714 agent safety)", async () => { + it("does not persist unseeded gateway recoveries to the on-disk registry for agent safety (#5714)", async () => { // `openshell sandbox list` does not expose the agent type, so persisting a // recovered entry would default agent to "openclaw" everywhere downstream // and permanently misclassify a Deep Agents/Hermes sandbox. Recovery is diff --git a/src/lib/sandbox-base-image.test.ts b/src/lib/sandbox-base-image.test.ts index b033bb06b4b..82110080319 100644 --- a/src/lib/sandbox-base-image.test.ts +++ b/src/lib/sandbox-base-image.test.ts @@ -162,7 +162,7 @@ describe("sandbox base image helpers", () => { expect(output).toContain("the --mount option requires BuildKit"); }); - it("surfaces stdout-only build diagnostics — BuildKit can land errors there (Codex review on #3584)", () => { + it("surfaces stdout-only build diagnostics because BuildKit can put errors there per Codex review (#3584)", () => { const output = formatBuildFailureDiagnostics({ stderr: "", stdout: diff --git a/src/lib/shields/timer.test.ts b/src/lib/shields/timer.test.ts index 16480768f80..ab5e97203fd 100644 --- a/src/lib/shields/timer.test.ts +++ b/src/lib/shields/timer.test.ts @@ -381,7 +381,7 @@ describe("shields timer authorization", () => { // (This narrows the revert window; it does not close the TOCTOU.) // ------------------------------------------------------------------------- - it("#4663 re-verifies the auto-restore lock after settle so a reconciler reverting .config-hash perms is caught", async () => { + it("re-verifies the auto-restore lock after settle so a reconciler reverting .config-hash perms is caught (#4663)", async () => { const stateDir = path.join(tmpHome, ".nemoclaw", "state"); fs.mkdirSync(stateDir, { recursive: true }); @@ -443,7 +443,7 @@ describe("shields timer authorization", () => { expect(JSON.parse(fs.readFileSync(stateFile, "utf-8")).shieldsDown).toBe(false); }); - it("#4663 leaves shields DOWN and audits when the post-settle re-lock cannot hold .config-hash perms", async () => { + it("leaves shields DOWN and audits when the post-settle re-lock cannot hold .config-hash perms (#4663)", async () => { const stateDir = path.join(tmpHome, ".nemoclaw", "state"); fs.mkdirSync(stateDir, { recursive: true }); diff --git a/src/lib/state/onboard-session.test.ts b/src/lib/state/onboard-session.test.ts index ae7d6f28b4d..ba8c7505e1a 100644 --- a/src/lib/state/onboard-session.test.ts +++ b/src/lib/state/onboard-session.test.ts @@ -476,7 +476,7 @@ describe("onboard session", () => { // disk and the next rebuild preflight demanded a credential the current // sandbox did not need. - it("clears credentialEnv when provider-selection update passes null (GH #2625)", () => { + it("clears credentialEnv when a provider-selection update passes null (#2625)", () => { // Seed with a prior remote-provider onboard state. session.saveSession(session.createSession()); markStepCompleteLegacy(session, stepMutation, "provider_selection", { @@ -776,7 +776,7 @@ describe("onboard session", () => { expect(fresh.messagingPlan).toBeNull(); }); - it("#1737: persists telegramConfig across save/load roundtrips (requireMention=true)", () => { + it("persists telegramConfig across save/load roundtrips with requireMention=true (#1737)", () => { const created = session.createSession(); created.telegramConfig = { requireMention: true }; session.saveSession(created); @@ -785,7 +785,7 @@ describe("onboard session", () => { expect(loaded.telegramConfig).toEqual({ requireMention: true }); }); - it("#1737: persists telegramConfig across save/load roundtrips (requireMention=false)", () => { + it("persists telegramConfig across save/load roundtrips with requireMention=false (#1737)", () => { const created = session.createSession(); created.telegramConfig = { requireMention: false }; session.saveSession(created); @@ -794,7 +794,7 @@ describe("onboard session", () => { expect(loaded.telegramConfig).toEqual({ requireMention: false }); }); - it("#1737: rejects malformed telegramConfig on load", () => { + it("rejects malformed telegramConfig on load (#1737)", () => { // Simulate a hand-edited session file with garbage in telegramConfig. // Going through saveSession() would re-normalize the value before it // hits disk, so write raw JSON directly to exercise the load-time @@ -809,7 +809,7 @@ describe("onboard session", () => { expect(loaded.telegramConfig).toBeNull(); }); - it("#1737: defaults telegramConfig to null for fresh sessions", () => { + it("defaults telegramConfig to null for fresh sessions (#1737)", () => { const fresh = session.createSession(); expect(fresh.telegramConfig).toBeNull(); }); @@ -986,7 +986,7 @@ describe("onboard session", () => { } }); - it("regression #1281: stale-cleanup race does not unlink a fresh lock claimed by another process", () => { + it("does not unlink a fresh lock claimed by another process during a stale-cleanup race (#1281)", () => { // Reproduces the race: the lock file we read as 'stale' gets replaced // with a fresh claim from a faster concurrent process between our // read and our unlink. The slower process must NOT unlink the fresh @@ -1211,7 +1211,7 @@ describe("onboard session", () => { expect(loaded.messagingPlan).toBeNull(); }); - it("#1737: filterSafeUpdates routes telegramConfig through markStepComplete", () => { + it("routes telegramConfig through markStepComplete in filterSafeUpdates (#1737)", () => { session.saveSession(session.createSession()); markStepCompleteLegacy(session, stepMutation, "provider_selection", { telegramConfig: { requireMention: true }, @@ -1226,7 +1226,7 @@ describe("onboard session", () => { expect(cleared.telegramConfig).toBeNull(); }); - it("#1737: filterSafeUpdates drops malformed telegramConfig values", () => { + it("drops malformed telegramConfig values in filterSafeUpdates (#1737)", () => { session.saveSession(session.createSession()); // Non-boolean requireMention — must not leak through. markStepCompleteLegacy(session, stepMutation, "provider_selection", { @@ -1267,7 +1267,7 @@ describe("onboard session", () => { expect(loaded.wechatConfig).toBeNull(); }); - it("createSession with messagingPlan override", () => { + it("creates a session with a messagingPlan override", () => { const plan = makeMessagingPlan("my-assistant", ["telegram", "slack"]); const created = session.createSession({ messagingPlan: plan }); expect(created.messagingPlan).toEqual(plan); diff --git a/src/lib/tunnel/services.test.ts b/src/lib/tunnel/services.test.ts index 6c5e503dbb2..e2a49fbf093 100644 --- a/src/lib/tunnel/services.test.ts +++ b/src/lib/tunnel/services.test.ts @@ -147,7 +147,7 @@ describe("sandbox name validation", () => { }); }); -describe("#1077 — status host service PID dir matches start/stop env", () => { +describe("status host service PID dir matches start/stop env (#1077)", () => { const savedSandboxName = process.env.SANDBOX_NAME; const savedNemoclawSandbox = process.env.NEMOCLAW_SANDBOX; const savedNemoclawSandboxName = process.env.NEMOCLAW_SANDBOX_NAME; diff --git a/src/lib/validation.test.ts b/src/lib/validation.test.ts index 869fcdb0e4d..19f3b9d5e62 100644 --- a/src/lib/validation.test.ts +++ b/src/lib/validation.test.ts @@ -59,7 +59,7 @@ describe("classifyValidationFailure", () => { }); }); - it("classifies 400 + expired key message as credential (#1942 — Gemini)", () => { + it("classifies a Gemini 400 with an expired-key message as a credential error (#1942)", () => { // Gemini returns HTTP 400 with this exact message when the API key has expired. // Must classify as "credential" so the onboard wizard prompts to re-enter the // key instead of looping back to provider selection. @@ -74,7 +74,7 @@ describe("classifyValidationFailure", () => { }); }); - it("classifies 400 + API_KEY_INVALID message as credential (#1942 — Gemini)", () => { + it("classifies a Gemini 400 with API_KEY_INVALID as a credential error (#1942)", () => { // Gemini also uses "API_KEY_INVALID" as the status string for revoked keys. expect( classifyValidationFailure({ @@ -87,7 +87,7 @@ describe("classifyValidationFailure", () => { }); }); - it("classifies bare 'API key not valid' message as credential (#1942 — Gemini .message only)", () => { + it("classifies a bare Gemini 'API key not valid' .message as a credential error (#1942)", () => { // When the message field is extracted without the API_KEY_INVALID status // prefix, the bare wording must still classify as credential. Flagged by // CodeRabbit on #2132. @@ -279,7 +279,7 @@ describe("classifySandboxCreateFailure", () => { expect(result.kind).toBe("image_upload_container_missing"); }); - it("does NOT classify an unrelated 404 as image_upload_container_missing (#3266 regression guard)", () => { + it("does not classify an unrelated 404 as image_upload_container_missing (#3266)", () => { // A generic 404 with no upload-tar phrase and no gateway container name // must not be mistaken for the ARM64 upload failure. expect( diff --git a/src/lib/verify-deployment.test.ts b/src/lib/verify-deployment.test.ts index 97b2e2e9b07..32a5894cbdc 100644 --- a/src/lib/verify-deployment.test.ts +++ b/src/lib/verify-deployment.test.ts @@ -34,7 +34,7 @@ describe("verifyDeployment", () => { expect(result.verification.dashboardReachable).toBe(true); }); - it("treats HTTP 401 as gateway alive (device auth enabled — fixes #2342)", async () => { + it("treats HTTP 401 as a live gateway with device auth enabled (#2342)", async () => { const deps = makeDeps({ executeSandboxCommand: () => ({ status: 0, stdout: "401", stderr: "" }), probeHostPort: () => 401, @@ -261,7 +261,7 @@ describe("verifyDeployment", () => { expect(msgDiag?.hint).toContain("rebuild"); }); - it("surfaces an inconclusive runtime probe as a messaging warn (catches malformed openclaw.json #4156)", async () => { + it("surfaces an inconclusive runtime probe as a messaging warning for malformed openclaw.json (#4156)", async () => { const deps = makeDeps({ getMessagingChannels: () => ["telegram"], providerExistsInGateway: () => true, diff --git a/test/channels-add-preset.test.ts b/test/channels-add-preset.test.ts index 5557cb5c6c4..6a7ed85ee85 100644 --- a/test/channels-add-preset.test.ts +++ b/test/channels-add-preset.test.ts @@ -281,7 +281,7 @@ module.exports = { `; } -describe("channels add applies matching policy preset (issue #3437)", () => { +describe("channels add applies a matching policy preset (#3437)", () => { it("plans channel enrollment through the messaging manifest workflow", () => { const script = `${buildPreamble()} const ctx = module.exports; @@ -1576,7 +1576,7 @@ const ctx = module.exports; // startup breadcrumb confirmation or an actionable warning. These tests // drive the verifier through stubbed sandbox-exec output so the contract // is pinned regardless of OpenClaw/OpenShell runtime availability. -describe("channels add verifies bridge startup after rebuild (issue #4314, #4390)", () => { +describe("channels add verifies bridge startup after rebuild (#4314, #4390)", () => { function buildInteractivePreamble(): string { return String.raw` const resolver = require(${j("adapters/openshell/resolve.js")}); diff --git a/test/channels-remove-full-teardown.test.ts b/test/channels-remove-full-teardown.test.ts index c9adb0a733f..06b5be1b265 100644 --- a/test/channels-remove-full-teardown.test.ts +++ b/test/channels-remove-full-teardown.test.ts @@ -218,7 +218,7 @@ module.exports = { `; } -describe("channels remove full teardown (issue #3998)", () => { +describe("channels remove full teardown (#3998)", () => { for (const sandboxAgent of ["openclaw", "hermes"] as const) { it(`strips '${sandboxAgent}' session.policyPresets and clears the in-sandbox whatsapp state dir`, () => { const script = `${buildPreamble({ sandboxAgent })} diff --git a/test/cli/connect-recovery-settle.test.ts b/test/cli/connect-recovery-settle.test.ts index f7188465ea6..bd0f4787464 100644 --- a/test/cli/connect-recovery-settle.test.ts +++ b/test/cli/connect-recovery-settle.test.ts @@ -15,7 +15,7 @@ import path from "node:path"; import { runWithEnv, writeSandboxRegistry } from "./helpers"; describe("CLI dispatch", () => { - it("fails probe-only when the gateway serves once and then drops its listener (#4710 wedge)", () => { + it("fails probe-only when a wedged gateway serves once and then drops its listener (#4710)", () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cli-connect-probe-wedge-")); const localBin = path.join(home, "bin"); const markerFile = path.join(home, "openshell-calls"); diff --git a/test/cli/dispatch-basics.test.ts b/test/cli/dispatch-basics.test.ts index 85021a586fd..9330df7613c 100644 --- a/test/cli/dispatch-basics.test.ts +++ b/test/cli/dispatch-basics.test.ts @@ -169,7 +169,7 @@ describe("CLI dispatch", () => { expect(r.out).toContain("langchain-deepagents-code"); }); - it("--help exits 0", () => { + it("exits 0 for --help", () => { expect(run("--help").code).toBe(0); }); @@ -179,7 +179,7 @@ describe("CLI dispatch", () => { expect(r.out.trim()).toMatch(/^nemoclaw v/); }); - it("-h exits 0", () => { + it("exits 0 for -h", () => { expect(run("-h").code).toBe(0); }); diff --git a/test/cli/onboard-compatibility.test.ts b/test/cli/onboard-compatibility.test.ts index 93b953f8a05..1f30478589c 100644 --- a/test/cli/onboard-compatibility.test.ts +++ b/test/cli/onboard-compatibility.test.ts @@ -156,7 +156,7 @@ describe("CLI onboard compatibility", () => { expect(r.out.includes("nemoclaw onboard")).toBeTruthy(); }); - it("#2753: refuses non-interactive --resume when sandbox step never completed and no name is provided", () => { + it("refuses non-interactive --resume when the sandbox step never completed and no name is provided (#2753)", () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cli-resume-no-name-")); const localBin = path.join(home, "bin"); const nemoclawDir = path.join(home, ".nemoclaw"); @@ -180,7 +180,7 @@ describe("CLI onboard compatibility", () => { expect(r.out.includes("--name ")).toBeTruthy(); }); - it("#2753: whitespace-only NEMOCLAW_SANDBOX_NAME does not satisfy the resume guard", () => { + it("does not let whitespace-only NEMOCLAW_SANDBOX_NAME satisfy the resume guard (#2753)", () => { // The env-var ingest pipeline trims and rejects whitespace-only values // before populating requestedSandboxName, so the guard sees no recovered // name and fires correctly. diff --git a/test/credentials.test.ts b/test/credentials.test.ts index e5d5e890bfb..f4e4e611a76 100644 --- a/test/credentials.test.ts +++ b/test/credentials.test.ts @@ -464,8 +464,8 @@ describe("legacy credentials.json migration (two-phase: stage then remove)", () }); }); -describe("removeLegacyCredentialsFileIfEmpty (post-upgrade cleanup, #3105)", () => { - it("removes an empty {} legacy file (regression #3105)", async () => { +describe("removeLegacyCredentialsFileIfEmpty post-upgrade cleanup (#3105)", () => { + it("removes an empty legacy file containing {} (#3105)", async () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-creds-")); const credsDir = path.join(home, ".nemoclaw"); const legacyFile = path.join(credsDir, "credentials.json"); diff --git a/test/destroy-wipe-sandbox-state.test.ts b/test/destroy-wipe-sandbox-state.test.ts index c6f10b256fa..f8a8b29d027 100644 --- a/test/destroy-wipe-sandbox-state.test.ts +++ b/test/destroy-wipe-sandbox-state.test.ts @@ -106,7 +106,7 @@ describe("wipeSandboxState (#5449)", () => { // shell-quoted but fed straight into `rm -rf -- ...` inside `cd ${dir}`, // where the relative form would traverse outside the agent config dir. // Validate paths against the resolved config dir and skip with a warning. - it("skips a state_dir whose resolved path escapes the agent config dir (#5455 PRA-6)", () => { + it("skips a state_dir whose resolved path escapes the agent config dir for PRA-6 (#5455)", () => { const { deps, runOpenshell } = buildDeps({ loadAgent: vi.fn(() => ({ configPaths: { dir: "/sandbox/.openclaw" }, @@ -138,7 +138,7 @@ describe("wipeSandboxState (#5449)", () => { } }); - it("skips a state_file whose resolved path escapes the agent config dir (#5455 PRA-6)", () => { + it("skips a state_file whose resolved path escapes the agent config dir for PRA-6 (#5455)", () => { const { deps, runOpenshell } = buildDeps({ loadAgent: vi.fn(() => ({ configPaths: { dir: "/sandbox/.openclaw" }, @@ -168,7 +168,7 @@ describe("wipeSandboxState (#5449)", () => { // script intact, single-quoted, with no expansion or word-splitting risk. // shellQuote already handles this; the assertion locks the contract in so a // future refactor of the targets-construction can't accidentally drop it. - it("shell-quotes accepted manifest paths so metacharacters cannot break out of `rm -rf` (#5455 PRA-3)", () => { + it("shell-quotes accepted manifest paths so metacharacters cannot break out of `rm -rf` for PRA-3 (#5455)", () => { const { deps, runOpenshell } = buildDeps({ loadAgent: vi.fn(() => ({ configPaths: { dir: "/sandbox/.openclaw" }, @@ -209,7 +209,7 @@ describe("wipeSandboxState (#5449)", () => { dir: "/sandbox/.openclaw/../../etc", label: "absolute path escapes after agent subdir via `..`", }, - ])("refuses to wipe when the agent config dir is unsafe ($label) (#5455 PRA-2)", ({ dir }) => { + ])("refuses to wipe when the $label agent config dir is unsafe for PRA-2 (#5455)", ({ dir }) => { const { deps, runOpenshell } = buildDeps({ loadAgent: vi.fn(() => ({ configPaths: { dir }, @@ -239,7 +239,7 @@ describe("wipeSandboxState (#5449)", () => { // re-onboard must NOT inherit USER.md from the prior sandbox. The proof // here is that the wipe script targets workspace/ under the agent config // dir AND contains no path escape that could rm -rf outside it. - it("targets workspace/ under the agent config dir and never contains a `..` escape (#5455 PRA-7)", () => { + it("targets workspace/ under the agent config dir without a `..` escape for PRA-7 (#5455)", () => { const { deps, runOpenshell } = buildDeps(); destroy.wipeSandboxState("test-sb", deps as never); @@ -269,7 +269,7 @@ describe("wipeSandboxState (#5449)", () => { // clean state. Skips on Windows because the `cd ... && rm -rf` script // is POSIX-shell-only. it.skipIf(process.platform === "win32")( - "actually deletes USER.md / SOUL.md when the constructed script is executed (#5455 PRA-1/PRA-2 behavioral)", + "deletes USER.md and SOUL.md when the constructed script executes for PRA-1 and PRA-2 (#5455)", () => { const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-wipe-behavioral-")); try { @@ -390,7 +390,7 @@ describe("wipeSandboxState (#5449)", () => { stateFiles: [{ path: "config.toml" }, { path: "hooks.json" }], label: "langchain-deepagents-code", }, - ])("wipes the shipped $label manifest shape under its own /sandbox/ dir (#5455 Ultra PRA-2)", ({ + ])("wipes the shipped $label manifest shape under its own /sandbox/ dir for Ultra PRA-2 (#5455)", ({ agent, configDir, stateDirs, @@ -418,7 +418,7 @@ describe("wipeSandboxState (#5449)", () => { // state_dirs and state_files must still issue the wipe so the multi-agent // `workspace-*` glob runs, but the `rm -rf --` argv must not collapse into // a syntactically broken command. - it("issues a syntactically valid wipe even when state_dirs and state_files are both empty (#5455 Ultra PRA-2)", () => { + it("issues a syntactically valid wipe with empty state_dirs and state_files for Ultra PRA-2 (#5455)", () => { const { deps, runOpenshell } = buildDeps({ loadAgent: vi.fn(() => ({ configPaths: { dir: "/sandbox/.openclaw" }, diff --git a/test/e2e-scenario/live/openclaw-tui-chat-correlation.test.ts b/test/e2e-scenario/live/openclaw-tui-chat-correlation.test.ts index 26034af662a..82736136578 100644 --- a/test/e2e-scenario/live/openclaw-tui-chat-correlation.test.ts +++ b/test/e2e-scenario/live/openclaw-tui-chat-correlation.test.ts @@ -488,7 +488,7 @@ async function runLiveIssue2603ReproWithEventCaptureRetry( // ─── The live regression guard ───────────────────────────────────── test( - "openclaw-tui-chat-correlation: rapid TUI/webchat sends stay correlated on a real OpenClaw sandbox (#2603 + #3145)", + "openclaw-tui-chat-correlation keeps rapid TUI and webchat sends correlated on a real OpenClaw sandbox (#2603, #3145)", async ({ artifacts, environment, onboard, sandbox, secrets }) => { secrets.required("NVIDIA_INFERENCE_API_KEY"); diff --git a/test/e2e-scenario/support-tests/ci-compatible-inference.test.ts b/test/e2e-scenario/support-tests/ci-compatible-inference.test.ts index 421c09820fb..95f8026f131 100644 --- a/test/e2e-scenario/support-tests/ci-compatible-inference.test.ts +++ b/test/e2e-scenario/support-tests/ci-compatible-inference.test.ts @@ -21,7 +21,7 @@ describe("gateway-managed compatible inference detection", () => { NVIDIA_INFERENCE_API_KEY: "hosted-compatible-test-key", }, }, - ])("skips the live issue #4434 sandbox-egress repro for $label", ({ env: hostedEnv }) => { + ])("skips the live sandbox-egress repro for $label (#4434)", ({ env: hostedEnv }) => { const { NEMOCLAW_E2E_USE_HOSTED_INFERENCE: _hostedSentinel, NEMOCLAW_PROVIDER: _provider, diff --git a/test/e2e-scenario/support-tests/e2e-scenario-matrix.test.ts b/test/e2e-scenario/support-tests/e2e-scenario-matrix.test.ts index 791d02f8f0d..8e9dd128f61 100644 --- a/test/e2e-scenario/support-tests/e2e-scenario-matrix.test.ts +++ b/test/e2e-scenario/support-tests/e2e-scenario-matrix.test.ts @@ -135,7 +135,7 @@ describe("live Vitest scenario matrix", () => { ]); }); - it("--emit-live-matrix prints a single-line JSON array for supported live Vitest scenarios", () => { + it("prints a single-line JSON array of supported live Vitest scenarios for --emit-live-matrix", () => { const result = runEmitLiveMatrix(); expect(result.status, result.stderr).toBe(0); const lines = result.stdout.trim().split("\n"); @@ -148,7 +148,7 @@ describe("live Vitest scenario matrix", () => { ]); }); - it("--emit-live-matrix honors explicit scenario selections", () => { + it("honors explicit scenario selections for --emit-live-matrix", () => { const result = runEmitLiveMatrix(["--scenarios", "ubuntu-repo-cloud-hermes"]); expect(result.status, result.stderr).toBe(0); const parsed = JSON.parse(result.stdout.trim()); diff --git a/test/gateway-state-reconcile-2276.test.ts b/test/gateway-state-reconcile-2276.test.ts index 11ff6948089..ea92feba053 100644 --- a/test/gateway-state-reconcile-2276.test.ts +++ b/test/gateway-state-reconcile-2276.test.ts @@ -293,7 +293,7 @@ afterEach(() => { }); // ─── Scenario 1 ─── connect is now non-destructive (#4497) ───────────────── -describe("Scenario 1: connect — healthy nemoclaw active + sandbox NotFound truly gone", () => { +describe("connect with a healthy active nemoclaw gateway and a truly gone sandbox in scenario 1", () => { it("preserves the registry entry and session, points at rebuild/destroy, and exits 1", { timeout: TIMEOUT_MS, }, () => { @@ -324,7 +324,7 @@ describe("Scenario 1: connect — healthy nemoclaw active + sandbox NotFound tru }); // ─── Scenario 2 ─── passive `status` must preserve registry state ───────── -describe("Scenario 2: status — healthy nemoclaw active + sandbox NotFound truly gone", () => { +describe("status with a healthy active nemoclaw gateway and a truly gone sandbox in scenario 2", () => { it("reports the missing live sandbox without removing local registry state", { timeout: TIMEOUT_MS, }, () => { @@ -352,7 +352,7 @@ describe("Scenario 2: status — healthy nemoclaw active + sandbox NotFound trul }); // ─── Scenario 3 ─── self-heal via gateway select succeeds ────────────────── -describe("Scenario 3: status — select succeeds, sandbox reappears, registry intact", () => { +describe("status preserves the registry when selection succeeds and the sandbox reappears in scenario 3", () => { it("attempts `gateway select nemoclaw`, re-queries, proceeds; registry preserved", { timeout: TIMEOUT_MS, }, () => { @@ -386,7 +386,7 @@ describe("Scenario 3: status — select succeeds, sandbox reappears, registry in }); // ─── Scenario 4 ─── select fails → wrong_gateway_active, registry intact ─── -describe("Scenario 4: connect — select fails, sandbox still NotFound", () => { +describe("connect when selection fails and the sandbox remains NotFound in scenario 4", () => { it("surfaces wrong_gateway_active guidance, preserves registry, exits 1", { timeout: TIMEOUT_MS, }, () => { @@ -419,7 +419,7 @@ describe("Scenario 4: connect — select fails, sandbox still NotFound", () => { }); // ─── Scenario 5 ─── exact #2276 repro: registry entry still present ──────── -describe("Scenario 5: #2276 repro — failed connect must leave registry entry intact", () => { +describe("failed connect leaves the registry entry intact in scenario 5 (#2276)", () => { it("after a failed connect triggered by drifted gateway, entry is still present", { timeout: TIMEOUT_MS, }, () => { @@ -446,7 +446,7 @@ describe("Scenario 5: #2276 repro — failed connect must leave registry entry i }); // ─── Scenario 6 ─── nemoclaw gateway missing + NotFound ──────────────────── -describe("Scenario 6: connect — nemoclaw gateway missing after restart", () => { +describe("connect with a missing nemoclaw gateway after restart in scenario 6", () => { it("returns gateway_missing_after_restart, preserves registry, exits 1", { timeout: TIMEOUT_MS, }, () => { @@ -476,7 +476,7 @@ describe("Scenario 6: connect — nemoclaw gateway missing after restart", () => }); // ─── Scenario 7 ─── nemoclaw gateway unreachable + NotFound ──────────────── -describe("Scenario 7: connect — nemoclaw gateway unreachable after restart", () => { +describe("connect with an unreachable nemoclaw gateway after restart in scenario 7", () => { it("returns gateway_unreachable_after_restart, preserves registry, exits 1", { timeout: TIMEOUT_MS, }, () => { @@ -506,7 +506,7 @@ describe("Scenario 7: connect — nemoclaw gateway unreachable after restart", ( }); // ─── Scenario 8 ─── gateway info fails / unparseable ─────────────────────── -describe("Scenario 8: gateway info fails — safe default, registry preserved", () => { +describe("gateway info failure preserves the registry with a safe default in scenario 8", () => { it("non-zero exit on `openshell gateway info -g nemoclaw` still preserves registry", { timeout: TIMEOUT_MS, }, () => { @@ -533,8 +533,8 @@ describe("Scenario 8: gateway info fails — safe default, registry preserved", }); // ─── Scenario 9 ─── openshell status empty / malformed ───────────────────── -describe("Scenario 9: empty or malformed status — registry untouched", () => { - it("empty status + gateway info missing → registry preserved, no removal", { +describe("empty or malformed status leaves the registry untouched in scenario 9", () => { + it("preserves the registry without removal when status is empty and gateway info is missing", { timeout: TIMEOUT_MS, }, () => { writeStubOpenshell({ @@ -557,7 +557,7 @@ describe("Scenario 9: empty or malformed status — registry untouched", () => { assert.doesNotMatch(r.stderr, /Removed stale local registry entry/); }); - it("malformed status + malformed gateway info → registry preserved", { + it("preserves the registry when status and gateway info are malformed", { timeout: TIMEOUT_MS, }, () => { writeStubOpenshell({ @@ -581,7 +581,7 @@ describe("Scenario 9: empty or malformed status — registry untouched", () => { }); // ─── Scenario 10 ─── non-interactive mode: no prompts ────────────────────── -describe("Scenario 10: non-interactive mode — deterministic exit, no prompts", () => { +describe("non-interactive mode exits deterministically without prompts in scenario 10", () => { it("NEMOCLAW_NON_INTERACTIVE=1 does not block on user input and exits 1", { timeout: TIMEOUT_MS, }, () => { @@ -609,7 +609,7 @@ describe("Scenario 10: non-interactive mode — deterministic exit, no prompts", }); // ─── Scenario 11 ─── cross-command parity: status drifts same way ────────── -describe("Scenario 11: status — wrong gateway active yields guidance, not removal", () => { +describe("status gives guidance instead of removal for the wrong active gateway in scenario 11", () => { it("drift case under `status` preserves registry and prints guidance", { timeout: TIMEOUT_MS, }, () => { @@ -641,7 +641,7 @@ describe("Scenario 11: status — wrong gateway active yields guidance, not remo }); // ─── Scenario 12 ─── cross-command parity: skill install drifts same way ─── -describe("Scenario 12: skill install — wrong gateway active yields guidance, not removal", () => { +describe("skill install gives guidance instead of removal for the wrong active gateway in scenario 12", () => { it("skill install under drift preserves registry, exits 1 with guidance", { timeout: TIMEOUT_MS, }, () => { @@ -710,7 +710,7 @@ describe("Scenario 12: skill install — wrong gateway active yields guidance, n // (a) locates the preserved entry (no "does not exist"), (b) does NOT dead-end // at "Cannot back up state", and (c) reports the stale state and proceeds to // recreate from the preserved registry metadata instead of aborting. -describe("Scenario 14 (#4497): connect preserves registry so rebuild can recover", () => { +describe("connect preserves the registry so rebuild can recover in scenario 14 (#4497)", () => { it("after a non-destructive connect, `rebuild --yes` recovers the stale sandbox", { timeout: TIMEOUT_MS, }, () => { diff --git a/test/generate-hermes-config.test.ts b/test/generate-hermes-config.test.ts index 90438f406b5..ce06ea66b7d 100644 --- a/test/generate-hermes-config.test.ts +++ b/test/generate-hermes-config.test.ts @@ -380,7 +380,7 @@ describe("agents/hermes/generate-config.ts", () => { ).toEqual(["INTERNAL_API line 3"]); }); - it("regression #4230: configures Anthropic Messages routing for Hermes managed inference", () => { + it("configures Anthropic Messages routing for Hermes managed inference (#4230)", () => { const { config } = runConfigScript({ NEMOCLAW_PROVIDER_KEY: "anthropic", NEMOCLAW_INFERENCE_BASE_URL: "https://inference.local", diff --git a/test/generate-openclaw-config.test.ts b/test/generate-openclaw-config.test.ts index f518a6e2794..1c3ef8ac1d9 100644 --- a/test/generate-openclaw-config.test.ts +++ b/test/generate-openclaw-config.test.ts @@ -356,13 +356,13 @@ describe("generate-openclaw-config.mts: config generation", () => { expect(config.gateway.controlUi.allowedOrigins).toEqual(["http://127.0.0.1:18789"]); }); - it("#3256: emits gateway.port from a non-default CHAT_UI_URL port", () => { + it("emits gateway.port from a non-default CHAT_UI_URL port (#3256)", () => { const config = runConfigScript({ CHAT_UI_URL: "http://127.0.0.1:18790" }); expect(config.gateway.port).toBe(18790); expect(config.gateway.controlUi.allowedOrigins).toEqual(["http://127.0.0.1:18790"]); }); - it("#3256: lets NEMOCLAW_DASHBOARD_PORT drive gateway.port when set", () => { + it("lets NEMOCLAW_DASHBOARD_PORT drive gateway.port when set (#3256)", () => { const config = runConfigScript({ CHAT_UI_URL: "", NEMOCLAW_DASHBOARD_PORT: "18790", @@ -402,7 +402,7 @@ describe("generate-openclaw-config.mts: config generation", () => { expect(config.gateway.controlUi.allowedOrigins).toContain("http://remote.example"); }); - it("includes portless origin for reverse-proxy access (Fixes #3000)", () => { + it("includes a portless origin for reverse-proxy access (#3000)", () => { const config = runConfigScript({ CHAT_UI_URL: "https://nemoclaw0-abc123.brevlab.com:18789", }); @@ -616,7 +616,7 @@ describe("generate-openclaw-config.mts: config generation", () => { expect(config.channels.discord.accounts.default.proxy).toBeUndefined(); }); - it("#3894: routes Discord gateway traffic through OpenClaw's managed proxy", () => { + it("routes Discord gateway traffic through OpenClaw's managed proxy (#3894)", () => { const channels = Buffer.from(JSON.stringify(["discord"])).toString("base64"); const config = runConfigScript({ NEMOCLAW_MESSAGING_CHANNELS_B64: channels, @@ -844,7 +844,7 @@ describe("generate-openclaw-config.mts: config generation", () => { expect(config.agents.defaults.heartbeat).toEqual({ every: "30m" }); }); - it("disables heartbeat when set to 0m (NemoClaw#2880)", () => { + it("disables heartbeat when set to 0m (#2880)", () => { const config = runConfigScript({ NEMOCLAW_AGENT_HEARTBEAT_EVERY: "0m" }); expect(config.agents.defaults.heartbeat).toEqual({ every: "0m" }); }); @@ -1818,13 +1818,13 @@ describe("generate-openclaw-config.mts: config generation", () => { expect(config.plugins.entries.xai.enabled).toBe(false); }); - it("#4246: enables the discord plugin entry when Discord channel is configured", () => { + it("enables the discord plugin entry when Discord is configured (#4246)", () => { const channels = Buffer.from(JSON.stringify(["discord"])).toString("base64"); const config = runConfigScript({ NEMOCLAW_MESSAGING_CHANNELS_B64: channels }); expect(config.plugins.entries.discord).toEqual({ enabled: true }); }); - it("#4246: omits the discord plugin entry when Discord channel is not configured", () => { + it("omits the discord plugin entry when Discord is not configured (#4246)", () => { const config = runConfigScript(); expect(config.plugins.entries.discord).toBeUndefined(); }); diff --git a/test/generate-platform-docs.test.ts b/test/generate-platform-docs.test.ts index 2db0c2be6f9..30f477d8099 100644 --- a/test/generate-platform-docs.test.ts +++ b/test/generate-platform-docs.test.ts @@ -271,7 +271,7 @@ print(module.generate_platform_table_full(platforms)) expect(full).toContain("WSL"); }); - it("--check exits non-zero on placeholder owner in real matrix", () => { + it("exits non-zero for --check on a placeholder owner in the real matrix", () => { const tmp = mkdtempSync(path.join(tmpdir(), "genplatform-")); const matrixPath = path.join(tmp, "matrix.json"); writeFileSync( diff --git a/test/hermes-gateway-wrapper.test.ts b/test/hermes-gateway-wrapper.test.ts index 64626fce5a9..13d04c0b74c 100644 --- a/test/hermes-gateway-wrapper.test.ts +++ b/test/hermes-gateway-wrapper.test.ts @@ -108,7 +108,7 @@ describe.skipIf(!canRun)("agents/hermes/hermes-wrapper.sh", () => { expect(run.realInvoked).toBe(false); }); - it("cannot be bypassed by shadowing python3 on PATH (#4981 review)", () => { + it("cannot be bypassed by shadowing python3 on PATH after review (#4981)", () => { // PATH is part of the untrusted env; a planted python3 that exits 0 must not // let the gateway start with a raw secret. The wrapper uses a trusted // absolute interpreter, so the guard still refuses. diff --git a/test/host-artifact-cleanup.test.ts b/test/host-artifact-cleanup.test.ts index 262a550a38d..675f3cb466e 100644 --- a/test/host-artifact-cleanup.test.ts +++ b/test/host-artifact-cleanup.test.ts @@ -47,7 +47,7 @@ afterEach(() => { vi.unstubAllEnvs(); }); -describe("cleanupStaleHostFiles (post-upgrade sweep, #3105)", () => { +describe("cleanupStaleHostFiles post-upgrade sweep (#3105)", () => { it("removes an empty legacy credentials.json and logs the removal", () => { const home = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-cleanup-")); const credsDir = path.join(home, ".nemoclaw"); diff --git a/test/http-proxy-fix-rewrite.test.ts b/test/http-proxy-fix-rewrite.test.ts index 7dae7ca38de..a5f24a7e9c6 100644 --- a/test/http-proxy-fix-rewrite.test.ts +++ b/test/http-proxy-fix-rewrite.test.ts @@ -51,7 +51,7 @@ function loadWrapper() { require(FIX_PATH); } -describe("http-proxy-fix rewrite (deepinfra-style failure, follow-up to #2344)", () => { +describe("http-proxy-fix rewrite for a deepinfra-style failure (#2344)", () => { let origHttpRequest: typeof http.request; let httpsSpy: ReturnType; let captured: RewrittenOptions | null; diff --git a/test/install-preflight.test.ts b/test/install-preflight.test.ts index a2504274080..13a53359b39 100644 --- a/test/install-preflight.test.ts +++ b/test/install-preflight.test.ts @@ -414,7 +414,7 @@ exit 98 expect(output).not.toMatch(/deprecated compatibility wrapper/); }); - it("--help exits 0 and shows install usage", () => { + it("exits 0 and shows install usage for --help", () => { const result = spawnSync("bash", [INSTALLER, "--help"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -449,7 +449,7 @@ exit 98 expect(output).toMatch(/aliases: cloud -> build, nim -> nim-local/); }); - it("--version exits 0 and prints the version number", () => { + it("exits 0 and prints the version number for --version", () => { const result = spawnSync("bash", [INSTALLER, "--version"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -461,7 +461,7 @@ exit 98 expect(output).not.toMatch(/0\.1\.0/); }); - it("-v exits 0 and prints the version number", () => { + it("exits 0 and prints the version number for -v", () => { const result = spawnSync("bash", [INSTALLER, "-v"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -982,7 +982,7 @@ fi`, // #2430: --fresh is the escape hatch. Even with a session file on disk // (failed or otherwise), the installer should skip the auto-resume check // and let the onboard command create a new session. - it("--fresh skips auto-resume regardless of session state (#2430)", () => { + it("skips auto-resume with --fresh regardless of session state (#2430)", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-install-fresh-")); const fakeBin = path.join(tmp, "bin"); const prefix = path.join(tmp, "prefix"); @@ -2900,7 +2900,7 @@ describe("installer flag parsing", () => { expect(output).toMatch(/NemoClaw Installer/); // usage was printed }); - it("--help shows NEMOCLAW_INSTALL_TAG in environment section", () => { + it("shows NEMOCLAW_INSTALL_TAG in the --help environment section", () => { const result = spawnSync("bash", [INSTALLER, "--help"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -3125,7 +3125,7 @@ exit 0`, return { result, args }; } - it("#2670: ACCEPT_THIRD_PARTY_SOFTWARE=1 alone clears the notice in non-TTY mode", () => { + it("clears the notice in non-TTY mode with ACCEPT_THIRD_PARTY_SOFTWARE=1 alone (#2670)", () => { const { result, args } = callShowUsageNotice({ // Simulates curl|bash mode: stdin is not a TTY, NON_INTERACTIVE is unset, // and only --yes-i-accept-third-party-software was passed. @@ -3160,7 +3160,7 @@ exit 0`, expect(output).not.toMatch(/\/dev\/tty/); }); - it("#3058: error message includes a working curl|bash example users can copy-paste", () => { + it("includes a working curl|bash example users can copy-paste in the error message (#3058)", () => { // The reporter on #3058 hit this error with `curl ... | bash` on a // non-TTY box and was left guessing how to combine the env var with // the documented one-liner. The fix surfaces the exact invocations @@ -3772,7 +3772,7 @@ sys.exit(exit_code) return runInstallerWithTty(answer, "tty"); } - it("#2671: headless curl|bash with no flags exits 1 BEFORE phase 1 (atomic — no Node/CLI install)", () => { + it("exits 1 before phase 1 for headless curl|bash with no flags and installs nothing (#2671)", () => { const { result, phases } = runInstaller({}); expect(result.status).not.toBe(0); const output = `${result.stdout}${result.stderr}`; @@ -3855,7 +3855,7 @@ sys.exit(exit_code) expect(state).toBe(""); }); - it("--non-interactive alone with a controlling TTY still stops before phase 1", () => { + it("stops before phase 1 for --non-interactive alone with a controlling TTY", () => { const { result, phases, state } = runInstallerWithTty("yes\n", "pipe", { NEMOCLAW_NON_INTERACTIVE: "1", }); @@ -3871,7 +3871,7 @@ sys.exit(exit_code) expect(state).toBe(""); }); - it("--yes-i-accept-third-party-software alone is sufficient to clear the fail-fast gate", () => { + it("clears the fail-fast gate with --yes-i-accept-third-party-software alone", () => { // The flag implies non-interactive intent (set by main() before the // preflight check), so it must clear the gate AND let the install // progress past preflight into phase 1 — assert phases is non-empty @@ -3883,7 +3883,7 @@ sys.exit(exit_code) expect(phases).not.toBe(""); }); - it("--non-interactive alone does not clear the fail-fast gate", () => { + it("does not clear the fail-fast gate with --non-interactive alone", () => { const { result, phases } = runInstaller({ NEMOCLAW_NON_INTERACTIVE: "1" }); const output = `${result.stdout}${result.stderr}`; expect(result.status).not.toBe(0); diff --git a/test/install-stage-from-stdin.test.ts b/test/install-stage-from-stdin.test.ts index b6f66c1fb9e..48b7c113387 100644 --- a/test/install-stage-from-stdin.test.ts +++ b/test/install-stage-from-stdin.test.ts @@ -126,7 +126,7 @@ function runEntryGuard(opts: { }; } -describe("install.sh entry-guard staging — #4414 curl|bash stdin self-stage", () => { +describe("install.sh entry-guard staging for curl|bash stdin self-stage (#4414)", () => { it("stages to /tmp and would exec bash on the staged file when invoked via curl|bash", () => { // Pipe-mode invocation: BASH_SOURCE[0] empty. Without staging, // ensure_docker's sg(1) re-exec from #4419 has no file to point at diff --git a/test/install-upgrade-sandboxes-severity.test.ts b/test/install-upgrade-sandboxes-severity.test.ts index 7ba627b4c58..355ea81f136 100644 --- a/test/install-upgrade-sandboxes-severity.test.ts +++ b/test/install-upgrade-sandboxes-severity.test.ts @@ -95,7 +95,7 @@ describe("install.sh print_done — auto-upgrade severity (#5735)", () => { }); }); -describe("install.sh finalize_install — fatal exit on failed auto-upgrade (#5735 PRA-5)", () => { +describe("install.sh finalize_install fatal exit on failed auto-upgrade for PRA-5 (#5735)", () => { it("exits zero and prints the clean banner when no upgrade failed", () => { const result = runFinalizeInstall(false); expect(result.status, result.stderr).toBe(0); diff --git a/test/issue-4434-tui-unreachable-inference.test.ts b/test/issue-4434-tui-unreachable-inference.test.ts index e679af4615a..84a02e8cc0f 100644 --- a/test/issue-4434-tui-unreachable-inference.test.ts +++ b/test/issue-4434-tui-unreachable-inference.test.ts @@ -98,7 +98,7 @@ function driveMockOpenClawGatewayChatPath(params: { }; } -describe("issue #4434 unreachable inference TUI behavior", () => { +describe("unreachable inference TUI behavior (#4434)", () => { it("classifies the captured spinner plus connected status with no error as the broken signature", () => { const capture = [ " flibbertigibbeting... - 3m 42s | connected", diff --git a/test/issue-5667-hosted-inference-model-namespace.test.ts b/test/issue-5667-hosted-inference-model-namespace.test.ts index 1fd1b9b1e5e..bbe15e7f082 100644 --- a/test/issue-5667-hosted-inference-model-namespace.test.ts +++ b/test/issue-5667-hosted-inference-model-namespace.test.ts @@ -121,7 +121,7 @@ function writeFakeDeepAgentsCodeModule(tmpDir: string): string { return pythonPath; } -describe("issue #5667: hosted inference default model namespace", () => { +describe("hosted inference default model namespace (#5667)", () => { // Snapshot the whole environment and restore it wholesale so the teardown // stays linear (no per-key conditional): clear every key, then repopulate // from the snapshot. Keys added during a test are dropped; original values diff --git a/test/messaging-build-applier.test.ts b/test/messaging-build-applier.test.ts index 4649c6996eb..c32ecd8a5bc 100644 --- a/test/messaging-build-applier.test.ts +++ b/test/messaging-build-applier.test.ts @@ -721,7 +721,7 @@ describe("messaging-build-applier.mts: agent-install", () => { expect(result.stderr).toContain("unexpected-package==1.2.3"); }); - it("#4246: messaging post-agent-install render reaches the mocked OpenClaw doctor boundary", () => { + it("reaches the mocked OpenClaw doctor boundary during post-agent-install messaging render (#4246)", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-discord-runtime-contract-")); const tracePath = path.join(tmp, "openclaw.trace"); const fakeOpenclaw = path.join(tmp, "openclaw"); diff --git a/test/nemo-deepagents-alias.test.ts b/test/nemo-deepagents-alias.test.ts index 07c9ac0e5d7..69677ad67a7 100644 --- a/test/nemo-deepagents-alias.test.ts +++ b/test/nemo-deepagents-alias.test.ts @@ -83,7 +83,7 @@ describe("nemo-deepagents alias", () => { expect(stat.mode & 0o100).not.toBe(0); }); - it("--version outputs nemo-deepagents branding", () => { + it("outputs nemo-deepagents branding for --version", () => { const { code, out } = runDeepAgents("--version"); expect(code).toBe(0); expect(out).toMatch(/^nemo-deepagents v[\d.]+/); diff --git a/test/nemoclaw-start-gateway-health.test.ts b/test/nemoclaw-start-gateway-health.test.ts index 878c37aa111..552b9e40aff 100644 --- a/test/nemoclaw-start-gateway-health.test.ts +++ b/test/nemoclaw-start-gateway-health.test.ts @@ -358,7 +358,7 @@ describe("gateway serving watchdog (#4710)", () => { }); describe("record_gateway_pid", () => { - it("replaces a planted symlink without writing through it (#4710 pidfile race)", () => { + it("replaces a planted symlink without writing through it during the pidfile race (#4710)", () => { // In root mode the pidfile lives in sticky /tmp; a sandbox process can // plant a symlink at that path between respawns. The update must replace // the symlink as a directory entry (atomic rename), never open it. diff --git a/test/nemoclaw-start-plugin-refresh.test.ts b/test/nemoclaw-start-plugin-refresh.test.ts index a9158caf5ff..aa27cad13c6 100644 --- a/test/nemoclaw-start-plugin-refresh.test.ts +++ b/test/nemoclaw-start-plugin-refresh.test.ts @@ -268,7 +268,7 @@ describe("plugin refresh log preparation", () => { }); }); -describe("plugin registry refresh workaround (#2021, openclaw/openclaw#89606)", () => { +describe("plugin registry refresh workaround for openclaw/openclaw#89606 (#2021)", () => { it("invokes `openclaw plugins registry --refresh` once the gateway reports ready", () => { const { result, refreshLog, callLog, tmpDir } = runRefreshBlock(); try { diff --git a/test/nemoclaw-start.test.ts b/test/nemoclaw-start.test.ts index 7f05a16439e..192325648d0 100644 --- a/test/nemoclaw-start.test.ts +++ b/test/nemoclaw-start.test.ts @@ -484,7 +484,7 @@ describe("nemoclaw-start non-root fallback", () => { expect(result.stdout).not.toContain("SHOULD_NOT_CONFIGURE"); }); - it("#3256: only requires early gateway token generation for gateway and OpenClaw commands", () => { + it("only requires early gateway token generation for gateway and OpenClaw commands (#3256)", () => { const src = fs.readFileSync(START_SCRIPT, "utf-8"); const script = [ "set -euo pipefail", @@ -507,7 +507,7 @@ describe("nemoclaw-start non-root fallback", () => { expect(result.stdout).toContain("no:bash"); }); - it("#4517: refreshes startup tokens but only ensures direct OpenClaw command tokens", () => { + it("refreshes startup tokens but only ensures direct OpenClaw command tokens (#4517)", () => { const src = fs.readFileSync(START_SCRIPT, "utf-8"); const script = [ "set -euo pipefail", @@ -752,7 +752,7 @@ describe("nemoclaw-start gateway token export (#1114)", () => { expect(envFile).not.toContain(".profile"); }); - it("#3256: writes gateway port and URL into the runtime shell env", () => { + it("writes the gateway port and URL into the runtime shell env (#3256)", () => { const { result, envFile } = runGatewayTokenHarness( JSON.stringify({ gateway: { auth: { token: "token" } } }), "stale-token", @@ -766,7 +766,7 @@ describe("nemoclaw-start gateway token export (#1114)", () => { expect(envFile).toContain("export OPENCLAW_GATEWAY_TOKEN='token'"); }); - it("#3730: writes OpenClaw state env for connect-shell pairing approval", () => { + it("writes OpenClaw state env for connect-shell pairing approval (#3730)", () => { const { result, envFile } = runGatewayTokenHarness( JSON.stringify({ gateway: { auth: { token: "token" } } }), ); @@ -781,7 +781,7 @@ describe("nemoclaw-start gateway token export (#1114)", () => { ); }); - it("#3256: generates a gateway token before writing the runtime shell env", () => { + it("generates a gateway token before writing the runtime shell env (#3256)", () => { const { result, envFile, configAfter, hashAfter } = runGatewayTokenHarness( JSON.stringify({ gateway: { auth: {} } }), "stale-token", @@ -800,7 +800,7 @@ describe("nemoclaw-start gateway token export (#1114)", () => { expect(hashAfter).toMatch(/ openclaw\.json\n$/); }); - it("#4517: rotates an existing gateway token before writing the runtime shell env", () => { + it("rotates an existing gateway token before writing the runtime shell env (#4517)", () => { const oldToken = "old-token-before-rebuild"; const { result, envFile, configAfter, hashAfter } = runGatewayTokenHarness( JSON.stringify({ gateway: { auth: { token: oldToken } } }), @@ -820,7 +820,7 @@ describe("nemoclaw-start gateway token export (#1114)", () => { expect(hashAfter).toMatch(/ openclaw\.json\n$/); }); - it("#4517: rotates an existing gateway token from JSON5 config", () => { + it("rotates an existing gateway token from JSON5 config (#4517)", () => { const oldToken = "old-json5-token-before-rebuild"; const { result, envFile, configAfter, hashAfter } = runGatewayTokenHarness( [ @@ -1028,7 +1028,7 @@ describe("nemoclaw-start configure guard behavior", () => { fs.rmSync(setup.tmpDir, { recursive: true, force: true }); } }); - it("#4462: unsets gateway env and recovers constrained replacement state", () => { + it("unsets gateway env and recovers constrained replacement state (#4462)", () => { const setup = writeProxyEnvWithGuard(); const stateDir = path.join(setup.tmpDir, "openclaw-state"); const devicesDir = path.join(stateDir, "devices"); @@ -1103,7 +1103,7 @@ exit 1 // existing test above only exercises `add slack`. Lock in coverage for every // (channel × op) combo so the guard cannot regress for any one of them // while passing for another. - it("#2592: blocks every (channel × op) mutating combo and surfaces the host-side hint", () => { + it("blocks every mutating channel-operation combination and surfaces the host-side hint (#2592)", () => { const setup = writeProxyEnvWithGuard(); try { const channels = ["slack", "telegram", "discord", "wechat", "whatsapp"]; diff --git a/test/nemohermes-alias.test.ts b/test/nemohermes-alias.test.ts index f355a1db11c..4ae5fbb4068 100644 --- a/test/nemohermes-alias.test.ts +++ b/test/nemohermes-alias.test.ts @@ -77,7 +77,7 @@ describe("nemohermes alias", () => { expect(stat.mode & 0o100).not.toBe(0); }); - it("--version outputs nemohermes branding", () => { + it("outputs nemohermes branding for --version", () => { const { code, out } = runHermes("--version"); expect(code).toBe(0); expect(out).toMatch(/^nemohermes v[\d.]+/); diff --git a/test/ollama-tools-capability.test.ts b/test/ollama-tools-capability.test.ts index 865ce12ecfd..ada1675eb67 100644 --- a/test/ollama-tools-capability.test.ts +++ b/test/ollama-tools-capability.test.ts @@ -347,7 +347,7 @@ describe("checkOllamaModelToolSupport", () => { } }); - it("interactive yes → {ok:true, allowToolsIncompatible:true}", async () => { + it("allows a tools-incompatible model after interactive confirmation", async () => { const h = loadProxyWithStubs(); h.setProbeResult({ source: "api", @@ -365,7 +365,7 @@ describe("checkOllamaModelToolSupport", () => { expect(h.promptCalls.length).toBeGreaterThan(0); }); - it("interactive no → {ok:false} with 'choose a tools-capable model' message", async () => { + it("returns ok=false with guidance after interactive rejection", async () => { const h = loadProxyWithStubs(); h.setProbeResult({ source: "api", @@ -379,7 +379,7 @@ describe("checkOllamaModelToolSupport", () => { expect(out.message!.toLowerCase()).toContain("tools-capable"); }); - it("non-interactive default → {ok:false} with stderr containing NEMOCLAW_OLLAMA_REQUIRE_TOOLS=0", async () => { + it("returns ok=false with override guidance by default in non-interactive mode", async () => { process.env.NEMOCLAW_NON_INTERACTIVE = "1"; const h = loadProxyWithStubs(); h.setProbeResult({ @@ -392,7 +392,7 @@ describe("checkOllamaModelToolSupport", () => { expect(h.errors.some((e) => e.includes("NEMOCLAW_OLLAMA_REQUIRE_TOOLS=0"))).toBe(true); }); - it("non-interactive + NEMOCLAW_OLLAMA_REQUIRE_TOOLS=0 → {ok:true, allowToolsIncompatible:true} after stderr warning", async () => { + it("allows a tools-incompatible model with NEMOCLAW_OLLAMA_REQUIRE_TOOLS=0 after warning", async () => { process.env.NEMOCLAW_NON_INTERACTIVE = "1"; process.env.NEMOCLAW_OLLAMA_REQUIRE_TOOLS = "0"; const h = loadProxyWithStubs(); @@ -411,7 +411,7 @@ describe("checkOllamaModelToolSupport", () => { expect(matched).toBe(true); }); - it("NEMOCLAW_YES=1 → {ok:true, allowToolsIncompatible:true} after note", async () => { + it("allows a tools-incompatible model with NEMOCLAW_YES=1 after a note", async () => { process.env.NEMOCLAW_YES = "1"; const h = loadProxyWithStubs(); h.setProbeResult({ @@ -512,7 +512,7 @@ describe("checkOllamaModelToolSupport", () => { expect(confirm).not.toHaveBeenCalled(); }); - it("probe failed (capabilities unknown) → {ok:true} (graceful degradation)", async () => { + it("returns ok=true for graceful degradation after a capability probe failure", async () => { const h = loadProxyWithStubs(); h.setProbeResult({ source: "unknown", @@ -665,8 +665,8 @@ describe("validateOllamaModel — no-tools override propagation (#4241)", () => // validateOllamaModel does not know about the override and re-rejects. // ───────────────────────────────────────────────────────────────── -describe("override propagation across checkOllamaModelToolSupport → validateOllamaModel (#4241)", () => { - it("user accepts override → later validateOllamaModel does NOT reject for the same tools-incompatible error", async () => { +describe("override propagation from checkOllamaModelToolSupport to validateOllamaModel (#4241)", () => { + it("does not reject the same tools-incompatible error after the user accepts an override", async () => { const h = loadProxyWithStubs(); h.setProbeResult({ source: "api", @@ -702,7 +702,7 @@ describe("override propagation across checkOllamaModelToolSupport → validateOl expect(result.ok).toBe(true); }); - it("user declines override → both stages refuse and no later validation runs", async () => { + it("refuses both stages without later validation after the user declines an override", async () => { const h = loadProxyWithStubs(); h.setProbeResult({ source: "api", diff --git a/test/onboard-ollama-autostart.test.ts b/test/onboard-ollama-autostart.test.ts index 7a35c3d8332..a627af8421e 100644 --- a/test/onboard-ollama-autostart.test.ts +++ b/test/onboard-ollama-autostart.test.ts @@ -346,8 +346,8 @@ process.exit = (code) => { return JSON.parse(lastBraceLine); } -describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { - it("Scenario A: stopped Ollama + flag set → no spawn, warning, falls back to DEFAULT_OLLAMA_MODEL", { +describe("nemoclaw onboard --no-ollama-autostart (#3751)", () => { + it("avoids spawning stopped Ollama, warns, and falls back to DEFAULT_OLLAMA_MODEL with the flag in scenario A", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { const payload = runOllamaAutostartScenario({ @@ -403,7 +403,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { ); }); - it("Scenario B: stopped Ollama + flag NOT set → existing spawn path preserved", { + it("preserves the existing spawn path for stopped Ollama without the flag in scenario B", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { const payload = runOllamaAutostartScenario({ @@ -436,7 +436,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { ); }); - it("Scenario C (flag unset): Ollama already running → behavior unchanged, no spawn, no warning", { + it("leaves running Ollama unchanged without spawning or warning when the flag is unset in scenario C", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { const payload = runOllamaAutostartScenario({ @@ -465,7 +465,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { assert.equal(payload.sentinelTripped, true); }); - it("Scenario C (flag set): Ollama already running + flag set → no warning, no spawn", { + it("avoids warning or spawning when Ollama is running and the flag is set in scenario C", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { const payload = runOllamaAutostartScenario({ @@ -486,7 +486,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { assert.equal(payload.sentinelTripped, true); }); - it("Scenario D: non-interactive + flag set → no process.exit, warning, model = DEFAULT_OLLAMA_MODEL", { + it("warns and selects DEFAULT_OLLAMA_MODEL without process.exit in non-interactive scenario D", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { const payload = runOllamaAutostartScenario({ @@ -526,7 +526,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { assert.equal(payload.sentinelTripped, false); }); - it("Scenario G (#4365): pinned-provider runner crash exits instead of looping on Ollama model selection", { + it("exits instead of looping on Ollama model selection after a pinned-provider runner crash in scenario G (#4365)", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { // Reporter's second-step: Ollama responds, user reaches model selection, @@ -565,7 +565,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { ); }); - it("Scenario H (#4365): pinned-provider runner crash also exits when NEMOCLAW_PROVIDER uses a casing variant", { + it("exits after a pinned-provider runner crash with a casing variant in scenario H (#4365)", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { // NEMOCLAW_PROVIDER=OLLAMA is accepted by getNonInteractiveProvider's @@ -597,7 +597,7 @@ describe("nemoclaw onboard --no-ollama-autostart (issue #3751)", () => { ); }); - it("Scenario E: stopped Ollama + flag NOT set + NEMOCLAW_PROVIDER=ollama + waitForHttp timeout → process.exit, no selectionLoop re-entry", { + it("exits without re-entering selectionLoop after an Ollama waitForHttp timeout in scenario E", { timeout: OLLAMA_AUTOSTART_TEST_TIMEOUT_MS, }, () => { // Reporter scenario: provider pinned via env, gate not set, Ollama diff --git a/test/onboard-preset-diff.test.ts b/test/onboard-preset-diff.test.ts index cc00fba546c..60b58d3e01b 100644 --- a/test/onboard-preset-diff.test.ts +++ b/test/onboard-preset-diff.test.ts @@ -128,7 +128,7 @@ const { setupPoliciesWithSelection } = require(${onboardPath}); `; } -describe("setupPoliciesWithSelection preset-diff (issue #2177)", () => { +describe("setupPoliciesWithSelection preset diff (#2177)", () => { // In non-interactive mode a user who runs onboard twice — first with Balanced // defaults (applies 5 presets), second with NEMOCLAW_POLICY_PRESETS=npm — // expects the final sandbox to have ONLY npm. Previously-applied presets diff --git a/test/onboard.test.ts b/test/onboard.test.ts index 51552087d86..4057cdcb861 100644 --- a/test/onboard.test.ts +++ b/test/onboard.test.ts @@ -437,7 +437,7 @@ startGateway(null).catch(() => {}); } }); - it("#2753: ignores an incomplete session sandbox name when checking resume conflicts", () => { + it("ignores an incomplete session sandbox name when checking resume conflicts (#2753)", () => { // A pre-fix on-disk session may carry sandboxName even though the // sandbox step never completed. Treating that as a conflict source // would block users from running `--resume --name ` to recover. @@ -4667,7 +4667,7 @@ const { setupInference } = require(${onboardPath}); assert.equal(commands.length, 4); }); - it("regression #1904: pullAndResolveBaseImageDigest uses sandbox-base registry", () => { + it("uses the sandbox-base registry in pullAndResolveBaseImageDigest (#1904)", () => { // Structural check: verify the constant matches the Dockerfile default // and does NOT reference the openshell-community registry. assert.ok( diff --git a/test/openclaw-chat-send-patch.test.ts b/test/openclaw-chat-send-patch.test.ts index d19ed827958..d3500b0a5bc 100644 --- a/test/openclaw-chat-send-patch.test.ts +++ b/test/openclaw-chat-send-patch.test.ts @@ -686,7 +686,7 @@ describe("OpenClaw chat.send compatibility patch", () => { } }); - it("--audit reports all recognizers as would-apply on fresh fixtures without mutating files", () => { + it("reports all recognizers as would-apply on fresh fixtures without mutation for --audit", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-openclaw-chat-send-audit-fresh-")); const dist = path.join(tmp, "dist"); fs.mkdirSync(dist); @@ -720,7 +720,7 @@ describe("OpenClaw chat.send compatibility patch", () => { } }); - it("--audit reports all recognizers as already-applied on a patched dist", () => { + it("reports all recognizers as already-applied on a patched dist for --audit", () => { const tmp = fs.mkdtempSync( path.join(os.tmpdir(), "nemoclaw-openclaw-chat-send-audit-applied-"), ); @@ -748,7 +748,7 @@ describe("OpenClaw chat.send compatibility patch", () => { } }); - it("--audit exits non-zero and surfaces a per-recognizer miss without mutating files", () => { + it("exits non-zero and surfaces a per-recognizer miss without mutation for --audit", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-openclaw-chat-send-audit-miss-")); const dist = path.join(tmp, "dist"); fs.mkdirSync(dist); @@ -780,7 +780,7 @@ describe("OpenClaw chat.send compatibility patch", () => { } }); - it("--audit exits non-zero and reports each missing file when selectors fail", () => { + it("exits non-zero and reports each missing file for --audit when selectors fail", () => { const tmp = fs.mkdtempSync( path.join(os.tmpdir(), "nemoclaw-openclaw-chat-send-audit-not-found-"), ); @@ -800,7 +800,7 @@ describe("OpenClaw chat.send compatibility patch", () => { } }); - it("--audit rejects extra positional arguments", () => { + it("rejects extra positional arguments for --audit", () => { const result = spawnSync(process.execPath, [PATCH_SCRIPT, "--audit", "/nonexistent", "extra"], { encoding: "utf-8", timeout: 10000, diff --git a/test/openclaw-tui-chat-correlation.test.ts b/test/openclaw-tui-chat-correlation.test.ts index aca870d36d1..c948091f245 100644 --- a/test/openclaw-tui-chat-correlation.test.ts +++ b/test/openclaw-tui-chat-correlation.test.ts @@ -579,7 +579,7 @@ function runLiveIssue2603ReproWithEventCaptureRetry(sandboxName: string): LiveIs } describe("OpenClaw TUI chat correlation regression (#2603)", () => { - it("classifies the observed #2603 gateway trace as broken", () => { + it("classifies the observed gateway trace as broken (#2603)", () => { const analysis = analyzeIssue2603Trace(capturedIssue2603Trace); expect(analysis.emptyFinalsForSubmittedRuns).toEqual([ diff --git a/test/package-contract/cli/policy-dispatch.test.ts b/test/package-contract/cli/policy-dispatch.test.ts index fef7099de84..c673473157a 100644 --- a/test/package-contract/cli/policy-dispatch.test.ts +++ b/test/package-contract/cli/policy-dispatch.test.ts @@ -239,7 +239,7 @@ Promise.resolve(require(${CLI_PATH}).mainPromise).finally(() => { expect(result.stderr).toMatch(/Aborting --from-dir/); }); - it("--from-dir skips hidden dotfile yaml presets", () => { + it("skips hidden dotfile YAML presets for --from-dir", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-from-dir-hidden-")); fs.writeFileSync(path.join(dir, ".bad.yaml"), "preset:\n name: bad\nnetwork_policies: {}\n"); fs.writeFileSync( @@ -260,7 +260,7 @@ Promise.resolve(require(${CLI_PATH}).mainPromise).finally(() => { expect(result.stderr).toMatch(/Directory not found/); }); - it("--from-dir skips sub-directories whose names end in .yaml/.yml", () => { + it("skips subdirectories ending in .yaml or .yml for --from-dir", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-from-dir-skipdir-")); // A real preset file and a directory that happens to match the yaml glob. fs.writeFileSync( diff --git a/test/package-contract/repro-2010.test.ts b/test/package-contract/repro-2010.test.ts index c663af8ac5e..a20c7153215 100644 --- a/test/package-contract/repro-2010.test.ts +++ b/test/package-contract/repro-2010.test.ts @@ -153,7 +153,7 @@ function callGetGatewayPresets( return JSON.parse(stdout.trim()); } -describe("issue #2010 — policy state inconsistency", () => { +describe("policy state inconsistency (#2010)", () => { describe("getGatewayPresets — matching logic", () => { it("returns telegram when gateway has telegram policy loaded", () => { const result = callGetGatewayPresets(buildGatewayYaml(["telegram"])); diff --git a/test/package-contract/ssrf-parity.test.ts b/test/package-contract/ssrf-parity.test.ts index 6ce37378a97..518d11c9180 100644 --- a/test/package-contract/ssrf-parity.test.ts +++ b/test/package-contract/ssrf-parity.test.ts @@ -295,7 +295,7 @@ describe("CLI and plugin isPrivateHostname agree on every CIDR boundary", () => ]; for (const [addr, expected, label] of vectors) { - it(`${label}: ${addr} → ${String(expected)}`, () => { + it(`classifies ${label} at ${addr} as ${String(expected)}`, () => { expect(pluginHelper.isPrivateHostname(addr)).toBe(expected); expect(cliHelper.isPrivateHostname(addr)).toBe(expected); }); @@ -332,7 +332,7 @@ describe("CLI and plugin isPrivateHostname agree on wrapper-level cases", () => ]; for (const [addr, expected, label] of extras) { - it(`${label}: ${JSON.stringify(addr)} → ${String(expected)}`, () => { + it(`classifies ${label} at ${JSON.stringify(addr)} as ${String(expected)}`, () => { expect(pluginHelper.isPrivateHostname(addr)).toBe(expected); expect(cliHelper.isPrivateHostname(addr)).toBe(expected); }); diff --git a/test/platform.test.ts b/test/platform.test.ts index ff68772a624..cf9e6771e2f 100644 --- a/test/platform.test.ts +++ b/test/platform.test.ts @@ -236,7 +236,7 @@ describe("platform helpers", () => { }); }); - it("discovers the bare ~/.colima/docker.sock layout (regression for #3503)", () => { + it("discovers the bare ~/.colima/docker.sock layout (#3503)", () => { // The reporter's Colima setup puts the socket at the top-level // ~/.colima/docker.sock rather than under ~/.colima/default/. Before // this fix, detection returned null and the gateway fell back to diff --git a/test/policies.test.ts b/test/policies.test.ts index 68755169487..df83d4922cc 100644 --- a/test/policies.test.ts +++ b/test/policies.test.ts @@ -862,7 +862,7 @@ exit 1 // (the installer's user-local location) but PATH from a non-interactive shell does // not include ~/.local/bin/, buildPolicySetCommand / buildPolicyGetCommand must // resolve openshell to an absolute path so spawnSync does not raise ENOENT. - describe("issue 4224: spawnSync openshell ENOENT in non-interactive shells", () => { + describe("spawnSync openshell ENOENT in non-interactive shells (#4224)", () => { let tmpHome: string; let fakeOpenshell: string; let origHome: string | undefined; @@ -1003,7 +1003,7 @@ exit 1 }); }); - describe("issue 4586: preset apply must not overwrite a live policy that could not be read", () => { + describe("preset apply must not overwrite a live policy that could not be read (#4586)", () => { const registryModule = requireForTest( path.join(REPO_ROOT, "src", "lib", "state", "registry.ts"), ) as Record; @@ -1104,7 +1104,7 @@ exit 1 }); }); - describe("issue 4510: policy-add --from-file false success when the sandbox is absent from the registry", () => { + describe("policy-add --from-file false success when the sandbox is absent from the registry (#4510)", () => { const registryModule = requireForTest( path.join(REPO_ROOT, "src", "lib", "state", "registry.ts"), ) as Record; diff --git a/test/policy-tiers.test.ts b/test/policy-tiers.test.ts index 1bff6d702af..df79f318903 100644 --- a/test/policy-tiers.test.ts +++ b/test/policy-tiers.test.ts @@ -71,7 +71,7 @@ describe("tiers", () => { expect(listTiers()).toHaveLength(3); }); - it("tiers are ordered restricted → balanced → open", () => { + it("orders tiers as restricted, balanced, then open", () => { const names = listTiers().map((tier: Tier) => tier.name); expect(names).toEqual(["restricted", "balanced", "open"]); }); diff --git a/test/rebuild-credential-hydration.test.ts b/test/rebuild-credential-hydration.test.ts index f3b50b4fe9b..3434de2a4d1 100644 --- a/test/rebuild-credential-hydration.test.ts +++ b/test/rebuild-credential-hydration.test.ts @@ -106,7 +106,7 @@ process.stdout.write(JSON.stringify(payload)); return { result, tmpDir }; } -describe("Issue #2273 Layer 1: credential hydration from legacy storage", () => { +describe("credential hydration from legacy storage, layer 1 (#2273)", () => { // Test each provider's credential env to ensure parametric coverage const providers = [ { diff --git a/test/rebuild-credential-preflight.test.ts b/test/rebuild-credential-preflight.test.ts index 18a0c768413..3e3dc716bec 100644 --- a/test/rebuild-credential-preflight.test.ts +++ b/test/rebuild-credential-preflight.test.ts @@ -359,7 +359,7 @@ function registryHasSandbox(fixture: ReturnType): boolean } } -describe("Issue #2273: atomic rebuild", () => { +describe("atomic rebuild (#2273)", () => { describe("Layer 2: preflight credential check", () => { it("cancels interactive rebuild before credential preflight or backup on non-affirmative input", { timeout: 60_000, @@ -616,7 +616,7 @@ describe("Issue #2273: atomic rebuild", () => { it.each([ ["ollama-local"], ["vllm-local"], - ])("migrates legacy %s sandbox off OPENAI_API_KEY (GH #2519)", (provider) => { + ])("migrates a legacy %s sandbox off OPENAI_API_KEY (#2519)", (provider) => { // Pre-fix sandboxes recorded credentialEnv="OPENAI_API_KEY" even // though local inference never actually needed it. After the fix, // the wizard records null. Rebuild must accept the legacy value, diff --git a/test/rebuild-shields-auto-unlock.test.ts b/test/rebuild-shields-auto-unlock.test.ts index cb510657aae..8aebb38ee11 100644 --- a/test/rebuild-shields-auto-unlock.test.ts +++ b/test/rebuild-shields-auto-unlock.test.ts @@ -308,7 +308,7 @@ function runRebuild(fixture: ReturnType) { ); } -describe("Issue #3113: rebuild auto-unlocks when shields are UP", () => { +describe("rebuild auto-unlocks when shields are UP (#3113)", () => { it("detects locked shields and prints auto-unlock notice", { timeout: 60_000 }, () => { const f = createFixture({ shieldsLocked: true }); const r = runRebuild(f); diff --git a/test/rebuild-stale-recovery.test.ts b/test/rebuild-stale-recovery.test.ts index 945d6528dbf..23f85e44e05 100644 --- a/test/rebuild-stale-recovery.test.ts +++ b/test/rebuild-stale-recovery.test.ts @@ -205,7 +205,7 @@ function registryHasSandbox(fixture: { nemoclawDir: string; sandboxName: string } } -describe("Issue #4497: stale sandbox rebuild recovery", () => { +describe("stale sandbox rebuild recovery (#4497)", () => { it("does NOT abort with 'Cannot back up state' when the live sandbox is gone", { timeout: 90_000, }, () => { diff --git a/test/repro-1751-extra.test.ts b/test/repro-1751-extra.test.ts index 110a5d1c053..74912a8b917 100644 --- a/test/repro-1751-extra.test.ts +++ b/test/repro-1751-extra.test.ts @@ -40,7 +40,7 @@ afterEach(() => { } }); -describe("Issue #1751 — GPU passthrough session persistence", () => { +describe("GPU passthrough session persistence (#1751)", () => { it("filterSafeUpdates: gpuPassthrough=true is propagated to safe", () => { session.saveSession(session.createSession()); markStepCompleteLegacy(session, stepMutation, "provider_selection", { gpuPassthrough: true }); diff --git a/test/repro-2201.test.ts b/test/repro-2201.test.ts index 7efc4dac9f7..ccf100709a0 100644 --- a/test/repro-2201.test.ts +++ b/test/repro-2201.test.ts @@ -316,7 +316,7 @@ function readSessionMessagingPlan( return readSession(fixture).messagingPlan; } -describe("Issue #2201: rebuild syncs agent from registry, not stale session", () => { +describe("rebuild syncs agent from registry instead of a stale session (#2201)", () => { it("rebuild openclaw after hermes was onboarded last (reporter scenario)", { timeout: 60_000, }, () => { @@ -361,7 +361,7 @@ describe("Issue #2201: rebuild syncs agent from registry, not stale session", () }); }); -describe("Issue #2301: rebuild forwards stored --from Dockerfile to onboard", () => { +describe("rebuild forwards the stored --from Dockerfile to onboard (#2301)", () => { it("rebuild does not hit fromDockerfile conflict when session has a stored --from path", { timeout: 60_000, }, () => { diff --git a/test/repro-2376.test.ts b/test/repro-2376.test.ts index bd27a552c63..449afd1515d 100644 --- a/test/repro-2376.test.ts +++ b/test/repro-2376.test.ts @@ -59,7 +59,7 @@ function runRcFile( } } -describe("Issue #2376: Hermes rc files source HERMES_HOME from proxy-env", () => { +describe("Hermes rc files source HERMES_HOME from proxy-env (#2376)", () => { for (const rcFileName of [".bashrc", ".profile"] as const) { it(`${rcFileName} exports HERMES_HOME when proxy-env exists`, () => { const out = runRcFile(rcFileName, "export HERMES_HOME=/sandbox/.hermes\n"); diff --git a/test/repro-2666-silent-list-status.test.ts b/test/repro-2666-silent-list-status.test.ts index 3786322d166..5a8a0251f05 100644 --- a/test/repro-2666-silent-list-status.test.ts +++ b/test/repro-2666-silent-list-status.test.ts @@ -67,7 +67,7 @@ function buildDepsWithThrowingRecovery(): ListSandboxesCommandDeps { }; } -describe("#2666 — silent empty output regression", () => { +describe("silent empty output regression (#2666)", () => { it("nemoclaw list renders the registry-only listing when recovery fails", async () => { const deps = buildDepsWithThrowingRecovery(); const inventory = await getSandboxInventory(deps); @@ -90,7 +90,7 @@ describe("#2666 — silent empty output regression", () => { }); }); -describe("#2666 — list-command-deps resilience wrapper", () => { +describe("list-command-deps resilience wrapper (#2666)", () => { // Exercises the actual exported `recoverRegistryEntriesWithFallback` from // src/lib/list-command-deps.ts, not a parallel re-implementation. If the // production wrapper regresses, these tests fail. @@ -145,7 +145,7 @@ describe("#2666 — list-command-deps resilience wrapper", () => { }); }); -describe("#2666 — subprocess regression: simulated (container-stopped + foreign-port-holder)", () => { +describe("simulated container-stopped and foreign-port-holder subprocess regression (#2666)", () => { // End-to-end test that runs the real `nemoclaw` binary against a fake // `openshell` shell script simulating the bug repro: the openshell sandbox // container is stopped AND a foreign listener holds port 8080. In that diff --git a/test/repro-2749-extra.test.ts b/test/repro-2749-extra.test.ts index 1791712e748..c069cad47fa 100644 --- a/test/repro-2749-extra.test.ts +++ b/test/repro-2749-extra.test.ts @@ -43,8 +43,8 @@ function listPolicyFiles(): string[] { return files; } -describe("Issue #2749 — additional coverage on top of existing tls:terminate guard", () => { - it("PARSE SAFETY: every policy YAML input still parses after deletions", () => { +describe("keeps policy YAML valid while removing `tls: terminate` (#2749)", () => { + it("parses every policy YAML input after deletions", () => { for (const file of listPolicyFiles()) { const content = fs.readFileSync(file, "utf-8"); // js-yaml throws on syntactic damage (dangling list markers, broken @@ -57,7 +57,7 @@ describe("Issue #2749 — additional coverage on top of existing tls:terminate g } }); - it("OVER-DELETION GUARD: `tls: skip` entries for WS pass-through are preserved", () => { + it("preserves `tls: skip` entries for WebSocket pass-through", () => { // The PR removes `tls: terminate` (deprecated) but the body explicitly // calls out that `tls: skip` for WebSocket pass-through should stay. // Confirm at least one built-in preset still has `tls: skip` so a diff --git a/test/repro-4538-raw-doctor-perms.test.ts b/test/repro-4538-raw-doctor-perms.test.ts index f08c6b4529c..efa643f5133 100644 --- a/test/repro-4538-raw-doctor-perms.test.ts +++ b/test/repro-4538-raw-doctor-perms.test.ts @@ -121,7 +121,7 @@ function seedTightenedConfigTree(): { tmpDir: string; configDir: string; configF return { tmpDir, configDir, configFile }; } -describe("#4538 raw `openclaw doctor --fix` mutable-perm restore", () => { +describe("raw `openclaw doctor --fix` mutable-perm restore (#4538)", () => { const src = fs.readFileSync(START_SCRIPT, "utf-8"); it("restore helper re-asserts 2770/660 after the tree is tightened to 700/600", () => { @@ -362,7 +362,7 @@ function resolveSandboxImage(): string | null { } describe.skipIf(!RUN_DOCKER_E2E || !dockerAvailable())( - "#4538 reporter workflow E2E (real sandbox image, raw openclaw doctor --fix)", + "reporter workflow E2E with a real sandbox image and raw openclaw doctor --fix (#4538)", () => { it("restores 2770/660 after a connect-shell `openclaw doctor --fix`", () => { const image = resolveSandboxImage(); diff --git a/test/runner.test.ts b/test/runner.test.ts index aa2d5881210..bef0190f7be 100644 --- a/test/runner.test.ts +++ b/test/runner.test.ts @@ -229,7 +229,7 @@ describe("runner env merging", () => { expect(firstCall[2]?.env?.PATH).toBe("/usr/local/bin:/usr/bin"); }); - it("#2616: runCaptureEx injects NO_PROXY=localhost,127.0.0.1 when http_proxy is set", () => { + it("injects NO_PROXY=localhost,127.0.0.1 in runCaptureEx when http_proxy is set (#2616)", () => { // Regression for the macOS Privoxy scenario: validateOllamaModel calls // runCaptureEx with a curl probe against http://localhost:11434. Before // the fix, runCaptureEx merged raw process.env (including the user's diff --git a/test/sandbox-init.test.ts b/test/sandbox-init.test.ts index c7386ec5835..a5dd71e7e65 100644 --- a/test/sandbox-init.test.ts +++ b/test/sandbox-init.test.ts @@ -475,7 +475,7 @@ EOF // Default (no NEMOCLAW_REQUIRE_CAP_DROP): warns and CONTINUES even though // dangerous caps remain — preserving the zero-regression posture for // CAP_SETPCAP-less hosts. report_residual_capabilities still names them. - it("warns but does NOT refuse to start when CAP_SETPCAP is unavailable (issue #3280)", () => { + it("warns without refusing to start when CAP_SETPCAP is unavailable (#3280)", () => { const { stdout } = runWithLib( [ "TMP=$(mktemp -d)", diff --git a/test/sandbox-provisioning.test.ts b/test/sandbox-provisioning.test.ts index be21d9d7dda..848fa5d277a 100644 --- a/test/sandbox-provisioning.test.ts +++ b/test/sandbox-provisioning.test.ts @@ -1338,7 +1338,7 @@ describe("Hermes sandbox provisioning", () => { return { result, tmp }; } - it("regression #4230: installs Hermes' native Anthropic provider dependency", () => { + it("installs Hermes' native Anthropic provider dependency (#4230)", () => { const { result, tmp } = runHermesUvExtrasExpansion(); try { expect(result.status).toBe(0); diff --git a/test/security-sandbox-tar-traversal.test.ts b/test/security-sandbox-tar-traversal.test.ts index a8693d1c5c5..12c8308ae5d 100644 --- a/test/security-sandbox-tar-traversal.test.ts +++ b/test/security-sandbox-tar-traversal.test.ts @@ -391,7 +391,7 @@ describe("Fix: safeTarExtract blocks malicious archives and extracts safe ones", // is extracted on the host, these absolute targets don't exist on the host // and were falsely rejected as escapes. The fix maps /sandbox/ paths onto // the extraction root before checking, matching the sandbox-internal view. - it("regression #2317: allows known-safe /sandbox/.openclaw-data symlinks in backup archives", async () => { + it("allows known-safe /sandbox/.openclaw-data symlinks in backup archives (#2317)", async () => { const { safeTarExtract } = await loadSandboxState(); const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-2317-")); try { @@ -414,7 +414,7 @@ describe("Fix: safeTarExtract blocks malicious archives and extracts safe ones", } }); - it("regression #2317: still blocks absolute symlinks outside /sandbox/.openclaw-data", async () => { + it("still blocks absolute symlinks outside /sandbox/.openclaw-data (#2317)", async () => { const { safeTarExtract } = await loadSandboxState(); const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-2317-block-")); try { @@ -515,7 +515,7 @@ describe("Fix: safeTarExtract blocks malicious archives and extracts safe ones", } }); - it("regression #2317: blocks path traversal within allowed prefix (/sandbox/.openclaw-data/../../etc/passwd)", async () => { + it("blocks path traversal within the allowed /sandbox/.openclaw-data prefix (#2317)", async () => { const { safeTarExtract } = await loadSandboxState(); const workDir = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-2317-traversal-")); try { diff --git a/test/service-env.test.ts b/test/service-env.test.ts index 128a49d944d..ab73f6e57a7 100644 --- a/test/service-env.test.ts +++ b/test/service-env.test.ts @@ -176,7 +176,7 @@ describe("service environment", () => { }); }); - describe("GIT_SSL_CAINFO for proxy CA trust (issue #2270)", () => { + describe("GIT_SSL_CAINFO for proxy CA trust (#2270)", () => { const sandboxInitSource = `source ${JSON.stringify(join(import.meta.dirname, "../scripts/lib/sandbox-init.sh"))}`; it("entrypoint exports GIT_SSL_CAINFO when SSL_CERT_FILE points to a real file", () => { @@ -380,7 +380,7 @@ describe("service environment", () => { }); }); - describe("XDG and tool cache redirects (issue #804)", () => { + describe("XDG and tool cache redirects (#804)", () => { it("entrypoint pre-creates redirected dirs and restricts GNUPGHOME permissions", () => { const scriptPath = join(import.meta.dirname, "../scripts/nemoclaw-start.sh"); const src = readFileSync(scriptPath, "utf-8"); @@ -438,7 +438,7 @@ describe("service environment", () => { }); }); - describe("proxy environment variables (issue #626)", () => { + describe("proxy environment variables (#626)", () => { // The proxy persistence block calls emit_sandbox_sourced_file from the // shared library. Wrappers that execute the extracted block must source it. const sandboxInitSource = `source ${JSON.stringify(join(import.meta.dirname, "../scripts/lib/sandbox-init.sh"))}`; @@ -1098,7 +1098,7 @@ describe("service environment", () => { } }); - it("[simulation] sourcing proxy-env.sh overrides narrow NO_PROXY and no_proxy", () => { + it("overrides narrow NO_PROXY and no_proxy while sourcing proxy-env.sh in simulation", () => { const fakeDataDir = mkdtempSync(join(tmpdir(), "nemoclaw-bashi-test-")); try { const envContent = [ @@ -1138,7 +1138,7 @@ describe("service environment", () => { } }); - it("regression #2109: proxy-env.sh includes NODE_OPTIONS --require when NODE_USE_ENV_PROXY=1", () => { + it("includes NODE_OPTIONS --require in proxy-env.sh when NODE_USE_ENV_PROXY=1 (#2109)", () => { const fakeDataDir = join(tmpdir(), `nemoclaw-http-fix-test-${process.pid}`); execFileSync("mkdir", ["-p", fakeDataDir]); const tmpFile = join(tmpdir(), `nemoclaw-http-fix-env-${process.pid}.sh`); @@ -1182,7 +1182,7 @@ describe("service environment", () => { } }); - it("regression #2109: proxy-env.sh does NOT include NODE_OPTIONS when NODE_USE_ENV_PROXY is unset", () => { + it("omits NODE_OPTIONS from proxy-env.sh when NODE_USE_ENV_PROXY is unset (#2109)", () => { const fakeDataDir = join(tmpdir(), `nemoclaw-http-noop-test-${process.pid}`); execFileSync("mkdir", ["-p", fakeDataDir]); const tmpFile = join(tmpdir(), `nemoclaw-http-noop-env-${process.pid}.sh`); diff --git a/test/snapshot-restore-existing-dest.test.ts b/test/snapshot-restore-existing-dest.test.ts index 938642594cd..fc30cafb93a 100644 --- a/test/snapshot-restore-existing-dest.test.ts +++ b/test/snapshot-restore-existing-dest.test.ts @@ -242,7 +242,7 @@ describe("snapshot restore --to existing destination (#3756)", () => { expect(log).not.toMatch(/sandbox delete dst/); }); - it("refuses by default before running source-image preflight (Codex #3796 P2)", () => { + it("refuses by default before running source-image preflight per Codex P2 review (#3796)", () => { // Existing destination + unresolvable source image. The user must see the // precise "destination exists" error, not the "cannot resolve image" // misdirection that would land if the refusal came after preflight. diff --git a/test/stale-dist-check.test.ts b/test/stale-dist-check.test.ts index 6d6c8774c3f..9014ca70784 100644 --- a/test/stale-dist-check.test.ts +++ b/test/stale-dist-check.test.ts @@ -80,7 +80,7 @@ describe("stale-dist-check", () => { expect(checkStaleDist(root)).toBeNull(); }); - it("warnIfStale writes a build:cli hint mentioning #1958", () => { + it("writes a build:cli hint mentioning the tracked issue in warnIfStale (#1958)", () => { writeFile(path.join(root, "dist", "lib", "foo.js"), "x", 1_000_000); writeFile(path.join(root, "src", "lib", "foo.ts"), "x", 5_000_000); const chunks: string[] = []; diff --git a/test/test-title-style.test.ts b/test/test-title-style.test.ts new file mode 100644 index 00000000000..81b6f35acda --- /dev/null +++ b/test/test-title-style.test.ts @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { describe, expect, it } from "vitest"; + +import { scanTestTitleStyle } from "../scripts/checks/test-title-style"; + +function rulesFor(source: string): string[] { + return scanTestTitleStyle("test/virtual-title-style.test.ts", source).map( + (violation) => violation.rule, + ); +} + +describe("enforces behavior-oriented Vitest titles", () => { + it("detects issue-first, metadata-first, placeholder-only, and arrow-label titles", () => { + const rules = rulesFor(` + import { describe, it } from "vitest"; + describe("issue #1234 spline behavior", () => { + it("#1234: fixes splines", () => {}); + it("--force bypasses validation", () => {}); + it("Scenario A: spline fixed", () => {}); + it.each([["spline"]])("%s", () => {}); + it("input → output", () => {}); + }); + `); + + expect(rules).toEqual([ + "issue-reference-suffix", + "leading-metadata", + "issue-reference-suffix", + "leading-metadata", + "leading-metadata", + "leading-metadata", + "placeholder-only", + "result-arrow", + ]); + }); + + it("accepts behavior-oriented titles and final issue suffixes through Vitest modifiers", () => { + const violations = scanTestTitleStyle( + "test/virtual-title-style.test.ts", + ` + import { describe, it } from "vitest"; + describe.skipIf(false)("spline behavior (#1234)", () => { + it("reticulates splines correctly (#1234)", () => {}); + it.each([["cubic"]])("reticulates %s splines", () => {}); + }); + `, + ); + + expect(violations).toEqual([]); + }); + + it("ignores external repository issue references and nonliteral titles", () => { + const violations = scanTestTitleStyle( + "test/virtual-title-style.test.ts", + ` + import { describe, it } from "vitest"; + const generated = "generated elsewhere"; + describe("upstream owner/repo#123 behavior", () => { + it(generated, () => {}); + }); + `, + ); + + expect(violations).toEqual([]); + }); +}); diff --git a/test/uninstall.test.ts b/test/uninstall.test.ts index ec9f99058f2..9ba76ea4e5e 100644 --- a/test/uninstall.test.ts +++ b/test/uninstall.test.ts @@ -19,7 +19,7 @@ describe("uninstall CLI flags", () => { } } - it("--help exits 0 and shows usage", () => { + it("exits 0 and shows usage for --help", () => { const result = spawnSync("bash", [UNINSTALL_SCRIPT, "--help"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -33,7 +33,7 @@ describe("uninstall CLI flags", () => { expect(output).toMatch(/--delete-models/); }); - it("--help uses NemoHermes branding when Hermes is the active agent", () => { + it("uses NemoHermes branding for --help when Hermes is active", () => { const result = spawnSync("bash", [UNINSTALL_SCRIPT, "--help"], { cwd: path.join(import.meta.dirname, ".."), encoding: "utf-8", @@ -52,7 +52,7 @@ describe("uninstall CLI flags", () => { expect(output).not.toMatch(/NemoClaw Uninstaller/); }); - it("--yes skips the confirmation prompt and completes successfully", () => { + it("skips the confirmation prompt and completes successfully for --yes", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemoclaw-uninstall-yes-")); const fakeBin = path.join(tmp, "bin"); writeFakeTools(fakeBin); @@ -81,7 +81,7 @@ describe("uninstall CLI flags", () => { } }, 60_000); - it("--yes uses NemoHermes branding when Hermes is the active agent", () => { + it("uses NemoHermes branding for --yes when Hermes is active", () => { const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "nemohermes-uninstall-yes-")); const fakeBin = path.join(tmp, "bin"); writeFakeTools(fakeBin); diff --git a/test/validate-blueprint.test.ts b/test/validate-blueprint.test.ts index 76fbad0e1e2..70db9adfc94 100644 --- a/test/validate-blueprint.test.ts +++ b/test/validate-blueprint.test.ts @@ -133,7 +133,7 @@ describe("blueprint.yaml", () => { expect(Object.keys(defined ?? {}).length).toBeGreaterThan(0); }); - it("regression #1438: sandbox image is pinned by digest, not by mutable tag", () => { + it("pins the sandbox image by digest instead of a mutable tag (#1438)", () => { // The blueprint MUST NOT pull a sandbox image by a mutable tag like // ":latest" — a registry compromise or accidental force-push could // silently swap the image. Pin via @sha256:... so the image cannot @@ -151,7 +151,7 @@ describe("blueprint.yaml", () => { expect(digestMatch).not.toBeNull(); }); - it("regression #1438: top-level digest field is populated and matches the image digest", () => { + it("populates the top-level digest field with the image digest (#1438)", () => { // The top-level `digest:` field at the top of blueprint.yaml is // documented as "Computed at release time" and was empty on main, // which left blueprint-level integrity unverifiable. Mirror the @@ -205,12 +205,12 @@ describe("blueprint.yaml", () => { describe("Model Router pool config", () => { const pool = loadYaml(ROUTER_POOL_CONFIG_PATH); - it("regression #3255: routes NVIDIA API keys to the public NVIDIA inference endpoint", () => { + it("routes NVIDIA API keys to the public NVIDIA inference endpoint (#3255)", () => { const apiBases = new Set((pool.models ?? []).map((model) => model.api_base)); expect(apiBases).toEqual(new Set(["https://integrate.api.nvidia.com/v1"])); }); - it("regression #3255: uses valid LiteLLM NVIDIA model identifiers", () => { + it("uses valid LiteLLM NVIDIA model identifiers (#3255)", () => { const modelsByName = new Map( (pool.models ?? []).map((model) => [model.name, model.litellm_model]), ); @@ -313,12 +313,12 @@ describe("base sandbox policy", () => { return out; } - it("regression #1437: base policy does not expose sentry.io by default", () => { + it("does not expose sentry.io in the base policy by default (#1437)", () => { const sentryEndpoints = findEndpoints((h) => h === "sentry.io"); expect(sentryEndpoints).toEqual([]); }); - it("regression #1583: base policy does not silently grant GitHub access", () => { + it("does not silently grant GitHub access in the base policy (#1583)", () => { // Until #1583, github.com / api.github.com plus the git/gh // binaries lived in network_policies and were therefore included // in every sandbox regardless of user opt-in. The fix moves the @@ -336,7 +336,7 @@ describe("base sandbox policy", () => { expect(githubHosts).toEqual([]); }); - it("regression #2663: managed_inference policy allows inference.local:443 GET and POST", () => { + it("allows inference.local:443 GET and POST in the managed_inference policy (#2663)", () => { // inference.local is the OpenShell gateway's managed inference virtual // hostname — the gateway proxies it to the configured provider (OpenAI, // NVIDIA, etc.). Every sandbox uses this route regardless of provider. @@ -360,7 +360,7 @@ describe("base sandbox policy", () => { expect(hasPost).toBe(true); }); - it("regression #2663: managed_inference allows openclaw and tool binaries", () => { + it("allows openclaw and tool binaries in the managed_inference policy (#2663)", () => { const np = policy.network_policies ?? {}; const binaries = (np.managed_inference?.binaries ?? []).map((b) => b.path).sort(); expect(binaries).toEqual([ @@ -377,7 +377,7 @@ describe("base sandbox policy", () => { expect(serialized).not.toContain("/usr/local/bin/claude"); }); - it("regression #2180: base policy does not silently grant Telegram access", () => { + it("does not silently grant Telegram access in the base policy (#2180)", () => { // Until #1705 (later regressed by #1700 and re-surfaced in #2180), // `api.telegram.org` plus a /usr/local/bin/node binary lived in the // base network_policies, so every sandbox could call the Telegram @@ -393,7 +393,7 @@ describe("base sandbox policy", () => { expect(telegramHosts).toEqual([]); }); - it("regression #2180: base policy does not silently grant Discord access", () => { + it("does not silently grant Discord access in the base policy (#2180)", () => { // Parallel to the Telegram regression above. Discord (discord.com, // gateway.discord.gg, cdn.discordapp.com, media.discordapp.net) is // the opt-in preset path, not baseline. Re-adding these endpoints @@ -413,7 +413,7 @@ describe("base sandbox policy", () => { expect(discordHosts).toEqual([]); }); - it("regression #2180: base policy does not silently grant Slack access", () => { + it("does not silently grant Slack access in the base policy (#2180)", () => { // Slack was never in the baseline, but guard against it being added // in the same merge-conflict-resolution pattern that re-added // Telegram and Discord after #1705. Slack access is in @@ -431,7 +431,7 @@ describe("base sandbox policy", () => { expect(slackHosts).toEqual([]); }); - it("regression #1458: baseline npm_registry must not include npm or node binaries", () => { + it("omits npm and node binaries from the baseline npm_registry policy (#1458)", () => { const np = policy.network_policies ?? {}; const npmRegistry = np.npm_registry; expect(npmRegistry).toBeDefined(); @@ -485,7 +485,7 @@ describe("permissive sandbox policy", () => { expect(policy.network_policies).toBeDefined(); }); - it("regression #2513: managed_inference block allows inference.local:443", () => { + it("allows inference.local:443 in the managed_inference block (#2513)", () => { const np = policy.network_policies ?? {}; expect(np.managed_inference).toBeDefined(); const endpoints = np.managed_inference?.endpoints ?? []; @@ -499,7 +499,7 @@ describe("permissive sandbox policy", () => { expect(inferenceEp?.enforcement).toBe("enforce"); }); - it("regression #2513: managed_inference uses permissive '/**' binary allowlist", () => { + it("uses a permissive '/**' binary allowlist for managed_inference (#2513)", () => { const np = policy.network_policies ?? {}; const binaries = (np.managed_inference?.binaries ?? []).map((b) => b.path); // Matches the permissive-file convention used by every other block @@ -541,7 +541,7 @@ describe("Hermes sandbox policy", () => { ]); } - it("regression #4230: managed_inference keeps a narrow inference API allowlist", () => { + it("keeps a narrow inference API allowlist for managed_inference (#4230)", () => { expectManagedInferenceSecurityShape(); }); @@ -567,7 +567,7 @@ describe("github preset", () => { import.meta.url, ); - it("regression #1583: github preset file exists and parses", () => { + it("parses the existing github preset file (#1583)", () => { const parsed = loadYaml(PRESET_PATH); expect(parsed).toEqual(expect.objectContaining({})); const meta = parsed.preset; @@ -576,7 +576,7 @@ describe("github preset", () => { expect(np && "github" in np).toBe(true); }); - it("regression #2179: github preset only advertises the installed git binary", () => { + it("only advertises the installed git binary in the github preset (#2179)", () => { const parsed = loadYaml(PRESET_PATH); const meta = parsed.preset; expect(meta?.description).toBe("GitHub.com and GitHub API access (git)"); @@ -612,7 +612,7 @@ describe("huggingface preset", () => { return Array.isArray(hf?.endpoints) ? hf.endpoints : []; } - it("regression #1432: huggingface.co has no POST allow rule", () => { + it("omits POST allow rules for huggingface.co (#1432)", () => { const endpoints = presetEndpoints().filter((ep) => ep.host === "huggingface.co"); expect(endpoints.length).toBeGreaterThan(0); for (const ep of endpoints) { @@ -628,7 +628,7 @@ describe("huggingface preset", () => { } }); - it("regression #1432: huggingface.co retains GET so downloads still work", () => { + it("retains GET for huggingface.co so downloads still work (#1432)", () => { const endpoints = presetEndpoints().filter((ep) => ep.host === "huggingface.co"); for (const ep of endpoints) { const rules = Array.isArray(ep.rules) ? ep.rules : []; @@ -651,7 +651,7 @@ describe("jira preset", () => { ); const jiraPreset = loadYaml(JIRA_PRESET_PATH); - it("regression #3758: Jira allows Node but not curl", () => { + it("allows Node but not curl for Jira (#3758)", () => { const binaries = (jiraPreset.network_policies?.atlassian?.binaries ?? []) .map((binary) => binary.path) .sort(); @@ -766,7 +766,7 @@ describe("npm preset", () => { const REGISTRY_HOSTS = ["registry.npmjs.org", "registry.yarnpkg.com"]; for (const host of REGISTRY_HOSTS) { - it(`regression #2767: ${host} uses L4 tunnel (access: full, tls: skip) for CONNECT compatibility`, () => { + it(`uses an L4 tunnel for CONNECT compatibility on ${host} (access: full, tls: skip) (#2767)`, () => { const endpoints = npmEndpoints().filter((ep) => ep.host === host); expect(endpoints.length).toBeGreaterThan(0); for (const ep of endpoints) { diff --git a/test/wsl2-probe-timeout.test.ts b/test/wsl2-probe-timeout.test.ts index aabb8c35582..60aa6b76888 100644 --- a/test/wsl2-probe-timeout.test.ts +++ b/test/wsl2-probe-timeout.test.ts @@ -30,7 +30,7 @@ if (!isOnboardValidationInternals(onboardValidationInternals)) { } const { getValidationProbeCurlArgs } = onboardValidationInternals; -describe("WSL2 inference verification timeouts (issue #987)", () => { +describe("WSL2 inference verification timeouts (#987)", () => { describe("getValidationProbeCurlArgs", () => { it("returns standard timeouts on non-WSL platforms", () => { expect(getValidationProbeCurlArgs({ isWsl: false })).toEqual([