Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
0227ad7
fix(cli): use provider-neutral onboarding copy
yimoj Aug 12, 2026
8637936
test(cli): exercise resumed provider copy
yimoj Aug 12, 2026
84b8d56
Merge branch 'main' into fix/8853-onboarding-workspace-copy
cv Aug 12, 2026
a19e816
merge: refresh PR branch from main
apurvvkumaria Aug 12, 2026
c821cfb
Merge branch 'main' into fix/8853-onboarding-workspace-copy
cv Aug 12, 2026
bd4f0c5
Merge branch 'main' into fix/8853-onboarding-workspace-copy
cv Aug 12, 2026
c7b0094
docs(workspace): list seeded OpenClaw files
apurvvkumaria Aug 12, 2026
1011969
test(docs): enforce workspace inventory split
apurvvkumaria Aug 12, 2026
2619dfe
Merge branch 'main' into fix/8853-onboarding-workspace-copy
prekshivyas Aug 13, 2026
b0edfbc
docs(workspaces): correct named workspace seeding
apurvvkumaria Aug 13, 2026
1cf46d3
Merge remote-tracking branch 'origin/main' into fix/8853-onboarding-w…
prekshivyas Aug 13, 2026
95d866d
Merge branch 'main' into fix/8853-onboarding-workspace-copy
prekshivyas Aug 13, 2026
af22c7d
fix(ci): restore static lint baseline
prekshivyas Aug 13, 2026
968a875
fix(ci): track managed image test size
prekshivyas Aug 13, 2026
3365b6e
test(ci): split managed image retention coverage
prekshivyas Aug 13, 2026
0feda92
test(ci): extract managed image workflow types
prekshivyas Aug 13, 2026
690b8ff
test(ci): allow loaded shields setup
prekshivyas Aug 13, 2026
bba5781
fix(ci): refresh libssh2 staging source
prekshivyas Aug 13, 2026
3df3d36
merge: resolve conflicts with main
github-actions[bot] Aug 13, 2026
3aafc7f
merge: resolve conflicts with main
github-actions[bot] Aug 13, 2026
aa10692
chore: remove unrelated CI changes
cv Aug 13, 2026
4f353eb
chore: finish PR scope cleanup
cv Aug 13, 2026
d469718
fix(ci): refresh libssh2 staging source
cv Aug 13, 2026
cdd1cab
docs(workspace): define template seeding condition
cv Aug 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/managed-images.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,8 @@ jobs:
CANDIDATE_SHA: ${{ github.event.pull_request.head.sha }}
# Retains the reviewed discovery-permission repair and the current
# managed-image security inventory. The previous staging source pinned
# Vim 9.2.0782, which cannot satisfy the candidate's 9.2.0858 contract.
STAGING_QA_SOURCE_SHA: af2a73f0d6ce8f08a2975560f376470387c535d0
# libssh2 nemoclaw1, which cannot satisfy the candidate's nemoclaw2 contract.
STAGING_QA_SOURCE_SHA: ce96811ddb418ad01c040521a1fe912b5bcb405e
STAGING_QA_BASE_IMAGE: nemoclaw-deepagents-code-base:staging-31396519688
STAGING_QA_FINAL_IMAGE: nemoclaw-managed-pr/langchain-deepagents-code-staging-qa
steps:
Expand Down
5 changes: 5 additions & 0 deletions ci/source-shape-test-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,11 @@
"test": "matches the bundled local-inference host-gateway ports (#5744)",
"category": "compatibility"
},
{
"file": "test/agent-variant-docs.test.ts",
"test": "keeps the OpenClaw workspace inventory aligned with seeded runtime templates",
"category": "compatibility"
},
{
"file": "test/code-change-considerations.test.ts",
"test": "keeps the stage-neutral questions in one concise owner",
Expand Down
20 changes: 15 additions & 5 deletions docs/manage-sandboxes/workspace-files.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,19 @@ These files live at `/sandbox/.openclaw/workspace/` and are collectively called

## File Reference

NemoClaw seeds the six template files below when the default workspace directory exists, is not a symbolic link, and is empty.
Set `NEMOCLAW_MINIMAL_BOOTSTRAP=1` before onboarding to skip default workspace template seeding.

| File | Purpose |
|---|---|
| `SOUL.md` | Defines the agent's persona, tone, and communication style. |
| `USER.md` | Stores information about the human the agent assists. |
| `IDENTITY.md` | Short identity card with name, language, emoji, and creature type. |
| `AGENTS.md` | Behavioral rules, memory conventions, safety guidelines, and session workflow. |
| `MEMORY.md` | Curated long-term memory distilled from daily notes. |
| `memory/` | Directory of daily note files (`YYYY-MM-DD.md`) for session continuity. |
| `TOOLS.md` | Records workspace-specific tool guidance and operational notes. |
| `HEARTBEAT.md` | Defines recurring heartbeat checks when heartbeat processing is enabled. |
| `MEMORY.md` | Curated long-term memory distilled from daily notes. OpenClaw creates this file when it first stores long-term memory. |
| `memory/` | Directory of daily note files (`YYYY-MM-DD.md`) for session continuity. OpenClaw creates this directory when it first stores a daily note. |

## Where They Live

Expand All @@ -93,11 +98,13 @@ All workspace files reside inside the sandbox filesystem:
```text
/sandbox/.openclaw/workspace/
├── AGENTS.md
├── HEARTBEAT.md
├── IDENTITY.md
├── MEMORY.md
├── MEMORY.md # created on first long-term memory use
├── SOUL.md
├── TOOLS.md
├── USER.md
└── memory/
└── memory/ # created on first daily memory use
├── 2026-03-18.md
└── 2026-03-19.md
```
Expand All @@ -123,7 +130,10 @@ For example, a Teams-integrated deployment can use a shared `main` agent plus pe
└── workspace-ops/ # named agent "ops"
```

Each per-agent workspace contains the same Markdown file structure as the default workspace: `SOUL.md`, `USER.md`, `IDENTITY.md`, `AGENTS.md`, `MEMORY.md`, and `memory/`.
A named workspace does not receive the same seeded Markdown file structure as the default workspace: `AGENTS.md`, `SOUL.md`, `IDENTITY.md`, `USER.md`, `TOOLS.md`, and `HEARTBEAT.md`.
NemoClaw seeds these files only in the default `workspace/`.
The sandbox entrypoint provisions each named workspace directory without copying the default templates into it.
OpenClaw creates `MEMORY.md` and `memory/` separately in each workspace when that agent first uses long-term or daily memory.
Files are per-agent.
Changes in `workspace-main/AGENTS.md` are not visible to `workspace-support/`.

Expand Down
2 changes: 1 addition & 1 deletion src/lib/onboard/machine/definition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const ONBOARD_MACHINE_STATE_DEFINITIONS = [
state: "provider_selection",
terminal: false,
stepName: "provider_selection",
progress: { number: 3, total: 8, title: "Configuring inference (NIM)" },
progress: { number: 3, total: 8, title: "Configuring inference provider" },
},
{
state: "inference",
Expand Down
20 changes: 18 additions & 2 deletions src/lib/onboard/machine/progress.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
// SPDX-License-Identifier: Apache-2.0

import { describe, expect, it } from "vitest";
import { describe, expect, it, vi } from "vitest";

import { step } from "../prompt-helpers";
import { skippedStepMessage } from "../skipped-step-message";
import { ONBOARD_MACHINE_STATE_DEFINITIONS } from "./definition";
import { getOnboardProgressStep, ONBOARD_PROGRESS_STEPS } from "./progress";

vi.mock("../prompt-helpers", () => ({ step: vi.fn() }));

describe("onboard progress metadata", () => {
it("derives state-backed progress labels from machine definitions", () => {
for (const definition of ONBOARD_MACHINE_STATE_DEFINITIONS) {
Expand All @@ -18,7 +22,7 @@ describe("onboard progress metadata", () => {
expect(ONBOARD_PROGRESS_STEPS).toEqual({
preflight: { number: 1, total: 8, title: "Preflight checks" },
gateway: { number: 2, total: 8, title: "Starting OpenShell gateway" },
provider_selection: { number: 3, total: 8, title: "Configuring inference (NIM)" },
provider_selection: { number: 3, total: 8, title: "Configuring inference provider" },
inference: { number: 4, total: 8, title: "Setting up inference provider" },
messaging: { number: 5, total: 8, title: "Messaging channels" },
sandbox: { number: 6, total: 8, title: "Creating sandbox" },
Expand All @@ -35,4 +39,16 @@ describe("onboard progress metadata", () => {
});
expect(getOnboardProgressStep("not-a-step")).toBeNull();
});

it("renders provider-neutral copy when resume skips Ollama selection (#8853)", () => {
const log = vi.spyOn(console, "log").mockImplementation(() => {});

skippedStepMessage("provider_selection", "ollama-local / qwen3.5:9b");

expect(step).toHaveBeenCalledWith(3, 8, "Configuring inference provider");
expect(step).not.toHaveBeenCalledWith(3, 8, expect.stringContaining("NIM"));
expect(log).toHaveBeenCalledWith(
" [resume] Skipping provider_selection (ollama-local / qwen3.5:9b)",
);
});
});
91 changes: 91 additions & 0 deletions test/agent-variant-docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,97 @@ Use \`$$nemoclaw\` for the current variant.
`;

describe("agent variant docs", () => {
// source-shape-contract: compatibility -- OpenClaw workspace documentation must list every runtime-seeded template so generated variants cannot omit operator-visible files.
it("keeps the OpenClaw workspace inventory aligned with seeded runtime templates", () => {
const workspaceFiles = readFileSync(
new URL("../docs/manage-sandboxes/workspace-files.mdx", import.meta.url),
"utf8",
);
const startScript = readFileSync(
new URL("../scripts/nemoclaw-start.sh", import.meta.url),
"utf8",
);
const seedFunction = startScript.slice(
startScript.indexOf("seed_default_workspace_templates()"),
);
const seededFiles = seedFunction
.match(/for file in ([^;]+); do/)?.[1]
.trim()
.split(/\s+/);

expect(seededFiles).toEqual([
"AGENTS.md",
"SOUL.md",
"IDENTITY.md",
"USER.md",
"TOOLS.md",
"HEARTBEAT.md",
]);
const expectedSeededFiles = seededFiles ?? [];
const deferredFiles = ["MEMORY.md", "memory/"];

const rendered = renderAgentVariantPage(workspaceFiles, "openclaw", {
sourcePath: "/repo/docs/manage-sandboxes/workspace-files.mdx",
});
expect(rendered).toContain(
"when the default workspace directory exists, is not a symbolic link, and is empty",
);
expect(rendered).toContain(
"Set `NEMOCLAW_MINIMAL_BOOTSTRAP=1` before onboarding to skip default workspace template seeding.",
);
const fileReference = rendered.match(
/## File Reference\n\n([\s\S]*?)\n\n## Where They Live/,
)?.[1];
const tableFiles = Array.from(
fileReference?.matchAll(/^\| `([^`]+)` \|/gm) ?? [],
(match) => match[1],
);
expect(tableFiles.filter((file) => !deferredFiles.includes(file)).sort()).toEqual(
[...expectedSeededFiles].sort(),
);
expect(tableFiles.filter((file) => deferredFiles.includes(file)).sort()).toEqual(
[...deferredFiles].sort(),
);

const workspaceTree = rendered.match(
/```text\n\/sandbox\/\.openclaw\/workspace\/\n([\s\S]*?)\n```/,
)?.[1];
const treeFiles = Array.from(
workspaceTree?.matchAll(/^[├└]── ([^\s]+)/gm) ?? [],
(match) => match[1],
);
for (const file of expectedSeededFiles) {
expect(tableFiles).toContain(file);
expect(treeFiles).toContain(file);
}
expect(treeFiles.filter((file) => !deferredFiles.includes(file)).sort()).toEqual(
[...expectedSeededFiles].sort(),
);
expect(treeFiles.filter((file) => deferredFiles.includes(file)).sort()).toEqual(
[...deferredFiles].sort(),
);

const multiAgentSection = rendered.match(
/## Multi-Agent Deployments\n\n([\s\S]*?)\n\n## Persistence Behavior/,
)?.[1];
const seededSummary = multiAgentSection?.match(
/same seeded Markdown file structure[^:]*: ([^\n]+)\n/,
)?.[1];
const deferredSummary = multiAgentSection?.match(
/OpenClaw creates ([^\n]+) separately in each workspace/,
)?.[1];
const summarySeededFiles = Array.from(
seededSummary?.matchAll(/`([^`]+)`/g) ?? [],
(match) => match[1],
);
const summaryDeferredFiles = Array.from(
deferredSummary?.matchAll(/`([^`]+)`/g) ?? [],
(match) => match[1],
);
expect(summarySeededFiles.sort()).toEqual([...expectedSeededFiles].sort());
expect(summaryDeferredFiles.sort()).toEqual([...deferredFiles].sort());
});

it("renders OpenClaw placeholder code and content", () => {
const rendered = renderAgentVariantPage(source, "openclaw");

Expand Down
2 changes: 1 addition & 1 deletion test/managed-image-publication-workflow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,7 @@ describe("complete managed-image publication workflow", () => {
expect(qaBuilder.permissions).toEqual({ contents: "read" });
expect(qaBuilder.env).toMatchObject({
CANDIDATE_SHA: "${{ github.event.pull_request.head.sha }}",
STAGING_QA_SOURCE_SHA: "af2a73f0d6ce8f08a2975560f376470387c535d0",
STAGING_QA_SOURCE_SHA: "ce96811ddb418ad01c040521a1fe912b5bcb405e",
STAGING_QA_BASE_IMAGE: "nemoclaw-deepagents-code-base:staging-31396519688",
});
expect(qaBuilder.env).not.toHaveProperty("STAGING_PRODUCER_SHA");
Expand Down
Loading