Skip to content

fix(build): restore the low-order Ed25519 suite that #93 removed - #127

Merged
srpatcha merged 2 commits into
embeddedos-org:masterfrom
Kartikey1306:fix/restore-loworder-test-registration
Sep 8, 2026
Merged

srpatcha merged 2 commits into
embeddedos-org:masterfrom
Kartikey1306:fix/restore-loworder-test-registration

Conversation

@Kartikey1306

@Kartikey1306 Kartikey1306 commented Sep 2, 2026 •

Copy link
Copy Markdown
Contributor

CI — eos and Python Unit Tests are both red on master (769191a), and both fail for the same single reason:

these suites exist in tests/ but no add_executable() in tests/CMakeLists.txt
builds them, so they never run: ['test_crypto_ed25519_loworder.c']

What happened

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt") removed all four lines that build and register test_crypto_ed25519_loworder:

-# --- test_crypto_ed25519_loworder: reject keys outside the prime-order subgroup ---
-add_executable(test_crypto_ed25519_loworder test_crypto_ed25519_loworder.c)
-target_link_libraries(test_crypto_ed25519_loworder PRIVATE eos_crypto)
-add_test(NAME test_crypto_ed25519_loworder COMMAND test_crypto_ed25519_loworder)

It was not a duplicate. There was exactly one registration before that commit:

$ git show 3aa8644^:tests/CMakeLists.txt | grep -c 'add_executable(test_crypto_ed25519_loworder'
1

Why it matters

What stopped running is the suite guarding #101 — Ed25519 rejecting low-order public keys. A low-order key makes every term of the verification equation collapse to the identity regardless of the message, so a signature of all zeros verifies against any content at all.

The fix itself is still in services/crypto/src/ed25519_verify.c and still correct.

Correction (thanks @srpatcha). An earlier version of this paragraph said "nothing has been checking it since #93 merged". That is overstated, and the overstatement undersells what the restore buys. Since #120, test_pkg_trust_anchor.c:165 test_low_order_keys_are_refused_as_anchors has covered ed25519_public_key_is_usable(), so the anchor-configuration path has been guarded. What went unguarded is ed25519_verify()'s own rejection and the forgeable-message sweep.

And that substitute is host-only: test_pkg_trust_anchor sits inside if(NOT CMAKE_CROSSCOMPILING) because eos_eapp is a host-build library, so on Cross-compile ARM Cortex-M4 and Cross-compile ARM64 kernel nothing has checked low-order key rejection since 3aa8644. The suite restored here links only eos_crypto and is unconditional, so it closes that gap too - the part with no substitute. The .c file stayed in the tree the whole time, which is what made it invisible: a suite that is present but unbuilt looks exactly like a suite that passes.

The fix

Restored verbatim from 3aa8644^, in its original position before test_crypto_sha512. No other file changes.

Worth noting: the guard that caught this is tests/unit/test_cmake_test_registration.py, added recently. This is the first thing it found, and it is precisely the class it was written for.

Validation

master this PR
test_cmake_test_registration.py 1 failed, 4 passed 5 passed
pytest tests/ 14 passed, 1 failed 15 passed
ctest suites built 38 39
ctest — 39/39 passed
test_crypto_ed25519_loworder not built 5/5 tests passed

CI on this head (updated)

The paragraph above describes master at 769191a; master is now feee272. On the current head the two checks this PR is about - Run Python tests and Build & Test (Linux x86_64) - are green.

Two others are red, and neither is caused by this PR: Cross-compile ARM64 kernel and Full-stack integration summary. Both are the eBoot master build failure reached through .github/workflows/eos-simulation.yml. eBoot master does not compile - no member named 'reserved' in 'eos_image_header_t', plus a duplicate point_is_identity() and a test_ed25519.c missing the data its tests iterate. Repair opened as eBoot#97; Full-stack integration summary fails downstream of it via needs:.

@srpatcha srpatcha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review — eos#127 "fix(build): restore the low-order Ed25519 suite that #93 removed"

head: 2a26424 author: Kartikey1306 ci: pass

Verdict: Clean. Five lines, restored verbatim to their original position, and the load-
bearing claim is checkable and checks out: git show 3aa8644^:tests/CMakeLists.txt | grep -c 'add_executable(test_crypto_ed25519_loworder ' returns 1, so it was not a duplicate.
Of the four open PRs adding this registration, this is the one to take.

Two things beyond the diff, one of which nobody has raised: 3aa8644 is worse than this PR
says, and there is a second loss from the same commit still sitting in CHANGELOG.md.

Findings

# Severity File:line Finding Recommended fix
1 High CHANGELOG.md:6 (not this diff — 3aa8644) #93's diagnosis was false in both directions, and it is still doing damage in a second file. This PR shows the block it deleted was not a duplicate. The duplicates it claimed to be removing did not exist either: at 3aa8644^, grep -c 'add_executable(test_crypto_aes ' is 1 and grep -c 'add_executable(test_crypto_sha512 ' is 1. git show --numstat 3aa8644 is two files — CHANGELOG.md 1/1 and tests/CMakeLists.txt 0/5 — and the only thing removed from the build file was the Ed25519 block. So the add_executable cannot create target "test_crypto_aes" error quoted in that commit message cannot have come from this file at that commit. The CHANGELOG hunk is the part still unfixed: 3aa8644 overwrote the Unreleased / Fixed entry for eos_queue_send / eos_queue_receive — "A full send/recv waiter table now returns EOS_KERN_NO_MEMORY instead of blocking a task that can never be woken" — replacing it with a "Build:" entry describing the configure failure that did not happen. A real fix lost its changelog line, and a fictitious one gained one. (The test for that queue fix is test_queue_send_waiter_overflow, which #123 is separately trying to switch back on — same defect, three places.) Second commit on this branch, or its own PR: restore the eos_queue_send / eos_queue_receive line at CHANGELOG.md:6 and delete the "Build:" entry above it. Then re-read the rest of 3aa8644's CHANGELOG context — a commit this wrong about its own subject should not be assumed right anywhere else.
2 Low tests/CMakeLists.txt:93-97 Four open PRs add this registration, at two positions: this one restores it after test_crypto_rsa; #125, #118 and #126 insert it after test_hal_stm32f4; the comments on #126 name a #128 as well. At most one merges cleanly. Take this one and drop the hunk from the other three. It is the original position, it keeps the test_crypto_* suites grouped, and it is the position #122 is already written against — #122's tests/CMakeLists.txt hunk carries add_executable(test_crypto_ed25519_loworder …) as its @@ -95 context line.

The diff itself has nothing wrong with it. It restores four lines and a comment verbatim,
touches no other file, links eos_crypto where ed25519_verify lives, and the validation
table in the body distinguishes before and after per check rather than asserting a bare
"tested". The note that Simulation Test and EoSim Sanity are also red on master and are
not addressed here is the right kind of scope statement — it says what was left out
instead of implying coverage. All 26 required checks pass in checks.txt for this head.

Architecture conformance

§21 — conforms. Test build configuration only, inside tests/, which is where
.ai/architect.md's target shape puts the suites. No dependency direction, tier or public
header touched.

§14.1 and .ai/security.md — this is squarely the point of both. "Reject invalid and
low-order public keys explicitly; do not assume the library did." The suite being restored
is the one proving ed25519_verify refuses a key outside the prime-order subgroup, the
all-zero encoding included — and eos_pkg.c shipped static const uint8_t eos_pkg_public_key[32] = {0}, which is one of those eight points. .ai/security.md:
"ctest exits 0 when it finds no tests at all. A security suite that silently collected
nothing is a failed check reported as green." A registered-then-deleted suite is the same
failure with an extra step, and it went unnoticed for the whole window because the .c
file never left the tree.

§28 Status, evidence and claims — worth stating plainly. Between 3aa8644 merging and
this landing, "Ed25519 rejects low-order public keys" was an Implemented claim whose
"code and functional tests" evidence did not execute. The guard in
tests/unit/test_cmake_test_registration.py is what turned that back into a visible fact,
which is the mechanism §28 needs and this repository has been building.

No master-design proposal from this PR.

Proposed changes

  1. Merge as is.
  2. Follow-up commit restoring the eos_queue_send / eos_queue_receive entry at
    CHANGELOG.md:6 and removing the incorrect "Build:" entry (finding 1).
  3. Close #125 and drop the same hunk from #118 and #126 (finding 2).

Not checked

  • I did not build or run ctest. The 5/5, 39/39, 15 passed figures are the author's;
    checks.txt is green for this head and is the independent evidence.
  • I verified the history claims directly in the local clone (git show 3aa8644,
    git show --numstat 3aa8644, git show 3aa8644^:tests/CMakeLists.txt). I did not
    read tests/test_crypto_ed25519_loworder.c, so I cannot say whether its five cases
    actually cover the eight low-order points its header describes — only that they now run.
  • I did not audit the rest of 3aa8644's effect beyond the two files it changed, and I did
    not look for other commits from the same cleanup series.
  • Simulation Test and EoSim Sanity, correctly declared out of scope here. EoSim was
    skipped by this run's sync step as a dirty working tree, so I could not look at them
    either.

Automated architecture review of 2a26424c9a9b — scheduled, model claude-opus-5, checked against the EmbeddedOS Master Design v2.0. Advisory only: this reviewer never approves, requests changes, or merges. Reply here to discuss or push back — a wrong finding is a bug worth reporting.

Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…ARM file

Answers the review on embeddedos-org#126.

Finding 1 (Medium) -- the head carried an undisclosed second change, the
test_crypto_ed25519_loworder registration, which contradicted this PR's own
2026-09-02 comment ("this PR changes one file ... and does not touch tests/").
It was also the fourth open copy of that registration, at a different position
from embeddedos-org#127's. Dropped: the branch is rebased to the toolchain change alone, and
embeddedos-org#127 is the one to take for the registration -- it restores the four lines to
their original position, keeps the crypto suites grouped, and is the position
embeddedos-org#122 is already written against.

Finding 2 (Medium) -- nothing would catch this coming back. Every ARM
toolchain here sets CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY and no
cross build links an executable, so `Cross-compile ARM Cortex-M4` passed
identically with the duplicate present and absent; it passed on the unfixed
commit. The defect surfaced by accident when embeddedos-org#118 briefly put cmd/eos into the
cross build, and embeddedos-org#118 has since gated that to host builds -- removing the
accident too.

