Skip to content

fix(libc): return NULL from fgets for a size below 2 as the console does - #14863

Merged
oneandonlydean merged 1 commit into
boykopovar:mainfrom
msbutt1:fix/libc-fgets-size-one
Nov 5, 2026
Merged

oneandonlydean merged 1 commit into
boykopovar:mainfrom
msbutt1:fix/libc-fgets-size-one

Conversation

@msbutt1

@msbutt1 msbutt1 commented Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

What

fgets now returns NULL for any size below 2, as the console's does, and checks the size before anything else.

The console's fgets (NID KdP-nULpuGw, in the libc.prx a title ships in sce_module) begins with cmp esi, 0x2 / jge to its main path; below 2 it clears the return register and jumps to the epilogue, which returns it. It does not lock the stream, write the buffer, set errno or change the stream's flags. The same entry is in Cult of the Lamb (PPSA06465, SDK 5.00) and Sifu (PPSA03001, SDK 3.00), read from my dumps.

fgets_nid_postfix instead:

  • for a size of 0 or less, set the stream's error flag and errno to EINVAL (from FreeBSD, in 17e03d3);
  • for a size of 1, passed the call to the host fgets, whose result depends on the host C library: the UCRT returns the buffer with an empty string, and on Ubuntu 22.04, where GCC defines _FORTIFY_SOURCE=2 and the call is built as glibc 2.35's __fgets_chk, it returns NULL. So guest_standard_streams fails on Linux since test(libc): check fgets with a size of 1 stores only the null character #13529 added CheckFgetsSizeOne, which expects the empty string.

CheckFgetsSize (sizes 0, −1 and INT_MIN) now expects NULL, an untouched buffer, errno and the error and end-of-file flags unchanged, and the position unchanged. CheckFgetsSizeOne expects the same for a size of 1 at the start of the stream and at end of file.

The size check also comes before the stream orientation that #10324 added, since the console returns before it reads the stream. GuestFwide.cpp now expects a stream left unoriented after fgets with a size of 0, and the fwide entry in docs/dev/debt/libc.md says so. The firmware's own fgets (BunnyFl's review) and the titles' libc.prx copies match.

Tested

Ubuntu 22.04.5 (glibc 2.35), Linux 6.8.0-138-generic, GCC 13.4, Release.

  • guest_fwide, guest_scan_count, guest_wide_formatting, guest_wide_stream_scan, guest_stdio_variadic, the five guest_standard_streams* tests, file_streams, guest_formatting and guest_filesystem pass 3 of 3. With the updated tests and main's StandardStreams.cpp, guest_fwide and guest_standard_streams abort.
  • Full ctest -j 8 on this head: 3,731 tests. Four fail, none in libc: agc_driver_htile_mask_lifetime and guest_runtime_lifecycle fail the same way on main here, avplayer_http_patched_modules fails with a symbol lookup error with main's StandardStreams.cpp too, and agc_driver_image_table_vouched_lookups timed out at 120 s under -j 8 and passes alone (55 s).
  • Not run on Windows; the size check returns before the host stream is used there too.

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw (NotImplemented_nid_no_patch); silent stubs are listed under Silent stubs in their library's technical debt file
  • No notes, investigation .md files or images added to the repository (attach them to this PR)
  • New third-party code is a submodule built from source
  • Generic behaviour, not specific to one title
  • Depends on: none
  • Fixes: none
  • Unblocks: none
  • GPUs tested: n/a
  • AI-assisted: yes

@oneandonlydean

Copy link
Copy Markdown
Collaborator

Reviewed at 2b3de79.

Thanks for tracking down the __fgets_chk difference. The console's own fgets behaves like glibc's __fgets_chk here, though, not like FreeBSD or the UCRT. In the console libc (fgets, NID KdP-nULpuGw, in the libc.prx a title ships in sce_module) the function begins:

The check is cmp esi,0x2 / jge to the main path; below 2 it zeroes the return register (xor r14d,r14d) and jumps to the epilogue that returns it.

So for a size below 2 it returns NULL. It doesn't lock the stream, write the buffer, set errno or touch the stream flags.

That means CheckFgetsSizeOne (core/libs/tests/GuestStandardStreams.cpp lines 319-345), added in #13529, expects behaviour the console doesn't have. Could this PR go the other way?

  • In fgets_nid_postfix (core/libs/prx/libc/src/StandardStreams.cpp lines 229-234), return nullptr for a size of 1 without locking or writing the buffer, instead of storing the terminator.
  • Change CheckFgetsSizeOne to expect nullptr, an untouched buffer (buffer[0] == '!'), and an unchanged position and flags, at the start of the stream and at end of file. The lock-wait part (lines 333-343) then no longer applies.

