Commit Graph
240 Commits
Author SHA1 Message Date
Dario Gabriel LipicarandClaude Opus 5 9259fe86a1 fix: a protocol version that will not parse must not silently vanish
A Rust module's EXPORT SET is decided by the --protocol-version string:
lidl-gen gates logos_module_grant_host_services on >= 0.3 and the teardown
pair on >= 0.5. It was passed as

    ${lib.optionalString (protocolVersion != null) "--protocol-version ..."}

and that does not make a bad version WRONG — it makes the flag VANISH.
lidl-gen then falls back to "0.1.0", emits the seven founding exports, and
exits 0. Every Rust module in the workspace would quietly regenerate
incomplete, link cleanly, and fail at dlopen() on Linux with an undefined
symbol — invisible on macOS, three repos away from the cause, and reported
by the runtime as a module that LOADED.

protocolVersion goes null in two different situations and they are two
different bugs, so they now throw with two different messages: no
logos-protocol input at all (a caller error), versus a header that did not
parse (a bug in the regex right above it). Neither is a thing to fall back
from.

checks.module-impl-abi-nm (#207) already DETECTS this by reading the built
plugin's symbol table. This is the other half — refuse at the point of the
mistake, so it never reaches a build. Metadata stamping is untouched: null
there legitimately means "pre-protocol, load as legacy".

Proven: renaming the macro in the split pattern used to yield a
silently-broken plugin. It now yields

    error: logos-module-builder: could not read LOGOS_PROTOCOL_VERSION_STRING
    from /nix/store/...-source/cpp/logos_protocol.h, needed to generate the
    Rust cdylib scaffold for 'rust_native_dep_module'. The header moved or
    changed shape — fix the parse above; do NOT let it fall back, because
    the fallback silently emits an incomplete module-impl C ABI.

Also deletes a dead duplicate of the same parse. It sat in the devShells
block with no consumer anywhere in it — an exhaustive grep finds exactly
four mentions of protocolVersion: this definition, the two uses in
`packages`, and that orphan. A duplicated, half-dead parse of the value
this whole failure mode hinges on is not something to leave lying around.

All eight checks green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 16:27:11 -03:00
Dario Gabriel LipicarandClaude Opus 5 26a886bb7b test(abi): read the module-impl exports off the BUILT plugin, per backend
The source-level checks that just landed (logos-cpp-sdk#144,
logos-rust-sdk#46) validate each GENERATOR against the protocol header.
Three things they structurally cannot see, all of which end as the same
"undefined symbol" at dlopen:

  * THE CARRIER. lib/mkLogosModule.nix:383 derives protocolVersion by
    regexing the protocol header and :483 passes it through
    lib.optionalString (protocolVersion != null). A regex miss does not
    make the flag wrong, it makes it VANISH — logos-lidl-gen then defaults
    to "0.1.0" and every Rust module in the workspace regenerates with only
    the seven founding exports. Each backend's own check still passes,
    because each passes the version correctly.
  * EMITTED BUT NOT COMPILED IN — cfg-gated out, gc-sectioned, lost to link
    order. Present in the generator's output, absent from the artifact.
  * A LANGUAGE BACKEND NOBODY WIRED A CHECK INTO (the Nim path in #202).

So build a module per backend and read its symbol table:

  (1) every export logos-protocol DECLARES is DEFINED in the plugin;
  (2) NO logos_module_* symbol is UNDEFINED — literally "will this dlopen".

Both fire independently on the same real defect, which is why both are here.

The part that makes this worth its build time: nm sees an undefined symbol
on BOTH platforms, while the runtime failure is Linux-only (nixpkgs hardens
with -Wl,-z,now; macOS links plugins -undefined dynamic_lookup and never
binds). So a defect that is fatal in CI and invisible on a developer's Mac
becomes catchable on that Mac.

Proven non-redundant, not merely proven to fail. Driving protocolVersion to
null leaves all seven existing checks GREEN — six of them produce
byte-identical store paths, never reaching the affected code, and the
seventh, rust-native-dep, rebuilds the very same broken plugin and still
passes, because its only assertion is that the output directory exists.
This check goes red naming the three missing exports.

Anti-vacuity is the bulk of the file, since a check that cannot fail is
worse than none: the plugin is located by a glob that must match EXACTLY
one; zero defined logos_module_* symbols is a hard failure printing the raw
table; the undefined side cannot self-check (empty IS the pass) so the raw
table is asserted non-empty first; and grep's "no match" is distinguished
from a real error. Each was demonstrated to fire.

Also lists rust-native-dep in CI. It was excluded over a protocol pin that
has since moved, and this step already compiles the same fixture.

The logos-protocol pin moves 0d2a3c0 -> 480f40f because
packages.<sys>.module-impl-abi did not exist before it. flake.nix throws a
named error if a future pin ever predates that output.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 15:57:30 -03:00
Dario LipicarandClaude Opus 5 bdc9804efb chore(deps): relock logos-rust-sdk onto the teardown exports (#45) (#206)
logos-rust-sdk 8d44040 -> 49dbb07

#204 bumped logos-protocol to 0d2a3c0 (ABI 0.5, which DECLARES the teardown
pair) and logos-plugin-qt/core to 55713b9 (the cdylib glue that CALLS it), but
left logos-rust-sdk at 8d44040 — the one repo that owes the DEFINITION. Two of
the three halves of the change landed, so every Rust module built through this
builder came out with `logos_module_about_to_unload` and
`logos_module_set_unload_done_callback` undefined.

That links clean: an undefined symbol in an ELF shared object is legal, and the
Rust side is pulled in as a static archive, so nothing complains at build time.
Then nixpkgs hardens with -Wl,-z,now, dlopen has to bind eagerly, and the module
fails to load. macOS links plugins -undefined dynamic_lookup and never binds, so
it stayed green throughout and proved nothing.

Master has been red since #204 on that exact split — run 32441757256, ubuntu
117 passed / 14 failed, macOS clean. The tell from the doctest side is quiet:
`load-module cpp_frontdesk_module` still answers status "ok", but the Rust
dependency is simply ABSENT from `dependencies_loaded`, and every call after it
fails METHOD_FAILED / RPC_FAILED. A name missing from `dependencies_loaded` on
Linux only is a cdylib that failed dlopen.

The `#if LOGOS_PROTOCOL_VERSION_MINOR >= 5` guard in the glue does not protect
against this. It tracks the version of the headers the GLUE compiled against,
not whether this particular cdylib exports the symbol, so host-new plus
cdylib-old is precisely the case it lets through.

Verified rather than assumed: the generated scaffold now emits both
`#[no_mangle] pub extern "C"` definitions, and `nm -g` on the built
rust_native_dep plugin reports both as T (defined) where they were previously
absent. checks.rust-native-dep is green.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 10:22:05 -03:00
Dario Lipicar 3711449044 chore(deps): relock logos-standalone-app onto the cycle cut (#205)
Final level of the chain that started at logos-capability-module#25. This repo
was the head of a dependency cycle that ran back to itself:

  logos-module-builder -> logos-standalone-app -> logos-liblogos
    -> logos-capability-module -> logos-module-builder -> ...

flake.lock cannot express a cycle, so Nix unrolled it until the refs reached a
fixed point -- 5 repetitions, 23 levels deep -- duplicating the whole subtree at
every level. That was 3,261 of this lock's 3,390 nodes.

  3,390 -> 360 nodes   (2.37 MB -> 0.21 MB, -82,082 lines)

Exactly one root input moves: logos-standalone-app 13b81c9 -> 5f6784f. All 16
others resolve to the byte-identical rev they did before.

Every downstream module and app picks this up on its next lock bump.
2026-08-20 23:59:22 -03:00
Dario LipicarandClaude Opus 5 88e2f8f2b5 chore(deps): bump the SDK chain onto master (#204)
Six inputs move to their current masters, so modules pick up work that has
landed across the SDK chain and been stuck behind this lock:

  logos-cpp-sdk    95d7b3a -> 667990f
  logos-qt-sdk     19c844f -> 4a1104c
  logos-rust-sdk   a3d0d71 -> 8d44040
  logos-protocol   f4407ff -> 0d2a3c0
  logos-plugin-qt  9b2c64e -> 55713b9
  logos-plugin-core 9b2c64e -> 55713b9

What this carries into module builds, concretely:

  - The lp consumer surface gains `<name>AsyncResult` (logos-cpp-sdk#142), so a
    Qt-free module can finally tell a failed async call from a provider that
    legitimately returned nothing.
  - A provider REJECTION now reaches the caller's error channel instead of
    decoding into a default value — on the C++ sync and AsyncResult surfaces
    (cpp-sdk#142) and on all four Rust consumer paths (logos-rust-sdk#44).
  - logos::LpClient no longer races on lazy client creation, and rust-sdk no
    longer holds its cache mutex across lp_client_create (which blocks on the Qt
    main thread, so a worker could deadlock against the dispatch thread).
  - logos-qt-sdk's LpBridge drops the second lp_client it kept per
    (origin, target) purely because LpClient had no error-carrying async, and
    the never-destroyed leak that came with it (logos-qt-sdk#36).

lock-only: flake.nix is untouched, so no `follows` or input URL changes ride
along. The cdylib generator migration this bump used to require already landed
here (logos-qt-host-generator at both call sites, LOGOS_QT_HOST_ROOT wired, and
the CMake probe switched off the retired logos_api.h sentinel), which is why
this is now a one-file change.

Verified: all five checks green on aarch64-darwin — default, qml-integration,
rust-native-dep, static-extlib, test-framework-integration. rust-native-dep and
test-framework-integration are the two that fail against these masters without
the migration above, so they are the ones that actually exercise the bump.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 21:35:28 -03:00
Khushboo Mehta 378bdba4ed chore: bump design system 2026-08-20 09:45:48 +02:00
Dario LipicarandClaude Opus 5 8cd62c7427 feat: take the view templates from logos-view-module (#203)
* fix(cmake): one LogosModule.cmake, the union of the two that had diverged

There were two copies — here and in logos-plugin-qt — and the received wisdom
that "the builder's shadows the backend's" is wrong. Selection is BY MODULE
CLASS, and each copy was missing what the other had:

  mkLogosModule.nix:79      tests the BUILDER root, always true, so CORE
                            modules take this copy;
  buildCppPlugin.nix:191    tests the MODULE'S OWN src, almost never true, so
                            every ui_qml module falls through to the backend's.

This copy had no REP_FILE, no logos_replica_factory, no LogosViewPluginBase — a
core module passing REP_FILE had it silently swallowed into
MODULE_UNPARSED_ARGUMENTS. The backend's had no generated_code/*.cpp glob, no
metadata.json configure_file, no LOGOS_API_STYLE and no Rust static-lib block.

Merged as a strict union, 721 -> 888 lines. logos_find_qt stays a macro(): the
function() form is the older one, and the macro IS the mingw fix. The four
LogosView*.in templates come along because the replica-factory function
resolves them as siblings through CMAKE_CURRENT_FUNCTION_LIST_DIR.

Prerequisite for relocating the view-plugin templates, not a cleanup — that
move cannot be reasoned about while two files disagree about what a view module
even is.

Verified: a core module (test_basic_module_cpp) and a ui_qml module
(test_fullapi_ui) both build through this copy, and all five of the builder's
own checks pass, including rust-native-dep, which is what exercises the
LOGOS_MODULE_RUST_STATIC_LIBS block the merge carried.

NOTE this de-duplicates nothing yet: buildCppPlugin.nix still routes ui_qml at
the backend's copy, so every in-tree REP_FILE consumer still compiles against
logos-plugin-qt's. Flipping that routing, and having the backend re-export this
file, is the follow-up.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(qml): resolve interface_dependencies in the QML pipeline

buildCppPlugin.nix never resolved interface_dependencies, so a ui_qml module's
interfaces reached the generator only through cpp-generator's own re-read of
metadata.json — invisible to the plugin backend, and silently skipping any entry
with an `input:` (a cross-repo interface), which can only be resolved to a path
by the builder.

Now resolved and passed as interfaceDeps, exactly as mkLogosModule.nix does for
core modules. Needed because the qt consumer wrappers are now emitted per
dependency and per interface by the backend, which has to be told what they are.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: retire interface "provider"; throw instead of generating nothing

Drops providerCodegen and the PROVIDER_HEADER plumbing in LogosModule.cmake.

autoCodegen ends in a catch-all `else ""`, so simply deleting the branch would
make `interface: "provider"` mean "generate no glue at all": the module builds
green and is then un-callable from every consumer, with nothing in the log to
say why. It throws instead, naming the replacement.

tests/test-module-pre-configure.nix covers exactly that: a mutation control
confirms the suite fails with `expected expression to throw, but it succeeded
with ""` when the throw is removed. 258 -> 265 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: parse and validate metadata.json#host_services (C2)

A closed set — token_registry, token_delivery, dynamic_calls — parsed and
VALIDATED, unlike the older `capabilities` field, which parseMetadata.nix never
reads. A security decision must not ride on a key nothing looks at, which is why
this is a new field rather than a new meaning for that one.

Two tiers, and the distinction is the point:

  * token_registry / token_delivery are TRUST-ROOT — a module holding them can
    enumerate the token store or hand authority to an arbitrary target — so they
    are additionally restricted to a hardcoded allowlist of module names
    (capability_module). Hardcoded rather than configurable: an allowlist a
    module could extend from its own metadata would not be an allowlist.
  * dynamic_calls is elevated but NOT name-restricted, because a third-party
    module that genuinely forwards untyped calls (a webview shell) has to be
    able to ask for it.

An unknown service name is refused OUTRIGHT rather than filtered out: it means
the module believes it holds a privilege that does not exist, and silently
dropping the entry hides that from whoever wrote it. Same reasoning as
lp_grant_host_services, which rejects an unknown name wholesale and leaves the
existing grant untouched.

The declaration is advisory. Authority is the host's grant, pushed into the
module's own image over the module-impl C ABI.

265 -> 273 tests. The allowlist case carries a mutation control: with the
trust-root check removed the suite fails with `expected expression to throw, but
it succeeded with ... "name":"sneaky_module","host_services":["token_registry"]`,
so the test genuinely guards the boundary rather than restating it. Includes the
bypass attempt of mixing a permitted service in with a restricted one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: emit cdylib glue with logos-qt-host-generator, not qt-sdk's stale copy

B1 relocated the cdylib Qt-plugin glue generator to logos-plugin-qt (the Qt
plugin BACKEND owns the glue; the SDK does not), but universalCodegen and
cdylibCodegen still called `logos-qt-generator` from logos-qt-sdk, which ships
an OLDER copy of the same emitter. Both copies compile and both emit working
glue, so nothing failed — the builds simply used stale glue.

That is not theoretical. It is why the Phase C host-services grant never
reached a module: the new glue reads the `hostServices` property and forwards
it across the C ABI, the old one does not, and every build stayed green while
capability_module refused every requestModule for want of a grant that was
delivered to its process and then dropped on the floor.

Measured: `hostServices` appears 4 times in logos-plugin-qt's copy and 0 times
in logos-qt-sdk's. After the switch the built capability_module plugin
references logos_module_grant_host_services twice (the export plus the glue's
call) where it previously referenced it once.

Threads the plugin-qt FLAKE (not its lib — the generator is a package of it)
from flake.nix through lib/default.nix into buildCppPlugin, mkLogosModule and
mkLogosModuleTests, and puts logos-qt-host-generator on PATH everywhere
logos-qt-generator already was. `--backend ui` still uses qt-sdk's generator:
the view backend has not moved.

logos-test-modules ipc-tests: FAIL -> PASS, which is the end-to-end proof that
a universal capability_module now mints tokens under a host-granted privilege.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix: thread logos-plugin-qt through the ui_qml build path too

mkLogosQmlModule imports buildCppPlugin at its OWN call site, separate from
lib/default.nix's, and that one was left unwired — so every ui_qml module with a
C++ backend hit `logos-plugin-qt = null` and failed to evaluate.

Caught by the regression batch: logos-accounts-ui went from building to FAIL
while all five test-modules checks passed, which is the shape of a defect in one
module CLASS rather than in the generator switch itself.

logos-accounts-ui builds again.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test: pin the cdylib glue to the maintained generator

The existing assertion ("universal still emits the cdylib Qt glue") checked for
the string `logos-qt-generator` and so failed the moment the builder switched to
`logos-qt-host-generator` — the test doing its job. Retargeted at the invariant
that actually matters (`--backend cdylib` is still emitted) and extended into a
drift guard:

  * both codegen paths MUST name logos-qt-host-generator
  * neither may fall back to `logos-qt-generator ` (trailing space, so it cannot
    match the host generator; `--backend ui` still legitimately uses qt-sdk's)

Worth the four assertions because the failure they catch is invisible: both
copies of the emitter compile and both produce loadable glue, so reverting to
qt-sdk's would emit stale glue and stay green — which is precisely how the
host-services grant went undelivered for a whole phase.

265 -> 277 tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* test(fixtures): declare universal authoring where the fixture already claimed it

The `core-universal-module` fixture is named "universal", is described as a
"Universal interface module fixture (mirrors logos-accounts-module pattern)",
and sits under a test section headed "Universal interface core module" -- but
it carried no `interface` key at all, so parseMetadata resolved it to
"legacy". The fixture was not testing the thing its name claims. The real
logos-accounts-module is `interface: universal`.

Declare `interface: universal` on the three `type: core` fixtures whose
legacy-ness was incidental, and pin the value so it cannot silently regress:

  core-universal-module  mirrors logos-accounts-module (universal)
  core-module            mirrors the minimal template (universal)
  extlib-module          mirrors the external-lib template and the real
                         vendor-extlib core modules (storage/wallet/libp2p/
                         blockchain) -- all universal

No `type: core` module in the workspace is still legacy-authored.

The other four legacy fixtures are LEFT legacy on purpose, and are now pinned
with an assertion + the evidence, so the coverage is deliberate instead of
accidental:

  ui-qml-backend-module  mirrors logos-package-manager-ui, which is STILL
                         legacy; the universal ui_qml shape is already covered
                         on disk by the ui-qml-backend TEMPLATE
  qml-module             QML-only: no C++ backend to derive a contract from;
  module-with-deps       mkLogosQmlModule never reads `interface`, and
                         universal+ui_qml routes to uiCodegen which demands a
                         .rep neither fixture has
  ui-module              `type: ui` has no template and one real instance
                         (logos-basecamp), which is legacy

These are metadata-parse fixtures only -- none of the three edited fixtures
contains any C++, so this is a metadata-shape correction, not a code port.
No fixture in this repo held a hand-written Qt plugin to migrate.

Verified: all 5 checks run BY NAME (default, qml-integration, rust-native-dep,
static-extlib, test-framework-integration) pass before and after. `default`
goes 277 -> 284 assertions, exactly the 7 added. qml-integration,
static-extlib and test-framework-integration produce byte-identical store
paths to baseline. rust-native-dep's path changes only because mkLogosModule
embeds the builder's own flake source (its fixture src is byte-identical:
xa6m5ci38fc5adcbi0hfyqvgzrmyr3j9) -- attributed by rebuilding with the change
stashed. Positive control: reverting core-universal-module to legacy makes
`default` FAIL on the new assertion, so it bites.

NOT verified: no plugin is built from any edited fixture (they are metadata
only), so there is no `lm methods` surface to diff -- the method-surface check
does not apply here. Nothing downstream of this repo was rebuilt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: hand buildHeaders the qt generator, so a contract can drive the wrapper

`contractLidl` already reached buildHeaders on every call — the qt backend just
declined to read it outside cross-compilation. What was missing was the tool:
logos-qt-generator went into buildPlugin's extraNativeBuildInputs and nowhere
near buildHeaders, so the contract-driven branch could not have run even if it
had been selected.

Passing it is the whole change on this side. `logosQtGenerator` is already the
BUILD-platform binary (buildSystemFor), which is what a cross build needs from
a pure tool role, and the backend puts only the generator its selected emitter
actually uses on PATH — so an lp variant and a contract-less module keep the
derivation they had.

Deliberately NOT passed to moduleIncludeLp: logos-qt-generator has no lp
backend. The lp wrapper still comes from logos-cpp-generator's lp emitter,
which is not the legacy Qt one and is not what this migration is about.

buildCppPlugin — the ui_qml pipeline — is left on the legacy path and now says
why. It computes no `lidl` output for the module it builds, so it has no
contract to hand over; and it costs nothing today, because mkLogosQmlModule
reads `moduleLib` and never `moduleInclude`, so that derivation is never
realised. A ui_qml module is a leaf and nothing consumes its client wrapper.
Fixing it therefore starts with giving that pipeline a contract, not with
adding a flag here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: link the Qt host runtime from logos-qt-host, not logos-qt-sdk

The Qt host runtime -- LogosAPI, LogosAPIProvider, LogosProviderBase and the
legacy PluginInterface -- moved out of logos-qt-sdk into logos-plugin-qt, where
it ships as the `logos-qt-host` CMake package. LogosModule.cmake now takes it
from LOGOS_QT_HOST_ROOT and links logos-qt-host::logos_qt_host; mkLogosModule,
buildCppPlugin, mkLogosModuleTests and the module dev shell all pass that root.

logos-qt-sdk is NOT dropped. It stays required for the Qt-typed headers that
were never part of the host runtime -- logos_qt_lp_bridge.h and logos_qt_wire.h,
which the generated Qt consumer wrappers #include by name, and
logos_ui_plugin_context.h -- and for the logos-qt-generator that emits them. Its
include dirs are kept, but AFTER the host runtime's, so the host headers win the
five names the two roots share.

mkLogosModuleTests now also passes -DLOGOS_QT_HOST_ROOT to logos-test-framework,
whose LogosTest.cmake already prefers it and falls back to LOGOS_QT_SDK_ROOT.

logos-plugin-qt's logos-protocol input gains a `follows` (as logos-qt-sdk's
already had). Building logos-qt-host makes that input load-bearing for the first
time, and a second logos-protocol on the link line would mean a second
TokenManager singleton.

Nothing here fails open. A LOGOS_QT_HOST_ROOT with no runtime under it is a
FATAL_ERROR rather than a fall-through to logos-qt-sdk; a host package that
resolves without defining its target is a FATAL_ERROR rather than a plugin
linked without a runtime; and the legacy logos-qt-sdk fallback, which stays
available while other consumers migrate, announces itself in the configure log.
The new `qt-host-repoint` check pins all four selection paths plus both guards.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(b2b): probe logos-qt-sdk by a header it owns, and drop the qt-sdk fallback

Two changes, both forced by logos-qt-sdk no longer forwarding the host runtime's
headers.

The presence probe tested ${LOGOS_QT_SDK_ROOT}/cpp/logos_api.h or
include/cpp/logos_api.h. Neither exists any more: the host split moved
logos_api.h out of qt-sdk's cpp/, and the forwarder that kept it in include/cpp/
is gone. A correct root would have been reported as "logos-qt-sdk not found"
while CMake was looking straight at it. It now probes logos_qt_wire.h, which
this SDK does own -- along with logos_qt_lp_bridge.h and
logos_ui_plugin_context.h, the only reason the root is still required at all.
(The source branch was already dead on arrival: qt-sdk's cpp/ has held no
logos_api.h since B1, so LOGOS_QT_SDK_IS_SOURCE could never be TRUE.)

The legacy fallback -- no LOGOS_QT_HOST_ROOT means take the host runtime from
logos-qt-sdk's forwarding package -- is deleted. Its premise is false now, and
what it would do instead is worse than failing: find_package(logos-qt-sdk)
still succeeds and its INTERFACE target still chains logos-qt-host, so the
build would work by accident through one more hop while the include path it set
up (${LOGOS_QT_SDK_ROOT}/include{,/cpp,/core}) contained none of the headers it
was chosen for. LOGOS_QT_HOST_ROOT is now the one source, and its absence is a
FATAL_ERROR that says so.

test-qt-host-repoint.nix follows: its test 3 asserted the fallback happened and
was announced; it now asserts the fallback is gone -- a build with no
LOGOS_QT_HOST_ROOT aborts even with a good logos-qt-sdk root present. Test 3b is
new and pins the probe fix directly: that same run must NOT say "logos-qt-sdk
not found", and a qt-sdk root scaffolded the way the real prefix now looks
(logos_qt_wire.h, no logos_api.h) is still found.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: LogosModule.cmake is this repo's, and every module type now gets it

logos-plugin-qt shipped a second copy of cmake/LogosModule.cmake, and which
one a module configured with was decided by its TYPE:

  buildCppPlugin.nix set LOGOS_MODULE_BUILDER_ROOT only when the MODULE's own
  repo carried a cmake/LogosModule.cmake. No module does, so every ui_qml
  plugin fell through to the value logos-plugin-qt's lib/default.nix sets --
  its own root -- while mkLogosModule's core path used this repo's copy.

Both compiled, so the two drifted with nothing to say so: this copy carried
A1's union (the generated_code/*.cpp glob, LOGOS_API_STYLE, the metadata
configure_file, Go/Rust static-lib linking, the external-library FATAL_ERRORs,
logos_find_qt as a Windows-safe macro) while plugin-qt's had B2b's addition of
logos_qt_arg_decode.{cpp,h} to the host-runtime source list -- which
qt_provider_object.cpp's dispatch needs, and whose absence is a link error, not
a configure error. That file is ported here.

Routing: both nix entry points now pass LOGOS_MODULE_BUILDER_ROOT
unconditionally, so a ui_qml plugin and a core module configure with the same
file. The old module-local branch is gone with it -- an unconditional value is
what makes "there is one copy" a property of the code rather than of what
happens to be on disk -- and a missing file now throws instead of falling back
to something that quietly configures differently.

logos_module() prints the file it came from, so a future fork shows up in any
configure log rather than after a phase of debugging.

Verified: counter, counter_qml, logos-storage-ui, logos-package-manager-ui,
test_fullapi_ui, logos-accounts-ui, logos-blockchain-ui, logos-wallet-ui and
logos-evm-wallet-ui (ui_qml) plus test_basic_module_cpp and capability_module
(core) all build, and their configure logs name this file. Checks by name:
default, qml-integration, qt-host-repoint, static-extlib OK; rust-native-dep
and test-framework-integration fail identically at the pre-change baseline
(this repo's own lock pins a logos-protocol without TokenManager::forIdentity,
so logos-qt-host will not compile from it -- green in the workspace closure).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: read the view templates from their owner, and check the ABI they declare

Two changes, one theme: nothing in this build should be able to pick a second
copy of a file without saying so.

1. cmake/LogosView*.in are deleted. They now live once, in logos-plugin-qt's
   cmake/, and _logos_module_add_replica_factory takes the directory from
   LOGOS_VIEW_TEMPLATE_DIR (cache variable, then environment) instead of
   probing for a sibling of this .cmake file.

   The sibling probe is what made the duplication possible: it meant the
   templates had to sit next to LogosModule.cmake, but logos-plugin-qt's
   rep-file-plugin fixture instantiates them too and cannot depend on this
   repo, so it kept a byte-identical copy. logos-plugin-qt is the only place
   both consumers can read one copy from; see its cmake/README.md.

   There is no fallback. An unset variable, or a directory missing any of the
   four templates, is a FATAL_ERROR naming what it wanted.

2. A new `view-interface-abi` check, in CI.

   LogosViewPlugin and LogosViewReplicaFactory are declared TWICE and always
   will be: module side in logos-plugin-qt's templates, host side in
   logos-view-module-runtime's headers. They cannot share a header — a module
   plugin has to compile against Qt alone, and logos-view-module-runtime
   depends on logos-plugin-qt, so the include could only point the wrong way.
   They bind at runtime through the IID string, and a mismatch there is silent
   the whole way: both sides compile, the plugin loads, qobject_cast returns
   nullptr, the view is blank.

   Until now the only thing holding those two in agreement was a comment
   asking a human to keep them in sync — which had already rotted (it pointed
   at src/, the headers are in include/) and prevented none of the five
   duplicate-source defects in this refactor.

   This repo depends on logos-plugin-qt AND on logos-view-module-runtime, and
   is the only one that does, so the comparison lives here. tests/
   view-interface-abi.py extracts the IID and the ordered pure-virtual list
   from both sides and fails on any difference — including a class rename,
   which would otherwise turn the comparison into a vacuous pass.

   Proven by mutation: adding an argument to enableRemoting on the module side,
   bumping the IID on the host side, and renaming the host-side class each turn
   the check red with a specific message; reverting each turns it green.

Note: like the already-red rust-native-dep and test-framework-integration
checks, view-interface-abi does not evaluate until this repo's logos-plugin-qt
pin advances past the commit that adds packages.<sys>.logos-view-templates. It
says exactly that, and names logos-plugin-qt as the input to move.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(view-abi): check the class that actually binds at runtime, and let it run

The guard had two independent faults; either one alone made it decorative.

1. IT DID NOT CATCH WHAT IT NAMED. The script read `#define <Name>_iid` and
   the `virtual ...;` list inside `class <Name>`. Both of those are the
   abstract shape. The runtime binding is declared on the CONCRETE class the
   template also contains — @LOGOS_FACTORY_CLASS@, with Q_OBJECT,
   Q_PLUGIN_METADATA and Q_INTERFACES — and that class was never inspected.
   Two mutations that both reach production were run against it and it stayed
   green on both:

     Q_PLUGIN_METADATA(IID LogosViewReplicaFactory_iid)
       -> Q_PLUGIN_METADATA(IID "logos.view.replica_factory/2.0")
     Q_INTERFACES(LogosViewReplicaFactory) deleted

   It now parses every class in the file, finds the ones deriving from the
   interface, and requires: at least one exists (so a dropped base class or a
   rename cannot make the comparison vacuous); a QObject-derived one has
   Q_OBJECT; a QObject-derived one names the interface in Q_INTERFACES; and
   any Q_PLUGIN_METADATA IID resolves to the IID the HOST casts on.

   Two more windows were open and are now closed: the argument of
   Q_DECLARE_INTERFACE was only checked for EXISTENCE, though it is the string
   qobject_cast compares and need not be the #define — it is now resolved
   through the file's #defines and compared both across sides and against that
   side's own #define; and the interface's own base list, which is its vtable
   layout, was not compared at all.

   Proven by mutation, 12 cases, each reverted to green afterwards: the 2
   above, plus a changed Q_DECLARE_INTERFACE IID, a dropped interface base, a
   renamed interface, a dropped Q_OBJECT, a base added to the interface, a
   *ViewPluginBase that stops implementing LogosViewPlugin, the 2 the old
   guard already caught (regression), and 2 null controls — a comment edit and
   a reflowed declaration — which must stay GREEN and do.

   The nix wrapper also sets `set -o pipefail` explicitly and folds stderr
   into $out. `... | tee $out` takes tee's exit status; that it worked at all
   depended on stdenv happening to set pipefail, which is not a thing a guard
   should rest on.

2. IT COULD NOT EXECUTE. With no overrides — the CI condition — the check
   EVAL-THREW: this flake pinned logos-plugin-qt at b8b9b414, which predates
   packages.<sys>.logos-view-templates. So the step was unconditionally red
   from the day it landed, camouflaged by test-framework-integration being red
   beside it for the same stale pin.

   Both logos-plugin-qt and logos-plugin-core (the same repo, selected per
   module type — a split pin gives core and ui modules two different
   LogosModule.cmake and two Qt host runtimes) now name rev fcf5a29 in the
   URL, not just in the lock, so `nix flake update` cannot walk them back onto
   a master that still lacks those outputs.

   That bump fixes the EVAL failure for all three affected checks.
   view-interface-abi is now GREEN with no overrides. rust-native-dep and
   test-framework-integration now evaluate and build, and fail later on an
   unrelated pre-existing problem: logos-qt-host does not compile against the
   pinned logos-protocol, whose TokenManager has no forIdentity /
   isolateIdentity. That is the same failure logos-plugin-qt's own #qt-host
   check has at fcf5a29 with its own lock and no overrides, so it is not
   introduced here and cannot be fixed here.

CI: qt-host-repoint is added — it is hermetic and it existed without ever
being listed. rust-native-dep is deliberately still not listed, with the
reason recorded next to it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(view-abi): follow bases transitively, or the guard watches an empty window

Implementor detection matched only the immediate base clause, so one level of
indirection skipped every concrete-class check — and the "no implementors"
backstop did not fire either, because the helper base still counted as an
implementor in its own right.

That is not a hypothetical shape. lidl_gen_ui.cpp emits every real ui_qml
plugin deriving from LogosViewPlugin INDIRECTLY, via <Rep>ViewPluginBase, so
for that half of the pair the window this guard exists to watch was empty in
production.

Resolve each class's transitive base set through the other classes in the file
before testing membership, and make the QObject check transitive with it. The
two mutations that escaped — an indirect base with the IID bumped to /2.0, and
an indirect base with Q_INTERFACES deleted — now fail, each naming its own
cause; indirection alone stays green and reports both implementors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): bump the B3/B4 stack, and rev-pin the inputs update cannot reach

Moves every input this builder shares with the SDK codegen stack onto the
revisions that stack has actually pushed:

  logos-protocol             03842db -> c8bab12  feat/per-client-token-store
  logos-cpp-sdk              e3744fb -> a04b278  feat/sdk-codegen-b3-d11
  logos-qt-sdk               c6be61d -> 8a06b87  feat/sdk-codegen-b3-d11
  logos-plugin-qt            fcf5a29 -> cc24fa1  feat/b4-qt-host-windows-target
  logos-plugin-core          fcf5a29 -> cc24fa1  same rev, per the type split
  logos-view-module-runtime  471dd56 -> 5510acd  feat/sdk-codegen-b4-qt-host
  logos-standalone-app       288fec2 -> 39f4f2b  feat/sdk-codegen-b4-qt-host
  logos-test-framework       eb1600c -> c382ab1  feat/sdk-codegen-b4-test-framework

Every one of those is a BRANCH TIP, not master. Six of them were plain
`github:logos-co/<repo>` urls, so `nix flake update` would have relocked them
onto master and the bump would silently not have happened -- the update
succeeds, the lock changes, and the rev is still wrong. They are rev-pinned
here for the same reason logos-plugin-qt already was. Each is a fast-forward
from its own master (03842db, e3744fb, c6be61d, 8846fc5, 471dd56, 288fec2 and
eb1600c are ancestors of their targets), so pinning gives up nothing; drop the
revs as the branches land.

logos-plugin-qt/-core go to cc24fa1, the tip of feat/b4-qt-host-windows-target,
NOT to the sibling feat/b4-qt-host-windows-target-8ccb1fc. The sibling
re-baselines onto 8ccb1fc and drops the LogosModule.cmake repoint and the
view-templates commit, so its flake exposes no packages.<sys>.logos-view-templates
and `view-interface-abi` would hit its own throw instead of running. cc24fa1
carries the old fcf5a29 pin's content under rebased shas (4c581a6/e4ea357/fcf5a29
are fe780a6/34704d1/3d7e3e6 there).

logos-test-framework's pin is the least obvious and the one worth keeping: this
branch teaches mkLogosModuleTests to pass -DLOGOS_QT_HOST_ROOT, and it is
LogosTest.cmake on c382ab1 that prefers it -- master's copy knows only
LOGOS_QT_SDK_ROOT, so a master lock links the unit tests against the wrong
runtime root without failing.

The follows keep one logos-protocol on the link line: root, logos-plugin-qt,
logos-plugin-core, logos-qt-sdk and logos-cpp-sdk all resolve to c8bab12.

All seven checks build on aarch64-darwin: default, qml-integration,
qt-host-repoint, rust-native-dep, static-extlib, test-framework-integration,
view-interface-abi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat(metadata): codegen.consumer_api_style, gated on how the image gets tokens

Lets a module declare its CONSUMER type surface independently of its provider
packaging. A cdylib-packaged module can now hold Qt-typed dependency wrappers —
the combination that was previously inexpressible, and the only reason
`interface: "provider"` and `--provider-header` are still alive.

The enabling codegen landed first (logos-cpp-sdk 620f2e1, logos-qt-sdk 6b88630):
a default-constructible Qt umbrella and a consumer binding that takes an
explicit origin instead of a LogosAPI. An earlier attempt at this key FAILED
because it was added without that codegen, so its only reachable outcome was a
compile error inside generated code. It is meaningful now.

── The predicate ───────────────────────────────────────────────────────────

    packagedAsCdylib = interface == "cdylib"
                    || (interface == "universal" && type != "ui_qml")

character-for-character what modulePreConfigure.autoCodegen branches on when it
decides to emit the module-impl C ABI, so the two cannot drift.

The distinction is NOT "this image has no LogosAPI" — a cdylib module's plugin
does contain one, in the Qt glue that receives tokens. What separates the two
worlds is where the image's own TokenManager is FILLED:

  cdylib-packaged : logos_module_accept_token -> lp_token_save, same image.
                    An origin-bound wrapper's null sync hook costs nothing.
  Qt plugin       : only LpBridge::syncTokens, installed exclusively by
                    forTarget(api, …). An origin-bound wrapper there would be
                    silently unauthenticated — the exact defect syncTokens
                    exists for.

Measured with `nm -gU`, not argued: `logos_module_accept_token` is defined in
exactly the shapes the predicate calls true, across six real plugins.
`universal` + `ui_qml` is the trap — it looks cdylib-shaped but uiCodegen emits
only view glue, so it is a Qt object holding a LogosAPI. Excluded.

`--binding origin` is not selectable from metadata at all: it is derived as
`isQt && packagedAsCdylib` in the backend, an AND no key can reach from the
wrong side. What a key CAN express wrongly is the mirror move — `lp` on a Qt
plugin — unsafe for the identical reason, and refused at eval naming the key.

Verified additive on seven modules spanning legacy/core, universal/core,
universal/ui_qml and QML-only: `diff -r` clean, narHash identical where
comparable, and `test_qml_only` byte-identical by store path. Note compiled
modules cannot share a store path across any builder edit, since mkLogosModule
bakes LOGOS_MODULE_BUILDER_ROOT — narHash is the honest comparison.

Three mutation controls, each failing loudly: removing the eval throw, nulling
the backend's own assertion, and dropping the packagedAsCdylib conjunct from
`originBound` — the last being the one that pins the token-mirror hazard rather
than just the lp refusal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): take the backend that honours consumer_api_style

08ae7ac added the metadata key but this repo still pinned logos-plugin-qt at
cc24fa1c, which predates 2d25069's `originBound` derivation. Both backend
inputs resolved there, so the key parsed and validated and then reached a
backend that ignored it: a module asking for Qt-typed consumers still got
lp-typed wrappers, and failed to compile with a type mismatch 20 errors deep
rather than anything naming the key.

Both `logos-plugin-qt` and `logos-plugin-core` move — they are two inputs onto
the same repo, selected by module type, and a split pin would give core and ui
modules different backends.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): take the SDKs that carry the origin-bound consumer codegen

The backend pin (723244b) was necessary but not sufficient. consumer_api_style
= "qt" on a cdylib emits `new LogosModules()` against the Qt umbrella, which is
default-constructible only from logos-cpp-sdk 620f2e1; and the wrapper it
constructs binds through LpBridge::forOrigin, which arrives with logos-qt-sdk
aca2951. Pinned at a04b278 / 8a06b87 the key resolved, reached a backend that
honoured it, and then failed at

    no matching constructor for initialization of 'LogosModules'

in generated code — three pin levels away from anything naming the feature.

Recording the chain, because it took four bumps to find: a module's consumer
surface is decided by test-modules -> module-builder -> {plugin-qt, cpp-sdk,
qt-sdk}, and every level has to move together. Each intermediate state built
something; none of them built the right thing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: refuse a core module that ships a plugin with no interface

`interface` defaults to "legacy", and legacy generates no glue. For a CONSUMER
that is correct: a ui_qml view plugin is never loaded by liblogos, and a fixture
that only builds tests has nothing to expose. For a module that ships a plugin
liblogos loads and other modules call, it is the silent form of exactly what the
`provider` branch already throws for — the module builds green and is
un-callable from every consumer, with the first symptom arriving at runtime in a
different process.

`main` is the discriminator, and measurement is why. `type` alone cannot do it:
of the modules that reach autoCodegen with no interface, the ten ui_qml ones are
consumers and test_framework_module is core but ships no plugin at all — it has
no plugin source, only calculator.{h,cpp}, and exists to drive mkLogosModuleTests.
Gating on `type == "core"` would have thrown for it. A provider ships a plugin,
so it names one in `main`; that fixture's `main` was vestigial and is dropped
here, which is also the honest description of what it always was.

Both directions checked, not just the quiet one: with the vestigial `main` put
back, test-framework-integration fails to evaluate with this message; with it
dropped, the same attribute evaluates and the check builds. The workspace still
evaluates on aarch64-darwin (624 packages) and x86_64-windows (386).

Depends on logos-test-modules retiring test_ipc_module, which was the last
module this would have thrown for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(metadata): drop `dynamic_calls`, which broke the grants it sat beside

`dynamic_calls` was listed as a host service and is not one. It gated nothing
and could not: the by-name path is ungated at every layer, so any module could
always make dynamic calls without asking.

What the declaration actually did was break the asking module. `hostServiceBit`
(logos_protocol.cpp:109-111) recognises exactly `token_registry` and
`token_delivery`; `lp_grant_host_services` returns LP_ERR_INVALID_ARG on the
first entry it does not recognise (:695-702), which fails the WHOLE grant. So a
module asking for `dynamic_calls` alongside a real service silently lost the
real one — the opposite of the "ADVISORY … an ungranted module gets
LP_ERR_UNSUPPORTED" the comment here promised.

Removed from `known` rather than made inert, so asking is now a build error
instead of a silent downgrade. No module in the tree declares it.

The supported surface for by-name calls is LogosModules::dynamic(target) plus
LpClient::getMethods(), added in logos-cpp-sdk alongside this. Neither needs a
grant, which is the whole point.

Two tests move with it. The "accepted for any module" case becomes an
assertThrows. The "allowlist is not bypassed by mixing in a permitted service"
case loses its premise — both remaining services are trust-root, so there is no
"permitted for anyone" name left to mix, and keeping the old form would have
passed for the wrong reason (unknown name, not the allowlist). It now pins that
asking for BOTH trust-root services does not slip past.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* ci: use logos-co/setup-nix-cache-action for Nix setup and caching

Replaces the per-repo installer + cachix pair with the shared action, which
installs Nix with the Logos Attic cache (cache.nix.logos.co) preconfigured and
publishes what the job builds — master to the public cache, every other ref to
ci.

Each converted job also gains

    environment: ${{ github.ref == 'refs/heads/master' && 'public-cache' || '' }}

because ATTIC_TOKEN_PUBLIC only exists inside that environment. Without it the
secret resolves empty on master and publishing is silently skipped — the job
still passes, so the omission would not show up as a failure.

The action installs Nix itself on every runner, macOS included. That is a
deliberate reversal of the workaround these files carried: the comments here
said cachix/install-nix-action collides with the runner's pre-existing _nixbld
users (eDSRecordAlreadyExists), so DeterminateSystems' installer was used
instead. It no longer reproduces — logos-delivery-module has already been
converted the plain way and its `build-and-test (macos-latest)` leg passes.
Keeping the workaround would have meant a second installer plus a duplicated
substituter/key block in ten files, guarding against something two green runs
say does not happen. If it ever recurs it fails loudly at install, which is
recoverable; the silent-skip above is the failure mode worth engineering
against.

One property is deliberately NOT carried over: the old cachix step ran with
`continue-on-error: true` so a failed cache push could not fail a job whose
tests passed. The action exposes no equivalent, and adding one here would also
swallow genuine setup failures now that the same step installs Nix rather than
only publishing at the end.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: align guides and skills with the universal codegen pipeline

The docs/ set, the authoring skills and the CMakeLists template still told
authors to write `interface: "provider"` classes with LOGOS_METHOD, or claimed
the builder runs `logos-cpp-generator --from-header` to emit the Qt glue. The
universal path is three steps — `--header-to-lidl` to derive the contract, then
`logos-qt-host-generator --backend cdylib` for the plugin glue, then
`logos-cpp-generator --lidl --backend cdylib` for the C-ABI exports — and
`--from-header` drives the separate `interface: "cdylib"` authoring path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* feat: take the view templates from logos-view-module

The LogosView*.in templates moved out of logos-plugin-qt, which is now limited
to making a cdylib loadable by logos-module-loader-qt. Add logos-view-module as
an input and read them from there.

- view-interface-abi now reads logos-view-module's logos-view-templates and
  diffs it against logos-view-module-runtime's host headers. The throw fallback
  named logos-plugin-qt and told the reader to bump that pin; it names
  logos-view-module now.
- This repo supplies LOGOS_VIEW_TEMPLATE_DIR itself, since the backend no
  longer does, via the extraCmakeFlags/extraEnv seams already carrying
  LOGOS_CPP_SDK_ROOT and the other four roots. Both channels are load-bearing:
  the cmake flag drives the nix build, the env var is what a dev shell resolves
  when someone runs cmake by hand.
- Indexed through common.buildSystemFor, NOT the raw system: logos-view-module
  publishes only the four native systems, while this repo's systems list adds
  x86_64-windows, so a raw index would EVAL-fail the Windows leg.
- LogosModule.cmake's hard error named logos-plugin-qt as the caller that
  passes the variable; it names this repo now.

The input is left on master rather than rev-pinned: the output does not exist
on any pushed branch yet, so view-interface-abi hits the throw until the move
lands and the pin can be set. Verified locally with
--override-input logos-view-module path:...

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): pin logos-view-module at the merged template move

logos-view-module#2 merged as 1f95a75, so packages.<sys>.logos-view-templates
now exists on its master and the input no longer has to sit unpinned.

Rev-pinned for the same reason logos-plugin-qt is: `nix flake update` must not
be able to walk this back to a commit without that output, which
view-interface-abi and every ui_qml plugin build need.

view-interface-abi now passes with NO --override-input, which is what the
unpinned state could not demonstrate.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): pin logos-standalone-app at the branch that absorbed master

The last pin on this branch that walked master backward. master's b960f44 had
bumped logos-standalone-app to e582e6c; this branch pinned 39f4f2b, which is on
feat/sdk-codegen-b4-qt-host only and does not contain it — so merging would
have reverted the hot-reload fix (#36) and the capability-bundling removal.

That branch has now absorbed master (logos-standalone-app b67eddd, `nix build
.#default` green) and this pins the result, exactly as logos-view-module-runtime
was handled in 8484fbb.

Every root input on this branch now CONTAINS master's rev: cpp-sdk, plugin-qt,
plugin-core, protocol, qt-sdk, test-framework, standalone-app and
view-module-runtime all check out as ancestors. Nothing walks master backward
any more.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): track master for every input whose PR has merged

logos-protocol (#59), logos-cpp-sdk (#138), logos-plugin-qt (#19) and
logos-qt-sdk (#33) have all landed, so the four rev pins bridging to them are
retired and each stale rationale is rewritten to name the PR that closed it.

logos-plugin-core moves in lockstep with logos-plugin-qt — it is the same repo
under a second input name, and a split pin would put two logos-qt-hosts in one
closure.

All four were SQUASH-merged, so `merge-base --is-ancestor <pin> master` is
correctly false while the content is in master. Every retirement was confirmed
against master's FILES via gh api, not ancestry.

The qt-sdk pin is worth a note: nothing makes logos-qt-sdk `follows` anywhere, so
one revision across consumers was upheld by hand-pinning the same rev — and it
had already drifted (this repo pinned aca2951 while logos-test-framework and
logos-basecamp pinned 8a06b870). Tracking master makes it structural.

Kept: logos-test-framework @ c382ab1 (master's LogosTest.cmake still knows only
LOGOS_QT_SDK_ROOT, not LOGOS_QT_HOST_ROOT — verified, 0 hits) and logos-rust-sdk
@ 0b4b8ed (no confirmed upstream merge).

All 7 checks build on aarch64-darwin and the flake evaluates on all 4 systems.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): retire four more pins, now that their PRs have merged

Takes this flake from five rev pins to one.

* logos-test-framework c382ab1 -> master. The pin existed because master's
  cmake/LogosTest.cmake knew only LOGOS_QT_SDK_ROOT, so an unpinned input made
  `test-framework-integration` link the unit tests against the wrong runtime
  root. logos-test-framework#6 merged and master now has LOGOS_QT_HOST_ROOT
  (6 references) — the exact gap the comment named.
* logos-standalone-app b67eddd -> master (13b81c9, #37). The host shell for
  ui_qml `nix run` / integration tests carries the qt-host repoint, the
  hot-reload fix (#36) and the capability-bundling removal on master now.
* logos-view-module-runtime 3ef779c -> master (b9a6778f, #25). Master no longer
  rev-pins logos-plugin-qt itself, so both sides of the view ABI check are back
  in step.
* logos-view-module 1f95a75 -> master. 1f95a75 IS that repo's master tip (the #2
  merge), so this pin was already a no-op.

Every rationale is rewritten to name the PR that closed it rather than deleted.
All four upstreams were SQUASH-merged, so the old revs are not ancestors of the
new masters even though their content is in them; the retirements were confirmed
against master's FILES.

Relocked with an explicit --update-input per retired input, NOT a bare
`nix flake lock`: that does not re-resolve an input which is already locked, even
once its url stops carrying a rev, and it silently walked pins backward twice
earlier today. All eight now read master — view-module-runtime b9a6778f,
view-module 1f95a75f, standalone-app 13b81c9e, test-framework 5f75c941,
cpp-sdk 95d7b3a9, protocol f4407ff4, plugin-qt 9b2c64e5, qt-sdk 19c844f2.

logos-rust-sdk @0b4b8ed stays: no merged upstream confirmed for it.

All 7 checks build, including test-framework-integration — the one that would
actually catch a mislocked test-framework rather than compile around it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): retire the last rev pin, logos-rust-sdk

This flake now pins nothing by rev.

0b4b8ed was kept on the grounds that no merged upstream could be confirmed for
it. That was wrong: it is an ANCESTOR of logos-rust-sdk's master (7c65d31), so it
was never waiting on anything — just behind. Unlike every other pin here it also
carried no stated reason; the comment beside it explains the `follows` that cuts
the rust-sdk -> module-builder test cycle, not the rev.

Master adds exactly two commits over it, neither touching what this builder
consumes (logos-lidl-gen and the SDK source the crate links):
  671bcc6 chore(lidl-gen): delete the dead gen_provider example (#39)
  7c65d31 ci: use logos-co/setup-nix-cache-action for Nix setup and caching (#40)

checks.rust-native-dep — the one that compiles a Rust module through lidl-gen —
rebuilds from source against rust-sdk master and passes, as do `default` and
view-interface-abi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): take the rust-sdk that emits the 11th module-impl export

logos-rust-sdk#41 merged (52f0c6f), so its provider scaffold now emits
logos_module_grant_host_services — the one module-impl C ABI export it was
missing.

That is what broke this branch's cross-language doctests. Since ed50731 the
cdylib Qt glue comes from logos-plugin-qt's logos-qt-host-generator, and the
protocol bump turned on that glue's LOGOS_PROTOCOL_VERSION_MINOR >= 3 call to
the export. logos-protocol only declares it; each language backend owes the
definition, and logos-cpp-sdk had one where logos-rust-sdk did not. Every Rust
plugin therefore linked a call with no definition — not a link error on ELF, but
nixpkgs' -Wl,-z,now forces eager binding, so the module's host process aborted at
dlopen. It died AFTER the token exchange, so the runtime reported a successful
load and only the post-load registry snapshot showed the module gone, which is
why it looked like the dependency resolver dropping the Rust module.

doctests/cross-language-composition.test.yaml: 19 passed, 0 failed — including
the assertion that failed in CI (load-module cpp_frontdesk_module listing
rust_orchestrator_module) and every typed call across the boundary in both
directions. checks.rust-native-dep also builds.

Caveat: this was verified on macOS, which links plugins -undefined
dynamic_lookup and so never binds the symbol at all — that is exactly why the
bug was invisible there and fatal on Linux. What macOS proves is that nothing
regressed and the qt-sdk/cpp-sdk skew is gone; the ubuntu doc-tests are what
confirm the Rust modules now load.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): take the rust-sdk that calls grant_host_services publicly

logos-rust-sdk#42 merged (a3d0d71). #41 had emitted
`logos_rust_sdk::ffi::lp_grant_host_services(...)`, but `mod ffi` is private, so
every Rust module generated against protocol >= 0.3 failed to COMPILE with
error[E0603] — which is why this branch's doctests went from "the Rust module
does not load" to "Build it FAILED". #42 routes the call through a public
wrapper, matching how the crate already exposes save_token.

checks.rust-native-dep rebuilds a real Rust module through the generator against
a3d0d71 and passes, and doctests/cross-language-composition.test.yaml is 19
passed / 0 failed — including the load-module assertion that started this and
every typed call across the C++/Rust boundary in both directions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 00:31:52 -03:00
Khushboo Mehta 82f420e332 chore: bump design system 2026-08-19 18:13:07 +02:00
Khushboo-dev-cpp 632f601901 Merge pull request #201 from logos-co/chore/bumpStandaloneApp
chore: bump logos-standalone-app and logos-view-module-runtime
2026-08-18 16:45:54 +02:00
Khushboo Mehta b960f44166 chore: bump logos-standalone-app and logos-view-module-runtime 2026-08-18 15:53:37 +02:00
Dario LipicarandClaude Opus 5 9ac3a15e9c docs: drop unused krb5 from nix.packages.runtime examples (#198)
krb5 was never a real dependency of the example modules — nothing in a
chat/protobuf module links against Kerberos. Because it sat in the docs it
got copied verbatim into metadata.json across eight modules, none of which
reference Kerberos or GSSAPI anywhere.

It is not just dead weight: krb5 pulls in a transitive bash whose
meta.platforms excludes mingw, so it refuses to evaluate for
x86_64-windows. zstd and abseil-cpp, the other entries modules copied,
cross-evaluate fine.

Also adds a note that nix.packages.runtime entries are evaluated for the
target platform, so an unused package can break a cross build even when it
is harmless natively.

Docs only — the templates already ship "runtime": [].

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-15 15:12:18 -03:00
Dario LipicarandClaude Opus 5 d256a42faa feat(windows): give a cross Rust module winpthreads, headers and archive (#200)
nixpkgs builds mingw-w64 against mcfgthread, so winpthreads is a separate
package on no default path. Plenty of vendored C assumes the standard
mingw environment, where it is simply present. aws-lc-sys is the case
that surfaced this, and it matters because a module cannot route around
it: aws-lc-rs arrives through third-party crates (reqwest ->
hyper-rustls -> rustls, and the alloy stack), and Cargo features are
additive, so a consumer cannot switch rustls to `ring` from its own
manifest.

Two halves, and the first alone is a trap:

- Headers. Without them the build dies on `fatal error: sched.h: No such
  file or directory`, which reads like the platform is unsupported when
  it is only unwired. cc-rs appends CFLAGS_<triple>/CXXFLAGS_<triple> to
  the compiler invocations it drives, so this reaches build-script C
  without touching the Rust compile.

- The archive. With <sched.h> reachable, aws-lc compiles aws-lc's
  thread_pthread.c, and the plugin link then wants pthread_rwlock_*,
  pthread_once, pthread_key_create, sched_yield. winpthreads goes in as a
  buildInput so its lib/ lands on NIX_LDFLAGS, and LogosModule.cmake's
  WIN32 branch names `pthread`.

Adding `pthread` to that branch is free for modules that do not need it:
ld pulls archive members on demand. Verified — keystore_module's Windows
plugin imports no libwinpthread and stages no pthread DLL, and both its
targets still build.

The linker half of this same story (`-L native=<pthreads>/lib` for
windows-gnu std) landed in #197; this completes it. Native builds are
untouched: every branch is guarded by `rustCrossTarget != null`.

Note for review: the resulting image carries both libmcfgthread-2.dll
(libstdc++) and libwinpthread-1.dll (aws-lc). Both are thin layers over
Win32 primitives and coexist in ordinary mingw distributions, but the
DLL has not yet been exercised on real Windows — a green link is not a
green run.

Verified: chat_module cross-builds to a 40MB PE32+ x86-64 plugin
exporting qt_plugin_instance / qt_plugin_query_metadata_v2, with the
crypto stack (aws-lc-rs, rustls, de-mls, libchat) really linked in and
libwinpthread-1.dll staged into its DLL closure.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 18:50:34 -03:00
Dario LipicarandClaude Opus 5 ea3b393438 fix(cross): fail loudly when a dep publishes nothing for the target system (#199)
* fix(cross): fail loudly when a dep publishes nothing for the target system

Two fixes, both found while chasing why chat_ui's Windows build died on
`fatal error: chat_module_api.h: No such file or directory`.

**The silent fallback.** Resolving a transitional header-copy dependency
did `input.packages.${system} or null`, and on null fell back to `input`
itself. For a flake input that is the dependency's SOURCE TREE, so the
plugin build gets a header root with no generated headers in it and fails
far away inside a generated TU — or, worse, succeeds against whatever
stale headers happen to be checked in. chat_module v0.2.2 publishes only
the four native systems, so every x86_64-windows consumer of it resolved
its headers to the chat_module checkout.

A flake that publishes `packages` but nothing for this system is now an
error naming the dep, the system, and what it does publish. The fallback
survives only for a genuinely bare-derivation input (no `packages` attr
at all), which is the pre-refactor shape it exists for.

**One resolver, not two.** That logic was copy-pasted into
mkLogosModule.nix (core modules) and buildCppPlugin.nix (ui_qml view
modules). It now lives once in common.nix and both call it. This is not
tidying: chat_ui is a view module, so a fix applied only to
mkLogosModule.nix left the case that motivated it untouched — which is
exactly what happened on the first attempt here.

Also single-sources the Windows build platform from
logos-nix's `windowsBuildSystems` instead of repeating "x86_64-linux" as
a literal in two places. Pinning it (rather than using the evaluating
system) is deliberate: it keeps packages.x86_64-windows.* one
well-defined derivation whoever evaluates it, so a Darwin and a Linux
checkout agree and share a cache. Widening it is now a change in
logos-nix, not here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(cross): fail loudly in collectAllModuleDeps too

Same silent-fallback shape as the header-copy resolver, one level out:
when a dependency published no usable package for the target system,
`collectAllModuleDeps` fell through to `input` -- putting the dep's
SOURCE TREE where an LGX package belongs. mkStandaloneApp then ships a
directory of .cpp files in place of a module, and the failure only shows
up at runtime as a module that never loads.

The two autoBundleLgx throws right above it already guard the adjacent
case ("a silent fallback would cause mkStandaloneApp to silently omit
the dependency at runtime"), so this closes the remaining hole in the
same function.

A bare-derivation input (no `packages` attr at all) still takes the
fallback -- that is the shape it exists for.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 18:50:19 -03:00
Dario LipicarandClaude Opus 5 f007edf1d7 feat(windows): stage declared runtime files, and read <drv>/bin (#196)
Two changes, both about runtime libraries that reach a module's output
through nothing the build can infer.

1. metadata `include` now does something.

The field is parsed (parseMetadata.nix:31) and read by NOTHING: it drives
no copy, no error, not even a log line, and nix-bundle-lgx omits it from
the manifest allowlist. Three modules declare it today.

Implement what it always advertised: each declared filename is looked up
in the module's runtime nix packages and resolved external libs -- in
both lib/ and bin/ -- and copied into $out/lib.

Nothing else can do this. The Windows DLL walk (logos-plugin-qt's
postFixup -> linkDLLsInfolder) is IMPORT-TABLE driven, so a library
reached only through dlopen appears in no table and is invisible to it;
on Unix there is equally no DT_NEEDED entry to follow. delivery_module
hit exactly this with libpq: declared, needed at runtime, and silently
absent from the module output.

A name that matches nothing is NORMAL, not an error: the list is a
deliberate cross-platform superset (modules name the .so, .dylib and .dll
spellings side by side), so at most one spelling can ever match.

It runs before the module's own postInstall, so author hooks can react to
what was staged, and before the Windows postFixup, so linkDLLsInfolder
then also walks the staged library's OWN imports -- libpq pulls in
libssl/libcrypto that way.

2. copyExternalLibsToLib also reads <drv>/bin.

Mirror of logos-plugin-qt#18 for the tests/QML staging path: a library
following the Windows convention ships its runtime half in bin/. Matching
only *.dll keeps it inherently Windows-only, so no platform flag is
needed here (this file only receives `lib`).

Verified by cross-building logos-delivery-module for x86_64-windows: the
module output gains libpq.dll ("staged libpq.dll" in the build log), and
the plugin loads and dispatches on real Windows.

Depends on logos-co/logos-plugin-qt#18 for the plugin build path.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 16:17:11 -03:00
Dario LipicarandClaude Opus 5 95869a1ced feat(windows): cross-compile Rust modules to x86_64-pc-windows-gnu (#197)
A `codegen.rust` module could not be built for Windows. Two independent
halves were missing.

**The crate compile.** The Rust toolchain has to RUN on the builder and
merely TARGET Windows, so it is taken from the build system with
`targets = [ "x86_64-pc-windows-gnu" ]` rather than from the cross set —
asking the cross set for `rust-bin` evaluates `targetPackages.threads.package`,
which only the MinGW branch defines. That choice then has a consequence:
nixpkgs' `cargoBuildHook` derives `--target` from the stdenv's HOST
platform, so left alone it silently builds for the BUILDER and yields a
perfectly good Linux archive that cannot link into a PE. The cross case
therefore drives cargo directly. Alongside that, `CC_/CXX_/AR_<triple>`
are set so cc-rs compiles build-script C for Windows too, `nix.rust`
build packages resolve from `buildPackages` (they are host tools), and
`lidl-gen` resolves from the build system (it is a generator, not a
shipped artifact).

**The plugin link.** `LogosModule.cmake` chose the native libraries Rust's
`std` leaves undefined with a two-way `if(APPLE)/else()`, so "else" meant
"Linux" and put `pthread dl` on the Windows link line — `dl` does not
exist on Windows, and `pthread` lives in a separate mingw_w64-pthreads
package that is not on the sysroot search path. The new `elseif(WIN32)`
list is derived from the archive's own undefined symbols rather than
guessed, and the finished DLL's import table confirms all of it is
reachable and none of it redundant.

Every cross branch keys off `rustCrossTarget != null`, and
`buildSystemFor` is the identity on native systems, so native builds take
exactly the path they did before.

Verified: keystore_module and token_list_module both produce PE32+ x86-64
plugin DLLs exporting qt_plugin_instance / qt_plugin_query_metadata_v2,
with the Rust crypto and TLS stacks (eth-keystore, coins-bip39,
alloy-signer-local; ring, rustls, webpki, hyper, reqwest) really linked
in, and both still build for x86_64-linux unchanged.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-14 14:45:47 -03:00
9d3b7cc1f5 feat(windows): give mkLogosModule an x86_64-windows target (#194)
* feat(windows): give mkLogosModule an x86_64-windows target

Routes every package-set construction in the builder through one helper,
common.mkPkgs, and teaches that helper the "x86_64-windows" pseudo-system.
This is the leverage point for the whole module ecosystem: modules do not
construct pkgs themselves, so fixing it here lets all ~40 of them target
Windows without each re-deriving the cross plumbing.

x86_64-windows cannot be produced by `import nixpkgs { system = ...; }` --
a cross derivation's `system` attribute is its BUILD platform, so it needs
localSystem/crossSystem plus the mingw overlays, which is exactly what
logos-nix.lib.mkWindowsPkgs wraps. logos-nix was already an input of this
flake but was never threaded into the lib; it now reaches common.nix via
lib/default.nix, and the pseudo-system is only advertised when it is
present, so a caller without it is unaffected.

Ten sites moved: common.nix, buildCppPlugin.nix x2, mkLogosQmlModule.nix
x2, mkLogosModuleTests.nix, mkLogosModule.nix x4. `systems` and
`forAllSystems` moved into the let block, since an attribute set is not
recursive and both now reference each other.

The Rust path keeps its rust-overlay via mkPkgsWith, which THROWS for a
Windows target rather than silently dropping the overlay and handing back a
package set that is not what the caller asked for. Rust modules on Windows
were already out of scope; this makes that explicit at eval time instead of
producing a subtly wrong build.

Verified: logos-capability-module evaluates unchanged on aarch64-darwin and
now also evaluates for x86_64-windows.

* feat(windows): split host tools from target artifacts in the builder

Follow-up to the x86_64-windows target: getting the pseudo-system to
EVALUATE was not enough, because several things the builder puts on the
build machine were being taken from the TARGET package set.

* common.buildSystemFor names the system a build for a target actually runs
  on (identity natively, x86_64-linux for x86_64-windows). Host tools now
  come from there.

* logos-cpp-sdk's default output serves two roles at once -- it carries the
  logos-cpp-generator BINARY and the target headers/CMake package. Under
  cross those must come from different package sets, so the 15 consumers
  are now split by role: everything landing in nativeBuildInputs (plus
  buildHeaders and the moduleLidl generator invocation) takes the new
  logosSdkBuild, while every -DLOGOS_CPP_SDK_ROOT / LOGOS_CPP_SDK_ROOT /
  buildInputs slot keeps the untouched target logosSdk. Getting this
  backwards is worse than the failure it fixes: it would SUCCEED and link
  the wrong architecture. mkLogosModuleTests is the proof case -- same
  derivation, logosSdkBuild in nativeBuildInputs, logosSdk in buildInputs.

  (The symptom was "logos-cpp-generator: command not found" rather than an
  exec-format error because logos-cpp-sdk/nix/bin.nix:39 guards the copy on
  the unsuffixed name with no else, so a mingw build silently ships an
  EMPTY bin/. Worth fixing there too.)

* pkgs.jq -> pkgs.buildPackages.jq: jq is target-typed as well and runs in
  preConfigure.

* extraCmakeFlags now prepend pkgs.logosQtCrossCmakeFlags, which point Qt at
  its host TOOL packages (repc et al). Absent -- and so empty -- natively,
  hence no isWindows guard. The symptom is misleading: CMake names
  Qt6RemoteObjects, but the target config resolves fine and it is
  Qt6RemoteObjectsTools that is missing.

capability-module now gets through evaluation, generation and Qt discovery.
It does NOT build yet: Qt 6.11.1 then hits a duplicate imported-target error
in Qt6EntryPointMinGW32Target.cmake, which is a Qt-on-MinGW CMake issue
rather than a Logos one.

* fix(windows): make logos_find_qt a macro, and pass the header contract

Two changes that together get a module cross-building.

1. logos_find_qt was a function(). Qt's mingw Qt6EntryPointMinGW32Target.cmake
   guards itself with a bare include_guard(), which CMake scopes to the most
   recent FUNCTION scope, while add_library(IMPORTED) creates a DIRECTORY-scoped
   target. So the guard variable died at endfunction() while the target
   survived, and the next find_package(Qt6) -- via logos-protocol's or
   logos-qt-sdk's find_dependency -- re-entered and hit "cannot create imported
   target EntryPointMinGW32".

   This is upstream Qt: QTCREATORBUG-32887, confirmed by a Qt maintainer,
   never fixed in qtbase (the file is byte-identical 6.7 through dev). Qt's own
   fix was consumer-side, converting a function to a macro for exactly this
   reason (qt-creator 9ded3246). Every other target file in that tree guards on
   TARGET existence, which is scope-proof.

   The two PARENT_SCOPE qualifiers had to go: under a macro they would have
   written to logos_module's CALLER.

2. Threads a per-module header contract through to the backend's buildHeaders,
   so a legacy module without a derived LIDL can still produce typed headers
   when the plugin cannot be introspected. See logos-plugin-qt.

* fix(windows): find and ship external libraries under mingw

LogosModule.cmake's EXT_LIB_NAMES listed only .dylib/.so/.a, so every
module declaring nix.external_libraries failed to cross-compile with
"External library '<x>' ... was not found in .../lib" -- which reads like
a staging bug rather than a missing filename spelling.

On Windows a shared library is TWO files: you LINK the import library
(lib<x>.dll.a under mingw) and SHIP the .dll. Both are now searched.

The runtime copy needed care of its own: the import library's name ends
in ".a", so the existing `NOT MATCHES "\\.a$"` test would classify it as
a static archive and skip the copy -- producing a plugin that links
cleanly and then fails to load with no DLL beside it. When a .dll.a or
.lib was linked, the companion .dll is resolved and copied instead, and
its absence is a hard error rather than a silent omission.

Unblocks logos-package-downloader-module and every other external-library
module under cross.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore(deps): re-pin the L1-L5 inputs to their merged revs

logos-nix (L1); logos-protocol, logos-module (L2); logos-cpp-sdk,
logos-plugin-qt, logos-plugin-core, nix-bundle-lgx, logos-design-system (L3);
logos-qt-sdk, nix-bundle-logos-module-install (L4); and logos-view-module-runtime
(L5) are all on their default branches now, so the lock can name the merged revs
instead of the pre-merge branch tips it was resolving against while those PRs
were open.

Deliberately NOT repinned, because they carry no Windows work and are not part
of this chain: rust-overlay, logos-standalone-app, logos-test-framework, and
logos-rust-sdk (which is pinned to an explicit rev in flake.nix).

Co-authored-by: Cursor <cursoragent@cursor.com>

* test: update the systems assertion for the x86_64-windows pseudo-system

550c7c9 appended "x86_64-windows" to common.systems but left
tests/test-common.nix asserting four entries, so the unit-test derivation threw:

    error: FAIL systems has 4 entries: expected 4, got 5

This is NOT a consequence of re-pinning logos-nix. The list is

    [ 4 native ] ++ lib.optional (logos-nix != null) "x86_64-windows"

and flake.nix passes that input unconditionally, so the count has been 5 since
550c7c9 regardless of which logos-nix rev is locked -- verified by evaluating
common.systems at 1cc41fa, the commit before the re-pin, which also gives 5. The
only reason it surfaced now is that this branch had no CI run between 550c7c9
and the PR being opened.

Asserts membership of the pseudo-system as well as the count, so the two cannot
drift apart again silently, and records what makes the fifth entry a pseudo-
system: a cross derivation's `system` is its BUILD platform, so it evaluates
anywhere and realises on x86_64-linux.

Verified: nix build .#checks.aarch64-darwin.default -> 257 tests passed.
Co-authored-by: Cursor <cursoragent@cursor.com>

* test: assert the extlib copy contract against the code, not a comment

1cc41fa reflowed the sentence "static archives are linked in, no runtime copy
needed" onto two lines. tests/test-static-extlib.nix grepped for that exact
prose, so the check failed on a comment rewrap while the behaviour it claimed to
cover was untouched:

    PASS: .a names added to find_library NAMES list
    PASS: copy_if_different is guarded by NOT EXT_LIB_FILENAME MATCHES
    <exit 1>

Like the systems assertion, this predates the re-pin: it breaks on 1cc41fa's tree
regardless of which revs are locked, and only surfaced now because the branch had
no CI run between that commit and the PR.

Test 3 now asserts the contract instead of the prose -- EXT_RUNTIME_LIB stays
empty for a plain .a and the copy is guarded on it -- so a reflow cannot break it
and a behaviour change cannot slip past it.

Adds Test 3b for the hazard 1cc41fa actually introduced, which nothing covered: a
mingw IMPORT library lib<x>.dll.a ends in ".a", so the static-archive guard would
skip it and ship a plugin that links clean and then cannot load. The regex script
now pins both halves of that overlap -- .dll.a matches the narrow pattern AND the
broad one, a real .a matches only the broad one -- which is the whole reason the
.dll.a arm must be tested first.

Verified: nix build .#checks.aarch64-darwin.static-extlib passes.
Co-authored-by: Cursor <cursoragent@cursor.com>

* chore(deps): re-pin logos-standalone-app to the lock that matches its runtime

This flake pins logos-view-module-runtime itself and forces it on
logos-standalone-app via `follows`, so that bumping it for module testing needs
no standalone release. That is deliberate, but it means standalone gets a
view-module-runtime its OWN lock knows nothing about.

At 592af8aa that paired a new LogosQmlBridge with a pre-deferred-events
logos-qt-sdk (09365f5e), and the doc-tests failed to LINK:

    undefined reference to `LogosAPIClient::eventSubscriptionState(unsigned long long) const'
    undefined reference to `LogosAPIClient::onEventWhenAvailable(...)'
    undefined reference to `LogosAPIClient::pendingEventSubscriptions() const'
    undefined reference to `LogosAPIClient::whenObjectAvailable(...)'

288fec2 brings standalone's lock onto the same SDK layer this flake uses --
logos-qt-sdk c6be61d0 on both sides now -- so the forced runtime and the SDK it
needs agree. Nothing in this flake had to reach further into standalone's
dependency graph to achieve it.

Note this failure was NOT specific to the Windows work: it needed only the
view-module-runtime bump, which this branch happens to be the first to carry.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-11 18:07:50 -03:00
Khushboo-dev-cpp d7427f9801 Merge pull request #193 from logos-co/feat/addPackageIcons
feat: add icons that will be used as art work on the basecamp
2026-08-11 20:05:28 +00:00
Khushboo Mehta b78429e5c9 feat: add icons that will be used as art work on the basecamp 2026-08-11 21:29:14 +02:00
Dario LipicarandClaude Opus 5 7fbb9420a3 chore(deps): bump logos-cpp-sdk to 9d50829 (deferred generated event subscriptions) (#191)
Without this, logos-cpp-sdk#134 reaches nothing. Every module built through
mkLogosModule gets its generated dependency wrappers from THIS pin, and it was
still dfd4628 -- so real modules kept emitting the old shape:

    LogosObject* origin = ensureReplica();   // blocking requestObject
    if (!origin) return false;               // PERMANENT, never retried

which asks "is this module reachable right now" at the one moment the answer is
no: every C++ consumer subscribes from init(), onContextReady() or a backend
constructor, all of which run while the dependency's host has been spawned and
has not called listen(). That guard was dead code for years because
isConnected() returned an always-true latch; logos-protocol#47 made it truthful,
which turns the same code into an instant, permanent, silent failure. Pinning
the protocol without the generator is therefore the WORST of the two orders, and
that is the state this repo has been in since #190.

Pairs with the protocol already pinned here: 0183e8c has onEventWhenAvailable,
which the new emission calls, and the tryAcquireNow use-after-free fix, which
matters because generated Qt consumers subscribe in exactly the shape that
triggered it -- one on<Event> per declared event, from init().

This exact combination is already green: logos-cpp-sdk#134's doc-tests built
their modules with module-builder master (protocol 0183e8c) and cpp-sdk
overridden to 9d50829, which is what this lock now produces. The Qt spec's
assertions passed there -- subscription accepted at onInit() while the dependency
was unreachable, event delivered decoded, exactly once.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:19:07 -03:00
Dario LipicarandClaude Opus 5 dcdb9f79c3 chore(deps): bump logos-protocol to 0183e8c (#190)
Every module built through mkLogosModule compiles its generated dependency
wrappers against THIS pin, not against whatever the consumer repo pins, because
`logos-cpp-sdk.inputs.logos-protocol.follows` and
`logos-qt-sdk.inputs.logos-protocol.follows` both point at it. So a generator
that starts emitting a newer protocol symbol cannot be validated by bumping the
SDK alone -- the emitted call has nothing to bind to.

That is exactly what happened: logos-cpp-sdk#134 makes the generated Qt wrapper
call onEventWhenAvailable(), bumped its own flake.lock, and still failed to
build in the doc-tests with

  generated_code/notifier_module_api.cpp:63:22: error:
    'class LogosAPIClient' has no member named 'onEventWhenAvailable'

because the module build resolved protocol through this repo's 0f26ffd.

0183e8c is protocol master with #47, #53 and #55. Deliberately not the first
commit introducing onEventWhenAvailable: #47's tip also carries the tryAcquireNow
use-after-free fix, and generated Qt consumers subscribe in exactly the shape
that triggers it -- one on<Event> per declared event, from init(). A lower pin
would let modules compile and then crash.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 15:45:30 -03:00
Dario LipicarandClaude Opus 5 e9c8da45d3 chore: bump logos-protocol + logos-qt-sdk (handshake surface) (#189)
logos-protocol  0f26ffd -> c1b0a0f5  (#42: token-only handshake surface published
                                        before a module's initializer, so capability
                                        can reach a module that is still starting up;
                                        plus the trust-anchor fallback, the owner-thread
                                        marshal and the handshake negative cache)
  logos-qt-sdk    3cd5297 -> 60d7a083  (#28: publish the handshake surface before the
                                        initializer, and seed the trust anchor before
                                        it goes live)

Carries the startup-wedge fix to every module built by this builder. On a native
Linux host the wedge reproduced in 5 of 20 launches of the wallet-ui app bundle
and in 0 of 66 with these two; with capability_module held at its old pin, these
alone give 6/25 -> 0/25 (one-tailed p = 0.011).

logos-cpp-sdk intentionally NOT bumped: dfd4628 composes correctly with qt-sdk
60d7a083 (which itself pins 198f031). Verified by building logos-capability-module
through this builder -- the resulting plugin carries ModuleHandshakeProxy (32),
seedHandshakeTrustAnchor (2), and both new consumer paths.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 08:28:27 -03:00
Dario LipicarandClaude Opus 5 ddddd8cc40 chore: re-pin qt-sdk + rust-sdk (emitters, ?any, jsonReturn) (#188)
logos-qt-sdk    8a9efdf -> 3cd5297  (#31 jsonReturn shapes are already Qt
                                       types — stop converting)
  logos-rust-sdk  6310031 -> 0b4b8ed  (#36 admit Option<serde_json::Value> and
                                       scope the optional-return refusal,
                                       #37 refresh the roundtrip goldens,
                                       #38 typed event emitters speak records)

BREAKING for one downstream, on purpose: #38 changes a record event emitter from
`emit_x(&serde_json::Value)` to `emit_x(&Record)`, so
logos-test-modules/test-fullapi-ext-module-rust must change its call site from
`emit_blob_event(&v.to_json())` to `emit_blob_event(&v)` in the SAME change that
picks this builder up — it cannot compile against both. That module's own
comment described the old signature as a workaround ("the emitter takes raw JSON
even for a record parameter"), which is exactly what #38 removes.

The wire payload is unchanged: the author was already encoding through the
record's `to_json`, and that is the encoder the generator now calls. The
conformance case `event/Record` in logos-test-modules pins the payload
(`{"id":"e","n":7,"payload":{"_bytes":"aGk"}}`) and is unaffected — including
the tagged bstr field.

Verified: `nix build .#checks.<system>.default` passes on the new pins.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 18:43:11 -03:00
Dario LipicarandClaude Opus 5 2da85d5758 chore: re-pin qt-sdk + rust-sdk to the merged optionality work (#187)
* chore: re-pin qt-sdk + rust-sdk to the merged optionality work

  logos-qt-sdk    36ed70d -> 8a9efdf  (#29 optional params, #30 golden fix)
  logos-rust-sdk  6d3e4c0 -> 6310031  (#35 Option<T> in the Rust-first frontend)

Optionality (`?T`) was already implemented in logos-lidl, the C++ generators and
the Rust provider/consumer; those two PRs closed the last two gaps (the Qt
generators, and `Option<T>` in the Rust-first authoring direction). None of it is
reachable by a module until the builder pins it — this is that pin.

logos-rust-sdk is pinned by REV rather than tracking master, so `nix flake
update` alone does not move it; the URL is bumped here as well.

The qt-sdk `follows` are unchanged and still resolve to the root logos-protocol
and logos-cpp-sdk nodes (verified in the lock, not just by name — flake.lock
node keys are deduplicated, so a node called `logos-qt-sdk` is not necessarily
the one the root uses).

Verified: `nix build .#checks.<system>.default` passes on the new pins.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: pin logos-rust-sdk by full 40-char rev

Matches the convention everywhere else in the org — the workspace flake pins all
55 of its inputs by full rev; this file's single short pin was the outlier. The
lock is unchanged apart from the URL string: both inputs still resolve to the
same commits.

Review nit from #187.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 17:33:50 -03:00
Dario LipicarandClaude Opus 5 97ffa59c63 chore: re-pin the SDK stack so logos-protocol is 0f26ffd everywhere (#186)
* chore: re-pin the SDK stack so logos-protocol is 0f26ffd everywhere

Two statically-linked logos-protocol revisions in one macOS image get their
weak definitions coalesced by dyld, so one image's code silently binds to the
other's. Across this particular ABI difference that is a real hazard:
RpcValue's variant gained uint64_t in the MIDDLE (renumbering later
alternatives) and awaitCompletion gained two parameters. Both changes are
internal to the plain transport, so the mismatch is source-compatible and
never shows up at build time.

Six upstream PRs pinned each repo's own root logos-protocol to 0f26ffd. This
re-pins our four SDK inputs so every path through our lock reads that same
revision:

  logos-cpp-sdk             f3369fac -> dfd46282
  logos-qt-sdk              cde7d42d -> 36ed70db
  logos-view-module-runtime 049978f1 -> 2b178f74   (its protocol 1e960047 -> 0f26ffd)
  logos-standalone-app      0d1b1775 -> 9aff419f   (its protocol 362b03fb -> 0f26ffd,
                                                    its liblogos 4ef9736a -> 2f4162a9,
                                                    that liblogos' protocol 4ee85b26 -> 0f26ffd)

Root-pinning alone was not sufficient: logos-standalone-app master still pins
an old logos-liblogos whose own root protocol is 4ee85b26 (Jul 29), so
'logos-standalone-app -> logos-liblogos -> logos-protocol' read stale even
with all six merged.

No 'follows' lines are added. The point of doing this by re-pinning is that
the overrides become unnecessary; flake.nix is untouched.

logos-rust-sdk is pinned by rev in flake.nix and is deliberately left alone.

* chore: re-pin logos-standalone-app onto its updated head

logos-standalone-app#34 grew a commit: it now also re-pins
logos-view-module-runtime, so that repo no longer ships a ui-host built against
the pre-uint64_t protocol. Track that head.

This makes one thing here honest that was not before. Master carries
`logos-standalone-app.inputs.logos-view-module-runtime.follows`, and until now
that override was what unified `logos-standalone-app -> logos-view-module-runtime
-> logos-protocol` — a from-scratch re-lock without it put that path back on
1e960047. With #34's own vmr pin current, the path resolves to 0f26ffd on pins
alone and the override no longer carries the unification.

All protocol paths now read 0f26ffd, including the two that were not on the
original acceptance list:

  logos-protocol
  logos-cpp-sdk                              -> logos-protocol
  logos-qt-sdk                               -> logos-protocol
  logos-view-module-runtime                  -> logos-protocol
  logos-standalone-app                       -> logos-protocol
  logos-standalone-app -> logos-liblogos     -> logos-protocol
  logos-standalone-app -> logos-view-module-runtime -> logos-protocol

logos-standalone-app's `original` stays bare, so when #34 merges this is a plain
`nix flake update logos-standalone-app` with no flake.nix edit. logos-rust-sdk's
by-rev pin is untouched.

STILL BLOCKED ON logos-standalone-app#34 — do not merge this first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: point logos-standalone-app at its merge commit

logos-standalone-app#34 merged as 592af8aa. Same tree as the PR head this was
pinned to — narHash is unchanged (sha256-EzA+u5lFSvd/AxIoFytwtJHegIvWH4NDCJF9MK3JuQk=),
only the revision label moves — so this unblocks the merge order without altering
what gets built.

All seven protocol paths still read 0f26ffd. logos-rust-sdk's by-rev pin untouched.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 15:22:43 -03:00
Dario LipicarandClaude Opus 5 f28f41a4ff chore: re-pin all four SDKs to master (#182)
* chore: re-pin all four SDKs to master

  logos-protocol  72754ab -> 3a31c91   off-strand socket close races (#38, #39)
  logos-cpp-sdk   44b92b0 -> f3369fa   rejection surfacing, optional spellings,
                                       header-parser drops, async error channel
                                       + sync timeout (#129 #130 #131 #132)
  logos-qt-sdk    a3ee2fc -> cde7d42   its own protocol bump
  logos-rust-sdk  8e5900d -> 6d3e4c0   per-call timeout entry points

logos-rust-sdk is pinned BY REV in flake.nix:44, where `nix flake update` is a
silent no-op, so that line is edited by hand -- the trap this workspace has hit
before.

Every logos-protocol node in the resolved closure agrees on 3a31c91, checked
with a follows-aware resolver (a `follows` entry in flake.lock is a PATH ARRAY
resolved from the root, not a node name; a naive read reports the wrong revs).
Bumping only the root pin is not sufficient in general: logos-logoscore-cli had
a correct root pin and still segfaulted 4/2000 calls because liblogos brought
its own older protocol.

All five checks build. Worth running all of them -- on an earlier bump in this
repo `default` passed while rust-native-dep and test-framework-integration
failed.

KNOWN DOWNSTREAM BREAKAGE, deliberate. cpp-sdk#127 makes an unrecognised C++
spelling a build error instead of a silent `any`. Two modules relied on that
silence and need four declarations widened:
  logos-execution-zone-module  lez_core: restore_storage(depth) and the
                               instruction vectors of send_generic_{public,
                               private}_transaction -- uint32_t -> uint64_t
  logos-libp2p-module          createXpr(services): the pair's second element
                               carries RAW BINARY, published as [any], i.e.
                               UTF-8-lossy today. {tstr: bstr} keeps it exact.
PRs for both are open.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: refresh the lock onto current masters (logos-protocol 0f26ffd)

  logos-protocol  3a31c91 -> 0f26ffd   report the failures that happen AFTER
                                       acquire, on both twins, without moving
                                       the ABI (#41, which carried #40)

The other three SDKs were already at their master heads and did not move:
  logos-cpp-sdk   f3369fa  (master)
  logos-qt-sdk    cde7d42  (master)
  logos-rust-sdk  6d3e4c0  (master) -- by-rev pin at flake.nix:44; confirmed
                           `nix flake update logos-rust-sdk` is a silent no-op
                           there (zero output, zero lock diff), and 6d3e4c0 is
                           the current master head, so no hand edit is due.
  logos-lidl      35f33d8  (master, via logos-cpp-sdk's own lock)

Follows-aware resolution from the root (a `follows` in flake.lock is a PATH
ARRAY resolved from the root, not a node name):

  logos-protocol                 0f26ffdeef18
  logos-cpp-sdk/logos-protocol   0f26ffdeef18   (follows root)
  logos-qt-sdk/logos-protocol    0f26ffdeef18   (follows root)

Cross-checked against Nix itself via builtins.getFlake .inputs.*.rev, not only
against a hand-rolled lock reader.

The two nodes that pin their own protocol rather than following the root --
logos-view-module-runtime (1e96004, 07-17) and logos-standalone-app (362b03f,
07-28) -- are reachable in the lock but contribute nothing to any check: three
checks have zero logos-protocol derivations in their build closure, and the two
that compile against it (test-framework-integration, rust-native-dep) resolve to
exactly one logos-protocol-lib, built from the 0f26ffd source. Reachable in the
lock is not the same as present in a built process image.

Checks, all five, actually run rather than substituted:
  default                     256 tests passed
  static-extlib                 9 PASS
  qml-integration              11 PASS
  test-framework-integration    2 PASS
  rust-native-dep               1 PASS

Each of those was falsified before being trusted: a static_assert(false) in the
test-framework fixture fails the compile, and a flipped expectation in
test-parse-metadata.nix fails `default` at eval.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 17:27:37 -03:00
Khushboo-dev-cpp 8bb50362df Merge pull request #183 from logos-co/feat/qmlHotReloading
feat: this helps create a .#ui-dev binary that can hot reload live qml changes for rapid qml ui developement
2026-08-05 12:21:55 +00:00
Khushboo Mehta daff5e0b6f feat: this helps create a .#ui-dev binary that can hot reload live qml changes for rapid qml ui developement 2026-08-05 13:41:27 +02:00
Dario LipicarandClaude Opus 5 ed0cde56e2 chore: re-pin logos-cpp-sdk for the type contract (no silent any admissions) (#179)
logos-cpp-sdk#127: an unrecognised C++ spelling is now a build error naming
the type and the fix, instead of being published as the opaque `any`. Also
brings invalid_args on a wrong argument count (matching what logos-rust-sdk
already emitted), std::unordered_map<std::string,T> support, and the deletion
of the last emitted base64 codec.

logos-cpp-sdk 198f031 -> 44b92b0. All five checks build, so no module in this
repo's own check set declares an unrepresentable type.

Verified the gate is actually live rather than assuming the green checks prove
it -- a probe header with `uint32_t depth` is now rejected:

  method 'narrowParam': parameter 'depth' is `uint32_t`, which has no LIDL type.
    LIDL numbers are 64-bit only. Declare it `uint64_t` (LIDL `uint`).

KNOWN DOWNSTREAM BREAKAGE, deliberate and not fixed here. Two modules relied
on the old silence and stop building until four declarations are widened:
  logos-execution-zone-module  lez_core: restore_storage(depth), and the
                               instruction vectors of send_generic_{public,
                               private}_transaction -- uint32_t -> uint64_t
  logos-libp2p-module          createXpr(services): the pair's second element
                               carries RAW BINARY and was published as [any],
                               i.e. UTF-8-lossy today. {tstr: bstr} keeps it
                               byte-exact.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 11:58:01 -03:00
Dario LipicarandClaude Opus 5 3a2bafd0c4 chore: re-pin logos-protocol for Codec<std::optional<T>> (#178)
This repo declares logos-cpp-sdk.inputs.logos-protocol.follows and
logos-qt-sdk.inputs.logos-protocol.follows (flake.nix:12,17), so ITS protocol
pin is authoritative for the whole closure -- cpp-sdk's own pin of the
protocol carrying Codec<std::optional<T>> (logos-protocol#37) is overridden
and never reaches a module build.

Result: the generator correctly emits Codec<std::optional<std::string>> for
an optional record field and the module then fails to compile it:

  error: implicit instantiation of undefined template
         'logos::detail::Codec<std::optional<std::string>>'

#177 bumped cpp-sdk, rust-sdk and qt-sdk but not protocol, and the follows
made that omission invisible -- every check passed, because nothing in this
repo's own check set declares an optional field.

logos-protocol 4ee85b2 -> 72754ab. All five checks build.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 08:39:49 -03:00
Dario LipicarandClaude Opus 5 f8aa7fce07 chore: re-pin the SDK stack for LIDL optional support (#177)
logos-cpp-sdk 5d0a99a -> 198f031  (optional: cdylib gate, record codecs, header parser)
logos-rust-sdk e43eb3d -> 8e5900d  (optional: Ty::Opt, Option<T> emission)
logos-qt-sdk   7832345 -> a3ee2fc  (re-pins ITS lidl to match cpp-sdk's frontend)

logos-rust-sdk is pinned BY REV in flake.nix, where 'nix flake update' is a
silent no-op, so line 44 is edited by hand. That is the trap this workspace
has hit before.

Two of the five checks -- rust-native-dep and test-framework-integration --
failed on the first attempt because logos-qt-generator compiles cpp-sdk's
shipped lidl-frontend while qt-sdk pinned its own, older logos-lidl. Fixed
upstream in logos-qt-sdk#25; this bump carries it. All five checks now build.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 08:00:34 -03:00
Dario LipicarandClaude Opus 5 43c1c015b5 chore: re-pin logos-cpp-sdk and logos-qt-sdk to master (#176)
* chore: re-pin logos-cpp-sdk to master (ApiStyle::Std retired)

Completes the two-part std retirement: #175 stopped calling --api-style std,
logos-cpp-sdk#122 dropped support for it. This is the first closure where both
halves meet.

logos-cpp-sdk 5f63af6 -> 5d0a99a.

Verified: all five module-builder checks build against the new pin
(default, static-extlib, rust-native-dep, test-framework-integration,
qml-integration). Since the new generator makes --api-style std a hard error,
a surviving std call site anywhere in these paths would fail the build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* chore: re-pin logos-qt-sdk to master as well

The Qt argument-validation work (logos-qt-sdk#22, #21, #23) is on qt-sdk
master but module-builder still pinned a5874fe, so no downstream repo could
see it. logos-test-modules#35 adds tests asserting the fixed behaviour and
fails against the old pin -- it consumes both SDKs only through this repo.

logos-qt-sdk a5874fe -> 7832345, alongside the cpp-sdk bump this branch
already carried.

All five checks build against both bumps.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 00:26:01 -03:00
Dario LipicarandClaude Opus 5 01bb03f91f chore: stop producing and requesting the std header variant (#175)
Every module was building a third `headers-std` derivation (a full
logos-cpp-generator run) that nothing ever consumed. buildPlugin.nix
picks a dep's headers with `dep."headers-${apiStyle}"`, and apiStyle is
only ever "lp" (cdylib / core universal) or "qt" (everything else) —
the "std" branch has been unreachable since lp replaced it. Measured:
the recursive derivation closure of a built module contains zero
headers-std, and no revision of buildPlugin.nix ever selected "std".

So drop the producer (`moduleIncludeStd`) and both output aliases
(`<name>-headers-std` and `headers-std`).

The std rung also sat in the middle of the dep-resolution fallback
chain (`headers-lp = ps.headers-lp or ps.headers-std or ps.headers-qt
or ...`). Rather than let it collapse to headers-qt, the lp chain now
throws: handing Qt-typed wrappers to a module whose own codegen ran
with `--api-style lp` fails deep inside a generated source file with a
wall of unrelated-looking Qt type errors. The throw names the dep and
says to rebuild/re-pin it against a current builder. It is lazy, so it
only fires when an lp consumer actually reads `headers-lp` — a Qt
consumer with the same stale dep still resolves to headers-qt exactly
as before.

buildCppPlugin.nix gets the same treatment. Its struct had no
headers-lp entry at all, so an lp consumer reaching that path died with
"cannot coerce a set to a string" from the header copy; now it gets the
same actionable message. (In practice everything built through there is
a ui_qml view module, which is always typed "qt".)

The generator still accepts `--api-style std`; removing that is a
separate step, ordered after this lands and is re-pinned.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 21:25:13 -03:00
osmaczko 79aeeab0c8 chore: bump logos-standalone-app to the object-form dependency fix (#174)
logos-standalone-app e4eb3c0, carrying liblogos 4ef9736 (#171) and logos-module 9fb81c5 (#22): a dependency entry written in the manifest's object form ({name, version?, signer?}) was read with toString(), which yields an empty string for anything but a JSON string, so the entry was dropped and the module it names never loaded.

mkStandaloneApp runs the app from this pin, so a module whose metadata declares such an entry gets it resolved under `nix run .#app` only once the pin moves.
2026-07-30 23:48:04 +02:00
Khushboo Mehta 0bc123bb35 chore: bump-design-system 2026-07-30 13:50:13 +02:00
Dario LipicarandClaude Opus 5 72eb720987 chore: bump to the single-source LIDL codec (#172)
logos-protocol -> 4ee85b2  (#33 path threading, #34 adopted byte coverage)
  logos-cpp-sdk  -> 5f63af6  (#117 the codec exists once, #118 test decoupling)

Modules built through this builder no longer carry a generated copy of the codec.
The generic half — scalars, bstr, vector/map composition — now comes from
logos_codec.h; what the generator still emits is one Codec specialization per
record the module declares.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 17:07:48 -03:00
Dario LipicarandClaude Opus 5 ec3887454a chore: bump protocol + cpp-sdk to the whole-valued-float fix (#171)
logos-protocol -> 3da8de9  (#32)
  logos-cpp-sdk  -> c364133  (#116)

The signedness check bumped in #170 rejected 3.0 as well as 3.7, which broke four
test_basic_module_cpp cases that pass a whole-valued double where the contract
declares an integer. A float now decodes as an integer when it has no fractional
part and fits; 3.7 is still refused.

Both repos, because the cdylib generator emits its own copy of the codec and a
rule applied to one leaves the other disagreeing.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:34:32 -03:00
Dario LipicarandClaude Opus 5 6a5073eeae chore: bump the SDKs to the scalar + void fixes (#170)
logos-protocol -> c0df466  (#31)  signedness/range in Codec<T>; the
                                     pending-call sentinel matched by shape
  logos-cpp-sdk  -> ff8c300  (#115) cdylib typed scalars go through the codec,
                                     and the EMITTED codec checks signedness
  logos-qt-sdk   -> a5874fe  (#20)  `void` converged in the shared cdylib glue
  logos-rust-sdk -> e43eb3d  (#30)  `void` arms on both sides + metadata

Together these close two provider divergences on a contract both sides share:
`echoUint(-1)` answered 18446744073709551615 on the C++ provider and
`dispatch_failed` on Rust; a `void` method answered `true` from one and
METHOD_FAILED from the other.

logos-rust-sdk is pinned BY REV in flake.nix (line 44) to break the cycle where
rust-sdk depends back on this builder, so `nix flake update logos-rust-sdk` is a
silent no-op and the URL has to be edited. Doing only the flake update leaves the
pin untouched while reporting success — that has already cost this chain one
round trip.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 13:13:30 -03:00
Dario LipicarandClaude Opus 5 695588e522 chore: bump logos-protocol to the uint64 fixes (#168)
logos-protocol 8b8a358 (#30) — two places where a uint64 above int64max stopped
being itself:

  * the universal -> Qt EVENT bridge converted with QJsonDocument::fromJson +
    QJsonValue::toVariant instead of the canonical helper the method path uses,
    so uintEvent(2^64-1) arrived as 1.8446744073709552e+19 while the equivalent
    method return was exact. The same bridge also failed to decode canonical
    tagged bytes into a QByteArray.

  * the plain (tcp/tcp_ssl) wire had no unsigned alternative in RpcValue, so the
    same value wrapped to -1 — silently, and independently in each direction.

Retires M6 from the LIDL conformance matrix.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 08:22:16 -03:00
Dario LipicarandClaude Opus 5 9ac32359b2 chore: bump logos-rust-sdk to the records support (#167)
logos-rust-sdk  a55fdac -> 2eabc95  (#29)

The input is pinned by REV in flake.nix, not by branch — that pin breaks the
cycle where rust-sdk depends back on this builder — so `nix flake update` is a
no-op for it and the URL has to be edited.

Brings the Rust provider/consumer record codegen: a `type Foo { … }` in a
contract now generates a real struct on both sides instead of a
serde_json::Value, and the `-> Void` regression is fixed (a Named type is a
record only if the module declares it; `void` is not a LIDL builtin).

Verified: test_fullapi_ext_rust — whose Blob/Wrapper come from that codegen —
builds through this builder, as does test_fullapi_rust.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 22:07:55 -03:00
Dario LipicarandClaude Opus 5 ab0c776ffe chore: bump logos-protocol and logos-cpp-sdk (#166)
logos-protocol  ae2f7e1 -> 362b03f  one canonical LIDL <-> JSON codec (#29)
  logos-cpp-sdk   350a289 -> 3d322bd  64-bit int/uint, records, cdylib
                                       composites (#111, #113)

The protocol bump is the substantive one: it folds the Qt and plain-wire copies
into the shared codec, which fixes a bstr nested in a container being UTF-8
mangled, an empty nested bstr arriving as null, and a uint64 above int64max
degrading to a double once nested.

192 tests pass.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-07-28 20:18:40 -03:00
Khushboo Mehta 5a400e30e4 chore: bump design system with keyboard handling 2026-07-28 22:12:08 +02:00
Khushboo-dev-cpp 3dd6b3a01e Merge pull request #165 from logos-co/chore/bump-design-system
chore: bump logos-design-system to the merged controls polish
2026-07-28 08:10:53 +00:00
osmaczko 1ff733db92 chore: bump logos-design-system to the merged controls polish
Picks up the overlay scroll bar that reveals itself only while in use, LogosButton sizing from its own label with an icon slot on each side, the scrim behind a modal dialog, focus reaching LogosTextField's editor, and a monospace family token (logos-co/logos-design-system#39). QML and theme only, so the static targets an importer links are unchanged.
2026-07-28 10:10:23 +02:00
Dario LipicarandClaude Opus 5 8e4ea1c1d0 chore: re-pin logos-protocol to the lp owner-thread fix (#28) (#164)
Picks up logos-protocol ae2f7e1 (PR #28, merged): lp clients on a Qt-affine
transport are now constructed on the Qt main thread instead of on whichever
thread happens to make the module's first outbound call.

Every `interface: universal` module builds its client wrappers through this
repo's api-style=lp path, and bind_<iface>() creates the client lazily — so a
module whose first inter-module call came from a worker thread (an HTTP
handler, a timer thread) bound its QtRO node and socket to a thread with no
event loop. Replica acquisition then never completed and each call burned its
full 20s timeout, silently returning an empty result. openmetrics-module saw a
40s GET /metrics missing a module's payload; with this pin the same request
takes 8ms and is complete.

logos-cpp-sdk and logos-qt-sdk both take their logos-protocol from this flake
via `follows`, so this one pin carries the fix into every module build.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
0.2.5
2026-07-26 08:58:32 -03:00
Khushboo Mehta 5c580a890f bump: logos design system 2026-07-24 15:37:34 +02:00
Khushboo-dev-cpp dab72ecaf6 Merge pull request #163 from logos-co/chore/bumpDesignSystemAndStandaloneApp
chore: bump design system and standlaone app to use the static design…
2026-07-24 12:08:43 +00:00
Khushboo Mehta 1706c45249 chore: bump design system and standlaone app to use the static design system 2026-07-24 13:39:28 +02:00
Khushboo-dev-cpp 77ac1aef56 Merge pull request #162 from logos-co/chore/bumpDesignSystem
chore: bump design system
2026-07-23 13:57:13 +00:00
Khushboo Mehta c3bfabb7ef chore: bump design system 2026-07-23 15:56:41 +02:00
Khushboo-dev-cpp afe4430ee6 Merge pull request #160 from logos-co/chore/viewRuntimeFlakeDepdendency
chore: forward logos-view-module-runtime dependency to standalone via  module-builder
0.2.4
2026-07-23 08:07:51 +00:00