TASK 48 slice 3: the self-updater is deleted - the Store updates the app

The creator's ruling (2026-09-27) is Store MSIX + Store IAP, so Microsoft delivers
the updates and the in-app updater is gone rather than parked. This is a
COMPLIANCE action, not cleanup: the Velopack bootstrap downloaded and executed a
payload, which Store policy 10.2.2 does not permit.

Three edit sites, as planned: the PackageReference in ytLive.csproj, 'using
Velopack;' in App.xaml.cs, and the bootstrap block. The self-hosted DigitalOcean
update-URL droplet dies with it.

Guard: ytLive.Tests/StorePackagingTests.cs - the ONE test asserts the app project
references no updater package (Velopack/Squirrel/NetSparkle/AutoUpdater/WinSparkle)
and that no app source file uses the updater API. The check matches CODE (a using
directive, a type usage), not a bare word, so a file's prose may still name what it
deliberately does not do.

Smoke-tested after the removal: MainWindow loaded, 0 exceptions in startup.log.
Full suite 437/437, 0 warnings.
This commit is contained in:
2026-10-22 06:57:34 -07:00
parent b1d6034662
commit 20cfadca0c
9 changed files with 109 additions and 36 deletions
+2 -14
View File
@@ -1,6 +1,5 @@
using System.Windows;
using System.Windows.Threading;
using Velopack;
using ytLive.Helpers;
namespace ytLive;
@@ -31,19 +30,8 @@ public partial class App : Application
// the per-build GUID is forensic only and lands here.
AppLog.Write($"Build {ytLive.Helpers.BuildStamp.Id} (compiled {ytLive.Helpers.BuildStamp.BuiltLocal})");
// Velopack auto-update bootstrap — checks for updates on startup and
// applies them (the app restarts automatically if an update is found).
// Update URL is configured per-deployment (self-hosted on DO droplet);
// when no URL is set, the check is a no-op.
try
{
VelopackApp.Build()
.Run();
}
catch (Exception ex)
{
AppLog.Write($"Velopack bootstrap failed (non-fatal): {ex.Message}");
}
// There is deliberately NO self-update bootstrap here: the Microsoft Store updates the
// app (TASK 48 — Store MSIX route), and a second updater would fight it.
base.OnStartup(e);
AppLog.Write("App OnStartup end");
+6 -4
View File
@@ -409,10 +409,12 @@ Set-AuthenticodeSignature -FilePath "LlamaCasty.exe" `
### 4.4 Update Mechanism — ⛔ OBSOLETE (2026-09-27)
> **Superseded by Store updates.** Microsoft delivers and updates the app from the listing, so
> there is nothing to check and no manifest to host. The Velopack updater currently in
> `App.xaml.cs` **downloads *and executes***, which Store policy **10.2.2** does not permit — its
> removal is a **compliance action**, not optional cleanup (TASK 48 item 3). The Polar-native design
> below is retained as a record of the approach that was rejected; it is not an option.
> there is nothing to check and no manifest to host. The Velopack updater that used to sit in
> `App.xaml.cs` **downloaded *and executed***, which Store policy **10.2.2** does not permit — its
> removal was a **compliance action**, not optional cleanup, and it is **done** (TASK 48 slice 3,
> 2026-10-22: the package reference and the bootstrap are gone; `ytLive.Tests/StorePackagingTests.cs`
> guards it). The Polar-native design below is retained as a record of the approach that was
> rejected; it is not an option.
**Polar-native approach (no custom infrastructure):**
+14 -4
View File
@@ -77,6 +77,16 @@
unchanged; the seams are two `internal` properties). Guard:
`ytLive.Tests/AppLogRotationTests.cs`. Slices 2 (temp alert-clip cleanup) and 3 (low-disk
guard before recording) are still open.
- **TASK 48 slice 3 — the self-updater is gone (shipped 2026-10-22).** The Store route (creator
ruling 2026-09-27) means the Store updates the app, so the Velopack package + bootstrap were
**deleted** (`ytLive.csproj`, `App.xaml.cs`) — a compliance action, not cleanup: the updater
downloaded *and executed*, which Store policy 10.2.2 does not permit. The DO update-URL droplet
dies with it. Guard: `ytLive.Tests/StorePackagingTests.cs` (no updater package, no updater API
in any app source file). The app was smoke-tested after the removal: `MainWindow loaded`, no
exceptions. **Still open on TASK 48:** slice 2 (the `Package.appxmanifest` + packaging project),
slice 6 (the full-trust recording invariant comment), slice 7 (disqualifier re-check), slice 8
(the pre-submission gates) — and slices 4/5 are the creator's (Certification Kit, Partner
Center listing, the demo account).
- **The previous session (HA lights) produced NO ytLive code** — it was spent on the creator's Home
Assistant light alert system (off-repo, `192.168.50.145`). See `MyMistakes.md` → 2026-10-21: the plan
was one tweak to a verified-working script; the script was rewritten twice, the alert path broke, and
@@ -270,10 +280,10 @@ Zombie entities deleted from the registry: `script.leak_strobe`, `script.doorbel
Galaxy `cef.log` at 2.7 GB is NOT ours.)
## Tests
- Last full run: **436/436 passed, 0 warnings** (2026-10-22 — after TASK 55 slice 1's
`AppLogRotationTests`; the run before was 435/435 after TASK 63, and the runs before that were
435/435 after TASK 61+62, 433/433 after TASK 60, 432/432 after TASK 59 and 431/431 after
TASK 58). The one failure seen earlier in the
- Last full run: **437/437 passed, 0 warnings** (2026-10-22 — after TASK 48 slice 3's
`StorePackagingTests`; the run before was 436/436 after TASK 55 slice 1's
`AppLogRotationTests`, and the one before that 435/435 after TASK 63). The one failure seen
earlier in the
session was `LayerReorderPersistenceTests.RealMouseDrag_OnTheLayerList_PersistsTheReorder`,
which **passes in isolation** (re-run: 3/3) — the known real-mouse flake, not a regression.
- Known flaky-by-environment (all pass in isolation): `AudioPipelineTests.Mix_HonorsProviderGains…`,
+2 -2
View File
@@ -63,7 +63,7 @@
| 45 | TEST-tab chat fix #2: insert body must declare `snippet.type` (400 MISSING_REQUIRED_FIELD) | ✅ Done (2026-09-25) | [`TASKS/task-45-chat-insert-type.md`](TASKS/task-45-chat-insert-type.md) |
| 46 | Drawers: click outside the rail closes whichever is open — TEST added to the existing Stream Settings + YPP dismiss behavior | ✅ Done (2026-09-25) | [`TASKS/task-46-drawer-click-outside-close.md`](TASKS/task-46-drawer-click-outside-close.md) |
| 47 | Alert box video: built-in/custom alert clip (+ six-animation fallback) with read-time fade in/out + ticker (now in the preview too, 3 display methods) + alert volume in the live mix | ✅ Done (2026-09-26) | [`TASKS/task-47-alert-videos.md`](TASKS/task-47-alert-videos.md) |
| 48 | Distribution & packaging: **MSIX + Store IAP** | 🔶 In progress — **✅ route decided 2026-09-27 (Store MSIX + Store IAP)**; **item 1 (bundle ffmpeg) SHIPPED 2026-09-28** | [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md) |
| 48 | Distribution & packaging: **MSIX + Store IAP** | 🔶 In progress — **✅ route decided 2026-09-27 (Store MSIX + Store IAP)**; **item 1 (bundle ffmpeg) SHIPPED 2026-09-28**; **item 3 (remove the Velopack updater) SHIPPED 2026-10-22** — the Store updates the app | [`TASKS/task-48-distribution-msix.md`](TASKS/task-48-distribution-msix.md) |
| 49 | Chat profanity filter (local, opt-in, non-persistent, user word list) | ☐ Queued (2026-09-27) | [`TASKS/task-49-chat-profanity-filter.md`](TASKS/task-49-chat-profanity-filter.md) |
| 50 | One-button "Publish this video" (resumable upload of the local recording) | ☐ Queued (2026-09-28) | [`TASKS/task-50-publish-recording.md`](TASKS/task-50-publish-recording.md) |
| 51 | **Webcam background effects: Remove + Blur** | ✅ **Built 2026-10-09** (v1 requirement) — bundled MediaPipe Selfie Segmenter → ONNX (verified vs Google's runtime), Remove + Blur with strength, per-scene config persisted, one processing point in CameraManager, radio-pill UI in the webcam's Layers properties. Creator check 2026-10-09: **Remove jitter FIXED** (temporal mask smoothing 0.75/0.25, OBS/Jitsi-derived) and **Blur "heavy" FIXED** (quadratic radius 1→6 small px, default 0.5 = 2, StreamFX-consensus); recheck on the real camera pending | [`TASKS/task-51-webcam-bg-effects.md`](TASKS/task-51-webcam-bg-effects.md) |
@@ -129,7 +129,7 @@ second encoder path needing a "redirect". Record+simulcast is one ffmpeg with tw
`DroppedFrames`/`StreamDuration`; `SessionTeardownTests` is the natural home for the roll-up.
- **TASK 3** — items 16 (Text source) + 20 (RewardEvent capture → SQLite, Alerts' persistence half) open; **17 (Alerts) is DONE via TASK 43 (2026-09-24) — native six-event alert box**
- **TASK 9** — item 5 open (webcam identity key reconciliation)
- **TASK 10** — Velopack update URL pending → **now owned by TASK 48** (retires if the Store handles updates)
- **TASK 10** — the Velopack update URL is **RETIRED (2026-10-22)**: the updater was deleted (TASK 48 slice 3) and the Store updates the app
- **Shipping / release build (creator-queued 2026-09-26)** — no publish config exists yet:
the csproj has only `OutputType=WinExe` + `TargetFramework`, so a plain `dotnet publish`
is **framework-dependent** (customer needs the .NET 8 Desktop Runtime preinstalled).
+4 -4
View File
@@ -68,7 +68,7 @@ licensing path is being deleted because Store IAP supplies the same bit from the
handles purchase. Paid users see zero license UI.
7. ⛔ **Licensing model / renewal-lapse path** — already dead before the Store ruling (a perpetual
key never expires). Now superseded again: **Store IAP has no keys at all.**
8. 🔶 **Velopack auto-updates** — NuGet packages + bootstrap in `App.xaml.cs`. **TEARDOWN is a
compliance action, not cleanup:** the updater downloads *and executes*, and Store policy 10.2.2
does not permit it. Tracked as [`TASKS/task-48-distribution-msix.md`](task-48-distribution-msix.md)
item 3; the Store updates the app instead.
8. ✅ **Velopack auto-updates** — NuGet package + bootstrap in `App.xaml.cs`. **DELETED
2026-10-22 (TASK 48 slice 3)** — a compliance action, not cleanup: the updater downloaded
*and executed*, and Store policy 10.2.2 does not permit it. The Store updates the app
instead. Guard: `ytLive.Tests/StorePackagingTests.cs`.
+11 -5
View File
@@ -158,15 +158,21 @@ There is currently **no `.manifest` file in the repo at all**, so this is purely
**Do NOT use `appContainer`** — see the invariant in item 6.
## 3. ☐ Remove Velopack — 3 edit sites
## 3. ✅ Remove Velopack — 3 edit sites (DONE 2026-10-22)
The Store handles updates, so this is simply **deleted**. Trivially removable; the code was
written defensively for exactly this.
- `ytLive.csproj:92` — `<PackageReference Include="Velopack" Version="1.2.0" />`
- `App.xaml.cs:3` — `using Velopack;`
- `App.xaml.cs:38-46` — the sole call site, already try/caught and commented
"(non-fatal) … when no URL is set, the check is a no-op"
- `ytLive.csproj:92` — `<PackageReference Include="Velopack" Version="1.2.0" />` — **REMOVED**
- `App.xaml.cs:3` — `using Velopack;` — **REMOVED**
- `App.xaml.cs:38-46` — the sole call site (already try/caught and commented
"(non-fatal) … when no URL is set, the check is a no-op") — **REMOVED**
**Guard:** `ytLive.Tests/StorePackagingTests.cs` — the ONE test asserts the app project
references no updater package (Velopack/Squirrel/NetSparkle/AutoUpdater/WinSparkle) and that no
app source file uses the updater API (matched as CODE — a `using` directive, a type usage — not
as a bare word, so a file's prose may still name what it deliberately does not do). The app was
smoke-tested after the removal: `MainWindow loaded`, no exceptions.
**Retires:** the pending "Velopack update URL" item (`TASKS.md` open items,
`task-10-monetization.md:43`) and the self-hosted DigitalOcean droplet.
+4 -2
View File
@@ -2583,8 +2583,10 @@ before re-deriving it. Executable checklist: `TASKS/task-48-distribution-msix.md
**Consequences that are architecture, not packaging detail:**
- **⛔ Velopack and the update URL are deleted** — the Store auto-updates. 3 edit sites,
`ytLive.csproj:92` + `App.xaml.cs:3,38-46`. The self-hosted DO droplet dies with it.
- **⛔ Velopack and the update URL are deleted** — the Store auto-updates. **DONE 2026-10-22**
(TASK 48 slice 3): the `PackageReference` in `ytLive.csproj` and the bootstrap in
`App.xaml.cs` are gone, and `ytLive.Tests/StorePackagingTests.cs` guards that no updater
package or updater API comes back. The self-hosted DO droplet dies with it.
- **⛔ The Polar licensing subsystem is deleted** — `PolarLicenseService`, `PolarLicense`,
`MainViewModel.License.cs` (`PremiumUrl`, customer portal, the
`OfflineGracePeriod = 14 days` subscription-era artifact, renewal/lapse copy), and the
+66
View File
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Xunit;
namespace ytLive.Tests;
/// <summary>TASK 48, slice 3 — the creator's ruling (2026-09-27): the app ships as a
/// **Microsoft Store MSIX** and the **Store updates it**. The self-update subsystem that
/// existed for the abandoned self-hosted route (a Velopack bootstrap plus the DigitalOcean
/// update-URL droplet) is therefore deleted, not parked: a second updater inside a
/// Store-installed package fights the Store's own updater.
///
/// <para>This test is the guard: the app project references no updater package and no app
/// source file uses the updater API — so the deleted subsystem cannot creep back in, and
/// the "the Store updates it" decision stays the only update path.</para></summary>
public class StorePackagingTests
{
/// <summary>Known self-update packages. If one of these comes back, the Store route has
/// been quietly reversed.</summary>
private static readonly string[] UpdaterPackages =
{
"Velopack", "Squirrel", "NetSparkle", "AutoUpdater", "WinSparkle"
};
[Fact]
public void The_app_ships_no_self_update_subsystem()
{
// 1. The app project references no updater package.
var project = XDocument.Load(SourceAssert.RepoPath("ytLive.csproj"));
var packages = project.Descendants()
.Where(e => e.Name.LocalName == "PackageReference")
.Select(e => (string?)e.Attribute("Include"))
.Where(name => name != null)
.ToHashSet(StringComparer.OrdinalIgnoreCase);
foreach (var updater in UpdaterPackages)
Assert.DoesNotContain(updater, packages);
// 2. No app source file uses the updater API. Matched as CODE (a using directive,
// a type usage), not as a bare word — a file's prose is allowed to name what it
// deliberately does not do.
var root = Path.GetDirectoryName(SourceAssert.RepoPath("ytLive.csproj"))!;
foreach (var file in AppSourceFiles(root))
{
var text = File.ReadAllText(file);
Assert.False(
Regex.IsMatch(text, @"^\s*using\s+Velopack\s*;", RegexOptions.Multiline),
$"{Path.GetFileName(file)} still imports the Velopack updater — the Store updates this app.");
Assert.False(
Regex.IsMatch(text, @"\bVelopackApp\b"),
$"{Path.GetFileName(file)} still calls the Velopack updater — the Store updates this app.");
}
}
/// <summary>Every C# file of the app project (not the test project, not build output).</summary>
private static IEnumerable<string> AppSourceFiles(string root)
{
return Directory.EnumerateFiles(root, "*.cs", SearchOption.AllDirectories)
.Where(path => !path.Contains($"{Path.DirectorySeparatorChar}ytLive.Tests{Path.DirectorySeparatorChar}"))
.Where(path => !path.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}"))
.Where(path => !path.Contains($"{Path.DirectorySeparatorChar}bin{Path.DirectorySeparatorChar}"));
}
}
-1
View File
@@ -125,7 +125,6 @@
<PackageReference Include="SQLitePCLRaw.bundle_e_sqlite3" Version="2.1.12" />
<PackageReference Include="System.Security.Cryptography.ProtectedData" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
<PackageReference Include="Velopack" Version="1.2.0" />
</ItemGroup>
</Project>