The partner dashboard offers exactly one download because there is only one: the SDK zip
carries the runtime (steam_api64.dll) and the release tooling (sdk/tools/ContentBuilder
with steamcmd.exe for Windows and steamcmd.sh for Linux/macOS, plus the app_build and
depot_build VDF templates). Verified against the zip rather than assumed.
scripts/steam-sdk.sh gains --contentbuilder, opt-in because it is 27 MB of binaries that
only the uploading machine needs.
Distribution.md §2 now opens with that fact — there is no separate ContentBuilder
download to hunt for — plus a reference table of the pages that actually get used:
SteamPipe upload, DLC setup, BIsDlcInstalled, store artwork sizes, Coming Soon window,
the Wallet rule, recurring subs, partner support and Steamworks.NET.
The SDK zip is 24 MB of headers, examples and tools we never build. The runtime
dependency is one native library, so scripts/steam-sdk.sh extracts exactly
sdk/redistributable_bin/win64/steam_api64.dll into steam/ (gitignored, like ffmpeg/ —
the script is the audit trail) and writes steam_appid.txt = 480, Valve's Spacewar app,
so SteamAPI.Init() resolves an app id locally before LlamaCasty has one of its own.
Two csproj details that would have cost a debugging session:
- Link="steam_api64.dll" puts it BESIDE the exe; without Link, None items keep their
relative path and it lands in steam/, where the loader does not look.
- ExcludeFromSingleFile, for the same reason ffmpeg needed it: a native DLL otherwise
gets swallowed into the single-file bundle and cannot be loaded at runtime.
steam_appid.txt is Debug-only — Valve's guidance is not to ship it — and the publish
output confirms it: dll present at the root, no appid file, exe still 127 MB (proof the
DLL was not bundled).
Also queued, as asked: preview undo (Ctrl+Z / Ctrl+Shift+Z) in edit mode, noted as the
partner to the defaults-vs-current split — undo covers the slip, defaults cover the mess.
Renames AssemblyName to LlamaCasty before the first Steam depot — Steamworks is explicit
that renaming a live exe is painful, so this had to land first, not later.
Four assembly-qualified pack URIs carried the old name, not three: MainWindow (window
icon), OverlayHost (logo), RenameRecordingDialog and KeepBroadcastDialog. Pack URIs
resolve by assembly name at runtime, so a stale one compiles, passes every test and then
silently renders nothing the first time that view opens. AssemblyIdentityTests guards
both halves — the assembly name, and no source XAML still naming the legacy assembly.
Packaging/Package.appxmanifest and package-msix.sh carry the exe name too.
scripts/publish.sh replaces the by-hand publish: single-file win-x64, ReadyToRun,
ffmpeg beside the exe, and it refuses to finish if ffmpeg, LICENSE, PRIVACY.md or
THIRD-PARTY-NOTICES.txt are missing, or if a test assembly leaked in.
That last guard exists because the first publish was wrong in a way no test could see:
PublishSingleFile + IncludeNativeLibrariesForSelfExtract bundled the libav*.dll family
into the exe — a 9-file output with no ffmpeg folder and a 182 MB binary, so the locator
would have found nothing at runtime. ExcludeFromSingleFile="true" on the content keeps
the folder a folder. Diffing against a control publish without the single-file flags
found it; guessing at item globs wasted two attempts first (recorded in MyMistakes.md).
Measured: 127 MB exe + 129 MB ffmpeg = 257 MB depot. Tests: AssemblyIdentity,
StorePackaging, BuildStamp, BundledFonts all pass; app builds with 0 warnings.
TASKS.md 467 → 66 lines: one line per live item, detail stays in TASKS/
task-*.md. The old catalogue (63 rows of paragraph status prose plus the
historical narration) is preserved verbatim as TASKS/archive.md — a
2026-10-29 snapshot, not the live queue. The "out of product" list and every
retired decision moved to docs/ai/decisions.md with cause of death.
HANDOFF.md 440 → 40 lines, and it is now OVERWRITTEN, not appended: branch,
what's in flight, next action, live landmines, pending creator checks. History
belongs to git log; the previous handoff is reachable at ee3c592:HANDOFF.md.
docs/ai/decisions.md (new) collects what no longer governs: the paid tier and
its pricing, Polar, Store IAP, the Store route and why it died, the deleted
Velopack updater, signing economics, the retired monetization ideas, and the
permanent out-of-product list.
Facts that lived in the old ai.md "Current limitations" section were re-homed
rather than dropped with the router rewrite: OAuth/DPAPI/session behaviour →
docs/ai/youtube-api.md; paths, persistence and the Google project number →
docs/ai/environment.md.
verify.sh caps now cover all four always-read files (AGENTS 210, ai 100,
HANDOFF 40, TASKS 120).
Docs-only unit: scope-check run, no build.
ai.md 228 → 64 lines. It now carries only: what the product is, the build
commands, the architecture table, a "where the truth lives" table, and a
pointer to HANDOFF. No narrative, no status, no duplicated invariants.
AGENTS.md (195 lines) is now the single always-read file:
- the 20 invariants that break output, wipe user data, or ship a legal/trust
breach — one line each, pointing at the doc that explains it (they replace
110 lines of invariant stubs that duplicated docs/ai/*.md verbatim)
- Response style + No-Fluff Mode moved in from ai.md (rules belong in the rules file)
- onboarding is now: HANDOFF → ai.md router → the ONE docs/ai file → dir index;
TASKS.md is read when the question is "what's next", not to write code
- memory rides with the change but is no longer a unit of its own; HANDOFF is
≤40 lines and overwritten, never appended
Anti-rebloat gate: scripts/verify.sh gains step 1/4, a doc-budget check
(AGENTS ≤210, ai ≤100; HANDOFF/TASKS caps land with the queue trim). A commit
that pads a capped file instead of moving detail fails the gate.
schema.md and docs/index.md corrected for the new roles and reading order.
Docs-only unit: scope-check run, no build.
The manifest the Store route needs, and a real package built from it.
Packaging/Package.appxmanifest is a full-trust packagedClassicApp: EntryPoint
Windows.FullTrustApplication, uap10:TrustLevel=mediumIL,
uap10:RuntimeBehavior=packagedClassicApp, the rescap runFullTrust capability, and
webcam + microphone DeviceCapabilities. That combination is what lets the packaged
app launch the bundled ffmpeg as a child process and write to the user's profile;
an appContainer package would virtualise the recordings folder instead. x64 only,
en-US only (policy 10.7).
Packaging/Assets/ carries the four tiles the packer requires, generated from the
app's own Assets/llamacasty-logo.jpg by scripts/make-store-logos.py - listing art
and app art cannot drift.
scripts/package-msix.sh publishes Release win-x64 self-contained into
artifacts/msix-stage (a RID-less publish would copy every RID's ONNX native),
stages the manifest as AppxManifest.xml + Assets, and runs the Windows SDK's
makeappx. MSIX_PFX/MSIX_PFX_PASSWORD sign a package for a local test install; the
Store signs submissions. artifacts/ is gitignored.
First real package: artifacts/LlamaCasty.msix, 159 MB, 509 files, 'Package
creation succeeded'.
Schema traps found by packing (makeappx reports one error at a time):
Square310x310Logo and Wide310x150Logo are NOT uap:VisualElements attributes - they
live on uap:DefaultTile.
Guard: StorePackagingTests.The_package_manifest_declares_full_trust_camera_and_
microphone - identity/architecture, the full-trust application element, the three
capabilities, no appContainer VALUE (not the file text: the manifest's own comment
says the word), en-US only, and every referenced logo present.
Full suite 438/438, 0 warnings. Cold-start smoke test after the Velopack removal:
MainWindow loaded, no exceptions.
The measurement unit existed to name which of the six scene elements eats ~140ms per
composite. The creator's Release recording (ty-20261016-1841-559-0002.mp4, 174.5s,
1920x1080@60) names none of them — the whole composite costs ~1ms:
composites 301 per 5s = 60/s, every slot, 0 dropped, 0 stalls after warm-up
avg render 1.0-1.1ms (worst 2.5ms) = ~7% of the 16.67ms budget
parts "#5 HD Pro Webcam C920 0.4ms, #5 ... raster 0.2ms, backdrop 0.2ms"
- the other four elements never reach the 0.1ms print floor
C4 cache 151R/150H: half the ticks were a whole-frame BlockCopy
webcam 30.0/s at the resolver, longest gap 50.0ms (one 60Hz frame of
sampling phase, not a drop)
the FILE 29.6 distinct webcam images/s, 82% of images held exactly 2 frames,
longest hold 267ms (the healthy 30fps-in-60fps signature)
The two stalls are not composite cost: 124ms on the very first render (warm-up) and
53ms entering the "Chat" scene (a rebake).
So the historic slow numbers are not a slow scene but different configurations:
- Oct 9 19:48 (only bad take still on disk) was the VERTICAL tier:
"FramePump started (1080x1920 @ 60 fps) scene='Live' comp=608x1080" - the 607x1080
centre crop upscaled to 2M px, 92 renders/5s, avg render 45.0ms, worst 278ms.
- Oct 5 12:01 (141ms) logged no scene=/comp= at all - the suffix did not exist yet,
so the map's "21:9 -> 1920x803" explanation stays a hypothesis, not a reading.
- Every slow window shows "cache xR/0H" (the whole-frame cache never reusing) while
this take shows 151R/150H, and one Oct 5 take latches mid-take (1.5ms -> 3.0ms ->
45.2ms in 15s, then stays). That latch is the lead for the next take; it is a
hypothesis and the parts line is what will decide it.
Corrections to the map (same commit, stale facts replaced not appended):
- "It was never Debug-vs-Release" is WITHDRAWN: same 1920x1080 tier, Oct 9 = 10.4ms
vs this Release take's 1.0ms, and Build <hash> carries no configuration - every
pre-2026-10-16 render number is config-unknown.
- The "same scene shape" 10:01/12:01 comparison is superseded: it was an eye
comparison of takes whose geometry was never logged.
New: scripts/webcam-cadence.py - the ground-truth probe for what the FILE holds,
beside what the pump says it did. It took three wrong readings to get right (H.264
re-encoding defeats exact-pixel comparison; a round-clip crop's corners leak 60fps
background; a fixed crop across a scene switch reads background), all recorded in
MyMistakes.md as a recipe, including an ffmpeg-native cross-check
(tblend=difference + signalstats.YAVG - the same metric, the technique already in
this repo's A/V-sync recipe) which agreed: 52.2% duplicates / 28.6 distinct/s vs
50.7% / 29.6/s on different windows of the same take.
Next: no fix is allowed yet - the creator makes a Release take in the failing
geometry (vertical 1080p60 tier first), and that take's parts line names the stage.
Docs in the same commit: ai.md (new "What the first instrumented take said" section,
the two corrections, the parts-line denominators, the tool in the inventory),
TASKS.md (reading outcome), HANDOFF.md (rewritten), MyMistakes.md (recipe).
Gates: clean build 0 warnings, full suite 428/428, scope check passed.
Store policy 10.2.2 forbids dynamic code inclusion, and this was the only
third-party binary the app ever downloaded and ran. It had already failed in
production: the pin aged out of BtbN's 14-day retention and the download 404'd
on the creator's first real recording attempt (2026-09-01). Bundling kills
that failure class permanently and deletes the startup network dependency.
- scripts/fetch-ffmpeg.sh populates a gitignored ./ffmpeg from a pinned
month-end BtbN LGPL-shared artifact, SHA-256 verified before extraction,
idempotent, FFMPEG_ZIP= override. 135 MB of DLLs in git would tax every
commit on this 9p tree; the URL + hash are the audit trail LGPL §6 wants.
- ytLive.csproj copies ffmpeg\** to output and publish (Always), and a
GuardBundledFfmpeg target fails the BUILD if the folder is missing, so a
fresh clone finds out before a take rather than during one.
- FfmpegLocator loses the downloader, the zip extraction and the
%APPDATA%\ytLlive\tools cache. Order is now bundle -> PATH, deliberately
inverted: a PATH ffmpeg may be a different major version or a GPL build,
and the bundle is what was pinned, license-vetted and tested. A missing
bundle throws loudly; the old "found something on disk" path could satisfy
a missing bundle with a stale copy, and silent drift is the worst failure
mode for a dependency certification inspects.
- ffplay.exe (18 MB) is not shipped; nothing calls it.
- THIRD-PARTY-NOTICES.txt: "downloads" -> "bundled" throughout, and the stale
build tag autobuild-2026-08-09-13-03 — the tag that 404'd — corrected to
autobuild-2026-08-31-13-27 with the exact artifact name and archive hash.
OpenH264 made precise: compiled INTO avcodec-63.dll, no separate binary.
Checked whether other third-party binaries needed bundling: no. e_sqlite3.dll
and WebView2Loader.dll already ship via NuGet into runtimes\win-x64\native\,
OpenH264 is inside avcodec-63.dll, the rest are managed. Two gaps remain that
bundling cannot fix, recorded in the map: the WebView2 Evergreen runtime is a
machine prerequisite (TASK 48 item 2), and Velopack's updater downloads and
executes, so its teardown is a compliance action.
Tests: 16 locator facts, suite 379/379, 0 warnings. The integration test runs
the bundled ffmpeg -version, which is the only assertion that proves the
sibling libav*.dll resolved — presence checks pass with the DLLs missing and a
shared build then fails to start. TheLocatorCarriesNoNetworkCapability keeps
10.2.2 from rotting back.
Analysis: TASKS/research-store-certification.md §4.