harvester/harvester-ui-extension · tserong · reviewed at 462f9df (base aa6d11a) · 2026-11-10 · two passes: Cody's process (ai-skills 5f9619b) and ours
Preview: https://harvesterpr1236r.mf-created-dashboard.ui.rancher.space/ · built from 462f9df, backend Harvester v1.10.0-rc1 (.105, one node, node-manager #251 not installed). Login admin / HARVESTER_PASSWORD.
Verdict: request changes (both passes)
The kB change is right and the new field saves a number, but the whole Hugepages tab goes blank when the Hugepage object has no spec.static. Meminfo and Transparent Hugepages disappear with it, on the host page and on Edit Config. That's .105 today (v1.10.0-rc1 without the backend PR). After node-manager #251 it should still happen on clusters upgraded with Longhorn V2 off, because nothing writes spec.static there. The simple fix is to treat a missing count as 0 in both components, which also fixes the empty value you get for 0 pages.
Besides that: 3 questions (merge order and field name, requested vs allocated count, help text for the rke2 restart and the memory reserved at boot) and 2 optional points. Nothing is posted, and there's no pending review on GitHub.
Test it yourself
Open the host hp-159-tink-system → Hugepages on main and on the PR. On the PR the panel is empty, and the Console shows Cannot read properties of undefined (reading 'nrHugepages'). Add ?mode=edit to the URL for Edit Config, which breaks the same way.
To see the feature itself: DevTools → Network → block *subscribe*, then Override content on the node.harvesterhci.io.hugepages request and add "static": {"nrHugepages": 1024} to the host's spec. For the 0 case use "static": {}.
Don't press Save on .105. It writes the node and the other host resources too. The payloads below were captured with every write answered locally.
How the two passes compare
Cody's process
Ours
Verdict
Request changes (acceptance criterion AC4 not met)
Request changes (1 blocker)
Filed
5 line comments (blank tab on view, blank tab on edit, empty value at 0, input values, info link name) + before/after demos; 2 open questions kept in the report (field rename, a memory warning in the GUI)
1 blocker (blank tab, with the 0 case folded in), 1 optional nit, 3 questions
Found by both
Blank tab without spec.static (live on .105) · empty value and empty required field at 0 · empty/decimal/negative values sent as typed · merge order and the nrHugepages rename on #251 · the request for a GUI hint about memory reserved at boot
Only one pass
The new info link has no accessible name (axe link-name, serious; ours saw it and left it out as main's pattern) · before/after demos · acceptance table · the webhook message checked in the growl and the notification center
The blank tab also after #251, on upgraded clusters with Longhorn V2 off · requested vs allocated count · Save makes node-manager restart rke2 on the node · ran lint and the feature-flag unit tests
Dropped in the merge
Nothing. Every finding held when I re-checked it against the code at 462f9df and on the previews.
Cost
This session: code-only steps while ours ran, browser steps after it
Fresh agent: ~23 min, 248k tokens, 80 tool calls
Order: ours ran first as a fresh agent that never saw Cody's pass. Cody's code-only steps ran meanwhile, and its candidate list (blank tab, empty 0 value, input values) was written before ours reported. Its browser checks ran after ours had finished, so they aren't fully independent of it.
Findings, merged
1
The Hugepages tab goes blank when the Hugepage object has no spec.static
Both templates read spec.static.nrHugepages. When static is missing, rendering throws TypeError: Cannot read properties of undefined (reading 'nrHugepages') and Vue drops the whole component. On Edit Config, the PR's new static: {} default doesn't help, because fetch() replaces spec with the server's object (line 108).
When it happens
Today, on any v1.10 build without node-manager #251. The flag is version-based and ignores -rc, so it's on for .105, whose CRD has no static at all. Live, not stubbed.
After #251 too, on clusters upgraded with Longhorn V2 off. In #251 the hugepage controller only calls UpdateStatus (and the CRD has a status subresource). spec.static is only written when a host's object is created, or by the nodeconfig controller when Longhorn V2 needs more pages. Code-read of #251 at c5bb824, re-checked. This is not the upgrade case tserong answered Copilot about: the allocation is handled, but the field the UI reads isn't added.
Same cause, milder:nrHugepages is uint64,omitempty, so 0 pages comes back as static: {}. The view then shows an empty value, and Edit Config an empty required field. Live, stubbed.
main, host page (your screenshot)PR, same host (your screenshot)
main, Edit ConfigPR, Edit Config: the selects are gone
PR, 0 pages (stubbed static: {}): no valuePR, 0 pages: empty required field
Simplest fix
Treat a missing count as 0: make sure spec.static exists with 0 after fetch() takes the spec, and read it with a 0 fallback on the detail page. One change covers the rc builds, the upgraded clusters and the 0 case.
2
Merge after node-manager #251, once the field name is settled?
config/feature-flags.js:89
Cody'sOurs
Question
The flag turns the section on for every v1.10.0 build, rc builds without the backend included. On #251, votdev questioned the nr prefix of nrHugepages, and nobody has answered yet. If it's renamed, this PR has to follow.
3
The view shows the requested count, not the allocated one
#251 also reports status.static.nrHugepages, the count actually allocated, and tserong said on #251 that a partial allocation can need a reboot. With 2048 requested and 1536 allocated, the section says 2048 while Total Hugepages above says 1536. Is Meminfo enough? Live, stubbed.
PR, stubbed spec 2048 / status 1536
4
Should the field say it restarts rke2 and reserves memory at boot?
Once a new count is reached, #251's restartKubelet() restarts whichever of rke2-server/rke2-agent runs on the node. On a single-node cluster that briefly takes the API down. The count is also persisted to boot, and abonillabeeche asked on #251 for "a simple warning, either in the GUI (e.g. "this will reserve ~X GiB at boot") or called out clearly in the docs". GUI or docs is still open. Code-read.
5
Empty, decimal and negative values go to the API as typed
min="0" isn't enforced on a number input, and .number leaves an empty field as a string. Captured Save payloads (stubbed, every write answered locally):
Typed
Sent as spec.static.nrHugepages
2048
2048 (a number, so the normal path works)
(empty)
""
1.5
1.5
-3
-3
The API should refuse all three (the CRD field is an integer, and #251's webhook decodes into uint64), so nothing bad is stored. The user just gets a raw type error instead of a message on the field. Code-read for the API side.
6
The new info link has no accessible name
l10n/en-us.yaml:667
Cody's
Your call
<a href target="_blank"><i class="icon icon-info" /></a>: Chromium's AX tree names it "", and axe reports link-name (serious, WCAG 2.4.4/4.1.2), so a screen reader only says "link". The Transparent Hugepages link on main is the same, which is why ours left it out. Cody's rule keeps it, because the PR adds a new instance. I'd post it as optional.
Checked, nothing to raise
What
Result
How
kB change
Right on every backend
Meminfo has been bytes since the CR was added (node-manager master and #251). Live: 2097152 B → 2048 kB, 12121538560 B → ~11.8M kB.
New field saves a number
Yes
Stubbed 1024 → typed 2048 → PUT carries spec.static.nrHugepages: 2048 next to the unchanged transparent
Webhook refusal (Longhorn V2 minimum)
Message reaches the user
Simulated 422: growl with the webhook text plus the notification center. The "saveHugepages error" banner is main's existing path.
Other locales, CI
Fine
en-us is the only locale in the repo. Lint, unit tests and DCO green; ours re-ran lint and the feature-flag tests (7/7).
Simpler shape, reused components, design
Nothing in scope
No new component, no design; the computeds follow the file's own pattern
a11y probe on Edit Config (light, dark)
Nothing else from the PR
Other hits are on main's Transparent Hugepages selects, or shell's LabeledInput (focus drawn on the wrapper, the dark-theme required * at 2.38:1 = theme color)
Nothing reached .105
Confirmed
Every non-GET except login answered locally. The Hugepage CR's spec and spec-owning managedFields entry (2026-04-08) are identical before and after. resourceVersion moves on its own (status updates every ~minute).
Cody's process: demos and acceptance
Before: main on .105After: PR, Hugepage object stubbed as #251 returns it (1024 pages); Save answered locally
Criterion (source)
Verdict
How
AC1
The view shows the static allocation (PR body, HEP API changes)
Partly met
Stubbed 1024: shown. 0 pages: empty value.
AC2
Edit Config sets the count, Save applies it (#9459 "per-node settings", HEP user experience)
Met
Stubbed; PUT payload above
AC3
Going under Longhorn V2's need is refused and the user is told why (HEP goals)
Met
Simulated webhook 422
AC4
The Hugepages tab keeps working on every supported version (implied by the flag)
Not met
Live on .105: blank tab, view and edit
AC5
Meminfo reads in kB and matches the labels (PR body)
Met
Live
Not in this PR: sizes other than 2 MiB and a cluster-wide setting (both HEP non-goals). Passes with nothing in scope: simplify, component reuse, design fidelity. Preexisting check: all 5 comments introduced (the info link as a new instance of main's pattern).
Our pass: coverage
File
correctness
tests
a11y
i18n
conventions
errors
perf
detail HarvesterHugepages.vue
finding
finding
cleared
cleared
cleared
finding
cleared
edit HarvesterHugepages.vue
finding
finding
cleared
cleared
cleared
finding
cleared
feature-flags.js
finding (Q)
cleared
n-a
n-a
cleared
n-a
n-a
doc-links.js, en-us.yaml
cleared
n-a
n-a
cleared
cleared
n-a
n-a
The tests cells are the blocker folded in: there are no unit tests for either component on main or in the PR, and a mount test with an object that has no spec.static would catch it.
What I'd post (not posted)
Thanks Tim! The kB change and the new field work, but the Hugepages tab goes blank on a v1.10 cluster whose Hugepage object has no static yet. A few questions below too.
Optional for the body: the before/after videos above.
When the Hugepage object has no spec.static, this throws and the whole Hugepages tab renders empty, Meminfo and Transparent Hugepages included. That's what a v1.10.0-rc1 cluster without node-manager#251 returns today. After #251 it should still happen on clusters upgraded with Longhorn V2 off, since only the status gets updated there. With 0 pages the value is empty too, because nrHugepages is omitempty. Defaulting the count to 0 when it's missing would cover all of these.
Same on Edit Config: the spec from the server replaces the static: {} default here, so the template throws and the Transparent Hugepages selects disappear too. With 0 pages the field comes up empty but required. The same 0 default would fix both.
Images: main / this PR (Edit Config).
pkg/harvester/config/feature-flags.js:89
Question: this turns the section on for every v1.10.0 build, rc builds without the backend included, and the field name is still being discussed on node-manager#251. Is the plan to merge this after #251, once the name is settled?
Question: this shows the requested count. With 2048 requested and only 1536 allocated, the section says 2048 while Total Hugepages above it says 1536. Is Meminfo enough, or should this show status.static too?
Question: once a new count is allocated, node-manager restarts rke2 on that node, and the count is kept for boot, where a big number can reserve most of the memory. abonillabeeche asked on #251 for a "this will reserve ~X GiB" hint. Should the field say that, or is it left to the docs?
Optional: min="0" isn't enforced, so an empty, decimal or negative value is sent as typed ("", 1.5, -3), and the user gets the API's type error instead of a message on the field.
pkg/harvester/l10n/en-us.yaml:667
Optional: like the Transparent Hugepages one, this info link has no accessible name, so a screen reader only announces "link".