Linux and Windows then give the same result, whatever the host C library does.

@msbutt1 msbutt1 changed the title fix(libc): return an empty string from fgets with a size of 1 fix(libc): return NULL from fgets for a size below 2 as the console does Nov 1, 2026
@msbutt1
msbutt1 force-pushed the fix/libc-fgets-size-one branch from 2b3de79 to 262c040 Compare November 1, 2026 21:33
@msbutt1

msbutt1 commented Nov 1, 2026

Copy link
Copy Markdown
Contributor Author

@oneandonlydean Thanks, that's the better evidence. I found the same entry in the libc.prx of Cult of the Lamb (PPSA06465, SDK 5.00) and of Sifu (PPSA03001, SDK 3.00): cmp esi, 0x2 / jge / xor r14d, r14d / jmp to an epilogue that only returns r14.

I have changed the PR to follow it, and widened it slightly, since that check covers every size below 2: sizes of 0 and less (CheckFgetsSize, from 17e03d3 "like FreeBSD") set the error flag and EINVAL on main, which the console doesn't do either. fgets_nid_postfix now returns NULL for a size below 2 first, before the closed-stream check, without locking or touching the buffer, errno or the flags; CheckFgetsSize and CheckFgetsSizeOne expect that, at the start of the stream and at end of file, and the lock-wait part is gone. The commit and title are now fix(libc): return NULL from fgets for a size below 2 as the console does. If you'd rather keep the sizes of 0 and less for a separate PR, I'll split it.

@github-actions

github-actions Bot commented Nov 1, 2026 •

Copy link
Copy Markdown

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 5021 core/libs/prx/libc/src/StandardStreams.cpp
PR 8150 core/libs/prx/libc/src/StandardStreams.cpp
PR 10405 core/libs/prx/libc/src/StandardStreams.cpp
core/libs/tests/GuestStandardStreams.cpp
PR 12057 core/libs/prx/libc/src/StandardStreams.cpp
PR 12292 core/libs/prx/libc/src/StandardStreams.cpp
core/libs/tests/GuestStandardStreams.cpp
PR 12920 core/libs/prx/libc/src/StandardStreams.cpp
core/libs/tests/GuestStandardStreams.cpp
PR 14559 core/libs/prx/libc/src/StandardStreams.cpp
PR 14560 core/libs/prx/libc/src/StandardStreams.cpp
PR 14878 core/libs/prx/libc/src/StandardStreams.cpp

@github-actions github-actions Bot added the libc label Nov 1, 2026
@github-actions github-actions Bot added the needs rebase Conflicts with main, rebase needed label Nov 2, 2026
@B-Howell

B-Howell commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

@msbutt1 I tested this on Linux (WSL Ubuntu 22.04, GCC 11.4, glibc 2.35, Release) against current main 92d2fdc343, where guest_standard_streams aborts at CheckFgetsSizeOne (GuestStandardStreams.cpp:325). The fscanf: suppressed conversion passed line that ends the log is only the last progress message: Require aborts without printing. On that host fgets builds to __fgets_chk, which returns NULL for a size of 1.

Merged onto 92d2fdc343, this PR conflicts with fgets_nid_postfix's if (stream) stream->Orientation(-1); from #10324 (a12f8fc4d3). I kept that line after your size < 2 return. Results:

  • guest_standard_streams passes.
  • guest_fwide then fails at GuestFwide.cpp:187, wide(stream, 0) == -1 ("fgets should orient the stream even for size 0"), because a size below 2 now returns before orienting. With that expectation changed to == 0, these 13 pass: guest_fwide, guest_scan_count, guest_wide_formatting, guest_wide_stream_scan, guest_stdio_variadic, guest_standard_streams and its 4 variants, file_streams, guest_formatting, guest_filesystem. On plain main only guest_standard_streams fails.

So the rebase needs two more edits besides the conflict: GuestFwide.cpp:187, and the fwide entry at docs/dev/debt/libc.md:109, which says a stream is oriented "also when fgets, fgetws or ungetwc then fail on their arguments". After this PR that no longer holds for fgets with a size below 2. #10324 says its ordering followed FreeBSD and was not measured on a console, so the console disassembly quoted in review here should win.

Hosted Linux CI on main (Ubuntu 24.04) passes this test, and the Linux job is skipped on PRs, which is why it went unnoticed. Only these focused tests ran, not the full suite.

AI-assisted: yes (Claude Code)

@BunnyFl BunnyFl 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.

Checked against the system libc in firmware 13.42 (libSceLibcInternal.sprx, sha256 a01795ab49df639dd40abd6194956ada062244826824ce486c9acab227bf5285), where fgets (KdP-nULpuGw) is exported at 0x60090. It matches your reading of the title's libc.prx:

600a1: cmp  $0x2,%esi
600a4: jge  0x600ad
600a6: xor  %ebx,%ebx
600a8: jmp  0x6022e
6022e: mov  %rbx,%rax        (epilogue, ret)

Below 2 nothing is called and neither the buffer nor the stream is read or written, so if (size < 2) return nullptr; as the first statement is right. It also removes a null dereference on main: there, fgets(buf, 0, NULL) passes the stream && guard and then calls stream->SetError() on a null pointer. With this PR it returns NULL like the console. (For a size of 2 or more the firmware dereferences the stream with no null check, movzwl (%rdi) after the first call, so the remaining null-stream dereference there matches the console.)

Tested on Windows 11, WinLibs MinGW GCC, at 262c040: all five guest_standard_streams* tests pass. With only StandardStreams.cpp restored from the merge-base, guest_standard_streams aborts (0xc0000409), so the tests catch the old behaviour on Windows too.

For the rebase: the conflict is with main's new if (stream) stream->Orientation(-1); line in fgets_nid_postfix. Keep the size check ahead of it, because the firmware returns before touching the stream, so a size below 2 must not set the stream's orientation either. I'll re-review once the rebase is pushed.

@msbutt1
msbutt1 force-pushed the fix/libc-fgets-size-one branch from 262c040 to 4f6d9d4 Compare November 2, 2026 23:50
@msbutt1

msbutt1 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto main 5cb015c (now 4f6d9d4). Thanks @BunnyFl for checking the firmware's fgets and running it on Windows, and @B-Howell for the Linux run and for finding the two follow-on edits.

  • Conflict with feat(libc): track stream orientation and implement fwide #10324: if (size < 2) return nullptr; stays the first statement, and if (stream) stream->Orientation(-1); comes after the closed-stream check, so a size below 2 doesn't orient the stream, since the console returns before it touches the stream.
  • GuestFwide.cpp: after fgets with a size of 0, the stream is now expected to be unoriented (wide(stream, 0) == 0). The fgetws and ungetwc checks are unchanged.
  • docs/dev/debt/libc.md: the fwide entry now says the stream isn't oriented when fgets has a size below 2, but still is when fgetws or ungetwc fail on their arguments.

Tested on Ubuntu 22.04.5 (glibc 2.35), GCC 13.4, Release:

  • The 13 tests B-Howell listed pass 3 of 3.
  • With main's StandardStreams.cpp and the new tests, guest_fwide and guest_standard_streams abort.
  • check_conventions.py --base origin/main passes.

Overlap check for the PRs the bot couldn't compare: none implements the same behaviour.

@B-Howell

B-Howell commented Nov 3, 2026

Copy link
Copy Markdown
Contributor

@msbutt1 Re-tested 4f6d9d4b1f. It already contains current main 5cb015caaa (0 behind), so I tested the head itself.

  • The 13 tests from my earlier comment pass 3 of 3 (--repeat until-fail:3) on Windows 11 (WinLibs GCC 15.2.0 r7, UCRT, Ninja, Release) and on WSL Ubuntu 22.04 (GCC 11.4, glibc 2.35, Release).
  • On Windows, with main's StandardStreams.cpp and the new tests, guest_fwide aborts at GuestFwide.cpp:187 ("fgets with a size below 2 should leave the stream unoriented"), and guest_standard_streams aborts (0xc0000409) without printing a message. The other 11 pass.
  • The order in StandardStreams.cpp:234-236 and the fwide entry at docs/dev/debt/libc.md:110 match the code. check_conventions.py passes with the live title and body, and git diff --check is clean. Only these focused tests ran.
  • Optional: the orientation check covers only a size of 0. A size of 1 takes the same size < 2 return, but no test checks that it leaves the stream unoriented.

AI-assisted: yes (Claude Code)

@BunnyFl BunnyFl 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.

Re-checked after the rebase: approve

Head 4f6d9d4b1f keeps the ordering the firmware needs. In 13.42's libSceLibcInternal.sprx (sha256 a01795ab49df639dd40abd6194956ada062244826824ce486c9acab227bf5285), fgets at 0x60090 returns NULL at 0x600a1-0x600a8 for a size below 2, before it reads the stream. This PR's fgets_nid_postfix now:

  • returns nullptr for size < 2 first;
  • then does the closed-stream check from main;
  • then calls stream->Orientation(-1).