Adds tests/unit/test_toolchain_specs.py, modelled on
test_cmake_test_registration.py: parse every toolchains/*.cmake, extract each
-specs=<file>, and fail if one appears in more than one variable that reaches
a link line (CMake links through the compiler driver, so C/CXX flags do). A
second test asserts specs are declared on the linker flags rather than the
compiler flags, which is the arrangement that turns into a duplicate the
moment someone adds the linker entry that looks missing. No ARM toolchain
needed; runs in the existing pytest job.

Finding 3 (Low) -- arm-none-eabi-r5.cmake put --specs=nosys.specs in
CMAKE_C_FLAGS_INIT with nothing in CMAKE_EXE_LINKER_FLAGS_INIT. Not broken
today, and one added linker -specs= away from the same failure. Moved, so all
three ARM bare-metal toolchains state it the same way and the new guard is
green across all of them.

Verified:
  pytest tests/unit/test_toolchain_specs.py    2 passed
  pytest tests/                                all pass
  discrimination: reintroducing the duplicate in arm-cortex-m4.cmake gives
      arm-cortex-m4.cmake: -specs=nosys.specs is set in CMAKE_C_FLAGS_INIT
      and CMAKE_EXE_LINKER_FLAGS_INIT, so it reaches the link line more than
      once
    on both tests, so the guard catches the original defect rather than
    restating the fix.
  No ARM toolchain on this machine, so the cross link itself is NOT verified
  here; that remains CI's to show.

Refs embeddedos-org#126
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…ARM file

Answers the review on embeddedos-org#126.

Finding 1 (Medium) -- the head carried an undisclosed second change, the
test_crypto_ed25519_loworder registration, which contradicted this PR's own
2026-09-02 comment ("this PR changes one file ... and does not touch tests/").
It was also the fourth open copy of that registration, at a different position
from embeddedos-org#127's. Dropped: the branch is rebased to the toolchain change alone, and
embeddedos-org#127 is the one to take for the registration -- it restores the four lines to
their original position, keeps the crypto suites grouped, and is the position
embeddedos-org#122 is already written against.

Finding 2 (Medium) -- nothing would catch this coming back. Every ARM
toolchain here sets CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY and no
cross build links an executable, so `Cross-compile ARM Cortex-M4` passed
identically with the duplicate present and absent; it passed on the unfixed
commit. The defect surfaced by accident when embeddedos-org#118 briefly put cmd/eos into the
cross build, and embeddedos-org#118 has since gated that to host builds -- removing the
accident too.

Adds tests/unit/test_toolchain_specs.py, modelled on
test_cmake_test_registration.py: parse every toolchains/*.cmake, extract each
-specs=<file>, and fail if one appears in more than one variable that reaches
a link line (CMake links through the compiler driver, so C/CXX flags do). A
second test asserts specs are declared on the linker flags rather than the
compiler flags, which is the arrangement that turns into a duplicate the
moment someone adds the linker entry that looks missing. No ARM toolchain
needed; runs in the existing pytest job.

Finding 3 (Low) -- arm-none-eabi-r5.cmake put --specs=nosys.specs in
CMAKE_C_FLAGS_INIT with nothing in CMAKE_EXE_LINKER_FLAGS_INIT. Not broken
today, and one added linker -specs= away from the same failure. Moved, so all
three ARM bare-metal toolchains state it the same way and the new guard is
green across all of them.

Verified:
  pytest tests/unit/test_toolchain_specs.py    2 passed
  pytest tests/                                all pass
  discrimination: reintroducing the duplicate in arm-cortex-m4.cmake gives
      arm-cortex-m4.cmake: -specs=nosys.specs is set in CMAKE_C_FLAGS_INIT
      and CMAKE_EXE_LINKER_FLAGS_INIT, so it reaches the link line more than
      once
    on both tests, so the guard catches the original defect rather than
    restating the fix.
  No ARM toolchain on this machine, so the cross link itself is NOT verified
  here; that remains CI's to show.

Refs embeddedos-org#126
…removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (embeddedos-org#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards embeddedos-org#101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since embeddedos-org#93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)
@Kartikey1306
Kartikey1306 force-pushed the fix/restore-loworder-test-registration branch from 2a26424 to 13a8ffa Compare September 3, 2026 10:05
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
Retitles and reduces this PR to what it still does, per the review.

Both repairs it was opened for have landed on master by other routes:
net/src/net_posix.c is byte-identical to origin/master, and master's default
configuration builds clean. Against current master the branch's only real
content was this one line -- and merging it as it stood would have *reverted*
646 lines that landed since, including embeddedos-org#120's pkg trust anchor,
tests/test_pkg_trust_anchor.c and tests/unit/test_cmake_test_registration.py.
Reset to master and re-applied the one surviving change rather than leave a
stale branch that still looks mergeable.

The include is genuinely unused: tests/test_net.c has zero assert() calls and
uses its own CHECK() macro at 22 call sites. Worth correcting the record, per
finding 2: embeddedos-org#124 added it one commit ago as "the missing assert.h include that
broke master", and the file had no assert() at embeddedos-org#124's parent either. What
fixed that link error was the assert() -> CHECK() conversion already on
master; the include was never load-bearing, so removing it changes nothing
except the next reader's understanding.

Stacked on embeddedos-org#127, which restores the test_crypto_ed25519_loworder registration
master is currently red on.

Verified:
  pytest tests/    15 passed
  ctest            39/39 PASS

Refs embeddedos-org#116, embeddedos-org#124
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…-back structural

Answers the review on embeddedos-org#118.

Finding 1 (Medium) -- systems/src/firmware.c includes eos/backend.h and calls
eos_backend_find(), but eos_systems linked only eos_core and eos_toolchains;
the dependency was satisfied at each consumer instead, so every future user of
eos_systems had to rediscover it. eos_systems now declares it, and
eos_backends comes off the test_firmware line.

Finding 2 (Medium) -- backends/include was added to the global
include_directories(), putting a build-orchestration header on the include
path of every target in the tree, eos_kernel and eos_hal included. Nothing
then stops kernel or HAL code acquiring a dependency master design 5.1
forbids. Taken the reviewer's version rather than a narrower one: the header
moves to include/eos/backend.h -- replacing the stale copy that caused the
original shadowing -- backends/include is gone, and both the global entry and
backends' own target_include_directories with it. include/ stays the single
public-header contract, and the shadow is now structurally impossible rather
than merely resolved.

Finding 3 (Medium) -- the pop-back fired only for seven literal keys, which
made it a second authority on what a `system` child key is, kept in step with
`case SEC_SYSTEM:` by hand and by nothing else. Add an eighth key there and
the swallow returns for it silently. Now structural: every descendant of a
system sub-section sits at indent >= 2 and list items `continue` earlier, so
indent <= 1 is exactly the condition. Same behaviour on every config under
examples/, and it cannot drift.

Finding 4 (Low) -- applied the same structural guard to
SEC_TOOLCHAIN_LINUX/SEC_TOOLCHAIN_RTOS, which popped back for one sibling and
none respectively. A `target:` at toolchain's own indent written after the
linux:/rtos: blocks landed in toolchain.rtos_target instead of
toolchain.target. Not reachable by any config in examples/ today, which is
why it had not bitten.

Also dropped the test_crypto_ed25519_loworder registration hunk; this branch
is stacked on embeddedos-org#127, which is the copy to keep.

NOT fixed, and confirmed while testing: finding 5, `system.entry` is parsed by
nothing -- no branch in `case SEC_SYSTEM:` and no field on EosSystemConfig --
while six shipped configs set it. examples/multicore-amp/eos.yaml sets
`entry: main.c` and `eos info` reports nothing for it. Same failure mode, but
it needs a new struct field and a decision about what consumes it, so it is a
follow-up rather than something to fold in here.

Verified:
  ctest                    40/40 PASS
  pytest tests/            15 passed
  eos info, in examples/industrial-gateway:
      kind: hybrid / kernel provider: buildroot / RTOS targets (1)
    which is the parser fix visible end to end -- RTOS targets reads 0 before
    it. examples/multicore-amp reports 0, correctly: that config has no rtos
    list at all.

Refs embeddedos-org#118
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
… the guard checks

Reduces this PR to what it still contributes, and answers the review.

The four registrations it opened with have landed on master by other routes --
test_mpu_validate, test_e2e, test_hal_stm32f4 and the registration guard are
all there. Merging the branch as it stood would have reverted 505 lines that
landed since, including embeddedos-org#120's pkg trust anchor. Reset onto embeddedos-org#127 and kept only
what is still true.

Finding 1 (Medium) -- test_net_mock was registered with no
target_link_libraries and includes no EmbeddedOS header. Its mock_net_*
functions are called directly rather than standing in for eos_net_*, so no
change to net/ can make it fail: 614 lines asserting a mock against itself.
Registering it added a green ctest entry covering nothing, which is precisely
the failure mode the guard in this same commit exists to surface -- and the
guard's own NOT_BUILT policy is where it belongs. Listed there with the reason,
including what would make it worth building: driving the real eos_net_* API
against the mock transport, which is what its docblock already claims it does.

Finding 2 (Low) -- documented the guard's limit where the policy is defined.
It checks that every suite is built and registered with ctest. It does not
check that a suite reaches the code it is named after, and a file that links
nothing satisfies it completely. Worth stating next to NOT_BUILT, because that
is the list someone reaches for when the guard fires.

Findings 3 and 4 are moot at this head: the test_mpu_validate comment and
test_net_mock's vestigial assert.h include both belonged to hunks that are
either upstream or removed here.

Verified:
  pytest tests/     15 passed
  ctest             37/37 PASS (was 38 with test_net_mock, which tested nothing)

Refs embeddedos-org#114
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
… the guard checks

Reduces this PR to what it still contributes, and answers the review.

The four registrations it opened with have landed on master by other routes --
test_mpu_validate, test_e2e, test_hal_stm32f4 and the registration guard are
all there. Merging the branch as it stood would have reverted 505 lines that
landed since, including embeddedos-org#120's pkg trust anchor. Reset onto embeddedos-org#127 and kept only
what is still true.

Finding 1 (Medium) -- test_net_mock was registered with no
target_link_libraries and includes no EmbeddedOS header. Its mock_net_*
functions are called directly rather than standing in for eos_net_*, so no
change to net/ can make it fail: 614 lines asserting a mock against itself.
Registering it added a green ctest entry covering nothing, which is precisely
the failure mode the guard in this same commit exists to surface -- and the
guard's own NOT_BUILT policy is where it belongs. Listed there with the reason,
including what would make it worth building: driving the real eos_net_* API
against the mock transport, which is what its docblock already claims it does.

Finding 2 (Low) -- documented the guard's limit where the policy is defined.
It checks that every suite is built and registered with ctest. It does not
check that a suite reaches the code it is named after, and a file that links
nothing satisfies it completely. Worth stating next to NOT_BUILT, because that
is the list someone reaches for when the guard fires.

Findings 3 and 4 are moot at this head: the test_mpu_validate comment and
test_net_mock's vestigial assert.h include both belonged to hunks that are
either upstream or removed here.

Verified:
  pytest tests/     15 passed
  ctest             38/38 PASS
    (test_net_mock is no longer among them; the count is unchanged from
    master because embeddedos-org#127, which this is stacked on, adds
    test_crypto_ed25519_loworder -- a suite that does exercise real code.)

Refs embeddedos-org#114
@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Closed my duplicate of this (#128) in favour of this one — same restore, and
this is the minimal version.

One thing from it worth folding in if you want it: tests/CMakeLists.txt
already carries this exact comment on a neighbouring target —

# --- test_mpu_validate: MPU region programmability ---
# Landed with the fix it guards (#109) and lost its registration to a merge,
# so the six cases stopped being compiled while the file stayed in the tree.

so a matching note here would make the pattern visible in the file rather than
only in the PR history. Suggested wording:

# Registered by #101 alongside the fix it guards, then removed by #93, whose
# base predated it: the target looked like one more duplicate to delete. It is
# the regression test for the #98 forgery -- an all-zero key accepting a
# package -- so between #93 merging and this, nothing checked that fix.

Entirely optional; the restore is correct without it.

Evidence I gathered on the duplicate, in case it is useful here: the target was
the only non-duplicate in #93's diff, and it is load-bearing — disabling the
subgroup check in services/crypto/src/ed25519_verify.c makes
test_all_zero_key_is_rejected fail, so the suite genuinely guards the #98 fix
rather than merely existing.

@srpatcha srpatcha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review — eos#127 "fix(build): restore the low-order Ed25519 suite that #93 removed"

head: 13a8ffa author: Kartikey1306 ci: fail (2 of 28 — both inherited from eBoot master, neither caused by this PR)

Verdict: Clean, still needed, and now verified by execution rather than read. This head is a rebase — the PR's own diff against its merge-base is byte-identical to the 2a26424c I reviewed earlier (git diff <merge-base> refs/pull/127/head → tests/CMakeLists.txt | 5 +++++, nothing else). The five lines are correct and the suite they switch back on is load-bearing. Merge it.

I built it and ran it, so the author's validation table is no longer just a claim:

git worktree add --detach <tmp> refs/pull/127/head
cmake -S . -B bt -DEOS_BUILD_TESTS=ON     # rc=0
cmake --build bt -j4                      # rc=0
ctest --test-dir bt --no-tests=error      # 39/39 passed
ctest --test-dir bt -R test_crypto_ed25519_loworder --no-tests=error
                                          # Test #15 ... Passed

The 38 → 39 suite count checks out exactly: tests/CMakeLists.txt registers 37 unconditional add_test on origin/master and 38 on this head, plus the one add_test guarded by if(NOT CMAKE_CROSSCOMPILING) at tests/CMakeLists.txt:221.

The PR is still required: origin/master (feee272) ships tests/test_crypto_ed25519_loworder.c with no add_executable for it.

Findings

# Severity File:line Finding Recommended fix
1 Low PR body, "Why it matters" "nothing has been checking it since #93 merged" is overstated, and the overstatement is worth correcting because it understates what the restore actually buys. I mutated ed25519_key_has_prime_order() (services/crypto/src/ed25519_verify.c:93) to return 1 and rebuilt: two suites fail, not one — test_crypto_ed25519_loworder (#15, restored here) at tests/test_crypto_ed25519_loworder.c:112 test_all_zero_key_is_rejected, and test_pkg_trust_anchor (#33, already on master since #120) at tests/test_pkg_trust_anchor.c:172 test_low_order_keys_are_refused_as_anchors. So since #120 landed, the anchor-configuration path has been guarded. What went unguarded is ed25519_verify()'s own rejection and the forgeable-message sweep. One sentence in the body: since #120, test_pkg_trust_anchor has covered ed25519_public_key_is_usable(); what this restores is the ed25519_verify() path and the message sweep — and the cross-compiled coverage, which is the part that has no substitute (see finding 2).
2 Low tests/CMakeLists.txt:218-222 (master, not this diff) The substitute coverage identified above is host-only. test_pkg_trust_anchor sits inside if(NOT CMAKE_CROSSCOMPILING) because eos_eapp is a host-build library, so on Cross-compile ARM Cortex-M4 and Cross-compile ARM64 kernel nothing has checked low-order key rejection since 3aa8644. The suite this PR restores links only eos_crypto and is unconditional, so it closes that gap too. Not a defect in this PR — an argument for it, and a note for whoever reads the two suites as interchangeable. No change here. Worth an issue: eos_pkg's trust-anchor behaviour has no cross-target test at all, which .ai/security.md ("keep target-specific hardware security adapters behind stable interfaces") and §22's Reference-tier requirements both eventually need.
3 Low PR body vs. checks.txt The body's CI narrative describes master at 769191a; master is now feee272 and this head's CI is different. Two checks are red — Cross-compile ARM64 kernel and Full-stack integration summary — and neither is one the body discusses. Both are the inherited eBoot master build failure (eos_image_header_t has no member named 'reserved', eBoot#94) reached through .github/workflows/eos-simulation.yml; Full-stack integration summary fails downstream via needs:. Run Python tests and Build & Test (Linux x86_64) — the two the body is about — are green on this head. Refresh the body's first paragraph and the "Also red on master" section against feee272, or delete them; they now point a reviewer at resolved failures and away from the live ones.

Resolved since my review of 2a26424c

Recorded so nobody re-opens them:

  • Prior finding 1 (High) — fixed. 3aa8644 had overwritten the Unreleased / Fixed entry for eos_queue_send / eos_queue_receive. It is back on origin/master:CHANGELOG.md:10, verbatim, and the fictitious "Build:" entry describing a configure failure that never happened is gone. Restored by #120 (769191a), not by this PR.
  • Prior finding 2 (Low) — half fixed. #128 is closed by its author in favour of this PR. #125, #118, #126 and #122 still carry or are written against this hunk; this PR restores it at its original position after test_crypto_rsa, which remains the position to take.
  • Still open from that review and not caused by anything here: test_queue_send_waiter_overflow (tests/test_kernel.c:159) is a function inside test_kernel.c and is not reached by tests/unit/test_cmake_test_registration.py, which only checks that each .c file has an add_executable. A test function that is compiled but never called is invisible to that guard.

Architecture conformance

§21 — conforms. Test build configuration inside tests/, which is where .ai/architect.md's target shape puts suites. No tier crossed, no dependency direction changed, no public header touched. target_link_libraries(... PRIVATE eos_crypto) links downward to the library that owns ed25519_verify.

§14.1 / .ai/security.md — this is the exact failure mode that file names first. "ctest exits 0 when it finds no tests at all. A security suite that silently collected nothing is a failed check reported as green." A suite whose .c file stays in the tree while its add_executable is deleted is that failure with one extra step of camouflage, and the mutation run above is the evidence that it was guarding something real rather than merely existing. "Reject invalid and low-order public keys explicitly; do not assume the library did" is the requirement; test #15 is its proof.

§28 — restores an Implemented claim to having evidence. Between 3aa8644 and this PR, "Ed25519 rejects low-order public keys" was asserted at §28's Implemented level, whose required evidence is "code and functional tests", while one of its two functional tests did not execute on any target and neither executed on cross targets (finding 2).

No master-design proposal from this PR.

Proposed changes

  1. Merge as is. The diff is right and now independently executed; findings 1 and 3 are body text, finding 2 is a follow-up issue.
  2. Correct the two body claims (findings 1, 3) — no code change, no re-run needed.
  3. Drop the same hunk from #125/#118/#126 and rebase #122, so the four-way conflict at this anchor resolves onto this PR.
  4. Open an issue for cross-target eos_pkg trust-anchor coverage (finding 2).
  5. Separately: test_cmake_test_registration.py catches an unbuilt file but not an uncalled function — test_queue_send_waiter_overflow is the live example. Worth extending, out of scope here.

I opened no fix PR: nothing here is rated High, and the two body corrections are the author's text to change.

Not checked

  • Host build only, x86_64 Linux, GCC as configured by default. I did not cross-compile, so finding 2's claim that the restored suite will build under CMAKE_CROSSCOMPILING is read from tests/CMakeLists.txt (unconditional, links only eos_crypto) — not observed.
  • The 38 → 39 figure was confirmed by counting add_test in both trees and by ctest reporting 39 on this head. I did not build origin/master, so "38 on master" is arithmetic plus a grep, not a run.
  • The mutation test proves the two suites detect the removal of ed25519_key_has_prime_order's body. It says nothing about whether test_crypto_ed25519_loworder.c's cases cover all eight low-order encodings, or whether the identity-rejection branch (ed25519_point_is_identity, ed25519_verify.c:81) is covered independently — I mutated the whole function, not each branch.
  • I did not audit 3aa8644 beyond the two files it touched, and did not look for other commits in that cleanup series.
  • The two red checks are attributed to eBoot#94 on the strength of the eos#126 investigation in this same report series; I did not re-run the eBoot build for this head. EoSim was skipped by this run's sync as a dirty tree, so its own red status is unexamined.
  • tests/test_crypto_ed25519_loworder.c itself was read only at line 112, where the mutant failed.

Automated architecture review of 13a8ffa2d19f — scheduled, model claude-opus-5, checked against the EmbeddedOS Master Design v2.0. Advisory only: this reviewer never approves, requests changes, or merges. Reply here to discuss or push back — a wrong finding is a bug worth reporting.

Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…orrowed hunk

Addresses findings 1-4 of the review on 126bd0c.

Finding 4 first, because it is the one that was making the branch look better
than it is. The test_crypto_ed25519_loworder registration was carried here
from embeddedos-org#127. It made Run Python tests green on a branch that has nothing to do
with it, contradicted this PR's own 2026-09-02 comment, and sat at a different
anchor to embeddedos-org#127's, so it would have conflicted. Dropped. That check is red
again until embeddedos-org#127 lands, which is the honest state -- folding a master repair
into a toolchain PR to get a green tick is how a red master becomes everyone's
problem to route around.

Finding 1: the parser recognised one CMake idiom, set(VAR "literal"). It could
not see string(APPEND ...), an unquoted set(), or -- the one that matters here
-- a spec arriving through ${CPU_FLAGS}, which is how both Cortex-M4 toolchains
build their flag strings. Putting -specs= there would have reached both
CMAKE_C_FLAGS_INIT and CMAKE_EXE_LINKER_FLAGS_INIT while every check passed.
The file is now walked in order with a variable table, ${...} resolved against
what is in force at that point, and string(APPEND ...) applied. Two tests pin
those two bypasses.

Finding 3: CMAKE_ASM_FLAGS_INIT added to LINK_REACHING. CMake picks a target's
linker language from its sources, so an executable built only from .S files
links with the ASM flags. Both Cortex-M4 files set it from ${CPU_FLAGS}. The
current tree still passes; no toolchain names a spec there today.

Finding 2: the .yaml descriptors are scanned too. They are the second
authority on these flags -- toolchains/src/toolchain.c parses them into
EosToolchain.cflags/ldflags, a separate consumer from CMake -- and all five
already keep specs in ldflags, which is a good argument for this change and a
better one for holding both descriptions to one rule.

Found while verifying rather than assuming: my first regex required the closing
paren on the same line, so arm-none-eabi-stm32f4.cmake's three-line
set(CMAKE_EXE_LINKER_FLAGS_INIT ...) was invisible and every check passed
vacuously for that file. That is this test's own failure mode one level up.
Fixed, and pinned by test_a_multi_line_set_is_still_seen and
test_every_arm_toolchain_is_actually_parsed -- the latter asserts the parser
finds specs in all three ARM files, so a parser that silently sees nothing can
no longer pass.

Verified: the parser reports arm-cortex-m4 [nosys, nano], arm-none-eabi-r5
[nosys], arm-none-eabi-stm32f4 [nano, nosys], all on
CMAKE_EXE_LINKER_FLAGS_INIT. Mutations caught: a spec hidden in CPU_FLAGS, and
the original duplicate re-introduced on CMAKE_C_FLAGS_INIT.

7 toolchain tests pass, ctest 38/38.
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…he workflow test itself

Answers the review on embeddedos-org#129. The two diagnoses in the previous commit hold --
there is no v0.1.0 tag and no EoSim release ships a Python artifact -- and
this addresses what the replacement pin did not.

Finding 1 (High) -- `EOSIM_VERSION: "1.5.0"` is a label, not a version.
Re-verified against the remote rather than taken from the review:

  tag      commit    committed    commit subject             pyproject  __init__
  v1.5.0   7dec3460  2026-05-27   "production-ready v1.4.0"    3.0.1      3.0.1
  v3.0.1   b297ec26  2026-05-16   "v3.0.1 — unified ..."       3.0.1      2.0.0

v1.5.0 is the release GitHub marks Latest and the newest tag by commit date,
tags a commit whose own message claims v1.4.0, and installs a dist declaring
3.0.1. A reader of `EOSIM_VERSION: "1.5.0"` would believe eos is simulated
against EoSim 1.5.0. It is not, and nothing in the file would reveal that.

Both workflows now pin `EOSIM_COMMIT` to the full SHA that tag resolves to
today, with the table above in the env block and the date it was checked.
Not v3.0.1 instead: its `__init__.py` says 2.0.0, so it is only marginally
less confusing. A SHA cannot be moved under us; the comment says to go back to
a tag once EoSim's tags mean something.

Finding 3 (Medium) -- `eosim --version` cannot detect a wrong install.
EoSim hardcodes it (`eosim/cli/main.py:49`,
`@click.version_option(version="2.0.0")`), confirmed by reading that file at
v1.5.0, so it prints 2.0.0 from every tag -- which is exactly why the previous
commit could record the version oddity as a curiosity rather than as a failed
pin. All three sites now also assert `importlib.metadata.version("eosim")`
against `EOSIM_EXPECTED_DIST`, which is the only version the commit pin
controls.

Finding 4 (Medium) -- this PR could not be validated by CI and was not: both
workflows were schedule- and dispatch-only, so neither appeared in this head's
checks and all 25 green ones were other workflows. Added
`pull_request: paths:` on both files listing both files, so a change to either
now runs both. `workflow_dispatch` on the fork is not available to me --
GitHub resolves the workflow on the fork's default branch and returns 404 --
so this trigger is the evidence that is actually obtainable, and it applies to
every future change to these files rather than to this one only.

Finding 2 (High) -- after this, nothing in the org exercises a published EoSim
artifact, and none exists. Verified independently: 13 releases whose assets
are promo .mp4 files, a manifest.json and EoSim-guide.pdf; no wheel or sdist
at any tag; `https://pypi.org/pypi/eosim/json` returns 404. Converting to a
clone is right -- a permanent 404 is not a useful red -- but it would also
make the gap green and invisible, which is the "verification whose result is
discarded" shape.

So `published-artifact-watch` runs on every execution, checks the releases and
PyPI, and says out loud that the packaging debt is open. It cannot fail the
workflow, deliberately: it is a tracking signal, not a gate on someone else's
repo. It emits a `::notice::` the day an artifact appears, which is when it
should be deleted and the installs pointed at it. The debt is also stated in
the PR body rather than left implied, and raised against EoSim.

Finding 5 (Low) -- `/tmp/EoSim` was hardcoded in the two jobs that run on
windows-latest and macos-latest, where the Windows job sets no `shell:` and
gets pwsh. Now `${{ runner.temp }}/EoSim` everywhere.

Finding 6 (Low) -- dropped `-e`. Nothing needs an editable install, it is
further from what a developer does in a workflow meant to prove the install
works, and it made the dist-metadata assertion depend on the clone staying
put. Changed at all five sites, not two, so they are consistent.

Also, while in this file: `sanity-gate` had the fail-open shape --
`needs:` five jobs, tested `install-validate` alone, then printed "All EoSim
sanity checks passed". Replaced with the `toJSON(needs)` body, which cannot
fall out of step with `needs:`, and the new job is included in it.

Verified:
  ctest                                       39/39 PASS
  pytest tests/                               15 passed
  both workflows parse, and structurally:
    pull_request paths list this file           both
    EOSIM_VERSION gone from every code line     both
    EOSIM_COMMIT is a full 40-char sha          both
    no /tmp/EoSim literal, no `pip install -e`  both
    every `git clone --filter` has a matching `checkout --detach`
    three importlib.metadata assertions
    sanity-gate needs == every other job in the file
  the SHA resolves: refs/tags/v1.5.0 -> tag object 978f215f -> commit
    7dec3460cba76083b6645eb4a867e39aa2af97df

  NOT RUN: neither workflow. `workflow_dispatch` on the fork returns 404
  because GitHub resolves it on the fork's default branch. The
  `pull_request: paths:` trigger should make both run on this PR for the
  first time -- that will be the first execution either has ever had, and I
  will report what it shows rather than predicting it.

Finding 7 (Low): still stacked on embeddedos-org#127; the tests/CMakeLists.txt hunk goes
when that lands.

Refs embeddedos-org#129
@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Both findings are right and both are now corrected in the body. Verified them rather than taking them on trust:

Finding 1 — overstated claim. Confirmed. tests/test_pkg_trust_anchor.c:165 test_low_order_keys_are_refused_as_anchors is on master and does cover ed25519_public_key_is_usable(), so "nothing has been checking it since #93" was wrong. Body now says what actually went unguarded: ed25519_verify()'s own rejection and the message sweep.

Finding 2 — cross-target gap. Confirmed and it is the stronger half of the argument. test_pkg_trust_anchor sits inside if(NOT CMAKE_CROSSCOMPILING) at tests/CMakeLists.txt:218 because eos_eapp is host-only, so on the two cross-compile legs nothing has checked low-order rejection since 3aa8644. The restored suite links only eos_crypto and is unconditional. I have folded that into the body as the part with no substitute, and will open the issue for cross-target eos_pkg trust-anchor coverage.

Finding 3 — stale CI narrative. Corrected, and I can now name the cause you left as attributed-not-verified. The two red checks are the eBoot master build failure, and eBoot master genuinely does not compile — I reproduced it:

include/eos_image.h:135: error: no member named 'reserved' in 'eos_image_header_t'
include/eos_image.h:142: error: no member named 'reserved' in 'eos_image_header_t'
core/ed25519_verify.c:338: error: redefinition of 'point_is_identity'
tests/unit/test_ed25519.c:263: error: redefinition of 'test_ed25519_identity_key_forgery_rejected'
tests/unit/test_ed25519.c:299: error: use of undeclared identifier 'k_low_order'

Three conflict-free merges — #87 pinned reserved[] while #57 split it into tlv_len/tlv_hash; #86 and #57 each added an identical point_is_identity(); and the test merge kept tests while dropping messages[] and k_low_order[8][32]. My PRs are half of that. Repair is eBoot#97: ctest 21/21, low-order probe 0 accepted / 2048, RFC 8032 vectors still accepted.


On your closing note about test_cmake_test_registration.py catching an unbuilt file but not an uncalled function — that class is live in eBoot too, and worse than the eos instance. test_ed25519.c had test_ed25519_low_order_R_with_a_valid_key_is_not_a_forgery() compiled but never called (the merge kept the function, dropped its run_ call), and tests_run hardcoded to 11 while 12 tests ran, so the suite failed on its own arithmetic. Both fixed in #97, with tests_run now incremented by the macro that runs each test so it cannot drift again.

That suggests the guard's natural extension is per-TEST()-symbol rather than per-file: for these suites, every TEST(x) should have a matching run_x() in main(). Cheap to check with the same parser, and it would have caught both the eBoot case and test_queue_send_waiter_overflow. Happy to write it if you want it as a follow-up.

One correction to your "Not checked" note, offered because it changes what the mutation test proves: you mutated ed25519_key_has_prime_order(). On eBoot that function is now dead code — the verifier calls public_key_is_valid_subgroup() — though on eos the name is still the live one, so your run stands for eos. Flagging only in case the same mutation is reused across repos.

@Kartikey1306

Copy link
Copy Markdown
Contributor Author

Built the guard you suggested at the end of your review: #134.

It closes the half this file could not reach — test_cmake_test_registration.py answers "is this file compiled?", not "is this test called?", and an uncalled test function is valid C that compiles, links, runs its suite and passes having skipped itself.

Run against master it finds exactly one instance, with no false positives, and it is the one you named:

['test_kernel.c::test_queue_send_waiter_overflow']

Calling it, it passes — so the eos_queue_send/eos_queue_receive waiter-table fix was right and simply had nothing executing against it. Good outcome, but it was luck rather than evidence, which is the argument for the guard.

Two details you may want to poke at, since a guard with false positives is worse than none:

  • Occurrences are counted by bare identifier, not name(, so a function passed by address is not reported dead. test_entry in that same file is exactly that case and is correctly ignored.
  • Comments are stripped first, so a name appearing only in prose does not read as a call.

Also fixed in the same commit: test_kernel.c printed a hardcoded (12/12) while thirteen tests sat in the file, so the literal agreed with the omission rather than exposing it. Counted now via a RUN() macro — the same shape eBoot#94 applied to its tests_run for the identical reason.

eBoot has no equivalent of this guard at all, and it is the repo where the class already caused a merge to drop a test. I'd port it once you're happy with the shape here.

Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
Residue from 0957ac4, which dropped the borrowed
test_crypto_ed25519_loworder registration and left the blank line that had
separated it from the block below. It is the only remaining change this PR
makes to tests/CMakeLists.txt, it changes nothing, and it sits at the exact
anchor where embeddedos-org#118, embeddedos-org#119 and embeddedos-org#127 each add the real registration block -- so
it would have conflicted with whichever of those lands first, for no reason
at all.

tests/CMakeLists.txt is now untouched by this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…ARM file

Answers the review on embeddedos-org#126.

Finding 1 (Medium) -- the head carried an undisclosed second change, the
test_crypto_ed25519_loworder registration, which contradicted this PR's own
2026-09-02 comment ("this PR changes one file ... and does not touch tests/").
It was also the fourth open copy of that registration, at a different position
from embeddedos-org#127's. Dropped: the branch is rebased to the toolchain change alone, and
embeddedos-org#127 is the one to take for the registration -- it restores the four lines to
their original position, keeps the crypto suites grouped, and is the position
embeddedos-org#122 is already written against.

Finding 2 (Medium) -- nothing would catch this coming back. Every ARM
toolchain here sets CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY and no
cross build links an executable, so `Cross-compile ARM Cortex-M4` passed
identically with the duplicate present and absent; it passed on the unfixed
commit. The defect surfaced by accident when embeddedos-org#118 briefly put cmd/eos into the
cross build, and embeddedos-org#118 has since gated that to host builds -- removing the
accident too.

Adds tests/unit/test_toolchain_specs.py, modelled on
test_cmake_test_registration.py: parse every toolchains/*.cmake, extract each
-specs=<file>, and fail if one appears in more than one variable that reaches
a link line (CMake links through the compiler driver, so C/CXX flags do). A
second test asserts specs are declared on the linker flags rather than the
compiler flags, which is the arrangement that turns into a duplicate the
moment someone adds the linker entry that looks missing. No ARM toolchain
needed; runs in the existing pytest job.

Finding 3 (Low) -- arm-none-eabi-r5.cmake put --specs=nosys.specs in
CMAKE_C_FLAGS_INIT with nothing in CMAKE_EXE_LINKER_FLAGS_INIT. Not broken
today, and one added linker -specs= away from the same failure. Moved, so all
three ARM bare-metal toolchains state it the same way and the new guard is
green across all of them.

Verified:
  pytest tests/unit/test_toolchain_specs.py    2 passed
  pytest tests/                                all pass
  discrimination: reintroducing the duplicate in arm-cortex-m4.cmake gives
      arm-cortex-m4.cmake: -specs=nosys.specs is set in CMAKE_C_FLAGS_INIT
      and CMAKE_EXE_LINKER_FLAGS_INIT, so it reaches the link line more than
      once
    on both tests, so the guard catches the original defect rather than
    restating the fix.
  No ARM toolchain on this machine, so the cross link itself is NOT verified
  here; that remains CI's to show.

Refs embeddedos-org#126
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…orrowed hunk

Addresses findings 1-4 of the review on 126bd0c.

Finding 4 first, because it is the one that was making the branch look better
than it is. The test_crypto_ed25519_loworder registration was carried here
from embeddedos-org#127. It made Run Python tests green on a branch that has nothing to do
with it, contradicted this PR's own 2026-09-02 comment, and sat at a different
anchor to embeddedos-org#127's, so it would have conflicted. Dropped. That check is red
again until embeddedos-org#127 lands, which is the honest state -- folding a master repair
into a toolchain PR to get a green tick is how a red master becomes everyone's
problem to route around.

Finding 1: the parser recognised one CMake idiom, set(VAR "literal"). It could
not see string(APPEND ...), an unquoted set(), or -- the one that matters here
-- a spec arriving through ${CPU_FLAGS}, which is how both Cortex-M4 toolchains
build their flag strings. Putting -specs= there would have reached both
CMAKE_C_FLAGS_INIT and CMAKE_EXE_LINKER_FLAGS_INIT while every check passed.
The file is now walked in order with a variable table, ${...} resolved against
what is in force at that point, and string(APPEND ...) applied. Two tests pin
those two bypasses.

Finding 3: CMAKE_ASM_FLAGS_INIT added to LINK_REACHING. CMake picks a target's
linker language from its sources, so an executable built only from .S files
links with the ASM flags. Both Cortex-M4 files set it from ${CPU_FLAGS}. The
current tree still passes; no toolchain names a spec there today.

Finding 2: the .yaml descriptors are scanned too. They are the second
authority on these flags -- toolchains/src/toolchain.c parses them into
EosToolchain.cflags/ldflags, a separate consumer from CMake -- and all five
already keep specs in ldflags, which is a good argument for this change and a
better one for holding both descriptions to one rule.

Found while verifying rather than assuming: my first regex required the closing
paren on the same line, so arm-none-eabi-stm32f4.cmake's three-line
set(CMAKE_EXE_LINKER_FLAGS_INIT ...) was invisible and every check passed
vacuously for that file. That is this test's own failure mode one level up.
Fixed, and pinned by test_a_multi_line_set_is_still_seen and
test_every_arm_toolchain_is_actually_parsed -- the latter asserts the parser
finds specs in all three ARM files, so a parser that silently sees nothing can
no longer pass.

Verified: the parser reports arm-cortex-m4 [nosys, nano], arm-none-eabi-r5
[nosys], arm-none-eabi-stm32f4 [nano, nosys], all on
CMAKE_EXE_LINKER_FLAGS_INIT. Mutations caught: a spec hidden in CPU_FLAGS, and
the original duplicate re-introduced on CMAKE_C_FLAGS_INIT.

7 toolchain tests pass, ctest 38/38.
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
Residue from 0957ac4, which dropped the borrowed
test_crypto_ed25519_loworder registration and left the blank line that had
separated it from the block below. It is the only remaining change this PR
makes to tests/CMakeLists.txt, it changes nothing, and it sits at the exact
anchor where embeddedos-org#118, embeddedos-org#119 and embeddedos-org#127 each add the real registration block -- so
it would have conflicted with whichever of those lands first, for no reason
at all.

tests/CMakeLists.txt is now untouched by this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 3, 2026
…ot a copy of it

Restacked onto embeddedos-org#127. The previous head dropped the registration hunk
entirely, per finding 4, which left Run Python tests red by design until
embeddedos-org#127 landed. Stacking gets the same result without a duplicate: the hunk
comes from embeddedos-org#127, this branch adds none of its own, and the diff against
embeddedos-org#127 is toolchain-only.

Verified locally before pushing: pytest 22 passed (including
test_every_c_suite_is_built_or_listed, the check that was failing), cmake
configure and build clean, ctest 39/39 with test_crypto_ed25519_loworder
among them.
Every open eos pull request is currently red on `EoS Full-Stack Simulation`,
and none of them caused it. The kernel job checks out embeddedos-org/eBoot at
`ref: master`, and eBoot master does not compile: include/eos_image.h:135 and
:142 static-assert offsetof(eos_image_header_t, reserved) for a member that
became tlv_len + tlv_hash, and core/ed25519_verify.c redefines
point_is_identity -- two pairs of PRs that each merged clean and broke the
build together. eBoot embeddedos-org#94 repairs it and waits on review; until it lands, no
change to any eos PR can make this job pass, and after it lands the next
broken eBoot master does the same thing again.

A floating `ref: master` makes every eos PR's CI depend on the moment-to-
moment state of another repository's default branch. Pinning the two
compiled-against checkouts makes the job a function of this repository plus
two recorded SHAs -- red means the PR broke something, green means it did not,
and a cross-repo bump is a reviewable one-line diff with CI on it.

  EBOOT_COMMIT  a172a6d6e1e6  the newest eBoot master ancestor that compiles;
                found by walking master back and building each candidate.
                Its child 8a015b2 already carries the ed25519 redefinition.
  EBUILD_COMMIT e5d8052f3e2c  ebuild's current master, frozen as-is. Both of
                its checkouts feed steps that are commented out (eFab does
                not exist), so this pin only removes the float.

The middleware matrix (10 repos) still floats; it runs those repos' own
tests rather than compiling eos against them, and 10 more pins deserve their
own decision. When eBoot embeddedos-org#94 merges, bump EBOOT_COMMIT to the merge SHA.

Verified before pushing, master vs pin under the job's own configuration
(-DEBLDR_BOARD=qemu_arm64 -DEBLDR_VERIFY_STAGE1=OFF -DCMAKE_BUILD_TYPE=Release,
cross-compile mode): eBoot@master fails at the eos_image.h asserts, the exact
error in the job log; eBoot@a172a6d builds every target, rc=0. Both pinned
SHAs verified reachable from their repos' master. Workflow parses; the three
`ref:` edits and the env block are the whole diff. This PR's own simulation
run is the live test: it uses this branch's workflow and should go green
while every master-based PR stays red.

Carried onto this branch from embeddedos-org#135 (001128d) so this PR's own simulation
job runs against a buildable eBoot: each pull request executes its own copy
of the workflow, so the fix greens a PR only once its branch contains it.
Byte-identical to embeddedos-org#135's commit -- git drops it as already-upstream the
moment embeddedos-org#135 merges. The pin itself is embeddedos-org#135's to review.

@srpatcha srpatcha left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Review — eos#127 "fix(build): restore the low-order Ed25519 suite that #93 removed"

head: 83800db author: Kartikey1306 ci: pass (26 checks, 0 failures)

Verdict: The restore is correct and I verified it byte-for-byte: the four lines in tests/CMakeLists.txt are identical to 3aa8644^ and sit in the original position before test_crypto_sha512, tests/test_crypto_ed25519_loworder.c is present on origin/master with no add_executable building it, and the CI narrative in the body is accurate down to the compiler error. But this head is not the one-hunk change the title and body describe. It carries a second commit that is the whole subject of eos#135, and the body says "No other file changes."

Findings

# Severity File:line Finding Recommended fix
1 Medium .github/workflows/eos-simulation.yml:12-20, 41, 48, 288; PR body, "The fix" The body states "Restored verbatim from 3aa8644^, in its original position. No other file changes." The head has two commits: 13a8ffa (the restore) and 83800db "ci: pin the eBoot and ebuild checkouts instead of floating on master", which adds EBOOT_COMMIT/EBUILD_COMMIT and replaces three ref: master checkouts. That second commit is the entire subject of eos#135 — I diffed the branches: git diff pr-127 pr-135 is .github/workflows/upstream-drift.yml (146 lines) and nothing else, so #127 is a strict subset of #135. The identical pin also sits in eos#129 (d3b5852). Three open PRs carry the same CI change; the first to merge makes the others' hunk empty or conflicting, and a reviewer of #127 who trusts the body will not review the CI change at all. Drop 83800db so this PR is the four-line restore its title claims, and let #135 carry the pin plus the drift job — or close #127 and keep #135, which contains it. Either way the body's "No other file changes" has to go. Separately: the same restore hunk is in #119, #122, #129 and #135 (and #126 adds a stray blank line at the same anchor), so whichever of those lands second conflicts on this anchor too.
2 Low .github/workflows/eos-simulation.yml:16-19 The pin's own comment names the wrong repair: "eBoot #94 repairs it … when eBoot #94 lands, bump EBOOT_COMMIT to that merge." The PR body and your own 17:20 comment both name eBoot#97 ("Repair opened as eBoot#97", "Repair is eBoot#97"). One of the two is wrong, and the wrong one is the one that lives in the file and will be followed a month from now by someone who never reads this thread. Name the actual repair PR in the comment, and give the pin an owner — the recorded design rule for this asks for the lift condition and a named owner in the same file (.ai/autoreview/proposals/2026-09.md, 2026-09-04 addendum).
3 Low .github/workflows/eos-simulation.yml:19 The pin is a bare commit SHA, and .github/STANDARDS.md ("Release model") names a vX.Y.Z tag as the recommended pin for a consumer, with the release branch and master as the other two options — a raw SHA is outside the documented set, so a future maintainer will read this as sloppiness and "fix" it to a tag. It is not sloppiness, and I checked why: eBoot's newest tag v3.0.1 (35ed483, 2026-05-16) has a coherent include/eos_image.h and would build, but it does not contain a172a6d (eBoot#86) — the fix that rejects public keys outside the prime-order subgroup. Pinning eos's CI to the released tag would pin it to an eBoot that still accepts a low-order-key forgery. The SHA is the right call; the file does not say so. One sentence in the comment: v3.0.1 was evaluated and rejected because it predates eBoot#86's subgroup fix, so no released ref is usable until eBoot tags one that contains it.

Architecture conformance

Conforms. §21 tier placement: both files are Tier-1 eos infrastructure. §5.1 dependency direction, applied to CI as the recorded proposal reads it: eos (Tier 1) consuming eBoot (Tier 1) and ebuild (Tier 1) is sibling-tier, not upward, so the direction is legal and only the ref was ever the problem. §28 evidence policy is what the restore serves — a suite present in the tree but built by nothing is an Implemented/Validated claim with no evidence behind it, and tests/unit/test_cmake_test_registration.py catching it is the guard working as intended.

The design gaps behind both halves of this branch are already recorded and no new proposal is filed: build-/CI-time cross-repo dependency direction and the released-ref rule in .ai/autoreview/proposals/2026-09.md (2026-09-03, "§5.1 governs runtime dependencies and says nothing about build- or CI-time ones", triggered in part by #119/#122), and the SHA-as-stated-exception plus mandatory drift job in the 2026-09-04 addendum triggered by #135 — which is exactly what upstream-drift.yml in #135 implements. §28's other gap, that a registration guard answers "is this file compiled?" and not "is this test called?", is what you built in #134; that is the right place for it.

Proposed changes

  1. git rebase -i this branch down to 13a8ffa alone, force-push. #127 then reads as its title, and #135 keeps the CI work.
  2. Fix the eBoot PR number in the workflow comment (in #135, once #127 no longer carries the file).
  3. Add the v3.0.1-was-rejected sentence next to the pin.

Verified in this review

  • The restore hunk is byte-identical to 3aa8644^:tests/CMakeLists.txt and in the same position. Not just "looks right" — the two extracts compare equal.
  • 3aa8644^ had exactly one add_executable(test_crypto_ed25519_loworder, so #93's "remove duplicate test targets" removed a non-duplicate. Your grep -c claim reproduces.
  • eBoot master genuinely does not compile, which was previously an attribution rather than a verified fact. At 22d8f8b the struct carries tlv_len (:53) and tlv_hash (:54) where reserved[30] used to be, and EOS_IMG_STATIC_ASSERT(offsetof(eos_image_header_t, reserved) == 62) (:135) and sizeof(((eos_image_header_t *)0)->reserved) == 30 (:142) survived the merge — the exact two errors in the eos ARM64 job log. Your account of the three conflict-free merges matches the source.
  • The pinned a172a6d is coherent: reserved[30] present at include/eos_image.h:37, no assert referencing a member it lacks.
  • The pin demonstrably works, and this is the cleanest evidence in the batch. Within six minutes on 2026-09-04, Cross-compile ARM64 kernel completed success on #127 (06:37:18), #129 (06:37:45) and #135 (06:41:27) — the three heads carrying the pin — and failure on #119 (06:35:46) and #122, the two that still float on master. Same merge base for four of the five. That is an A/B, not a coincidence of timing.

Not checked

  • Nothing was built or run locally. ctest 39/39, pytest 15 passed and test_crypto_ed25519_loworder 5/5 are the author's figures; CI is green on this head across all 26 checks, which supports them but is not the same as reproducing them.
  • I did not compile eBoot at a172a6d — the coherence check above is source inspection of the specific asserts that fail on master, so I can say the known breakage is absent, not that the tree builds clean.
  • The EBUILD_COMMIT pin (e5d8052f…) was not examined at all; the ebuild clone under the working root is dirty, so the sync step left it untouched and that commit is not in the local object store.
  • Your mutation evidence that the restored suite is load-bearing (disabling the subgroup check makes test_all_zero_key_is_rejected fail) is not reproduced.
  • The cross-target coverage argument — test_pkg_trust_anchor sitting inside if(NOT CMAKE_CROSSCOMPILING) at tests/CMakeLists.txt:218 — I read the guard and it is there; I did not confirm by build that the restored target is reached on the two cross legs.

Automated architecture review of 83800dbee807 — scheduled, model claude-opus-5, checked against the EmbeddedOS Master Design v2.0. Advisory only: this reviewer never approves, requests changes, or merges. Reply here to discuss or push back — a wrong finding is a bug worth reporting.

srpatcha pushed a commit that referenced this pull request Sep 8, 2026
* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* chore(tests): drop the unused assert.h include from test_net.c

Retitles and reduces this PR to what it still does, per the review.

Both repairs it was opened for have landed on master by other routes:
net/src/net_posix.c is byte-identical to origin/master, and master's default
configuration builds clean. Against current master the branch's only real
content was this one line -- and merging it as it stood would have *reverted*
646 lines that landed since, including #120's pkg trust anchor,
tests/test_pkg_trust_anchor.c and tests/unit/test_cmake_test_registration.py.
Reset to master and re-applied the one surviving change rather than leave a
stale branch that still looks mergeable.

The include is genuinely unused: tests/test_net.c has zero assert() calls and
uses its own CHECK() macro at 22 call sites. Worth correcting the record, per
finding 2: #124 added it one commit ago as "the missing assert.h include that
broke master", and the file had no assert() at #124's parent either. What
fixed that link error was the assert() -> CHECK() conversion already on
master; the include was never load-bearing, so removing it changes nothing
except the next reader's understanding.

Stacked on #127, which restores the test_crypto_ed25519_loworder registration
master is currently red on.

Verified:
  pytest tests/    15 passed
  ctest            39/39 PASS

Refs #116, #124

* ci: pin the eBoot and ebuild checkouts instead of floating on master

Every open eos pull request is currently red on `EoS Full-Stack Simulation`,
and none of them caused it. The kernel job checks out embeddedos-org/eBoot at
`ref: master`, and eBoot master does not compile: include/eos_image.h:135 and
:142 static-assert offsetof(eos_image_header_t, reserved) for a member that
became tlv_len + tlv_hash, and core/ed25519_verify.c redefines
point_is_identity -- two pairs of PRs that each merged clean and broke the
build together. eBoot #94 repairs it and waits on review; until it lands, no
change to any eos PR can make this job pass, and after it lands the next
broken eBoot master does the same thing again.

A floating `ref: master` makes every eos PR's CI depend on the moment-to-
moment state of another repository's default branch. Pinning the two
compiled-against checkouts makes the job a function of this repository plus
two recorded SHAs -- red means the PR broke something, green means it did not,
and a cross-repo bump is a reviewable one-line diff with CI on it.

  EBOOT_COMMIT  a172a6d6e1e6  the newest eBoot master ancestor that compiles;
                found by walking master back and building each candidate.
                Its child 8a015b2 already carries the ed25519 redefinition.
  EBUILD_COMMIT e5d8052f3e2c  ebuild's current master, frozen as-is. Both of
                its checkouts feed steps that are commented out (eFab does
                not exist), so this pin only removes the float.

The middleware matrix (10 repos) still floats; it runs those repos' own
tests rather than compiling eos against them, and 10 more pins deserve their
own decision. When eBoot #94 merges, bump EBOOT_COMMIT to the merge SHA.

Verified before pushing, master vs pin under the job's own configuration
(-DEBLDR_BOARD=qemu_arm64 -DEBLDR_VERIFY_STAGE1=OFF -DCMAKE_BUILD_TYPE=Release,
cross-compile mode): eBoot@master fails at the eos_image.h asserts, the exact
error in the job log; eBoot@a172a6d builds every target, rc=0. Both pinned
SHAs verified reachable from their repos' master. Workflow parses; the three
`ref:` edits and the env block are the whole diff. This PR's own simulation
run is the live test: it uses this branch's workflow and should go green
while every master-based PR stays red.

Carried onto this branch from #135 (001128d) so this PR's own simulation
job runs against a buildable eBoot: each pull request executes its own copy
of the workflow, so the fix greens a PR only once its branch contains it.
Byte-identical to #135's commit -- git drops it as already-upstream the
moment #135 merges. The pin itself is #135's to review.
srpatcha added a commit that referenced this pull request Sep 8, 2026
* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* test: catch tests that are compiled but never called

Closes the other half of the gap @srpatcha identified reviewing #127.

test_cmake_test_registration.py answers "is this file compiled?". It
cannot answer "is this test called?", because a test function nothing
invokes is valid C: it compiles, it links, ctest runs its suite, and the
suite passes having skipped it. No build error, no ctest failure, and
no registration check can see it.

Added test_every_test_function_is_called(). Run against master it finds
exactly one instance across every registered suite, with no false
positives:

    test_kernel.c::test_queue_send_waiter_overflow

That is the suite guarding the eos_queue_send / eos_queue_receive waiter
table -- a full table now returns EOS_KERN_NO_MEMORY instead of blocking
a task that can never be woken. It has been defined and uncalled since
it landed, so that fix has had no executing test. Calling it, the test
passes: the fix was right, nothing was checking it.

The same class had already bitten eBoot, where a merge kept a test
function and dropped its run_ call (eBoot#94).

Two declaration styles are handled -- `static void test_x(void)` called
as test_x(), and `TEST(x)` called as run_x(). Occurrences are counted by
bare identifier rather than by `name(`, so a function passed by address
(a task entry point, a callback) is not reported as dead; test_entry in
this same file is exactly that case and is correctly ignored.

test_kernel.c's summary printed a hardcoded "(12/12)" while thirteen
tests sat in the file, so the literal agreed with the omission instead
of exposing it. It is counted now, by a RUN() macro that increments as
it calls, which is the same shape as the fix eBoot#94 applied to its
tests_run.

Verified:
  guard against master's tree  -> 1 failure, names the right function
  guard after the fix          -> 7 passed
  test_kernel                  -> 13/13 (was 12 called, 13 defined)
  pytest tests/                -> 17 passed
  ctest                        -> 39/39 passed

Stacked on #127, which master needs before any of this runs.

---------

Co-authored-by: Srikanth Patchava <srikanth.patchava@outlook.com>
srpatcha pushed a commit that referenced this pull request Sep 8, 2026
…cs is passed twice (#126)

* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* fix(toolchain): stop passing nosys.specs twice on a Cortex-M4 link

arm-cortex-m4.cmake sets -specs=nosys.specs in CMAKE_C_FLAGS_INIT and again
in CMAKE_EXE_LINKER_FLAGS_INIT. CMake links through the compiler driver, so
both reach the link line and the specs file is read twice. A specs file may
only be read once -- the second read tries to re-rename a spec it has
already renamed:

    arm-none-eabi-gcc: fatal error: nosys.specs: attempt to rename spec
    'link_gcc_c_sequence' to already defined spec 'nosys_link_gcc_c_sequence'

It has been latent because CMAKE_TRY_COMPILE_TARGET_TYPE is STATIC_LIBRARY
and nothing in the cross build links an executable, so the duplicate never
reached a link line. It surfaced the moment one did.

The flag belongs on the link line only; arm-none-eabi-stm32f4.cmake already
keeps it there and nowhere else. This matches it. The compile line does not
need it: nosys.specs supplies the syscall stubs the linker wants.

Verified with arm-none-eabi-gcc rather than by reading. The mechanism, using
a specs file this toolchain does ship:

    once:  arm-none-eabi-gcc ... m.o -specs=dup.specs -o a.elf     -> links
    twice: arm-none-eabi-gcc -specs=dup.specs m.o -specs=dup.specs -> fatal error:
           attempt to rename spec 'link_gcc_c_sequence' to already defined
           spec 'dup_link_gcc_c_sequence'

the same error CI reports, from the same cause. And the generated build:
before, every compile command carried -specs=nosys.specs; after, none does
and the exe link flags still carry it once.

The full cross build could not be run here -- the Homebrew arm-none-eabi-gcc
ships no newlib, so nosys.specs and stdint.h are both absent. Configure
succeeds with this change and fails without it on that toolchain, which is
the part it governs.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(toolchain): guard the duplicate-specs class, and align the third ARM file

Answers the review on #126.

Finding 1 (Medium) -- the head carried an undisclosed second change, the
test_crypto_ed25519_loworder registration, which contradicted this PR's own
2026-09-02 comment ("this PR changes one file ... and does not touch tests/").
It was also the fourth open copy of that registration, at a different position
from #127's. Dropped: the branch is rebased to the toolchain change alone, and
#127 is the one to take for the registration -- it restores the four lines to
their original position, keeps the crypto suites grouped, and is the position
#122 is already written against.

Finding 2 (Medium) -- nothing would catch this coming back. Every ARM
toolchain here sets CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY and no
cross build links an executable, so `Cross-compile ARM Cortex-M4` passed
identically with the duplicate present and absent; it passed on the unfixed
commit. The defect surfaced by accident when #118 briefly put cmd/eos into the
cross build, and #118 has since gated that to host builds -- removing the
accident too.

Adds tests/unit/test_toolchain_specs.py, modelled on
test_cmake_test_registration.py: parse every toolchains/*.cmake, extract each
-specs=<file>, and fail if one appears in more than one variable that reaches
a link line (CMake links through the compiler driver, so C/CXX flags do). A
second test asserts specs are declared on the linker flags rather than the
compiler flags, which is the arrangement that turns into a duplicate the
moment someone adds the linker entry that looks missing. No ARM toolchain
needed; runs in the existing pytest job.

Finding 3 (Low) -- arm-none-eabi-r5.cmake put --specs=nosys.specs in
CMAKE_C_FLAGS_INIT with nothing in CMAKE_EXE_LINKER_FLAGS_INIT. Not broken
today, and one added linker -specs= away from the same failure. Moved, so all
three ARM bare-metal toolchains state it the same way and the new guard is
green across all of them.

Verified:
  pytest tests/unit/test_toolchain_specs.py    2 passed
  pytest tests/                                all pass
  discrimination: reintroducing the duplicate in arm-cortex-m4.cmake gives
      arm-cortex-m4.cmake: -specs=nosys.specs is set in CMAKE_C_FLAGS_INIT
      and CMAKE_EXE_LINKER_FLAGS_INIT, so it reaches the link line more than
      once
    on both tests, so the guard catches the original defect rather than
    restating the fix.
  No ARM toolchain on this machine, so the cross link itself is NOT verified
  here; that remains CI's to show.

Refs #126

* test(toolchains): see the spec wherever it is written, and drop the borrowed hunk

Addresses findings 1-4 of the review on 126bd0c.

Finding 4 first, because it is the one that was making the branch look better
than it is. The test_crypto_ed25519_loworder registration was carried here
from #127. It made Run Python tests green on a branch that has nothing to do
with it, contradicted this PR's own 2026-09-02 comment, and sat at a different
anchor to #127's, so it would have conflicted. Dropped. That check is red
again until #127 lands, which is the honest state -- folding a master repair
into a toolchain PR to get a green tick is how a red master becomes everyone's
problem to route around.

Finding 1: the parser recognised one CMake idiom, set(VAR "literal"). It could
not see string(APPEND ...), an unquoted set(), or -- the one that matters here
-- a spec arriving through ${CPU_FLAGS}, which is how both Cortex-M4 toolchains
build their flag strings. Putting -specs= there would have reached both
CMAKE_C_FLAGS_INIT and CMAKE_EXE_LINKER_FLAGS_INIT while every check passed.
The file is now walked in order with a variable table, ${...} resolved against
what is in force at that point, and string(APPEND ...) applied. Two tests pin
those two bypasses.

Finding 3: CMAKE_ASM_FLAGS_INIT added to LINK_REACHING. CMake picks a target's
linker language from its sources, so an executable built only from .S files
links with the ASM flags. Both Cortex-M4 files set it from ${CPU_FLAGS}. The
current tree still passes; no toolchain names a spec there today.

Finding 2: the .yaml descriptors are scanned too. They are the second
authority on these flags -- toolchains/src/toolchain.c parses them into
EosToolchain.cflags/ldflags, a separate consumer from CMake -- and all five
already keep specs in ldflags, which is a good argument for this change and a
better one for holding both descriptions to one rule.

Found while verifying rather than assuming: my first regex required the closing
paren on the same line, so arm-none-eabi-stm32f4.cmake's three-line
set(CMAKE_EXE_LINKER_FLAGS_INIT ...) was invisible and every check passed
vacuously for that file. That is this test's own failure mode one level up.
Fixed, and pinned by test_a_multi_line_set_is_still_seen and
test_every_arm_toolchain_is_actually_parsed -- the latter asserts the parser
finds specs in all three ARM files, so a parser that silently sees nothing can
no longer pass.

Verified: the parser reports arm-cortex-m4 [nosys, nano], arm-none-eabi-r5
[nosys], arm-none-eabi-stm32f4 [nano, nosys], all on
CMAKE_EXE_LINKER_FLAGS_INIT. Mutations caught: a spec hidden in CPU_FLAGS, and
the original duplicate re-introduced on CMAKE_C_FLAGS_INIT.

7 toolchain tests pass, ctest 38/38.

* test(toolchains): drop the blank line the removed hunk left behind

Residue from 0957ac4, which dropped the borrowed
test_crypto_ed25519_loworder registration and left the blank line that had
separated it from the block below. It is the only remaining change this PR
makes to tests/CMakeLists.txt, it changes nothing, and it sits at the exact
anchor where #118, #119 and #127 each add the real registration block -- so
it would have conflicted with whichever of those lands first, for no reason
at all.

tests/CMakeLists.txt is now untouched by this branch.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(build): take the loworder registration from #127, not a copy of it

Restacked onto #127. The previous head dropped the registration hunk
entirely, per finding 4, which left Run Python tests red by design until
#127 landed. Stacking gets the same result without a duplicate: the hunk
comes from #127, this branch adds none of its own, and the diff against
#127 is toolchain-only.

Verified locally before pushing: pytest 22 passed (including
test_every_c_suite_is_built_or_listed, the check that was failing), cmake
configure and build clean, ctest 39/39 with test_crypto_ed25519_loworder
among them.

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
@srpatcha
srpatcha merged commit 5ca22f8 into embeddedos-org:master Sep 8, 2026
28 checks passed
srpatcha pushed a commit that referenced this pull request Sep 8, 2026
…rd it (#114)

* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* test: stop building test_net_mock, which covers nothing, and say what the guard checks

Reduces this PR to what it still contributes, and answers the review.

The four registrations it opened with have landed on master by other routes --
test_mpu_validate, test_e2e, test_hal_stm32f4 and the registration guard are
all there. Merging the branch as it stood would have reverted 505 lines that
landed since, including #120's pkg trust anchor. Reset onto #127 and kept only
what is still true.

Finding 1 (Medium) -- test_net_mock was registered with no
target_link_libraries and includes no EmbeddedOS header. Its mock_net_*
functions are called directly rather than standing in for eos_net_*, so no
change to net/ can make it fail: 614 lines asserting a mock against itself.
Registering it added a green ctest entry covering nothing, which is precisely
the failure mode the guard in this same commit exists to surface -- and the
guard's own NOT_BUILT policy is where it belongs. Listed there with the reason,
including what would make it worth building: driving the real eos_net_* API
against the mock transport, which is what its docblock already claims it does.

Finding 2 (Low) -- documented the guard's limit where the policy is defined.
It checks that every suite is built and registered with ctest. It does not
check that a suite reaches the code it is named after, and a file that links
nothing satisfies it completely. Worth stating next to NOT_BUILT, because that
is the list someone reaches for when the guard fires.

Findings 3 and 4 are moot at this head: the test_mpu_validate comment and
test_net_mock's vestigial assert.h include both belonged to hunks that are
either upstream or removed here.

Verified:
  pytest tests/     15 passed
  ctest             38/38 PASS
    (test_net_mock is no longer among them; the count is unchanged from
    master because #127, which this is stacked on, adds
    test_crypto_ed25519_loworder -- a suite that does exercise real code.)

Refs #114

* ci: pin the eBoot and ebuild checkouts instead of floating on master

Every open eos pull request is currently red on `EoS Full-Stack Simulation`,
and none of them caused it. The kernel job checks out embeddedos-org/eBoot at
`ref: master`, and eBoot master does not compile: include/eos_image.h:135 and
:142 static-assert offsetof(eos_image_header_t, reserved) for a member that
became tlv_len + tlv_hash, and core/ed25519_verify.c redefines
point_is_identity -- two pairs of PRs that each merged clean and broke the
build together. eBoot #94 repairs it and waits on review; until it lands, no
change to any eos PR can make this job pass, and after it lands the next
broken eBoot master does the same thing again.

A floating `ref: master` makes every eos PR's CI depend on the moment-to-
moment state of another repository's default branch. Pinning the two
compiled-against checkouts makes the job a function of this repository plus
two recorded SHAs -- red means the PR broke something, green means it did not,
and a cross-repo bump is a reviewable one-line diff with CI on it.

  EBOOT_COMMIT  a172a6d6e1e6  the newest eBoot master ancestor that compiles;
                found by walking master back and building each candidate.
                Its child 8a015b2 already carries the ed25519 redefinition.
  EBUILD_COMMIT e5d8052f3e2c  ebuild's current master, frozen as-is. Both of
                its checkouts feed steps that are commented out (eFab does
                not exist), so this pin only removes the float.

The middleware matrix (10 repos) still floats; it runs those repos' own
tests rather than compiling eos against them, and 10 more pins deserve their
own decision. When eBoot #94 merges, bump EBOOT_COMMIT to the merge SHA.

Verified before pushing, master vs pin under the job's own configuration
(-DEBLDR_BOARD=qemu_arm64 -DEBLDR_VERIFY_STAGE1=OFF -DCMAKE_BUILD_TYPE=Release,
cross-compile mode): eBoot@master fails at the eos_image.h asserts, the exact
error in the job log; eBoot@a172a6d builds every target, rc=0. Both pinned
SHAs verified reachable from their repos' master. Workflow parses; the three
`ref:` edits and the env block are the whole diff. This PR's own simulation
run is the live test: it uses this branch's workflow and should go green
while every master-based PR stays red.

Carried onto this branch from #135 (001128d) so this PR's own simulation
job runs against a buildable eBoot: each pull request executes its own copy
of the workflow, so the fix greens a PR only once its branch contains it.
Byte-identical to #135's commit -- git drops it as already-upstream the
moment #135 merges. The pin itself is #135's to review.
srpatcha added a commit that referenced this pull request Sep 8, 2026
…rtos (#118)

* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* fix(build): declare the dependency where it belongs, and make the pop-back structural

Answers the review on #118.

Finding 1 (Medium) -- systems/src/firmware.c includes eos/backend.h and calls
eos_backend_find(), but eos_systems linked only eos_core and eos_toolchains;
the dependency was satisfied at each consumer instead, so every future user of
eos_systems had to rediscover it. eos_systems now declares it, and
eos_backends comes off the test_firmware line.

Finding 2 (Medium) -- backends/include was added to the global
include_directories(), putting a build-orchestration header on the include
path of every target in the tree, eos_kernel and eos_hal included. Nothing
then stops kernel or HAL code acquiring a dependency master design 5.1
forbids. Taken the reviewer's version rather than a narrower one: the header
moves to include/eos/backend.h -- replacing the stale copy that caused the
original shadowing -- backends/include is gone, and both the global entry and
backends' own target_include_directories with it. include/ stays the single
public-header contract, and the shadow is now structurally impossible rather
than merely resolved.

Finding 3 (Medium) -- the pop-back fired only for seven literal keys, which
made it a second authority on what a `system` child key is, kept in step with
`case SEC_SYSTEM:` by hand and by nothing else. Add an eighth key there and
the swallow returns for it silently. Now structural: every descendant of a
system sub-section sits at indent >= 2 and list items `continue` earlier, so
indent <= 1 is exactly the condition. Same behaviour on every config under
examples/, and it cannot drift.

Finding 4 (Low) -- applied the same structural guard to
SEC_TOOLCHAIN_LINUX/SEC_TOOLCHAIN_RTOS, which popped back for one sibling and
none respectively. A `target:` at toolchain's own indent written after the
linux:/rtos: blocks landed in toolchain.rtos_target instead of
toolchain.target. Not reachable by any config in examples/ today, which is
why it had not bitten.

Also dropped the test_crypto_ed25519_loworder registration hunk; this branch
is stacked on #127, which is the copy to keep.

NOT fixed, and confirmed while testing: finding 5, `system.entry` is parsed by
nothing -- no branch in `case SEC_SYSTEM:` and no field on EosSystemConfig --
while six shipped configs set it. examples/multicore-amp/eos.yaml sets
`entry: main.c` and `eos info` reports nothing for it. Same failure mode, but
it needs a new struct field and a decision about what consumes it, so it is a
follow-up rather than something to fold in here.

Verified:
  ctest                    40/40 PASS
  pytest tests/            15 passed
  eos info, in examples/industrial-gateway:
      kind: hybrid / kernel provider: buildroot / RTOS targets (1)
    which is the parser fix visible end to end -- RTOS targets reads 0 before
    it. examples/multicore-amp reports 0, correctly: that config has no rtos
    list at all.

Refs #118

* ci: pin the eBoot and ebuild checkouts instead of floating on master

Every open eos pull request is currently red on `EoS Full-Stack Simulation`,
and none of them caused it. The kernel job checks out embeddedos-org/eBoot at
`ref: master`, and eBoot master does not compile: include/eos_image.h:135 and
:142 static-assert offsetof(eos_image_header_t, reserved) for a member that
became tlv_len + tlv_hash, and core/ed25519_verify.c redefines
point_is_identity -- two pairs of PRs that each merged clean and broke the
build together. eBoot #94 repairs it and waits on review; until it lands, no
change to any eos PR can make this job pass, and after it lands the next
broken eBoot master does the same thing again.

A floating `ref: master` makes every eos PR's CI depend on the moment-to-
moment state of another repository's default branch. Pinning the two
compiled-against checkouts makes the job a function of this repository plus
two recorded SHAs -- red means the PR broke something, green means it did not,
and a cross-repo bump is a reviewable one-line diff with CI on it.

  EBOOT_COMMIT  a172a6d6e1e6  the newest eBoot master ancestor that compiles;
                found by walking master back and building each candidate.
                Its child 8a015b2 already carries the ed25519 redefinition.
  EBUILD_COMMIT e5d8052f3e2c  ebuild's current master, frozen as-is. Both of
                its checkouts feed steps that are commented out (eFab does
                not exist), so this pin only removes the float.

The middleware matrix (10 repos) still floats; it runs those repos' own
tests rather than compiling eos against them, and 10 more pins deserve their
own decision. When eBoot #94 merges, bump EBOOT_COMMIT to the merge SHA.

Verified before pushing, master vs pin under the job's own configuration
(-DEBLDR_BOARD=qemu_arm64 -DEBLDR_VERIFY_STAGE1=OFF -DCMAKE_BUILD_TYPE=Release,
cross-compile mode): eBoot@master fails at the eos_image.h asserts, the exact
error in the job log; eBoot@a172a6d builds every target, rc=0. Both pinned
SHAs verified reachable from their repos' master. Workflow parses; the three
`ref:` edits and the env block are the whole diff. This PR's own simulation
run is the live test: it uses this branch's workflow and should go green
while every master-based PR stays red.

Carried onto this branch from #135 (001128d) so this PR's own simulation
job runs against a buildable eBoot: each pull request executes its own copy
of the workflow, so the fix greens a PR only once its branch contains it.
Byte-identical to #135's commit -- git drops it as already-upstream the
moment #135 merges. The pin itself is #135's to review.

---------

Co-authored-by: Srikanth Patchava <srikanth.patchava@outlook.com>
srpatcha pushed a commit that referenced this pull request Sep 8, 2026
…129)

* fix(build): restore the low-order Ed25519 suite that #93 removed

`CI — eos` and `Python Unit Tests` are red on master (769191a), and both
fail for one reason:

    these suites exist in tests/ but no add_executable() in
    tests/CMakeLists.txt builds them, so they never run:
    ['test_crypto_ed25519_loworder.c']

3aa8644 (#93, "remove duplicate test targets from tests/CMakeLists.txt")
removed all four lines that build and register
test_crypto_ed25519_loworder. It was not a duplicate — there was exactly
one registration before that commit, and it took it.

What stopped running is the suite that guards #101: Ed25519 rejecting
low-order public keys. A low-order key makes every term of the
verification equation collapse to the identity regardless of the
message, so a signature of all zeros verifies against any content at
all. The fix is still in services/crypto/src/ed25519_verify.c and still
correct; nothing has been checking it since #93 merged, and nothing
would have noticed if a later change had removed it too.

Restored verbatim from 3aa8644^, in its original position before
test_crypto_sha512. No other file changes.

The guard that caught this is test_cmake_test_registration.py, added
recently. It did exactly its job — this is the first thing it found.

Verified:
  test_cmake_test_registration.py    -> 5 passed (1 failed on master)
  pytest tests/                      -> 15 passed (14 passed 1 failed on master)
  test_crypto_ed25519_loworder       -> 5/5 tests passed
  ctest                              -> 39/39 passed (master builds 38)

* ci: finish the EoSim fix — a tag that exists, and no wheel anywhere

`Simulation Test` and `EoSim Sanity` are both red on master. #106 fixed
this class in eosim-sanity.yml but did not reach every site.

simulation-test.yml still pins EOSIM_VERSION: "0.1.0". EoSim has no
v0.1.0 tag — its releases run v1.0.0 to v3.0.1 — so every run failed at
the clone:

    fatal: Remote branch v0.1.0 not found in upstream origin

Bumped to 1.5.0, the release marked Latest and the one eosim-sanity.yml
already pins.

eosim-sanity.yml still installs a wheel on its Windows and macOS legs:

    ERROR: HTTP error 404 while getting
    .../releases/download/v1.5.0/eosim-1.5.0-py3-none-any.whl

No EoSim release ships a wheel, so that URL 404s at any version — the
version bump alone could never fix these two. The other three jobs in
the same workflow already install from a clone; these two were missed.
Converted to the same clone + `pip install -e`.

Verified, against the real remote rather than from the error message:

    wheel URL                       -> HTTP 404
    git ls-remote --tags | v0.1.0   -> 0 matches
    git ls-remote --tags | v1.5.0   -> 1 match
    clone v1.5.0 + pip install -e   -> rc 0
    eosim --version                 -> eosim, version 2.0.0
    eosim doctor                    -> rc 0

(The v1.5.0 tag reporting version 2.0.0 is EoSim's own inconsistency,
not a wrong pin — v1.5.0 is what its releases page marks Latest.)

Both files still parse as YAML. Stacked on the #127 registration fix so
this branch's CI is not red for an unrelated reason.

* ci(eosim): pin the commit, assert the version pip resolved, and let the workflow test itself

Answers the review on #129. The two diagnoses in the previous commit hold --
there is no v0.1.0 tag and no EoSim release ships a Python artifact -- and
this addresses what the replacement pin did not.

Finding 1 (High) -- `EOSIM_VERSION: "1.5.0"` is a label, not a version.
Re-verified against the remote rather than taken from the review:

  tag      commit    committed    commit subject             pyproject  __init__
  v1.5.0   7dec3460  2026-05-27   "production-ready v1.4.0"    3.0.1      3.0.1
  v3.0.1   b297ec26  2026-05-16   "v3.0.1 — unified ..."       3.0.1      2.0.0

v1.5.0 is the release GitHub marks Latest and the newest tag by commit date,
tags a commit whose own message claims v1.4.0, and installs a dist declaring
3.0.1. A reader of `EOSIM_VERSION: "1.5.0"` would believe eos is simulated
against EoSim 1.5.0. It is not, and nothing in the file would reveal that.

Both workflows now pin `EOSIM_COMMIT` to the full SHA that tag resolves to
today, with the table above in the env block and the date it was checked.
Not v3.0.1 instead: its `__init__.py` says 2.0.0, so it is only marginally
less confusing. A SHA cannot be moved under us; the comment says to go back to
a tag once EoSim's tags mean something.

Finding 3 (Medium) -- `eosim --version` cannot detect a wrong install.
EoSim hardcodes it (`eosim/cli/main.py:49`,
`@click.version_option(version="2.0.0")`), confirmed by reading that file at
v1.5.0, so it prints 2.0.0 from every tag -- which is exactly why the previous
commit could record the version oddity as a curiosity rather than as a failed
pin. All three sites now also assert `importlib.metadata.version("eosim")`
against `EOSIM_EXPECTED_DIST`, which is the only version the commit pin
controls.

Finding 4 (Medium) -- this PR could not be validated by CI and was not: both
workflows were schedule- and dispatch-only, so neither appeared in this head's
checks and all 25 green ones were other workflows. Added
`pull_request: paths:` on both files listing both files, so a change to either
now runs both. `workflow_dispatch` on the fork is not available to me --
GitHub resolves the workflow on the fork's default branch and returns 404 --
so this trigger is the evidence that is actually obtainable, and it applies to
every future change to these files rather than to this one only.

Finding 2 (High) -- after this, nothing in the org exercises a published EoSim
artifact, and none exists. Verified independently: 13 releases whose assets
are promo .mp4 files, a manifest.json and EoSim-guide.pdf; no wheel or sdist
at any tag; `https://pypi.org/pypi/eosim/json` returns 404. Converting to a
clone is right -- a permanent 404 is not a useful red -- but it would also
make the gap green and invisible, which is the "verification whose result is
discarded" shape.

So `published-artifact-watch` runs on every execution, checks the releases and
PyPI, and says out loud that the packaging debt is open. It cannot fail the
workflow, deliberately: it is a tracking signal, not a gate on someone else's
repo. It emits a `::notice::` the day an artifact appears, which is when it
should be deleted and the installs pointed at it. The debt is also stated in
the PR body rather than left implied, and raised against EoSim.

Finding 5 (Low) -- `/tmp/EoSim` was hardcoded in the two jobs that run on
windows-latest and macos-latest, where the Windows job sets no `shell:` and
gets pwsh. Now `${{ runner.temp }}/EoSim` everywhere.

Finding 6 (Low) -- dropped `-e`. Nothing needs an editable install, it is
further from what a developer does in a workflow meant to prove the install
works, and it made the dist-metadata assertion depend on the clone staying
put. Changed at all five sites, not two, so they are consistent.

Also, while in this file: `sanity-gate` had the fail-open shape --
`needs:` five jobs, tested `install-validate` alone, then printed "All EoSim
sanity checks passed". Replaced with the `toJSON(needs)` body, which cannot
fall out of step with `needs:`, and the new job is included in it.

Verified:
  ctest                                       39/39 PASS
  pytest tests/                               15 passed
  both workflows parse, and structurally:
    pull_request paths list this file           both
    EOSIM_VERSION gone from every code line     both
    EOSIM_COMMIT is a full 40-char sha          both
    no /tmp/EoSim literal, no `pip install -e`  both
    every `git clone --filter` has a matching `checkout --detach`
    three importlib.metadata assertions
    sanity-gate needs == every other job in the file
  the SHA resolves: refs/tags/v1.5.0 -> tag object 978f215f -> commit
    7dec3460cba76083b6645eb4a867e39aa2af97df

  NOT RUN: neither workflow. `workflow_dispatch` on the fork returns 404
  because GitHub resolves it on the fork's default branch. The
  `pull_request: paths:` trigger should make both run on this PR for the
  first time -- that will be the first execution either has ever had, and I
  will report what it shows rather than predicting it.

Finding 7 (Low): still stacked on #127; the tests/CMakeLists.txt hunk goes
when that lands.

Refs #129

* ci(eosim): copy platforms/ in every job that runs a simulation

The first real execution of these workflows -- which the pull_request trigger
added in the previous commit made possible -- failed, and showed the next
blocker behind the missing v0.1.0 tag:

    FileNotFoundError: [Errno 2] No such file or directory:
      '/opt/hostedtoolcache/Python/3.12.14/x64/lib/python3.12/site-packages/platforms'

on all three Guest OS Install legs and all seven Nested Simulation platforms.

platforms/ is data, not package data: eosim/cli/main.py sets PLATFORMS_DIR to
<site-packages>/platforms while the wheel ships only
eosim/platforms/__init__.py. None of the five jobs that run eosim copied it,
so none of them could ever have worked. A tag that exists was necessary and
not sufficient, exactly as the review said.

  Worth recording: this is also what the dropped `pip install -e` was
  accidentally papering over in the jobs that had a copy elsewhere. With an
  editable install eosim.__file__ points into the clone, so PLATFORMS_DIR
  landed beside the checked-out platforms/ by luck. A plain install is the
  right thing and it made the real dependency visible.

install-validate went green in that run and the tracking job reported the
packaging debt as intended; the failures were confined to the jobs that
actually boot a platform.

Verified:
  both workflows parse; all five eosim-running jobs now copy platforms/
  the run that produced the error: 33783625398 (EoSim Sanity), first
  execution this workflow has ever had

Refs #129

* ci(eosim): run the bash steps in bash, including on the Windows legs

Second real execution, after the platforms/ fix: ubuntu and macOS now pass
Install & Validate; all three windows-latest legs fail.

    Install & Validate (windows-latest, Python 3.12)
      #   FileNotFoundError: .../site-packages/platforms
      ##[error]Process completed with exit code 1

The give-away is the leading `#` in that line: pwsh echoed my comment as a
command. install-validate is a matrix over ubuntu/windows/macos and set no
`shell:`, so on the Windows legs it got the runner default, where
`VAR=$(...)`, `cp -r` and a leading `#` are all wrong. windows-sanity had
the same gap.

`shell: bash` on the six run steps in the two Windows-capable jobs. This is
the same defect the review raised as finding 5 for the hardcoded /tmp path --
${{ runner.temp }} fixed where the clone goes, and this fixes what interprets
the script.

Worth stating plainly: none of this was visible before the pull_request
trigger. Two runs have now found two blockers behind the missing v0.1.0 tag --
platforms/ never copied in any job, and bash steps running under pwsh -- in a
workflow that had never executed once. The trigger is doing more work than the
pin.

Verified:
  parses; no run step in a windows-capable job is left without a shell
  run 33784349071: ubuntu + macOS Install & Validate green, windows red
    with the error above

Refs #129

* ci(sim): run the bash steps in bash on the Windows leg too

Third run. EoSim Sanity is green -- 23/23 jobs, the first green run that
workflow has ever had. Simulation Test has one failure left,
`Cross-Platform (windows-latest)`, and it is the same defect I fixed in
eosim-sanity.yml one commit ago and did not apply here:

    Cross-Platform (windows-latest)  Install EoSim from source
      # fails with FileNotFoundError.
      ##[error]Process completed with exit code 1

pwsh echoing a `#` comment as a command. The cross-platform matrix includes
windows-latest and its two run steps set no `shell:`.

Refs #129

* ci(eosim): one install recipe, gates that refuse empty input, and a watch that stops posing as a check

Answers the overnight review on #129.

Finding 3 (Low, and the structural one) -- the five-line install recipe was
copy-pasted at seven sites across two workflows, and the PR's own history
shows the cost: -e dropped "at all five sites, not two", runner.temp at all
of them, shell: bash at several, each a separate round because each site was
a separate edit. Now .github/actions/install-eosim owns it -- clone at the
pinned commit, install, stage platforms/, assert the dist version -- and all
seven sites are a four-line `uses:`. The platforms/ workaround and the
version assertion get one place to be deleted from when EoSim fixes its
packaging. The triplicated inline importlib.metadata assert is gone with it.

Finding 5 (Low), folded into the composite -- SITE_PACKAGES comes from
sysconfig.get_paths()["purelib"], which needs no import and cannot be
shadowed, matching eBoot's copy of the workaround instead of solving the same
problem a second way.

Finding 1 (Medium) -- the inline gate accepted nothing as everything: an
empty RESULTS printed "All EoSim sanity jobs succeeded." and exited 0, and
null produced a raw jq error, where eos#121's extracted ci-gate-check.sh
refuses both. Both gates now refuse empty/null input with a diagnosis before
jq runs, under set -euo pipefail, and carry a note to become a call to #121's
script once it is on master so the two cannot drift.

  Reproduced the review's side-by-side on the hardened body:
      ""      rc=1  gate received no results to check   (was rc=0 PASS)
      null    rc=1  gate received no results to check   (was raw jq error)
      {}      rc=0  PASS   -- shared with #121's script; reported there, and
      []      rc=0  PASS      unreachable here: needs has five entries
      skipped rc=1  FAIL: a: skipped
      success rc=0  PASS

Also applied to simulation-test.yml's Simulation Gate, which still had the
original fail-open shape -- needs: [simulate, cross-platform], a test of
`simulate` alone, then "All simulation tests passed". Same toJSON(needs)
body, same guards.

Finding 4 (Low) -- published-artifact-watch could not fail and sat in
sanity-gate's needs under a name that made a green tick assert a deficiency.
Now "EoSim artifact publication (informational)", schedule/dispatch only, out
of the gate: pull requests no longer carry a permanently-green status about
another repository's release process, and the debt still gets said out loud
every night.

Finding 6 (Low) -- simulation-test's EOSIM_COMMIT comment argued tag
selection for what is now a SHA; it now states the actual reason (EoSim's tag
names do not identify their contents) and gains the EOSIM_EXPECTED_DIST the
composite asserts.

Finding 2 (Medium) is a two-line change in eos#121's registry, not here --
both workflows need NOT_REQUIRED entries with a paths-filter reason once both
PRs land, or #121's classification test fails. Posted on both threads with
the exact entries; not folded in, because that registry lives on #121's
branch and this PR must not carry another PR's hunks.

Verified locally before push:
  all three YAML files parse
  composite referenced at 7 sites; zero inline clone/install/copy remnants
  eosim-sanity gate needs == every job except itself and the informational
    watch; sim gate iterates toJSON(needs), no single-dependency branch
  the five-input table above, executed
  the workflows themselves: this push's pull_request run is the test, and I
  will report what it shows rather than predict it.

Refs #129

* ci: pin the eBoot and ebuild checkouts instead of floating on master

Every open eos pull request is currently red on `EoS Full-Stack Simulation`,
and none of them caused it. The kernel job checks out embeddedos-org/eBoot at
`ref: master`, and eBoot master does not compile: include/eos_image.h:135 and
:142 static-assert offsetof(eos_image_header_t, reserved) for a member that
became tlv_len + tlv_hash, and core/ed25519_verify.c redefines
point_is_identity -- two pairs of PRs that each merged clean and broke the
build together. eBoot #94 repairs it and waits on review; until it lands, no
change to any eos PR can make this job pass, and after it lands the next
broken eBoot master does the same thing again.

A floating `ref: master` makes every eos PR's CI depend on the moment-to-
moment state of another repository's default branch. Pinning the two
compiled-against checkouts makes the job a function of this repository plus
two recorded SHAs -- red means the PR broke something, green means it did not,
and a cross-repo bump is a reviewable one-line diff with CI on it.

  EBOOT_COMMIT  a172a6d6e1e6  the newest eBoot master ancestor that compiles;
                found by walking master back and building each candidate.
                Its child 8a015b2 already carries the ed25519 redefinition.
  EBUILD_COMMIT e5d8052f3e2c  ebuild's current master, frozen as-is. Both of
                its checkouts feed steps that are commented out (eFab does
                not exist), so this pin only removes the float.

The middleware matrix (10 repos) still floats; it runs those repos' own
tests rather than compiling eos against them, and 10 more pins deserve their
own decision. When eBoot #94 merges, bump EBOOT_COMMIT to the merge SHA.

Verified before pushing, master vs pin under the job's own configuration
(-DEBLDR_BOARD=qemu_arm64 -DEBLDR_VERIFY_STAGE1=OFF -DCMAKE_BUILD_TYPE=Release,
cross-compile mode): eBoot@master fails at the eos_image.h asserts, the exact
error in the job log; eBoot@a172a6d builds every target, rc=0. Both pinned
SHAs verified reachable from their repos' master. Workflow parses; the three
`ref:` edits and the env block are the whole diff. This PR's own simulation
run is the live test: it uses this branch's workflow and should go green
while every master-based PR stays red.

Carried onto this branch from #135 (001128d) so this PR's own simulation
job runs against a buildable eBoot: each pull request executes its own copy
of the workflow, so the fix greens a PR only once its branch contains it.
Byte-identical to #135's commit -- git drops it as already-upstream the
moment #135 merges. The pin itself is #135's to review.
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 14, 2026
…ARM file

Answers the review on embeddedos-org#126.

Finding 1 (Medium) -- the head carried an undisclosed second change, the
test_crypto_ed25519_loworder registration, which contradicted this PR's own
2026-09-02 comment ("this PR changes one file ... and does not touch tests/").
It was also the fourth open copy of that registration, at a different position
from embeddedos-org#127's. Dropped: the branch is rebased to the toolchain change alone, and
embeddedos-org#127 is the one to take for the registration -- it restores the four lines to
their original position, keeps the crypto suites grouped, and is the position
embeddedos-org#122 is already written against.

Finding 2 (Medium) -- nothing would catch this coming back. Every ARM
toolchain here sets CMAKE_TRY_COMPILE_TARGET_TYPE to STATIC_LIBRARY and no
cross build links an executable, so `Cross-compile ARM Cortex-M4` passed
identically with the duplicate present and absent; it passed on the unfixed
commit. The defect surfaced by accident when embeddedos-org#118 briefly put cmd/eos into the
cross build, and embeddedos-org#118 has since gated that to host builds -- removing the
accident too.

Adds tests/unit/test_toolchain_specs.py, modelled on
test_cmake_test_registration.py: parse every toolchains/*.cmake, extract each
-specs=<file>, and fail if one appears in more than one variable that reaches
a link line (CMake links through the compiler driver, so C/CXX flags do). A
second test asserts specs are declared on the linker flags rather than the
compiler flags, which is the arrangement that turns into a duplicate the
moment someone adds the linker entry that looks missing. No ARM toolchain
needed; runs in the existing pytest job.

Finding 3 (Low) -- arm-none-eabi-r5.cmake put --specs=nosys.specs in
CMAKE_C_FLAGS_INIT with nothing in CMAKE_EXE_LINKER_FLAGS_INIT. Not broken
today, and one added linker -specs= away from the same failure. Moved, so all
three ARM bare-metal toolchains state it the same way and the new guard is
green across all of them.

Verified:
  pytest tests/unit/test_toolchain_specs.py    2 passed
  pytest tests/                                all pass
  discrimination: reintroducing the duplicate in arm-cortex-m4.cmake gives
      arm-cortex-m4.cmake: -specs=nosys.specs is set in CMAKE_C_FLAGS_INIT
      and CMAKE_EXE_LINKER_FLAGS_INIT, so it reaches the link line more than
      once
    on both tests, so the guard catches the original defect rather than
    restating the fix.
  No ARM toolchain on this machine, so the cross link itself is NOT verified
  here; that remains CI's to show.

Refs embeddedos-org#126
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 14, 2026
…orrowed hunk

Addresses findings 1-4 of the review on 126bd0c.

Finding 4 first, because it is the one that was making the branch look better
than it is. The test_crypto_ed25519_loworder registration was carried here
from embeddedos-org#127. It made Run Python tests green on a branch that has nothing to do
with it, contradicted this PR's own 2026-09-02 comment, and sat at a different
anchor to embeddedos-org#127's, so it would have conflicted. Dropped. That check is red
again until embeddedos-org#127 lands, which is the honest state -- folding a master repair
into a toolchain PR to get a green tick is how a red master becomes everyone's
problem to route around.

Finding 1: the parser recognised one CMake idiom, set(VAR "literal"). It could
not see string(APPEND ...), an unquoted set(), or -- the one that matters here
-- a spec arriving through ${CPU_FLAGS}, which is how both Cortex-M4 toolchains
build their flag strings. Putting -specs= there would have reached both
CMAKE_C_FLAGS_INIT and CMAKE_EXE_LINKER_FLAGS_INIT while every check passed.
The file is now walked in order with a variable table, ${...} resolved against
what is in force at that point, and string(APPEND ...) applied. Two tests pin
those two bypasses.

Finding 3: CMAKE_ASM_FLAGS_INIT added to LINK_REACHING. CMake picks a target's
linker language from its sources, so an executable built only from .S files
links with the ASM flags. Both Cortex-M4 files set it from ${CPU_FLAGS}. The
current tree still passes; no toolchain names a spec there today.

Finding 2: the .yaml descriptors are scanned too. They are the second
authority on these flags -- toolchains/src/toolchain.c parses them into
EosToolchain.cflags/ldflags, a separate consumer from CMake -- and all five
already keep specs in ldflags, which is a good argument for this change and a
better one for holding both descriptions to one rule.

Found while verifying rather than assuming: my first regex required the closing
paren on the same line, so arm-none-eabi-stm32f4.cmake's three-line
set(CMAKE_EXE_LINKER_FLAGS_INIT ...) was invisible and every check passed
vacuously for that file. That is this test's own failure mode one level up.
Fixed, and pinned by test_a_multi_line_set_is_still_seen and
test_every_arm_toolchain_is_actually_parsed -- the latter asserts the parser
finds specs in all three ARM files, so a parser that silently sees nothing can
no longer pass.

Verified: the parser reports arm-cortex-m4 [nosys, nano], arm-none-eabi-r5
[nosys], arm-none-eabi-stm32f4 [nano, nosys], all on
CMAKE_EXE_LINKER_FLAGS_INIT. Mutations caught: a spec hidden in CPU_FLAGS, and
the original duplicate re-introduced on CMAKE_C_FLAGS_INIT.

7 toolchain tests pass, ctest 38/38.
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 14, 2026
Residue from 0957ac4, which dropped the borrowed
test_crypto_ed25519_loworder registration and left the blank line that had
separated it from the block below. It is the only remaining change this PR
makes to tests/CMakeLists.txt, it changes nothing, and it sits at the exact
anchor where embeddedos-org#118, embeddedos-org#119 and embeddedos-org#127 each add the real registration block -- so
it would have conflicted with whichever of those lands first, for no reason
at all.

tests/CMakeLists.txt is now untouched by this branch.
Kartikey1306 added a commit to Kartikey1306/eos that referenced this pull request Sep 14, 2026
…ot a copy of it

Restacked onto embeddedos-org#127. The previous head dropped the registration hunk
entirely, per finding 4, which left Run Python tests red by design until
embeddedos-org#127 landed. Stacking gets the same result without a duplicate: the hunk
comes from embeddedos-org#127, this branch adds none of its own, and the diff against
embeddedos-org#127 is toolchain-only.

Verified locally before pushing: pytest 22 passed (including
test_every_c_suite_is_built_or_listed, the check that was failing), cmake
configure and build clean, ctest 39/39 with test_crypto_ed25519_loworder
among them.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants