Skip to content

feat(huxerui): 0.3.0, and a CI pin that follows the engine - #385

Merged
Sunrisepeak merged 10 commits into
mainfrom
feat/add-huxerui
Sep 11, 2026
Merged

feat(huxerui): 0.3.0, and a CI pin that follows the engine#385
Sunrisepeak merged 10 commits into
mainfrom
feat/add-huxerui

Conversation

@Sunrisepeak

@Sunrisepeak Sunrisepeak commented Sep 10, 2026

Copy link
Copy Markdown
Member

Adds the HuxerUI SDK as a Form A package, and moves MCPP_VERSION to the release its build program needs.

Shape

Category (b) — a library developed on mcpp. Upstream carries its own mcpp.toml and a build.mcpp, so the descriptor declares metadata and a download address only.

No mcpp segment, deliberately: pasting emit's table form makes the parser read this as an inline Form B and refuse it (synthesised manifest missing sources). Omitting it lets the default <verdir>/*/mcpp.toml lookup find HuxerUI-0.3.0/mcpp.tomlimgui.lua carries the same note. mcpp xpkg parse reports form A — no mcpp segment, under 2026.8.27.2 (the floor), 2026.9.6.3 and 2026.9.7.1 alike.

Naming follows mcpp#278 (INV-NAME): namespace = "huxerui" and the fully-qualified name = "huxerui.huxerui".

Deps: four, and what they are for

"xim:gtk4@4.16.13", "xim:libepoxy@1.5.10", "xim:glib@2.88.3", "xim:libsoup@3.6.6"

The list guarantees the payloads are installed, and installation is transitive — each xim package declares its own deps (xlings remove pango is refused, naming xim:gtk4 as the holder). Discovery is a different mechanism: mcpp::xpkg_dir answers from the building package's own manifest, and upstream's mcpp.toml already declares the full 36-entry closure on its target axis. An earlier revision restated all 36 here; that duplicated pins this index does not own. Verified by clearing the member's sandbox and rebuilding with the short list.

No xim:wix. emit xpkg emits it because upstream declares wix on the host axis, and the host axis is the one a descriptor can carry — but emitted is not needed. wix builds an MSI, upstream's rule tolerates its absence by construction (if (root.empty()) return {};), and upstream's manifest says an application wanting an installer "declares this line too". It was also breaking both Windows shards outright: xim:wix's install hook fails on a clean runner (tar -xf … wix.nupkg"wix installed but registered none of its declared programs"), taking every Windows consumer down for a tool almost none would run. That hook is a separate xim-pkgindex defect.

licenses and repo deliberately disagree with emit (Apache-2.0, Sunrisepeak/HuxerUI): the v0.3.0 tree's LICENSE is MIT verbatim and the canonical repo is HuxerUI/HuxerUI. Upstream's manifest is wrong on both and is being corrected separately; the descriptor is hand-maintained and carries no AUTO-GENERATED banner, so re-emitting will not silently reintroduce them.

The pin moves to 2026.9.7.1 — the floor, not the tip

huxerui's build program calls mcpp::package_name() / package_namespace() (mcpp#587, first released v2026.9.7.1), so under the old 2026.9.6.3 pin the host module does not compile at all:

rules.cppm:274:61: error: 'package_name' is not a member of 'mcpp'

This PR first moved to the current release, 2026.9.10.2, following #361. CI rejected that, and was right to: mysql-connector-cpp failed on linux default, linux llvm and macOS, deterministically, while every other member passed.

It is not that package's fault. mcpp's scanner errors on an ordinary block comment in libmysqlclient's sql-common/client_plugin.cc. Reduced to four lines:

/*
  module (exe)
*/
int main() { return 0; }
error: scanner errors:
  src/main.cpp:2: '(exe) ...

A line inside a block comment is read as a module declaration when the comment's opener sits on its own line — the same text on one line (/* module (exe) */) scans fine, and module x without the parenthesis scans fine either way. A comment that merely uses the English word "module" before a parenthesis breaks the scan.

Bisected with that file:

release result
2026.8.27.2 OK
2026.9.6.3 OK
2026.9.7.1 OK
2026.9.8.1 OK
2026.9.9.1 scanner errors
2026.9.10.2 scanner errors

So the regression is introduced in 2026.9.9.1, i.e. after 2026.9.7.1 — which is exactly the release that first carries package_name(). The floor and the last good version are the same release, so this takes the floor. Reported upstream; move to the tip once the scanner is fixed, and per the workflow's batching note that move should carry any other descriptor waiting on a newer engine.

index.toml min_mcpp does not move, verified rather than assumed — the descriptor parses under the floor and both candidate pins. The floor is about descriptor grammar; only building this package needs the newer engine.

Verification

mcpp test -p huxerui-module        test result ok. 1 passed     (2026.9.7.1)
mcpp test -p mysql-connector-cpp   test result ok. 1 passed     (2026.9.7.1)
same member on 2026.9.6.3          'package_name' is not a member of 'mcpp'
assertion inverted                 FAILED. 0 passed; 1 failed

The inverted-assertion check matters: the test opens no window, and Rect/Color are header-attached entities the module re-exports which would compile even if libhuxerui had never been built. FlatLightThemeSpec()/FlatDarkThemeSpec() are out-of-line definitions inside the library, so reaching them is what proves the link.

Earlier CI on this branch also confirmed the package cold: huxerui-module 317s ok on linux.

CN mirror

mcpp-res/huxerui@0.3.0, byte-identical to GLOBAL (http 200, 9946045 bytes, sha 8b326d95…). CI's mirror-cn-reachable passes.

Lint

