From f8bdabaccc0b8c5b2dea4b1a2d99fa3963f1b5cb Mon Sep 17 00:00:00 2001 From: Julie Yaunches Date: Wed, 29 Jul 2026 14:31:13 -0400 Subject: [PATCH] docs: enforce agent variant publication scope Signed-off-by: Julie Yaunches --- .../nemoclaw-contributor-update-docs/SKILL.md | 9 +- docs/AGENTS.md | 9 +- docs/CONTRIBUTING.md | 12 ++ docs/about/ecosystem-deepagents.mdx | 1 + docs/about/ecosystem-hermes.mdx | 1 + docs/about/ecosystem.mdx | 1 + .../configure-agent-heartbeats.mdx | 1 + .../understand-context-compaction.mdx | 1 + .../gateway-lifecycle-authority.mdx | 16 ++- docs/deployment/install-openclaw-plugins.mdx | 1 + docs/deployment/sandbox-hardening.mdx | 1 + docs/get-started/quickstart-hermes.mdx | 1 + .../quickstart-langchain-deepagents-code.mdx | 1 + docs/get-started/quickstart.mdx | 1 + docs/index.yml | 11 +- .../configure-model-capabilities.mdx | 1 + .../inference/declarative-agents-manifest.mdx | 1 + docs/inference/set-up-ollama.mdx | 1 + docs/inference/set-up-sub-agent.mdx | 1 + docs/inference/use-hermes-provider.mdx | 1 + .../add-channels-after-onboarding.mdx | 1 + .../enable-channels-during-onboarding.mdx | 1 + .../gateway-lifecycle-control.mdx | 1 + .../install-plugins-hermes.mdx | 1 + .../manage-messaging-channels.mdx | 1 + docs/manage-sandboxes/messaging-channels.mdx | 1 + docs/manage-sandboxes/runtime-controls.mdx | 1 + docs/manage-sandboxes/set-up-discord.mdx | 1 + .../set-up-microsoft-teams.mdx | 1 + docs/manage-sandboxes/set-up-slack.mdx | 1 + docs/manage-sandboxes/set-up-telegram.mdx | 1 + docs/manage-sandboxes/set-up-wechat.mdx | 1 + docs/manage-sandboxes/set-up-whatsapp.mdx | 1 + .../manage-deepagents-trace-export.mdx | 1 + docs/monitoring/monitor-sandbox-activity.mdx | 1 + .../set-up-deepagents-trace-export.mdx | 1 + .../understand-deepagents-trace-export.mdx | 1 + .../verify-deepagents-trace-export.mdx | 1 + .../configure-raw-tls-passthrough.mdx | 1 + .../explain-network-policy-to-agents.mdx | 1 + .../integration-policy-examples.mdx | 1 + .../set-up-gmail-with-an-app-password.mdx | 1 + docs/reference/enterprise-readiness.mdx | 21 +-- .../engineer-agentic-documentation.mdx | 62 +++++---- .../security/configure-corporate-ca-trust.mdx | 1 + docs/security/credential-rotation.mdx | 1 + docs/security/openclaw-controls.mdx | 1 + .../openshell-0.0.71-gateway-auth-review.mdx | 1 + docs/security/tcb-boundary.mdx | 1 + scripts/sync-agent-variant-docs.mts | 120 ++++++++++++++++-- test/check-docs-published-routes.test.ts | 18 +++ test/sync-agent-variant-docs.test.ts | 99 +++++++++++++++ 52 files changed, 365 insertions(+), 54 deletions(-) diff --git a/.agents/skills/nemoclaw-contributor-update-docs/SKILL.md b/.agents/skills/nemoclaw-contributor-update-docs/SKILL.md index e34f715fd2d..1d8d21b5c36 100644 --- a/.agents/skills/nemoclaw-contributor-update-docs/SKILL.md +++ b/.agents/skills/nemoclaw-contributor-update-docs/SKILL.md @@ -117,6 +117,11 @@ For each relevant commit, determine which doc page(s) it affects. Use this mappi | `Dockerfile` | `docs/reference/architecture.mdx` | | Inference-related changes | Start at `docs/inference/how-inference-routing-works.mdx`, then update the focused provider, model, setup, management, or validation page that owns the behavior. | +For every target page, determine which agent runtimes execute the behavior and which guide variants must publish it. +Use implementation gates, tests, or accepted product scope as evidence for each inclusion or exclusion. +Do not infer applicability from the page's current navigation placement. +Host-side NemoClaw and OpenShell behavior is not OpenClaw-only unless the implementation or accepted scope establishes that restriction. + If a commit does not map to any existing page but introduces a user-visible concept, flag it as needing a new page. If a commit already changes files under `docs/`, include those pages in the target page list and run a docs review or edit pass against them using the style guidance in Step 5. Do not assume an existing doc change is complete, correctly placed, or style-compliant just because it landed with the source commit. @@ -201,7 +206,9 @@ When updating an existing page: When creating a new page: - Follow the frontmatter template from existing pages in `docs/`. -- Add the page to the appropriate navigation entry in `docs/index.yml`. +- Add the page to every applicable navigation variant in `docs/index.yml`. +- Use generated navigation targets when the page applies to more than one variant. +- Declare the exact `agent-variants` subset in frontmatter when the page intentionally applies to fewer than OpenClaw, Hermes, and Deep Agents. ## Step 6: Present the Results diff --git a/docs/AGENTS.md b/docs/AGENTS.md index 02a0a68f110..5aec3136cd0 100644 --- a/docs/AGENTS.md +++ b/docs/AGENTS.md @@ -24,6 +24,9 @@ Treat `docs/` as the source of truth for published content and AI-agent Markdown - Check `docs/.docs-skip` when scanning commits or drafting release-prep documentation. - Read the full target page before editing it. - Map code changes to existing pages before proposing a new page. +- For every target page, determine which agent runtimes execute the documented behavior and which guide variants must publish it. +- Use source code, tests, or accepted product scope as evidence for each inclusion or exclusion. +- Do not infer agent applicability from the page's current navigation placement. - Update `.agents/skills/nemoclaw-user-guide/SKILL.md` only when AI-agent docs routing guidance changes. ## Writing Rules @@ -44,8 +47,10 @@ Treat `docs/` as the source of truth for published content and AI-agent Markdown ## NemoClaw Doc Patterns -- Use `$$nemoclaw` for host CLI command examples on shared OpenClaw, Hermes, and Deep Agents pages. -- Use literal command names on pages that have only one agent variant. +- Use `$$nemoclaw` for host CLI command examples on source pages shared by OpenClaw, Hermes, and Deep Agents guide variants. +- Use literal command names on source pages published for one guide variant. +- Publish shared source pages through generated navigation targets in every applicable guide variant. +- Declare `agent-variants` in frontmatter when a source page intentionally applies to fewer than all three guide variants. - Use `` blocks only when content differs by behavior, setup flow, state layout, or agent-specific wording. - Treat `` as a non-nested build-time directive with opening and closing tags at the first column on their own lines; do not import a runtime component for it. - Use route-style links without `.mdx` extensions for links between docs pages. diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 882e1a483c6..d00d8c6319f 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -171,6 +171,12 @@ The source pages stay in their normal `docs/` locations, and generated pages are Navigation in `docs/index.yml` points Fern at generated pages for shared entries so Fern still renders normal fenced code blocks with copy buttons and syntax highlighting. OpenClaw-only, Hermes-only, or Deep Agents-only pages stay as source pages in navigation. +Determine page applicability from the implementation, tests, or accepted product scope before adding or moving navigation entries. +Do not use the current navigation tree as evidence that a page is agent-specific. +Publish a shared source page through generated navigation targets in every applicable variant. +When a page intentionally applies to fewer than all three variants, declare the exact subset in frontmatter, for example `agent-variants: ["openclaw", "hermes"]`. +The sync command fails when a subset declaration is missing or differs from navigation membership. + When shared page content is the same except for the host CLI binary, write one source page and use `$$nemoclaw` as a build-time placeholder. Do not duplicate fenced code blocks or inline command examples only to switch among `nemoclaw`, `nemohermes`, and `nemo-deepagents`. @@ -262,6 +268,12 @@ position: 1 --- ``` +When the page intentionally applies to fewer than OpenClaw, Hermes, and Deep Agents, add the exact subset to frontmatter: + +```yaml +agent-variants: ["openclaw", "hermes"] +``` + ### Page Structure 1. Start MDX pages with a one- or two-sentence introduction stating what the page covers. diff --git a/docs/about/ecosystem-deepagents.mdx b/docs/about/ecosystem-deepagents.mdx index af6ab433235..f5c53418916 100644 --- a/docs/about/ecosystem-deepagents.mdx +++ b/docs/about/ecosystem-deepagents.mdx @@ -8,6 +8,7 @@ description-agent: "Explains how LangChain Deep Agents Code, OpenShell, and Nemo keywords: ["nemoclaw deep agents ecosystem", "dcode openshell sandbox", "nemo-deepagents", "langchain deep agents code"] content: type: "concept" +agent-variants: ["deepagents"] --- NemoClaw provides onboarding, lifecycle management, policy, and inference routing for LangChain Deep Agents Code in OpenShell containers. Use the `nemo-deepagents` CLI alias when you work from the Deep Agents guide; it is equivalent to `nemoclaw` with `langchain-deepagents-code` pre-selected. diff --git a/docs/about/ecosystem-hermes.mdx b/docs/about/ecosystem-hermes.mdx index 66c075fc648..b9118ae619f 100644 --- a/docs/about/ecosystem-hermes.mdx +++ b/docs/about/ecosystem-hermes.mdx @@ -8,6 +8,7 @@ description-agent: "Explains how Hermes, OpenShell, and NemoClaw form the ecosys keywords: ["nemoclaw ecosystem", "hermes agent", "nemohermes", "nemoclaw vs openshell", "run hermes openshell sandbox"] content: type: "concept" +agent-variants: ["hermes"] --- NemoClaw provides onboarding, lifecycle management, and Hermes operations in OpenShell containers. Use the `nemohermes` CLI alias when you work from the Hermes agent guide; it is equivalent to `nemoclaw` with the Hermes agent pre-selected. diff --git a/docs/about/ecosystem.mdx b/docs/about/ecosystem.mdx index 010c524ad95..e2258263ad1 100644 --- a/docs/about/ecosystem.mdx +++ b/docs/about/ecosystem.mdx @@ -8,6 +8,7 @@ description-agent: "Explains how OpenClaw, OpenShell, and NemoClaw form the ecos keywords: ["nemoclaw ecosystem", "openclaw openshell", "nemoclaw vs openshell", "sandboxed openclaw"] content: type: "concept" +agent-variants: ["openclaw"] --- NemoClaw provides onboarding, lifecycle management, and OpenClaw operations in OpenShell containers. diff --git a/docs/configure-agents/configure-agent-heartbeats.mdx b/docs/configure-agents/configure-agent-heartbeats.mdx index 9d8aa3cc4e0..26b1ba25971 100644 --- a/docs/configure-agents/configure-agent-heartbeats.mdx +++ b/docs/configure-agents/configure-agent-heartbeats.mdx @@ -8,6 +8,7 @@ description-agent: "Configures OpenClaw heartbeat cadence through NemoClaw onboa keywords: ["nemoclaw heartbeat", "openclaw heartbeat", "HEARTBEAT.md"] content: type: "how_to" +agent-variants: ["openclaw"] --- OpenClaw heartbeats run periodic main-session agent turns. diff --git a/docs/configure-agents/understand-context-compaction.mdx b/docs/configure-agents/understand-context-compaction.mdx index cd02f7eddec..5324204fa4d 100644 --- a/docs/configure-agents/understand-context-compaction.mdx +++ b/docs/configure-agents/understand-context-compaction.mdx @@ -8,6 +8,7 @@ description-agent: "Explains OpenClaw context compaction behavior in NemoClaw-ma keywords: ["nemoclaw context compaction", "openclaw transcript compaction", "openclaw context management"] content: type: "concept" +agent-variants: ["openclaw"] --- NemoClaw configures safeguard compaction for OpenClaw sandboxes that use its managed `inference.local` route, except for Local Ollama routes. diff --git a/docs/deployment/gateway-lifecycle-authority.mdx b/docs/deployment/gateway-lifecycle-authority.mdx index 89b6d862f2f..36104324b95 100644 --- a/docs/deployment/gateway-lifecycle-authority.mdx +++ b/docs/deployment/gateway-lifecycle-authority.mdx @@ -9,6 +9,10 @@ keywords: ["nemoclaw gateway lifecycle", "externally supervised gateway", "opens content: type: "reference" --- +This page covers the host-side OpenShell gateway that NemoClaw uses for OpenClaw, Hermes, and LangChain Deep Agents Code sandboxes. +It does not describe the in-sandbox agent gateway used by OpenClaw and Hermes. +Deep Agents Code has no in-sandbox agent gateway. + At every point, exactly one component owns the OpenShell gateway lifecycle. Platform images sometimes supervise the gateway with their own service. @@ -149,15 +153,15 @@ A completed gateway step does not bypass listener, supervisor, identity, health, ## Inspect the selected authority -Run `nemoclaw status` to see the management mode and redacted owner identity selected by the last onboarding run. +Run `$$nemoclaw status` to see the management mode and redacted owner identity selected by the last onboarding run. The JSON form includes the same secret-free fields under `gatewayAuthority`. ```bash -nemoclaw status -nemoclaw status --json +$$nemoclaw status +$$nemoclaw status --json ``` -`nemoclaw debug` also records `gatewayAuthority` in `onboard-session-summary.txt`. +`$$nemoclaw debug` also records `gatewayAuthority` in `onboard-session-summary.txt`. The status and debug views omit the external state directory and never include credential values. ## Keep external lifecycle paths inert @@ -174,7 +178,7 @@ When a valid checkpoint exists, they compare the current authority with that che The comparison uses the exact gateway name and port. If the authority changed, teardown stops before it scans listeners or changes gateway runtime resources. -`nemoclaw stop` does not scan or signal the externally supervised gateway. +`$$nemoclaw stop` does not scan or signal the externally supervised gateway. Final-sandbox cleanup can stop local dashboard forwards and remove the modern local gateway registration. It does not signal the gateway, use the legacy `gateway destroy` fallback, or remove its Docker volumes. Uninstall can delete the selected sandboxes, providers, and local registration. @@ -198,5 +202,5 @@ For `systemd-user`, use the user manager: systemctl --user status openshell-gateway.service ``` -Bring the supervised gateway up, leave exactly one verified process holding the configured port, and rerun `nemoclaw onboard`. +Bring the supervised gateway up, leave exactly one verified process holding the configured port, and rerun `$$nemoclaw onboard`. To hand the lifecycle back to NemoClaw, remove the platform gateway service and declare `nemoclaw-managed`, or remove the declaration. diff --git a/docs/deployment/install-openclaw-plugins.mdx b/docs/deployment/install-openclaw-plugins.mdx index 9bd8cd55255..95864ff3571 100644 --- a/docs/deployment/install-openclaw-plugins.mdx +++ b/docs/deployment/install-openclaw-plugins.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 20 +agent-variants: ["openclaw"] --- diff --git a/docs/deployment/sandbox-hardening.mdx b/docs/deployment/sandbox-hardening.mdx index f00bf76a961..e5f4a4027f1 100644 --- a/docs/deployment/sandbox-hardening.mdx +++ b/docs/deployment/sandbox-hardening.mdx @@ -8,6 +8,7 @@ description-agent: "Includes the sandbox container image hardening reference, co keywords: ["nemoclaw sandbox hardening", "container security", "docker capabilities", "process limits"] content: type: "reference" +agent-variants: ["openclaw"] --- The NemoClaw sandbox image applies several security measures to reduce the attack surface and limit damage from untrusted workloads. diff --git a/docs/get-started/quickstart-hermes.mdx b/docs/get-started/quickstart-hermes.mdx index 05af40dccef..d8592cddd64 100644 --- a/docs/get-started/quickstart-hermes.mdx +++ b/docs/get-started/quickstart-hermes.mdx @@ -10,6 +10,7 @@ content: type: "get_started" skill: priority: 20 +agent-variants: ["hermes"] --- Create a sandboxed Hermes agent, then chat with it from the dashboard or terminal. The `nemohermes` command is the NemoClaw CLI with Hermes pre-selected. diff --git a/docs/get-started/quickstart-langchain-deepagents-code.mdx b/docs/get-started/quickstart-langchain-deepagents-code.mdx index a4fe4fc06db..30a3e837d9d 100644 --- a/docs/get-started/quickstart-langchain-deepagents-code.mdx +++ b/docs/get-started/quickstart-langchain-deepagents-code.mdx @@ -13,6 +13,7 @@ audience: "operators" status: published content: type: "get_started" +agent-variants: ["deepagents"] --- Create a sandboxed LangChain Deep Agents Code agent, then run your first prompt. The `nemo-deepagents` command is an alias for `nemoclaw` with the `langchain-deepagents-code` agent pre-selected. diff --git a/docs/get-started/quickstart.mdx b/docs/get-started/quickstart.mdx index 039ae538bab..0e6391ec86e 100644 --- a/docs/get-started/quickstart.mdx +++ b/docs/get-started/quickstart.mdx @@ -10,6 +10,7 @@ content: type: "get_started" skill: priority: 10 +agent-variants: ["openclaw"] --- Create a sandboxed OpenClaw agent, then send it a first prompt. diff --git a/docs/index.yml b/docs/index.yml index 54731b70094..e541f56255c 100644 --- a/docs/index.yml +++ b/docs/index.yml @@ -313,7 +313,7 @@ navigation: path: _build/agent-variants/deployment/deploy-to-headless-server.openclaw.generated.mdx slug: deploy-to-headless-server - page: "Gateway Lifecycle Authority" - path: deployment/gateway-lifecycle-authority.mdx + path: _build/agent-variants/deployment/gateway-lifecycle-authority.openclaw.generated.mdx slug: gateway-lifecycle-authority - section: "Monitoring" slug: monitoring @@ -628,6 +628,9 @@ navigation: - page: "Deploy to a Headless Server" path: _build/agent-variants/deployment/deploy-to-headless-server.deepagents.generated.mdx slug: deploy-to-headless-server + - page: "Gateway Lifecycle Authority" + path: _build/agent-variants/deployment/gateway-lifecycle-authority.deepagents.generated.mdx + slug: gateway-lifecycle-authority - section: "Monitoring" slug: monitoring collapsed: open-by-default @@ -664,6 +667,9 @@ navigation: - page: "Platform Support" path: reference/platform-support.mdx slug: platform-support + - page: "Enterprise Readiness" + path: _build/agent-variants/reference/enterprise-readiness.deepagents.generated.mdx + slug: enterprise-readiness - page: "Architecture Details" path: _build/agent-variants/reference/architecture.deepagents.generated.mdx slug: architecture @@ -995,6 +1001,9 @@ navigation: - page: "Deploy to a Headless Server" path: _build/agent-variants/deployment/deploy-to-headless-server.hermes.generated.mdx slug: deploy-to-headless-server + - page: "Gateway Lifecycle Authority" + path: _build/agent-variants/deployment/gateway-lifecycle-authority.hermes.generated.mdx + slug: gateway-lifecycle-authority - section: "Monitoring" slug: monitoring collapsed: open-by-default diff --git a/docs/inference/configure-model-capabilities.mdx b/docs/inference/configure-model-capabilities.mdx index feb3c3f023f..54dfc554ae4 100644 --- a/docs/inference/configure-model-capabilities.mdx +++ b/docs/inference/configure-model-capabilities.mdx @@ -8,6 +8,7 @@ description-agent: "Configures OpenClaw reasoning mode, reasoning effort, and te keywords: ["nemoclaw reasoning model", "nemoclaw reasoning effort", "nemoclaw vision model"] content: type: "how_to" +agent-variants: ["openclaw"] --- Declare model capabilities before onboarding so NemoClaw can bake them into the OpenClaw configuration. diff --git a/docs/inference/declarative-agents-manifest.mdx b/docs/inference/declarative-agents-manifest.mdx index b91aad1f80c..0dc983e7607 100644 --- a/docs/inference/declarative-agents-manifest.mdx +++ b/docs/inference/declarative-agents-manifest.mdx @@ -21,6 +21,7 @@ content: skill: priority: 30 status: published +agent-variants: ["openclaw"] --- NemoClaw can bake a multi-agent OpenClaw layout into a sandbox image from a single checked-in manifest. diff --git a/docs/inference/set-up-ollama.mdx b/docs/inference/set-up-ollama.mdx index cdf26bb4a2f..4b0604972d4 100644 --- a/docs/inference/set-up-ollama.mdx +++ b/docs/inference/set-up-ollama.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 10 +agent-variants: ["openclaw", "hermes"] --- Use Ollama when you want the default local inference setup path. NemoClaw detects Ollama on the host and can install, start, or upgrade it on supported systems. diff --git a/docs/inference/set-up-sub-agent.mdx b/docs/inference/set-up-sub-agent.mdx index 06e2093f022..3a2acc0765d 100644 --- a/docs/inference/set-up-sub-agent.mdx +++ b/docs/inference/set-up-sub-agent.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 30 +agent-variants: ["openclaw"] --- OpenClaw documents the sub-agent behavior, `sessions_spawn` tool, `agents.list` configuration, tool policy, nesting, and auth model in [Sub-Agents](https://docs.openclaw.ai/tools/subagents). Use that page as the source of truth for how OpenClaw sub-agents work. diff --git a/docs/inference/use-hermes-provider.mdx b/docs/inference/use-hermes-provider.mdx index b12d766e39a..979b292d35a 100644 --- a/docs/inference/use-hermes-provider.mdx +++ b/docs/inference/use-hermes-provider.mdx @@ -8,6 +8,7 @@ description-agent: "Sets up Hermes Provider for Hermes Agent. Use when choosing keywords: ["Hermes Provider", "NemoClaw Hermes inference", "Hermes models"] content: type: "how_to" +agent-variants: ["hermes"] --- Hermes Provider routes Hermes Agent through the host OpenShell provider that NemoClaw registers during Hermes onboarding. This option is available only for Hermes Agent. diff --git a/docs/manage-sandboxes/add-channels-after-onboarding.mdx b/docs/manage-sandboxes/add-channels-after-onboarding.mdx index a7d185319c5..885fe5f0176 100644 --- a/docs/manage-sandboxes/add-channels-after-onboarding.mdx +++ b/docs/manage-sandboxes/add-channels-after-onboarding.mdx @@ -8,6 +8,7 @@ description-agent: "Explains channels list and channels add behavior, policy app keywords: ["nemoclaw channels add", "add messaging channel", "channels list"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Run channel commands from the host, not from inside the sandbox. diff --git a/docs/manage-sandboxes/enable-channels-during-onboarding.mdx b/docs/manage-sandboxes/enable-channels-during-onboarding.mdx index 67e8aa74ee6..6e6b0189cf6 100644 --- a/docs/manage-sandboxes/enable-channels-during-onboarding.mdx +++ b/docs/manage-sandboxes/enable-channels-during-onboarding.mdx @@ -8,6 +8,7 @@ description-agent: "Explains the interactive and scripted onboarding flows for s keywords: ["nemoclaw onboard messaging", "messaging channel picker", "channel environment variables"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Enable channels during onboarding when you are creating or recreating a sandbox. diff --git a/docs/manage-sandboxes/gateway-lifecycle-control.mdx b/docs/manage-sandboxes/gateway-lifecycle-control.mdx index fb23397a5ea..74a7704ec65 100644 --- a/docs/manage-sandboxes/gateway-lifecycle-control.mdx +++ b/docs/manage-sandboxes/gateway-lifecycle-control.mdx @@ -10,6 +10,7 @@ content: type: "concept" skill: priority: 20 +agent-variants: ["openclaw", "hermes"] --- Built-in OpenClaw and Hermes images support two direct-container lifecycle topologies for `recover` and `gateway restart`. diff --git a/docs/manage-sandboxes/install-plugins-hermes.mdx b/docs/manage-sandboxes/install-plugins-hermes.mdx index 6cd74b97b16..653de645247 100644 --- a/docs/manage-sandboxes/install-plugins-hermes.mdx +++ b/docs/manage-sandboxes/install-plugins-hermes.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 40 +agent-variants: ["hermes"] --- Hermes plugins extend the Hermes runtime inside a NemoClaw-managed sandbox. They are different from NemoClaw skills and from OpenClaw plugins, so install them through the Hermes plugin path instead of `skill install`. diff --git a/docs/manage-sandboxes/manage-messaging-channels.mdx b/docs/manage-sandboxes/manage-messaging-channels.mdx index 42c057bba34..546ea495c18 100644 --- a/docs/manage-sandboxes/manage-messaging-channels.mdx +++ b/docs/manage-sandboxes/manage-messaging-channels.mdx @@ -8,6 +8,7 @@ description-agent: "Explains channel credential rotation, destructive removal, p keywords: ["nemoclaw channels remove", "nemoclaw channels stop", "messaging credential rotation", "channel conflicts"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Use host-side channel commands to change a configured messaging channel. diff --git a/docs/manage-sandboxes/messaging-channels.mdx b/docs/manage-sandboxes/messaging-channels.mdx index 425370170f8..3cb76cd4b71 100644 --- a/docs/manage-sandboxes/messaging-channels.mdx +++ b/docs/manage-sandboxes/messaging-channels.mdx @@ -10,6 +10,7 @@ content: type: "concept" skill: priority: 30 +agent-variants: ["openclaw", "hermes"] --- NemoClaw supports Telegram, Discord, Slack, WeChat, WhatsApp, and Microsoft Teams for OpenClaw and Hermes sandboxes. OpenShell-managed processes and gateway resources carry channel traffic. diff --git a/docs/manage-sandboxes/runtime-controls.mdx b/docs/manage-sandboxes/runtime-controls.mdx index b6388f51a0a..4aca95e49cf 100644 --- a/docs/manage-sandboxes/runtime-controls.mdx +++ b/docs/manage-sandboxes/runtime-controls.mdx @@ -10,6 +10,7 @@ content: type: "concept" skill: priority: 10 +agent-variants: ["openclaw", "hermes"] --- Use this matrix to choose the operation that makes a sandbox change take effect. NemoClaw applies its security posture in three layers: what onboarding writes into the sandbox image, what the running sandbox can hot-reload, and what requires a rebuild or re-onboard. diff --git a/docs/manage-sandboxes/set-up-discord.mdx b/docs/manage-sandboxes/set-up-discord.mdx index 934da244140..e2c6f03cd8a 100644 --- a/docs/manage-sandboxes/set-up-discord.mdx +++ b/docs/manage-sandboxes/set-up-discord.mdx @@ -8,6 +8,7 @@ description-agent: "Explains Discord bot credentials and NemoClaw server, user, keywords: ["nemoclaw discord", "discord bot token", "discord server id"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Create a Discord bot and choose its server and user access before you enable the channel. diff --git a/docs/manage-sandboxes/set-up-microsoft-teams.mdx b/docs/manage-sandboxes/set-up-microsoft-teams.mdx index 57cb44e381d..fa587e6cdd8 100644 --- a/docs/manage-sandboxes/set-up-microsoft-teams.mdx +++ b/docs/manage-sandboxes/set-up-microsoft-teams.mdx @@ -8,6 +8,7 @@ description-agent: "Explains the experimental Microsoft Teams Bot Framework webh keywords: ["nemoclaw teams", "microsoft teams bot framework", "teams webhook"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Microsoft Teams support is experimental and uses a public Bot Framework webhook that forwards to the sandbox. diff --git a/docs/manage-sandboxes/set-up-slack.mdx b/docs/manage-sandboxes/set-up-slack.mdx index 2aa9b7baad5..0173688efd3 100644 --- a/docs/manage-sandboxes/set-up-slack.mdx +++ b/docs/manage-sandboxes/set-up-slack.mdx @@ -8,6 +8,7 @@ description-agent: "Explains Slack bot and app token validation, allowlists, men keywords: ["nemoclaw slack", "slack socket mode", "slack app token", "slack allowlist"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Slack uses Socket Mode and requires both a bot token and an app-level token. diff --git a/docs/manage-sandboxes/set-up-telegram.mdx b/docs/manage-sandboxes/set-up-telegram.mdx index 4017c870b90..2825d002e3b 100644 --- a/docs/manage-sandboxes/set-up-telegram.mdx +++ b/docs/manage-sandboxes/set-up-telegram.mdx @@ -8,6 +8,7 @@ description-agent: "Explains Telegram bot creation and NemoClaw allowlist, group keywords: ["nemoclaw telegram", "telegram bot token", "telegram allowed ids"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Create a Telegram bot and choose the direct-message and group behavior before you enable the channel. diff --git a/docs/manage-sandboxes/set-up-wechat.mdx b/docs/manage-sandboxes/set-up-wechat.mdx index ef71831e785..d7391e7783a 100644 --- a/docs/manage-sandboxes/set-up-wechat.mdx +++ b/docs/manage-sandboxes/set-up-wechat.mdx @@ -8,6 +8,7 @@ description-agent: "Explains the experimental personal WeChat iLink QR flow, pro keywords: ["nemoclaw wechat", "wechat qr", "wechat ilink"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- WeChat support is experimental and uses Tencent's iLink gateway. The supported mode in this release is personal WeChat with `bot_type=3`. diff --git a/docs/manage-sandboxes/set-up-whatsapp.mdx b/docs/manage-sandboxes/set-up-whatsapp.mdx index 8e5ae75cae2..59dbf03daa1 100644 --- a/docs/manage-sandboxes/set-up-whatsapp.mdx +++ b/docs/manage-sandboxes/set-up-whatsapp.mdx @@ -8,6 +8,7 @@ description-agent: "Explains the experimental in-sandbox WhatsApp QR pairing flo keywords: ["nemoclaw whatsapp", "whatsapp qr pairing", "whatsapp session"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- WhatsApp support is experimental and pairs inside the sandbox rather than through a host-side token or OpenShell credential provider. diff --git a/docs/monitoring/manage-deepagents-trace-export.mdx b/docs/monitoring/manage-deepagents-trace-export.mdx index 30e68aff472..0faf1550a66 100644 --- a/docs/monitoring/manage-deepagents-trace-export.mdx +++ b/docs/monitoring/manage-deepagents-trace-export.mdx @@ -8,6 +8,7 @@ description-agent: "Manages Deep Agents trace export. Use when stopping the coll keywords: ["manage deep agents traces", "disable nemoclaw observability", "remove otel collector"] content: type: "how_to" +agent-variants: ["deepagents"] --- Manage sandbox instrumentation and the operator-owned host collector as separate lifecycle controls. NemoClaw does not start, stop, upgrade, or remove the collector. diff --git a/docs/monitoring/monitor-sandbox-activity.mdx b/docs/monitoring/monitor-sandbox-activity.mdx index 142618f4a30..1f6dfcdbe2d 100644 --- a/docs/monitoring/monitor-sandbox-activity.mdx +++ b/docs/monitoring/monitor-sandbox-activity.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 10 +agent-variants: ["openclaw", "hermes"] --- Use NemoClaw status commands, log streams, and OpenShell TUI views to inspect sandbox health, trace agent behavior, and diagnose problems. diff --git a/docs/monitoring/set-up-deepagents-trace-export.mdx b/docs/monitoring/set-up-deepagents-trace-export.mdx index 81330f487f6..299a8fdee9c 100644 --- a/docs/monitoring/set-up-deepagents-trace-export.mdx +++ b/docs/monitoring/set-up-deepagents-trace-export.mdx @@ -8,6 +8,7 @@ description-agent: "Sets up Deep Agents trace export. Use when enabling observab keywords: ["set up deep agents otlp", "nemoclaw observability collector", "langsmith otel collector"] content: type: "how_to" +agent-variants: ["deepagents"] --- Enable Deep Agents trace export, then configure an operator-owned collector on the host. This example uses Linux with Docker and the OpenTelemetry Collector Contrib `0.155.0` image. diff --git a/docs/monitoring/understand-deepagents-trace-export.mdx b/docs/monitoring/understand-deepagents-trace-export.mdx index 51e1cbabbca..ecd5e4c1def 100644 --- a/docs/monitoring/understand-deepagents-trace-export.mdx +++ b/docs/monitoring/understand-deepagents-trace-export.mdx @@ -8,6 +8,7 @@ description-agent: "Explains Deep Agents trace export boundaries. Use when evalu keywords: ["deep agents trace export", "nemoclaw otlp privacy", "dcode observability security"] content: type: "concept" +agent-variants: ["deepagents"] --- NemoClaw can export Deep Agents Code traces to an OTLP/HTTP collector that you operate on the host. Review the data and trust boundaries before you enable the exporter. diff --git a/docs/monitoring/verify-deepagents-trace-export.mdx b/docs/monitoring/verify-deepagents-trace-export.mdx index b169d0e744d..d05ca420241 100644 --- a/docs/monitoring/verify-deepagents-trace-export.mdx +++ b/docs/monitoring/verify-deepagents-trace-export.mdx @@ -8,6 +8,7 @@ description-agent: "Verifies Deep Agents trace export. Use when proving local an keywords: ["verify deep agents traces", "trace delivery diagnosis", "troubleshoot otlp collector"] content: type: "how_to" +agent-variants: ["deepagents"] --- Verify the local collector and downstream backend independently. Deep Agents work continues when export fails, so the agent exit status is not delivery evidence. diff --git a/docs/network-policy/configure-raw-tls-passthrough.mdx b/docs/network-policy/configure-raw-tls-passthrough.mdx index 39634fc8838..bb1b88cc282 100644 --- a/docs/network-policy/configure-raw-tls-passthrough.mdx +++ b/docs/network-policy/configure-raw-tls-passthrough.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 10 +agent-variants: ["openclaw", "hermes"] --- Use raw TLS passthrough only when an allowed endpoint requires direct TLS negotiation. Keep L7 inspection for every endpoint that supports it. diff --git a/docs/network-policy/explain-network-policy-to-agents.mdx b/docs/network-policy/explain-network-policy-to-agents.mdx index dea65530491..977c8b18472 100644 --- a/docs/network-policy/explain-network-policy-to-agents.mdx +++ b/docs/network-policy/explain-network-policy-to-agents.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 10 +agent-variants: ["openclaw", "hermes"] --- Use `policy explain` to give a sandbox agent a compact, redacted view of its active network policy. The summary helps the agent distinguish policy denials, missing credentials, unsupported capabilities, and upstream failures. diff --git a/docs/network-policy/integration-policy-examples.mdx b/docs/network-policy/integration-policy-examples.mdx index 0d1fafd752e..0bb1bd3947c 100644 --- a/docs/network-policy/integration-policy-examples.mdx +++ b/docs/network-policy/integration-policy-examples.mdx @@ -10,6 +10,7 @@ content: type: "how_to" skill: priority: 15 +agent-variants: ["openclaw", "hermes"] --- Use these examples when a sandbox is already installed and an integration needs network access. This page covers only integrations that NemoClaw currently ships as maintained policy preset YAML under `nemoclaw-blueprint/policies/presets/`. diff --git a/docs/network-policy/set-up-gmail-with-an-app-password.mdx b/docs/network-policy/set-up-gmail-with-an-app-password.mdx index 5b8b9e1f8e1..76145f7238e 100644 --- a/docs/network-policy/set-up-gmail-with-an-app-password.mdx +++ b/docs/network-policy/set-up-gmail-with-an-app-password.mdx @@ -9,6 +9,7 @@ content: type: "how_to" skill: priority: 15 +agent-variants: ["openclaw", "hermes"] --- Use the `gmail` preset when a Python script needs to receive mail through IMAP or send mail through SMTP with a Gmail App Password. The preset allows only `/usr/bin/python3` to open raw TLS connections to `imap.gmail.com:993` and `smtp.gmail.com:465`. diff --git a/docs/reference/enterprise-readiness.mdx b/docs/reference/enterprise-readiness.mdx index 7dcceb38b55..bdb823e274d 100644 --- a/docs/reference/enterprise-readiness.mdx +++ b/docs/reference/enterprise-readiness.mdx @@ -51,7 +51,7 @@ Knowing who enforces each boundary prevents misattributing a limitation to NemoC | Host CLI and onboarding | NemoClaw | Onboarding, provider validation, blueprint resolution, sandbox lifecycle commands, and credential handling on the host. | | Blueprint and policy presets | NemoClaw | Versioned blueprint, baseline network policy, filesystem and process defaults, and integration presets. | | Gateway, sandbox runtime, and egress enforcement | OpenShell | Network namespace isolation, the CONNECT proxy, policy enforcement, inference routing, TLS termination, and structured platform logging. | -| Agent behavior | OpenClaw, Hermes, or Deep Agents Code | The agent loop, tools, skills, and in-sandbox configuration. | +| Agent behavior | OpenClaw, Hermes, or LangChain Deep Agents Code | The agent loop, tools, skills, and in-sandbox configuration. | | Model inference and data handling | Inference provider | Model execution, per-token cost, rate limits, and provider-side data policies. | | Operator decisions | You | Endpoint approvals, policy widening, posture choices, provider selection, credential rotation, and host observability collector configuration. | @@ -68,16 +68,16 @@ Each row links to deeper documentation and, when a concrete fix is in progress, | Network policy configuration | Supported | Edit baseline policy in the blueprint, apply presets, or add endpoints to a running sandbox with `$$nemoclaw policy add --from-file`. Refer to [Customize the Network Policy](../network-policy/customize-network-policy) and [Network Policies](network-policies). | | Network policy and denial visibility | Supported with caveats | Live activity appears in `openshell term`; lifecycle and gateway output appear in `$$nemoclaw logs`. Denial log readability is being improved in [#4760](https://github.com/NVIDIA/NemoClaw/issues/4760). Default-policy gaps for plugin installs are tracked in [#4104](https://github.com/NVIDIA/NemoClaw/issues/4104) and [#4015](https://github.com/NVIDIA/NemoClaw/issues/4015), and a `policy add` YAML defect in [#991](https://github.com/NVIDIA/NemoClaw/issues/991). | | Model and provider switching | Supported | Switch the active provider or model with the NemoClaw inference commands. Some changes rebuild the sandbox image. Refer to [Switch Inference Providers](../inference/manage-inference/switch-providers) and [Choose an Inference Provider](../inference/learn-and-choose/choose-inference-provider). | -| Multi-agent and multi-sandbox usage | Supported with caveats | Side-by-side sandboxes run on distinct names and dashboard ports, and each name maps to exactly one agent type. Known multi-instance issues include gateway-port collisions ([#5359](https://github.com/NVIDIA/NemoClaw/issues/5359)) and parallel inference routing fallback ([#5343](https://github.com/NVIDIA/NemoClaw/issues/5343)). A declarative multi-agent manifest is roadmap ([#2853](https://github.com/NVIDIA/NemoClaw/issues/2853)). | -| Monitoring and health | Supported | Use `$$nemoclaw status`, `$$nemoclaw logs --follow`, and `openshell term`. Refer to [Monitor Sandbox Activity](../monitoring/monitor-sandbox-activity). | +| Multi-agent and multi-sandbox usage | Supported with caveats | Side-by-side sandboxes use distinct names, and each name maps to exactly one agent type. OpenClaw and Hermes sandboxes also use distinct dashboard ports. Known multi-instance issues include gateway-port collisions ([#5359](https://github.com/NVIDIA/NemoClaw/issues/5359)) and parallel inference routing fallback ([#5343](https://github.com/NVIDIA/NemoClaw/issues/5343)). A declarative multi-agent manifest is roadmap ([#2853](https://github.com/NVIDIA/NemoClaw/issues/2853)). | +| Monitoring and health | Supported | Use `$$nemoclaw status`, `$$nemoclaw logs --follow`, and `openshell term`. Refer to [CLI Commands Reference](commands). | | External telemetry and observability export | Supported with caveats | OpenClaw can emit conversation traces through its diagnostics plugin, and LangChain Deep Agents Code can explicitly opt into bounded-content OTLP/HTTP traces with `--observability`. Both paths require an operator-run host collector. NemoClaw does not manage the collector, remote exporter credentials, fleet-wide routing, metrics or logs export, or authenticated tenant identity. Treat exported prompts, responses, tool inputs, and tool results as sensitive application data. Refer to [Deep Agents Code OTLP Traces](commands#deep-agents-code-otlp-traces). Broader observability adapter work remains tracked in [#3915](https://github.com/NVIDIA/NemoClaw/issues/3915). | -| Audit and session records | Supported with caveats | OpenClaw stores per-session JSONL event logs you can export for audit or compliance review; Hermes stores its own runtime state. Export is manual per sandbox. Refer to [Inspect Agent Session State](../monitoring/monitor-sandbox-activity#inspect-agent-session-state). | +| Audit and session records | Supported with caveats | OpenClaw stores per-session JSONL event logs, and Hermes stores its own runtime state. Deep Agents Code has no equivalent session-log export, but it can emit bounded-content traces when you explicitly enable observability. Export is manual per sandbox. | | Resource quotas | Supported with caveats | The entrypoint applies best-effort process and file-descriptor limits (`ulimit -u 512`, `ulimit -n 65536`). Set hard limits through the container runtime for fail-closed enforcement. Refer to [Process Controls](../security/best-practices#process-controls). | | Cost and spend controls | Platform or partner-owned | Deny-by-default egress and routed inference reduce exfiltration and stray endpoints, but NemoClaw does not enforce per-token spend budgets. Set spend limits with your inference provider and monitor unattended agents. | | Credential isolation | Supported | Inference credentials stay on the host and never enter the sandbox; the agent reaches models through `inference.local`. CLI output redaction adds defense in depth, and OpenClaw sandboxes also run a memory secret scanner. Refer to [Credential Storage](../security/credential-storage) and [Security Best Practices](../security/best-practices). | -| Upgrades and lifecycle | Supported with caveats | Upgrade NemoClaw, then run `$$nemoclaw rebuild` to recreate the sandbox with the current image while backing up and restoring state. Do not run `openclaw update` inside the sandbox; the agent is image-pinned. Known gaps include config restore after rebuild ([#5202](https://github.com/NVIDIA/NemoClaw/issues/5202)) and version attachment flexibility ([#2217](https://github.com/NVIDIA/NemoClaw/issues/2217)). | +| Upgrades and lifecycle | Supported with caveats | Upgrade NemoClaw, then run `$$nemoclaw rebuild` to recreate the sandbox with the current image while backing up and restoring state. Do not update the agent runtime in place inside the sandbox because the agent is image-pinned. Known gaps include config restore after rebuild ([#5202](https://github.com/NVIDIA/NemoClaw/issues/5202)) and version attachment flexibility ([#2217](https://github.com/NVIDIA/NemoClaw/issues/2217)). | | Backup and restore | Supported | Create snapshots and restore workspace and agent state with the snapshot and backup commands. Refer to [Create and Restore Snapshots](../manage-sandboxes/state-and-backups/create-and-restore-snapshots). | -| Remote and cloud deployment | Supported with caveats | Provision a tested Linux host, connect over SSH, run the installer, and run `$$nemoclaw onboard`. Keep dashboards bound to loopback and access them through SSH port forwarding. Automatic recovery after a host reboot is not guaranteed; follow the manual recovery flow in [Deploy to a Headless Server](../deployment/deploy-to-headless-server). | +| Remote and cloud deployment | Supported with caveats | Provision a tested Linux host, connect over SSH, run the installer, and run `$$nemoclaw onboard`. For OpenClaw or Hermes, keep the agent dashboard bound to loopback and access it through SSH port forwarding. Automatic recovery after a host reboot is not guaranteed; follow the manual recovery flow in [Deploy to a Headless Server](../deployment/deploy-to-headless-server). | For container-level hardening beyond the entrypoint defaults, refer to [Sandbox Hardening](../manage-sandboxes/configure-sandboxes/review-sandbox-hardening). @@ -93,9 +93,9 @@ The following table classifies each admin and control-plane expectation by curre |---|---|---| | Centralized fleet and sandbox management across hosts | Manual or admin-only | Each host is managed independently with the NemoClaw CLI. There is no cross-host management console. | | Role-based access control for operators | Out of scope | NemoClaw assumes a single trusted operator per host. There is no operator RBAC layer. | -| Enterprise identity integration (SSO, OIDC, SAML) | Roadmap-only | Gateway access uses device pairing for the OpenClaw dashboard or bearer-token auth for the Hermes API, not enterprise identity providers. | +| Agent gateway access and enterprise identity integration (SSO, OIDC, SAML) | Roadmap-only | OpenClaw agent gateway access uses dashboard device pairing, and Hermes agent gateway access uses bearer-token authentication for its API. Neither in-sandbox agent gateway integrates with enterprise identity providers. Deep Agents Code has no in-sandbox agent gateway. | | Multi-tenant isolation | Out of scope | Isolation is per-sandbox at the container level. NemoClaw does not provide tenant separation for multiple untrusted users on one host. | -| Centralized audit export and SIEM integration | Manual or admin-only | Export per-session JSONL logs by hand for audit review. Deep Agents Code traces can reach an operator-managed backend, but that local collector path is not centralized fleet audit, authenticated tenant identity, or a managed SIEM integration. Broader forwarding work remains tracked in [#3915](https://github.com/NVIDIA/NemoClaw/issues/3915). | +| Centralized audit export and SIEM integration | Manual or admin-only | Export OpenClaw session logs or Hermes runtime state by hand for audit review. Deep Agents Code traces can reach an operator-managed backend, but that local collector path is not centralized fleet audit, authenticated tenant identity, or a managed SIEM integration. Broader forwarding work remains tracked in [#3915](https://github.com/NVIDIA/NemoClaw/issues/3915). | | Usage quotas, cost budgets, and billing | Platform or partner-owned | Set token and rate limits with your inference provider. NemoClaw does not meter or cap spend. | | Credential and secrets management | Supported with caveats | Provider credentials live on the host with restricted permissions and redaction. Integration with an external secrets manager is manual. Refer to [Credential Storage](../security/credential-storage). | | Policy as code distributed across a fleet | Manual or admin-only | Baseline policy and presets are versioned in the blueprint and applied per sandbox. There is no fleet-wide policy distribution service. | @@ -139,6 +139,11 @@ This guidance must stay accurate for the duration of the evaluation window. - [Security Best Practices](../security/best-practices) for the full control-by-control risk framework. - [Network Policies](network-policies) for the baseline egress policy reference. + - [Monitor Sandbox Activity](../monitoring/monitor-sandbox-activity) for status, logs, audit records, and the TUI. + + +- [Understand Trace Export](../monitoring/understand-deepagents-trace-export) for Deep Agents Code observability boundaries. + - [Troubleshooting](troubleshooting) for installation, onboarding, and runtime issue resolution. - [How It Works](../about/how-it-works) for the protection-layer architecture. diff --git a/docs/resources/engineer-agentic-documentation.mdx b/docs/resources/engineer-agentic-documentation.mdx index d34a83d547f..0ef65298c2e 100644 --- a/docs/resources/engineer-agentic-documentation.mdx +++ b/docs/resources/engineer-agentic-documentation.mdx @@ -191,7 +191,8 @@ Apply these source rules: - Organize procedures around the reader journey from choosing through setup, operation, validation, and troubleshooting. - Keep reusable troubleshooting and reference facts with one canonical owner, then link to that owner. - Generate tables and reference values from authoritative product data when that removes manual synchronization. -- Store navigation, slugs, and variant membership in a machine-readable index. +- Store navigation and route slugs in a machine-readable index. +- Declare intentional partial variant scope in source metadata and validate it against published navigation membership. - Treat generated pages as disposable build output and never edit them by hand. - Preserve published routes with direct redirects when content moves. @@ -311,14 +312,15 @@ Use these rules: - Replace a build-time placeholder when only a literal name or command differs. - Use a conditional block when the workflow, behavior, state layout, or security boundary differs. - Use a separate source page when most of the procedure is variant-specific. -- Keep variant membership and route slugs in the navigation model. -- Regenerate every variant before route and link validation. -- Inspect rendered variants for broken lists, joined paragraphs, and missing context. +- Keep published guide variant membership and route slugs in the navigation model. +- Declare the intended guide variant subset in source metadata when a page does not apply to every guide variant. +- Regenerate every guide variant before route and link validation. +- Inspect rendered guide variants for broken lists, joined paragraphs, and missing context. -NemoClaw generates shared OpenClaw, Hermes, and Deep Agents pages from one MDX source. -Its build rewrites the host CLI placeholder, removes inapplicable conditional blocks, and publishes a distinct route for each supported agent. +NemoClaw generates shared OpenClaw, Hermes, and Deep Agents guide variants from one MDX source. +Its build rewrites the host CLI placeholder, removes inapplicable conditional blocks, and publishes a distinct route for each guide variant. -### Separate the Source from Its Variant Targets +### Separate the Source from Its Guide Variant Targets Authors edit the normal source page under `docs/`. For this guide, the only source is `docs/resources/engineer-agentic-documentation.mdx`. @@ -379,19 +381,28 @@ Use $$nemoclaw for all shared host CLI references and wrap The resolver rejects a generated page if it still contains an unresolved line-oriented `` directive or a runtime agent component. These checks make static resolution a publishing invariant instead of relying on client-side hydration to correct variant content. -Variant membership is explicit in `docs/index.yml`. -Each agent variant contains its own navigation tree, and a shared page appears in a tree through a generated target path with this contract: +Published guide variant membership is explicit in `docs/index.yml`. +Each guide variant contains its own navigation tree, and a shared page appears in a tree through a generated target path with this contract: ```text _build/agent-variants//..generated.mdx ``` The `` value is `openclaw`, `hermes`, or `deepagents`. -The directory and base name before the variant suffix must match the canonical source path under `docs/`. +The directory and base name before the guide variant suffix must match the canonical source path under `docs/`. + +When a source intentionally applies to fewer than all three guide variants, its frontmatter declares the exact subset: + +```yaml +agent-variants: ["openclaw", "hermes"] +``` + +The generator compares this declaration with the source's actual navigation membership and fails when the declaration is missing or differs. +An all-variant source omits the declaration, so removing one of its navigation targets also fails until the author restores the route or explicitly declares the narrower scope. If this source-only guide is added to Fern navigation, it would use the following mapping: -| Variant | Generated target in `docs/index.yml` | Published route suffix | +| Guide variant | Generated target in `docs/index.yml` | Published route suffix | |---|---|---| | OpenClaw | `_build/agent-variants/resources/engineer-agentic-documentation.openclaw.generated.mdx` | `/user-guide/openclaw/resources/engineer-agentic-documentation`. | | Hermes | `_build/agent-variants/resources/engineer-agentic-documentation.hermes.generated.mdx` | `/user-guide/hermes/resources/engineer-agentic-documentation`. | @@ -421,7 +432,7 @@ This separation has several consequences: - A source file's filesystem location does not independently define its public URL. - The same source can be published in several variant trees with the same page slug. -- A source can remain single-variant by pointing navigation directly to its source MDX file instead of a generated target. +- A source page can apply to one guide variant by declaring that guide variant in frontmatter and pointing navigation directly to its source MDX file instead of a generated target. - Moving or renaming a published page requires a navigation update and a redirect review, even when the source file stays in the same directory. - Generated filenames are an interface with the generator and should not use an arbitrary naming pattern. @@ -431,16 +442,17 @@ The resolver is `scripts/sync-agent-variant-docs.mts`. Run it directly through `npm run docs:sync-agent-variants`, or let `npm run docs` invoke it through the shared `docs:prepare` step. The script reads `docs/index.yml` before it reads shared source pages. -For each recognized generated path, it performs the following work: +It derives each source page's published guide variant membership and validates every intentional subset declaration before rendering. +For each recognized generated path, it then performs the following work: -1. It reads the active navigation variant from the `openclaw`, `hermes`, or `deepagents` tree. +1. It reads the active guide variant from the `openclaw`, `hermes`, or `deepagents` navigation tree. 2. It removes the `_build/agent-variants/` prefix and the `..generated.mdx` suffix. 3. It resolves the remaining path to one canonical `.mdx` source under `docs/`. -4. It renders that source for the active variant. +4. It renders that source for the active guide variant. 5. It writes the result under `docs/_build/agent-variants/` using the path declared in the index. 6. It removes stale generated files that no longer have a navigation target. -Rendering replaces the host CLI placeholder with the correct binary name, removes conditional blocks that do not apply to the active variant, and adjusts relative image and component-import paths for the deeper generated directory. +Rendering replaces the host CLI placeholder with the correct binary name, removes conditional blocks that do not apply to the active guide variant, and adjusts relative image and component-import paths for the deeper generated directory. Route-style links between documentation pages stay tied to the published navigation model instead of the generated filesystem location. The generated directory is ignored by Git. @@ -449,10 +461,10 @@ Contributors commit the canonical source and `docs/index.yml` mapping, while the ### Validate the Complete Route Graph The `npm run docs` command prepares generated content before Fern validation. -The validation sequence checks the starter prompt, regenerates the agent variants, verifies generated-page freshness, derives published routes from `docs/index.yml`, and runs the pinned Fern checker. +The validation sequence checks the starter prompt, validates declared guide variant scope, regenerates the guide variants, verifies generated-page freshness, derives published routes from `docs/index.yml`, and runs the pinned Fern checker. -This order matters because Fern cannot validate a variant route until the generated target named by `docs/index.yml` exists. -It also ensures that source edits, variant transforms, navigation mappings, and published routes are tested as one contract. +This order matters because Fern cannot validate a guide variant route until the generated target named by `docs/index.yml` exists. +It also ensures that source edits, guide variant transforms, navigation mappings, and published routes are tested as one contract. ## Treat Documentation as an Executable Product Surface @@ -462,7 +474,8 @@ Syntax validation alone does not catch behavioral drift. | Gate | Defect it prevents | NemoClaw example | |---|---|---| | Source formatting | Unreadable diffs and inconsistent authoring. | Markdown linting, one sentence per source line, and copyable command rules. | -| Generated freshness | Stale prompts or agent variants. | Build-time generation followed by read-only freshness checks. | +| Guide variant scope | A page is omitted from an applicable guide or silently published for the wrong agent. | Source `agent-variants` declarations checked against navigation membership. | +| Generated freshness | Stale prompts or guide variants. | Build-time generation followed by read-only freshness checks. | | Route graph | Links or redirects that target unpublished pages. | Published-route validation derived from `docs/index.yml`. | | Product parity | Reference documentation that disagrees with the product. | CLI command, flag, installer, and environment-variable parity checks. | | Pull-request preview | Layout or navigation failures hidden by source checks. | An isolated Fern preview for each docs pull request. | @@ -552,13 +565,14 @@ The current NemoClaw repository provides concrete examples for each layer. | Canonical content | `docs/**/*.mdx`. | | Repository-wide documentation contract | `AGENTS.md`. | | Authoring and review policy | `docs/CONTRIBUTING.md` and `docs/AGENTS.md`. | -| Navigation and variants | `docs/index.yml`. | +| Navigation and published guide variants | `docs/index.yml`. | +| Intentional partial guide variant scope | Source `agent-variants` frontmatter validated by `scripts/sync-agent-variant-docs.mts`. | | Site and redirect configuration | `fern/docs.yml` and `fern/fern.config.json`. | | Machine delivery | Published Markdown routes, `llms.txt`, and the read-only docs MCP server. | | Routing skill | `.agents/skills/nemoclaw-user-guide/SKILL.md`. | | Starter prompt | `docs/resources/starter-prompt.md` and `scripts/generate-starter-prompt.mts`. | | Product-owned generated facts | `ci/platform-matrix.json` and `scripts/generate-platform-docs.py`. | -| Variant generation | `scripts/sync-agent-variant-docs.mts`. | +| Guide variant generation | `scripts/sync-agent-variant-docs.mts`. | | Route validation | `scripts/check-docs-published-routes.mts`. | | Update workflow | `.agents/skills/nemoclaw-contributor-update-docs/SKILL.md`. | | Documentation review receipt | `.github/PULL_REQUEST_TEMPLATE.md`, `.github/workflows/docs-review-receipt.yaml`, and `scripts/docs-review-receipt.mts`. | @@ -578,13 +592,13 @@ Before calling a documentation system agent-ready, confirm the following conditi - Every priority task has routing metadata, a stable machine-readable route, and a verification step. - Search, the docs index, and rendered navigation resolve to the same release and canonical owner. - Routing prompts and skills point to the source instead of copying it. -- Variant generation is deterministic and variant behavior is tested. +- Guide variant generation is deterministic, partial scope declarations match published routes, and guide variant behavior is tested. - Agent workflows define scope, approvals, stop conditions, and completion evidence. - Code-changing and documentation-only pull requests record a documentation-review result tied to the current head and instruction version. - New commits invalidate the recorded review until the documentation writer subagent runs again. - Pull requests validate generated output, routes, links, product parity, and rendered previews. - Release publication uses an approved immutable revision. -- Retrieval, citation, task, variant, safety, and freshness regressions have repeatable evaluations. +- Retrieval, citation, task, guide variant, safety, and freshness regressions have repeatable evaluations. The first practical milestone is one product journey that passes every condition above. Expand the system only after that loop is reliable. diff --git a/docs/security/configure-corporate-ca-trust.mdx b/docs/security/configure-corporate-ca-trust.mdx index e2e26922043..0f0291aa0df 100644 --- a/docs/security/configure-corporate-ca-trust.mdx +++ b/docs/security/configure-corporate-ca-trust.mdx @@ -8,6 +8,7 @@ description-agent: "Configures corporate proxy CA trust for runtime TLS and supp keywords: ["nemoclaw corporate ca", "corporate proxy tls", "certificate verification"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- Configure a corporate Certificate Authority (CA) before onboarding when an enterprise proxy re-signs external TLS with a root that OpenShell does not provide. diff --git a/docs/security/credential-rotation.mdx b/docs/security/credential-rotation.mdx index 312c3a0ffd9..842f183e475 100644 --- a/docs/security/credential-rotation.mdx +++ b/docs/security/credential-rotation.mdx @@ -8,6 +8,7 @@ description-agent: "Step-by-step guide for rotating inference API keys, messagin keywords: ["nemoclaw credential rotation", "rotate api key", "update inference key", "messaging token", "nemoclaw credentials reset"] content: type: "how_to" +agent-variants: ["openclaw", "hermes"] --- NemoClaw uses different rotation paths for inference, messaging, and web search credentials. Inference credentials can normally be updated while reusing the existing sandbox. diff --git a/docs/security/openclaw-controls.mdx b/docs/security/openclaw-controls.mdx index cfea9d4d207..e5dfeb0193e 100644 --- a/docs/security/openclaw-controls.mdx +++ b/docs/security/openclaw-controls.mdx @@ -8,6 +8,7 @@ description-agent: "Lists OpenClaw security controls that operate independently keywords: ["openclaw security controls", "nemoclaw security boundary", "prompt injection", "tool access control"] content: type: "concept" +agent-variants: ["openclaw"] --- NemoClaw provides infrastructure-layer security through sandbox isolation, network policy, filesystem restrictions, SSRF validation, and credential handling. It delegates all application-layer security to OpenClaw. diff --git a/docs/security/openshell-0.0.71-gateway-auth-review.mdx b/docs/security/openshell-0.0.71-gateway-auth-review.mdx index 4f547882d07..737002b352c 100644 --- a/docs/security/openshell-0.0.71-gateway-auth-review.mdx +++ b/docs/security/openshell-0.0.71-gateway-auth-review.mdx @@ -10,6 +10,7 @@ description-agent: >- keywords: ["OpenShell 0.0.71", "gateway authentication", "mTLS", "sandbox JWT"] content: type: "reference" +agent-variants: ["openclaw", "hermes"] --- Review date: 2026-06-26 diff --git a/docs/security/tcb-boundary.mdx b/docs/security/tcb-boundary.mdx index e273098c7a5..a725149fdb2 100644 --- a/docs/security/tcb-boundary.mdx +++ b/docs/security/tcb-boundary.mdx @@ -10,6 +10,7 @@ description-agent: >- keywords: ["nemoclaw trusted computing base", "gateway lifecycle security", "shields trust boundary"] content: type: "reference" +agent-variants: ["openclaw", "hermes"] --- NemoClaw uses a small set of host and sandbox components to restart built-in gateways and change shields posture without granting lifecycle authority to the sandbox agent. diff --git a/scripts/sync-agent-variant-docs.mts b/scripts/sync-agent-variant-docs.mts index 02d920c7d21..0bcac3a035d 100644 --- a/scripts/sync-agent-variant-docs.mts +++ b/scripts/sync-agent-variant-docs.mts @@ -20,6 +20,7 @@ type RenderTarget = { sourcePath: string; variant: AgentVariant; }; +type NavigationVariantMembership = Map>; type RenderAgentVariantOptions = { outputPath?: string; sourcePath?: string; @@ -62,10 +63,13 @@ function main(): void { writeGeneratedFiles(generatedVariantPages); } -function splitFrontmatter(source: string): { frontmatter: string; body: string } { +function splitFrontmatter( + source: string, + sourceLabel = "source page", +): { frontmatter: string; body: string } { const match = source.match(/^(\uFEFF?---\r?\n[\s\S]*?\r?\n---\r?\n)([\s\S]*)$/); if (!match) { - throw new Error("commands.mdx must start with YAML frontmatter"); + throw new Error(`${sourceLabel} must start with YAML frontmatter`); } return { frontmatter: match[1], body: match[2] }; } @@ -282,7 +286,13 @@ function renderGeneratedAgentVariantPages(): RenderedFile[] { } function findAgentVariantTargets(): RenderTarget[] { - const sharedSources = findSharedNavigationSourcePaths(); + const variantMembership = findNavigationVariantMembership(); + assertDeclaredAgentVariantScope(variantMembership); + const sharedSources = new Set( + [...variantMembership.entries()] + .filter(([, variants]) => variants.size > 1) + .map(([sourcePath]) => sourcePath), + ); assertNoUnsharedPlaceholders(sharedSources); return findGeneratedNavigationTargets().sort((left, right) => { const sourceOrder = left.sourcePath.localeCompare(right.sourcePath); @@ -316,18 +326,29 @@ function collectGeneratedTargets(nodes: NavigationNode[], variant: AgentVariant) }); } -function findSharedNavigationSourcePaths(): Set { +function findNavigationVariantMembership(): NavigationVariantMembership { const docsIndex = parse(readFileSync(path.join(docsRoot, "index.yml"), "utf8")) as DocsIndex; const userGuide = docsIndex.navigation?.find((item) => Array.isArray(item.variants)); - const openclaw = userGuide?.variants?.find((variant) => variant.slug === "openclaw"); - const hermes = userGuide?.variants?.find((variant) => variant.slug === "hermes"); - if (!openclaw?.layout || !hermes?.layout) { - throw new Error("docs/index.yml must define openclaw and hermes navigation variants"); + if (!userGuide?.variants) { + throw new Error("docs/index.yml must define navigation variants"); } - const openclawPaths = collectSourcePaths(openclaw.layout); - const hermesPaths = collectSourcePaths(hermes.layout); - return new Set([...openclawPaths].filter((sourcePath) => hermesPaths.has(sourcePath))); + const membership: NavigationVariantMembership = new Map(); + for (const variant of userGuide.variants) { + if (!isAgentVariant(variant.slug) || !variant.layout) continue; + for (const sourcePath of collectSourcePaths(variant.layout)) { + const variants = membership.get(sourcePath) ?? new Set(); + variants.add(variant.slug); + membership.set(sourcePath, variants); + } + } + + for (const variant of agentVariants) { + if (!userGuide.variants.some((entry) => entry.slug === variant && entry.layout)) { + throw new Error(`docs/index.yml must define the ${variant} navigation variant`); + } + } + return membership; } function collectSourcePaths(nodes: NavigationNode[]): Set { @@ -370,6 +391,81 @@ function normalizeLegacyVariantSource(navPath: string): string { return navPath; } +function assertDeclaredAgentVariantScope(membership: NavigationVariantMembership): void { + const violations: string[] = []; + + for (const [sourcePath, publishedVariants] of [...membership.entries()].sort(([left], [right]) => + left.localeCompare(right), + )) { + const sourceFilePath = path.join(docsRoot, sourcePath); + const declaredVariants = readDeclaredAgentVariants( + readFileSync(sourceFilePath, "utf8"), + sourcePath, + ); + const published = orderedAgentVariants(publishedVariants); + + if (!declaredVariants) { + if (publishedVariants.size < agentVariants.length) { + violations.push( + `docs/${sourcePath} is published for [${published.join(", ")}] but does not declare agent-variants`, + ); + } + continue; + } + + const declared = orderedAgentVariants(declaredVariants); + if ( + declared.length !== published.length || + declared.some((variant, index) => variant !== published[index]) + ) { + violations.push( + `docs/${sourcePath} declares agent-variants [${declared.join(", ")}] but navigation publishes [${published.join(", ")}]`, + ); + } + } + + if (violations.length > 0) { + throw new Error( + [ + "Guide variant scope does not match docs/index.yml:", + ...violations.map((violation) => ` - ${violation}`), + "Publish each source page in every applicable guide variant, or declare the intentional subset in frontmatter.", + ].join("\n"), + ); + } +} + +function readDeclaredAgentVariants(source: string, sourcePath: string): Set | null { + const { frontmatter } = splitFrontmatter(source, `docs/${sourcePath}`); + const frontmatterSource = frontmatter + .replace(/^\uFEFF?---\r?\n/, "") + .replace(/\r?\n---\r?\n$/, ""); + const parsed = parse(frontmatterSource) as { "agent-variants"?: unknown } | null; + const value = parsed?.["agent-variants"]; + if (value === undefined) return null; + if (!Array.isArray(value) || value.length === 0) { + throw new Error(`docs/${sourcePath} agent-variants must be a non-empty list`); + } + + const variants = new Set(); + for (const entry of value) { + if (typeof entry !== "string" || !isAgentVariant(entry)) { + throw new Error( + `docs/${sourcePath} agent-variants contains unsupported value ${JSON.stringify(entry)}`, + ); + } + if (variants.has(entry)) { + throw new Error(`docs/${sourcePath} agent-variants repeats ${entry}`); + } + variants.add(entry); + } + return variants; +} + +function orderedAgentVariants(variants: ReadonlySet): AgentVariant[] { + return agentVariants.filter((variant) => variants.has(variant)); +} + function assertNoUnsharedPlaceholders(sharedSources: Set): void { const offenderPaths: string[] = []; for (const sourcePath of findPlaceholderSourcePaths()) { @@ -380,7 +476,7 @@ function assertNoUnsharedPlaceholders(sharedSources: Set): void { [ "The following non-shared nav pages contain $$nemoclaw and would render it literally:", ...offenderPaths.map((offenderPath) => ` - docs/${offenderPath}`), - "Use a literal CLI name on single-variant pages, or add the page to both nav variants.", + "Use a literal CLI name on single-variant pages, or publish the page in every applicable variant.", ].join("\n"), ); } diff --git a/test/check-docs-published-routes.test.ts b/test/check-docs-published-routes.test.ts index 6f9abdfe0f7..62b9cdc535a 100644 --- a/test/check-docs-published-routes.test.ts +++ b/test/check-docs-published-routes.test.ts @@ -527,6 +527,24 @@ describe("headless server deployment routes", () => { }); }); +describe("gateway lifecycle authority routes", () => { + const index = buildPublishedRouteIndex(); + + it("publishes the OpenShell gateway guide for every guide variant (#6576)", () => { + for (const variant of ["openclaw", "hermes", "deepagents"]) { + expect( + index.routes.has(`/user-guide/${variant}/deployment/gateway-lifecycle-authority`), + ).toBe(true); + } + }); + + it("resolves every OpenShell gateway guide link for each guide variant (#6576)", () => { + expect(findBrokenPublishedRoutes("deployment/gateway-lifecycle-authority.mdx", index)).toEqual( + [], + ); + }); +}); + describe("native changelog legacy routes", () => { it("redirects every retired Release Notes route directly to the changelog", () => { expect(findMissingDirectLegacyReleaseNotesRedirects()).toEqual([]); diff --git a/test/sync-agent-variant-docs.test.ts b/test/sync-agent-variant-docs.test.ts index c2699124e44..f0762b30ab8 100644 --- a/test/sync-agent-variant-docs.test.ts +++ b/test/sync-agent-variant-docs.test.ts @@ -22,6 +22,8 @@ import { renderAgentVariantPage } from "../scripts/sync-agent-variant-docs.mts"; const REPO_ROOT = path.resolve(fileURLToPath(new URL("..", import.meta.url))); const SYNC_SCRIPT = path.join(REPO_ROOT, "scripts/sync-agent-variant-docs.mts"); const NODE_MODULES = path.join(REPO_ROOT, "node_modules"); +const AGENT_VARIANTS = ["openclaw", "hermes", "deepagents"] as const; +type AgentVariant = (typeof AGENT_VARIANTS)[number]; const FRONTMATTER = `--- # SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. @@ -36,7 +38,104 @@ content: --- `; +function runVariantScopeFixture( + source: string, + publishedVariants: readonly AgentVariant[], +): { output: string; status: number | null } { + const fixtureRoot = mkdtempSync(path.join(tmpdir(), "nemoclaw-agent-variant-scope-")); + try { + const fixtureScript = path.join(fixtureRoot, "scripts/sync-agent-variant-docs.mts"); + mkdirSync(path.dirname(fixtureScript), { recursive: true }); + writeFileSync(fixtureScript, readFileSync(SYNC_SCRIPT, "utf8")); + symlinkSync(NODE_MODULES, path.join(fixtureRoot, "node_modules"), "junction"); + + const docsRoot = path.join(fixtureRoot, "docs"); + mkdirSync(path.join(docsRoot, "reference"), { recursive: true }); + const variantNavigation = AGENT_VARIANTS.map((variant) => { + const layout = publishedVariants.includes(variant) + ? ` + - page: Example + path: reference/example.mdx` + : " []"; + return ` - slug: ${variant} + layout:${layout}`; + }).join("\n"); + writeFileSync( + path.join(docsRoot, "index.yml"), + `navigation: + - section: User Guide + variants: +${variantNavigation} +`, + ); + writeFileSync(path.join(docsRoot, "reference/example.mdx"), source); + + const result = spawnSync(process.execPath, ["--import", "tsx", realpathSync(fixtureScript)], { + cwd: fixtureRoot, + encoding: "utf8", + timeout: 10_000, + }); + return { + output: `${result.stdout}\n${result.stderr}`, + status: result.status, + }; + } finally { + rmSync(fixtureRoot, { recursive: true, force: true }); + } +} + describe("sync-agent-variant-docs", () => { + it("rejects a partial-variant page without an explicit scope declaration (#6576)", () => { + const result = runVariantScopeFixture( + `--- +title: "Example" +--- +OpenClaw content. +`, + ["openclaw"], + ); + + expect(result.status).toBe(1); + expect(result.output).toContain( + "docs/reference/example.mdx is published for [openclaw] but does not declare agent-variants", + ); + expect(result.output).toContain( + "Publish each source page in every applicable guide variant, or declare the intentional subset in frontmatter.", + ); + }); + + it("rejects a scope declaration that differs from navigation membership (#6576)", () => { + const result = runVariantScopeFixture( + `--- +title: "Example" +agent-variants: ["openclaw", "hermes"] +--- +OpenClaw content. +`, + ["openclaw"], + ); + + expect(result.status).toBe(1); + expect(result.output).toContain( + "docs/reference/example.mdx declares agent-variants [openclaw, hermes] but navigation publishes [openclaw]", + ); + }); + + it("accepts an explicit scope that matches navigation membership (#6576)", () => { + const result = runVariantScopeFixture( + `--- +title: "Example" +agent-variants: ["openclaw"] +--- +OpenClaw content. +`, + ["openclaw"], + ); + + expect(result.status).toBe(0); + expect(result.output).not.toContain("Guide variant scope does not match docs/index.yml"); + }); + it("passes --check when generated docs are already synchronized", () => { const fixtureRoot = mkdtempSync(path.join(tmpdir(), "nemoclaw-agent-variant-check-")); try {