Commit Graph
681 Commits
Author SHA1 Message Date
Mikhail ChusavitinandClaude Sonnet 5 f31971f440 fix(webui): make metrics history immune to system-clock changes
Every sample was stamped with time.Now(), so a timezone switch or NTP
step punched a multi-hour gap into the series: old points collapsed to
the left edge, new points bunched at the right, joined by one diagonal.

Stamp rows from a monotonic seqClock instead — seeded once from the wall
clock (or the newest persisted row) and thereafter advanced only by the
monotonic elapsed time between writes. Rebase Downsample/Prune on the
newest sample rather than time.Now() so a clock step just before the
hourly compaction can't drop fresh data.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GofKhuF9xQHaz3UFfncR6D
2026-09-04 10:04:13 +03:00
Mikhail Chusavitin 4953bb33b2 docs: set 16 GB minimum supported RAM 2026-09-04 09:14:38 +03:00
Mikhail Chusavitin 677f4f4006 fix(webui): keep Check-only tasks out of Load v14.03 2026-09-03 15:41:18 +03:00
Mikhail Chusavitin a9a9b18a8c fix(webui): format server and browser time consistently 2026-09-03 15:41:11 +03:00
Mikhail Chusavitin b14417b729 fix(runtime): avoid Copy to RAM worker panic 2026-09-03 15:34:30 +03:00
Mikhail ChusavitinandClaude Sonnet 5 eae5570730 fix(iso): stop fast-path repack from clobbering merged-usr /lib
The NVIDIA GSP firmware was staged into the overlay at a bare
lib/firmware/nvidia/<ver>/. On a merged-usr Debian root /lib is a symlink
to usr/lib, so the fast-path repack's `rsync -a` (overlay stage -> the
unsquashfs'd tree) replaced that symlink with a plain directory holding
only the firmware. Everything else reachable only through /lib
(/lib/systemd/systemd, /lib/x86_64-linux-gnu/ld-linux + libc, ...) was
orphaned. The squashfs still packed and mounted, but boot panicked:

    run-init: can't execute '/sbin/init': No such file or directory
    run-init: can't execute '/bin/sh': No such file or directory
    Kernel panic - not syncing: Attempted to kill init

Full `lb build` was unaffected (live-build copies includes.chroot with
cp -a, which follows the /lib symlink), so only fast-path ISOs were bad.

Three fixes:
- build.sh: stage GSP firmware under usr/lib/firmware/nvidia/<ver>/, the
  canonical merged-usr path. Changing the overlay path also makes
  overlay_paths_were_removed() force one full build on the next run.
- fast-path.sh: add `rsync --keep-dirlinks` so a real directory in the
  overlay stage can never again replace a symlink-to-directory in the
  root. Verified: firmware lands in usr/lib/firmware, /lib stays a symlink.
- iso-validation.sh: new validate_iso_rootfs_layout, run for every variant
  on both build paths. Fails the build if the squashfs has a plain-dir
  /bin|/sbin|/lib|/lib64, or if /usr/sbin/init is present without a
  resolvable /usr/lib/systemd/systemd and /lib symlink. Verified it flags
  the broken v14.02-1-g642e686 ISO and passes a correct layout.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2LLuid8PFhBqBPcxXxkQU
2026-09-03 15:32:43 +03:00
Mikhail ChusavitinandClaude Sonnet 5 489dd2830c fix(iso): retry toram RAM copy with exponential backoff
On hosts where the boot medium is BMC/IPMI virtual media, the ~2.8 GB
squashfs read often drops mid-transfer: rsync dies with "Input/output
error (5)", every file "has vanished", and live-boot moves the partial
RAM copy over the medium, ending the boot with "No supported filesystem
images found at /live".

Add hook 9013-toram-retry, which patches live-boot's 9990-toram-todisk.sh
so the whole-medium copy runs in a retry loop: up to 8 attempts with a
geometrically growing pause (15s -> 900s cap), unmounting/waiting/remounting
the medium between tries and honouring rsync's exit code (upstream ignores
it). rsync resumes from where it stopped, so a retry needing only the tail
finishes fast.

Verified against live-boot 1:20230131+deb12u1: substitution applies,
sh -n is clean, hook is idempotent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018JA6Cj84yxUfk8N3ppw6mr
2026-09-03 15:32:35 +03:00
Mikhail ChusavitinandClaude Sonnet 5 d0ed1bda91 fix(iso): hash build scripts into the fast-path safety check
needs_full_build() decided the fast path was safe by hashing only config
inputs (VERSIONS, package lists, hooks, archives, auto/config, Dockerfile).
It never hashed the build logic itself, so a change to build.sh or
lib/fast-path.sh counted as a "light" file and the next build silently
reused a squashfs produced by the old code. The merged-usr /lib fix in the
previous commit only forced a full rebuild by accident (the overlay
manifest saw firmware move from lib/ to usr/lib/).

