Files
logos-liblogos/src/logos_core/protocol_gate.h
T
Dario LipicarandClaude Opus 4.8 050f2d3628 Qt-split retarget + logos_protocol_version load gate (#142)
* Qt-split retarget + protocol-version load gate

- Link the split SDK stack: logos-qt-sdk (LogosAPI/provider glue; the
  logos_sdk alias now points at logos-qt-sdk::logos_qt_sdk, chaining
  logos-protocol) + Qt-free logos-cpp-sdk headers.
- Protocol-version load gate (the first real consumer of module
  metadata pre-load): ModuleManager reads the module's embedded
  logos_protocol_version before runtime.load() and applies the one
  compatibility rule — equal protocol MAJOR loads, different MAJOR is
  refused with a diagnostic naming both versions, missing/unparseable
  stamp (pre-protocol modules) loads permissively with a warning. The
  decision logic is std-only (logos_core/protocol_gate.h) and unit
  tested (refuse bumped major / warn-load legacy / silent minor skew).
- ModuleDescriptor.rawMetadata is now actually populated for runtimes.

* lock: pin extraction-chain branch revs for standalone CI

Temporary — drop when the chain PRs merge (re-lock against masters).

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

* doctest: pin logoscore-cli to its qt-split branch head

The doc-test builds logoscore-cli at latest master with only liblogos
overridden to the commit under test; master logoscore-cli cannot build
against qt-split liblogos. Pin the runtime to the chain branch
(logos-co/logos-logoscore-cli#43) so the doc-test exercises the
coherent stack. Temporary — revert to the unpinned URL when the chain
merges.

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

* host: surface the spawn auth token as a LogosAPI property

cdylib-authored modules run their own statically-linked protocol stack
whose TokenManager is a separate copy of the singleton; the generated Qt
glue reads this property (cross-image-safe, like modulePath) and seeds
the cdylib's stack via logos_module_accept_token so the module's
outbound calls authenticate.

* host: set the authToken property before registerObject

registerObject runs the provider object's init() — where the cdylib glue
reads the property. Setting it afterwards meant cdylib modules always saw
an empty token.

* lock: protocol+cpp-sdk merged to master — pins advance (protocol 9de4165, cpp-sdk f0fe8cb, qt-sdk 722e590)

* lock: qt-sdk#1 merged — pin advances to qt-sdk master

* gate: drop QJson from the Qt-free core — parse rawMetadataJson with nlohmann

The protocol-version load gate had pulled QJsonDocument/QJsonObject into
src/logos_core (Qt-free territory). logos-module now exposes the embedded
metadata as a compact JSON string, so the gate reads it via nlohmann and
the std::string extractMetadata overload.

* lock: logos-module b42805d (result-lm untracked)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 21:45:49 -03:00

60 lines
2.2 KiB
C++

#ifndef LOGOS_PROTOCOL_GATE_H
#define LOGOS_PROTOCOL_GATE_H
#include <cstdlib>
#include <string>
// ---------------------------------------------------------------------------
// Protocol-version load gate (pure decision logic, std-only).
//
// Every module built by a current logos-module-builder carries the
// logos-protocol semver it was compiled against in its embedded metadata
// (`logos_protocol_version`). One rule governs Logos load/call
// compatibility: two participants interoperate iff they share the same
// protocol MAJOR. The host (this library) refuses to load a module whose
// major differs from its own; modules with no stamp predate the scheme and
// load permissively ("legacy") so the existing fleet never hard-fails.
//
// The caller (ModuleManager::loadModuleInternal) extracts the stamp from the
// plugin metadata pre-load and logs according to the decision.
// ---------------------------------------------------------------------------
namespace LogosCore {
enum class ProtocolGateDecision {
Allow, // same major — compatible
AllowLegacy, // no/unparseable stamp — pre-protocol module, load + warn
Refuse, // different major — incompatible, do not load
};
struct ProtocolGateResult {
ProtocolGateDecision decision;
int moduleMajor = -1; // -1 when absent/unparseable
};
// Parse the MAJOR component of "MAJOR.MINOR.PATCH". Returns -1 when the
// string does not start with a non-negative integer.
inline int protocolVersionMajor(const std::string& version)
{
if (version.empty()) return -1;
char* end = nullptr;
const long major = std::strtol(version.c_str(), &end, 10);
if (end == version.c_str() || major < 0) return -1;
return static_cast<int>(major);
}
inline ProtocolGateResult evaluateProtocolGate(const std::string& moduleVersion,
int hostMajor)
{
const int moduleMajor = protocolVersionMajor(moduleVersion);
if (moduleMajor < 0)
return {ProtocolGateDecision::AllowLegacy, -1};
if (moduleMajor == hostMajor)
return {ProtocolGateDecision::Allow, moduleMajor};
return {ProtocolGateDecision::Refuse, moduleMajor};
}
} // namespace LogosCore
#endif // LOGOS_PROTOCOL_GATE_H