Commit Graph
80 Commits
Author SHA1 Message Date
Dario Gabriel LipicarandClaude Opus 4.8 3094fcd0ee chore: bump logos-protocol to merged master (protocol#5)
logos-protocol  9de4165 → 4ea32a3  (concurrent-dispatch handshake coalescing, now on master)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:00:13 -03:00
Dario Gabriel Lipicar d2cd834878 Merge remote-tracking branch 'origin/master' into feat/concurrent-dispatch
# Conflicts:
#	.github/workflows/doctests.yml
#	doctests/run.sh
2026-06-19 15:59:52 -03:00
Dario Gabriel LipicarandClaude Opus 4.8 0b74c3251e test: green the universal-cdylib concurrent-dispatch doctest + wire into CI
The driver/worker split a declarations-only impl header (so the cpp-generator's
--header-to-lidl doesn't choke on inline std calls) from the impl body. That
body was never compiled — metadata's nix.cmake.extra_sources is parsed but not
consumed by the LogosModule.cmake the build actually uses — so the impl symbols
(FanoutDriverModuleImpl::fanOut / ::peak) were UNDEFINED in the dylib and the
plugin null-jumped (bl -> 0x0) on the first cross-module call. Pass the impl
.cpp via logos_module()'s existing SOURCES argument so it's compiled and linked.

With this the cpp universal-cdylib reaches worker peak overlap 4 end-to-end (a
single-threaded driver fans out 4 async calls into a concurrency:"multi" worker
and all four overlap), matching the Rust half. Wire the spec into doctests.yml
so the workspace pipeline runs it.

(Auto-wiring metadata.extra_sources — so the split pattern works without listing
SOURCES by hand — needs the consumer added to the backend LogosModule.cmake
copies in logos-plugin-core / logos-plugin-qt; tracked separately.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 14:10:46 -03:00
Dario Gabriel LipicarandClaude Opus 4.8 ca0165ee6d fix: wire universal-cdylib modules() independent of the context latch
A C++ interface:"universal" cdylib that calls another module via
modules().<dep>... segfaulted on its FIRST cross-module call: the typed
dependency surface (LogosModules) was wired inside lidlTryFireContext, which
returns early when no persistence context was stored (g_ctxStored == false).
When the daemon never delivers a context (observed: zero set_context calls for
a context-less module), maybeSetLogosModules never ran, m_logosModulesPtr
stayed null, and LogosModuleContext::modules() dereferenced null.

modules() does not need the context — each dependency client bakes its
target+origin at codegen time and creates its lp client lazily on first call.
So wire it in its own context-independent once-latch (lidlEnsureModulesWired),
called at the top of lidlTryFireContext before the context-gated early return,
i.e. on the first dispatch / set_context / set_emit_callback. A module with
deps but no stored context now has modules() wired before any handler runs.
(Bump the concurrent-dispatch doctest's post-daemon-start sleep 3 -> 6 to match
the rust spec's cold-start margin.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:57:25 -03:00
Dario LipicarandClaude Opus 4.8 4fc85b71bf docs: generator doc-test — the three LIDL ⇄ C++ flows (#94)
Add an executable doc-test that exercises logos-cpp-generator across the
three flows it supports, on the commit under test:

  1. provider header → LIDL  (--header-to-lidl): distil the contract from
     a plain C++ impl class.
  2. LIDL → provider         (--lidl --backend cdylib): emit the C-ABI
     provider glue that exports the impl class to the host.
  3. LIDL → consumer header  (--lidl --module-only): emit the typed
     caller header consumers compile against.

The example is a rich `sensor_module` interface — methods of arity 0–4,
the full type set (int/uint/float64/bool/tstr/bstr, typed arrays,
result), and multi-line docs on methods and events. A final section adds
the composite types (records, maps, optionals).

Registered in doctests/run.sh and the doctests workflow.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 12:28:11 -03:00
Dario Gabriel LipicarandClaude Opus 4.8 891cc18b9d docs: cpp-sdk concurrent-dispatch doctest (concurrency:"multi" showcase)
A concurrency:multi C++ worker + a single driver firing concurrent calls, showing
the multi worker overlaps them. The C++ cdylib generator needs NO change — its
logos_module_dispatch is already safe to call concurrently; the worker pool lives
in the Qt glue and the result is deferred via a sentinel + completion event.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 20:54:24 -03:00
Dario Gabriel LipicarandClaude Opus 4.8 760f42dbd4 feat: emit logos_module_dispatch_async for concurrency:multi C++ modules
The cdylib C-ABI exports gain an async dispatch entry (each call run on a worker
thread, reply on completion) for universal + cdylib C++ modules. --concurrency
multi flag in logos-cpp-generator.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 16:30:37 -03:00
haeliusandÁlex f032cf291e fix(header-parser): join multi-line method declarations before parsing (#91)
* fix(header-parser): join multi-line method declarations before parsing

* strip qualifiers/attributes before return-type matching (#92)

---------

Co-authored-by: Álex <alex93cabeza@gmail.com>
2026-06-18 08:30:32 -04:00
Dario LipicarandClaude Opus 4.8 182d850e72 docs: update project.md for the logos-lidl frontend split (#90)
The embedded LIDL frontend (lidl_ast/lexer/parser/serializer/validator) was
deleted — it now lives in the logos-lidl repo, linked via find_package and
bridged onto the Qt backends by experimental/lidl_compat.h. Update the project
doc to match: describe the consumed logos-lidl frontend + the compat shim,
list the backends cpp-generator actually keeps (lidl_emit_common,
lidl_gen_client, lidl_gen_cdylib, impl_header_parser), drop the deleted
frontend files and their (removed) tests, and note the qt provider glue lives
in logos-qt-generator and the Rust backend in logos-rust-sdk.

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 23:35:56 -03:00
Dario LipicarandClaude Opus 4.8 1bc101df1f feat: cpp-generator consumes logos-lidl; delete embedded frontend (#89)
* feat: cpp-generator consumes logos-lidl; delete embedded frontend

The canonical LIDL frontend now lives in logos-lidl. cpp-generator links it
and keeps only the C++/Qt-specific parts (impl-header parsing, the gen_client/
gen_cdylib backends, the Qt type-name mapping).

- Delete the embedded lidl_lexer/parser/serializer/validator/ast.
- Add experimental/lidl_compat.h: brings logos-lidl's std AST into the global
  scope the backends use (via `using`), a qs() std::string→QString helper, a
  QTextStream<<std::string overload, and name-compatible shims (lidlParse/
  lidlSerialize/lidlValidate) so the emission code keeps compiling.
- Re-point impl_header_parser, lidl_gen_client (+ Doxygen /// docs on the
  generated client methods), lidl_gen_cdylib, lidl_emit_common, and legacy/
  main at lidl::ModuleDecl.
- CMake: C++17 + find_package(logos-lidl) + link logos-lidl::logos_lidl.
- bin.nix: distribute only the shared C++/Qt backend helpers (compat +
  impl_header_parser + emit_common) under share/lidl-frontend, not the frontend.
- tests: drop the 4 frontend test files (covered by logos-lidl now); the
  backend tests link logos-lidl.

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

* fix: pin logos-lidl to the C-ABI commit + lock it

The logos-lidl input was declared in flake.nix but missing from flake.lock,
so override chains that don't reach the nested input (the doctest harness
building a scaffolded module) couldn't resolve it. Pin the branch rev and
lock it so the component is self-contained. Re-point at master once logos-lidl
lands.

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

* chore: re-point logos-lidl to merged master (#5)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 21:00:42 -03:00
Dario LipicarandClaude Opus 4.8 676154070c codegen: Qt-free outbound — ApiStyle::Lp typed wrappers over the lp_* C ABI (#88)
* ci: drop doctest chain pins — the qt-split chain is fully merged

logoscore-cli and module-builder masters now contain the chain; the
temporary --release-for pins (added so stacked-branch CI could resolve
compatible cross-repo revs) default back to latest releases.

* codegen: Qt-free outbound — ApiStyle::Lp typed wrappers over the lp_* C ABI

Adds a third generator flavor (ApiStyle::Lp, --api-style lp) whose typed
dependency wrappers + LogosModules umbrella call the logos-protocol C ABI
directly via a new header-only logos::LpClient, instead of LogosAPIClient.
This lets a module make outbound typed calls and event subscriptions with NO
Qt in its translation units — Qt stays confined to the QRO transport (inside
logos-protocol) and the generated plugin glue.

- cpp/logos_lp_client.h: header-only logos::LpClient (lazy lp_client_create
  on a baked origin; invoke / invokeAsync / subscribe; std<->nlohmann JSON;
  CallError out-param) + RAII logos::LpSubscription (unsubscribes on drop) +
  json<->std helpers. The C++ analog of rust-sdk PluginProxy.
- generator: makeHeaderLp/makeSourceLp emit the Lp wrappers; the Lp umbrella
  drops the LogosAPI ctor and bakes this module name as the lp_client origin
  (LogosModules() default-constructible). Qt/Std emission is byte-unchanged
  (dispatch added at the top of makeHeader/makeSource).

Verified: generator builds; generated wrappers + umbrella compile to .o with
ONLY cpp-sdk + logos-protocol headers + nlohmann (no Qt); cpp-sdk tests pass.

* cdylib: wire the Qt-free typed dependency surface (modules()) into the impl

When a cdylib module declares dependencies, the generated exports now include
the Lp umbrella (logos_sdk.h) and construct LogosModules() + maybeSetLogosModules
on the impl just before onContextReady — so the author can call
modules().<dep>... and subscribe to dep events from a Qt-free cdylib. Guarded
on module.depends so dependency-less cdylib modules are byte-unchanged.

The umbrella + dep wrappers themselves are produced by the --general-only
--api-style lp generation; feeding the dep .lidl files into that during the
module build is the remaining build-system wiring (module-builder + plugin-qt
dep resolution).

* cdylib: wire modules() unconditionally (deps come from metadata, not the .lidl)

The umbrella wiring was guarded on the .lidl module.depends, but a cdylib
module declares its dependencies in metadata.json#dependencies — the .lidl
contract.depends is typically empty — so modules() was left null and a typed
outbound call segfaulted. Always include the generated logos_sdk.h umbrella
and maybeSetLogosModules(impl, new LogosModules()) before onContextReady; the
overload is a no-op for context-less impls and the umbrella codegen emits an
(empty) logos_sdk.h for every cdylib, so this is safe in all cases.

* fix(headers): ship logos_lp_client.h in the include/cpp source-export root

A cdylib module's generated dep wrapper includes "logos_lp_client.h" and,
transitively, "logos_result.h". The wrapper is compiled with the cpp-sdk
source-export include root (include/cpp), so logos_lp_client.h must sit
beside logos_result.h there — a quoted include resolves siblings relative
to the including file's directory. Previously logos_lp_client.h shipped
only at the top-level include/ (the CMake-export layout via cpp/
CMakeLists.txt), so it pulled in include/logos_result.h while the impl's
logos_module_context.h pulled include/cpp/logos_result.h. Those are two
distinct realpaths under the symlinkJoin, so #pragma once could not dedup
them and StdLogosResult was redefined. Install every std header into both
roots so a single TU only ever sees one logos_result.h.

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

* fix(cdylib): route logos_module_accept_token into the protocol TokenManager

The generated module-impl export stored accepted tokens in a process-local
std::map (g_tokens) that nothing ever read, so a cdylib module's OUTBOUND
lp_client (modules().<dep>...) never saw the capability_module bootstrap
token the host delivers at load. The automatic requestModule flow then ran
unauthenticated: capability_module rejected requestModule, no per-target
token was issued, and the cross-module call was rejected (returning a
default-constructed result, e.g. 0).

Forward the token into lp_token_save, which writes the same
TokenManager::instance() singleton the cdylib's lp_client reads. The
capability/token handshake now completes and typed Qt-free outbound calls
return real results. Drop the dead g_tokens map + mutex.

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

* fix(lidl): exclude LogosModuleContext hooks from header-derived contracts

--header-to-lidl parses an impl class's public methods. An impl commonly
overrides onContextReady() (and could redeclare a context accessor) in its
own public section, so the derived LIDL would include onContextReady /
modules / modulePath / instanceId / instancePersistencePath. Those are
framework plumbing, not API methods — and feeding them to the cdylib
backend breaks cdylib-eligibility (e.g. the inherited accessors' Qt-free
return-type check), which is exactly what header-first universal modules
now hit. Skip the reserved LogosModuleContext names in the parser so both
the Qt --from-header path and the cdylib --header-to-lidl path emit clean,
API-only contracts.

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

* fix(cdylib): support the full std type set in header-derived contracts

Routing core universal modules through the cdylib backend surfaced gaps
between the cdylib subset and what the std apiStyle handled — a universal
module that built under std must also build as a header-first cdylib.

- lidl parser: restore the return-shape flags (resultReturn / jsonReturn)
  from the parsed return TypeExpr, so a header -> .lidl -> cdylib round-trip
  (the universal path, needed to feed the Qt glue) preserves the semantics
  the impl-header parser sets from C++ types (StdLogosResult -> result;
  LogosMap/LogosList -> json). Without this the cdylib codegen/eligibility
  mis-handled result / map / list returns.
- cdylib eligibility + dispatch: `void` is not a lidlBuiltinType, so the
  parser yields it as a Named "void" (header path uses empty name) — treat
  both as void in the eligibility check and the dispatch (was relying on
  lidlTypeToQt=="void", which didn't match Named "void" -> generated an
  `auto result = <void call>`).
- typeSupported: accept `any` (both directions), `void`/`result` (returns),
  arrays-of-any, and Map ({k:v}/LogosMap) — the Qt-free-via-nlohmann set.

Verified: a probe with void / LogosMap / LogosList / StdLogosResult /
const returns is cdylib-eligible and dispatches correctly.

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

* fix(lidl): carry method/event descriptions across the .lidl round-trip

Header-first universal modules go header -> .lidl -> cdylib backend. The
impl-header parser captures /// and /** */ doc comments into method/event
descriptions, but the .lidl serializer emitted only the signature, so the
descriptions were dropped — introspection (lm methods / --json, getMethods)
then showed no docs (regressing the wrap-external-lib + tutorial doctests).

Serialize each method/event's description as a trailing `description "..."`
clause (escaped for the string literal; the lexer already decodes \\ \" \n
\t) and parse it back in parseMethodDef/parseEventDef. Module description
now escaped too. Verified: /// docs survive header -> .lidl -> getMethods.

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

* fix(lp): bound-interface wrappers are handles over umbrella-owned state

The Lp interface (bind_<iface>) wrapper owned its LpClient + RAII
subscriptions BY VALUE, so the idiomatic transient handle —
  modules().bind_calculator(p).fibonacciAsync(...)
  modules().bind_calculator(p).onVersionReady(...)
— tore the client/subscription down when the temporary died, cancelling
the async callback and the event subscription. (Sync calls completed before
the temporary's destruction, so they worked; the Qt/std flavor works because
its handle is thin over a LogosAPI-owned persistent client.)

Make the Lp Bound wrapper a THIN, copyable handle over `State { LpClient
client; vector<LpSubscription> subs; }` that the LogosModules umbrella OWNS
per provider (std::map<provider, unique_ptr<State>>) for the module's
lifetime. bind_<iface>(p) creates/looks up the State and returns a handle to
it, so a transient handle's async/event registrations outlive it. Concrete
(Static) dep wrappers are unchanged — they're already persistent umbrella
members, so by-value ownership is fine there.

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

* fix(cdylib): lenient bytes-param decode (string / array / tagged)

A universal module's bstr (std::vector<uint8_t>) PARAM arrived empty when
the caller sent a plain string rather than the tagged {"_bytes": base64url}
form — lidlBytesFromJson only accepted the tagged object, so
byteArraySize("12345") and byteArraySize(b"\x01..") both saw 0 bytes (the
return direction already worked). The std path was lenient (a QString or
QByteArray arg both became bytes). Accept all three forms: a plain JSON
string (raw UTF-8 bytes), an array of byte values, and the tagged
{"_bytes"} form (base64url). Return direction unchanged.

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

* fix(cdylib): a number arg to a bytes param decodes as its decimal text

byteArraySize("12345") arrives as a JSON number (the logoscore CLI's type
auto-detection turns the string "12345" into int 12345), and the Qt path
gives QVariant(int)->QByteArray "12345" (5 bytes). The cdylib bstr decode
returned 0 for a number. Treat a JSON number as its decimal text bytes
(j.dump()), matching the Qt behaviour, so a bare-number arg to a bytes
param round-trips identically. Verified: byteArraySize 12345 -> 5.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 00:47:09 -03:00
Dario Lipicar d1d341fdcf ci: drop doctest chain pins — the qt-split chain is fully merged (#87)
logoscore-cli and module-builder masters now contain the chain; the
temporary --release-for pins (added so stacked-branch CI could resolve
compatible cross-repo revs) default back to latest releases.
2026-06-12 23:23:29 -03:00
Dario LipicarandClaude Opus 4.8 87abcd8044 Cdylib authoring: --backend cdylib emits the common C ABI wrapper + uniform Qt glue (#84)
* 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).

* Cdylib authoring backend: --backend cdylib emits the common C ABI wrapper + uniform Qt glue

From a module's LIDL contract the generator now emits:
- <name>_module_impl.cpp — the Qt-FREE logos_module_impl.h export
  wrapper (dispatch/get_methods/set_context/set_emit_callback/
  accept_token/get_protocol_version/string_free) around the universal
  impl class; compiled into the module's cdylib. Tagged {"_bytes"}
  bytes, StdLogosResult -> {success,value,error}, context via the
  existing _logos_codegen_::maybeSet* SFINAE helpers.
- <name>_events_cdylib.cpp — typed logos_events: bodies marshalling
  into nlohmann::json (the cdylib flavor of the events sidecar).
- <name>_cdylib_glue.{h,cpp} — the UNIFORM Qt-plugin glue forwarding
  LogosProviderObject to the C symbols; identical regardless of the
  module's source language (the Rust SDK emits the same exports).
- the .lidl sidecar.

Qt-container types are rejected at generation time (a cdylib impl is
Qt-free by definition). Verified end-to-end by dlopen smoke: typed
dispatch, typed events through the emit callback, result returns,
introspection, and the protocol-version handshake — and the SAME C
harness passes against a Rust cdylib generated by logos-lidl-gen
--provider.

* 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>

* 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>

* generator: glue-only cdylib mode for non-C++ impls

--lidl x.lidl --backend cdylib (no --impl-class) emits just the uniform
Qt-plugin glue; the C exports come from the module's own language
backend (e.g. the Rust SDK's lidl-gen --provider). This is the entry
point logos-module-builder's cdylib interface uses for Rust modules.

* cdylib glue: root plugin implements PluginInterface

logos_host's module_initializer hard-requires PluginInterface on the
root plugin before any provider detection — same bases as the qt glue
(QObject, PluginInterface, LogosProviderPlugin). Caught by the first
host-loaded run of a cdylib-authored module; the dlopen smoke harness
exercised only the C seam.

* cdylib glue: seed the cdylib's protocol stack with the host auth token

The glue's init() reads the authToken property module_initializer now
surfaces on the LogosAPI object and forwards it across
logos_module_accept_token under the initializer's own keys
(core / capability_module). Without it the cdylib's TokenManager (a
separate static copy of the singleton) is empty and every outbound
call — including the capability requestModule bootstrap — is rejected
as unauthorized.

* cdylib glue: provider derives LogosProviderBase; tokens land in BOTH stacks

informModuleToken = base save (host-stack TokenManager — what ModuleProxy
validates inbound calls against, incl. the grant the daemon pushes after
a capability requestModule) + C-ABI forward (the cdylib's own stack, for
outbound auth). init folds into onInit; the authToken property seeding
stays.

* lock: protocol integer-fidelity fix

* 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.

* fix: restore a clean flake.lock after the merge conflict (protocol 176fbc8)

* 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.

* generator: contract-first C++ cdylib modules from --lidl

--lidl x.lidl --backend cdylib with --impl-class/--impl-header now emits
the FULL set (C-ABI export wrapper + events + uniform glue) around the
named hand-written Qt-free impl class — the C++ mirror of declaring the
contract in .lidl and implementing the Rust trait. Without --impl-class
the glue-only mode is unchanged.

* 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.

* cdylib: fire the context-ready hook at module LOAD, not first dispatch

The impl-exports wrapper used to fire maybeSetContext (stamp + hook)
immediately inside logos_module_set_context — which the glue calls during
onInit, BEFORE the auth token is seeded and BEFORE ModuleProxy wires the
emit callback. A hook that made outbound calls or emitted events ran
half-wired; and the Rust scaffold deferred its hook to first dispatch
entirely, so a Rust module couldn't subscribe/emit/act until someone
called it — a capability gap vs the universal C++ path.

Two changes, applied uniformly:
- The glue's onInit now seeds the auth token FIRST and forwards the
  context LAST (the same context-last convention as the universal onInit
  ordering fix).
- The impl-exports gained a ready-latch (lidlTryFireContext): the context
  is stored on set_context and the hook fires ONCE as soon as both the
  context AND the emit callback have been delivered — during module
  registration, before the module is published for inbound calls. Hosts
  that never wire an emit callback still get the hook before the first
  dispatch (requireEmit=false fallback in logos_module_dispatch).

The Rust scaffold (logos-rust-sdk lidl-gen) implements the same latch, so
on_context_ready now matches C++ onContextReady semantics: subscriptions,
authenticated outbound calls and typed emission all work from the hook at
load time.

* 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.

* generator: --backend ui — universal authoring for UI plugin backends

New emitters (lidl_gen_ui.{h,cpp}) for type=ui_qml + interface=universal
modules: from the author's single clean impl class (optionally deriving
LogosModuleContext), generate
  <name>.rep            — the view contract, one SLOT per public method
                          (framework hooks like onContextReady excluded)
  <name>_ui_interface.h — PluginInterface subclass + IID
  <name>_ui_glue.{h,cpp}— plugin deriving <Cls>SimpleSource +
                          <Cls>Interface + <Cls>ViewPluginBase; slots
                          forward to the impl (std<->Qt at the boundary);
                          Q_INVOKABLE initLogos(LogosAPI*) builds
                          LogosModules, wires modules(), stamps context
                          (fires onContextReady — same order as the
                          universal core glue), then setBackend(this).

Typed dependency callers, typed event subscriptions and bind_<interface>
binders come from the existing umbrella pass (logos_sdk.h), which already
runs for UI modules. ui-host's reflection-based initLogos call site is
unchanged; legacy LogosAPI*-based UI plugins are untouched. v1 view API
types: void/int/uint/float64/bool/string; logos_events: rejected for ui
backends (views talk to QML via .rep, not module events).

* 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.

* generator: Qt glue emission removed — logos-qt-generator owns it

The deletion half of the generator split (counterpart: logos-qt-sdk
3b37474, builder d7a2272). This tool now emits ONLY Qt-free outputs:

  kept     std typed wrappers + logos_sdk umbrella (--general-only),
           cdylib C-ABI impl-exports + typed event emitters
           (--lidl/--from-header --backend cdylib --impl-class),
           LIDL derivation/serialization (--header-to-lidl), client stubs
  removed  universal Qt glue (--backend qt), the uniform cdylib Qt glue,
           the ui backend emitters — all relocated verbatim (byte-identical
           output verified) to logos-qt-generator; invocations here now
           fail with a pointer to the right tool

The provider-glue golden tests travel with the emitters (to be re-homed
in logos-qt-sdk's test suite); test_lidl_type_mapping stays — it covers
lidl_emit_common, which both generators compile.

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

* 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.

* docs: note the generator split (Qt glue emission lives in logos-qt-sdk)

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:10:16 -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 LipicarandClaude Opus 4.8 38bc77e127 Consume logos-protocol: the transport/token/IPC layer moves behind the lp_* C ABI (#82)
* 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

* 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: protocol at the typed-requestModule P1 port (1e4bc72)

* lock: protocol at master (protocol#2 merged)

The extraction is on protocol master now (29afbac); the temporary branch
pin is dropped.

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 19:21:00 -03:00
Iuri Matias f5a127dd11 use updated capability module (#85)
* use refactored capability module

use refactored capability module

remove unneded comments

* use cpp types
2026-06-11 15:44:37 -04:00
Dario LipicarandClaude Opus 4.8 bb6d87b6ec fix: parse declarations on same line as logos_events: / access specifier (#76) (#81)
* fix: parse declarations on same line as logos_events/access specifier (#76)

The impl header parser updated its section state and immediately broke out
of line processing when it matched `logos_events:` (or `public:`/`private:`),
discarding any declaration on the same physical line. This meant clang-format
/ prettier output like

    logos_events : void versionReady(const std::string &version);

silently dropped the event, while the newline-separated form parsed fine —
the same valid C++ was handled differently based on formatting.

Strip any leading section specifiers in a loop, updating the section state,
then let the remainder of the line fall through to the declaration parser.
Brace counting still happens once per physical line and blank-line doc-comment
reset is preserved.

Adds a regression test (SameLineSectionSpecifiers) with a fixture covering the
exact prettier form from the issue, a follow-on same-line event, the newline
form alongside it, and the symmetric inline `public:` method case.

Fixes #76

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

* fix: attach doc comments to same-line logos_events/access-specifier decls

Address review feedback: the first pass cleared pendingDoc on every
specifier match, so a `///` comment above a collapsed
`logos_events : void foo();` did not attach to the event. In the collapsed
form there is nowhere else to put the doc comment, so this left
documentation formatting-dependent — the same bug class as #76, one level up.

Only clear pendingDoc for a *bare* specifier (a section boundary, matching
Qt `signals:` semantics); when a declaration shares the line, keep the
pending doc so the declaration parser attaches it.

Extend the fixture with a `///`-documented same-line event and assert the
description is captured.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 14:46:15 -03:00
Dario LipicarandClaude Opus 4.8 40e7631402 Marshal inter-module calls to the owner thread (#79)
* Marshal inter-module calls to the owner thread

Logos inter-module calls go over Qt Remote Objects, whose replicas only
work on the thread that created them (the module's main/event-loop thread).
A module that makes calls from a worker thread — e.g. an embedded HTTP
server serving /metrics — would otherwise hang on replica acquisition.

Make LogosAPIClient transparently marshal to its owner thread when called
off-thread (guarded so same-thread calls run directly with no overhead):
- LogosAPI::getClient creates the client/consumer/replicas on the owner thread
- LogosAPIClient::invokeRemoteMethod / requestObject / onEvent run there too

New header logos_thread_marshal.h (runOnOwnerThread). No new data members —
ABI-safe for statically-linked plugins.

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

* Add regression test for worker-thread inter-module calls

A provider records the thread its method runs on; a consumer calls it from
a worker thread via LogosAPIClient::invokeRemoteMethod. The call must execute
on the owner (main/event-loop) thread, not the worker thread.

Fails without the marshaling change (the call runs on the worker thread —
0x..d80d0 vs owner 0x..c53e0, "executed on the worker thread instead of the
owner thread"); passes with it (511/511).

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

* Address review: async marshaling, helper constraints, test ownership

- invokeRemoteMethodAsync now also marshals to the owner thread (non-blocking
  QueuedConnection) — the async path acquires a replica too, so calling it from
  a worker thread previously re-introduced the off-thread bug.
- runOnOwnerThread: document the return-type constraints (void or
  default-constructible, non-reference) and static_assert against references.
- test: declare the provider before its LogosAPI so the ModuleProxy (which
  holds a raw pointer to it) is torn down first — removes the leak and the
  inaccurate comment.

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

* doctest: call a module from a worker thread (HTTP server)

Adds cpp-sdk-worker-thread-http.test.yaml: builds a sensor_module callee and
an http_module caller that embeds a libmicrohttpd server, runs them in
logoscore, starts the server, and curls it. The HTTP handler calls
sensor_module.readTemperature() from the server's worker thread — which only
works because the SDK marshals the cross-module call onto the module's owner
thread. The module stays pure C++.

Wired into doctests/run.sh and the doctests CI workflow. Validated locally
(23/23 steps pass): `curl` returns `temperature 42`.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 16:47:24 -03:00
Iuri MatiasandCopilot Autofix powered by AI d142262436 Fix: F-007: add function to redact logged tokens (#80)
* add function to redact logged tokens

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-08 15:22:27 -04:00
Iuri MatiasandCopilot Autofix powered by AI 448aa9002c fix: F-002: only allow core and capability module to call informTokenModule (#78)
* only allow core and capability module to call informTokenModule

* Potential fix for pull request finding

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
2026-06-08 14:27:07 -04:00
Dario LipicarandClaude Opus 4.7 42a8b9ed5c feat: LIDL interface IR — --header-to-lidl frontend + --dep backend (#77)
* feat: LIDL as the interface IR — --header-to-lidl frontend + --dep backend

Make the generator pivot around LIDL so every binding flows source -> LIDL ->
C++ (and a future Rust module plugs into the same backend via Rust -> LIDL):

- --header-to-lidl <impl.h> --impl-class X --metadata m.json -o out.lidl: the
  standalone C++ frontend. Runs parseImplHeader -> lidlSerialize and emits ONLY
  the <name>.lidl contract (no Qt glue/dispatch), so a module can publish a
  cheap `lidl` artifact without compiling its plugin.
- --dep <name>=<lidl>: the LIDL backend for concrete dependencies. Reuses the
  interface-wrapper path with BindMode::Static, emitting the name-baked
  modules().<dep> wrapper from the dep's published LIDL. Deduped vs each other
  and vs --interface names.
- generateInterfaceWrappers gains a BindMode param (default Bound); --interface
  stays Bound, --dep is Static. parseInterfaceFlags generalized to
  parseSpecFlags(args, flag) for both --interface and --dep.

The umbrella already emits a `<dep>` member per metadata dependency, so no
umbrella change is needed.

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

* address review: strip leading '@' in --header-to-lidl paths; fix doc comment

- --header-to-lidl now strips a leading '@' from the header/metadata/output
  path args (matches legacy_main; some build drivers pass @/abs/path).
- Remove the stale "bound wrapper" doc comment above generateInterfaceWrappers
  (it now generates Static dep wrappers too via BindMode).

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-08 11:43:10 -03:00
Dario LipicarandClaude Opus 4.7 eb71a1aa90 feat: dependency interfaces — SDK code generator (bound wrappers) (#74)
* feat: dependency interfaces — runtime-bound typed wrappers

Turn a declared "interface" (a .lidl file or a pure-C++ header with a
logos_events: block) into a BOUND client wrapper: the target module name
is a constructor argument instead of a baked-in literal, so one interface
can be bound to any satisfying module at runtime.

- generator_lib: new BindMode { Static, Bound }. In Bound mode the ctor
  takes (LogosAPI*, const QString& moduleName) and every invokeRemoteMethod*
  / ensureReplica routes through m_moduleName. Default Static leaves existing
  name-baked output byte-for-byte unchanged.
- legacy/main.cpp: repeatable --interface <name>=<path>[=<impl_class>] flag,
  consumed in --general-only. Parses .lidl via lidlParse and .h via
  parseImplHeader, emits the bound <name>_api.{h,cpp}, and adds
  bind_<name>(moduleName) factories (QString + std::string) to the
  LogosModules umbrella. Also self-resolves local interface_dependencies
  from metadata.json for non-nix builds.
- experimental/lidl_gen_client: same BindMode parity for the --lidl path.

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

* address review: dedup and validate --interface specs

A repeated --interface <name>=... would emit duplicate #include and
bind_<name>(...) into logos_sdk.h and fail to compile; empty name/path were
silently accepted. Dedup the flag-derived specs by name and drop malformed
ones with an explanatory message on stderr. (Copilot review, PR #74.)

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

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-05 20:44:43 -03:00
Iuri Matias 825b80aeee re-add token auth (#73) 2026-06-05 18:12:29 -04:00
Iuri Matias b7eb07efed add doctests (#75)
* add doctests

* add doctest for cross module call
2026-06-05 14:47:47 -04:00
Dario LipicarandClaude Opus 4.8 3bdd8858f5 handle reserved words in names/parameters correctly (#72)
* handle reserved words in names/parameters correctly

* parser: accept reserved words as dependency names too

Addresses review feedback on the depends list: parseMetadata() still
hard-required LidlToken::Ident for each entry, so a dependency named after a
keyword (e.g. `version`) would fail to parse even though lidlSerialize()
emits it unquoted. Use atName() there too, consistent with the
contextual-keyword rule applied to the other name positions. Adds a
KeywordAsDependencyName regression test.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:14:15 -03:00
Dario LipicarandClaude Opus 4.8 7b62ac2017 Per-event documentation + getPluginEvents introspection (#71)
* Add per-event documentation + getPluginEvents introspection

Mirror the per-method documentation pipeline for events. Events
(declared in a universal module's logos_events: section) now carry a
description parsed from their /// doc comments, and are introspectable
at runtime via a new getPluginEvents framework call.

- lidl_ast: EventDecl gains a description field.
- impl_header_parser: capture the event's doc comment (previously
  discarded) and an optional metadata.json events[].description.
- lidl_gen_provider: generated universal provider emits
  getEvents() override, mirroring getMethods() (name/signature/
  parameters/description; no returnType/isInvokable — events are void).
- logos_provider_object: default-empty virtual getEvents() so the
  legacy provider path and QtProviderObject inherit empty.
- module_proxy / qt_provider_object: intercept getPluginEvents next to
  the getPluginMethods special-case.
- docs: spec + README event-documentation notes.

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

* Add unit tests for event documentation + getEvents generation

Address review feedback (#71): cover the event-introspection paths that
previously only had method-side tests.

- impl_header_parser test: assert metadata.json events[].description is
  parsed; new documented_events fixture asserts `///` doc-comment
  capture on a logos_events: block (multi-line joined with \n,
  adjacent-only, plain // ignored).
- lidl_gen_provider test: assert the generated dispatch contains
  getEvents() emitting each event's name/signature/parameters and an
  escaped description, and that events carry no returnType/isInvokable.

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

* Fold event introspection into getMethods() to keep the provider ABI stable

The previous approach added a getEvents() virtual to LogosProviderObject,
which inserted a new vtable slot and shifted every later slot — an ABI
break that would misdispatch virtual calls whenever an old and new
host/module were mixed across the in-process plugin boundary.

Instead, report events INSIDE the existing getMethods() call: it now
returns the module's whole interface, with each entry tagged
type "method" or "event" (events omit returnType/isInvokable). The
provider vtable is therefore byte-for-byte unchanged, so old/new hosts
and modules stay binary-compatible — a new host reading an old module
sees no event entries (zero events), and an old host reading a new
module just ignores the "type" field (cosmetic). An entry with no
"type" is treated as a method.

- logos_provider_object.h: remove the getEvents() virtual; document that
  getMethods() carries both, and why.
- generator (lidl_gen_provider): emit events as type "event" entries
  inside getMethods(); tag methods type "method"; no getEvents() output.
- module_proxy / qt_provider_object: getPluginMethods()/getPluginEvents()
  are now type-filtered views of getMethods(), plus a new
  getPluginInterface() returning the whole list. (These are name-
  dispatched Q_INVOKABLEs, not vtable surface — adding them is safe.)
- tests: generator asserts events fold into getMethods() tagged "event";
  ModuleProxy asserts the three filtered views; parser tests unchanged.
- docs: spec/project/docs/README updated, incl. an ABI rationale note.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 17:06:12 -03:00
Dario LipicarandClaude Opus 4.8 760916e97b Parse method doc comments into per-method description (#70)
* parse adjacent method comments to populate description

* preserve line breaks in method descriptions

Join doc-comment lines with newlines instead of spaces (markers stripped,
leading/trailing blank lines dropped, interior blanks kept), and escape \n
when emitting the description into the generated getMethods(). Both codegen
paths updated; docs corrected.

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

* don't count braces inside comment lines (impl-header parser)

A brace in a doc/line comment (e.g. `/// returns { ... }`) no longer affects
class-scope tracking, which previously could make the parser think the class
ended early and drop later declarations. Addresses review feedback on #70.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 11:38:27 -03:00
Dario Lipicar c71089abe9 remove legacy emitEvent (#69) tutorial-v3 2026-06-02 15:54:54 -03:00
Igor SirotinandClaude Opus 4.7 d77c3dd616 fix: marshal provider events onto the source thread (#68)
* fix: marshal provider events onto the source thread

ModuleProxy's event listener emitted eventResponse directly on whatever thread
the module fired the event from (its worker/FFI thread). QtRemoteObjects then
serialized and sent the event from that foreign thread, racing the source
socket against a method reply being sent from the source thread, which silently
dropped the reply.

This is why a method that emits an event mid-call never returns to the caller
(e.g. delivery_module start(), which emits connectionStateChanged as the node
connects) while a method that emits nothing (createNode) returns fine.

Marshal the emission onto the ModuleProxy's own thread via a queued invocation
so events and method replies are serialized on the single thread
QtRemoteObjects expects to own the source.

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

* docs: generalize the threading comment

* fix: use AutoConnection so same-thread emits stay synchronous

QueuedConnection deferred every emission, breaking same-thread callers that
emit-then-assert and crashing when a queued lambda outlived the object.
AutoConnection invokes synchronously when already on the source thread and only
queues cross-thread emissions (the actual fix); passing 'this' as context
cancels a queued call if the object is destroyed first.

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-25 09:35:25 -04:00
Iuri Matias 4e20614cda add universal qt-free provider interface (#67)
add universal qt-free provider interface

address review
2026-05-22 17:44:07 -04:00
Iuri Matias c87f3437e3 add overloads supporting nlohmann::json as intermediate steps to remove qt (#66) 2026-05-22 16:08:24 -04:00
Iuri Matias 3eea564de1 overload with method that don't use QString (#65) 2026-05-21 15:37:50 -04:00
Iuri Matias e29ba9b185 overload with method that don't use QString (#64)
overload with method that don't use QString

overload with method that don't use QString

overload with method that don't use QString

overload with method that don't use QString
2026-05-21 14:04:25 -04:00
Dario Lipicar 8bdbd13848 Extend universal modules with module context (#61)
* extend universal modules with module context

* implement module calls and events for universal modules

* pr comments
2026-05-19 12:49:15 -03:00
Dario Lipicar 25c88f4d48 support non-local remote transports (#57)
* support non-local remote transports

* fix LogosResult

* allow getting client over specific transport

* fix ssl

* investiage ssl error

* pr comments

* allow transport set configuration on any module

* pr comments

* add docs

* pr comments

* propagate only non-qt dependencies

* restore ABI compatibility
tutorial-v2
2026-05-07 12:27:14 -03:00
Dario Lipicar ecd369d48b properly handle subscribers to all events (#56) 2026-04-22 15:31:20 -03:00
Iuri Matias f7c855b110 add logos result type (#55) 2026-04-21 09:38:37 -04:00
Iuri Matias 04b75c84b8 add setProperty without using qt type (#52) 2026-04-16 13:15:18 -04:00
Iuri Matias 1a0cb031db add overloards using cpp types to replace qt ones later (#51) 2026-04-16 12:41:17 -04:00
Iuri Matias 1468180b25 add support for new types (#50) 2026-04-13 13:29:26 -04:00
Dario Lipicar 2a21637e02 fix async call crash (#49)
* fix async call crash

* add regression test
2026-04-10 10:39:14 -04:00
Dario Lipicar 8b1cfadf09 make async method calls truly async (#48)
* make async method calls truly async

* PR comments
2026-04-09 11:37:51 -03:00
Iuri Matias d633575677 add IDL parser & generator (wip) (#33)
* add IDL parser & generator (wip)

* fix fixtures issues affecting tests

* fix fixtures issues affecting tests
tutorial-v1
2026-03-31 16:29:35 -04:00
Khushboo-dev-cppandDario Lipicar 38006e7240 add support for QVariantList and QVariantMap (#31) (#32)
Co-authored-by: Dario Lipicar <lipigl@gmail.com>
2026-03-31 14:02:35 +02:00
Dario Lipicar 01221559b7 add support for QVariantList and QVariantMap (#31) 2026-03-26 10:12:18 -03:00
Iuri MatiasandLogos Workspace 02baa918a8 remove proxy api (moved to logos-module-client) (#30)
Co-authored-by: Logos Workspace <logos@workspace.local>
2026-03-25 15:00:21 -04:00
Jakub c937eae156 chore: add missing MIT and Apache v2 licenses (#29)
Signed-off-by: Jakub Sokołowski <jakub@status.im>
2026-03-25 12:20:11 -04:00
Iuri MatiasandLogos Workspace 21f8fb43d3 abstract/move some of proxy api logic to cpp-sdk (#28)
Co-authored-by: Logos Workspace <logos@workspace.local>
2026-03-25 08:46:45 -04: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
Iuri MatiasandLogos Workspace 4197ee1830 Finish Abstraction & Refactor (Ongoing) - part 1 (#25)
* refactor: abstract connection/transport; and clearly separate qt remote obj and qt local into separate implementations

* abstract qt remote registry

* add mock implementation; these serves to further test the abstraction but also useful for testing modules later

* use LogosObject instead of QObject

* abstract provider side

* updates to use new api

* re-add async api back

---------

Co-authored-by: Logos Workspace <logos@workspace.local>
2026-03-23 11:45:25 -04:00