Skip to content

refactor(onboard): centralize machine state metadata - #4362

Merged
cv merged 4 commits into
mainfrom
stack/onboard-fsm-definition
May 29, 2026
Merged

refactor(onboard): centralize machine state metadata#4362
cv merged 4 commits into
mainfrom
stack/onboard-fsm-definition

Conversation

@cv

@cv cv commented May 27, 2026

Copy link
Copy Markdown
Collaborator

Summary

Introduce a canonical metadata table for the coarse onboarding machine states. Existing state/type exports remain stable while state IDs, terminal IDs, and non-terminal IDs now flow from the shared machine definition.

Changes

  • Add src/lib/onboard/machine/definition.ts as the ordered catalog of onboard FSM states and metadata.
  • Update src/lib/onboard/machine/types.ts to re-export state catalogs from the definition.
  • Add src/lib/onboard/machine/definition.test.ts to guard ordering, terminal/non-terminal catalogs, and step metadata uniqueness.

Type of Change

  • Code change (feature, bug fix, or refactor)
  • Code change with doc updates
  • Doc only (prose changes, no code sample modifications)
  • Doc only (includes code sample changes)

Verification

  • npx prek run --all-files passes
  • npm test passes
  • Tests added or updated for new or changed behavior
  • No secrets, API keys, or credentials committed
  • Docs updated for user-facing behavior changes
  • npm run docs builds without warnings (doc changes only)
  • Doc pages follow the style guide (doc changes only)
  • New doc pages include SPDX header and frontmatter (new pages only)

Signed-off-by: Carlos Villela cvillela@nvidia.com

Summary by CodeRabbit

  • Tests

    • Added test suite validating onboard state definitions, transitions, and metadata constraints.
  • Refactor

    • Consolidated onboard state machine definitions into a canonical source with improved organization and helper utilities for state lookup and validation.

Review Change Stack

cv added 2 commits May 27, 2026 15:18
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv cv self-assigned this May 27, 2026
@copy-pr-bot

copy-pr-bot Bot commented May 27, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@coderabbitai

coderabbitai Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ebe95942-5792-4ac6-9e2e-e34e56d8598f

📥 Commits

Reviewing files that changed from the base of the PR and between fb1b32d and 0931bc7.

📒 Files selected for processing (2)
  • src/lib/onboard/machine/definition.test.ts
  • src/lib/onboard/machine/definition.ts
💤 Files with no reviewable changes (2)
  • src/lib/onboard/machine/definition.test.ts
  • src/lib/onboard/machine/definition.ts

📝 Walkthrough

Walkthrough

This PR centralizes onboard machine state vocabulary by introducing a canonical state-definition module that exports a readonly tuple of state objects with terminal flags, optional step names, and progress metadata. It derives ordered state ID arrays and TypeScript union types from these definitions, then refactors the existing types.ts module to import these canonical exports instead of maintaining duplicate declarations. A comprehensive test suite validates state ordering consistency, terminal/non-terminal derivation, step name uniqueness, progress metadata constraints, and state lookup behavior.

Changes

Onboard FSM State Vocabulary

Layer / File(s) Summary
Core state definitions
src/lib/onboard/machine/definition.ts
Establishes ONBOARD_MACHINE_STATE_DEFINITIONS as a readonly constant tuple with each state object containing a state ID, terminal flag, and optional stepName and progress metadata.
Derived types, constants, and lookup
src/lib/onboard/machine/definition.ts
Derives state ID union types and ordered arrays (all, terminal-only, non-terminal-only) from the definition tuple. Exports narrowing types for definitions containing stepName or progress. Implements getOnboardMachineStateDefinition function that performs lookup and throws for unknown states.
Refactor types.ts to use centralized definitions
src/lib/onboard/machine/types.ts
Replaces local state declarations and computed non-terminal types with imports of *_STATE_IDS constants and Onboard*StateId type aliases from the new definition module.
State definition validation tests
src/lib/onboard/machine/definition.test.ts
Vitest suite asserting canonical state ordering alignment, terminal/non-terminal consistency, unique stepName values, progress metadata constraints (fixed total of 8, bounded number field, non-empty title), and correct lookup/error behavior.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested labels

NemoClaw CLI, v0.0.51

Suggested reviewers

  • ericksoa
  • cjagwani

Poem

