fix #435 (part 2, closes it): attribute the unowned probes — delete 7, reclassify 8, restore 1

Part 1 deleted probes whose owning issues were closed. These 14 named no
issue at all, so each was traced to its introducing commit
(git log -S) instead of guessed at. Attribution split them three ways:

DELETED (7, investigations closed): ACDREAM_A8_DUMP_PV and
ACDREAM_DUMP_LIVE_SPAWNS (Phase A8), ACDREAM_DUMP_CLOTHING (#37),
ACDREAM_DUMP_EDGE_SLIDE (#32), ACDREAM_DUMP_STEPUP (L.2.3d-f),
ACDREAM_DUMP_VENDOR (the vendor campaign, 25 call sites across 8 files),
ACDREAM_DUMP_VITALS (#5, four independent read sites). VendorDiagnostics.cs
went entirely.

RECLASSIFIED (8, tools misfiled as probes): the DUMP_CELLS/DUMP_GFXOBJS
fixture-extraction family (replay-harness tooling with a roundtrip test),
PROBE_CELL (standing cell-transit tracer, pair of the permanent
PROBE_RESOLVE), DUMP_SKY and HIDE_PART (generic isolation tools), and
DUMP_STEEP_ROOF — which looked like an L.4 relic but observes LIVE
divergence-register row AD-56; deleting it would have removed the only
runtime lens on an active divergence. All moved to Permanent diagnostics
with their attribution recorded.

RESTORED (1): ACDREAM_DUMP_MOVE_TRUTH was deleted and un-deleted the same
day. It is not a probe — the canonical nine-stop soak
(run-connected-r6-soak.ps1) hard-fails every destination without its
'move-truth OUT' records, with a message that would misdirect the next
operator. Under the no-workarounds rule the gate's mechanism is restored,
not left broken with an IOU (#437, closed). Process lesson recorded on
both issues: a closed owning issue is NOT sufficient to delete a probe —
grep tools/ and the contract tests for consumers first.

Also lands the owner-requested default-off invariant: every diagnostic in
the codebase is inert until its env var is explicitly set. Exactly four
flags default ON and none is a diagnostic — RETAIL_CHASE, CAMERA_COLLIDE,
CAMERA_ALIGN_SLOPE, RETAIL_CLOSE_DEGRADES are retail behaviors wearing an
A/B off-switch. That set is now FROZEN by
LaunchOptionsDocumentationTests.OnlyTheFourRetailBehaviorFlagsDefaultOn;
docs/launch-options.md's Conventions and CLAUDE.md state the rule, and
CLAUDE.md now binds future probes to a documented row in the same commit.

The client reads 137 environment variables (161 at audit start); 40
temporary probes remain, every one attributed. Full hermetic suite 15,322
passed / 0 failed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Erik 2026-08-24 12:32:42 +02:00
parent 0c5057c9ff
commit c1e6e3da44
26 changed files with 256 additions and 693 deletions

View file

@ -822,13 +822,6 @@ public static class GameEventWiring
registrar.Register(GameEventType.ApproachVendor, e =>
{
var p = VendorApproach.TryParse(e.Payload.Span);
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] ApproachVendor(0x0062) inbound parsed={p is not null} "
+ $"vendorGuid={(p is null ? "n/a" : $"0x{p.Value.VendorGuid:X8}")} "
+ $"itemCount={(p is null ? "n/a" : p.Value.Items.Count.ToString())}");
}
if (p is null) return;
var profile = new VendorShopProfile(
@ -846,14 +839,6 @@ public static class GameEventWiring
for (int i = 0; i < shopItems.Length; i++)
{
VendorApproach.ItemProfile item = p.Value.Items[i];
if (VendorDiagnostics.DumpVendorEnabled && i < 5)
{
Console.WriteLine(
$"[vendor-diag] ApproachVendor wire-item[{i}] guid=0x{item.ItemGuid:X8} "
+ $"name={item.Desc.Name ?? "null"} "
+ $"descStackSize={(item.Desc.StackSize is { } ds ? ds.ToString() : "null")} "
+ $"stackSizeMax={(item.Desc.StackSizeMax is { } sm ? sm.ToString() : "null")}");
}
shopItems[i] = new VendorShopItem(
item.ItemGuid,
item.StackSize,
@ -950,12 +935,6 @@ public static class GameEventWiring
registrar.Register(GameEventType.UseDone, e =>
{
uint? err = GameEvents.ParseUseDone(e.Payload.Span);
if (VendorDiagnostics.DumpVendorEnabled)
{
Console.WriteLine(
$"[vendor-diag] UseDone(0x01C7) inbound parsed={err is not null} "
+ $"err={(err is null ? "n/a" : $"0x{err.Value:X4}")}");
}
if (err is null) return;
// Already the diagnostics-only log line SHOULD-FIX 4 asks for —
// it fires unconditionally, so an unmapped code below stays
@ -1038,12 +1017,9 @@ public static class GameEventWiring
// player this is the authoritative source (the per-item
// SpellBook flag in IdentifyObjectResponse is for caster
// items / scrolls only).
bool dumpPd = Environment.GetEnvironmentVariable("ACDREAM_DUMP_VITALS") == "1";
registrar.Register(GameEventType.PlayerDescription, e =>
{
var p = PlayerDescriptionParser.TryParse(e.Payload.Span);
if (dumpPd)
Console.WriteLine($"vitals: PlayerDescription body.len={e.Payload.Length} parsed={(p is null ? "NULL" : $"vec={p.Value.VectorFlags} attrs={p.Value.Attributes.Count} spells={p.Value.Spells.Count}")}");
if (p is null) return;
// R3: a trailer-truncated parse carries zero placeholder option
@ -1106,8 +1082,6 @@ public static class GameEventWiring
if (attr.Current is uint cur)
{
// Vital entry (id 7/8/9) — has absolute current.
if (dumpPd)
Console.WriteLine($"vitals: PD-vital id={attr.AtType} ranks={attr.Ranks} start={attr.Start} cur={cur}");
localPlayer.OnVitalUpdate(
vitalId: attr.AtType,
ranks: attr.Ranks,
@ -1120,8 +1094,6 @@ public static class GameEventWiring
// Primary attribute (id 1..6) — Endurance+Self feed
// the vital max formula (Endurance/2 for Health,
// Endurance for Stamina, Self for Mana).
if (dumpPd)
Console.WriteLine($"vitals: PD-attr id={attr.AtType} ranks={attr.Ranks} start={attr.Start}");
localPlayer.OnAttributeUpdate(
atType: attr.AtType,
ranks: attr.Ranks,
@ -1174,10 +1146,6 @@ public static class GameEventWiring
int total = (int)(formulaBonus + s.Init + s.Ranks);
if (s.SkillId == 24u) runSkill = total;
else if (s.SkillId == 22u) jumpSkill = total;
if (dumpPd)
Console.WriteLine(
$"vitals: PD-skill id={s.SkillId} init={s.Init} ranks={s.Ranks} formulaBonus={formulaBonus} total={total}");
}
if (runSkill >= 0 || jumpSkill >= 0)
onSkillsUpdated?.Invoke(runSkill, jumpSkill);