Commit Graph
100 Commits
Author SHA1 Message Date
prosolisandClaude Opus 4.7 41c60617db Adv 2.0 D&D Phase 12 E3a: Sunken Temple tidal event
Adds the §7.1 tidal event hook: warnings on Days 4 and 5, peak event
on Day 6 with forced 2× supply burn (overrides tier-2 HarshMod 1.5×),
silenced if the boss has been defeated.

Wires zone-temporal events into deliverBriefing as two new hook points
— pre-burn (mutates burn multiplier) and post-rollover (appends
flavor-bearing log entries). Future E3b–E3f layer onto the same hooks.

Reuses the prewritten flavor.SunkenTempleTidalWarning / TidalEvent
pools per feedback_reuse_existing_flavor.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 9cb66d46ac Adv 2.0 D&D Phase 12 E2e: !threat command
§14 surfaces the threat clock as a first-class command. !threat shows
level/100, current band, the per-band combat & supply effects (built
from ThreatBandInfo so display tracks any future band re-tuning), and
the last 5 ThreatEvent log entries with deltas + reasons. Adds a
"siege is active" / "past 70" footer when those gates have triggered.

Active-enemy camp guard and close-call evening recap stay deferred —
both need combat state inside an expedition, which doesn't exist yet
(expeditions still don't host their own combat loop). They land in the
combat-link phase along with the §8.1 combat-driven threat modifiers.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 f844068660 Adv 2.0 D&D Phase 12 E2d: Fortified Camp branch + overnight rest effects
§5.1 fortified camp unlocked in !camp, gated on exp.BossDefeated for
now (cache-site waypoints land in E3+). +2 SU cost was already in the
E1e cost table; this commit just stops rejecting the type.

§3 / §5.1 / §8.1 overnight rest applied at briefing rollover via
processOvernightCamp:
  - rough: HP floor of HPMax/2 (no spell refresh)
  - standard: full HP, exhaustion -1, refreshAllResources +
    refreshSpellSlots
  - fortified / base: standard rest + 1d6 HP bonus on wake +
    threat-clock -5 (§8.1 modifier)

The camp auto-breaks once the rest applies — staying camped multi-day
isn't a thing in the spec; the player can re-pitch on the next night
if they want to. Briefing body now includes a 💤 line summarizing the
HP/threat delta from the night's rest.

Combat-driven side of fortified ('+1d6 HP on wake' as a rare flavor
event) currently surfaces as the rest summary; a peaceful-night TwinBee
narration variant could come later if we add a dedicated pool — for
now reusing the existing CampEstablished ambience suffices.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 5b8ef740f8 Adv 2.0 D&D Phase 12 E2c: Siege Mode effects + threat-70 warning
§8.3 siege economics:
- applyDailyBurn now takes an explicit siege flag and enforces a 2× floor
  on supply burn even when HarshMod is below 2 (tier-1 zones still get
  starved out per spec).
- currentBurn mirrors the same precedence so status/briefing readouts
  stay consistent.
- Briefing rollover passes e.SiegeMode through, decoupling siege from
  the harsh-conditions composite.

§8.3 threat-70 warning: applyDailyThreatDrift emits a one-time
appendApproachingSiegeLog when the level crosses 70 (prevLevel<70 and
new level≥70). Pulls from a new flavor.ThreatClockApproachingSiege pool
seeded with the spec's verbatim warning beat plus two voice-matched
alternates.

Other siege effects (boss +20 HP / Legendary Resistance, cleared-room
respawn, no-short-rest enforcement) stay deferred to the combat-link
phase — ThreatBandInfo already exposes the flags that engine will read.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 9b48dda79e Adv 2.0 D&D Phase 12 E2b: Wandering monster table + night phase
§6 night-phase resolution. Fires once per recap when the player has an
active camp. resolveWanderingCheck rolls 1d20 + threat mod (+1 per full
10 above 30) + camp mod (rough +3, fortified -4, base -6) + class mod
(ranger -2 in wilderness zones), then buckets per §6.1 into Peaceful /
Passage / Minor / Standard / Elite / Ambush.

Encounter side picks a roster entry from the zone (weighted by
SpawnWeight, biased to elite for Elite/Ambush) and persists the result
to camp.NightEvents + a "night"-typed log entry. Signs-of-passage adds
+2 threat per spec. Combat resolution itself is deferred — expeditions
don't host their own combat loop yet — so encounter outcomes log as
pending and surface in the recap with an "encounter pending" hint;
the combat-link phase will read these on next !advance.

Flavor reuse: encounter narration pulls from the existing ThreatClock
{Stirring,Alert,Hostile,Siege} pools, matched to the current band so
voice tracks the dungeon's awareness level.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 b520b0ce2d Adv 2.0 D&D Phase 12 E2a: Threat Clock state machine
Threshold model on top of the E1a persistence: ThreatBand bracketing
(Quiet/Stirring/Alert/Hostile/Siege) with per-band combat/supply/rest
knobs (ThreatBandInfo). Daily threat drift (+3/day, GMMood-modded:
effusive→-3 elated, hostile→+5 wrathful) wired into the 06:00 briefing
rollover; no-op once the boss is down.

Threshold crossings emit a flavor-bearing log entry pulling from the
prewritten flavor.ThreatClock{Stirring,Alert,Hostile,Siege} pools.
applyBossDefeatThreat is the -20 hook for the eventual combat-completion
wiring (combat→expedition link is a later phase).

Combat-driven modifiers (loud-ability, escape, combat-in-new-room) are
deferred to the same combat-link phase since expeditions don't currently
host their own combat path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 0a3417a341 Adv 2.0 D&D Phase 12 E1e: !camp rough/standard + placement guards
Wires the basic camp surface per gogobee_expedition_system.md §5. Two
camp types ship: rough (any location, +0.5 SU, partial rest) and
standard (cleared room, +1 SU, full long rest). Fortified and base
camps remain explicitly rejected at the surface — those wire up in E2
and E4 respectively, so the command tree is forward-compatible.

Placement validation per §5.2: boss rooms and trap rooms reject; non-
cleared rooms downgrade an intended standard camp to rough. Active-
enemy detection is deferred to E2 (it needs the combat-state hooks
that don't exist on the expedition path yet). Until !advance is wired
into the expedition layer, the player is treated as standing at the
entry room (always cleared), so today's validation effectively only
fires once room context catches up.

Camp-pitch consumes SU immediately and uses flavor.CampEstablished for
the narrative line. !camp break struck and recorded. !camp with no
arg shows help + current camp state.

Tests cover deduction amounts, double-pitch rejection, break, locked-
type rejection (fortified/base), insufficient-supply rejection, and
boss-room placement guard via a synthetic zone run.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 7528d5c239 Adv 2.0 D&D Phase 12 E1d: real-time day cycle (06:00 briefing / 21:00 recap)
Two new tickers in adventure.go startup. Each minute, when UTC clock hits
the trigger hour, walks every active expedition whose last_briefing_at /
last_recap_at is stale relative to today's threshold and fires it. Per-
expedition gating (rather than global db.JobCompleted) means a player who
started mid-day still gets their first briefing the next morning.

Briefing: applies one day's supply burn (harsh mult kicks in when threat
> 60 / siege / temporal stack > 0), advances current_day by one, picks
flavor.MorningBriefing<DayBand> (Day1/Day3/Day7/Day14/Day21 → otherwise
Generic), DMs the §12.1 block, appends a 'briefing' log entry, stamps
last_briefing_at.

Recap: scans today's log entries — boss kill picks
flavor.EveningRecapBossKilled, no combat + no narrative entries picks
EveningRecapNothingHappened, otherwise EveningRecapGeneric. DMs the
§12.2 block (HP-aware close-call detection deferred to E2 since it
needs combat hooks). Stamps last_recap_at.

Tests cover day-band selection, day advance + supply burn (incl. harsh
mult), threshold filtering, recap log entry creation, and boss-kill
override.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 1042e2ab71 Adv 2.0 D&D Phase 12 E1c: !expedition start/status/log/abandon
Command surface for the multi-day expedition layer. Wired in
adventure.go alongside !zone. Subcommands: list (delegates to zone
list), start <zone> [Ns] [Md], status, log, abandon, help. Default
outfit is one standard pack; pack tokens parse as e.g. "2s 1d".

start debits coins via p.euro.Debit, refunds on persistence failure,
and blocks if a !zone single-session run is also active. Status
renders the §12.1 daily briefing block with HP, supplies (current
burn factors in harsh conditions / siege), threat threshold label,
and depletion warning if rationing or worse. log shows the last 5
ExpeditionEntry rows. abandon flips status to 'abandoned'.

Start uses the prewritten flavor.ExpeditionStart pool for the
narrative coda and writes it to the log so !expedition log shows it
back. Tests cover parser combinations (1s default, 3s 1d, error
paths), threshold band edges, the start→status→log→abandon roundtrip,
and the underfunded-start no-debit guarantee.

Day cycle (E1d) and !camp (E1e) wire on top in subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 2413f892e5 Adv 2.0 D&D Phase 12 E1b: supply system (procurement, burn, depletion)
Pure logic per gogobee_expedition_system.md §4. Tier→base SU/day and
harsh-conditions multiplier tables (§4.1). Standard pack 10 SU/50c
(max 3) and deluxe pack 20 SU/90c (max 1) per §4.2. SupplyPurchase
struct with Validate / Total / Cost. Depletion brackets (>25 / 10–25
/ 1–9 / 0) → roll modifier and long-rest gate per §4.3. applyDailyBurn
deducts and clamps; resets the per-day forage flag.

Procurement is exposed as a pure helper here; the !expedition start
flow wires it interactively in E1c.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 9608ce554a Adv 2.0 D&D Phase 12 E1a: Expedition data model + persistence
Lays the multi-day expedition substrate per gogobee_expedition_system.md
§4.4/§5.3/§8.4/§9. Schema dnd_expedition + dnd_expedition_log added in
db.go. Expedition struct, ExpeditionSupplies, CampState, ThreatEvent
shipped in dnd_expedition.go with start/get/scan/abandon/complete,
updateSupplies, updateCamp, applyThreatDelta (clamped + sticky siege),
advanceExpeditionDay, append/recent log helpers. Round-trip + concurrent
rejection + threat clamp/siege + log ordering covered.

E1b (supply procurement) and E1c (commands) wire on top in subsequent
commits; day cycle (E1d) and camp commands (E1e) follow.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 c5defc9b34 Adv 2.0 D&D Phase 11 D11: GMNarrationType coverage test
Lock in the D6 audit conclusion that every GMNarrationType routes to a
non-empty pool for every registered zone. New zones or types added
without wiring their pool now fail TestNarrationCoverage_AllZonesAllTypes
instead of silently rendering "".

Audit confirmed all 12 types × 10 zones already covered (per-zone pools
where they exist, generic fallback otherwise) — no flavor written.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 654bd07b9b Adv 2.0 D&D Phase 11 D10: ASCII !zone map renderer
Replace the flat numbered list in !zone map with a horizontal ASCII
layout — room glyphs joined by ── on top, status markers (✓/▶/·)
underneath, plus a one-line legend. Closes the "ASCII !map renderer"
sub-item of D6 polish from gogobee_dungeon_zones.md §8.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 4638cb83a1 Adv 2.0 D&D Phase 11 D9: !zone lore subcommand
Wires the existing GMLore narration pool (zone-specific LoreLines* +
generic LoreLines fallback, all prewritten) into a !zone lore command.
Deterministic per (run, room) — re-asking in the same room yields the
same line; cross-room calls vary. No mood or state side-effects.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 7bb922ae62 Adv 2.0 D&D Phase 11 D8: mood-banded TwinBee aside
Adds a TwinBee aside on room entry when GM mood sits at the extreme
bands (≤19 hostile, ≥80 effusive). Mid-bands stay silent — the aside is
strictly extra flavor, never replacing core narration. Two new pools
(MoodAsidesHostile, MoodAsidesEffusive) live alongside the existing
TwinBee voice in twinbee_gm_flavor.go. Wired into zoneCmdEnter and
zoneCmdAdvance; the advance path reloads the run so post-combat
nat20/nat1 deltas are reflected before band lookup.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 71594a0d89 Adv 2.0 D&D Phase 11 D7: !zone taunt / !zone compliment
Wires the prewritten flavor.TauntResponses and flavor.ComplimentResponses
pools to the existing MoodEventTaunt (-10) / MoodEventCompliment (+5)
mood deltas. Both subcommands require an active run and report the new
mood band after applying the delta. Deterministic line selection per
(runID, roomIdx) so repeated taunts in the same room are stable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 ebce89b6c6 Adv 2.0 D&D Phase 11 D6: boss phase-two narration
Wires the eight prewritten *PhaseTwoLines pools (Valdris / HollowKing /
Aldric / Thyrak / Ilvaras / Thornmother / Infernax / Belaxath) into
resolveBossRoom. When the combat event log shows enemy HP crossing the
zone's PhaseTwoAt threshold, a TwinBee phase-two callout is injected
between the boss-stat header and the win/loss line. Bosses without a
phase-two transition (Grol, Aboleth) are unaffected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 618f6dc34f Adv 2.0 D&D Phase 11 D5b: Tier 4-5 zone flavor files
Adds zone_underdark, zone_feywild_crossing, zone_dragons_lair, and
zone_abyss_portal flavor files with elite-room intros, boss-entry
pools (where canonical twinbee_gm_flavor.go didn't already ship one),
boss ability callouts, and zone-specific lore. Wires the four pools
into zoneRoomEntryPool / bossEntryPool / bossSignaturePool /
eliteRoomEntryPool / zoneLorePool, and replaces the now-obsolete
"unflavored zone" assertions with Tier 4-5 positive routing tests.

RoomEntryUnderdark, RoomEntryDragonsLair, BossEntryInfernax, and
BossEntryBelaxath are reused verbatim from twinbee_gm_flavor.go per
the canonical-flavor-first rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 807864ac8d Adv 2.0 D&D Phase 11 D5a: Tier 4-5 zones + bestiary
Underdark + Feywild Crossing (T4) and Dragon's Lair + Abyss Portal (T5)
registered. Zone hooks, atmosphere, factions, and boss descriptions
reuse the prewritten flavor in gogobee_dungeon_zones.md §5.

New bestiary entries:
  Underdark: drow, drow_elite_warrior, drow_mage, mind_flayer,
    hook_horror, roper, boss_ilvaras_xunyl
  Feywild: redcap, will_o_wisp, quickling, night_hag, fomorian,
    boss_thornmother (reuses Tier 2 green_hag)
  Dragon's Lair: kobold, guard_drake, kobold_scale_sorcerer,
    dragonborn_cultist, young_red_dragon, boss_infernax
  Abyss Portal: quasit, vrock, hezrou, nalfeshnee, marilith,
    boss_belaxath

Boss-entry pools (BossEntryInfernax, BossEntryBelaxath) and per-zone
RoomEntry pools for Underdark + Dragon's Lair already live in
twinbee_gm_flavor.go from a prior phase and are wired in
dnd_zone_narration.go — no narration switch changes here. Feywild and
Abyss room/boss/elite flavor lands in D5b.

Bumps TestBestiaryByCR cap from CR≤20 to CR≤30 to cover Infernax (CR
24, Ancient Red Dragon). New T4/T5 presence tests added.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 eea3126412 Adv 2.0 D&D Phase 11 D4c: Tier 3 zone flavor files
New additive flavor files (zone_manor_blackspire_flavor.go,
zone_underforge_flavor.go) with elite-room intros, boss ability
callouts, and zone lore. Manor reuses RoomEntryHauntedManor from
twinbee_gm_flavor.go; Underforge had no canonical RoomEntry/BossEntry
pools yet, so both are defined in the new zone file.

  Aldric Blackspire: Multiattack (life drain) / Charm / Children of the
    Night / Mist Form / Phase Two
  Emberlord Thyrak: Molten Strike / Forge Breath / Living Forge /
    Construct Resilience / Phase Two

Wires zoneRoomEntryPool, bossEntryPool, zoneLorePool,
bossSignaturePool, and eliteRoomEntryPool to the new tier-3 pools.
Adds Tier 3 routing tests for boss-entry, elite line, lore, and
room-entry overlays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 8eaada5c1b Adv 2.0 D&D Phase 11 D4b: Tier 3-5 trap catalog
Adds tier3TrapCatalog (Antimagic Field, Necrotic Miasma, Symbol of
Death, Planar Rift) per design doc §6 Tier 3-5 table. Tiers 3, 4,
and 5 share this pool — the doc lumps them together. Antimagic
Field uses a new SuppressesMagic flag (no damage, narrative
suppression beat); the other three deliver dice damage and skip
the unmodelled secondary effects (max-HP debuff, drop-to-0,
teleport) for a later phase.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 61b7fff9e2 Adv 2.0 D&D Phase 11 D4a: Tier 3 zones + bestiary
Haunted Manor of Blackspire + The Underforge registered. New bestiary
entries (poltergeist, banshee, wraith, vampire spawn, revenant,
Aldric Blackspire boss; magmin, azer, salamander, fire elemental,
helmed horror, Emberlord Thyrak boss) per dungeon zones doc §5.
Manor reuses Tier 1 shadow; Underforge reuses Tier 1 flameskull.
Manor Blackspire RoomEntry pool already lives in
twinbee_gm_flavor.go and is wired in dnd_zone_narration.go from a
prior phase, so no new flavor file is needed for that zone in D4a.
L1 tier-gate test bumped 4→6 now that T3 is visible to T1 players.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 df3110e2d7 Adv 2.0 D&D Phase 11 D3c: Tier 2 zone flavor files
New additive flavor files (zone_forest_shadows_flavor.go,
zone_sunken_temple_flavor.go) with elite-room intros, boss ability
callouts, and zone lore. Reuses existing RoomEntryForestShadows and
BossEntryHollowKing from twinbee_gm_flavor.go. The Sunken Temple had
no canonical RoomEntry/BossEntry pools yet, so both are defined in the
new zone file.

  Hollow King: Corrupting Aura / Root Surge / Devour Light / Phase Two
  Dreaming Aboleth: Tentacle Multiattack / Enslave / Mucus Cloud /
    Legendary Actions

Wires zoneRoomEntryPool, bossEntryPool, zoneLorePool,
bossSignaturePool, and eliteRoomEntryPool to the new tier-2 pools.
Updates the D2b "no callout pool" test to use Dragon's Lair (still
unflavored) and adds Tier 2 routing tests for boss-entry, elite line,
lore, and room-entry overlays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 4ef152fbe6 Adv 2.0 D&D Phase 11 D3b: Tier 2 trap catalog
Adds Flame Jet (3d6 fire), Collapsing Ceiling (4d6 bludgeoning), and
Glyph of Warding (5d8 lightning) per design doc §6 Tier 2. Detect DCs
match the table (Perception/Investigation/Arcana 14). Save mechanics
("DEX DC X half") are not modeled — full dice resolve on a failed
detect, matching the Tier 1 convention from D2a.

trapsByTier now dispatches Apprentice → tier2TrapCatalog. Added
deterministic-pick + variety + range tests for the new tier.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 9e43b0b4b1 Adv 2.0 D&D Phase 11 D3a: Tier 2 zones + bestiary
Forest of Shadows + Sunken Temple of Dar'eth registered. New bestiary
entries (dire wolf, bandit captain, owlbear, corrupted dryad, displacer
beast, green hag, kuo-toa + whip, water elemental, merrow, aboleth thrall,
Hollow King boss, Dreaming Aboleth boss) per dungeon zones doc §5. L1
tier-gate test expectation bumped from 2→4 zones now that T2 is visible
to T1 players.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 4a6ddf45ae Adv 2.0 D&D Phase 11 D2b: Tier 1 zone flavor files
New additive flavor files (zone_goblin_warrens_flavor.go,
zone_crypt_valdris_flavor.go) with elite-room intros, boss ability
callouts (Grol: Surprise Attack / Heart of Hruggek / Terrifying Roar;
Valdris: Corrupting Touch / Legendary Resistance / Call of the Grave /
Phase Two), and zone-specific lore deep-dives. Existing
twinbee_gm_flavor.go is untouched per the protected-files contract.

Wires three narration helpers in dnd_zone_narration.go:
  composeBossEntry — boss-entry render plus a one-line ability callout
  eliteRoomEntryLine — elite-room atmospheric prefix
  zoneLorePool — zone-specific !lore pool with generic fallback

Boss-entry callsite in dnd_zone_cmd.go now uses composeBossEntry; the
elite branch of resolveCombatRoom prefixes with the zone's elite line.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 98a9c930fa Adv 2.0 D&D Phase 11 D2a: Tier 1 trap catalog
Replaces the flat-percent HP nick from D1e with the design doc's three
Tier 1 traps (Pit, Tripwire Alarm, Poison Dart). Each trap defines its
own detect skill + DC and damage dice; selection is deterministic per
(runID, roomIdx).

resolveTrapRoom now picks a trap, rolls Perception/Investigation, and
either narrates a clean spot (zero damage) or rolls the trap's dice and
applies HP loss with the existing KO-protection clamp. Tripwire deals
no damage but raises the alarm narratively.

Higher-tier zones still fall through to resolveTrapRoomLegacy until
D3a/D4a fill in their catalogs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 d2a1b79ee3 Adv 2.0 D&D Phase 11 D1e: zone combat + trap + boss resolution
Wires real combat into !zone advance. Each room now resolves through
its own path: Entry is pure flavor, Exploration spawns a SpawnWeight-
biased non-elite from the zone roster, Elite filters to elite-flagged
entries, Trap nicks 8–20% MaxHP scaled by tier (KO-protected), Boss
runs the bestiary entry from zone.Boss with the zone Loot table on
victory. Combat reuses the existing dungeonCombatPhases pipeline with
the player's full D&D layer (class/race/subclass passives, equipment,
HP scaling, armed abilities, pending casts) and persists HP, subclass
state, and CR-weighted XP after each kill.

Mood event triggers fold in: nat-20s/nat-1s scanned from CombatResult
events apply +3/-2 deltas, player_death applies -5 + abandons the run,
zone_complete (already wired in D1d) lands when the boss falls. Loot
drops materialize into adventure_inventory with coin patterns
("coins_2d10x5") expanded into rolled gold-pouch treasure rows and
named items rendered as tier-scaled placeholder treasure (real
equipment-registry wiring is a later content phase).

Updates the D1d mood-on-completion test to drive the persistence layer
directly, since real combat against the L1 Goblin Warrens roster is
non-deterministic.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 9dc0e37340 Adv 2.0 D&D Phase 11 D1d: TwinBee narration + mood triggers
Wires the existing internal/flavor TwinBee GM pools into the !zone
state machine. Adds GMNarrationType, mood band derivation, the §3.2
mood-event delta table, and ±2/hr passive decay toward 50. Zone enter
narrates the entry room; advance narrates each subsequent room (boss
rooms use the named BossEntry* pools, completion uses ZoneComplete
and applies MoodEventZoneComplete). Line selection is deterministic
on (runID, roomIdx) so repeat reads render the same prose.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 a1738e4a22 Adv 2.0 D&D Phase 11 D1c: !zone command surface
dnd_zone_cmd.go ships handleDnDZoneCmd with subdispatch over the D1b
state machine: list, enter <id|#|name>, status, map, advance, abandon.
Bare !zone (or !zone list) shows zones available at the player's level
with !zone enter hints; an active run is flagged inline so the player
can't lose track of it.

Enter resolves zone input by ID, 1-based list index, or display name
(case/whitespace-insensitive, "the " prefix tolerated), and rejects
unknown / tier-locked zones plus duplicate concurrent runs without
clobbering the active run. Status reports current room / cleared /
loot / GM mood with a band label (effusive/friendly/neutral/grumpy/
hostile per design doc §3.2). Map prints an ASCII layout with ✓ for
cleared rooms and ▶ for the current room. Advance is a deliberate D1c
stub that calls markRoomCleared and reports the next room — combat
resolution per room wires in D1e. Abandon delegates to abandonZoneRun
and is idempotent (no-active surfaces a friendly DM, not an error).

Wired into adventure.go OnMessage right after the existing D&D
commands. 10 new tests cover dispatch (no-char nudge, list with char,
enter by ID and by index, unknown-zone rejection, duplicate-run guard,
status/map with no run, advance moves the room cursor, abandon clears
active and is idempotent), resolveZoneInput across ID/display/index/
case forms, and gmMoodLabel band edges. Full repo test suite green
(modulo a pre-existing RNG flake in
TestSimulateCombat_FirstAttackBonusImprovesEarlyHits unrelated to this
change).

TwinBee narration + mood triggers land in D1d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 ee3b2977aa Adv 2.0 D&D Phase 11 D1b: DungeonRun state machine + dnd_zone_run schema
dnd_zone_run table (run_id, zone_id, room_seq_json, rooms_cleared,
boss_defeated, abandoned, loot_collected, gm_mood, started_at,
last_action_at, completed_at) added to db.go schema with active-run
index.

dnd_zone_run.go ships RoomType (entry|exploration|trap|elite|boss),
DungeonRun struct, generateRoomSequence (Entry → ExplorationxN1 → Trap
→ ExplorationxN2 → Elite → Boss within zone's [MinRooms, MaxRooms]),
and persistence helpers: startZoneRun (gates on level tier + active-run
exclusivity), getActiveZoneRun, getZoneRun, markRoomCleared
(advances and auto-completes on boss kill), abandonZoneRun,
adjustGMMood (clamped to [0,100]), addLoot.

8 new tests covering room-sequence shape (entry first, boss last,
exactly one trap/elite, ≥2 explorations), happy-path start,
concurrent-run rejection, unknown-zone rejection, full advance-to-boss
flow with completion, abandon idempotency, GM mood clamping at both
bounds, and loot accumulation order. Full repo test suite green.

Boss-room behavior, !zone command surface, and TwinBee narration
arrive in D1c/D1d.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 ba2a2b5e90 Adv 2.0 D&D Phase 11 D1a: zone registry + Tier 1 zones
Zone Infrastructure SUB-A: ZoneDefinition / ZoneTier / ZoneEnemy /
ZoneBoss / ZoneLootEntry types and dndZoneRegistry. Tier 1 zones
populated per gogobee_dungeon_zones.md §5: Goblin Warrens (T1, L1-3,
boss Grol the Unbroken CR3) and Crypt of Valdris (T1, L1-3, boss
Valdris the Unburied CR5, phase 2 at 50% HP).

Helpers: getZone, zonesForLevel (2-tier-above ceiling), zonesByTier,
allZones.

Bestiary additions for Tier 1 enemy rosters: goblin_sneak,
goblin_archer, hobgoblin_grunt, worg, goblin_shaman,
hobgoblin_warchief, zombie, shadow, specter, wight, flameskull, plus
the two zone bosses. Zombie AC bumped 8->10 to satisfy engine
TestBestiaryAllWellFormed minimum.

7 new tests covering registry presence, bestiary-ref resolution, boss
CR floor, level-tier gating, loot-chance bounds, room-count sanity,
elite flag presence, and tier->level-range alignment. Full plugin
suite green.

Also marks SUB3 done in gogobee_subclass_system.md (all 15 subclasses
through L15 shipped across SUB3a-d).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 feccd61614 Adv 2.0 D&D Phase 10 SUB3d: Ranger L10/L15
Hunter / Beast Master / Gloom Stalker L10/L15 capstones — last subclass
batch of Phase 10 SUB3.

Hunter L10 Multiattack (Volley/Whirlwind): +15% DamageBonus, collapsing
the AoE extra-target throughput onto the single 1v1 target.
Hunter L15 Superior Hunter's Defense: ~15% incoming damage reduction
(Uncanny Dodge proxy — most generally applicable of the three picks).

Beast Master L10 Share Spells: pet floor lifts to PetAttackProc 0.45 /
PetAttackDmg 6 + lvl/2, plus +5% DamageBonus rider for the Ranger's own
buffed swings. L15 Superior Bond: pet floor 0.55 proc / 8 + lvl/2 dmg
plus 0.92 DamageReduct ("body-blocks more reliably"). Both still use
the existing max-style floors so a stronger external pet (legacy
adventure pet, etc.) is preserved.

Gloom Stalker L10 Stalker's Flurry: +12% DamageBonus representing
recovered misses across the fight (L7 already covers the first-attack
re-roll via AssassinateAdvantage). L15 Shadowy Dodge: ~10% incoming
damage reduction (reaction-imposed disadvantage proxy).

11 new tests; ranger suite + full plugin suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:22 -07:00
prosolisandClaude Opus 4.7 83654eadc7 Adv 2.0 D&D Phase 10 SUB3c: Cleric L10/L15
Life/War/Trickery Domain L10/L15 capstones.

L10 Divine Strike (all 3 subclasses): +4 flat per weapon hit, scaling
to +9 at L14. New CombatModifiers.DivineStrikePerHit channel, gated on
Weapon != nil since 5e specs "weapon hit". Damage type (radiant/weapon/
poison) varies by domain but isn't tracked by the engine.

Life L15 Supreme Healing: heal-spell dice resolve at max instead of
rolled. Wired through resolveHealOutOfCombat via lifeDomainSupremeHealing
helper.

War L15 Avatar of Battle: 5e physical resistance vs. non-magical
weapons → flat 0.80 DamageReduct (softer than full 50% to account for
elemental hits).

Trickery L15 Improved Duplicity: 5e duplicates grant ally-flank
advantage; no allies in 1v1, so proxied passively as +1 SporeCloud
round and +5% damage (duplicates flicker around the foe).

10 new tests; cleric-suite green. Pre-existing rng flakes
(TestOrcRageFiresOnLowHP, TestSimulateCombat_FirstAttackBonus...) are
unrelated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 fcd067b3c4 Adv 2.0 D&D Phase 10 SUB3b-i: Mage L10/L15
Evocation L10 Overchannel maximizes leveled-spell dice (slot 1-5),
proxied as +50% SpellPreDamage on the queued cast. Self-damage drawback
omitted — our model queues at most one cast/fight, so the "first per
rest" exemption applies. L15 Sculptural Mastery skipped: AoE-only, no
allies in 1v1 (same shape as SUB2b skips).

Abjuration L10 Spell Resistance → DamageReduct *= 0.92 (8% generic DR
proxy; the saves-advantage half is inert since enemies rarely force
saves). L15 Spell Reflection → ReflectNext += 0.30, riding the existing
reflect channel since Counterspell is reaction-deferred to Phase 11.

Necromancy L10 Command Undead has no surface in 1v1 (no thrall/ally
system), re-fluffed as deeper authority over death amplifying the
harvest itself: GrimHarvest multipliers tick up by 1 (2x→3x non-necrotic,
3x→4x necrotic). L15 Improved Undead Thralls proxied as a permanent
skeletal minion via PetAttack channel (proc 0.30, dmg 6 + CON mod);
respects existing higher-pet dmg/proc.

11 new tests; full plugin suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 a9ee7d0f70 Adv 2.0 D&D Phase 10 SUB3a-iii: Thief L10/L15
L10 Use Magic Device: no scroll/wand item system to gate, so we proxy the
"now allowed to use magic items" fantasy as a small per-fight wand zap
(FlatDmgStart += 6) plus a +5% DamageBonus representing better tactical use
of magical implements.

L15 Thief's Reflexes: engine has no "extra turn round 1" primitive, so we
ride the AssassinateBonusDmg + AssassinateAdvantage channel (same one Gloom
Stalker's Dread Ambusher uses for bonus opener damage). Flat +8 on first
hit approximates one extra turn's hit; advantage on first attack covers
the "you swing first" piece.

3 new tests cover the L10 thresholds, L9 gate, and the L15 layer stacking
on top of L10. Full suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 64c70dda27 Adv 2.0 D&D Phase 10 SUB3a-ii: Battle Master L10/L15
L10 Improved Combat Superiority — superiority dice grow d8 → d10. The
two scalar maneuvers (Precision Attack +4→+5; Rally heal +4→+5) bump in
their Apply hooks; Trip Attack has no scalar to scale.

L15 Relentless — 5e regenerates one die when initiative is rolled with
an empty pool. Proxied as +1 max pool size (4→5), which gives the same
"always at least one die per encounter" guarantee across the long-rest
cycle without introducing per-fight regen state.

subclassResourceMax now takes level; initSubclassResources upserts the
max (and bumps current by the delta) so existing characters grow on
level-up. Hooked into the dnd_xp level-up loop so Battle Masters
crossing L15 see the new die without waiting for a long rest. 4 new
tests; no regression in subclass/resource/level-up paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 97c2c37396 Adv 2.0 D&D Phase 10 SUB3a-i: Champion + Berserker L10/L15
Champion L10 Additional Fighting Style → +1 AC + 10% damage (Defense +
Dueling collapse, since one-shot combat can't surface a style picker).
Champion L15 Superior Critical → CritThreshold drops 19 → 18.
Berserker L10 Intimidating Presence → 2 rounds of SporeCloud miss
chance (Action-based fright proxied via the existing fog-cloud channel).
Berserker L15 Retaliation → +15% damage (reaction-strike average uplift,
since the engine has no reaction primitive).

6 new tests; full plugin suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 b56e53300e Adv 2.0 D&D Phase 10 SUB2d: Ranger subclasses (Hunter/Beast Master/Gloom Stalker)
L5/L7 abilities for the three Ranger subclasses, plus Gloom Stalker
Stealth advantage from Umbral Sight. Beast Master pet floors use a
max-style merge so the legacy adventure-pet path can still upgrade.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 a733d52166 Adv 2.0 D&D Phase 10 SUB2c: Cleric subclasses (Life/War/Trickery)
Channel Divinity resource pool (2/long-rest) shared across all three
Cleric domains. One armed expression per fight, mirroring Battle Master.

Life Domain
  - L5 Disciple of Life: heal spells restore +(2 + slot level) extra HP
    (lifeDomainHealBonus, hooked into resolveHealOutOfCombat).
  - L5 Preserve Life: Channel Divinity heal — sets HealItem to 5×Cleric
    level, capped at HPMax/2; fires when player drops below 50%.
  - L7 Blessed Healer skipped — no allies in 1v1 combat to "heal an ally".

War Domain
  - L5 War Priest passive: +1 attack bonus + 0.15 DamageBonus, proxy for
    bonus-action extra-attack throughput one-shot combat can't model
    discretely.
  - L5 Guided Strike: Channel Divinity → +10 to first attack roll
    (FirstAttackBonus).
  - L7 War God's Blessing skipped — ally reaction, no allies.

Trickery Domain
  - L5 Invoke Duplicity: Channel Divinity → AssassinateAdvantage (reroll
    first miss) + 0.10 DamageBonus, proxy for "advantage on attacks vs.
    creatures adjacent to the duplicate".
  - L7 Cloak of Shadows passive: +2 SporeCloud rounds (15% enemy miss)
    proxy for the brief invisibility window.
  - L5 Blessing of the Trickster skipped — non-combat Stealth flavor.

15 new tests, all green. Same pre-existing
TestSimulateCombat_FirstAttackBonusImprovesEarlyHits flake.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 596e17dc73 Adv 2.0 D&D Phase 10 SUB2b: Mage subclasses (Evocation/Abjuration/Necromancy)
Evocation L7 Empowered Evocation adds INT mod (min +1) to Mage evocation
spell damage. Abjuration L5 Arcane Ward grants a 2×level HP buffer
absorbed before player HP, +prof at L7. Necromancy L5 Grim Harvest heals
2× spell level on spell-kill (3× necrotic) via post-combat hook.

Sculpt Spells / Potent Cantrip / Inured to Undeath skipped — no allies,
save-half is already uniform, and no necrotic enemy damage to resist.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 4a5159ee14 Adv 2.0 D&D Phase 10 SUB2-AT: Arcane Trickster spellcasting
Slots into the Phase 9 spell pipeline so an L5+ Rogue who picks Arcane
Trickster becomes a third-caster on the Mage list, INT-based.

  - isSpellcaster(c) gate: full casters by class plus AT Rogue at L5+.
    !cast / !spells now consult this; the cast class-gate also accepts
    Mage-tagged spells when the player is an AT Rogue.
  - subclassSpellSlots() / slotsForCharacter() / setSpellSlotsForCharacter()
    layer subclass slots on top of the class baseline. AT pool: 3 L1 at
    L5–6, 4 L1 + 2 L2 at L7+, +2 L3 at L13+ (third-caster table).
  - spellcastingMod returns INT for AT Rogue.
  - L7 Magical Ambush proxy: spellSaveDC bumps +2 once Rogue+AT hits L7,
    standing in for "targets have disadvantage when casting from hidden"
    since the one-shot combat layer doesn't model hidden state.
  - ensureSpellsForCharacter bootstraps a Mage-list starter spellbook
    (minor_illusion, shocking_grasp, magic_missile, shield, sleep at L5;
    mirror_image + misty_step at L7+; hypnotic_pattern at L13+) and
    refreshes the slot pool on level/subclass change.
  - applySubclassChoice runs ensureSpellsForCharacter for AT so the
    spellbook is provisioned at selection time (idempotent).
  - !cast clamps upcasts to highestAvailableSlotForChar when AT, since
    third-caster's slot ceiling lags behind the spell list.

Tests added: AT spellcaster gate (L5 cutoff, non-AT Rogue rejected), INT
mod, subclass slot pool by tier, L7 ambush DC bump, ensureSpells default
list (all on Mage list, slots correct), end-to-end !cast queues
magic_missile for AT Rogue, !cast rejects plain Thief Rogue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 c8fac7ffd9 Adv 2.0 D&D Phase 10 SUB2a-ii: Battle Master + Assassin L5/L7
Battle Master:
  - New "superiority" resource pool (4/long-rest at L5), provisioned via
    initSubclassResources at !subclass selection.
  - Three armed maneuvers fueled by superiority dice:
    * Precision Attack — +d8 (≈+4) to first attack roll
    * Tripping Attack  — enemy skips first attack (reuses Phase 9
      SpellEnemySkipFirst)
    * Rally            — +(d8 + CHA) HealItem at <50% HP
  - L7 Know Your Enemy proxied as +1 AttackBonus passive.

Assassin:
  - L5 Assassinate: AssassinateAdvantage (re-roll first miss, take better
    of two d20s) + AssassinateBonusDmg = level (5 at L5) stacked on top
    of the Rogue's existing Sneak Attack auto-crit.
  - L7 Impostor bumps the bonus damage by +3.
  - L5 Infiltration Expertise → +5 Deception; L7 Impostor → +10.

Engine:
  - CombatModifiers gains FirstAttackBonus, AssassinateAdvantage,
    AssassinateBonusDmg.
  - resolvePlayerAttack consumes each on the first attack only via new
    combatState one-shot flags.

Tests added: 14 covering passive gating (BM L7+, Assassin L5/L7),
maneuver Apply flags, superiority pool init for BM only, !arm gating
across class/subclass for precision_attack, Deception bonus tiers,
statistical lift from FirstAttackBonus and AssassinateBonusDmg in
SimulateCombat.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolis 437460c9b2 Adv 2.0 D&D Phase 10 SUB2a-i: Champion + Berserker + Thief L5/L7
First mechanical slice of the subclass system. Each subclass gets its L5
and L7 abilities wired through the existing combat engine + skill check
substrate; no new resource pools introduced.

Champion (L5/L7):
- Improved Critical: new Mods.CritThreshold lowers crit floor to nat 19+
  in resolvePlayerAttack. Default 20 preserved for non-Champions.
- Remarkable Athlete: +½ proficiency bonus to STR/DEX/CON skill checks
  via subclassSkillBonus, layered on raceSkillBonus.

Berserker (L5/L7):
- Rage: new !arm-able active ability gated to Berserker subclass via
  DnDAbility.Subclass field. Reuses the Fighter stamina pool (3/long-rest
  matches 5e's rage uses). On fire: +2 flat damage per hit, halve incoming
  weapon damage, +50% damage approximation for Frenzy's bonus-attack-per-
  turn (one-shot combat can't model that literally).
- Frenzy exhaustion: new exhaustion column on dnd_character; incremented
  by persistDnDPostCombatSubclass after any rage'd combat. Decremented by
  one per long rest.
- Mindless Rage: documented; charm/frighten conditions don't exist in the
  combat engine yet, so no functional effect until P11 zone bosses land.

Thief (L5/L7):
- Sleight of Hand added to dndSkillTable.
- Fast Hands: +5 to Sleight of Hand checks via subclassSkillBonus.
- Supreme Sneak: advantage on Stealth (best of two d20s) via new
  subclassSkillAdvantage hook in performSkillCheck. 5e's half-speed gate
  dropped — we don't track movement speed.

Plumbing:
- applySubclassPassives layered after applyClassPassives in both arena
  and dungeon combat bridges.
- DnDAbility.Subclass enables per-subclass gating; characterActiveAbilities
  filters !arm and !abilities lists by both class and subclass.
- Existing !respec full-wipe also clears Exhaustion.

Tests: Champion CritThreshold gating across L4/L5/no-subclass; rage Apply
flag-set; rage probabilistic win-rate lift vs tougher enemy; exhaustion
increment only on raged combats; long-rest decrement; Champion bonus on
STR/DEX/CON only and only at L7+; Thief Fast Hands SoH-only; Thief
Supreme Sneak L7+ Stealth-only; statistical advantage roll lifts Thief
stealth average by ≥2 vs plain Rogue; !arm rage subclass gating; schema
roundtrip; characterActiveAbilities visibility.
2026-05-09 14:25:21 -07:00
prosolis 1ee4276bcd Adv 2.0 D&D Phase 10 SUB1: subclass selection system
Implements the identity layer of gogobee_subclass_system.md: 15 subclasses
(3 per class) selectable at L5 via !subclass.

- Schema: new subclass + last_subclass_respec_at columns on dnd_character.
- Registry (dnd_subclass.go): 15 entries with class, display, tagline, and
  TwinBee flavor line; helpers for prompt rendering and input resolution
  (number, id, or display-name; case/space/hyphen-insensitive).
- !subclass command: bare form prints prompt or current; !subclass <name>
  selects (free first time) or changes (500 euros + 30-day cooldown via
  separate timestamp from the full !respec wipe). Save-then-debit ordering
  mirrors !respec audit fix B.
- Level-up DM at L5+ now embeds the real selection prompt instead of the
  Phase 9 placeholder; suppressed once a subclass is set.
- Sheet shows the subclass below the class line, or nudges !subclass when
  unchosen at L5+.
- Existing !respec full-wipe also clears subclass + cooldown timestamp.

Mechanical effects of L5/7/10/15 subclass abilities deferred to SUB2/SUB3.
2026-05-09 14:25:21 -07:00
prosolis b53e516bf0 Adv 2.0 D&D Phase 9 SP4: mage spellbook cap + level-up nudge
- Enforce per-level Mage known-spell cap in handleSpellsLearn; surface
  spellbook budget in renderSpellsList.
- Mage level-up DM now nudges with "Spells available to learn: N" via
  extracted buildLevelUpMessage.
- Extract halveSavedDamage and enemySpellSaveMod for clarity; document
  single-target AoE limitation in applySpellDamageSave.
- Add tests: mage learn cap, prepare flow, AoE behavior, spell save
  rounding, spells migration.
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 01c70f5297 Adv 2.0 D&D Phase 9 SP3: wire pending spells into combat
applyPendingCast resolves c.PendingCast against the upcoming fight before
SimulateCombat. Damage spells (Fire Bolt, Burning Hands, Magic Missile,
Fireball, etc.) emit a pre-combat spell_cast event via new
CombatModifiers.SpellPreDamage{,Desc}. Control spells (Hold Person, Sleep,
Command) set SpellEnemySkipFirst so the engine skips the enemy's round-1
attack with a spell_held event; Hold-family also primes AutoCritFirst.
Buffs (Mage Armor, Bless, Hunter's Mark, Shield of Faith, Aid, Spiritual
Weapon, Mirror Image, Greater Invisibility) fold into stats/mods directly.

Concentration-on-damage break is left for Phase 11 (turn-based bosses);
ConcentrationSpell persists across fights until manually dropped.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 9e1a1f606c Adv 2.0 D&D layer: Phases 1-8 + Phase 9 SP1+SP2
Combines all uncommitted D&D work into one checkpoint:

Phases 1-8 (per gogobee_dnd_session_summary.md):
- Race/class/stats system, !setup flow, !sheet, !respec
- d20-vs-AC combat with race/class passives, auto-migration
- XP/level-up, skill checks, NPC refund hooks
- Equipment slot mapping, rarity, !rest short/long
- Pre-arm active abilities, resource pools
- Bestiary, !roll/!stats/!level, onboarding DM
- Audit fixes A-I, flavor wiring under internal/flavor/
- Phase 8 weapon dice + armor AC tables (gogobee_equipment_appendix)

Phase 9 SP1 — spell system foundations:
- 79 SpellDefinitions (76 in-scope + 3 reaction-deferred)
- dnd_known_spells, dnd_spell_slots tables
- pending_cast / concentration_* columns on dnd_character
- Slot tables for Mage/Cleric/Ranger, DC + attack-bonus math
- Long-rest refreshes slots; respec wipes spell state
- Auto-grant default known list on !setup confirm and auto-migration

Phase 9 SP2 — !cast / !spells / !prepare commands:
- Out-of-combat HEAL and UTILITY resolve immediately
- Damage/control/buff queue as pending_cast for next combat
- Audit-style save-then-debit, slot refund on save failure
- !cast --drop, concentration supersession, prep-cap enforcement
- Reaction spells refused with Phase 11 note

SP3 (combat-time resolution of pending_cast) and SP4 (full prep/learn
tests) are next. Build clean, full test suite green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 8e0fe0230c Death source tracking + combat threat/jitter fixes
- Adventure characters now record DeathSource ("adventure"|"arena") and
  DeathLocation when killed. Threaded through Kill() and transitionDeath.
  Daily report uses the recorded death location instead of misattributing
  arena deaths to the day's adventure log: when DeathSource != "adventure",
  the adventure block shows the alive icon and a separate "Later fell in
  {location}." line. Standout-loss flavor uses DeathLocation. Empty
  DeathSource (legacy rows) falls back to current behavior.
- calcDamage applies a ±15% per-hit jitter, so successive hits in a phase
  no longer produce identical numbers. Previously every hit in a phase was
  flat (e.g. 17, 17, 17, 17) and mirror-symmetric between player/enemy,
  reading as scripted.
- assessThreat rewritten to use the engine's actual penetration formula:
  damage-per-round each direction, then rounds-to-kill ratio. The old
  additive HP+Attack*3 model ignored Defense, so even fights could be
  rated trivial — players died after consumables were skipped with the
  "threat assessed as manageable" message.
- SelectConsumables never skips on trivial when arenaRound > 0. Arena
  losses cost real money and equipment, so the cost of a wrong assessment
  is too high to gamble on; adventure-side fights still skip trivial
  threats to avoid wasting items on chump enemies.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolisandClaude Opus 4.7 a00987e75c Coop lockout fix, holidays full list, !fx convert, lottery cleanup
- Co-op: when a run wipes or completes, clear the 99-sentinel combat
  lock for participants and pin combat_actions_used to maxCombatActions.
  Previously the lock only released on the next midnight reset, so
  members were stuck unable to combat for the rest of the day after
  their run ended.
- Holidays: !holidays now prints the full list. Previously it called
  the same featured-only formatter as TwinBee's morning auto-post,
  so users saw the same truncated message twice with a misleading
  "Use !holidays for the full list" hint that pointed at itself.
- Forex: add currency conversion. Auto-detects when the first arg is
  numeric (`!fx 1500 USD EUR`) and also accepts an explicit `convert`
  subcommand. Tolerant parsing: "to"/"into"/"in"/"=" filters, comma
  and k/m suffix support, and any of `1500 USD EUR`, `1500 USD/EUR`,
  `USD/EUR 1500` orderings. Includes parser + comma-formatter tests.
- Lottery: drop 2-match prize tier. At 1/16 odds × €25 it was +EV per
  ticket on its own and bled the community pot (~€775/wk recently),
  preventing jackpot rollover from accumulating. Removed from scoring,
  payout loops, draw announcement, !lottery odds, and !lottery history.
  Match2Winners DB column kept (history rows preserved); always written
  as 0 going forward.
- Lottery: shift week boundary so Sat/Sun return next Monday. Friday's
  23:59 draw used to leave players locked at their 100-ticket cap until
  next Monday rolled in; now the cap resets immediately after the draw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 14:25:21 -07:00
prosolis 4a4ef95d65 Merge pull request #9 from prosolis/holdem-tips-validation
Crafting feedback loop + consumable protection + housekeeping
2026-04-30 07:50:24 -07:00
prosolisandClaude Opus 4.7 c66c32e698 .gitignore: skip cmd/ binaries (gensolver, holdem-train)
Both are 16MB Go binaries built from cmd/gensolver and cmd/holdem-train.
They were showing up as untracked on every status check. Also drops a
stray 0-byte internal/db/gogobee.db that someone created at the wrong
path (real DB lives in data/, already gitignored).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 07:49:40 -07:00
prosolisandClaude Opus 4.7 d9ac998458 Adventure: surface crafts in !adventure status + document crafting
- Status sheet now shows lifetime craft count + a hint pointing at
  !adventure recipes (only displayed once the player has crafted at
  least once — keeps the line absent for non-foragers).
- README gains a Crafting subsection documenting the auto-craft flow,
  XP rewards, level gates, the recipes command, and the consumable
  protection (Robbie + sell-all both skip them).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:26:06 -07:00
prosolisandClaude Opus 4.7 0bebcb56cd Adventure: !adventure recipes — list known crafting recipes
Players had no way to see what they could craft without grinding
ingredients and watching combat narrative. New command lists every recipe
unlocked at the player's current Foraging level, grouped by tier, with:

- Result name + ingredient list (so players know what to gather)
- Per-recipe success rate at the player's current level
- Locked-recipe count + next-unlock threshold
- Max auto-crafts per combat (1 + (level-10)/10)

Sub-Foraging-10 players see only the unlock hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:23:53 -07:00
prosolisandClaude Opus 4.7 8e3b8377c0 Adventure crafting: grant foraging XP per attempt, track lifetime crafts
Crafting was a downstream consumer of Foraging skill but never gave any
XP back, so the feedback loop was one-directional — only gathering
contributed to the level that gates better recipes.

Now: each auto-craft attempt grants Foraging XP (success > failure).
Per-tier values:
  tier 1:  +12 / +3
  tier 2:  +25 / +5
  tier 3:  +40 / +8
  tier 4:  +60 / +12
  tier 5:  +90 / +18

Successful T1 ≈ 30% of a Foraging Success haul; T5 ≈ 40%. Failures get a
small consolation grant — the attempt still produced practical knowledge
(and lost ingredients).

Schema: adventure_characters.crafts_succeeded INT for lifetime tracking.
Migration entry included.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:19:06 -07:00
prosolisandClaude Opus 4.7 671773abd5 Adventure: protect consumables from Robbie and 'sell all'
Consumables (auto-crafted from foraged ingredients or dropped from T2+
activities) are a curated stockpile — selling them should be an explicit
choice, not a side effect.

Two surfaces tightened:
- Robbie's qualifying-items filter now skips Type == "consumable" the
  same way it skips Arena gear and cards.
- !adventure sell all leaves consumables in inventory and reports them
  in the kept-items tally. The merchant message points players at
  !adventure sell <name> for explicit consumable sales.

!adventure sell <name> still works fine on consumables — that path is
already explicit by definition.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 23:13:09 -07:00
prosolis 5fadfb255f Merge pull request #8 from prosolis/holdem-tips-validation
Co-op dungeons + audit fixes + prod-running adventure tweaks
2026-04-28 22:41:14 -07:00
prosolisandClaude Opus 4.7 e22d4f8362 Hold'em: route sender-private replies to DM origin + jitter broadcasts
Two changes that were running in prod but hadn't been committed:

1. Sender-private DM routing. When a player invokes a holdem command from
   their DM, the dispatcher rewrites ctx.RoomID to the game room so
   actions affect the right table. But sender-private replies (errors,
   help, status, "unknown command") would then post to the public games
   room instead of back to the DM that asked. Now: the dispatcher saves
   ctx.OriginRoomID = the DM, and a new reply() helper routes sender-
   private messages to OriginRoomID when set, falling back to in-thread
   reply otherwise. (OriginRoomID was added to MessageContext earlier.)

2. broadcastDM jitter. 150-400ms sleep between sends to avoid Matrix
   rate-limit bursts when a game has 4+ players each in their own DM.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:37:19 -07:00
prosolisandClaude Opus 4.7 4826b5e466 Adventure: scale random-event gold rewards 100× to match current economy
Random event gold ranges (€20-250) were authored against an earlier
economy and have been near-trivial against current per-action haul values
(thousands at higher tiers). Scaled the GoldMin/GoldMax across all event
entries by 100× so the rewards feel like real moments instead of
rounding errors.

Pure flavor-table numeric rebalance — no logic changes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:37:04 -07:00
prosolisandClaude Opus 4.7 7878ce0999 Adventure: defer random-event roll to 1-3h after a player acts
Previous scheduler built one fixed roll-minute per player per UTC day in
the 10:00-16:00 window, regardless of when (or whether) the player
actually played. Players who acted late in the day might miss their roll
entirely; players who acted early still had to wait for an arbitrary
mid-day window.

New scheduler:
- Each ticker minute, scan for players who have acted today but don't
  yet have a scheduled roll. Assign each a one-shot roll-minute 60-180
  minutes in the future (capped to 23:50 UTC).
- At the assigned minute, fire the 0.5% trigger roll. Mark advEventRolled
  so it won't fire again that day.
- New day rebuilds both maps fresh.

Result: events trigger relative to the player's actual activity, not
clock time. Late-day actors get their chance; players who skip a day
don't get a roll.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:36:52 -07:00
prosolisandClaude Opus 4.7 6eadb09c1e Adventure: extend NPC encounter expiry to end of UTC day
NPC encounter pending interactions were using advDMResponseWindow
(the short DM-reply window), so a player who didn't get to their DMs
quickly would lose the encounter prompt. Bumped to "until midnight UTC"
so the encounter is available throughout the day the NPC arrived.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:36:36 -07:00
prosolisandClaude Opus 4.7 d93dc4faa4 Adventure: skip overlevel penalty when no higher tier is accessible
Previously, advOverlevelMultiplier penalized every action where the
player's effective level exceeded the location's MinLevel by 4+, with
no escape valve. That punished players grinding at their max-available
tier (e.g., a level 50 character at the highest mining location they
qualify for, with nothing higher unlocked).

Now: the multiplier short-circuits to 1.0 when no accessible higher-tier
location of the same activity exists for that player. Signature changed
from (effectiveLevel, minLevel) to (effectiveLevel, *AdvLocation) so the
helper can scan the activity's location list. combat_bridge.go updated
to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:36:17 -07:00
prosolisandClaude Opus 4.7 07ca5288c3 Coop: stack same-type gifts on the same day into a single vote post
Multiple baskets (or multiple mimics) sent during the same day now share
one game-room post, one vote, one resolution. First-in becomes the stack
"lead"; subsequent same-type sends become followers that inherit the
lead's deadline and votes.

Behavior:
- Send a basket → new lead, new post, 6h timer starts
- Send another basket within the window → silently joins the stack,
  edits lead's post to bump count
- At stack size 2, TwinBee adds a "this gift looks REALLY special" line
  (one of 5 variants picked deterministically by lead id). No further
  escalation as the stack grows
- One !coop giftvote on the lead's id covers the whole stack
- Resolution applies the same outcome to every gift in the stack;
  modifier is N × ±6
- End-of-run gift log groups by stack with all senders attributed

Schema: coop_dungeon_gifts.stack_lead_id INTEGER (NULL for lead/standalone,
otherwise points at lead's id). Migration entry included.

Why first-in deadline (vs extending on each follower): exploitability.
Saboteurs could spam-extend a stack to delay resolution. Locked deadline
keeps senders honest about timing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 22:28:23 -07:00
prosolisandClaude Opus 4.7 48e5000745 Coop: lock combat actions for the full duration of an active run
Players in a co-op were getting their combat action back every midnight
reset and offered the dungeon option in the morning DM. Per spec they
should be "off in the Co-op" and unable to also solo-grind dungeons.

Fix at the DB layer (so all CanDoCombat() paths agree):
- New helper lockCoopCombatActions() sets combat_actions_used=99 for any
  user in an active coop run. Called immediately after every
  resetAllAdvDailyActions() — at startup and at midnight.
- Render layer: morning DM now shows "combat locked (in Co-op #N, day
  X/Y)" and replaces the dungeon section with an explanatory line
  pointing them at !coop status.

Combat returns to normal automatically once the run completes/wipes —
the next midnight reset finds status != 'active' and skips the lock.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:38:00 -07:00
prosolisandClaude Opus 4.7 c060e13b41 Coop: surface per-gift countdowns in !coop status
Each gift now has its own 6h voting window, but the only places the
remaining time was visible were the original arrival post and any
edit-triggered re-render after a vote. Mid-window the displayed
countdown was stale.

!coop status now lists pending gifts (anything with vote_result still
NULL on this run) with current open/leave tally and a fresh per-gift
countdown derived from expires_at. The party can poll this any time to
see what's about to close.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 20:20:00 -07:00
prosolisandClaude Opus 4.7 b7077eeea1 Coop: substitute {count} and {leader} placeholders in gift flavor
The TwinBeeGiftArrival pool entries each end with a templated footer
authored to the spec format ("Open: {count} · Leave it: {count}\nMajority
rules. Ties go to {leader}."), but the placeholders were never being
substituted — players saw the literal "{count}" and "{leader}" strings.

Substitute in renderCoopGiftPost: ordered Replace for the two {count}
positions (opens, then leaves), ReplaceAll for {leader}. Also dropped
the redundant "Current votes:" line my code was appending — the flavor's
own tally line covers it after substitution.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 18:14:49 -07:00
prosolisandClaude Opus 4.7 9f094549b7 Coop: per-gift voting timers (6h) — gifts resolve throughout the day
Replace the single daily-tick gift resolution with independent per-gift
expiries. Each gift now has its own 6h voting window; once that elapses,
the votes are tallied, the sender gets DM feedback, and the live game-room
post is edited to reveal the type and resolved modifier. The modifier
sits on the run waiting for the next floor resolution to merge it in.

Effect:
- Gifts fire continuously throughout the day rather than all at once at
  08:00 UTC, surfacing sender activity in real time.
- Senders get fast feedback (~6h instead of waiting for the next daily
  tick).
- Floor resolution becomes purely "sum pending modifiers from already-
  resolved gifts" — cleaner separation of concerns.
- Atomic per-gift apply via markCoopGiftApplied (UPDATE...WHERE applied_at
  IS NULL) prevents double-application on retry.

Schema additions:
- coop_dungeon_gifts.expires_at (when voting closes)
- coop_dungeon_gifts.applied_at (when modifier merged into a floor)
- Migration entries provided.

Defensive backstop: floor resolution still force-resolves any gifts whose
expiry was missed (e.g., bot down during the timer window).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:55:14 -07:00
prosolisandClaude Opus 4.7 4bc541ca85 Coop: shorten first-day window — require 12h since lock, not date change
Previous guard skipped resolution if lock_date == today_date, pushing
first-day windows to 24-32h regardless of when in the UTC day the lock
fired. With per-minute lock checks, the original same-tick race no longer
exists, so the date guard was just adding latency.

New guard: at least 12h since lock. Late-night locks still get >12h
funding/voting windows; early-morning locks get 24h+ as before. No effect
on the existing run #1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 17:50:24 -07:00
prosolisandClaude Opus 4.7 5e4de7b4c3 README: document !coop admgift admin debug command
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 19:25:07 -07:00
prosolisandClaude Opus 4.7 90edb27147 Coop: add !coop admgift admin debug path
Admin-only command that creates a gift without the party-member or
harvest-action checks. Lets a solo tester exercise the basket/mimic
mechanic without recruiting a non-party spectator.

Usage: !coop admgift <run_id> <basket|mimic> [sender_user_id]
       (sender defaults to the caller)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 18:15:26 -07:00
prosolisandClaude Opus 4.7 ecca66da58 Coop: split lock checks (per-minute) from resolution (daily)
Locks were gated behind the same once-per-day 08:00 UTC tick as floor
resolution. A run created after morningHour would wait until next day's
tick to be eligible — meaning actual lock latency could be 24-48h, not the
24h advertised. Worse, a run created late on Day N missed the Day N+1 tick
(too early) AND the Day N+2 tick happens 48h after creation.

Fix: lock checks fire on every ticker minute (cheap timestamp scan over
typically 0-5 open runs). Resolution stays daily, gated by JobCompleted.

Real symptom that prompted this: Run #1 created 17:17 UTC, locked never
fired because the morning tick at 08:00 UTC the next day saw the run as
not-yet-eligible (only 15h elapsed), then marked the daily job complete
before the actual 24h elapsed at 17:17 UTC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 17:35:37 -07:00
prosolisandClaude Opus 4.7 9d949dc649 Hold'em: deduplicate broadcast in solo-vs-bot games
Win/end announcements were sent twice in solo-vs-bot: once via
SendMessage(game.RoomID, ...) for the public room, once via broadcastDM
which iterated the human player's DMRoomID — but in solo-vs-bot the
game.RoomID IS the player's DM. Skip players whose DMRoomID matches
game.RoomID in broadcastDM so the room post isn't echoed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 20:38:16 -07:00
prosolisandClaude Opus 4.7 a1ede53fa9 Equalize co-op gift magnitudes to kill 'always leave' dominant strategy
Old values (open ±7, leave ±4) had matching EV but asymmetric variance;
risk-averse parties had a strict preference to always leave gifts, which
collapsed the dilemma. New values (all ±6) keep EV=0 for both strategies
AND equalize variance. Choice now depends entirely on inferring sender
intent.

Adds TestCoopGiftVarianceSymmetric — fails loud if anyone reintroduces
asymmetric magnitudes. EV test was already in place but only checked the
mean; missed this on first pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:12:22 -07:00
prosolisandClaude Opus 4.7 1ca9e77fa0 Update README with co-op dungeons and revised blacksmith rates
- New Co-op Dungeons subsection covering tier table, all !coop commands,
  funding mechanics, floor events, party strength factors, newbie liability,
  spectator betting, gift system, loot/masterwork rules, wipe semantics, and
  pinning behavior
- Refresh the Adventure feature bullet to mention co-op
- Update Blacksmith section with the post-tuning base rates (T0-T5: 1, 2,
  5, 12, 30, 80) and the softened convexity formula

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:06:58 -07:00
prosolisandClaude Opus 4.7 f3d1e65bf1 Pin co-op invite posts; unpin on lock/cancel
Adds Base.PinEvent / Base.UnpinEvent helpers (m.room.pinned_events state)
that read-modify-write the existing pin list — idempotent on both sides.
The bot needs power level for state events; failures are logged but not
fatal.

Schema: add coop_dungeon_runs.invite_post_id to remember which event to
unpin. Migration entry included.

Wired:
- !coop start: pin the invite post in the games room
- !coop cancel: unpin before posting cancellation
- coopProcessLocks (auto-lock or auto-cancel): unpin before lock/expiry post

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 10:24:21 -07:00
prosolisandClaude Opus 4.7 91af4f1e83 Thanks, Ollama.
Replace flat 'LLM might be offline' error strings in vibe and howami with
the deadpan 'Thanks, Ollama.' callback. Skipped tarot — its 'union-mandated
vacation' line was already the right register and shouldn't be displaced.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 10:09:32 -07:00
prosolisandClaude Opus 4.7 73f3362400 Fix misleading 'combat action refunded' DM on coop invite expiry
Combat actions are only deducted at lock, never at start or join, so
there's nothing to refund when an unjoined invite expires. Just say so.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 09:25:08 -07:00
prosolisandClaude Opus 4.7 9639a86268 Add !coop stats — public telemetry for runs, gold flow, betting, gifts
Aggregates that surface community-level patterns:
- Per-tier outcomes table (open/active/won/wiped/cancelled, win%, avg party size)
- Gold flow: rewards distributed vs funding forfeited to wipes
- Betting: bets placed, distinct bettors, total wagered, paid to winners,
  estimated house cut
- Gift activity: basket/mimic sent, open rates by type
- TwinBee helpfulness: last 30 events vs lifetime

Public command (no admin gate) so the community can debate strategy with
shared context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 09:14:45 -07:00
prosolisandClaude Opus 4.7 e8a3b8b35d Co-op audit fixes: lock scheduler, idempotent payouts, deterministic ties
Audit pass addressed concurrency races, crash-recovery double-pay risks, and
non-determinism in tie tallying.

CRITICAL — concurrency
- coopResolveFloor now acquires advUserLock for every party member (sorted by
  UserID) before mutating state, so concurrent !coop fund / vote / giftvote
  commands serialize against the scheduler.

CRITICAL — crash idempotency
- Add coop_dungeon_runs.last_resolved_day and coop_dungeon_members.member_payout
  columns (with migration entries) for crash-resume tracking.
- Skip resolution only if last_resolved_day >= day AND status is terminal;
  otherwise re-enter and finish via idempotent operations.
- On resume detection (event already has outcome), reuse the saved roll
  instead of re-rolling — prevents different outcomes on retry.
- claimCoopMemberPayout / claimCoopBetPayout: atomic UPDATE...WHERE col IS NULL
  pattern. Each member/bet can only be paid once; retries are silent no-ops.
- createCoopEvent now INSERT OR IGNORE — idempotent on retry.
- last_resolved_day is set as the FINAL write per floor.

HIGH — bugs
- Lazy-create floor event in resolver if missing — covers crash window
  between lockCoopRun and the original createCoopEvent.
- coopTallyVote sorts tied winners alphabetically before fallback (was
  non-deterministic via Go map iteration).
- Removed dead day-1 wipe combat-action refund block — refund check was
  always false because midnight reset clears state before resolution.

LOW
- Log slog.Error on malformed JSON in parseCoopVotes / parseCoopFundingMap
  instead of silently dropping data.

Tests
- Tally test extended to assert determinism across 50 runs (catches map-iteration
  leaks).
- Added coverage for new tie-with-leader-vote-outside-winners case.
- Idempotency-guard sanity check on LastResolvedDay vs CurrentDay.

Race detector: clean. Full suite: green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 09:01:29 -07:00
prosolisandClaude Opus 4.7 8ad31a0009 Add co-op dungeon system (party runs, voting, betting, gifts, items)
Multi-day party runs with funding decisions, TwinBee-narrated floor events,
spectator parimutuel betting, basket/mimic gift system, and weighted-roll
item distribution including masterwork drops at T4-T5.

Schema (5 tables, 2 fewer than spec by computing helpfulness on-the-fly and
skipping the loot-pending state):
- coop_dungeon_runs / _members (daily funding as JSON column)
- coop_dungeon_events (votes as JSON, used to derive TwinBee helpfulness)
- coop_dungeon_bets / _gifts

Mechanics:
- 2-4 player parties, 24h invite window, locks consume one combat action
- Per-floor success roll: base + sum(funding) + level/pet bonuses + event
  vote modifier + active gift modifiers, clamped to 5..95
- Funding tiers (none/min/std/agg/all_in) with liability cap at +8% for
  under-leveled players
- TwinBee narrates events from existing flavor pool; 20 authored events with
  per-option modifiers and embedded recommendation
- Parimutuel betting with 10% rake; odds line shown on lock/daily/status posts
- Gift modifiers symmetric at 50/50 sender mix so no dominant strategy
- Item drops on success via weight-by-contribution roll; T4 25% / T5 100%
  masterwork chance (random pick from existing T4/T5 defs)

Balance via Monte Carlo (coop_dungeon_balance_test.go):
- All tiers exceed 1.5x solo daily income at average party profile
- Optimal funding strategy walks up tiers correctly (Min/Std/Std/Mixed/Agg)
- All-In stays -EV at every tier (boost lever, not optimal play)

Tests: parsing, liability cap, JSON roundtrips, vote tally with leader
tiebreak, event meta consistency, parimutuel payouts, gift EV symmetry,
weighted-roll distribution (Monte Carlo), masterwork tier gates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 07:58:13 -07:00
prosolisandClaude Opus 4.7 16d64323d9 Fix babysit logging, achievement table name, and blacksmith repair costs
- runAutoBabysitDay now logs daily totals (was silently skipping logBabysitActivity)
- Stop wiping babysit_log on expiry/cancel; clear at next purchase instead so history persists between services
- Fix achievement query referencing wrong table name (babysit_log -> adventure_babysit_log)
- Tune blacksmith baseRates ([1,3,8,20,55,150] -> [1,2,5,12,30,80]) and soften the convexity (damage/100 -> damage/200) so mid-game repair drag isn't punitive

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 07:57:31 -07:00
prosolisandClaude Opus 4.7 be76973fd2 Fix arena gear lockout, extend DM window, scrub mining flavor
Arena helmet auto-equip now preserves the existing helmet to inventory
(or stashes the new drop if the existing piece is strictly higher tier),
and arena gear can be re-equipped via !adventure equip. Shop only blocks
swaps that aren't an upgrade over current arena tier. DM response window
bumped from 15m to 3h so NPC/treasure/shop prompts don't expire while
players are AFK. MiningSuccess flavor pools no longer name specific ores
in prose — uses {ore}/{location}/{tool} placeholders so the narration
matches the actual loot.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 22:23:58 -07:00
prosolisandClaude Opus 4.6 d77c2ebbbb Add activity combo system with +200% per-step multiplier
Messages, links, and images each have independent combo streaks
that multiply passive euro earning. Combo grows 3x per step
(+200%), resets after 10 min idle, with daily caps (50 msg,
10 links, 15 images). !combo command shows current streaks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 16:06:28 -07:00
prosolisandClaude Opus 4.6 148b8d20f2 Add tip audit logging, CFR alignment tests, and multiway scenarios
Three confidence features for the poker tip system:
- Tip audit table (holdem_tip_audit) logs every tip with full context
  for bulk review after real sessions
- CFR alignment test validates all 32 scenarios against the trained
  5M-iteration policy — rules engine never contradicts the bot's AI
- 8 new multiway test scenarios covering all streets and key decisions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 14:11:59 -07:00
prosolisandClaude Opus 4.6 202056e802 Update README with blacksmith, babysit, rivals, housing, pets, NPC docs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 01:07:41 -07:00
prosolisandClaude Opus 4.6 b15c13cde7 Add version system, tax tracking, lottery winner DMs, fmtEuro, audit fixes
- Version system: per-plugin versions via Versioned interface, crash_log and
  version_history DB tables, !version command, crash stats in !botinfo
- Tax tracking: tax_ledger table, trackTaxPaid across all communityTax and
  communityPotAdd sites, tax paid display in !stats and !superstatsexplusalpha
- Lottery: DM winners with winning ticket details and matched numbers bolded
- fmtEuro: generic currency formatter with thousand separators across all
  economy display paths
- Audit fixes: streak_decayed column for Streak Survivor achievement,
  combat_narrative empty-phase guard, crafting success rate integer division,
  RecordCrash nil-DB guard

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 01:07:25 -07:00
prosolisandClaude Opus 4.6 42e6e23900 Overhaul hold'em tips: solver-backed scenarios, equity ranges, validation suite
Replaces hardcoded tip scenarios with solver-frequency-backed decisions, adds
equity range display, fixes bet-size matching tolerance (25% threshold), and
adds comprehensive test coverage for scenario validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 01:06:50 -07:00
prosolisandClaude Opus 4.6 7c450aaefb Add forward-simulating combat engine with consumables, monster abilities, and narrative rendering
Replaces the single-roll probability system for dungeon and arena combat with
a multi-phase simulation engine where gear, buffs, pets, and NPCs are meaningful
mechanical inputs. Adds consumable items (auto-crafted from forage/mine/fish drops),
monster abilities for T2+ enemies, and Dragon Quest-style combat narrative with
phased message delivery.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-18 01:05:22 -07:00
prosolisandClaude Opus 4.6 08e9925d8f Wordle: prefer common words via DreamDict frequency batch lookup
Gather up to 5 candidate words from DreamDict RandomWord, then use
FrequencyBatch to pick the most commonly used one. Players get
recognizable everyday words instead of obscure ones that just clear
the min_freq floor.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 17:15:16 -07:00
prosolisandClaude Opus 4.6 76110f61ca Add housing/pets/Thom Krooke, wordle overhaul, boost system, backup strategy
Part 3 (Housing, Thom Krooke & Pets):
- Housing system with tiered upgrades, mortgage loans, FRED API rates
- Thom Krooke realtor NPC with !thom commands and !thom pay extra principal
- Pet system with arrival, naming, leveling, combat, morning defense
- Pet ditch recovery scales with level, name validation

Wordle overhaul:
- Remove daily expiration — puzzles persist until solved/failed
- Auto-start new puzzle after solve, fail, or skip
- Sequential puzzle IDs instead of date-based
- Auto-create puzzle on first guess if none active

Adventure fixes:
- Double XP/money boost toggle (!adv boost, admin only)
- Fix ensureCharacter wiping existing data on query errors
- Fix rival RPS format string bug
- Fix daily summary empty data (load today+yesterday logs)
- Fix holdem DM-to-room reply routing
- Fix Robbie payout to 25% of item value
- Add fishing to leaderboard and TwinBee calculations
- Add Thom to morning menu
- Persist mortgage rate across restarts via db.CacheGet/Set

Infrastructure:
- Daily database backup via VACUUM INTO with 7-day retention
- Admin DM notification on backup failure (async)
- Daily NPC house balance reset for holdem

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-10 16:50:49 -07:00
prosolisandClaude Opus 4.6 90865d1c51 Fix NPC audit findings: race conditions, debit checks, sniper mechanics
Concurrency:
- npcTrackMessage now acquires advUserLock to prevent lost updates
  on message count and double encounter triggers from rapid messages
- Lock released before spawning encounter goroutine (avoids deadlock)
- Only one NPC encounter fires per message

Security:
- Debit() return value checked for both Misty and Arina payments
- Buff only applied on successful debit; failed debit falls through
  to decline/debuff path
- NPC encounter won't overwrite existing pending interactions (shop,
  treasure discard, etc.)

Arena sniper rework:
- Sniper checked independently before combat roll, not after
- If sniper fires, combat roll is skipped entirely (not a death save)
- Sniper kill renders as standalone narrative with no combat log
- Sniper flavor text rewritten: reads as random arena chaos with no
  hint of Arina's involvement

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 23:01:39 -07:00
prosolisandClaude Opus 4.6 2ef7a29f02 Add Misty & Arina NPCs, fix tier gating, babysit actions, Robbie filter, streak/scheduler bugs
Misty & Arina: random encounter NPCs triggered by chat message counting
(5-10 msg threshold, 7-day cooldown, 7.5% roll). Misty asks for €100 —
accept gives 7-day arena buff (20% gourmet food heals gear + 5 enemy dmg),
decline gives 7-day debuff (20% crowd revenge damage). Arina asks for €5000 —
accept gives 7-day sniper buff (8% instant kill in arena). Effects integrate
into arena round resolution after combat roll; sniper overrides death.

Other fixes:
- Tier gating now uses base skill only — buffs improve success, not access
- Babysitter uses all harvest actions (3/day, 4 on holidays) instead of 1
- Robbie collects all non-equipped items, not just slotted gear
- Robbie timing: fire on first tick >= target hour, not exact minute match
- Streak: dead players who acted still get streak credit
- Streak: dead players skip shame DM (use LastDeathDate, not Alive flag)
- Midnight ticker: in-memory date cache prevents 1439 wasted DB checks/day
- T1 loot values ~3x across all activities to close T1/T2 gap

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 22:41:21 -07:00
prosolisandClaude Opus 4.6 1b825498bd Fix harvest actions remaining to use consistent harvestMax pattern
The nudge message after an action was computing harvest remaining
inline with maxHarvestActions + 1 for holidays instead of using the
harvestMax++ pattern used everywhere else. Functionally equivalent
but inconsistent — would break if the holiday bonus ever changed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 17:22:04 -07:00
prosolisandClaude Opus 4.6 e459b6e78d Full codebase audit: 21 security/robustness fixes, 328 tests across 4 packages
Security & economy:
- Credit()/Debit() reject non-positive amounts (closes infinite-money exploit)
- Lottery ticket purchase uses in-transaction count check (closes TOCTOU race)
- Arena bail channel ownership prevents double-close panic
- Entry.ID validated before exec.Command in fetch-esteemed
- Internal errors no longer leaked to users (forex, esteemed)

Robustness:
- safeGo() panic recovery added to 17 goroutine launch sites across 14 plugins
- db.Close() added and called on shutdown
- Miniflux mutex pattern fixed (snapshot-under-lock)
- Silently ignored DB/JSON errors now logged (lottery_db)

Performance:
- Added indexes: idx_arena_runs_user(user_id, status), idx_euro_bal_user(user_id)

UX:
- Empty !buy args shows usage instead of "No item matching ''"
- "Failed to load character" now suggests !adventure

Test coverage:
- internal/util/parser_test.go (19 tests: XP, levels, progress bar, commands, parsing)
- internal/crypto/crypto_test.go (12 tests: encrypt/decrypt, HMAC, ParseKey)
- internal/plugin/helpers_test.go (30 tests: formatNumber, calc, lottery, chat perks)
- internal/plugin/audit_fixes_test.go (25 tests: safeGo, bail channels, error leaks, overflow)
- internal/db/db_test.go (4 tests: Init, Close, schema indexes)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 17:04:40 -07:00
prosolisandClaude Opus 4.6 68b2f8b7a5 Add chat level perks: XP bonus, shop flavor, rare drop nudge, death pardon (Adventure 2.5 steps 3-6)
- XP bonus: +5% per 10 chat levels (cap +25% at 50+), applies to all
  adventure and arena XP
- Shop flavor: shopkeeper greeting changes at chat levels 10/30/50,
  replacing random flavor for recognized players
- Rare drop nudge: +0.5% per 10 chat levels (cap +2.5%) applied
  additively to treasure and masterwork drop rates
- Death pardon: chat level 20+ gets 33% chance to survive death once
  per 7 days (converts to bad-failure, quiet DM). Does not apply in
  arena. Fires before Sovereign Death's Reprieve check.
- New adventure_chat_perks.go with perk calculation helpers
- LastPardonUsed field + migration on adventure_characters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:50:28 -07:00
prosolisandClaude Opus 4.6 a44a9d9234 Fix character sheet holiday actions, remove dead holiday prompt, cap babysit counter
- Character sheet now checks isHolidayToday() for correct remaining
  action counts on holidays
- Remove renderAdvHolidaySecondPrompt (dead code after economy split)
  and its test
- Babysit harvest counter clamped to max to prevent exceeding budget

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:31:25 -07:00
prosolisandClaude Opus 4.6 7e4fbe5ec8 Add action economy split and cross-plugin chat level lookup (Adventure 2.5 steps 1-2)
Action economy: replace single daily action with 1 combat + 3 harvest
actions per day. Holidays grant +1 to each bucket. Rest consumes all
remaining actions. Arena remains outside both buckets.

- Add CombatActionsUsed/HarvestActionsUsed counters to AdventureCharacter
- Add CanDoCombat/CanDoHarvest/AllActionsUsed/HasActedToday helpers
- Update all 14 ActionTakenToday check sites across adventure, scheduler,
  babysit, twinbee, events, and render
- Morning DM shows action budget and grays out exhausted categories
- Activity resolution checks per-bucket availability before proceeding
- Midnight reset zeros both counters
- Post-action nudge shows remaining actions instead of holiday prompt

Cross-plugin lookup: export XPPlugin.GetLevel(), wire into AdventurePlugin
via p.chatLevel(userID) for upcoming chat level perks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 15:24:49 -07:00
prosolisandClaude Opus 4.6 f57dcd93a5 Fix streak grace period, UNO earnings, hospital UX, wordle/lottery atomicity
- Streak grace now checks LastDeathDate instead of LastActionDate (was
  suppressing streak updates for all active players)
- UNO single-player earnings use net payout (minus wager) not gross
- Hospital sends error message on save failure instead of silent no-op
- Wordle total_earned update moved into stats transaction
- Lottery ticket inserts + community pot add wrapped in single transaction
  with euro refund on failure
- Lottery fixed-tier ticket prize updated with actual prorated amount
- Added last_death_date column to adventure_characters

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 00:54:58 -07:00
prosolisandClaude Opus 4.6 26d2846481 Add !adv alias, hospital insurance billing, wordle/UNO earnings tracking, streak death protection
- Add !adv shorthand for !adventure commands and DM replies
- Hospital bill now shows insurance deduction and amount due
- Refactor hospital nudge from per-goroutine sleep to shared ticker
- Dead players' streaks are frozen; grace period on revival day
- Track wordle earnings in wordle_stats, show in !stats and superstats
- Show UNO net earnings and trivia accuracy % in superstats
- Disable sentiment/profanity emoji reactions (classification still logs)
- Add URL_PREVIEW_IGNORE_USERS env var to skip bot users
- Add DISABLE_WOTD_POST env var, remove unused wotd.Prefetch()
- README updates for all of the above

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-08 00:24:51 -07:00
prosolisandClaude Opus 4.6 ec4574f469 Add Dependabot for weekly Go module updates and tidy go.mod
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-07 23:49:33 -07:00