news: Pete learns to report a contract killing

gogobee's Mischief Makers ships four new event types — a hit going out, the
target walking away from it, the target not walking away, and the monster
turning up to an empty dungeon. Pete 400s an unknown event_type, gogobee retries
and then parks the bulletin forever, so this deploys first.

The anonymity is the story, not an implementation detail. A contract is
anonymous unless the buyer paid extra to sign it, so the lede reports the money
and pointedly not the name Pete doesn't have. A survival unseals the buyer
whether or not they signed — that exposure is the only brake on casual griefing,
and it's the one beat worth leading with. Being maimed buys you nothing: an
anonymous buyer stays anonymous when the contract lands.
This commit is contained in:
prosolis
2026-07-13 20:03:28 -07:00
parent 99574db3e9
commit e85ebe56f7
2 changed files with 97 additions and 0 deletions

View File

@@ -194,6 +194,14 @@ func advEventMeta(eventType string) (label, emoji string) {
return "Pulled out", "🎒"
case "departure":
return "Wandered off", "🚪"
case "mischief_contract":
return "Coin on their head", "😈"
case "mischief_survived":
return "They walked away", "🛡️"
case "mischief_downed":
return "The contract landed", "💀"
case "mischief_fizzled":
return "Nobody home", "🚪"
}
return "Dispatch", "📣"
}
@@ -401,6 +409,37 @@ func renderAdventure(f AdvFact) (headline, lede string, ok bool) {
// the joke tells itself, and the player it's about may well be reading.
return fmt.Sprintf("%s got bored and left without waiting.", f.Subject),
fmt.Sprintf("No orders, no escort, no fuss — %s packed the cheapest kit on the shelf and set off into %s%s. Nobody told them to. Nobody talked them out of it either. We'll let you know how it goes.", f.Subject, f.Zone, atLevel), true
case "mischief_contract":
// Somebody paid to have a monster sent after an adventurer who is out in a
// dungeon right now. Anonymous unless the buyer paid extra to sign it, and
// the anonymity is the story: Pete reports the money, not the name he
// doesn't have. Opponent carries the buyer only when it's public.
if f.Opponent != "" {
return fmt.Sprintf("%s has put %s on %s's head.", f.Opponent, f.Stakes, f.Subject),
fmt.Sprintf("No secret about it — %s paid for a %s to go find %s out in whatever hole they're currently down, and signed the thing. It's out there looking right now. If %s comes back breathing, they keep a cut of that money.",
f.Opponent, strings.ToLower(f.Boss), f.Subject, f.Subject), true
}
return fmt.Sprintf("Someone's put %s on %s's head.", f.Stakes, f.Subject),
fmt.Sprintf("Word came in quiet: %s has been paid for a %s to go looking for %s, and whoever paid it isn't saying so. It's already out there. Survive it and the money's theirs — and we all find out who signed the cheque.",
f.Stakes, strings.ToLower(f.Boss), f.Subject), true
case "mischief_survived":
// The unseal. A survival is the only thing that names an anonymous buyer,
// and it is the whole brake on casual griefing — so Pete leads with it.
return fmt.Sprintf("%s walked away from it. It was %s who paid.", f.Subject, f.Opponent),
fmt.Sprintf("%s came for %s, and %s is the one still standing — %s richer for the trouble. The contract's been opened up, and the name inside it is %s. Make of that what you will, folks.",
f.Boss, f.Subject, f.Subject, f.Stakes, f.Opponent), true
case "mischief_downed":
who := "Nobody's saying who paid for it"
if f.Opponent != "" {
who = fmt.Sprintf("%s paid for it, and put their name on it", f.Opponent)
}
return fmt.Sprintf("%s didn't walk away.", f.Subject),
fmt.Sprintf("A %s found %s mid-run%s and put them on the floor. They're being carried home — alive, which is more than the contract asked for, but that expedition's finished. %s.",
f.Boss, f.Subject, atLevel, who), true
case "mischief_fizzled":
return fmt.Sprintf("The monster sent for %s arrived to an empty dungeon.", f.Subject),
fmt.Sprintf("Somebody spent good money to have %s ambushed, and %s had already gone home. It wandered the halls for a bit and left. Most of the fee's been refunded. The rest, the town's keeping.",
f.Subject, f.Subject), true
case "arrival":
return fmt.Sprintf("Welcome to the realm, %s!", f.Subject),
fmt.Sprintf("A new %s just walked through the gates. Say hello if you see them out there.", f.ClassRace), true

View File

@@ -311,3 +311,61 @@ func TestAdventureDisabled(t *testing.T) {
t.Errorf("disabled: status = %d, want 404", rw.Code)
}
}
// TestRenderMischief: gogobee's four mischief event types must all render. An
// unknown event_type is a 400 at ingest, which gogobee retries and then parks
// forever — so "Pete deploys first" only helps if Pete actually knows the types.
//
// It also pins the anonymity contract, which is the feature's whole social
// engine: an unsigned contract must not name the buyer, and a survival must.
func TestRenderMischief(t *testing.T) {
anon := AdvFact{EventType: "mischief_contract", Tier: "priority",
Subject: "Josie", Boss: "Elite", Stakes: "€350", Level: 14}
hl, lede, ok := renderAdventure(anon)
if !ok {
t.Fatal("mischief_contract did not render — ingest would 400")
}
if strings.Contains(hl+lede, "Brannigan") {
t.Error("anonymous contract leaked a buyer name")
}
if !strings.Contains(hl, "€350") {
t.Errorf("contract headline lost the stakes: %q", hl)
}
signed := anon
signed.Opponent = "Brannigan"
hl, _, ok = renderAdventure(signed)
if !ok || !strings.Contains(hl, "Brannigan") {
t.Errorf("signed contract should name the buyer: %q (ok=%v)", hl, ok)
}
// The unseal: a survival names the buyer whether or not they signed.
survived := AdvFact{EventType: "mischief_survived", Tier: "priority",
Subject: "Josie", Opponent: "Brannigan", Boss: "Bone Colossus", Stakes: "€228"}
hl, _, ok = renderAdventure(survived)
if !ok || !strings.Contains(hl, "Brannigan") {
t.Errorf("survival must unseal the buyer: %q (ok=%v)", hl, ok)
}
// A downed target with an anonymous buyer stays anonymous — being maimed
// doesn't buy you the name.
downed := AdvFact{EventType: "mischief_downed", Tier: "priority",
Subject: "Josie", Boss: "Bone Colossus", Level: 14}
hl, lede, ok = renderAdventure(downed)
if !ok {
t.Fatal("mischief_downed did not render")
}
if strings.Contains(hl+lede, "Brannigan") {
t.Error("anonymous buyer named on a downed contract")
}
if _, _, ok := renderAdventure(AdvFact{EventType: "mischief_fizzled", Subject: "Josie", Stakes: "€315"}); !ok {
t.Error("mischief_fizzled did not render")
}
for _, et := range []string{"mischief_contract", "mischief_survived", "mischief_downed", "mischief_fizzled"} {
if lbl, _ := advEventMeta(et); lbl == "Dispatch" {
t.Errorf("%s has no permalink label", et)
}
}
}