Upstream currency: MM 1.24.2 stack + Wave-2/3 hardening#5
Merged
Conversation
…libqrtr-glib 1.4.0; canonical .dsc↔salsa tree link
… frozen package sets + external-dep preflight
Add ci/check-package-sets.sh: for each *.changes, assert its binary package
set EQUALS (not >=/count) the [<source> all-artifact] set in
expected-packages.txt, so any add/remove/rename fails closed naming the
package. Wire it into build-bookworm.sh after the runtime-closure check and
run it standalone per-arch.
Finalize expected-packages.txt all-artifact sets against the real amd64 and
arm64 *.changes (PROVISIONAL -> FINALIZED). The dbgsym enumeration inherited
from the debian/ refresh proved exact: a -dbgsym is emitted for exactly the 9
ELF-shipping packages and nothing else; no other delta on either arch.
build-bookworm.sh: install the freshly-built gir1.2-*-1.0 typelib packages
into the build environment before each dependent source. Bookworm's GI-1.74
dh_girepository resolves cross-namespace typelib deps (Qmi-1.0 imports Qrtr-1.0;
ModemManager-1.0 imports Qmi/Mbim/Qrtr) via the installed dependency .typelib,
and the rebuilt -dev packages carry an empty ${gir:Depends} under GI-1.74, so
libqmi otherwise died with "Could not find Qrtr-1.0.typelib dependency". This
is build-orchestration only (mirrors an archive build where every gir1.2-* is
co-installable) and changes no produced package's contents or the emitted set.
Document both in packaging/README.md.
…-publish, integrity-idempotent npm, staged sanitized assets
…modules (behavior-locked)
A reconnect/drop test kills the private bus and abandons the fake service without stopping it. A SlowPing whose reply delay had not yet elapsed left a dangling timer that, when it fired, wrote the owed reply to the closed stream and threw "Can't write a message to a closed stream" asynchronously — seconds later, inside an unrelated test file — flipping the run's exit code to 1 while no test actually failed. Track the reply timers and cancel them the instant the connection stream ends (and in stop()), so a dead fake never writes. The live-reply paths are untouched: 'end' only fires when the bus is genuinely killed.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Brings the ModemManager packaging stack to current upstream and lands the Wave-2/Wave-3 hardening that rides with it. This is the single integration PR for the
upstream-currencyeffort — 19 commits across packaging, CI, the transport seam, and docs. No package versions are bumped here (bothpackage.jsonfiles stay at0.1.0); the0.2.0cut is a separate release-prep PR that branches off this merge.The upstream pin moves to ModemManager 1.24.2 / libmbim 1.34.0 / libqmi 1.38.0 / libqrtr-glib 1.4.0 (from the 1.24.0 / 1.32.0 / 1.36.0 / 1.2.2 era). Grouped by area:
upstream-pins.yamland refreshed all fourdebian/trees to the new salsa revisions; re-derived the bookworm adaptations (the maintainers modernized the introspection build-deps to GI ≥ 1.80, which bookworm cannot satisfy — reverted to thelibgirepository1.0-dev/ GI-1.74 form, empirically proven sufficient); froze the per-source package sets and enforce exact set-equality on rebuild; added a sharedread-pin.shso CI and BENCH read one pin source textually, with revision-exact and direction-aware version assertions. Both arches rebuild with byte-exact package-set equality; the daemon smoke now does a functional GObject-introspection check (mmcli 1.24.2, typelib import, vala compile+link), not just a presence probe.verify-upstream-pins.shgained a fourth chain link: the pinned.debian.tartree is compared against the salsa packaging tag as a canonical manifest (type + exec-bit + symlink-target + content sha256), on top of the existing signer /.dsc-signature /.orig.tarchecks. Four offline negative fixtures each reject on their specific named field.release.ymlnow pins every job checkout to the tag's peeled commit SHA, quarantines all workflow inputs throughenv:(zero${{ }}in anyrun:body), sequencestag-guard → test → build-deb → publish-npm → create-release, makes the npm publish integrity-idempotent with a pre-publish TOCTOU re-check, and reconciles release assets immutably (existing assets are integrity-compared, never overwritten).transport.tswas split into lifecycle / call-dispatch / signal modules (each ≤ 250 lines), behavior-locked by a new characterization suite committed and green before the split. The forbidden-surface detector was rebuilt on the TypeScript compiler API (replacing a regex), and the library-leak guard now enumerates modules dynamically.dbus-run-session -- bun testintermittently exited 1 with a "Can't write a message to a closed stream" error attributed to an unrelated test. Root cause: the characterization suite's mid-drop test issues an in-flightSlowPing(5s reply), kills the bus, and deliberately abandons the fake — leaving a dangling reply timer that, when it fired seconds later, wrote to the closed stream. The final commit cancels the fake's reply timers when the connection stream ends (and instop()), so a dead fake never writes. It changes no assertion; see the Rule-E note below.Why
Upstream shipped the FM350 MTK-plugin fixes and a large run of library changes; staying on the old pins leaves the device image behind on both features and security fixes. The Wave-2/3 work is the hardening the plan gated on the same bump — provenance depth, injection-safe release automation, a maintainable transport seam, and honest docs — so it lands atomically with the pins rather than as drift afterward.
How to verify
All eight local gates were run on the merge head (
f01b8e4) and exit 0:bun install --frozen-lockfiledbus-run-session -- bun testbunx tsc --noEmitbunx biome check .bash packaging/ci/contract.shbash packaging/ci/test-tag-guard.shvX.Y.Zaccepted)bash packaging/ci/test-verify-upstream-pins.shbash packaging/ci/verify-upstream-pins.shThe live provenance run verifies lineage +
.dscsignature (GOODSIG in an isolated keyring) + checksums +.orig.tar+debian/tree for all four sources. Fail-closed behavior is exercised, not assumed: the four verify fixtures reject onsigner_fingerprint/dsc_signature/orig_tar_sha256/debian_tree, and the tag guard rejects-rc.1,+build5, bare1.0.0,v1.0,v1.0.0.0, and empty.Rule-E justification — the three sanctioned test-file changes (each strengthens or is additive; none weakens)
control/src/transport/no-library-leak.test.ts— strictly stronger. The guard that asserts only the quarantined facade imports the D-Bus library was a fixed five-name list, each checked with.not.toContain. It now reads every non-test.tsintransport/viareaddirSync, collects the importers, and asserts the set is exactly['dbus-native.ts']. The old list could never catch a new module it did not name — the split addedcalls.tsandsignals.ts, exactly the case the fixed list was blind to. The file's other three tests are byte-unchanged (the only other edit is thereaddirSyncimport).control/src/ports/forbidden-surface.test.ts— strengthened, positives preserved. The regex member-name detector is replaced by a TypeScript compiler-API walk that inspects declared interface/type-literal/class members (including optional, quoted, computed-string, and accessor names). Every existing positive expectation still holds; the retired regex is kept in-file aslegacyRegexMemberNamessolely to power a differential test that runs both and proves the AST catches shapes (computed/quoted names, optional members, accessors, arrow-typed props) the regex silently missed. Real port files still produce zero false positives.control/src/backend/features.test.ts— purely additive. One new case,parseMmVersion('1.24.2')→{ major: 1, minor: 24 }, covering the new FM350-fix release. Zero existing lines touched.Additionally, the harness fix in
control/src/transport/test-support/fake-service.tsis not one of the three sanctioned test files and is not a test assertion — it is the fake D-Bus service double. Cancelling a dead connection's owed reply weakens nothing: the live-reply paths (characterization "late reply", reliability "late reply") are untouched because the newend-listener only fires when the bus is genuinely killed. A stash/restore before-and-after proof is in the run evidence (without the fix the repro exits 1 with three closed-stream hits; with it, clean).Chronology anchor — characterization tests predate the split (Wave 2, task 2.3)
The transport refactor was done tests-first, and the ordering is preserved in genuine commit history:
6de9d03—test(transport): characterization coverage for reconnect/ordering edges(the characterization suite alone)da0e37f—refactor(transport): split transport.ts into lifecycle/calls/signals modules (behavior-locked)(the split)6de9d03precedesda0e37fin real commit order with real timestamps, preserved through this branch via--no-ffmerges (not squashed). The GitHub PR commits listing for this PR shows6de9d03beforeda0e37f, which is the durable chronology anchor after this PR is squash-merged. Locally, the full transport suite (42/42) passed when only6de9d03existed — i.e. before any split module was written — which is the "tests characterize current behavior first" evidence. (The plan originally wanted a draft PR opened on6de9d03alone to capture that ordering as a CI-run anchor; the branch was instead assembled from parallel worktrees merged sequentially, so the commits-API ordering + the pre-split local pass stand in for that run.)Risks
debian/trees. Mitigated by the four-link provenance chain, verified live in this PR: signer fingerprint,.dscGPG signature,.orig.tarsha256, and a byte-identical salsa-vs-.debian.tartree manifest — plus a full amd64 + arm64 rebuild with exact per-source package-set equality (Wave-1 evidence). Thedebian/*.symbolsfiles are salsa-faithful and were never regenerated.1.2.2matched bookworm stock and needed--allow-downgrades; at1.4.0it outranks stock, so the install is a plain upgrade. Verified empirically with realdpkg --compare-versions/apt-cache madison, and the contract suite is now direction-aware.0.2.0release drill in the follow-up. This PR only lands the workflow and its dependency-free helper scripts, all lint/actionlint clean.