Commit Graph
9 Commits
Author SHA1 Message Date
Dario Gabriel LipicarandClaude Opus 5 b505ee95eb fix(generator): widen BOTH rejection detectors to a CLOSED SET of codes
The two emitted detectors — logosDispatchRejection (QVariant, Qt surface) and
logosDispatchRejectionJson (nlohmann, lp surface) — each matched the single
literal "dispatch_failed". Providers have been answering a wrong argument COUNT
with "invalid_args" all along: this repo's own cdylib dispatch emits it
(experimental/lidl_gen_cdylib.cpp:805) and so does logos-rust-sdk's
args::invalid_args. Nothing detected it. The refusal therefore arrived as a
VALUE and the return table erased it — `_result.toList()` on that map is `[]`,
`.toString()` is "", `.toLongLong()` is 0 — so a caller could not tell "you sent
me the wrong number of arguments" from "the provider returned nothing".
Measured on the untyped surface, where the erasure is visible:

  logosctl call test_basic_module isPositive     (missing required argument)
  -> exit 0, status:"ok", result {"code":"invalid_args", ...}

The set is now {dispatch_failed, invalid_args, unknown_method}, in ONE
kRejectionCodes array. Both emitters build their condition text from it, so the
Qt and Qt-free twins cannot drift apart — which is what two hand-written copies
of the same literal were always going to do.

"unknown_method" is listed before any provider emits it, on purpose. An unknown
method is currently answered with a bare null, byte-identical to a legitimate
null return (logos-protocol logos_protocol.h says so outright), and closing that
is a provider-contract change across the SDKs. Detectors go first because
widening one is backwards-compatible on its own — nothing emits the code, so
nothing changes — whereas a new provider code shipped against narrow detectors
would arrive at consumers as DATA: the same silent-success bug, freshly minted.

The set stays CLOSED. NOT "any three-key object with a code": a method may
legitimately return a three-string map, and an `any` return certainly can, so a
shape-only match would let user data impersonate a refusal. Every guard above
the compare — exactly three keys, all three present, all three strings — is
untouched.

WHY FIVE COPIES AND NOT ONE. The other four are logos-qt-sdk's byte-identical
lidl_gen_qt_consumer.cpp, logos-rust-sdk's args::as_dispatch_rejection, and
logos-logoscore-cli's core_service/call_envelope.cpp. Two shared homes were
considered, both rejected here and both recorded at kRejectionCodes: a shared
EMITTER in share/lidl-frontend (the channel exists — it already ships
lidl_emit_common to logos-qt-generator) collapses 2 of 5 and turns four
independently landable fixes into an ordered stack; a runtime predicate in
logos-protocol is the principled end state, and is how the analogous CONVERSION
duplication was actually solved, but it trades a text-level duplication for a
build-level version coupling — the emitted body is self-contained today, so a
wrapper compiles against whatever protocol its module pins. Each repo instead
holds the vocabulary in one named place, so a drift is visible.

