Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
199 commits
Select commit Hold shift + click to select a range
bef4860
feat(registry): add morph::model::ValidationError
Yaraslaut Jul 20, 2026
c184207
feat(registry): enforce ActionValidator + precision reconciliation in…
Yaraslaut Jul 20, 2026
407a17c
feat(bridge): enforce ActionValidator in executeVia's localOp on Loca…
Yaraslaut Jul 20, 2026
f14f6fe
test(bridge): verify ValidationError surfaces as an err reply over Si…
Yaraslaut Jul 20, 2026
378b49e
docs: fold server-side action validation into docs/spec, retire plann…
Yaraslaut Jul 20, 2026
7396fab
feat(session,remote): add IAuthorizer::authorizeRegister register gate
Yaraslaut Jul 20, 2026
ee3382d
feat(remote): replace sequential _nextId with opaque model ids
Yaraslaut Jul 20, 2026
8647fb8
docs: fold register-authorization & opaque-id spec into docs/spec
Yaraslaut Jul 20, 2026
179a7d3
feat(backend): add RemoteServer::LimitPolicy with maxLiveModels enfor…
Yaraslaut Jul 20, 2026
db0d5b4
feat(backend): add RemoteServer::LimitPolicy maxInFlightExecutes enfo…
Yaraslaut Jul 20, 2026
fb2b98e
feat(backend): add LimitPolicy::executeTimeout with TimeoutScheduler …
Yaraslaut Jul 20, 2026
f93ae74
feat(qt): add QtWebSocketServerConfig with maxConnections + maxMessag…
Yaraslaut Jul 20, 2026
ae6ba91
feat(qt): add QtWebSocketServerConfig::messagesPerSecond token-bucket…
Yaraslaut Jul 20, 2026
a4e7842
feat(qt): add QtWebSocketServerConfig handshakeTimeout + idleTimeout
Yaraslaut Jul 20, 2026
4040667
docs: fold transport_limits.md into backend.md/security.md/wire.md, r…
Yaraslaut Jul 20, 2026
8248aea
feat(qt): add tlsVerifyingConfig/tlsPinnedConfig/tlsInsecureNoVerify …
Yaraslaut Jul 20, 2026
526b3e9
test(qt): add throwaway mitm cert pair, add SAN to the server test cert
Yaraslaut Jul 20, 2026
c69a427
test(qt): verify tlsPinnedConfig accepts the real server and rejects …
Yaraslaut Jul 20, 2026
2185278
feat(qt): add QtWebSocketServerConfig exposure guard for non-loopback…
Yaraslaut Jul 20, 2026
b376bfe
docs(qt): add a worked TLS pinned-certificate example
Yaraslaut Jul 20, 2026
60dace4
docs: fold TLS peer verification into security.md/backend.md, retire …
Yaraslaut Jul 20, 2026
4698abd
feat(remote): add ConnectionId scope skeleton to RemoteServer
Yaraslaut Jul 20, 2026
aba3767
feat(remote): wire register/deregister to connection scope, add scope…
Yaraslaut Jul 20, 2026
281fde7
feat(qt): reclaim connection-scoped models on WebSocket disconnect
Yaraslaut Jul 20, 2026
10695db
docs: fold connection-scoped cleanup into backend.md, remove planned …
Yaraslaut Jul 20, 2026
02cb592
feat(session_auth): add opt-in MORPH_REQUIRE_VETTED_HMAC build option
Yaraslaut Jul 20, 2026
8392246
feat(session_auth): drop hmacSha256 default arg under MORPH_REQUIRE_V…
Yaraslaut Jul 20, 2026
cac5869
test(session_auth): try_compile guard proving MORPH_REQUIRE_VETTED_HM…
Yaraslaut Jul 20, 2026
70077da
feat(examples): add libsodium HMAC-SHA256 MacFunction adapter example
Yaraslaut Jul 20, 2026
ff37b76
feat(examples): add OpenSSL HMAC-SHA256 MacFunction adapter example
Yaraslaut Jul 20, 2026
beeb745
docs(security): fold vetted-HMAC spec into security.md; retire planne…
Yaraslaut Jul 20, 2026
385affc
feat(wire): add protocol-version primitives (kProtocolVersion, protoc…
Yaraslaut Jul 20, 2026
4813730
feat(remote): RemoteServer answers "hello" with its supported version…
Yaraslaut Jul 20, 2026
0374679
feat(remote): add SimulatedRemoteBackend::negotiateProtocolVersion()
Yaraslaut Jul 20, 2026
e7756fd
feat(qt): add QtWebSocketBackend::negotiateProtocolVersion()
Yaraslaut Jul 20, 2026
abc03e9
fix(registry): BRIDGE_REGISTER_ACTION's fromJson/resultFromJson are f…
Yaraslaut Jul 20, 2026
3f1350c
docs: fold protocol-version negotiation and action-evolution policy i…
Yaraslaut Jul 20, 2026
2f25d78
feat(offline): add QueueItem::attempts and IOfflineQueue::setAttempts
Yaraslaut Jul 20, 2026
fd500e1
feat(offline): add SyncWorker::DeadLetterSink and durable attempt see…
Yaraslaut Jul 20, 2026
ecc7b3c
docs(offline): fold durable attempts + DeadLetterSink into offline.md
Yaraslaut Jul 20, 2026
157e51e
feat(journal): add LogEntry::idempotencyKey for outbox dedup
Yaraslaut Jul 21, 2026
39da4b6
feat(journal): InMemoryActionLog dedups append() by idempotencyKey
Yaraslaut Jul 21, 2026
c0e56bb
feat(journal): FileActionLog dedups append() by idempotencyKey across…
Yaraslaut Jul 21, 2026
fbe0d43
feat(core): IModelHolder::setOutboxManaged suppresses auto-append
Yaraslaut Jul 21, 2026
dd98592
feat(journal): add OutboxRelay to relay outbox rows into a durable IA…
Yaraslaut Jul 21, 2026
614105c
docs: fold the transactional outbox into journal.md/registry.md, drop…
Yaraslaut Jul 21, 2026
8fe4272
feat(core): add morph::observe metrics/trace seam
Yaraslaut Jul 21, 2026
250422e
feat(core): emit metrics/trace spans from RemoteServer dispatch, add …
Yaraslaut Jul 21, 2026
3a39cc1
feat(core): emit metrics and trace spans from LocalBackend execute
Yaraslaut Jul 21, 2026
b2f4325
feat(offline): emit queueDepth/reconnectAttempts/reconnectOutcome met…
Yaraslaut Jul 21, 2026
cf96111
docs: fold observability.md into docs/spec, delete the planned spec
Yaraslaut Jul 21, 2026
8d1abfb
feat(backend): add RemoteServer::beginShutdown() to reject new regist…
Yaraslaut Jul 21, 2026
614a975
feat(backend): add RemoteServer::drainedWithin() over a shared in-fli…
Yaraslaut Jul 21, 2026
9ce0891
feat(qt): add QtWebSocketServer::closeGracefully(deadline)
Yaraslaut Jul 21, 2026
b809dd0
docs: fold graceful shutdown/drain into backend.md and delete the pla…
Yaraslaut Jul 21, 2026
347a921
test: pin key constants (kMaxEnvelopeBytes, kMaxDecimalPlaces, kClock…
Yaraslaut Jul 21, 2026
3602826
test: pin AuthError/LogLevel/ReconnectOutcome cardinality
Yaraslaut Jul 21, 2026
131179f
test: pin BackendChangedError/BridgeDestroyedError/DisconnectedError …
Yaraslaut Jul 21, 2026
146fb24
test: pin RemoteServer's unauthorized/model-not-found/register reply …
Yaraslaut Jul 21, 2026
8a98ad6
test: pin wire::decode's error_on_unknown_keys=false and last-wins be…
Yaraslaut Jul 21, 2026
1d41f4b
ci: add scripts/check_spec_citations.sh (prose-vs-manifest lint)
Yaraslaut Jul 21, 2026
bbc610b
docs: correct rationale comment in check_spec_citations.sh
Yaraslaut Jul 21, 2026
37cc2c4
ci: add drift-guard workflow running the prose-vs-manifest lint
Yaraslaut Jul 21, 2026
bea6432
docs: fold the drift-guard mechanism into CONTRIBUTING.md; delete the…
Yaraslaut Jul 21, 2026
efa8b70
test: add fuzz_wire_decode libFuzzer harness (MORPH_BUILD_FUZZERS)
Yaraslaut Jul 21, 2026
8cc1e5a
test: add fuzz_dispatch_execute harness + replay-mode regression check
Yaraslaut Jul 21, 2026
f664e2d
test: add switchBackend churn soak test (MORPH_BUILD_LOAD_TESTS)
Yaraslaut Jul 21, 2026
c203866
test: add NetworkMonitor/ReconnectCoordinator/SyncWorker flap-churn s…
Yaraslaut Jul 21, 2026
0399911
test: add RemoteServer throughput/latency benchmark (MORPH_BUILD_LOAD…
Yaraslaut Jul 21, 2026
cbe83f5
test(qt): add adversarial cross-socket coverage for RemoteServer
Yaraslaut Jul 21, 2026
5b0798d
docs: fold testing_strategy.md into docs/spec, retire planned spec
Yaraslaut Jul 21, 2026
fc8983d
feat(forms): add FieldMeta field metadata (labels, help, placeholder,…
Yaraslaut Jul 21, 2026
2f2da54
feat(forms): add describe<&Action::field>() field-metadata sugar
Yaraslaut Jul 21, 2026
edf50a8
feat(forms-qml): render title/placeholder/read-only/hidden in Dynamic…
Yaraslaut Jul 21, 2026
242f2fb
docs: fold gui_field_metadata.md into docs/spec/forms/forms.md
Yaraslaut Jul 21, 2026
cc90068
feat(forms): add GroupKind/FieldGroup/FieldSpan layout descriptors
Yaraslaut Jul 21, 2026
15397eb
feat(forms): emit x-layout/x-group/x-section from formLayout
Yaraslaut Jul 21, 2026
fb5fdbe
feat(forms): emit x-colspan from fieldSpans
Yaraslaut Jul 21, 2026
4f77587
feat(examples/forms): demonstrate formLayout/fieldSpans on RecordMeas…
Yaraslaut Jul 21, 2026
483eb45
feat(examples/forms/gui_qml): render sections, tabs, accordions, and …
Yaraslaut Jul 21, 2026
4cb3e23
docs(forms): fold layout & grouping into forms.md, remove planned spec
Yaraslaut Jul 21, 2026
28b7c6b
feat(forms): add Multiline/Ranged widget-hint field wrappers
Yaraslaut Jul 21, 2026
7841e7f
feat(forms): emit x-widget/x-min/x-max/x-step from schemaJson
Yaraslaut Jul 21, 2026
aab7d7a
feat(forms-qml): render textarea/slider/radio controls from x-widget
Yaraslaut Jul 21, 2026
1cb7f38
docs(forms): add widget_hints.md spec for Multiline/Ranged
Yaraslaut Jul 21, 2026
a9e8cef
docs(forms): document x-widget/x-min/x-max/x-step in forms.md; retire…
Yaraslaut Jul 21, 2026
934f08d
feat(forms): add morph::forms::i18n message-key derivation
Yaraslaut Jul 21, 2026
a93794f
feat(forms): add FieldMeta::i18nKey stem override, emit x-i18nKey
Yaraslaut Jul 21, 2026
e44fc76
feat(render): add morph::render TranslationProvider catalog seam
Yaraslaut Jul 21, 2026
95377c0
feat(render): add locale numeric-entry normalisation and pin the Time…
Yaraslaut Jul 21, 2026
c8a7667
feat(gui_qml): add I18nCatalog, the QML host's TranslationProvider re…
Yaraslaut Jul 21, 2026
1fd4651
feat(gui_qml): resolve field label/help/placeholder through the i18n …
Yaraslaut Jul 21, 2026
85a053a
feat(gui_qml): normalize locale-formatted numeric entry at the contro…
Yaraslaut Jul 21, 2026
4b85458
feat(gui_qml): shift zoned Timestamp entry to canonical UTC before su…
Yaraslaut Jul 21, 2026
8d40136
docs: fold gui_i18n.md into forms.md; delete the planned spec
Yaraslaut Jul 21, 2026
a3ce951
refactor(qt): ship the QML forms renderer as src/qt/forms, decoupled …
Yaraslaut Jul 21, 2026
afb3783
feat(qt): factor FormsControllerCore<Model> out of the forms demo, fi…
Yaraslaut Jul 21, 2026
e788047
feat(qt): add x-widget dual-read and the SlotRegistry client-side ove…
Yaraslaut Jul 21, 2026
2da5465
feat(qt): add Accessible name/description/role and keyboard-operable …
Yaraslaut Jul 21, 2026
4adb75f
test(forms): add the conformance corpus's C++ fixtures + schemaJson d…
Yaraslaut Jul 21, 2026
e9c3e49
test(qt): add QML functional conformance assertions mirroring the C++…
Yaraslaut Jul 21, 2026
f1803cf
test(qt): add deliberately-broken-renderer negative assertions to the…
Yaraslaut Jul 21, 2026
1f4946a
docs(forms): fold the renderer toolkit into forms.md, retire the plan…
Yaraslaut Jul 21, 2026
639c3cc
feat(forms): add core cross-field rule engine (requiredWhen, engaged/…
Yaraslaut Jul 21, 2026
b1e7d55
feat(forms): add greater/greaterOrEqual/less/lessOrEqual cross-field …
Yaraslaut Jul 21, 2026
df37110
feat(forms): add equals condition and exactlyOneOf/atLeastOneOf/mutua…
Yaraslaut Jul 21, 2026
6dc08af
feat(forms): add visibleWhen/readonlyWhen presentation rules
Yaraslaut Jul 21, 2026
d762ab6
test(forms): prove cross-field rules reject identically on LocalBacke…
Yaraslaut Jul 21, 2026
5fd2bc9
feat(forms-qml): evaluate x-rules live in the reference renderer (req…
Yaraslaut Jul 21, 2026
951f3e4
docs: fold cross-field rules into docs/spec/forms/forms.md, retire pl…
Yaraslaut Jul 21, 2026
2037181
feat(forms): add computed-field declaration API and recompute engine
Yaraslaut Jul 21, 2026
3b31b32
feat(forms): emit x-computed/x-readonly for computed fields in schema…
Yaraslaut Jul 21, 2026
c1d35d0
feat(bridge): recompute declared computed fields live on the reactive…
Yaraslaut Jul 21, 2026
1109041
feat(bridge,registry): recompute computed fields authoritatively on e…
Yaraslaut Jul 21, 2026
43526fe
docs: fold computed-fields design into docs/spec, delete planned spec
Yaraslaut Jul 21, 2026
423360c
feat(forms): add Choice::DependsOn for cascading options
Yaraslaut Jul 21, 2026
8b8d69b
feat(forms): emit x-optionsDependsOn for dependent Choice fields
Yaraslaut Jul 21, 2026
dab8400
feat(forms-demo): add ShippingAddress cascading country/city example
Yaraslaut Jul 21, 2026
8266749
fix(forms): rename Rule/Condition::emit() to emitNode() (Qt macro col…
Yaraslaut Jul 21, 2026
e44c417
refactor(forms-qml): make fetchOptions a true name+body pass-through
Yaraslaut Jul 21, 2026
9062076
feat(forms-qml): fetch, re-fetch, and clear dependent Choice options
Yaraslaut Jul 21, 2026
bb67b42
docs: fold dependent Choice options into docs/spec/forms/
Yaraslaut Jul 21, 2026
bd5c054
feat(offline): add FileOfflineQueue, a dependency-free NDJSON durable…
Yaraslaut Jul 21, 2026
a892a4d
test(offline): cover FileOfflineQueue durability, dedup, and torn-lin…
Yaraslaut Jul 21, 2026
7dc0185
build(offline): add opt-in MORPH_BUILD_OFFLINE_SQLITE option and test…
Yaraslaut Jul 21, 2026
f62aa86
feat(offline): add SqliteOfflineQueue, an opt-in SQLite-backed durabl…
Yaraslaut Jul 21, 2026
be5bcc0
test(offline): cover SqliteOfflineQueue durability, dedup, and cross-…
Yaraslaut Jul 21, 2026
cb4a13b
docs(offline): fold durable-queue and SQLite/file-queue specs into of…
Yaraslaut Jul 21, 2026
1918868
feat(journal): make journal::fromJson decode leniently
Yaraslaut Jul 21, 2026
79a9cb3
feat(journal): stamp a line-format version on every LogEntry
Yaraslaut Jul 21, 2026
b620973
feat(journal): add FileActionLog::rotate() retention seam
Yaraslaut Jul 21, 2026
20291fa
docs: fold journal format versioning & retention into journal.md
Yaraslaut Jul 21, 2026
d24a1c8
test(backend): add parity-baseline tests for notifyBackendChanged
Yaraslaut Jul 21, 2026
d2fe22c
feat(model): add compile-time isBackendChangeAware/onBackendChanged t…
Yaraslaut Jul 21, 2026
e0fb42e
refactor(backend): drop dynamic_cast sweep from LocalBackend::notifyB…
Yaraslaut Jul 21, 2026
39b80b8
test(backend): cover _changeAware bookkeeping across register/deregister
Yaraslaut Jul 21, 2026
fc2b484
docs: fold compile-time onBackendChanged dispatch into backend.md/reg…
Yaraslaut Jul 21, 2026
8d32640
feat: add morph::version constants cross-checked against CMakeLists.txt
Yaraslaut Jul 21, 2026
277b134
ci: lint [[deprecated]] markers for a replacement and removal version
Yaraslaut Jul 21, 2026
2575a68
docs: publish the API stability / 1.0 versioning policy
Yaraslaut Jul 21, 2026
8cc9313
feat(net): scaffold MORPH_BUILD_NET option and add base64 encoder
Yaraslaut Jul 21, 2026
a586ea3
feat(net): add hand-rolled SHA-1 digest for the WebSocket handshake
Yaraslaut Jul 21, 2026
1e3a7f1
feat(net): add WebSocket handshake message builders/parsers
Yaraslaut Jul 21, 2026
3ac1141
feat(net): add TcpSocket RAII POSIX socket wrapper
Yaraslaut Jul 21, 2026
1aee33c
feat(net): add WebSocket frame encode/decode
Yaraslaut Jul 21, 2026
d538d85
feat(net): add socket-backed handshake I/O helpers
Yaraslaut Jul 21, 2026
2ea6e3f
feat(net): add SocketServer, a raw-socket WebSocket front for RemoteS…
Yaraslaut Jul 21, 2026
98c5dd0
feat(net): add SocketBackend, a raw-socket client-side IBackend
Yaraslaut Jul 21, 2026
fe8ce7e
test(net): cover SocketBackend disconnect/reconnect and fail-fast paths
Yaraslaut Jul 21, 2026
fe4e51c
test(net): prove morph::net and morph::qt transports interoperate
Yaraslaut Jul 21, 2026
ca9b440
docs: fold morph::net into backend.md and retire the planned spec
Yaraslaut Jul 21, 2026
a849ff5
feat(views): add ActionDescriptor/ColumnOverride/kind tags for view s…
Yaraslaut Jul 21, 2026
1ccbffb
feat(views): derive v-columns from a query's row-type schema
Yaraslaut Jul 21, 2026
fce5c0a
feat(views): assemble viewSchemaJson<V>() (v-kind/v-query/v-rowKey/v-…
Yaraslaut Jul 21, 2026
62419b2
feat(views): add ViewTraits/BRIDGE_REGISTER_VIEW/ViewRegistry
Yaraslaut Jul 21, 2026
b507948
feat(examples/forms): add EditSample/DeleteSample/CreateSample + Samp…
Yaraslaut Jul 21, 2026
d456bd4
feat(gui_qml): expose FormsController::viewsJson
Yaraslaut Jul 21, 2026
4c69f00
feat(gui_qml): add CollectionView.qml, the list/master-detail referen…
Yaraslaut Jul 21, 2026
eab5fa6
feat(gui_qml): wire CollectionView into Main.qml
Yaraslaut Jul 21, 2026
7053886
test(gui_qml): cover CollectionView (populate, columns, row-open, edi…
Yaraslaut Jul 21, 2026
76d8e81
docs: fold gui_collections_views planned spec into docs/spec/forms/vi…
Yaraslaut Jul 21, 2026
c0df5ec
feat(forms): add wizard descriptor types and wizardSchemaJson
Yaraslaut Jul 21, 2026
8b49c73
feat(forms): add FlowSession, the typed multi-step flow sequencer
Yaraslaut Jul 21, 2026
24fe351
feat(forms): add app-shell descriptor and appSchemaJson
Yaraslaut Jul 21, 2026
e6bfd16
feat(examples/forms): add RegisterSample action for the intake wizard…
Yaraslaut Jul 21, 2026
7ba23d1
feat(examples/forms): add IntakeWizard and LabApp descriptors
Yaraslaut Jul 21, 2026
9617e32
feat(examples/forms): expose wizard and app-shell schema JSON
Yaraslaut Jul 21, 2026
5d967e7
feat(examples/forms/gui_qml): expose wizard/app schemas and resolved-…
Yaraslaut Jul 21, 2026
a819164
feat(qt/forms): add WizardView, the multi-step stepper (MorphForms mo…
Yaraslaut Jul 21, 2026
1f5e93b
feat(examples/forms/gui_qml): add AppShell, the app's new default ent…
Yaraslaut Jul 21, 2026
fc68f76
docs(examples/forms): document the app-shell and intake wizard demo
Yaraslaut Jul 21, 2026
94aa0a1
docs(spec): fold the wizard/app-shell design into docs/spec, retire t…
Yaraslaut Jul 21, 2026
39bc2ed
docs(spec): fix cross-reference edits dropped from the previous commit
Yaraslaut Jul 21, 2026
0296259
docs: fold gui_overview.md into forms.md, close out the production-ha…
Yaraslaut Jul 21, 2026
b4f265a
fix(wire): fix two heap-safety bugs C3's fuzz harness found
Yaraslaut Jul 22, 2026
06376c6
examples: add concept-teaching tests for the production-hardening fea…
Yaraslaut Jul 22, 2026
5c1bec9
ci(docs): pin Doxygen to 1.17.0 instead of Ubuntu 24.04's stale apt p…
Yaraslaut Jul 24, 2026
83158d5
docs(spec/forms): fix dead links, contradictions, and forward references
Yaraslaut Jul 24, 2026
9c7e0e5
docs(examples/forms): improve example clarity
Yaraslaut Jul 24, 2026
4f2a945
docs(examples/concepts): consistency and clarity improvements
Yaraslaut Jul 24, 2026
4718a8d
fix(tests): update stale expectations shadowed by validate()/authenti…
Yaraslaut Jul 24, 2026
0e8ea62
fix: resolve GCC-only CI failures (useless-cast error, ODR violation)
Yaraslaut Jul 24, 2026
a82d931
test: close the remaining patch-coverage gaps (bridge/model/observabi…
Yaraslaut Jul 24, 2026
6c2f33d
fix: close two Windows-only bugs behind the flaky CI failures
Yaraslaut Jul 24, 2026
ba38363
wire: escape control bytes in every field, not just err messages
Yaraslaut Jul 27, 2026
6304f50
net: fix reconnect deadlock, close() hang, and fragment handling
Yaraslaut Jul 27, 2026
e179880
remote: stop resurrecting closed scopes, make the limits actually bound
Yaraslaut Jul 27, 2026
fd7ba99
observability: never call a sink under the lock or let one throw
Yaraslaut Jul 27, 2026
9a52a43
qt: address the oversize-frame reply, settle calls on a bad envelope
Yaraslaut Jul 27, 2026
29fb202
journal: repair torn tails and stop swallowing write failures
Yaraslaut Jul 27, 2026
fe1da67
offline: keep the id high-water mark across compaction
Yaraslaut Jul 27, 2026
fe622c2
forms: let a constexpr ruleList hold a string literal of any length
Yaraslaut Jul 27, 2026
38d5cdd
flows: ignore a step's reply once the flow has moved past it
Yaraslaut Jul 27, 2026
974ba2e
forms/qml: stop leaking editor state between rows and across tabs
Yaraslaut Jul 27, 2026
990bfad
render: take locale separators as strings, not chars
Yaraslaut Jul 27, 2026
f38f661
build: make the guards that were reporting green actually fire
Yaraslaut Jul 27, 2026
1f99f41
ci: build, test and lint the opt-in features that nothing covered
Yaraslaut Jul 27, 2026
2bd504e
build: fix the two CI failures the new feature coverage exposed
Yaraslaut Jul 27, 2026
59d2110
ci: install Qt 6.5+ for the jobs that build the QML forms renderer
Yaraslaut Jul 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
219 changes: 206 additions & 13 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
SCCACHE_DIR: /home/runner/.cache/sccache
CLANG_VERSION: "22"
# MORPH_BUILD_FORMS_QML needs Qt 6.5+; ubuntu-24.04 apt still ships 6.4.2.
QT_VERSION: "6.8.1"

jobs:
# ── Windows: MSVC + clang-cl ──────────────────────────────────────────
Expand Down Expand Up @@ -139,7 +141,7 @@ jobs:
- name: Install Clang ${{ env.CLANG_VERSION }} from apt.llvm.org
run: |
sudo apt-get update -q
sudo apt-get install -y ninja-build catch2
sudo apt-get install -y ninja-build catch2 libsqlite3-dev
wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- ${{ env.CLANG_VERSION }}

- name: Cache sccache
Expand All @@ -154,9 +156,17 @@ jobs:
curl -sSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
| tar -xz --strip-components=1 -C /usr/local/bin sccache-v0.9.1-x86_64-unknown-linux-musl/sccache

# morph::net and the SQLite offline queue are opt-in, but they are also
# where the memory/threading/UB risk actually lives (raw sockets, an I/O
# thread, a hand-rolled frame reader, a C API). Left off, the sanitizers
# and the coverage number both silently skipped them. Qt/QML and the
# fuzzers stay out of this matrix — they are covered by the
# linux-all-features job, and a GUI stack under TSan is mostly noise.
- name: Configure
run: |
cmake --preset ${{ matrix.preset }} \
-DMORPH_BUILD_NET=ON \
-DMORPH_BUILD_OFFLINE_SQLITE=ON \
-DCMAKE_C_COMPILER=clang-${{ env.CLANG_VERSION }} \
-DCMAKE_CXX_COMPILER=clang++-${{ env.CLANG_VERSION }} \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
Expand Down Expand Up @@ -243,6 +253,129 @@ jobs:
QT_QPA_PLATFORM: offscreen
run: ctest --preset gcc-debug

# ── Linux: every optional feature enabled at once ─────────────────────
# Every MORPH_BUILD_* option below is off by default, and until this job
# existed no CI configuration turned any of them on — so several thousand
# lines (morph::net, the Qt/QML forms renderer, the SQLite queue, the fuzz
# harnesses, the vetted-HMAC adapters, the soak/bench targets) were never
# compiled here, let alone tested. That is how a build that cannot configure
# (MORPH_REQUIRE_VETTED_HMAC) and a replay test matching the wrong file
# extension both reached master green. Enabling them together also proves
# they compose, which building each alone would not.
linux-all-features:
name: Linux / all optional features (${{ matrix.compiler }})
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
# Clang additionally builds the libFuzzer harnesses, which require it.
- compiler: clang
preset: clang-debug
fuzzers: 'ON'
- compiler: gcc
preset: gcc-debug
fuzzers: 'OFF'
steps:
- uses: actions/checkout@v4

- name: Cache apt packages
uses: actions/cache@v4
with:
path: /var/cache/apt/archives
key: apt-all-features-${{ matrix.compiler }}-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: apt-all-features-${{ matrix.compiler }}-

- name: Install toolchain and the non-Qt dependencies
run: |
sudo apt-get update -q
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -q
sudo apt-get install -y ninja-build catch2 \
libsqlite3-dev libsodium-dev libssl-dev \
libgl1-mesa-dev libxkbcommon-x11-0 libxcb-cursor0 libxcb-icccm4 \
libxcb-keysyms1 libxcb-shape0 libxcb-xinerama0
if [ "${{ matrix.compiler }}" = "gcc" ]; then
sudo apt-get install -y gcc-15 g++-15
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 15
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 15
else
wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- ${{ env.CLANG_VERSION }}
fi

# Not the distro's Qt: MORPH_BUILD_FORMS_QML needs 6.5+ (see the version
# floor in CMakeLists.txt) and Ubuntu 24.04 still ships 6.4.2, whose
# QQmlApplicationEngine has no loadFromModule.
- name: Install Qt ${{ env.QT_VERSION }}
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
modules: qtwebsockets
cache: true

- name: Cache sccache
uses: actions/cache@v4
with:
path: /home/runner/.cache/sccache
key: sccache-all-features-${{ matrix.compiler }}-${{ github.sha }}
restore-keys: sccache-all-features-${{ matrix.compiler }}-

- name: Install sccache
run: |
curl -sSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
| tar -xz --strip-components=1 -C /usr/local/bin sccache-v0.9.1-x86_64-unknown-linux-musl/sccache

# MORPH_REQUIRE_VETTED_HMAC is deliberately combined with
# MORPH_BUILD_TESTS=ON (the preset default). docs/spec/security.md
# documents that pairing as supported; this is what keeps it that way.
- name: Configure (every optional feature ON)
run: |
EXTRA=""
if [ "${{ matrix.compiler }}" = "clang" ]; then
EXTRA="-DCMAKE_C_COMPILER=clang-${{ env.CLANG_VERSION }} -DCMAKE_CXX_COMPILER=clang++-${{ env.CLANG_VERSION }}"
fi
# shellcheck disable=SC2086
cmake --preset ${{ matrix.preset }} \
-DMORPH_BUILD_NET=ON \
-DMORPH_BUILD_QT=ON \
-DMORPH_BUILD_FORMS_QML=ON \
-DMORPH_BUILD_OFFLINE_SQLITE=ON \
-DMORPH_BUILD_LOAD_TESTS=ON \
-DMORPH_BUILD_HMAC_EXAMPLES=ON \
-DMORPH_BUILD_FUZZERS=${{ matrix.fuzzers }} \
-DMORPH_REQUIRE_VETTED_HMAC=ON \
$EXTRA \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache

- name: Build
run: cmake --build --preset ${{ matrix.preset }}

# Includes the fuzz *replay* tests on the clang leg: each committed seed
# and crash reproducer is replayed once and must not crash. This is a
# deterministic regression check, not a fuzzing campaign.
- name: Test (offscreen Qt platform)
env:
QT_QPA_PLATFORM: offscreen
run: ctest --preset ${{ matrix.preset }} --output-on-failure

# A guard that never fires is indistinguishable from one that works, so
# assert the fuzz replay actually ran the committed crash reproducers
# rather than silently matching nothing (the `.txt`-vs-`.bin` glob bug).
- name: Verify the fuzz replay covered the committed reproducers
if: matrix.fuzzers == 'ON'
run: |
ctest --preset ${{ matrix.preset }} -R fuzz -V > fuzz-replay.log 2>&1
status=0
for finding in tests/fuzz/findings/*/*; do
if ! grep -qF "$(basename "$finding")" fuzz-replay.log; then
echo "::error::fuzz replay never referenced committed reproducer $finding"
status=1
fi
done
exit "$status"

# ── Valgrind (memcheck) ───────────────────────────────────────────────
valgrind:
name: Valgrind memcheck
Expand Down Expand Up @@ -279,22 +412,41 @@ jobs:
curl -sSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
| tar -xz --strip-components=1 -C /usr/local/bin sccache-v0.9.1-x86_64-unknown-linux-musl/sccache

# morph::net and the SQLite queue are where the raw pointers, manual
# buffers and C API calls live — precisely what memcheck is for — so they
# are built here rather than left to the default (Qt/QML stays out: it
# drags in a GUI stack whose own allocations dominate the report).
- name: Configure
run: cmake --preset gcc-debug -DCMAKE_C_COMPILER_LAUNCHER=sccache -DCMAKE_CXX_COMPILER_LAUNCHER=sccache
run: |
cmake --preset gcc-debug \
-DMORPH_BUILD_NET=ON \
-DMORPH_BUILD_OFFLINE_SQLITE=ON \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
-DCMAKE_CXX_COMPILER_LAUNCHER=sccache

- name: Build
run: cmake --build --preset gcc-debug

- name: Run tests under Valgrind
run: |
valgrind \
--tool=memcheck \
--leak-check=full \
--show-leak-kinds=definite,indirect \
--errors-for-leak-kinds=definite,indirect \
--error-exitcode=1 \
--suppressions=cmake/valgrind.supp \
build/gcc-debug/tests/morph_tests
status=0
for suite in tests/morph_tests tests/net/morph_net_tests tests/offline_sqlite/morph_offline_sqlite_tests; do
binary="build/gcc-debug/$suite"
if [ ! -x "$binary" ]; then
echo "::error::expected Valgrind suite $binary was not built"
exit 1
fi
echo "── memcheck: $suite ──"
valgrind \
--tool=memcheck \
--leak-check=full \
--show-leak-kinds=definite,indirect \
--errors-for-leak-kinds=definite,indirect \
--error-exitcode=1 \
--suppressions=cmake/valgrind.supp \
"$binary" || status=1
done
exit "$status"

# ── clang-tidy-diff (changed lines only) ──────────────────────────────
clang-tidy:
Expand All @@ -312,13 +464,24 @@ jobs:
key: apt-clang-tidy-${{ hashFiles('.github/workflows/ci.yml') }}
restore-keys: apt-clang-tidy-

- name: Install Clang ${{ env.CLANG_VERSION }} and clang-tidy from apt.llvm.org
- name: Install Clang ${{ env.CLANG_VERSION }}, clang-tidy, and the non-Qt deps
run: |
sudo apt-get update -q
sudo apt-get install -y ninja-build catch2
sudo apt-get install -y ninja-build catch2 \
libsqlite3-dev libsodium-dev libssl-dev \
libgl1-mesa-dev libxkbcommon-x11-0 libxcb-cursor0 libxcb-icccm4 \
libxcb-keysyms1 libxcb-shape0 libxcb-xinerama0
wget -qO- https://apt.llvm.org/llvm.sh | sudo bash -s -- ${{ env.CLANG_VERSION }}
sudo apt-get install -y clang-tidy-${{ env.CLANG_VERSION }}

# See the linux-all-features job: the QML renderer needs Qt 6.5+.
- name: Install Qt ${{ env.QT_VERSION }}
uses: jurplel/install-qt-action@v4
with:
version: ${{ env.QT_VERSION }}
modules: qtwebsockets
cache: true

- name: Cache sccache
uses: actions/cache@v4
with:
Expand All @@ -331,9 +494,22 @@ jobs:
curl -sSL https://github.com/mozilla/sccache/releases/download/v0.9.1/sccache-v0.9.1-x86_64-unknown-linux-musl.tar.gz \
| tar -xz --strip-components=1 -C /usr/local/bin sccache-v0.9.1-x86_64-unknown-linux-musl/sccache

- name: Configure (generates compile_commands.json)
# Every optional feature is ON here purely so its sources land in
# compile_commands.json. clang-tidy only ever analyses *changed lines*, so
# this costs a wider build, not a wider lint. Configured narrowly before,
# the whole opt-in surface — morph::net, the Qt transport, the QML forms
# renderer, the SQLite queue, the fuzz harnesses — was invisible to the
# one gate in this workflow that reads code rather than running it.
- name: Configure (generates compile_commands.json over every optional feature)
run: |
cmake --preset clang-debug \
-DMORPH_BUILD_NET=ON \
-DMORPH_BUILD_QT=ON \
-DMORPH_BUILD_FORMS_QML=ON \
-DMORPH_BUILD_OFFLINE_SQLITE=ON \
-DMORPH_BUILD_LOAD_TESTS=ON \
-DMORPH_BUILD_HMAC_EXAMPLES=ON \
-DMORPH_BUILD_FUZZERS=ON \
-DCMAKE_C_COMPILER=clang-${{ env.CLANG_VERSION }} \
-DCMAKE_CXX_COMPILER=clang++-${{ env.CLANG_VERSION }} \
-DCMAKE_C_COMPILER_LAUNCHER=sccache \
Expand Down Expand Up @@ -374,3 +550,20 @@ jobs:
with:
name: clang-tidy-report
path: clang-tidy-report.txt

# ── Deprecation-marker format lint ────────────────────────────────────
deprecation-lint:
name: Deprecation marker format
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

# The checker itself is tested first. A lint gate nobody tests reports
# green whether or not it still detects anything — this one was blind to
# both the bare `[[deprecated]]` form and any message wrapped across
# lines, which is exactly what VERSIONING.md forbids.
- name: Self-test the deprecation-marker checker
run: bash scripts/test_check_deprecated_markers.sh

- name: Check [[deprecated]] markers cite a replacement and removal version
run: bash scripts/check_deprecated_markers.sh include/morph
14 changes: 13 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,22 @@ jobs:
sudo apt-get install -y software-properties-common
sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test
sudo apt-get update -q
sudo apt-get install -y cmake ninja-build doxygen g++-15
sudo apt-get install -y cmake ninja-build g++-15
sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-15 15
sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-15 15

# Ubuntu 24.04's apt package is Doxygen 1.9.8, which misreports several
# correctly-documented symbols as undocumented (confirmed: two template
# overloads of the same free function, and a function immediately
# following a file-level doc comment both trip its param/return
# association) -- fixed upstream by 1.15.0. Install a release binary
# instead of relying on the distro package.
- name: Install Doxygen 1.17.0
run: |
curl -sL https://github.com/doxygen/doxygen/releases/download/Release_1_17_0/doxygen-1.17.0.linux.bin.tar.gz -o /tmp/doxygen.tar.gz
tar -xzf /tmp/doxygen.tar.gz -C /tmp
echo "/tmp/doxygen-1.17.0/bin" >> "$GITHUB_PATH"

- name: Configure (docs target only)
run: |
cmake -S . -B build -G Ninja \
Expand Down
39 changes: 39 additions & 0 deletions .github/workflows/drift-guard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Drift guard

# Two independent gates for the "spec <-> code drift" class of bug (a
# docs/spec/*.md file stating a mechanical fact -- an enum cardinality, a
# constant, a canonical error string, a glaze parsing flag -- that silently
# stops matching the code):
#
# 1. The compiled pinned-facts test (tests/test_pinned_facts.cpp) needs no
# job here: it is already part of the `morph_tests` target, so it runs
# in every job of the main CI workflow (.github/workflows/ci.yml) for
# free, and benefits from running under every compiler in that matrix
# (GCC, Clang, MSVC, clang-cl) since the enum-cardinality switch pins
# (see tests/test_pinned_facts.cpp) rely on compiler-specific warning
# behavior that is worth exercising on all of them, not just one.
# 2. The prose-vs-manifest lint (scripts/check_spec_citations.sh) is a
# repo-wide text scan, unrelated to compilation -- it runs here, in its
# own fast, dependency-free job.
#
# Unlike .github/workflows/spec-sync.yml, this gate has no "no docs update"
# label escape hatch: a wrong citation or a reintroduced banned term is never
# a legitimate state to merge, so there is nothing to opt out of.

on:
push:
branches: [main, master]
pull_request:

permissions:
contents: read

jobs:
prose-lint:
name: Spec-citation & banned-terminology lint
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Run prose-vs-manifest lint
run: bash scripts/check_spec_citations.sh
12 changes: 11 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Notable changes to morph, in [Keep a Changelog](https://keepachangelog.com/en/1.
format. morph is pre-1.0 and has no tagged releases yet: `master` is the only
supported line, this file tracks notable changes going forward, and history
predating it lives in `git log`. From 1.0 on, versioning follows the policy
planned in `docs/planned/api_stability.md` (SemVer for the public, non-`detail`
published in `docs/spec/VERSIONING.md` (SemVer for the public, non-`detail`
API surface).

## [Unreleased]
Expand All @@ -22,6 +22,16 @@ API surface).
`docs/planned/gui_cross_field_rules.md`; accessibility assertions in
`docs/planned/gui_renderer_toolkit.md`'s conformance kit; a
banned-terminology check in `docs/planned/drift_guard.md`'s prose lint.
- API stability & versioning policy (`docs/spec/VERSIONING.md`, folded in
from `docs/planned/api_stability.md`): the semantic-versioning rules, the
stable (non-`detail`) surface definition, and the deprecation-window
discipline morph commits to starting at 1.0. Backed by two new mechanical
checks: the `include/morph/version.hpp` version constants (cross-checked
against `CMakeLists.txt`'s `project(VERSION ...)` by
`tests/test_version.cpp`), and the `deprecation-lint` CI job
(`scripts/check_deprecated_markers.sh`) enforcing that every
`[[deprecated("...")]]` marker names a replacement and a target removal
version.

### Fixed

Expand Down
Loading
Loading