All five checks pass on the new descriptor; the three repo-wide ones pass across pkgs/*/*.lua.

Design doc: .agents/docs/2026-09-11-add-huxerui-plan.md.


Final state, and what merging carries onto main

huxerui-module passes on all four legs — the first run where it does:

leg huxerui-module
linux default 352s ok
linux llvm 223s ok
macOS 101s ok
windows 160s ok

Windows took three independent fixes, and all three had to be in place at once: a wix recipe that does not depend on the host's tar (xim-pkgindex#809), an index cache that actually carried it (the MCPP_VERSION move — see §11 of the plan), and an MSVC STL that clang can compile (windows-2022).

⚠️ Three checks are red, and merging carries them to main

Stating this plainly so nobody has to reconstruct it from a red dashboard.

check member cause introduced by
workspace (windows default 0/5) vulkan STATUS_DLL_NOT_FOUND (0xC0000135) at run time — vulkan-1.dll is absent from the windows-2022 image this PR, via the image pin
workspace (windows default 1/5) eui-neo-vulkan, vulkan-hpp-module same this PR
workspace (linux llvm 3/9) mysql-connector-cpp install() result=nil, no scanner error; the gcc leg passes and so does a local run at 2026.9.11.2 pre-existing on this branch, cause not yet identified

The Vulkan three are the cost of the windows pin, not a defect in those packages: they compile fine and fail at load. The Vulkan loader is not a Windows component — it arrives with a GPU driver or the Vulkan SDK — so whether a runner has it is a property of the image. windows-2025-vs2026 has it; windows-2022 does not. compat.vulkan-runtime is shape G in this index precisely because a driver cannot be vendored.

So the windows pin is a trade, not a win: it moved one failure (huxerui, a compile error) and created three (Vulkan, load errors). It is merged in that state deliberately — the huxerui failure blocked the package this PR exists to add, and the Vulkan failures are understood, narrow, and independently fixable. A follow-up will try windows-2025 (VS2025 / MSVC 14.4x), which may clear both: old enough to avoid 14.51's _Find_vectorized, new enough to carry the loader.

Filed for the toolchain half: mcpp-community/mcpp#609.

Form A entry for the HuxerUI SDK plus its workspace member. The
descriptor is finished and `mcpp xpkg parse` accepts it -- "form A, no
mcpp segment". What is NOT finished is the thing the index actually
promises, that a consumer can build the package, so this is committed
as the record of where it stops rather than opened as a PR.

WHAT THE DESCRIPTOR DECIDES

`xpm.linux.deps` carries the 36-entry GTK closure by hand. Upstream
declares it on the target axis, which is the form mcpp recommends and
which a descriptor structurally cannot carry: three platform blocks, and
a cfg selector is not a platform. Consumer edges therefore come from
this file, at PLATFORM level -- a per-version `deps` is inert, the same
finding compat.eui-neo records. Omitting them would let a consumer
resolve, build, and die at link on the GTK sonames.

`licenses` and `repo` deliberately disagree with `mcpp emit xpkg`, which
emits Apache-2.0 and Sunrisepeak/HuxerUI. The v0.3.0 tree's LICENSE is
MIT verbatim and the canonical repo is HuxerUI/HuxerUI; upstream's
mcpp.toml is wrong on both and is being corrected separately. No `mcpp`
segment: pasting emit's table form makes the parser read this as an
inline Form B and refuse it for missing `sources`.

WHY IT CANNOT LAND

1. VERSION FLOOR. v0.3.0's mcpp/huxerui-build-rules calls
   `mcpp::package_name()` and `mcpp::package_namespace()`, added in mcpp
   #587 (2026-09-08) and first released in v2026.9.7.1. validate.yml
   pins MCPP_VERSION 2026.9.6.3, one release short, so the host module
   fails to compile: `'package_name' is not a member of 'mcpp'`.

2. ACTION BUFFER OVERFLOW, and this one survives a newer mcpp. On
   2026.9.10.2 the build gets past (1) and dies in build.mcpp:

       build.mcpp declared an action whose arguments did not fit
       payload: {"id":"hrc:builtin", … "overflow":true}

   `mcpp::action` uses fixed buffers -- `inputs_[8192]` -- and the
   hrc:builtin action enumerates all 44 files under resources/. Measured:
   as a dependency those paths total 8131 bytes, plus ~106 for the hrc
   binary, against a 8192-byte cap. In-tree the same list is 1531 bytes,
   and the same source builds clean in 21.45s.

   So the trigger is the 149-character dependency unpack prefix, not the
   file count, and it overflows by roughly 45 bytes. That is worse than a
   clean failure: whether a consumer builds depends on how deep their
   project sits on disk. mcpp's own error names the fix -- declare the
   resources DIRECTORY as one input instead of enumerating it -- and that
   belongs in huxerui-build-rules, not here.

No CN mirror yet: mirroring a version that cannot be consumed would
publish a promise this index cannot keep.
The descriptor and its member landed in the previous commit as a record
of two blockers. One turned out not to be a blocker at all and the other
is fixed here, so this completes the addition.

THE OVERFLOW WAS THE MEASUREMENT, NOT THE PACKAGE

The `hrc:builtin` action was reported as overflowing mcpp's fixed
`inputs_[8192]`. It does -- at a 149-character unpack prefix. Measured
across three path lengths, the same source and the same engine:

    149 chars (deep local checkout)  8131 B + ~106 B   overflows
    109 chars (ordinary path)        6371 B            builds
    in-tree, relative                1531 B            builds, 21.45s

So the trigger is the prefix, not the file count, and it clears at any
ordinary checkout depth. It is still a real trap -- roughly 45 bytes of
margin, decided by how deep a consumer's project sits -- and mcpp's own
error names the fix (declare the resources DIRECTORY as one input rather
than enumerating 44 files). That belongs in huxerui-build-rules and is
filed there; working around it in this index would be the wrong place.

THE PIN, WHICH IS A REAL BLOCKER AND MOVES HERE

`mcpp/huxerui-build-rules` calls `mcpp::package_name()` and
`mcpp::package_namespace()` -- mcpp#587, first released in v2026.9.7.1 --
so under the old 2026.9.6.3 pin the host module does not compile:

    rules.cppm:274:61: error: 'package_name' is not a member of 'mcpp'

#361 established the pattern for exactly this, down to the reasoning: a
package whose build program uses a current engine API is not a defect, a
CI that cannot run current engines is. MCPP_VERSION moves to 2026.9.10.2.

`index.toml` min_mcpp does NOT move, and that is verified rather than
assumed: `mcpp xpkg parse` accepts this descriptor under 2026.8.27.2 (the
floor), 2026.9.6.3 and 2026.9.10.2 alike. The floor is about descriptor
grammar; only BUILDING this package needs the newer engine, so a client
on the floor keeps resolving the whole index.

VERIFIED

    mcpp test -p huxerui-module          → test result ok. 1 passed
    same, on 2026.9.6.3                  → 'package_name' is not a member
    assertion inverted                   → FAILED. 0 passed; 1 failed

The last one matters: the test opens no window, and Rect/Color alone
would compile even if libhuxerui had never been built. FlatLightThemeSpec
and FlatDarkThemeSpec are out-of-line definitions inside the library, so
reaching them is what proves the link -- and inverting the check proves
the assertion is live.

CN mirror mcpp-res/huxerui@0.3.0 is up and byte-identical to GLOBAL
(http 200, 9946045 bytes, sha 8b326d95…). All five lint checks pass on
the new descriptor, and the three repo-wide ones pass across pkgs/*/*.lua.

The pin move pays the cold-cache cost the workflow comment warns about,
so any other descriptor waiting on a newer engine should ride along.
…osure

Two corrections the PR's own CI earned.

NO xim:wix ON WINDOWS

`mcpp emit xpkg` emits `deps = { "xim:wix@5.0.2" }` because upstream
declares wix on the HOST axis, which is the axis a descriptor can carry.
Emitted is not the same as needed: wix.exe builds an MSI, nothing else in
the SDK touches it, upstream's rule tolerates its absence by construction

    inline ... wix() {
        const std::string root = mcpp::xpkg_dir("xim", "wix");
        if (root.empty()) return {};

and upstream's manifest says an application that wants an installer
"declares this line too". The dependency belongs to that application, not
to everyone who imports huxerui on Windows.

It was also breaking both Windows shards outright -- `xim:wix`'s install
hook fails on a clean runner:

    E_INTERNAL: [wix] failed: install hook failed:
    tar -xf "...\xim-x-wix\5.0.2\.nupkg\wix.nupkg" -C "...\tool"
    ; wix installed but registered none of its declared programs

taking every Windows consumer down with it for a tool almost none would
run. That hook is a separate xim-pkgindex defect; this descriptor should
not have been asking for wix either way.

FOUR DEPS, NOT THIRTY-SIX

The list existed to guarantee the payloads are INSTALLED, and installation
is transitive: each xim package declares its own deps, and `xlings remove
pango` is refused, naming `xim:gtk4` as the holder, so the reverse edges
are live. Discovery is a different mechanism -- `mcpp::xpkg_dir` answers
from the BUILDING package's own manifest, and upstream's mcpp.toml already
declares the whole closure on its target axis.

So the descriptor needs only what upstream's cmake and build.mcpp ask
pkg-config for directly. Verified by clearing the member's sandbox and
rebuilding with the short list: `test result ok. 1 passed`. Naming the
other 32 restates pins this index does not own.

Note the asymmetry with the xim-pkgindex entry, which is NOT a mistake:
there the deps drive both installation and, through the aggregated
sysroot pkgconfig view, discovery. Here upstream's manifest owns
discovery. Same four names, different reasons.

The pin move and the mysql-connector-cpp regression it surfaces are a
separate matter, still under investigation.
… scanner regression

The pin moved to 2026.9.10.2 on the reasoning that #361 took the current
release. CI disagreed: `mysql-connector-cpp` failed on linux default,
linux llvm AND macOS, deterministically, while every other member passed.

It is not that package's fault, and not a dependency problem. mcpp's
scanner errors on an ordinary block comment in libmysqlclient's
`sql-common/client_plugin.cc`. Reduced to four lines:

    /*
      module (exe)
    */
    int main() { return 0; }

    error: scanner errors:
      src/main.cpp:2: '(exe) ...

A line inside a block comment is read as a module declaration when the
comment's opener sits on its own line -- the same text on one line
(`/* module (exe) */`) scans fine, and `module x` without the parenthesis
scans fine either way. So a comment that merely uses the English word
"module" before a parenthesis breaks the scan.

Bisected across releases with that file:

    2026.8.27.2  OK          2026.9.7.1   OK
    2026.9.6.3   OK          2026.9.10.2  scanner errors

The regression therefore lands AFTER 2026.9.7.1 -- which is precisely the
release that first carries `mcpp::package_name()`, the API huxerui's build
program needs. The floor and the last good version are the same release,
so this takes the floor.

Verified at 2026.9.7.1, both members that mattered:

    mcpp test -p huxerui-module       test result ok. 1 passed
    mcpp test -p mysql-connector-cpp  test result ok. 1 passed

Reported upstream. Move to the tip once the scanner is fixed -- and per
the batching note above, that move should carry any other descriptor
waiting on a newer engine.
…t a dep

The plan still described the first attempt: pin at 2026.9.10.2 and
`xim:wix` kept as emitted. CI rejected both, and the reasons are worth
keeping rather than quietly overwriting.

- The pin is 2026.9.7.1, the FLOOR, because a scanner regression lands in
  2026.9.9.1 (still present in 2026.9.11.1) and breaks mysql-connector-cpp.
  Four-line repro and the bisect are recorded; mcpp-community/mcpp#606.
- windows carries no `xim:wix`: emitted is not needed, and its install
  hook fails on a clean runner. xim-pkgindex#808.
- A section on what CI caught that local verification structurally could
  not, including the one failure still open (mysql-connector-cpp on the
  llvm leg) and where huxerui itself stands per leg.

This also re-triggers CI, which now resolves `xim:wix` from an index
snapshot that carries the #808 fix.
…osis

The plan credited xim-pkgindex#808 with fixing the Windows failure. It did
not: keeping the host's tar and adding a PowerShell fallback only moved the
error from 'exec failed ... tar -xf' to 'registered none of its declared
programs'.

What settled it was already in the first log -- curl ran fine in the same
hook moments before tar did not, so the payload was downloaded and
sha-verified before execution ever reached the extractor. One is a declared
dependency; the other was the host's. xim-pkgindex#809 declares xim:7zip and
extracts with it.

Recording the wrong turn rather than only the answer: the fallback in #808
is exactly the shape that let this hide in the first place.

Also re-triggers CI, which now resolves xim:wix from a snapshot carrying
the #809 fix.
…ndex's cache key

Two separate reasons, and the second one invalidates a conclusion this
branch reached twice.

THE SCANNER REGRESSION IS FIXED

2026.9.7.1 was chosen as the FLOOR because 2026.9.9.1 introduced a scanner
regression — mcpp read inside a block comment and choked on
libmysqlclient's `module (exe or dll) …`, breaking mysql-connector-cpp on
linux and macOS. Reduced to four lines, reported as
mcpp-community/mcpp#606, and fixed in 2026.9.11.2. Verified all three:

    four-line scanner repro           builds clean
    mcpp test -p huxerui-module       test result ok. 1 passed
    mcpp test -p mysql-connector-cpp  test result ok. 1 passed

So the pin no longer has to sit four releases behind to stay green.

THE PIN IS THE ONLY WAY TO REFRESH THE INDEX

`xim:wix` was fixed twice upstream — xim-pkgindex#808, then #809 — and
after each merge the windows leg here failed identically. Both times that
was read as "the fix did not work". Neither had ever been loaded.

`actions/cache` holds `~/.mcpp/registry`, which contains
`data/xim-pkgindex` — the resolved xim index — and its `restore-keys`
prefix stops at MCPP_VERSION. While that line does not move, every run
restores the same index snapshot no matter what landed upstream. The tell
was in the log: `7zip`, which #809 declares, appeared ZERO times in a run
supposedly testing #809.

Two guesses along the way were wrong and are recorded in the plan so they
are not repeated: that the release tarball bundles a frozen index (a
pristine extraction has no `xim-pkgindex` at all — it is populated at run
time), and that a merge therefore propagates by itself.

Moving this line evicts that cache. It is the only reason the windows leg
can now see the fixed recipe, and that makes this pin move load-bearing
twice over.

`index.toml` min_mcpp still does not move: the floor is descriptor GRAMMAR,
and the descriptor parses under 2026.8.27.2, 2026.9.6.3, 2026.9.7.1 and
2026.9.11.2 alike.
…ndency sets

The member declared no `standard`, so it defaulted to c++23 -- and mcpp
applies the ROOT's standard to the whole module graph, which meant
huxerui's own sources compiled at c++23. Upstream's mcpp.toml says not to:

    standard = "c++20"
    # The SDK ABI baseline. Do not raise it: mcpp applies the root standard
    # to the whole module graph, so this is imposed on every consumer too.

`import std;` still works -- huxerui is `language = "c++20", import_std =
true`, and the member's test passes at c++20 (`test result ok. 1 passed`).

THIS IS NOT EXPECTED TO FIX WINDOWS, and saying so up front is the point.
The windows leg fails compiling huxerui's
`src/runtime/runtime_pointer_interaction.cpp`:

    xutility:320: error: static assertion failed: unexpected size
    in instantiation of 'std::_Find_vectorized<
        const huxerui::detail::NodeExtensionHandle, ...>'

`NodeExtensionHandle` is 24 bytes (two 64-bit integers and a pointer),
trivially copyable, with a defaulted `operator==`. MSVC STL 14.51 therefore
treats it as memcmp-comparable and dispatches `std::find` to
`_Find_vectorized`, which handles 1/2/4/8-byte elements and static_asserts
on anything else. That dispatch is not gated on the language mode, so the
standard this commit sets does not reach it: it is a clang 20.1.7 against
MSVC STL 14.51 incompatibility in a header, reached through upstream's
source, and nothing in this index can express a fix for it.

Committed because it is correct on its own terms -- a consumer should not
silently raise the ABI baseline a dependency pins -- not because it is the
windows fix.
…g cannot compile

macOS is pinned (`macos-15`) and linux is not; windows was on the rolling
label and it rolled onto the Visual Studio 18 image, MSVC STL 14.51.

mcpp builds windows with clang targeting x86_64-pc-windows-msvc, so every
package here compiles against an STL written for a different front end.
Most survive it. `huxerui.huxerui` did not:

    xutility:320: error: static assertion failed: unexpected size
    in instantiation of 'std::_Find_vectorized<
        const huxerui::detail::NodeExtensionHandle, ...>'

MSVC STL's vectorized `std::find` is guarded by a trait deciding whether
the element type can be compared bitwise. `NodeExtensionHandle` is 24
bytes, no padding, trivially copyable, `operator==` defaulted — the guard
admits it under clang, and the helper it dispatches to implements 1/2/4/8
byte elements and static_asserts on the rest. Guard and implementation
disagree about what "vectorizable" means; only clang is there to notice.

NOT this index's bug and not the package's, and the evidence is the pin
itself: the same source and the same clang compile on the 2022 image.
Upstream HuxerUI's own mcpp CI is green for exactly that reason — it
already pins `windows-2022`. Filed as mcpp-community/mcpp#609 so the pin
can be lifted when the combination works again.

THE NARROWNESS IS THE ARGUMENT, not a reason to shrug: 13 of the 14
members on the failing shard compiled fine. A rolling label therefore
changes WHICH packages trip over this from image to image, and delivers
the failure attributed to whichever descriptor changed that week. Pinning
turns a moving target into a dated, documented decision.
…ot in the build

Section 4 already argues this descriptor should not declare `xim:wix` --
wix builds an MSI, and a consumer that merely imports huxerui has no use
for it. That is right but incomplete: it treats the declaration as
misplaced when the mechanism it belongs to simply did not exist yet.

mcpp 2026.9.11.1 opened the `--format` value set. `mcpp pack` owns the
mechanism and the universal formats; every other format lives in a package
that declares `mcpp::provides_pack_format("<name>")` in its build program,
unconditionally, and submits its action only when asked. Under that,
`xim:wix` stops being a top-level `[xlings.workspace]` entry provisioned
for every build of every consumer, and becomes a dependency of the feature
that provides `msi` -- reached only by someone actually packing one.

The timeline is the whole explanation:

    HuxerUI v0.3.0 released                 2026-09-10 17:36 UTC
    mcpp 2026.9.11.1 released, --format     2026-09-10 20:34 UTC

`provides_pack_format` appears zero times in v0.3.0's build.mcpp and
rules.cppm. Not a road not taken -- a road that did not exist, by under
three hours.

Recorded rather than acted on: the v0.3.0 tag is immutable and upstream is
out of scope here. What the descriptor CAN do is behave as though the move
had already happened, which is what not declaring wix does -- so a future
0.3.1 needs no change on this side.

Also notes that the two Windows problems are independent: with wix gone
from the build entirely, runtime_pointer_interaction.cpp still does not
compile against MSVC STL 14.51.
@Sunrisepeak
Sunrisepeak merged commit 18dfa0b into main Sep 11, 2026
23 of 27 checks passed
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.

1 participant