Good news! The luci-app-attendedsysupgrade package does exactly what I was hoping for. Once installed in 19.07 & 21.02, you simply request a new build for your router, and it gets installed.
The package ships a list of the manually installed (non-default) packages to the ImageBuilder at https://asu.aparcar.org. The ImageBuilder creates a new image and sends it back to the browser, which then uploads the image to the router for flashing.
sysupgrade can already save a list of installed packages with -k option.
You also have options '-o -u' to save everything that is not from a package nor a changed config file. Sum both together and you'll have:
# sysupgrade -o -u -k openwrt-....img
After reboot, you'll have a list of installed packages at /etc/backup/installed_packages.txt. You just need to "parse" it and run opkg install:
The issue is that those packages that you are installing might be required to access the internet. The custom image generated by attendedsysupgrade is one solution for that problem. However, all the "extra packages" will be part of the image and not "upgradable" (but it is not an issue if you have plenty of flash space).
Another one would be a way to pack all required ipk files into the backup with a script that install them at early stages (uci-defaults?). I never did that but I think it is feasible, specially if an external service (like attendedsysupgrade) could gather those packages with dependencies for you.