Loading…
a327ex.com

Anchor Website 7

Opus 4.8

Summary

Resolved the long-standing Anchor 2 "minimize/unfocus hang" (root-caused to Windows window-ghosting, not a loop bug) and completed the M2b video pull for the a327ex.com renderer (112/120 media owned locally), including replacing dead video embeds and mapping out the remaining web-delivery roadmap.

Minimize/unfocus hang — root cause & fix (main focus):

  • Reopened the bug that last session ("Anchor Website 6") wrongly reported as fixed. Last session's render-throttle (skip render + SDL_GL_SwapWindow + SDL_Delay(10) while MINIMIZED/HIDDEN) was necessary but not sufficient — it stopped the free-spin but introduced a new symptom: a long-minimized window would no longer restore at all (window never reappears; force-kill needed).
  • User repro testing pinned the behavior: minimized (fullscreen or windowed) hangs after ~5 min; unfocused-but-visible does NOT hang; quick minimize→restore always works; happens while actively using the computer with the display on (ruled out display-sleep / screensaver / GPU TDR).
  • First built a Lua "flight recorder" (once/sec heartbeat → hang_trace.log) but found its clock was game-time, not wall-clock — update() is always called with a fixed dt = PHYSICS_RATE = 1/120, so t and upd were the same measurement and couldn't distinguish a healthy loop from one blocking in clamped catch-up bursts (MAX_UPDATES=10).
  • Replaced it with a C watchdog thread in anchor.c: real wall-clock (SDL_GetPerformanceCounter) + a main-loop iteration counter + a phase marker (TOP/EVENTS/UPDATE/RENDER/SWAP/DELAY) + cached SDL_GetWindowFlags, logged off-thread to watchdog.log so it survives a main-thread stall (marks STALL after ~2s of no iteration progress). Later added a raw Win32 message probe via SDL_SetWindowsMessageHook counting messages delivered before SDL processes them.
  • The watchdog proved the main loop was always healthy: across a ~58-min run, iter advanced ~100 Hz the entire time minimized, zero STALL, flat RSS, constant GL textures/FBOs. Window flags showed FULLSCREEN_DESKTOP|MINIMIZED (0x1067) set throughout. The raw-message count (nmsg) FROZE at 34 for ~58 minutes — zero Win32 messages reached the app during restore-click attempts.
  • Root cause: Windows window-ghosting. Because last session's fix makes the app stop presenting while minimized, DWM marks the window unresponsive after a few minutes and spawns a ghost; taskbar/Alt-Tab/restore clicks hit the ghost, and the real (alive) window receives nothing — so it never un-minimizes. Quick restores work because the ghost hasn't formed yet (explains the time-dependence). Also explains the symptom change vs last session (pre-fix the app kept presenting → no ghost).
  • Fix: one line — DisableProcessWindowsGhosting() at desktop startup (#ifdef _WIN32) in shared Anchor2/engine/src/anchor.c. User-verified end-to-end: after a 13.7-min minimize, the restore-click made nmsg climb, the MINIMIZED flag clear (0x1067→0x1227), real input flow (WM_MOUSEMOVE/WM_LBUTTONDOWN/WM_SYSKEYDOWN), and repeated re-minimize/restore all worked.
  • Removed all diagnostic scaffolding (watchdog thread, message hook, phase markers, extra globals, the Lua recorder); only DisableProcessWindowsGhosting() remains. Rebuilt the engine clean, redeployed renderer/anchor.exe. Updated PLAN.md + the project-anchor-hang-followup memory (now RESOLVED). Cost: a genuinely-hung Anchor app no longer shows the "Not Responding" ghost UI (Task Manager still kills it). Affects all Anchor2 desktop (Windows) games.

Video pull (M2b) — completed (112/120 usable):

  • tools/fetch_media.py (yt-dlp) pulls owned 1080p-h264 copies of every ::youtube/::short (106 refs) + 14 local ::video. 5 cached at start.
  • YouTube's 2026 anti-bot defenses required escalating fixes, diagnosed via the task logs: (1) cookie-less = HTTP 403 → --cookies-from-browser firefox; (2) heavy pulling rate-flags the IP ("No title found" degraded extraction, then "Sign in to confirm you're not a bot") → pull in batches with cooldowns; (3) the logged-in tv client requires solving the signature/n-challenge ("Only images available" across ALL clients — ios/mweb/web_safari/tv_embedded/android/android_vr/tv) → node JS runtime + yt-dlp EJS solver (--remote-components ejs:github, fetches yt.solver.lib.min.js); (4) YouTube throttles a download stream to ~0 B/s mid-file, hanging a no-timeout yt-dlp (one file wedged at 404 MB for an hour, process died silently) → --socket-timeout 30 --retries 10 --fragment-retries 10 --throttled-rate 100K.
  • Final: 98/106 YouTube + 14 local = 112/120 usable. The user logged into YouTube in Firefox mid-session (cookies 5→70) which cleared the bot-wall. The IP rate-flag pattern: a cooldown clears it; the heavy pulls re-trip it.
  • 8 unrecoverable: 6 Super People dev/test streams (game shut down, channel privated — inaccessible even logged in), HPM6rf0-e6M (Nas Daily / SBF, account terminated), gmbjqe9g7g0 (Baby Steps / NHK copyright-blocked). These get the per-post fallback card next session.

Dead-video replacements (4 posts):

  • User supplied working reuploads: Son of a Serpent mIafcqCZNewCFJsWP2prEs, Carnival 6BT8tBwLdtcu-u0t487byw (Thank You Scientist music videos — both had been pulled from YouTube), and the "You're Not a Lottery Ticket" talk iZM_JmZdqCwKuc6pBZ-Q0I.
  • Swapped embeds + inline discography links across son_of_a_serpent.md, the_foreign_man.md, small_games.md, snkrx_log.md (the lottery-ticket talk was embedded in 3 posts, 4 refs total). Stale &list=…&index=… params on the inline links were dropped to the clean bare URLs the user provided. All three replacements downloaded successfully.
  • Located + identified every dead (4) and private (7) video by title-from-context and file:line; 6 of the 7 private are Super People dev streams.

Web-delivery requirements (planning, no code):

  • Established the current architecture: a327ex.com is a Lua server (server/) behind nginx on a VPS, rendering markdown→HTML server-side — so SEO/crawler HTML already exists today. The desired model is progressive enhancement: keep the Lua server as the SEO/HTML layer, mount the engine WASM canvas on top (HTML stays in the DOM for crawlers, canvas covers it for humans).
  • Remaining web work, in dependency order: (1) Cloudflare in front of the domain — the "forgotten step" (README §144): the Name.com→Cloudflare registrar transfer was in progress; once complete, switch nameservers + SSL Full(strict) + orange-cloud proxy + nginx CF-Connecting-IP. (2) R2 for range-streamed video (zero egress), ideally on a media. subdomain (depends on Cloudflare-in-front). (3) Engine WASM web port (build-web-engine.sh/package-web-game.sh exist; needs lazy per-page content loading + browser-<video>gl.texImage2D; libav stays desktop-only). (4) Music-video DMCA decision (sharpened by seeing 2 TYS videos already pulled from YT).
  • Open decisions for the user: keep the Lua server permanently as the SEO layer (recommended: yes); the DMCA policy; physically verify whether the domain transfer completed.

Commits / housekeeping:

  • Anchor2 engine: DisableProcessWindowsGhosting() fix in engine/src/anchor.c — committed with a generic message, NOT pushed (private session targeting a shared repo).
  • a327ex-site: 4 post edits + renderer/PLAN.md (added a "▶ NEXT SESSION" handoff block) + renderer/anchor.exe (rebuilt clean) + renderer/tools/fetch_media.py (auth/EJS/robustness patches). renderer/data/ is gitignored (generated artifacts, incl. media.lua). renderer/media-cache/ (tens of GB of pulled video) gitignored → feeds R2 in the web-delivery phase.
  • Next session = two buckets: (1) per-post fidelity, (2) web delivery (recorded in PLAN.md, MEMORY.md, and the project memory).
  • Sealed privately as Private Session 11.

🔒 Only the summary of this log is public. Private because it contains too many website internal details.