hash_heavy_config now covers every *.sh under iso/builder (build.sh, the
build-*.sh helpers, lib/*.sh) plus auto/. A change to build logic is now
as heavy as a package-list change. config/bootloaders stays excluded (the
fast path regenerates the outer ISO layer from it every time). Hash is
deterministic (LC_ALL=C sort).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H2LLuid8PFhBqBPcxXxkQU
2026-09-03 15:32:33 +03:00
Mikhail ChusavitinandClaude Sonnet 5 76db45ee95 feat(audit): capture IPMI FRU/Elabel, mc info and LAN on every audit
The techdump — the only place the IPMI FRU / Elabel identity (Product Name
= server model, serials, part numbers) is recorded — only ran under LiveCD.
Run it on every audit so `bee audit` on an installed host captures it too.

Also add `ipmitool mc info` and `ipmitool lan print` to the techdump, and
pin `ipmitool fru print 0` to FRU device 0 to match the Export Tools
"FRU / Elabel" editor exactly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 15:18:01 +03:00
Mikhail ChusavitinandClaude Sonnet 5 319f08ac4b feat(topo): per-socket bar layout with disks under their controller
Rework the /topo diagram from side-by-side stacked cards into one tall
vertical bar per CPU socket with everything attached to it branching off
sideways (socket 0 left/branches right, socket 1 right/branches left).

Disks are now parented under the storage controller they physically hang
off (SATA/AHCI, SAS HBA, RAID) — itself a NUMA-affine PCIe device under one
socket — instead of a synthetic catch-all node. The disk->controller link
is read from a new storage-controllers.txt techdump
(platform.StorageControllerMapScript, a /sys/block walk); disks with no
resolvable controller fall back to an "Other" bar. No board/root node.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SYjrG6bVmeG1Z2Wmc8kg3o
2026-09-03 12:15:49 +03:00
Mikhail Chusavitin b1ab866f58 fix(runtime): make Copy to RAM switch live backing 2026-09-03 10:47:15 +03:00
Mikhail Chusavitin 347bc8310a fix(pcie): verify GPU links under real bandwidth load 2026-09-03 10:23:39 +03:00
Mikhail Chusavitin 642e68631d fix(iso): render isolinux flavour before validation v14.02 2026-09-01 15:22:31 +03:00
Mikhail Chusavitin 0a6ca8ba0f refactor: harden diagnostics and consolidate runtime code 2026-09-01 13:01:28 +03:00
Mikhail Chusavitin ac4bc0b2b7 refactor: modularize audit and harden build validation 2026-08-31 21:22:16 +03:00
Mikhail ChusavitinandClaude Sonnet 5 bb22ccfafe docs(bundle): document pcie-link and tpm SAT targets in bundle README
The bundle README is the bootstrap for an agent that knows nothing about
the layout; it listed neither the pcie-link (whole-bus link-retrain) nor
the tpm (read-only) SAT target, so a reader had no pointer to
tasks/bee-sat/pcie-link-* / tpm-* or their summary keys. Add a Fastest-
answers entry for each, refresh the component-status key examples, and add
a Signatures line for the bridge-side link-retrain false positive (bridge
DEGRADED at before==after==max while its downstream endpoint reads OK =
link at the device's native max, not a fault).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mx6AvuXcJNT7jx3jjWw4hc
v13.01
2026-08-28 11:34:45 +03:00
Mikhail ChusavitinandClaude Sonnet 5 366c1d5f29 fix(pcie): compare retrain verdict against peer-capped max speed
The downstream-capability adjustment lowered f.MaxSpeed/f.MaxWidth to the
rate the endpoint supports, but the Degraded verdict still compared the
retrained speed against the stale local maxSpeed holding the bridge's own
uncapped capability. A Gen5 root port feeding a Gen4 HBA (or a Gen2 NIC)
was flagged DEGRADED even though the link ran at the fastest rate the pair
supports, failing the PCIe link check on healthy hardware.

Keep the local maxSpeed in sync with the peer-capped f.MaxSpeed so both
the pre-retrain and post-retrain verdicts use the real target.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Mx6AvuXcJNT7jx3jjWw4hc
2026-08-28 11:30:57 +03:00
Mikhail Chusavitin c5c7a2dea3 fix(iso): serialize coldplug and harden DMA isolation v13.0 2026-08-28 09:51:51 +03:00
Mikhail Chusavitin 2354599889 fix(pcie): account for downstream link capability v12.91 2026-08-28 09:12:02 +03:00
Mikhail Chusavitin af95216c6f feat(tpm): add read-only TPM validation v12.90 2026-08-26 17:13:50 +03:00
Mikhail ChusavitinandClaude Sonnet 5 9e466b8a70 fix(pcie): stop misreporting empty/vanished slots as speed degradation
pcie-link's retrain check compared post-retrain speed against a port's
max capability without checking whether anything was actually seated
downstream. A switch downstream port with no card plugged in retrains
to zero lanes but still reports a reset-state speed, so it was
misreported as "retrained to Gen1, capable of Gen5" — indistinguishable
from a real degraded link.

Now a port that was already empty before the retrain is skipped (same
treatment as a disabled device: legitimately unpopulated slots are
common and shouldn't fail the check), while a device that answered
before the retrain and disappeared right after it still fails, with an
honest "no device detected downstream" message instead of a fabricated
speed reading.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.89
2026-08-26 12:44:57 +03:00
mchus ccc781856e fix: complete hardware collection diagnostics v12.88 2026-08-25 17:04:33 +03:00
mchusandClaude Sonnet 5 33ace33de5 chore: bump bible and internal/chart submodule pointers
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.87
2026-08-25 16:25:53 +03:00
mchusandClaude Sonnet 5 d2ac42dd7f build(iso): switch default Debian mirror, add --mirror override
mirror.mephi.ru was timing out during lb binary_rootfs. Default to
deb.debian.org (Fastly-backed, geo-balanced) and allow overriding via
--mirror on build-in-container.sh/build.sh, or BEE_MIRROR in auto/config.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-25 16:22:58 +03:00
Mikhail ChusavitinandClaude Sonnet 5 ba250330d5 fix(app): capture PCIe link / AER diagnostics at boot, not just on demand
pcie-nvidia-link.txt, pcie-nvidia-link-under-load.txt, and
kernel-aer-nvidia.txt only ever lived in supportBundleCommands, which
exclusively runs inside the on-demand "Download Support Bundle" web UI
action. The blackbox USB auto-sync worker never calls that function -
it only mirrors whatever CaptureTechnicalDump already wrote into the
live export tree at boot. So these three files were structurally
unreachable from a blackbox pull no matter how fresh the build was;
earlier analysis of a real blackbox misattributed their absence to
build/version drift instead.

Move the underlying scripts into shared exported constants
(platform.PCIeNvidiaLinkScript, PCIeNvidiaLinkUnderLoadScript,
KernelAERNvidiaScript) and add them to techDumpNvidiaCommands, so
CaptureTechnicalDump captures all three once at boot (bee-audit is
oneshot, so the ~8s bee-gpu-burn cost for the under-load sample is a
one-time boot cost, not a per-sync-cycle one) alongside the existing
nvidia-smi-* dumps. supportBundleCommands still re-runs the same
scripts on demand for a fresher sample - that's intentional, not a
duplicate to clean up.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.86
2026-08-24 18:39:28 +03:00
Mikhail ChusavitinandClaude Sonnet 5 b11018ac5e fix(collector): stop pinning GPU PCIe status on an unverified idle reading
NVIDIA GPUs deliberately downclock PCIe to Gen1 at idle for power
saving, and applyPCIeLinkSpeedWarning fired on every idle collector
pass regardless - since component-status DB records never downgrade
(Record() only ever raises severity), one boot-time idle sample
permanently pinned pcie:gpu:nvidia to Warning for the rest of the
session even after every load-bearing GPU SAT test passed clean. Two
prior fixes (nvidia-smi-sourced link speed, pcie_aspm=off boot flag)
didn't hold up against this hardware/driver combination - see
bible-local/decisions/2026-08-24-pcie-gpu-gen1-idle-warning.md for the
full history.

Rather than add a downgrade path, stop writing an unverified status in
the first place: parseLspciDevice no longer calls
applyPCIeLinkSpeedWarning on the idle path. LinkSpeed/MaxLinkSpeed stay
populated as plain descriptive fields; only a verified-under-load
caller may now turn them into a status verdict.

Two new SAT targets provide that verified signal:

- pcie-link (platform/pcie_link_check.go): forces every enabled PCIe
  device - not just GPUs - to retrain via the PCIe spec's Link Control
  "Retrain Link" bit, then compares the negotiated speed against the
  device's max. Covers NICs/HBAs/switches that have no bee-gpu-burn
  equivalent load tool. Classifies by PCI class code + vendor ID, not
  name substrings. Routes gpu_nvidia/gpu_amd/other sub-verdicts into
  their own component-status keys so a degraded NIC never reads as a
  GPU fault.
- nvidia-pcie-bandwidth (platform/nvidia_pcie_bandwidth.go): drives
  real host<->device traffic via dcgmi diag -r nvbandwidth and
  resamples link speed immediately after, independent of nvbandwidth's
  own pass/fail.

Both wired into the task queue/webui the same way as nvidia-config
(routes, dispatch, priority, Validate page cards, Run All Check SAT).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 18:39:19 +03:00
Mikhail ChusavitinandClaude Sonnet 5 7aa276320b fix(webui): trust IPMI FRU readback over ipmitool's exit code
`ipmitool fru edit` frequently exits non-zero after a field resize
(shrinking/growing a string shifts every later offset in the FRU
record) even though the write itself landed correctly on the BMC -
the failure is in ipmitool's own post-write re-parse, not the write.
bee was treating that exit code as authoritative and marking the task
failed even when the field was actually written.

runIPMIFRUWriteTask now re-reads the FRU on a non-zero exit and
compares the target field's actual value before deciding the task
failed, logging which path it took either way.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 18:39:02 +03:00
Mikhail ChusavitinandClaude Sonnet 5 198567dffe fix: surface CPU thermal throttling in status and SAT results
A CPU that had thermally throttled (sysfs thermal_throttle counter >
0) still reported status "OK" everywhere: dmidecode-derived CPU status
only distinguishes populated/enabled/disabled and never looked at the
throttle flag the collector already recorded next to it, and neither
SAT path meant to catch this actually could:

- The routine "cpu" SAT pack (RunCPUAcceptancePack) only checked
  lscpu/sensors/stress-ng exit codes — stress-ng exits 0 whether or
  not the CPU throttled while running it, so an 89°C/throttled CPU
  right after a "successful" run still showed cpu:all as OK in
  component-status.json.
- The more thorough platform-stress test already detected throttling
  and fan-spindown correctly, but wrote its verdict as "Overall: FAIL
  — ..." with no "=", which parseSATKV can't parse — so even a real
  detected throttle event never reached the component-status DB.

Fixes:
- cpu_telemetry.go: escalate a CPU's status to Warning (only-escalate,
  same severity ranking already used elsewhere) when Throttled is set.
- sat.go: add a before/after thermal-throttle-counter check job around
  the "cpu" pack's stress-ng run, so a throttle event during the run
  fails that job and (via the existing FAILED->Warning DB mapping)
  flips cpu:all to Warning.
- platform_stress.go: emit a machine-readable overall_status= line
  alongside the human-readable verdict so platform-stress results
  actually reach ApplySATResultToDB.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.85
2026-08-24 17:41:40 +03:00
Mikhail ChusavitinandClaude Sonnet 5 56d12b1f3c fix(collector): use word-boundary matching for dmesg severity
dmesgSeverity classified a kernel log line as Critical via plain
strings.Contains, without word boundaries, so common words that merely
contain a keyword as a substring false-positive — "disabled by
default" was flagged Critical because "default" contains "fault"
(de-fault). Found in a support bundle where 140/238 event_logs entries
came back Critical, most of them harmless boot messages (module load
notices, "... is initialized", "disabled by default"), drowning out
genuinely critical entries (Xid, AER, ECC) in the same list.

Switch to the same \b-bounded regexes already used for the capture
patterns above it in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 17:41:28 +03:00
Mikhail ChusavitinandClaude Sonnet 5 a09201c7ab fix(collector): recover storage model for NVMe drives exposed as SCSI
An NVMe SSD passed through a RAID/HBA as /dev/sdX (not /dev/nvmeX) gets
smartctl'd over the SCSI protocol, which reports its model in
scsi_model_name/scsi_vendor/scsi_product instead of the ATA/NVMe
model_name field. smartctlInfo only mapped model_name, so the drive's
model was silently dropped from reanimator.json even though smartctl
ran successfully and the serial number came through fine — caught via
a support bundle where lsblk showed "SSSTC CA6-8D1024" for a drive but
reanimator.json's storage entry had no model at all.

Add scsi_model_name/scsi_vendor/scsi_product to smartctlInfo and fall
back to them, in that order, when model_name is empty.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 17:41:23 +03:00
Mikhail ChusavitinandClaude Sonnet 5 9add5616dd fix(app): bound support-bundle subprocess calls with timeouts
A wedged GPU (fallen off the bus, needs a physical power-cycle) makes
nvidia-smi/nvidia-bug-report.sh/dcgmi hang indefinitely, so building a
support bundle on such a host never finished and the "Download Support
Bundle" button spun on "Building..." forever with no way to recover
short of restarting the service.

Give every subprocess this file shells out to a bounded context.
nvidia-bug-report.sh gets a longer timeout since it legitimately takes
tens of seconds on multi-GPU boxes even when healthy.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 17:41:18 +03:00
Mikhail ChusavitinandClaude Sonnet 5 134bed3eae refactor: dedupe status-severity ranking and small webui helpers
statusSeverity was byte-for-byte duplicated in collector/contract.go and
app/sat_overlay.go (same switch, same comment). Export it as
collector.StatusSeverity and drop the app-package copy.

page_topo.go inlined the same GPU/NIC/RAID class switch as its own
pcieDeviceKind function in one spot — call the function instead.

page_validate.go had its own copy of pages.go's firstNonEmpty (all call
sites already pre-trim their inputs via validateTrimPtr, so the extra
TrimSpace was redundant) — drop the duplicate.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-18 14:44:52 +03:00
Mikhail ChusavitinandClaude Sonnet 5 e3697c0a11 fix(pcie): stop misclassifying same-vendor non-GPU devices as GPUs
matchesGPUVendor (sat_overlay.go) matched any PCIe DeviceClass containing
the substring "Controller" instead of the actual GPU classes, so a
same-vendor non-GPU device (e.g. an NVIDIA-branded NIC/storage controller)
with a degraded PCIe link surfaced as a pcie:gpu:<vendor> alarm on the
Hardware Summary/webui.

isGPUDevice (app_format.go) had the same bug via a different path: an
"any NVIDIA-vendor device is a GPU" fallback that could inflate the
"GPU: N x <model>" audit summary line with non-GPU companion devices
(NVSwitch/NVLink bridges, etc).

Both now defer to collector.IsGPUClass (exported from the previously
unexported isGPUClass), the canonical exact-match classifier already used
by amdgpu.go — removing the duplicated/incorrect class checks instead of
reimplementing them. Also dedup two more locally-duplicated classifiers
within webui (which doesn't import collector by convention): NIC-class
matching (pages.go/page_topo.go) and GPU-class matching in
page_validate.go now share one copy per package instead of being
reimplemented per file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 12:50:50 +03:00
Mikhail ChusavitinandClaude Sonnet 5 4ac9863353 fix(webui): keep ramp-up burns as a single multi-GPU task
buildNvidiaTaskSet only treated ParallelGPUs as a reason to keep all
selected GPUs in one task, so ramp-up (StaggerGPUStart) fell through
to per-model task splitting. When that split left a single GPU in a
task, resolveNvidiaRampPlan silently returned StaggerSeconds=0,
making the ramp-up toggle a no-op.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 10:59:49 +03:00
Mikhail ChusavitinandClaude Sonnet 5 b1f165edb3 fix(collector): surface PCIe link-speed degradation in component-status DB, sample it under load
pcie:gpu:<vendor> in the component-status DB (what the Hardware
Summary/webui "check passed" status reads) was only ever written by SAT
diag results, and none of the nvidia/nvidia-config/nvidia-interconnect/
nvidia-bandwidth SAT jobs check PCIe link speed. So a real Gen1/Gen4
degradation the collector already flagged as Warning in the hardware
snapshot never reached the DB-backed status, and the audit kept
reporting "OK" despite GPUs training at Gen1 with BMC showing x16.

Add writePCIeGPUStatusesToDB, mirroring the existing PSU write-through,
so RunAudit pushes the collector's PCIe status into the DB alongside
SAT results.

Also add export/gpu/pcie-nvidia-link-under-load.txt to the support
bundle: NVIDIA drivers deliberately downclock PCIe at idle to save
power and re-train to full speed under load, so an idle Gen1 reading
alone can't distinguish real hardware/riser degradation from normal
power management. Resample the same sysfs link attributes while
bee-gpu-burn is actively loading the GPUs so both bundles ship
together.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.84
2026-08-06 13:51:14 +03:00
Mikhail ChusavitinandClaude Sonnet 5 3ec7ca08da feat(webui): sync host clock and timezone from browser
Adds POST /api/system/time-sync so the dashboard's "Sync with this
browser" button can set the host's date, time, and IANA timezone from
the client's browser — useful for appliances with no NTP/network
access to keep their own clock straight. Disables NTP before writing
the manual time so it doesn't get immediately overwritten.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 13:43:31 +03:00
Mikhail ChusavitinandClaude Sonnet 5 b2b3f86c8d fix(nvidia): surface Xid 79/154 GPU bus-fall-off as a physical-reboot-required signal
nvidia-bug-report.sh appends .gz to --output-file when gzip is available,
which silently produced empty nvidia-bug-report.txt in support bundles
(cat looked for the uncompressed name that never existed).

Also: a GPU that falls off the PCIe/NVLink bus (Xid 79) or gets flagged
for Node Reboot Required (Xid 154) mid-SAT-run left every downstream test
failing with generic, unrelated-looking errors (CUDA "unknown error",
"unable to determine device handle") with no indication the GPU needed a
physical power-cycle to recover. Detect these codes from SAT run logs and
surface a plain-English "physical reboot required" message in the task's
failure detail, the persisted component-status DB, a dashboard banner on
the Hardware Summary card, and topology diagram GPU-node severity.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-06 12:28:34 +03:00
Mikhail ChusavitinandClaude Sonnet 5 a34e823f82 fix(collector): stop NVIDIA enrichment from clobbering PCIe status
enrichPCIeWithNVIDIAData unconditionally overwrote dev.Status after
collectPCIe() had already flagged a Warning/Critical (e.g. PCIe link
speed degraded), so a clean ECC/remap/reset readout silently downgraded
that finding back to OK while leaving the stale ErrorDescription behind.

Add a severity-ordered merge (OK/Unknown < Warning < Critical) shared
via mergeDeviceStatus in contract.go, and route both the NVIDIA status
calculation and the driver-unavailable fallback through it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.83
2026-08-04 17:28:47 +03:00
Mikhail ChusavitinandClaude Sonnet 5 6c8be629d2 fix(nvidia): load ib_umad and install nvlsm/libnvidia-nscq for NVL5 fabric
The NVSwitch-detection fix (983f41a) was necessary but not sufficient on
HGX B200 (Kaytus KR9288-X3): ExecCondition now correctly runs fabricmanager,
but nvidia-fabricmanager-start.sh then aborts with "Kernel module ib_umad
has not been loaded" on this NVL5+ board, leaving the fabric stuck in
"In Progress" and cascading into failed dcgmi diag/NVBandwidth/NCCL/Validate
GPU SAT tasks (confirmed via support bundle 20260729-165331).

Neither nvidia-fabricmanager nor DCGM declare the NVLink5 support packages
as apt dependencies (checked against the cuda-repo Packages index directly):
ib_umad must be modprobed before FM starts, nvlsm (InfiniBand Subnet Manager)
is launched internally by FM's own start script once present, and DCGM
dlopens libnvidia-nscq at runtime for NVSwitch health monitoring.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.82
2026-07-30 10:34:11 +03:00
mchus 983f41a1f0 fix(nvidia): detect NVSwitch fabric via driver, not host-PCIe
On HGX B200 (Kaytus KR9288-X3) NVSwitch is managed via HMC/ERoT and never
appears on host lspci as 10de:0680, so the old check always returned
"not present" and silently skipped fabricmanager — leaving the fabric
untrained and CUDA/NCCL/dcgmi failing with "system not yet initialized"
on otherwise-healthy hardware. bee-check-nvswitch now checks, in order,
nvidia-smi's GPU Fabric GUID, /proc/driver/nvidia-nvswitch/devices, and
finally the legacy host-PCIe scan for older HGX generations.
v12.81
2026-07-29 18:58:19 +03:00
mchusandClaude Sonnet 5 8a91f0f783 fix(webui): repair broken scenario Run button onclick, dedupe build.sh overlay staging
- page_scenario.go: onclick built via JSON.stringify() embedded raw double
  quotes inside a double-quoted HTML attribute, truncating the attribute so
  the click handler never compiled; pass the name through an escaped
  data-scenario-name attribute instead.
- build.sh: overlay staging rsyncs (OVERLAY_DIR->stage, stage->includes.chroot)
  ran without --delete, so a scenario removed from the repo (a9924b0) stayed
  baked into every ISO built from the persistent stage cache since — the
  "second script" in the Scenario page's list.
- blackbox: rewritten around a deterministic local zip + incremental
  patch-the-changed-suffix onto removable media, instead of walking/copying
  ~90 files through a synchronous ntfs-3g FUSE mount every cycle. journalctl
  captures are now "--since last sync" (were "--since boot", growing with
  uptime) and metrics.db is excluded (was copied whole every cycle).
- scenario: nvbandwidth-acs-ab now escalates GPU count (same-socket pair,
  other socket's pair, one cross-socket pair, all GPUs) under each ACS state
  instead of always running all 6 GPUs at once, using a new `bee
  gpu-bandwidth-groups` subcommand that discovers socket layout from
  `nvidia-smi topo -m` at runtime — gpu_indices is host-specific, so this
  can't be baked into the scenario file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.80
2026-07-29 18:11:24 +03:00
mchusandClaude Opus 4.8 1045fa9118 build(iso): drop gsp-off from boot menu, fail-safe keeps GPUs functional
bee.nvidia.mode=gsp-off loads the driver with NVreg_EnableGpuFirmware=0 and
skips nvidia-uvm/nvidia-modeset, so CUDA (nvbandwidth/dcgm/nccl) can't run —
and on Hopper H200 GSP is effectively mandatory anyway, so the mode is dead
weight in this test image. Remove it from every boot entry. fail-safe is now
bee.nvidia.mode=normal pci=realloc iommu=pt, so the GPUs come up with uvm and
the GPU test actually runs under iommu=pt (the point of the entry). All four
sources (grub.cfg + live.cfg.in templates, build.sh write_canonical_*) match.

The internal GSP auto-fallback in bee-nvidia-load (normal mode, GSP init
hangs >90s → retry with EnableGpuFirmware=0, then still load uvm) is left
intact — it serves converted SXM->PCIe cards on other test targets and keeps
CUDA working, unlike the removed explicit menu mode.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v12.79
2026-07-29 14:57:35 +03:00
mchusandClaude Opus 4.8 9b5d4e3036 build(iso): redefine no-GUI and fail-safe boot entries
no-GUI is now just the normal entry minus the GUI: bee.gui=off with
bee.nvidia.mode=normal (drops the previous gsp-off/pci=realloc it had picked
up). fail-safe is now simply the GPU/PCIe-workaround boot —
bee.nvidia.mode=gsp-off pci=realloc iommu=pt over the standard base — instead
of the old noapic/nodma/nomce/nolapic/nosmp minimal mode, so it can actually
run GPU tests (nvbandwidth) to verify the iommu=pt half of the vendor fix.
Applied identically across all four sources (grub.cfg + live.cfg.in templates
and build.sh write_canonical_* generators).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v12.78
2026-07-29 14:23:25 +03:00
mchusandClaude Opus 4.8 95d3532bb9 build(iso): unify GRUB/isolinux boot menus, add iommu=pt to fail-safe
The GRUB and isolinux menus (and their two sources of truth — the staged
templates and build.sh's write_canonical_* generators that rewrite the built
menu) had drifted: GRUB shipped a lean set, isolinux carried extra
display-debug entries (gsp-off, kms, kms-gsp-off) and lacked a WIPE entry in
the canonical writer. Collapse all four to one set:

  normal, load-to-RAM (default), no-GUI/troubleshooting (gsp-off, pci=realloc),
  fail-safe, WIPE ALL DISKS, Memory Test, + UEFI Firmware Settings (EFI only)

The fail-safe entry gains iommu=pt (AMD-Vi passthrough) — the OS-side half of
the vendor fix for the MSI CG480-S6053 cross-socket nvbandwidth reboot; ACS
can't be toggled from a stock-kernel cmdline so that stays a BIOS setting.
default stays index 1 = toram (config.cfg set default=1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v12.77
2026-07-29 14:11:43 +03:00
mchusandClaude Opus 4.8 a9924b08b0 feat(scenario): add nvbandwidth-acs-ab, an ACS off/on A/B for cross-socket P2P
Replaces the old nvbandwidth-all-gpu-power-watch scenario (dropped: the
power/IPMI-sampler framing was a dead-end — the blackbox shows every other
load test, incl. NCCL all-reduce across all 6 GPUs, passes with power fully
connected; only nvbandwidth on cross-socket GPU P2P reboots the box).

nvbandwidth-acs-ab is a two-phase reproduction for the MSI CG480-S6053 case.
Phase 1 disables PCIe ACS redirect at runtime via setpci (saving each
bridge's original ACS control word to /run/bee-acs-orig) then runs
nvbandwidth across all 6 GPUs — expected to survive. Phase 2 restores the
saved ACS values and repeats — expected to reproduce the reboot. Safe phase
first so its full blackbox reaches the USB stick before the risky phase can
reset the box (sync-bracket before the final job flushes phase-1 evidence).

ACS mutation runs via `bash -c` since scenario cmd is exec'd directly with no
shell. Both shipped copies (repo scenarios/ and image overlay) identical;
verified both parse via ParseScenarioJSON (4 jobs).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v12.76
2026-07-29 13:48:05 +03:00
mchusandClaude Opus 4.8 3ed826aeb7 fix(cli): route bee run scenarios through App so blackbox mirrors them
`bee run <scenario>` called platform.System.RunScenario directly with an
empty base dir, which defaults to /var/log/bee-sat — a tree the blackbox
worker does not mirror (it mirrors DefaultExportDir, /appdata/bee/export).
So a scenario launched from the CLI wrote its logs where blackbox never
looked: the sync-bracket hooks fired and flushed the export dir, but the
scenario's own output never reached the USB stick. The web-UI path was
unaffected because it goes through App.RunScenario, which defaults the base
dir to DefaultSATBaseDir (under the export dir).

Route the CLI through App.RunScenario too — matching the doc comment that
already claimed both paths did — so `bee run` output lands under the mirrored
export tree and shows up in the blackbox capture.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 13:45:34 +03:00
mchusandClaude Opus 4.8 86aa11c2d7 fix(webui): dispatch "scenario" tasks in the real task runner, collapse duplicate target switch
The Scenario page's Run button enqueued a task with target "scenario" that
never launched any load: the queue runs each task in an external
bee-worker subprocess (RunPersistedTask -> executeTaskWithOptions in
task_runner.go), whose target switch had no "scenario" case, so the task
died with "unknown target: scenario". The case had only been added to
taskQueue.runTask's switch in tasks.go — a stale copy exercised solely by
unit tests, which is why the tests passed while the button did nothing.

Add the "scenario" case (ReadScenario -> ParseScenarioJSON -> RunScenario)
to executeTaskWithOptions, and collapse runTask into a thin delegate to it
so there is a single target dispatch. The old runTask switch had already
drifted into a subset (missing nvme-format, *-write, raid-*, nvidia-config,
...); removing it eliminates the divergence that hid this bug. Also drop the
now-orphaned taskQueue.statusDB helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 13:00:41 +03:00
Mikhail ChusavitinandClaude Sonnet 5 d108df7fe9 platform/webui: add scenario description, show it in the Scenario page's list
ScenarioSpec gains an optional "description" field. Listing (both
ListLocalScenarioFiles and ListScenarioFilesOnRemovableMedia, via the new
scenarioDescription helper) reads it out of each file without requiring
full ParseScenarioJSON validation to succeed, so a listing never hides a
scenario over an unrelated validation issue. The webui Scenario page now
renders Name/Description/Found-on/Run instead of just Name/Found-on — a
bare filename rarely tells anyone but the author what a scenario actually
does.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.75
2026-07-28 19:28:07 +03:00
Mikhail ChusavitinandClaude Sonnet 5 5a780e96b4 platform/app/webui: ship the power-watch scenario baked into the image
platform.LocalScenariosDir (/usr/share/bee/scenarios, populated from
iso/overlay/usr/share/bee/scenarios/ by build.sh's overlay rsync) is now
checked before removable media for both `bee run <name>` and the "6.
Scenario" page — a scenario shipped with the image works with no USB
stick required. ReadScenario/ListAvailableScenarios merge local + USB;
the removable-media-only functions from the previous commit are kept
as-is (still used directly where that's actually what's wanted) rather
than renamed out from under existing callers/tests.

iso/overlay/usr/share/bee/scenarios/nvbandwidth-all-gpu-power-watch.json
is a copy of scenarios/nvbandwidth-all-gpu-power-watch.json — the two
aren't auto-synced (documented in scenarios/README.md), so shipping a
scenario baked-in means checking it into both places.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.74
2026-07-28 19:10:35 +03:00
Mikhail ChusavitinandClaude Sonnet 5 bcf02e0515 webui: add "6. Scenario" page — run scriptable test scenarios from removable media through the normal task queue
Exposes the scenario engine (platform.System.RunScenario, added earlier)
in the web UI instead of only the `bee run` CLI: a new nav item lists every
scenarios/*.json found on mounted removable media (GET /api/scenario/list)
and runs one with a click (POST /api/scenario/run), enqueued as a normal
Task with target "scenario" — progress/logs live in Tasks like any other
SAT pack, no separate live-output UI needed.

- app.go: satRunner gains RunScenario, exportManager gains
  ListScenarioFilesOnRemovableMedia/ReadScenarioFromRemovableMedia — both
  already implemented on platform.System, just newly exposed through App.
- webui/tasks.go: taskParams.ScenarioName; runTask's "scenario" case reads
  the file from removable media, parses it, and runs it.
- webui/page_scenario.go: the page itself.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
v12.73
2026-07-28 18:58:11 +03:00