Skip to content

fix: unify onboarding internal boot state refresh#1923

Merged
elibosley merged 12 commits intomainfrom
codex/internal-boot-devs-source-of-truth
Mar 17, 2026
Merged

fix: unify onboarding internal boot state refresh#1923
elibosley merged 12 commits intomainfrom
codex/internal-boot-devs-source-of-truth

Conversation

@elibosley
Copy link
Member

@elibosley elibosley commented Mar 17, 2026

Summary

  • move onboarding internal boot reads to a backend-owned
  • refresh , , and on demand through
  • add a refresh action in onboarding so array start/stop and assignable drive state can be re-read without restarting the API

Validation

unraid-monorepo@4.29.2 lint /Users/elibosley/Code/api
pnpm -r lint

Scope: 8 of 9 workspace projects
unraid-ui lint$ eslint src
web lint$ pnpm lint:eslint && pnpm lint:prettier
web lint: > @unraid/web@4.29.2 lint:eslint /Users/elibosley/Code/api/web
web lint: > eslint --cache
web lint: > @unraid/web@4.29.2 lint:prettier /Users/elibosley/Code/api/web
web lint: > prettier --check "**/*.{js,ts,tsx,vue}"
web lint: Checking formatting...
unraid-ui lint: Done
web lint: All matched files use Prettier code style!
web lint: Done
api lint$ eslint --config .eslintrc.ts src/
api lint: Done

@unraid/api@4.29.2 type-check /Users/elibosley/Code/api/api
tsc --noEmit

@unraid/api@4.29.2 test /Users/elibosley/Code/api/api
NODE_ENV=test vitest run src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.spec.ts src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.spec.ts src/unraid-api/graph/resolvers/onboarding/onboarding.query.spec.ts

RUN v3.2.4 /Users/elibosley/Code/api/api

stdout | src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.spec.ts
[dotenv@17.2.1] injecting env (18) from .env.test -- tip: ⚙️ enable debug logging with { debug: true }

stdout | src/unraid-api/graph/resolvers/onboarding/onboarding.query.spec.ts
[dotenv@17.2.1] injecting env (18) from .env.test -- tip: ⚙️ write to custom object with { processEnv: myObject }

stdout | src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.spec.ts
[dotenv@17.2.1] injecting env (18) from .env.test -- tip: ⚙️ specify custom .env file path with { path: '/custom/path/.env' }

✓ src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.spec.ts (10 tests) 5ms
✓ src/unraid-api/graph/resolvers/onboarding/onboarding.query.spec.ts (1 test) 2ms
✓ src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.spec.ts (8 tests) 4ms

Test Files 3 passed (3)
Tests 19 passed (19)
Start at 12:25:35
Duration 1.37s (transform 278ms, setup 2.83s, collect 613ms, tests 11ms, environment 0ms, prepare 130ms)

unraid-monorepo@4.29.2 type-check /Users/elibosley/Code/api
pnpm -r type-check

Scope: 8 of 9 workspace projects
unraid-ui type-check$ vue-tsc --noEmit
web type-check$ vue-tsc --noEmit
unraid-ui type-check: Done
web type-check: Done
api type-check$ tsc --noEmit
api type-check: Done in

unraid-monorepo@4.29.2 test /Users/elibosley/Code/api
pnpm -r test test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts

Scope: 8 of 9 workspace projects
packages/unraid-api-plugin-health test$ echo "Error: no test specified" && exit 0 test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts
packages/unraid-shared test$ vitest run test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts
plugin test$ vitest && pnpm run test:extractor && pnpm run test:shell-detection test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts
unraid-ui test$ vitest test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts
packages/unraid-api-plugin-health test: Error: no test specified
packages/unraid-api-plugin-health test: sh: line 0: exit: too many arguments
packages/unraid-api-plugin-health test: Failed
/Users/elibosley/Code/api/packages/unraid-api-plugin-health:
 ERR_PNPM_RECURSIVE_RUN_FIRST_FAIL  unraid-api-plugin-health@4.25.3 test: echo "Error: no test specified" && exit 0 __test__/components/Onboarding/OnboardingInternalBootStep.test.ts __test__/components/Onboarding/OnboardingSummaryStep.test.ts
Exit status 1
web test$ vitest run test/components/Onboarding/OnboardingInternalBootStep.test.ts test/components/Onboarding/OnboardingSummaryStep.test.ts
 ELIFECYCLE  Test failed. See above for more details. in

Summary by CodeRabbit

  • New Features

    • Added manual refresh capability for internal boot context state with dedicated refresh button and loading indicators.
    • Simplified disk eligibility messaging to focus on size constraints only.
  • Improvements

    • Streamlined disk selection interface with updated data structure for assignable disks.
    • Enhanced internal boot onboarding context handling and state management.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 17, 2026

Walkthrough

Replaces per-disk emhttpDeviceId with a unified assignableDisks shape, consolidates internal-boot context into OnboardingInternalBootContext, adds refresh mutation/flow, introduces DisksService.getAssignableDisks and related resolvers/services, refactors emhttp device lookups to a Map, and updates frontend components/tests and GraphQL documents accordingly.

Changes

Cohort / File(s) Summary
GraphQL schema & documents
api/generated-schema.graphql, web/src/composables/gql/gql.ts, web/src/composables/gql/graphql.ts, web/src/components/Onboarding/graphql/getInternalBootContext.query.ts, web/src/components/Onboarding/graphql/refreshInternalBootContext.mutation.ts
Removed emhttpDeviceId from Disk; added OnboardingInternalBootContext, internalBootContext query, assignableDisks query field, and refreshInternalBootContext mutation. Updated generated documents, overloads, and client mutation/query entries to the new shape.
API onboarding resolvers & service
api/src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.ts, api/src/unraid-api/graph/resolvers/onboarding/onboarding.model.ts, api/src/unraid-api/graph/resolvers/onboarding/onboarding.query.ts, api/src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.ts, api/src/unraid-api/graph/resolvers/onboarding/onboarding.*.spec.ts
Added OnboardingInternalBootContext type, new service methods getInternalBootContext and refreshInternalBootContext, injected DisksService into the service, new query/mutation resolvers for internal boot context and refresh, and tests exercising new flows.
Disks service/resolver & model
api/src/unraid-api/graph/resolvers/disks/disks.service.ts, api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts, api/src/unraid-api/graph/resolvers/disks/disks.model.ts, api/src/unraid-api/graph/resolvers/disks/*.{spec,resolver.spec}.ts
Refactored emhttp device handling to a Map, removed emhttpDeviceId from Disk model, added getAssignableDisks() public API and assignableDisks resolver, updated parsing signatures, and added tests for assignable disk behavior.
Frontend components & UI logic
web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue, web/src/components/Onboarding/steps/OnboardingSummaryStep.vue, web/src/components/Onboarding/graphql/*, web/src/locales/en.json
Switched components to use internalBootContext.assignableDisks, removed per-disk eligibility codes (only TOO_SMALL remains), updated option/display id logic to use device/serialNum, added refresh button and mutation integration, and added i18n key for refresh action.
Frontend tests
web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts, web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts
Updated fixtures and expectations to new internalBootContext/assignableDisks shape, removed emhttpDeviceId checks, added mocks for refresh mutation and refetch behavior, and added scenarios for devs.ini-derived assignable disks.
GQL client documents map & types
web/src/composables/gql/gql.ts, web/src/composables/gql/graphql.ts
Aligned client Documents map and TypeScript GraphQL types/overloads to the revised GetInternalBootContext payload and the new RefreshInternalBootContext mutation.
State watcher & tests
api/src/store/watch/state-watch.ts, api/src/__test__/store/watch/state-watch.test.ts
Removed exclusion logic so devs.ini is always watched; added unit test verifying watcher subscribes and dispatches loadSingleStateFile for devs.ini changes.
Misc tests & wiring
api/src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.spec.ts, api/src/unraid-api/graph/resolvers/onboarding/onboarding.query.spec.ts, other spec updates
Updated/added tests to assert delegation to new service methods (getInternalBootContext, refreshInternalBootContext) and to reflect changed signatures/constructors.
Build/test scripts
web/package.json
Added pretest scripts to ensure UI build step runs before tests (pretest, pretest:watch, pretest:ci).

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Frontend
  participant GraphQL_Server
  participant OnboardingService
  participant DisksService
  participant DevsIniStore

  Client->>Frontend: user opens internal-boot UI / clicks refresh
  Frontend->>GraphQL_Server: query internalBootContext / mutation refreshInternalBootContext
  GraphQL_Server->>OnboardingService: getInternalBootContext() / refreshInternalBootContext()
  OnboardingService->>DisksService: getAssignableDisks()
  DisksService->>DevsIniStore: read devs.ini -> build device map
  DevsIniStore-->>DisksService: device map (device -> id/serial)
  DisksService-->>OnboardingService: Disk[] (id, device, size, serialNum, interfaceType)
  OnboardingService-->>GraphQL_Server: OnboardingInternalBootContext (assignableDisks + flags)
  GraphQL_Server-->>Frontend: response with internalBootContext
  Frontend-->>Client: render updated assignable disks / refresh UI state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇
I hop through schema, nibble a string,
Old IDs fall — new fields take wing.
Assignable rows sprout tidy and true,
A refresh button hums — I cheer for you! 🎉

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'fix: unify onboarding internal boot state refresh' clearly and concisely summarizes the main change of unifying and improving the onboarding internal boot state refresh mechanism across the codebase.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/internal-boot-devs-source-of-truth
📝 Coding Plan
  • Generate coding plan for human review comments

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

Tip

CodeRabbit can use TruffleHog to scan for secrets in your code with verification capabilities.

Add a TruffleHog config file (e.g. trufflehog-config.yml, trufflehog.yml) to your project to customize detectors and scanning behavior. The tool runs only when a config file is present.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6f7ca00f4a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +192 to +193
.filter((disk) => typeof disk.emhttpDeviceId === 'string' && disk.emhttpDeviceId.trim().length > 0)
.map((disk) => normalizeDeviceName(disk.device))

Choose a reason for hiding this comment

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

P1 Badge Do not infer unassigned status from emhttpDeviceId presence

emhttpDeviceId is not an unassigned-only signal in this codebase: the backend sets it for any disk that matches store.emhttp.devices (see getEmhttpDevices/findEmhttpDevice/parseDisk in api/src/unraid-api/graph/resolvers/disks/disks.service.ts, and assigned disks are covered in disks.service.spec.ts). Using that field here to build unassignedDevices causes assigned array/parity/cache/boot disks to have ASSIGNED_* codes removed, so they can appear eligible for internal boot selection and NO_UNASSIGNED_DISKS can be suppressed when no truly unassigned disks exist.

Useful? React with 👍 / 👎.

@codecov
Copy link

codecov bot commented Mar 17, 2026

Codecov Report

❌ Patch coverage is 89.78723% with 24 lines in your changes missing coverage. Please review.
✅ Project coverage is 51.66%. Comparing base (8e4d44d) to head (14ef96c).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
...api/graph/resolvers/onboarding/onboarding.model.ts 55.00% 9 Missing ⚠️
...ers/onboarding/onboarding-internal-boot.service.ts 90.24% 8 Missing ⚠️
...ts/Onboarding/steps/OnboardingInternalBootStep.vue 94.11% 3 Missing ⚠️
.../unraid-api/graph/resolvers/disks/disks.service.ts 92.59% 2 Missing ⚠️
...aid-api/graph/resolvers/mutation/mutation.model.ts 50.00% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1923      +/-   ##
==========================================
+ Coverage   51.55%   51.66%   +0.11%     
==========================================
  Files        1024     1026       +2     
  Lines       70846    70930      +84     
  Branches     7814     7841      +27     
==========================================
+ Hits        36522    36649     +127     
+ Misses      34202    34159      -43     
  Partials      122      122              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@elibosley elibosley force-pushed the codex/internal-boot-devs-source-of-truth branch from 6f7ca00 to 048ffbb Compare March 17, 2026 14:52
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts (1)

514-515: Prefer behavior assertions over internal eligibility-code token checks.

These two assertions are coupled to internal code labels and add little beyond the existing UI-state assertions in the same test.

Based on learnings: Applies to /test/components//*.ts : Test component behavior and output, not implementation details.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts` around
lines 514 - 515, Remove the two assertions that check for internal token strings
('ASSIGNED_TO_ARRAY' and 'NO_UNASSIGNED_DISKS') via wrapper.text(); instead
assert the observable UI behavior or elements that those tokens are meant to
influence (e.g., check absence/presence of specific labels, buttons, or messages
rendered by the OnboardingInternalBootStep component using wrapper.find /
wrapper.exists or wrapper.text() for user-facing strings). Update the test in
OnboardingInternalBootStep.test.ts to drop the implementation-detail checks and
replace them with behavior assertions tied to wrapper and the component's
rendered output.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts`:
- Around line 514-515: Remove the two assertions that check for internal token
strings ('ASSIGNED_TO_ARRAY' and 'NO_UNASSIGNED_DISKS') via wrapper.text();
instead assert the observable UI behavior or elements that those tokens are
meant to influence (e.g., check absence/presence of specific labels, buttons, or
messages rendered by the OnboardingInternalBootStep component using wrapper.find
/ wrapper.exists or wrapper.text() for user-facing strings). Update the test in
OnboardingInternalBootStep.test.ts to drop the implementation-detail checks and
replace them with behavior assertions tied to wrapper and the component's
rendered output.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 51b2b89f-0e99-4fd5-a10e-ce71a1cc5985

📥 Commits

Reviewing files that changed from the base of the PR and between 6f7ca00 and 048ffbb.

📒 Files selected for processing (2)
  • web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts
  • web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue (1)

160-166: ⚠️ Potential issue | 🟠 Major

Store device selection keyed on serial number, not device name.

optionValue is set to the device name (/dev/sdx) rather than the serial number. This value flows through selectedDevicesdraftStore.internalBootSelection.devices → the GraphQL mutation input. The API-side createInternalBootPool passes these device names directly to the cmdAssignDisk commands without any translation to current device paths.

Device enumeration order can change across reboots (devices may be reordered as /dev/sda, /dev/sdb, etc.). Storing unstable device names in the draft defeats the purpose of avoiding re-enumeration drift that this PR addresses. Either change the stored value to use the serial number as the stable identifier, or implement server-side remapping from serial to the current device path at pool creation time.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue` around
lines 160 - 166, The selection option is currently storing unstable device names
in optionValue (set to device) which flows into selectedDevices and
draftStore.internalBootSelection.devices; change optionValue to use the stable
serial number (serialNum) as the stored identifier (fall back to device only if
serialNum is empty), keep display label built by buildDeviceLabel unchanged so
UI still shows device and size, and ensure downstream usages (e.g.,
createInternalBootPool / cmdAssignDisk) expect serial identifiers or are
adjusted to remap serial → current device path on the server if necessary.
🧹 Nitpick comments (1)
api/src/unraid-api/graph/resolvers/disks/disks.service.spec.ts (1)

547-549: Also assert mapped id in assignable disk expectations.

This closes a small coverage gap and prevents regressions where only serialNum is remapped.

Suggested test assertion
             expect(disks.map((disk) => disk.device)).toEqual(['/dev/sda', '/dev/sdd']);
+            expect(disks.map((disk) => disk.id)).toEqual(['DEVS-SERIAL-SDA', 'DEVS-SERIAL-SDD']);
             expect(disks.map((disk) => disk.serialNum)).toEqual(['DEVS-SERIAL-SDA', 'DEVS-SERIAL-SDD']);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@api/src/unraid-api/graph/resolvers/disks/disks.service.spec.ts` around lines
547 - 549, Add an assertion to verify the mapped id on each returned disk to
close the coverage gap: after the existing expectations that check disks.map(d
=> d.device) and disks.map(d => d.serialNum), add an expectation on disks.map(d
=> d.id) to equal the same expected id values (e.g., ['DEVS-SERIAL-SDA',
'DEVS-SERIAL-SDD']) so the test ensures the id field is correctly remapped in
the disks variable used in this spec.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue`:
- Around line 152-158: The code currently treats disks with unknown size
(sizeMiB === null) as eligible; update the eligibility check around
toSizeMiB/disk.size so that if sizeMiB is null or non-positive it is considered
ineligible — i.e. change the condition to: if (sizeMiB === null || sizeMiB <
MIN_ELIGIBLE_DEVICE_SIZE_MIB) { ineligibilityCodes.push('TOO_SMALL'); } so disks
we cannot size are marked ineligible (use the existing ineligibilityCodes array
and symbols sizeMiB, toSizeMiB, MIN_ELIGIBLE_DEVICE_SIZE_MIB, and 'TOO_SMALL').

---

Outside diff comments:
In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue`:
- Around line 160-166: The selection option is currently storing unstable device
names in optionValue (set to device) which flows into selectedDevices and
draftStore.internalBootSelection.devices; change optionValue to use the stable
serial number (serialNum) as the stored identifier (fall back to device only if
serialNum is empty), keep display label built by buildDeviceLabel unchanged so
UI still shows device and size, and ensure downstream usages (e.g.,
createInternalBootPool / cmdAssignDisk) expect serial identifiers or are
adjusted to remap serial → current device path on the server if necessary.

---

Nitpick comments:
In `@api/src/unraid-api/graph/resolvers/disks/disks.service.spec.ts`:
- Around line 547-549: Add an assertion to verify the mapped id on each returned
disk to close the coverage gap: after the existing expectations that check
disks.map(d => d.device) and disks.map(d => d.serialNum), add an expectation on
disks.map(d => d.id) to equal the same expected id values (e.g.,
['DEVS-SERIAL-SDA', 'DEVS-SERIAL-SDD']) so the test ensures the id field is
correctly remapped in the disks variable used in this spec.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b73bc708-2e58-48f7-acb0-7c15f5c01a02

📥 Commits

Reviewing files that changed from the base of the PR and between 048ffbb and c633fb7.

⛔ Files ignored due to path filters (1)
  • api/src/unraid-api/cli/generated/graphql.ts is excluded by !**/generated/**
📒 Files selected for processing (13)
  • api/generated-schema.graphql
  • api/src/unraid-api/graph/resolvers/disks/disks.model.ts
  • api/src/unraid-api/graph/resolvers/disks/disks.resolver.spec.ts
  • api/src/unraid-api/graph/resolvers/disks/disks.resolver.ts
  • api/src/unraid-api/graph/resolvers/disks/disks.service.spec.ts
  • api/src/unraid-api/graph/resolvers/disks/disks.service.ts
  • web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts
  • web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts
  • web/src/components/Onboarding/graphql/getInternalBootContext.query.ts
  • web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue
  • web/src/components/Onboarding/steps/OnboardingSummaryStep.vue
  • web/src/composables/gql/gql.ts
  • web/src/composables/gql/graphql.ts
💤 Files with no reviewable changes (1)
  • api/src/unraid-api/graph/resolvers/disks/disks.model.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • web/test/components/Onboarding/OnboardingInternalBootStep.test.ts

Comment on lines 152 to 158
const sizeBytes = disk.size;
const sizeMiB = toSizeMiB(sizeBytes);
const ineligibilityCodes = Array.from(diskEligibilityCodesByDevice.value.get(device) ?? []);
const ineligibilityCodes: InternalBootDiskEligibilityCode[] = [];
if (sizeMiB !== null && sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
ineligibilityCodes.push('TOO_SMALL');
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Treat unknown-size disks as ineligible.

If disk.size is missing or non-positive, toSizeMiB() returns null but this branch still leaves the disk eligible. That later bypasses the max-size guard as well, so the wizard can submit against a drive we could not size.

💡 Minimal safeguard
-      if (sizeMiB !== null && sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
+      if (sizeMiB === null || sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
         ineligibilityCodes.push('TOO_SMALL');
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const sizeBytes = disk.size;
const sizeMiB = toSizeMiB(sizeBytes);
const ineligibilityCodes = Array.from(diskEligibilityCodesByDevice.value.get(device) ?? []);
const ineligibilityCodes: InternalBootDiskEligibilityCode[] = [];
if (sizeMiB !== null && sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
ineligibilityCodes.push('TOO_SMALL');
}
const sizeBytes = disk.size;
const sizeMiB = toSizeMiB(sizeBytes);
const ineligibilityCodes: InternalBootDiskEligibilityCode[] = [];
if (sizeMiB === null || sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
ineligibilityCodes.push('TOO_SMALL');
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue` around
lines 152 - 158, The code currently treats disks with unknown size (sizeMiB ===
null) as eligible; update the eligibility check around toSizeMiB/disk.size so
that if sizeMiB is null or non-positive it is considered ineligible — i.e.
change the condition to: if (sizeMiB === null || sizeMiB <
MIN_ELIGIBLE_DEVICE_SIZE_MIB) { ineligibilityCodes.push('TOO_SMALL'); } so disks
we cannot size are marked ineligible (use the existing ineligibilityCodes array
and symbols sizeMiB, toSizeMiB, MIN_ELIGIBLE_DEVICE_SIZE_MIB, and 'TOO_SMALL').

@elibosley elibosley changed the title fix: prefer devs.ini for onboarding boot eligibility fix: unify onboarding internal boot state refresh Mar 17, 2026
elibosley and others added 5 commits March 17, 2026 12:45
- Purpose: sync generated GraphQL artifacts with current schema and operations.

- Before: generated API and web files were out of date in this branch.

- Why this was a problem: stale generated types can cause type drift and inconsistent build behavior.

- What changed: refreshed API schema output, CLI generated GraphQL files, and web composable GraphQL outputs.

- How it works: reran \
> unraid-monorepo@4.29.2 codegen /Users/ajitmehrotra/Projects/wt-pr-1923
> pnpm -r codegen

Scope: 8 of 9 workspace projects
web codegen$ graphql-codegen --config codegen.ts -r dotenv/config
web codegen: ❯ Parse Configuration
web codegen: ✔ Parse Configuration
web codegen: ❯ Generate outputs
web codegen: ❯ Generate to src/composables/gql/
web codegen: ❯ Load GraphQL schemas
web codegen: ✔ Load GraphQL schemas
web codegen: ❯ Load GraphQL documents
web codegen: ✔ Load GraphQL documents
web codegen: ❯ Generate
web codegen: ✔ Generate
web codegen: ✔ Generate to src/composables/gql/
web codegen: ✔ Generate outputs
web codegen: Done
packages/unraid-api-plugin-connect codegen$ MOTHERSHIP_GRAPHQL_LINK='https://staging.mothership.unraid.net/ws' graphql-codegen --config codegen.ts
packages/unraid-api-plugin-connect codegen: [STARTED] Parse Configuration
packages/unraid-api-plugin-connect codegen: [COMPLETED] Parse Configuration
packages/unraid-api-plugin-connect codegen: [STARTED] Generate outputs
packages/unraid-api-plugin-connect codegen: [STARTED] Generate to src/graphql/generated/client/
packages/unraid-api-plugin-connect codegen: [STARTED] Load GraphQL schemas
packages/unraid-api-plugin-connect codegen: [COMPLETED] Load GraphQL schemas
packages/unraid-api-plugin-connect codegen: [STARTED] Load GraphQL documents
packages/unraid-api-plugin-connect codegen: [COMPLETED] Load GraphQL documents
packages/unraid-api-plugin-connect codegen: [STARTED] Generate
packages/unraid-api-plugin-connect codegen: [COMPLETED] Generate
packages/unraid-api-plugin-connect codegen: [COMPLETED] Generate to src/graphql/generated/client/
packages/unraid-api-plugin-connect codegen: [COMPLETED] Generate outputs
packages/unraid-api-plugin-connect codegen: Done
api codegen$ graphql-codegen --config codegen.ts
api codegen: [STARTED] Parse Configuration
api codegen: [COMPLETED] Parse Configuration
api codegen: [STARTED] Generate outputs
api codegen: [STARTED] Generate to src/unraid-api/cli/generated/
api codegen: [STARTED] Load GraphQL schemas
api codegen: [COMPLETED] Load GraphQL schemas
api codegen: [STARTED] Load GraphQL documents
api codegen: [COMPLETED] Load GraphQL documents
api codegen: [STARTED] Generate
api codegen: [COMPLETED] Generate
api codegen: [COMPLETED] Generate to src/unraid-api/cli/generated/
api codegen: [COMPLETED] Generate outputs
api codegen: Done at the monorepo root and committed only generated artifacts.
- Purpose: make web type-check pass by matching the test fixture to generated GraphQL operation types.

- Before: the fixture included \, which is not present in the query result type used by the test.

- Why that was a problem: TypeScript rejected the object literal and \
> unraid-monorepo@4.29.2 type-check /Users/ajitmehrotra/Projects/wt-pr-1923
> pnpm -r type-check

Scope: 8 of 9 workspace projects
web type-check$ vue-tsc --noEmit
unraid-ui type-check$ vue-tsc --noEmit
unraid-ui type-check: Done
web type-check: Done
api type-check$ tsc --noEmit
api type-check: Done failed.

- What changed: removed the extra \ field from the mocked \ payload.

- How it works: the fixture now conforms exactly to the generated \ shape.
- Purpose: make web tests self-sufficient in fresh worktrees.

- Before: test commands assumed unraid-ui dist artifacts already existed.

- Why this was a problem: imports from @unraid/ui and @unraid/ui/styles failed when dist was missing.

- What changed: added pretest, pretest:watch, and pretest:ci hooks to run build-ui-if-needed.js.

- How it works: test lifecycles now trigger a conditional @unraid/ui build only when needed.
@github-actions
Copy link
Contributor

This plugin has been deployed to Cloudflare R2 and is available for testing.
Download it at this URL:

https://preview.dl.unraid.net/unraid-api/tag/PR1923/dynamix.unraid.net.plg

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue (1)

167-168: ⚠️ Potential issue | 🟠 Major

Unknown-size disks are still treated as eligible.

At Line 167, sizeMiB === null is not marked ineligible, so disks with unknown/non-usable size can pass eligibility and be selected.

💡 Minimal fix
-      if (sizeMiB !== null && sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
+      if (sizeMiB === null || sizeMiB < MIN_ELIGIBLE_DEVICE_SIZE_MIB) {
         ineligibilityCodes.push('TOO_SMALL');
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue` around
lines 167 - 168, The eligibility check currently allows disks with sizeMiB ===
null to be considered eligible; update the logic in the function that builds
ineligibilityCodes (the block that references sizeMiB and
MIN_ELIGIBLE_DEVICE_SIZE_MIB) to treat unknown sizes as ineligible by pushing an
appropriate code (e.g., 'UNKNOWN_SIZE' or include null in the existing
'TOO_SMALL' check) when sizeMiB is null; modify the condition that now reads
against sizeMiB to instead check (sizeMiB === null || sizeMiB <
MIN_ELIGIBLE_DEVICE_SIZE_MIB) or add a separate null branch that pushes to
ineligibilityCodes so unknown-size disks are excluded from selection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue`:
- Around line 167-168: The eligibility check currently allows disks with sizeMiB
=== null to be considered eligible; update the logic in the function that builds
ineligibilityCodes (the block that references sizeMiB and
MIN_ELIGIBLE_DEVICE_SIZE_MIB) to treat unknown sizes as ineligible by pushing an
appropriate code (e.g., 'UNKNOWN_SIZE' or include null in the existing
'TOO_SMALL' check) when sizeMiB is null; modify the condition that now reads
against sizeMiB to instead check (sizeMiB === null || sizeMiB <
MIN_ELIGIBLE_DEVICE_SIZE_MIB) or add a separate null branch that pushes to
ineligibilityCodes so unknown-size disks are excluded from selection.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b6d286c8-b04a-4ccf-9a4b-87b7ef9dd612

📥 Commits

Reviewing files that changed from the base of the PR and between c633fb7 and 14ef96c.

⛔ Files ignored due to path filters (1)
  • api/src/unraid-api/cli/generated/graphql.ts is excluded by !**/generated/**
📒 Files selected for processing (22)
  • api/generated-schema.graphql
  • api/src/__test__/store/watch/state-watch.test.ts
  • api/src/store/watch/state-watch.ts
  • api/src/unraid-api/graph/resolvers/mutation/mutation.model.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.spec.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding-internal-boot.service.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding.model.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.spec.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding.mutation.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding.query.spec.ts
  • api/src/unraid-api/graph/resolvers/onboarding/onboarding.query.ts
  • api/src/unraid-api/graph/resolvers/resolvers.module.ts
  • web/__test__/components/Onboarding/OnboardingInternalBootStep.test.ts
  • web/__test__/components/Onboarding/OnboardingSummaryStep.test.ts
  • web/package.json
  • web/src/components/Onboarding/graphql/getInternalBootContext.query.ts
  • web/src/components/Onboarding/graphql/refreshInternalBootContext.mutation.ts
  • web/src/components/Onboarding/steps/OnboardingInternalBootStep.vue
  • web/src/components/Onboarding/steps/OnboardingSummaryStep.vue
  • web/src/composables/gql/gql.ts
  • web/src/composables/gql/graphql.ts
  • web/src/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (3)
  • api/generated-schema.graphql
  • web/test/components/Onboarding/OnboardingSummaryStep.test.ts
  • web/src/components/Onboarding/graphql/getInternalBootContext.query.ts

@elibosley elibosley merged commit d3032c1 into main Mar 17, 2026
13 checks passed
@elibosley elibosley deleted the codex/internal-boot-devs-source-of-truth branch March 17, 2026 18:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants