strlcpy is the one function in this directory's reach that nothing here
covers, and a machine directory may override it like any other.
Sweep every source and destination alignment pair against sizes 1 to 64,
and for each of those every capacity from zero to one past the length.
Check the return value, which is the length of src whether or not the
copy fit, the truncation point, the content, that a capacity of zero
writes nothing at all, and that nothing lands past the terminator.
The alignment pairs are the point. An implementation that walks one of
the two pointers to a boundary and then copies a register at a time is
correct whenever the two agree, so a test that only ever passes matching
alignments says nothing about it.
The timing half is guarded. perf_gettime() is not a system call, so an
application reaches it only where the C library builds its own copy or
where the application and the kernel are one image; calling it
unconditionally leaves the test unbuildable on a kernel build, which is
where the correctness half is still wanted.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
The existing speed checks time one call at one size, 128 bytes, with both
operands aligned. A machine implementation usually takes its wide path
only when the pointers satisfy some alignment condition, so that single
point reports the best case and says nothing about the rest of the input
space.
Measure the same functions across a size sweep and every source and
destination alignment pair instead, plus strlcpy. On rv64 the difference
this exposes is not marginal:
strcpy 32768 B s+0/d+0 2938.0 MB/s
strcpy 32768 B s+1/d+1 2942.0 MB/s
strcpy 32768 B s+1/d+2 626.0 MB/s
memcmp 32768 B s+0/d+0 412.4 MB/s
memcmp 32768 B s+1/d+2 41.0 MB/s
Two pointers misaligned by the same amount run at the aligned rate;
misaligned by different amounts they fall to a tenth of it. Neither
number is visible from an aligned measurement alone.
A function with no machine implementation reports the same rate at every
alignment, so the sweep also shows which of them a machine directory
actually covers.
Each result reports MB/s, which compares across machines, and cycles per
byte where perf_gettime() is reachable from an application, both from one
timed loop. strcat starts from an empty destination on each turn, since
appending to the last result would grow it without bound, so its figure
includes that store.
It sits behind TESTING_ARCH_LIBC_BENCH, default n, because a measurement
runs for a fixed interval and a full sweep takes about a minute.
Assisted-by: Claude:claude-opus-5
Signed-off-by: Justin Hammond <justin@dynam.ac>
Add Library.mk for declarative shared-library builds. Reuse the compiler-runtime lookup shared with Application.mk and cover multi-source libraries with sotest.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
NuttX x86_64 (qemu-intel64) builds with the native host gcc, which
exposed several host-environment leaks in the CPython cross build:
- Pass the -D/-U macro flags from CFLAGS as CPPFLAGS so
preprocessor-only configure probes (Misc/platform_triplet.c) do not
see the host's __linux__ and misdetect the platform as
x86_64-linux-gnu, enabling Linux-only code such as the perf
trampoline.
- Force linux/random.h and sys/xattr.h probes to no in config.site:
NuttX provides neither, but a native toolchain resolves them against
the host /usr/include. The former drags host ioctl macros into
posixmodule, the latter enables os xattr support with no xattr
syscalls to link against.
- Disable _curses, _curses_panel, _dbm, _gdbm, _hashlib and _tkinter:
their host libraries are discovered via pkg-config when the target
compiler can compile host headers. hashlib keeps working through
the built-in HACL implementations.
- Only build _posixsubprocess when the arch has a real fork(): its
vfork() support is only an optimization and the fork() fallback path
(PyOS_BeforeFork/PyOS_AfterFork_*) is compiled unconditionally but
declared only under HAVE_FORK. This also fixes rv-virt:python,
which became vfork-only after the fork/vfork split.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
Add an LVGL front end for browsing, installing, updating, and launching nxpkg entries. Keep network and install work off the UI path, and stop launched apps with SIGTERM.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
builtin_isavail() returns 0-based index on success and negative
errno on failure. The condition 'index > 0' incorrectly rejects
valid index 0, making the first builtin app (index 0) unusable
as an NSH command under CONFIG_NSH_BUILTIN_AS_COMMAND.
Fix by changing to 'index >= 0'.
Signed-off-by: zhaoyutao1 <zhaoyutao1@xiaomi.com>
The Depends-On feature (commit 2aebae740) made the Build workflow
trigger on PR description edits. A gate job checks whether the edit
changed any Depends-On declaration: if yes, the build jobs run again
with the new dependencies; on any other edit the gate skips all build
jobs.
The gate has a side effect that breaks PR check results. Skipped jobs
still register check results on the PR, and the PR checks view shows
the newest check run of each name. So after any description edit the
PR shows "skipped" for every build check instead of the pass/fail
from the real run. Re-running that newest run only repeats the skip,
so the real results never come back. This can also hide a red X from
a failed build.
Fix by not triggering Build on description edits at all: remove the
"edited" event type and the gate job.
Depends-On keeps working: dependencies are read from the description
at the start of every run against master, as before. Fetch-Source now
re-reads the description through the API instead of using the copy
stored in the event payload, so every run uses the current Depends-On
state no matter how it was triggered.
After editing a Depends-On line, retrigger CI by any of:
- pushing new or rebased commits to the PR branch
- closing and reopening the PR
- pressing "Re-run all jobs" on the existing Build run
A description edit alone no longer triggers anything, which is
exactly the behavior that corrupted the PR check results.
Signed-off-by: raiden00pl <raiden00@railab.me>
Assisted-by: Claude Code
Add a separate LVGL example that reports touchscreen state, presses, and coordinates without changing lvgldemo.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Add the remaining package commands. Order database and payload updates so failures do not leave stale version pointers.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Download catalogs and artifacts with bounded buffers and atomic staging. Prepare storage before taking the lock so first-run syncs are safe.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Validate package fields before using them and ensure version pointers refer to installed entries. Keep installed manifests available for rollback.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Make the nxpkg storage root configurable and write owned files through temporary paths before renaming them. Record lock ownership so processes can recover abandoned locks safely.
Signed-off-by: aviralgarg05 <gargaviral99@gmail.com>
Install a UNIX-style setuid-root sudo app: the kernel raises euid on
exec, userspace verifies the caller with passwd_verify(), then
setresuid/setgroups and execvp() run the command. Ostest covers
setuid exec after a hard credential drop.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
Toybox is the toolbox used on Android by default. Adding it to NuttX
allows to have more advanced features from Linux, even better support
for shell scripts.
Signed-off-by: Alan C. Assis <acassis@gmail.com>
Assisted-by: Claude Sonnet 5 <noreply@anthropic.com>
The `on <event>` action re-executed on every property poll because the
event pending flag was sticky: event_callback returned the same non-zero
pending value whether the event had just changed or had stayed satisfied
from an earlier change. init_action_foreach_event could not distinguish
an edge from a steady state and re-enqueued the action each round
(board_netinit ran 262 times per boot).
Introduce a three-state result (EVENT_STATE_UNSATISFIED / SATISFIED /
TRIGGERED). event_callback now returns TRIGGERED only on the edge where
pending flips false -> true. foreach folds per-event states into a
product clamped to TRIGGERED, enqueuing the action only when every event
is satisfied AND at least one fired this round.
Assisted-by: GitHubCopilot:claude-4.8-opus
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Action triggered on any event before this fix (e.g. both opposite actions in
init.rc below triggered when event "boot" triggered).
init.rc
on boot && property:sys.boot.reason=bootloader
echo "On boot, the reason is BL."
on boot && property:sys.boot.reason!=bootloader
echo "On boot, the reason is not BL."
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
init.rc
on boot && property:sys.boot.reason!=bootloader
echo "On boot, the reason is not BL."
Before fixing
init_main: action 0x40436120
init_main: sys.boot.reason!=bootloader
init_main: argv[0] 'echo'
init_main: argv[1] 'On boot, the reason is not BL.'
After fixing
init_main: action 0x40436120
init_main: sys.boot.reason!=bootloader
+ init_main: default==boot
init_main: argv[0] 'echo'
init_main: argv[1] 'On boot, the reason is not BL.'
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Previously only supported event trigger, now added support for
action triggers (property setting).
Steps to enable action triggers:
- Define all init_property_*() interfaces declared in this file.
- Data structures or functions that will likely be used:
- struct action_trigger_s
- init_action_for_every()
Example
```
on boot
setprop key_test
setprop key_test value_test /* property changed and matched */
trigger event_test
on event_test && property:key_test=value_test
echo "on event_test, property changed!"
on property:key_test=value_test
echo "property changed!"
```
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
Add the property backend and a setprop builtin so that setting a
property can feed action triggers. property_simple.c provides a minimal
init_property_*() implementation whose init_property_set() forwards the
key/value pair to init_action_trigger_event(), and init.c wires the
property poller into the init poll loop.
Signed-off-by: fangpeina <fangpeina@xiaomi.com>
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
nsh_consolemain always passes NSH_LOGIN_LOCAL, so treating any
non-NONE session as a login rewrote nsh> to nsh# whenever
SCHED_USER_IDENTITY was enabled. NTFC boot detection then timed
out on sim/citest and qemu-rv/citest.
Keep CONFIG_NSH_PROMPT_STRING until a successful console or telnet
login (or su).
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
When NSH_PROMPT_STRING_ROOT/USER are empty, keep NSH_PROMPT_STRING at
boot (for example, "nsh> ") so CI/NTFC boot detection still works.
After login, su, or telnet login, replace the last '>' with '#' (euid 0)
or '$' (non-zero euid) and ensure a trailing space. Refresh readline
after console/telnet login when line editing is enabled.
Signed-off-by: Abhishek Mishra <mishra.abhishek2808@gmail.com>
This commit adds a system utily 'stty' to NuttX, providing a standard
Unix-like interface for configuring terminal (TTY) device settings.
The command allows runtime configuration of termios attributes for
serial ports and other character devices.
Usage examples:
# Set raw mode on ttyS0 (for binary communication)
nsh> stty -F /dev/ttyS0 raw -echo
# Set console mode for interactive terminal
nsh> stty -F /dev/ttyS0 cooked
# Display current settings
nsh> stty -F /dev/ttyS0
# Configure stdin (if it's a TTY)
nsh> stty echo icanon
# Set baudrate (if driver support)
nsh> stty -F /dev/ttyS1 speed 115200
Signed-off-by: fangpeina <fangpeina@xiaomi.com>
ostest's "vfork" test was never testing vfork(). It has the child write a
global and the parent observe the write -- the defining property of *sharing*,
not of vfork(), whose defining property is that the parent is suspended and
whose contract forbids the child to write anything at all. It passed because
NuttX implemented fork() and vfork() as the same sharing primitive, which
apache/nuttx#19562 separates.
vfork.c is rewritten to test what vfork() promises. The child does only what
POSIX permits -- it calls _exit(42) and nothing else, not even exit(), which
would run atexit handlers and flush stdio in the parent's address space. Since
the child may not write memory and the parent cannot run while the child lives,
the observable is the child's exit status: had the parent not been suspended,
it would have reached waitpid() while the child was still alive. Where child
status is not retained -- ostest_main() sets SA_NOCLDWAIT for the whole run,
deliberately -- ECHILD is accepted as equally good evidence, since it says the
child was already gone when the parent asked.
fork.c is new and tests POSIX fork(): the child's writes to .data, .bss and
the heap are invisible to the parent and vice versa, a pointer to a stack local
taken before the fork names the same object in both, and the child does
everything a vfork() child may not -- calls malloc() and printf(), and returns
from the function that called fork().
Both run at the top of user_main(). They exercise the lowest-level machinery
in the suite -- address environments, stack setup, the architecture's register
context -- so a fault in one takes the process down instead of reporting a
failure. Learning that in seconds rather than after everything else has passed
matters when a port is being brought up.
Each test gates on the one primitive it tests, ARCH_HAVE_VFORK and
ARCH_HAVE_FORK respectively. There is no compatibility layer and no mapping
between symbols. vfork.c no longer requires SCHED_WAITPID: the suspension is
in the kernel primitive now, so the test's core assertion holds without it and
only the status check is conditional.
The simulator is the one exception. It selects ARCH_HAVE_VFORK, but ostest
takes the sim down as soon as the test runs there, so the call keeps the
!ARCH_SIM guard that apps ee7642793 put on the old test in 2024. The old gate
hid this: ARCH_HAVE_FORK is not set on the sim, so the test was not built
there at all.
The other in-tree callers are audited for which primitive they actually meant:
* interpreters/python's _posixsubprocess and netutils/libwebsockets'
LWS_HAVE_WORKING_VFORK want the fork-then-exec path -- ARCH_HAVE_VFORK.
* python's os.fork() and libwebsockets' LWS_HAVE_FORK mean real fork() and stay
on ARCH_HAVE_FORK, so they become *absent* rather than silently wrong.
* testing/fs/fdsantest's vfork case follows ARCH_HAVE_VFORK.
interpreters/bas is deliberately left alone. Its SHELL and EDIT statements
reach for vfork() under an ARCH_HAVE_FORK guard and want the same treatment,
but checkpatch.sh checks the whole of any file a patch touches and
bas_statement.c produces 1681 pre-existing findings against master, so a
one-line change there fails CI on its own. The consequence is small:
EXAMPLES_BAS_SHELL is EXPERIMENTAL and already depends on ARCH_HAVE_FORK, so it
becomes unselectable rather than misbehaving.
Assisted-by: Claude Code:claude-opus-5
Signed-off-by: Marco Casaroli <marco.casaroli@gmail.com>
_err() is a kernel-internal debug macro from <debug.h>; it resolves to
the kernel-only _err symbol, which an application cannot reference. In a
flat build the example therefore fails to link:
apa102_main.c: undefined reference to `_err'
(observed on xtensa/esp32s3). Report the failure with fprintf(stderr),
which is what the open() error path a few lines above already does.
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
This example drives the APA102 LED-strip character driver: it opens
/dev/leddrv0 and writes an array of struct apa102_ledstrip_s from
<nuttx/leds/apa102.h>, which is provided by drivers/leds/apa102.c
(CONFIG_LEDS_APA102). CONFIG_LCD_APA102 selects an unrelated driver,
drivers/lcd/apa102.c, which drives an APA102 matrix as a framebuffer LCD
and registers no /dev/leddrvN node at all.
The wrong dependency makes the example unusable either way: with the LED
strip driver enabled the example cannot be selected in menuconfig, and
with CONFIG_LCD_APA102 the dependency is met but the driver the example
needs is absent, so it fails at open().
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
Assisted-by: Claude Opus 5 (Claude Code)
Previously "ifconfig eth0 up" fell through to the host-IP parsing
branch, where inet_addr("up") returns INADDR_NONE, so the address was
silently set to 255.255.255.255 and the interface was never brought
up. Users had to run the separate "ifup"/"ifdown" commands.
Recognize the "up" and "down" tokens explicitly and apply them once
the rest of the configuration is in place, so up/down is just another
keyword in the argument list as it is in Linux ifconfig.
A bare "ifconfig <iface> up|down" needs no special case: the preceding
patch made the address, netmask, gateway, DNS and DHCP blocks check
whether they were asked for, so the command falls through them without
touching anything.
The up/down handling calls netlib_ifup()/netlib_ifdown() from netlib, so
it stays available regardless of CONFIG_NSH_DISABLE_IFUPDOWN (which only
strips the standalone ifup/ifdown commands).
Assisted-by: GitHubCopilot:claude-opus-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
cmd_ifconfig() returns early for argc <= 2, so by the time the argument
loop is reached argc > 2 always holds. Remove the dead condition and
unindent the loop.
No functional change.
Assisted-by: GitHubCopilot:claude-opus-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
cmd_ifconfig() pushed every setting to the device on each invocation,
whether or not the command line carried it, and stopped parsing as soon
as it saw "mtu". So a command meant to touch one thing quietly rewrote
the rest of the interface configuration:
ifconfig eth0 hw 00:11:22:33:44:55 # clears the address, netmask,
# gateway and resolver, and kicks
# off a DHCP request
ifconfig eth0 mtu 1500 # same, plus the default route
ifconfig eth0 mtu 1500 dns 8.8.8.8 # dns silently ignored
In detail:
- the address was written unconditionally. For IPv6 that read the
uninitialized addr6 off the stack and pushed whatever it held into
the device; for IPv4 it forced 0.0.0.0.
- the netmask fell back to a hard-coded 255.255.255.0, or to
ffff:ffff:ffff:ffff:: for IPv6.
- the IPv4 gateway was always written, unlike the IPv6 one, so it fell
back to INADDR_ANY.
- the resolver fell back to that gateway, i.e. to 0.0.0.0.
- the DHCP client was started whenever gip was left at zero instead of
when "dhcp" was asked for, and gip only becomes non-zero when an
address or a gateway is parsed.
- the "mtu" branch returned as soon as netlib_set_mtu() succeeded, so
every argument behind it was dropped.
Write each setting only when the user provided it, or when an address
is being assigned and the setting belongs to it. Keeping the address
case is deliberate: a freshly assigned address still needs a mask and
a route, so dropping the derived netmask and "x.x.x.1" gateway there
would be a regression of its own. DHCP now triggers on the "dhcp"
keyword, which is the only way to request it. And with nothing left
to clobber, the "mtu" branch no longer has to bail out early.
This is how ifconfig behaves elsewhere: Linux net-tools walks the
argument vector in a single loop where every keyword handler ends in
"continue" and none of them returns, so each keyword is an independent,
idempotent operation applied in the order it was written.
As a side effect mip is now only read along the paths that assign it,
since the IPv4 gateway fallback only derives an address from mip when
gip came from hostip.
Assisted-by: GitHubCopilot:claude-opus-5
Signed-off-by: wangjianyu3 <wangjianyu3@xiaomi.com>
test_strcpy(), test_strncpy() and test_stpcpy() applied the same offset
to the source and to the destination, so both pointers always shared the
same word congruence. Architecture optimized copy routines take a
different code path when the two offsets differ: a byte prologue to
align the destination, then either a byte fallback or a shift-merge loop
that recombines two source words per store. None of that was reached by
the test.
Vary the source and destination offsets independently over 0..7 in those
three tests, so both the equal congruence (aligned word copy) and the
unequal congruence (shift-merge) paths are covered, and report both
offsets on failure so a regression points at the offending combination.
Also drop the ARCH_TOOLCHAIN_GNU dependency from TESTING_ARCH_LIBC. The
test only uses standard C string functions and perf_gettime(), with no
GNU specific construct, so it builds with non GNU toolchains such as
TASKING as well.
Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).
Dropping the ARCH_TOOLCHAIN_GNU dependency only widens the set of
toolchains that may select the test, no existing configuration changes.
Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y. strcpy, strncpy and
stpcpy report PASSED for all 64 offset combinations, and
"arch_libc_test Passed".
Assisted-by: Claude:claude-opus-5
Signed-off-by: zhangyuan29 <zhangyuan29@xiaomi.com>
The adjacent overlap case in test_memmove() placed the source at a fixed
g_buf1 + align + 64 and the destination one size further, so the
destination tail reached align + 64 + 2 * size. g_buf1 is only
TEST_BUF_SIZE + MAX_ALIGN (528) bytes, so the larger swept sizes ran off
the end: align=0 with size=255 writes up to offset 573, that is 46 bytes
past the object. AddressSanitizer aborted arch_libctest with a
global-buffer-overflow.
Start the adjacent layout at g_buf1 + align instead. The tail then
reaches align + 2 * size, which is at most 7 + 2 * 257 = 521 and stays
inside g_buf1 for every alignment and boundary size that is swept, while
still keeping source and destination exactly adjacent.
Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).
Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y. memmove reports PASSED
with no sanitizer report, and "arch_libc_test Passed".
Assisted-by: Claude:claude-opus-5
Signed-off-by: dengwenqi <dengwenqi@xiaomi.com>
Add test_strchrnul() and speed_strchrnul(), selected by the new
CONFIG_TESTING_ARCH_LIBC_STRCHRNUL option, covering the hit, miss and
NUL cases.
Sweep alignment 0..7 and the boundary sizes {0, 1, 7, 8, 9, 15, 16, 17,
31, 32, 33, 63, 64, 65, 127, 128, 129, 255, 256, 257} in the scan
function tests (memcmp, memchr, strlen, strcmp, strchr, strncmp,
strnlen, strrchr) and in memmove. Those sizes sit on the 8 and 16 byte
chunk edges and on the sub-word tails, so vectorized (NEON/MVE) and
word-at-a-time implementations are stressed exactly at their alignment
and size boundaries instead of only at "nice" lengths. memmove is
additionally exercised across four overlap layouts: forward, backward,
contained and adjacent.
Impact: test only, selected by CONFIG_TESTING_ARCH_LIBC (default n).
Testing: built and ran qemu-armv7a:nsh (Cortex-A7, generic C
implementation) and sim:nsh on Linux x86_64 (Ubuntu 24.04, gcc 13.3.0)
with CONFIG_TESTING_ARCH_LIBC=y. All 16 enabled functions report
PASSED and "arch_libc_test Passed". These tests pass against the
generic C routines, which establishes the correctness baseline before
architecture optimized assembly is introduced.
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
The arch_libc test only covered strcpy, so the architecture optimized
implementations of the remaining string and memory routines were never
exercised by the test suite.
Extend the test to also cover memcpy, memmove, memset, memcmp, memchr,
strlen, strcmp, strchr, strncmp, strnlen, strncpy, stpcpy, strcat and
strrchr:
* Every function gets a correctness test that sweeps the buffer
alignment and the transfer size and compares the result against the
expected value.
* Every function gets a speed test that reports the average cycle count
measured with perf_gettime().
* Every individual test is selected by its own
CONFIG_TESTING_ARCH_LIBC_<FUNC> option (default y), so a target can
drop the ones it does not need.
Impact: test only. Nothing is built unless CONFIG_TESTING_ARCH_LIBC
(default n) is selected, so no existing board configuration changes.
Testing: built and ran sim:nsh on Linux x86_64 (Ubuntu 24.04,
gcc 13.3.0) with CONFIG_TESTING_ARCH_LIBC=y. All 15 enabled functions
report PASSED and "arch_libc_test Passed".
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
This companion change updates the nuttx-apps CMake build to use
NuttX’s NUTTX_DIR and NUTTX_BINARY_DIR instead of CMAKE_SOURCE_DIR
and CMAKE_BINARY_DIR, which incorrectly refer to the outermost project
when NuttX is embedded via add_subdirectory(). Since apps/ is itself
included from NuttX’s top-level CMakeLists.txt, these variables were
effectively being used as references to NuttX’s root and inherited
the same bug fixed in the matching NuttX change for #19697. All
self-referencing uses are replaced while intentionally preserving
standalone projects and unrelated custom variables or hardcoded paths.
The change affects only the CMake build system, preserves normal
standalone behavior, and was tested with sim:nsh both standalone and
embedded, with apps such as hello and ostest successfully built and
available in NSH.
Fixes#19697.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Alan Carvalho de Assis <acassis@gmail.com>