Files
Project-M/Docs/Vault/07_Sessions/2026/2026-07-12_Phase17_Boon_Overhaul.md
kronic 972e0d5b4f Docs: Phase 1.7 boon-overhaul build spec + DR-047 + session log + roadmap tick + CLAUDE.md 7-type-query gotcha
Boon overhaul CODE-COMPLETE (mechanic-changers): design forks locked,
pre-code review (11 findings folded), post-impl review (incomplete on a
session limit — raised findings self-verified, 2 real bugs fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 18:54:06 -07:00

6.2 KiB
Raw Permalink Blame History

title, date, tags, permalink
title date tags permalink
2026-07-12 — Phase 1.7 Boon Overhaul (mechanic-changers) 2026-07-12
session
coop-hades
phase-1.7
boons
netcode
dots
gamevault/07-sessions/2026-07-12-phase17-boon-overhaul

Phase 1.7 — Boon Overhaul

Goal: turn boons from flat-stat rows (the operator's "boring" verdict) into mechanic-changers so two runs feel different. The full /dots-dev spine ran (ground → forks → ctx7 → pre-code review → plan → serial impl → verify → post-impl review → docs). Build Spec: Boon_Overhaul_Build_Spec. Decision record: DR-047_Phase17_Boon_Overhaul_Mechanic_Changers.

Forks locked (operator, this session)

  1. Ability-swap boons DEFERRED to Phase 3 (content-thin — only Ranger has alt abilities; the Warrior cone has none; couples to the client's AbilityRef→class derivation). Phase 1.7 = behavior-boons only.
  2. ~8 mechanic-changers + ~4 stat boons (keep a few "safe" econ/pacing picks).
  3. Synergy = tags for dedup + dominated-offer protection + light build-bias (NO synergy bonuses in v1).

What shipped

Architecture (netcode-correct, review-hardened):

  • BoonEffects — new per-player [GhostComponent(SendToOwnerType.SendToOwner)] with [GhostField] byte Pierce,Fork,Chain,Flags. Baked INERT on the player (the BoonOffer idiom → one deliberate player-ghost re-bake); mutated on pick (non-structural); zeroed on the Returning edge. Rollback-correct via the GhostFields (owner is the sole predictor).
  • ProjectileEffectState — SERVER-ONLY (NOT a GhostField), baked inert on the projectile prefab → keeps the Projectile ghost hash FROZEN (the C1 correction; a field on Projectile itself WOULD change its StableTypeHash→serializer hash). Seeded server-side at spawn; a FixedList64Bytes<Entity> re-hit set excluded DURING target selection.
  • DashTrailState — server-only per-player Blade-Dash dedup keyed to DashState.StartTick (no reliable clear edge).
  • BoonDefBlob +Kind/EffectKind/Family discriminators (config blob, not wire); the 12-row table redefined.
  • TimedModifierUtil.Upsert (refresh-never-stack) + a TimedModifier-buffer RemoveBySourceId overload.
  • Tuning.FrenzySourceId = 0x00B0FFFF — pinned to the TOP of the boon band (the bottom-up pick counter can't reach it; the Returning range-strip still clears it free).

Combat hooks: pierce/chain in ProjectileDamageSystem (survive-and-continue, one destroy/tick preserved); fork = extra predicted projectiles at a spread in AbilityFireSystem (unique packed spawnId); knockback→pull negates Dir at the stamp (new KnockbackUtil.Stamp(..., bool pull)); Blade-Dash = new server DashTrailDamageSystem; Detonating Finisher inline in MeleeComboSystem (reads BoonEffects in the player loop, stashes IsFinisher/Detonate/Pull on PendingCleave); Siphon+Frenzy = new server KillRewardSystem off a Dying.KillerNetId+Rewarded latch stamped in HealthApplyDamageSystem.

Offers: BoonMath.PickBoons gains the owner's BoonEffects — dedup (skip an owned non-stacking flag), dominated-offer protection (no two same-Family in a deal → kills the "-15% vs -25% cd" bug), light ×1.5 build-bias. Integer-hash only.

Table (12): 1 Piercing·2 Split·3 Ricochet (Ranger, projectile) · 4 Detonating Finisher (Warrior) · 5 Blade Dash · 6 Gravity Pull · 7 Siphon · 8 Frenzy (both) · 9 Executioner · 10 Titan's Vigor · 11 Fleet Foot · 12 Berserker's Pace (both, stat).

Reviews

  • Pre-code (wf_c4bdd60d-7b2, 27 agents / 0 errors): 23 raised → 11 confirmed, all folded (C1 separate projectile component; C2 SendToOwner not All; C35 Frenzy top-of-band + Upsert + TimedModifier Returning-strip; C6 hit-set-excluded-in- selection; C7 dash dedup on StartTick; C8 inline finisher; C9 killer-capture-before-clear; C10 owned-state determinism doc).
  • Post-impl (wf_d2a0a673-af5) — INCOMPLETE: 4 verify agents died on a session limit (the masquerade hazard — checked the failures list). Self-verified the raised findings against the code and fixed 2 real bugs the Build Spec §5 open-risks predicted + 1 nit: (a) Fork uncappedshots = 1 + min(Fork,8) (forkIndex is 4 spawnId bits, wrapped at 16); (b) Blade-Dash hit-set overflow re-damaged an unrecordable enemy every tick → break once Hit full; (c) the Slice-2 comment was displaced onto the BoonEffects lookup line (the apply_text_edits swallow gotcha) → relabelled.

Verification

  • L1 console clean vs baseline (only benign Server-Tick-Batching warnings), no Burst ICE.
  • L2 488/488 EditMode (from 474; +13 new: pierce/chain, TimedModifierUtil Upsert, KillReward ×4, DashTrail ×2, offer dedup/owned-flag, a predicted-chain cycle guard) + live Play smoke: BoonEffects server==owner-client (set server Pierce3/Fork2/Chain1/Flags16 → replicated to the owning client), client connected (new GhostField didn't break the handshake → ghost hash consistent), world-creation exception-free (no ordering cycle).
  • L3 N/A — behavioral slice, no new visual assets; the boon effects in-combat are the operator's playtest.

Wire/bake churn

BoonEffects = ONE player-ghost re-bake (front-loaded; both worlds bake identically → handshake OK). ProjectileEffectState/ DashTrailState/Dying/BoonDefBlob = no ghost-hash change. StatModifier + RPC collection UNCHANGED.

Next-session intent

Phase 1.7 boon overhaul is CODE-COMPLETE + tests green + Play-smoke-verified. NEXT = the operator's feel/balance playtest (the gate: "two consecutive runs feel different from boons alone") — tune knobs: Fork cap (8), DashTrail radius/damage (1.6/12), Frenzy duration/mult (240t/-0.30), Siphon heal (8), finisher detonation radius (3.5). Then Phase 2 — Hub-ification (mothball sieges, win moves fully expedition-side, base=hub; adversarial design-review first). Order stays 1.7 → 2 → 3 → 4/5 parallel. Also OPEN (deferred): ability-swap boons (Phase 3), a client build-display HUD for active boons.

Related: Iteration_2026-07_CoopHades · DR-047_Phase17_Boon_Overhaul_Mechanic_Changers · Boon_Overhaul_Build_Spec · DR-044_Expedition_Redesign_Shipped_Demo_Polish