@ceralive/modem-control 1.0.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (480) hide show
  1. package/README.md +341 -0
  2. package/dist/backend/at-lease.d.ts +59 -0
  3. package/dist/backend/at-lease.js +118 -0
  4. package/dist/backend/cell-info.d.ts +46 -0
  5. package/dist/backend/cell-info.js +124 -0
  6. package/dist/backend/constants.d.ts +24 -0
  7. package/{src/backend/constants.ts → dist/backend/constants.js} +4 -9
  8. package/dist/backend/device-classifier.d.ts +46 -0
  9. package/dist/backend/device-classifier.js +258 -0
  10. package/dist/backend/enrichment.d.ts +28 -0
  11. package/dist/backend/enrichment.js +59 -0
  12. package/dist/backend/features.d.ts +61 -0
  13. package/dist/backend/features.js +109 -0
  14. package/dist/backend/identity-ladder.d.ts +57 -0
  15. package/dist/backend/identity-ladder.js +158 -0
  16. package/dist/backend/identity-registry.d.ts +51 -0
  17. package/dist/backend/identity-registry.js +101 -0
  18. package/dist/backend/index.d.ts +30 -0
  19. package/dist/backend/index.js +35 -0
  20. package/dist/backend/lifecycle-interlock.d.ts +25 -0
  21. package/dist/backend/lifecycle-interlock.js +18 -0
  22. package/dist/backend/managed-objects.d.ts +39 -0
  23. package/dist/backend/managed-objects.js +82 -0
  24. package/dist/backend/mapping.d.ts +11 -0
  25. package/dist/backend/mapping.js +148 -0
  26. package/dist/backend/mm-backend.d.ts +44 -0
  27. package/dist/backend/mm-backend.js +154 -0
  28. package/dist/backend/mm-location.d.ts +21 -0
  29. package/dist/backend/mm-location.js +237 -0
  30. package/dist/backend/mm-mutations.d.ts +42 -0
  31. package/dist/backend/mm-mutations.js +259 -0
  32. package/dist/backend/modem-actor.d.ts +41 -0
  33. package/dist/backend/modem-actor.js +78 -0
  34. package/dist/backend/nm-auto-apn.d.ts +54 -0
  35. package/dist/backend/nm-auto-apn.js +124 -0
  36. package/dist/backend/nm-gsm-fields.d.ts +23 -0
  37. package/dist/backend/nm-gsm-fields.js +107 -0
  38. package/dist/backend/nmcli-nm-port.d.ts +28 -0
  39. package/dist/backend/nmcli-nm-port.js +173 -0
  40. package/dist/backend/nmcli-runner.d.ts +24 -0
  41. package/dist/backend/nmcli-runner.js +35 -0
  42. package/dist/backend/observer.d.ts +37 -0
  43. package/dist/backend/observer.js +219 -0
  44. package/dist/backend/power-contract.d.ts +49 -0
  45. package/dist/backend/power-contract.js +34 -0
  46. package/dist/backend/recovery-attribution.d.ts +32 -0
  47. package/dist/backend/recovery-attribution.js +57 -0
  48. package/dist/backend/recovery-budget.d.ts +45 -0
  49. package/dist/backend/recovery-budget.js +44 -0
  50. package/dist/backend/recovery-ladder.d.ts +94 -0
  51. package/dist/backend/recovery-ladder.js +116 -0
  52. package/dist/backend/router-ethernet.d.ts +19 -0
  53. package/dist/backend/router-ethernet.js +66 -0
  54. package/dist/backend/row-store.d.ts +13 -0
  55. package/dist/backend/row-store.js +82 -0
  56. package/dist/backend/signal-setup.d.ts +30 -0
  57. package/dist/backend/signal-setup.js +92 -0
  58. package/dist/backend/sim-unlock.d.ts +13 -0
  59. package/dist/backend/sim-unlock.js +153 -0
  60. package/dist/backend/transition-preconditions.d.ts +101 -0
  61. package/dist/backend/transition-preconditions.js +132 -0
  62. package/dist/backend/usage/accounting.d.ts +39 -0
  63. package/dist/backend/usage/accounting.js +73 -0
  64. package/dist/backend/usage/billing-cycle.d.ts +11 -0
  65. package/dist/backend/usage/billing-cycle.js +39 -0
  66. package/dist/backend/usage/boot-id.d.ts +6 -0
  67. package/{src/backend/usage/boot-id.ts → dist/backend/usage/boot-id.js} +7 -7
  68. package/dist/backend/usage/index.d.ts +8 -0
  69. package/dist/backend/usage/index.js +11 -0
  70. package/dist/backend/usage/policy-store.d.ts +50 -0
  71. package/dist/backend/usage/policy-store.js +161 -0
  72. package/dist/backend/usage/policy-write.d.ts +65 -0
  73. package/dist/backend/usage/policy-write.js +112 -0
  74. package/dist/backend/usage/proc-net-dev.d.ts +18 -0
  75. package/{src/backend/usage/proc-net-dev.ts → dist/backend/usage/proc-net-dev.js} +39 -47
  76. package/dist/backend/usage/sampler.d.ts +75 -0
  77. package/dist/backend/usage/sampler.js +211 -0
  78. package/dist/backend/usage/store.d.ts +38 -0
  79. package/dist/backend/usage/store.js +126 -0
  80. package/dist/backend/usb-device-snapshot.d.ts +31 -0
  81. package/dist/backend/usb-device-snapshot.js +1 -0
  82. package/dist/backend/usb-enumerator.d.ts +21 -0
  83. package/dist/backend/usb-enumerator.js +153 -0
  84. package/dist/backend/usb-mode-transition.d.ts +29 -0
  85. package/dist/backend/usb-mode-transition.js +216 -0
  86. package/dist/band/band-names.d.ts +42 -0
  87. package/dist/band/band-names.js +150 -0
  88. package/dist/band/certification.d.ts +84 -0
  89. package/dist/band/certification.js +127 -0
  90. package/dist/band/certified-bands.json +4 -0
  91. package/dist/band/index.d.ts +2 -0
  92. package/dist/band/index.js +8 -0
  93. package/dist/capability/detect.d.ts +52 -0
  94. package/dist/capability/detect.js +86 -0
  95. package/dist/capability/five-g-preference.d.ts +104 -0
  96. package/dist/capability/five-g-preference.js +171 -0
  97. package/dist/capability/index.d.ts +3 -0
  98. package/dist/capability/index.js +10 -0
  99. package/dist/capability/support-claim.d.ts +39 -0
  100. package/dist/capability/support-claim.js +81 -0
  101. package/dist/domain/brand.d.ts +10 -0
  102. package/dist/domain/brand.js +21 -0
  103. package/dist/domain/errors.d.ts +32 -0
  104. package/dist/domain/errors.js +48 -0
  105. package/dist/domain/generation.d.ts +8 -0
  106. package/dist/domain/generation.js +12 -0
  107. package/dist/domain/guards.d.ts +6 -0
  108. package/dist/domain/guards.js +127 -0
  109. package/dist/domain/identity.d.ts +109 -0
  110. package/dist/domain/identity.js +86 -0
  111. package/dist/domain/index.d.ts +14 -0
  112. package/dist/domain/index.js +18 -0
  113. package/dist/domain/mm-enums.d.ts +12 -0
  114. package/dist/domain/mm-enums.js +139 -0
  115. package/dist/domain/modem-presentation.d.ts +10 -0
  116. package/dist/domain/modem-presentation.js +39 -0
  117. package/dist/domain/observation.d.ts +39 -0
  118. package/dist/domain/observation.js +4 -0
  119. package/dist/domain/operation.d.ts +118 -0
  120. package/dist/domain/operation.js +85 -0
  121. package/dist/domain/physical-identity.d.ts +43 -0
  122. package/dist/domain/physical-identity.js +113 -0
  123. package/{src/domain/policy.ts → dist/domain/policy.d.ts} +34 -69
  124. package/dist/domain/policy.js +38 -0
  125. package/dist/domain/shadow-divergence.d.ts +27 -0
  126. package/dist/domain/shadow-divergence.js +70 -0
  127. package/dist/domain/snapshot.d.ts +53 -0
  128. package/dist/domain/snapshot.js +75 -0
  129. package/{src/domain/state.ts → dist/domain/state.d.ts} +23 -122
  130. package/dist/domain/state.js +38 -0
  131. package/dist/fcc/coverage.d.ts +63 -0
  132. package/dist/fcc/coverage.js +102 -0
  133. package/dist/fcc/index.d.ts +3 -0
  134. package/dist/fcc/index.js +12 -0
  135. package/dist/fcc/policy-store.d.ts +40 -0
  136. package/dist/fcc/policy-store.js +146 -0
  137. package/dist/fcc/policy-write.d.ts +32 -0
  138. package/dist/fcc/policy-write.js +37 -0
  139. package/dist/hardware/hilink-protocol.d.ts +39 -0
  140. package/dist/hardware/hilink-protocol.js +58 -0
  141. package/dist/hardware/index.d.ts +3 -0
  142. package/dist/hardware/index.js +15 -0
  143. package/dist/hardware/router-parsers.d.ts +89 -0
  144. package/dist/hardware/router-parsers.js +234 -0
  145. package/dist/index.d.ts +20 -0
  146. package/dist/index.js +27 -0
  147. package/dist/journal/codec.d.ts +44 -0
  148. package/dist/journal/codec.js +198 -0
  149. package/dist/journal/engine.d.ts +28 -0
  150. package/dist/journal/engine.js +68 -0
  151. package/dist/journal/entry.d.ts +74 -0
  152. package/dist/journal/entry.js +56 -0
  153. package/dist/journal/index.d.ts +6 -0
  154. package/dist/journal/index.js +6 -0
  155. package/dist/journal/legacy-ceraui.d.ts +73 -0
  156. package/dist/journal/legacy-ceraui.js +227 -0
  157. package/dist/journal/recovery.d.ts +58 -0
  158. package/dist/journal/recovery.js +117 -0
  159. package/dist/journal/store.d.ts +55 -0
  160. package/dist/journal/store.js +150 -0
  161. package/dist/location/fix-state.d.ts +53 -0
  162. package/dist/location/fix-state.js +75 -0
  163. package/dist/location/index.d.ts +2 -0
  164. package/dist/location/index.js +8 -0
  165. package/dist/location/nmea.d.ts +10 -0
  166. package/dist/location/nmea.js +89 -0
  167. package/dist/observations/envelope.d.ts +63 -0
  168. package/dist/observations/envelope.js +77 -0
  169. package/dist/observations/freshness.d.ts +28 -0
  170. package/dist/observations/freshness.js +76 -0
  171. package/dist/observations/index.d.ts +13 -0
  172. package/dist/observations/index.js +24 -0
  173. package/dist/observations/metric.d.ts +61 -0
  174. package/dist/observations/metric.js +73 -0
  175. package/dist/observations/model.d.ts +80 -0
  176. package/dist/observations/model.js +11 -0
  177. package/dist/observations/provenance.d.ts +94 -0
  178. package/dist/observations/provenance.js +67 -0
  179. package/dist/observations/raw.d.ts +42 -0
  180. package/dist/observations/raw.js +146 -0
  181. package/dist/observations/reading.d.ts +51 -0
  182. package/dist/observations/reading.js +65 -0
  183. package/dist/observations/sources/hilink.d.ts +10 -0
  184. package/dist/observations/sources/hilink.js +84 -0
  185. package/dist/observations/sources/modemmanager.d.ts +18 -0
  186. package/dist/observations/sources/modemmanager.js +239 -0
  187. package/dist/observations/sources/router-shared.d.ts +29 -0
  188. package/dist/observations/sources/router-shared.js +68 -0
  189. package/dist/observations/sources/ufi.d.ts +10 -0
  190. package/dist/observations/sources/ufi.js +111 -0
  191. package/dist/observations/sources/zte.d.ts +7 -0
  192. package/dist/observations/sources/zte.js +71 -0
  193. package/dist/observations/state-separation.d.ts +64 -0
  194. package/dist/observations/state-separation.js +52 -0
  195. package/dist/operation-ids.d.ts +2 -0
  196. package/dist/operation-ids.js +26 -0
  197. package/dist/operations/contracts.d.ts +72 -0
  198. package/dist/operations/contracts.js +1 -0
  199. package/dist/operations/index.d.ts +1 -0
  200. package/dist/operations/index.js +1 -0
  201. package/dist/operations/operation-engine.d.ts +16 -0
  202. package/dist/operations/operation-engine.js +194 -0
  203. package/dist/ports/index.d.ts +12 -0
  204. package/{src/ports/index.ts → dist/ports/index.js} +12 -8
  205. package/dist/ports/location.d.ts +87 -0
  206. package/dist/ports/location.js +36 -0
  207. package/dist/ports/modem-manager.d.ts +89 -0
  208. package/dist/ports/modem-manager.js +9 -0
  209. package/dist/ports/mutation-admission.d.ts +27 -0
  210. package/dist/ports/mutation-admission.js +9 -0
  211. package/dist/ports/network-manager.d.ts +68 -0
  212. package/dist/ports/network-manager.js +13 -0
  213. package/{src/ports/observation.ts → dist/ports/observation.d.ts} +16 -27
  214. package/dist/ports/observation.js +7 -0
  215. package/dist/ports/ops.d.ts +44 -0
  216. package/dist/ports/ops.js +16 -0
  217. package/{src/ports/receipts.ts → dist/ports/receipts.d.ts} +5 -27
  218. package/dist/ports/receipts.js +9 -0
  219. package/dist/ports/reconcile.d.ts +33 -0
  220. package/dist/ports/reconcile.js +200 -0
  221. package/dist/ports/resource-ownership.d.ts +29 -0
  222. package/dist/ports/resource-ownership.js +1 -0
  223. package/dist/ports/router.d.ts +19 -0
  224. package/dist/ports/router.js +7 -0
  225. package/dist/ports/sms.d.ts +64 -0
  226. package/dist/ports/sms.js +24 -0
  227. package/dist/ports/uhubctl.d.ts +6 -0
  228. package/dist/ports/uhubctl.js +1 -0
  229. package/dist/providers/contracts.d.ts +124 -0
  230. package/dist/providers/contracts.js +10 -0
  231. package/dist/providers/huawei-hilink/index.d.ts +2 -0
  232. package/dist/providers/huawei-hilink/index.js +2 -0
  233. package/dist/providers/huawei-hilink/operations.d.ts +20 -0
  234. package/dist/providers/huawei-hilink/operations.js +56 -0
  235. package/dist/providers/huawei-hilink/provider.d.ts +52 -0
  236. package/dist/providers/huawei-hilink/provider.js +76 -0
  237. package/dist/providers/huawei-hilink/runtime.d.ts +22 -0
  238. package/dist/providers/huawei-hilink/runtime.js +171 -0
  239. package/dist/providers/huawei-hilink/session.d.ts +28 -0
  240. package/dist/providers/huawei-hilink/session.js +120 -0
  241. package/dist/providers/huawei-hilink/transport.d.ts +19 -0
  242. package/dist/providers/huawei-hilink/transport.js +1 -0
  243. package/dist/providers/index.d.ts +8 -0
  244. package/dist/providers/index.js +8 -0
  245. package/dist/providers/matcher.d.ts +3 -0
  246. package/dist/providers/matcher.js +205 -0
  247. package/dist/providers/modem-manager/errors.d.ts +7 -0
  248. package/dist/providers/modem-manager/errors.js +37 -0
  249. package/dist/providers/modem-manager/generic-operations.d.ts +10 -0
  250. package/dist/providers/modem-manager/generic-operations.js +209 -0
  251. package/dist/providers/modem-manager/index.d.ts +4 -0
  252. package/dist/providers/modem-manager/index.js +4 -0
  253. package/dist/providers/modem-manager/module-operations.d.ts +21 -0
  254. package/dist/providers/modem-manager/module-operations.js +118 -0
  255. package/dist/providers/modem-manager/provider.d.ts +41 -0
  256. package/dist/providers/modem-manager/provider.js +155 -0
  257. package/dist/providers/modem-manager/runtime-composition-operation.d.ts +32 -0
  258. package/dist/providers/modem-manager/runtime-composition-operation.js +151 -0
  259. package/dist/providers/modem-manager/snapshot.d.ts +5 -0
  260. package/dist/providers/modem-manager/snapshot.js +204 -0
  261. package/dist/providers/modem-manager/types.d.ts +137 -0
  262. package/dist/providers/modem-manager/types.js +1 -0
  263. package/dist/providers/network-manager/adapter.d.ts +71 -0
  264. package/dist/providers/network-manager/adapter.js +348 -0
  265. package/dist/providers/network-manager/index.d.ts +2 -0
  266. package/dist/providers/network-manager/index.js +2 -0
  267. package/dist/providers/network-manager/types.d.ts +171 -0
  268. package/dist/providers/network-manager/types.js +77 -0
  269. package/dist/providers/registry.d.ts +13 -0
  270. package/dist/providers/registry.js +33 -0
  271. package/dist/providers/ufi-himi/index.d.ts +6 -0
  272. package/dist/providers/ufi-himi/index.js +6 -0
  273. package/dist/providers/ufi-himi/operations.d.ts +41 -0
  274. package/dist/providers/ufi-himi/operations.js +66 -0
  275. package/dist/providers/ufi-himi/prohibitions.d.ts +62 -0
  276. package/dist/providers/ufi-himi/prohibitions.js +88 -0
  277. package/dist/providers/ufi-himi/provider.d.ts +41 -0
  278. package/dist/providers/ufi-himi/provider.js +204 -0
  279. package/dist/providers/ufi-himi/qualcomm-evidence.d.ts +32 -0
  280. package/dist/providers/ufi-himi/qualcomm-evidence.js +51 -0
  281. package/dist/providers/ufi-himi/session.d.ts +46 -0
  282. package/dist/providers/ufi-himi/session.js +92 -0
  283. package/dist/providers/ufi-himi/transport.d.ts +29 -0
  284. package/dist/providers/ufi-himi/transport.js +25 -0
  285. package/dist/providers/zte-goform/index.d.ts +2 -0
  286. package/dist/providers/zte-goform/index.js +2 -0
  287. package/dist/providers/zte-goform/provider.d.ts +56 -0
  288. package/dist/providers/zte-goform/provider.js +101 -0
  289. package/dist/providers/zte-goform/session.d.ts +23 -0
  290. package/dist/providers/zte-goform/session.js +197 -0
  291. package/dist/providers/zte-goform/transport.d.ts +16 -0
  292. package/dist/providers/zte-goform/transport.js +1 -0
  293. package/dist/radio/band-truth.d.ts +50 -0
  294. package/dist/radio/band-truth.js +92 -0
  295. package/dist/radio/index.d.ts +3 -0
  296. package/dist/radio/index.js +10 -0
  297. package/dist/radio/mode-combinations.d.ts +88 -0
  298. package/dist/radio/mode-combinations.js +198 -0
  299. package/dist/radio/mode-truth.d.ts +67 -0
  300. package/dist/radio/mode-truth.js +112 -0
  301. package/dist/redact.d.ts +15 -0
  302. package/dist/redact.js +189 -0
  303. package/dist/safety/composition-root.d.ts +28 -0
  304. package/dist/safety/composition-root.js +65 -0
  305. package/dist/safety/flock-resource-ownership.d.ts +11 -0
  306. package/dist/safety/flock-resource-ownership.js +138 -0
  307. package/dist/safety/index.d.ts +2 -0
  308. package/dist/safety/index.js +2 -0
  309. package/dist/sms/dbus-messaging.d.ts +17 -0
  310. package/dist/sms/dbus-messaging.js +185 -0
  311. package/dist/sms/inbox-store.d.ts +10 -0
  312. package/dist/sms/inbox-store.js +82 -0
  313. package/dist/sms/index.d.ts +4 -0
  314. package/dist/sms/index.js +10 -0
  315. package/dist/sms/mmcli-parse.d.ts +54 -0
  316. package/dist/sms/mmcli-parse.js +224 -0
  317. package/dist/sms/normalize.d.ts +42 -0
  318. package/dist/sms/normalize.js +95 -0
  319. package/dist/testing/domain-fakes.d.ts +58 -0
  320. package/dist/testing/domain-fakes.js +98 -0
  321. package/dist/testing/index.d.ts +2 -0
  322. package/dist/testing/index.js +17 -0
  323. package/dist/testing/provider-fakes.d.ts +45 -0
  324. package/dist/testing/provider-fakes.js +64 -0
  325. package/dist/transport/calls.d.ts +9 -0
  326. package/dist/transport/calls.js +88 -0
  327. package/dist/transport/codec.d.ts +3 -0
  328. package/dist/transport/codec.js +207 -0
  329. package/dist/transport/dbus-native.d.ts +57 -0
  330. package/dist/transport/dbus-native.js +17 -0
  331. package/dist/transport/errors.d.ts +21 -0
  332. package/{src/transport/errors.ts → dist/transport/errors.js} +34 -46
  333. package/dist/transport/index.d.ts +4 -0
  334. package/dist/transport/index.js +9 -0
  335. package/dist/transport/signals.d.ts +15 -0
  336. package/dist/transport/signals.js +123 -0
  337. package/dist/transport/signature.d.ts +7 -0
  338. package/dist/transport/signature.js +94 -0
  339. package/dist/transport/transport.d.ts +2 -0
  340. package/dist/transport/transport.js +202 -0
  341. package/dist/transport/types.d.ts +61 -0
  342. package/dist/transport/types.js +19 -0
  343. package/dist/usb-mode/catalog-schema.d.ts +139 -0
  344. package/dist/usb-mode/catalog-schema.js +97 -0
  345. package/dist/usb-mode/catalog.d.ts +21 -0
  346. package/{src/usb-mode/catalog.ts → dist/usb-mode/catalog.js} +10 -32
  347. package/dist/usb-mode/certified-catalog.json +67 -0
  348. package/dist/usb-mode/index.d.ts +6 -0
  349. package/dist/usb-mode/index.js +16 -0
  350. package/dist/usb-mode/ingestion.d.ts +111 -0
  351. package/dist/usb-mode/ingestion.js +187 -0
  352. package/dist/usb-mode/promotion-review.d.ts +21 -0
  353. package/dist/usb-mode/promotion-review.js +87 -0
  354. package/dist/usb-mode/runtime-capability.d.ts +59 -0
  355. package/dist/usb-mode/runtime-capability.js +157 -0
  356. package/dist/usb-mode/usb-devices-parse.d.ts +36 -0
  357. package/dist/usb-mode/usb-devices-parse.js +157 -0
  358. package/dist/ussd/calls.d.ts +32 -0
  359. package/dist/ussd/calls.js +96 -0
  360. package/dist/ussd/index.d.ts +5 -0
  361. package/dist/ussd/index.js +12 -0
  362. package/dist/ussd/mm-ussd.d.ts +37 -0
  363. package/dist/ussd/mm-ussd.js +205 -0
  364. package/dist/ussd/refusal.d.ts +53 -0
  365. package/dist/ussd/refusal.js +154 -0
  366. package/dist/ussd/registration.d.ts +20 -0
  367. package/dist/ussd/registration.js +101 -0
  368. package/dist/ussd/session.d.ts +106 -0
  369. package/dist/ussd/session.js +163 -0
  370. package/package.json +38 -4
  371. package/src/backend/at-lease.test.ts +0 -106
  372. package/src/backend/at-lease.ts +0 -158
  373. package/src/backend/cell-info.test.ts +0 -154
  374. package/src/backend/cell-info.ts +0 -160
  375. package/src/backend/device-classifier.test.ts +0 -168
  376. package/src/backend/device-classifier.ts +0 -248
  377. package/src/backend/enrichment.ts +0 -96
  378. package/src/backend/features.test.ts +0 -162
  379. package/src/backend/features.ts +0 -179
  380. package/src/backend/identity-ladder.test.ts +0 -117
  381. package/src/backend/identity-ladder.ts +0 -221
  382. package/src/backend/identity-registry.test.ts +0 -89
  383. package/src/backend/identity-registry.ts +0 -151
  384. package/src/backend/index.ts +0 -236
  385. package/src/backend/lifecycle-interlock.ts +0 -38
  386. package/src/backend/managed-objects.ts +0 -108
  387. package/src/backend/mapping.ts +0 -160
  388. package/src/backend/mm-backend.ts +0 -191
  389. package/src/backend/mm-mutations.ts +0 -228
  390. package/src/backend/modem-actor.test.ts +0 -95
  391. package/src/backend/modem-actor.ts +0 -112
  392. package/src/backend/nm-auto-apn.ts +0 -161
  393. package/src/backend/nm-gsm-fields.ts +0 -122
  394. package/src/backend/nmcli-nm-port.ts +0 -228
  395. package/src/backend/nmcli-runner.ts +0 -52
  396. package/src/backend/observer.ts +0 -297
  397. package/src/backend/power-contract.test.ts +0 -40
  398. package/src/backend/power-contract.ts +0 -83
  399. package/src/backend/recovery-attribution.test.ts +0 -102
  400. package/src/backend/recovery-attribution.ts +0 -86
  401. package/src/backend/recovery-budget.test.ts +0 -64
  402. package/src/backend/recovery-budget.ts +0 -84
  403. package/src/backend/recovery-ladder.test.ts +0 -257
  404. package/src/backend/recovery-ladder.ts +0 -249
  405. package/src/backend/router-ethernet.test.ts +0 -71
  406. package/src/backend/router-ethernet.ts +0 -90
  407. package/src/backend/row-store.ts +0 -105
  408. package/src/backend/signal-setup.ts +0 -112
  409. package/src/backend/sim-unlock.ts +0 -193
  410. package/src/backend/transition-preconditions.ts +0 -149
  411. package/src/backend/uhubctl-power-hook.test.ts +0 -274
  412. package/src/backend/uhubctl-power-hook.ts +0 -377
  413. package/src/backend/usage/accounting.test.ts +0 -147
  414. package/src/backend/usage/accounting.ts +0 -123
  415. package/src/backend/usage/billing-cycle.test.ts +0 -62
  416. package/src/backend/usage/billing-cycle.ts +0 -45
  417. package/src/backend/usage/index.ts +0 -60
  418. package/src/backend/usage/policy-store.test.ts +0 -164
  419. package/src/backend/usage/policy-store.ts +0 -216
  420. package/src/backend/usage/policy-write.test.ts +0 -198
  421. package/src/backend/usage/policy-write.ts +0 -207
  422. package/src/backend/usage/proc-net-dev.test.ts +0 -56
  423. package/src/backend/usage/sampler.test.ts +0 -327
  424. package/src/backend/usage/sampler.ts +0 -282
  425. package/src/backend/usage/store.test.ts +0 -148
  426. package/src/backend/usage/store.ts +0 -177
  427. package/src/backend/usb-enumerator.test.ts +0 -87
  428. package/src/backend/usb-enumerator.ts +0 -181
  429. package/src/backend/usb-mode-transition.test.ts +0 -323
  430. package/src/backend/usb-mode-transition.ts +0 -253
  431. package/src/domain/brand.ts +0 -29
  432. package/src/domain/errors.ts +0 -77
  433. package/src/domain/guards.test.ts +0 -218
  434. package/src/domain/guards.ts +0 -144
  435. package/src/domain/identity.test.ts +0 -83
  436. package/src/domain/identity.ts +0 -165
  437. package/src/domain/index.ts +0 -12
  438. package/src/domain/snapshot.test.ts +0 -266
  439. package/src/domain/snapshot.ts +0 -120
  440. package/src/index.test.ts +0 -6
  441. package/src/index.ts +0 -15
  442. package/src/ports/README.md +0 -61
  443. package/src/ports/forbidden-surface.test.ts +0 -241
  444. package/src/ports/modem-manager.ts +0 -72
  445. package/src/ports/network-manager.ts +0 -87
  446. package/src/ports/ops.ts +0 -60
  447. package/src/ports/ops.type-test.ts +0 -39
  448. package/src/ports/receipts.test.ts +0 -153
  449. package/src/ports/reconcile.test.ts +0 -152
  450. package/src/ports/reconcile.ts +0 -338
  451. package/src/ports/router.ts +0 -29
  452. package/src/redact.test.ts +0 -82
  453. package/src/redact.ts +0 -73
  454. package/src/transport/README.md +0 -65
  455. package/src/transport/calls.ts +0 -113
  456. package/src/transport/characterization.test.ts +0 -260
  457. package/src/transport/codec.test.ts +0 -118
  458. package/src/transport/codec.ts +0 -240
  459. package/src/transport/conformance-python.test.ts +0 -152
  460. package/src/transport/conformance-same-lib.test.ts +0 -115
  461. package/src/transport/dbus-native-lib.d.ts +0 -19
  462. package/src/transport/dbus-native.ts +0 -85
  463. package/src/transport/index.ts +0 -30
  464. package/src/transport/no-library-leak.test.ts +0 -61
  465. package/src/transport/reliability.test.ts +0 -173
  466. package/src/transport/signals.ts +0 -150
  467. package/src/transport/signature.ts +0 -110
  468. package/src/transport/test-support/fake-service.ts +0 -168
  469. package/src/transport/test-support/independent-producer.py +0 -110
  470. package/src/transport/test-support/private-bus.ts +0 -66
  471. package/src/transport/transport.ts +0 -250
  472. package/src/transport/types.ts +0 -118
  473. package/src/usb-mode/catalog-schema.test.ts +0 -181
  474. package/src/usb-mode/catalog-schema.ts +0 -113
  475. package/src/usb-mode/certified-catalog.json +0 -67
  476. package/src/usb-mode/index.ts +0 -58
  477. package/src/usb-mode/ingestion.test.ts +0 -268
  478. package/src/usb-mode/ingestion.ts +0 -297
  479. package/src/usb-mode/promotion-review.ts +0 -117
  480. package/src/usb-mode/usb-devices-parse.ts +0 -196