🐰 The onboard states now neatly dwell,
In one true source, a central shell,
With terminal flags and progress clear,
No duplication, just cheer!
The machine marches on, state by state.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The pull request title clearly summarizes the main change: centralizing machine state metadata into a canonical definition file instead of scattered across multiple files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch stack/onboard-fsm-definition

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

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

E2E Advisor Recommendation

Required E2E: cloud-onboard-e2e, onboard-resume-e2e
Optional E2E: onboard-repair-e2e, onboard-negative-paths-e2e

Dispatch hint: cloud-onboard-e2e,onboard-resume-e2e

Workflow run

Full advisor summary

E2E Recommendation Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required E2E

  • cloud-onboard-e2e (medium): Validates the real non-interactive cloud onboarding happy path end-to-end after the state catalog refactor, including install, gateway, inference, sandbox creation, policy presets, sandbox health, security checks, and inference.local.
  • onboard-resume-e2e (medium): Directly exercises interrupted onboarding and resume through the session/state machinery whose state vocabulary and non-terminal derivation changed.

Optional E2E

  • onboard-repair-e2e (medium): Useful adjacent confidence for repair and resume-conflict behavior if reviewers are concerned about failure-transition derivation or recorded-state compatibility.
  • onboard-negative-paths-e2e (medium): Covers onboarding validation/error paths and friendly failures; optional because this PR does not directly change validation logic or provider selection behavior.

New E2E recommendations

  • onboarding-fsm-progress-metadata (medium): The new canonical definition includes progress and step metadata, but existing E2E coverage appears to validate live onboarding outcomes rather than asserting emitted progress/state metadata alignment across the full user flow.
    • Suggested test: Add a lightweight E2E or scenario assertion that captures a live non-interactive onboarding run and verifies emitted/recorded machine states and progress step labels come from the canonical definition without gaps or duplicates.

Dispatch hint

  • Workflow: E2E / Nightly
  • jobs input: cloud-onboard-e2e,onboard-resume-e2e

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

E2E Scenario Advisor Recommendation

Required scenario E2E: ubuntu-repo-cloud-openclaw
Optional scenario E2E: ubuntu-no-docker-preflight-negative, ubuntu-repo-cloud-hermes

Dispatch required scenario E2E:

  • gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw

Workflow run

Full scenario advisor summary

E2E Scenario Advisor

Base: origin/main
Head: HEAD
Confidence: high

Required scenario E2E

  • ubuntu-repo-cloud-openclaw: Changes refactor the core onboarding finite-state-machine vocabulary used by repo-current onboarding. The Ubuntu repo cloud OpenClaw scenario is the primary path that exercises successful onboarding through completion plus smoke/baseline onboarding validation.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-openclaw

Optional scenario E2E

  • ubuntu-no-docker-preflight-negative: Optional coverage for the failure/terminal-state side of the onboarding machine, especially because terminal and non-terminal state catalogs are now derived from the new definition metadata.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-no-docker-preflight-negative
  • ubuntu-repo-cloud-hermes: Optional adjacent onboarding coverage for the Hermes agent path using the same onboarding machine vocabulary but a different agent profile.
    • Dispatch: gh workflow run e2e-scenarios.yaml --ref <pr-head-ref> --field scenarios=ubuntu-repo-cloud-hermes

Relevant changed files

  • src/lib/onboard/machine/definition.test.ts
  • src/lib/onboard/machine/definition.ts
  • src/lib/onboard/machine/types.ts

@github-actions

github-actions Bot commented May 27, 2026

Copy link
Copy Markdown
Contributor

PR Review Advisor

Findings: 0 needs attention, 2 worth checking, 0 nice ideas
Since last review: 2 prior items resolved, 1 still applies, 0 new items found

Review findings

🛠️ Needs attention

  • None.

🔎 Worth checking

  • Source-of-truth review needed: Onboard progress metadata: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: `definition.ts` uses progress numbers 1,2,3,4,6,7,7,8 with total 8; `src/lib/onboard.ts` still has `ONBOARD_STEP_INDEX.messaging` at number 5.
  • Progress metadata still has an undocumented messaging-step gap (src/lib/onboard/machine/definition.ts:41): The new FSM metadata is described as canonical and includes progress values with total 8, but its state-backed sequence is 1,2,3,4,6,7,7,8. The missing number 5 is still represented only in the legacy onboarding step source as the non-FSM `messaging` step. If follow-up progress helpers consume this catalog as canonical, they can silently omit or misrepresent the messaging step.
    • Recommendation: Either include an explicit representation for non-state progress such as messaging, document that FSM progress intentionally has a gap, or add a parity/gap regression test against the legacy onboarding progress source before downstream helpers derive user-facing progress from this metadata.
    • Evidence: `definition.ts` assigns `sandbox` progress number 6 and `policies` progress number 8 with `total: 8`, while `src/lib/onboard.ts` still defines `ONBOARD_STEP_INDEX.messaging = { number: 5, title: "Messaging channels" }`. The added test checks only bounds and state-backed attachment, not parity with the legacy progress source or the intentional omission.

🌱 Nice ideas

  • None.
Since last review details

Current findings:

  • Source-of-truth review needed: Onboard progress metadata: The advisor marked localized patch analysis as needs_followup.
    • Recommendation: Identify the invalid state, source boundary, source-fix constraint, regression test, and removal condition before merging the localized behavior.
    • Evidence: `definition.ts` uses progress numbers 1,2,3,4,6,7,7,8 with total 8; `src/lib/onboard.ts` still has `ONBOARD_STEP_INDEX.messaging` at number 5.
  • Progress metadata still has an undocumented messaging-step gap (src/lib/onboard/machine/definition.ts:41): The new FSM metadata is described as canonical and includes progress values with total 8, but its state-backed sequence is 1,2,3,4,6,7,7,8. The missing number 5 is still represented only in the legacy onboarding step source as the non-FSM `messaging` step. If follow-up progress helpers consume this catalog as canonical, they can silently omit or misrepresent the messaging step.
    • Recommendation: Either include an explicit representation for non-state progress such as messaging, document that FSM progress intentionally has a gap, or add a parity/gap regression test against the legacy onboarding progress source before downstream helpers derive user-facing progress from this metadata.
    • Evidence: `definition.ts` assigns `sandbox` progress number 6 and `policies` progress number 8 with `total: 8`, while `src/lib/onboard.ts` still defines `ONBOARD_STEP_INDEX.messaging = { number: 5, title: "Messaging channels" }`. The added test checks only bounds and state-backed attachment, not parity with the legacy progress source or the intentional omission.

Workflow run details

This is an automated advisory review. A human maintainer must make the final merge decision.

@wscurran wscurran added the refactor PR restructures code without intended behavior change label May 27, 2026
@cv cv added the v0.0.55 label May 27, 2026
@cv
cv requested a review from ericksoa May 27, 2026 23:07

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/lib/onboard/machine/definition.ts (1)

45-55: ⚡ Quick win

Consider whether shared progress.number: 7 for agent_setup and openclaw is intentional (lines 48, 54)

  • src/lib/onboard/machine/definition.test.ts validates only that progress.number is within 1..total (and stepNames are unique); it does not enforce uniqueness/ordering of progress.number.
  • Since sandbox branches to both openclaw and agent_setup, using the same progress.number/title may be deliberate. If progress.number is intended to represent distinct sequential UI steps, update openclaw/agent_setup to different progress.number and/or titles.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/machine/definition.ts` around lines 45 - 55, The entries for
state "agent_setup" and "openclaw" both use progress.number: 7 and the same
title, which may be unintended; review progress semantics and if these represent
sequential UI steps, change one of the progress.number values (and optionally
its progress.title) so "agent_setup" and "openclaw" have distinct progress
numbers (e.g., 7 and 8) and update any dependent labels; ensure the machine node
definitions for state "agent_setup" and state "openclaw" in definition.ts (and
any expectations in the test file definition.test.ts) remain consistent after
the change.
src/lib/onboard/machine/definition.test.ts (1)

79-84: ⚡ Quick win

Expand test coverage for getOnboardMachineStateDefinition to include error and edge cases.

The current test only validates the happy path (known state with stepName). Per the layer description, the function throws on unknown identifiers, but this error behavior is not tested. Consider adding test cases for:

  • Unknown state ID (should throw)
  • Terminal state lookup (e.g., "complete")
  • Non-terminal state without stepName (e.g., "init")
🧪 Proposed additional test cases
   it("looks up definitions by state", () => {
     expect(getOnboardMachineStateDefinition("gateway")).toMatchObject({
       state: "gateway",
       stepName: "gateway",
     });
+
+    expect(getOnboardMachineStateDefinition("complete")).toMatchObject({
+      state: "complete",
+      terminal: true,
+    });
+
+    expect(getOnboardMachineStateDefinition("init")).toMatchObject({
+      state: "init",
+      terminal: false,
+    });
+  });
+
+  it("throws on unknown state ID", () => {
+    expect(() => getOnboardMachineStateDefinition("unknown" as any)).toThrow();
   });
 });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/onboard/machine/definition.test.ts` around lines 79 - 84, Add tests
to cover error and edge cases for getOnboardMachineStateDefinition: add a test
asserting that calling getOnboardMachineStateDefinition with an unknown id
throws (use toThrow), add a test that looking up a terminal state (e.g.,
"complete") returns the correct object shape (state: "complete") and does not
require a stepName, and add a test for a non-terminal state that intentionally
lacks stepName (e.g., "init") asserting it returns the state and either stepName
is undefined or absent. Use the existing test style (expect(...).toMatchObject /
expect(() => ...).toThrow) and reference getOnboardMachineStateDefinition in
each case.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/onboard/machine/definition.ts`:
- Around line 72-74: Replace the hardcoded ONBOARD_MACHINE_TERMINAL_STATE_IDS
with a derived constant that filters ONBOARD_MACHINE_STATE_DEFINITIONS for
entries where def.isTerminal is true and maps to their id, and then move the
OnboardTerminalMachineStateId type so it is declared after that derived constant
(i.e., let OnboardTerminalMachineStateId = (typeof
ONBOARD_MACHINE_TERMINAL_STATE_IDS)[number]) to keep types consistent and
prevent future drift between definitions and terminal IDs.

---

Nitpick comments:
In `@src/lib/onboard/machine/definition.test.ts`:
- Around line 79-84: Add tests to cover error and edge cases for
getOnboardMachineStateDefinition: add a test asserting that calling
getOnboardMachineStateDefinition with an unknown id throws (use toThrow), add a
test that looking up a terminal state (e.g., "complete") returns the correct
object shape (state: "complete") and does not require a stepName, and add a test
for a non-terminal state that intentionally lacks stepName (e.g., "init")
asserting it returns the state and either stepName is undefined or absent. Use
the existing test style (expect(...).toMatchObject / expect(() => ...).toThrow)
and reference getOnboardMachineStateDefinition in each case.

In `@src/lib/onboard/machine/definition.ts`:
- Around line 45-55: The entries for state "agent_setup" and "openclaw" both use
progress.number: 7 and the same title, which may be unintended; review progress
semantics and if these represent sequential UI steps, change one of the
progress.number values (and optionally its progress.title) so "agent_setup" and
"openclaw" have distinct progress numbers (e.g., 7 and 8) and update any
dependent labels; ensure the machine node definitions for state "agent_setup"
and state "openclaw" in definition.ts (and any expectations in the test file
definition.test.ts) remain consistent after the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b211652d-f217-46f6-aa80-2c68225a3834

📥 Commits

Reviewing files that changed from the base of the PR and between 6b753bf and fb1b32d.

📒 Files selected for processing (3)
  • src/lib/onboard/machine/definition.test.ts
  • src/lib/onboard/machine/definition.ts
  • src/lib/onboard/machine/types.ts

Comment thread src/lib/onboard/machine/definition.ts Outdated
Base automatically changed from stack/onboard-fsm-doc to main May 28, 2026 18:05
@cjagwani

Copy link
Copy Markdown
Collaborator

lgtm. content audit verified:

  • 13 state IDs match transitions.ts 1:1
  • types.ts re-exports preserve existing API (no breaking changes)
  • discriminated-union via Extract<..., { stepName: string }> correctly carves the 8 user-visible steps
  • agent_setup + openclaw both map to step 7 — intentional alternates per docs(onboard): document FSM migration target #4361 README
  • circular-dep guardrail in file-level comment
  • 5 new tests pass + all 83 tests in src/lib/onboard/machine/ pass locally

delivers migration stage 1 from #4361's README: "state metadata is defined once and derived."

@cjagwani cjagwani left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

approving per audit above — definition matches transitions 1:1, types.ts re-exports preserve consumer API, tests pass locally + CI green.

@cv
cv marked this pull request as ready for review May 29, 2026 17:21
Signed-off-by: Carlos Villela <cvillela@nvidia.com>
@cv
cv merged commit ad2fa35 into main May 29, 2026
30 checks passed
@cv
cv deleted the stack/onboard-fsm-definition branch May 29, 2026 18:44
@wscurran wscurran added area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow and removed onboarding labels Jun 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: onboarding Onboarding FSM, provider setup, sandbox launch, or first-run flow refactor PR restructures code without intended behavior change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants