Commit graph

2 commits

Author SHA1 Message Date
Erik
bd359d5181 fix(chargen): Campaign CC gate round 1 closeout — Group 3: round review fixes (F4-F11, F14, F16)
The remaining code-bearing findings from the round review, F4-F16 minus
the doc-only items (batched separately):

- F4: three client-wide UiButton corpus sweeps (LabelBox path — exactly
  the 4 Town buttons, confined to chargen; conflicting custom-selection-
  pair + standard Normal/Highlight media — zero found, no gate
  tightening needed; per-state label-color map — 209 matches beyond
  chargen, confirming AP-222's mechanism has always been broadly active
  since it shipped generically in DatWidgetFactory).
- F5/F6: LayoutImporter's Batch C un-consumed-children carve-out now
  honors a child's own AuthoredInvisible flag (a narrow honor scoped to
  exactly that carve-out, not the general #408 client-wide one) — the
  chat transcript's new-text indicator (0x1000048C) was building as a
  visible phantom element retail never shows; verified both directions
  against the gold-frame pieces, which do not author Invisible.
- F7: BoundedProcessOutputCapture.AppendLine combines the line text and
  its trailing newline into one buffer and one file open/write/close
  instead of two.
- F9: corrected a stale comment in RuntimeSettingsTargets — #407 split
  DisplayModeCatalog's Resolutions/WindowedResolutions in two, so the
  fullscreen validator's own narrower list is now DELIBERATELY different
  from the Config dropdown's fuller offering, not the "must match" bug
  the comment described.
- F10: documented (not changed) why the LabelBox path's default 3px
  inset and the face-relative +4px gap in DatWidgetFactory.BuildButton
  are deliberately different numbers — neither carries a retail
  citation, and moving either to match the other would be an unfounded
  guess on a button that currently works correctly.
- F11: Heritage/Profession/Summary/Town description pages now compose
  DatRichText.Compose's result ONCE inside their already revision-gated
  Refresh, caching the built line list instead of re-wrapping on every
  draw call.
- F14: documented (not changed) why PrivateEntityViewportRenderer's
  _animatedIds set carrying a reserved-but-never-drawn backdrop id is
  harmless — BuildDrawEntities already excludes a null/empty backdrop
  from the actual draw list, so the id is never looked up.
- F16: the Summary preview now uses its own render-id pair
  (SummaryPreviewRenderId/SummaryPreviewBackdropRenderId, 0xDA11D035/
  0xDA11D036) instead of sharing the Appearance page's
  (0xDA11D032/0xDA11D034) — confirmed by tracing
  FixedEntityTextureOwnerLease through TextureCache to
  CompositeTextureArrayCache's shared owner tracker that both pages'
  previews share ONE process-wide TextureCache, so sharing render ids
  was a real cross-page texture-release collision (either page's own
  re-dress or disposal could release the OTHER page's still-active
  textures), not a theoretical one.

F3's own register bookkeeping (AP-229 addendum) and F12's register/AD
header-count corrections land in the docs-only commit alongside F15.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 15:34:07 +02:00
Erik
691b925952 fix #406: launcher session exit observation carries the real code + captures client stderr
GameWindow.Dispose() (via Program.cs's `using var window = ...`) runs
unconditionally even when invoked mid-unwind of an exception that escaped
Run()'s Silk.NET frame loop. Resource teardown itself can converge
cleanly regardless, so CompleteShutdown had no way to tell "normal Run()
return" from "a crash is propagating through me right now" and always
wrote the hardcoded exited{code:0,reason:"graceful"} — exactly the
symptom #406 observed against a real 0xE0434352 crash. Fixed by latching
_runFailure in Run()'s existing catch block (before the pre-existing
throw) and consulting it from a new ReportExited method, the one call
site for the terminal status write: crashed(1)/graceful(0)/
shutdown-incomplete(1) as appropriate. No wire-contract amendment needed
— §LA1 pins the exited event NAME, and reason is already free text that
StatusEventParser round-trips unchanged.

Sibling gap fixed in the same commit: the launcher discarded the child's
stdout/stderr entirely, which is why diagnosing this exact crash required
a manual console re-run. Added BoundedProcessOutputCapture, a 2 MiB-capped
sink mirroring SessionStatusWriter's open-append-flush-close-per-write
posture (a long-lived write handle is not actually concurrently readable
on Windows even with FileShare.Read — confirmed by isolated repro), wired
into both SystemChildProcess (ProcessStartInfo.RedirectStandardError;
Linux + Windows graphical children, i.e. this bug's own scenario) and
WindowsSystemChildProcess (a real native pipe via CreateChildOutputPipe,
mirroring the existing stdin pipe; Windows console-capable/Headless
children). Opt-in via LauncherProcessSpec.StderrLogPath (null = unchanged
behavior), threaded through SessionConfigComposer -> client.err.log
beside status.jsonl -> LauncherExecutableSet -> LauncherOrchestrator.

Tests: GameWindowCrashStatusTests (source-shape, matching the existing
GameWindow test pattern — the class cannot be constructed without a live
GPU/window), BoundedProcessOutputCaptureTests (10 unit tests), and three
new LauncherProcessSupervisorTests spawning real child processes through
both capture code paths.

Launcher.Core.Tests: 337/0 (was 324/0). Launcher.Tests: 67/0 (unchanged).
Full solution build green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-16 11:44:46 +02:00