@@ -1,266 +0,0 @@
1
- import { describe, expect, test } from 'bun:test';
2
- import { ImpossibleStateError, RevisionMonotonicityError } from './errors';
3
- import { imeiEquipmentId, type ModemIdentity, runtimePath } from './identity';
4
- import {
5
- applyTransition,
6
- type CellularSnapshot,
7
- createSnapshot,
8
- initialSnapshot,
9
- markSourceUnavailable,
10
- revision,
11
- supersede,
12
- } from './snapshot';
13
- import {
14
- type DataInterface,
15
- epochMillis,
16
- isRegistered,
17
- MM_STATES_REQUIRING_RADIO,
18
- type MmState,
19
- type NmActivation,
20
- type RadioAccessTechnology,
21
- type RadioPower,
22
- type RecoveryStage,
23
- type RecoveryState,
24
- type RegistrationStatus,
25
- SIM_LOCK_REQUIRES_CARD,
26
- type SimLock,
27
- type SimSlot,
28
- } from './state';
29
-
30
- const IDENTITY: ModemIdentity = {
31
- equipmentId: imeiEquipmentId('490154203237518'),
32
- runtimePath: runtimePath('/org/freedesktop/ModemManager1/Modem/0'),
33
- };
34
-
35
- describe('reducers', () => {
36
- test('initialSnapshot is a valid absent baseline at revision 0', () => {
37
- const snapshot = initialSnapshot(IDENTITY);
38
- expect(snapshot.presence).toBe('absent');
39
- expect(snapshot.revision).toBe(revision(0));
40
- expect(() => createSnapshot(snapshot)).not.toThrow();
41
- });
42
-
43
- test('applyTransition bumps the revision by exactly one', () => {
44
- const start = initialSnapshot(IDENTITY);
45
- const next = applyTransition(start, { reconcileStatus: 'reconciling' });
46
- expect(next.revision).toBe(revision(1));
47
- expect(next.reconcileStatus).toBe('reconciling');
48
- });
49
-
50
- test('applyTransition re-validates and throws on an impossible patch', () => {
51
- const start = applyTransition(initialSnapshot(IDENTITY), {
52
- presence: 'present',
53
- radioPower: 'on',
54
- mmState: 'registered',
55
- registration: { status: 'home', activeRats: new Set(['lte']) },
56
- });
57
- expect(() => applyTransition(start, { presence: 'absent' })).toThrow(ImpossibleStateError);
58
- });
59
-
60
- test('markSourceUnavailable keeps presence and marks the source, bumping revision', () => {
61
- const present = applyTransition(initialSnapshot(IDENTITY), { presence: 'present' });
62
- const unavailable = markSourceUnavailable(present);
63
- expect(unavailable.presence).toBe('present');
64
- expect(unavailable.sourceHealth).toBe('sourceUnavailable');
65
- expect(unavailable.revision).toBe(revision(2));
66
- });
67
-
68
- test('supersede rejects a stale or equal revision', () => {
69
- const current = applyTransition(initialSnapshot(IDENTITY), { reconcileStatus: 'converged' });
70
- const stale: CellularSnapshot = { ...initialSnapshot(IDENTITY), revision: revision(0) };
71
- expect(() => supersede(current, stale)).toThrow(RevisionMonotonicityError);
72
- });
73
-
74
- test('supersede accepts a strictly-newer valid snapshot', () => {
75
- const current = initialSnapshot(IDENTITY);
76
- const newer: CellularSnapshot = { ...initialSnapshot(IDENTITY), revision: revision(9) };
77
- expect(supersede(current, newer).revision).toBe(revision(9));
78
- });
79
- });
80
-
81
- // --- randomized property test ----------------------------------------------
82
-
83
- const MM_STATES: readonly MmState[] = [
84
- 'failed',
85
- 'unknown',
86
- 'initializing',
87
- 'locked',
88
- 'disabled',
89
- 'enabled',
90
- 'searching',
91
- 'registered',
92
- 'connecting',
93
- 'connected',
94
- ];
95
- const REG_STATUSES: readonly RegistrationStatus[] = [
96
- 'idle',
97
- 'home',
98
- 'searching',
99
- 'denied',
100
- 'unknown',
101
- 'roaming',
102
- ];
103
- const NM_STATES: readonly NmActivation[] = [
104
- 'unmanaged',
105
- 'unavailable',
106
- 'disconnected',
107
- 'activating',
108
- 'activated',
109
- 'failed',
110
- ];
111
- const POWERS: readonly RadioPower[] = ['unknown', 'off', 'low', 'on'];
112
- const STAGES: readonly RecoveryStage[] = [
113
- 'idle',
114
- 'attributing',
115
- 'nm-cycle',
116
- 'mm-cycle',
117
- 'reset',
118
- 'power-cycle',
119
- 'cooldown',
120
- 'exhausted',
121
- ];
122
- const LOCKS: readonly SimLock[] = [
123
- 'unknown',
124
- 'none',
125
- 'sim-pin',
126
- 'sim-puk',
127
- 'net-pers',
128
- 'permanently-blocked',
129
- ];
130
- const RATS: readonly RadioAccessTechnology[] = ['gsm', 'umts', 'lte', '5gnr'];
131
-
132
- /** Deterministic xorshift32 PRNG so a failing case is always reproducible. */
133
- function makePrng(seed: number): () => number {
134
- let state = seed >>> 0 || 1;
135
- return () => {
136
- state ^= state << 13;
137
- state >>>= 0;
138
- state ^= state >> 17;
139
- state ^= state << 5;
140
- state >>>= 0;
141
- return state / 0xffffffff;
142
- };
143
- }
144
-
145
- function pick<T>(rng: () => number, values: readonly T[]): T {
146
- const index = Math.min(values.length - 1, Math.floor(rng() * values.length));
147
- return values[index] as T;
148
- }
149
-
150
- function randomSnapshot(rng: () => number): CellularSnapshot {
151
- const rats = new Set<RadioAccessTechnology>();
152
- for (const rat of RATS) {
153
- if (rng() < 0.5) {
154
- rats.add(rat);
155
- }
156
- }
157
-
158
- const slotCount = Math.floor(rng() * 3);
159
- const simSlots: SimSlot[] = [];
160
- for (let i = 0; i < slotCount; i += 1) {
161
- simSlots.push({
162
- index: i + 1,
163
- occupied: rng() < 0.7,
164
- active: rng() < 0.5,
165
- lock: pick(rng, LOCKS),
166
- });
167
- }
168
-
169
- let dataInterface: DataInterface;
170
- if (rng() < 0.6) {
171
- dataInterface = rng() < 0.5 ? { present: true, name: 'wwan0' } : { present: true };
172
- } else {
173
- dataInterface = rng() < 0.5 ? { present: false } : { present: false, name: 'wwan0' };
174
- }
175
-
176
- const stage = pick(rng, STAGES);
177
- const attempts = Math.floor(rng() * 4);
178
- const recoveryState: RecoveryState =
179
- rng() < 0.5
180
- ? { stage, attempts, cooldownUntil: epochMillis(1_700_000_000_000) }
181
- : { stage, attempts };
182
-
183
- return {
184
- identity: IDENTITY,
185
- presence: rng() < 0.5 ? 'present' : 'absent',
186
- sourceHealth: pick(rng, ['live', 'stale', 'sourceUnavailable'] as const),
187
- simSlots,
188
- radioPower: pick(rng, POWERS),
189
- mmState: pick(rng, MM_STATES),
190
- registration: { status: pick(rng, REG_STATUSES), activeRats: rats },
191
- nmActivation: pick(rng, NM_STATES),
192
- dataInterface,
193
- reconcileStatus: pick(rng, [
194
- 'converged',
195
- 'reconciling',
196
- 'pending',
197
- 'divergent',
198
- 'unsupported',
199
- ] as const),
200
- recoveryState,
201
- revision: revision(Math.floor(rng() * 1_000_000)),
202
- };
203
- }
204
-
205
- /** Independent restatement of the invariants — never calls the guards under test. */
206
- function assertInvariants(s: CellularSnapshot): void {
207
- if (isRegistered(s.registration.status)) {
208
- expect(s.presence).toBe('present');
209
- expect(s.radioPower).not.toBe('off');
210
- expect(s.registration.activeRats.size).toBeGreaterThan(0);
211
- }
212
- if (MM_STATES_REQUIRING_RADIO.has(s.mmState)) {
213
- expect(s.presence).toBe('present');
214
- }
215
- if (s.nmActivation === 'activated') {
216
- expect(s.presence).toBe('present');
217
- expect(s.dataInterface.present).toBe(true);
218
- expect(s.mmState).toBe('connected');
219
- }
220
- if (s.radioPower === 'off') {
221
- const onAir =
222
- MM_STATES_REQUIRING_RADIO.has(s.mmState) ||
223
- s.registration.status === 'searching' ||
224
- s.nmActivation === 'activating' ||
225
- s.nmActivation === 'activated';
226
- expect(onAir).toBe(false);
227
- }
228
- expect(s.simSlots.filter((slot) => slot.active).length).toBeLessThanOrEqual(1);
229
- for (const slot of s.simSlots) {
230
- if (!slot.occupied) {
231
- expect(SIM_LOCK_REQUIRES_CARD.has(slot.lock)).toBe(false);
232
- }
233
- }
234
- if (!s.dataInterface.present) {
235
- expect(s.dataInterface.name).toBeUndefined();
236
- }
237
- expect(s.recoveryState.attempts).toBeGreaterThanOrEqual(0);
238
- expect(s.recoveryState.cooldownUntil !== undefined).toBe(s.recoveryState.stage === 'cooldown');
239
- }
240
-
241
- describe('randomized property test', () => {
242
- test('every constructed snapshot upholds the invariants; every rejection is typed', () => {
243
- const rng = makePrng(0x9e3779b9);
244
- let constructed = 0;
245
- let rejected = 0;
246
-
247
- for (let i = 0; i < 5000; i += 1) {
248
- const candidate = randomSnapshot(rng);
249
- let built: CellularSnapshot | null = null;
250
- try {
251
- built = createSnapshot(candidate);
252
- } catch (error) {
253
- rejected += 1;
254
- expect(error).toBeInstanceOf(ImpossibleStateError);
255
- continue;
256
- }
257
- constructed += 1;
258
- assertInvariants(built);
259
- // The plan's headline invariant, stated on its own.
260
- expect(isRegistered(built.registration.status) && built.presence === 'absent').toBe(false);
261
- }
262
-
263
- expect(constructed).toBeGreaterThan(0);
264
- expect(rejected).toBeGreaterThan(0);
265
- });
266
- });
@@ -1,120 +0,0 @@
1
- // CellularSnapshot — the whole coherent state of one modem at one revision.
2
- //
3
- // A snapshot composes the identity and all eight orthogonal dimensions plus a
4
- // monotonic `revision`. Every snapshot that exists has passed the guards: the
5
- // constructors here are the ONLY sanctioned way to build or advance one, so an
6
- // impossible combination can never be observed downstream. Revisions strictly
7
- // increase, letting the observer (A3.1) and consumers order and dedupe events.
8
-
9
- import type { Brand } from './brand';
10
- import { nonNegativeInteger } from './brand';
11
- import { RevisionMonotonicityError } from './errors';
12
- import { assertSnapshot } from './guards';
13
- import type { ModemIdentity } from './identity';
14
- import type {
15
- DataInterface,
16
- MmState,
17
- NmActivation,
18
- Presence,
19
- RadioPower,
20
- ReconcileStatus,
21
- RecoveryState,
22
- Registration,
23
- SimSlot,
24
- SourceHealth,
25
- } from './state';
26
-
27
- /** A monotonically increasing snapshot revision. */
28
- export type Revision = Brand<number, 'Revision'>;
29
-
30
- /** Construct a `Revision` from a non-negative integer. */
31
- export function revision(value: number): Revision {
32
- return nonNegativeInteger(value, 'revision') as Revision;
33
- }
34
-
35
- /** The revision every fresh identity starts at. */
36
- export const INITIAL_REVISION: Revision = revision(0);
37
-
38
- /** The next revision after `current`. */
39
- export function nextRevision(current: Revision): Revision {
40
- return (current + 1) as Revision;
41
- }
42
-
43
- /** The full coherent state of one modem at one point in time. */
44
- export interface CellularSnapshot {
45
- readonly identity: ModemIdentity;
46
- readonly presence: Presence;
47
- readonly sourceHealth: SourceHealth;
48
- readonly simSlots: readonly SimSlot[];
49
- readonly radioPower: RadioPower;
50
- readonly mmState: MmState;
51
- readonly registration: Registration;
52
- readonly nmActivation: NmActivation;
53
- readonly dataInterface: DataInterface;
54
- readonly reconcileStatus: ReconcileStatus;
55
- readonly recoveryState: RecoveryState;
56
- readonly revision: Revision;
57
- }
58
-
59
- /** A partial update to a snapshot's dimensions; `revision` is managed, not patched. */
60
- export type SnapshotPatch = Partial<Omit<CellularSnapshot, 'revision'>>;
61
-
62
- /**
63
- * Validate and return a snapshot. The guards run here: an impossible combination
64
- * throws `ImpossibleStateError` rather than producing an incoherent value. This
65
- * is the sole sanctioned constructor for an arbitrary snapshot.
66
- */
67
- export function createSnapshot(fields: CellularSnapshot): CellularSnapshot {
68
- assertSnapshot(fields);
69
- return fields;
70
- }
71
-
72
- /** A valid baseline for a freshly-observed-but-absent modem, at revision 0. */
73
- export function initialSnapshot(identity: ModemIdentity): CellularSnapshot {
74
- return {
75
- identity,
76
- presence: 'absent',
77
- sourceHealth: 'live',
78
- simSlots: [],
79
- radioPower: 'unknown',
80
- mmState: 'unknown',
81
- registration: { status: 'unknown', activeRats: new Set() },
82
- nmActivation: 'unavailable',
83
- dataInterface: { present: false },
84
- reconcileStatus: 'pending',
85
- recoveryState: { stage: 'idle', attempts: 0 },
86
- revision: INITIAL_REVISION,
87
- };
88
- }
89
-
90
- /**
91
- * Apply a dimension patch, bump the revision, and re-validate. Monotonicity is
92
- * automatic (revision always advances by one); an impossible result throws.
93
- */
94
- export function applyTransition(prev: CellularSnapshot, patch: SnapshotPatch): CellularSnapshot {
95
- const next: CellularSnapshot = { ...prev, ...patch, revision: nextRevision(prev.revision) };
96
- assertSnapshot(next);
97
- return next;
98
- }
99
-
100
- /**
101
- * Replace a snapshot with a fully-formed successor that carries its own revision
102
- * (the observer path). Enforces strict monotonicity — a stale or equal revision
103
- * throws `RevisionMonotonicityError` — and validates the successor.
104
- */
105
- export function supersede(prev: CellularSnapshot, next: CellularSnapshot): CellularSnapshot {
106
- if (next.revision <= prev.revision) {
107
- throw new RevisionMonotonicityError(prev.revision, next.revision);
108
- }
109
- assertSnapshot(next);
110
- return next;
111
- }
112
-
113
- /**
114
- * The source (MM daemon / bus) dropped: mark the data `sourceUnavailable` while
115
- * KEEPING presence and all other facts. Stale is never removal — only an
116
- * authoritative snapshot confirms absence (draft §Oracle round-1 lifecycle).
117
- */
118
- export function markSourceUnavailable(prev: CellularSnapshot): CellularSnapshot {
119
- return applyTransition(prev, { sourceHealth: 'sourceUnavailable' });
120
- }
package/src/index.test.ts DELETED
@@ -1,6 +0,0 @@
1
- import { expect, test } from 'bun:test';
2
- import { PACKAGE_NAME } from './index';
3
-
4
- test('control package exposes its name', () => {
5
- expect(PACKAGE_NAME).toBe('@ceralive/modem-control');
6
- });
package/src/index.ts DELETED
@@ -1,15 +0,0 @@
1
- // @ceralive/modem-control — package entry point.
2
- //
3
- // Phase A: the domain model (identity + orthogonal state + revisions) under
4
- // `./domain`, the MM / NM / Router port contracts + desired-state planner under
5
- // `./ports`, the redaction module (`./redact`), and the epoch-scoped ModemManager
6
- // D-Bus observer under `./backend`. The NetworkManager adapter, USB composition-mode
7
- // model, and data-usage sampler land in later waves.
8
-
9
- export const PACKAGE_NAME = '@ceralive/modem-control';
10
-
11
- export * from './backend';
12
- export * from './domain';
13
- export * from './ports';
14
- export * from './redact';
15
- export * from './usb-mode';
@@ -1,61 +0,0 @@
1
- # Port contracts + ownership matrix
2
-
3
- The adapter boundaries `@ceralive/modem-control` reconciles across. Every concrete
4
- backend — the A2.3 fake harness, the A3.x ModemManager D-Bus backend, the A4.1
5
- `nmcli` NetworkManager adapter — implements one of these interfaces. Nothing here
6
- performs I/O; these are pure TypeScript contracts.
7
-
8
- ## The ports
9
-
10
- | Port | File | Responsibility |
11
- |------|------|----------------|
12
- | `ModemObservationPort` | [`observation.ts`](./observation.ts) | Read-only: `start()`, `observe()`, `stop()`; emits discriminated `ObservationList` results that **retain rows** on source failure (removal is only ever an authoritative snapshot omission). |
13
- | `ModemManagerPort` | [`modem-manager.ts`](./modem-manager.ts) | **Extends** the observation port with the mutations MM owns: `setRadioModes`, `setPrimarySimSlot`, `sendPin`/`sendPuk`, `scanNetworks`, `inhibit`/`uninhibit`. **NO bearer/connect verb.** |
14
- | `NetworkManagerPort` | [`network-manager.ts`](./network-manager.ts) | GSM profile CRUD; `activate`/`deactivate` taking **both** `(connectionId, deviceIfname)`; quiesce lease. |
15
- | `RouterPort` | [`router.ts`](./router.ts) | Presence + advisory health only, for devices MM cannot control. |
16
-
17
- ## Ownership matrix — one sole writer per resource
18
-
19
- Each resource below has **exactly one** owner. No other component writes it. This is
20
- the NM-owns-bearers architecture the whole package is built around (independent
21
- review correction, draft §Oracle #1): the controller is a reconciler over two
22
- adapters, never a second writer of the same resource.
23
-
24
- | Resource | Sole writer | Port / owner |
25
- |----------|-------------|--------------|
26
- | APN | NetworkManager | `NetworkManagerPort` (`gsm.apn` / auto-config) |
27
- | Connection auth (username / password) | NetworkManager | `NetworkManagerPort` (`gsm.username` / `gsm.password`) |
28
- | Roaming | NetworkManager | `NetworkManagerPort` (`gsm.home-only`) |
29
- | Autoconnect | NetworkManager | `NetworkManagerPort` (`connection.autoconnect`) |
30
- | Activation (bearer lifecycle) | NetworkManager | `NetworkManagerPort` (`activate`/`deactivate`) |
31
- | Radio access-technology modes | ModemManager | `ModemManagerPort.setRadioModes` |
32
- | SIM operations (PIN / PUK / primary slot / scan) | ModemManager | `ModemManagerPort` |
33
- | Recovery ladder | Local controller | policy `recovery` (disabled by default, A3.4) |
34
- | Usage policy (cycle / threshold) | Local controller | policy `usage` (A4.3 sampler) |
35
-
36
- ### The bearer invariant (safety-critical)
37
-
38
- **The controller NEVER calls MM's `Simple.Connect`, `CreateBearer`, or
39
- `Bearer.Connect`.** Bearers, APN, and connection activation belong exclusively to
40
- NetworkManager. `ModemManagerPort` therefore has no `connect`, `createBearer`, or
41
- `bearerConnect` method, and none may ever be added. This is enforced at build time
42
- by [`forbidden-surface.test.ts`](./forbidden-surface.test.ts), which scans every
43
- port source file and fails if any bearer/connect method declaration appears.
44
-
45
- ## Port-tagged ops
46
-
47
- The planner ([`reconcile.ts`](./reconcile.ts)) emits **port-tagged ops**
48
- ([`ops.ts`](./ops.ts)): a discriminated union `{ port: 'mm', op: MmOp } | { port:
49
- 'nm', op: NmOp }` whose op-kind spaces are disjoint. A radio op tagged for the NM
50
- port — `{ port: 'nm', op: { kind: 'setRadioModes', … } }` — is a **compile-time type
51
- error**, proved by [`ops.type-test.ts`](./ops.type-test.ts) (`@ts-expect-error`
52
- lines that `tsc --noEmit` must flag). The ownership matrix is thus enforced by the
53
- type system, not merely by convention.
54
-
55
- ## Receipts
56
-
57
- Reconciliation is honest: each policy dimension yields exactly one
58
- [`Receipt`](./receipts.ts) with a status (`applied | pending | unsupported |
59
- failed`) and a **reason**. Nothing is silently dropped — "prefer 5G" on a 4G-only
60
- modem returns `unsupported` with an explicit reason, never a quiet downgrade to a
61
- 4G-only mode.