Tests: each code asserted present in the emitted condition on both surfaces,
plus the negatives that keep the match closed — exactly three comparisons and
no more, the shape guards still emitted, and the compare still ahead of
`return true`. 316/316 ctest.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-22 18:06:13 -03:00
Dario LipicarandClaude Opus 5 9c05d4ad11 feat(lp): emit the AsyncResult twin, and fix the LpClient create race (#142)
* feat(lp): emit the AsyncResult twin, and fix the LpClient create race

Two related changes to the Qt-free (ApiStyle::Lp) consumer surface.

1. logos::LpClient::ensure() published its lazily-created lp_client through a
   plain pointer with no synchronization. Two threads reach a dep's FIRST call
   concurrently more often than the lazy-init shape suggests: a
   concurrency:"multi" module dispatches handlers on concurrent QThreads, and
   any module running a worker of its own (an HTTP handler, a chain-sync pump)
   races that worker against the dispatch thread. So this was a data race, and
   it leaked whichever client lost.

   A mutex around the body is the obvious fix and the wrong one: for a Qt-affine
   transport lp_client_create marshals construction onto the Qt main thread and
   BLOCKS there, so a worker holding the lock would wait for the main thread
   while the main thread, reaching the same ensure() from an inbound call, waits
   for the lock — trading a data race for a deadlock. Construct outside any lock
   and publish with a CAS instead; the loser destroys its own client, which
   lp_client_destroy permits from any thread. A failed create is not latched.

2. `<name>AsyncResult` is now emitted for the lp surface, matching the Qt one.

   It was withheld for a reason that belonged to the transport rather than the
   emitter: lp_invoke_async used to hard-code `cb(1, ...)`, so an AsyncResult
   over it would have reported ok() for a call to a module that was not even
   loaded — an error channel that lies is worse than none. logos-protocol#40
   fixed that, and the new logos::LpClient::invokeAsyncResult surfaces the
   failure in C++.

   The generated twin also folds a provider REJECTION: a provider that ran and
   refused answers {"code": "dispatch_failed", ...} as its RESULT, not as a
   transport error, so the decode would otherwise erase it into a default value.
   The lp SYNC path folds it too now, as the Qt sync path already did — with no
   qWarning fallback, since a Qt-free wrapper has no logger to fall back to.

   lp `<name>Async` is deliberately unchanged: its callback takes the value
   alone, exactly as on the Qt side.

Also unblocks logos-qt-sdk's LpBridge::invokeAsyncResult, which keeps a private
second lp_client only because logos::LpClient had no error-carrying async.

Verified: sdk tests 309/309 (incl. 9 new LpClient and 10 new generator tests),
generator-cli green, and the generated wrapper compiles under -Wall -Wextra
-Werror. The concurrency test was checked against the pre-fix header as a
control: 8 threads, 8 clients created, 7 leaked, threads disagreeing on which
client was the module's.

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

* fix(tests): stub lp_string_free, and keep the path that needs it live

The sdk_tests link failed on GCC/Linux with an undefined reference to
lp_string_free from logos::LpClient::getMethods(), while linking cleanly on
macOS/clang.

The stub set was genuinely missing lp_string_free. It went unnoticed because the
lp_get_methods stub returned NULL, which made getMethods()'s free call
unreachable: lp_get_methods is a non-inline extern "C" function DEFINED IN THE
SAME TU as the test, so clang may inline it, prove the pointer null and delete
the call — no reference, no link error. GCC kept the call, and the linker wanted
the symbol.

Adding the stub alone would fix the link and leave the trap: the free path would
still be dead, so the next compiler that keeps the call decides whether this
builds. So lp_get_methods now returns a real heap allocation, which is the ABI's
actual contract ("every char* RETURNED by this library is owned by the caller;
free it with lp_string_free"), and lp_string_free frees it and counts. The
single-threaded test asserts the count, so the ownership rule is pinned rather
than merely satisfied.

Verified by reproducing the failure on macOS with -O0 (which stops clang folding
the call away): the pre-fix file fails with exactly `"_lp_string_free",
referenced from: logos::LpClient::getMethods()`, and the fixed one links and
runs 9/9. Full check: 308/308.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-20 19:24:20 -03:00
Dario LipicarandClaude Opus 5 9d508292eb fix(generator): defer generated event subscriptions instead of acquiring a replica (#134)
* fix(generator): defer generated event subscriptions instead of acquiring a replica

Both C++ generators emitted, at all three subscription sites (the generic
on(QString, RawEventCallback), its EventCallback overload, and every typed
on<Event>):

    LogosObject* origin = ensureReplica();          // blocking requestObject
    if (!origin) return false;                       // PERMANENT -- never retried
    m_client->onEvent(origin, eventName, callback);

That asks "is the 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 but
has not called listen() yet. The guard inside requestObject was dead code for
years -- isConnected() returned a latch that was always true -- so the call fell
through to a blocking wait that usually succeeded, slowly. Making isConnected()
truthful turns the same code into an instant, permanent, silent failure: the
wrapper compiles, returns a bool, and never delivers.

All three sites now route through the deferred channel:

    return m_client->onEventWhenAvailable(m_moduleName, eventName, callback) != 0;

and ensureReplica() / m_eventReplica are deleted from both generators. Keeping a
per-wrapper replica would reintroduce both halves at once -- a blocking acquire
on the subscriber's thread, and a permanent failure when the module had simply
not started yet.

The return becomes ACCEPTED rather than live, false only for errors no retry can
fix. That is stated in the emitted comment so it reaches every generated file
rather than only this message.

VERIFIED AT THREE LEVELS, because the first two prove less than they look:
  emits   -- 265/265 cpp-sdk tests. The goldens now pin the emitted CALL SITE and
             EXPECT_FALSE the removed symbols; they are string comparisons and
             would pass on code that does not compile, which is exactly how this
             defect survived.
  compiles-- both generators' output compiled against the local protocol branch
             (EXIT=0), including a 15-event contract with a 3-parameter event.
  defers  -- real A/B on a live qt_remote transport with real generated code:
             7/7 green on the migrated generator, 3/3 red in 0-8 ms on the
             pristine one, with published-first controls green in both.

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

* chore(deps): bump logos-protocol to 0183e8c for onEventWhenAvailable

The generator change in this PR emits `m_client->onEventWhenAvailable(...)` at
all three subscription sites. This repo pinned logos-protocol 0f26ffd, which has
zero occurrences of that symbol -- compiling the emitted wrapper against it gave
EXIT=1 and 16 errors, every one "no member named 'onEventWhenAvailable' in
'LogosAPIClient'". That is why this PR was opened as a draft and why the bump has
to ride in the SAME commit range as the emission change: split them and cpp-sdk
master is red for every Qt-api-style consumer.

0183e8c is logos-protocol master with #47, #53 and #55 in. It is deliberately not
the first commit that introduces onEventWhenAvailable: #47's tip also carries the
use-after-free fix for tryAcquireNow (09f684f), without which a consumer that
subscribes more than once to a not-yet-reachable module frees a QtRO facade that
is still registered in a shared replica implementation's connect list. Generated
Qt consumers subscribe exactly that way -- one on<Event> per declared event, from
init() -- so pinning below that commit would make this change crash rather than
merely fail to compile.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 16:07:18 -03:00
Dario LipicarandClaude Opus 5 f3369faca4 feat(generator): async callers can see the error, sync callers can set a deadline (#132)
The two consumer surfaces had complementary holes:

  sync :  T    foo(params…, logos::CallError* err = nullptr)   error yes, timeout NO
  async:  void fooAsync(params…, cb, Timeout = Timeout())      timeout yes, error NO

so an async caller could not tell a failed remote call from a provider that
legitimately returned 0 / "" / false — the exact ambiguity the sync path's
CallError* was added to resolve — and a sync caller could not say how long it
was willing to wait, even though the transport overload the generator already
calls takes both.

Both fixes are additive:

  T    foo(params…, logos::CallError* err = nullptr, Timeout timeout = Timeout());
  void fooAsync(params…, std::function<void(T)> cb, Timeout timeout = Timeout());   // unchanged
  void fooAsyncResult(params…, std::function<void(logos::AsyncResult<T>)> cb,
                      Timeout timeout = Timeout());                                  // new

logos::AsyncResult<T> (new, Qt-free, cpp/logos_async_result.h) is {value, error}
plus ok(); AsyncResult<void> carries only the error so every fooAsyncResult has
the same callback shape. The name is distinct rather than an overload because
std::function<void(AsyncResult<T>)> next to std::function<void(T)> is ambiguous
for a generic lambda.

Applied to both emitters that produce this surface — legacy/generator_lib.cpp
(the module-builder path) and experimental/lidl_gen_client.cpp (`--lidl
--module-only`, from a published contract) — since a consumer can reach either
for the same contract.

The Qt-free (ApiStyle::Lp) surface gets the sync timeout (spelled `int
timeout_ms`; `Timeout` lives behind a Qt header) but NOT fooAsyncResult:
logos-protocol's lp_invoke_async hard-codes `cb(1, …)`, so an AsyncResult there
would report ok() on a failed call. Measured, not assumed. See the note in
makeHeaderLp.

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-03 10:19:08 -03:00
Dario Lipicar b594d1a38c fix(generator): the Qt consumer must not flatten a rejection into an empty value (#129)
* fix(generator): the Qt consumer must not flatten a rejection into an empty value

A provider that REJECTS a call answers the canonical
{"code":"dispatch_failed", "message":..., "origin":...} object as its RESULT,
not as a transport error — the same object from every provider flavour
(logos-qt-sdk dispatchFailedVariant, the generated cdylib dispatch, the Rust
provider's args::dispatch_failed).

The generated Qt consumer wrapper converted it like any other value, which
ERASES it. `_result.toList()` on that map is `[]`, `.toString()` is "",
`.toLongLong()` is 0 — so `echoUintList([1, -1, 3])`, answered `dispatch_failed`
by the provider, reached the caller as `[]`: the whole list, not the bad
element, and indistinguishable from "the provider returned nothing". Losing the
error is a consumer bug whatever the provider did.

Reported through the channel this surface already uses for a failed call: the
`logos::CallError*` out-parameter every generated sync method carries (today
"object_unavailable" when the target cannot be acquired). No signature changes,
no return value changes — a caller that passes `err` now sees
code="dispatch_failed" with the provider's diagnostic and origin; a caller that
does not gets the same default it always got, plus the qWarning the wrapper
already emits for a failed call.

  * the detector is emitted once per wrapper, in an anonymous namespace, and
    matches EXACTLY (three string fields, that code) for the same reason
    logos_rpc_status.h's isUnauthorizedSentinel matches exactly: an `any` or map
    return carrying user data must never false-match.
  * the result is now captured for `void` returns too — a void method can be
    rejected, and the rejection object is the only place that says so.
  * the async overload's callback takes the value alone and has no error
    parameter; giving it one would change the generated public surface (which
    logos-qt-sdk's veneer mirrors 1:1), so an async rejection is logged rather
    than delivered. Recorded in cpp-generator/docs/project.md.

Scope: CONSUMER side only. The provider half of registry entry Q1 (a Qt-typed
provider cannot validate the ELEMENT of a typed numeric array, because a C++
signature spells [uint] and [any] alike as QVariantList) is explicitly out of
scope and unchanged. The lp wrapper is untouched — its generated output is
byte-identical before and after.

* fix(generator): guard the emitted detector — the umbrella is one translation unit

`logos_sdk.cpp` textually `#include`s every generated `<dep>_api.cpp`, so a
module with more than one dependency compiles several copies of the detector
into ONE translation unit:

    test_fullapi_rust_api.cpp:15:6: error: redefinition of 'logosDispatchRejection'

Internal linkage covers the separate-TU case; only the preprocessor covers this
one. Found by building test_fullapi_qtproxy (3 wrappers: two concrete deps plus
the bound `full_api` interface) against this generator — a single-wrapper
contract cannot reach it.
2026-08-03 09:41:55 -03:00
Dario Lipicar e8966bf7a9 fix(codegen): correct [T]-array arg packing and any-value return in the Qt client (#105)
* fix(codegen): pack Qt client args as one element each, not a spread list

The generated Qt client wrapper packed a method's arguments with
`QVariantList{a, b, ...}` (sync) and `QVariantList{...}` / `QVariantList() << a`
(async). For a QVariantList-typed argument -- every `[T]` list type (`[any]`,
`[int]`, `[uint]`, `[float64]`, `[bool]`) -- a braced `QVariantList{v}` and
`<< v` both CONCATENATE the list's elements into the args list, so
`echoList([1,2,3])` went out as three positional args instead of one array arg.
The receiver saw an arg-count mismatch and the list round-tripped empty; through
a UI->proxy->provider 2-hop it hung the call outright. This is the long-standing
"typed arrays empty over the Qt path" bug.

Fix both generators that emit the Qt client:
- legacy `generator_lib.cpp` (the production `logos-cpp-generator`): wrap each
  arg in `QVariant::fromValue(...)` in the sync and async call sites.
- experimental `lidl_gen_client.cpp`: route both paths through the existing
  `packVariantList` helper (which already wraps with `QVariant::fromValue`), the
  same helper the event `trigger` path uses.

`QVariant::fromValue` does not double-wrap an already-QVariant (`any`) arg, and
scalars/QString/QVariantMap/QByteArray were never affected (they don't
concatenate). Empirically: `QVariantList{v}` / `<< v` give size 3 for a 3-element
list; the wrapped forms give size 1.

Tests: legacy generator_tests gain ListArgWrappedAsOneElement and update the
param-packing assertions to the wrapped form; experimental gains
ListArgIsPackedAsOneElement. 167/167 green.

* fix(codegen): pass `any` (QVariant) return through raw in the lp wrapper

The Qt-free (lp) client generator maps both `any` (QVariant) and the `{tstr:any}`
map (QVariantMap) to the same `LogosMap` std type, and decoded a `LogosMap`
return as `jv.is_object() ? jv : LogosMap::object()`. For a genuine map that is
a no-op, but for `any` it collapsed every NON-object value (a string, a number,
an array) to an empty object `{}`. A universal proxy forwarding `echoAny("x")`
through this wrapper therefore returned `{}` instead of `"x"` — the concrete
cross-version blocker (the UI's runMethods verified echoAny and got FAIL:echoAny
through the 2-hop).

`lpFromJsonExpr` still receives the original Qt type, so it can tell `any`
(mapReturnType == "QVariant") from the map (QVariantMap): pass `any` through
unchanged, keep the object coercion only for the map.

Test: MakeSourceTest.LpAnyReturnPassesThroughButMapForcesObject.

With this + the arg-spread fix, a UI drives the full method surface (incl.
echoAny and every array type) through a universal proxy 2-hop end to end.
2026-07-19 01:03:30 -03:00
Dario LipicarandClaude Opus 4.8 f0fe8cbfeb Make the base SDK Qt-free: Qt developer layer moves to logos-qt-sdk (#83)
* Extract the protocol layer into logos-protocol; consume it as a flake input

The transport/token/IPC layer (transports incl. QRO + plain TCP/TLS,
consumer core LogosAPIClient/LogosAPIConsumer with the capability
auto-requestModule flow, ModuleProxy, token manager, QVariant<->JSON
conversion, the abstract LogosProviderObject interface) now lives in the
logos-protocol repo behind the versioned lp_* C ABI.

This SDK keeps the typed C++ developer layer (LogosAPI, provider base
classes + Qt provider glue, module context, code generator) and still
compiles the protocol sources INTO liblogos_sdk.a from the flake input,
so the installed artifact (archive symbols, include/ + include/cpp
layouts, cmake config) stays byte-compatible: existing consumers need
no changes. Public headers are unchanged; logos_provider_object.h keeps
its name and now re-exports the abstract interface from
logos_provider_interface.h.

Transport/protocol component tests moved to logos-protocol with the
code; the remaining sdk/generator/experimental suites are unchanged
(432/432 green against the local protocol checkout).

* lock: add logos-protocol input

* Make the base SDK Qt-free: move the Qt developer layer to logos-qt-sdk

LogosAPI, LogosAPIProvider, LogosProviderBase/LOGOS_PROVIDER macros, the
QObject provider glue (QtProviderObject) and the legacy PluginInterface
(core/interface.h) move to the new logos-qt-sdk repo. The protocol
sources are no longer compiled into a monolithic archive — consumers
link logos-qt-sdk (which layers on logos-protocol) instead.

What remains here is header-only std C++: logos_module_context.h,
logos_result.h (StdLogosResult), logos_json.h — exported as the CMake
INTERFACE target logos-cpp-sdk::logos_headers — plus the code generator
(a build-time tool; its introspection mode now includes
logos_provider_interface.h from logos-protocol, where
LogosProviderPlugin moved).

Mechanically verified Qt-free: the logos-cpp-lib / logos-cpp-include
closures contain only nlohmann_json. Tests: 245/245 (module-context std
suite + generator + experimental).

* fix: accept the installed source-export layout in the protocol-root check

The fail-fast only tested <root>/cpp/logos_protocol.h, but the LP_SRC
selection right below (and the error message itself) support the
installed export layout <root>/include/cpp as well. Pointing
LOGOS_PROTOCOL_ROOT at an installed export tripped the FATAL_ERROR
before that fallback could apply.

Caught by Copilot review on #82.

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

* lock: pin logos-protocol to the qt-free-split branch head

The Qt-free SDK (and the cdylib backend stacked on it) reference
LogosProviderPlugin from protocol's logos_provider_interface.h, which
lands on feat/qt-free-split — the P1-branch pin no longer compiles
standalone. Temporary — drop when the chain PRs merge.

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

* doctest: pin the logoscore runtime via its {release} placeholder

The spec built logoscore-cli at bare master with only the cpp-sdk inputs
overridden — master's stack cannot compile against the qt-free SDK, so
the suite failed on the chain branches. With the placeholder, CI's
--release-for pins expand it to the workspace's logoscore commit (and
local runs without a pin still fall back to master, unchanged).

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

* doctest: override the nested module builders to {release} too

capability_module (via logoscore's lock) and the cloned accounts module
resolve module-builder from their own locks — pre-split revs whose
LogosModule.cmake still detects the SDK by logos_api.h, which the
qt-free SDK no longer ships ('logos-cpp-sdk not found'). Overriding the
builder itself to the workspace-pinned chain rev (keeping the nested
cpp-sdk override) builds both modules with the split-aware builder.
Verified end-to-end locally with the exact doctest command.

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

* doctest: apply the {release} + nested-builder overrides to all three specs

The runtime spec got the treatment in 210eea1; the composition and
worker-thread specs have the same logoscore/module build commands and
failed identically (pre-split builders from the modules' own locks).
All executed run: blocks now pin logoscore-cli{release} and override
the nested module builders to logos-module-builder{release}; the
displayed code_block: variants stay in their generic master form.

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

* codegen: typed wrappers throw on call failure; dispatch catches escapes

Generated sync client wrappers call the new err-out invokeRemoteMethod
overload and throw logos::LogosCallError when the call fails (e.g. the
bound module is missing) — previously the empty QVariant silently
degraded to the return type's default and a caller could not tell
failure from a legitimate 0 / "". Both generators (legacy + LIDL),
both API styles. Async paths unchanged.

Generated provider dispatch (universal qt glue + LOGOS_PROVIDER) wraps
the method body in a catch-all that logs and returns an invalid QVariant
— an escaped exception becomes an ordinary METHOD_FAILED instead of
unwinding through Qt event dispatch and killing the module process.

* codegen: CallError out-param instead of throwing wrappers

Per review, the generated sync wrappers expose the error channel as an
optional trailing parameter — add(a, b, &err) — rather than throwing:
explicit, stateless, works on temporaries, and existing call sites
compile unchanged (they keep default-on-failure, now with a qWarning so
failures are visible in the module log). The dispatch catch-all from the
previous commit stays: it contains author exceptions, it doesn't
introduce any.

* glue: fire onContextReady AFTER modules()/event wiring

The generated onInit set the context (which fires the impl's
onContextReady hook) before constructing the LogosModules aggregate and
wiring typed event emission — so an impl doing its documented one-time
setup there (typed dependency calls, event subscriptions) dereferenced
a null aggregate and crashed the module process (signal 11). Found by
the first module to subscribe to a dependency's typed event from
onContextReady. Context now goes last.

* ci: run workflows on stacked PRs + workflow_dispatch

Both workflows filtered pull_request to master-based PRs, so stacked PRs
(feat/qt-free-sdk -> feat/extract-logos-protocol, feat/cdylib-authoring
-> feat/qt-free-sdk) ran NO checks at all. Drop the base-branch filter
for pull_request and add workflow_dispatch for manual runs. Same fix as
logos-module-builder 232b8a2.

* lock: protocol at the typed-requestModule port (3de5398)

* ci: chain pins for the doc-tests (drop at merge)

In repo CI only cpp-sdk's {release} is the commit under test —
logoscore-cli and module-builder expanded to master, which doesn't link
against the chain SDK the specs override in ('Build the CLI with the SDK
override' failed on every run since the stacked-PR triggers were
enabled). Pin both to the extraction-chain heads; the workspace pipeline
is unaffected (it pins every repo itself).

* generator: distribute the LIDL frontend for external generators

First step of moving ALL Qt glue emission out of this repo into
logos-qt-sdk's logos-qt-generator (cpp-sdk's generator keeps only the
Qt-free outputs: std typed wrappers, logos_sdk umbrella, cdylib
impl-exports, LIDL derivation).

- Shared emit helpers (lidlToPascalCase, lidlTypeToQt, lidlTypeToStd,
  lidlIsStdConvertible) move to a new lidl_emit_common.{h,cpp} unit, used
  by both generators.
- The frontend set (AST, lexer, parser, serializer, validator,
  impl-header parser, emit-common) is installed under
  share/lidl-frontend/ — the qt generator compiles these sources in
  directly, so the two tools share one frontend without a binary ABI.

* lock: protocol#3 merged — pin advances to protocol master

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 19:56:19 -03:00
Dario Lipicar 01221559b7 add support for QVariantList and QVariantMap (#31) 2026-03-26 10:12:18 -03:00
Iuri Matias 39b0a9acce Add tests; CI (#27)
* add tests for cpp-generator and sdk

* add tests for new api, module_proxy and factories

* add CI tests

* add tests for a module definition
2026-03-24 10:14:17 -04:00