So a size below 2 leaves the stream unoriented, as on the console.

The GuestFwide.cpp change (wide(stream, 0) == 0 after fgets with size 0) and the fwide debt entry follow from that ordering.

Tested

Windows 11, WinLibs GCC 15.2.0, Release, BUILD_TESTING=ON. Head 4f6d9d4b1f sits on current main.

Run ctest -R '^guest_(fwide|standard_streams)$'
This PR 2/2 pass
Same tree with main's core/libs/prx/libc/src/StandardStreams.cpp 0/2; both abort (0xc0000409)

@github-actions github-actions Bot added docs and removed needs rebase Conflicts with main, rebase needed labels Nov 3, 2026
@TechLevelZero

Copy link
Copy Markdown
Contributor

❌ AMD GPU test: failed

PR head 4f6d9d4 merged onto main 7feb37e · tested 2026-11-03 04:08 UTC · 25 min

GPU AMD Radeon RX 6300 (RADV NAVI24), driver Mesa 25.2.8-0ubuntu0.24.04.4, Vulkan 1.4.318
Host Ubuntu 26.04.1 LTS, kernel 7.0.0-42-generic, container ubuntu:24.04
Logs build and test logs for this run
Build ✅ passed (Release, -DANYPS5_ENABLE_SPIRV_TOOLS=ON)
Tests ❌ 3,619 passed · 15 failed (1 new: 1 pass on main with the PR's tests, 13 also fail on main, 1 unstable on main) · 59 skipped (ANYPS5_REQUIRE_VULKAN=1)
Test setup CPU-only tests without a GPU; GPU tests (and any CPU-only test that failed or skipped without one) once on the GPU with VK_LAYER_KHRONOS_validation and ANYPS5_NO_SHADER_CACHE=1; main ran the same way
Test selection 3,693 of 3,743 tests run: the ones this change can reach, chosen by tools/select_tests.py from main's build graph
Vulkan validation ✅ no new validation IDs vs main (1309 tests run on the GPU)
Slow tests (>30 s, GPU tests timed with validation layers) ⚠️ agc_driver_pipeline_journal 120.2 s, ⚠️ agc_driver_graphics_maps_fallback 97.5 s, ⚠️ agc_driver_graphics 89.3 s, ⚠️ public_headers 64.6 s, ⚠️ agc_driver_image_sample_unnormalized 60.2 s

Failing tests

This PR changes test files, so each failing test was also built and run on main with only the PR's test changes applied. ✅ there means the failure comes from the PR's other changes; ❌ means the test fails on main too, so this run does not show the PR caused it.

Test With this PR Same test on main 7feb37e (main + this PR's test changes only)
guest_pthread_start ❌ failed ✅ passed
guest_pthread_start: Timeout (also failed when re-run alone), assertion and diagnostics
ctest: Timeout


Unstable on main (1, these also failed in recent main runs on this machine; not counted against this PR)
agc_driver_pipeline_journal: failed in 5 of the last 7 main runs
Failing on main too (13, not counted against this PR)
agc_driver_guest_memory_lifetime_direct
agc_driver_image_table
agc_driver_job_manager_compute_capacity
agc_driver_native_depth_bda_clear
agc_driver_native_depth_buffer_clear
agc_driver_native_depth_clears
agc_driver_native_multisample_depth
agc_driver_submit_progress
agc_idle_publish_wait
agc_submit_progress
agc_submit_progress_no_timeline
guest_mmap_executable
guest_socket_options_multicast
Passed on retry (1, failed in the parallel run, passed when re-run with a 360 s timeout; not counted against this PR)
vulkan_validation_gate_attribution: Failed in the parallel run; also passed only on retry in 7 other runs in the last 7 days
Validated but not compared (1, not in the main baseline's validation pass)
guest_pthread_start
Validation IDs already present on main (4 IDs in 752 tests)
VUID-VkDeviceCreateInfo-pNext-pNext (752 tests): agc_compute_pipeline_replay, agc_driver, agc_driver_address_passes, agc_driver_address_passes_draw_transitions, agc_driver_address_passes_targets_split, agc_driver_alias_copy_label, agc_driver_alpha_to_coverage, agc_driver_array_layer_target, and 744 more
VUID-vkCmdCopyImage-srcImage-01548 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-vkCmdCopyImage-srcImage-01551 (11 tests): agc_driver_depth_clear_hle, agc_driver_depth_plane_buffer_memory, agc_driver_depth_plane_cpu_write_after_memo, agc_driver_depth_plane_cpu_write_after_page_unwatch, agc_driver_depth_plane_cpu_write_after_partial_unwatch, agc_driver_depth_plane_partial_store, agc_driver_depth_plane_partial_store_unimported, agc_driver_depth_storage_dispatch, and 3 more
VUID-VkPipelineColorBlendStateCreateInfo-pAttachments-00605 (2 tests): agc_driver_prepared_exports, agc_driver_srgb8_color_target
Skipped (59)
agc_driver_bda_reads_shadowed_results: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_bda_reads_shadowed_results_per_build: skipped, the device reports watched host memory written when it imports or reads it
agc_driver_copied_writer_label_order: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_copied_writer_label_order_drain: skipped, the device reports a minStorageBufferOffsetAlignment of 4, so the dispatch binds its record at +0x1004 in place and nothing is written back; the test needs an alignment above 4
agc_driver_float16_unused_nan_payloads: skipped, the device has no native f16 conversions with RTE rounding and kept f16 denormals
agc_driver_image_table_vouched_lookups: skipped, the device reports 4 of 4 host-imported pages written after a GPU read, so a watched imported table cannot be vouched for
agc_driver_linear_target_direct_write_completion: skipped, the device does not keep host imports write-watched, so no completion stamp over the target is tracked
agc_driver_ngg_vertex_wave_reduction: skipped, s3 describes a 32-lane wave only on 32-lane subgroups, and the device has 64 lanes
agc_driver_scattered_runs: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_sparse_dma_buf_import: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_driver_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_driver_unit_shadow_budget: skipped, the device does not import watched host memory for unit shadows
agc_driver_vertex_elected_atomic_max: skipped, the device runs vertex programs in subgroups, so they keep their cross-lane reductions
agc_host_import_sparse: skipped, host imports are unavailable: /dev/udmabuf is not accessible
agc_import_settle_initial_dcc: skipped, the device has no deferred settle of host imports: imports under 1 MiB wait for one only on Windows
agc_shadowed_key_stores: skipped, the device imports no watched host memory under a unit shadow
agc_small_tiled_shadow_label: skipped, host imports are unavailable: the test block is not imported and watched
agc_storage_clear_hand_over: skipped, the device imports no watched host memory for the clear-key hand-over
agc_unit_shadow: skipped, the device does not import watched host memory for unit shadows
agc_unit_shadow_completion_store: skipped, the device reports watched host memory written when it imports or reads it
guest_http_uri_locale: 
guest_net_ctl: skipped, the host has no IPv4 address
guest_video_out_color_space: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_hdr: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_high_frame_rate: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_output_mode: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_resolution_status: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
guest_video_out_wqhd_detection: skipped, no display or Vulkan device: SDL_InitSubSystem(VIDEO | GAMECONTROLLER) failed: wayland,x11 not available
video_out_flip_aftergpu: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_backtoback: skipped, no display or Vulkan device: DisplayWindow: Vulkan support is either not configured in SDL or not available in current SDL video driver (offscreen) or platform
video_out_flip_controls: skipped, no display or Vulkan device: DisplayWi
Reproduce
git fetch origin 4f6d9d4b1f36156c37e2b21f86e4cafb592bbbab 7feb37ee3e30e10139b483fb4df689d5b5b69ab8
git checkout 7feb37ee3e30e10139b483fb4df689d5b5b69ab8 && git merge --no-ff 4f6d9d4b1f36156c37e2b21f86e4cafb592bbbab
git submodule update --init --recursive
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=ON -DANYPS5_ENABLE_SPIRV_TOOLS=ON
cmake --build build && cmake --build build --target libs
VK_DRIVER_FILES=/nonexistent/icd.json ctest --test-dir build -E '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 12 --output-on-failure
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(agc|shader|recompiler|videoout|video_out|guest_video_out|vulkan)|gpu' --timeout 120 --parallel 16 --output-on-failure
# failures are then re-run alone: --timeout 360 --parallel 2; new validation IDs: the test alone, twice, --parallel 1
# the failing tests, on this merge and on main:
ANYPS5_REQUIRE_VULKAN=1 ANYPS5_NO_SHADER_CACHE=1 VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation \
  ctest --test-dir build -R '^(guest_pthread_start)$' --parallel 1 -V

Automated build and test on a contributor's AMD machine; it does not replace upstream CI. A new comment is posted for each new push. Full logs.

@oneandonlydean
oneandonlydean merged commit 5f51026 into boykopovar:main Nov 5, 2026
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants